Merge pull request #429 from mapbase-source/develop

Mapbase v8.0
This commit is contained in:
Blixibon 2025-07-29 10:11:42 -05:00 committed by GitHub
commit 6cc2937f5a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
152 changed files with 35263 additions and 1719 deletions

7
.gitignore vendored
View File

@ -19,6 +19,8 @@ ipch
*.idb
*.vcxproj
*.sln
*.vcxproj.FileListAbsolute.txt
*.pdb
# OSX/Linux build products
*.mak
@ -49,6 +51,8 @@ client.dylib.dSYM/
server.dylib
server.dylib.dSYM/
sp/game/bin
# files generated by running a mod
config.cfg
@ -61,6 +65,7 @@ config.cfg
*.filters
*.vpc_crc
*.sentinel
*.recipe
# Backup files
*.bak
@ -90,4 +95,4 @@ sp/src/vgui2/*.dll
sp/src/vgui2/*.pdb
# Build support files
sp/src/devtools/gcc9+support.o
sp/src/devtools/gcc9+support.o

33
README
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
@ -52,6 +52,7 @@ Mapbase uses content from the following non-Source SDK 2013 Valve games or SDKs:
-- Alien Swarm (Used to port assets from the aforementioned SDK code features, e.g. game instructor icons)
-- Left 4 Dead (Used to port certain animations as well as assets from the aforementioned SDK code features, e.g. particle rain)
-- Half-Life: Source (Used to port friction tool textures)
-- Half-Life 2: Survivor (Used to port Gordon Freeman model)
Valve allows assets from these titles to be distributed for modding purposes. Note that ported assets are only used in the release build, not the code repository.
@ -118,7 +119,9 @@ Direct contributions:
- https://github.com/mapbase-source/source-sdk-2013/pull/245 (ViewPunch random fix by Mr0maks)
- 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/281 (npc_combinedropship DropStrider input by Bronzehawk75)
- 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,17 +129,33 @@ 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/source-sdk-2013/pull/412 (Compile tool negative threads support by Unusuario2)
- https://github.com/mapbase-source/source-sdk-2013/pull/414 (VRAD %alphatexture by SirYodaJedi)
- https://github.com/mapbase-source/source-sdk-2013/pull/415 (Parallax corrected cubemaps optimization and fix by Zeldaboy14 and White_Red_Dragons)
- https://github.com/mapbase-source/source-sdk-2013/pull/418 (Server ragdoll death poses by AnOldLady)
- https://github.com/mapbase-source/source-sdk-2013/pull/419 (npc_helicopter removal crash fix by Wikot235)
- https://github.com/mapbase-source/source-sdk-2013/pull/420 (sk_combine_head_dmg_multiplier cvar by Wikot235)
- https://github.com/mapbase-source/source-sdk-2013/pull/422 (Expanded HL2 NPC custom model support by Maestra Fenix [committed by Blixibon])
- 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)
- Demo autorecord code provided by Klems
- cc_emit crash fix provided by 1upD
- npc_barnacle poison zombie crash fix provided by Agrimar
- Custom HL2 ammo crate models created by Rykah (Textures created by Blixibon; This is asset-based and, aside from the SLAM crate, not reflected in the code)
- Combine lock hardware on door01_left.mdl created by Kralich (This is asset-based and not reflected in the code)
- npc_vehicledriver fixes provided by CrAzY
- npc_combine cover behavior patches provided by iohnnyboy
- logic_playmovie icon created by URAKOLOUY5 (This is asset-based and not reflected in the code)
- Dropship APC save/load fix provided by Cvoxulary
- c_arms support on HL2 viewmodels by Inaki and ReverendV92 (This is asset-based and not reflected in the code)
- Custom c_arms for HL2 characters by Notewell (This is asset-based and not reflected in the code)
== Contributions from samisalreadytaken:
=-- https://github.com/mapbase-source/source-sdk-2013/pull/47 (VScript utility/consistency changes)
@ -154,11 +173,19 @@ 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/302 (VScript debugger)
=-- 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)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/411 (VScript debugger cleanup)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/423 (GetPropFloatArray Vector indexing fix)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/431 (VScript save/restore and debugger fixes)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/441 (VScript instance helper fallback)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/447 (Update VScript sqdbg)
== Contributions from z33ky:
=-- https://github.com/mapbase-source/source-sdk-2013/pull/21 (Various GCC/Linux compilation fixes)
@ -172,7 +199,10 @@ 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)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/436 (VScript member function call safety)
== Contributions from Petercov:
=-- https://github.com/mapbase-source/source-sdk-2013/pull/182 (NPCs load dynamic interactions from all animation MDLs)
@ -183,6 +213,7 @@ Direct contributions:
=-- https://github.com/mapbase-source/source-sdk-2013/pull/230 (Caption fixes)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/231 (Sentence source bug fix)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/264 (Outputs for vgui_screen)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/427 (CBaseCombatWeapon::WeaponClassFromString typo fix [committed by vizzys])
//-------------------------------------------------------------------------------------------------------------------------

View File

@ -1531,35 +1531,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

@ -464,8 +464,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;
}
@ -224,8 +223,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;
}
@ -458,7 +465,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
@ -511,11 +518,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();
@ -534,6 +540,10 @@ int C_BaseCombatWeapon::DrawModel( int flags )
return iDraw;
}
else
{
return 0;
}
}
#endif
if ( localplayer->IsObserver() && GetOwner() )
@ -551,6 +561,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

@ -433,8 +433,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

@ -125,6 +125,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
void RecvProxy_LocalVelocityX( const CRecvProxyData *pData, void *pStruct, void *pOut );
void RecvProxy_LocalVelocityY( const CRecvProxyData *pData, void *pStruct, void *pOut );
@ -280,6 +290,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
@ -1486,13 +1497,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
@ -1501,12 +1685,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())
@ -3057,13 +3245,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;
}
@ -3084,6 +3280,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.
@ -3100,7 +3353,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
{
@ -3108,7 +3361,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

@ -293,6 +293,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 );
@ -468,6 +477,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;
@ -502,6 +514,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();
@ -556,6 +572,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

@ -47,8 +47,12 @@ $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\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\game_timer.cpp"
$File "$SRCDIR\game\shared\mapbase\game_timer.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]
@ -71,6 +75,7 @@ $Project
$File "mapbase\c_vgui_text_display.cpp"
$File "mapbase\c_weapon_custom_hl2.cpp"
$File "mapbase\mapbase_autocubemap.cpp"
$File "mapbase\hud_generic_timer.cpp"
}
$Folder "HL2 DLL"

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 MAPBASE
RecvPropInt( RECVINFO( m_nProtagonistIndex ) ),
#endif
#ifdef SP_ANIM_STATE
RecvPropFloat( RECVINFO( m_flAnimRenderYaw ) ),
RecvPropFloat( RECVINFO( m_flAnimRenderZ ) ),
#endif
END_RECV_TABLE()
@ -78,6 +82,8 @@ C_BaseHLPlayer::C_BaseHLPlayer()
#ifdef MAPBASE
ConVarRef scissor("r_flashlightscissor");
scissor.SetValue("0");
m_nProtagonistIndex = -1;
#endif
}
@ -103,6 +109,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:
//-----------------------------------------------------------------------------
@ -664,24 +686,76 @@ 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()
{
if (m_flAnimRenderZ != 0.0f)
{
static Vector vecRender;
vecRender = BaseClass::GetRenderOrigin();
vecRender.z += m_flAnimRenderZ;
return vecRender;
}
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

@ -16,7 +16,7 @@
#include "c_hl2_playerlocaldata.h"
#if !defined( HL2MP ) && defined ( MAPBASE )
#include "mapbase/singleplayer_animstate.h"
#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 );
@ -62,8 +63,14 @@ public:
bool IsWeaponLowered( void ) { return m_HL2Local.m_bWeaponLowered; }
#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:
@ -86,11 +93,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
CSinglePlayerAnimState *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

@ -0,0 +1,511 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: Generic timer HUD element for HL2-based mods
//
// Author: Blixibon
//
//=============================================================================//
#include "cbase.h"
#include "hud.h"
#include "hud_macros.h"
#include "hudelement.h"
#include "hud_numericdisplay.h"
#include "iclientmode.h"
#include <vgui/ILocalize.h>
#include <vgui/ISurface.h>
#include <vgui_controls/AnimationController.h>
#include <vgui_controls/EditablePanel.h>
#include "baseviewport.h"
#include "mapbase/game_timer.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern bool g_bAnyGameTimerActive;
ConVar hud_timer_max_bars( "hud_timer_max_bars", "15" );
//-----------------------------------------------------------------------------
// Purpose: Timer panel
//-----------------------------------------------------------------------------
class CHudGenericGameTimer : public CHudElement, public vgui::Panel
{
DECLARE_CLASS_SIMPLE( CHudGenericGameTimer, vgui::Panel );
public:
CHudGenericGameTimer( const char *pElementName );
virtual void Init( void );
virtual void VidInit( void );
virtual void Reset( void );
virtual void OnThink();
virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
virtual bool ShouldDraw( void );
virtual void Paint();
C_GameTimer *GetTimer();
bool FindTimer();
void SetDisplayValue( int value ) { m_iValue = value; }
void SetLabelText( const wchar_t *text );
virtual void PaintNumbers( vgui::HFont font, int xpos, int ypos );
virtual void PaintBars( int xpos, int ypos, int wide );
int GetTimerIndex() const { return m_iTimerIndex; }
private:
void SetTimerLabel( void );
private:
int m_iTimerIndex;
bool m_bTimerDisplayed;
bool m_bPlayingFadeout;
float m_flShutoffTime;
bool m_bTimerPaused;
bool m_bTimerWarned;
int m_iValue;
int m_iTimerMaxLength;
bool m_bShowTimeRemaining;
int m_nProgressBarMax;
int m_nProgressBarOverride;
float m_flOverrideX, m_flOverrideY;
wchar_t m_LabelText[32];
int m_nLabelWidth, m_nLabelHeight;
int m_nTimerWidth, m_nTimerHeight;
CPanelAnimationVarAliasType( float, m_flMinWidth, "MinWidth", "100", "proportional_float" );
CPanelAnimationVarAliasType( float, m_flBorder, "Border", "24", "proportional_float" );
CPanelAnimationVarAliasType( float, m_flLabelTimerSpacing, "LabelTimerSpacing", "2", "proportional_float" );
CPanelAnimationVarAliasType( float, m_flBarHeight, "BarHeight", "5", "proportional_float" );
CPanelAnimationVarAliasType( float, m_flBarChunkGap, "BarChunkGap", "3", "proportional_float" );
CPanelAnimationVarAliasType( float, m_flBarVerticalGap, "BarVerticalGap", "8", "proportional_float" );
CPanelAnimationVar( int, m_iBarDisabledAlpha, "BarDisabledAlpha", "70" );
CPanelAnimationVar( float, m_flBlur, "Blur", "0" );
CPanelAnimationVar( vgui::HFont, m_hNumberFont, "NumberFont", "HudHintTextLarge" );
CPanelAnimationVar( vgui::HFont, m_hNumberGlowFont, "NumberGlowFont", "HudHintTextLarge" );
CPanelAnimationVar( vgui::HFont, m_hTextFont, "TextFont", "HudHintTextSmall" );
};
DECLARE_HUDELEMENT( CHudGenericGameTimer );
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CHudGenericGameTimer::CHudGenericGameTimer( const char *pElementName )
: CHudElement( pElementName ),
BaseClass( NULL, "HudGenericGameTimer" )
{
Panel *pParent = g_pClientMode->GetViewport();
SetParent( pParent );
SetHiddenBits( HIDEHUD_MISCSTATUS );
m_iTimerIndex = 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::Init()
{
Reset();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::Reset()
{
SetDisplayValue( 0 );
SetAlpha( 0 );
m_flShutoffTime = 0.0f;
m_bTimerDisplayed = false;
m_iTimerIndex = 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::VidInit()
{
Reset();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::OnThink()
{
C_BasePlayer *local = C_BasePlayer::GetLocalPlayer();
if ( !local )
{
// Not ready to init!
return;
}
// If our timer has been disabled, close the menu
C_GameTimer *pTimer = GetTimer();
if ( !pTimer || pTimer->IsDisabled() || pTimer->IsMarkedForDeletion() )
{
if ( m_flShutoffTime == 0.0f )
{
m_flShutoffTime = gpGlobals->curtime + 1.0f;
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "GenericGameTimerClose" );
}
m_iTimerIndex = 0;
return;
}
// Check pause state
if ( m_bTimerPaused && !pTimer->IsTimerPaused() )
{
m_bTimerPaused = false;
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "GenericGameTimerPulse" );
}
else if ( pTimer->IsTimerPaused() )
{
m_bTimerPaused = true;
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "GenericGameTimerPulseOnce" );
}
float flTimeRemaining = floorf( pTimer->GetTimeRemaining() );
// Check warn state
float flWarnTime = pTimer->GetWarnTime();
if (flWarnTime > 0.0f)
{
if ( m_bTimerWarned && flTimeRemaining > flWarnTime )
{
// Turn back to normal
m_bTimerWarned = false;
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "GenericGameTimerUnwarn" );
}
else if ( flTimeRemaining <= flWarnTime )
{
// Turn red
m_bTimerWarned = true;
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "GenericGameTimerWarn" );
}
}
SetDisplayValue( flTimeRemaining );
m_iTimerMaxLength = pTimer->GetTimerMaxLength();
m_bShowTimeRemaining = pTimer->ShowTimeRemaining();
m_nProgressBarMax = pTimer->GetProgressBarMaxSegments();
if (m_nProgressBarMax == -1)
{
// Default to timer max length
m_nProgressBarMax = min( m_iTimerMaxLength, hud_timer_max_bars.GetInt() );
}
m_nProgressBarOverride = pTimer->GetProgressBarOverride();
pTimer->GetPositionOverride( m_flOverrideX, m_flOverrideY );
}
//-----------------------------------------------------------------------------
// Purpose: hud scheme settings
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::ApplySchemeSettings( vgui::IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
SetPaintBackgroundEnabled( false );
// set our size
int screenWide, screenTall;
int x, y;
GetPos( x, y );
GetHudSize( screenWide, screenTall );
SetBounds( 0, y, screenWide, screenTall - y );
// Start with a 0:00 timer
m_nTimerWidth = (vgui::surface()->GetCharacterWidth( m_hNumberFont, '0' ) * 3);
m_nTimerWidth += vgui::surface()->GetCharacterWidth( m_hNumberFont, ':' );
m_nTimerHeight = vgui::surface()->GetFontTall( m_hNumberFont );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CHudGenericGameTimer::ShouldDraw( void )
{
if ( !CHudElement::ShouldDraw() )
return false;
if ( !m_bTimerDisplayed )
{
// Check if we should find a new timer
if ( g_bAnyGameTimerActive )
{
if ( FindTimer() )
return true;
}
return false;
}
// check for if menu is set to disappear
if ( m_flShutoffTime > 0 && m_flShutoffTime <= gpGlobals->curtime )
{
// times up, shutoff
m_bTimerDisplayed = false;
return false;
}
if ( m_flOverrideX == -1.0f && m_flOverrideY == -1.0f )
{
// Don't overlap with the weapon selection HUD
vgui::Panel *pWeaponSelection = GetParent()->FindChildByName( "HudWeaponSelection" );
if ( pWeaponSelection && pWeaponSelection->IsVisible() )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
inline C_GameTimer *CHudGenericGameTimer::GetTimer()
{
if (m_iTimerIndex <= 0)
return NULL;
// Need to do a dynamic_cast because this entity index could've been replaced since it was last used
return dynamic_cast<C_GameTimer*>(C_BaseEntity::Instance( m_iTimerIndex ));
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CHudGenericGameTimer::FindTimer()
{
// Find a new timer
for ( int i = 0; i < IGameTimerAutoList::AutoList().Count(); i++ )
{
C_GameTimer *pTimer = static_cast<C_GameTimer *>( IGameTimerAutoList::AutoList()[i] );
if ( !pTimer->IsDisabled() && !pTimer->IsMarkedForDeletion() )
{
m_iTimerIndex = pTimer->entindex();
break;
}
}
C_GameTimer *pTimer = GetTimer();
if ( pTimer )
{
// New timer selected, set the caption
wchar_t wszLabelText[128];
const wchar_t *wLocalizedItem = g_pVGuiLocalize->Find( pTimer->GetTimerCaption() );
if (wLocalizedItem)
{
V_wcsncpy( wszLabelText, wLocalizedItem, sizeof( wszLabelText ) );
}
else
{
g_pVGuiLocalize->ConvertANSIToUnicode( pTimer->GetTimerCaption(), wszLabelText, sizeof( wszLabelText ) );
}
SetLabelText( wszLabelText );
m_flShutoffTime = 0;
m_bTimerDisplayed = true;
m_bTimerPaused = pTimer->IsTimerPaused();
m_bTimerWarned = false;
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_bTimerPaused ? "GenericGameTimerShow" : "GenericGameTimerShowFlash" );
SetDisplayValue( ceil( pTimer->GetTimeRemaining() ) );
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::SetLabelText( const wchar_t *text )
{
wcsncpy( m_LabelText, text, sizeof( m_LabelText ) / sizeof( wchar_t ) );
m_LabelText[(sizeof( m_LabelText ) / sizeof( wchar_t )) - 1] = 0;
m_nLabelWidth = 0;
m_nLabelHeight = 0;
if (m_LabelText[0] != '\0')
{
int nLabelLen = V_wcslen( m_LabelText );
for (int ch = 0; ch < nLabelLen; ch++)
{
m_nLabelWidth += vgui::surface()->GetCharacterWidth( m_hTextFont, m_LabelText[ch] );
}
m_nLabelHeight = vgui::surface()->GetFontTall( m_hTextFont );
m_nLabelHeight += m_flLabelTimerSpacing;
}
}
//-----------------------------------------------------------------------------
// Purpose: paints a number at the specified position
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::PaintNumbers( vgui::HFont font, int xpos, int ypos )
{
vgui::surface()->DrawSetTextFont(font);
int nTimeToDisplay = m_iValue;
if ( !m_bShowTimeRemaining )
{
nTimeToDisplay = m_iTimerMaxLength - nTimeToDisplay;
}
int nMinutes = 0;
int nSeconds = 0;
wchar_t unicode[6];
if (nTimeToDisplay <= 0)
{
nMinutes = 0;
nSeconds = 0;
}
else
{
nMinutes = nTimeToDisplay / 60;
nSeconds = nTimeToDisplay % 60;
}
V_snwprintf( unicode, ARRAYSIZE(unicode), L"%d:%02d", nMinutes, nSeconds );
vgui::surface()->DrawSetTextPos( xpos, ypos );
vgui::surface()->DrawUnicodeString( unicode );
// draw the overbright blur
for (float fl = m_flBlur; fl > 0.0f; fl -= 1.0f)
{
if (fl >= 1.0f)
{
vgui::surface()->DrawSetTextPos( xpos, ypos );
vgui::surface()->DrawUnicodeString( unicode );
}
else
{
// draw a percentage of the last one
Color col = GetFgColor();
col[3] *= fl;
vgui::surface()->DrawSetTextColor( col );
vgui::surface()->DrawSetTextPos( xpos, ypos );
vgui::surface()->DrawUnicodeString( unicode );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: paints bars at the specified position
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::PaintBars( int xpos, int ypos, int wide )
{
// get bar chunks
int barChunkWidth = (wide / m_nProgressBarMax) - m_flBarChunkGap;
int enabledChunks;
if (m_nProgressBarOverride > -1)
enabledChunks = m_nProgressBarOverride;
else
{
enabledChunks = (int)floorf( ((float)m_iValue / (float)m_iTimerMaxLength) * (float)m_nProgressBarMax );
if (!m_bShowTimeRemaining)
{
enabledChunks = m_nProgressBarMax - enabledChunks;
}
}
// draw the suit power bar
vgui::surface()->DrawSetColor( GetFgColor() );
for (int i = 0; i < enabledChunks; i++)
{
vgui::surface()->DrawFilledRect( xpos, ypos, xpos + barChunkWidth, ypos + m_flBarHeight );
xpos += (barChunkWidth + m_flBarChunkGap);
}
// draw the exhausted portion of the bar.
Color clrExhausted = GetFgColor();
clrExhausted[3] = ((float)clrExhausted[3] / 255.0f) * m_iBarDisabledAlpha;
vgui::surface()->DrawSetColor( clrExhausted );
for (int i = enabledChunks; i < m_nProgressBarMax; i++)
{
vgui::surface()->DrawFilledRect( xpos, ypos, xpos + barChunkWidth, ypos + m_flBarHeight );
xpos += (barChunkWidth + m_flBarChunkGap);
}
}
//-----------------------------------------------------------------------------
// Purpose: renders the vgui panel
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::Paint()
{
// Check for 00:00 timer
int nTimerWidth = m_nTimerWidth;
if (m_iValue > 600)
nTimerWidth += vgui::surface()->GetCharacterWidth( m_hNumberFont, '0' );
// draw the background
int wide = max( nTimerWidth, m_nLabelWidth ) + m_flBorder;
if (m_flMinWidth > wide)
wide = m_flMinWidth;
int tall = m_nTimerHeight + m_nLabelHeight + (m_flBorder/2);
if (m_nProgressBarMax > 0)
tall += m_flBarHeight + m_flBarVerticalGap;
else
tall += (m_flBorder/2);
int screenW, screenH;
GetHudSize( screenW, screenH );
float flScalarX = (m_flOverrideX != -1.0f ? m_flOverrideX : 0.5f);
float flScalarY = (m_flOverrideY != -1.0f ? m_flOverrideY : 0.05f);
int x = (screenW - wide) * flScalarX;
int y = (screenH - tall) * flScalarY;
DrawBox( x, y, wide, tall, GetBgColor(), 1.0f);
y += (m_flBorder/2);
// draw our bars
if (m_nProgressBarMax > 0)
{
int barX = x + (m_flBorder/2);
PaintBars( barX, y, wide - m_flBorder );
y += m_flBarVerticalGap;
}
if (m_nLabelHeight > 0)
{
vgui::surface()->DrawSetTextFont( m_hTextFont );
vgui::surface()->DrawSetTextColor( GetFgColor() );
vgui::surface()->DrawSetTextPos( x + ((wide - m_nLabelWidth) * 0.5f), y );
vgui::surface()->DrawUnicodeString( m_LabelText );
y += m_nLabelHeight;
}
// draw our numbers
vgui::surface()->DrawSetTextColor( GetFgColor() );
int digitX = x + ((wide - nTimerWidth) * 0.5f);
int digitY = y;
PaintNumbers( m_hNumberFont, digitX, digitY );
}

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

@ -37,6 +37,9 @@ char g_szPrelocalisedMenuString[MAX_MENU_STRING];
DECLARE_HUDELEMENT( CHudMenu );
DECLARE_HUD_MESSAGE( CHudMenu, ShowMenu );
#ifdef MAPBASE
DECLARE_HUD_MESSAGE( CHudMenu, ShowMenuComplex );
#endif
//
//-----------------------------------------------------
@ -71,6 +74,9 @@ CHudMenu::CHudMenu( const char *pElementName ) :
void CHudMenu::Init( void )
{
HOOK_HUD_MESSAGE( CHudMenu, ShowMenu );
#ifdef MAPBASE
HOOK_HUD_MESSAGE( CHudMenu, ShowMenuComplex );
#endif
m_bMenuTakesInput = false;
m_bMenuDisplayed = false;
@ -81,6 +87,24 @@ void CHudMenu::Init( void )
Reset();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudMenu::LevelInit()
{
CHudElement::LevelInit();
#ifdef MAPBASE
if (m_bMapDefinedMenu)
{
// Fixes menu retaining on level change/reload
// TODO: Would non-map menus benefit from this as well?
m_bMenuTakesInput = false;
m_bMenuDisplayed = false;
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
@ -114,7 +138,11 @@ void CHudMenu::OnThink()
float flSelectionTimeout = MENU_SELECTION_TIMEOUT;
// If we've been open for a while without input, hide
#ifdef MAPBASE
if ( m_bMenuDisplayed && ( gpGlobals->curtime - m_flSelectionTime > flSelectionTimeout && !m_bMapDefinedMenu ) )
#else
if ( m_bMenuDisplayed && ( gpGlobals->curtime - m_flSelectionTime > flSelectionTimeout ) )
#endif
{
m_bMenuDisplayed = false;
}
@ -130,11 +158,24 @@ bool CHudMenu::ShouldDraw( void )
return false;
// check for if menu is set to disappear
if ( m_flShutoffTime > 0 && m_flShutoffTime <= gpGlobals->realtime )
if ( m_flShutoffTime > 0 )
{
// times up, shutoff
m_bMenuDisplayed = false;
return false;
#ifdef MAPBASE
if ( m_bMapDefinedMenu && !m_bPlayingFadeout && (m_flShutoffTime - m_flOpenCloseTime) <= GetMenuTime() )
{
// Begin closing the menu
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "MenuClose" );
m_bMenuTakesInput = false;
m_bPlayingFadeout = true;
}
else
#endif
if ( m_flShutoffTime <= GetMenuTime() )
{
// times up, shutoff
m_bMenuDisplayed = false;
return false;
}
}
return draw;
@ -169,23 +210,21 @@ void CHudMenu::Paint()
return;
// center it
int x = 20;
int x = m_nBorder;
Color menuColor = m_MenuColor;
Color itemColor = m_ItemColor;
int c = m_Processed.Count();
int border = 20;
int wide = m_nMaxPixels + border;
int tall = m_nHeight + border;
int wide = m_nMaxPixels + m_nBorder;
int tall = m_nHeight + m_nBorder;
int y = ( ScreenHeight() - tall ) * 0.5f;
DrawBox( x - border/2, y - border/2, wide, tall, m_BoxColor, m_flSelectionAlphaOverride / 255.0f );
DrawBox( x - m_nBorder/2, y - m_nBorder/2, wide, tall, m_BoxColor, m_flSelectionAlphaOverride / 255.0f );
//DrawTexturedBox( x - border/2, y - border/2, wide, tall, m_BoxColor, m_flSelectionAlphaOverride / 255.0f );
//DrawTexturedBox( x - m_nBorder/2, y - m_nBorder/2, wide, tall, m_BoxColor, m_flSelectionAlphaOverride / 255.0f );
menuColor[3] = menuColor[3] * ( m_flSelectionAlphaOverride / 255.0f );
itemColor[3] = itemColor[3] * ( m_flSelectionAlphaOverride / 255.0f );
@ -195,7 +234,18 @@ void CHudMenu::Paint()
ProcessedLine *line = &m_Processed[ i ];
Assert( line );
Color clr = line->menuitem != 0 ? itemColor : menuColor;
#ifdef MAPBASE
bool isItem = true;
if (line->menuitem == 0 && line->startchar < (MAX_MENU_STRING-1) && g_szMenuString[ line->startchar ] != L'0' && g_szMenuString[ line->startchar+1 ] != L'.')
{
// Can't use 0 directly because it gets conflated with the cancel item
isItem = false;
}
#else
bool isItem = line->menuitem != 0;
#endif
Color clr = isItem ? itemColor : menuColor;
bool canblur = false;
if ( line->menuitem != 0 &&
@ -208,15 +258,15 @@ void CHudMenu::Paint()
vgui::surface()->DrawSetTextColor( clr );
int drawLen = line->length;
if ( line->menuitem != 0 )
if (isItem)
{
drawLen *= m_flTextScan;
}
vgui::surface()->DrawSetTextFont( line->menuitem != 0 ? m_hItemFont : m_hTextFont );
vgui::surface()->DrawSetTextFont( isItem ? m_hItemFont : m_hTextFont );
PaintString( &g_szMenuString[ line->startchar ], drawLen,
line->menuitem != 0 ? m_hItemFont : m_hTextFont, x, y );
isItem ? m_hItemFont : m_hTextFont, x, y );
if ( canblur )
{
@ -242,6 +292,20 @@ void CHudMenu::Paint()
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
inline float CHudMenu::GetMenuTime( void )
{
#ifdef MAPBASE
// In singleplayer, use the curtime instead. This fixes issues with menus disappearing after pausing
if (gpGlobals->maxClients <= 1)
return gpGlobals->curtime;
#endif
return gpGlobals->realtime;
}
//-----------------------------------------------------------------------------
// Purpose: selects an item from the menu
//-----------------------------------------------------------------------------
@ -260,7 +324,7 @@ void CHudMenu::SelectMenuItem( int menu_item )
// remove the menu quickly
m_bMenuTakesInput = false;
m_flShutoffTime = gpGlobals->realtime + m_flOpenCloseTime;
m_flShutoffTime = GetMenuTime() + m_flOpenCloseTime;
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("MenuClose");
}
}
@ -339,9 +403,20 @@ void CHudMenu::ProcessText( void )
{
ProcessedLine *l = &m_Processed[ i ];
Assert( l );
#ifdef MAPBASE
bool isItem = true;
if (l->menuitem == 0 && l->startchar < (MAX_MENU_STRING-1) && g_szMenuString[ l->startchar ] != L'0' && g_szMenuString[ l->startchar+1 ] != L'.')
{
// Can't use 0 directly because it gets conflated with the cancel item
isItem = false;
}
#else
bool isItem = l->menuitem != 0;
#endif
int pixels = 0;
vgui::HFont font = l->menuitem != 0 ? m_hItemFont : m_hTextFont;
vgui::HFont font = isItem ? m_hItemFont : m_hTextFont;
for ( int ch = 0; ch < l->length; ch++ )
{
@ -364,7 +439,7 @@ void CHudMenu::ProcessText( void )
void CHudMenu::HideMenu( void )
{
m_bMenuTakesInput = false;
m_flShutoffTime = gpGlobals->realtime + m_flOpenCloseTime;
m_flShutoffTime = GetMenuTime() + m_flOpenCloseTime;
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("MenuClose");
}
@ -381,6 +456,11 @@ void CHudMenu::ShowMenu( const char * menuName, int validSlots )
m_flShutoffTime = -1;
m_bitsValidSlots = validSlots;
m_fWaitingForMore = 0;
m_nBorder = 20;
#ifdef MAPBASE
m_bMapDefinedMenu = false;
m_bPlayingFadeout = false;
#endif
Q_strncpy( g_szPrelocalisedMenuString, menuName, sizeof( g_szPrelocalisedMenuString ) );
@ -408,6 +488,11 @@ void CHudMenu::ShowMenu_KeyValueItems( KeyValues *pKV )
m_flShutoffTime = -1;
m_fWaitingForMore = 0;
m_bitsValidSlots = 0;
m_nBorder = 20;
#ifdef MAPBASE
m_bMapDefinedMenu = false;
m_bPlayingFadeout = false;
#endif
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("MenuOpen");
m_nSelectedItem = -1;
@ -426,7 +511,11 @@ void CHudMenu::ShowMenu_KeyValueItems( KeyValues *pKV )
const char *pszItem = item->GetName();
const wchar_t *wLocalizedItem = g_pVGuiLocalize->Find( pszItem );
#ifdef MAPBASE
nCount = _snwprintf( pWritePosition, nRemaining, L"->%d. %ls\n", i+1, wLocalizedItem );
#else
nCount = _snwprintf( pWritePosition, nRemaining, L"%d. %ls\n", i+1, wLocalizedItem );
#endif
nRemaining -= nCount;
pWritePosition += nCount;
@ -436,7 +525,11 @@ void CHudMenu::ShowMenu_KeyValueItems( KeyValues *pKV )
// put a cancel on the end
m_bitsValidSlots |= (1<<9);
#ifdef MAPBASE
nCount = _snwprintf( pWritePosition, nRemaining, L"->0. %ls\n", g_pVGuiLocalize->Find( "#Cancel" ) );
#else
nCount = _snwprintf( pWritePosition, nRemaining, L"0. %ls\n", g_pVGuiLocalize->Find( "#Cancel" ) );
#endif
nRemaining -= nCount;
pWritePosition += nCount;
@ -465,8 +558,7 @@ void CHudMenu::MsgFunc_ShowMenu( bf_read &msg)
if ( DisplayTime > 0 )
{
m_flShutoffTime = m_flOpenCloseTime + DisplayTime + gpGlobals->realtime;
m_flShutoffTime = m_flOpenCloseTime + DisplayTime + GetMenuTime();
}
else
{
@ -511,8 +603,131 @@ void CHudMenu::MsgFunc_ShowMenu( bf_read &msg)
}
m_fWaitingForMore = NeedMore;
m_nBorder = 20;
#ifdef MAPBASE
m_bMapDefinedMenu = false;
m_bPlayingFadeout = false;
#endif
}
#ifdef MAPBASE
ConVar hud_menu_complex_border( "hud_menu_complex_border", "30" );
//-----------------------------------------------------------------------------
// Purpose: Message handler for ShowMenu message with more options for game_menu
// takes four values:
// short : a bitfield of keys that are valid input
// float : the duration, in seconds, the menu should stay up. -1 means it stays until something is chosen.
// byte : a boolean, TRUE if there is more string yet to be received before displaying the menu, false if it's the last string
// string: menu string to display
// if this message is never received, then scores will simply be the combined totals of the players.
//-----------------------------------------------------------------------------
void CHudMenu::MsgFunc_ShowMenuComplex( bf_read &msg)
{
m_bitsValidSlots = (short)msg.ReadWord();
float DisplayTime = msg.ReadFloat();
int NeedMore = msg.ReadByte();
m_nBorder = hud_menu_complex_border.GetInt();
m_bMapDefinedMenu = true;
m_bPlayingFadeout = false;
if ( DisplayTime > 0 )
{
m_flShutoffTime = m_flOpenCloseTime + DisplayTime + GetMenuTime();
}
else
{
m_flShutoffTime = -1;
}
if ( m_bitsValidSlots > -1 )
{
char szString[2048];
msg.ReadString( szString, sizeof(szString) );
if ( !m_fWaitingForMore ) // this is the start of a new menu
{
Q_strncpy( g_szPrelocalisedMenuString, szString, sizeof( g_szPrelocalisedMenuString ) );
}
else
{ // append to the current menu string
Q_strncat( g_szPrelocalisedMenuString, szString, sizeof( g_szPrelocalisedMenuString ), COPY_ALL_CHARACTERS );
}
if ( !NeedMore )
{
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("MenuOpenFlash");
m_nSelectedItem = -1;
// we have the whole string, so we can localise it now
wchar_t *pWritePosition = g_szMenuString;
int nRemaining = sizeof( g_szMenuString ) / sizeof( wchar_t );
int nCount;
char *pszToken = strtok( szString, "\n" );
int nCurItem = 0;
for (; pszToken != NULL; pszToken = strtok( NULL, "\n" ), nCurItem++)
{
if (!*pszToken || *pszToken == ' ')
continue;
wchar_t wszMenuItem[128];
const wchar_t *wLocalizedItem = g_pVGuiLocalize->Find( pszToken );
if (wLocalizedItem)
{
V_wcsncpy( wszMenuItem, wLocalizedItem, sizeof( wszMenuItem ) );
}
else
{
g_pVGuiLocalize->ConvertANSIToUnicode( pszToken, wszMenuItem, sizeof( wszMenuItem ) );
}
if (nCurItem == 0)
{
// First item is title
nCount = _snwprintf( pWritePosition, nRemaining, L"%ls\n", wszMenuItem );
}
else
{
// If this item isn't valid, skip until it is
//while (!(m_bitsValidSlots & (1 << nCurItem)) && nCurItem < 10)
//{
// nCurItem++;
//}
if (nCurItem == 10)
nCurItem = 0;
nCount = _snwprintf( pWritePosition, nRemaining, L"->%d. %ls\n", nCurItem, wszMenuItem );
}
nRemaining -= nCount;
pWritePosition += nCount;
}
ProcessText();
}
m_bMenuDisplayed = true;
if (m_bitsValidSlots > 0)
m_bMenuTakesInput = true;
else
m_bMenuTakesInput = false;
m_flSelectionTime = gpGlobals->curtime;
}
else
{
HideMenu();
}
m_fWaitingForMore = NeedMore;
}
#endif
//-----------------------------------------------------------------------------
// Purpose: hud scheme settings
//-----------------------------------------------------------------------------

View File

@ -26,10 +26,14 @@ class CHudMenu : public CHudElement, public vgui::Panel
public:
CHudMenu( const char *pElementName );
void Init( void );
void LevelInit( void );
void VidInit( void );
void Reset( void );
virtual bool ShouldDraw( void );
void MsgFunc_ShowMenu( bf_read &msg );
#ifdef MAPBASE
void MsgFunc_ShowMenuComplex( bf_read &msg );
#endif
void HideMenu( void );
void ShowMenu( const char * menuName, int keySlot );
void ShowMenu_KeyValueItems( KeyValues *pKV );
@ -42,6 +46,8 @@ private:
virtual void Paint();
virtual void ApplySchemeSettings(vgui::IScheme *pScheme);
private:
float GetMenuTime( void );
void ProcessText( void );
void PaintString( const wchar_t *text, int textlen, vgui::HFont& font, int x, int y );
@ -59,6 +65,7 @@ private:
int m_nMaxPixels;
int m_nHeight;
int m_nBorder;
bool m_bMenuDisplayed;
int m_bitsValidSlots;
@ -69,6 +76,12 @@ private:
float m_flSelectionTime;
#ifdef MAPBASE
// Indicates this menu is defined by game_menu
bool m_bMapDefinedMenu;
bool m_bPlayingFadeout;
#endif
CPanelAnimationVar( float, m_flOpenCloseTime, "OpenCloseTime", "1" );
CPanelAnimationVar( float, m_flBlur, "Blur", "0" );

View File

@ -855,7 +855,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

@ -129,6 +129,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" );
@ -1306,6 +1307,37 @@ void CViewRender::Render( vrect_t *rect )
g_ClientVirtualReality.OverlayHUDQuadWithUndistort( view, 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;
}
view.angles += cameraAngles * cl_camera_anim_intensity.GetFloat();
view.origin += cameraOrigin * cl_camera_anim_intensity.GetFloat();
}
}
}
#endif // MAPBASE
}

View File

@ -28,6 +28,10 @@
extern IScriptManager *scriptmanager;
extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * );
#ifdef MAPBASE_VSCRIPT
ConVar script_connect_debugger_on_mapspawn_client( "script_connect_debugger_on_mapspawn_client", "0" );
#endif
// #define VMPROFILE 1
#ifdef VMPROFILE
@ -234,8 +238,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" )
@ -683,6 +686,17 @@ bool VScriptClientInit()
//g_pScriptVM->RegisterInstance( &g_ScriptEntityIterator, "Entities" );
#endif
#ifdef MAPBASE_VSCRIPT
if ( script_connect_debugger_on_mapspawn_client.GetInt() == 2 )
{
g_pScriptVM->ConnectDebugger( vscript_debugger_port, 10.0f );
}
else if ( script_connect_debugger_on_mapspawn_client.GetInt() != 0 )
{
g_pScriptVM->ConnectDebugger( vscript_debugger_port );
}
#endif
if (scriptLanguage == SL_SQUIRREL)
{
g_pScriptVM->Run( g_Script_vscript_client );
@ -771,11 +785,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"
@ -471,7 +475,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

@ -2594,6 +2594,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 );
@ -2603,6 +2615,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
@ -2614,6 +2627,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 );
@ -2624,6 +2638,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 );
@ -2634,6 +2649,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 );
@ -2644,6 +2660,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 );
@ -2654,6 +2671,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 );
@ -2664,6 +2682,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 );
@ -2674,6 +2693,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
//
@ -746,10 +754,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) );
@ -758,6 +775,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()
@ -765,6 +846,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()
@ -832,7 +941,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;
}
@ -867,6 +976,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 )
@ -2595,6 +2768,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
}
//=========================================================
@ -2847,7 +3043,7 @@ void CAI_BaseNPC::SetHeadDirection( const Vector &vTargetPos, float flInterval)
//--------------------------------------
// Set head yaw
//--------------------------------------
float flDesiredYaw = VecToYaw(vTargetPos - GetLocalOrigin()) - GetLocalAngles().y;
float flDesiredYaw = VecToYaw(vTargetPos - GetAbsOrigin()) - GetAbsAngles().y;
if (flDesiredYaw > 180)
flDesiredYaw -= 360;
if (flDesiredYaw < -180)
@ -6107,6 +6303,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))
{
@ -7791,7 +8003,7 @@ void CAI_BaseNPC::NPCInit ( void )
SetGravity(1.0); // Don't change
m_takedamage = DAMAGE_YES;
GetMotor()->SetIdealYaw( GetLocalAngles().y );
GetMotor()->SetIdealYaw( GetAbsAngles().y );
m_iMaxHealth = m_iHealth;
m_lifeState = LIFE_ALIVE;
SetIdealState( NPC_STATE_IDLE );// Assume npc will be idle, until proven otherwise
@ -8979,6 +9191,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;
}
@ -8988,6 +9214,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;
}
@ -9404,14 +9644,14 @@ float CAI_BaseNPC::CalcIdealYaw( const Vector &vecTarget )
vecProjection.x = -vecTarget.y;
vecProjection.y = vecTarget.x;
return UTIL_VecToYaw( vecProjection - GetLocalOrigin() );
return UTIL_VecToYaw( vecProjection - GetAbsOrigin() );
}
else if ( GetNavigator()->GetMovementActivity() == ACT_STRAFE_RIGHT )
{
vecProjection.x = vecTarget.y;
vecProjection.y = vecTarget.x;
return UTIL_VecToYaw( vecProjection - GetLocalOrigin() );
return UTIL_VecToYaw( vecProjection - GetAbsOrigin() );
}
#ifdef MAPBASE
// Allow hint nodes to override the yaw without needing to control AI
@ -9422,7 +9662,7 @@ float CAI_BaseNPC::CalcIdealYaw( const Vector &vecTarget )
#endif
else
{
return UTIL_VecToYaw ( vecTarget - GetLocalOrigin() );
return UTIL_VecToYaw ( vecTarget - GetAbsOrigin() );
}
}
@ -9622,7 +9862,7 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent )
//DevMsg( "Turned!\n" );
SetIdealActivity( ACT_IDLE );
Forget( bits_MEMORY_TURNING );
SetBoneController( 0, GetLocalAngles().y );
SetBoneController( 0, GetAbsAngles().y );
IncrementInterpolationFrame();
break;
}
@ -10901,7 +11141,7 @@ Vector CAI_BaseNPC::GetShootEnemyDir( const Vector &shootOrigin, bool bNoisy )
else
{
Vector forward;
AngleVectors( GetLocalAngles(), &forward );
AngleVectors( GetAbsAngles(), &forward );
return forward;
}
}
@ -11529,6 +11769,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()
@ -12307,7 +12554,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." )
@ -12315,6 +12572,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." )
@ -12362,6 +12624,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." )
@ -12380,6 +12652,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
@ -12409,6 +12688,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
@ -13874,7 +14179,7 @@ bool CAI_BaseNPC::OverrideMove( float flInterval )
float CAI_BaseNPC::VecToYaw( const Vector &vecDir )
{
if (vecDir.x == 0 && vecDir.y == 0 && vecDir.z == 0)
return GetLocalAngles().y;
return GetAbsAngles().y;
return UTIL_VecToYaw( vecDir );
}

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; }
@ -1180,6 +1180,11 @@ public:
void SetDeathPose( const int &iDeathPose ) { m_iDeathPose = iDeathPose; }
void SetDeathPoseFrame( const int &iDeathPoseFrame ) { m_iDeathFrame = iDeathPoseFrame; }
#ifdef MAPBASE
int GetDeathPose() { return m_iDeathPose; }
int GetDeathPoseFrame() { return m_iDeathFrame; }
#endif
void SelectDeathPose( const CTakeDamageInfo &info );
virtual bool ShouldPickADeathPose( void ) { return true; }
@ -1271,9 +1276,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 +1328,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
//-----------------------------------------------------
@ -2399,6 +2436,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) )
@ -918,7 +964,7 @@ void CAI_BaseNPC::StartTurn( float flDeltaYaw )
{
float flCurrentYaw;
flCurrentYaw = UTIL_AngleMod( GetLocalAngles().y );
flCurrentYaw = UTIL_AngleMod( GetAbsAngles().y );
GetMotor()->SetIdealYaw( UTIL_AngleMod( flCurrentYaw + flDeltaYaw ) );
SetTurnActivity();
}
@ -1111,7 +1157,7 @@ void CAI_BaseNPC::StartScriptMoveToTargetTask( int task )
{
TaskFail(FAIL_NO_TARGET);
}
else if ( (m_hTargetEnt->GetAbsOrigin() - GetLocalOrigin()).Length() < 1 )
else if ( (m_hTargetEnt->GetAbsOrigin() - GetAbsOrigin()).Length() < 1 )
{
TaskComplete();
}
@ -1576,7 +1622,7 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask )
break;
case TASK_SET_IDEAL_YAW_TO_CURRENT:
GetMotor()->SetIdealYaw( UTIL_AngleMod( GetLocalAngles().y ) );
GetMotor()->SetIdealYaw( UTIL_AngleMod( GetAbsAngles().y ) );
TaskComplete();
break;
@ -1730,7 +1776,7 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask )
{
TaskFail(FAIL_NO_TARGET);
}
else if ( (pTarget->GetAbsOrigin() - GetLocalOrigin()).Length() < 1 )
else if ( (pTarget->GetAbsOrigin() - GetAbsOrigin()).Length() < 1 )
{
TaskComplete();
}
@ -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;
@ -2965,7 +3021,7 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask )
{
if ( m_hTargetEnt != NULL )
{
GetMotor()->SetIdealYaw( UTIL_AngleMod( m_hTargetEnt->GetLocalAngles().y ) );
GetMotor()->SetIdealYaw( UTIL_AngleMod( m_hTargetEnt->GetAbsAngles().y ) );
}
if ( m_scriptState != SCRIPT_CUSTOM_MOVE_TO_MARK )
@ -3294,7 +3350,7 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask )
pTarget = GetEnemy();
if ( pTarget )
{
GetMotor()->SetIdealYawAndUpdate( pTarget->GetAbsOrigin() - GetLocalOrigin() , AI_KEEP_YAW_SPEED );
GetMotor()->SetIdealYawAndUpdate( pTarget->GetAbsOrigin() - GetAbsOrigin(), AI_KEEP_YAW_SPEED );
}
if ( IsActivityFinished() )

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

@ -298,6 +298,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()
@ -749,4 +786,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

@ -317,7 +317,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, "" )
DEFINE_SCRIPTFUNC( StudioFrameAdvance, "" )
DEFINE_SCRIPTFUNC( GetPlaybackRate, "" )
@ -1261,7 +1261,8 @@ void CBaseAnimating::DispatchAnimEvents ( CBaseAnimating *eventHandler )
}
#ifdef MAPBASE_VSCRIPT
if (eventHandler->ScriptHookHandleAnimEvent( &event ) == false)
scriptanimevent_t wrapper( event );
if (!eventHandler->ScriptHookHandleAnimEvent( wrapper ))
continue;
#endif
@ -1299,11 +1300,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 };
@ -2305,21 +2306,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;
@ -146,7 +149,7 @@ public:
virtual void DispatchAnimEvents ( CBaseAnimating *eventHandler ); // Handle events that have happend since last time called up until X seconds into the future
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 );
@ -208,7 +211,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 );
// For VScript
int GetSkin() { return m_nSkin; }

View File

@ -3482,13 +3482,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

@ -1628,10 +1628,53 @@ void 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;
}
}
g_pScriptVM->RemoveInstance( hInfo );
}
#endif
OnTakeDamage( info );
}
}
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
@ -2251,14 +2294,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." )
@ -2377,6 +2422,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" )
@ -2515,14 +2561,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()
@ -2544,6 +2591,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();
@ -7544,6 +7597,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
}
//-----------------------------------------------------------------------------
@ -10154,7 +10262,7 @@ void CBaseEntity::SetScriptOwnerEntity(HSCRIPT pOwner)
// ScriptFindKey, ScriptGetFirstSubKey, ScriptGetString,
// ScriptGetInt, ScriptGetFloat, ScriptGetNextKey
//-----------------------------------------------------------------------------
HSCRIPT CBaseEntity::ScriptGetModelKeyValues( void )
HSCRIPT_RC CBaseEntity::ScriptGetModelKeyValues( void )
{
KeyValues *pModelKeyValues = new KeyValues("");
HSCRIPT hScript = NULL;
@ -10163,16 +10271,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

View File

@ -1440,7 +1440,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.
@ -2114,7 +2114,7 @@ public:
#endif
const char* ScriptGetModelName(void) const;
HSCRIPT ScriptGetModelKeyValues(void);
HSCRIPT_RC ScriptGetModelKeyValues(void);
void ScriptStopSound(const char* soundname);
void ScriptEmitSound(const char* soundname);
@ -2177,10 +2177,13 @@ public:
static ScriptHook_t g_Hook_VPhysicsCollision;
static ScriptHook_t g_Hook_FireBullets;
static ScriptHook_t g_Hook_OnDeath;
static ScriptHook_t g_Hook_OnTakeDamage;
static ScriptHook_t g_Hook_OnKilledOther;
static ScriptHook_t g_Hook_HandleInteraction;
static ScriptHook_t g_Hook_ModifyEmitSoundParams;
static ScriptHook_t g_Hook_ModifySentenceParams;
static ScriptHook_t g_Hook_ModifyOrAppendCriteria;
static ScriptHook_t g_Hook_CanBeSeenBy;
#endif
string_t m_iszVScripts;
@ -2188,9 +2191,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
};

View File

@ -816,7 +816,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();
@ -1654,6 +1654,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() );
@ -2093,8 +2099,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 )
{
@ -2106,11 +2120,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)
@ -2147,13 +2167,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

@ -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!!!
@ -121,6 +122,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
@ -267,6 +269,7 @@ public:
void InputSetHandModelBodyGroup( inputdata_t &inputdata );
void InputSetPlayerModel( inputdata_t &inputdata );
void InputSetPlayerDrawLegs( inputdata_t &inputdata );
void InputSetPlayerDrawExternally( inputdata_t &inputdata );
#endif
@ -591,6 +594,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 ),
@ -605,6 +610,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()
@ -623,6 +632,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
@ -638,6 +650,10 @@ CHL2_Player::CHL2_Player()
m_flArmorReductionTime = 0.0f;
m_iArmorReductionFrom = 0;
#ifdef MAPBASE
m_nProtagonistIndex = -1;
#endif
}
//
@ -672,8 +688,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 MAPBASE
SendPropInt( SENDINFO( m_nProtagonistIndex ), 8, SPROP_UNSIGNED ),
#endif
#ifdef SP_ANIM_STATE
SendPropFloat( SENDINFO(m_flAnimRenderYaw), 0, SPROP_NOSCALE ),
SendPropFloat( SENDINFO(m_flAnimRenderZ), 0, SPROP_NOSCALE ),
#endif
END_SEND_TABLE()
@ -1166,6 +1186,18 @@ void CHL2_Player::PostThink( void )
m_pPlayerAnimState->Update( angEyeAngles.y, angEyeAngles.x );
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 );
if (player_process_scene_events.GetBool())
{
ProcessSceneEvents();
}
}
#endif
}
@ -1244,6 +1276,11 @@ void CHL2_Player::Activate( void )
#endif
GetPlayerProxy();
#ifdef MAPBASE
if (m_iszProtagonistName != NULL_STRING)
SetProtagonist( STRING( m_iszProtagonistName ) );
#endif
}
//------------------------------------------------------------------------------
@ -1483,7 +1520,10 @@ CStudioHdr *CHL2_Player::OnNewModel()
}
extern char g_szDefaultPlayerModel[MAX_PATH];
extern bool g_bDefaultPlayerLegs;
extern bool g_bDefaultPlayerDrawExternally;
extern char g_szDefaultProtagonist[MAX_PROTAGONIST_NAME];
#endif
//-----------------------------------------------------------------------------
@ -1515,7 +1555,14 @@ 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 );
if (m_iszProtagonistName != NULL_STRING)
SetProtagonist( STRING( m_iszProtagonistName ) );
#endif
//
@ -3349,6 +3396,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 );
@ -3754,6 +3806,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 )
@ -3776,6 +3833,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 )
@ -3989,6 +4051,21 @@ 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);
}
else
{
m_flAnimRenderYaw = FLT_MAX;
}
}
#endif
}
//---------------------------------------------------------
@ -4040,6 +4117,10 @@ bool CHL2_Player::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex
StopZooming();
}
#ifdef MAPBASE
RefreshProtagonistWeaponData( pWeapon );
#endif
return BaseClass::Weapon_Switch( pWeapon, viewmodelindex );
}
@ -4435,6 +4516,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
//-----------------------------------------------------------------------------
@ -4594,6 +4813,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" ),
@ -5072,6 +5292,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

@ -19,7 +19,7 @@
#if defined ( HL2MP )
#include "basemultiplayerplayer.h"
#elif defined ( MAPBASE )
#include "mapbase/singleplayer_animstate.h"
#include "mapbase/mapbase_playeranimstate.h"
#endif
class CAI_Squad;
@ -358,6 +358,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;
@ -442,12 +451,19 @@ private:
friend class CHL2GameMovement;
#ifdef MAPBASE
// Protagonist used by protagonist_system.h
string_t m_iszProtagonistName;
CNetworkVar( int, m_nProtagonistIndex );
#endif
#ifdef SP_ANIM_STATE
CSinglePlayerAnimState* m_pPlayerAnimState;
CMapbasePlayerAnimState* m_pPlayerAnimState;
// 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
};

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

@ -256,7 +256,7 @@ END_DATADESC()
//-----------------------------------------------------------------------------
void CNPC_PoisonZombie::Precache( void )
{
PrecacheModel("models/zombie/poison.mdl");
PrecacheModel( DefaultOrCustomModel( "models/zombie/poison.mdl" ) );
PrecacheScriptSound( "NPC_PoisonZombie.Die" );
PrecacheScriptSound( "NPC_PoisonZombie.ThrowWarn" );
@ -509,7 +509,7 @@ void CNPC_PoisonZombie::SetZombieModel( void )
}
else
{
SetModel( "models/zombie/poison.mdl" );
SetModel( DefaultOrCustomModel( "models/zombie/poison.mdl" ) );
SetHullType(HULL_HUMAN);
}

View File

@ -472,8 +472,8 @@ void CNPC_Antlion::Precache( void )
#ifdef HL2_EPISODIC
if ( IsWorker() )
{
PrecacheModel( ANTLION_WORKER_MODEL );
PropBreakablePrecacheAll( MAKE_STRING( ANTLION_WORKER_MODEL ) );
PrecacheModel( DefaultOrCustomModel( ANTLION_WORKER_MODEL ) );
PropBreakablePrecacheAll( MAKE_STRING( DefaultOrCustomModel( ANTLION_WORKER_MODEL ) ) );
UTIL_PrecacheOther( "grenade_spit" );
PrecacheParticleSystem( "blood_impact_antlion_worker_01" );
PrecacheParticleSystem( "antlion_gib_02" );
@ -482,8 +482,8 @@ void CNPC_Antlion::Precache( void )
else
#endif // HL2_EPISODIC
{
PrecacheModel( ANTLION_MODEL );
PropBreakablePrecacheAll( MAKE_STRING( ANTLION_MODEL ) );
PrecacheModel( DefaultOrCustomModel( ANTLION_MODEL ) );
PropBreakablePrecacheAll( MAKE_STRING( DefaultOrCustomModel( ANTLION_MODEL ) ) );
PrecacheParticleSystem( "blood_impact_antlion_01" );
PrecacheParticleSystem( "AntlionGib" );
}

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
//-----------------------------------------------------------------------------
@ -3785,13 +4055,16 @@ void CNPC_AttackHelicopter::Event_Killed( const CTakeDamageInfo &info )
}
m_lifeState = LIFE_DYING;
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
controller.SoundChangeVolume( m_pGunFiringSound, 0.0, 0.1f );
if ( GetSleepState() != AISS_WAITING_FOR_INPUT )
{
CSoundEnvelopeController& controller = CSoundEnvelopeController::GetController();
controller.SoundChangeVolume( m_pGunFiringSound, 0.0, 0.1f );
}
if( GetCrashPoint() == NULL )
{
CBaseEntity *pCrashPoint = gEntList.FindEntityByClassname( NULL, "info_target_helicopter_crash" );
CBaseEntity *pCrashPoint = FindCrashPoint();
if( pCrashPoint != NULL )
{
m_hCrashPoint.Set( pCrashPoint );
@ -3812,6 +4085,12 @@ void CNPC_AttackHelicopter::Event_Killed( const CTakeDamageInfo &info )
return;
}
}
#ifdef MAPBASE
else
{
assert_cast<CTargetHelicopterCrash*>( GetCrashPoint() )->HelicopterCrashedOnTarget( this );
}
#endif
Chopper_BecomeChunks( this );
StopLoopingSounds();
@ -3869,7 +4148,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 );
@ -4821,6 +5104,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;
}
@ -4885,6 +5177,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

@ -839,6 +839,9 @@ void CNPC_Citizen::SelectModel()
}
}
// Models selected this way must be unique to avoid conflicts in save/restore
m_Type = CT_UNIQUE;
// Just set the model right here
SetModelName( AllocPooledString( returnValue.m_pszString ) );
return;

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

@ -275,7 +275,7 @@ CNPC_CombineCamera::~CNPC_CombineCamera()
//-----------------------------------------------------------------------------
void CNPC_CombineCamera::Precache()
{
PrecacheModel(COMBINE_CAMERA_MODEL);
PrecacheModel( DefaultOrCustomModel( COMBINE_CAMERA_MODEL ) );
PrecacheModel(COMBINE_CAMERA_GLOW_SPRITE);
PrecacheModel(COMBINE_CAMERA_FLASH_SPRITE);
@ -304,8 +304,7 @@ void CNPC_CombineCamera::Precache()
void CNPC_CombineCamera::Spawn()
{
Precache();
SetModel(COMBINE_CAMERA_MODEL);
SetModel( DefaultOrCustomModel( COMBINE_CAMERA_MODEL ) );
m_pEyeFlash = CSprite::SpriteCreate(COMBINE_CAMERA_FLASH_SPRITE, GetLocalOrigin(), FALSE);
m_pEyeFlash->SetTransparency(kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation);

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" ),
@ -902,7 +921,7 @@ CNPC_CombineDropship::~CNPC_CombineDropship(void)
void CNPC_CombineDropship::Spawn( void )
{
Precache( );
SetModel( "models/combine_dropship.mdl" );
SetModel( DefaultOrCustomModel( "models/combine_dropship.mdl" ) );
#ifdef _XBOX
AddEffects( EF_NOSHADOW );
@ -962,12 +981,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, m_hContainer, this );
@ -1121,7 +1188,7 @@ void CNPC_CombineDropship::Activate( void )
void CNPC_CombineDropship::Precache( void )
{
// Models
PrecacheModel("models/combine_dropship.mdl");
PrecacheModel( DefaultOrCustomModel( "models/combine_dropship.mdl" ) );
switch ( m_iCrateType )
{
case CRATE_SOLDIER:
@ -1201,13 +1268,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 );
@ -1429,8 +1507,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;
}
@ -1779,6 +1863,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;
@ -1790,6 +1884,7 @@ void CNPC_CombineDropship::InputDropStrider( inputdata_t &inputdata )
m_hContainer->SetAbsVelocity( vec3_origin );
m_hContainer = NULL;
#endif
}
//-----------------------------------------------------------------------------
@ -2016,7 +2111,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 );
}
@ -2112,8 +2211,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 );
}
@ -2315,6 +2421,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.
@ -2817,7 +3015,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 );
@ -2850,7 +3052,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 )
{
@ -3150,6 +3356,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()
@ -547,11 +564,11 @@ void CNPC_CombineGunship::Spawn( void )
if ( HasSpawnFlags( SF_GUNSHIP_USE_CHOPPER_MODEL ) )
{
SetModel( "models/combine_helicopter.mdl" );
SetModel( DefaultOrCustomModel( "models/combine_helicopter.mdl" ) );
}
else
{
SetModel( "models/gunship.mdl" );
SetModel( DefaultOrCustomModel( "models/gunship.mdl" ) );
}
ExtractBbox( SelectHeaviestSequence( ACT_GUNSHIP_PATROL ), m_cullBoxMins, m_cullBoxMaxs );
@ -673,12 +690,12 @@ void CNPC_CombineGunship::Precache( void )
{
if ( HasSpawnFlags( SF_GUNSHIP_USE_CHOPPER_MODEL ) )
{
PrecacheModel( "models/combine_helicopter.mdl" );
PrecacheModel( DefaultOrCustomModel( "models/combine_helicopter.mdl" ) );
Chopper_PrecacheChunks( this );
}
else
{
PrecacheModel("models/gunship.mdl");
PrecacheModel( DefaultOrCustomModel( "models/gunship.mdl" ) );
}
PrecacheModel("sprites/lgtning.vmt");
@ -708,7 +725,7 @@ void CNPC_CombineGunship::Precache( void )
g_iGunshipEffectIndex = PrecacheModel( "sprites/physbeam.vmt" );
}
PropBreakablePrecacheAll( MAKE_STRING("models/gunship.mdl") );
PropBreakablePrecacheAll( MAKE_STRING( DefaultOrCustomModel( "models/gunship.mdl" ) ) );
BaseClass::Precache();
}
@ -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

@ -38,6 +38,11 @@ ConVar sk_combine_guard_kick( "sk_combine_guard_kick", "0");
ConVar combine_guard_spawn_health( "combine_guard_spawn_health", "1" );
extern ConVar sk_plr_dmg_buckshot;
#ifdef MAPBASE
ConVar sk_combine_head_dmg_multiplier( "sk_combine_head_dmg_multiplier", "2" );
#endif
extern ConVar sk_plr_num_shotgun_pellets;
//Whether or not the combine should spawn health on death
@ -222,8 +227,14 @@ float CNPC_CombineS::GetHitgroupDamageMultiplier( int iHitGroup, const CTakeDama
{
case HITGROUP_HEAD:
{
#ifdef MAPBASE
// Now you can change the multiplier of headshot damage in console!
return sk_combine_head_dmg_multiplier.GetFloat();
#else
// Soldiers take double headshot damage
return 2.0f;
#endif
}
}

View File

@ -455,7 +455,7 @@ void CNPC_Dog::Spawn( void )
BaseClass::Spawn();
SetModel( "models/dog.mdl" );
SetModel( DefaultOrCustomModel( "models/dog.mdl" ) );
SetHullType( HULL_WIDE_HUMAN );
SetHullSizeNormal();
@ -638,7 +638,7 @@ void CNPC_Dog::PullObject( bool bMantain )
//-----------------------------------------------------------------------------
void CNPC_Dog::Precache( void )
{
PrecacheModel( "models/dog.mdl" );
PrecacheModel( DefaultOrCustomModel( "models/dog.mdl" ) );
PrecacheScriptSound( "Weapon_PhysCannon.Launch" );

View File

@ -396,7 +396,7 @@ static const char *s_pLegsModel = "models/gibs/fast_zombie_legs.mdl";
//-----------------------------------------------------------------------------
void CFastZombie::Precache( void )
{
PrecacheModel("models/zombie/fast.mdl");
PrecacheModel( DefaultOrCustomModel( "models/zombie/fast.mdl" ) );
#ifdef HL2_EPISODIC
PrecacheModel("models/zombie/Fast_torso.mdl");
PrecacheScriptSound( "NPC_FastZombie.CarEnter1" );
@ -773,7 +773,7 @@ void CFastZombie::SetZombieModel( void )
}
else
{
SetModel( "models/zombie/fast.mdl" );
SetModel( DefaultOrCustomModel( "models/zombie/fast.mdl" ) );
SetHullType(HULL_HUMAN);
}

View File

@ -133,7 +133,7 @@ END_DATADESC()
//-----------------------------------------------------------------------------
void CNPC_Fisherman::SelectModel()
{
SetModelName( AllocPooledString( FISHERMAN_MODEL ) );
SetModelName( AllocPooledString( DefaultOrCustomModel( FISHERMAN_MODEL ) ) );
}
//-----------------------------------------------------------------------------

View File

@ -97,7 +97,7 @@ void CNPC_GMan::Spawn()
BaseClass::Spawn();
SetModel( "models/gman.mdl" );
SetModel( DefaultOrCustomModel( "models/gman.mdl" ) );
SetHullType(HULL_HUMAN);
SetHullSizeNormal();
@ -123,7 +123,7 @@ void CNPC_GMan::Spawn()
//-----------------------------------------------------------------------------
void CNPC_GMan::Precache()
{
PrecacheModel( "models/gman.mdl" );
PrecacheModel( DefaultOrCustomModel( "models/gman.mdl" ) );
BaseClass::Precache();
}

View File

@ -299,7 +299,7 @@ Activity CNPC_Monk::NPC_TranslateActivity( Activity eNewActivity )
//-----------------------------------------------------------------------------
void CNPC_Monk::Precache()
{
PrecacheModel( "models/Monk.mdl" );
PrecacheModel( DefaultOrCustomModel( "models/Monk.mdl" ) );
PrecacheScriptSound( "NPC_Citizen.FootstepLeft" );
PrecacheScriptSound( "NPC_Citizen.FootstepRight" );
@ -317,7 +317,7 @@ void CNPC_Monk::Spawn()
BaseClass::Spawn();
SetModel( "models/Monk.mdl" );
SetModel( DefaultOrCustomModel( "models/Monk.mdl" ) );
SetHullType(HULL_HUMAN);
SetHullSizeNormal();

View File

@ -99,7 +99,7 @@ void CNPC_Mossman::Spawn()
BaseClass::Spawn();
SetModel( "models/mossman.mdl" );
SetModel( DefaultOrCustomModel( "models/mossman.mdl" ) );
SetHullType(HULL_HUMAN);
SetHullSizeNormal();
@ -124,7 +124,7 @@ void CNPC_Mossman::Spawn()
//-----------------------------------------------------------------------------
void CNPC_Mossman::Precache()
{
PrecacheModel( "models/mossman.mdl" );
PrecacheModel( DefaultOrCustomModel( "models/mossman.mdl" ) );
BaseClass::Precache();
}

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

@ -250,7 +250,7 @@ void CZombie::Precache( void )
{
BaseClass::Precache();
PrecacheModel( "models/zombie/classic.mdl" );
PrecacheModel( DefaultOrCustomModel( "models/zombie/classic.mdl" ) );
PrecacheModel( "models/zombie/classic_torso.mdl" );
PrecacheModel( "models/zombie/classic_legs.mdl" );
@ -515,7 +515,7 @@ void CZombie::SetZombieModel( void )
}
else
{
SetModel( "models/zombie/classic.mdl" );
SetModel( DefaultOrCustomModel( "models/zombie/classic.mdl" ) );
SetHullType( HULL_HUMAN );
}

View File

@ -249,7 +249,7 @@ void CNPC_Zombine::Precache( void )
{
BaseClass::Precache();
PrecacheModel( "models/zombie/zombie_soldier.mdl" );
PrecacheModel( DefaultOrCustomModel( "models/zombie/zombie_soldier.mdl" ) );
PrecacheScriptSound( "Zombie.FootstepRight" );
PrecacheScriptSound( "Zombie.FootstepLeft" );
@ -270,7 +270,7 @@ void CNPC_Zombine::Precache( void )
void CNPC_Zombine::SetZombieModel( void )
{
SetModel( "models/zombie/zombie_soldier.mdl" );
SetModel( DefaultOrCustomModel( "models/zombie/zombie_soldier.mdl" ) );
SetHullType( HULL_HUMAN );
SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, !m_fIsHeadless );

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

@ -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

@ -14,6 +14,7 @@
#include "entityoutput.h"
#ifdef MAPBASE
#include "eventqueue.h"
#include "saverestore_utlvector.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
@ -825,3 +826,449 @@ void CGamePlayerTeam::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TY
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose: Displays a custom number menu for player(s)
//-----------------------------------------------------------------------------
LINK_ENTITY_TO_CLASS( game_menu, CGameMenu );
BEGIN_DATADESC( CGameMenu )
DEFINE_UTLVECTOR( m_ActivePlayers, FIELD_EHANDLE ),
DEFINE_UTLVECTOR( m_ActivePlayerTimes, FIELD_TIME ),
DEFINE_KEYFIELD( m_flDisplayTime, FIELD_FLOAT, "holdtime" ),
DEFINE_KEYFIELD( m_iszTitle, FIELD_STRING, "Title" ),
DEFINE_KEYFIELD( m_iszOption[0], FIELD_STRING, "Case01" ),
DEFINE_KEYFIELD( m_iszOption[1], FIELD_STRING, "Case02" ),
DEFINE_KEYFIELD( m_iszOption[2], FIELD_STRING, "Case03" ),
DEFINE_KEYFIELD( m_iszOption[3], FIELD_STRING, "Case04" ),
DEFINE_KEYFIELD( m_iszOption[4], FIELD_STRING, "Case05" ),
DEFINE_KEYFIELD( m_iszOption[5], FIELD_STRING, "Case06" ),
DEFINE_KEYFIELD( m_iszOption[6], FIELD_STRING, "Case07" ),
DEFINE_KEYFIELD( m_iszOption[7], FIELD_STRING, "Case08" ),
DEFINE_KEYFIELD( m_iszOption[8], FIELD_STRING, "Case09" ),
DEFINE_KEYFIELD( m_iszOption[9], FIELD_STRING, "Case10" ),
// Inputs
DEFINE_INPUTFUNC( FIELD_VOID, "ShowMenu", InputShowMenu ),
DEFINE_INPUTFUNC( FIELD_VOID, "HideMenu", InputHideMenu ),
DEFINE_INPUTFUNC( FIELD_VOID, "__DoRestore", InputDoRestore ),
// Outputs
DEFINE_OUTPUT( m_OnCase[0], "OnCase01" ),
DEFINE_OUTPUT( m_OnCase[1], "OnCase02" ),
DEFINE_OUTPUT( m_OnCase[2], "OnCase03" ),
DEFINE_OUTPUT( m_OnCase[3], "OnCase04" ),
DEFINE_OUTPUT( m_OnCase[4], "OnCase05" ),
DEFINE_OUTPUT( m_OnCase[5], "OnCase06" ),
DEFINE_OUTPUT( m_OnCase[6], "OnCase07" ),
DEFINE_OUTPUT( m_OnCase[7], "OnCase08" ),
DEFINE_OUTPUT( m_OnCase[8], "OnCase09" ),
DEFINE_OUTPUT( m_OnCase[9], "OnCase10" ),
DEFINE_OUTPUT( m_OutValue, "OutValue" ),
DEFINE_OUTPUT( m_OnTimeout, "OnTimeout" ),
DEFINE_THINKFUNC( TimeoutThink ),
END_DATADESC()
IMPLEMENT_AUTO_LIST( IGameMenuAutoList );
static const char *s_pTimeoutContext = "TimeoutContext";
#define MENU_INFINITE_TIME -1.0f
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CGameMenu::CGameMenu()
{
m_flDisplayTime = 5.0f;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMenu::OnRestore()
{
BaseClass::OnRestore();
// Do this a bit after we restore since the HUD might not be ready yet
g_EventQueue.AddEvent( this, "__DoRestore", 0.4f, this, this );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMenu::InputDoRestore( inputdata_t &inputdata )
{
// Check if we should restore the menu on anyone
FOR_EACH_VEC_BACK( m_ActivePlayers, i )
{
if (m_ActivePlayers[i].Get())
{
if (m_ActivePlayerTimes[i] > gpGlobals->curtime || m_ActivePlayerTimes[i] == MENU_INFINITE_TIME)
{
CRecipientFilter filter;
filter.AddRecipient( static_cast<CBasePlayer*>( m_ActivePlayers[i].Get() ) );
ShowMenu( filter, m_ActivePlayerTimes[i] - gpGlobals->curtime );
continue;
}
}
// Remove this player since it's no longer valid
m_ActivePlayers.Remove( i );
m_ActivePlayerTimes.Remove( i );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMenu::TimeoutThink()
{
float flNextLowestTime = FLT_MAX;
FOR_EACH_VEC( m_ActivePlayerTimes, i )
{
// If the player is still in our list, then they must not have selected an option
if (m_ActivePlayerTimes[i] != MENU_INFINITE_TIME)
{
if (m_ActivePlayerTimes[i] <= gpGlobals->curtime)
{
m_OnTimeout.FireOutput( m_ActivePlayers[i], this );
// Remove this player since it's no longer valid
m_ActivePlayers.Remove( i );
m_ActivePlayerTimes.Remove( i );
break;
}
else if (m_ActivePlayerTimes[i] < flNextLowestTime)
{
flNextLowestTime = m_ActivePlayerTimes[i];
}
}
}
if (flNextLowestTime < FLT_MAX)
{
SetNextThink( flNextLowestTime, s_pTimeoutContext );
}
else
{
SetContextThink( NULL, TICK_NEVER_THINK, s_pTimeoutContext );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMenu::ShowMenu( CRecipientFilter &filter, float flDisplayTime )
{
// Before showing the menu, check each menu to see if there's already one being shown to one of our recipients
for ( int i = 0; i < IGameMenuAutoList::AutoList().Count(); i++ )
{
CGameMenu *pMenu = static_cast<CGameMenu*>( IGameMenuAutoList::AutoList()[i] );
if ( pMenu != this && pMenu->IsActive() )
{
for ( int j = 0; j < filter.GetRecipientCount(); j++ )
{
CBaseEntity *ent = CBaseEntity::Instance( filter.GetRecipientIndex( j ) );
if ( pMenu->IsActiveOnTarget( ent ) )
{
Msg( "%s overriding menu %s for player %i\n", GetDebugName(), pMenu->GetDebugName(), j );
pMenu->RemoveTarget( ent );
}
}
}
}
if (flDisplayTime == 0.0f)
{
flDisplayTime = m_flDisplayTime;
}
char szString[512] = { 0 };
int nBitsValidSlots = 0;
if (m_iszTitle != NULL_STRING)
{
V_strncat( szString, STRING( m_iszTitle ), sizeof( szString ) );
}
else
{
// Insert space to tell menu code to skip
V_strncat( szString, " ", sizeof( szString ) );
}
// Insert newline even if there's no string
V_strncat( szString, "\n", sizeof( szString ) );
// Populate the options
for (int i = 0; i < MAX_MENU_OPTIONS; i++)
{
if (m_iszOption[i] != NULL_STRING)
{
nBitsValidSlots |= (1 << i);
V_strncat( szString, STRING( m_iszOption[i] ), sizeof( szString ) );
}
else
{
// Insert space to tell menu code to skip
V_strncat( szString, " ", sizeof( szString ) );
}
// Insert newline even if there's no string
V_strncat( szString, "\n", sizeof( szString ) );
}
if (nBitsValidSlots <= 0 && m_iszTitle == NULL_STRING)
{
Warning( "%s ShowMenu: Can't show menu with no options or title\n", GetDebugName() );
return;
}
UserMessageBegin( filter, "ShowMenuComplex" );
WRITE_WORD( nBitsValidSlots );
WRITE_FLOAT( flDisplayTime );
WRITE_BYTE( 0 );
WRITE_STRING( szString );
MessageEnd();
float flMenuTime;
if (flDisplayTime <= 0.0f)
{
flMenuTime = MENU_INFINITE_TIME;
}
else
{
flMenuTime = gpGlobals->curtime + flDisplayTime;
}
for ( int j = 0; j < filter.GetRecipientCount(); j++ )
{
CBaseEntity *ent = CBaseEntity::Instance( filter.GetRecipientIndex( j ) );
// Check if we already track this player. If not, make a new one
bool bFound = false;
FOR_EACH_VEC( m_ActivePlayers, i )
{
if (m_ActivePlayers[i].Get() == ent)
{
m_ActivePlayerTimes[i] = flMenuTime;
bFound = true;
break;
}
}
if (!bFound)
{
m_ActivePlayers.AddToTail( ent );
m_ActivePlayerTimes.AddToTail( flMenuTime );
}
}
if (GetNextThink( s_pTimeoutContext ) == TICK_NEVER_THINK)
{
SetContextThink( &CGameMenu::TimeoutThink, gpGlobals->curtime + flDisplayTime, s_pTimeoutContext );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMenu::HideMenu( CRecipientFilter &filter )
{
UserMessageBegin( filter, "ShowMenuComplex" );
WRITE_WORD( -1 );
WRITE_FLOAT( 0.0f );
WRITE_BYTE( 0 );
WRITE_STRING( "" );
MessageEnd();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMenu::MenuSelected( int nSlot, CBaseEntity *pActivator )
{
if (nSlot <= 0 || nSlot > MAX_MENU_OPTIONS)
{
Warning( "%s: Invalid slot %i\n", GetDebugName(), nSlot );
return;
}
m_OnCase[nSlot-1].FireOutput( pActivator, this );
m_OutValue.Set( nSlot, pActivator, this );
RemoveTarget( pActivator );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CGameMenu::IsActive()
{
FOR_EACH_VEC_BACK( m_ActivePlayers, i )
{
if (m_ActivePlayers[i].Get())
{
if (m_ActivePlayerTimes[i] > gpGlobals->curtime || m_ActivePlayerTimes[i] == MENU_INFINITE_TIME)
return true;
}
// Remove this player since it's no longer valid
m_ActivePlayers.Remove( i );
m_ActivePlayerTimes.Remove( i );
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CGameMenu::IsActiveOnTarget( CBaseEntity *pPlayer )
{
FOR_EACH_VEC_BACK( m_ActivePlayers, i )
{
if (m_ActivePlayers[i].Get() == pPlayer)
{
if (m_ActivePlayerTimes[i] > gpGlobals->curtime || m_ActivePlayerTimes[i] == MENU_INFINITE_TIME)
return true;
// Remove this player since it's no longer valid
m_ActivePlayers.Remove( i );
m_ActivePlayerTimes.Remove( i );
return false;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMenu::RemoveTarget( CBaseEntity *pPlayer )
{
FOR_EACH_VEC_BACK( m_ActivePlayers, i )
{
if (m_ActivePlayers[i].Get() == pPlayer)
{
m_ActivePlayers.Remove( i );
m_ActivePlayerTimes.Remove( i );
break;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMenu::InputShowMenu( inputdata_t &inputdata )
{
if (HasSpawnFlags( SF_GAMEMENU_ALLPLAYERS ))
{
CRecipientFilter filter;
filter.AddAllPlayers();
ShowMenu( filter );
}
else
{
CBasePlayer *pPlayer = NULL;
// If we're in singleplayer, show the message to the player.
if ( gpGlobals->maxClients == 1 )
{
pPlayer = UTIL_GetLocalPlayer();
}
// Otherwise show the message to the player that triggered us.
else if ( inputdata.pActivator && inputdata.pActivator->IsNetClient() )
{
pPlayer = ToBasePlayer( inputdata.pActivator );
}
if (pPlayer)
{
CRecipientFilter filter;
filter.AddRecipient( pPlayer );
ShowMenu( filter );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMenu::InputHideMenu( inputdata_t &inputdata )
{
if (HasSpawnFlags( SF_GAMEMENU_ALLPLAYERS ))
{
CRecipientFilter filter;
FOR_EACH_VEC_BACK( m_ActivePlayers, i )
{
// Select all players in our list who are still active, and remove them
if (m_ActivePlayerTimes[i] > gpGlobals->curtime || m_ActivePlayerTimes[i] == MENU_INFINITE_TIME)
{
filter.AddRecipient( static_cast<CBasePlayer*>(m_ActivePlayers[i].Get()) );
}
m_ActivePlayers.Remove( i );
m_ActivePlayerTimes.Remove( i );
}
if (filter.GetRecipientCount() <= 0)
return;
HideMenu( filter );
}
else
{
CBasePlayer *pPlayer = NULL;
// If we're in singleplayer, show the message to the player.
if ( gpGlobals->maxClients == 1 )
{
pPlayer = UTIL_GetLocalPlayer();
}
// Otherwise show the message to the player that triggered us.
else if ( inputdata.pActivator && inputdata.pActivator->IsNetClient() )
{
pPlayer = ToBasePlayer( inputdata.pActivator );
}
if (!pPlayer)
return;
// Verify that this player is in our list
CRecipientFilter filter;
FOR_EACH_VEC( m_ActivePlayers, i )
{
if (m_ActivePlayers[i].Get() == pPlayer && (m_ActivePlayerTimes[i] > gpGlobals->curtime || m_ActivePlayerTimes[i] == MENU_INFINITE_TIME))
{
filter.AddRecipient( pPlayer );
// Remove since the player won't have the menu anymore
m_ActivePlayers.Remove( i );
m_ActivePlayerTimes.Remove( i );
break;
}
}
if (filter.GetRecipientCount() <= 0)
return;
HideMenu( filter );
}
}
#endif

View File

@ -9,7 +9,55 @@
#ifndef MAPRULES_H
#define MAPRULES_H
#ifdef MAPBASE
#define MAX_MENU_OPTIONS 10
#define SF_GAMEMENU_ALLPLAYERS 0x0001
//-----------------------------------------------------------------------------
// Purpose: Displays a custom number menu for player(s)
//-----------------------------------------------------------------------------
DECLARE_AUTO_LIST( IGameMenuAutoList );
class CGameMenu : public CLogicalEntity, public IGameMenuAutoList
{
public:
DECLARE_CLASS( CGameMenu, CLogicalEntity );
DECLARE_DATADESC();
CGameMenu();
void OnRestore();
void InputDoRestore( inputdata_t &inputdata );
void TimeoutThink();
void ShowMenu( CRecipientFilter &filter, float flDisplayTime = 0.0f );
void HideMenu( CRecipientFilter &filter );
void MenuSelected( int nSlot, CBaseEntity *pActivator );
bool IsActive();
bool IsActiveOnTarget( CBaseEntity *pPlayer );
void RemoveTarget( CBaseEntity *pPlayer );
// Inputs
void InputShowMenu( inputdata_t &inputdata );
void InputHideMenu( inputdata_t &inputdata );
private:
CUtlVector<EHANDLE> m_ActivePlayers;
CUtlVector<float> m_ActivePlayerTimes;
float m_flDisplayTime;
string_t m_iszTitle;
string_t m_iszOption[MAX_MENU_OPTIONS];
// Outputs
COutputEvent m_OnCase[MAX_MENU_OPTIONS]; // Fired for the option chosen
COutputInt m_OutValue; // Outputs the option chosen
COutputEvent m_OnTimeout; // Fires when no option was chosen in time
};
#endif
#endif // MAPRULES_H

View File

@ -22,6 +22,7 @@
#include "hierarchy.h"
#ifdef MAPBASE
#include "decals.h"
#include "death_pose.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
@ -29,6 +30,7 @@
#ifdef MAPBASE
ConVar ragdoll_autointeractions("ragdoll_autointeractions", "1", FCVAR_NONE, "Controls whether we should rely on hardcoded keyvalues or automatic flesh checks for ragdoll physgun interactions.");
ConVar ai_death_pose_server_enabled("ai_death_pose_server_enabled", "1", FCVAR_NONE, "Toggles the death pose fix code, but for server ragdolls.");
#define IsBody() VPhysicsIsFlesh()
ConVar ragdoll_always_allow_use( "ragdoll_always_allow_use", "0", FCVAR_NONE, "Allows all ragdolls to be used and, if they aren't explicitly set to prevent pickup, picked up." );
@ -788,7 +790,11 @@ void CRagdollProp::SetOverlaySequence( Activity activity )
}
}
#ifdef MAPBASE
void CRagdollProp::InitRagdoll( const Vector& forceVector, int forceBone, const Vector& forcePos, matrix3x4_t* pPrevBones, matrix3x4_t* pBoneToWorld, float dt, int collisionGroup, bool activateRagdoll, bool bWakeRagdoll, bool bDeathPose )
#else
void CRagdollProp::InitRagdoll( const Vector &forceVector, int forceBone, const Vector &forcePos, matrix3x4_t *pPrevBones, matrix3x4_t *pBoneToWorld, float dt, int collisionGroup, bool activateRagdoll, bool bWakeRagdoll )
#endif
{
SetCollisionGroup( collisionGroup );
@ -811,7 +817,11 @@ void CRagdollProp::InitRagdoll( const Vector &forceVector, int forceBone, const
params.forceVector = forceVector;
params.forceBoneIndex = forceBone;
params.forcePosition = forcePos;
#ifdef MAPBASE
params.pCurrentBones = bDeathPose ? pPrevBones : pBoneToWorld;
#else
params.pCurrentBones = pBoneToWorld;
#endif
params.jointFrictionScale = 1.0;
params.allowStretch = HasSpawnFlags(SF_RAGDOLLPROP_ALLOW_STRETCH);
#ifdef MAPBASE
@ -1492,6 +1502,43 @@ CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, con
float fPreviousCycle = clamp(pAnimating->GetCycle()-( dt * ( 1 / fSequenceDuration ) ),0.f,1.f);
float fCurCycle = pAnimating->GetCycle();
#ifdef MAPBASE
int deathpose = ACT_INVALID;
int deathframe = 0;
if (ai_death_pose_server_enabled.GetBool() && pAnimating->IsNPC()) {
CAI_BaseNPC* npc = (CAI_BaseNPC*)pAnimating;
if (npc) {
deathpose = Activity(npc->GetDeathPose());
deathframe = npc->GetDeathPoseFrame();
}
}
if (deathpose != ACT_INVALID) {
int currentSequence = pAnimating->GetSequence();
//Force pAnimating to position the deathpose
pAnimating->SetSequence(deathpose);
pAnimating->SetCycle((float)deathframe / MAX_DEATHPOSE_FRAMES);
//Store the position
pAnimating->SetupBones(pBoneToWorldNext, BONE_USED_BY_ANYTHING);
//Restore the current sequence and cycle
pAnimating->SetSequence(currentSequence);
pAnimating->SetCycle(fCurCycle);
pAnimating->SetupBones(pBoneToWorld, BONE_USED_BY_ANYTHING);
}
else {
// Get current bones positions
pAnimating->SetupBones(pBoneToWorldNext, BONE_USED_BY_ANYTHING);
// Get previous bones positions
pAnimating->SetCycle(fPreviousCycle);
pAnimating->SetupBones(pBoneToWorld, BONE_USED_BY_ANYTHING);
// Restore current cycle
pAnimating->SetCycle(fCurCycle);
}
#else
// Get current bones positions
pAnimating->SetupBones( pBoneToWorldNext, BONE_USED_BY_ANYTHING );
// Get previous bones positions
@ -1499,6 +1546,7 @@ CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, con
pAnimating->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );
// Restore current cycle
pAnimating->SetCycle( fCurCycle );
#endif
// Reset previous bone flags
pAnimating->ClearBoneCacheFlags( BCF_NO_ANIMATION_SKIP );
@ -1573,7 +1621,11 @@ CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, con
}
else
{
#ifdef MAPBASE
pRagdoll->InitRagdoll(info.GetDamageForce(), forceBone, info.GetDamagePosition(), pBoneToWorld, pBoneToWorldNext, dt, collisionGroup, true, true, deathpose != ACT_INVALID);
#else
pRagdoll->InitRagdoll( info.GetDamageForce(), forceBone, info.GetDamagePosition(), pBoneToWorld, pBoneToWorldNext, dt, collisionGroup, true );
#endif
}
// Are we dissolving?

View File

@ -75,7 +75,11 @@ public:
// locals
void InitRagdollAnimation( void );
#ifdef MAPBASE
void InitRagdoll( const Vector& forceVector, int forceBone, const Vector& forcePos, matrix3x4_t* pPrevBones, matrix3x4_t* pBoneToWorld, float dt, int collisionGroup, bool activateRagdoll, bool bWakeRagdoll = true, bool bDeathPose = false );
#else
void InitRagdoll( const Vector &forceVector, int forceBone, const Vector &forcePos, matrix3x4_t *pPrevBones, matrix3x4_t *pBoneToWorld, float dt, int collisionGroup, bool activateRagdoll, bool bWakeRagdoll = true );
#endif
void RecheckCollisionFilter( void );
void SetDebrisThink();

View File

@ -4475,6 +4475,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)
@ -7959,7 +7964,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;
@ -8866,6 +8871,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

@ -1179,6 +1179,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
@ -1222,6 +1225,7 @@ private:
char m_szNetname[MAX_PLAYER_NAME_LENGTH];
#ifdef MAPBASE
CNetworkVar( bool, m_bDrawPlayerLegs );
CNetworkVar( bool, m_bDrawPlayerModelExternally );
#endif

View File

@ -393,7 +393,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 )
@ -412,6 +420,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 );
@ -482,6 +494,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; }
@ -489,6 +504,7 @@ public:
bool HasFlexAnimation( void );
#ifdef MAPBASE
bool IsPlayingSpeech( void );
CBaseFlex *GetLastSpeakingActor( void );
#endif
void SetCurrentTime( float t, bool forceClientSync );
@ -594,6 +610,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();
@ -643,6 +662,9 @@ private:
bool m_bGenerated;
string_t m_iszSoundName;
CHandle< CBaseFlex > m_hActor;
#ifdef MAPBASE
bool m_bChoreoSentence;
#endif
EHANDLE m_hActivator;
@ -736,6 +758,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 ),
@ -757,8 +782,9 @@ BEGIN_DATADESC( CSceneEntity )
DEFINE_INPUTFUNC( FIELD_STRING, "InterjectResponse", InputInterjectResponse ),
DEFINE_INPUTFUNC( FIELD_VOID, "StopWaitingForActor", InputStopWaitingForActor ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "Trigger", InputTriggerEvent ),
#ifdef MAPBASE
DEFINE_INPUTFUNC( FIELD_VOID, "PauseAtLastInterrupt", InputPauseAtLastInterrupt ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget1", InputSetTarget1 ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget2", InputSetTarget2 ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget3", InputSetTarget3 ),
@ -953,6 +979,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:
//-----------------------------------------------------------------------------
@ -960,6 +1126,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 ) );
}
@ -1138,6 +1311,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.
@ -1185,6 +1374,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
@ -1930,6 +2146,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 )
@ -1939,6 +2175,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 );
@ -2468,6 +2705,39 @@ void CSceneEntity::InputInterjectResponse( inputdata_t &inputdata )
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
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::SetTarget( int nTarget, string_t pTargetName, CBaseEntity *pActivator, CBaseEntity *pCaller )
@ -3096,13 +3366,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);
@ -3115,7 +3394,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;
}
#else
AI_Response *result = pBaseActor->SpeakFindResponse( STRING(m_iszResumeSceneFile), NULL );
@ -5667,6 +5946,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

@ -46,8 +46,12 @@ $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\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\game_timer.cpp"
$File "$SRCDIR\game\shared\mapbase\game_timer.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]
@ -63,6 +67,8 @@ $Project
$File "mapbase\ai_grenade.h"
$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

@ -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

@ -22,6 +22,10 @@
extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * );
#ifdef MAPBASE_VSCRIPT
ConVar script_connect_debugger_on_mapspawn( "script_connect_debugger_on_mapspawn", "0" );
#endif
// #define VMPROFILE 1
#ifdef VMPROFILE
@ -663,6 +667,17 @@ bool VScriptServerInit()
RegisterSharedScriptFunctions();
#endif
#ifdef MAPBASE_VSCRIPT
if ( script_connect_debugger_on_mapspawn.GetInt() == 2 )
{
g_pScriptVM->ConnectDebugger( vscript_debugger_port, 10.0f );
}
else if ( script_connect_debugger_on_mapspawn.GetInt() != 0 )
{
g_pScriptVM->ConnectDebugger( vscript_debugger_port );
}
#endif
if (scriptLanguage == SL_SQUIRREL)
{
g_pScriptVM->Run( g_Script_vscript_server );

View File

@ -72,16 +72,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 )
@ -106,10 +106,9 @@ function __ReplaceClosures( script, scope )
}
}
local __OutputsPattern = regexp("^On.*Output$");
function ConnectOutputs( table )
{
local __OutputsPattern = regexp("^On.*Output$");
local nCharsToStrip = 6;
foreach( key, val in table )
{

View File

@ -2694,6 +2694,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 );
@ -2702,6 +2714,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
@ -2713,6 +2726,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 );
@ -2723,6 +2737,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 );
@ -2733,6 +2748,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 );
@ -2743,6 +2759,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 );
@ -2753,6 +2770,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 );
@ -2763,6 +2781,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 );
@ -2773,6 +2792,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

@ -2598,6 +2598,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,
@ -2606,6 +2618,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
@ -2618,6 +2631,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,
@ -2628,6 +2642,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,
@ -2638,6 +2653,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,
@ -2648,6 +2664,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,
@ -2658,6 +2675,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,
@ -2668,6 +2686,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,
@ -2678,6 +2697,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
@ -654,9 +654,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" );
@ -942,6 +944,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
}
@ -1024,9 +1035,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

@ -507,6 +507,11 @@ bool CBaseCombatWeapon::UsesHands() const
{
return GetWpnData().m_bUsesHands;
}
int CBaseCombatWeapon::GetHandRig() const
{
return GetWpnData().m_nHandRig;
}
#endif
//-----------------------------------------------------------------------------
@ -1127,7 +1132,7 @@ WeaponClass_t CBaseCombatWeapon::WeaponClassFromString(const char *str)
return WEPCLASS_RIFLE;
else if (FStrEq(str, "WEPCLASS_SHOTGUN"))
return WEPCLASS_SHOTGUN;
else if (FStrEq(str, "WEPCLASS_HEAY"))
else if (FStrEq(str, "WEPCLASS_HEAVY"))
return WEPCLASS_HEAVY;
else if (FStrEq(str, "WEPCLASS_MELEE"))
@ -3127,6 +3132,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

@ -307,6 +307,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;
#endif
@ -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

@ -3033,7 +3033,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

@ -255,7 +255,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

@ -27,6 +27,9 @@
#include "player_resource.h"
#include "tactical_mission.h"
#include "gamestats.h"
#ifdef MAPBASE
#include "maprules.h"
#endif
#endif
@ -619,6 +622,27 @@ bool CGameRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
{
if( GetVoiceGameMgr()->ClientCommand( static_cast<CBasePlayer*>(pEdict), args ) )
return true;
#ifdef MAPBASE
if ( FStrEq( args[0], "menuselect" ) )
{
if ( args.ArgC() >= 2 )
{
int slot = atoi( args[1] );
// See if this is from a game_menu
for ( int i = 0; i < IGameMenuAutoList::AutoList().Count(); i++ )
{
CGameMenu *pMenu = static_cast<CGameMenu*>( IGameMenuAutoList::AutoList()[i] );
if ( pMenu->IsActiveOnTarget( pEdict ) )
{
pMenu->MenuSelected( slot, pEdict );
return true;
}
}
}
}
#endif
}
return false;

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"
@ -436,4 +439,66 @@ const WeaponProficiencyInfo_t *CBaseHLCombatWeapon::GetDefaultProficiencyValues(
return g_BaseWeaponProficiencyTable;
}
#endif
#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

@ -0,0 +1,658 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: Generic custom timer entity
//
// Author: Blixibon
//
//=============================================================================//
#include "cbase.h"
#include "game_timer.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern bool IsInCommentaryMode( void );
ConVar sv_game_menu_default_warn_frac( "sv_game_menu_default_warn_frac", "0.25", FCVAR_REPLICATED );
LINK_ENTITY_TO_CLASS( game_timer, CGameTimer );
IMPLEMENT_NETWORKCLASS_ALIASED( GameTimer, DT_GameTimer )
BEGIN_NETWORK_TABLE_NOBASE( CGameTimer, DT_GameTimer )
#ifdef CLIENT_DLL
RecvPropBool( RECVINFO( m_bTimerPaused ) ),
RecvPropTime( RECVINFO( m_flTimerInitialLength ) ),
RecvPropTime( RECVINFO( m_flTimerMaxLength ) ),
RecvPropTime( RECVINFO( m_flTimeRemaining ) ),
RecvPropTime( RECVINFO( m_flTimerEndTime ) ),
RecvPropTime( RECVINFO( m_flWarnTime ) ),
RecvPropBool( RECVINFO( m_bIsDisabled ) ),
RecvPropBool( RECVINFO( m_bStartPaused ) ),
RecvPropBool( RECVINFO( m_bShowTimeRemaining ) ),
RecvPropInt( RECVINFO( m_nProgressBarMaxSegments ) ),
RecvPropInt( RECVINFO( m_nProgressBarOverride ) ),
RecvPropFloat( RECVINFO( m_flOverrideX ) ),
RecvPropFloat( RECVINFO( m_flOverrideY ) ),
RecvPropString( RECVINFO( m_szTimerCaption ) ),
RecvPropInt( RECVINFO( m_iTeamNum ) ),
#else
SendPropBool( SENDINFO( m_bTimerPaused ) ),
SendPropTime( SENDINFO( m_flTimerInitialLength ) ),
SendPropTime( SENDINFO( m_flTimerMaxLength ) ),
SendPropTime( SENDINFO( m_flTimeRemaining ) ),
SendPropTime( SENDINFO( m_flTimerEndTime ) ),
SendPropTime( SENDINFO( m_flWarnTime ) ),
SendPropBool( SENDINFO( m_bIsDisabled ) ),
SendPropBool( SENDINFO( m_bStartPaused ) ),
SendPropBool( SENDINFO( m_bShowTimeRemaining ) ),
SendPropInt( SENDINFO( m_nProgressBarMaxSegments ) ),
SendPropInt( SENDINFO( m_nProgressBarOverride ) ),
SendPropFloat( SENDINFO( m_flOverrideX ) ),
SendPropFloat( SENDINFO( m_flOverrideY ) ),
SendPropString( SENDINFO( m_szTimerCaption ) ),
SendPropInt( SENDINFO( m_iTeamNum ), TEAMNUM_NUM_BITS, 0 ),
#endif
END_NETWORK_TABLE()
#ifndef CLIENT_DLL
BEGIN_DATADESC( CGameTimer )
DEFINE_KEYFIELD( m_flTimerInitialLength, FIELD_FLOAT, "timer_length" ),
DEFINE_KEYFIELD( m_flTimerMaxLength, FIELD_FLOAT, "max_length" ),
DEFINE_KEYFIELD( m_flWarnTime, FIELD_FLOAT, "warn_time" ),
DEFINE_KEYFIELD( m_bIsDisabled, FIELD_BOOLEAN, "StartDisabled" ),
DEFINE_KEYFIELD( m_bStartPaused, FIELD_BOOLEAN, "start_paused" ),
DEFINE_KEYFIELD( m_bShowTimeRemaining, FIELD_BOOLEAN, "show_time_remaining" ),
DEFINE_KEYFIELD( m_bDisableOnFinish, FIELD_BOOLEAN, "disable_on_finish" ),
DEFINE_KEYFIELD( m_bShowInHUD, FIELD_BOOLEAN, "show_in_hud" ),
DEFINE_KEYFIELD( m_nProgressBarMaxSegments, FIELD_INTEGER, "progress_bar_max" ),
DEFINE_KEYFIELD( m_nProgressBarOverride, FIELD_INTEGER, "progress_bar_override" ),
DEFINE_KEYFIELD( m_flOverrideX, FIELD_FLOAT, "x" ),
DEFINE_KEYFIELD( m_flOverrideY, FIELD_FLOAT, "y" ),
DEFINE_AUTO_ARRAY( m_szTimerCaption, FIELD_CHARACTER ),
DEFINE_KEYFIELD( m_iszPlayerFilterName, FIELD_STRING, "PlayerFilter" ),
DEFINE_FIELD( m_hPlayerFilter, FIELD_EHANDLE ),
DEFINE_FIELD( m_flTimerEndTime, FIELD_TIME ),
DEFINE_FIELD( m_flTimeRemaining, FIELD_FLOAT ),
DEFINE_FIELD( m_bTimerPaused, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bStartedWarn, FIELD_BOOLEAN ),
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Pause", InputPause ),
DEFINE_INPUTFUNC( FIELD_VOID, "Resume", InputResume ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetTime", InputSetTime ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "AddTime", InputAddTime ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "RemoveTime", InputRemoveTime ),
DEFINE_INPUTFUNC( FIELD_VOID, "Restart", InputRestart ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetMaxTime", InputSetMaxTime ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetTimerCaption", InputSetTimerCaption ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetProgressBarMaxSegments", InputSetProgressBarMaxSegments ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetProgressBarOverride", InputSetProgressBarOverride ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetX", InputSetX ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetY", InputSetY ),
DEFINE_INPUTFUNC( FIELD_VOID, "GetTimeRemaining", InputGetTimeRemaining ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetPlayerFilter", InputSetPlayerFilter ),
DEFINE_OUTPUT( m_OnFinished, "OnFinished" ),
DEFINE_OUTPUT( m_OnPaused, "OnPaused" ),
DEFINE_OUTPUT( m_OnResumed, "OnResumed" ),
DEFINE_OUTPUT( m_OnWarned, "OnWarned" ),
DEFINE_OUTPUT( m_OnTick, "OnTick" ),
DEFINE_OUTPUT( m_OnGetTimeRemaining, "OnGetTimeRemaining" ),
DEFINE_THINKFUNC( TimerThink ),
END_DATADESC();
#endif
#ifdef CLIENT_DLL
IMPLEMENT_AUTO_LIST( IGameTimerAutoList );
#else
#define TIMER_THINK "GameTimerThink"
#endif
//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
CGameTimer::CGameTimer( void )
{
m_bIsDisabled = true;
m_bTimerPaused = true;
m_flTimeRemaining = 0;
m_flTimerEndTime = 0;
m_flWarnTime = -1.0f;
m_bStartPaused = false;
m_bShowTimeRemaining = true;
m_flTimerInitialLength = 0;
m_flTimerMaxLength = 0;
m_nProgressBarMaxSegments = -1;
m_nProgressBarOverride = -1;
m_flOverrideX = m_flOverrideY = -1.0f;
#ifndef CLIENT_DLL
m_bDisableOnFinish = true;
m_bShowInHUD = true;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: destructor
//-----------------------------------------------------------------------------
CGameTimer::~CGameTimer( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::Spawn( void )
{
#ifndef CLIENT_DLL
if ( IsDisabled() ) // we need to get the data initialized before actually become disabled
{
m_bIsDisabled = false;
SetTimeRemaining( m_flTimerInitialLength );
m_bIsDisabled = true;
}
else
{
SetTimeRemaining( m_flTimerInitialLength );
if ( !m_bStartPaused )
ResumeTimer();
}
if ( m_iszPlayerFilterName != NULL_STRING )
{
m_hPlayerFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iszPlayerFilterName, this ));
}
#endif
BaseClass::Spawn();
}
#ifndef CLIENT_DLL
bool CGameTimer::KeyValue( const char *szKeyName, const char *szValue )
{
if ( FStrEq( szKeyName, "timer_caption" ) )
{
Q_strncpy( m_szTimerCaption.GetForModify(), szValue, MAX_GAME_TIMER_CAPTION );
}
else
{
return BaseClass::KeyValue( szKeyName, szValue );
}
return true;
}
bool CGameTimer::GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen )
{
if ( FStrEq( szKeyName, "timer_caption" ) )
{
Q_snprintf( szValue, iMaxLen, "%s", m_szTimerCaption.Get() );
return true;
}
return BaseClass::GetKeyValue( szKeyName, szValue, iMaxLen );
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Gets the seconds left on the timer, paused or not.
//-----------------------------------------------------------------------------
float CGameTimer::GetTimeRemaining( void )
{
float flSecondsRemaining;
if ( m_bTimerPaused )
{
flSecondsRemaining = m_flTimeRemaining;
}
else
{
flSecondsRemaining = m_flTimerEndTime - gpGlobals->curtime;
}
if ( flSecondsRemaining < 0 )
{
flSecondsRemaining = 0.0f;
}
return flSecondsRemaining;
}
//-----------------------------------------------------------------------------
// Purpose: Gets the timer's warning time
//-----------------------------------------------------------------------------
float CGameTimer::GetWarnTime( void )
{
if ( m_flWarnTime < 0 )
{
// TODO: All of the default warning stuff is on the client!!!
return GetTimerMaxLength() * sv_game_menu_default_warn_frac.GetFloat();
}
return m_flWarnTime;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CGameTimer::GetTimerMaxLength( void )
{
if ( m_flTimerMaxLength )
return m_flTimerMaxLength;
return m_flTimerInitialLength;
}
#ifdef CLIENT_DLL
bool g_bAnyGameTimerActive = false;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_GameTimer::OnDataChanged( DataUpdateType_t updateType )
{
BaseClass::OnDataChanged( updateType );
if ( !m_bIsDisabled )
{
g_bAnyGameTimerActive = true;
}
else if ( !m_bOldDisabled )
{
// Go through all of the timers and mark when one is active
g_bAnyGameTimerActive = false;
for ( int i = 0; i < IGameTimerAutoList::AutoList().Count(); i++ )
{
C_GameTimer *pTimer = static_cast<C_GameTimer *>( IGameTimerAutoList::AutoList()[i] );
if ( !pTimer->IsDisabled() && !pTimer->IsMarkedForDeletion() )
{
g_bAnyGameTimerActive = true;
break;
}
}
}
m_bOldDisabled = m_bIsDisabled;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_GameTimer::UpdateOnRemove( void )
{
BaseClass::UpdateOnRemove();
// Update timer presence state
g_bAnyGameTimerActive = false;
for ( int i = 0; i < IGameTimerAutoList::AutoList().Count(); i++ )
{
C_GameTimer *pTimer = static_cast<C_GameTimer *>( IGameTimerAutoList::AutoList()[i] );
if ( pTimer != this && !pTimer->IsDisabled() && !pTimer->IsMarkedForDeletion() )
{
g_bAnyGameTimerActive = true;
break;
}
}
}
#else
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::TimerThink( void )
{
if ( IsDisabled() /*|| IsInCommentaryMode() || gpGlobals->eLoadType == MapLoad_Background*/ )
{
SetContextThink( &CGameTimer::TimerThink, gpGlobals->curtime + 0.05, TIMER_THINK );
return;
}
float flTime = GetTimeRemaining();
int nTick = (int)floorf( flTime );
if (nTick != m_OnTick.Get())
{
m_OnTick.Set( nTick, this, this );
}
if ( flTime <= 0.0f )
{
OnTimerFinished();
PauseTimer();
if (m_bDisableOnFinish)
m_bIsDisabled = true;
return;
}
else if ( flTime <= GetWarnTime() && !m_bStartedWarn)
{
OnTimerWarned();
m_bStartedWarn = true;
}
SetContextThink( &CGameTimer::TimerThink, gpGlobals->curtime + 0.05, TIMER_THINK );
}
//-----------------------------------------------------------------------------
// Purpose: To set the initial timer duration
//-----------------------------------------------------------------------------
void CGameTimer::SetTimeRemaining( float flTime )
{
// make sure we don't go over our max length
flTime = m_flTimerMaxLength > 0 ? MIN( flTime, m_flTimerMaxLength ) : flTime;
m_flTimeRemaining = flTime;
m_flTimerEndTime = gpGlobals->curtime + m_flTimeRemaining;
if ( m_flTimeRemaining > m_flWarnTime )
{
m_bStartedWarn = false;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::OnTimerFinished()
{
m_OnFinished.FireOutput( this, this );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::OnTimerWarned()
{
m_OnWarned.FireOutput( this, this );
}
//-----------------------------------------------------------------------------
// Purpose: Timer is paused at round end, stops the countdown
//-----------------------------------------------------------------------------
void CGameTimer::PauseTimer( CBaseEntity *pActivator )
{
if ( IsDisabled() )
return;
if ( m_bTimerPaused == false )
{
m_bTimerPaused = true;
m_flTimeRemaining = m_flTimerEndTime - gpGlobals->curtime;
m_OnPaused.FireOutput( pActivator ? pActivator : this, this );
SetContextThink( NULL, TICK_NEVER_THINK, TIMER_THINK );
}
}
//-----------------------------------------------------------------------------
// Purpose: To start or re-start the timer after a pause
//-----------------------------------------------------------------------------
void CGameTimer::ResumeTimer( CBaseEntity *pActivator )
{
if ( IsDisabled() )
return;
if ( m_bTimerPaused == true )
{
m_bTimerPaused = false;
m_flTimerEndTime = gpGlobals->curtime + m_flTimeRemaining;
m_OnResumed.FireOutput( pActivator ? pActivator : this, this );
TimerThink();
SetContextThink( &CGameTimer::TimerThink, gpGlobals->curtime + 0.05, TIMER_THINK );
}
}
//-----------------------------------------------------------------------------
// Purpose: Add seconds to the timer while it is running or paused
//-----------------------------------------------------------------------------
void CGameTimer::AddTimerSeconds( float flTimeToAdd )
{
if ( IsDisabled() )
return;
if ( m_flTimerMaxLength > 0 )
{
// will adding this many seconds push us over our max length?
if ( GetTimeRemaining() + flTimeToAdd > m_flTimerMaxLength)
{
// adjust to only add up to our max length
flTimeToAdd = m_flTimerMaxLength - GetTimeRemaining();
}
}
if ( m_bTimerPaused )
{
m_flTimeRemaining += flTimeToAdd;
}
else
{
m_flTimerEndTime += flTimeToAdd;
}
}
//-----------------------------------------------------------------------------
// Purpose: Should we transmit it to the client?
//-----------------------------------------------------------------------------
int CGameTimer::UpdateTransmitState()
{
if ( !m_bShowInHUD )
{
return SetTransmitState( FL_EDICT_DONTSEND );
}
if ( m_hPlayerFilter || GetTeamNumber() > TEAM_UNASSIGNED )
{
return SetTransmitState( FL_EDICT_FULLCHECK );
}
return SetTransmitState( FL_EDICT_ALWAYS );
}
//-----------------------------------------------------------------------------
// Purpose: Which clients should we be transmitting to?
//-----------------------------------------------------------------------------
int CGameTimer::ShouldTransmit( const CCheckTransmitInfo *pInfo )
{
int result = BaseClass::ShouldTransmit( pInfo );
if ( result != FL_EDICT_DONTSEND )
{
CBaseEntity *pClient = (CBaseEntity *)(pInfo->m_pClientEnt->GetUnknown());
if ( pClient )
{
if ( m_hPlayerFilter )
{
// Don't send to players who don't pass our filter
if ( !m_hPlayerFilter->PassesFilter( this, pClient ) )
return FL_EDICT_DONTSEND;
}
else if ( GetTeamNumber() > TEAM_UNASSIGNED )
{
// If we don't have an explicit filter, then just check if it's on the same team as us
if ( pClient->GetTeamNumber() != GetTeamNumber() )
return FL_EDICT_DONTSEND;
}
return FL_EDICT_ALWAYS;
}
}
return result;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputPause( inputdata_t &input )
{
PauseTimer( input.pActivator );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputResume( inputdata_t &input )
{
ResumeTimer( input.pActivator );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputSetTime( inputdata_t &input )
{
float flTime = input.value.Float();
SetTimeRemaining( flTime );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputAddTime( inputdata_t &input )
{
float flTime = input.value.Float();
AddTimerSeconds( flTime );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputRemoveTime( inputdata_t &input )
{
float flTime = input.value.Float();
AddTimerSeconds( -flTime );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputSetMaxTime( inputdata_t &input )
{
float flTime = input.value.Float();
m_flTimerMaxLength = flTime;
if (m_flTimerMaxLength > 0)
{
// make sure our current time is not above the max length
if (GetTimeRemaining() > m_flTimerMaxLength)
{
SetTimeRemaining( m_flTimerMaxLength );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputSetTimerCaption( inputdata_t &input )
{
Q_strncpy( m_szTimerCaption.GetForModify(), input.value.String(), MAX_GAME_TIMER_CAPTION );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputSetProgressBarMaxSegments( inputdata_t &input )
{
m_nProgressBarMaxSegments = input.value.Int();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputSetProgressBarOverride( inputdata_t &input )
{
m_nProgressBarOverride = input.value.Int();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputSetX( inputdata_t &input )
{
m_flOverrideX = input.value.Float();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputSetY( inputdata_t &input )
{
m_flOverrideY = input.value.Float();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputRestart( inputdata_t &input )
{
SetTimeRemaining( m_flTimerInitialLength );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputGetTimeRemaining( inputdata_t &input )
{
m_OnGetTimeRemaining.Set( GetTimeRemaining(), input.pActivator, this);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputEnable( inputdata_t &input )
{
if (GetTimeRemaining() == 0.0f)
SetTimeRemaining( m_flTimerInitialLength );
m_bIsDisabled = false;
if ( !m_bStartPaused )
ResumeTimer( input.pActivator );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputDisable( inputdata_t &input )
{
PauseTimer( input.pActivator );
m_bIsDisabled = true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputSetPlayerFilter( inputdata_t &inputdata )
{
m_iszPlayerFilterName = inputdata.value.StringID();
if ( m_iszPlayerFilterName != NULL_STRING )
{
m_hPlayerFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iszPlayerFilterName, this ));
}
else
{
m_hPlayerFilter = NULL;
}
}
#endif

View File

@ -0,0 +1,145 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: Generic custom timer entity
//
// Author: Blixibon
//
//=============================================================================//
#include "cbase.h"
#ifdef CLIENT_DLL
#include "c_baseentity.h"
#else
#include "filters.h"
#endif
#ifdef CLIENT_DLL
DECLARE_AUTO_LIST( IGameTimerAutoList );
#define CGameTimer C_GameTimer
#endif
#define MAX_GAME_TIMER_CAPTION 32
//-----------------------------------------------------------------------------
// Purpose: Displays a custom timer
//-----------------------------------------------------------------------------
class CGameTimer : public CBaseEntity
#ifdef CLIENT_DLL
, public IGameTimerAutoList
#endif
{
public:
DECLARE_CLASS( CGameTimer, CBaseEntity );
DECLARE_NETWORKCLASS();
#ifndef CLIENT_DLL
DECLARE_DATADESC();
#endif
CGameTimer();
~CGameTimer();
virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
void Spawn();
#ifndef CLIENT_DLL
bool KeyValue( const char *szKeyName, const char *szValue );
virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen );
#endif
// Based on teamplay_round_timer
virtual float GetTimeRemaining( void );
virtual float GetTimerMaxLength( void );
virtual bool StartPaused( void ) { return m_bStartPaused; }
bool ShowTimeRemaining( void ) { return m_bShowTimeRemaining; }
float GetWarnTime( void );
const char *GetTimerCaption( void ) { return m_szTimerCaption.Get(); }
int GetProgressBarMaxSegments( void ) { return m_nProgressBarMaxSegments; }
int GetProgressBarOverride( void ) { return m_nProgressBarOverride; }
bool OverridesPosition( void ) { return m_flOverrideX != -1.0f || m_flOverrideY != -1.0f; }
void GetPositionOverride( float &flX, float &flY ) { flX = m_flOverrideX; flY = m_flOverrideY; }
bool IsDisabled( void ) { return m_bIsDisabled; }
bool IsTimerPaused( void ) { return m_bTimerPaused; }
#ifndef CLIENT_DLL
virtual void SetTimeRemaining( float flTime ); // Set the initial length of the timer
virtual void AddTimerSeconds( float flTimeToAdd ); // Add time to an already running ( or paused ) timer
virtual void OnTimerFinished();
virtual void OnTimerWarned();
virtual void PauseTimer( CBaseEntity *pActivator = NULL );
virtual void ResumeTimer( CBaseEntity *pActivator = NULL );
void SetProgressBarMaxSegments( int nSegments ) { m_nProgressBarMaxSegments = nSegments; }
void SetProgressBarOverride( int nSegments ) { m_nProgressBarOverride = nSegments; }
int UpdateTransmitState();
// Inputs
void InputEnable( inputdata_t &input );
void InputDisable( inputdata_t &input );
void InputPause( inputdata_t &input );
void InputResume( inputdata_t &input );
void InputSetTime( inputdata_t &input );
void InputAddTime( inputdata_t &input );
void InputRemoveTime( inputdata_t &input );
void InputRestart( inputdata_t &input );
void InputSetMaxTime( inputdata_t &input );
void InputSetTimerCaption( inputdata_t &input );
void InputSetProgressBarMaxSegments( inputdata_t &input );
void InputSetProgressBarOverride( inputdata_t &input );
void InputSetX( inputdata_t &input );
void InputSetY( inputdata_t &input );
void InputGetTimeRemaining( inputdata_t &input );
void InputSetPlayerFilter( inputdata_t &inputdata );
#endif
private:
#ifdef CLIENT_DLL
void OnDataChanged( DataUpdateType_t updateType );
void UpdateOnRemove( void );
#else
int ShouldTransmit( const CCheckTransmitInfo *pInfo );
void TimerThink( void );
#endif
private:
CNetworkVar( bool, m_bIsDisabled );
CNetworkVar( float, m_flTimerInitialLength );
CNetworkVar( float, m_flTimerMaxLength );
CNetworkVar( float, m_flTimerEndTime );
CNetworkVar( float, m_flTimeRemaining );
CNetworkVar( float, m_flWarnTime ); // Time at which timer turns red, starts ticking loudly, etc.
CNetworkVar( int, m_nProgressBarMaxSegments ); // Overrides maximum segments in progress bar if greater than -1
CNetworkVar( int, m_nProgressBarOverride ); // Overrides progress bar value if greater than -1
CNetworkVar( float, m_flOverrideX );
CNetworkVar( float, m_flOverrideY );
CNetworkVar( bool, m_bTimerPaused );
CNetworkVar( bool, m_bStartPaused );
CNetworkVar( bool, m_bShowTimeRemaining );
CNetworkString( m_szTimerCaption, MAX_GAME_TIMER_CAPTION );
#ifdef CLIENT_DLL
bool m_bOldDisabled;
#else
bool m_bStartedWarn;
bool m_bDisableOnFinish;
bool m_bShowInHUD; // TODO: ShowInHUD input? Would require client to know it shouldn't show the timer anymore
string_t m_iszPlayerFilterName;
CHandle<CBaseFilter> m_hPlayerFilter;
// Outputs
COutputEvent m_OnFinished;
COutputEvent m_OnPaused;
COutputEvent m_OnResumed;
COutputEvent m_OnWarned;
COutputInt m_OnTick;
COutputFloat m_OnGetTimeRemaining;
#endif
};

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