Merge latest Mapbase v8.0 changes into MP branch

This commit is contained in:
ALLEN-PC\acj30 2025-03-01 00:10:12 -06:00
commit 9a776b666b
128 changed files with 30900 additions and 1422 deletions

1
.gitignore vendored
View File

@ -26,7 +26,6 @@ ipch
*.xcodeproj/
obj*/
!devtools/*.mak
sp/src/devtools/makefile_base_posix.mak.link
!utils/smdlexp/smdlexp.mak
# Specific Source build products

View File

@ -1,6 +1,6 @@
//=========================================================================================================================
Mapbase v7.3 - Source 2013
Mapbase v8.0 - Source 2013
https://github.com/mapbase-source/source-sdk-2013
https://www.moddb.com/mods/mapbase
@ -119,6 +119,7 @@ Direct contributions:
- https://github.com/mapbase-source/source-sdk-2013/pull/248 (soundlevel_t conversation warning fix by Mechami)
- https://github.com/mapbase-source/source-sdk-2013/pull/266 ("OnPhysGunPull" output in CPhysicsProp by rlenhub)
- https://github.com/mapbase-source/source-sdk-2013/pull/292 (env_headcrabcanister random spawn type by arbabf)
- https://github.com/mapbase-source/source-sdk-2013/pull/293 (Restore text selection code by SanyaSho)
- https://github.com/mapbase-source/source-sdk-2013/pull/294 (SDK_LightmappedGeneric editor blend swap fix by azzyr)
- https://github.com/mapbase-source/source-sdk-2013/pull/308 (BlurFilterY fix by Wikot235)
- https://github.com/mapbase-source/source-sdk-2013/pull/312 (Zombie improvements by Wikot235)
@ -126,6 +127,12 @@ Direct contributions:
- https://github.com/mapbase-source/source-sdk-2013/pull/324 (server-only info/func_null by SirYodaJedi)
- https://github.com/mapbase-source/source-sdk-2013/pull/333 (Cvar to transition levels while in MOVETYPE_NOCLIP by Wikot235)
- https://github.com/mapbase-source/source-sdk-2013/pull/342 (NaN particle cull radius fix by celisej567)
- https://github.com/mapbase-source/source-sdk-2013/pull/360 (.gitignore update by Unusuario2)
- https://github.com/mapbase-source/source-sdk-2013/pull/381 (color_correction_volume crash fix by Nbc66)
- https://github.com/mapbase-source/source-sdk-2013/pull/385 (VRAD -ultrafast command by Unusuario2)
- https://github.com/mapbase-source/source-sdk-2013/pull/391 (VRAD -extrapasses command by Unusuario2)
- https://github.com/mapbase-source/source-sdk-2013/pull/393 (Additional VBSP options doc by Unusuario2)
- https://github.com/mapbase-source/source-sdk-2013/pull/397 (Viewmodel camera bone by Nbc66)
- https://github.com/mapbase-source/mapbase-game-src/pull/1 (Advanced video options duplicate field name fix by arbabf; This is asset-based and not reflected in the code)
- https://github.com/mapbase-source/mapbase-game-src/pull/2 (gameinfo.txt typo fix by CarePackage17; This is asset-based and not reflected in the code)
- https://github.com/mapbase-source/mapbase-game-src/pull/3 (HudMessage cutoff fix by arbabf; This is asset-based and not reflected in the code)
@ -154,10 +161,12 @@ Direct contributions:
=-- https://github.com/mapbase-source/source-sdk-2013/pull/192 (VScript hook manager and fixes)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/206 (Fix CScriptNetMsgHelper::WriteEntity())
=-- https://github.com/mapbase-source/source-sdk-2013/pull/213 (VScript HUD visibility control, optimizations for 3D skybox angles/fake worldportals)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/221 (VScript serialization rewrite)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/229 (VScript VGUI HUD viewport parenting, game_text and vgui_text_display VScript font fallback)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/260 (CScriptNetPropManager rewrite)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/261 (Misc VScript additions)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/279 (weapon_custom_scripted fixes)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/331 (VScript leak fixes)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/332 (Fix OOB access)
== Contributions from z33ky:
@ -172,6 +181,8 @@ Direct contributions:
=-- https://github.com/mapbase-source/source-sdk-2013/pull/159 (Additional GCC/Linux compilation fixes)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/162 (VS2019 exception specification fix)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/170 (HL2 non-Episodic build fix)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/320 (Fix ScriptHook_t initialization order)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/321 (Prevent return of dangling Vector/QAngle to VScript)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/322 (Small Mapbase fixes)
== Contributions from Petercov:

View File

@ -1577,35 +1577,32 @@ float C_BaseAnimating::ClampCycle( float flCycle, bool isLooping )
//-----------------------------------------------------------------------------
const Vector& C_BaseAnimating::ScriptGetAttachmentOrigin( int iAttachment )
{
static Vector absOrigin;
static QAngle qa;
QAngle qa;
C_BaseAnimating::GetAttachment( iAttachment, absOrigin, qa );
return absOrigin;
}
const Vector& C_BaseAnimating::ScriptGetAttachmentAngles( int iAttachment )
const QAngle& C_BaseAnimating::ScriptGetAttachmentAngles( int iAttachment )
{
static Vector absOrigin;
static Vector absAngles;
static QAngle qa;
Vector absOrigin;
C_BaseAnimating::GetAttachment( iAttachment, absOrigin, qa );
absAngles.x = qa.x;
absAngles.y = qa.y;
absAngles.z = qa.z;
return absAngles;
return qa;
}
HSCRIPT C_BaseAnimating::ScriptGetAttachmentMatrix( int iAttachment )
HSCRIPT_RC C_BaseAnimating::ScriptGetAttachmentMatrix( int iAttachment )
{
static matrix3x4_t matrix;
matrix3x4_t *matrix = new matrix3x4_t;
C_BaseAnimating::GetAttachment( iAttachment, matrix );
return g_pScriptVM->RegisterInstance( &matrix );
if ( C_BaseAnimating::GetAttachment( iAttachment, *matrix ) )
return g_pScriptVM->RegisterInstance( matrix, true );
delete matrix;
return NULL;
}
void C_BaseAnimating::ScriptGetBoneTransform( int iBone, HSCRIPT hTransform )

View File

@ -469,8 +469,8 @@ public:
#ifdef MAPBASE_VSCRIPT
int ScriptLookupAttachment( const char *pAttachmentName ) { return LookupAttachment( pAttachmentName ); }
const Vector& ScriptGetAttachmentOrigin(int iAttachment);
const Vector& ScriptGetAttachmentAngles(int iAttachment);
HSCRIPT ScriptGetAttachmentMatrix(int iAttachment);
const QAngle& ScriptGetAttachmentAngles(int iAttachment);
HSCRIPT_RC ScriptGetAttachmentMatrix(int iAttachment);
void ScriptGetBoneTransform( int iBone, HSCRIPT hTransform );
void ScriptSetBoneTransform( int iBone, HSCRIPT hTransform );

View File

@ -90,11 +90,10 @@ static inline bool ShouldDrawLocalPlayerViewModel( void )
C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer();
if (localplayer)
{
if (localplayer->m_bDrawPlayerModelExternally)
if (localplayer->DrawingPlayerModelExternally() && localplayer->InFirstPersonView())
{
// If this isn't the main view, draw the weapon.
view_id_t viewID = CurrentViewID();
if (viewID != VIEW_MAIN && viewID != VIEW_INTRO_CAMERA)
if (!localplayer->InPerspectiveView())
return false;
}
@ -227,8 +226,16 @@ ShadowType_t C_BaseCombatWeapon::ShadowCastType()
if (!IsBeingCarried())
return SHADOWS_RENDER_TO_TEXTURE;
if (IsCarriedByLocalPlayer() && !C_BasePlayer::ShouldDrawLocalPlayer())
return SHADOWS_NONE;
if (IsCarriedByLocalPlayer())
{
if (!C_BasePlayer::ShouldDrawLocalPlayer())
return SHADOWS_NONE;
#ifdef MAPBASE
if (C_BasePlayer::GetLocalPlayer() && C_BasePlayer::GetLocalPlayer()->ShadowCastType() == SHADOWS_NONE)
return SHADOWS_NONE;
#endif
}
return SHADOWS_RENDER_TO_TEXTURE;
}
@ -461,7 +468,7 @@ bool C_BaseCombatWeapon::ShouldDraw( void )
#ifdef MAPBASE
// We're drawing this in non-main views, handle it in DrawModel()
if ( pLocalPlayer->m_bDrawPlayerModelExternally )
if ( pLocalPlayer->DrawingPlayerModelExternally() )
return true;
#endif
@ -514,11 +521,10 @@ int C_BaseCombatWeapon::DrawModel( int flags )
if ( localplayer )
{
#ifdef MAPBASE
if (localplayer->m_bDrawPlayerModelExternally)
if (GetOwner() == localplayer && localplayer->DrawingPlayerModelExternally())
{
// If this isn't the main view, draw the weapon.
view_id_t viewID = CurrentViewID();
if ( (!localplayer->InFirstPersonView() || (viewID != VIEW_MAIN && viewID != VIEW_INTRO_CAMERA)) && (viewID != VIEW_SHADOW_DEPTH_TEXTURE || !localplayer->IsEffectActive(EF_DIMLIGHT)) )
if ( (!localplayer->InPerspectiveView() || !localplayer->InFirstPersonView()) && (CurrentViewID() != VIEW_SHADOW_DEPTH_TEXTURE || !localplayer->IsEffectActive(EF_DIMLIGHT)))
{
// TODO: Is this inefficient?
int nModelIndex = GetModelIndex();
@ -537,6 +543,10 @@ int C_BaseCombatWeapon::DrawModel( int flags )
return iDraw;
}
else
{
return 0;
}
}
#endif
if ( localplayer->IsObserver() && GetOwner() )
@ -554,6 +564,24 @@ int C_BaseCombatWeapon::DrawModel( int flags )
return BaseClass::DrawModel( flags );
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool C_BaseCombatWeapon::DispatchMuzzleEffect( const char *options, bool isFirstPerson )
{
// Don't show muzzle flashes in first-person
C_BasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if (pPlayer)
{
if (pPlayer->DrawingPlayerModelExternally() && pPlayer->InFirstPersonView())
return false;
}
return BaseClass::DispatchMuzzleEffect( options, isFirstPerson );
}
#endif
//-----------------------------------------------------------------------------
// Allows the client-side entity to override what the network tells it to use for

View File

@ -437,8 +437,7 @@ ScriptHook_t C_BaseEntity::g_Hook_UpdateOnRemove;
ScriptHook_t C_BaseEntity::g_Hook_ModifyEmitSoundParams;
#endif
BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities" )
DEFINE_SCRIPT_INSTANCE_HELPER( &g_BaseEntityScriptInstanceHelper )
BEGIN_ENT_SCRIPTDESC_ROOT_WITH_HELPER( C_BaseEntity, "Root class of all client-side entities", &g_BaseEntityScriptInstanceHelper )
DEFINE_SCRIPTFUNC_NAMED( GetAbsOrigin, "GetOrigin", "" )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetForward, "GetForwardVector", "Get the forward vector of the entity" )
#ifdef MAPBASE_VSCRIPT

View File

@ -129,6 +129,16 @@ ConVar demo_fov_override( "demo_fov_override", "0", FCVAR_CLIENTDLL | FCVAR_DONT
// This value is found by hand, and a good value depends more on the in-game models than on actual human shapes.
ConVar cl_meathook_neck_pivot_ingame_up( "cl_meathook_neck_pivot_ingame_up", "7.0" );
ConVar cl_meathook_neck_pivot_ingame_fwd( "cl_meathook_neck_pivot_ingame_fwd", "3.0" );
#ifdef MAPBASE
ConVar cl_meathook_neck_pivot_override( "cl_meathook_neck_pivot_override", "0", FCVAR_NONE, "Overrides playermodel values for meathook and uses cvars only" );
//-------------------------------------------------------------------------------------
ConVar cl_playermodel_draw_externally_override( "cl_playermodel_draw_externally_override", "-1", FCVAR_ARCHIVE, "Overrides developer-placed options to draw the player's model externally." );
ConVar cl_playermodel_legs_override( "cl_playermodel_legs_override", "-1", FCVAR_ARCHIVE, "Overrides developer-placed options to draw the player's model below the camera." );
ConVar cl_playermodel_legs_scale_bones( "cl_playermodel_legs_scale_bones", "1" );
#endif
static ConVar cl_clean_textures_on_death( "cl_clean_textures_on_death", "0", FCVAR_DEVELOPMENTONLY, "If enabled, attempts to purge unused textures every time a freeze cam is shown" );
@ -291,6 +301,7 @@ END_RECV_TABLE()
// See baseplayer_shared.h for more details.
RecvPropInt ( RECVINFO( m_spawnflags ), 0, RecvProxy_ShiftPlayerSpawnflags ),
RecvPropBool ( RECVINFO( m_bDrawPlayerLegs ) ),
RecvPropBool ( RECVINFO( m_bDrawPlayerModelExternally ) ),
RecvPropBool ( RECVINFO( m_bInTriggerFall ) ),
#endif
@ -1537,13 +1548,186 @@ bool C_BasePlayer::ShouldInterpolate()
}
#ifdef MAPBASE
bool C_BasePlayer::InPerspectiveView() const
{
// VIEW_NONE is used by the water intersection view, see CAboveWaterView::CIntersectionView::Draw()
// (TODO: Consider changing the view ID at the source to VIEW_REFRACTION? VIEW_NONE could be an oversight)
view_id_t viewID = CurrentViewID();
return (viewID == VIEW_MAIN || viewID == VIEW_INTRO_CAMERA || viewID == VIEW_REFRACTION || viewID == VIEW_NONE);
}
bool C_BasePlayer::DrawingPlayerModelExternally() const
{
if (cl_playermodel_draw_externally_override.GetInt() > -1)
return cl_playermodel_draw_externally_override.GetBool();
return m_bDrawPlayerModelExternally;
}
bool C_BasePlayer::DrawingLegs() const
{
if (cl_playermodel_legs_override.GetInt() > -1)
return cl_playermodel_legs_override.GetBool();
// For now, don't draw legs if looking up in any way
// (fixes issues with some animations causing clipping with chest)
if (GetAbsAngles().x < 0.0f)
return false;
return m_bDrawPlayerLegs;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CStudioHdr *C_BasePlayer::OnNewModel( void )
{
CStudioHdr *hdr = BaseClass::OnNewModel();
if (!hdr)
return NULL;
KeyValues *modelKeyValues = new KeyValues( "" );
CUtlBuffer buf( 1024, 0, CUtlBuffer::TEXT_BUFFER );
// Init values
m_FirstPersonModelData.Reset();
if (!modelinfo->GetModelKeyValue( GetModel(), buf ))
{
modelKeyValues->deleteThis();
return hdr;
}
if (modelKeyValues->LoadFromBuffer( modelinfo->GetModelName( GetModel() ), buf ))
{
CUtlVector<string_t> iszUsedNames;
for (KeyValues *pkvModelBlock = modelKeyValues; pkvModelBlock != nullptr; pkvModelBlock = pkvModelBlock->GetNextKey())
{
KeyValues *pkvPlayerModelData = pkvModelBlock->FindKey( "playermodel_data" );
if (pkvPlayerModelData)
{
m_FirstPersonModelData.ParseModelData( this, pkvPlayerModelData );
break;
}
}
}
modelKeyValues->deleteThis();
return hdr;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BasePlayer::FirstPersonModelData_t::ParseModelData( C_BasePlayer *pPlayer, KeyValues *pkvPlayerModelData )
{
m_flFirstPersonNeckPivotUp = pkvPlayerModelData->GetFloat( "neck_pivot_up", FLT_MAX );
m_flFirstPersonNeckPivotFwd = pkvPlayerModelData->GetFloat( "neck_pivot_fwd", FLT_MAX );
m_flFirstPersonNeckPivotDuckUp = pkvPlayerModelData->GetFloat( "neck_pivot_duck_up", FLT_MAX );
m_flFirstPersonNeckPivotDuckFwd = pkvPlayerModelData->GetFloat( "neck_pivot_duck_fwd", FLT_MAX );
KeyValues *pkvBoneScales = pkvPlayerModelData->FindKey( "bone_transforms" );
if (pkvBoneScales)
{
KeyValues *pkvSpineTransforms = pkvBoneScales->FindKey( "spine" );
if (pkvSpineTransforms)
{
for (KeyValues *pkvBone = pkvSpineTransforms->GetFirstSubKey(); pkvBone != nullptr; pkvBone = pkvBone->GetNextKey())
{
int nBone = pPlayer->LookupBone( pkvBone->GetName() );
if (nBone == -1)
continue;
m_FirstPersonBoneScales[BoneScales_Spine].Insert(nBone, pkvBone->GetFloat());
}
}
KeyValues *pkvArmsTransforms = pkvBoneScales->FindKey( "arms" );
if (pkvArmsTransforms)
{
for (KeyValues *pkvBone = pkvArmsTransforms->GetFirstSubKey(); pkvBone != nullptr; pkvBone = pkvBone->GetNextKey())
{
int nBone = pPlayer->LookupBone( pkvBone->GetName() );
if (nBone == -1)
continue;
m_FirstPersonBoneScales[BoneScales_Arms].Insert( nBone, pkvBone->GetFloat() );
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: move position and rotation transforms into global matrices
//-----------------------------------------------------------------------------
void C_BasePlayer::BuildTransformations( CStudioHdr *hdr, Vector *pos, Quaternion *q, const matrix3x4_t &cameraTransform, int boneMask, CBoneBitList &boneComputed )
{
BaseClass::BuildTransformations( hdr, pos, q, cameraTransform, boneMask, boneComputed );
if (DrawingLegs() && InPerspectiveView() && InFirstPersonView())
{
//BuildFirstPersonMeathookTransformations( hdr, pos, q, cameraTransform, boneMask, boneComputed, "ValveBiped.Bip01_Head1" );
if (cl_playermodel_legs_scale_bones.GetBool())
{
// For now, only do transforms when we have an active weapon
// (since we typically just cull stuff influenced by viewmodels and upper-body weapon posture, like arms)
if ((GetActiveWeapon() && !GetActiveWeapon()->IsEffectActive( EF_NODRAW )) || GetUseEntity())
{
matrix3x4_t *pFirstZero = NULL;
for (int nMap = 0; nMap < FirstPersonModelData_t::BoneScales_Max; nMap++)
{
const CUtlMap<int, float> &scaleMap = m_FirstPersonModelData.m_FirstPersonBoneScales[nMap];
FOR_EACH_MAP( scaleMap, i )
{
int nBone = scaleMap.Key(i);
if (nBone == -1)
continue;
if (!(hdr->boneFlags( nBone ) & boneMask))
continue;
float flScale = scaleMap.Element(i);
matrix3x4_t &mTransform = GetBoneForWrite( nBone );
if (flScale == 0.0f)
{
if (!pFirstZero)
{
MatrixScaleByZero( mTransform );
pFirstZero = &mTransform;
}
else
{
// Keep zeroes in one place
MatrixCopy( *pFirstZero, mTransform );
}
}
else
{
MatrixScaleBy( flScale, mTransform );
}
}
}
}
}
}
}
#endif
bool C_BasePlayer::ShouldDraw()
{
#ifdef MAPBASE
// We have to "always draw" a player with m_bDrawPlayerModelExternally in order to show up in whatever rendering list all of the views use,
// but we can't put this in ShouldDrawThisPlayer() because we would have no way of knowing if it stomps the other checks that draw the player model anyway.
// As a result, we have to put it here in the central ShouldDraw() function. DrawModel() makes sure we only draw in non-main views and nothing's drawing the model anyway.
return (ShouldDrawThisPlayer() || m_bDrawPlayerModelExternally) && BaseClass::ShouldDraw();
return (ShouldDrawThisPlayer() || DrawingPlayerModelExternally() || DrawingLegs()) && BaseClass::ShouldDraw();
#else
return ShouldDrawThisPlayer() && BaseClass::ShouldDraw();
#endif
@ -1552,12 +1736,16 @@ bool C_BasePlayer::ShouldDraw()
int C_BasePlayer::DrawModel( int flags )
{
#ifdef MAPBASE
if (m_bDrawPlayerModelExternally)
if (DrawingLegs() && InFirstPersonView() && InPerspectiveView())
{
return BaseClass::DrawModel( flags );
}
if (DrawingPlayerModelExternally())
{
// Draw the player in any view except the main or "intro" view, both of which are default first-person views.
// HACKHACK: Also don't draw in shadow depth textures if the player's flashlight is on, as that causes the playermodel to block it.
view_id_t viewID = CurrentViewID();
if (viewID == VIEW_MAIN || viewID == VIEW_INTRO_CAMERA || (viewID == VIEW_SHADOW_DEPTH_TEXTURE && IsEffectActive(EF_DIMLIGHT)))
if (InPerspectiveView() || (CurrentViewID() == VIEW_SHADOW_DEPTH_TEXTURE && IsEffectActive(EF_DIMLIGHT)))
{
// Make sure the player model wouldn't draw anyway...
if (!ShouldDrawThisPlayer())
@ -3122,13 +3310,21 @@ void C_BasePlayer::BuildFirstPersonMeathookTransformations( CStudioHdr *hdr, Vec
return;
}
#ifdef MAPBASE
if ( !InPerspectiveView() )
#else
if ( !DrawingMainView() )
#endif
{
return;
}
// If we aren't drawing the player anyway, don't mess with the bones. This can happen in Portal.
#ifdef MAPBASE
if ( !ShouldDrawThisPlayer() && !DrawingPlayerModelExternally() && !DrawingLegs() )
#else
if( !ShouldDrawThisPlayer() )
#endif
{
return;
}
@ -3149,6 +3345,63 @@ void C_BasePlayer::BuildFirstPersonMeathookTransformations( CStudioHdr *hdr, Vec
Vector vHeadTransformTranslation ( mHeadTransform[0][3], mHeadTransform[1][3], mHeadTransform[2][3] );
float flNeckPivotUp = cl_meathook_neck_pivot_ingame_up.GetFloat();
float flNeckPivotFwd = cl_meathook_neck_pivot_ingame_fwd.GetFloat();
#ifdef MAPBASE
if (DrawingLegs() && !cl_meathook_neck_pivot_override.GetBool())
{
if (m_FirstPersonModelData.m_flFirstPersonNeckPivotUp != FLT_MAX || m_FirstPersonModelData.m_flFirstPersonNeckPivotFwd != FLT_MAX)
{
if (m_FirstPersonModelData.m_flFirstPersonNeckPivotUp != FLT_MAX)
flNeckPivotUp = m_FirstPersonModelData.m_flFirstPersonNeckPivotUp;
if (m_FirstPersonModelData.m_flFirstPersonNeckPivotFwd != FLT_MAX)
flNeckPivotFwd = m_FirstPersonModelData.m_flFirstPersonNeckPivotFwd;
if (GetFlags() & FL_DUCKING || m_Local.m_flDucktime > 0.0f)
{
if (!IsLocalPlayer() || m_Local.m_flDucktime <= 0.0f)
{
if (m_FirstPersonModelData.m_flFirstPersonNeckPivotDuckUp != FLT_MAX)
flNeckPivotUp = m_FirstPersonModelData.m_flFirstPersonNeckPivotDuckUp;
if (m_FirstPersonModelData.m_flFirstPersonNeckPivotDuckFwd != FLT_MAX)
flNeckPivotFwd = m_FirstPersonModelData.m_flFirstPersonNeckPivotDuckFwd;
}
else
{
bool bDucking;
if (IsLocalPlayer())
bDucking = input->GetButtonBits(0) & IN_DUCK;
else
bDucking = GetCurrentUserCommand()->buttons & IN_DUCK;
// HACKHACK using constants from game movement
float flPerc = SimpleSpline( RemapValClamped( m_Local.m_flDucktime, bDucking ? 600.0f : 800.0f, 1000.0f, 0.0f, 1.0f ) );
if (bDucking)
{
// Ducking
//Msg( "Ducking with perc %f (%f)\n", flPerc, m_Local.m_flDucktime );
if (m_FirstPersonModelData.m_flFirstPersonNeckPivotDuckUp != FLT_MAX)
flNeckPivotUp = FLerp( m_FirstPersonModelData.m_flFirstPersonNeckPivotDuckUp, flNeckPivotUp, flPerc );
if (m_FirstPersonModelData.m_flFirstPersonNeckPivotDuckFwd != FLT_MAX)
flNeckPivotFwd = FLerp( m_FirstPersonModelData.m_flFirstPersonNeckPivotDuckFwd, flNeckPivotFwd, flPerc );
}
else
{
// Unducking
//Msg( "Unducking with perc %f (%f)\n", flPerc, m_Local.m_flDucktime );
if (m_FirstPersonModelData.m_flFirstPersonNeckPivotDuckUp != FLT_MAX)
flNeckPivotUp = FLerp( flNeckPivotUp, m_FirstPersonModelData.m_flFirstPersonNeckPivotDuckUp, flPerc );
if (m_FirstPersonModelData.m_flFirstPersonNeckPivotDuckFwd != FLT_MAX)
flNeckPivotFwd = FLerp( flNeckPivotFwd, m_FirstPersonModelData.m_flFirstPersonNeckPivotDuckFwd, flPerc );
}
}
}
}
}
#endif
// Find out where the player's head (driven by the HMD) is in the world.
// We can't move this with animations or effects without causing nausea, so we need to move
// the whole body so that the animated head is in the right place to match the player-controlled head.
@ -3165,7 +3418,7 @@ void C_BasePlayer::BuildFirstPersonMeathookTransformations( CStudioHdr *hdr, Vec
// The head bone is the neck pivot point of the in-game character.
Vector vRealMidEyePos = mWorldFromMideye.GetTranslation();
vRealPivotPoint = vRealMidEyePos - ( mWorldFromMideye.GetUp() * cl_meathook_neck_pivot_ingame_up.GetFloat() ) - ( mWorldFromMideye.GetForward() * cl_meathook_neck_pivot_ingame_fwd.GetFloat() );
vRealPivotPoint = vRealMidEyePos - ( mWorldFromMideye.GetUp() * flNeckPivotUp ) - ( mWorldFromMideye.GetForward() * flNeckPivotFwd );
}
else
{
@ -3173,7 +3426,7 @@ void C_BasePlayer::BuildFirstPersonMeathookTransformations( CStudioHdr *hdr, Vec
Vector vForward, vRight, vUp;
AngleVectors( MainViewAngles(), &vForward, &vRight, &vUp );
vRealPivotPoint = MainViewOrigin() - ( vUp * cl_meathook_neck_pivot_ingame_up.GetFloat() ) - ( vForward * cl_meathook_neck_pivot_ingame_fwd.GetFloat() );
vRealPivotPoint = MainViewOrigin() - ( vUp * flNeckPivotUp ) - ( vForward * flNeckPivotFwd );
}
Vector vDeltaToAdd = vRealPivotPoint - vHeadTransformTranslation;

View File

@ -294,6 +294,15 @@ public:
virtual bool ShouldInterpolate();
#ifdef MAPBASE
bool InPerspectiveView() const; // In a view that renders directly from the player's perspective (and may, for example, render the playermodel)
bool DrawingPlayerModelExternally() const;
bool DrawingLegs() const;
virtual CStudioHdr *OnNewModel( void );
virtual void BuildTransformations( CStudioHdr *pStudioHdr, Vector *pos, Quaternion q[], const matrix3x4_t &cameraTransform, int boneMask, CBoneBitList &boneComputed );
#endif
virtual bool ShouldDraw();
virtual int DrawModel( int flags );
@ -472,6 +481,9 @@ public:
inline void RemoveSpawnFlags( int flags ) { m_spawnflags &= ~flags; }
inline void AddSpawnFlags( int flags ) { m_spawnflags |= flags; }
// Draws the player's model below the camera, visible when the player looks down.
bool m_bDrawPlayerLegs;
// Allows the player's model to draw on non-main views, like monitors or mirrors.
bool m_bDrawPlayerModelExternally;
@ -506,6 +518,10 @@ protected:
virtual void FireGameEvent( IGameEvent *event );
#ifdef MAPBASE
inline CUtlMap<int, float> &GetFirstPersonArmScales() { return m_FirstPersonModelData.m_FirstPersonBoneScales[FirstPersonModelData_t::BoneScales_Arms]; }
#endif
protected:
// Did we just enter a vehicle this frame?
bool JustEnteredVehicle();
@ -560,6 +576,41 @@ private:
bool m_bFiredWeapon;
#ifdef MAPBASE
struct FirstPersonModelData_t
{
void Reset()
{
m_flFirstPersonNeckPivotUp = m_flFirstPersonNeckPivotFwd = FLT_MAX;
m_flFirstPersonNeckPivotDuckUp = m_flFirstPersonNeckPivotDuckFwd = FLT_MAX;
for (int i = 0; i < BoneScales_Max; i++)
{
m_FirstPersonBoneScales[i].RemoveAll();
m_FirstPersonBoneScales[i].SetLessFunc( DefLessFunc( int ) );
}
}
void ParseModelData( C_BasePlayer *pPlayer, KeyValues *pkvPlayerModelData );
enum
{
BoneScales_Spine,
BoneScales_Arms,
BoneScales_Max
};
// Values to scale bones by when drawing playermodel in first person
CUtlMap<int, float> m_FirstPersonBoneScales[BoneScales_Max];
float m_flFirstPersonNeckPivotUp, m_flFirstPersonNeckPivotFwd = FLT_MAX;
float m_flFirstPersonNeckPivotDuckUp, m_flFirstPersonNeckPivotDuckFwd = FLT_MAX;
};
FirstPersonModelData_t m_FirstPersonModelData;
#endif
// Player flashlight dynamic light pointers
CFlashlightEffect *m_pFlashlight;

View File

@ -399,10 +399,38 @@ int C_SpriteRenderer::DrawSprite(
if ( group == RENDER_GROUP_VIEW_MODEL_TRANSLUCENT || group == RENDER_GROUP_VIEW_MODEL_OPAQUE )
return 0;
}
#ifdef MAPBASE
if (ent->m_iViewHideFlags > 0)
{
// Hide this entity if it's not supposed to be drawn in this view.
if (ent->m_iViewHideFlags & (1 << CurrentViewID()))
{
return 0;
}
}
#endif
QAngle temp;
ent->GetAttachment( attachmentindex, effect_origin, temp );
}
}
#ifdef MAPBASE
if ( entity )
{
C_BaseEntity *ent = entity->GetBaseEntity();
if ( ent )
{
if (ent->m_iViewHideFlags > 0)
{
// Hide this entity if it's not supposed to be drawn in this view.
if (ent->m_iViewHideFlags & (1 << CurrentViewID()))
{
return 0;
}
}
}
}
#endif
if ( rendermode != kRenderNormal )
{

View File

@ -49,8 +49,6 @@ $Project
$File "$SRCDIR\game\shared\mapbase\MapEdit.h"
$File "$SRCDIR\game\shared\mapbase\matchers.cpp"
$File "$SRCDIR\game\shared\mapbase\matchers.h"
$File "$SRCDIR\game\shared\mapbase\singleplayer_animstate.cpp"
$File "$SRCDIR\game\shared\mapbase\singleplayer_animstate.h"
$File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.cpp" [$MAPBASE_VSCRIPT]
$File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.h" [$MAPBASE_VSCRIPT]
$File "$SRCDIR\game\shared\mapbase\vscript_singletons.cpp" [$MAPBASE_VSCRIPT]

View File

@ -10,6 +10,10 @@ $Project
{
$Folder "Mapbase"
{
$File "$SRCDIR\game\shared\mapbase\mapbase_playeranimstate.cpp"
$File "$SRCDIR\game\shared\mapbase\mapbase_playeranimstate.h"
$File "$SRCDIR\game\shared\mapbase\protagonist_system.cpp"
$File "$SRCDIR\game\shared\mapbase\protagonist_system.h"
$File "$SRCDIR\game\shared\mapbase\vscript_funcs_hl2.cpp" [$MAPBASE_VSCRIPT]
$File "mapbase\c_weapon_custom_hl2.cpp"
}

View File

@ -157,6 +157,22 @@ void CColorCorrectionMgr::CommitColorCorrectionWeights()
}
m_colorCorrectionWeights.RemoveAll();
}
void CColorCorrectionMgr::LevelShutdownPreEntity()
{
//Clean up the vectors when shuting down a level
//will keep dangling pointers inside of the vector causing a nullptr crash
if (g_ColorCorrectionVolumeList.Base())
{
g_ColorCorrectionVolumeList.Purge();
}
if (g_ColorCorrectionList.Base())
{
g_ColorCorrectionList.Purge();
}
}
#else
void CColorCorrectionMgr::SetColorCorrectionWeight( ClientCCHandle_t h, float flWeight )
{

View File

@ -76,6 +76,8 @@ private:
CUtlVector< SetWeightParams_t > m_colorCorrectionWeights;
void CommitColorCorrectionWeights();
void LevelShutdownPreEntity();
#endif
};

View File

@ -31,8 +31,12 @@ ConVar cl_npc_speedmod_outtime( "cl_npc_speedmod_outtime", "1.5", FCVAR_CLIENTDL
IMPLEMENT_CLIENTCLASS_DT(C_BaseHLPlayer, DT_HL2_Player, CHL2_Player)
RecvPropDataTable( RECVINFO_DT(m_HL2Local),0, &REFERENCE_RECV_TABLE(DT_HL2Local) ),
RecvPropBool( RECVINFO( m_fIsSprinting ) ),
#ifdef SP_ANIM_STATE
#ifdef MAPBASE
RecvPropInt( RECVINFO( m_nProtagonistIndex ) ),
#endif
#if defined(SP_ANIM_STATE) && !defined(MAPBASE_MP)
RecvPropFloat( RECVINFO( m_flAnimRenderYaw ) ),
RecvPropFloat( RECVINFO( m_flAnimRenderZ ) ),
#endif
END_RECV_TABLE()
@ -96,6 +100,8 @@ C_BaseHLPlayer::C_BaseHLPlayer()
#ifdef MAPBASE
ConVarRef scissor("r_flashlightscissor");
scissor.SetValue("0");
m_nProtagonistIndex = -1;
#endif
}
@ -111,7 +117,7 @@ void C_BaseHLPlayer::OnDataChanged( DataUpdateType_t updateType )
SetNextClientThink( CLIENT_THINK_ALWAYS );
}
#ifdef SP_ANIM_STATE
#if defined(SP_ANIM_STATE) && !defined(MAPBASE_MP)
if (m_flAnimRenderYaw != FLT_MAX)
{
m_angAnimRender = QAngle( 0, m_flAnimRenderYaw, 0 );
@ -121,6 +127,22 @@ void C_BaseHLPlayer::OnDataChanged( DataUpdateType_t updateType )
BaseClass::OnDataChanged( updateType );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseHLPlayer::AddEntity( void )
{
BaseClass::AddEntity();
#ifdef MAPBASE_MP
if (m_pPlayerAnimState)
{
QAngle angEyeAngles = EyeAngles();
m_pPlayerAnimState->Update( angEyeAngles.y, angEyeAngles.x );
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
@ -682,24 +704,78 @@ bool C_BaseHLPlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd )
void C_BaseHLPlayer::BuildTransformations( CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed )
{
BaseClass::BuildTransformations( hdr, pos, q, cameraTransform, boneMask, boneComputed );
/*#ifdef MAPBASE
// BuildFirstPersonMeathookTransformations is used prior to this when drawing legs
if (!DrawingLegs() || !InPerspectiveView() || !InFirstPersonView())
#endif*/
BuildFirstPersonMeathookTransformations( hdr, pos, q, cameraTransform, boneMask, boneComputed, "ValveBiped.Bip01_Head1" );
}
#ifdef SP_ANIM_STATE
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const Vector &C_BaseHLPlayer::GetRenderOrigin()
{
#ifndef MAPBASE_MP
if (m_flAnimRenderZ != 0.0f)
{
static Vector vecRender;
vecRender = BaseClass::GetRenderOrigin();
vecRender.z += m_flAnimRenderZ;
return vecRender;
}
#endif
return BaseClass::GetRenderOrigin();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const QAngle& C_BaseHLPlayer::GetRenderAngles( void )
{
#ifdef MAPBASE_MP
if ( m_pPlayerAnimState )
{
return m_pPlayerAnimState->GetRenderAngles();
}
#else
if ( m_flAnimRenderYaw != FLT_MAX )
{
return m_angAnimRender;
}
#endif
else
{
return BaseClass::GetRenderAngles();
}
}
//-----------------------------------------------------------------------------
// Purpose: model-change notification. Fires on dynamic load completion as well
//-----------------------------------------------------------------------------
CStudioHdr *C_BaseHLPlayer::OnNewModel()
{
CStudioHdr *hdr = BaseClass::OnNewModel();
#ifdef MAPBASE_MP
// Clears the animation state if we already have one.
if ( m_pPlayerAnimState != NULL )
{
m_pPlayerAnimState->Release();
m_pPlayerAnimState = NULL;
}
if ( hdr && hdr->HaveSequenceForActivity(ACT_HL2MP_IDLE) /*&& hl2_use_sp_animstate.GetBool()*/ )
{
// Here we create and init the player animation state.
m_pPlayerAnimState = CreatePlayerAnimationState(this);
}
#endif
return hdr;
}
#endif

View File

@ -15,8 +15,8 @@
#include "c_baseplayer.h"
#include "c_hl2_playerlocaldata.h"
#if !defined( HL2MP ) && defined ( MAPBASE )
#include "mapbase/singleplayer_animstate.h"
#if defined ( MAPBASE )
#include "mapbase/mapbase_playeranimstate.h"
#endif
class C_BaseHLPlayer : public C_BasePlayer
@ -29,6 +29,7 @@ public:
C_BaseHLPlayer();
virtual void OnDataChanged( DataUpdateType_t updateType );
virtual void AddEntity( void );
void Weapon_DropPrimary( void );
@ -65,8 +66,14 @@ public:
virtual void HandleSpeedChanges( CMoveData *mv ){}
virtual void ReduceTimers( CMoveData *mv ){}
#ifdef MAPBASE
int GetProtagonistIndex() const { return m_nProtagonistIndex; }
#endif
#ifdef SP_ANIM_STATE
virtual const Vector& GetRenderOrigin();
virtual const QAngle& GetRenderAngles( void );
virtual CStudioHdr *OnNewModel();
#endif
public:
@ -89,11 +96,18 @@ private:
bool m_bPlayUseDenySound; // Signaled by PlayerUse, but can be unset by HL2 ladder code...
float m_flSpeedMod;
float m_flExitSpeedMod;
#ifdef MAPBASE
int m_nProtagonistIndex;
#endif
#ifdef SP_ANIM_STATE
#ifdef MAPBASE_MP
CMapbasePlayerAnimState *m_pPlayerAnimState;
#elif MAPBASE
// At the moment, we network the render angles since almost none of the player anim stuff is done on the client in SP.
// If any of this is ever adapted for MP, this method should be replaced with replicating/moving the anim state to the client.
float m_flAnimRenderYaw;
float m_flAnimRenderZ;
QAngle m_angAnimRender;
#endif

View File

@ -100,7 +100,11 @@ void CHudSuitPower::OnThink( void )
}
bool flashlightActive = pPlayer->IsFlashlightActive();
#ifdef MAPBASE
bool sprintActive = pPlayer->IsSprintActive();
#else
bool sprintActive = pPlayer->IsSprinting();
#endif
bool breatherActive = pPlayer->IsBreatherActive();
int activeDevices = (int)flashlightActive + (int)sprintActive + (int)breatherActive;
@ -250,7 +254,11 @@ void CHudSuitPower::Paint()
ypos += text2_gap;
}
#ifdef MAPBASE
if (pPlayer->IsSprintActive())
#else
if (pPlayer->IsSprinting())
#endif
{
tempString = g_pVGuiLocalize->Find("#Valve_Hud_SPRINT");

View File

@ -1031,7 +1031,7 @@ void CScriptSurface::DrawColoredTextRect( int font, int x, int y, int w, int h,
#define __base() this->_base
#define BEGIN_SCRIPTDESC_VGUI( panelClass )\
BEGIN_SCRIPTDESC_NAMED( CScript_##panelClass##_Helper, IScriptVGUIObject, #panelClass, "" )\
BEGIN_SCRIPTDESC_NAMED_WITH_HELPER( CScript_##panelClass##_Helper, IScriptVGUIObject, #panelClass, "", VGUI_SCRIPT_INSTANCE_HELPER )\
DEFINE_VGUI_SCRIPTFUNC_##panelClass()
//
@ -1235,9 +1235,9 @@ class CScriptVGUIScriptInstanceHelper : public IScriptInstanceHelper
static CScriptVGUIScriptInstanceHelper g_ScriptVGUIScriptInstanceHelper;
#define DEFINE_VGUI_SCRIPT_INSTANCE_HELPER() DEFINE_SCRIPT_INSTANCE_HELPER( &g_ScriptVGUIScriptInstanceHelper )
#define VGUI_SCRIPT_INSTANCE_HELPER &g_ScriptVGUIScriptInstanceHelper
#else
#define DEFINE_VGUI_SCRIPT_INSTANCE_HELPER()
#define VGUI_SCRIPT_INSTANCE_HELPER NULL
#endif
@ -1866,8 +1866,6 @@ public:
};
#define DEFINE_VGUI_SCRIPTFUNC_Panel()\
DEFINE_VGUI_SCRIPT_INSTANCE_HELPER()\
\
DEFINE_SCRIPTFUNC( Destroy, "" )\
DEFINE_SCRIPTFUNC( MakeReadyForUse, "" )\
DEFINE_SCRIPTFUNC( GetName, "" )\
@ -2612,9 +2610,17 @@ public:
static inline void SetHScript( HSCRIPT &var, HSCRIPT val )
{
if ( var && g_pScriptVM )
g_pScriptVM->ReleaseScript( var );
var = val;
if ( g_pScriptVM )
{
if ( var )
g_pScriptVM->ReleaseScript( var );
var = g_pScriptVM->CopyObject( val );
}
else
{
var = NULL;
}
}
#define CheckCallback(s)\

View File

@ -912,7 +912,7 @@ void CPrediction::RunCommand( C_BasePlayer *player, CUserCmd *ucmd, IMoveHelper
C_BaseCombatWeapon *weapon = dynamic_cast< C_BaseCombatWeapon * >( CBaseEntity::Instance( ucmd->weaponselect ) );
if ( weapon )
{
player->SelectItem( weapon->GetClassname(), ucmd->weaponsubtype );
player->SelectItem( weapon->GetName(), ucmd->weaponsubtype );
}
}

View File

@ -127,6 +127,7 @@ ConVar gl_clear_randomcolor( "gl_clear_randomcolor", "0", FCVAR_CHEAT, "Clear th
static ConVar r_farz( "r_farz", "-1", FCVAR_CHEAT, "Override the far clipping plane. -1 means to use the value in env_fog_controller." );
#ifdef MAPBASE
static ConVar r_nearz( "r_nearz", "-1", FCVAR_CHEAT, "Override the near clipping plane. -1 means to use the default value (usually 7)." );
static ConVar cl_camera_anim_intensity("cl_camera_anim_intensity", "1.0", FCVAR_ARCHIVE, "Intensity of cambone animations");
#endif
static ConVar cl_demoviewoverride( "cl_demoviewoverride", "0", 0, "Override view during demo playback" );
@ -1254,6 +1255,37 @@ void CViewRender::Render( vrect_t *rect )
g_ClientVirtualReality.OverlayHUDQuadWithUndistort( viewEye, bDoUndistort, g_pClientMode->ShouldBlackoutAroundHUD(), bTranslucent );
}
}
#ifdef MAPBASE
//--------------------------------
// Handle camera anims
//--------------------------------
if (!UseVR() && pPlayer && cl_camera_anim_intensity.GetFloat() > 0)
{
if (pPlayer->GetViewModel(0))
{
int attachment = pPlayer->GetViewModel(0)->LookupAttachment("camera");
if (attachment != -1)
{
int rootBone = pPlayer->GetViewModel(0)->LookupAttachment("camera_root");
Vector cameraOrigin = Vector(0, 0, 0);
QAngle cameraAngles = QAngle(0, 0, 0);
Vector rootOrigin = Vector(0, 0, 0);
QAngle rootAngles = QAngle(0, 0, 0);
pPlayer->GetViewModel(0)->GetAttachmentLocal(attachment, cameraOrigin, cameraAngles);
if (rootBone != -1)
{
pPlayer->GetViewModel(0)->GetAttachmentLocal(rootBone, rootOrigin, rootAngles);
cameraOrigin -= rootOrigin;
cameraAngles -= rootAngles;
}
viewEye.angles += cameraAngles * cl_camera_anim_intensity.GetFloat();
viewEye.origin += cameraOrigin * cl_camera_anim_intensity.GetFloat();
}
}
}
#endif // MAPBASE
}

View File

@ -44,6 +44,10 @@
extern IScriptManager *scriptmanager;
extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * );
#ifdef MAPBASE_VSCRIPT
extern int vscript_debugger_port;
#endif
// #define VMPROFILE 1
#ifdef VMPROFILE
@ -250,8 +254,7 @@ class CMaterialProxyScriptInstanceHelper : public IScriptInstanceHelper
CMaterialProxyScriptInstanceHelper g_MaterialProxyScriptInstanceHelper;
BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptMaterialProxy, "CScriptMaterialProxy", "Material proxy for VScript" )
DEFINE_SCRIPT_INSTANCE_HELPER( &g_MaterialProxyScriptInstanceHelper )
BEGIN_SCRIPTDESC_ROOT_NAMED_WITH_HELPER( CScriptMaterialProxy, "CScriptMaterialProxy", "Material proxy for VScript", &g_MaterialProxyScriptInstanceHelper )
DEFINE_SCRIPTFUNC( GetVarString, "Gets a material var's string value" )
DEFINE_SCRIPTFUNC( GetVarInt, "Gets a material var's int value" )
DEFINE_SCRIPTFUNC( GetVarFloat, "Gets a material var's float value" )
@ -761,6 +764,14 @@ bool VScriptClientInit()
//g_pScriptVM->RegisterInstance( &g_ScriptEntityIterator, "Entities" );
#endif
#ifdef MAPBASE_VSCRIPT
if ( vscript_debugger_port )
{
g_pScriptVM->ConnectDebugger( vscript_debugger_port );
vscript_debugger_port = 0;
}
#endif
if ( scriptLanguage == SL_SQUIRREL )
{
g_pScriptVM->Run( g_Script_vscript_client );
@ -862,11 +873,19 @@ public:
VScriptClientTerm();
}
virtual void FrameUpdatePostEntityThink()
#ifdef MAPBASE_VSCRIPT
virtual void Update( float frametime )
{
if ( g_pScriptVM )
g_pScriptVM->Frame( frametime );
}
#else
virtual void FrameUpdatePostEntityThink()
{
if ( g_pScriptVM )
g_pScriptVM->Frame( gpGlobals->frametime );
}
#endif
bool m_bAllowEntityCreationInScripts;
};

View File

@ -16,6 +16,10 @@
#include "saverestore_utlvector.h"
#include "dt_utlvector_send.h"
#ifdef MAPBASE_VSCRIPT
#include "mapbase/vscript_funcs_shared.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
@ -473,7 +477,8 @@ void CAnimationLayer::DispatchAnimEvents( CBaseAnimating *eventHandler, CBaseAni
}
#ifdef MAPBASE_VSCRIPT
if (eventHandler->m_ScriptScope.IsInitialized() && eventHandler->ScriptHookHandleAnimEvent( &event ) == false)
scriptanimevent_t wrapper( event );
if (eventHandler->m_ScriptScope.IsInitialized() && !eventHandler->ScriptHookHandleAnimEvent( wrapper ))
continue;
#endif

View File

@ -42,17 +42,18 @@ HSCRIPT INextBotComponent::GetScriptInstance()
}
//--------------------------------------------------------------------------------------------------------------
#ifdef MAPBASE_VSCRIPT // TODO: Better way of supporting this?
#ifdef TF_DLL
BEGIN_ENT_SCRIPTDESC_ROOT_WITH_HELPER( INextBotComponent, "Next bot component", &g_NextBotComponentScriptInstanceHelper )
#else
BEGIN_ENT_SCRIPTDESC_ROOT( INextBotComponent, "Next bot component" )
#endif
#else
#ifdef TF_DLL
#ifndef MAPBASE_VSCRIPT // TODO: Need better way of supporting this
DEFINE_SCRIPT_INSTANCE_HELPER( INextBotComponent, &g_NextBotComponentScriptInstanceHelper )
#endif
#endif
BEGIN_ENT_SCRIPTDESC_ROOT( INextBotComponent, "Next bot component" )
#ifdef TF_DLL
#ifdef MAPBASE_VSCRIPT
DEFINE_SCRIPT_INSTANCE_HELPER( &g_NextBotComponentScriptInstanceHelper )
#endif
#endif
#endif
DEFINE_SCRIPTFUNC( Reset, "Resets the internal update state" )
DEFINE_SCRIPTFUNC( ComputeUpdateInterval, "Recomputes the component update interval" )
DEFINE_SCRIPTFUNC( GetUpdateInterval, "Returns the component update interval" )

View File

@ -107,7 +107,11 @@ inline HSCRIPT ToHScript( INextBotComponent *pNextBotComponent )
return ( pNextBotComponent ) ? pNextBotComponent->GetScriptInstance() : NULL;
}
#ifdef MAPBASE_VSCRIPT
template <> ScriptClassDesc_t *GetScriptDesc<INextBotComponent>( INextBotComponent *, bool );
#else
template <> ScriptClassDesc_t *GetScriptDesc<INextBotComponent>( INextBotComponent * );
#endif
inline INextBotComponent *ToNextBotComponent( HSCRIPT hScript )
{
return ( IsValid( hScript ) ) ? (INextBotComponent *)g_pScriptVM->GetInstanceValue( hScript, GetScriptDescForClass( INextBotComponent ) ) : NULL;

View File

@ -2841,6 +2841,18 @@ void CAI_BaseNPC::InitDefaultActivitySR(void)
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_MELEE );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SLAM );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_PISTOL );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_SHOTGUN );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_SMG1 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_AR2 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_PHYSGUN );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_GRENADE );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_RPG );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_MELEE );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_SLAM );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_RUN_REVOLVER );
@ -2850,6 +2862,7 @@ void CAI_BaseNPC::InitDefaultActivitySR(void)
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELOAD_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_REVOLVER );
#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES
@ -2861,6 +2874,7 @@ void CAI_BaseNPC::InitDefaultActivitySR(void)
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK_AR1 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR1 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELOAD_AR1 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_AR1 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_AR1 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_AR3 );
@ -2871,6 +2885,7 @@ void CAI_BaseNPC::InitDefaultActivitySR(void)
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK_AR3 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR3 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELOAD_AR3 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_AR3 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_AR3 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_SMG2 );
@ -2881,6 +2896,7 @@ void CAI_BaseNPC::InitDefaultActivitySR(void)
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG2 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG2 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELOAD_SMG2 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_SMG2 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_SMG2 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_SMG3 );
@ -2891,6 +2907,7 @@ void CAI_BaseNPC::InitDefaultActivitySR(void)
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG3 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG3 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELOAD_SMG3 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_SMG3 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_SMG3 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_HMG1 );
@ -2901,6 +2918,7 @@ void CAI_BaseNPC::InitDefaultActivitySR(void)
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK_HMG1 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_HMG1 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELOAD_HMG1 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_HMG1 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_HMG1 );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_SNIPER_RIFLE );
@ -2911,6 +2929,7 @@ void CAI_BaseNPC::InitDefaultActivitySR(void)
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK_SNIPER_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SNIPER_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELOAD_SNIPER_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_SNIPER_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_SNIPER_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_IDLE_DUAL_PISTOLS );
@ -2921,6 +2940,7 @@ void CAI_BaseNPC::InitDefaultActivitySR(void)
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK_DUAL_PISTOLS );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RANGE_ATTACK2_DUAL_PISTOLS );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELOAD_DUAL_PISTOLS );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_GESTURE_RELAX_DUAL_PISTOLS );
ADD_ACTIVITY_TO_SR( ACT_HL2MP_JUMP_DUAL_PISTOLS );
#endif

View File

@ -318,6 +318,14 @@ ScriptHook_t CAI_BaseNPC::g_Hook_TranslateSchedule;
ScriptHook_t CAI_BaseNPC::g_Hook_GetActualShootPosition;
ScriptHook_t CAI_BaseNPC::g_Hook_OverrideMove;
ScriptHook_t CAI_BaseNPC::g_Hook_ShouldPlayFakeSequenceGesture;
ScriptHook_t CAI_BaseNPC::g_Hook_IsValidEnemy;
ScriptHook_t CAI_BaseNPC::g_Hook_CanBeAnEnemyOf;
ScriptHook_t CAI_BaseNPC::g_Hook_UpdateEnemyMemory;
ScriptHook_t CAI_BaseNPC::g_Hook_OnSeeEntity;
ScriptHook_t CAI_BaseNPC::g_Hook_OnListened;
ScriptHook_t CAI_BaseNPC::g_Hook_BuildScheduleTestBits;
ScriptHook_t CAI_BaseNPC::g_Hook_StartTask;
ScriptHook_t CAI_BaseNPC::g_Hook_RunTask;
#endif
//
@ -748,10 +756,19 @@ Vector CAI_BaseNPC::VScriptGetEnemyLKP()
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
HSCRIPT CAI_BaseNPC::VScriptFindEnemyMemory( HSCRIPT pEnemy )
int CAI_BaseNPC::VScriptNumEnemies()
{
return GetEnemies()->NumEnemies();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
HSCRIPT CAI_BaseNPC::VScriptGetFirstEnemyMemory()
{
HSCRIPT hScript = NULL;
AI_EnemyInfo_t *info = GetEnemies()->Find( ToEnt(pEnemy) );
AIEnemiesIter_t iter;
AI_EnemyInfo_t *info = GetEnemies()->GetFirst( &iter );
if (info)
{
hScript = g_pScriptVM->RegisterInstance( reinterpret_cast<Script_AI_EnemyInfo_t*>(info) );
@ -760,6 +777,70 @@ HSCRIPT CAI_BaseNPC::VScriptFindEnemyMemory( HSCRIPT pEnemy )
return hScript;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
HSCRIPT CAI_BaseNPC::VScriptGetNextEnemyMemory( HSCRIPT hMemory )
{
Script_AI_EnemyInfo_t *pCurEMemory = HScriptToClass<Script_AI_EnemyInfo_t>( hMemory );
if (!pCurEMemory)
return NULL;
HSCRIPT hScript = NULL;
AIEnemiesIter_t iter = (AIEnemiesIter_t)GetEnemies()->FindIndex( pCurEMemory->hEnemy );
AI_EnemyInfo_t *pEMemory = GetEnemies()->GetNext( &iter );
if (pEMemory)
{
hScript = g_pScriptVM->RegisterInstance( reinterpret_cast<Script_AI_EnemyInfo_t*>(pEMemory) );
}
return hScript;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
HSCRIPT CAI_BaseNPC::VScriptFindEnemyMemory( HSCRIPT hEnemy )
{
HSCRIPT hScript = NULL;
AI_EnemyInfo_t *info = GetEnemies()->Find( ToEnt(hEnemy) );
if (info)
{
hScript = g_pScriptVM->RegisterInstance( reinterpret_cast<Script_AI_EnemyInfo_t*>(info) );
}
return hScript;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::VScriptUpdateEnemyMemory( HSCRIPT hEnemy, const Vector &position, HSCRIPT hInformer )
{
return UpdateEnemyMemory( ToEnt( hEnemy ), position, ToEnt( hInformer ) );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_BaseNPC::VScriptClearEnemyMemory( HSCRIPT hEnemy )
{
CBaseEntity *pEnemy = ToEnt( hEnemy );
if (!pEnemy)
return;
GetEnemies()->ClearMemory( pEnemy );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_BaseNPC::VScriptSetFreeKnowledgeDuration( float flDuration )
{
GetEnemies()->SetFreeKnowledgeDuration( flDuration );
}
void CAI_BaseNPC::VScriptSetEnemyDiscardTime( float flDuration )
{
GetEnemies()->SetEnemyDiscardTime( flDuration );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CAI_BaseNPC::VScriptGetState()
@ -767,6 +848,34 @@ int CAI_BaseNPC::VScriptGetState()
return (int)GetState();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CAI_BaseNPC::VScriptGetIdealState()
{
return (int)GetIdealState();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_BaseNPC::VScriptSetIdealState( int nNPCState )
{
SetIdealState( (NPC_STATE)nNPCState );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
HSCRIPT CAI_BaseNPC::VScriptGetTarget()
{
return ToHScript( GetTarget() );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_BaseNPC::VScriptSetTarget( HSCRIPT hTarget )
{
SetTarget( ToEnt( hTarget ) );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
HSCRIPT CAI_BaseNPC::VScriptGetHintNode()
@ -834,7 +943,7 @@ int CAI_BaseNPC::VScriptGetTaskID()
const Task_t *pTask = GetTask();
int iID = -1;
if (pTask)
iID = GetTaskID( TaskName( pTask->iTask ) );
iID = AI_RemapFromGlobal( GetTaskID( TaskName( pTask->iTask ) ) );
return iID;
}
@ -869,6 +978,70 @@ HSCRIPT CAI_BaseNPC::VScriptGetSquad()
return hScript;
}
HSCRIPT CAI_BaseNPC::VScriptGetBestSound( int validTypes )
{
HSCRIPT hScript = NULL;
CSound *pSound = GetBestSound( validTypes );
if (pSound)
{
hScript = g_pScriptVM->RegisterInstance( pSound );
}
return hScript;
}
HSCRIPT CAI_BaseNPC::VScriptGetFirstHeardSound()
{
HSCRIPT hScript = NULL;
AISoundIter_t iter;
CSound *pSound = GetSenses()->GetFirstHeardSound( &iter );
if (pSound)
{
hScript = g_pScriptVM->RegisterInstance( pSound );
}
return hScript;
}
HSCRIPT CAI_BaseNPC::VScriptGetNextHeardSound( HSCRIPT hSound )
{
CSound *pCurSound = HScriptToClass<CSound>( hSound );
if (!pCurSound)
return NULL;
int iCurrent = pCurSound->m_iNextAudible;
if ( iCurrent == SOUNDLIST_EMPTY )
return NULL;
HSCRIPT hScript = NULL;
CSound *pNextSound = CSoundEnt::SoundPointerForIndex( iCurrent );
if (pNextSound)
{
hScript = g_pScriptVM->RegisterInstance( pNextSound );
}
return hScript;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
HSCRIPT CAI_BaseNPC::VScriptGetFirstSeenEntity( int nSeenType )
{
AISightIter_t iter;
return ToHScript( GetSenses()->GetFirstSeenEntity( &iter, (seentype_t)nSeenType ) );
}
HSCRIPT CAI_BaseNPC::VScriptGetNextSeenEntity( HSCRIPT hEnt, int nSeenType )
{
CBaseEntity *pEnt = ToEnt( hEnt );
AISightIter_t iter;
GetSenses()->GetSeenEntityIndex( &iter, pEnt, (seentype_t)nSeenType );
return ToHScript( GetSenses()->GetNextSeenEntity( &iter ) );
}
#endif
bool CAI_BaseNPC::PassesDamageFilter( const CTakeDamageInfo &info )
@ -2597,6 +2770,29 @@ void CAI_BaseNPC::OnListened()
{
m_OnHearCombat.FireOutput(this, this);
}
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_OnListened.CanRunInScope( m_ScriptScope ))
{
ScriptVariant_t functionReturn;
g_Hook_OnListened.Call( m_ScriptScope, &functionReturn, NULL );
}
#endif
}
//-----------------------------------------------------------------------------
void CAI_BaseNPC::OnSeeEntity( CBaseEntity *pEntity )
{
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_OnSeeEntity.CanRunInScope( m_ScriptScope ))
{
// entity
ScriptVariant_t functionReturn;
ScriptVariant_t args[] = { ToHScript( pEntity ) };
g_Hook_OnSeeEntity.Call( m_ScriptScope, &functionReturn, args );
}
#endif
}
//=========================================================
@ -6109,6 +6305,22 @@ bool CAI_BaseNPC::UpdateEnemyMemory( CBaseEntity *pEnemy, const Vector &position
if ( GetEnemies() )
{
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_UpdateEnemyMemory.CanRunInScope( m_ScriptScope ))
{
// enemy, position, informer
ScriptVariant_t functionReturn;
ScriptVariant_t args[] = { ToHScript( pEnemy ), position, ToHScript( pInformer ) };
if (g_Hook_UpdateEnemyMemory.Call( m_ScriptScope, &functionReturn, args ))
{
// Returning false normally indicates this is a known enemy
// Most uses of that functionality involve checking for new enemies, so this is acceptable
if (functionReturn.m_bool == false)
return false;
}
}
#endif
// If the was eluding me and allow the NPC to play a sound
if (GetEnemies()->HasEludedMe(pEnemy))
{
@ -8981,6 +9193,20 @@ bool CAI_BaseNPC::IsValidEnemy( CBaseEntity *pEnemy )
if ( m_hEnemyFilter.Get()!= NULL && m_hEnemyFilter->PassesFilter( this, pEnemy ) == false )
return false;
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_IsValidEnemy.CanRunInScope(m_ScriptScope))
{
// enemy
ScriptVariant_t functionReturn;
ScriptVariant_t args[] = { ToHScript( pEnemy ) };
if (g_Hook_IsValidEnemy.Call( m_ScriptScope, &functionReturn, args ))
{
if (functionReturn.m_bool == false)
return false;
}
}
#endif
return true;
}
@ -8990,6 +9216,20 @@ bool CAI_BaseNPC::CanBeAnEnemyOf( CBaseEntity *pEnemy )
if ( GetSleepState() > AISS_WAITING_FOR_THREAT )
return false;
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_CanBeAnEnemyOf.CanRunInScope(m_ScriptScope))
{
// enemy
ScriptVariant_t functionReturn;
ScriptVariant_t args[] = { ToHScript( pEnemy ) };
if (g_Hook_CanBeAnEnemyOf.Call( m_ScriptScope, &functionReturn, args ))
{
if (functionReturn.m_bool == false)
return false;
}
}
#endif
return true;
}
@ -11543,6 +11783,13 @@ float CAI_BaseNPC::GetEnemyLastTimeSeen() const
void CAI_BaseNPC::MarkEnemyAsEluded()
{
GetEnemies()->MarkAsEluded( GetEnemy() );
#ifdef MAPBASE
if (m_pSquad)
{
m_pSquad->MarkEnemyAsEluded( this, GetEnemy() );
}
#endif
}
void CAI_BaseNPC::ClearEnemyMemory()
@ -12321,7 +12568,17 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC
DEFINE_SCRIPTFUNC_NAMED( VScriptSetEnemy, "SetEnemy", "Set the NPC's current enemy." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetEnemyLKP, "GetEnemyLKP", "Get the last known position of the NPC's current enemy." )
DEFINE_SCRIPTFUNC_NAMED( VScriptNumEnemies, "NumEnemies", "Get the number of enemies this NPC knows about." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetFirstEnemyMemory, "GetFirstEnemyMemory", "Get information about the NPC's first enemy." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetNextEnemyMemory, "GetNextEnemyMemory", "Get information about the NPC's next enemy." )
DEFINE_SCRIPTFUNC_NAMED( VScriptFindEnemyMemory, "FindEnemyMemory", "Get information about the NPC's current enemy." )
DEFINE_SCRIPTFUNC_NAMED( VScriptUpdateEnemyMemory, "UpdateEnemyMemory", "Update information on this enemy. First parameter is the enemy, second is the position we now know the enemy is at, third parameter is the informer (e.g. squadmate who sees enemy, null if I see it myself). Returns true if this is a new enemy." )
DEFINE_SCRIPTFUNC_NAMED( VScriptClearEnemyMemory, "ClearEnemyMemory", "Makes the NPC forget about the specified enemy." )
DEFINE_SCRIPTFUNC_NAMED( VScriptSetFreeKnowledgeDuration, "SetFreeKnowledgeDuration", "Sets the amount of time the NPC can always know an enemy's location after losing sight." )
DEFINE_SCRIPTFUNC_NAMED( VScriptSetEnemyDiscardTime, "SetEnemyDiscardTime", "Sets the amount of time needed before the NPC discards an unseen enemy's memory." )
DEFINE_SCRIPTFUNC( GetLastAttackTime, "Get the last time the NPC has used an attack (e.g. fired a bullet from a gun)." )
DEFINE_SCRIPTFUNC( GetLastDamageTime, "Get the last time the NPC has been damaged." )
@ -12329,6 +12586,11 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC
DEFINE_SCRIPTFUNC( GetLastEnemyTime, "Get the last time the NPC has seen an enemy." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetState, "GetNPCState", "Get the NPC's current state." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetIdealState, "GetIdealNPCState", "Get the NPC's ideal state." )
DEFINE_SCRIPTFUNC_NAMED( VScriptSetIdealState, "SetIdealNPCState", "Set the NPC's ideal state." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetTarget, "GetNPCTarget", "Get the NPC's AI target." )
DEFINE_SCRIPTFUNC_NAMED( VScriptSetTarget, "SetNPCTarget", "Set the NPC's AI target." )
DEFINE_SCRIPTFUNC_NAMED( VScriptWake, "Wake", "Awakens the NPC if it is currently asleep." )
DEFINE_SCRIPTFUNC_NAMED( VScriptSleep, "Sleep", "Puts the NPC into a sleeping state." )
@ -12376,6 +12638,16 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC
DEFINE_SCRIPTFUNC_NAMED( VScriptClearCondition, "ClearCondition", "Clear a condition on the NPC." )
DEFINE_SCRIPTFUNC_NAMED( ClearCondition, "ClearConditionID", "Clear a condition on the NPC by ID." )
DEFINE_SCRIPTFUNC_NAMED( VScriptSetCustomInterruptCondition, "SetCustomInterruptCondition", "Use with BuildScheduleTestBits to define conditions which should interrupt the schedule." )
DEFINE_SCRIPTFUNC_NAMED( VScriptIsCustomInterruptConditionSet, "IsCustomInterruptConditionSet", "Use with BuildScheduleTestBits to define conditions which should interrupt the schedule." )
DEFINE_SCRIPTFUNC_NAMED( VScriptClearCustomInterruptCondition, "ClearCustomInterruptCondition", "Use with BuildScheduleTestBits to define conditions which should interrupt the schedule." )
DEFINE_SCRIPTFUNC_NAMED( VScriptChainStartTask, "ChainStartTask", "Use with StartTask to redirect to the specified task." )
DEFINE_SCRIPTFUNC_NAMED( VScriptChainRunTask, "ChainRunTask", "Use with RunTask to redirect to the specified task." )
DEFINE_SCRIPTFUNC_NAMED( VScriptFailTask, "FailTask", "Fails the currently running task with the specified error message." )
DEFINE_SCRIPTFUNC_NAMED( VScriptCompleteTask, "CompleteTask", "Completes the currently running task." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetTaskStatus, "GetTaskStatus", "Gets the current task's status." )
DEFINE_SCRIPTFUNC( IsMoving, "Check if the NPC is moving." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetExpresser, "GetExpresser", "Get a handle for this NPC's expresser." )
@ -12394,6 +12666,13 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC
DEFINE_SCRIPTFUNC( IsCrouching, "Returns true if the NPC is crouching." )
DEFINE_SCRIPTFUNC( Crouch, "Tells the NPC to crouch." )
DEFINE_SCRIPTFUNC( Stand, "Tells the NPC to stand if it is crouching." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetBestSound, "GetBestSound", "Get the NPC's best sound of the specified type(s). Use 'ALL_SOUNDS' to get any sound." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetFirstHeardSound, "GetFirstHeardSound", "Get the NPC's first heard sound." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetNextHeardSound, "GetNextHeardSound", "Get the NPC's next heard sound." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetFirstSeenEntity, "GetFirstSeenEntity", "Get the NPC's first seen entity in the specified 'SEEN_' list." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetNextSeenEntity, "GetNextSeenEntity", "Get the NPC's next seen entity in the specified 'SEEN_' list." )
//
// Hooks
@ -12423,6 +12702,32 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC
DEFINE_SCRIPTHOOK_PARAM( "activity", FIELD_CSTRING )
DEFINE_SCRIPTHOOK_PARAM( "translatedActivity", FIELD_CSTRING )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_IsValidEnemy, "IsValidEnemy", FIELD_BOOLEAN, "Whether or not the specified enemy should be considered valid." )
DEFINE_SCRIPTHOOK_PARAM( "enemy", FIELD_HSCRIPT )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_CanBeAnEnemyOf, "CanBeAnEnemyOf", FIELD_BOOLEAN, "Whether or not this NPC can be an enemy of another NPC." )
DEFINE_SCRIPTHOOK_PARAM( "enemy", FIELD_HSCRIPT )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_UpdateEnemyMemory, "UpdateEnemyMemory", FIELD_BOOLEAN, "Whether or not this NPC can be an enemy of another NPC." )
DEFINE_SCRIPTHOOK_PARAM( "enemy", FIELD_HSCRIPT )
DEFINE_SCRIPTHOOK_PARAM( "position", FIELD_VECTOR )
DEFINE_SCRIPTHOOK_PARAM( "informer", FIELD_HSCRIPT )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_OnSeeEntity, "OnSeeEntity", FIELD_VOID, "Called when the NPC sees an entity." )
DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT )
END_SCRIPTHOOK()
DEFINE_SIMPLE_SCRIPTHOOK( CAI_BaseNPC::g_Hook_OnListened, "OnListened", FIELD_VOID, "Called when the NPC assigns sound conditions after checking for sounds it hears." )
DEFINE_SIMPLE_SCRIPTHOOK( CAI_BaseNPC::g_Hook_BuildScheduleTestBits, "BuildScheduleTestBits", FIELD_VOID, "Called when the NPC is determining which conditions can interrupt the current schedule." )
BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_StartTask, "StartTask", FIELD_VOID, "Called when a task is starting. The task is provided in both string and ID form. Return false to override actual task functionality." )
DEFINE_SCRIPTHOOK_PARAM( "task", FIELD_CSTRING )
DEFINE_SCRIPTHOOK_PARAM( "task_id", FIELD_INTEGER )
DEFINE_SCRIPTHOOK_PARAM( "task_data", FIELD_FLOAT )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_RunTask, "RunTask", FIELD_VOID, "Called every think while the task is running. The task is provided in both string and ID form. Return false to override actual task functionality." )
DEFINE_SCRIPTHOOK_PARAM( "task", FIELD_CSTRING )
DEFINE_SCRIPTHOOK_PARAM( "task_id", FIELD_INTEGER )
DEFINE_SCRIPTHOOK_PARAM( "task_data", FIELD_FLOAT )
END_SCRIPTHOOK()
END_SCRIPTDESC();
#endif

View File

@ -1104,7 +1104,7 @@ public:
virtual void OnLooked( int iDistance );
virtual void OnListened();
virtual void OnSeeEntity( CBaseEntity *pEntity ) {}
virtual void OnSeeEntity( CBaseEntity *pEntity );
// If true, AI will try to see this entity regardless of distance.
virtual bool ShouldNotDistanceCull() { return false; }
@ -1271,9 +1271,24 @@ private:
void VScriptSetEnemy( HSCRIPT pEnemy );
Vector VScriptGetEnemyLKP();
HSCRIPT VScriptFindEnemyMemory( HSCRIPT pEnemy );
int VScriptNumEnemies();
HSCRIPT VScriptGetFirstEnemyMemory();
HSCRIPT VScriptGetNextEnemyMemory( HSCRIPT hMemory );
HSCRIPT VScriptFindEnemyMemory( HSCRIPT hEnemy );
bool VScriptUpdateEnemyMemory( HSCRIPT hEnemy, const Vector &position, HSCRIPT hInformer );
void VScriptClearEnemyMemory( HSCRIPT hEnemy );
void VScriptSetFreeKnowledgeDuration( float flDuration );
void VScriptSetEnemyDiscardTime( float flDuration );
int VScriptGetState();
int VScriptGetIdealState();
void VScriptSetIdealState( int nNPCState );
HSCRIPT VScriptGetTarget();
void VScriptSetTarget( HSCRIPT hTarget );
void VScriptWake( HSCRIPT hActivator ) { Wake( ToEnt(hActivator) ); }
void VScriptSleep() { Sleep(); }
@ -1308,12 +1323,29 @@ private:
void VScriptSetCondition( const char *szCondition ) { SetCondition( GetConditionID( szCondition ) ); }
void VScriptClearCondition( const char *szCondition ) { ClearCondition( GetConditionID( szCondition ) ); }
void VScriptSetCustomInterruptCondition( const char *szCondition ) { SetCustomInterruptCondition( GetConditionID( szCondition ) ); }
bool VScriptIsCustomInterruptConditionSet( const char *szCondition ) { return IsCustomInterruptConditionSet( GetConditionID( szCondition ) ); }
void VScriptClearCustomInterruptCondition( const char *szCondition ) { ClearCustomInterruptCondition( GetConditionID( szCondition ) ); }
void VScriptChainStartTask( const char *szTask, float flTaskData ) { ChainStartTask( AI_RemapFromGlobal( GetTaskID( szTask ) ), flTaskData ); }
void VScriptChainRunTask( const char *szTask, float flTaskData ) { ChainRunTask( AI_RemapFromGlobal( GetTaskID( szTask ) ), flTaskData ); }
void VScriptFailTask( const char *szFailReason ) { TaskFail( szFailReason ); }
void VScriptCompleteTask() { TaskComplete(); }
int VScriptGetTaskStatus() { return (int)GetTaskStatus(); }
HSCRIPT VScriptGetExpresser();
HSCRIPT VScriptGetCine();
int GetScriptState() { return m_scriptState; }
HSCRIPT VScriptGetSquad();
HSCRIPT VScriptGetBestSound( int validTypes );
HSCRIPT VScriptGetFirstHeardSound();
HSCRIPT VScriptGetNextHeardSound( HSCRIPT hSound );
HSCRIPT VScriptGetFirstSeenEntity( int nSeenType );
HSCRIPT VScriptGetNextSeenEntity( HSCRIPT hEnt, int nSeenType );
#endif
//-----------------------------------------------------
@ -2398,6 +2430,14 @@ public:
static ScriptHook_t g_Hook_GetActualShootPosition;
static ScriptHook_t g_Hook_OverrideMove;
static ScriptHook_t g_Hook_ShouldPlayFakeSequenceGesture;
static ScriptHook_t g_Hook_IsValidEnemy;
static ScriptHook_t g_Hook_CanBeAnEnemyOf;
static ScriptHook_t g_Hook_UpdateEnemyMemory;
static ScriptHook_t g_Hook_OnSeeEntity;
static ScriptHook_t g_Hook_OnListened;
static ScriptHook_t g_Hook_BuildScheduleTestBits;
static ScriptHook_t g_Hook_StartTask;
static ScriptHook_t g_Hook_RunTask;
#endif
private:

View File

@ -45,6 +45,10 @@ extern ConVar ai_use_think_optimizations;
ConVar ai_simulate_task_overtime( "ai_simulate_task_overtime", "0" );
#ifdef MAPBASE
ConVar ai_enemy_memory_fixes( "ai_enemy_memory_fixes", "0", FCVAR_NONE, "Toggles Mapbase fixes for certain NPC AI not using enemy memory when it should." );
#endif
#define MAX_TASKS_RUN 10
struct TaskTimings
@ -276,6 +280,14 @@ void CAI_BaseNPC::NextScheduledTask ( void )
void CAI_BaseNPC::BuildScheduleTestBits( void )
{
//NOTENOTE: Always defined in the leaf classes
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_BuildScheduleTestBits.CanRunInScope( m_ScriptScope ))
{
ScriptVariant_t functionReturn;
g_Hook_BuildScheduleTestBits.Call( m_ScriptScope, &functionReturn, NULL );
}
#endif
}
@ -730,6 +742,23 @@ void CAI_BaseNPC::MaintainSchedule ( void )
AI_PROFILE_SCOPE_BEGIN_( pszTaskName );
AI_PROFILE_SCOPE_BEGIN(CAI_BaseNPC_StartTask);
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_StartTask.CanRunInScope( m_ScriptScope ))
{
// task, task_id, task_data
ScriptVariant_t functionReturn;
ScriptVariant_t args[] = { (bDebugTaskNames) ? pszTaskName : TaskName( pTask->iTask ), pTask->iTask, pTask->flTaskData };
if (g_Hook_StartTask.Call( m_ScriptScope, &functionReturn, args ))
{
// Returning false overrides original functionality
if (functionReturn.m_bool != false)
StartTask( pTask );
}
else
StartTask( pTask );
}
else
#endif
StartTask( pTask );
AI_PROFILE_SCOPE_END();
@ -766,6 +795,23 @@ void CAI_BaseNPC::MaintainSchedule ( void )
int j;
for (j = 0; j < 8; j++)
{
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_RunTask.CanRunInScope( m_ScriptScope ))
{
// task, task_id, task_data
ScriptVariant_t functionReturn;
ScriptVariant_t args[] = { (bDebugTaskNames) ? pszTaskName : TaskName( pTask->iTask ), pTask->iTask, pTask->flTaskData };
if (g_Hook_RunTask.Call( m_ScriptScope, &functionReturn, args ))
{
// Returning false overrides original functionality
if (functionReturn.m_bool != false)
RunTask( pTask );
}
else
RunTask( pTask );
}
else
#endif
RunTask( pTask );
if ( GetTaskInterrupt() == 0 || TaskIsComplete() || HasCondition(COND_TASK_FAILED) )
@ -1971,7 +2017,17 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask )
flMaxRange = m_flDistTooFar;
}
#ifdef MAPBASE
// By default, TASK_GET_PATH_TO_ENEMY_LKP_LOS acts identical to TASK_GET_PATH_TO_ENEMY_LOS.
// Considering the fact TASK_GET_PATH_TO_ENEMY_LKP doesn't use this code, this appears to be a mistake.
// In HL2, this task is used by Combine soldiers, metrocops, striders, and hunters.
// With this change, these NPCs will establish LOS according to enemy memory instead of where the enemy
// actually is. This may make the NPCs more consistent and fair, but their AI and levels built around it
// may have been designed around this bug, so this is currently being tied to a cvar.
Vector vecEnemy = ( task != TASK_GET_PATH_TO_ENEMY_LKP_LOS || !ai_enemy_memory_fixes.GetBool() ) ? GetEnemy()->GetAbsOrigin() : GetEnemyLKP();
#else
Vector vecEnemy = ( task != TASK_GET_PATH_TO_ENEMY_LKP ) ? GetEnemy()->GetAbsOrigin() : GetEnemyLKP();
#endif
Vector vecEnemyEye = vecEnemy + GetEnemy()->GetViewOffset();
Vector posLos;

View File

@ -500,6 +500,13 @@ public:
g_AI_SensedObjectsManager.Init();
}
#ifdef MAPBASE_VSCRIPT
virtual void RegisterVScript()
{
g_pScriptVM->RegisterInstance( &g_AI_SensedObjectsManager, "SensedObjectsManager" );
}
#endif
void LevelShutdownPreEntity()
{
CBaseCombatCharacter::ResetVisibilityCache();

View File

@ -78,6 +78,7 @@ static void DispatchComeback( CAI_ExpresserWithFollowup *pExpress, CBaseEntity *
// See DispatchFollowupThroughQueue()
criteria.AppendCriteria( "From_idx", CNumStr( pSpeaker->entindex() ) );
criteria.AppendCriteria( "From_class", pSpeaker->GetClassname() );
pSpeaker->AppendContextToCriteria( criteria, "From_" );
#endif
// if a SUBJECT criteria is missing, put it back in.
if ( criteria.FindCriterionIndex( "Subject" ) == -1 )

View File

@ -225,6 +225,18 @@ AI_EnemyInfo_t *CAI_Enemies::Find( CBaseEntity *pEntity, bool bTryDangerMemory )
}
//-----------------------------------------------------------------------------
#ifdef MAPBASE
unsigned char CAI_Enemies::FindIndex( CBaseEntity *pEntity )
{
if ( pEntity == AI_UNKNOWN_ENEMY )
pEntity = NULL;
return m_Map.Find( pEntity );
}
#endif
//-----------------------------------------------------------------------------
AI_EnemyInfo_t *CAI_Enemies::GetDangerMemory()

View File

@ -63,6 +63,9 @@ public:
AI_EnemyInfo_t *GetFirst( AIEnemiesIter_t *pIter );
AI_EnemyInfo_t *GetNext( AIEnemiesIter_t *pIter );
AI_EnemyInfo_t *Find( CBaseEntity *pEntity, bool bTryDangerMemory = false );
#ifdef MAPBASE
unsigned char FindIndex( CBaseEntity *pEntity );
#endif
AI_EnemyInfo_t *GetDangerMemory();
int NumEnemies() const { return m_Map.Count(); }

View File

@ -302,6 +302,43 @@ CBaseEntity *CAI_Senses::GetNextSeenEntity( AISightIter_t *pIter ) const
return NULL;
}
//-----------------------------------------------------------------------------
#ifdef MAPBASE
bool CAI_Senses::GetSeenEntityIndex( AISightIter_t *pIter, CBaseEntity *pSightEnt, seentype_t iSeenType ) const
{
COMPILE_TIME_ASSERT( sizeof( AISightIter_t ) == sizeof( AISightIterVal_t ) );
AISightIterVal_t *pIterVal = (AISightIterVal_t *)pIter;
// If we're searching for a specific type, start in that array
pIterVal->SeenArray = (char)iSeenType;
int iFirstArray = ( iSeenType == SEEN_ALL ) ? 0 : iSeenType;
for ( int i = iFirstArray; i < ARRAYSIZE( m_SeenArrays ); i++ )
{
for ( int j = pIterVal->iNext; j < m_SeenArrays[i]->Count(); j++ )
{
if ( (*m_SeenArrays[i])[j].Get() == pSightEnt )
{
pIterVal->array = i;
pIterVal->iNext = j+1;
return true;
}
}
pIterVal->iNext = 0;
// If we're searching for a specific type, don't move to the next array
if ( pIterVal->SeenArray != SEEN_ALL )
break;
}
(*pIter) = (AISightIter_t)(-1);
return false;
}
#endif
//-----------------------------------------------------------------------------
void CAI_Senses::BeginGather()
@ -753,4 +790,27 @@ void CAI_SensedObjectsManager::AddEntity( CBaseEntity *pEntity )
m_SensedObjects.AddToTail( pEntity );
}
#ifdef MAPBASE
void CAI_SensedObjectsManager::RemoveEntity( CBaseEntity *pEntity )
{
int i = m_SensedObjects.Find( pEntity );
if (i == m_SensedObjects.InvalidIndex())
return;
pEntity->RemoveFlag( FL_OBJECT );
m_SensedObjects.FastRemove( i );
}
#endif
//-----------------------------------------------------------------------------
#ifdef MAPBASE_VSCRIPT
BEGIN_SCRIPTDESC_ROOT( CAI_SensedObjectsManager, SCRIPT_SINGLETON "Manager which handles sensed objects." )
DEFINE_SCRIPTFUNC_NAMED( ScriptAddEntity, "AddEntity", "Adds an entity to the sensed object list." )
DEFINE_SCRIPTFUNC_NAMED( ScriptRemoveEntity, "RemoveEntity", "Removes an entity from the sensed object list." )
END_SCRIPTDESC();
#endif
//=============================================================================

View File

@ -82,6 +82,9 @@ public:
CBaseEntity * GetFirstSeenEntity( AISightIter_t *pIter, seentype_t iSeenType = SEEN_ALL ) const;
CBaseEntity * GetNextSeenEntity( AISightIter_t *pIter ) const;
#ifdef MAPBASE
bool GetSeenEntityIndex( AISightIter_t *pIter, CBaseEntity *pSightEnt, seentype_t iSeenType ) const;
#endif
CSound * GetFirstHeardSound( AISoundIter_t *pIter );
CSound * GetNextHeardSound( AISoundIter_t *pIter );
@ -152,6 +155,14 @@ public:
CBaseEntity * GetNext( int *pIter );
virtual void AddEntity( CBaseEntity *pEntity );
#ifdef MAPBASE
virtual void RemoveEntity( CBaseEntity *pEntity );
#endif
#ifdef MAPBASE_VSCRIPT
void ScriptAddEntity( HSCRIPT hEnt ) { AddEntity( ToEnt( hEnt ) ); }
void ScriptRemoveEntity( HSCRIPT hEnt ) { RemoveEntity( ToEnt( hEnt ) ); }
#endif
private:
virtual void OnEntitySpawned( CBaseEntity *pEntity );

View File

@ -18,6 +18,7 @@
#include "sceneentity.h"
#include "ai_speechqueue.h"
#ifdef MAPBASE
#include "mapbase/choreosentence.h"
#include "ai_squad.h"
#endif
@ -846,6 +847,9 @@ bool CAI_Expresser::SpeakDispatchResponse( AIConcept_t concept, AI_Response *res
case ResponseRules::RESPONSE_NONE:
break;
#ifdef MAPBASE
case ResponseRules::RESPONSE_CHOREOSENTENCE:
#endif
case ResponseRules::RESPONSE_SPEAK:
{
if ( !result->ShouldntUseScene() )
@ -918,7 +922,7 @@ bool CAI_Expresser::SpeakDispatchResponse( AIConcept_t concept, AI_Response *res
textParams.g1 = 255;
textParams.b1 = 255;
if (ai_speech_print_mode.GetBool() && GetOuter()->GetGameTextSpeechParams( textParams ))
if (ai_speech_print_mode.GetBool() && GetOuter()->DispatchGetGameTextSpeechParams( textParams ))
{
CRecipientFilter filter;
filter.AddAllPlayers();
@ -1194,6 +1198,15 @@ float CAI_Expresser::GetResponseDuration( AI_Response *result )
case ResponseRules::RESPONSE_NONE:
case ResponseRules::RESPONSE_ENTITYIO:
return 0.0f;
#ifdef MAPBASE
case ResponseRules::RESPONSE_CHOREOSENTENCE:
{
const ChoreoSentence_t *pSentence = LookupChoreoSentence( GetOuter(), response );
if (pSentence)
return GetChoreoSentenceDuration( GetOuter(), *pSentence );
}
break;
#endif
}
return 0.0f;

View File

@ -18,6 +18,10 @@
CAI_SquadManager g_AI_SquadManager;
#ifdef MAPBASE
ConVar ai_squad_broadcast_elusion("ai_squad_broadcast_elusion", "0", FCVAR_NONE, "Tells the entire squad when an enemy is eluded");
#endif
//-----------------------------------------------------------------------------
// CAI_SquadManager
//
@ -740,6 +744,25 @@ void CAI_Squad::UpdateEnemyMemory( CAI_BaseNPC *pUpdater, CBaseEntity *pEnemy, c
//------------------------------------------------------------------------------
#ifdef MAPBASE
void CAI_Squad::MarkEnemyAsEluded( CAI_BaseNPC *pUpdater, CBaseEntity *pEnemy )
{
if (!ai_squad_broadcast_elusion.GetBool())
return;
//Broadcast to all members of the squad
for ( int i = 0; i < m_SquadMembers.Count(); i++ )
{
if ( m_SquadMembers[i] != pUpdater )
{
m_SquadMembers[i]->GetEnemies()->MarkAsEluded( pEnemy );
}
}
}
#endif
//------------------------------------------------------------------------------
#ifdef PER_ENEMY_SQUADSLOTS
AISquadEnemyInfo_t *CAI_Squad::FindEnemyInfo( CBaseEntity *pEnemy )
@ -883,14 +906,14 @@ void CAI_Squad::ScriptRemoveFromSquad( HSCRIPT hNPC ) { RemoveFromSquad( HScrip
bool CAI_Squad::ScriptIsSilentMember( HSCRIPT hNPC ) { return IsSilentMember( HScriptToClass<CAI_BaseNPC>( hNPC ) ); }
void CAI_Squad::ScriptSetSquadData( int iSlot, const char *data )
void CAI_Squad::ScriptSetSquadData( int iSlot, int data )
{
SetSquadData( iSlot, data );
}
const char *CAI_Squad::ScriptGetSquadData( int iSlot )
int CAI_Squad::ScriptGetSquadData( int iSlot )
{
const char *data;
int data;
GetSquadData( iSlot, &data );
return data;
}

View File

@ -107,6 +107,12 @@ public:
void SquadNewEnemy ( CBaseEntity *pEnemy );
void UpdateEnemyMemory( CAI_BaseNPC *pUpdater, CBaseEntity *pEnemy, const Vector &position );
#ifdef MAPBASE
// The idea behind this is that, if one squad member fails to locate the enemy, nobody in the squad knows where the enemy is
// Makes combat utilizing elusion a bit smoother
// (gated by ai_squad_broadcast_elusion cvar)
void MarkEnemyAsEluded( CAI_BaseNPC *pUpdater, CBaseEntity *pEnemy );
#endif
bool OccupyStrategySlotRange( CBaseEntity *pEnemy, int slotIDStart, int slotIDEnd, int *pSlot );
void VacateStrategySlot( CBaseEntity *pEnemy, int slot);
@ -186,8 +192,8 @@ private:
bool ScriptIsSilentMember( HSCRIPT hNPC );
void ScriptSetSquadData( int iSlot, const char *data );
const char *ScriptGetSquadData( int iSlot );
void ScriptSetSquadData( int iSlot, int data );
int ScriptGetSquadData( int iSlot );
#endif
private:

View File

@ -320,7 +320,7 @@ BEGIN_ENT_SCRIPTDESC( CBaseAnimating, CBaseEntity, "Animating models" )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetSequenceActivity, "GetSequenceActivity", "Gets the activity ID of the specified sequence index" )
DEFINE_SCRIPTFUNC_NAMED( ScriptSelectWeightedSequence, "SelectWeightedSequence", "Selects a sequence for the specified activity ID" )
DEFINE_SCRIPTFUNC_NAMED( ScriptSelectHeaviestSequence, "SelectHeaviestSequence", "Selects the sequence with the heaviest weight for the specified activity ID" )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetSequenceKeyValues, "GetSequenceKeyValues", "Get a KeyValue class instance on the specified sequence. WARNING: This uses the same KeyValue pointer as GetModelKeyValues!" )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetSequenceKeyValues, "GetSequenceKeyValues", "Get a KeyValue class instance on the specified sequence" )
DEFINE_SCRIPTFUNC( ResetSequenceInfo, "" )
#endif
DEFINE_SCRIPTFUNC( IsSequenceFinished, "Ask whether the main sequence is done playing" )
@ -1318,7 +1318,8 @@ void CBaseAnimating::DispatchAnimEvents ( CBaseAnimating *eventHandler )
}
#ifdef MAPBASE_VSCRIPT
if (eventHandler->ScriptHookHandleAnimEvent( &event ) == false)
scriptanimevent_t wrapper( event );
if (!eventHandler->ScriptHookHandleAnimEvent( wrapper ))
continue;
#endif
@ -1357,11 +1358,11 @@ void CBaseAnimating::DispatchAnimEvents ( CBaseAnimating *eventHandler )
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseAnimating::ScriptHookHandleAnimEvent( animevent_t *pEvent )
bool CBaseAnimating::ScriptHookHandleAnimEvent( scriptanimevent_t &event )
{
if (m_ScriptScope.IsInitialized() && g_Hook_HandleAnimEvent.CanRunInScope(m_ScriptScope))
{
HSCRIPT hEvent = g_pScriptVM->RegisterInstance( reinterpret_cast<scriptanimevent_t*>(pEvent) );
HSCRIPT hEvent = g_pScriptVM->RegisterInstance( &event );
// event
ScriptVariant_t args[] = { hEvent };
@ -2385,21 +2386,14 @@ void CBaseAnimating::ScriptGetBoneTransform( int iBone, HSCRIPT hTransform )
//-----------------------------------------------------------------------------
// VScript access to sequence's key values
// for iteration and value access, use:
// ScriptFindKey, ScriptGetFirstSubKey, ScriptGetString,
// ScriptGetInt, ScriptGetFloat, ScriptGetNextKey
// NOTE: This is recycled from ScriptGetModelKeyValues() and uses its pointer!!!
//-----------------------------------------------------------------------------
HSCRIPT CBaseAnimating::ScriptGetSequenceKeyValues( int iSequence )
HSCRIPT_RC CBaseAnimating::ScriptGetSequenceKeyValues( int iSequence )
{
KeyValues *pSeqKeyValues = GetSequenceKeyValues( iSequence );
HSCRIPT hScript = NULL;
if ( pSeqKeyValues )
{
// UNDONE: how does destructor get called on this
m_pScriptModelKeyValues = hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, pSeqKeyValues, true );
// UNDONE: who calls ReleaseInstance on this??? Does name need to be unique???
hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, pSeqKeyValues );
}
return hScript;

View File

@ -16,6 +16,9 @@
#include "datacache/idatacache.h"
#include "tier0/threadtools.h"
#ifdef MAPBASE_VSCRIPT
struct scriptanimevent_t;
#endif
struct animevent_t;
struct matrix3x4_t;
@ -157,7 +160,7 @@ public:
}
virtual void HandleAnimEvent( animevent_t *pEvent );
#ifdef MAPBASE_VSCRIPT
bool ScriptHookHandleAnimEvent( animevent_t *pEvent );
bool ScriptHookHandleAnimEvent( scriptanimevent_t &event );
#endif
int LookupPoseParameter( CStudioHdr *pStudioHdr, const char *szName );
@ -227,7 +230,7 @@ public:
int ScriptSelectHeaviestSequence( int activity ) { return SelectHeaviestSequence( (Activity)activity ); }
int ScriptSelectWeightedSequence( int activity, int curSequence ) { return SelectWeightedSequence( (Activity)activity, curSequence ); }
HSCRIPT ScriptGetSequenceKeyValues( int iSequence );
HSCRIPT_RC ScriptGetSequenceKeyValues( int iSequence );
static ScriptHook_t g_Hook_OnServerRagdoll;
static ScriptHook_t g_Hook_HandleAnimEvent;

View File

@ -3522,13 +3522,13 @@ void CBaseCombatCharacter::AddRelationship( const char *pszRelationship, CBaseEn
bool bFoundEntity = false;
// Try to get pointer to an entity of this name
CBaseEntity *entity = gEntList.FindEntityByName( NULL, entityString );
CBaseEntity *entity = gEntList.FindEntityByName( NULL, entityString, this, pActivator );
while( entity )
{
// make sure you catch all entities of this name.
bFoundEntity = true;
AddEntityRelationship(entity, disposition, priority );
entity = gEntList.FindEntityByName( entity, entityString );
entity = gEntList.FindEntityByName( entity, entityString, this, pActivator );
}
if( !bFoundEntity )

View File

@ -1806,6 +1806,27 @@ int CBaseEntity::TakeDamage( const CTakeDamageInfo &inputInfo )
DamageFilterDamageMod(info);
#endif
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_OnTakeDamage.CanRunInScope( m_ScriptScope ))
{
HSCRIPT hInfo = g_pScriptVM->RegisterInstance( &info );
// info
ScriptVariant_t functionReturn;
ScriptVariant_t args[] = { ScriptVariant_t( hInfo ) };
if ( g_Hook_OnTakeDamage.Call( m_ScriptScope, &functionReturn, args ) )
{
if (functionReturn.m_type == FIELD_BOOLEAN && functionReturn.m_bool == false)
{
g_pScriptVM->RemoveInstance( hInfo );
return 0;
}
}
g_pScriptVM->RemoveInstance( hInfo );
}
#endif
if ( ScriptHookEnabled( "OnTakeDamage" ) )
{
IScriptVM *pVM = g_pScriptVM;
@ -1866,6 +1887,28 @@ int CBaseEntity::TakeDamage( const CTakeDamageInfo &inputInfo )
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Allows entities to be 'invisible' to NPC senses.
//-----------------------------------------------------------------------------
bool CBaseEntity::CanBeSeenBy( CAI_BaseNPC *pNPC )
{
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_CanBeSeenBy.CanRunInScope(m_ScriptScope))
{
// npc
ScriptVariant_t functionReturn;
ScriptVariant_t args[] = { ToHScript( pNPC ) };
if (g_Hook_CanBeSeenBy.Call( m_ScriptScope, &functionReturn, args ))
{
if (functionReturn.m_bool == false)
return false;
}
}
#endif
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Returns a value that scales all damage done by this entity.
//-----------------------------------------------------------------------------
@ -2487,14 +2530,16 @@ ScriptHook_t CBaseEntity::g_Hook_OnEntText;
ScriptHook_t CBaseEntity::g_Hook_VPhysicsCollision;
ScriptHook_t CBaseEntity::g_Hook_FireBullets;
ScriptHook_t CBaseEntity::g_Hook_OnDeath;
ScriptHook_t CBaseEntity::g_Hook_OnTakeDamage;
ScriptHook_t CBaseEntity::g_Hook_OnKilledOther;
ScriptHook_t CBaseEntity::g_Hook_HandleInteraction;
ScriptHook_t CBaseEntity::g_Hook_ModifyEmitSoundParams;
ScriptHook_t CBaseEntity::g_Hook_ModifySentenceParams;
ScriptHook_t CBaseEntity::g_Hook_ModifyOrAppendCriteria;
ScriptHook_t CBaseEntity::g_Hook_CanBeSeenBy;
#endif
BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" )
DEFINE_SCRIPT_INSTANCE_HELPER( &g_BaseEntityScriptInstanceHelper )
BEGIN_ENT_SCRIPTDESC_ROOT_WITH_HELPER( CBaseEntity, "Root class of all server-side entities", &g_BaseEntityScriptInstanceHelper )
DEFINE_SCRIPTFUNC_NAMED( ConnectOutputToScript, "ConnectOutput", "Adds an I/O connection that will call the named function when the specified output fires" )
DEFINE_SCRIPTFUNC_NAMED( DisconnectOutputFromScript, "DisconnectOutput", "Removes a connected script function from an I/O event." )
@ -2717,6 +2762,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities"
DEFINE_SCRIPTFUNC_NAMED( ScriptGetContext, "GetContext", "Get a response context value" )
DEFINE_SCRIPTFUNC_NAMED( ScriptAddContext, "AddContext", "Add a response context value" )
DEFINE_SCRIPTFUNC( RemoveContext, "Remove a response context" )
DEFINE_SCRIPTFUNC( GetContextExpireTime, "Get a response context's expiration time" )
DEFINE_SCRIPTFUNC( GetContextCount, "Get the number of response contexts" )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetContextIndex, "GetContextIndex", "Get a response context at a specific index in the form of a table" )
@ -2831,14 +2877,15 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities"
DEFINE_SCRIPTHOOK_PARAM( "normal", FIELD_VECTOR )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_FireBullets, "FireBullets", FIELD_VOID, "Called for every single VPhysics-related collision experienced by this entity." )
DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT )
DEFINE_SCRIPTHOOK_PARAM( "speed", FIELD_FLOAT )
DEFINE_SCRIPTHOOK_PARAM( "point", FIELD_VECTOR )
DEFINE_SCRIPTHOOK_PARAM( "normal", FIELD_VECTOR )
BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_FireBullets, "FireBullets", FIELD_VOID, "Called when the entity fires bullets from itself or from a weapon. The parameter is the associated FireBulletsInfo_t handle. Return false to cancel bullet firing." )
DEFINE_SCRIPTHOOK_PARAM( "info", FIELD_HSCRIPT )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_OnDeath, "OnDeath", FIELD_BOOLEAN, "Called when the entity dies (Event_Killed). Returning false makes the entity cancel death, although this could have unforeseen consequences. For hooking any damage instead of just death, see filter_script and PassesFinalDamageFilter." )
BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_OnDeath, "OnDeath", FIELD_BOOLEAN, "Called when the entity dies (Event_Killed). Returning false makes the entity cancel death, although this could have unforeseen consequences. For hooking any damage instead of just death, use OnTakeDamage." )
DEFINE_SCRIPTHOOK_PARAM( "info", FIELD_HSCRIPT )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_OnTakeDamage, "OnTakeDamage", FIELD_BOOLEAN, "Called when the entity takes damage (OnTakeDamage). Returning false makes the entity cancel the damage, similar to a damage filter. This is called after any damage filter operations." )
DEFINE_SCRIPTHOOK_PARAM( "info", FIELD_HSCRIPT )
END_SCRIPTHOOK()
@ -2860,6 +2907,12 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities"
BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_ModifySentenceParams, "ModifySentenceParams", FIELD_VOID, "Called every time a sentence is emitted on this entity, allowing for its parameters to be modified." )
DEFINE_SCRIPTHOOK_PARAM( "params", FIELD_HSCRIPT )
END_SCRIPTHOOK()
DEFINE_SIMPLE_SCRIPTHOOK( CBaseEntity::g_Hook_ModifyOrAppendCriteria, "ModifyOrAppendCriteria", FIELD_HSCRIPT, "Called when the criteria set is collected for a response. Return a table of keyvalues to add to the criteria set." )
BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_CanBeSeenBy, "CanBeSeenBy", FIELD_BOOLEAN, "Whether or not this entity can be seen by the specified NPC." )
DEFINE_SCRIPTHOOK_PARAM( "npc", FIELD_HSCRIPT )
END_SCRIPTHOOK()
#endif
END_SCRIPTDESC();
@ -6182,7 +6235,7 @@ void CBaseEntity::Remove( )
// ScriptFindKey, ScriptGetFirstSubKey, ScriptGetString,
// ScriptGetInt, ScriptGetFloat, ScriptGetNextKey
//-----------------------------------------------------------------------------
HSCRIPT CBaseEntity::ScriptGetModelKeyValues( void )
HSCRIPT_RC CBaseEntity::ScriptGetModelKeyValues( void )
{
KeyValues *pModelKeyValues = new KeyValues("");
HSCRIPT hScript = NULL;
@ -6191,16 +6244,12 @@ HSCRIPT CBaseEntity::ScriptGetModelKeyValues( void )
if ( pModelKeyValues->LoadFromBuffer( pszModelName, pBuffer ) )
{
// UNDONE: how does destructor get called on this
#ifdef MAPBASE_VSCRIPT
m_pScriptModelKeyValues = hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, pModelKeyValues, true ); // Allow VScript to delete this when the instance is removed.
hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, pModelKeyValues );
#else
// UNDONE: how does destructor get called on this
m_pScriptModelKeyValues = new CScriptKeyValues( pModelKeyValues );
#endif
// UNDONE: who calls ReleaseInstance on this??? Does name need to be unique???
#ifndef MAPBASE_VSCRIPT
hScript = g_pScriptVM->RegisterInstance( m_pScriptModelKeyValues );
#endif
@ -6217,8 +6266,6 @@ HSCRIPT CBaseEntity::ScriptGetModelKeyValues( void )
}
*/
}
return hScript;
}
@ -8023,6 +8070,61 @@ void CBaseEntity::ModifyOrAppendCriteria( AI_CriteriaSet& set )
set.AppendCriteria("spawnflags", UTIL_VarArgs("%i", GetSpawnFlags()));
set.AppendCriteria("flags", UTIL_VarArgs("%i", GetFlags()));
#endif
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_ModifyOrAppendCriteria.CanRunInScope(m_ScriptScope))
{
ScriptVariant_t functionReturn;
if (g_Hook_ModifyOrAppendCriteria.Call( m_ScriptScope, &functionReturn, NULL ))
{
if (functionReturn.m_hScript != NULL)
{
int nIterator = -1;
ScriptVariant_t varKey, varValue;
while ((nIterator = g_pScriptVM->GetKeyValue( functionReturn.m_hScript, nIterator, &varKey, &varValue )) != -1)
{
float flWeight = 1.0f;
char szValue[128];
switch (varValue.m_type)
{
case FIELD_CSTRING:
{
char *colon = V_strstr( varValue.m_pszString, ":" );
if (colon)
{
// Use as weight
flWeight = atof(colon+1);
*colon = NULL;
}
V_strncpy( szValue, varValue.m_pszString, sizeof( szValue ) );
}
break;
case FIELD_BOOLEAN:
V_snprintf( szValue, sizeof( szValue ), "%d", varValue.m_bool );
break;
case FIELD_INTEGER:
V_snprintf( szValue, sizeof( szValue ), "%i", varValue.m_int );
break;
case FIELD_FLOAT:
V_snprintf( szValue, sizeof( szValue ), "%f", varValue.m_float );
break;
case FIELD_VECTOR:
V_snprintf( szValue, sizeof( szValue ), "%f %f %f", varValue.m_pVector->x, varValue.m_pVector->y, varValue.m_pVector->z );
break;
default:
Warning( "ModifyOrAppendCriteria doesn't know how to handle field %i for %s\n", varValue.m_type, varKey.m_pszString );
break;
}
set.AppendCriteria( varKey.m_pszString, szValue, flWeight );
g_pScriptVM->ReleaseValue( varKey );
g_pScriptVM->ReleaseValue( varValue );
}
}
}
}
#endif
}
//-----------------------------------------------------------------------------
@ -10629,11 +10731,11 @@ inline bool AnyPlayersInHierarchy_R( CBaseEntity *pEnt )
if ( AnyPlayersInHierarchy_R( pCur ) )
return true;
}
return false;
}
void CBaseEntity::RecalcHasPlayerChildBit()
{
if ( AnyPlayersInHierarchy_R( this ) )

View File

@ -1522,7 +1522,7 @@ public:
return (int)GetSolid();
}
HSCRIPT ScriptGetModelKeyValues( void );
HSCRIPT_RC ScriptGetModelKeyValues( void );
void ScriptPrecacheModel( const char *name );
void ScriptPrecacheScriptSound( const char *name );
@ -1609,9 +1609,7 @@ public:
CScriptScope m_ScriptScope;
HSCRIPT m_hScriptInstance;
string_t m_iszScriptId;
#ifdef MAPBASE_VSCRIPT
HSCRIPT m_pScriptModelKeyValues;
#else
#ifndef MAPBASE_VSCRIPT
CScriptKeyValues *m_pScriptModelKeyValues;
#endif
@ -1706,7 +1704,7 @@ public:
virtual bool FVisible ( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL );
virtual bool FVisible( const Vector &vecTarget, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL );
virtual bool CanBeSeenBy( CAI_BaseNPC *pNPC ) { return true; } // allows entities to be 'invisible' to NPC senses.
virtual bool CanBeSeenBy( CAI_BaseNPC *pNPC ); // allows entities to be 'invisible' to NPC senses.
// This function returns a value that scales all damage done by this entity.
// Use CDamageModifier to hook in damage modifiers on a guy.
@ -2330,6 +2328,9 @@ public:
private:
CThreadFastMutex m_CalcAbsolutePositionMutex;
static ScriptHook_t g_Hook_OnTakeDamage;
static ScriptHook_t g_Hook_ModifyOrAppendCriteria;
static ScriptHook_t g_Hook_CanBeSeenBy;
bool m_bTruceValidForEnt;
};

View File

@ -856,7 +856,7 @@ bool CBaseFlex::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CCh
textParams.g1 = 255;
textParams.b1 = 255;
if ( GetGameTextSpeechParams( textParams ) )
if ( DispatchGetGameTextSpeechParams( textParams ) )
{
CRecipientFilter filter;
filter.AddAllPlayers();
@ -1693,6 +1693,12 @@ bool CBaseFlex::ProcessSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, C
case CChoreoEvent::SPEAK:
return true;
#ifdef MAPBASE
// Prevents "unknown type" console spam on players
case CChoreoEvent::GENERIC:
return true;
#endif
default:
{
Msg( "unknown type %d in ProcessSceneEvent()\n", event->GetType() );
@ -2132,8 +2138,16 @@ float CBaseFlex::PlayScene( const char *pszScene, float flDelay, AI_Response *re
#ifdef MAPBASE
float CBaseFlex::PlayAutoGeneratedSoundScene( const char *soundname, float flDelay, AI_Response *response, IRecipientFilter *filter )
{
if (response && response->GetType() == ResponseRules::RESPONSE_CHOREOSENTENCE)
return InstancedChoreoSentenceScene( this, soundname, NULL, flDelay, false, response, false, filter );
return InstancedAutoGeneratedSoundScene( this, soundname, NULL, flDelay, false, response, false, filter );
}
float CBaseFlex::PlayChoreoSentenceScene( const char *pszSentence, float flDelay, AI_Response *response, IRecipientFilter *filter )
{
return InstancedChoreoSentenceScene( this, pszSentence, NULL, flDelay, false, response, false, filter );
}
#else
float CBaseFlex::PlayAutoGeneratedSoundScene( const char *soundname )
{
@ -2145,11 +2159,17 @@ float CBaseFlex::PlayAutoGeneratedSoundScene( const char *soundname )
//-----------------------------------------------------------------------------
// Purpose: Parameters for scene event AI_GameText
//-----------------------------------------------------------------------------
bool CBaseFlex::GetGameTextSpeechParams( hudtextparms_t &params )
bool CBaseFlex::DispatchGetGameTextSpeechParams( hudtextparms_t &params )
{
// First, get any default values overridden by this class
bool bReturn = GetGameTextSpeechParams( params );
// Allow VScript to override after
ScriptVariant_t varTable;
if (g_pScriptVM->GetValue(m_ScriptScope, "m_GameTextSpeechParams", &varTable) && varTable.m_type == FIELD_HSCRIPT)
if (g_pScriptVM->GetValue( m_ScriptScope, "m_GameTextSpeechParams", &varTable ) && varTable.m_type == FIELD_HSCRIPT)
{
bReturn = true;
int nIterator = -1;
ScriptVariant_t varKey, varValue;
while ((nIterator = g_pScriptVM->GetKeyValue( varTable.m_hScript, nIterator, &varKey, &varValue )) != -1)
@ -2186,13 +2206,18 @@ bool CBaseFlex::GetGameTextSpeechParams( hudtextparms_t &params )
{
params.fxTime = varValue.m_float;
}
else if (FStrEq( varKey.m_pszString, "disabled" ))
{
// Allow the params to disable game_text choreo if needed
bReturn = !(varValue.m_int > 0);
}
g_pScriptVM->ReleaseValue( varKey );
g_pScriptVM->ReleaseValue( varValue );
}
}
return true;
return bReturn;
}
#endif

View File

@ -129,6 +129,7 @@ public:
virtual float PlayScene( const char *pszScene, float flDelay = 0.0f, AI_Response *response = NULL, IRecipientFilter *filter = NULL );
#ifdef MAPBASE
virtual float PlayChoreoSentenceScene( const char *pszSentenceName, float flDelay = 0.0f, AI_Response *response = NULL, IRecipientFilter *filter = NULL );
virtual float PlayAutoGeneratedSoundScene( const char *soundname, float flDelay = 0.0f, AI_Response *response = NULL, IRecipientFilter *filter = NULL );
#else
virtual float PlayAutoGeneratedSoundScene( const char *soundname );
@ -141,7 +142,8 @@ public:
virtual int GetSpecialDSP( void ) { return 0; }
#ifdef MAPBASE
virtual bool GetGameTextSpeechParams( hudtextparms_t &params );
bool DispatchGetGameTextSpeechParams( hudtextparms_t &params );
virtual bool GetGameTextSpeechParams( hudtextparms_t &params ) { return true; }
#endif
protected:

View File

@ -42,6 +42,10 @@ virtual void NullThink( void );
ConVar g_debug_basehelicopter( "g_debug_basehelicopter", "0", FCVAR_CHEAT );
#ifdef MAPBASE
ConVar g_helicopter_use_sight_condition( "g_helicopter_use_sight_condition", "0", 0, "If enabled, helicopters will use the AI sight condition instead of just checking if there's a clear path to the player. This prevents them from cheating." );
#endif
//---------------------------------------------------------
//---------------------------------------------------------
// TODOs
@ -702,7 +706,11 @@ void CBaseHelicopter::UpdateEnemy()
{
CBaseEntity *pEnemy = GetEnemy();
GatherEnemyConditions( pEnemy );
#ifdef MAPBASE
if ( (g_helicopter_use_sight_condition.GetBool() && pEnemy->Classify() != CLASS_BULLSEYE) ? HasCondition( COND_SEE_ENEMY ) : FVisible( pEnemy ) )
#else
if ( FVisible( pEnemy ) )
#endif
{
if (m_flLastSeen < gpGlobals->curtime - 2)
{

View File

@ -1020,7 +1020,7 @@ float CBounceBomb::FindNearestNPC()
}
#ifdef MAPBASE_MP
for (i = 1; i <= gpGlobals->maxClients; i++)
for (int i = 1; i <= gpGlobals->maxClients; i++)
{
CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
if ( pPlayer && !(pPlayer->GetFlags() & FL_NOTARGET) )

View File

@ -297,6 +297,19 @@ void CSpeaker::DispatchResponse( const char *conceptName )
CAI_Expresser::RunScriptResponse( pTarget, response, &set, true );
break;
}
#endif
#ifdef MAPBASE
case ResponseRules::RESPONSE_CHOREOSENTENCE:
{
CBaseFlex *pFlex = NULL;
if (pTarget != this)
{
// Attempt to get flex on the target
pFlex = dynamic_cast<CBaseFlex*>(pTarget);
}
InstancedAutoGeneratedSoundScene(pFlex, response);
break;
}
#endif
default:
break;

View File

@ -58,6 +58,7 @@
#ifdef MAPBASE
#include "triggers.h"
#include "mapbase/variant_tools.h"
#include "mapbase/protagonist_system.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
@ -119,6 +120,7 @@ ConVar player_autoswitch_enabled( "player_autoswitch_enabled", "1", FCVAR_NONE,
#ifdef SP_ANIM_STATE
ConVar hl2_use_sp_animstate( "hl2_use_sp_animstate", "1", FCVAR_NONE, "Allows SP HL2 players to use HL2:DM animations for custom player models. (changes may not apply until model is reloaded)" );
ConVar player_process_scene_events( "player_process_scene_events", "1", FCVAR_NONE, "Allows players to process scene events." );
#endif
#endif
@ -265,6 +267,7 @@ public:
void InputSetHandModelBodyGroup( inputdata_t &inputdata );
void InputSetPlayerModel( inputdata_t &inputdata );
void InputSetPlayerDrawLegs( inputdata_t &inputdata );
void InputSetPlayerDrawExternally( inputdata_t &inputdata );
#endif
@ -585,6 +588,8 @@ BEGIN_DATADESC( CHL2_Player )
DEFINE_INPUTFUNC( FIELD_VOID, "DisableGeigerCounter", InputDisableGeigerCounter ),
DEFINE_INPUTFUNC( FIELD_VOID, "ShowSquadHUD", InputShowSquadHUD ),
DEFINE_INPUTFUNC( FIELD_VOID, "HideSquadHUD", InputHideSquadHUD ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetProtagonist", InputSetProtagonist ),
#endif
DEFINE_SOUNDPATCH( m_sndLeeches ),
@ -599,6 +604,10 @@ BEGIN_DATADESC( CHL2_Player )
DEFINE_FIELD( m_flTimeNextLadderHint, FIELD_TIME ),
#ifdef MAPBASE
DEFINE_KEYFIELD( m_iszProtagonistName, FIELD_STRING, "ProtagonistName" ),
#endif
//DEFINE_FIELD( m_hPlayerProxy, FIELD_EHANDLE ), //Shut up class check!
END_DATADESC()
@ -617,6 +626,9 @@ BEGIN_ENT_SCRIPTDESC( CHL2_Player, CBasePlayer, "The HL2 player entity." )
DEFINE_SCRIPTFUNC( RemoveCustomSuitDevice, "Removes a custom suit device ID. (1-3)" )
DEFINE_SCRIPTFUNC( IsCustomSuitDeviceActive, "Checks if a custom suit device is active." )
DEFINE_SCRIPTFUNC( GetProtagonistName, "Gets the player's protagonist name." )
DEFINE_SCRIPTFUNC( SetProtagonist, "Sets the player's protagonist entry." )
#ifdef SP_ANIM_STATE
DEFINE_SCRIPTFUNC( AddAnimStateLayer, "Adds a custom sequence index as a misc. layer for the singleplayer anim state, wtih parameters for blending in/out, setting the playback rate, holding the animation at the end, and only playing when the player is still." )
#endif
@ -632,6 +644,10 @@ CHL2_Player::CHL2_Player()
m_flArmorReductionTime = 0.0f;
m_iArmorReductionFrom = 0;
#ifdef MAPBASE
m_nProtagonistIndex = -1;
#endif
}
//
@ -666,8 +682,12 @@ CSuitPowerDevice SuitDeviceCustom[] =
IMPLEMENT_SERVERCLASS_ST(CHL2_Player, DT_HL2_Player)
SendPropDataTable(SENDINFO_DT(m_HL2Local), &REFERENCE_SEND_TABLE(DT_HL2Local), SendProxy_SendLocalDataTable),
SendPropBool( SENDINFO(m_fIsSprinting) ),
#ifdef SP_ANIM_STATE
#ifdef MAPBASE
SendPropInt( SENDINFO( m_nProtagonistIndex ), 8, SPROP_UNSIGNED ),
#endif
#if defined(SP_ANIM_STATE) && !defined(MAPBASE_MP)
SendPropFloat( SENDINFO(m_flAnimRenderYaw), 0, SPROP_NOSCALE ),
SendPropFloat( SENDINFO(m_flAnimRenderZ), 0, SPROP_NOSCALE ),
#endif
END_SEND_TABLE()
@ -1225,7 +1245,21 @@ void CHL2_Player::PostThink( void )
QAngle angEyeAngles = EyeAngles();
m_pPlayerAnimState->Update( angEyeAngles.y, angEyeAngles.x );
#ifndef MAPBASE_MP
m_flAnimRenderYaw.Set( m_pPlayerAnimState->GetRenderAngles().y );
if (m_pPlayerAnimState->IsJumping() && !m_pPlayerAnimState->IsDuckJumping())
{
m_flAnimRenderZ.Set( -(GetViewOffset().z) );
}
else
m_flAnimRenderZ.Set( 0.0f );
#endif
if (player_process_scene_events.GetBool())
{
ProcessSceneEvents();
}
}
#endif
}
@ -1304,6 +1338,11 @@ void CHL2_Player::Activate( void )
#endif
GetPlayerProxy();
#ifdef MAPBASE
if (m_iszProtagonistName != NULL_STRING)
SetProtagonist( STRING( m_iszProtagonistName ) );
#endif
}
//------------------------------------------------------------------------------
@ -1533,17 +1572,22 @@ CStudioHdr *CHL2_Player::OnNewModel()
// Here we create and init the player animation state.
m_pPlayerAnimState = CreatePlayerAnimationState(this);
}
#ifndef MAPBASE_MP
else
{
m_flAnimRenderYaw = FLT_MAX;
}
#endif
#endif
return hdr;
}
extern char g_szDefaultPlayerModel[MAX_PATH];
extern bool g_bDefaultPlayerLegs;
extern bool g_bDefaultPlayerDrawExternally;
extern char g_szDefaultProtagonist[MAX_PROTAGONIST_NAME];
#endif
//-----------------------------------------------------------------------------
@ -1575,7 +1619,11 @@ void CHL2_Player::Spawn(void)
RemoveEffects( EF_NODRAW );
}
SetDrawPlayerLegs( g_bDefaultPlayerLegs );
SetDrawPlayerModelExternally( g_bDefaultPlayerDrawExternally );
if (m_iszProtagonistName == NULL_STRING && *g_szDefaultProtagonist)
m_iszProtagonistName = MAKE_STRING( g_szDefaultProtagonist );
#endif
//
@ -3386,6 +3434,11 @@ void CHL2_Player::Weapon_Equip( CBaseCombatWeapon *pWeapon )
if( GetActiveWeapon() == NULL )
{
m_HL2Local.m_bWeaponLowered = false;
#ifdef SP_ANIM_STATE
if (m_pPlayerAnimState)
m_pPlayerAnimState->StopWeaponRelax();
#endif
}
BaseClass::Weapon_Equip( pWeapon );
@ -3791,6 +3844,11 @@ bool CHL2_Player::Weapon_Lower( void )
m_HL2Local.m_bWeaponLowered = true;
#ifdef SP_ANIM_STATE
if (m_pPlayerAnimState)
m_pPlayerAnimState->StartWeaponRelax();
#endif
CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon *>(GetActiveWeapon());
if ( pWeapon == NULL )
@ -3813,6 +3871,11 @@ bool CHL2_Player::Weapon_Ready( void )
m_HL2Local.m_bWeaponLowered = false;
#ifdef SP_ANIM_STATE
if (m_pPlayerAnimState)
m_pPlayerAnimState->StopWeaponRelax();
#endif
CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon *>(GetActiveWeapon());
if ( pWeapon == NULL )
@ -4026,6 +4089,23 @@ void CHL2_Player::OnRestore()
{
BaseClass::OnRestore();
m_pPlayerAISquad = g_AI_SquadManager.FindCreateSquad(AllocPooledString(PLAYER_SQUADNAME));
#ifdef SP_ANIM_STATE
if ( m_pPlayerAnimState == NULL )
{
if ( GetModelPtr() && GetModelPtr()->HaveSequenceForActivity(ACT_HL2MP_IDLE) && hl2_use_sp_animstate.GetBool() )
{
// Here we create and init the player animation state.
m_pPlayerAnimState = CreatePlayerAnimationState(this);
}
#ifndef MAPBASE_MP
else
{
m_flAnimRenderYaw = FLT_MAX;
}
#endif
}
#endif
}
//---------------------------------------------------------
@ -4077,6 +4157,10 @@ bool CHL2_Player::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex
StopZooming();
}
#ifdef MAPBASE
RefreshProtagonistWeaponData( pWeapon );
#endif
return BaseClass::Weapon_Switch( pWeapon, viewmodelindex );
}
@ -4472,6 +4556,144 @@ bool CHL2_Player::IsCustomSuitDeviceActive( int iDeviceID )
return SuitPower_IsDeviceActive( SuitDeviceCustom[iDeviceID] );
}
//-----------------------------------------------------------------------------
// Purpose: Gets our protagonist name, if we have one
//-----------------------------------------------------------------------------
const char *CHL2_Player::GetProtagonistName() const
{
return STRING( m_iszProtagonistName );
}
//-----------------------------------------------------------------------------
// Purpose: Gets our protagonist index, if we have one
//-----------------------------------------------------------------------------
int CHL2_Player::GetProtagonistIndex() const
{
return m_nProtagonistIndex;
}
//-----------------------------------------------------------------------------
// Purpose: Sets our protagonist to the specified entry
//-----------------------------------------------------------------------------
void CHL2_Player::InputSetProtagonist( inputdata_t &inputdata )
{
SetProtagonist( inputdata.value.String() );
}
//-----------------------------------------------------------------------------
// Purpose: Sets our protagonist to the specified entry
//-----------------------------------------------------------------------------
void CHL2_Player::SetProtagonist( const char *pszProtagonist )
{
if (!pszProtagonist || !*pszProtagonist)
{
ResetProtagonist();
return;
}
int nIndex = g_ProtagonistSystem.FindProtagonistIndex( pszProtagonist );
if (nIndex == -1)
{
Warning( "\"%s\" is not a valid protagonist\n", pszProtagonist );
return;
}
if (m_nProtagonistIndex != -1)
{
// Flush any pre-existing data
ResetProtagonist();
}
m_nProtagonistIndex = nIndex;
m_iszProtagonistName = AllocPooledString( pszProtagonist );
RefreshProtagonistData();
}
//-----------------------------------------------------------------------------
// Purpose: Resets protagonist data
//-----------------------------------------------------------------------------
void CHL2_Player::ResetProtagonist()
{
SetModel( g_szDefaultPlayerModel );
m_nSkin = 0;
m_nBody = 0;
CBaseViewModel *vm = GetViewModel( 1 );
if (vm)
{
extern char g_szDefaultHandsModel[MAX_PATH];
vm->SetWeaponModel( g_szDefaultHandsModel, NULL );
vm->m_nSkin = 0;
vm->m_nBody = 0;
}
// RemoveContext will automatically remove contexts by name, regardless of how values are specified
char szContexts[128] = { 0 };
g_ProtagonistSystem.GetProtagonist_ResponseContexts( this, szContexts, sizeof( szContexts ) );
if (szContexts[0])
RemoveContext( szContexts );
m_iszProtagonistName = NULL_STRING;
m_nProtagonistIndex = -1;
}
//-----------------------------------------------------------------------------
// Purpose: Refreshes protagonist data
//-----------------------------------------------------------------------------
void CHL2_Player::RefreshProtagonistData()
{
if (m_nProtagonistIndex == -1)
return;
g_ProtagonistSystem.PrecacheProtagonist( this, m_nProtagonistIndex );
const char *pszProtagModel = g_ProtagonistSystem.GetProtagonist_PlayerModel( this );
if (pszProtagModel)
SetModel( pszProtagModel );
m_nSkin = g_ProtagonistSystem.GetProtagonist_PlayerModelSkin( this );
m_nBody = g_ProtagonistSystem.GetProtagonist_PlayerModelBody( this );
char szContexts[128] = { 0 };
g_ProtagonistSystem.GetProtagonist_ResponseContexts( this, szContexts, sizeof( szContexts ) );
if (szContexts[0])
AddContext( szContexts );
RefreshProtagonistWeaponData( GetActiveWeapon() );
}
//-----------------------------------------------------------------------------
// Purpose: Refreshes protagonist data
//-----------------------------------------------------------------------------
void CHL2_Player::RefreshProtagonistWeaponData( CBaseCombatWeapon *pWeapon )
{
if (m_nProtagonistIndex == -1)
return;
CBaseViewModel *vm = GetViewModel( 1 );
if (vm)
{
const char *pszHandModel = g_ProtagonistSystem.GetProtagonist_HandModel( this, pWeapon );
if (pszHandModel)
{
vm->SetWeaponModel( pszHandModel, NULL );
vm->m_nSkin = g_ProtagonistSystem.GetProtagonist_HandModelSkin( this, pWeapon );
vm->m_nBody = g_ProtagonistSystem.GetProtagonist_HandModelBody( this, pWeapon );
}
else
{
extern char g_szDefaultHandsModel[MAX_PATH];
vm->SetWeaponModel( g_szDefaultHandsModel, NULL );
vm->m_nSkin = 0;
vm->m_nBody = 0;
}
}
}
#endif
//-----------------------------------------------------------------------------
@ -4631,6 +4853,7 @@ BEGIN_DATADESC( CLogicPlayerProxy )
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHandModelSkin", InputSetHandModelSkin ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHandModelBodyGroup", InputSetHandModelBodyGroup ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetPlayerModel", InputSetPlayerModel ),
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetPlayerDrawLegs", InputSetPlayerDrawLegs ),
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetPlayerDrawExternally", InputSetPlayerDrawExternally ),
DEFINE_INPUT( m_MaxArmor, FIELD_INTEGER, "SetMaxInputArmor" ),
DEFINE_INPUT( m_SuitZoomFOV, FIELD_INTEGER, "SetSuitZoomFOV" ),
@ -5109,6 +5332,15 @@ void CLogicPlayerProxy::InputSetPlayerModel( inputdata_t &inputdata )
m_hPlayer->SetModel( STRING(iszModel) );
}
void CLogicPlayerProxy::InputSetPlayerDrawLegs( inputdata_t &inputdata )
{
if (!m_hPlayer)
return;
CBasePlayer *pPlayer = static_cast<CBasePlayer*>(m_hPlayer.Get());
pPlayer->SetDrawPlayerLegs( inputdata.value.Bool() );
}
void CLogicPlayerProxy::InputSetPlayerDrawExternally( inputdata_t &inputdata )
{
if (!m_hPlayer)

View File

@ -18,8 +18,10 @@
// In HL2MP we need to inherit from BaseMultiplayerPlayer!
#if defined ( HL2MP )
#include "basemultiplayerplayer.h"
#elif defined ( MAPBASE )
#include "mapbase/singleplayer_animstate.h"
#endif
#ifdef MAPBASE
#include "mapbase/mapbase_playeranimstate.h"
#endif
class CAI_Squad;
@ -357,6 +359,15 @@ public:
void AddCustomSuitDevice( int iDeviceID );
void RemoveCustomSuitDevice( int iDeviceID );
bool IsCustomSuitDeviceActive( int iDeviceID );
// Protagonist system
const char *GetProtagonistName() const;
int GetProtagonistIndex() const;
void InputSetProtagonist( inputdata_t &inputdata );
void SetProtagonist( const char *pszProtagonist );
void ResetProtagonist();
void RefreshProtagonistData();
void RefreshProtagonistWeaponData( CBaseCombatWeapon *pWeapon );
#endif
CSoundPatch *m_sndLeeches;
@ -438,12 +449,21 @@ private:
friend class CHL2GameMovement;
#ifdef SP_ANIM_STATE
CSinglePlayerAnimState* m_pPlayerAnimState;
#ifdef MAPBASE
// Protagonist used by protagonist_system.h
string_t m_iszProtagonistName;
CNetworkVar( int, m_nProtagonistIndex );
#endif
#ifdef SP_ANIM_STATE
CMapbasePlayerAnimState* m_pPlayerAnimState;
#ifndef MAPBASE_MP
// At the moment, we network the render angles since almost none of the player anim stuff is done on the client in SP.
// If any of this is ever adapted for MP, this method should be replaced with replicating/moving the anim state to the client.
CNetworkVar( float, m_flAnimRenderYaw );
CNetworkVar( float, m_flAnimRenderZ );
#endif
#endif
};

View File

@ -63,3 +63,88 @@ BEGIN_DATADESC( CItemBattery )
END_DATADESC()
#endif
#ifdef MAPBASE
extern ConVar sk_battery;
//-----------------------------------------------------------------------------
// Custom player battery. Heals the player when picked up.
//-----------------------------------------------------------------------------
class CItemBatteryCustom : public CItem
{
public:
DECLARE_CLASS( CItemBatteryCustom, CItem );
CItemBatteryCustom();
void Spawn( void );
void Precache( void );
bool MyTouch( CBasePlayer *pPlayer );
float GetItemAmount() { return m_flPowerAmount; }
void InputSetPowerAmount( inputdata_t &inputdata ) { m_flPowerAmount = inputdata.value.Float(); }
float m_flPowerAmount;
string_t m_iszTouchSound;
DECLARE_DATADESC();
};
LINK_ENTITY_TO_CLASS( item_battery_custom, CItemBatteryCustom );
#ifdef MAPBASE
BEGIN_DATADESC( CItemBatteryCustom )
DEFINE_KEYFIELD( m_flPowerAmount, FIELD_FLOAT, "PowerAmount" ),
DEFINE_KEYFIELD( m_iszTouchSound, FIELD_STRING, "TouchSound" ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPowerAmount", InputSetPowerAmount ),
END_DATADESC()
#endif
CItemBatteryCustom::CItemBatteryCustom()
{
SetModelName( AllocPooledString( "models/items/battery.mdl" ) );
m_flPowerAmount = sk_battery.GetFloat();
m_iszTouchSound = AllocPooledString( "ItemBattery.Touch" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CItemBatteryCustom::Spawn( void )
{
Precache();
SetModel( STRING( GetModelName() ) );
BaseClass::Spawn();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CItemBatteryCustom::Precache( void )
{
PrecacheModel( STRING( GetModelName() ) );
PrecacheScriptSound( STRING( m_iszTouchSound ) );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pPlayer -
// Output :
//-----------------------------------------------------------------------------
bool CItemBatteryCustom::MyTouch( CBasePlayer *pPlayer )
{
CHL2_Player *pHL2Player = assert_cast<CHL2_Player *>(pPlayer);
if (!pHL2Player || sk_battery.GetFloat() == 0.0f)
return false;
float flPowerMult = m_flPowerAmount / sk_battery.GetFloat();
return pHL2Player->ApplyBattery( flPowerMult );
}
#endif

View File

@ -44,6 +44,7 @@
#ifdef MAPBASE
#include "filters.h"
#include "ai_hint.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
@ -164,6 +165,15 @@ ConVar g_helicopter_bullrush_mega_bomb_health( "g_helicopter_bullrush_mega_bomb_
ConVar g_helicopter_bomb_danger_radius( "g_helicopter_bomb_danger_radius", "120" );
#ifdef MAPBASE
ConVar g_helicopter_crashpoint_nearest( "g_helicopter_crashpoint_nearest", "1", 0, "Selects the nearest crash point instead of just the first in the entity list." );
#ifdef HL2_EPISODIC
ConVar g_helicopter_phys_follow_while_crashing( "g_helicopter_phys_follow_while_crashing", "0", 0, "Allows the phys_bone_followers to follow the helicopter while flying to crash point" );
#endif
#endif
Activity ACT_HELICOPTER_DROP_BOMB;
Activity ACT_HELICOPTER_CRASHING;
@ -191,7 +201,60 @@ enum
#define GRENADE_HELICOPTER_MODEL "models/combine_helicopter/helicopter_bomb01.mdl"
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CTargetHelicopterCrash : public CPointEntity
{
DECLARE_CLASS( CTargetHelicopterCrash, CPointEntity );
public:
DECLARE_DATADESC();
void InputEnable( inputdata_t &inputdata )
{
m_bDisabled = false;
}
void InputDisable( inputdata_t &inputdata )
{
m_bDisabled = true;
}
bool IsDisabled( void )
{
return m_bDisabled;
}
void HelicopterCrashedOnTarget( CBaseHelicopter *pChopper )
{
m_OnCrashed.FireOutput( pChopper, this );
}
void HelicopterAcquiredCrashTarget( CBaseHelicopter *pChopper )
{
m_OnBeginCrash.FireOutput( pChopper, this );
}
private:
bool m_bDisabled;
COutputEvent m_OnCrashed;
COutputEvent m_OnBeginCrash;
};
LINK_ENTITY_TO_CLASS( info_target_helicopter_crash, CTargetHelicopterCrash );
BEGIN_DATADESC( CTargetHelicopterCrash )
DEFINE_FIELD( m_bDisabled, FIELD_BOOLEAN ),
// Inputs
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
// Outputs
DEFINE_OUTPUT( m_OnCrashed, "OnCrashed" ),
DEFINE_OUTPUT( m_OnBeginCrash, "OnBeginCrash" ),
END_DATADESC()
#else
LINK_ENTITY_TO_CLASS( info_target_helicopter_crash, CPointEntity );
#endif
//------------------------------------------------------------------------------
@ -499,6 +562,13 @@ private:
// Death, etc.
void InputSelfDestruct( inputdata_t &inputdata );
#ifdef MAPBASE
// This is identical to SelfDestruct, except the helicopter won't throw out chunks while flying to a crash point.
// This input is meant to be used when the pilot is killed and there's nothing wrong with the helicopter itself.
// If there are no crash points, the helicopter will explode in place as normal.
void InputSelfDestructNoFX( inputdata_t &inputdata );
#endif
// Enemy visibility check
CBaseEntity *FindTrackBlocker( const Vector &vecViewPoint, const Vector &vecTargetPos );
@ -522,6 +592,7 @@ private:
// Various states of the helicopter firing...
bool PoseGunTowardTargetDirection( const Vector &vTargetDir );
float GetGunPoseSpeed() const;
// Compute the position to fire at (vehicle + non-vehicle case)
void ComputeFireAtPosition( Vector *pVecActualTargetPosition );
@ -533,6 +604,17 @@ private:
bool DoGunFiring( const Vector &vBasePos, const Vector &vGunDir, const Vector &vecFireAtPosition );
void FireElectricityGun( );
#ifdef MAPBASE
// Idle aiming
bool FindIdleAimTarget();
bool ValidIdleAimTarget( CBaseEntity *pAimTarget );
bool FValidateHintType( CAI_Hint *pHint );
inline CBaseEntity *GetIdleAimTarget() const { return m_hIdleAimTarget; }
inline void SetIdleAimTarget( CBaseEntity *pNewAimTarget ) { m_hIdleAimTarget = pNewAimTarget; }
#endif
// Chooses a point within the circle of death to fire in
void PickDirectionToCircleOfDeath( const Vector &vBasePos, const Vector &vecFireAtPosition, Vector *pResult );
@ -650,6 +732,7 @@ private:
void SpotlightStartup();
void SpotlightShutdown();
CBaseEntity *FindCrashPoint();
CBaseEntity *GetCrashPoint() { return m_hCrashPoint.Get(); }
private:
@ -721,6 +804,17 @@ private:
bool m_bBombsExplodeOnContact;
bool m_bNonCombat;
#ifdef MAPBASE
bool m_bIdleAimAround;
EHANDLE m_hIdleAimTarget;
Vector m_vecIdleAimDir;
float m_flNextIdleAimTime;
bool m_bDisableSmokeTrails;
bool m_bDisableCorpses;
bool m_bDisableExplosions;
#endif
int m_nNearShots;
int m_nMaxNearShots;
@ -860,6 +954,15 @@ BEGIN_DATADESC( CNPC_AttackHelicopter )
#ifdef MAPBASE
DEFINE_KEYFIELD( m_flFieldOfView, FIELD_FLOAT, "FieldOfView" ),
DEFINE_KEYFIELD( m_bIdleAimAround, FIELD_BOOLEAN, "IdleAimAround" ),
DEFINE_FIELD( m_hIdleAimTarget, FIELD_EHANDLE ),
DEFINE_FIELD( m_vecIdleAimDir, FIELD_VECTOR ),
DEFINE_FIELD( m_flNextIdleAimTime, FIELD_TIME ),
DEFINE_KEYFIELD( m_bDisableSmokeTrails, FIELD_BOOLEAN, "DisableSmokeTrails" ),
DEFINE_KEYFIELD( m_bDisableCorpses, FIELD_BOOLEAN, "DisableCorpses" ),
DEFINE_KEYFIELD( m_bDisableExplosions, FIELD_BOOLEAN, "DisableExplosions" ),
#endif
DEFINE_FIELD( m_hCrashPoint, FIELD_EHANDLE ),
@ -896,6 +999,9 @@ BEGIN_DATADESC( CNPC_AttackHelicopter )
DEFINE_INPUTFUNC( FIELD_VOID, "EnablePathVisibilityTests", InputEnablePathVisibilityTests ),
DEFINE_INPUTFUNC( FIELD_VOID, "SelfDestruct", InputSelfDestruct ),
#ifdef MAPBASE
DEFINE_INPUTFUNC( FIELD_VOID, "SelfDestructNoFX", InputSelfDestructNoFX ),
#endif
DEFINE_THINKFUNC( BlinkLightsThink ),
DEFINE_THINKFUNC( SpotlightThink ),
@ -1646,6 +1752,21 @@ void CNPC_AttackHelicopter::InputSelfDestruct( inputdata_t &inputdata )
TakeDamage( info );
}
#ifdef MAPBASE
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void CNPC_AttackHelicopter::InputSelfDestructNoFX( inputdata_t &inputdata )
{
m_bDisableSmokeTrails = true;
m_bDisableCorpses = true;
m_bDisableExplosions = true;
m_lifeState = LIFE_ALIVE; // Force to die properly.
CTakeDamageInfo info( this, this, Vector(0, 0, 1), WorldSpaceCenter(), GetMaxHealth(), CLASS_MISSILE );
TakeDamage( info );
}
#endif
//-----------------------------------------------------------------------------
// For scripted times where it *has* to shoot
//-----------------------------------------------------------------------------
@ -2245,21 +2366,23 @@ bool CNPC_AttackHelicopter::PoseGunTowardTargetDirection( const Vector &vTargetD
}
}
float flPoseSpeed = GetGunPoseSpeed();
if (angles.x > m_angGun.x)
{
m_angGun.x = MIN( angles.x, m_angGun.x + 12 );
m_angGun.x = MIN( angles.x, m_angGun.x + flPoseSpeed );
}
if (angles.x < m_angGun.x)
{
m_angGun.x = MAX( angles.x, m_angGun.x - 12 );
m_angGun.x = MAX( angles.x, m_angGun.x - flPoseSpeed );
}
if (angles.y > m_angGun.y)
{
m_angGun.y = MIN( angles.y, m_angGun.y + 12 );
m_angGun.y = MIN( angles.y, m_angGun.y + flPoseSpeed );
}
if (angles.y < m_angGun.y)
{
m_angGun.y = MAX( angles.y, m_angGun.y - 12 );
m_angGun.y = MAX( angles.y, m_angGun.y - flPoseSpeed );
}
SetPoseParameter( m_poseWeapon_Pitch, -m_angGun.x );
@ -2269,6 +2392,20 @@ bool CNPC_AttackHelicopter::PoseGunTowardTargetDirection( const Vector &vTargetD
}
//------------------------------------------------------------------------------
// Various states of the helicopter firing...
//------------------------------------------------------------------------------
float CNPC_AttackHelicopter::GetGunPoseSpeed() const
{
#ifdef MAPBASE
if (m_bIdleAimAround && !GetEnemy())
return 4.0f;
#endif
return 12.0f;
}
//------------------------------------------------------------------------------
// Compute the enemy position (non-vehicle case)
//------------------------------------------------------------------------------
@ -2363,6 +2500,87 @@ bool CNPC_AttackHelicopter::DoGunIdle( const Vector &vGunDir, const Vector &vTar
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Finds an idle aim target. Based on CNPC_PlayerCompanion code
//-----------------------------------------------------------------------------
bool CNPC_AttackHelicopter::FindIdleAimTarget()
{
CAI_Hint *pHint;
CHintCriteria hintCriteria;
CBaseEntity *pPriorAimTarget = GetIdleAimTarget();
hintCriteria.SetHintType( HINT_WORLD_VISUALLY_INTERESTING );
hintCriteria.SetFlag( bits_HINT_NODE_VISIBLE | bits_HINT_NODE_IN_VIEWCONE | bits_HINT_NPC_IN_NODE_FOV | bits_HINT_NODE_USE_GROUP | bits_HINT_NODE_NEAREST );
hintCriteria.AddIncludePosition( GetAbsOrigin(), CHOPPER_MAX_GUN_DIST );
pHint = CAI_HintManager::FindHint( this, hintCriteria );
if( pHint )
{
if ( !ValidIdleAimTarget( pHint ) )
{
return false;
}
if ( pHint != pPriorAimTarget )
{
// Notify of the change.
SetIdleAimTarget( pHint );
return true;
}
}
// Didn't find an aim target, or found the same one.
return false;
}
//-----------------------------------------------------------------------------
// Finds an idle aim target. Based on CNPC_PlayerCompanion code
//-----------------------------------------------------------------------------
bool CNPC_AttackHelicopter::ValidIdleAimTarget( CBaseEntity *pAimTarget )
{
Vector vecTargetOrigin = pAimTarget->GetAbsOrigin();
Vector vBasePos;
GetAttachment( m_nGunBaseAttachment, vBasePos );
if ( vecTargetOrigin.z > vBasePos.z || (vecTargetOrigin - vBasePos).Length() < 128.0f )
{
// Too close!
return false;
}
trace_t tr;
UTIL_TraceLine( vBasePos, vecTargetOrigin, MASK_BLOCKLOS, this, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction < 1.0f && tr.m_pEnt != pAimTarget )
{
// No LOS
return false;
}
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CNPC_AttackHelicopter::FValidateHintType( CAI_Hint *pHint )
{
switch( pHint->HintType() )
{
case HINT_WORLD_VISUALLY_INTERESTING:
return true;
break;
default:
break;
}
return BaseClass::FValidateHintType( pHint );
}
#endif
//------------------------------------------------------------------------------
// How easy is the target to hit?
//------------------------------------------------------------------------------
@ -3653,12 +3871,20 @@ int CNPC_AttackHelicopter::OnTakeDamage_Alive( const CTakeDamageInfo &info )
}
}
#ifdef MAPBASE
if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_SMOKE_TRAILS ) && !m_bDisableSmokeTrails )
#else
if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_SMOKE_TRAILS ) )
#endif
{
AddSmokeTrail( info.GetDamagePosition() );
}
#ifdef MAPBASE
if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_CORPSES ) && !m_bDisableCorpses )
#else
if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_CORPSES ) )
#endif
{
if ( nPrevHealth != GetMaxHealth() )
{
@ -3666,7 +3892,11 @@ int CNPC_AttackHelicopter::OnTakeDamage_Alive( const CTakeDamageInfo &info )
}
}
#ifdef MAPBASE
if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_EXPLOSIONS ) && !m_bDisableExplosions )
#else
if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_EXPLOSIONS ) )
#endif
{
ExplodeAndThrowChunk( info.GetDamagePosition() );
}
@ -3774,6 +4004,46 @@ void Chopper_BecomeChunks( CBaseEntity *pChopper )
pBodyChunk->m_pCockpitConstraint = physenv->CreateFixedConstraint( pBodyObject, pCockpitObject, pGroup, fixed );
}
//-----------------------------------------------------------------------------
// Purpose: Find a valid crash point
//-----------------------------------------------------------------------------
CBaseEntity *CNPC_AttackHelicopter::FindCrashPoint()
{
#ifdef MAPBASE
float flNearest = MAX_TRACE_LENGTH * MAX_TRACE_LENGTH;
CTargetHelicopterCrash *pNearest = NULL;
CBaseEntity *pEnt = NULL;
while( (pEnt = gEntList.FindEntityByClassname(pEnt, "info_target_helicopter_crash")) != NULL )
{
CTargetHelicopterCrash *pCrashTarget = assert_cast<CTargetHelicopterCrash*>(pEnt);
if ( pCrashTarget->IsDisabled() )
continue;
if (g_helicopter_crashpoint_nearest.GetBool())
{
float flDist = ( pEnt->WorldSpaceCenter() - WorldSpaceCenter() ).LengthSqr();
if( flDist < flNearest )
{
pNearest = pCrashTarget;
flNearest = flDist;
}
}
else
{
pNearest = pCrashTarget;
break;
}
}
if (pNearest)
pNearest->HelicopterAcquiredCrashTarget( this );
return pNearest;
#else
return gEntList.FindEntityByClassname( NULL, "info_target_helicopter_crash" );
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Start us crashing
//-----------------------------------------------------------------------------
@ -3791,7 +4061,7 @@ void CNPC_AttackHelicopter::Event_Killed( const CTakeDamageInfo &info )
if( GetCrashPoint() == NULL )
{
CBaseEntity *pCrashPoint = gEntList.FindEntityByClassname( NULL, "info_target_helicopter_crash" );
CBaseEntity *pCrashPoint = FindCrashPoint();
if( pCrashPoint != NULL )
{
m_hCrashPoint.Set( pCrashPoint );
@ -3812,6 +4082,12 @@ void CNPC_AttackHelicopter::Event_Killed( const CTakeDamageInfo &info )
return;
}
}
#ifdef MAPBASE
else
{
assert_cast<CTargetHelicopterCrash*>( GetCrashPoint() )->HelicopterCrashedOnTarget( this );
}
#endif
SendOnKilledGameEvent( info );
@ -3871,7 +4147,11 @@ void CNPC_AttackHelicopter::PrescheduleThink( void )
SetDesiredPosition( GetCrashPoint()->WorldSpaceCenter() );
}
#ifdef MAPBASE
if ( !m_bDisableExplosions && random->RandomInt( 0, 4 ) == 0 )
#else
if ( random->RandomInt( 0, 4 ) == 0 )
#endif
{
Vector explodePoint;
CollisionProp()->RandomPointInBounds( Vector(0.25,0.25,0.25), Vector(0.75,0.75,0.75), &explodePoint );
@ -4823,6 +5103,15 @@ void CNPC_AttackHelicopter::Hunt( void )
{
Flight();
UpdatePlayerDopplerShift( );
#if defined(MAPBASE) && defined(HL2_EPISODIC)
if (g_helicopter_phys_follow_while_crashing.GetBool())
{
// Update our bone followers
m_BoneFollowerManager.UpdateBoneFollowers( this );
}
#endif // HL2_EPISODIC
return;
}
@ -4887,6 +5176,55 @@ void CNPC_AttackHelicopter::Hunt( void )
#endif
}
#ifdef MAPBASE
if (m_bIdleAimAround)
{
if (!GetEnemy())
{
if (GetIdleAimTarget())
{
if (!ValidIdleAimTarget( GetIdleAimTarget() ))
{
SetIdleAimTarget( NULL );
m_flNextIdleAimTime = gpGlobals->curtime;
}
}
if (m_flNextIdleAimTime < gpGlobals->curtime)
{
if (!FindIdleAimTarget())
{
SetIdleAimTarget( NULL );
// Find a random direction in front of us instead
Vector vBasePos, vecForward, vecRight, vecUp;
GetAttachment( m_nGunBaseAttachment, vBasePos, &vecForward, &vecRight, &vecUp );
m_vecIdleAimDir = vecForward + (vecRight * RandomFloat( -0.25f, 0.25f )) + (vecUp * RandomFloat( -0.8f, -0.25f ));
}
m_flNextIdleAimTime = gpGlobals->curtime + RandomFloat( 4.0f, 6.0f );
}
if (GetIdleAimTarget())
{
// Get gun attachment points
Vector vBasePos;
GetAttachment( m_nGunBaseAttachment, vBasePos );
m_vecIdleAimDir = GetIdleAimTarget()->GetAbsOrigin() - vBasePos;
VectorNormalize( m_vecIdleAimDir );
}
PoseGunTowardTargetDirection( m_vecIdleAimDir );
}
else if (GetIdleAimTarget())
{
SetIdleAimTarget( NULL );
}
}
#endif
#ifdef HL2_EPISODIC
// Update our bone followers
m_BoneFollowerManager.UpdateBoneFollowers(this);

View File

@ -2551,6 +2551,24 @@ int CNPC_Combine::TranslateSchedule( int scheduleType )
}
}
#ifdef MAPBASE
extern ConVar ai_enemy_memory_fixes;
// SCHED_COMBINE_ESTABLISH_LINE_OF_FIRE uses TASK_GET_PATH_TO_ENEMY_LKP_LOS, a task with a mistake
// detailed in CAI_BaseNPC::StartTask and fixed by ai_enemy_memory_fixes.
//
// However, SCHED_COMBINE_ESTABLISH_LINE_OF_FIRE only stops being used once the NPC has LOS to its target.
// Since the fixed task now uses the enemy's last known position instead of the enemy's actual position,
// this schedule risks getting stuck in a loop.
//
// This code makes the soldier run up directly to the last known position if it's visible, allowing the AI
// to mark the enemy as eluded.
if ( ai_enemy_memory_fixes.GetBool() && FVisible( GetEnemyLKP() ) )
{
return SCHED_COMBINE_PRESS_ATTACK;
}
#endif
return SCHED_COMBINE_ESTABLISH_LINE_OF_FIRE;
}
break;

View File

@ -69,6 +69,7 @@ public:
int RangeAttack2Conditions( float flDot, float flDist ); // For innate grenade attack
int MeleeAttack1Conditions( float flDot, float flDist ); // For kick/punch
bool FVisible( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL );
bool FVisible( const Vector &vecTarget, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL ) { return BaseClass::FVisible( vecTarget, traceMask, ppBlocker ); }
virtual bool IsCurTaskContinuousMove();
virtual float GetJumpGravity() const { return 1.8f; }

View File

@ -134,6 +134,11 @@ enum LandingState_t
LANDING_HOVER_DESCEND,
LANDING_HOVER_TOUCHDOWN,
LANDING_END_HOVER,
#ifdef MAPBASE
// Strider dropoff
LANDING_STRIDER,
#endif
};
@ -295,6 +300,9 @@ private:
bool IsHovering();
void UpdateGroundRotorWashSound( float flAltitude );
void UpdateRotorWashVolume( CSoundPatch *pRotorSound, float flVolume, float flDeltaTime );
#ifdef MAPBASE
void DeathNotice(CBaseEntity* pVictim);
#endif
private:
// Timers
@ -353,6 +361,12 @@ private:
string_t m_sRollermineTemplate;
string_t m_sRollermineTemplateData;
#ifdef MAPBASE
// Template for strider carried by this dropship
string_t m_sStriderTemplate;
string_t m_sStriderTemplateData;
#endif
// Cached attachment points
int m_iMuzzleAttachment;
int m_iMachineGunBaseAttachment;
@ -827,6 +841,11 @@ BEGIN_DATADESC( CNPC_CombineDropship )
DEFINE_KEYFIELD( m_sRollermineTemplate, FIELD_STRING, "RollermineTemplate" ),
DEFINE_FIELD( m_sRollermineTemplateData, FIELD_STRING ),
#ifdef MAPBASE
DEFINE_KEYFIELD(m_sStriderTemplate, FIELD_STRING, "StriderTemplate"),
DEFINE_FIELD(m_sStriderTemplateData, FIELD_STRING),
#endif
DEFINE_ARRAY( m_sNPCTemplateData, FIELD_STRING, DROPSHIP_MAX_SOLDIERS ),
DEFINE_KEYFIELD( m_sNPCTemplate[0], FIELD_STRING, "NPCTemplate" ),
DEFINE_KEYFIELD( m_sNPCTemplate[1], FIELD_STRING, "NPCTemplate2" ),
@ -961,12 +980,60 @@ void CNPC_CombineDropship::Spawn( void )
break;
case CRATE_STRIDER:
#ifdef MAPBASE
if ( m_sStriderTemplate != NULL_STRING )
{
m_sStriderTemplateData = Templates_FindByTargetName( STRING( m_sStriderTemplate ) );
if (m_sStriderTemplateData == NULL_STRING)
{
Warning("npc_combinedropship %s: Strider Template %s not found!\n", STRING(GetEntityName()), STRING(m_sStriderTemplate));
break;
}
CAI_BaseNPC* pent = NULL;
CBaseEntity* pEntity = NULL;
MapEntity_ParseEntity( pEntity, STRING( m_sStriderTemplateData ), NULL);
if ( pEntity != NULL )
{
pent = ( CAI_BaseNPC* )pEntity;
}
if ( !FClassnameIs(pent, "npc_strider"))
{
Warning("npc_combinedropship %s: Strider Template %s is not a strider!\n", STRING(GetEntityName()), STRING(m_sStriderTemplate));
break;
}
m_OnSpawnNPC.Set( pEntity, pEntity, this );
pent->RemoveSpawnFlags( SF_NPC_TEMPLATE );
pent->SetOwnerEntity( this );
m_hContainer = pent;
m_hContainer->SetAbsOrigin( GetAbsOrigin() - Vector(0, 0, 100) );
m_hContainer->SetAbsAngles( GetAbsAngles() );
m_hContainer->SetParent( this, 0 );
m_hContainer->SetOwnerEntity( this );
DispatchSpawn(pent);
}
else
{
m_hContainer = ( CBaseAnimating* )CreateEntityByName("npc_strider");
m_hContainer->SetAbsOrigin( GetAbsOrigin() - Vector(0, 0, 100) );
m_hContainer->SetAbsAngles( GetAbsAngles() );
m_hContainer->SetParent( this, 0 );
m_hContainer->SetOwnerEntity( this );
m_hContainer->Spawn();
}
#else
m_hContainer = (CBaseAnimating*)CreateEntityByName( "npc_strider" );
m_hContainer->SetAbsOrigin( GetAbsOrigin() - Vector( 0, 0 , 100 ) );
m_hContainer->SetAbsAngles( GetAbsAngles() );
m_hContainer->SetParent(this, 0);
m_hContainer->SetOwnerEntity(this);
m_hContainer->Spawn();
#endif
m_hContainer->SetAbsOrigin( GetAbsOrigin() - Vector( 0, 0 , 100 ) );
#ifdef MAPBASE
m_OnSpawnNPC.Set( m_hContainer->GetBaseEntity(), m_hContainer, this);
@ -1204,13 +1271,24 @@ void CNPC_CombineDropship::Precache( void )
void CNPC_CombineDropship::Flight( void )
{
// Only run the flight model in some flight states
#ifdef MAPBASE
bool bRunFlight = ( GetLandingState() == LANDING_NO ||
GetLandingState() == LANDING_LEVEL_OUT ||
GetLandingState() == LANDING_LIFTOFF ||
GetLandingState() == LANDING_SWOOPING ||
GetLandingState() == LANDING_DESCEND ||
GetLandingState() == LANDING_HOVER_LEVEL_OUT ||
GetLandingState() == LANDING_HOVER_DESCEND );
GetLandingState() == LANDING_HOVER_DESCEND ||
GetLandingState() == LANDING_STRIDER );
#else
bool bRunFlight = (GetLandingState() == LANDING_NO ||
GetLandingState() == LANDING_LEVEL_OUT ||
GetLandingState() == LANDING_LIFTOFF ||
GetLandingState() == LANDING_SWOOPING ||
GetLandingState() == LANDING_DESCEND ||
GetLandingState() == LANDING_HOVER_LEVEL_OUT ||
GetLandingState() == LANDING_HOVER_DESCEND );
#endif
Vector forward, right, up;
GetVectors( &forward, &right, &up );
@ -1432,8 +1510,14 @@ void CNPC_CombineDropship::Flight( void )
}
// If we're landing, deliberately tuck in the back end
#ifdef MAPBASE
if ( GetLandingState() == LANDING_DESCEND || GetLandingState() == LANDING_TOUCHDOWN ||
GetLandingState() == LANDING_UNLOADING || GetLandingState() == LANDING_UNLOADED ||
GetLandingState() == LANDING_STRIDER || IsHovering() )
#else
if ( GetLandingState() == LANDING_DESCEND || GetLandingState() == LANDING_TOUCHDOWN ||
GetLandingState() == LANDING_UNLOADING || GetLandingState() == LANDING_UNLOADED || IsHovering() )
#endif
{
finspeed = -60;
}
@ -1782,6 +1866,16 @@ void CNPC_CombineDropship::InputDropStrider( inputdata_t &inputdata )
return;
}
#ifdef MAPBASE
if (m_iszLandTarget != NULL_STRING)
{
LandCommon();
}
else
{
SetLandingState(LANDING_STRIDER);
}
#else
QAngle angles = GetAbsAngles();
angles.x = 0.0;
@ -1793,6 +1887,7 @@ void CNPC_CombineDropship::InputDropStrider( inputdata_t &inputdata )
m_hContainer->SetAbsVelocity( vec3_origin );
m_hContainer = NULL;
#endif
}
//-----------------------------------------------------------------------------
@ -2025,7 +2120,11 @@ void CNPC_CombineDropship::SetLandingState( LandingState_t landingState )
if ( m_pDescendingWarningSound )
{
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
#ifdef MAPBASE
if ( ( landingState == LANDING_DESCEND ) || ( landingState == LANDING_TOUCHDOWN ) || ( landingState == LANDING_UNLOADING ) || ( landingState == LANDING_UNLOADED ) || ( landingState == LANDING_HOVER_DESCEND ) || ( landingState == LANDING_STRIDER ) )
#else
if ( ( landingState == LANDING_DESCEND ) || ( landingState == LANDING_TOUCHDOWN ) || ( landingState == LANDING_UNLOADING ) || ( landingState == LANDING_UNLOADED ) || ( landingState == LANDING_HOVER_DESCEND ) )
#endif
{
controller.SoundChangeVolume( m_pDescendingWarningSound, m_bSuppressSound ? 0.0f : 1.0f, 0.3f );
}
@ -2121,8 +2220,15 @@ void CNPC_CombineDropship::PrescheduleThink( void )
if ( flDistance < 70 && flSpeed < 100 )
{
m_flLandingSpeed = flSpeed;
if( IsHovering() )
#ifdef MAPBASE
if ( m_iCrateType == CRATE_STRIDER )
{
SetLandingState( LANDING_STRIDER );
}
else if( IsHovering() )
#else
if ( IsHovering() )
#endif
{
SetLandingState( LANDING_HOVER_DESCEND );
}
@ -2324,6 +2430,98 @@ void CNPC_CombineDropship::PrescheduleThink( void )
}
break;
#ifdef MAPBASE
case LANDING_STRIDER:
{
if (!m_hContainer)
{
// Strider died, get out of here
SetLandingState(LANDING_LIFTOFF);
return;
}
// Orient myself to the desired direction
bool bStillOrienting = false;
Vector targetDir;
if (m_hLandTarget)
{
// We've got a land target, so match it's orientation
AngleVectors(m_hLandTarget->GetAbsAngles(), &targetDir);
}
else
{
// No land target.
targetDir = GetDesiredPosition() - GetAbsOrigin();
}
// Don't unload until we're facing the way the dropoff point specifies
float flTargetYaw = UTIL_VecToYaw(targetDir);
float flDeltaYaw = UTIL_AngleDiff(flTargetYaw, GetAbsAngles().y);
if (fabs(flDeltaYaw) > 5)
{
bStillOrienting = true;
}
// Ensure we land on the drop point. Stop dropping if we're still turning.
Vector vecToTarget = (GetDesiredPosition() - GetAbsOrigin());
float flDistance = vecToTarget.Length();
float flRampedSpeed = m_flLandingSpeed * (flDistance / 70);
Vector vecVelocity = (flRampedSpeed / flDistance) * vecToTarget;
#define STRIDER_LANDING_HEIGHT 540.0f
float flFactor = MIN(1.0, MAX(0.1f, (flAltitude - STRIDER_LANDING_HEIGHT) / flAltitude));
float flDescendVelocity = MIN(-75, MAX_LAND_VEL * flFactor);
vecVelocity.z = flDescendVelocity;
SetAbsVelocity(vecVelocity);
if (flAltitude < 600)
{
QAngle angles = GetLocalAngles();
// Level out quickly.
angles.x = UTIL_Approach(0.0, angles.x, 0.2);
angles.z = UTIL_Approach(0.0, angles.z, 0.2);
SetLocalAngles(angles);
}
else
{
// randomly move as if buffeted by ground effects
// gently flatten ship from starting pitch/yaw
m_existPitch = UTIL_Approach(0.0, m_existPitch, 1);
m_existRoll = UTIL_Approach(0.0, m_existRoll, 1);
QAngle angles = GetLocalAngles();
angles.x = m_existPitch + (sin(gpGlobals->curtime * 3.5f) * DROPSHIP_MAX_LAND_TILT);
angles.z = m_existRoll + (sin(gpGlobals->curtime * 3.75f) * DROPSHIP_MAX_LAND_TILT);
SetLocalAngles(angles);
}
DoRotorWash();
if (!bStillOrienting && flAltitude < STRIDER_LANDING_HEIGHT)
{
QAngle angles = GetAbsAngles();
m_hContainer->SetParent(NULL, 0);
m_hContainer->SetOwnerEntity(NULL);
m_hContainer->SetAbsAngles(angles);
m_hContainer->SetAbsVelocity(vec3_origin);
m_hContainer = NULL;
m_flTimeTakeOff = gpGlobals->curtime + 3.5f;
SetLandingState(LANDING_UNLOADING);
return;
}
}
break;
#endif
case LANDING_UNLOADING:
{
// If we've got no specified takeoff time, we're still waiting for troops to exit. Idle.
@ -2826,7 +3024,11 @@ void CNPC_CombineDropship::UpdatePickupNavigation( void )
void CNPC_CombineDropship::UpdateLandTargetNavigation( void )
{
Vector vecPickup = m_hLandTarget->WorldSpaceCenter();
#ifdef MAPBASE
vecPickup.z += ( m_iCrateType == CRATE_STRIDER ) ? 732 : 256;
#else
vecPickup.z += 256;
#endif
SetDesiredPosition( vecPickup );
//NDebugOverlay::Cross3D( GetDesiredPosition(), -Vector(32,32,32), Vector(32,32,32), 0, 255, 255, true, 0.1f );
@ -2859,7 +3061,11 @@ void CNPC_CombineDropship::Hunt( void )
// Face our desired position.
m_vecDesiredFaceDir = desiredDir;
#ifdef MAPBASE
if ( GetLandingState() == LANDING_DESCEND || GetLandingState() == LANDING_LEVEL_OUT || GetLandingState() == LANDING_STRIDER || IsHovering() )
#else
if ( GetLandingState() == LANDING_DESCEND || GetLandingState() == LANDING_LEVEL_OUT || IsHovering() )
#endif
{
if ( m_hLandTarget )
{
@ -3159,6 +3365,19 @@ void CNPC_CombineDropship::MakeTracer( const Vector &vecTracerSrc, const trace_t
}
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose: Need a way to tell if our strider died
//-----------------------------------------------------------------------------
void CNPC_CombineDropship::DeathNotice( CBaseEntity *pVictim )
{
if ( m_iCrateType == CRATE_STRIDER && GetLandingState() == LANDING_NO )
{
m_OnContainerShotDownBeforeDropoff.Set( 1, m_hContainer, this );
}
}
#endif
AI_BEGIN_CUSTOM_NPC( npc_combinedropship, CNPC_CombineDropship )
DECLARE_ACTIVITY( ACT_DROPSHIP_FLY_IDLE );

View File

@ -187,15 +187,29 @@ public:
{
return m_bDisabled;
}
#ifdef MAPBASE
void GunshipCrashedOnTarget( CBaseHelicopter *pGunship )
{
m_OnCrashed.FireOutput( pGunship, this );
}
void GunshipAcquiredCrashTarget( CBaseHelicopter *pGunship )
{
m_OnBeginCrash.FireOutput( pGunship, this );
}
#else
void GunshipCrashedOnTarget( void )
{
m_OnCrashed.FireOutput( this, this );
}
#endif
private:
bool m_bDisabled;
COutputEvent m_OnCrashed;
#ifdef MAPBASE
COutputEvent m_OnBeginCrash;
#endif
};
LINK_ENTITY_TO_CLASS( info_target_gunshipcrash, CTargetGunshipCrash );
@ -209,6 +223,9 @@ BEGIN_DATADESC( CTargetGunshipCrash )
// Outputs
DEFINE_OUTPUT( m_OnCrashed, "OnCrashed" ),
#ifdef MAPBASE
DEFINE_OUTPUT( m_OnBeginCrash, "OnBeginCrash" ),
#endif
END_DATADESC()
@ -1564,7 +1581,11 @@ void CNPC_CombineGunship::PrescheduleThink( void )
{
BeginDestruct();
m_OnCrashed.FireOutput( this, this );
#ifdef MAPBASE
m_hCrashTarget->GunshipCrashedOnTarget( this );
#else
m_hCrashTarget->GunshipCrashedOnTarget();
#endif
return;
}
}
@ -1976,6 +1997,10 @@ bool CNPC_CombineGunship::FindNearestGunshipCrash( void )
m_flNextGunshipCrashFind = gpGlobals->curtime + 0.5;
m_flEndDestructTime = 0;
#ifdef MAPBASE
m_hCrashTarget->GunshipAcquiredCrashTarget( this );
#endif
if ( g_debug_gunship.GetInt() )
{
NDebugOverlay::Line(GetAbsOrigin(), m_hCrashTarget->GetAbsOrigin(), 0,255,0, true, 0.5);

View File

@ -1327,6 +1327,15 @@ void CNPC_Strider::BuildScheduleTestBits()
//---------------------------------------------------------
int CNPC_Strider::SelectSchedule()
{
#ifdef MAPBASE
if( GetMoveType() == MOVETYPE_NONE )
{
// Dropship just released me.
AddFlag(FL_FLY);
SetMoveType( MOVETYPE_STEP );
return SCHED_STRIDER_FALL_TO_GROUND;
}
#endif
/*
if( GetMoveType() != MOVETYPE_FLY )
{
@ -1511,6 +1520,26 @@ int CNPC_Strider::TranslateSchedule( int scheduleType )
return SCHED_COMBAT_PATROL;
}
}
else
{
#ifdef MAPBASE
extern ConVar ai_enemy_memory_fixes;
// Striders convert TASK_GET_PATH_TO_ENEMY_LOS to TASK_GET_PATH_TO_ENEMY_LKP_LOS, a task which incorrectly
// acts identically to the former. This is detailed in CAI_BaseNPC::StartTask and fixed by ai_enemy_memory_fixes.
//
// However, SCHED_ESTABLISH_LINE_OF_FIRE only stops being used once the NPC has LOS to its target.
// Since the fixed task now uses the enemy's last known position instead of the enemy's actual position,
// this schedule risks getting stuck in a loop.
//
// This code chains back up to SCHED_ESTABLISH_LINE_OF_FIRE_FALLBACK, which is what's supposed to happen when a
// strider is eluded in this way.
if ( ai_enemy_memory_fixes.GetBool() && FVisible( GetEnemyLKP() ) )
{
return TranslateSchedule( SCHED_ESTABLISH_LINE_OF_FIRE_FALLBACK );
}
#endif
}
break;
@ -5818,6 +5847,18 @@ AI_BEGIN_CUSTOM_NPC( npc_strider, CNPC_Strider )
" COND_STRIDER_SHOULD_STAND"
)
#ifdef MAPBASE
DEFINE_SCHEDULE
(
SCHED_STRIDER_FALL_TO_GROUND,
" Tasks "
" TASK_SOUND_WAKE 0"
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_STRIDER_DEPLOY"
""
" Interrupts "
)
#else
DEFINE_SCHEDULE
(
SCHED_STRIDER_FALL_TO_GROUND,
@ -5827,7 +5868,7 @@ AI_BEGIN_CUSTOM_NPC( npc_strider, CNPC_Strider )
""
" Interrupts "
)
#endif
AI_END_CUSTOM_NPC()

View File

@ -188,6 +188,7 @@ public:
bool HasPass() { return m_PlayerFreePass.HasPass(); }
bool FVisible( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL );
bool FVisible( const Vector &vecTarget, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL ) { return BaseClass::FVisible( vecTarget, traceMask, ppBlocker ); }
Vector BodyTarget( const Vector &posSrc, bool bNoisy );
bool IsValidEnemy( CBaseEntity *pTarget );

View File

@ -2645,16 +2645,19 @@ Vector CProtoSniper::DesiredBodyTarget( CBaseEntity *pTarget )
{
// By default, aim for the center
Vector vecTarget = pTarget->WorldSpaceCenter();
const Vector vecBulletOrigin = GetBulletOrigin();
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_GetActualShootPosition.CanRunInScope(m_ScriptScope))
if ( m_ScriptScope.IsInitialized() && g_Hook_GetActualShootPosition.CanRunInScope( m_ScriptScope ) )
{
ScriptVariant_t functionReturn;
ScriptVariant_t args[] = { GetBulletOrigin(), ToHScript( pTarget ) };
if (g_Hook_GetActualShootPosition.Call( m_ScriptScope, &functionReturn, args ))
ScriptVariant_t args[] = { vecBulletOrigin, ToHScript( pTarget ) };
if ( g_Hook_GetActualShootPosition.Call( m_ScriptScope, &functionReturn, args ) )
{
if (functionReturn.m_type == FIELD_VECTOR && functionReturn.m_pVector->LengthSqr() != 0.0f)
if ( functionReturn.m_type == FIELD_VECTOR && functionReturn.m_pVector->LengthSqr() != 0.0f )
{
return *functionReturn.m_pVector;
}
}
}
#endif
@ -2682,12 +2685,12 @@ Vector CProtoSniper::DesiredBodyTarget( CBaseEntity *pTarget )
{
if( flTimeSinceLastMiss > 0.0f && flTimeSinceLastMiss < 4.0f && hl2_episodic.GetBool() )
{
vecTarget = pTarget->BodyTarget( GetBulletOrigin(), false );
vecTarget = pTarget->BodyTarget( vecBulletOrigin, false );
}
else
{
// Shoot zombies in the headcrab
vecTarget = pTarget->HeadTarget( GetBulletOrigin() );
vecTarget = pTarget->HeadTarget( vecBulletOrigin );
}
}
else if( pTarget->Classify() == CLASS_ANTLION )

View File

@ -561,9 +561,9 @@ Vector CPlayerViewProxy::EyePosition( void )
Vector vecOrigin;
QAngle angAngles;
float fldummy;
m_hPlayer->CalcView( vecOrigin, angAngles, fldummy, fldummy, fldummy );
m_hPlayer.Get()->CalcView(vecOrigin, angAngles, fldummy, fldummy, fldummy);
return GetAbsOrigin() + (vecOrigin - m_hPlayer->GetAbsOrigin());
return GetAbsOrigin() + (vecOrigin - m_hPlayer.Get()->GetAbsOrigin());
}
else
return BaseClass::EyePosition();
@ -579,9 +579,9 @@ const QAngle &CPlayerViewProxy::EyeAngles( void )
Vector vecOrigin;
static QAngle angAngles;
float fldummy;
m_hPlayer->CalcView( vecOrigin, angAngles, fldummy, fldummy, fldummy );
m_hPlayer.Get()->CalcView( vecOrigin, angAngles, fldummy, fldummy, fldummy );
angAngles = GetAbsAngles() + (angAngles - m_hPlayer->GetAbsAngles());
angAngles = GetAbsAngles() + (angAngles - m_hPlayer.Get()->GetAbsAngles());
return angAngles;
//return m_hPlayer.Get()->EyeAngles();
@ -596,7 +596,7 @@ const QAngle &CPlayerViewProxy::EyeAngles( void )
const QAngle &CPlayerViewProxy::LocalEyeAngles( void )
{
if (m_hPlayer.Get()) {
static QAngle angAngles = GetAbsAngles() + (m_hPlayer->LocalEyeAngles() - m_hPlayer->GetAbsAngles());
static QAngle angAngles = GetAbsAngles() + (m_hPlayer.Get()->LocalEyeAngles() - m_hPlayer.Get()->GetAbsAngles());
return angAngles;
} else
return BaseClass::LocalEyeAngles();

View File

@ -0,0 +1,985 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: VCD-based sentences.
//
//=============================================================================//
#include "cbase.h"
#include "utlhashtable.h"
#include "engine/IEngineSound.h"
#include "soundchars.h"
#include "choreosentence.h"
#include "mapbase/matchers.h"
#include "ai_speech_new.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//----------------------------------------------------------------------------
#define IsVirtualWord(pszWord) (pszWord[0] == 'V' && pszWord[1] == '_')
#define RemoveVirtualPrefix(pszWord) IsVirtualWord(pszWord) ? pszWord + 2 : pszWord
//----------------------------------------------------------------------------
class CChoreoSentenceSystem : public CAutoGameSystem
{
public:
struct VirtualWord_t;
struct VirtualWordDefinition_t;
struct VirtualWordDefinitionList_t;
struct SentenceContextPrerequisite_t;
//----------------------------------------------------------------------------
CChoreoSentenceSystem() : m_Strings( 256, 0, &StringLessThan ) { }
~CChoreoSentenceSystem() { PurgeSentences(); }
bool Init()
{
// Load the default choreo sentences files
LoadChoreoSentenceManifest( "scripts/choreosentences_manifest.txt" );
return true;
}
void Shutdown()
{
PurgeSentences();
}
//----------------------------------------------------------------------------
void LoadChoreoSentenceManifest( const char *pszFile )
{
KeyValues *pManifest = new KeyValues( "ChoreoSentencesManifest" );
if (pManifest->LoadFromFile( filesystem, pszFile ))
{
FOR_EACH_SUBKEY( pManifest, pSubKey )
{
LoadChoreoSentenceFile( pSubKey->GetString() );
}
}
pManifest->deleteThis();
//m_Strings.Compact( true );
}
void LoadChoreoSentenceFile( const char *pszFile )
{
KeyValues *pManifest = new KeyValues( "ChoreoSentences" );
if (pManifest->LoadFromFile( filesystem, pszFile ))
{
//----------------------------------------------------------------------------
// Context Prerequisites
//----------------------------------------------------------------------------
KeyValues *pSentenceContextPrerequisites = pManifest->FindKey( "SentenceContextPrerequisites" );
FOR_EACH_SUBKEY( pSentenceContextPrerequisites, pSubKey )
{
KeyValues *pCondition = pSubKey->GetFirstSubKey();
if (pCondition)
{
//
// Only add if there is a subkey for condition
//
int i = m_ContextPrerequisites.AddToTail();
m_ContextPrerequisites[i].nConditionType = DeduceCondition( pCondition->GetName(), V_strlen( pCondition->GetName() ) );
m_ContextPrerequisites[i].pszContextName = AllocateString( pSubKey->GetName() );
m_ContextPrerequisites[i].pszCondition = AllocateString( pCondition->GetString() );
}
}
//----------------------------------------------------------------------------
// Virtual Words
//----------------------------------------------------------------------------
KeyValues *pVirtualWords = pManifest->FindKey( "VirtualWords" );
FOR_EACH_SUBKEY( pVirtualWords, pSubKey )
{
//
// Determine if this has nested virtual words or not
//
KeyValues *pMatches = pSubKey->FindKey( "matches" );
if (pMatches)
{
//
// It has nested virtual words
//
int i = m_VirtualWordLists.Insert( RemoveVirtualPrefix( pSubKey->GetName() ) );
m_VirtualWordLists[i].pszCriterion = AllocateString( pSubKey->GetString( "criterion" ) );
bool bFirst = true;
FOR_EACH_SUBKEY( pMatches, pWordList )
{
if (bFirst)
{
// First one is always default
if (LoadVirtualWordDefinitionFromKV( pWordList, m_VirtualWordLists[i].m_DefaultWordDefinition, pSubKey->GetName() ))
bFirst = false;
}
else
{
int i2 = m_VirtualWordLists[i].m_WordDefinitions.Insert( pWordList->GetName() );
if (!LoadVirtualWordDefinitionFromKV( pWordList, m_VirtualWordLists[i].m_WordDefinitions[i2], pSubKey->GetName() ))
m_VirtualWordLists[i].m_WordDefinitions.RemoveAt( i2 );
}
}
}
else
{
//
// It is a virtual word
//
int i = m_VirtualWords.Insert( RemoveVirtualPrefix( pSubKey->GetName() ) );
if (!LoadVirtualWordDefinitionFromKV( pSubKey, m_VirtualWords[i], pSubKey->GetName() ))
m_VirtualWords.RemoveAt( i );
}
}
//----------------------------------------------------------------------------
// Sentences
//----------------------------------------------------------------------------
KeyValues *pSentences = pManifest->FindKey( "Sentences" );
FOR_EACH_SUBKEY( pSentences, pSetKey )
{
FOR_EACH_SUBKEY( pSetKey, pSubKey )
{
if (pSubKey->GetName() && *pSubKey->GetString())
{
int i = m_Sentences.Insert( pSubKey->GetName() );
char szPathName[MAX_PATH];
V_strncpy( szPathName, pSetKey->GetName(), sizeof( szPathName ) );
V_FixSlashes( szPathName );
m_Sentences[i].pszPrefix = AllocateString( szPathName );
//m_Sentences[i].pszPrefix = AllocateString( pSetKey->GetName() );
ParseChoreoSentence( NULL, pSubKey->GetString(), m_Sentences[i] );
m_Sentences[i].pszName = m_Sentences.GetElementName( i );
}
}
}
}
pManifest->deleteThis();
}
bool LoadVirtualWordDefinitionFromKV( KeyValues *pWordList, VirtualWordDefinition_t &wordDefinition, const char *pszWordName )
{
//
// Get the condition for this virtual word
//
KeyValues *pCondition = pWordList->FindKey( "condition" );
if (pCondition)
{
int nStrLen = V_strlen( pCondition->GetString() );
const char *pColon = V_strnchr( pCondition->GetString(), ':', nStrLen );
if (pColon)
{
wordDefinition.pszCondition = AllocateString( pColon + 1 );
wordDefinition.nConditionType = DeduceCondition( pCondition->GetString(), pColon - pCondition->GetString() );
}
else
{
wordDefinition.nConditionType = DeduceCondition( pCondition->GetString(), nStrLen );
}
}
else
{
wordDefinition.nConditionType = 0;
wordDefinition.pszCondition = "";
}
//
// Get the words themselves
//
KeyValues *pWords = pWordList->FindKey( "words" );
if (pWords)
{
FOR_EACH_SUBKEY( pWords, pWord )
{
int i = wordDefinition.m_Words.AddToTail();
V_strncpy( wordDefinition.m_Words[i].szWord, pWord->GetString(), sizeof( wordDefinition.m_Words[i].szWord ) );
}
}
else
{
pWords = pWordList->FindKey( "words_from" );
if (pWords)
{
const char *pszWordsTarget = RemoveVirtualPrefix( pWords->GetString() );
//
// Get from another virtual word
//
FOR_EACH_DICT_FAST( m_VirtualWords, i )
{
if (FStrEq( m_VirtualWords.GetElementName( i ), pszWordsTarget ))
{
wordDefinition.m_Words.AddVectorToTail( m_VirtualWords[i].m_Words );
}
}
}
}
if (wordDefinition.m_Words.Count() <= 0)
{
Warning( "WARNING: No words found on virtual word %s\n", pszWordName );
return false;
}
return true;
}
//----------------------------------------------------------------------------
void PurgeSentences()
{
m_ContextPrerequisites.RemoveAll();
m_VirtualWordLists.RemoveAll();
m_VirtualWords.RemoveAll();
m_Sentences.RemoveAll();
for (unsigned int i = 0; i < m_Strings.Count(); i++)
{
delete m_Strings[i];
}
m_Strings.Purge();
}
//----------------------------------------------------------------------------
void PrintEverything()
{
Msg( "CONTEXT PREREQUISITES\n\n" );
FOR_EACH_VEC( m_ContextPrerequisites, i )
{
Msg( "\t\"%s\"\n\t\tCondition: %i (\"%s\")\n\n", m_ContextPrerequisites[i].pszContextName, m_ContextPrerequisites[i].nConditionType, m_ContextPrerequisites[i].pszCondition );
}
Msg( "VIRTUAL WORDS\n\n" );
FOR_EACH_DICT_FAST( m_VirtualWords, i )
{
char szWords[256] = { 0 };
FOR_EACH_VEC( m_VirtualWords[i].m_Words, nWord )
{
V_strncat( szWords, "\t\t\t\"", sizeof( szWords ) );
V_strncat( szWords, m_VirtualWords[i].m_Words[nWord].szWord, sizeof( szWords ) );
V_strncat( szWords, "\"\n", sizeof( szWords ) );
}
Msg( "\t\"%s\"\n\t\tCondition: %i (\"%s\")\n\t\tWords:\n%s\n\n", m_VirtualWords.GetElementName( i ), m_VirtualWords[i].nConditionType, m_VirtualWords[i].pszCondition, szWords );
}
Msg( "SENTENCES\n\n" );
FOR_EACH_DICT_FAST( m_Sentences, i )
{
char szWords[128] = { 0 };
FOR_EACH_VEC( m_Sentences[i].m_Words, nWord )
{
if (m_Sentences[i].m_Words[nWord].nPitch != 100)
{
V_snprintf( szWords, sizeof( szWords ), "%s(p%i) ", szWords, m_Sentences[i].m_Words[nWord].nPitch );
}
if (m_Sentences[i].m_Words[nWord].nVol != 100)
{
V_snprintf( szWords, sizeof( szWords ), "%s(v%i) ", szWords, m_Sentences[i].m_Words[nWord].nVol );
}
V_strncat( szWords, "\"", sizeof( szWords ) );
V_strncat( szWords, m_Sentences[i].m_Words[nWord].pszWord, sizeof( szWords ) );
V_strncat( szWords, "\" ", sizeof( szWords ) );
}
Msg( "\t\"%s\"\n\t\tPrefix: \"%s\"\n\t\tCaption: \"%s\"\n\t\tWords: %s\n\n", m_Sentences.GetElementName( i ), m_Sentences[i].pszPrefix, m_Sentences[i].pszCaption ? m_Sentences[i].pszCaption : "N/A", szWords);
}
}
void PrintStrings()
{
CUtlVector<const char*> strings( 0, m_Strings.Count() );
for ( UtlHashHandle_t i = m_Strings.FirstInorder(); i != m_Strings.InvalidIndex(); i = m_Strings.NextInorder(i) )
{
strings.AddToTail( m_Strings[i] );
}
struct _Local {
static int __cdecl F(const char * const *a, const char * const *b) { return strcmp(*a, *b); }
};
strings.Sort( _Local::F );
for ( int i = 0; i < strings.Count(); ++i )
{
Msg( " %d (0x%p) : %s\n", i, strings[i], strings[i] );
}
Msg( "\n" );
Msg( "Size: %d items\n", strings.Count() );
}
//----------------------------------------------------------------------------
const ChoreoSentence_t *LookupChoreoSentence( CBaseEntity *pSpeaker, const char *pszSentenceName )
{
FOR_EACH_DICT_FAST( m_Sentences, i )
{
if (FStrEq( m_Sentences.GetElementName( i ), pszSentenceName ))
{
return &m_Sentences[i];
}
}
return NULL;
}
//----------------------------------------------------------------------------
void PrecacheVirtualWord( const char *pszWord, const char *pszPrefix, const char *pszSuffix )
{
pszWord = RemoveVirtualPrefix( pszWord );
FOR_EACH_DICT_FAST( m_VirtualWords, i )
{
if (FStrEq( m_VirtualWords.GetElementName( i ), pszWord ))
{
// Precache all words
FOR_EACH_VEC( m_VirtualWords[i].m_Words, i2 )
{
CBaseEntity::PrecacheScriptSound( UTIL_VarArgs( "%s%s%s", pszPrefix, m_VirtualWords[i].m_Words[i2].szWord, pszSuffix ) );
}
return;
}
}
FOR_EACH_DICT_FAST( m_VirtualWordLists, i )
{
if (FStrEq( m_VirtualWordLists.GetElementName( i ), pszWord ))
{
// Precache all words in default definition
FOR_EACH_VEC( m_VirtualWordLists[i].m_DefaultWordDefinition.m_Words, i2 )
{
CBaseEntity::PrecacheScriptSound( UTIL_VarArgs( "%s%s%s", pszPrefix, m_VirtualWordLists[i].m_DefaultWordDefinition.m_Words[i2].szWord, pszSuffix));
}
// Precache all words in nested definitions
FOR_EACH_DICT( m_VirtualWordLists[i].m_WordDefinitions, i2 )
{
FOR_EACH_VEC( m_VirtualWordLists[i].m_WordDefinitions[i2].m_Words, i3 )
{
CBaseEntity::PrecacheScriptSound( UTIL_VarArgs( "%s%s%s", pszPrefix, m_VirtualWordLists[i].m_WordDefinitions[i2].m_Words[i3].szWord, pszSuffix ) );
}
}
return;
}
}
}
inline const VirtualWord_t &ResolveVirtualWordDefinition( CBaseEntity *pSpeaker, const VirtualWordDefinition_t &wordDefinition )
{
// Resolve this condition
int nIndex = ResolveVirtualCondition( pSpeaker, wordDefinition.nConditionType, wordDefinition.pszCondition, wordDefinition.m_Words.Count() );
Assert( nIndex >= 0 && nIndex < wordDefinition.m_Words.Count() );
return wordDefinition.m_Words[nIndex];
}
const char *ResolveVirtualWord( CBaseEntity *pSpeaker, const char *pszWord )
{
pszWord = RemoveVirtualPrefix( pszWord );
FOR_EACH_DICT_FAST( m_VirtualWords, i )
{
if (FStrEq( m_VirtualWords.GetElementName( i ), pszWord ))
{
return ResolveVirtualWordDefinition( pSpeaker, m_VirtualWords[i] ).szWord;
}
}
FOR_EACH_DICT_FAST( m_VirtualWordLists, i )
{
if (FStrEq( m_VirtualWordLists.GetElementName( i ), pszWord ))
{
AI_CriteriaSet set;
if (pSpeaker)
pSpeaker->ModifyOrAppendCriteria( set );
int nCriterion = set.FindCriterionIndex( m_VirtualWordLists[i].pszCriterion );
if (set.IsValidIndex( nCriterion ))
{
const char *pszValue = set.GetValue( nCriterion );
// Find the set of virtual words that matches the criterion
FOR_EACH_DICT( m_VirtualWordLists[i].m_WordDefinitions, i2 )
{
if (Matcher_Match( m_VirtualWordLists[i].m_WordDefinitions.GetElementName(i2), pszValue ))
{
return ResolveVirtualWordDefinition( pSpeaker, m_VirtualWordLists[i].m_WordDefinitions[i2] ).szWord;
}
}
// Return the default
return ResolveVirtualWordDefinition( pSpeaker, m_VirtualWordLists[i].m_DefaultWordDefinition ).szWord;
}
}
}
CGWarning( 0, CON_GROUP_CHOREO, "Choreo sentence can't find virtual word \"%s\"\n", pszWord );
return "";
}
int ResolveVirtualCondition( CBaseEntity *pSpeaker, int nConditionType, const char *pszCondition, int nWordCount )
{
switch (nConditionType)
{
default:
case ConditionType_None:
// Randomize between each word
return RandomInt( 0, nWordCount - 1 );
break;
case ConditionType_Context:
// Return context as integer
if (pSpeaker)
{
if (pSpeaker->FindContextByName( pszCondition ) == -1)
{
// Check if this is a prerequisite, and if it is, then apply it
FOR_EACH_VEC( m_ContextPrerequisites, i )
{
if (FStrEq( m_ContextPrerequisites[i].pszContextName, pszCondition ))
{
pSpeaker->AddContext( pszCondition, UTIL_VarArgs("%i", ResolveVirtualCondition(pSpeaker, m_ContextPrerequisites[i].nConditionType, m_ContextPrerequisites[i].pszCondition, nWordCount)));
}
}
}
int nContext = clamp( atoi( pSpeaker->GetContextValue( pszCondition ) ), 0, nWordCount - 1 );
return nContext;
}
break;
case ConditionType_VScript:
{
// Return VScript code result
g_pScriptVM->SetValue( "_choreo_val", "" );
HSCRIPT hCreateChainScript = g_pScriptVM->CompileScript( UTIL_VarArgs( "_choreo_val = (%s)", pszCondition ) );
g_pScriptVM->Run( hCreateChainScript, pSpeaker ? pSpeaker->GetOrCreatePrivateScriptScope() : NULL );
ScriptVariant_t scriptVar;
g_pScriptVM->GetValue( "_choreo_val", &scriptVar );
g_pScriptVM->ClearValue( "_choreo_val" );
if (scriptVar.m_int < 0 || scriptVar.m_int >= nWordCount)
{
Warning("Choreo sentence script var %i from '%s' out of range\n", scriptVar.m_int, pszCondition );
scriptVar.m_int = 0;
}
return scriptVar.m_int;
}
break;
case ConditionType_DistTo:
case ConditionType_DirTo:
case ConditionType_GridX:
case ConditionType_GridY:
{
CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, pszCondition, pSpeaker );
if (pSpeaker && pTarget)
{
if (nConditionType == ConditionType_DistTo)
{
// Convert to meters
return (pSpeaker->GetAbsOrigin() - pTarget->GetAbsOrigin()).Length() / 52.49344;
}
else if (nConditionType == ConditionType_DirTo)
{
Vector vecDir = (pSpeaker->GetAbsOrigin() - pTarget->GetAbsOrigin());
return vecDir.y;
}
else if (nConditionType == ConditionType_GridX)
{
return (int)(pTarget->GetAbsOrigin().x / 10.0f) % 20;
}
else if (nConditionType == ConditionType_GridY)
{
return (int)(pTarget->GetAbsOrigin().y / 10.0f) % 20;
}
}
}
break;
}
return 0;
}
//----------------------------------------------------------------------------
enum
{
ConditionType_None, // No condition (random)
ConditionType_Context, // Word should match value from specified context
ConditionType_VScript, // Word should match value returned from specified VScript code
// HL2 sentence parameters
ConditionType_DistTo, // Word should match distance to specified target
ConditionType_DirTo, // Word should match direction to specified target
ConditionType_GridX, // Word should match world X axis grid of specified target
ConditionType_GridY, // Word should match world Y axis grid of specified target
};
int DeduceCondition( const char *pszCondition, int nNumChars )
{
if (V_strncmp( pszCondition, "context", nNumChars ) == 0)
return ConditionType_Context;
else if (V_strncmp( pszCondition, "vscript", nNumChars ) == 0)
return ConditionType_VScript;
else if (V_strncmp( pszCondition, "dist_to", nNumChars ) == 0)
return ConditionType_DistTo;
else if (V_strncmp( pszCondition, "dir_to", nNumChars ) == 0)
return ConditionType_DirTo;
else if (V_strncmp( pszCondition, "gridx", nNumChars ) == 0)
return ConditionType_GridX;
else if (V_strncmp( pszCondition, "gridy", nNumChars ) == 0)
return ConditionType_GridY;
return ConditionType_None;
}
struct VirtualWord_t
{
char szWord[MAX_CHOREO_SENTENCE_VIRTUAL_WORD_LEN];
};
struct VirtualWordDefinition_t
{
VirtualWordDefinition_t() {}
VirtualWordDefinition_t( const VirtualWordDefinition_t &src )
{
pszCondition = src.pszCondition;
nConditionType = src.nConditionType;
m_Words.RemoveAll();
m_Words.AddVectorToTail( src.m_Words );
}
const char *pszCondition;
int nConditionType;
CUtlVector< VirtualWord_t > m_Words;
};
struct VirtualWordDefinitionList_t
{
VirtualWordDefinitionList_t() {}
VirtualWordDefinitionList_t( const VirtualWordDefinitionList_t &src )
{
pszCriterion = src.pszCriterion;
m_DefaultWordDefinition = src.m_DefaultWordDefinition;
m_WordDefinitions.RemoveAll();
m_WordDefinitions.EnsureCapacity( src.m_WordDefinitions.Count() );
FOR_EACH_DICT_FAST( src.m_WordDefinitions, i )
m_WordDefinitions.Insert( src.m_WordDefinitions.GetElementName( i ), src.m_WordDefinitions[i] );
}
const char *pszCriterion;
VirtualWordDefinition_t m_DefaultWordDefinition;
CUtlDict< VirtualWordDefinition_t, int > m_WordDefinitions;
};
struct SentenceContextPrerequisite_t
{
SentenceContextPrerequisite_t() {}
SentenceContextPrerequisite_t( const SentenceContextPrerequisite_t &src )
{
pszContextName = src.pszContextName;
pszCondition = src.pszCondition;
nConditionType = src.nConditionType;
}
const char *pszContextName;
const char *pszCondition;
int nConditionType;
};
//----------------------------------------------------------------------------
const char *FindString( const char *string )
{
unsigned short i = m_Strings.Find( string );
return i == m_Strings.InvalidIndex() ? NULL : m_Strings[i];
}
const char *AllocateString( const char *string )
{
int i = m_Strings.Find( string );
if ( i != m_Strings.InvalidIndex() )
{
return m_Strings[i];
}
int len = Q_strlen( string );
char *out = new char[ len + 1 ];
Q_memcpy( out, string, len );
out[ len ] = 0;
return m_Strings[ m_Strings.Insert( out ) ];
}
//----------------------------------------------------------------------------
const CUtlDict< ChoreoSentence_t, int > &GetSentences() { return m_Sentences; }
private:
// Context prerequisites to be applied if not already applied
CUtlVector< SentenceContextPrerequisite_t > m_ContextPrerequisites;
// Embedded lists of virtual words based on a criterion
CUtlDict< VirtualWordDefinitionList_t, int > m_VirtualWordLists;
// Lists of virtual words (does not include nested ones)
CUtlDict< VirtualWordDefinition_t, int > m_VirtualWords;
// Sentences themselves
CUtlDict< ChoreoSentence_t, int > m_Sentences;
// Dedicated strings, copied from game string pool
CUtlRBTree<const char *> m_Strings;
};
static CChoreoSentenceSystem g_ChoreoSentenceSystem;
//-----------------------------------------------------------------------------
CON_COMMAND( choreosentence_reload, "Reloads choreo sentences" )
{
//int nStringCount = g_ChoreoSentenceSystem.GetStringCount();
g_ChoreoSentenceSystem.PurgeSentences();
//g_ChoreoSentenceSystem.ReserveStrings( nStringCount );
g_ChoreoSentenceSystem.LoadChoreoSentenceManifest( "scripts/choreosentences_manifest.txt" );
}
CON_COMMAND( choreosentence_dump, "Prints all choreo sentence stuff" )
{
g_ChoreoSentenceSystem.PrintEverything();
}
CON_COMMAND( choreosentence_dump_strings, "Prints all strings allocated by choreo sentences" )
{
g_ChoreoSentenceSystem.PrintStrings();
}
//------------------------------------------------------------------------------
// Purpose :
//------------------------------------------------------------------------------
static int AutoCompleteChoreoSentences(const char *cmdname, CUtlVector< CUtlString > &commands, CUtlRBTree< CUtlString > &symbols, char *substring, int checklen = 0)
{
FOR_EACH_DICT_FAST( g_ChoreoSentenceSystem.GetSentences(), i )
{
CUtlString sym = g_ChoreoSentenceSystem.GetSentences().GetElementName( i );
if (Q_strnicmp( sym, substring, checklen ) != 0)
continue;
int idx = symbols.Find( sym );
if (idx == symbols.InvalidIndex())
{
symbols.Insert( sym );
}
// Too many
if (symbols.Count() >= COMMAND_COMPLETION_MAXITEMS)
break;
}
// Now fill in the results
for (int i = symbols.FirstInorder(); i != symbols.InvalidIndex(); i = symbols.NextInorder(i))
{
const char *name = symbols[i].String();
char buf[512];
Q_snprintf( buf, sizeof( buf ), "%s %s", cmdname, name );
Q_strlower( buf );
CUtlString command;
command = buf;
commands.AddToTail(command);
}
return symbols.Count();
}
class CChoreoSentenceAutoCompletionFunctor : public ICommandCallback, public ICommandCompletionCallback
{
public:
virtual void CommandCallback( const CCommand &command )
{
if (command.ArgC() != 2)
{
Msg( "Format: choreosentence_play <sentence name>\n" );
return;
}
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
if (pPlayer)
{
const ChoreoSentence_t *pSentence = LookupChoreoSentence( pPlayer, command.Arg( 1 ) );
if (pSentence)
PrecacheChoreoSentence( *pSentence );
pPlayer->PlayChoreoSentenceScene( command.Arg( 1 ) );
}
}
virtual int CommandCompletionCallback( const char *partial, CUtlVector< CUtlString > &commands )
{
if ( !g_pGameRules )
{
return 0;
}
const char *cmdname = "choreosentence_play";
char *substring = (char *)partial;
if ( Q_strstr( partial, cmdname ) )
{
substring = (char *)partial + strlen( cmdname ) + 1;
}
int checklen = Q_strlen( substring );
extern bool UtlStringLessFunc( const CUtlString & lhs, const CUtlString & rhs );
CUtlRBTree< CUtlString > symbols( 0, 0, UtlStringLessFunc );
return AutoCompleteChoreoSentences(cmdname, commands, symbols, substring, checklen);
}
};
static CChoreoSentenceAutoCompletionFunctor g_ChoreoSentenceAutoComplete;
static ConCommand choreosentence_play("choreosentence_play", &g_ChoreoSentenceAutoComplete, "Plays the specified choreo sentence on the player", FCVAR_CHEAT, &g_ChoreoSentenceAutoComplete );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *ChoreoSentence_t::GetWordString( CBaseEntity *pSpeaker, int i ) const
{
const char *pszExtension = "";
if (V_strrchr( pszPrefix, CORRECT_PATH_SEPARATOR ))
{
// Use waves if our prefix is a path
pszExtension = ".wav";
}
// TODO: Something more elaborate than UTIL_VarArgs?
if (m_Words[i].bVirtual)
{
const char *pszVirtualWord = g_ChoreoSentenceSystem.ResolveVirtualWord( pSpeaker, m_Words[i].pszWord );
return UTIL_VarArgs( "%s%s%s", pszPrefix, pszVirtualWord, pszExtension );
}
return UTIL_VarArgs( "%s%s%s", pszPrefix, m_Words[i].pszWord, pszExtension );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void LoadChoreoSentenceFile( const char *pszFile )
{
g_ChoreoSentenceSystem.LoadChoreoSentenceFile( pszFile );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const ChoreoSentence_t *LookupChoreoSentence( CBaseEntity *pSpeaker, const char *pszSentenceName )
{
return g_ChoreoSentenceSystem.LookupChoreoSentence( pSpeaker, pszSentenceName );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void PrecacheChoreoSentence( const ChoreoSentence_t &sentence )
{
FOR_EACH_VEC( sentence.m_Words, i )
{
if (sentence.m_Words[i].bVirtual)
{
// Precache all virtual words
const char *pszExtension = "";
if (V_strrchr( sentence.pszPrefix, CORRECT_PATH_SEPARATOR ))
pszExtension = ".wav";
g_ChoreoSentenceSystem.PrecacheVirtualWord( sentence.m_Words[i].pszWord, sentence.pszPrefix, pszExtension );
}
else
{
CBaseEntity::PrecacheScriptSound( sentence.GetWordString( NULL, i ) );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool ParseChoreoSentence( CBaseEntity *pSpeaker, const char *pszRawSentence, ChoreoSentence_t &sentence )
{
if (pszRawSentence == NULL || *pszRawSentence == NULL)
return false;
char szSentence[256];
// First, try getting the prefix
const char *pColon = V_strnchr( pszRawSentence, ':', V_strlen( pszRawSentence ) );
if (pColon)
{
// Sentence is everything after colon
Q_strncpy( szSentence, pColon + 1, sizeof( szSentence ) );
// Copy everything before colon for prefix
char szPathName[MAX_PATH];
V_strncpy( szPathName, pszRawSentence, pColon - pszRawSentence + 1 );
V_FixSlashes( szPathName );
sentence.pszPrefix = g_ChoreoSentenceSystem.AllocateString( szPathName );
}
else
{
// It's all one sentence
Q_strncpy( szSentence, pszRawSentence, sizeof( szSentence ) );
}
// Now get any parameters
const char *pSemiColon = V_strnchr( szSentence, ';', sizeof( szSentence ) );
if (pSemiColon)
{
// Caption is whatever's after the semicolon
const char *pszCaption = pSemiColon+1;
if (pszCaption[0] == ' ')
pszCaption++;
sentence.pszCaption = g_ChoreoSentenceSystem.AllocateString( pszCaption );
// Replace semicolon with null terminator
szSentence[pSemiColon - szSentence] = '\0';
}
// Next, split up the sentence itself
bool success = ParseChoreoSentenceContents( pSpeaker, szSentence, sentence );
return success;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool ParseChoreoSentenceContents( CBaseEntity *pSpeaker, char *pszSentence, ChoreoSentence_t &sentence )
{
int nCurVol = 100, nCurPitch = 100;
char *pszToken = strtok( pszSentence, " " );
for (; pszToken != NULL; pszToken = strtok( NULL, " " ))
{
if (!pszToken || !*pszToken)
continue;
// Check if this is a command number
if (pszToken[0] == '(')
{
pszToken++;
// Remove closing parentheses
//int end = V_strlen( pszToken )-1;
//if (pszToken[end] == ')')
// pszToken[end] = '\0';
int nNum = atoi( pszToken + 1 );
if (nNum > 0)
{
switch (pszToken[0])
{
// TODO: Recognize e, t, etc.?
case 'v': nCurVol = nNum; break;
case 'p': nCurPitch = nNum; break;
}
continue;
}
else
{
Msg( "Zero command number in %s\n", pszSentence );
}
}
int nWord = sentence.m_Words.AddToTail();
sentence.m_Words[nWord].nVol = nCurVol;
sentence.m_Words[nWord].nPitch = nCurPitch;
// Check if this is virtual
if (IsVirtualWord( pszToken ))
sentence.m_Words[nWord].bVirtual = true;
// Check for periods or commas
int end = V_strlen( pszToken )-1;
if (pszToken[end] == ',')
{
int nWord2 = sentence.m_Words.AddToTail();
sentence.m_Words[nWord2].pszWord = g_ChoreoSentenceSystem.AllocateString( "_comma" );
sentence.m_Words[nWord2].nVol = nCurVol;
sentence.m_Words[nWord2].nPitch = nCurPitch;
pszToken[end] = '\0';
}
else if (pszToken[end] == '.')
{
int nWord2 = sentence.m_Words.AddToTail();
sentence.m_Words[nWord2].pszWord = g_ChoreoSentenceSystem.AllocateString( "_period" );
sentence.m_Words[nWord2].nVol = nCurVol;
sentence.m_Words[nWord2].nPitch = nCurPitch;
pszToken[end] = '\0';
}
sentence.m_Words[nWord].pszWord = g_ChoreoSentenceSystem.AllocateString( pszToken );
}
return sentence.m_Words.Count() > 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float GetChoreoSentenceDuration( CBaseFlex *pSpeaker, const ChoreoSentence_t &sentence )
{
const char *actormodel = (pSpeaker ? STRING( pSpeaker->GetModelName() ) : NULL);
float flLength = 0.0f;
FOR_EACH_VEC( sentence.m_Words, i )
{
//float duration = CBaseEntity::GetSoundDuration( sentence.GetWordString(pSpeaker, i), actormodel );
float duration;
const char *pszWord = sentence.GetWordString( pSpeaker, i );
// For now, call the engine functions manually instead of using CBaseEntity::GetSoundDuration so that we could get around the WaveTrace warning
if ( V_stristr( pszWord, ".wav" ) || V_stristr( pszWord, ".mp3" ) )
{
duration = enginesound->GetSoundDuration( PSkipSoundChars( pszWord ) );
}
else
{
extern ISoundEmitterSystemBase *soundemitterbase;
duration = enginesound->GetSoundDuration( PSkipSoundChars( soundemitterbase->GetWavFileForSound( pszWord, actormodel ) ) );
}
flLength += duration;
}
return flLength;
}

View File

@ -0,0 +1,67 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: VCD-based sentences.
//
//=============================================================================//
#ifndef CHOREOSENTENCE_H
#define CHOREOSENTENCE_H
#include "cbase.h"
#define MAX_CHOREO_SENTENCE_PREFIX_LEN 64
#define MAX_CHOREO_SENTENCE_WORD_LEN 32
#define MAX_CHOREO_SENTENCE_CAPTION_LEN 64
#define MAX_CHOREO_SENTENCE_VIRTUAL_WORD_LEN 32
struct ChoreoSentenceWord_t
{
ChoreoSentenceWord_t() {}
ChoreoSentenceWord_t( const ChoreoSentenceWord_t &src )
{
pszWord = src.pszWord;
nPitch = src.nPitch; nVol = src.nVol;
bVirtual = src.bVirtual;
}
const char *pszWord;
int nPitch, nVol = 100;
bool bVirtual = false;
};
struct ChoreoSentence_t
{
ChoreoSentence_t() {}
ChoreoSentence_t( const ChoreoSentence_t &src )
{
pszName = src.pszName;
pszPrefix = src.pszPrefix;
pszCaption = src.pszCaption;
m_Words.RemoveAll();
m_Words.AddVectorToTail( src.m_Words );
}
const char *GetWordString( CBaseEntity *pSpeaker, int i ) const;
CUtlVector< ChoreoSentenceWord_t > m_Words;
const char *pszName;
const char *pszPrefix;
const char *pszCaption;
};
//----------------------------------------------------------------------------
extern void LoadChoreoSentenceFile( const char *pszFile );
extern const ChoreoSentence_t *LookupChoreoSentence( CBaseEntity *pSpeaker, const char *pszSentenceName );
extern void PrecacheChoreoSentence( const ChoreoSentence_t &sentence );
bool ParseChoreoSentence( CBaseEntity *pSpeaker, const char *pszRawSentence, ChoreoSentence_t &sentence );
bool ParseChoreoSentenceContents( CBaseEntity *pSpeaker, char *pszSentence, ChoreoSentence_t &sentence );
extern float GetChoreoSentenceDuration( CBaseFlex *pSpeaker, const ChoreoSentence_t &sentence );
#endif // CHOREOSENTENCE_H

View File

@ -38,8 +38,8 @@ public:
void InputReload( inputdata_t &inputdata );
#ifdef MAPBASE_VSCRIPT
HSCRIPT ScriptGetKeyValues( void );
HSCRIPT ScriptGetKeyValueBlock( void );
HSCRIPT_RC ScriptGetKeyValues( void );
HSCRIPT_RC ScriptGetKeyValueBlock( void );
void ScriptSetKeyValues( HSCRIPT hKV );
void ScriptSetKeyValueBlock( HSCRIPT hKV );
@ -275,7 +275,7 @@ void CLogicExternalData::InputReload( inputdata_t &inputdata )
#ifdef MAPBASE_VSCRIPT
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
HSCRIPT CLogicExternalData::ScriptGetKeyValues( void )
HSCRIPT_RC CLogicExternalData::ScriptGetKeyValues( void )
{
if (m_bReloadBeforeEachAction)
LoadFile();
@ -283,8 +283,9 @@ HSCRIPT CLogicExternalData::ScriptGetKeyValues( void )
HSCRIPT hScript = NULL;
if (m_pRoot)
{
// Does this need to be destructed or freed? m_pScriptModelKeyValues apparently doesn't.
hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, m_pRoot, false );
KeyValues *pCopy = new KeyValues( NULL );
*pCopy = *m_pRoot;
hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, pCopy );
}
return hScript;
@ -292,7 +293,7 @@ HSCRIPT CLogicExternalData::ScriptGetKeyValues( void )
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
HSCRIPT CLogicExternalData::ScriptGetKeyValueBlock( void )
HSCRIPT_RC CLogicExternalData::ScriptGetKeyValueBlock( void )
{
if (m_bReloadBeforeEachAction)
LoadFile();
@ -300,8 +301,9 @@ HSCRIPT CLogicExternalData::ScriptGetKeyValueBlock( void )
HSCRIPT hScript = NULL;
if (m_pBlock)
{
// Does this need to be destructed or freed? m_pScriptModelKeyValues apparently doesn't.
hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, m_pBlock, false );
KeyValues *pCopy = new KeyValues( NULL );
*pCopy = *m_pBlock;
hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, pCopy );
}
return hScript;
@ -321,7 +323,8 @@ void CLogicExternalData::ScriptSetKeyValues( HSCRIPT hKV )
KeyValues *pKV = scriptmanager->GetKeyValuesFromScriptKV( g_pScriptVM, hKV );
if (pKV)
{
m_pRoot = pKV;
m_pRoot = new KeyValues( NULL );
*m_pRoot = *pKV;
}
}
@ -336,7 +339,8 @@ void CLogicExternalData::ScriptSetKeyValueBlock( HSCRIPT hKV )
KeyValues *pKV = scriptmanager->GetKeyValuesFromScriptKV( g_pScriptVM, hKV );
if (pKV)
{
m_pBlock = pKV;
m_pBlock = new KeyValues( NULL );
*m_pBlock = *pKV;
}
}

View File

@ -93,6 +93,7 @@ public:
virtual Activity GetPrimaryAttackActivity(void) { return m_CustomData.m_bHitUsesMissAnim ? ACT_VM_MISSCENTER : BaseClass::GetPrimaryAttackActivity(); }
const char* GetWeaponScriptName() { return m_iszWeaponScriptName.Get(); }
const char* GetName( void ) const { return STRING( m_iClassname ); }
virtual int GetDamageType() { return g_nDamageClassTypeBits[m_CustomData.m_nDamageClass]; }
virtual void InitCustomWeaponFromData(const void* pData, const char* pszWeaponScript);
@ -381,6 +382,7 @@ public:
CHLCustomWeaponGun();
virtual void InitCustomWeaponFromData(const void* pData, const char* pszWeaponScript);
const char* GetWeaponScriptName() { return m_iszWeaponScriptName.Get(); }
const char* GetName( void ) const { return STRING( m_iClassname ); }
// Weapon behaviour
virtual void ItemPostFrame(void); // called each frame by the player PostThink

View File

@ -4610,6 +4610,11 @@ void CBasePlayer::SetSuitUpdate(const char *name, int fgroup, int iNoRepeatTime)
return;
}
#ifdef MAPBASE
if ( HasContext("silent_suit", "1") )
return;
#endif
// if name == NULL, then clear out the queue
if (!name)
@ -8136,7 +8141,7 @@ Activity CBasePlayer::Weapon_TranslateActivity( Activity baseAct, bool *pRequire
{
Activity weaponTranslation = BaseClass::Weapon_TranslateActivity( baseAct, pRequired );
if ( GetActiveWeapon() && GetActiveWeapon()->IsEffectActive(EF_NODRAW) && baseAct != ACT_ARM )
if ( GetActiveWeapon() && !GetActiveWeapon()->IsWeaponVisible() && baseAct != ACT_ARM )
{
// Our weapon is holstered. Use the base activity.
return baseAct;
@ -9053,6 +9058,7 @@ void SendProxy_ShiftPlayerSpawnflags( const SendProp *pProp, const void *pStruct
// See baseplayer_shared.h for more details.
SendPropInt ( SENDINFO( m_spawnflags ), 3, SPROP_UNSIGNED, SendProxy_ShiftPlayerSpawnflags ),
SendPropBool ( SENDINFO( m_bDrawPlayerLegs ) ),
SendPropBool ( SENDINFO( m_bDrawPlayerModelExternally ) ),
SendPropBool ( SENDINFO( m_bInTriggerFall ) ),
#endif

View File

@ -1205,6 +1205,9 @@ public:
int m_nNumCrateHudHints;
#ifdef MAPBASE
bool GetDrawPlayerLegs( void ) { return m_bDrawPlayerLegs; }
void SetDrawPlayerLegs( bool bToggle ) { m_bDrawPlayerLegs.Set( bToggle ); }
bool GetDrawPlayerModelExternally( void ) { return m_bDrawPlayerModelExternally; }
void SetDrawPlayerModelExternally( bool bToggle ) { m_bDrawPlayerModelExternally.Set( bToggle ); }
#endif
@ -1251,6 +1254,7 @@ private:
char m_szNetname[MAX_PLAYER_NAME_LENGTH];
#ifdef MAPBASE
CNetworkVar( bool, m_bDrawPlayerLegs );
CNetworkVar( bool, m_bDrawPlayerModelExternally );
#endif

View File

@ -387,7 +387,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
if ( weapon )
{
VPROF( "player->SelectItem()" );
player->SelectItem( weapon->GetClassname(), ucmd->weaponsubtype );
player->SelectItem( weapon->GetName(), ucmd->weaponsubtype );
}
}

View File

@ -41,6 +41,10 @@
#include "npc_alyx_episodic.h"
#endif // HL2_EPISODIC
#ifdef MAPBASE
#include "mapbase/choreosentence.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
@ -57,6 +61,10 @@ class CBaseFlex;
static ConVar scene_forcecombined( "scene_forcecombined", "0", 0, "When playing back, force use of combined .wav files even in english." );
static ConVar scene_maxcaptionradius( "scene_maxcaptionradius", "1200", 0, "Only show closed captions if recipient is within this many units of speaking actor (0==disabled)." );
#ifdef MAPBASE
static ConVar scene_resume_use_last_speaking( "scene_resume_use_last_speaking", "1", 0, "Uses the last actor who spoke for resume scene calls, rather than the first actor of the scene" );
#endif
// Assume sound system is 100 msec lagged (only used if we can't find snd_mixahead cvar!)
#define SOUND_SYSTEM_LATENCY_DEFAULT ( 0.1f )
@ -411,6 +419,10 @@ public:
// If this scene is waiting on an actor, give up and quit trying.
void InputStopWaitingForActor( inputdata_t &inputdata );
#ifdef MAPBASE
void InputPauseAtLastInterrupt( inputdata_t &inputdata );
#endif
virtual void StartPlayback( void );
virtual void PausePlayback( void );
virtual void ResumePlayback( void );
@ -481,6 +493,9 @@ public:
bool InvolvesActor( CBaseEntity *pActor ); // NOTE: returns false if scene hasn't loaded yet
void GenerateSoundScene( CBaseFlex *pActor, const char *soundname );
#ifdef MAPBASE
void GenerateChoreoSentenceScene( CBaseFlex *pActor, const char *pszSentenceName );
#endif
virtual float GetPostSpeakDelay() { return 1.0; }
@ -488,6 +503,7 @@ public:
bool HasFlexAnimation( void );
#ifdef MAPBASE
bool IsPlayingSpeech( void );
CBaseFlex *GetLastSpeakingActor( void );
#endif
void SetCurrentTime( float t, bool forceClientSync );
@ -589,6 +605,9 @@ private:
void PrecacheScene( CChoreoScene *scene );
CChoreoScene *GenerateSceneForSound( CBaseFlex *pFlexActor, const char *soundname );
#ifdef MAPBASE
CChoreoScene *GenerateSceneForSentenceName( CBaseFlex *pFlexActor, const char *pszSentenceName );
#endif
bool CheckActors();
@ -638,6 +657,9 @@ private:
bool m_bGenerated;
string_t m_iszSoundName;
CHandle< CBaseFlex > m_hActor;
#ifdef MAPBASE
bool m_bChoreoSentence;
#endif
EHANDLE m_hActivator;
@ -731,6 +753,9 @@ BEGIN_DATADESC( CSceneEntity )
DEFINE_FIELD( m_bGenerated, FIELD_BOOLEAN ),
DEFINE_FIELD( m_iszSoundName, FIELD_STRING ),
DEFINE_FIELD( m_hActor, FIELD_EHANDLE ),
#ifdef MAPBASE
DEFINE_FIELD( m_bChoreoSentence, FIELD_BOOLEAN ),
#endif
DEFINE_FIELD( m_hActivator, FIELD_EHANDLE ),
// DEFINE_FIELD( m_bSceneMissing, FIELD_BOOLEAN ),
@ -752,9 +777,9 @@ BEGIN_DATADESC( CSceneEntity )
DEFINE_INPUTFUNC( FIELD_STRING, "InterjectResponse", InputInterjectResponse ),
DEFINE_INPUTFUNC( FIELD_VOID, "StopWaitingForActor", InputStopWaitingForActor ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "Trigger", InputTriggerEvent ),
DEFINE_KEYFIELD( m_iPlayerDeathBehavior, FIELD_INTEGER, "onplayerdeath" ),
DEFINE_INPUTFUNC( FIELD_VOID, "ScriptPlayerDeath", InputScriptPlayerDeath ),
DEFINE_INPUTFUNC( FIELD_VOID, "PauseAtLastInterrupt", InputPauseAtLastInterrupt ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget1", InputSetTarget1 ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget2", InputSetTarget2 ),
@ -948,6 +973,146 @@ CChoreoScene *CSceneEntity::GenerateSceneForSound( CBaseFlex *pFlexActor, const
return scene;
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose:
// Input : *actor -
// *pszSentenceName -
// Output : CChoreoScene
//-----------------------------------------------------------------------------
CChoreoScene *CSceneEntity::GenerateSceneForSentenceName( CBaseFlex *pFlexActor, const char *pszSentenceName )
{
const ChoreoSentence_t *pSentence = LookupChoreoSentence( pFlexActor, pszSentenceName );
if ( !pSentence )
{
Warning( "CSceneEntity::GenerateSceneForSentenceName: Couldn't find sentence '%s'\n", pszSentenceName );
return NULL;
}
// TODO: Raw sentence support?
// ChoreoSentence_t sentence;
// if ( !ParseChoreoSentence( pFlexActor, pszSentence, sentence ) )
// {
// Warning( "CSceneEntity::GenerateSceneForSentence: Couldn't parse sentence from '%s'\n", pszSentence );
// return NULL;
// }
CChoreoScene *scene = new CChoreoScene( this );
if ( !scene )
{
Warning( "CSceneEntity::GenerateSceneForSentenceName: Failed to allocated new scene!!!\n" );
}
else
{
scene->SetPrintFunc( LocalScene_Printf );
CChoreoActor *actor = scene->AllocActor();
CChoreoChannel *channel = scene->AllocChannel();
Assert( actor );
Assert( channel );
if ( !actor || !channel )
{
Warning( "CSceneEntity::GenerateSceneForSentenceName: Alloc of actor or channel failed!!!\n" );
delete scene;
return NULL;
}
// Set us up the actorz
actor->SetName( "!self" ); // Could be pFlexActor->GetName()?
actor->SetActive( true );
// Set us up the channelz
channel->SetName( STRING( m_iszSceneFile ) );
channel->SetActor( actor );
// Add to actor
actor->AddChannel( channel );
// Set us up the eventz
const char *actormodel = (pFlexActor ? STRING( pFlexActor->GetModelName() ) : NULL);
float flCurTime = 0.0f;
FOR_EACH_VEC( pSentence->m_Words, i )
{
const char *pszWord = pSentence->GetWordString( pFlexActor, i );
float duration = CBaseEntity::GetSoundDuration( pszWord, actormodel );
if (duration <= 0.0f)
{
Warning( "CSceneEntity::GenerateSceneForSentenceName: Couldn't determine duration of %s\n", pszWord );
}
CChoreoEvent *event = scene->AllocEvent();
Assert( event );
if ( !event )
{
Warning( "CSceneEntity::GenerateSceneForSentenceName: Alloc of event failed!!!\n" );
delete scene;
return NULL;
}
if (pSentence->pszCaption)
{
// First word gets the caption, others fall back to it
if (i == 0)
{
event->SetCloseCaptionType( CChoreoEvent::CC_MASTER );
event->SetCloseCaptionToken( pSentence->pszCaption );
}
else
{
event->SetCloseCaptionType( CChoreoEvent::CC_SLAVE );
}
}
//else if (pSentence->pszName)
//{
// // TODO: Caption from name?
//}
if (pSentence->m_Words[i].nVol != 100)
{
event->SetYaw( pSentence->m_Words[i].nVol );
}
if (pSentence->m_Words[i].nPitch != 100)
{
duration *= (100.0f / ((float)pSentence->m_Words[i].nPitch));
event->SetPitch( pSentence->m_Words[i].nPitch );
}
// HACKHACK: Need to be spaced away from repeated sound to avoid changing the previous sound's pitch instead
if (i+1 < pSentence->m_Words.Count() && pSentence->m_Words[i+1].pszWord == pSentence->m_Words[i].pszWord
&& pSentence->m_Words[i + 1].nPitch != 100)
duration += 0.1f;
event->SetType( CChoreoEvent::SPEAK );
event->SetName( pszWord );
event->SetParameters( pszWord );
event->SetStartTime( flCurTime );
event->SetUsingRelativeTag( false );
event->SetEndTime( flCurTime + duration );
event->SnapTimes();
//Msg( "%i %s: %f -> %f (%f)\n", i, pszWord, flCurTime, flCurTime + duration, duration );
// Add to channel
channel->AddEvent( event );
// Point back to our owners
event->SetChannel( channel );
event->SetActor( actor );
flCurTime += duration;
}
}
return scene;
}
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
@ -955,6 +1120,13 @@ void CSceneEntity::Activate()
{
if ( m_bGenerated && !m_pScene )
{
#ifdef MAPBASE
if (m_bChoreoSentence)
{
m_pScene = GenerateSceneForSentenceName( m_hActor, STRING( m_iszSoundName ) );
}
else
#endif
m_pScene = GenerateSceneForSound( m_hActor, STRING( m_iszSoundName ) );
}
@ -1133,6 +1305,22 @@ void CSceneEntity::GenerateSoundScene( CBaseFlex *pActor, const char *soundname
m_hActor = pActor;
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pActor -
// *pszSentenceName -
//-----------------------------------------------------------------------------
void CSceneEntity::GenerateChoreoSentenceScene( CBaseFlex *pActor, const char *pszSentenceName )
{
m_bGenerated = true;
m_iszSoundName = MAKE_STRING( pszSentenceName );
m_hActor = pActor;
m_bChoreoSentence = true;
}
#endif
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
@ -1180,6 +1368,33 @@ bool CSceneEntity::IsPlayingSpeech( void )
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseFlex *CSceneEntity::GetLastSpeakingActor( void )
{
if ( m_pScene )
{
CChoreoActor *pLastActor = NULL;
float flTime = m_pScene->GetTime();
for ( int i = 0; i < m_pScene->GetNumEvents(); i++ )
{
CChoreoEvent *e = m_pScene->GetEvent( i );
if ( e->GetType() == CChoreoEvent::SPEAK )
{
if ( flTime >= e->GetStartTime() )
pLastActor = e->GetActor();
}
}
if (pLastActor)
return FindNamedActor( pLastActor );
}
// Fall back to the first actor
return FindNamedActor( 0 );
}
#endif
@ -1929,6 +2144,26 @@ void CSceneEntity::DispatchStartSpeak( CChoreoScene *scene, CBaseFlex *actor, CC
// Warning( "Speak %s\n", soundname );
#ifdef MAPBASE
if ( m_fPitch != 1.0f || event->GetPitch() != 0 )
{
if ( es.m_nPitch && es.m_nPitch != 100 )
es.m_nPitch = static_cast<float>( es.m_nPitch ) * m_fPitch;
else
{
float flPitch = (event->GetPitch() != 0 ? event->GetPitch() : 100.0f);
es.m_nPitch = flPitch * m_fPitch;
}
es.m_nFlags |= SND_CHANGE_PITCH;
}
if ( event->GetYaw() != 0 )
{
es.m_flVolume = (((float)event->GetYaw()) / 100.0f);
es.m_nFlags |= SND_CHANGE_VOL;
}
#else
if ( m_fPitch != 1.0f )
{
if ( es.m_nPitch )
@ -1938,6 +2173,7 @@ void CSceneEntity::DispatchStartSpeak( CChoreoScene *scene, CBaseFlex *actor, CC
es.m_nFlags |= SND_CHANGE_PITCH;
}
#endif
EmitSound( filter2, actor->entindex(), es );
actor->AddSceneEvent( scene, event );
@ -2463,6 +2699,39 @@ void CSceneEntity::InputInterjectResponse( inputdata_t &inputdata )
}
}
void CSceneEntity::InputPauseAtLastInterrupt( inputdata_t &inputdata )
{
PausePlayback();
m_bPausedViaInput = true;
if ( m_pScene )
{
float flLastInterrupt = 0.0f;
float flTime = m_pScene->GetTime();
for ( int i = 0; i < m_pScene->GetNumEvents(); i++ )
{
CChoreoEvent *e = m_pScene->GetEvent( i );
if ( e->GetType() == CChoreoEvent::INTERRUPT )
{
if ( flTime > e->GetEndTime() && e->GetEndTime() > flLastInterrupt )
{
// Set the scene's time to the last interrupt point's end time
flLastInterrupt = e->GetEndTime();
break;
}
}
}
if (flLastInterrupt != 0.0f)
{
// Set the scene's time to the last interrupt point's end time
m_pScene->SetTime( flLastInterrupt );
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSceneEntity::InputStopWaitingForActor( inputdata_t &inputdata )
@ -2991,13 +3260,22 @@ void CSceneEntity::QueueResumePlayback( void )
// If it has ".vcd" somewhere in the string, try using it as a scene file first
if ( Q_stristr( STRING(m_iszResumeSceneFile), ".vcd" ) )
{
bStartedScene = InstancedScriptedScene( NULL, STRING(m_iszResumeSceneFile), &m_hWaitingForThisResumeScene, 0, false ) != 0;
#ifdef MAPBASE
CBaseFlex *pActor = scene_resume_use_last_speaking.GetBool() ? GetLastSpeakingActor() : FindNamedActor( 0 );
#else
CBaseFlex *pActor = NULL;
#endif
bStartedScene = InstancedScriptedScene( pActor, STRING(m_iszResumeSceneFile), &m_hWaitingForThisResumeScene, 0, false ) != 0;
}
// HACKHACK: For now, get the first target, and see if we can find a response for him
if ( !bStartedScene )
{
#ifdef MAPBASE
CBaseFlex *pActor = scene_resume_use_last_speaking.GetBool() ? GetLastSpeakingActor() : FindNamedActor( 0 );
#else
CBaseFlex *pActor = FindNamedActor( 0 );
#endif
if ( pActor )
{
CAI_BaseActor *pBaseActor = dynamic_cast<CAI_BaseActor*>(pActor);
@ -3008,7 +3286,7 @@ void CSceneEntity::QueueResumePlayback( void )
if ( result )
{
const char *szResponse = response.GetResponsePtr();
bStartedScene = InstancedScriptedScene( NULL, szResponse, &m_hWaitingForThisResumeScene, 0, false ) != 0;
bStartedScene = InstancedScriptedScene( pActor, szResponse, &m_hWaitingForThisResumeScene, 0, false ) != 0;
}
}
}
@ -5547,6 +5825,62 @@ void CInstancedSceneEntity::OnLoaded()
#endif
}
#ifdef MAPBASE
float InstancedChoreoSentenceScene( CBaseFlex *pActor, const char *pszSentence, EHANDLE *phSceneEnt,
float flPostDelay, bool bIsBackground, AI_Response *response,
bool bMultiplayer, IRecipientFilter *filter /* = NULL */ )
{
if ( !pActor )
{
Warning( "InstancedChoreoSentenceScene: Expecting non-NULL pActor for sound %s\n", pszSentence );
return 0;
}
CInstancedSceneEntity *pScene = (CInstancedSceneEntity *)CBaseEntity::CreateNoSpawn( "instanced_scripted_scene", vec3_origin, vec3_angle );
Q_strncpy( pScene->m_szInstanceFilename, UTIL_VarArgs( "AutoGenerated(%s)", pszSentence ), sizeof( pScene->m_szInstanceFilename ) );
pScene->m_iszSceneFile = MAKE_STRING( pScene->m_szInstanceFilename );
pScene->m_hOwner = pActor;
pScene->m_bHadOwner = pActor != NULL;
pScene->GenerateChoreoSentenceScene( pActor, pszSentence );
pScene->m_bMultiplayer = bMultiplayer;
pScene->SetPostSpeakDelay( flPostDelay );
DispatchSpawn( pScene );
pScene->Activate();
pScene->m_bIsBackground = bIsBackground;
pScene->SetBackground( bIsBackground );
pScene->SetRecipientFilter( filter );
if ( response )
{
float flPreDelay = response->GetPreDelay();
if ( flPreDelay )
{
pScene->SetPreDelay( flPreDelay );
}
}
pScene->StartPlayback();
if ( response )
{
// If the response wants us to abort on NPC state switch, remember that
pScene->SetBreakOnNonIdle( response->ShouldBreakOnNonIdle() );
}
if ( phSceneEnt )
{
*phSceneEnt = pScene;
}
return pScene->EstimateLength();
}
#endif
bool g_bClientFlex = true;
LINK_ENTITY_TO_CLASS( scene_manager, CSceneManager );

View File

@ -28,6 +28,7 @@ int GetRecentNPCSpeech( recentNPCSpeech_t speech[ SPEECH_LIST_MAX_SOUNDS ] );
float InstancedScriptedScene( CBaseFlex *pActor, const char *pszScene, EHANDLE *phSceneEnt = NULL, float flPostDelay = 0.0f, bool bIsBackground = false, AI_Response *response = NULL, bool bMultiplayer = false, IRecipientFilter *filter = NULL );
#ifdef MAPBASE
float InstancedAutoGeneratedSoundScene( CBaseFlex *pActor, char const *soundname, EHANDLE *phSceneEnt = NULL, float flPostDelay = 0.0f, bool bIsBackground = false, AI_Response *response = NULL, bool bMultiplayer = false, IRecipientFilter *filter = NULL );
float InstancedChoreoSentenceScene( CBaseFlex *pActor, char const *pszSentence, EHANDLE *phSceneEnt = NULL, float flPostDelay = 0.0f, bool bIsBackground = false, AI_Response *response = NULL, bool bMultiplayer = false, IRecipientFilter *filter = NULL );
#else
float InstancedAutoGeneratedSoundScene( CBaseFlex *pActor, char const *soundname, EHANDLE *phSceneEnt = NULL );
#endif

View File

@ -48,8 +48,6 @@ $Project
$File "$SRCDIR\game\shared\mapbase\MapEdit.h"
$File "$SRCDIR\game\shared\mapbase\matchers.cpp"
$File "$SRCDIR\game\shared\mapbase\matchers.h"
$File "$SRCDIR\game\shared\mapbase\singleplayer_animstate.cpp"
$File "$SRCDIR\game\shared\mapbase\singleplayer_animstate.h"
$File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.cpp" [$MAPBASE_VSCRIPT]
$File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.h" [$MAPBASE_VSCRIPT]
$File "$SRCDIR\game\shared\mapbase\vscript_singletons.cpp" [$MAPBASE_VSCRIPT]
@ -62,6 +60,8 @@ $Project
$File "mapbase\ai_monitor.cpp"
$File "mapbase\ai_weaponmodifier.cpp"
$File "mapbase\choreosentence.cpp"
$File "mapbase\choreosentence.h"
$File "mapbase\custom_weapon_factory.cpp"
$File "mapbase\custom_weapon_factory.h"
$File "mapbase\closecaption_entity.cpp"

View File

@ -10,6 +10,10 @@ $Project
{
$Folder "Mapbase"
{
$File "$SRCDIR\game\shared\mapbase\mapbase_playeranimstate.cpp"
$File "$SRCDIR\game\shared\mapbase\mapbase_playeranimstate.h"
$File "$SRCDIR\game\shared\mapbase\protagonist_system.cpp"
$File "$SRCDIR\game\shared\mapbase\protagonist_system.h"
$File "$SRCDIR\game\shared\mapbase\vscript_funcs_hl2.cpp" [$MAPBASE_VSCRIPT]
$File "mapbase\ai_grenade.cpp"

View File

@ -66,6 +66,7 @@ BEGIN_SCRIPTDESC_ROOT( CSound, "A sound NPCs can hear." )
DEFINE_SCRIPTFUNC( ValidateOwner, "Returns true if the sound's owner is still valid or if the sound never had an owner in the first place." )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetOwner, "GetOwner", "Gets the sound's owner." )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetTarget, "GetTarget", "Gets the sound's target." )
DEFINE_SCRIPTFUNC_NAMED( ScriptFreeSound, "FreeSound", "Frees the sound from the sound list." )
END_SCRIPTDESC();
#endif

View File

@ -140,6 +140,8 @@ public:
// For VScript functions
HSCRIPT ScriptGetOwner() const { return ToHScript( m_hOwner ); }
HSCRIPT ScriptGetTarget() const { return ToHScript( m_hTarget ); }
void ScriptFreeSound() { m_flExpireTime = gpGlobals->curtime; m_bNoExpirationTime = false; }
#endif
EHANDLE m_hOwner; // sound's owner

View File

@ -31,13 +31,12 @@ ConVar tf_show_incursion_range_max( "tf_show_incursion_range_max", "0", FCVAR_CH
// Script access to manipulate the nav
//--------------------------------------------------------------------------------------------------------------
#ifndef MAPBASE_VSCRIPT // TODO: Need better way of supporting this
#ifdef MAPBASE_VSCRIPT
BEGIN_ENT_SCRIPTDESC_ROOT_WITH_HELPER( CTFNavArea, "Navigation areas class", &g_NavAreaScriptInstanceHelper )
#else
DEFINE_SCRIPT_INSTANCE_HELPER( CTFNavArea, &g_NavAreaScriptInstanceHelper )
#endif
BEGIN_ENT_SCRIPTDESC_ROOT( CTFNavArea, "Navigation areas class" )
#ifdef MAPBASE_VSCRIPT
DEFINE_SCRIPT_INSTANCE_HELPER( &g_NavAreaScriptInstanceHelper )
#endif
DEFINE_SCRIPTFUNC_NAMED( ScriptGetID, "GetID", "Get area ID." )
DEFINE_SCRIPTFUNC( GetAttributes, "Get area attribute bits" )

View File

@ -212,7 +212,11 @@ inline HSCRIPT ToHScript( CTFNavArea *pArea )
return ( pArea ) ? pArea->GetScriptInstance() : NULL;
}
#ifdef MAPBASE_VSCRIPT
template <> ScriptClassDesc_t *GetScriptDesc<CTFNavArea>( CTFNavArea *, bool );
#else
template <> ScriptClassDesc_t *GetScriptDesc<CTFNavArea>( CTFNavArea * );
#endif
inline CTFNavArea *ToNavArea( HSCRIPT hScript )
{
return ( IsValid( hScript ) ) ? (CTFNavArea *)g_pScriptVM->GetInstanceValue( hScript, GetScriptDescForClass(CTFNavArea) ) : NULL;

View File

@ -60,6 +60,10 @@ extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * );
extern CServerGameDLL g_ServerGameDLL;
#ifdef MAPBASE_VSCRIPT
extern int vscript_debugger_port;
#endif
// #define VMPROFILE 1
#ifdef VMPROFILE
@ -3764,6 +3768,14 @@ REGISTER_SCRIPT_CONST_TABLE( Server )
g_pScriptVM->SetValue( "Constants", vConstantsTable );
#ifdef MAPBASE_VSCRIPT
if ( vscript_debugger_port )
{
g_pScriptVM->ConnectDebugger( vscript_debugger_port );
vscript_debugger_port = 0;
}
#endif
if ( scriptLanguage == SL_SQUIRREL )
{
g_pScriptVM->Run( g_Script_vscript_server );

View File

@ -71,16 +71,16 @@ function ImpulseScale( flTargetMass, flDesiredSpeed )
}
__Documentation.RegisterHelp( "ImpulseScale", "float ImpulseScale(float, float)", "Returns an impulse scale required to push an object." );
local PrecacheModel = PrecacheModel
local PrecacheModel = DoPrecacheModel
function PrecacheModel( a, b = true )
{
return PrecacheModel( a, b )
}
local PrecacheOther = PrecacheOther
local PrecacheOther = DoPrecacheOther
function PrecacheOther( a, b = "" )
{
PrecacheOther( a, b )
return PrecacheOther( a, b )
}
function __ReplaceClosures( script, scope )
@ -114,10 +114,9 @@ revisit, otherwise we'll leave if off and broken.
*/
if (!VSCRIPT_PRIORITIZE_TF2_SYNTAX)
{
local __OutputsPattern = regexp("^On.*Output$");
function ConnectOutputs( table )
{
local __OutputsPattern = regexp("^On.*Output$");
local nCharsToStrip = 6;
foreach( key, val in table )
{

View File

@ -2941,6 +2941,18 @@ void ActivityList_RegisterSharedActivities( void )
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_MELEE );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SLAM );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_PISTOL );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_SHOTGUN );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_SMG1 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_AR2 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_PHYSGUN );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_GRENADE );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_RPG );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_CROSSBOW );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_MELEE );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_SLAM );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_REVOLVER );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_REVOLVER );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_REVOLVER );
@ -2949,6 +2961,7 @@ void ActivityList_RegisterSharedActivities( void )
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_REVOLVER );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_REVOLVER );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_REVOLVER );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_REVOLVER );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_REVOLVER );
#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES
@ -2960,6 +2973,7 @@ void ActivityList_RegisterSharedActivities( void )
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_AR1 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR1 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_AR1 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_AR1 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_AR1 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_AR3 );
@ -2970,6 +2984,7 @@ void ActivityList_RegisterSharedActivities( void )
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_AR3 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR3 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_AR3 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_AR3 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_AR3 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_SMG2 );
@ -2980,6 +2995,7 @@ void ActivityList_RegisterSharedActivities( void )
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG2 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG2 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_SMG2 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_SMG2 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_SMG2 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_SMG3 );
@ -2990,6 +3006,7 @@ void ActivityList_RegisterSharedActivities( void )
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG3 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG3 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_SMG3 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_SMG3 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_SMG3 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_HMG1 );
@ -3000,6 +3017,7 @@ void ActivityList_RegisterSharedActivities( void )
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_HMG1 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_HMG1 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_HMG1 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_HMG1 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_HMG1 );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_SNIPER_RIFLE );
@ -3010,6 +3028,7 @@ void ActivityList_RegisterSharedActivities( void )
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_SNIPER_RIFLE );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SNIPER_RIFLE );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_SNIPER_RIFLE );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_SNIPER_RIFLE );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_SNIPER_RIFLE );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_DUAL_PISTOLS );
@ -3020,6 +3039,7 @@ void ActivityList_RegisterSharedActivities( void )
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_DUAL_PISTOLS );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_DUAL_PISTOLS );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_DUAL_PISTOLS );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELAX_DUAL_PISTOLS );
REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_DUAL_PISTOLS );
#endif

View File

@ -2854,6 +2854,18 @@ typedef enum
ACT_HL2MP_GESTURE_RANGE_ATTACK2_MELEE,
ACT_HL2MP_GESTURE_RANGE_ATTACK2_SLAM,
ACT_HL2MP_GESTURE_RELAX,
ACT_HL2MP_GESTURE_RELAX_PISTOL,
ACT_HL2MP_GESTURE_RELAX_SHOTGUN,
ACT_HL2MP_GESTURE_RELAX_SMG1,
ACT_HL2MP_GESTURE_RELAX_AR2,
ACT_HL2MP_GESTURE_RELAX_PHYSGUN,
ACT_HL2MP_GESTURE_RELAX_GRENADE,
ACT_HL2MP_GESTURE_RELAX_RPG,
ACT_HL2MP_GESTURE_RELAX_CROSSBOW,
ACT_HL2MP_GESTURE_RELAX_MELEE,
ACT_HL2MP_GESTURE_RELAX_SLAM,
ACT_HL2MP_IDLE_REVOLVER,
ACT_HL2MP_RUN_REVOLVER,
ACT_HL2MP_WALK_REVOLVER,
@ -2862,6 +2874,7 @@ typedef enum
ACT_HL2MP_GESTURE_RANGE_ATTACK_REVOLVER,
ACT_HL2MP_GESTURE_RANGE_ATTACK2_REVOLVER,
ACT_HL2MP_GESTURE_RELOAD_REVOLVER,
ACT_HL2MP_GESTURE_RELAX_REVOLVER,
ACT_HL2MP_JUMP_REVOLVER,
#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES
@ -2874,6 +2887,7 @@ typedef enum
ACT_HL2MP_GESTURE_RANGE_ATTACK_AR1,
ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR1,
ACT_HL2MP_GESTURE_RELOAD_AR1,
ACT_HL2MP_GESTURE_RELAX_AR1,
ACT_HL2MP_JUMP_AR1,
ACT_HL2MP_IDLE_AR3,
@ -2884,6 +2898,7 @@ typedef enum
ACT_HL2MP_GESTURE_RANGE_ATTACK_AR3,
ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR3,
ACT_HL2MP_GESTURE_RELOAD_AR3,
ACT_HL2MP_GESTURE_RELAX_AR3,
ACT_HL2MP_JUMP_AR3,
ACT_HL2MP_IDLE_SMG2,
@ -2894,6 +2909,7 @@ typedef enum
ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG2,
ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG2,
ACT_HL2MP_GESTURE_RELOAD_SMG2,
ACT_HL2MP_GESTURE_RELAX_SMG2,
ACT_HL2MP_JUMP_SMG2,
ACT_HL2MP_IDLE_SMG3,
@ -2904,6 +2920,7 @@ typedef enum
ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG3,
ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG3,
ACT_HL2MP_GESTURE_RELOAD_SMG3,
ACT_HL2MP_GESTURE_RELAX_SMG3,
ACT_HL2MP_JUMP_SMG3,
ACT_HL2MP_IDLE_HMG1,
@ -2914,6 +2931,7 @@ typedef enum
ACT_HL2MP_GESTURE_RANGE_ATTACK_HMG1,
ACT_HL2MP_GESTURE_RANGE_ATTACK2_HMG1,
ACT_HL2MP_GESTURE_RELOAD_HMG1,
ACT_HL2MP_GESTURE_RELAX_HMG1,
ACT_HL2MP_JUMP_HMG1,
ACT_HL2MP_IDLE_SNIPER_RIFLE,
@ -2924,6 +2942,7 @@ typedef enum
ACT_HL2MP_GESTURE_RANGE_ATTACK_SNIPER_RIFLE,
ACT_HL2MP_GESTURE_RANGE_ATTACK2_SNIPER_RIFLE,
ACT_HL2MP_GESTURE_RELOAD_SNIPER_RIFLE,
ACT_HL2MP_GESTURE_RELAX_SNIPER_RIFLE,
ACT_HL2MP_JUMP_SNIPER_RIFLE,
ACT_HL2MP_IDLE_DUAL_PISTOLS,
@ -2934,6 +2953,7 @@ typedef enum
ACT_HL2MP_GESTURE_RANGE_ATTACK_DUAL_PISTOLS,
ACT_HL2MP_GESTURE_RANGE_ATTACK2_DUAL_PISTOLS,
ACT_HL2MP_GESTURE_RELOAD_DUAL_PISTOLS,
ACT_HL2MP_GESTURE_RELAX_DUAL_PISTOLS,
ACT_HL2MP_JUMP_DUAL_PISTOLS,
#endif

View File

@ -31,6 +31,9 @@
#ifdef GAME_DLL
#include "sceneentity.h"
#ifdef MAPBASE
#include "mapbase/choreosentence.h"
#endif
#endif
#include "networkstringtabledefs.h"
@ -492,6 +495,17 @@ void CGameResponseSystem::Precache()
CBaseEntity::PrecacheScriptSound( response.value );
}
break;
#ifdef MAPBASE
case RESPONSE_CHOREOSENTENCE:
{
const ChoreoSentence_t *pSentence = LookupChoreoSentence( NULL, response.value );
if (pSentence)
PrecacheChoreoSentence( *pSentence );
else
Msg( "Choreo scene '%s' not found\n", response.value );
}
break;
#endif
}
}
}

View File

@ -18,12 +18,12 @@
#include "c_baseplayer.h"
#include "engine/ivdebugoverlay.h"
ConVar cl_showanimstate( "cl_showanimstate", "-1", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Show the (client) animation state for the specified entity (-1 for none)." );
ConVar showanimstate_log( "cl_showanimstate_log", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "1 to output cl_showanimstate to Msg(). 2 to store in AnimStateClient.log. 3 for both." );
ConVar cl_showanimstate( "cl_showanimstate", "-1", FCVAR_CHEAT /*| FCVAR_DEVELOPMENTONLY*/, "Show the (client) animation state for the specified entity (-1 for none)." );
ConVar showanimstate_log( "cl_showanimstate_log", "0", FCVAR_CHEAT /*| FCVAR_DEVELOPMENTONLY*/, "1 to output cl_showanimstate to Msg(). 2 to store in AnimStateClient.log. 3 for both." );
#else
#include "player.h"
ConVar sv_showanimstate( "sv_showanimstate", "-1", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Show the (server) animation state for the specified entity (-1 for none)." );
ConVar showanimstate_log( "sv_showanimstate_log", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "1 to output sv_showanimstate to Msg(). 2 to store in AnimStateServer.log. 3 for both." );
ConVar sv_showanimstate( "sv_showanimstate", "-1", FCVAR_CHEAT /*| FCVAR_DEVELOPMENTONLY*/, "Show the (server) animation state for the specified entity (-1 for none)." );
ConVar showanimstate_log( "sv_showanimstate_log", "0", FCVAR_CHEAT /*| FCVAR_DEVELOPMENTONLY*/, "1 to output sv_showanimstate to Msg(). 2 to store in AnimStateServer.log. 3 for both." );
#endif
@ -657,9 +657,11 @@ void CBasePlayerAnimState::ComputePoseParam_MoveYaw( CStudioHdr *pStudioHdr )
if ( m_AnimConfig.m_LegAnimType == LEGANIM_9WAY )
{
#ifndef MAPBASE // This causes problems with entities that rely on the player having a pitch (TODO: gate behind virtual function?)
#ifndef CLIENT_DLL
//Adrian: Make the model's angle match the legs so the hitboxes match on both sides.
GetOuter()->SetLocalAngles( QAngle( 0, m_flCurrentFeetYaw, 0 ) );
#endif
#endif
int iMoveX = GetOuter()->LookupPoseParameter( pStudioHdr, "move_x" );
@ -945,6 +947,15 @@ void CBasePlayerAnimState::GetOuterAbsVelocity( Vector& vel ) const
#else
vel = GetOuter()->GetAbsVelocity();
#endif
#ifdef MAPBASE
if (GetOuter()->IsPlayer())
{
CBasePlayer *pPlayer = ToBasePlayer( GetOuter() );
if (pPlayer->GetLaggedMovementValue() != 1.0f)
vel *= pPlayer->GetLaggedMovementValue();
}
#endif
}
@ -1027,9 +1038,15 @@ void CBasePlayerAnimState::DebugShowAnimState( int iStartLine )
(float)pLayer->m_flWeight );
}
#ifdef MAPBASE
for ( int i=0; i < m_pOuter->GetNumAnimOverlays(); i++ )
{
CAnimationLayer *pLayer = m_pOuter->GetAnimOverlay( i );
#else
for ( int i=0; i < m_pOuter->GetNumAnimOverlays()-1; i++ )
{
CAnimationLayer *pLayer = m_pOuter->GetAnimOverlay( AIMSEQUENCE_LAYER + i );
#endif
#ifdef CLIENT_DLL
AnimStatePrintf( iLine++, "%s(%d), weight: %.2f, cycle: %.2f, order (%d), aim (%d)",
!pLayer->IsActive() ? "-- ": (pLayer->m_nSequence == 0 ? "-- " : GetSequenceName( m_pOuter->GetModelPtr(), pLayer->m_nSequence ) ),

View File

@ -524,6 +524,11 @@ bool CBaseCombatWeapon::UsesHands() const
{
return GetWpnData().m_bUsesHands;
}
int CBaseCombatWeapon::GetHandRig() const
{
return GetWpnData().m_nHandRig;
}
#endif
//-----------------------------------------------------------------------------
@ -3175,6 +3180,9 @@ BEGIN_ENT_SCRIPTDESC( CBaseCombatWeapon, CBaseAnimating, "The base class for all
DEFINE_SCRIPTFUNC( GetViewModel, "Get the weapon's view model." )
DEFINE_SCRIPTFUNC( GetDroppedModel, "Get the weapon's unique dropped model if it has one." )
DEFINE_SCRIPTFUNC( UsesHands, "" )
DEFINE_SCRIPTFUNC( GetHandRig, "" )
DEFINE_SCRIPTFUNC( GetWeight, "Get the weapon's weight." )
DEFINE_SCRIPTFUNC( GetPrintName, "" )

View File

@ -304,6 +304,10 @@ public:
virtual bool ShouldBlockPrimaryFire() { return false; }
#ifdef CLIENT_DLL
#ifdef MAPBASE
virtual bool DispatchMuzzleEffect( const char *options, bool isFirstPerson );
#endif
virtual void CreateMove( float flInputSampleTime, CUserCmd *pCmd, const QAngle &vecOldViewAngles ) {}
virtual int CalcOverrideModelIndex() OVERRIDE;
@ -419,12 +423,13 @@ public:
virtual bool UsesClipsForAmmo2( void ) const;
bool IsMeleeWeapon() const;
#ifdef MAPBASE
float GetViewmodelFOVOverride() const;
virtual float GetViewmodelFOVOverride() const;
float GetBobScale() const;
float GetSwayScale() const;
float GetSwaySpeedScale() const;
virtual const char *GetDroppedModel( void ) const;
bool UsesHands( void ) const;
virtual bool UsesHands( void ) const;
virtual int GetHandRig( void ) const;
#endif
// derive this function if you mod uses encrypted weapon info files

View File

@ -3043,7 +3043,7 @@ void CBaseEntity::ScriptSetContextThink( const char* szContext, HSCRIPT hFunc, f
float nextthink = gpGlobals->curtime + flTime;
pf->m_hfnThink = hFunc;
pf->m_hfnThink = g_pScriptVM->CopyObject( hFunc );
pf->m_flNextThink = nextthink;
#ifdef GAME_DLL

View File

@ -258,7 +258,7 @@ inline HSCRIPT ToHScript(CBaseEntity* pEnt)
return ( pEnt ) ? pEnt->GetScriptInstance() : NULL;
}
template <> ScriptClassDesc_t* GetScriptDesc<CBaseEntity>(CBaseEntity*);
template <> ScriptClassDesc_t* GetScriptDesc<CBaseEntity>(CBaseEntity*, bool);
inline CBaseEntity* ToEnt(HSCRIPT hScript)
{
return ( hScript ) ? (CBaseEntity *)g_pScriptVM->GetInstanceValue( hScript, GetScriptDescForClass(CBaseEntity) ) : NULL;

View File

@ -6,6 +6,9 @@
#include "cbase.h"
#include "basehlcombatweapon_shared.h"
#ifdef MAPBASE
#include "mapbase/protagonist_system.h"
#endif
#include "hl2_player_shared.h"
@ -437,3 +440,65 @@ const WeaponProficiencyInfo_t *CBaseHLCombatWeapon::GetDefaultProficiencyValues(
}
#endif
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CBaseHLCombatWeapon::GetViewModel( int viewmodelindex ) const
{
if (GetOwner() && GetOwner()->IsPlayer() && viewmodelindex == 0)
{
const char *pszProtagVM = g_ProtagonistSystem.GetProtagonist_ViewModel( static_cast<CBasePlayer *>(GetOwner()), this );
if (pszProtagVM)
return pszProtagVM;
}
return BaseClass::GetViewModel( viewmodelindex );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CBaseHLCombatWeapon::GetViewmodelFOVOverride() const
{
if (GetOwner() && GetOwner()->IsPlayer())
{
float *flVMFOV = g_ProtagonistSystem.GetProtagonist_ViewModelFOV( static_cast<CBasePlayer *>(GetOwner()), this );
if (flVMFOV)
return *flVMFOV;
}
return BaseClass::GetViewmodelFOVOverride();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseHLCombatWeapon::UsesHands() const
{
if (GetOwner() && GetOwner()->IsPlayer())
{
bool *bProtagUsesHands = g_ProtagonistSystem.GetProtagonist_UsesHands( static_cast<CBasePlayer *>(GetOwner()), this );
if (bProtagUsesHands)
return *bProtagUsesHands;
}
return BaseClass::UsesHands();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CBaseHLCombatWeapon::GetHandRig() const
{
if (GetOwner() && GetOwner()->IsPlayer())
{
int *nProtagHandRig = g_ProtagonistSystem.GetProtagonist_HandRig( static_cast<CBasePlayer *>(GetOwner()), this );
if (nProtagHandRig)
return *nProtagHandRig;
}
return BaseClass::GetHandRig();
}
#endif

View File

@ -53,6 +53,13 @@ public:
virtual void ItemHolsterFrame( void );
#ifdef MAPBASE
virtual const char *GetViewModel( int viewmodelindex = 0 ) const;
virtual float GetViewmodelFOVOverride() const;
virtual bool UsesHands( void ) const;
virtual int GetHandRig( void ) const;
#endif
int m_iPrimaryAttacks; // # of primary attacks performed with this weapon
int m_iSecondaryAttacks; // # of secondary attacks performed with this weapon

View File

@ -15,7 +15,7 @@
//=============================================================================//
#include "cbase.h"
#include "singleplayer_animstate.h"
#include "mapbase_playeranimstate.h"
#include "tier0/vprof.h"
#include "animation.h"
#include "studio.h"
@ -23,25 +23,33 @@
#include "utldict.h"
#include "filesystem.h"
#include "in_buttons.h"
#include "gamemovement.h"
#include "datacache/imdlcache.h"
#ifdef CLIENT_DLL
#include "input.h"
#endif
extern ConVar mp_facefronttime, mp_feetyawrate, mp_ik;
extern ConVar mp_facefronttime, mp_feetyawrate;
ConVar sv_playeranimstate_animtype( "sv_playeranimstate_animtype", "0", FCVAR_NONE, "The leg animation type used by the singleplayer animation state. 9way = 0, 8way = 1, GoldSrc = 2" );
ConVar sv_playeranimstate_bodyyaw( "sv_playeranimstate_bodyyaw", "45.0", FCVAR_NONE, "The maximum body yaw used by the singleplayer animation state." );
ConVar sv_playeranimstate_use_aim_sequences( "sv_playeranimstate_use_aim_sequences", "1", FCVAR_NONE, "Allows the singleplayer animation state to use aim sequences." );
ConVar sv_playeranimstate_animtype( "sv_playeranimstate_animtype", "0", FCVAR_NONE, "The leg animation type used by the Mapbase animation state. 9way = 0, 8way = 1, GoldSrc = 2" );
ConVar sv_playeranimstate_bodyyaw( "sv_playeranimstate_bodyyaw", "45.0", FCVAR_NONE, "The maximum body yaw used by the Mapbase animation state." );
ConVar sv_playeranimstate_use_aim_sequences( "sv_playeranimstate_use_aim_sequences", "0", FCVAR_NONE, "Allows the Mapbase animation state to use aim sequences." );
ConVar sv_playeranimstate_use_walk_anims( "sv_playeranimstate_use_walk_anims", "0", FCVAR_NONE, "Allows the Mapbase animation state to use walk animations when the player is walking." );
#define MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION 15.0f
#define WEAPON_RELAX_TIME 0.5f
#define FIRESEQUENCE_LAYER (AIMSEQUENCE_LAYER+NUM_AIMSEQUENCE_LAYERS)
#define RELOADSEQUENCE_LAYER (FIRESEQUENCE_LAYER + 1)
#define NUM_LAYERS_WANTED (RELOADSEQUENCE_LAYER + 1)
#define MISCSEQUENCE_LAYER (RELOADSEQUENCE_LAYER + 1)
#define NUM_LAYERS_WANTED (MISCSEQUENCE_LAYER + 1)
CSinglePlayerAnimState *CreatePlayerAnimationState( CBasePlayer *pPlayer )
CMapbasePlayerAnimState *CreatePlayerAnimationState( CBasePlayer *pPlayer )
{
MDLCACHE_CRITICAL_SECTION();
CSinglePlayerAnimState *pState = new CSinglePlayerAnimState( pPlayer );
CMapbasePlayerAnimState *pState = new CMapbasePlayerAnimState( pPlayer );
// Setup the movement data.
CModAnimConfig movementData;
@ -65,22 +73,25 @@ CSinglePlayerAnimState *CreatePlayerAnimationState( CBasePlayer *pPlayer )
extern ConVar sv_backspeed;
extern ConVar mp_feetyawrate;
extern ConVar mp_facefronttime;
extern ConVar mp_ik;
CSinglePlayerAnimState::CSinglePlayerAnimState( CBasePlayer *pPlayer ): m_pPlayer( pPlayer )
CMapbasePlayerAnimState::CMapbasePlayerAnimState( CBasePlayer *pPlayer ): m_pPlayer( pPlayer )
{
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Activity CSinglePlayerAnimState::CalcMainActivity()
Activity CMapbasePlayerAnimState::CalcMainActivity()
{
#ifdef CLIENT_DLL
return ACT_IDLE;
#else
float speed = GetOuter()->GetAbsVelocity().Length2D();
if (m_pPlayer->GetLaggedMovementValue() != 1.0f)
speed *= m_pPlayer->GetLaggedMovementValue();
// May not always be precise
if (speed < 0.01f)
speed = 0.0f;
if ( HandleJumping() )
{
return ACT_HL2MP_JUMP;
@ -95,7 +106,24 @@ Activity CSinglePlayerAnimState::CalcMainActivity()
}
else
{
if ( GetOuter()->GetFlags() & FL_DUCKING )
bool bDucking = (GetOuter()->GetFlags() & FL_DUCKING) ? true : false;
// (currently singleplayer-exclusive since clients can't read whether other players are holding down IN_DUCK)
if (m_pPlayer->m_Local.m_flDucktime > 0 && gpGlobals->maxClients == 1)
{
// Consider ducking if half-way through duck time
bDucking = (m_pPlayer->m_Local.m_flDucktime < (GAMEMOVEMENT_DUCK_TIME * 0.9f));
// Unducking
#ifdef CLIENT_DLL
if (!((m_pPlayer->IsLocalPlayer() ? input->GetButtonBits( 0 ) : m_pPlayer->GetCurrentUserCommand()->buttons) & IN_DUCK))
#else
if (!(m_pPlayer->m_nButtons & IN_DUCK))
#endif
bDucking = !bDucking;
}
if ( bDucking )
{
if ( speed > 0 )
{
@ -111,7 +139,7 @@ Activity CSinglePlayerAnimState::CalcMainActivity()
if ( speed > 0 )
{
#if EXPANDED_HL2DM_ACTIVITIES
if ( m_pPlayer->GetButtons() & IN_WALK )
if ( m_pPlayer->m_nButtons & IN_WALK && sv_playeranimstate_use_walk_anims.GetBool() )
{
idealActivity = ACT_HL2MP_WALK;
}
@ -132,19 +160,22 @@ Activity CSinglePlayerAnimState::CalcMainActivity()
}
//return m_pPlayer->GetActivity();
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CSinglePlayerAnimState::SetPlayerAnimation( PLAYER_ANIM playerAnim )
void CMapbasePlayerAnimState::SetPlayerAnimation( PLAYER_ANIM playerAnim )
{
if ( playerAnim == PLAYER_ATTACK1 )
{
m_iFireSequence = SelectWeightedSequence( TranslateActivity( ACT_HL2MP_GESTURE_RANGE_ATTACK ) );
m_bFiring = m_iFireSequence != -1;
m_flFireCycle = 0;
// Be sure to stop reloading
m_bReloading = false;
m_flReloadCycle = 0;
}
else if ( playerAnim == PLAYER_ATTACK2 )
{
@ -155,6 +186,10 @@ void CSinglePlayerAnimState::SetPlayerAnimation( PLAYER_ANIM playerAnim )
#endif
m_bFiring = m_iFireSequence != -1;
m_flFireCycle = 0;
// Be sure to stop reloading
m_bReloading = false;
m_flReloadCycle = 0;
}
else if ( playerAnim == PLAYER_JUMP )
{
@ -162,6 +197,7 @@ void CSinglePlayerAnimState::SetPlayerAnimation( PLAYER_ANIM playerAnim )
if (!m_bJumping)
{
m_bJumping = true;
m_bDuckJumping = (GetOuter()->GetFlags() & FL_DUCKING) ? true : false; //m_pPlayer->m_nButtons & IN_DUCK;
m_bFirstJumpFrame = true;
m_flJumpStartTime = gpGlobals->curtime;
}
@ -184,13 +220,13 @@ void CSinglePlayerAnimState::SetPlayerAnimation( PLAYER_ANIM playerAnim )
if (m_iWeaponSwitchSequence != -1)
{
// clear other events that might be playing in our layer
m_bPlayingMisc = false;
//m_bPlayingMisc = false;
m_bReloading = false;
m_bWeaponSwitching = true;
m_flWeaponSwitchCycle = 0;
m_flMiscBlendOut = 0.1f;
m_flMiscBlendIn = 0.1f;
//m_flMiscBlendOut = 0.1f;
//m_flMiscBlendIn = 0.1f;
m_bMiscNoOverride = false;
}
}
@ -199,19 +235,19 @@ void CSinglePlayerAnimState::SetPlayerAnimation( PLAYER_ANIM playerAnim )
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Activity CSinglePlayerAnimState::TranslateActivity( Activity actDesired )
Activity CMapbasePlayerAnimState::TranslateActivity( Activity actDesired )
{
#ifdef CLIENT_DLL
return actDesired;
#if defined(CLIENT_DLL) && !defined(MAPBASE_MP)
return actDesired;
#else
return m_pPlayer->Weapon_TranslateActivity( actDesired );
return actDesired; // m_pPlayer->Weapon_TranslateActivity( actDesired ); (TODO)
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CSinglePlayerAnimState::HandleJumping()
bool CMapbasePlayerAnimState::HandleJumping()
{
if ( m_bJumping )
{
@ -230,6 +266,7 @@ bool CSinglePlayerAnimState::HandleJumping()
if ( m_pOuter->GetFlags() & FL_ONGROUND || GetOuter()->GetGroundEntity() != NULL)
{
m_bJumping = false;
m_bDuckJumping = false;
RestartMainSequence(); // Reset the animation.
}
}
@ -242,20 +279,21 @@ bool CSinglePlayerAnimState::HandleJumping()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CSinglePlayerAnimState::ComputeSequences( CStudioHdr *pStudioHdr )
void CMapbasePlayerAnimState::ComputeSequences( CStudioHdr *pStudioHdr )
{
CBasePlayerAnimState::ComputeSequences(pStudioHdr);
ComputeFireSequence();
ComputeMiscSequence();
ComputeReloadSequence();
ComputeWeaponSwitchSequence();
ComputeWeaponSwitchSequence();
ComputeRelaxSequence();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CSinglePlayerAnimState::AddMiscSequence( int iSequence, float flBlendIn, float flBlendOut, float flPlaybackRate, bool bHoldAtEnd, bool bOnlyWhenStill )
void CMapbasePlayerAnimState::AddMiscSequence( int iSequence, float flBlendIn, float flBlendOut, float flPlaybackRate, bool bHoldAtEnd, bool bOnlyWhenStill )
{
Assert( iSequence != -1 );
@ -265,7 +303,7 @@ void CSinglePlayerAnimState::AddMiscSequence( int iSequence, float flBlendIn, fl
m_bPlayingMisc = true;
m_bMiscHoldAtEnd = bHoldAtEnd;
m_bReloading = false;
//m_bReloading = false;
m_flMiscCycle = 0;
m_bMiscOnlyWhenStill = bOnlyWhenStill;
m_bMiscNoOverride = true;
@ -275,12 +313,36 @@ void CSinglePlayerAnimState::AddMiscSequence( int iSequence, float flBlendIn, fl
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CSinglePlayerAnimState::ClearAnimationState()
void CMapbasePlayerAnimState::StartWeaponRelax()
{
if (m_bWeaponRelaxing)
return;
m_bWeaponRelaxing = true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMapbasePlayerAnimState::StopWeaponRelax()
{
if (!m_bWeaponRelaxing)
return;
m_bWeaponRelaxing = false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMapbasePlayerAnimState::ClearAnimationState()
{
m_bJumping = false;
m_bDuckJumping = false;
m_bFiring = false;
m_bReloading = false;
m_bWeaponSwitching = false;
m_bWeaponRelaxing = false;
m_bPlayingMisc = false;
m_flReloadBlendIn = 0.0f;
m_flReloadBlendOut = 0.0f;
@ -289,32 +351,42 @@ void CSinglePlayerAnimState::ClearAnimationState()
CBasePlayerAnimState::ClearAnimationState();
}
void CSinglePlayerAnimState::ClearAnimationLayers()
void CMapbasePlayerAnimState::ClearAnimationLayers()
{
VPROF( "CBasePlayerAnimState::ClearAnimationLayers" );
// In c_baseanimatingoverlay.cpp, this sometimes desyncs from the interpolated overlays and causes a crash in ResizeAnimationLayerCallback when the player dies. (pVec->Count() != pVecIV->Count())
// Is there a better way of getting around this issue?
#ifndef CLIENT_DLL
if ( !m_pOuter )
return;
m_pOuter->SetNumAnimOverlays( NUM_LAYERS_WANTED );
for ( int i=0; i < m_pOuter->GetNumAnimOverlays(); i++ )
{
// If we're not using aim sequences, leave the aim layers alone
// (allows them to be used outside of anim state)
if ( !m_AnimConfig.m_bUseAimSequences && i <= NUM_AIMSEQUENCE_LAYERS )
continue;
m_pOuter->GetAnimOverlay( i )->SetOrder( CBaseAnimatingOverlay::MAX_OVERLAYS );
#ifndef CLIENT_DLL
m_pOuter->GetAnimOverlay( i )->m_fFlags = 0;
#endif
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CSinglePlayerAnimState::CalcAimLayerSequence( float *flCycle, float *flAimSequenceWeight, bool bForceIdle )
int CMapbasePlayerAnimState::CalcAimLayerSequence( float *flCycle, float *flAimSequenceWeight, bool bForceIdle )
{
// TODO?
return m_pOuter->LookupSequence( "soldier_Aim_9_directions" );
}
void CSinglePlayerAnimState::UpdateLayerSequenceGeneric( int iLayer, bool &bEnabled,
void CMapbasePlayerAnimState::UpdateLayerSequenceGeneric( int iLayer, bool &bEnabled,
float &flCurCycle, int &iSequence, bool bWaitAtEnd,
float fBlendIn, float fBlendOut, bool bMoveBlend, float fPlaybackRate, bool bUpdateCycle /* = true */ )
{
@ -383,7 +455,7 @@ void CSinglePlayerAnimState::UpdateLayerSequenceGeneric( int iLayer, bool &bEnab
pLayer->m_flPlaybackRate = fPlaybackRate;
pLayer->m_flWeight = 1.0f;
if (iLayer == RELOADSEQUENCE_LAYER)
if (fBlendIn > 0.0f || fBlendOut > 0.0f)
{
// blend this layer in and out for smooth reloading
if (flCurCycle < fBlendIn && fBlendIn>0)
@ -418,25 +490,91 @@ void CSinglePlayerAnimState::UpdateLayerSequenceGeneric( int iLayer, bool &bEnab
pLayer->SetOrder( iLayer );
}
void CSinglePlayerAnimState::ComputeFireSequence()
void CMapbasePlayerAnimState::ComputeFireSequence()
{
UpdateLayerSequenceGeneric( FIRESEQUENCE_LAYER, m_bFiring, m_flFireCycle, m_iFireSequence, false );
}
void CSinglePlayerAnimState::ComputeReloadSequence()
void CMapbasePlayerAnimState::ComputeReloadSequence()
{
UpdateLayerSequenceGeneric( RELOADSEQUENCE_LAYER, m_bReloading, m_flReloadCycle, m_iReloadSequence, false, m_flReloadBlendIn, m_flReloadBlendOut, false, m_fReloadPlaybackRate );
}
void CSinglePlayerAnimState::ComputeWeaponSwitchSequence()
void CMapbasePlayerAnimState::ComputeWeaponSwitchSequence()
{
UpdateLayerSequenceGeneric( RELOADSEQUENCE_LAYER, m_bWeaponSwitching, m_flWeaponSwitchCycle, m_iWeaponSwitchSequence, false, 0, 0.5f );
}
// does misc gestures if we're not firing
void CSinglePlayerAnimState::ComputeMiscSequence()
void CMapbasePlayerAnimState::ComputeRelaxSequence()
{
UpdateLayerSequenceGeneric( RELOADSEQUENCE_LAYER, m_bPlayingMisc, m_flMiscCycle, m_iMiscSequence, m_bMiscHoldAtEnd, m_flMiscBlendIn, m_flMiscBlendOut, m_bMiscOnlyWhenStill, m_fMiscPlaybackRate );
bool bRelaxing = m_bWeaponRelaxing;
float flRelaxSpeed = 0.05f;
if ((m_bFiring && m_flFireCycle < 1.0f) || m_bReloading)
{
// Keep weapon raised
bRelaxing = false;
flRelaxSpeed = 0.5f;
//GetOuter()->SetPoseParameter( GetOuter()->LookupPoseParameter( "weapon_lower" ), 0.0f );
}
if (bRelaxing ? m_flWeaponRelaxAmount != 1.0f : m_flWeaponRelaxAmount != 0.0f)
{
if (bRelaxing)
m_flWeaponRelaxAmount += flRelaxSpeed;
else
m_flWeaponRelaxAmount -= flRelaxSpeed;
m_flWeaponRelaxAmount = clamp( m_flWeaponRelaxAmount, 0.0f, 1.0f );
GetOuter()->SetPoseParameter( GetOuter()->LookupPoseParameter( "weapon_lower" ), m_flWeaponRelaxAmount );
/*int nPose = GetOuter()->LookupPoseParameter( "weapon_lower" );
if (nPose != -1)
{
float flValue = RemapValClamped( (m_flWeaponRelaxTime - gpGlobals->curtime), 0.0f, 0.5f, 0.0f, 1.0f );
if (flValue <= 0.0f)
{
// All done
m_flWeaponRelaxTime = FLT_MAX;
}
if (m_bWeaponRelaxing)
flValue = 1.0f - flValue;
GetOuter()->SetPoseParameter( nPose, SimpleSpline( flValue ) );
}*/
}
else if (bRelaxing)
{
GetOuter()->SetPoseParameter( GetOuter()->LookupPoseParameter( "weapon_lower" ), 1.0f );
}
/*bool bEnabled = m_bWeaponRelaxing;
bool bUpdateCycle = true;
if (bEnabled)
{
if (m_flWeaponRelaxCycle >= 0.5f)
{
// Pause at 0.5
m_flWeaponRelaxCycle = 0.5f;
bUpdateCycle = false;
}
}
else if (m_flWeaponRelaxCycle < 1.0f)
{
// Make sure we exit the relax
bEnabled = true;
}
UpdateLayerSequenceGeneric( AIMSEQUENCE_LAYER, bEnabled, m_flWeaponRelaxCycle, m_iWeaponRelaxSequence, false, 0.5f, 0.5f, false, 1.0f, bUpdateCycle );*/
}
// does misc gestures if we're not firing
void CMapbasePlayerAnimState::ComputeMiscSequence()
{
UpdateLayerSequenceGeneric( MISCSEQUENCE_LAYER, m_bPlayingMisc, m_flMiscCycle, m_iMiscSequence, m_bMiscHoldAtEnd, m_flMiscBlendIn, m_flMiscBlendOut, m_bMiscOnlyWhenStill, m_fMiscPlaybackRate );
}
//-----------------------------------------------------------------------------
@ -444,7 +582,7 @@ void CSinglePlayerAnimState::ComputeMiscSequence()
// Input : -
// Output : float
//-----------------------------------------------------------------------------
float CSinglePlayerAnimState::GetCurrentMaxGroundSpeed()
float CMapbasePlayerAnimState::GetCurrentMaxGroundSpeed()
{
CStudioHdr *pStudioHdr = GetOuter()->GetModelPtr();
@ -481,11 +619,42 @@ float CSinglePlayerAnimState::GetCurrentMaxGroundSpeed()
return speed;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
inline bool CMapbasePlayerAnimState::ShouldUseAimPoses( void ) const
{
return GetAimPoseBlend() > 0.0f;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CMapbasePlayerAnimState::GetAimPoseBlend( void ) const
{
if (!GetOuter()->MyCombatCharacterPointer() || !GetOuter()->MyCombatCharacterPointer()->GetActiveWeapon()
|| GetOuter()->MyCombatCharacterPointer()->GetActiveWeapon()->IsEffectActive( EF_NODRAW ))
return 0.0f;
return 1.0f - m_flWeaponRelaxAmount;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CMapbasePlayerAnimState::SetOuterBodyYaw( float flValue )
{
float flAimPoseBlend = GetAimPoseBlend();
GetOuter()->SetPoseParameter( GetOuter()->LookupPoseParameter( "aim_yaw" ), flValue * flAimPoseBlend );
return CBasePlayerAnimState::SetOuterBodyYaw( flValue * (1.0f - flAimPoseBlend) );
}
//-----------------------------------------------------------------------------
// Purpose: Override for backpeddling
// Input : dt -
//-----------------------------------------------------------------------------
void CSinglePlayerAnimState::ComputePoseParam_BodyYaw( void )
void CMapbasePlayerAnimState::ComputePoseParam_BodyYaw( void )
{
CBasePlayerAnimState::ComputePoseParam_BodyYaw();
@ -495,7 +664,7 @@ void CSinglePlayerAnimState::ComputePoseParam_BodyYaw( void )
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CSinglePlayerAnimState::ComputePoseParam_BodyLookYaw( void )
void CMapbasePlayerAnimState::ComputePoseParam_BodyLookYaw( void )
{
// See if we even have a blender for pitch
int upper_body_yaw = GetOuter()->LookupPoseParameter( "aim_yaw" );
@ -632,7 +801,7 @@ void CSinglePlayerAnimState::ComputePoseParam_BodyLookYaw( void )
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CSinglePlayerAnimState::ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr )
void CMapbasePlayerAnimState::ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr )
{
// Get pitch from v_angle
float flPitch = m_flEyePitch;
@ -643,16 +812,19 @@ void CSinglePlayerAnimState::ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr
}
flPitch = clamp( flPitch, -90, 90 );
//float flAimPoseBlend = GetAimPoseBlend();
// See if we have a blender for pitch
GetOuter()->SetPoseParameter( pStudioHdr, "aim_pitch", flPitch );
GetOuter()->SetPoseParameter( pStudioHdr, "head_pitch", flPitch );
ComputePoseParam_HeadPitch( pStudioHdr );
//ComputePoseParam_HeadPitch( pStudioHdr );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CSinglePlayerAnimState::ComputePoseParam_HeadPitch( CStudioHdr *pStudioHdr )
void CMapbasePlayerAnimState::ComputePoseParam_HeadPitch( CStudioHdr *pStudioHdr )
{
// Get pitch from v_angle
int iHeadPitch = GetOuter()->LookupPoseParameter("head_pitch");

View File

@ -14,8 +14,8 @@
//
//=============================================================================//
#ifndef SINGLEPLAYER_ANIMSTATE_H
#define SINGLEPLAYER_ANIMSTATE_H
#ifndef MAPBASE_PLAYERANIMSTATE_H
#define MAPBASE_PLAYERANIMSTATE_H
#ifdef _WIN32
#pragma once
#endif
@ -34,10 +34,10 @@
#define SP_ANIM_STATE 1
#endif
class CSinglePlayerAnimState : public CBasePlayerAnimState
class CMapbasePlayerAnimState : public CBasePlayerAnimState
{
public:
CSinglePlayerAnimState( CBasePlayer *pPlayer );
CMapbasePlayerAnimState( CBasePlayer *pPlayer );
Activity CalcMainActivity();
int CalcAimLayerSequence( float *flCycle, float *flAimSequenceWeight, bool bForceIdle );
@ -49,10 +49,16 @@ public:
void ComputeSequences( CStudioHdr *pStudioHdr );
void AddMiscSequence( int iSequence, float flBlendIn = 0.0f, float flBlendOut = 0.0f, float flPlaybackRate = 1.0f, bool bHoldAtEnd = false, bool bOnlyWhenStill = false );
void StartWeaponRelax();
void StopWeaponRelax();
void ClearAnimationState();
void ClearAnimationLayers();
inline bool IsJumping() const { return m_bJumping; }
inline bool IsDuckJumping() const { return m_bDuckJumping; }
private:
bool HandleJumping();
@ -60,13 +66,19 @@ private:
void ComputeFireSequence();
void ComputeReloadSequence();
void ComputeWeaponSwitchSequence();
void ComputeRelaxSequence();
void ComputeMiscSequence();
void UpdateLayerSequenceGeneric( int iLayer, bool &bEnabled, float &flCurCycle,
int &iSequence, bool bWaitAtEnd,
float fBlendIn=0.15f, float fBlendOut=0.15f, bool bMoveBlend = false,
float fBlendIn=0.0f, float fBlendOut=0.0f, bool bMoveBlend = false,
float fPlaybackRate=1.0f, bool bUpdateCycle = true );
bool ShouldUseAimPoses() const;
float GetAimPoseBlend() const;
float SetOuterBodyYaw( float flValue );
void ComputePoseParam_BodyYaw( void );
void ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr );
void ComputePoseParam_BodyLookYaw( void );
@ -76,6 +88,7 @@ private:
// Current state variables.
bool m_bJumping; // Set on a jump event.
bool m_bDuckJumping; // Jump started while ducking
float m_flJumpStartTime;
bool m_bFirstJumpFrame;
@ -90,6 +103,9 @@ private:
float m_flWeaponSwitchCycle;
int m_iWeaponSwitchSequence;
bool m_bWeaponRelaxing;
float m_flWeaponRelaxAmount;
bool m_bPlayingMisc;
float m_flMiscCycle, m_flMiscBlendOut, m_flMiscBlendIn;
int m_iMiscSequence;
@ -105,6 +121,6 @@ private:
float m_flFireCycle;
};
CSinglePlayerAnimState *CreatePlayerAnimationState( CBasePlayer *pPlayer );
CMapbasePlayerAnimState *CreatePlayerAnimationState( CBasePlayer *pPlayer );
#endif // SINGLEPLAYER_ANIMSTATE_H
#endif // MAPBASE_PLAYERANIMSTATE_H

View File

@ -29,6 +29,10 @@
#include "AI_ResponseSystem.h"
#include "mapbase/SystemConvarMod.h"
#include "gameinterface.h"
#include "mapbase/choreosentence.h"
#endif
#if defined(HL2_DLL) || defined(HL2_CLIENT_DLL)
#include "protagonist_system.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
@ -87,11 +91,17 @@ char g_iszGameName[128];
#ifdef GAME_DLL
// Default player configuration
char g_szDefaultPlayerModel[MAX_PATH];
bool g_bDefaultPlayerLegs;
bool g_bDefaultPlayerDrawExternally;
char g_szDefaultHandsModel[MAX_PATH];
int g_iDefaultHandsSkin;
int g_iDefaultHandsBody;
#ifdef HL2_DLL
// See protagonist_system.h
char g_szDefaultProtagonist[MAX_PROTAGONIST_NAME];
#endif
#endif
enum
@ -109,12 +119,15 @@ enum
MANIFEST_HUDLAYOUT,
#else
MANIFEST_TALKER,
//MANIFEST_SENTENCES,
MANIFEST_CHOREOSENTENCES,
MANIFEST_ACTBUSY,
#endif
#ifdef MAPBASE_VSCRIPT
MANIFEST_VSCRIPT,
#endif
#if defined(HL2_DLL) || defined(HL2_CLIENT_DLL)
MANIFEST_PROTAGONISTS, // See protagonist_system.h
#endif
// Must always be kept below
MANIFEST_NUM_TYPES,
@ -149,12 +162,15 @@ static const ManifestType_t gm_szManifestFileStrings[MANIFEST_NUM_TYPES] = {
{ "hudlayout", "mapbase_load_hudlayout", "Should we load map-specific HUD layout overrides? e.g. \"maps/<mapname>_hudlayout.res\"" },
#else
{ "talker", "mapbase_load_talker", "Should we load map-specific talker files? e.g. \"maps/<mapname>_talker.txt\"" },
//{ "sentences", "mapbase_load_sentences", "Should we load map-specific sentences? e.g. \"maps/<mapname>_sentences.txt\"" },
{ "choreosentences", "mapbase_load_choreosentences", "Should we load map-specific choreo sentences? e.g. \"maps/<mapname>_choreosentences.txt\"" },
{ "actbusy", "mapbase_load_actbusy", "Should we load map-specific actbusy files? e.g. \"maps/<mapname>_actbusy.txt\"" },
#endif
#ifdef MAPBASE_VSCRIPT
{ "vscript", "mapbase_load_vscript", "Should we load map-specific VScript map spawn files? e.g. \"maps/<mapname>_mapspawn.nut\"" },
#endif
#if defined(HL2_DLL) || defined(HL2_CLIENT_DLL)
{ "protagonists", "mapbase_load_protagonists", "Should we load map-specific protagonist files? e.g. \"maps/<mapname>_protagonists.txt\"" },
#endif
};
//-----------------------------------------------------------------------------
@ -237,11 +253,16 @@ public:
#ifdef GAME_DLL
Q_strncpy( g_szDefaultPlayerModel, gameinfo->GetString( "player_default_model", "models/player.mdl" ), sizeof( g_szDefaultPlayerModel ) );
g_bDefaultPlayerLegs = gameinfo->GetBool( "player_default_legs", false );
g_bDefaultPlayerDrawExternally = gameinfo->GetBool( "player_default_draw_externally", false );
Q_strncpy( g_szDefaultHandsModel, gameinfo->GetString( "player_default_hands", "models/weapons/v_hands.mdl" ), sizeof( g_szDefaultHandsModel ) );
g_iDefaultHandsSkin = gameinfo->GetInt( "player_default_hands_skin", 0 );
g_iDefaultHandsBody = gameinfo->GetInt( "player_default_hands_body", 0 );
#ifdef HL2_DLL
Q_strncpy( g_szDefaultProtagonist, gameinfo->GetString( "player_default_protagonist", "" ), sizeof( g_szDefaultProtagonist ) );
#endif
#endif
}
gameinfo->deleteThis();
@ -467,13 +488,16 @@ public:
LoadResponseSystemFile(value); //PrecacheCustomResponseSystem( value );
} break;
//case MANIFEST_SOUNDSCAPES: { g_SoundscapeSystem.AddSoundscapeFile(value); } break;
//case MANIFEST_SENTENCES: { engine->PrecacheSentenceFile(value); } break;
case MANIFEST_CHOREOSENTENCES: { LoadChoreoSentenceFile(value); } break;
#ifdef HL2_DLL
case MANIFEST_ACTBUSY: { ParseCustomActbusyFile(value); } break;
#endif
#endif
#ifdef MAPBASE_VSCRIPT
case MANIFEST_VSCRIPT: { VScriptRunScript(value, false); } break;
#endif
#if defined(HL2_DLL) || defined(HL2_CLIENT_DLL)
case MANIFEST_PROTAGONISTS: { g_ProtagonistSystem.LoadProtagonistFile( value ); } break;
#endif
}
}
@ -614,6 +638,7 @@ public:
#else
void LoadCustomTalkerFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_TALKER, false ); }
void LoadCustomActbusyFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_ACTBUSY, false ); }
void LoadCustomChoreoSentenceFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_CHOREOSENTENCES, false ); }
#endif
const char *GetModName() { return g_iszGameName; }
@ -662,6 +687,7 @@ BEGIN_SCRIPTDESC_ROOT( CMapbaseSystem, SCRIPT_SINGLETON "All-purpose Mapbase sys
#else
DEFINE_SCRIPTFUNC( LoadCustomTalkerFile, "Loads a custom talker file." )
DEFINE_SCRIPTFUNC( LoadCustomActbusyFile, "Loads a custom actbusy file." )
DEFINE_SCRIPTFUNC( LoadCustomChoreoSentenceFile, "Loads a custom choreo sentence file." )
#endif
DEFINE_SCRIPTFUNC( GetModName, "Gets the name of the mod. This is the name which shows up on Steam, RPC, etc." )

View File

@ -0,0 +1,732 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: System to easily switch between player characters.
//
// Author: Blixibon
//
//=============================================================================//
#include "cbase.h"
#include "protagonist_system.h"
#include "weapon_parse.h"
#include "filesystem.h"
#include "hl2_player_shared.h"
#ifdef HL2MP
#include "hl2mp_gamerules.h"
#endif
CProtagonistSystem g_ProtagonistSystem;
//=============================================================================
//=============================================================================
bool CProtagonistSystem::Init()
{
return true;
}
void CProtagonistSystem::Shutdown()
{
PurgeProtagonists();
}
void CProtagonistSystem::LevelInitPreEntity()
{
LoadProtagonistManifest( "scripts/protagonists/protagonists_manifest.txt" );
}
void CProtagonistSystem::LevelShutdownPostEntity()
{
PurgeProtagonists();
}
//----------------------------------------------------------------------------
void CProtagonistSystem::LoadProtagonistManifest( const char *pszFile )
{
KeyValues *pManifest = new KeyValues( "ProtagonistsManifest" );
if (pManifest->LoadFromFile( filesystem, pszFile ))
{
FOR_EACH_SUBKEY( pManifest, pSubKey )
{
LoadProtagonistFile( pSubKey->GetString() );
}
}
pManifest->deleteThis();
}
void CProtagonistSystem::LoadProtagonistFile( const char *pszFile )
{
KeyValues *pManifest = new KeyValues( "Protagonists" );
if (pManifest->LoadFromFile( filesystem, pszFile ))
{
FOR_EACH_SUBKEY( pManifest, pProtagKey )
{
if (!pProtagKey->GetName()[0])
continue;
ProtagonistData_t *pProtag = FindProtagonist( pProtagKey->GetName() );
if (!pProtag)
{
pProtag = &m_Protagonists[m_Protagonists.AddToTail()];
V_strncpy( pProtag->szName, pProtagKey->GetName(), sizeof( pProtag->szName ) );
}
FOR_EACH_SUBKEY( pProtagKey, pSubKey )
{
const char *pszSubKeyName = pSubKey->GetName();
//----------------------------------------------------------------------------
// Metadata
//----------------------------------------------------------------------------
if (FStrEq( pszSubKeyName, "inherits_from" ))
{
char szParents[128];
V_strncpy( szParents, pSubKey->GetString(), sizeof( szParents ) );
char *pszToken = strtok( szParents, ";" );
for (; pszToken != NULL; pszToken = strtok( NULL, ";" ))
{
if (!pszToken || !*pszToken)
continue;
int nParent = FindProtagonistIndex( pszToken );
if (nParent >= 0)
{
pProtag->vecParents.FindAndRemove( nParent ); // If it already exists, it will be moved to the front
pProtag->vecParents.AddToTail( nParent );
}
}
}
#ifdef CLIENT_DLL
#else
//----------------------------------------------------------------------------
// Playermodel
//----------------------------------------------------------------------------
else if (V_strnicmp( pszSubKeyName, "playermodel", 11 ) == 0)
{
pszSubKeyName += 11;
if (!pszSubKeyName[0])
{
// Model
pProtag->pszPlayerModel = AllocateString( pSubKey->GetString() );
}
else if (FStrEq( pszSubKeyName, "_skin" ))
{
// Skin
pProtag->nPlayerSkin = pSubKey->GetInt();
}
else if (FStrEq( pszSubKeyName, "_body" ))
{
// Bodygroup
pProtag->nPlayerBody = pSubKey->GetInt();
}
}
//----------------------------------------------------------------------------
// Hands
//----------------------------------------------------------------------------
else if (V_strnicmp( pszSubKeyName, "hands", 5 ) == 0)
{
pszSubKeyName += 5;
if (!pszSubKeyName[0])
{
// Model
pProtag->pszHandModels[HANDRIG_DEFAULT] = AllocateString( pSubKey->GetString() );
}
else if (FStrEq( pszSubKeyName, "_skin" ))
{
// Skin
pProtag->nHandSkin = pSubKey->GetInt();
}
else if (FStrEq( pszSubKeyName, "_body" ))
{
// Bodygroup
pProtag->nHandBody = pSubKey->GetInt();
}
else
{
// Other Rigs
pszSubKeyName += 1;
for (int i = 0; i < NUM_HAND_RIG_TYPES; i++)
{
extern const char *pHandRigs[NUM_HAND_RIG_TYPES];
if (V_stricmp( pszSubKeyName, pHandRigs[i] ) == 0)
{
pProtag->pszHandModels[i] = AllocateString( pSubKey->GetString() );
break;
}
}
}
}
//----------------------------------------------------------------------------
// Responses
//----------------------------------------------------------------------------
else if (FStrEq( pszSubKeyName, "response_contexts" ))
{
pProtag->pszResponseContexts = AllocateString( pSubKey->GetString() );
}
//----------------------------------------------------------------------------
// Multiplayer
//----------------------------------------------------------------------------
else if (FStrEq( pszSubKeyName, "team" ))
{
#ifdef HL2MP
if (FStrEq( pszSubKeyName, "combine" ))
{
pProtag->nTeam = TEAM_COMBINE;
}
else if (FStrEq( pszSubKeyName, "rebels" ))
{
pProtag->nTeam = TEAM_REBELS;
}
else
#endif
{
// Try to get a direct integer
pProtag->nTeam = atoi( pszSubKeyName );
}
}
#endif
//----------------------------------------------------------------------------
// Weapon Data
//----------------------------------------------------------------------------
else if (V_strnicmp( pszSubKeyName, "wpn_viewmodels", 14 ) == 0)
{
// "wpn_viewmodels_c" = models which support arms
bool bHands = FStrEq( pszSubKeyName + 14, "_c" );
FOR_EACH_SUBKEY( pSubKey, pWeaponKey )
{
int i = pProtag->dictWpnData.Find( pWeaponKey->GetName() );
if (i == pProtag->dictWpnData.InvalidIndex())
i = pProtag->dictWpnData.Insert( pWeaponKey->GetName() );
pProtag->dictWpnData[i].pszVM = AllocateString( pWeaponKey->GetString() );
pProtag->dictWpnData[i].bUsesHands = bHands;
}
}
else if (FStrEq( pszSubKeyName, "wpn_data" )) // More expanded/explicit
{
FOR_EACH_SUBKEY( pSubKey, pWeaponKey )
{
int i = pProtag->dictWpnData.Find( pWeaponKey->GetName() );
if (i == pProtag->dictWpnData.InvalidIndex())
i = pProtag->dictWpnData.Insert( pWeaponKey->GetName() );
const char *pszVM = pWeaponKey->GetString( "viewmodel", NULL );
if (pszVM)
pProtag->dictWpnData[i].pszVM = AllocateString( pszVM );
const char *pszHandRig = pWeaponKey->GetString( "hand_rig", NULL );
if (pszHandRig)
{
// If there's a specific rig, then it must use hands
pProtag->dictWpnData[i].bUsesHands = true;
for (int j = 0; j < NUM_HAND_RIG_TYPES; j++)
{
extern const char *pHandRigs[NUM_HAND_RIG_TYPES];
if (V_stricmp( pszHandRig, pHandRigs[j] ) == 0)
{
pProtag->dictWpnData[i].nHandRig = j;
break;
}
}
}
else
{
KeyValues *pUsesHands = pWeaponKey->FindKey( "uses_hands" );
if (pUsesHands)
pProtag->dictWpnData[i].bUsesHands = pUsesHands->GetBool();
}
KeyValues *pVMFOV = pWeaponKey->FindKey( "viewmodel_fov" );
if (pVMFOV)
pProtag->dictWpnData[i].flVMFOV = pVMFOV->GetFloat();
}
}
}
}
}
pManifest->deleteThis();
}
//----------------------------------------------------------------------------
CProtagonistSystem::ProtagonistData_t *CProtagonistSystem::GetPlayerProtagonist( const CBasePlayer *pPlayer )
{
#ifdef CLIENT_DLL
const C_BaseHLPlayer *pHL2Player = static_cast<const C_BaseHLPlayer *>(pPlayer);
#else
const CHL2_Player *pHL2Player = static_cast<const CHL2_Player *>(pPlayer);
#endif
if (!pHL2Player)
return NULL;
int i = pHL2Player->GetProtagonistIndex();
if (i >= 0 && i < m_Protagonists.Count())
{
return &g_ProtagonistSystem.m_Protagonists[i];
}
return NULL;
}
CProtagonistSystem::ProtagonistData_t *CProtagonistSystem::FindProtagonist( const char *pszName )
{
FOR_EACH_VEC( m_Protagonists, i )
{
if (FStrEq( pszName, m_Protagonists[i].szName ))
{
return &m_Protagonists[i];
}
}
return NULL;
}
CProtagonistSystem::ProtagonistData_t *CProtagonistSystem::FindProtagonist( int nIndex )
{
if (nIndex < 0 || nIndex >= m_Protagonists.Count())
return NULL;
return &m_Protagonists[nIndex];
}
int CProtagonistSystem::FindProtagonistIndex( const char *pszName )
{
FOR_EACH_VEC( m_Protagonists, i )
{
if (FStrEq( pszName, m_Protagonists[i].szName ))
{
return i;
}
}
return -1;
}
const char *CProtagonistSystem::FindProtagonistByModel( const char *pszModelName )
{
#ifndef CLIENT_DLL
FOR_EACH_VEC( m_Protagonists, i )
{
if (m_Protagonists[i].pszPlayerModel && FStrEq( pszModelName, m_Protagonists[i].pszPlayerModel ))
{
return m_Protagonists[i].szName;
}
}
#endif
return NULL;
}
void CProtagonistSystem::PrecacheProtagonist( CBaseEntity *pSource, int nIdx )
{
#ifndef CLIENT_DLL
if (nIdx < 0)
return;
ProtagonistData_t &pProtag = m_Protagonists[nIdx];
// Playermodel
if (pProtag.pszPlayerModel)
{
pSource->PrecacheModel( pProtag.pszPlayerModel );
}
// Hands
for (int i = 0; i < NUM_HAND_RIG_TYPES; i++)
{
if (pProtag.pszHandModels[i])
{
pSource->PrecacheModel( pProtag.pszHandModels[i] );
}
}
// Weapon Data
FOR_EACH_DICT_FAST( pProtag.dictWpnData, i )
{
if (pProtag.dictWpnData[i].pszVM)
{
pSource->PrecacheModel( pProtag.dictWpnData[i].pszVM );
}
}
#endif
}
//----------------------------------------------------------------------------
#define GetProtagParamInner( name, ... ) DoGetProtagonist_##name( *pProtag, ##__VA_ARGS__ )
#define GetProtagParam( name, type, invoke, ... ) \
type CProtagonistSystem::GetProtagonist_##name( const CBasePlayer *pPlayer, ##__VA_ARGS__ ) \
{ \
ProtagonistData_t *pProtag = GetPlayerProtagonist( pPlayer ); \
if (!pProtag) \
return NULL; \
return invoke; \
} \
type CProtagonistSystem::GetProtagonist_##name( const int nProtagonistIndex, ##__VA_ARGS__ ) \
{ \
ProtagonistData_t *pProtag = FindProtagonist( nProtagonistIndex ); \
if (!pProtag) \
return NULL; \
return invoke; \
} \
#define GetProtagParamBody( name, type, invoke, body, ... ) \
type CProtagonistSystem::GetProtagonist_##name( const CBasePlayer *pPlayer, ##__VA_ARGS__ ) \
{ \
ProtagonistData_t *pProtag = GetPlayerProtagonist( pPlayer ); \
if (!pProtag) \
return NULL; \
type returnVal = invoke; \
body \
return returnVal; \
} \
type CProtagonistSystem::GetProtagonist_##name( const int nProtagonistIndex, ##__VA_ARGS__ ) \
{ \
ProtagonistData_t *pProtag = FindProtagonist( nProtagonistIndex ); \
if (!pProtag) \
return NULL; \
type returnVal = invoke; \
body \
return returnVal; \
} \
#ifdef CLIENT_DLL
#else
GetProtagParam( PlayerModel, const char*, GetProtagParamInner( PlayerModel ) )
GetProtagParam( PlayerModelSkin, int, GetProtagParamInner( PlayerModelSkin ) )
GetProtagParam( PlayerModelBody, int, GetProtagParamInner( PlayerModelBody ) )
GetProtagParam( HandModel, const char*, GetProtagParamInner( HandModel, pWeapon ), const CBaseCombatWeapon *pWeapon )
GetProtagParam( HandModelSkin, int, GetProtagParamInner( HandModelSkin, pWeapon ), const CBaseCombatWeapon *pWeapon )
GetProtagParam( HandModelBody, int, GetProtagParamInner( HandModelBody, pWeapon ), const CBaseCombatWeapon *pWeapon )
GetProtagParamBody( ResponseContexts, bool, GetProtagParamInner( ResponseContexts, pszContexts, nContextsSize ), {
if (pszContexts[0] != '\0')
{
// Replace trailing comma
int nLast = V_strlen( pszContexts )-1;
if (pszContexts[nLast] == ',')
{
Msg( "Removing trailing comma from \"%s\"\n", pszContexts );
pszContexts[nLast] = '\0';
}
}
}, char *pszContexts, int nContextsSize )
GetProtagParam( Team, int, GetProtagParamInner( Team ) )
#endif
GetProtagParam( ViewModel, const char*, GetProtagParamInner( ViewModel, pWeapon ), const CBaseCombatWeapon *pWeapon )
GetProtagParam( ViewModelFOV, float*, GetProtagParamInner( ViewModelFOV, pWeapon ), const CBaseCombatWeapon *pWeapon )
GetProtagParam( UsesHands, bool*, GetProtagParamInner( UsesHands, pWeapon ), const CBaseCombatWeapon *pWeapon )
GetProtagParam( HandRig, int*, GetProtagParamInner( HandRig, pWeapon ), const CBaseCombatWeapon *pWeapon )
//----------------------------------------------------------------------------
#define GetProtagonistRecurse( funcName, ... ) \
FOR_EACH_VEC( pProtag.vecParents, i ) \
{ \
ProtagonistData_t *pParent = FindProtagonist(pProtag.vecParents[i]); \
if (!pParent) \
continue; \
auto returnVar = funcName( *pParent, ##__VA_ARGS__ ); \
if (returnVar) \
return returnVar; \
} \
#define GetProtagonistRecurseNoReturn( funcName, ... ) \
FOR_EACH_VEC( pProtag.vecParents, i ) \
{ \
ProtagonistData_t *pParent = FindProtagonist(pProtag.vecParents[i]); \
if (!pParent) \
continue; \
funcName( *pParent, ##__VA_ARGS__ ); \
} \
#ifdef CLIENT_DLL
#else
const char *CProtagonistSystem::DoGetProtagonist_PlayerModel( ProtagonistData_t &pProtag )
{
if (pProtag.pszPlayerModel)
return pProtag.pszPlayerModel;
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_PlayerModel )
return NULL;
}
int CProtagonistSystem::DoGetProtagonist_PlayerModelSkin( ProtagonistData_t &pProtag )
{
if (pProtag.nPlayerSkin >= 0)
return pProtag.nPlayerSkin;
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_PlayerModelSkin )
return 0;
}
int CProtagonistSystem::DoGetProtagonist_PlayerModelBody( ProtagonistData_t &pProtag )
{
if (pProtag.nPlayerBody >= 0)
return pProtag.nPlayerBody;
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_PlayerModelBody )
return 0;
}
const char *CProtagonistSystem::DoGetProtagonist_HandModel( ProtagonistData_t &pProtag, const CBaseCombatWeapon *pWeapon )
{
int nRigType = pWeapon ? pWeapon->GetHandRig() : HANDRIG_DEFAULT;
if (pProtag.pszHandModels[nRigType])
return pProtag.pszHandModels[nRigType];
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_HandModel, pWeapon )
return NULL;
}
int CProtagonistSystem::DoGetProtagonist_HandModelSkin( ProtagonistData_t &pProtag, const CBaseCombatWeapon *pWeapon )
{
if (pProtag.nHandSkin >= 0)
return pProtag.nHandSkin;
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_HandModelSkin, pWeapon )
return NULL;
}
int CProtagonistSystem::DoGetProtagonist_HandModelBody( ProtagonistData_t &pProtag, const CBaseCombatWeapon *pWeapon )
{
if (pProtag.nHandBody >= 0)
return pProtag.nHandBody;
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_HandModelBody, pWeapon )
return NULL;
}
bool CProtagonistSystem::DoGetProtagonist_ResponseContexts( ProtagonistData_t &pProtag, char *pszContexts, int nContextsSize )
{
if (pProtag.pszResponseContexts)
{
V_strncat( pszContexts, pProtag.pszResponseContexts, nContextsSize );
V_strncat( pszContexts, ",", nContextsSize );
}
// Recursively search parent protagonists
GetProtagonistRecurseNoReturn( DoGetProtagonist_ResponseContexts, pszContexts, nContextsSize )
return pszContexts[0] != '\0';
}
int CProtagonistSystem::DoGetProtagonist_Team( ProtagonistData_t &pProtag )
{
if (pProtag.nTeam >= -1)
return pProtag.nTeam;
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_Team )
return TEAM_ANY;
}
#endif
const char *CProtagonistSystem::DoGetProtagonist_ViewModel( ProtagonistData_t &pProtag, const CBaseCombatWeapon *pWeapon )
{
FOR_EACH_DICT_FAST( pProtag.dictWpnData, i )
{
// HACKHACK: GetClassname is not const
if (!FStrEq( pProtag.dictWpnData.GetElementName( i ), const_cast<CBaseCombatWeapon*>(pWeapon)->GetClassname() ))
continue;
if (pProtag.dictWpnData[i].pszVM)
return pProtag.dictWpnData[i].pszVM;
break;
}
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_ViewModel, pWeapon )
return NULL;
}
float *CProtagonistSystem::DoGetProtagonist_ViewModelFOV( ProtagonistData_t &pProtag, const CBaseCombatWeapon *pWeapon )
{
FOR_EACH_DICT_FAST( pProtag.dictWpnData, i )
{
// HACKHACK: GetClassname is not const
if (!FStrEq( pProtag.dictWpnData.GetElementName( i ), const_cast<CBaseCombatWeapon*>(pWeapon)->GetClassname() ))
continue;
return &pProtag.dictWpnData[i].flVMFOV;
}
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_ViewModelFOV, pWeapon )
return NULL;
}
bool *CProtagonistSystem::DoGetProtagonist_UsesHands( ProtagonistData_t &pProtag, const CBaseCombatWeapon *pWeapon )
{
FOR_EACH_DICT_FAST( pProtag.dictWpnData, i )
{
// HACKHACK: GetClassname is not const
if (!FStrEq( pProtag.dictWpnData.GetElementName( i ), const_cast<CBaseCombatWeapon*>(pWeapon)->GetClassname() ))
continue;
return &pProtag.dictWpnData[i].bUsesHands;
}
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_UsesHands, pWeapon )
return NULL;
}
int *CProtagonistSystem::DoGetProtagonist_HandRig( ProtagonistData_t &pProtag, const CBaseCombatWeapon *pWeapon )
{
FOR_EACH_DICT_FAST( pProtag.dictWpnData, i )
{
// HACKHACK: GetClassname is not const
if (!FStrEq( pProtag.dictWpnData.GetElementName( i ), const_cast<CBaseCombatWeapon*>(pWeapon)->GetClassname() ))
continue;
return &pProtag.dictWpnData[i].nHandRig;
}
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_HandRig, pWeapon )
return NULL;
}
//----------------------------------------------------------------------------
const char *CProtagonistSystem::FindString( const char *string )
{
unsigned short i = m_Strings.Find( string );
return i == m_Strings.InvalidIndex() ? NULL : m_Strings[i];
}
const char *CProtagonistSystem::AllocateString( const char *string )
{
int i = m_Strings.Find( string );
if (i != m_Strings.InvalidIndex())
{
return m_Strings[i];
}
int len = Q_strlen( string );
char *out = new char[len + 1];
Q_memcpy( out, string, len );
out[len] = 0;
return m_Strings[m_Strings.Insert( out )];
}
//----------------------------------------------------------------------------
void CProtagonistSystem::PurgeProtagonists()
{
m_Protagonists.RemoveAll();
for (unsigned int i = 0; i < m_Strings.Count(); i++)
{
delete m_Strings[i];
}
m_Strings.Purge();
}
void CProtagonistSystem::PrintProtagonistData()
{
Msg( "PROTAGONISTS\n\n" );
FOR_EACH_VEC( m_Protagonists, i )
{
ProtagonistData_t &pProtag = m_Protagonists[i];
Msg( "\t\"%s\"\n", pProtag.szName );
Msg( "\t\tParents: %i\n", pProtag.vecParents.Count() );
if (pProtag.vecParents.Count() > 0)
{
FOR_EACH_VEC( pProtag.vecParents, j )
{
ProtagonistData_t *pParent = FindProtagonist( pProtag.vecParents[j] );
if (!pParent)
continue;
Msg( "\t\t\t\"%s\"\n", pParent->szName );
}
}
#ifdef CLIENT_DLL
#else
if (pProtag.pszPlayerModel)
Msg( "\t\tPlayer model: \"%s\" (%i, %i)\n", pProtag.pszPlayerModel, pProtag.nPlayerSkin, pProtag.nPlayerBody );
for (int j = 0; j < NUM_HAND_RIG_TYPES; j++)
{
extern const char *pHandRigs[NUM_HAND_RIG_TYPES];
if (pProtag.pszHandModels[j])
Msg( "\t\tHand %s model: \"%s\" (%i, %i)\n", pHandRigs[j], pProtag.pszHandModels[j], pProtag.nPlayerSkin, pProtag.nPlayerBody );
}
// Weapon Data
Msg( "\t\tWeapon Data: %i\n", pProtag.dictWpnData.Count() );
FOR_EACH_DICT_FAST( pProtag.dictWpnData, j )
{
Msg( "\t\t\t%s\n", pProtag.dictWpnData.GetElementName(j) );
if (pProtag.dictWpnData[j].pszVM)
Msg( "\t\t\t\tViewmodel: \"%s\"\n", pProtag.dictWpnData[j].pszVM );
Msg( "\t\t\t\tUses hands: %d, hand rig: %i\n", pProtag.dictWpnData[j].bUsesHands, pProtag.dictWpnData[j].nHandRig );
}
#endif
}
}
//----------------------------------------------------------------------------
CON_COMMAND_SHARED( protagonist_reload, "Reloads protagonist data" )
{
#ifdef CLIENT_DLL
if (C_BasePlayer::GetLocalPlayer() == NULL)
{
Msg( "Must be in a level\n" );
return;
}
#endif
g_ProtagonistSystem.PurgeProtagonists();
g_ProtagonistSystem.LoadProtagonistManifest( "scripts/protagonists/protagonists_manifest.txt" );
}
CON_COMMAND_SHARED( protagonist_dump, "Dumps protagonist data" )
{
#ifdef CLIENT_DLL
if (C_BasePlayer::GetLocalPlayer() == NULL)
{
Msg( "Must be in a level\n" );
return;
}
#endif
g_ProtagonistSystem.PrintProtagonistData();
}

View File

@ -0,0 +1,153 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: System to easily switch between player characters.
//
// Author: Blixibon
//
//=============================================================================//
#ifndef PROTAGONIST_SYSTEM_H
#define PROTAGONIST_SYSTEM_H
#ifdef _WIN32
#pragma once
#endif
#ifdef CLIENT_DLL
#include "c_baseplayer.h"
#else
#include "player.h"
#endif
#define MAX_PROTAGONIST_NAME 32
//=============================================================================
//=============================================================================
class CProtagonistSystem : public CAutoGameSystem
{
public:
CProtagonistSystem() : m_Strings( 256, 0, &StringLessThan ) { }
~CProtagonistSystem() { PurgeProtagonists(); }
private:
struct ProtagonistData_t
{
ProtagonistData_t()
{
#ifndef CLIENT_DLL
for (int i = 0; i < NUM_HAND_RIG_TYPES; i++)
pszHandModels[i] = NULL;
#endif
}
char szName[MAX_PROTAGONIST_NAME];
CUtlVector<int> vecParents;
#ifdef CLIENT_DLL
#else
// Playermodel
const char *pszPlayerModel = NULL;
int nPlayerSkin = -1;
int nPlayerBody = -1;
// Hands
const char *pszHandModels[NUM_HAND_RIG_TYPES];
int nHandSkin = -1;
int nHandBody = -1;
// Responses
const char *pszResponseContexts = NULL;
// Multiplayer
int nTeam = TEAM_ANY;
#endif
// Weapon Data
struct WeaponDataOverride_t
{
const char *pszVM = NULL;
bool bUsesHands = false;
int nHandRig = 0;
float flVMFOV = 0.0f;
};
CUtlDict<WeaponDataOverride_t> dictWpnData;
};
public:
bool Init();
void Shutdown();
void LevelInitPreEntity();
void LevelShutdownPostEntity();
//----------------------------------------------------------------------------
void LoadProtagonistManifest( const char *pszFile );
void LoadProtagonistFile( const char *pszFile );
//----------------------------------------------------------------------------
int FindProtagonistIndex( const char *pszName );
const char *FindProtagonistByModel( const char *pszModelName );
void PrecacheProtagonist( CBaseEntity *pSource, int nIdx );
//----------------------------------------------------------------------------
#define DeclareProtagonistFunc(type, name, ...) \
type GetProtagonist_##name( const CBasePlayer *pPlayer, ##__VA_ARGS__ ); \
type GetProtagonist_##name( const int nProtagonistIndex, ##__VA_ARGS__ ); \
private: \
type DoGetProtagonist_##name( ProtagonistData_t &pProtag, ##__VA_ARGS__ ); \
public: \
#ifdef CLIENT_DLL
#else
DeclareProtagonistFunc( const char*, PlayerModel )
DeclareProtagonistFunc( int, PlayerModelSkin )
DeclareProtagonistFunc( int, PlayerModelBody )
DeclareProtagonistFunc( const char*, HandModel, const CBaseCombatWeapon *pWeapon )
DeclareProtagonistFunc( int, HandModelSkin, const CBaseCombatWeapon *pWeapon )
DeclareProtagonistFunc( int, HandModelBody, const CBaseCombatWeapon *pWeapon )
DeclareProtagonistFunc( bool, ResponseContexts, char *pszContexts, int nContextsSize )
DeclareProtagonistFunc( int, Team )
#endif
DeclareProtagonistFunc( const char*, ViewModel, const CBaseCombatWeapon *pWeapon )
DeclareProtagonistFunc( float*, ViewModelFOV, const CBaseCombatWeapon *pWeapon )
DeclareProtagonistFunc( bool*, UsesHands, const CBaseCombatWeapon *pWeapon )
DeclareProtagonistFunc( int*, HandRig, const CBaseCombatWeapon *pWeapon )
//----------------------------------------------------------------------------
void PurgeProtagonists();
void PrintProtagonistData();
//----------------------------------------------------------------------------
private:
ProtagonistData_t *GetPlayerProtagonist( const CBasePlayer *pPlayer );
ProtagonistData_t *FindProtagonist( const char *pszName );
ProtagonistData_t *FindProtagonist( int nIndex );
//----------------------------------------------------------------------------
const char *FindString( const char *string );
const char *AllocateString( const char *string );
//----------------------------------------------------------------------------
private:
CUtlVector<ProtagonistData_t> m_Protagonists;
// Dedicated strings, copied from game string pool
CUtlRBTree<const char *> m_Strings;
};
extern CProtagonistSystem g_ProtagonistSystem;
#endif // PROTAGONIST_SYSTEM_H

View File

@ -14,6 +14,7 @@
#include "c_ai_basenpc.h"
#else
#include "ai_basenpc.h"
#include "ai_senses.h"
#include "globalstate.h"
#endif
@ -365,7 +366,8 @@ void RegisterSharedScriptConstants()
ScriptRegisterConstant( g_pScriptVM, ROPE_NO_GRAVITY, "Disable gravity on this rope. (for use in rope flags)" );
ScriptRegisterConstant( g_pScriptVM, ROPE_NUMFLAGS, "The number of rope flags recognized by the game." );
ScriptRegisterConstantNamed( g_pScriptVM, Vector( ROPE_GRAVITY ), "ROPE_GRAVITY", "Default rope gravity vector." );
static Vector vecRopeGravity( ROPE_GRAVITY );
ScriptRegisterConstantNamed( g_pScriptVM, vecRopeGravity, "ROPE_GRAVITY", "Default rope gravity vector." );
//
// Sounds
@ -435,8 +437,8 @@ void RegisterSharedScriptConstants()
#ifdef GAME_DLL
//
// AI Sounds
// (QueryHearSound hook can use these)
// AI Senses
// (NPC hooks can use these)
//
ScriptRegisterConstant( g_pScriptVM, SOUND_NONE, "Sound type used in QueryHearSound hooks, etc." );
ScriptRegisterConstant( g_pScriptVM, SOUND_COMBAT, "Sound type used in QueryHearSound hooks, etc." );
@ -489,6 +491,11 @@ void RegisterSharedScriptConstants()
ScriptRegisterConstantNamed( g_pScriptVM, (int)SOUNDENT_VOLUME_PISTOL, "SOUNDENT_VOLUME_PISTOL", "Sound volume preset for use in InsertAISound, etc." );
ScriptRegisterConstantNamed( g_pScriptVM, (int)SOUNDENT_VOLUME_EMPTY, "SOUNDENT_VOLUME_PISTOL", "Sound volume preset for use in InsertAISound, etc." );
ScriptRegisterConstantNamed( g_pScriptVM, (int)SEEN_ALL, "SEEN_ALL", "All NPC sight arrays. Used in GetFirstSeenEntity, etc." );
ScriptRegisterConstantNamed( g_pScriptVM, (int)SEEN_HIGH_PRIORITY, "SEEN_HIGH_PRIORITY", "NPC sight array for players. Used in GetFirstSeenEntity, etc." );
ScriptRegisterConstantNamed( g_pScriptVM, (int)SEEN_NPCS, "SEEN_NPCS", "NPC sight array for other NPCs. Used in GetFirstSeenEntity, etc." );
ScriptRegisterConstantNamed( g_pScriptVM, (int)SEEN_MISC, "SEEN_MISC", "NPC sight array for objects. Used in GetFirstSeenEntity, etc." );
//
// Capabilities
//

View File

@ -228,11 +228,10 @@ void ScriptDispatchSpawn( HSCRIPT hEntity )
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static HSCRIPT CreateDamageInfo( HSCRIPT hInflictor, HSCRIPT hAttacker, const Vector &vecForce, const Vector &vecDamagePos, float flDamage, int iDamageType )
static HSCRIPT_RC CreateDamageInfo( HSCRIPT hInflictor, HSCRIPT hAttacker, const Vector &vecForce, const Vector &vecDamagePos, float flDamage, int iDamageType )
{
// The script is responsible for deleting this via DestroyDamageInfo().
CTakeDamageInfo *damageInfo = new CTakeDamageInfo( ToEnt(hInflictor), ToEnt(hAttacker), flDamage, iDamageType );
HSCRIPT hScript = g_pScriptVM->RegisterInstance( damageInfo );
HSCRIPT hScript = g_pScriptVM->RegisterInstance( damageInfo, true );
damageInfo->SetDamagePosition( vecDamagePos );
damageInfo->SetDamageForce( vecForce );
@ -240,14 +239,8 @@ static HSCRIPT CreateDamageInfo( HSCRIPT hInflictor, HSCRIPT hAttacker, const Ve
return hScript;
}
static void DestroyDamageInfo( HSCRIPT hDamageInfo )
static void DestroyDamageInfo( HSCRIPT )
{
CTakeDamageInfo *pInfo = HScriptToClass< CTakeDamageInfo >( hDamageInfo );
if ( pInfo )
{
g_pScriptVM->RemoveInstance( hDamageInfo );
delete pInfo;
}
}
void ScriptCalculateExplosiveDamageForce( HSCRIPT info, const Vector &vecDir, const Vector &vecForceOrigin, float flScale )
@ -290,6 +283,8 @@ void ScriptGuessDamageForce( HSCRIPT info, const Vector &vecForceDir, const Vect
//
//-----------------------------------------------------------------------------
BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptGameTrace, "CGameTrace", "trace_t" )
DEFINE_SCRIPT_REFCOUNTED_INSTANCE()
DEFINE_SCRIPTFUNC( DidHitWorld, "Returns whether the trace hit the world entity or not." )
DEFINE_SCRIPTFUNC( DidHitNonWorldEntity, "Returns whether the trace hit something other than the world entity." )
DEFINE_SCRIPTFUNC( GetEntityIndex, "Returns the index of whatever entity this trace hit." )
@ -320,7 +315,7 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptGameTrace, "CGameTrace", "trace_t" )
DEFINE_SCRIPTFUNC( Surface, "" )
DEFINE_SCRIPTFUNC( Plane, "" )
DEFINE_SCRIPTFUNC( Destroy, "Deletes this instance. Important for preventing memory leaks." )
DEFINE_SCRIPTFUNC( Destroy, SCRIPT_HIDE )
END_SCRIPTDESC();
BEGIN_SCRIPTDESC_ROOT_NAMED( scriptsurfacedata_t, "surfacedata_t", "" )
@ -349,37 +344,28 @@ END_SCRIPTDESC();
CPlaneTInstanceHelper g_PlaneTInstanceHelper;
BEGIN_SCRIPTDESC_ROOT( cplane_t, "" )
DEFINE_SCRIPT_INSTANCE_HELPER( &g_PlaneTInstanceHelper )
BEGIN_SCRIPTDESC_ROOT_WITH_HELPER( cplane_t, "", &g_PlaneTInstanceHelper )
END_SCRIPTDESC();
static HSCRIPT ScriptTraceLineComplex( const Vector &vecStart, const Vector &vecEnd, HSCRIPT entIgnore, int iMask, int iCollisionGroup )
static HSCRIPT_RC ScriptTraceLineComplex( const Vector &vecStart, const Vector &vecEnd, HSCRIPT entIgnore, int iMask, int iCollisionGroup )
{
// The script is responsible for deleting this via Destroy().
CScriptGameTrace *tr = new CScriptGameTrace();
CBaseEntity *pIgnore = ToEnt( entIgnore );
UTIL_TraceLine( vecStart, vecEnd, iMask, pIgnore, iCollisionGroup, tr );
tr->RegisterSurface();
tr->RegisterPlane();
return tr->GetScriptInstance();
return g_pScriptVM->RegisterInstance( tr, true );
}
static HSCRIPT ScriptTraceHullComplex( const Vector &vecStart, const Vector &vecEnd, const Vector &hullMin, const Vector &hullMax,
static HSCRIPT_RC ScriptTraceHullComplex( const Vector &vecStart, const Vector &vecEnd, const Vector &hullMin, const Vector &hullMax,
HSCRIPT entIgnore, int iMask, int iCollisionGroup )
{
// The script is responsible for deleting this via Destroy().
CScriptGameTrace *tr = new CScriptGameTrace();
CBaseEntity *pIgnore = ToEnt( entIgnore );
UTIL_TraceHull( vecStart, vecEnd, hullMin, hullMax, iMask, pIgnore, iCollisionGroup, tr );
tr->RegisterSurface();
tr->RegisterPlane();
return tr->GetScriptInstance();
return g_pScriptVM->RegisterInstance( tr, true );
}
//-----------------------------------------------------------------------------
@ -450,12 +436,11 @@ void FireBulletsInfo_t::ScriptSetAdditionalIgnoreEnt( HSCRIPT value )
m_pAdditionalIgnoreEnt = ToEnt( value );
}
static HSCRIPT CreateFireBulletsInfo( int cShots, const Vector &vecSrc, const Vector &vecDirShooting,
static HSCRIPT_RC CreateFireBulletsInfo( int cShots, const Vector &vecSrc, const Vector &vecDirShooting,
const Vector &vecSpread, float iDamage, HSCRIPT pAttacker )
{
// The script is responsible for deleting this via DestroyFireBulletsInfo().
FireBulletsInfo_t *info = new FireBulletsInfo_t();
HSCRIPT hScript = g_pScriptVM->RegisterInstance( info );
HSCRIPT hScript = g_pScriptVM->RegisterInstance( info, true );
info->SetShots( cShots );
info->SetSource( vecSrc );
@ -467,14 +452,8 @@ static HSCRIPT CreateFireBulletsInfo( int cShots, const Vector &vecSrc, const Ve
return hScript;
}
static void DestroyFireBulletsInfo( HSCRIPT hBulletsInfo )
static void DestroyFireBulletsInfo( HSCRIPT )
{
FireBulletsInfo_t *pInfo = HScriptToClass< FireBulletsInfo_t >( hBulletsInfo );
if ( pInfo )
{
g_pScriptVM->RemoveInstance( hBulletsInfo );
delete pInfo;
}
}
//-----------------------------------------------------------------------------
@ -482,9 +461,7 @@ static void DestroyFireBulletsInfo( HSCRIPT hBulletsInfo )
//-----------------------------------------------------------------------------
CAnimEventTInstanceHelper g_AnimEventTInstanceHelper;
BEGIN_SCRIPTDESC_ROOT( scriptanimevent_t, "" )
DEFINE_SCRIPT_INSTANCE_HELPER( &g_AnimEventTInstanceHelper )
BEGIN_SCRIPTDESC_ROOT_WITH_HELPER( scriptanimevent_t, "", &g_AnimEventTInstanceHelper )
DEFINE_SCRIPTFUNC( GetEvent, "" )
DEFINE_SCRIPTFUNC( SetEvent, "" )
@ -508,7 +485,7 @@ bool CAnimEventTInstanceHelper::Get( void *p, const char *pszKey, ScriptVariant_
{
DevWarning( "VScript animevent_t.%s: animevent_t metamethod members are deprecated! Use 'script_help animevent_t' to see the correct functions.\n", pszKey );
animevent_t *ani = ((animevent_t *)p);
animevent_t *ani = &((scriptanimevent_t *)p)->event;
if (FStrEq( pszKey, "event" ))
variant = ani->event;
else if (FStrEq( pszKey, "options" ))
@ -531,18 +508,28 @@ bool CAnimEventTInstanceHelper::Set( void *p, const char *pszKey, ScriptVariant_
{
DevWarning( "VScript animevent_t.%s: animevent_t metamethod members are deprecated! Use 'script_help animevent_t' to see the correct functions.\n", pszKey );
animevent_t *ani = ((animevent_t *)p);
scriptanimevent_t *script_ani = ((scriptanimevent_t *)p);
animevent_t *ani = &script_ani->event;
if (FStrEq( pszKey, "event" ))
ani->event = variant;
{
return variant.AssignTo( &ani->event );
}
else if (FStrEq( pszKey, "options" ))
ani->options = variant;
{
char szOptions[64];
if (!variant.AssignTo( szOptions, sizeof( szOptions ) ))
{
return false;
}
script_ani->SetOptions( szOptions );
}
else if (FStrEq( pszKey, "cycle" ))
ani->cycle = variant;
return variant.AssignTo( &ani->cycle );
else if (FStrEq( pszKey, "eventtime" ))
ani->eventtime = variant;
return variant.AssignTo( &ani->eventtime );
else if (FStrEq( pszKey, "type" ))
ani->type = variant;
else if (FStrEq( pszKey, "source" ))
return variant.AssignTo( &ani->type );
else if (FStrEq( pszKey, "source" ) && variant.m_type == FIELD_HSCRIPT)
{
CBaseEntity *pEnt = ToEnt( variant.Get<HSCRIPT>() );
if (pEnt)
@ -1009,14 +996,14 @@ void RegisterSharedScriptFunctions()
#endif
ScriptRegisterFunction( g_pScriptVM, CreateDamageInfo, "" );
ScriptRegisterFunction( g_pScriptVM, DestroyDamageInfo, "" );
ScriptRegisterFunction( g_pScriptVM, DestroyDamageInfo, SCRIPT_HIDE );
ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalculateExplosiveDamageForce, "CalculateExplosiveDamageForce", "Fill out a damage info handle with a damage force for an explosive." );
ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalculateBulletDamageForce, "CalculateBulletDamageForce", "Fill out a damage info handle with a damage force for a bullet impact." );
ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalculateMeleeDamageForce, "CalculateMeleeDamageForce", "Fill out a damage info handle with a damage force for a melee impact." );
ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGuessDamageForce, "GuessDamageForce", "Try and guess the physics force to use." );
ScriptRegisterFunction( g_pScriptVM, CreateFireBulletsInfo, "" );
ScriptRegisterFunction( g_pScriptVM, DestroyFireBulletsInfo, "" );
ScriptRegisterFunction( g_pScriptVM, DestroyFireBulletsInfo, SCRIPT_HIDE );
ScriptRegisterFunctionNamed( g_pScriptVM, ScriptTraceLineComplex, "TraceLineComplex", "Complex version of TraceLine which takes 2 points, an ent to ignore, a trace mask, and a collision group. Returns a handle which can access all trace info." );
ScriptRegisterFunctionNamed( g_pScriptVM, ScriptTraceHullComplex, "TraceHullComplex", "Takes 2 points, min/max hull bounds, an ent to ignore, a trace mask, and a collision group to trace to a point using a hull. Returns a handle which can access all trace info." );
@ -1034,10 +1021,10 @@ void RegisterSharedScriptFunctions()
//
// Precaching
//
ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPrecacheModel, "PrecacheModel", "Precaches a model for later usage." );
ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPrecacheModel, "DoPrecacheModel", SCRIPT_ALIAS( "PrecacheModel", "Precaches a model for later usage." ) );
ScriptRegisterFunction( g_pScriptVM, PrecacheMaterial, "Precaches a material for later usage." );
ScriptRegisterFunction( g_pScriptVM, PrecacheParticleSystem, "Precaches a particle system for later usage." );
ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPrecacheOther, "PrecacheOther", "Precaches an entity class for later usage." );
ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPrecacheOther, "DoPrecacheOther", SCRIPT_ALIAS( "PrecacheOther", "Precaches an entity class for later usage." ) );
//
// NPCs

View File

@ -46,12 +46,14 @@ public:
class CSurfaceScriptHelper
{
public:
// This class is owned by CScriptGameTrace, and cannot be accessed without being initialised in CScriptGameTrace::RegisterSurface()
//CSurfaceScriptHelper() : m_pSurface(NULL), m_hSurfaceData(NULL) {}
CSurfaceScriptHelper() : m_pSurface(NULL), m_hSurfaceData(NULL) {}
~CSurfaceScriptHelper()
{
g_pScriptVM->RemoveInstance( m_hSurfaceData );
if ( m_hSurfaceData )
{
g_pScriptVM->RemoveInstance( m_hSurfaceData );
}
}
void Init( csurface_t *surf )
@ -98,16 +100,10 @@ class CScriptGameTrace : public CGameTrace
public:
CScriptGameTrace() : m_surfaceAccessor(NULL), m_planeAccessor(NULL)
{
m_hScriptInstance = g_pScriptVM->RegisterInstance( this );
}
~CScriptGameTrace()
{
if ( m_hScriptInstance )
{
g_pScriptVM->RemoveInstance( m_hScriptInstance );
}
if ( m_surfaceAccessor )
{
g_pScriptVM->RemoveInstance( m_surfaceAccessor );
@ -119,22 +115,6 @@ public:
}
}
void RegisterSurface()
{
m_surfaceHelper.Init( &surface );
m_surfaceAccessor = g_pScriptVM->RegisterInstance( &m_surfaceHelper );
}
void RegisterPlane()
{
m_planeAccessor = g_pScriptVM->RegisterInstance( &plane );
}
HSCRIPT GetScriptInstance() const
{
return m_hScriptInstance;
}
public:
float FractionLeftSolid() const { return fractionleftsolid; }
int HitGroup() const { return hitgroup; }
@ -154,15 +134,30 @@ public:
bool AllSolid() const { return allsolid; }
bool StartSolid() const { return startsolid; }
HSCRIPT Surface() const { return m_surfaceAccessor; }
HSCRIPT Plane() const { return m_planeAccessor; }
HSCRIPT Surface()
{
if ( !m_surfaceAccessor )
{
m_surfaceHelper.Init( &surface );
m_surfaceAccessor = g_pScriptVM->RegisterInstance( &m_surfaceHelper );
}
void Destroy() { delete this; }
return m_surfaceAccessor;
}
HSCRIPT Plane()
{
if ( !m_planeAccessor )
m_planeAccessor = g_pScriptVM->RegisterInstance( &plane );
return m_planeAccessor;
}
void Destroy() {}
private:
HSCRIPT m_surfaceAccessor;
HSCRIPT m_planeAccessor;
HSCRIPT m_hScriptInstance;
CSurfaceScriptHelper m_surfaceHelper;
@ -172,30 +167,47 @@ private:
//-----------------------------------------------------------------------------
// Exposes animevent_t to VScript
//-----------------------------------------------------------------------------
struct scriptanimevent_t : public animevent_t
struct scriptanimevent_t
{
int GetEvent() { return event; }
void SetEvent( int nEvent ) { event = nEvent; }
friend class CAnimEventTInstanceHelper;
const char *GetOptions() { return options; }
void SetOptions( const char *pOptions ) { options = pOptions; }
public:
scriptanimevent_t( animevent_t &event ) : event( event ), options( NULL ) { }
~scriptanimevent_t( ) { delete[] options; }
float GetCycle() { return cycle; }
void SetCycle( float flCycle ) { cycle = flCycle; }
int GetEvent() { return event.event; }
void SetEvent( int nEvent ) { event.event = nEvent; }
float GetEventTime() { return eventtime; }
void SetEventTime( float flEventTime ) { eventtime = flEventTime; }
const char *GetOptions() { return event.options; }
void SetOptions( const char *pOptions )
{
size_t len = strlen( pOptions );
delete[] options;
event.options = options = new char[len + 1];
memcpy( options, pOptions, len + 1 );
}
int GetType() { return type; }
void SetType( int nType ) { eventtime = type; }
float GetCycle() { return event.cycle; }
void SetCycle( float flCycle ) { event.cycle = flCycle; }
HSCRIPT GetSource() { return ToHScript( pSource ); }
float GetEventTime() { return event.eventtime; }
void SetEventTime( float flEventTime ) { event.eventtime = flEventTime; }
int GetType() { return event.type; }
void SetType( int nType ) { event.type = nType; }
HSCRIPT GetSource() { return ToHScript( event.pSource ); }
void SetSource( HSCRIPT hSource )
{
CBaseEntity *pEnt = ToEnt( hSource );
if (pEnt)
pSource = pEnt->GetBaseAnimating();
event.pSource = pEnt->GetBaseAnimating();
}
private:
animevent_t &event;
// storage for ScriptVariant_t string, which may be temporary
char *options;
};
class CAnimEventTInstanceHelper : public IScriptInstanceHelper

View File

@ -2920,7 +2920,7 @@ int CScriptGameEventListener::ListenToGameEvent( const char* szEvent, HSCRIPT hF
if ( bValid )
{
m_iContextHash = HashContext( szContext );
m_hCallback = hFunc;
m_hCallback = g_pScriptVM->CopyObject( hFunc );
m_bActive = true;
s_Listeners.AddToTail( this );
@ -3247,7 +3247,7 @@ public:
// NOTE: These two functions are new with Mapbase and have no Valve equivalent
static bool KeyValuesWrite( const char *szFile, HSCRIPT hInput );
static HSCRIPT KeyValuesRead( const char *szFile );
static HSCRIPT_RC KeyValuesRead( const char *szFile );
void LevelShutdownPostEntity()
{
@ -3433,7 +3433,7 @@ bool CScriptReadWriteFile::KeyValuesWrite( const char *szFile, HSCRIPT hInput )
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
HSCRIPT CScriptReadWriteFile::KeyValuesRead( const char *szFile )
HSCRIPT_RC CScriptReadWriteFile::KeyValuesRead( const char *szFile )
{
char pszFullName[MAX_PATH];
V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile );
@ -3458,7 +3458,7 @@ HSCRIPT CScriptReadWriteFile::KeyValuesRead( const char *szFile )
return NULL;
}
HSCRIPT hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, pKV, true ); // bAllowDestruct is supposed to automatically remove the involved KV
HSCRIPT hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, pKV );
return hScript;
}
@ -4609,9 +4609,9 @@ public:
CScriptConCommand( const char *name, HSCRIPT fn, const char *helpString, int flags, ConCommand *pLinked = NULL )
: BaseClass( name, this, helpString, flags, 0 ),
m_pLinked(pLinked),
m_hCallback(fn),
m_hCompletionCallback(NULL)
{
m_hCallback = g_pScriptVM->CopyObject( fn );
m_nCmdNameLen = V_strlen(name) + 1;
Assert( m_nCmdNameLen - 1 <= 128 );
}
@ -4701,7 +4701,7 @@ public:
BaseClass::m_pCommandCompletionCallback = this;
BaseClass::m_bHasCompletionCallback = true;
m_hCompletionCallback = fn;
m_hCompletionCallback = g_pScriptVM->CopyObject( fn );
}
else
{
@ -4720,7 +4720,8 @@ public:
if ( m_hCallback )
g_pScriptVM->ReleaseScript( m_hCallback );
m_hCallback = fn;
m_hCallback = g_pScriptVM->CopyObject( fn );
}
else
{
@ -4781,7 +4782,7 @@ public:
if (fn)
{
m_hCallback = fn;
m_hCallback = g_pScriptVM->CopyObject( fn );
BaseClass::InstallChangeCallback( (FnChangeCallback_t)ScriptConVarCallback );
}
else

View File

@ -60,6 +60,7 @@ public:
bool IsPredicted( void ) const { return m_iszClientScripts[0] != '\0'; }
const char* GetWeaponScriptName() { return m_iszWeaponScriptName[0] != '\0' ? m_iszWeaponScriptName : BaseClass::GetWeaponScriptName(); }
const char* GetName() const { return m_iszWeaponScriptName[0] != '\0' ? STRING( m_iClassname ) : BaseClass::GetName(); }
// Weapon selection
bool HasAnyAmmo( void ); // Returns true is weapon has ammo

View File

@ -33,6 +33,8 @@ END_DATADESC()
#ifdef MAPBASE_VSCRIPT
BEGIN_SCRIPTDESC_ROOT( CTakeDamageInfo, "Damage information handler." )
DEFINE_SCRIPT_REFCOUNTED_INSTANCE()
DEFINE_SCRIPTFUNC_NAMED( ScriptGetInflictor, "GetInflictor", "Gets the inflictor." )
DEFINE_SCRIPTFUNC_NAMED( ScriptSetInflictor, "SetInflictor", "Sets the inflictor." )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetWeapon, "GetWeapon", "Gets the weapon." )
@ -613,4 +615,4 @@ void CTakeDamageInfo::SetCritType( ECritType eType )
#define DMG_BLAST_SURFACE (1<<27) // A blast on the surface of water that cannot harm things underwater
#define DMG_DIRECT (1<<28)
#define DMG_BUCKSHOT (1<<29) // not quite a bullet. Little, rounder, different.
*/
*/

View File

@ -64,6 +64,7 @@ private:
#ifdef MAPBASE_VSCRIPT
// This is to ensure a dependency exists between the vscript library and the game DLLs
extern int vscript_token;
extern int vscript_debugger_port;
int vscript_token_hack = vscript_token;
#endif
@ -453,16 +454,30 @@ CON_COMMAND( script_debug, "Connect the vscript VM to the script debugger" )
if ( !IsCommandIssuedByServerAdmin() )
return;
#ifdef MAPBASE_VSCRIPT
#ifdef GAME_DLL
int port = 1212;
#else
int port = 1213;
#endif
#endif
if ( !g_pScriptVM )
{
#ifdef MAPBASE_VSCRIPT
CGWarning( 0, CON_GROUP_VSCRIPT, "Scripting disabled or no server running\n" );
vscript_debugger_port = port;
CGMsg( 0, CON_GROUP_VSCRIPT, "VScript VM is not running, waiting for it to attach the debugger to port %d...\n", port );
#else
Log_Warning( LOG_VScript, "Scripting disabled or no server running\n" );
#endif
return;
}
#ifdef MAPBASE_VSCRIPT
g_pScriptVM->ConnectDebugger( port );
#else
g_pScriptVM->ConnectDebugger();
#endif
}
#ifdef CLIENT_DLL
@ -586,7 +601,7 @@ void RunAutorunScripts()
//-----------------------------------------------------------------------------
static short VSCRIPT_SERVER_SAVE_RESTORE_VERSION = 2;
static short VSCRIPT_SERVER_SAVE_RESTORE_VERSION = 3;
//-----------------------------------------------------------------------------

View File

@ -30,12 +30,25 @@ inline bool VScriptRunScript( const char *pszScriptName, bool bWarnMissing = fal
#define DECLARE_ENT_SCRIPTDESC() ALLOW_SCRIPT_ACCESS(); virtual ScriptClassDesc_t *GetScriptDesc()
#ifdef MAPBASE_VSCRIPT
#define BEGIN_ENT_SCRIPTDESC( className, baseClass, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC( className, baseClass, description )
#define BEGIN_ENT_SCRIPTDESC_WITH_HELPER( className, baseClass, description, helper ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_WITH_HELPER( className, baseClass, description, helper )
#define BEGIN_ENT_SCRIPTDESC_ROOT( className, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT( className, description )
#define BEGIN_ENT_SCRIPTDESC_ROOT_WITH_HELPER( className, description, helper ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT_WITH_HELPER( className, description, helper )
#define BEGIN_ENT_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_NAMED( className, baseClass, scriptName, description )
#define BEGIN_ENT_SCRIPTDESC_NAMED_WITH_HELPER( className, baseClass, scriptName, description, helper ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_NAMED_WITH_HELPER( className, baseClass, scriptName, description, helper )
#define BEGIN_ENT_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT_NAMED( className, scriptName, description )
#define BEGIN_ENT_SCRIPTDESC_ROOT_NAMED_WITH_HELPER( className, scriptName, description, helper ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT_NAMED_WITH_HELPER( className, scriptName, description, helper )
#define _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ) template <> ScriptClassDesc_t * GetScriptDesc<className>( className *, bool ); ScriptClassDesc_t *className::GetScriptDesc() { return ::GetScriptDesc( this ); }
#else
#define BEGIN_ENT_SCRIPTDESC( className, baseClass, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC( className, baseClass, description )
#define BEGIN_ENT_SCRIPTDESC_ROOT( className, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT( className, description )
#define BEGIN_ENT_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_NAMED( className, baseClass, scriptName, description )
#define BEGIN_ENT_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT_NAMED( className, scriptName, description )
#define _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ) template <> ScriptClassDesc_t * GetScriptDesc<className>( className * ); ScriptClassDesc_t *className::GetScriptDesc() { return ::GetScriptDesc( this ); }
#endif
// Only allow scripts to create entities during map initialization
bool IsEntityCreationAllowedInScripts( void );

View File

@ -408,6 +408,7 @@ FileWeaponInfo_t::FileWeaponInfo_t()
m_flSwaySpeedScale = 1.0f;
szDroppedModel[0] = 0;
m_bUsesHands = false;
m_nHandRig = HANDRIG_DEFAULT;
m_nWeaponRestriction = WPNRESTRICT_NONE;
#endif
}
@ -417,6 +418,12 @@ extern ConVar hud_fastswitch;
#endif
#ifdef MAPBASE
const char* pHandRigs[NUM_HAND_RIG_TYPES] = {
"default",
"css",
"blender",
};
const char* pWeaponRestrictions[NUM_WEAPON_RESTRICTION_TYPES] = {
"none",
"player_only",
@ -493,6 +500,19 @@ void FileWeaponInfo_t::Parse( KeyValues *pKeyValuesData, const char *szWeaponNam
m_bUsesHands = ( pKeyValuesData->GetInt( "uses_hands", 0 ) != 0 ) ? true : false;
const char* pszHandRig = pKeyValuesData->GetString("hand_rig", nullptr);
if (pszHandRig)
{
for (int i = 0; i < NUM_HAND_RIG_TYPES; i++)
{
if (V_stricmp(pszHandRig, pHandRigs[i]) == 0)
{
m_nHandRig = i;
break;
}
}
}
const char* pszRestrictString = pKeyValuesData->GetString("usage_restriction", nullptr);
if (pszRestrictString)
{

View File

@ -59,6 +59,15 @@ class CHudTexture;
class KeyValues;
#ifdef MAPBASE
enum HandRigTypes_e
{
HANDRIG_DEFAULT, // Default HL2 rig
HANDRIG_CSS, // CS:S viewmodel rig
HANDRIG_BLENDER, // Blender IK rig
NUM_HAND_RIG_TYPES
};
enum WeaponUsageRestricions_e
{
WPNRESTRICT_NONE = 0,
@ -136,6 +145,7 @@ public:
char szDroppedModel[MAX_WEAPON_STRING]; // Model of this weapon when dropped on the ground
bool m_bUsesHands;
int m_nHandRig;
int m_nWeaponRestriction;
#endif

Some files were not shown because too many files have changed in this diff Show More