Merge pull request #250 from mapbase-source/develop

Mapbase v7.2
This commit is contained in:
Blixibon 2024-02-25 15:48:08 -06:00 committed by GitHub
commit 7f0aec4371
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
96 changed files with 4385 additions and 319 deletions

View File

@ -51,7 +51,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Add MSBuild to PATH
uses: microsoft/setup-msbuild@v1.1

View File

@ -13,6 +13,7 @@ on:
branches:
- develop
paths:
- '.github/workflows/mapbase_build-base.yml'
- '.github/workflows/mapbase_build-sp-rel-games.yml'
- 'sp/src/vpc_scripts/**'
- 'sp/src/game/**'

View File

@ -6,12 +6,12 @@
# https://github.com/actions/labeler
name: Pull Request Automation
on: [pull_request]
on: [pull_request] # pull_request_target
jobs:
label:
if: github.repository_owner == 'mapbase-source'
if: github.repository_owner == 'mapbase-source' # ${{ vars.MAPBASE_LABELS == 'true' }}
runs-on: ubuntu-latest
permissions:
contents: read

14
README
View File

@ -1,6 +1,6 @@
//=========================================================================================================================
Mapbase v7.1 - Source 2013
Mapbase v7.2 - Source 2013
https://github.com/mapbase-source/source-sdk-2013
https://www.moddb.com/mods/mapbase
@ -112,6 +112,12 @@ Direct contributions:
- https://github.com/mapbase-source/source-sdk-2013/issues/201 (env_projectedtexture shadow filter keyvalue from celisej567)
- https://github.com/mapbase-source/source-sdk-2013/pull/193 (RTB:R info_particle_system_coordinate by arbabf and Iridium77)
- https://github.com/mapbase-source/source-sdk-2013/pull/193 (Infinite prop_interactable cooldown by arbabf)
- https://github.com/mapbase-source/source-sdk-2013/pull/229 (Extended point_bugbait functionality by arbabf)
- https://github.com/mapbase-source/source-sdk-2013/pull/236 (Toggleable prop sprinting by Crimson-X1)
- https://github.com/mapbase-source/source-sdk-2013/pull/237 (Commander goal trace fix by Agrimar)
- 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/mapbase-game-src/pull/1 (Advanced video options duplicate field name 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
- 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)
@ -137,6 +143,9 @@ 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/229 (VScript VGUI HUD viewport parenting, game_text and vgui_text_display VScript font fallback)
=-- 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)
== Contributions from z33ky:
=-- https://github.com/mapbase-source/source-sdk-2013/pull/21 (Various GCC/Linux compilation fixes)
@ -156,6 +165,9 @@ Direct contributions:
=-- https://github.com/mapbase-source/source-sdk-2013/pull/184 (Projected texture horizontal FOV shadow fix)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/185 (Fix enemyfinders becoming visible when they wake)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/186 (Fix for brightly glowing teeth)
=-- https://github.com/mapbase-source/source-sdk-2013/pull/183 (Enhanced custom weapons support)
=-- 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)
//-------------------------------------------------------------------------------------------------------------------------

View File

@ -208,7 +208,7 @@ void CWeaponAR2::DelayedAttack( void )
// pOwner->SnapEyeAngles( angles );
pOwner->ViewPunch( QAngle( SharedRandomInt( "ar2pax", -8, -12 ), SharedRandomInt( "ar2pay", 1, 2 ), 0 ) );
pOwner->ViewPunch( QAngle( SharedRandomInt( "ar2pax", -12, -8 ), SharedRandomInt( "ar2pay", 1, 2 ), 0 ) );
// Decrease ammo
pOwner->RemoveAmmo( 1, m_iSecondaryAmmoType );

View File

@ -4042,6 +4042,92 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int
}
break;
#ifdef MAPBASE // From Alien Swarm SDK
case AE_CL_STOP_PARTICLE_EFFECT:
{
char token[256];
char szParticleEffect[256];
// Get the particle effect name
const char *p = options;
p = nexttoken(token, p, ' ', sizeof(token));
if ( token )
{
Q_strncpy( szParticleEffect, token, sizeof(szParticleEffect) );
}
// Get the attachment point index
p = nexttoken(token, p, ' ', sizeof(token));
bool bStopInstantly = ( token && !Q_stricmp( token, "instantly" ) );
ParticleProp()->StopParticlesNamed( szParticleEffect, bStopInstantly );
}
break;
case AE_CL_ADD_PARTICLE_EFFECT_CP:
{
int iControlPoint = 1;
int iAttachment = -1;
int iAttachType = PATTACH_ABSORIGIN_FOLLOW;
int iEffectIndex = -1;
char token[256];
char szParticleEffect[256];
// Get the particle effect name
const char *p = options;
p = nexttoken(token, p, ' ', sizeof(token));
if ( token )
{
Q_strncpy( szParticleEffect, token, sizeof(szParticleEffect) );
}
// Get the control point number
p = nexttoken(token, p, ' ', sizeof(token));
if ( token )
{
iControlPoint = atoi( token );
}
// Get the attachment type
p = nexttoken(token, p, ' ', sizeof(token));
if ( token )
{
iAttachType = GetAttachTypeFromString( token );
if ( iAttachType == -1 )
{
Warning("Invalid attach type specified for particle effect anim event. Trying to spawn effect '%s' with attach type of '%s'\n", szParticleEffect, token );
return;
}
}
// Get the attachment point index
p = nexttoken(token, p, ' ', sizeof(token));
if ( token )
{
iAttachment = atoi(token);
// See if we can find any attachment points matching the name
if ( token[0] != '0' && iAttachment == 0 )
{
iAttachment = LookupAttachment( token );
if ( iAttachment == -1 )
{
Warning("Failed to find attachment point specified for particle effect anim event. Trying to spawn effect '%s' on attachment named '%s'\n", szParticleEffect, token );
return;
}
}
}
iEffectIndex = ParticleProp()->FindEffect( szParticleEffect );
if ( iEffectIndex == -1 )
{
Warning("Failed to find specified particle effect. Trying to add CP to '%s' on attachment named '%s'\n", szParticleEffect, token );
return;
}
ParticleProp()->AddControlPoint( iEffectIndex, iControlPoint, this, (ParticleAttachment_t)iAttachType, iAttachment );
}
break;
#endif
case AE_CL_PLAYSOUND:
{
CLocalPlayerFilter filter;
@ -4291,6 +4377,22 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int
}
break;
#ifdef MAPBASE
case AE_VSCRIPT_RUN:
{
if (!RunScript( options ))
Warning( "%s failed to run AE_VSCRIPT_RUN on client with \"%s\"\n", GetDebugName(), options );
}
break;
case AE_VSCRIPT_RUN_FILE:
{
if (!RunScriptFile( options ))
Warning( "%s failed to run AE_VSCRIPT_RUN_FILE on client with \"%s\"\n", GetDebugName(), options );
}
break;
#endif
default:
break;
}

View File

@ -181,7 +181,7 @@ END_PREDICTION_DATA()
#ifdef MAPBASE_VSCRIPT
BEGIN_ENT_SCRIPTDESC( C_BaseCombatCharacter, CBaseEntity, "" )
BEGIN_ENT_SCRIPTDESC( C_BaseCombatCharacter, C_BaseAnimating, "" )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetAmmoCount, "GetAmmoCount", "" )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetActiveWeapon, "GetActiveWeapon", "" )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetWeapon, "GetWeapon", "" )

View File

@ -502,6 +502,9 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities
DEFINE_SCRIPTFUNC_NAMED( GetScriptOwnerEntity, "GetOwner", "Gets this entity's owner" )
DEFINE_SCRIPTFUNC_NAMED( SetScriptOwnerEntity, "SetOwner", "Sets this entity's owner" )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetGroundEntity, "GetGroundEntity", "Get the entity we're standing on." )
DEFINE_SCRIPTFUNC_NAMED( ScriptSetGroundEntity, "SetGroundEntity", "Set the entity we're standing on." )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorVector, "GetRenderColorVector", "Get the render color as a vector" )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorR, "GetRenderColorR", "Get the render color's R value" )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorG, "GetRenderColorG", "Get the render color's G value" )

View File

@ -288,6 +288,11 @@ public:
HSCRIPT GetScriptOwnerEntity();
virtual void SetScriptOwnerEntity(HSCRIPT pOwner);
#ifdef MAPBASE_VSCRIPT
HSCRIPT ScriptGetGroundEntity();
void ScriptSetGroundEntity( HSCRIPT hGroundEnt );
#endif
HSCRIPT GetScriptInstance();
HSCRIPT m_hScriptInstance;

View File

@ -69,6 +69,7 @@ $Project
$File "mapbase\c_func_fake_worldportal.h"
$File "mapbase\c_point_glow.cpp"
$File "mapbase\c_vgui_text_display.cpp"
$File "mapbase\c_weapon_custom_hl2.cpp"
$File "mapbase\mapbase_autocubemap.cpp"
}

View File

@ -1496,9 +1496,23 @@ void CHudCloseCaption::Process( const wchar_t *stream, float duration, const cha
if ( m_Items.Count() > 0 )
{
#ifndef MAPBASE
// Get the remaining life span of the last item
CCloseCaptionItem* final = m_Items[m_Items.Count() - 1];
float prevlife = final->GetTimeToLive();
#else
float prevlife = 0.f;
// Get the remaining life span of the last displayed item
for (int i = m_Items.Count() - 1; i >= 0; i--)
{
if (m_Items[i]->GetPreDisplayTime() > cc_predisplay_time.GetFloat())
continue;
prevlife = m_Items[i]->GetTimeToLive();
break;
}
#endif // !MAPBASE
if ( prevlife > lifespan )
{
@ -1532,7 +1546,31 @@ void CHudCloseCaption::Process( const wchar_t *stream, float duration, const cha
if ( wcslen( phrase ) > 0 )
{
CCloseCaptionItem *item = new CCloseCaptionItem( phrase, lifespan, addedlife, delay, valid, fromplayer );
#ifdef MAPBASE
if (m_Items.Count())
{
// Add it where it will appear
for (int i = m_Items.Count() - 1; i >= 0; i--)
{
if (m_Items[i]->GetPreDisplayTime() > delay + cc_predisplay_time.GetFloat())
{
if (i == 0)
{
m_Items.AddToHead(item);
break;
}
else
continue;
}
m_Items.InsertAfter(i, item);
break;
}
}
else
#endif // MAPBASE
m_Items.AddToTail(item);
if ( StreamHasCommand( phrase, L"sfx" ) )
{
// SFX show up instantly.
@ -1541,6 +1579,9 @@ void CHudCloseCaption::Process( const wchar_t *stream, float duration, const cha
if ( GetFloatCommandValue( phrase, L"len", override_duration ) )
{
#ifdef MAPBASE
override_duration += cc_linger_time.GetFloat();
#endif // MAPBASE
item->SetTimeToLive( override_duration );
}
}
@ -1569,6 +1610,29 @@ void CHudCloseCaption::Process( const wchar_t *stream, float duration, const cha
if ( wcslen( phrase ) > 0 )
{
CCloseCaptionItem *item = new CCloseCaptionItem( phrase, lifespan, addedlife, delay, valid, fromplayer );
#ifdef MAPBASE
if (m_Items.Count())
{
// Add it where it will appear
for (int i = m_Items.Count() - 1; i >= 0; i--)
{
if (m_Items[i]->GetPreDisplayTime() > delay + cc_predisplay_time.GetFloat())
{
if (i == 0)
{
m_Items.AddToHead(item);
break;
}
else
continue;
}
m_Items.InsertAfter(i, item);
break;
}
}
else
#endif // MAPBASE
m_Items.AddToTail(item);
if ( StreamHasCommand( phrase, L"sfx" ) )
@ -1579,6 +1643,10 @@ void CHudCloseCaption::Process( const wchar_t *stream, float duration, const cha
if ( GetFloatCommandValue( phrase, L"len", override_duration ) )
{
#ifdef MAPBASE
override_duration += cc_linger_time.GetFloat();
#endif // MAPBASE
item->SetTimeToLive( override_duration );
item->SetInitialLifeSpan( override_duration );
}
@ -2614,8 +2682,14 @@ void CHudCloseCaption::InitCaptionDictionary( const char *dbfile )
g_AsyncCaptionResourceManager.Clear();
#ifdef MAPBASE
int iBufferSize = filesystem->GetSearchPath("GAME", true, nullptr, 0);
char* searchPaths = (char*)stackalloc(iBufferSize);
filesystem->GetSearchPath("GAME", true, searchPaths, iBufferSize);
#else
char searchPaths[4096];
filesystem->GetSearchPath( "GAME", true, searchPaths, sizeof( searchPaths ) );
#endif
for ( char *path = strtok( searchPaths, ";" ); path; path = strtok( NULL, ";" ) )
{
@ -2626,8 +2700,13 @@ void CHudCloseCaption::InitCaptionDictionary( const char *dbfile )
}
char fullpath[MAX_PATH];
#ifndef MAPBASE
Q_snprintf(fullpath, sizeof(fullpath), "%s%s", path, dbfile);
Q_FixSlashes(fullpath);
#else
V_ComposeFileName(path, dbfile, fullpath, sizeof(fullpath));
#endif // !MAPBASE
if ( IsX360() )
{
@ -2692,8 +2771,7 @@ void CHudCloseCaption::AddAdditionalCaptionDictionary( const char *dbfile, CUtlV
}
char fullpath[MAX_PATH];
Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", path, dbfile );
Q_FixSlashes( fullpath );
V_ComposeFileName(path, dbfile, fullpath, sizeof(fullpath));
if ( IsX360() )
{

View File

@ -215,6 +215,13 @@ void C_TextDisplayPanel::UpdateText()
if (pszFontName && pszFontName[0] != '\0')
{
HFont font = scheme()->GetIScheme( GetScheme() )->GetFont( pszFontName );
if ( !font )
{
extern HFont GetScriptFont( const char *, bool );
font = GetScriptFont( pszFontName, false );
}
m_pDisplayTextLabel->SetFont( font );
}

View File

@ -0,0 +1,91 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: Client classes for Half-Life 2 based custom weapons.
//
// Author: Peter Covington (petercov@outlook.com)
//
//==================================================================================//
#include "cbase.h"
#include "c_weapon__stubs.h"
#include "basehlcombatweapon_shared.h"
#include "c_basehlcombatweapon.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
class C_HLCustomWeaponMelee : public C_BaseHLBludgeonWeapon
{
public:
DECLARE_CLASS(C_HLCustomWeaponMelee, C_BaseHLBludgeonWeapon);
DECLARE_CLIENTCLASS();
DECLARE_PREDICTABLE();
C_HLCustomWeaponMelee();
void OnDataChanged( DataUpdateType_t updateType );
virtual const char* GetWeaponScriptName() { return m_iszWeaponScriptName; }
private:
char m_iszWeaponScriptName[128];
};
STUB_WEAPON_CLASS_IMPLEMENT(weapon_hlcustommelee, C_HLCustomWeaponMelee);
IMPLEMENT_CLIENTCLASS_DT(C_HLCustomWeaponMelee, DT_HLCustomWeaponMelee, CHLCustomWeaponMelee)
RecvPropString(RECVINFO(m_iszWeaponScriptName)),
END_RECV_TABLE();
C_HLCustomWeaponMelee::C_HLCustomWeaponMelee()
{
m_iszWeaponScriptName[0] = '\0';
}
void C_HLCustomWeaponMelee::OnDataChanged( DataUpdateType_t updateType )
{
if (updateType == DATA_UPDATE_CREATED)
{
Precache();
}
BaseClass::OnDataChanged( updateType );
}
class C_HLCustomWeaponGun : public C_BaseHLCombatWeapon
{
public:
DECLARE_CLASS(C_HLCustomWeaponGun, C_BaseHLCombatWeapon);
DECLARE_CLIENTCLASS();
DECLARE_PREDICTABLE();
C_HLCustomWeaponGun();
void OnDataChanged( DataUpdateType_t updateType );
virtual const char* GetWeaponScriptName() { return m_iszWeaponScriptName; }
private:
char m_iszWeaponScriptName[128];
};
STUB_WEAPON_CLASS_IMPLEMENT(weapon_hlcustomgun, C_HLCustomWeaponGun);
IMPLEMENT_CLIENTCLASS_DT(C_HLCustomWeaponGun, DT_HLCustomWeaponGun, CHLCustomWeaponGun)
RecvPropString(RECVINFO(m_iszWeaponScriptName)),
END_RECV_TABLE();
C_HLCustomWeaponGun::C_HLCustomWeaponGun()
{
m_iszWeaponScriptName[0] = '\0';
}
void C_HLCustomWeaponGun::OnDataChanged( DataUpdateType_t updateType )
{
if (updateType == DATA_UPDATE_CREATED)
{
Precache();
}
BaseClass::OnDataChanged( updateType );
}

View File

@ -51,7 +51,7 @@
#include "view.h"
#include "hudelement.h"
//#include "iclientmode.h" // g_pClientMode->GetViewport()
#include "iclientmode.h" // g_pClientMode->GetViewport()
#include "vscript_vgui.h"
#include "vscript_vgui.nut"
@ -92,10 +92,18 @@
// Changing this is not backwards compatible, as existing top level script panel depth would then change relative to non-script panels.
#define SCRIPT_ENGINE_ROOT_PANELS 1
// NOTE: causes rendering issues
#define ALLOW_SCRIPT_HUD_VIEWPORT_ROOT_PANEL 0
//
// Options to restrict where script panels can be parented to.
// The safest options any game can have are HUD viewport and clientdll.
//
#define ALLOW_SCRIPT_GAMEUI_ROOT_PANEL 0
#define ALLOW_ROOT_PANEL_PARENT 1
#define ALLOW_HUD_VIEWPORT_ROOT_PARENT 1
#define ALLOW_CLIENTDLL_ROOT_PARENT 1
#define ALLOW_GAMEUI_ROOT_PARENT 0
// On level transitions Restore is called up to 4 times in a row (due to .hl? client state files), each time
// trying to restore script panels from pre and post transitions, failing every time because script panels are
@ -353,8 +361,10 @@ public:
}
};
#if ALLOW_CLIENTDLL_ROOT_PARENT
CScriptRootDLLPanel *g_pScriptClientDLLPanel = NULL;
#if ALLOW_SCRIPT_GAMEUI_ROOT_PANEL
#endif
#if ALLOW_GAMEUI_ROOT_PARENT
CScriptRootDLLPanel *g_pScriptGameUIDLLPanel = NULL;
#endif
#endif
@ -367,12 +377,14 @@ void VGUI_DestroyScriptRootPanels()
g_pScriptRootPanel = NULL;
}
#if SCRIPT_ENGINE_ROOT_PANELS
#if ALLOW_CLIENTDLL_ROOT_PARENT
if ( g_pScriptClientDLLPanel )
{
delete g_pScriptClientDLLPanel;
g_pScriptClientDLLPanel = NULL;
}
#if ALLOW_SCRIPT_GAMEUI_ROOT_PANEL
#endif
#if ALLOW_GAMEUI_ROOT_PARENT
if ( g_pScriptGameUIDLLPanel )
{
delete g_pScriptGameUIDLLPanel;
@ -384,30 +396,29 @@ void VGUI_DestroyScriptRootPanels()
VPANEL VGUI_GetScriptRootPanel( VGuiPanel_t type )
{
#if !SCRIPT_ENGINE_ROOT_PANELS
if ( !g_pScriptRootPanel )
g_pScriptRootPanel = new CScriptRootPanel();
return enginevgui->GetPanel( type );
#else
#if SCRIPT_ENGINE_ROOT_PANELS
switch ( type )
{
case PANEL_ROOT:
#if ALLOW_ROOT_PANEL_PARENT
{
if ( !g_pScriptRootPanel )
g_pScriptRootPanel = new CScriptRootPanel();
return g_pScriptRootPanel->GetVPanel();
}
#endif
case PANEL_CLIENTDLL:
#if ALLOW_CLIENTDLL_ROOT_PARENT
{
if ( !g_pScriptClientDLLPanel )
g_pScriptClientDLLPanel = new CScriptRootDLLPanel( PANEL_CLIENTDLL, "VScriptClient" );
return g_pScriptClientDLLPanel->GetVPanel();
}
#if ALLOW_SCRIPT_GAMEUI_ROOT_PANEL
#endif
case PANEL_GAMEUIDLL:
#if ALLOW_GAMEUI_ROOT_PARENT
{
if ( !g_pScriptGameUIDLLPanel )
g_pScriptGameUIDLLPanel = new CScriptRootDLLPanel( PANEL_GAMEUIDLL, "VScriptGameUI" );
@ -415,8 +426,10 @@ VPANEL VGUI_GetScriptRootPanel( VGuiPanel_t type )
return g_pScriptGameUIDLLPanel->GetVPanel();
}
#endif
default: return NULL;
}
return NULL;
#else
return enginevgui->GetPanel(type);
#endif
}
@ -663,6 +676,11 @@ int CScriptSurface::GetCharacterWidth( int font, int ch )
void CScriptSurface::CreateFont( const char *customName, const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags, int yresMin, int yresMax, bool proportional )
{
// Make sure font invalidation callback is established.
// Not necessary if script fonts are reloaded in engine.
if ( !g_pScriptRootPanel )
g_pScriptRootPanel = new CScriptRootPanel();
if ( flags & ISurface::FONTFLAG_BITMAP )
{
AssertMsg( 0, "Bitmap fonts are not supported!" );
@ -681,16 +699,41 @@ void CScriptSurface::CreateFont( const char *customName, const char *windowsFont
return;
}
#if 0
bool bProportionalFallbackFont = false;
if ( proportional )
{
// Find if this is a resolution filtered font alias
const char *fontAlias = GetFixedFontName( customName, false );
int idx = g_ScriptFonts.Find( fontAlias );
if ( idx != g_ScriptFonts.InvalidIndex() )
{
fontalias_t &alias = g_ScriptFonts[idx];
for ( int i = 0; i < alias.Count(); ++i )
{
FontData_t &data = alias.Element(i);
if ( data.yres_min && data.yres_max )
{
bProportionalFallbackFont = true;
// Save this proportional font in non-proportional alias
proportional = false;
break;
}
}
}
}
#endif
const char *fontAlias = GetFixedFontName( customName, proportional );
int idx = g_ScriptFonts.Find( fontAlias );
if ( idx != g_ScriptFonts.InvalidIndex() )
{
fontalias_t &alias = g_ScriptFonts[idx];
// TODO: One proportional font to fall back to amongst resolution filtered fonts.
#ifdef _DEBUG
if ( !yresMin && !yresMax )
if ( !yresMin && !yresMax /*&& !bProportionalFallbackFont*/ )
{
// There must be only one font registered.
Assert( alias.Count() == 1 );
@ -703,7 +746,7 @@ void CScriptSurface::CreateFont( const char *customName, const char *windowsFont
// Font changes will not be applied.
Assert( oldTall == newTall );
if ( oldName ) // can be null
Assert( !V_stricmp( oldName, windowsFontName ) );
AssertMsg( !V_stricmp( oldName, windowsFontName ), "'%s' != '%s'", oldName, windowsFontName );
}
#endif
@ -716,7 +759,10 @@ void CScriptSurface::CreateFont( const char *customName, const char *windowsFont
if ( yresMin == data.yres_min && yresMax == data.yres_max )
return;
}
#if 0
if ( bProportionalFallbackFont )
proportional = true;
#endif
DebugMsg( "Create font add '%s' [%d %d]\n", fontAlias, yresMin, yresMax );
FontData_t &newFont = alias.Element( alias.AddToTail() );
@ -731,6 +777,22 @@ void CScriptSurface::CreateFont( const char *customName, const char *windowsFont
newFont.yres_max = yresMax;
newFont.proportional = proportional;
#if 0
// Put the proportional font in the very end so that it is loaded only when no resolution is matched
struct L
{
static int __cdecl F( const FontData_t* a, const FontData_t* b )
{
if ( !a->proportional && b->proportional )
return -1;
if ( a->proportional && !b->proportional )
return 1;
return 0;
}
};
alias.Sort( L::F );
#endif
LoadFont( newFont DBG_PARAM(, fontAlias) );
}
else
@ -1067,7 +1129,7 @@ public:\
class CScript_##panelClass : public panelClass\
{\
DECLARE_SCRIPTVGUI_CLASS( panelClass )\
void Shutdown() {}\
void ScriptShutdown() {}\
\
public:\
CScript_##panelClass( Panel *parent, const char *name )\
@ -1085,7 +1147,7 @@ public:\
class CScript_##panelClass : public panelClass\
{\
DECLARE_SCRIPTVGUI_CLASS( panelClass )\
void Shutdown() {}\
void ScriptShutdown() {}\
\
public:\
CScript_##panelClass( Panel *parent, const char *name, const char *text )\
@ -1292,7 +1354,7 @@ public:
if ( GetVPanel() )
{
DebugMsg( " Destroy panel '%s' %s\n", _base->GetName(), GetDebugName() );
_base->Shutdown();
_base->ScriptShutdown();
ResolveChildren_r( _vpanel );
_base->MarkForDeletion();
}
@ -1339,6 +1401,11 @@ public:
}
AssertMsg( 0, "invalid parent" );
g_ScriptPanels.AddToTail( this );
// leave me parentless
return;
}
g_ScriptPanels.AddToTail( this );
@ -1348,27 +1415,37 @@ public:
//
// This parameter is hidden in script, and is defined by the return value of dummy functions.
VPANEL vparent = 0;
switch ( root )
{
#if ALLOW_ROOT_PANEL_PARENT
case 0:
vparent = VGUI_GetScriptRootPanel( PANEL_ROOT );
break;
#if ALLOW_SCRIPT_GAMEUI_ROOT_PANEL
#endif
#if ALLOW_GAMEUI_ROOT_PARENT
case 1:
vparent = VGUI_GetScriptRootPanel( PANEL_GAMEUIDLL );
break;
#endif
#if ALLOW_CLIENTDLL_ROOT_PARENT
case 2:
vparent = VGUI_GetScriptRootPanel( PANEL_CLIENTDLL );
break;
#if ALLOW_SCRIPT_HUD_VIEWPORT_ROOT_PANEL
// Hud viewport
case 10:
#endif
#if ALLOW_HUD_VIEWPORT_ROOT_PARENT
case 10: // Hud viewport
Assert( g_pClientMode && g_pClientMode->GetViewport() );
vparent = g_pClientMode->GetViewport()->GetVPanel();
break;
#endif
default: UNREACHABLE(); // Invalid parent panel
default:
#if SCRIPT_ENGINE_ROOT_PANELS
UNREACHABLE(); // Invalid parent panel
#else
// Allow everything defined in vscript_vgui.nut
vparent = VGUI_GetScriptRootPanel( (VGuiPanel_t)root );
#endif
}
_base->SetParent( vparent );
@ -1400,6 +1477,11 @@ public:
{
ivgui()->AddTickSignal( this->GetVPanel(), i );
}
void RemoveTickSignal()
{
ivgui()->RemoveTickSignal( this->GetVPanel() );
}
#if SCRIPT_VGUI_SIGNAL_INTERFACE
void AddActionSignalTarget( HSCRIPT messageTarget )
{
@ -1434,10 +1516,12 @@ public:
bool bRootParent = false;
#if SCRIPT_ENGINE_ROOT_PANELS
if ( ( parent == g_pScriptRootPanel->GetVPanel() )
#if ALLOW_SCRIPT_GAMEUI_ROOT_PANEL
#if ALLOW_GAMEUI_ROOT_PARENT
|| ( g_pScriptGameUIDLLPanel && parent == g_pScriptGameUIDLLPanel->GetVPanel() )
#endif
#if ALLOW_CLIENTDLL_ROOT_PARENT
|| ( g_pScriptClientDLLPanel && parent == g_pScriptClientDLLPanel->GetVPanel() )
#endif
)
{
bRootParent = true;
@ -1452,7 +1536,7 @@ public:
break;
}
}
#if ALLOW_SCRIPT_HUD_VIEWPORT_ROOT_PANEL
#if ALLOW_HUD_VIEWPORT_ROOT_PARENT
if ( g_pClientMode && g_pClientMode->GetViewport() && ( parent == g_pClientMode->GetViewport()->GetVPanel() ) )
bRootParent = true;
#endif
@ -1788,6 +1872,7 @@ public:
DEFINE_SCRIPTFUNC( MakeReadyForUse, "" )\
DEFINE_SCRIPTFUNC( GetName, "" )\
DEFINE_SCRIPTFUNC( AddTickSignal, "" )\
DEFINE_SCRIPTFUNC( RemoveTickSignal, "" )\
\
DEFINE_SCRIPTFUNC( GetParent, "" )\
DEFINE_SCRIPTFUNC( SetParent, "" )\
@ -1829,10 +1914,12 @@ public:
\
DEFINE_SCRIPTFUNC( SetCursor, "" )\
DEFINE_SCRIPTFUNC( IsCursorOver, "" )\
\
DEFINE_SCRIPTFUNC( HasFocus, "" )\
DEFINE_SCRIPTFUNC( RequestFocus, "" )\
DEFINE_SCRIPTFUNC( MakePopup, "" )\
DEFINE_SCRIPTFUNC( MoveToFront, "" )\
\
DEFINE_SCRIPTFUNC( SetMouseInputEnabled, "" )\
DEFINE_SCRIPTFUNC( SetKeyBoardInputEnabled, "" )\
\
@ -1842,10 +1929,7 @@ public:
//--------------------------------------------------------------
//--------------------------------------------------------------
// These need more testing.
// TODO: IScript_Panel::FindChildByName()
// TODO: DECLARE_BUILD_FACTORY_SCRIPT() to create overridable script panels from controls file
// TODO: CScript_EditablePanel::ApplySchemeSettings() callback
// (IScheme parameter can be passed as null until schemes are also tested)
#if BUILD_GROUPS_ENABLED
CLASS_HELPER_INTERFACE( EditablePanel, Panel )
{
@ -1855,11 +1939,27 @@ public:
{
__base()->LoadControlSettings( resName );
}
HSCRIPT FindChildByName( const char *childName )
{
Panel *pPanel = __base()->FindChildByName( childName, false );
if ( pPanel )
{
int i;
IScriptVGUIObject* obj = FindInScriptPanels( child, i );
if ( obj )
{
return obj->GetScriptInstance();
}
}
return NULL;
}
};
#define DEFINE_VGUI_SCRIPTFUNC_EditablePanel()\
DEFINE_VGUI_SCRIPTFUNC_Panel()\
DEFINE_SCRIPTFUNC( LoadControlSettings, "" )
DEFINE_SCRIPTFUNC( LoadControlSettings, "" )\
DEFINE_SCRIPTFUNC( FindChildByName, "" )
#endif
//--------------------------------------------------------------
//--------------------------------------------------------------
@ -2106,6 +2206,16 @@ public:
{
__base()->SetShouldScaleImage( state );
}
void SetRotation( int rotation )
{
Assert( rotation == ROTATED_UNROTATED ||
rotation == ROTATED_CLOCKWISE_90 ||
rotation == ROTATED_ANTICLOCKWISE_90 ||
rotation == ROTATED_FLIPPED );
__base()->SetRotation( rotation );
}
#if 0
void SetFrame( int nFrame )
{
@ -2120,6 +2230,7 @@ public:
DEFINE_SCRIPTFUNC( SetDrawColor, "" )\
DEFINE_SCRIPTFUNC( SetTileImage, "" )\
DEFINE_SCRIPTFUNC( SetShouldScaleImage, "" )\
DEFINE_SCRIPTFUNC( SetRotation, "" )\
//--------------------------------------------------------------
//--------------------------------------------------------------
@ -2435,10 +2546,10 @@ public:
//--------------------------------------------------------------
//--------------------------------------------------------------
#if VGUI_TGA_IMAGE_PANEL
CLASS_HELPER_INTERFACE( TGAImagePanel, Panel )
CLASS_HELPER_INTERFACE( TGAImage, Panel )
{
public:
void SetTGAImage( const char *p )
void SetImage( const char *p )
{
__base()->SetTGAImage( p );
}
@ -2454,9 +2565,37 @@ public:
}
};
#define DEFINE_VGUI_SCRIPTFUNC_TGAImagePanel()\
#define DEFINE_VGUI_SCRIPTFUNC_TGAImage()\
DEFINE_VGUI_SCRIPTFUNC_Panel()\
DEFINE_SCRIPTFUNC( SetTGAImage, "" )\
DEFINE_SCRIPTFUNC( SetImage, "" )\
DEFINE_SCRIPTFUNC( SetDrawColor, "" )\
DEFINE_SCRIPTFUNC( SetShouldScaleImage, "" )
#endif
//--------------------------------------------------------------
//--------------------------------------------------------------
#if 0
CLASS_HELPER_INTERFACE( PNGImage, Panel )
{
public:
void SetImage( const char *p )
{
__base()->SetPNGImage( p );
}
void SetDrawColor( int r, int g, int b, int a )
{
__base()->SetDrawColor( r, g, b, a );
}
void SetShouldScaleImage( bool i )
{
__base()->SetShouldScaleImage( i );
}
};
#define DEFINE_VGUI_SCRIPTFUNC_PNGImage()\
DEFINE_VGUI_SCRIPTFUNC_Panel()\
DEFINE_SCRIPTFUNC( SetImage, "" )\
DEFINE_SCRIPTFUNC( SetDrawColor, "" )\
DEFINE_SCRIPTFUNC( SetShouldScaleImage, "" )
#endif
@ -2479,7 +2618,7 @@ static inline void SetHScript( HSCRIPT &var, HSCRIPT val )
}
#define CheckCallback(s)\
if ( FStrEq( cb, #s ) )\
if ( !V_strcmp( cb, #s ) )\
{\
SetHScript( m_hfn##s, fn );\
return;\
@ -2515,6 +2654,7 @@ private:
HSCRIPT m_hfnOnKeyCodePressed;
HSCRIPT m_hfnOnKeyCodeReleased;
HSCRIPT m_hfnOnKeyCodeTyped;
#if SCRIPT_VGUI_SIGNAL_INTERFACE
HSCRIPT m_hfnOnCommand;
#endif
@ -2530,7 +2670,9 @@ public:
m_hfnPerformLayout(NULL),
m_hfnOnTick(NULL),
m_hfnOnScreenSizeChanged(NULL),
#if SCRIPT_VGUI_SIGNAL_INTERFACE
m_hfnOnCommand(NULL),
#endif
m_hfnOnCursorEntered(NULL),
m_hfnOnCursorExited(NULL),
m_hfnOnCursorMoved(NULL),
@ -2543,13 +2685,9 @@ public:
m_hfnOnKeyCodePressed(NULL),
m_hfnOnKeyCodeReleased(NULL),
m_hfnOnKeyCodeTyped(NULL)
#if SCRIPT_VGUI_SIGNAL_INTERFACE
,
m_hfnOnCommand(NULL)
#endif
{}
void Shutdown()
void ScriptShutdown()
{
ivgui()->RemoveTickSignal( GetVPanel() );
@ -2573,6 +2711,7 @@ public:
SetHScript( m_hfnOnKeyCodePressed, NULL );
SetHScript( m_hfnOnKeyCodeReleased, NULL );
SetHScript( m_hfnOnKeyCodeTyped, NULL );
#if SCRIPT_VGUI_SIGNAL_INTERFACE
SetHScript( m_hfnOnCommand, NULL );
#endif
@ -2775,6 +2914,7 @@ public:
CheckCallback( OnKeyCodePressed );
CheckCallback( OnKeyCodeReleased );
CheckCallback( OnKeyCodeTyped );
#if SCRIPT_VGUI_SIGNAL_INTERFACE
CheckCallback( OnCommand );
#endif
@ -2810,6 +2950,7 @@ private:
HSCRIPT m_hfnOnKeyCodePressed;
HSCRIPT m_hfnOnKeyCodeReleased;
HSCRIPT m_hfnOnKeyCodeTyped;
#if SCRIPT_VGUI_SIGNAL_INTERFACE
HSCRIPT m_hfnOnCommand;
#endif
@ -2826,6 +2967,9 @@ public:
m_hfnPerformLayout(NULL),
m_hfnOnTick(NULL),
m_hfnOnScreenSizeChanged(NULL),
#if SCRIPT_VGUI_SIGNAL_INTERFACE
m_hfnOnCommand(NULL),
#endif
m_hfnOnCursorEntered(NULL),
m_hfnOnCursorExited(NULL),
@ -2839,15 +2983,11 @@ public:
m_hfnOnKeyCodePressed(NULL),
m_hfnOnKeyCodeReleased(NULL),
m_hfnOnKeyCodeTyped(NULL)
#if SCRIPT_VGUI_SIGNAL_INTERFACE
,
m_hfnOnCommand(NULL)
#endif
{
SetFadeEffectDisableOverride( true );
}
void Shutdown()
void ScriptShutdown()
{
ivgui()->RemoveTickSignal( GetVPanel() );
@ -2866,6 +3006,7 @@ public:
SetHScript( m_hfnOnKeyCodePressed, NULL );
SetHScript( m_hfnOnKeyCodeReleased, NULL );
SetHScript( m_hfnOnKeyCodeTyped, NULL );
#if SCRIPT_VGUI_SIGNAL_INTERFACE
SetHScript( m_hfnOnCommand, NULL );
#endif
@ -2898,7 +3039,18 @@ public:
g_pScriptVM->ExecuteFunction( m_hfnPerformLayout, NULL, 0, NULL, NULL, true );
}
}
#if 0
void ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
if ( m_hfnApplySchemeSettings )
{
ScriptVariant_t arg;
g_pScriptVM->ExecuteFunction( m_hfnApplySchemeSettings, &arg, 1, NULL, NULL, true );
}
}
#endif
void OnTick()
{
g_pScriptVM->ExecuteFunction( m_hfnOnTick, NULL, 0, NULL, NULL, true );
@ -3072,6 +3224,7 @@ public:
CheckCallback( OnKeyCodePressed );
CheckCallback( OnKeyCodeReleased );
CheckCallback( OnKeyCodeTyped );
#if SCRIPT_VGUI_SIGNAL_INTERFACE
CheckCallback( OnCommand );
#endif
@ -3102,7 +3255,7 @@ public:
m_hfnDoClick(NULL)
{}
void Shutdown()
void ScriptShutdown()
{
SetHScript( m_hfnPaint, NULL );
SetHScript( m_hfnPaintBackground, NULL );
@ -3173,7 +3326,7 @@ public:
m_hfnTextChanged(NULL)
{}
void Shutdown()
void ScriptShutdown()
{
SetHScript( m_hfnTextChanged, NULL );
}
@ -3216,9 +3369,12 @@ public:
SetShouldDrawFriendIcon( false );
}
DEBUG_DESTRUCTOR( ~CScript_AvatarImage, CAvatarImagePanel )
~CScript_AvatarImage()
{
DebugDestructor( CAvatarImagePanel );
}
void Shutdown() {}
void ScriptShutdown() {}
};
#endif
//--------------------------------------------------------------
@ -3229,7 +3385,7 @@ class CTGAImagePanel : public Panel
DECLARE_SCRIPTVGUI_CLASS_EX( CTGAImagePanel, Panel );
private:
int m_iTextureID;
int m_iTexture;
int m_nWidth;
int m_nHeight;
Color m_ImageColor;
@ -3238,7 +3394,7 @@ private:
public:
CTGAImagePanel( Panel *parent, const char *name ) :
BaseClass( parent, name ),
m_iTextureID(-1),
m_iTexture(-1),
m_bScaleImage(0),
m_ImageColor( 255, 255, 255, 255 )
{
@ -3249,21 +3405,21 @@ public:
{
DebugDestructor( CTGAImagePanel );
if ( m_iTextureID != -1 )
if ( m_iTexture != -1 )
{
surface()->DestroyTextureID( m_iTextureID );
surface()->DestroyTextureID( m_iTexture );
}
}
void Shutdown() {}
void ScriptShutdown() {}
public:
void Paint()
{
if ( m_iTextureID != -1 )
if ( m_iTexture != -1 )
{
surface()->DrawSetColor( m_ImageColor );
surface()->DrawSetTexture( m_iTextureID );
surface()->DrawSetTexture( m_iTexture );
if ( m_bScaleImage )
{
@ -3288,19 +3444,21 @@ public:
public:
void SetTGAImage( const char *fileName )
{
if ( V_stricmp( V_GetFileExtension( fileName ), "tga" ) != 0 )
const char *ext = V_GetFileExtension( fileName );
if ( ext && V_stricmp( ext, "tga" ) != 0 )
return;
CUtlMemory< unsigned char > tga;
if ( TGALoader::LoadRGBA8888( fileName, tga, m_nWidth, m_nHeight ) )
{
if ( m_iTextureID == -1 )
if ( m_iTexture == -1 )
{
m_iTextureID = surface()->CreateNewTextureID( true );
m_iTexture = surface()->CreateNewTextureID( true );
}
surface()->DrawSetTextureRGBA( m_iTextureID, tga.Base(), m_nWidth, m_nHeight, false, false );
surface()->DrawSetTextureRGBA( m_iTexture, tga.Base(), m_nWidth, m_nHeight, false, false );
}
else
{
@ -3393,10 +3551,19 @@ END_SCRIPTDESC()
//--------------------------------------------------------------
//--------------------------------------------------------------
#if VGUI_TGA_IMAGE_PANEL
BEGIN_VGUI_HELPER_EX( TGAImagePanel, CTGAImagePanel )
BEGIN_VGUI_HELPER_EX( TGAImage, CTGAImagePanel )
END_VGUI_HELPER()
BEGIN_SCRIPTDESC_VGUI( TGAImagePanel )
BEGIN_SCRIPTDESC_VGUI( TGAImage )
END_SCRIPTDESC()
#endif
//--------------------------------------------------------------
//--------------------------------------------------------------
#if 0
BEGIN_VGUI_HELPER_EX( PNGImage, CPNGImagePanel )
END_VGUI_HELPER()
BEGIN_SCRIPTDESC_VGUI( PNGImage )
END_SCRIPTDESC()
#endif
//--------------------------------------------------------------
@ -3448,7 +3615,7 @@ HSCRIPT CScriptVGUI::CreatePanel( const char* panelClass, HSCRIPT parent, const
}
#define Check( _name )\
if ( FStrEq( panelClass, #_name ) )\
if ( !V_strcmp( panelClass, #_name ) )\
{\
CScript_##_name##_Helper *helper = AllocScriptPanel< CScript_##_name##_Helper >();\
helper->CreateFromScript< CScript_##_name##_Helper >( (HSCRIPT)parent, panelName, root );\
@ -3467,7 +3634,7 @@ HSCRIPT CScriptVGUI::CreatePanel( const char* panelClass, HSCRIPT parent, const
Check( AvatarImage );
#endif
#if VGUI_TGA_IMAGE_PANEL
Check( TGAImagePanel );
Check( TGAImage );
#endif
g_pScriptVM->RaiseException("invalid vgui class");
@ -3480,6 +3647,8 @@ void CScriptVGUI::LevelShutdownPostEntity()
{
DebugMsg( "LevelShutdownPostEntity()\n" );
if ( g_ScriptPanels.Count() )
{
while ( g_ScriptPanels.Count() )
{
Assert( g_ScriptPanels.Head() != g_ScriptPanels.InvalidIndex() );
@ -3487,9 +3656,13 @@ void CScriptVGUI::LevelShutdownPostEntity()
int head = g_ScriptPanels.Head();
g_ScriptPanels[ head ]->Destroy( head );
}
g_ScriptPanels.Purge();
FOR_EACH_VEC( g_ScriptTextureIDs, i )
g_ScriptPanels.Purge();
}
if ( int i = g_ScriptTextureIDs.Count() )
{
while ( i-- )
{
#ifdef _DEBUG
char tex[MAX_PATH];
@ -3498,7 +3671,9 @@ void CScriptVGUI::LevelShutdownPostEntity()
#endif
surface()->DestroyTextureID( g_ScriptTextureIDs[i] );
}
g_ScriptTextureIDs.Purge();
}
//
// Reset hud element visibility

View File

@ -1,6 +1,6 @@
static const char* g_Script_vgui_init = R"script(
local DoCreateFont = ISurface.CreateFont;
ISurface.CreateFont <- function( name, props )
function ISurface::CreateFont( name, props )
{
if ( !("name" in props) || typeof props.name != "string" )
throw "invalid parameter 'name'";
@ -86,12 +86,12 @@ ISurface.CreateFont <- function( name, props )
return DoCreateFont( name, props.name, props.tall, props.weight, blur, scanlines, flags, yres_min, yres_max, proportional );
}
local _Schemes = {}
local _FontTall = {}
local _Schemes = {}
local DoGetFont = ISurface.DoGetFont <- ISurface.GetFont;
local DoGetFontTall = ISurface.GetFontTall;
ISurface.GetFont <- function( name, proportional, sch = "" )
function ISurface::GetFont( name, proportional, sch = "" )
{
if ( sch in _Schemes )
{
@ -151,33 +151,28 @@ ISurface.GetTextureID <- function( name )
}
// Forward compatibility
IVGui.GetRootPanel <- function() { return 1000 }
//IVGui.GetGameUIRootPanel <- function() { return 1001 }
IVGui.GetClientDLLRootPanel <- function() { return 1002 }
//IVGui.GetHudViewportPanel <- function() { return 1010 }
IVGui.GetRootPanel <- function() { return 0x8888 }
//IVGui.GetGameUIRootPanel <- function() { return 0x8888+1 }
IVGui.GetClientDLLRootPanel <- function() { return 0x8888+2 }
IVGui.GetHudViewport <- function() { return 0x8888+10 }
local CreatePanel = IVGui.CreatePanel;
IVGui.CreatePanel <- function( type, parent, name )
function IVGui::CreatePanel( type, parent, name )
{
if ( !parent )
throw "invalid parent";
local root = 0;
local root = -1;
if ( typeof parent == "integer" )
{
switch ( parent )
root = parent-0x8888;
switch ( root )
{
case 1000:
root = 0;
case 0:
case 2:
case 10:
break;
case 1002:
root = 2;
break;
default:
throw "invalid parent";
default: throw "invalid parent";
}
parent = null;
}
@ -390,5 +385,6 @@ if ( __Documentation.RegisterHelp != dummy )
__Documentation.RegisterHelp( "IVGui::CreatePanel", "handle IVGui::CreatePanel(string, handle, string)", "" );
__Documentation.RegisterHelp( "IVGui::GetRootPanel", "handle IVGui::GetRootPanel()", "" );
__Documentation.RegisterHelp( "IVGui::GetClientDLLRootPanel", "handle IVGui::GetClientDLLRootPanel()", "" );
__Documentation.RegisterHelp( "IVGui::GetHudViewport", "handle IVGui::GetHudViewport()", "" );
}
)script";

View File

@ -447,6 +447,8 @@ void CHudMessage::MessageScanStart( void )
break;
}
// Font was just set in MessageDrawScan()
#ifndef MAPBASE
m_parms.font = g_hFontTrebuchet24;
if ( m_parms.vguiFontName != NULL &&
@ -455,6 +457,7 @@ void CHudMessage::MessageScanStart( void )
SetFont( vgui::scheme()->GetDefaultScheme(), m_parms.vguiFontName );
}
#endif
}
//-----------------------------------------------------------------------------
@ -497,7 +500,30 @@ void CHudMessage::MessageDrawScan( client_textmessage_t *pMessage, float time )
m_parms.totalWidth = 0;
m_parms.vguiFontName = pMessage->pVGuiSchemeFontName;
#ifdef MAPBASE
if ( m_parms.vguiFontName != NULL &&
m_parms.vguiFontName[ 0 ] )
{
SetFont( vgui::scheme()->GetDefaultScheme(), m_parms.vguiFontName );
#ifdef MAPBASE_VSCRIPT
if ( m_parms.font == vgui::INVALID_FONT )
{
extern vgui::HFont GetScriptFont( const char *, bool );
vgui::HFont font = GetScriptFont( m_parms.vguiFontName, IsProportional() );
textmessage->SetFont( font );
m_parms.font = font;
}
#endif
}
else
{
m_parms.font = g_hFontTrebuchet24;
}
#else
m_parms.font = g_hFontTrebuchet24;
#endif
while ( *pText )
{

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->GetName(), ucmd->weaponsubtype );
player->SelectItem( weapon->GetClassname(), ucmd->weaponsubtype );
}
}

View File

@ -431,7 +431,6 @@ const Vector& CScriptMaterialProxy::GetVarVector( int i )
if (m_MaterialVars[i]->GetType() != MATERIAL_VAR_TYPE_VECTOR)
return vec3_origin;
// This is really bad. Too bad!
return *(reinterpret_cast<const Vector*>(m_MaterialVars[i]->GetVecValue()));
}
@ -761,6 +760,8 @@ public:
virtual void LevelShutdownPostEntity( void )
{
#ifdef MAPBASE_VSCRIPT
g_ScriptEntityIterator.DisableEntityListening();
g_ScriptNetMsg->LevelShutdownPreVM();
GetScriptHookManager().OnShutdown();

View File

@ -76,6 +76,23 @@ int CAI_BaseNPC::GetActivityID(const char* actName)
return m_pActivitySR->GetStringID(actName);
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose: Gets an activity ID or registers a new private one if it doesn't exist
//-----------------------------------------------------------------------------
int CAI_BaseNPC::GetOrRegisterActivity( const char *actName )
{
int actID = GetActivityID( actName );
if (actID == ACT_INVALID)
{
actID = ActivityList_RegisterPrivateActivity( actName );
AddActivityToSR( actName, actID );
}
return actID;
}
#endif
#define ADD_ACTIVITY_TO_SR(activityname) AddActivityToSR(#activityname,activityname)
//-----------------------------------------------------------------------------

View File

@ -671,13 +671,27 @@ void CAI_BaseNPC::Ignite( float flFlameLifetime, bool bNPCOnly, float flSize, bo
{
BaseClass::Ignite( flFlameLifetime, bNPCOnly, flSize, bCalledByLevelDesigner );
#ifdef MAPBASE
// Alyx's enemy ignited code from below can now be run on any NPC as long as
// it's our current enemy.
if ( GetEnemy() && GetEnemy()->IsNPC() )
{
GetEnemy()->MyNPCPointer()->EnemyIgnited( this );
}
#endif
#ifdef HL2_EPISODIC
CBasePlayer *pPlayer = AI_GetSinglePlayer();
if ( pPlayer && pPlayer->IRelationType( this ) != D_LI )
{
CNPC_Alyx *alyx = CNPC_Alyx::GetAlyx();
#ifdef MAPBASE
// Alyx's code continues to run if Alyx was not this NPC's enemy.
if ( alyx && alyx != GetEnemy() )
#else
if ( alyx )
#endif
{
alyx->EnemyIgnited( this );
}
@ -3026,6 +3040,10 @@ void CAI_BaseNPC::PopulatePoseParameters( void )
m_poseAim_Yaw = LookupPoseParameter( "aim_yaw" );
m_poseMove_Yaw = LookupPoseParameter( "move_yaw" );
#ifdef MAPBASE
m_poseInteractionRelativeYaw = LookupPoseParameter( "interaction_relative_yaw" );
#endif
BaseClass::PopulatePoseParameters();
}
@ -9526,6 +9544,12 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent )
{
m_hCine->FireScriptEvent( atoi( pEvent->options ) );
}
#ifdef MAPBASE
else if ( GetHintNode() )
{
GetHintNode()->FireScriptEvent( atoi( pEvent->options ) );
}
#endif
else
{
// FIXME: look so see if it's playing a vcd and fire those instead
@ -12333,6 +12357,7 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC
DEFINE_SCRIPTFUNC( IsCommandable, "Check if the NPC is commandable." )
DEFINE_SCRIPTFUNC( IsInPlayerSquad, "Check if the NPC is in the player's squad." )
DEFINE_SCRIPTFUNC( IsMedic, "Returns true if this NPC is a medic." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetCine, "GetCine", "Get the NPC's currently running scripted sequence if it has one." )
DEFINE_SCRIPTFUNC( GetScriptState, "Get the NPC's current scripted sequence state." )
@ -12443,7 +12468,11 @@ BEGIN_SIMPLE_DATADESC( ScriptedNPCInteraction_t )
DEFINE_FIELD( bValidOnCurrentEnemy, FIELD_BOOLEAN ),
DEFINE_FIELD( flNextAttemptTime, FIELD_TIME ),
#ifdef MAPBASE
DEFINE_FIELD( MiscCriteria, FIELD_STRING ),//DEFINE_UTLVECTOR( MiscCriteria, FIELD_EMBEDDED ),
DEFINE_EMBEDDED_ARRAY( sTheirPhases, SNPCINT_NUM_PHASES ),
DEFINE_FIELD( bHasSeparateSequenceNames, FIELD_BOOLEAN ),
DEFINE_FIELD( flMaxAngleDiff, FIELD_FLOAT ),
DEFINE_FIELD( iszRelatedInteractions, FIELD_STRING ),
DEFINE_FIELD( MiscCriteria, FIELD_STRING ),
#endif
END_DATADESC()
@ -13200,17 +13229,14 @@ void CAI_BaseNPC::InputSetEnemyFilter( inputdata_t &inputdata )
//-----------------------------------------------------------------------------
void CAI_BaseNPC::InputSetHealthFraction( inputdata_t &inputdata )
{
// npc_helicopter uses SetHealth() instead of the regular NPC practice of TakeHealth() and TakeDamage().
// It also also uses 50, 75, etc. and scales it by 0.01 for some reason.
// We're using the same model as InputSetHealth() and just letting npc_helicopter override it. No big deal.
// We're also adding support for its "whole number * 0.01" thing too.
// npc_helicopter uses an identically named input and scales down whole numbers instead of using fractions directly.
// This function is overridden by npc_helicopter for other reasons, but support for its differing behavior is also available through this input.
float flFactor = inputdata.value.Float();
if ( flFactor > 1.0f )
{
flFactor *= 0.01f;
}
// Excuse the complication...
float flNewHealth = (GetMaxHealth() * flFactor);
int iNewHealth = (int)flNewHealth;
if (flNewHealth < (GetMaxHealth() / 2))
@ -13510,6 +13536,10 @@ bool CAI_BaseNPC::CineCleanup()
}
// Clear interaction partner, because we're not running a scripted sequence anymore
#ifdef MAPBASE
// We need the interaction partner for server ragdoll death cleanup, so don't clear if we're not alive
if (IsAlive())
#endif
m_hInteractionPartner = NULL;
CleanupForcedInteraction();
}
@ -14850,32 +14880,66 @@ void CAI_BaseNPC::ParseScriptedNPCInteractions(void)
else if (!Q_strncmp(szName, "entry_sequence", 14))
sInteraction.sPhases[SNPCINT_ENTRY].iszSequence = AllocPooledString(szValue);
else if (!Q_strncmp(szName, "entry_activity", 14))
sInteraction.sPhases[SNPCINT_ENTRY].iActivity = GetActivityID(szValue);
sInteraction.sPhases[SNPCINT_ENTRY].iActivity = GetOrRegisterActivity(szValue);
else if (!Q_strncmp(szName, "sequence", 8))
sInteraction.sPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString(szValue);
else if (!Q_strncmp(szName, "activity", 8))
sInteraction.sPhases[SNPCINT_SEQUENCE].iActivity = GetActivityID(szValue);
sInteraction.sPhases[SNPCINT_SEQUENCE].iActivity = GetOrRegisterActivity(szValue);
else if (!Q_strncmp(szName, "exit_sequence", 13))
sInteraction.sPhases[SNPCINT_EXIT].iszSequence = AllocPooledString(szValue);
else if (!Q_strncmp(szName, "exit_activity", 13))
sInteraction.sPhases[SNPCINT_EXIT].iActivity = GetActivityID(szValue);
sInteraction.sPhases[SNPCINT_EXIT].iActivity = GetOrRegisterActivity(szValue);
else if (!Q_strncmp(szName, "their_", 6))
{
const char *szTheirName = szName + 6;
sInteraction.bHasSeparateSequenceNames = true;
if (!Q_strncmp(szTheirName, "entry_sequence", 14))
sInteraction.sTheirPhases[SNPCINT_ENTRY].iszSequence = AllocPooledString(szValue);
else if (!Q_strncmp(szTheirName, "entry_activity", 14))
sInteraction.sTheirPhases[SNPCINT_ENTRY].iActivity = GetOrRegisterActivity(szValue);
else if (!Q_strncmp(szTheirName, "sequence", 8))
sInteraction.sTheirPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString(szValue);
else if (!Q_strncmp(szTheirName, "activity", 8))
sInteraction.sTheirPhases[SNPCINT_SEQUENCE].iActivity = GetOrRegisterActivity(szValue);
else if (!Q_strncmp(szTheirName, "exit_sequence", 13))
sInteraction.sTheirPhases[SNPCINT_EXIT].iszSequence = AllocPooledString(szValue);
else if (!Q_strncmp(szTheirName, "exit_activity", 13))
sInteraction.sTheirPhases[SNPCINT_EXIT].iActivity = GetOrRegisterActivity(szValue);
// Add anything else to our miscellaneous criteria
else
{
szCriteria = UTIL_VarArgs("%s,%s:%s", szCriteria, szName, szValue);
}
}
else if (!Q_strncmp(szName, "delay", 5))
sInteraction.flDelay = atof(szValue);
else if (!Q_strncmp(szName, "origin_max_delta", 16))
sInteraction.flDistSqr = atof(szValue);
else if (!Q_strncmp(szName, "angles_max_diff", 15))
sInteraction.flMaxAngleDiff = atof(szValue);
else if (!Q_strncmp(szName, "loop_in_action", 14) && !FStrEq(szValue, "0"))
sInteraction.iFlags |= SCNPC_FLAG_LOOP_IN_ACTION;
else if (!Q_strncmp(szName, "dont_teleport_at_end", 20))
{
if (!Q_stricmp(szValue, "me") || !Q_stricmp(szValue, "both"))
if (!Q_stricmp(szValue, "me"))
sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_ME;
else if (!Q_stricmp(szValue, "them") || !Q_stricmp(szValue, "both"))
else if (!Q_stricmp(szValue, "them"))
sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_THEM;
else if (!Q_stricmp( szValue, "both" ))
{
sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_ME;
sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_THEM;
}
}
else if (!Q_strncmp(szName, "needs_weapon", 12))
@ -14902,6 +14966,11 @@ void CAI_BaseNPC::ParseScriptedNPCInteractions(void)
sInteraction.iszTheirWeapon = AllocPooledString(szValue);
}
else if (!Q_strncmp(szName, "related_interactions", 20))
{
sInteraction.iszRelatedInteractions = AllocPooledString(szValue);
}
// Add anything else to our miscellaneous criteria
else
{
@ -15133,8 +15202,23 @@ void CAI_BaseNPC::AddScriptedNPCInteraction( ScriptedNPCInteraction_t *pInteract
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CAI_BaseNPC::GetScriptedNPCInteractionSequence( ScriptedNPCInteraction_t *pInteraction, int iPhase )
const char *CAI_BaseNPC::GetScriptedNPCInteractionSequence( ScriptedNPCInteraction_t *pInteraction, int iPhase, bool bOtherNPC )
{
#ifdef MAPBASE
if (bOtherNPC && pInteraction->bHasSeparateSequenceNames)
{
// Check unique phases
if ( pInteraction->sTheirPhases[iPhase].iActivity != ACT_INVALID )
{
int iSequence = SelectWeightedSequence( (Activity)pInteraction->sTheirPhases[iPhase].iActivity );
return GetSequenceName( iSequence );
}
if ( pInteraction->sTheirPhases[iPhase].iszSequence != NULL_STRING )
return STRING(pInteraction->sTheirPhases[iPhase].iszSequence);
}
#endif
if ( pInteraction->sPhases[iPhase].iActivity != ACT_INVALID )
{
int iSequence = SelectWeightedSequence( (Activity)pInteraction->sPhases[iPhase].iActivity );
@ -15228,6 +15312,37 @@ void CAI_BaseNPC::StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedN
// Setup next attempt
pInteraction->flNextAttemptTime = gpGlobals->curtime + pInteraction->flDelay + RandomFloat(-2,2);
#ifdef MAPBASE
if (pInteraction->iszRelatedInteractions != NULL_STRING)
{
// Delay related interactions as well
char szRelatedInteractions[256];
Q_strncpy( szRelatedInteractions, STRING( pInteraction->iszRelatedInteractions ), sizeof( szRelatedInteractions ) );
char *pszInteraction = strtok( szRelatedInteractions, "," );
while (pszInteraction)
{
bool bWildCard = Matcher_ContainsWildcard( pszInteraction );
for ( int i = 0; i < m_ScriptedInteractions.Count(); i++ )
{
ScriptedNPCInteraction_t *pOtherInteraction = &m_ScriptedInteractions[i];
if ( Matcher_NamesMatch( pszInteraction, STRING( pOtherInteraction->iszInteractionName ) ) && pOtherInteraction != pInteraction )
{
if (pOtherInteraction->flNextAttemptTime < pInteraction->flNextAttemptTime)
pOtherInteraction->flNextAttemptTime = pInteraction->flNextAttemptTime;
// Not looking for multiple
if (!bWildCard)
break;
}
}
pszInteraction = strtok( NULL, "," );
}
}
#endif
// Spawn a scripted sequence for this NPC to play the interaction anim
CAI_ScriptedSequence *pMySequence = (CAI_ScriptedSequence*)CreateEntityByName( "scripted_sequence" );
@ -15259,6 +15374,15 @@ void CAI_BaseNPC::StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedN
CAI_ScriptedSequence *pTheirSequence = NULL;
if ( pOtherNPC )
{
#ifdef MAPBASE
if (pInteraction->bHasSeparateSequenceNames)
{
pszEntrySequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_ENTRY, true );
pszSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_SEQUENCE, true );
pszExitSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_EXIT, true );
}
#endif
pTheirSequence = (CAI_ScriptedSequence*)CreateEntityByName( "scripted_sequence" );
pTheirSequence->KeyValue( "m_iszEntry", pszEntrySequence );
pTheirSequence->KeyValue( "m_iszPlay", pszSequence );
@ -15282,6 +15406,26 @@ void CAI_BaseNPC::StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedN
// Tell their sequence to keep their position relative to me
pTheirSequence->SetupInteractionPosition( this, pInteraction->matDesiredLocalToWorld );
#ifdef MAPBASE
if ( !(pInteraction->iFlags & SCNPC_FLAG_TEST_OTHER_ANGLES) )
{
// Set up interaction yaw pose if it exists
float flYaw = AngleDistance( angDesired.y, angOtherAngles.y );
int nInteractionPose = LookupPoseInteractionRelativeYaw();
if (nInteractionPose > -1)
{
SetPoseParameter( nInteractionPose, flYaw );
}
nInteractionPose = pOtherNPC->LookupPoseInteractionRelativeYaw();
if (nInteractionPose > -1)
{
pOtherNPC->SetPoseParameter( nInteractionPose, flYaw );
}
}
#endif
}
// Spawn both sequences at once
@ -15370,7 +15514,7 @@ bool CAI_BaseNPC::CanRunAScriptedNPCInteraction( bool bForced )
return false;
// Default AI prevents interactions while melee attacking, but not ranged attacking
if ( IsCurSchedule( SCHED_MELEE_ATTACK1 ) || IsCurSchedule( SCHED_MELEE_ATTACK2 ) )
if ( ( IsCurSchedule( SCHED_MELEE_ATTACK1 ) || IsCurSchedule( SCHED_MELEE_ATTACK2 ) ) && !CanStartDynamicInteractionDuringMelee() )
return false;
}
@ -15559,8 +15703,14 @@ void CAI_BaseNPC::CalculateValidEnemyInteractions( void )
if (bSame)
continue;
#endif
// Resolve the activity or sequence, and make sure our enemy has it
const char *pszSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_SEQUENCE, true );
if ( !pszSequence )
continue;
if ( pNPC->LookupSequence( pszSequence ) == -1 )
continue;
#else
// Use sequence? or activity?
if ( pInteraction->sPhases[SNPCINT_SEQUENCE].iActivity != ACT_INVALID )
{
@ -15576,53 +15726,6 @@ void CAI_BaseNPC::CalculateValidEnemyInteractions( void )
if ( pNPC->LookupSequence( STRING(pInteraction->sPhases[SNPCINT_SEQUENCE].iszSequence) ) == -1 )
continue;
}
#ifdef MAPBASE
if (pInteraction->MiscCriteria != NULL_STRING)
{
// Test against response system criteria
AI_CriteriaSet set;
ModifyOrAppendCriteria( set );
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
if( pPlayer )
pPlayer->ModifyOrAppendPlayerCriteria( set );
ReAppendContextCriteria( set );
DevMsg("Testing %s misc criteria\n", STRING(pInteraction->MiscCriteria));
int index;
const char *criteriavalue;
char key[128];
char value[128];
const char *p = STRING(pInteraction->MiscCriteria);
while ( p )
{
#ifdef NEW_RESPONSE_SYSTEM
p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL, STRING(pInteraction->MiscCriteria) );
#else
p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL );
#endif
index = set.FindCriterionIndex(key);
if (index != -1)
{
criteriavalue = set.GetValue(index);
if (!Matcher_Match(value, criteriavalue))
{
continue;
}
}
else
{
// Test with empty string in case our criteria is != or something
criteriavalue = "";
if (!Matcher_Match(value, criteriavalue))
{
continue;
}
}
}
}
#endif
pInteraction->bValidOnCurrentEnemy = true;
@ -15792,7 +15895,95 @@ bool CAI_BaseNPC::InteractionIsAllowed( CAI_BaseNPC *pOtherNPC, ScriptedNPCInter
return true;
// m_iDynamicInteractionsAllowed == TRS_FALSE case is already handled in CanRunAScriptedNPCInteraction().
return !(pInteraction->iFlags & SCNPC_FLAG_MAPBASE_ADDITION && m_iDynamicInteractionsAllowed == TRS_NONE);
if (pInteraction->iFlags & SCNPC_FLAG_MAPBASE_ADDITION && m_iDynamicInteractionsAllowed == TRS_NONE)
return false;
// Test misc. criteria here since some of it may not have been valid on initial calculation, but could be now
if (pInteraction->MiscCriteria != NULL_STRING)
{
// Test against response system criteria
AI_CriteriaSet set;
ModifyOrAppendCriteria( set );
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
if (pPlayer)
pPlayer->ModifyOrAppendPlayerCriteria( set );
// Get criteria from target if we want it
if ( V_strstr( STRING( pInteraction->MiscCriteria ), "their_" ) )
{
// Currently, in order to get everything which might be desired, we call the other NPC's ModifyOrAppendCriteria.
// We put it in a separate criteria set, then assign a prefix and append it to the main set, similar to how contexts are appended.
// This includes a few global criterions which we might not need, so we throw them out before they're merged.
// This isn't a very efficient solution, but there are no better options available without rewriting parts of the response criteria system.
AI_CriteriaSet theirSet;
pOtherNPC->ModifyOrAppendCriteria( theirSet );
set.EnsureCapacity( (theirSet.GetCount()-2) + set.GetCount() ); // We know we'll be throwing out 2 global criterions
char sz[ 128 ];
for ( int i = 0; i < theirSet.GetCount(); i++ )
{
const char *name = theirSet.GetName( i );
const char *value = theirSet.GetValue( i );
if (FStrEq( name, "map" ) || FStrEq( name, "episodic" ) || FStrEq( name, "is_console" )
|| FStrEq( name, "month" ) || FStrEq( name, "day" )
|| FStrEq( name, "is_console" ) || FStrEq( name, "is_pc" )
|| V_strnicmp( name, "world", 5 ) == 0)
{
// Global criterion, ignore
continue;
}
Q_snprintf( sz, sizeof( sz ), "their_%s", name );
if (ai_debug_dyninteractions.GetInt() == 3)
Msg( "%i: %s -> %s:%s\n", i, name, sz, value );
set.AppendCriteria( sz, value );
}
// Append this afterwards because it has its own prefix system
pOtherNPC->AppendContextToCriteria( set, "their_" );
}
ReAppendContextCriteria( set );
int index;
const char *criteriavalue;
char key[128];
char value[128];
const char *p = STRING( pInteraction->MiscCriteria );
while ( p )
{
#ifdef NEW_RESPONSE_SYSTEM
p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL, STRING( pInteraction->MiscCriteria ) );
#else
p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL );
#endif
index = set.FindCriterionIndex( key );
if (index != -1)
{
criteriavalue = set.GetValue( index );
if (!Matcher_Match( value, criteriavalue ))
{
return false;
}
}
else
{
// Test with empty string in case our criteria is != or something
criteriavalue = "";
if (!Matcher_Match( value, criteriavalue ))
{
return false;
}
}
}
}
return true;
}
#endif
@ -15830,6 +16021,11 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte
{
Msg(" %s distsqr: %0.2f (%0.2f %0.2f %0.2f), desired: <%0.2f (%0.2f %0.2f %0.2f)\n", GetDebugName(), flDistSqr,
pOtherNPC->GetAbsOrigin().x, pOtherNPC->GetAbsOrigin().y, pOtherNPC->GetAbsOrigin().z, pInteraction->flDistSqr, vecOrigin.x, vecOrigin.y, vecOrigin.z );
#ifdef MAPBASE
Vector vecForward, vecRight;
GetVectors( &vecForward, &vecRight, NULL );
NDebugOverlay::Circle( vecOrigin + Vector(0,0,2), vecForward, vecRight, FastSqrt(pInteraction->flDistSqr), 255, 0, 0, 255, true, 0.1f );
#endif
}
}
}
@ -15842,6 +16038,11 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte
Msg(" %s is at: %0.2f %0.2f %0.2f\n", GetDebugName(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z );
Msg(" %s distsqr: %0.2f (%0.2f %0.2f %0.2f), desired: (%0.2f %0.2f %0.2f)\n", GetDebugName(), flDistSqr,
pOtherNPC->GetAbsOrigin().x, pOtherNPC->GetAbsOrigin().y, pOtherNPC->GetAbsOrigin().z, vecOrigin.x, vecOrigin.y, vecOrigin.z );
#ifdef MAPBASE
Vector vecForward, vecRight;
GetVectors( &vecForward, &vecRight, NULL );
NDebugOverlay::Circle( vecOrigin + Vector( 0, 0, 2 ), vecForward, vecRight, FastSqrt( pInteraction->flDistSqr ), 255, 0, 0, 255, true, 0.1f );
#endif
if ( pOtherNPC )
{
@ -15858,14 +16059,28 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte
for ( int ang = 0; ang < 3; ang++ )
{
float flAngDiff = AngleDiff( angEnemyAngles[ang], angAngles[ang] );
#ifdef MAPBASE
if ( fabs(flAngDiff) > pInteraction->flMaxAngleDiff )
#else
if ( fabs(flAngDiff) > DSS_MAX_ANGLE_DIFF )
#endif
{
bMatches = false;
break;
}
}
if ( !bMatches )
{
#ifdef MAPBASE
if ( bDebug )
{
Msg(" %s angle not matched: (%0.2f %0.2f %0.2f), desired (%0.2f, %0.2f, %0.2f)\n", GetDebugName(),
anglemod(angEnemyAngles.x), anglemod(angEnemyAngles.y), anglemod(angEnemyAngles.z), anglemod(angAngles.x), anglemod(angAngles.y), anglemod(angAngles.z) );
Msg(" diff: (%0.2f, %0.2f, %0.2f)\n", AngleDiff( angEnemyAngles.x, angAngles.x ), AngleDiff( angEnemyAngles.y, angAngles.y ), AngleDiff( angEnemyAngles.z, angAngles.z ) );
}
#endif
return false;
}
if ( bDebug )
{
@ -15873,6 +16088,13 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte
anglemod(angEnemyAngles.x), anglemod(angEnemyAngles.y), anglemod(angEnemyAngles.z), anglemod(angAngles.x), anglemod(angAngles.y), anglemod(angAngles.z) );
}
}
#ifdef MAPBASE
else
{
// If we're not using angles, then use the NPC's current angles
angAngles = pOtherNPC->GetAbsAngles();
}
#endif
// TODO: Velocity check, if we're supposed to
if ( pInteraction->iFlags & SCNPC_FLAG_TEST_OTHER_VELOCITY )
@ -15952,6 +16174,7 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte
if ( bDebug )
{
NDebugOverlay::Box( vecPos, GetHullMins(), GetHullMaxs(), 255,0,0, 100, 1.0 );
NDebugOverlay::HorzArrow( GetAbsOrigin(), vecPos, 16.0f, 255, 0, 0, 255, true, 1.0f );
}
return false;
}
@ -15959,7 +16182,39 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte
{
//NDebugOverlay::Box( vecPos, GetHullMins(), GetHullMaxs(), 0,255,0, 100, 1.0 );
NDebugOverlay::Axis( vecPos, angAngles, 20, true, 10.0 );
NDebugOverlay::Axis( vecPos, angAngles, 20, true, 1.0 );
}
}
else
{
// Instead, make sure we fit into where the sequence movement ends at
const char *pszSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_SEQUENCE );
int nSeq = LookupSequence( pszSequence );
if ( pszSequence && nSeq != -1 )
{
Vector vecDeltaPos;
QAngle angDeltaAngles;
GetSequenceMovement( nSeq, 0.0f, 1.0f, vecDeltaPos, angDeltaAngles );
if (!vecDeltaPos.IsZero())
{
QAngle angInteraction = GetAbsAngles();
angInteraction[YAW] = m_flInteractionYaw;
Vector vecPos;
VectorRotate( vecDeltaPos, angInteraction, vecPos );
vecPos += GetAbsOrigin();
AI_TraceHull( vecPos, vecPos, GetHullMins(), GetHullMaxs(), MASK_SOLID, &traceFilter, &tr);
if ( tr.fraction != 1.0 )
{
if ( bDebug )
{
NDebugOverlay::Box( vecPos, GetHullMins(), GetHullMaxs(), 255,0,0, 100, 1.0 );
NDebugOverlay::HorzArrow( GetAbsOrigin(), vecPos, 16.0f, 255, 0, 0, 255, true, 1.0f );
}
return false;
}
}
}
}
#endif
@ -15983,6 +16238,25 @@ bool CAI_BaseNPC::HasInteractionCantDie( void )
return ( m_bCannotDieDuringInteraction && IsRunningDynamicInteraction() );
}
//-----------------------------------------------------------------------------
// Purpose: Return true if this NPC has valid interactions on the current enemy.
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::HasValidInteractionsOnCurrentEnemy( void )
{
if ( !GetEnemy() || !GetEnemy()->IsNPC() )
return false;
for ( int i = 0; i < m_ScriptedInteractions.Count(); i++ )
{
ScriptedNPCInteraction_t *pInteraction = &m_ScriptedInteractions[i];
if ( pInteraction->bValidOnCurrentEnemy )
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &inputdata -
@ -16216,6 +16490,21 @@ void CAI_BaseNPC::ModifyOrAppendEnemyCriteria( AI_CriteriaSet& set, CBaseEntity
set.AppendCriteria( "enemyclass", g_pGameRules->AIClassText( pEnemy->Classify() ) ); // UTIL_VarArgs("%i", pEnemy->Classify())
set.AppendCriteria( "distancetoenemy", UTIL_VarArgs( "%f", EnemyDistance(pEnemy) ) );
set.AppendCriteria( "timesincecombat", "-1" );
CAI_BaseNPC *pNPC = pEnemy->MyNPCPointer();
if (pNPC)
{
set.AppendCriteria("enemy_is_npc", "1");
set.AppendCriteria( "enemy_activity", CAI_BaseNPC::GetActivityName( pNPC->GetActivity() ) );
set.AppendCriteria( "enemy_weapon", pNPC->GetActiveWeapon() ? pNPC->GetActiveWeapon()->GetClassname() : "0" );
}
else
{
set.AppendCriteria("enemy_is_npc", "0");
}
pEnemy->AppendContextToCriteria( set, "enemy_" );
}
else
{

View File

@ -425,6 +425,9 @@ struct ScriptedNPCInteraction_t
iszTheirWeapon = NULL_STRING;
#ifdef MAPBASE
vecRelativeEndPos = vec3_origin;
bHasSeparateSequenceNames = false;
flMaxAngleDiff = DSS_MAX_ANGLE_DIFF;
iszRelatedInteractions = NULL_STRING;
MiscCriteria = NULL_STRING;
#endif
@ -432,6 +435,10 @@ struct ScriptedNPCInteraction_t
{
sPhases[i].iszSequence = NULL_STRING;
sPhases[i].iActivity = ACT_INVALID;
#ifdef MAPBASE
sTheirPhases[i].iszSequence = NULL_STRING;
sTheirPhases[i].iActivity = ACT_INVALID;
#endif
}
}
@ -459,10 +466,14 @@ struct ScriptedNPCInteraction_t
float flNextAttemptTime;
#ifdef MAPBASE
// Unrecognized keyvalues are tested against response criteria later.
// This was originally a CUtlVector that carries response contexts, but I couldn't get it working due to some CUtlVector-struct shenanigans.
// It works when we use a single string_t that's split and read each time the code runs, but feel free to improve on this.
string_t MiscCriteria; // CUtlVector<ResponseContext_t>
ScriptedNPCInteraction_Phases_t sTheirPhases[SNPCINT_NUM_PHASES]; // The animations played by the target NPC, if they are different
bool bHasSeparateSequenceNames;
float flMaxAngleDiff;
string_t iszRelatedInteractions; // These interactions will be delayed as well when this interaction is used.
// Unrecognized keyvalues which are tested against response criteria later.
string_t MiscCriteria;
#endif
DECLARE_SIMPLE_DATADESC();
@ -838,6 +849,9 @@ protected: // pose parameters
int m_poseAim_Pitch;
int m_poseAim_Yaw;
int m_poseMove_Yaw;
#ifdef MAPBASE
int m_poseInteractionRelativeYaw;
#endif
virtual void PopulatePoseParameters( void );
public:
@ -846,6 +860,10 @@ public:
// Return the stored pose parameter for "move_yaw"
inline int LookupPoseMoveYaw() { return m_poseMove_Yaw; }
#ifdef MAPBASE
inline int LookupPoseInteractionRelativeYaw() { return m_poseInteractionRelativeYaw; }
#endif
//-----------------------------------------------------
//
@ -1304,10 +1322,14 @@ private:
public:
float GetInteractionYaw( void ) const { return m_flInteractionYaw; }
bool IsRunningDynamicInteraction( void ) { return (m_iInteractionState != NPCINT_NOT_RUNNING && (m_hCine != NULL)); }
bool IsActiveDynamicInteraction( void ) { return (m_iInteractionState == NPCINT_RUNNING_ACTIVE && (m_hCine != NULL)); }
CAI_BaseNPC *GetInteractionPartner( void );
protected:
void ParseScriptedNPCInteractions( void );
void AddScriptedNPCInteraction( ScriptedNPCInteraction_t *pInteraction );
const char *GetScriptedNPCInteractionSequence( ScriptedNPCInteraction_t *pInteraction, int iPhase );
const char *GetScriptedNPCInteractionSequence( ScriptedNPCInteraction_t *pInteraction, int iPhase, bool bOtherNPC = false );
void StartRunningInteraction( CAI_BaseNPC *pOtherNPC, bool bActive );
void StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction, Vector vecOtherOrigin, QAngle angOtherAngles );
void CheckForScriptedNPCInteractions( void );
@ -1320,17 +1342,16 @@ protected:
#endif
bool InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction, Vector &vecOrigin, QAngle &angAngles );
virtual bool CanRunAScriptedNPCInteraction( bool bForced = false );
bool IsRunningDynamicInteraction( void ) { return (m_iInteractionState != NPCINT_NOT_RUNNING && (m_hCine != NULL)); }
bool IsActiveDynamicInteraction( void ) { return (m_iInteractionState == NPCINT_RUNNING_ACTIVE && (m_hCine != NULL)); }
ScriptedNPCInteraction_t *GetRunningDynamicInteraction( void ) { return &(m_ScriptedInteractions[m_iInteractionPlaying]); }
void SetInteractionCantDie( bool bCantDie ) { m_bCannotDieDuringInteraction = bCantDie; }
bool HasInteractionCantDie( void );
bool HasValidInteractionsOnCurrentEnemy( void );
virtual bool CanStartDynamicInteractionDuringMelee() { return false; }
void InputForceInteractionWithNPC( inputdata_t &inputdata );
void StartForcedInteraction( CAI_BaseNPC *pNPC, int iInteraction );
void CleanupForcedInteraction( void );
void CalculateForcedInteractionPosition( void );
CAI_BaseNPC *GetInteractionPartner( void );
private:
// Forced interactions
@ -1974,6 +1995,9 @@ public:
//---------------------------------
virtual void Ignite( float flFlameLifetime, bool bNPCOnly = true, float flSize = 0.0f, bool bCalledByLevelDesigner = false );
#ifdef MAPBASE
virtual void EnemyIgnited( CAI_BaseNPC *pVictim ) {}
#endif
virtual bool PassesDamageFilter( const CTakeDamageInfo &info );
//---------------------------------
@ -2228,6 +2252,9 @@ public:
static const char* GetActivityName (int actID);
static void AddActivityToSR(const char *actName, int conID);
#ifdef MAPBASE
static int GetOrRegisterActivity( const char *actName );
#endif
static void AddEventToSR(const char *eventName, int conID);
static const char* GetEventName (int actID);

View File

@ -1610,6 +1610,12 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask )
// as this should only run with the NPC "receiving" the interaction
ScriptedNPCInteraction_t *pInteraction = m_hForcedInteractionPartner->GetRunningDynamicInteraction();
if ( !(pInteraction->iFlags & SCNPC_FLAG_TEST_OTHER_ANGLES) )
{
TaskComplete();
return;
}
// Get our target's origin
Vector vecTarget = m_hForcedInteractionPartner->GetAbsOrigin();
@ -1617,7 +1623,7 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask )
float angInteractionAngle = pInteraction->angRelativeAngles.y;
angInteractionAngle += 180.0f;
GetMotor()->SetIdealYaw( CalcIdealYaw( vecTarget ) + angInteractionAngle );
GetMotor()->SetIdealYaw( AngleNormalize( CalcIdealYaw( vecTarget ) + angInteractionAngle ) );
if (FacingIdeal())
TaskComplete();
@ -4113,6 +4119,15 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask )
m_hCine->SynchronizeSequence( this );
}
}
#ifdef MAPBASE
if ( IsRunningDynamicInteraction() && m_poseInteractionRelativeYaw > -1 )
{
// Animations in progress require pose parameters to be set every frame, so keep setting the interaction relative yaw pose.
// The random value is added to help it pass server transmit checks.
SetPoseParameter( m_poseInteractionRelativeYaw, GetPoseParameter( m_poseInteractionRelativeYaw ) + RandomFloat( -0.1f, 0.1f ) );
}
#endif
break;
}

View File

@ -477,6 +477,20 @@ public:
AI_TraceHull( baseNPC->GetAbsOrigin(), vUpBit, baseNPC->GetHullMins(), baseNPC->GetHullMaxs(),
MASK_NPCSOLID, baseNPC, COLLISION_GROUP_NONE, &tr );
// NEW: For vphysics/flying entities, do a second attempt which teleports based on bounding box
if ( (baseNPC->GetMoveType() == MOVETYPE_VPHYSICS || baseNPC->CapabilitiesGet() & bits_CAP_MOVE_FLY) && (tr.startsolid || tr.fraction < 1.0) )
{
vUpBit.z += baseNPC->BoundingRadius();
baseNPC->Teleport( &vUpBit, NULL, NULL );
UTIL_DropToFloor( baseNPC, MASK_NPCSOLID );
Vector vUpBit2 = vUpBit;
vUpBit2.z += 1;
AI_TraceHull( vUpBit, vUpBit2, baseNPC->CollisionProp()->OBBMins(), baseNPC->CollisionProp()->OBBMaxs(),
MASK_NPCSOLID, baseNPC, COLLISION_GROUP_NONE, &tr );
}
if ( tr.startsolid || (tr.fraction < 1.0) )
{
baseNPC->SUB_Remove();

View File

@ -88,6 +88,15 @@ static void DispatchComeback( CAI_ExpresserWithFollowup *pExpress, CBaseEntity *
// add in any provided contexts from the parameters onto the ones stored in the followup
criteria.Merge( followup.followup_contexts );
#ifdef MAPBASE
if (CAI_ExpresserSink *pSink = dynamic_cast<CAI_ExpresserSink *>(pRespondent))
{
criteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", (pRespondent->GetAbsOrigin() - pSpeaker->GetAbsOrigin()).Length() ) );
g_ResponseQueueManager.GetQueue()->AppendFollowupCriteria( followup.followup_concept, criteria, pSink->GetSinkExpresser(), pSink, pRespondent, pSpeaker, kDRT_SPECIFIC );
pSink->Speak( followup.followup_concept, &criteria );
}
#else
// This is kludgy and needs to be fixed in class hierarchy, but for now, try to guess at the most likely
// kinds of targets and dispatch to them.
if (CBaseMultiplayerPlayer *pPlayer = dynamic_cast<CBaseMultiplayerPlayer *>(pRespondent))
@ -99,6 +108,7 @@ static void DispatchComeback( CAI_ExpresserWithFollowup *pExpress, CBaseEntity *
{
pActor->Speak( followup.followup_concept, &criteria );
}
#endif
}
#if 0

View File

@ -902,6 +902,17 @@ BEGIN_DATADESC( CAI_Hint )
DEFINE_OUTPUT( m_OnNPCStartedUsing, "OnNPCStartedUsing" ),
DEFINE_OUTPUT( m_OnNPCStoppedUsing, "OnNPCStoppedUsing" ),
#ifdef MAPBASE
DEFINE_OUTPUT( m_OnScriptEvent[0], "OnScriptEvent01" ),
DEFINE_OUTPUT( m_OnScriptEvent[1], "OnScriptEvent02" ),
DEFINE_OUTPUT( m_OnScriptEvent[2], "OnScriptEvent03" ),
DEFINE_OUTPUT( m_OnScriptEvent[3], "OnScriptEvent04" ),
DEFINE_OUTPUT( m_OnScriptEvent[4], "OnScriptEvent05" ),
DEFINE_OUTPUT( m_OnScriptEvent[5], "OnScriptEvent06" ),
DEFINE_OUTPUT( m_OnScriptEvent[6], "OnScriptEvent07" ),
DEFINE_OUTPUT( m_OnScriptEvent[7], "OnScriptEvent08" ),
#endif
END_DATADESC( );
#ifdef MAPBASE_VSCRIPT
@ -1325,7 +1336,7 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint
if ( distance > nRadius * nRadius )
{
REPORTFAILURE( "NPC is not within the node's radius." );
REPORTFAILURE( "Not within the node's radius." );
return false;
}
}
@ -1705,6 +1716,19 @@ void CAI_Hint::NPCStoppedUsing( CAI_BaseNPC *pNPC )
m_OnNPCStoppedUsing.Set( pNPC, pNPC, this );
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CAI_Hint::FireScriptEvent( int nEvent )
{
if ( ( nEvent >= 1 ) && ( nEvent <= 8 ) )
{
m_OnScriptEvent[nEvent - 1].FireOutput( m_hHintOwner, this );
}
}
#endif
CON_COMMAND(ai_dump_hints, "")
{
@ -1794,6 +1818,11 @@ hinttypedescs_t g_pszHintDescriptions[] =
{ HINT_HL1_WORLD_ALIEN_BLOOD, "HL1: World: Alien Blood" },
{ HINT_CSTRIKE_HOSTAGE_ESCAPE, "CS Port: Hostage Escape" },
#ifdef MAPBASE
{ HINT_TACTICAL_COVER_CUSTOM, "Mapbase: Custom Cover" },
{ HINT_TACTICAL_GRENADE_THROW, "Mapbase: Grenade Throw Hint" },
#endif
};
//-----------------------------------------------------------------------------

View File

@ -118,6 +118,7 @@ enum Hint_e
// (these start at a high number to avoid potential conflicts with mod hints)
HINT_TACTICAL_COVER_CUSTOM = 10000, // Cover node with a custom hint activity (NPCs can take cover and reload here while playing said activity)
HINT_TACTICAL_GRENADE_THROW, // Pre-determined position for NPCs to throw grenades at when their target in combat is near it
#endif
};
const char *GetHintTypeDescription( Hint_e iHintType );
@ -322,6 +323,9 @@ public:
void FixupTargetNode();
void NPCStartedUsing( CAI_BaseNPC *pNPC );
void NPCStoppedUsing( CAI_BaseNPC *pNPC );
#ifdef MAPBASE
void FireScriptEvent( int nEvent );
#endif
HintIgnoreFacing_t GetIgnoreFacing() const { return m_NodeData.fIgnoreFacing; }
@ -384,6 +388,10 @@ private:
float m_nodeFOV;
Vector m_vecForward;
#ifdef MAPBASE
COutputEvent m_OnScriptEvent[8];
#endif
// The next hint in list of all hints
friend class CAI_HintManager;

View File

@ -128,6 +128,12 @@ ConceptInfo_t g_ConceptInfos[] =
// Passenger behaviour
{ TLK_PASSENGER_NEW_RADAR_CONTACT, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
#ifdef MAPBASE
{ TLK_TAKING_FIRE, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
{ TLK_NEW_ENEMY, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
{ TLK_COMBAT_IDLE, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
#endif
};
//-----------------------------------------------------------------------------
@ -1259,6 +1265,38 @@ void CAI_PlayerAlly::OnKilledNPC( CBaseCombatCharacter *pKilled )
}
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_PlayerAlly::OnEnemyRangeAttackedMe( CBaseEntity *pEnemy, const Vector &vecDir, const Vector &vecEnd )
{
BaseClass::OnEnemyRangeAttackedMe( pEnemy, vecDir, vecEnd );
if ( IRelationType( pEnemy ) <= D_FR )
{
AI_CriteriaSet modifiers;
ModifyOrAppendEnemyCriteria( modifiers, pEnemy );
Vector vecEntDir = (pEnemy->EyePosition() - EyePosition());
float flDot = DotProduct( vecEntDir.Normalized(), vecDir );
modifiers.AppendCriteria( "shot_dot", CNumStr( flDot ) );
if (GetLastDamageTime() == gpGlobals->curtime)
modifiers.AppendCriteria( "missed", "0" );
else
modifiers.AppendCriteria( "missed", "1" );
// Check if they're out of ammo
if ( pEnemy->IsCombatCharacter() && pEnemy->MyCombatCharacterPointer()->GetActiveWeapon() && pEnemy->MyCombatCharacterPointer()->GetActiveWeapon()->Clip1() <= 0 )
modifiers.AppendCriteria( "last_attack", "1" );
else
modifiers.AppendCriteria( "last_attack", "0" );
SpeakIfAllowed( TLK_TAKING_FIRE, modifiers );
}
}
#endif
//-----------------------------------------------------------------------------
void CAI_PlayerAlly::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
{
@ -1762,6 +1800,54 @@ bool CAI_PlayerAlly::IsAllowedToSpeak( AIConcept_t concept, bool bRespondingToPl
return true;
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose: Specifically for player allies handling followup responses.
// Better-accounts for unknown concepts so that users are free in what they use.
//-----------------------------------------------------------------------------
bool CAI_PlayerAlly::IsAllowedToSpeakFollowup( AIConcept_t concept, CBaseEntity *pIssuer, bool bSpecific )
{
CAI_AllySpeechManager * pSpeechManager = GetAllySpeechManager();
ConceptInfo_t * pInfo = pSpeechManager->GetConceptInfo( concept );
ConceptCategory_t category = SPEECH_PRIORITY; // Must be SPEECH_PRIORITY to get around semaphore
if ( !IsOkToSpeak( category, true ) )
return false;
// If this followup is specifically targeted towards us, speak if we're not already speaking
// If it's meant to be spoken by anyone, respect speech delay and semaphore
if ( bSpecific )
{
if ( !GetExpresser()->CanSpeakAfterMyself() )
return false;
}
else
{
if ( !GetExpresser()->CanSpeak() )
return false;
CAI_TimedSemaphore *pSemaphore = GetExpresser()->GetMySpeechSemaphore( this );
if ( pSemaphore && !pSemaphore->IsAvailable( this ) )
{
// Only if the semaphore holder isn't the one dispatching the followup
if ( pSemaphore->GetOwner() != pIssuer )
return false;
}
}
if ( !pSpeechManager->ConceptDelayExpired( concept ) )
return false;
if ( ( pInfo && pInfo->flags & AICF_SPEAK_ONCE ) && GetExpresser()->SpokeConcept( concept ) )
return false;
if ( !GetExpresser()->CanSpeakConcept( concept ) )
return false;
return true;
}
#endif
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CAI_PlayerAlly::SpeakIfAllowed( AIConcept_t concept, const char *modifiers, bool bRespondingToPlayer, char *pszOutResponseChosen, size_t bufsize )

View File

@ -132,6 +132,13 @@
#define TLK_TGCATCHUP "TLK_TGCATCHUP"
#define TLK_TGENDTOUR "TLK_TGENDTOUR"
#ifdef MAPBASE
// Additional concepts for companions in mods
#define TLK_TAKING_FIRE "TLK_TAKING_FIRE" // Someone fired at me (regardless of whether I was hit)
#define TLK_NEW_ENEMY "TLK_NEW_ENEMY" // A new enemy appeared while combat was already in progress
#define TLK_COMBAT_IDLE "TLK_COMBAT_IDLE" // Similar to TLK_ATTACKING, but specifically for when *not* currently attacking (e.g. when in cover or reloading)
#endif
//-----------------------------------------------------------------------------
#define TALKRANGE_MIN 500.0 // don't talk to anyone farther away than this
@ -315,6 +322,10 @@ public:
//---------------------------------
void OnKilledNPC( CBaseCombatCharacter *pKilled );
#ifdef MAPBASE
void OnEnemyRangeAttackedMe( CBaseEntity *pEnemy, const Vector &vecDir, const Vector &vecEnd );
#endif
//---------------------------------
// Damage handling
//---------------------------------
@ -392,6 +403,9 @@ public:
bool ShouldSpeakRandom( AIConcept_t concept, int iChance );
bool IsAllowedToSpeak( AIConcept_t concept, bool bRespondingToPlayer = false );
#ifdef MAPBASE
bool IsAllowedToSpeakFollowup( AIConcept_t concept, CBaseEntity *pIssuer, bool bSpecific );
#endif
virtual bool SpeakIfAllowed( AIConcept_t concept, const char *modifiers = NULL, bool bRespondingToPlayer = false, char *pszOutResponseChosen = NULL, size_t bufsize = 0 );
#ifdef MAPBASE
virtual bool SpeakIfAllowed( AIConcept_t concept, AI_CriteriaSet& modifiers, bool bRespondingToPlayer = false, char *pszOutResponseChosen = NULL, size_t bufsize = 0 );

View File

@ -532,6 +532,7 @@ public:
// Must override CAI_Relationship
void Spawn() { m_bIsActive = false; }
void Activate();
bool KeyValue( const char *szKeyName, const char *szValue );
@ -557,6 +558,19 @@ BEGIN_DATADESC( CAI_ClassRelationship )
END_DATADESC()
//---------------------------------------------------------
//---------------------------------------------------------
void CAI_ClassRelationship::Activate()
{
BaseClass::Activate();
// Must re-apply every time a save is loaded
if ( m_bIsActive )
{
ApplyRelationship();
}
}
//-----------------------------------------------------------------------------
// Purpose: Caches entity key values until spawn is called.
// Input : szKeyName -
@ -615,6 +629,9 @@ void CAI_ClassRelationship::ChangeRelationships( int disposition, int iReverting
return;
}
if ( !CBaseCombatCharacter::DefaultRelationshipsLoaded() )
return;
if ( m_iPreviousDisposition == -1 && iReverting == NOT_REVERTING )
{
// Set previous disposition.

View File

@ -126,6 +126,13 @@ public:
virtual void OnSpokeConcept( AIConcept_t concept, AI_Response *response ) {};
virtual void OnStartSpeaking() {}
virtual bool UseSemaphore() { return true; }
#ifdef MAPBASE
// Works around issues with CAI_ExpresserHost<> class hierarchy
virtual CAI_Expresser *GetSinkExpresser() { return NULL; }
virtual bool IsAllowedToSpeakFollowup( AIConcept_t concept, CBaseEntity *pIssuer, bool bSpecific ) { return true; }
virtual bool Speak( AIConcept_t concept, AI_CriteriaSet *pCriteria, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL ) { return false; }
#endif
};
struct ConceptHistory_t
@ -244,9 +251,15 @@ public:
static bool RunScriptResponse( CBaseEntity *pTarget, const char *response, AI_CriteriaSet *criteria, bool file );
#endif
#ifdef MAPBASE
public:
#else
protected:
#endif
CAI_TimedSemaphore *GetMySpeechSemaphore( CBaseEntity *pNpc );
protected:
bool SpeakRawScene( const char *pszScene, float delay, AI_Response *response, IRecipientFilter *filter = NULL );
// This will create a fake .vcd/CChoreoScene to wrap the sound to be played
#ifdef MAPBASE
@ -311,11 +324,15 @@ private:
//
template <class BASE_NPC>
class CAI_ExpresserHost : public BASE_NPC, protected CAI_ExpresserSink
class CAI_ExpresserHost : public BASE_NPC, public CAI_ExpresserSink
{
DECLARE_CLASS_NOFRIEND( CAI_ExpresserHost, BASE_NPC );
public:
#ifdef MAPBASE
CAI_Expresser *GetSinkExpresser() { return this->GetExpresser(); }
#endif
virtual void NoteSpeaking( float duration, float delay );
virtual bool Speak( AIConcept_t concept, const char *modifiers = NULL, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL );

View File

@ -11,6 +11,9 @@
#include "ai_baseactor.h"
#include "ai_speech.h"
//#include "flex_expresser.h"
#ifdef MAPBASE
#include "sceneentity.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
@ -170,15 +173,25 @@ void CResponseQueue::RemoveExpresserHost(CBaseEntity *host)
}
}
#ifdef MAPBASE
/// Get the expresser for a base entity.
static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt, CAI_ExpresserSink **ppSink = NULL)
{
if ( CAI_ExpresserSink *pSink = dynamic_cast<CAI_ExpresserSink *>(pEnt) )
{
if (ppSink)
*ppSink = pSink;
return pSink->GetSinkExpresser();
}
return NULL;
}
#else
/// Get the expresser for a base entity.
/// TODO: Kind of an ugly hack until I get the class hierarchy straightened out.
static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt)
{
#ifdef MAPBASE
if ( CBasePlayer *pPlayer = ToBasePlayer(pEnt) )
#else
if ( CBaseMultiplayerPlayer *pPlayer = dynamic_cast<CBaseMultiplayerPlayer *>(pEnt) )
#endif
{
return pPlayer->GetExpresser();
}
@ -197,6 +210,7 @@ static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt)
return NULL;
}
}
#endif
void CResponseQueue::CDeferredResponse::Quash()
@ -205,6 +219,23 @@ void CResponseQueue::CDeferredResponse::Quash()
m_fDispatchTime = 0;
}
#ifdef MAPBASE
void CResponseQueue::AppendFollowupCriteria( AIConcept_t concept, AI_CriteriaSet &set, CAI_Expresser *pEx,
CAI_ExpresserSink *pSink, CBaseEntity *pTarget, CBaseEntity *pIssuer, DeferredResponseTarget_t nTargetType )
{
// Allows control over which followups interrupt speech routines
set.AppendCriteria( "followup_allowed_to_speak", (pSink->IsAllowedToSpeakFollowup( concept, pIssuer, nTargetType == kDRT_SPECIFIC )) ? "1" : "0" );
set.AppendCriteria( "followup_target_type", UTIL_VarArgs( "%i", (int)nTargetType ) );
// NOTE: This assumes any expresser entity derived from CBaseFlex is also derived from CBaseCombatCharacter
if (pTarget->IsCombatCharacter())
set.AppendCriteria( "is_speaking", (pEx->IsSpeaking() || IsRunningScriptedSceneWithSpeechAndNotPaused( assert_cast<CBaseFlex*>(pTarget) )) ? "1" : "0" );
else
set.AppendCriteria( "is_speaking", "0" );
}
#endif
bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response)
{
// find the target.
@ -272,9 +303,15 @@ bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response)
continue; // too far
}
#ifdef MAPBASE
CAI_ExpresserSink *pSink = NULL;
pEx = InferExpresserFromBaseEntity( pTarget, &pSink );
#else
pEx = InferExpresserFromBaseEntity(pTarget);
#endif
if ( !pEx || pTarget == pIssuer )
continue;
AI_CriteriaSet characterCriteria;
pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL);
characterCriteria.Merge(&deferredCriteria);
@ -282,6 +319,11 @@ bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response)
{
characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", sqrt(distIssuerToTargetSq) ) );
}
#ifdef MAPBASE
AppendFollowupCriteria( response.m_concept, characterCriteria, pEx, pSink, pTarget, pIssuer, kDRT_ALL );
#endif
AI_Response prospectiveResponse;
if ( pEx->FindResponse( prospectiveResponse, response.m_concept, &characterCriteria ) )
{
@ -304,14 +346,26 @@ bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response)
return false; // we're done right here.
// Get the expresser for the target.
#ifdef MAPBASE
CAI_ExpresserSink *pSink = NULL;
pEx = InferExpresserFromBaseEntity( pTarget, &pSink );
#else
pEx = InferExpresserFromBaseEntity(pTarget);
#endif
if (!pEx)
return false;
AI_CriteriaSet characterCriteria;
pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL);
characterCriteria.Merge(&deferredCriteria);
#ifdef MAPBASE
if ( pIssuer )
{
characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", (pTarget->GetAbsOrigin() - pIssuer->GetAbsOrigin()).Length() ) );
}
AppendFollowupCriteria( response.m_concept, characterCriteria, pEx, pSink, pTarget, pIssuer, kDRT_SPECIFIC );
#endif
pEx->Speak( response.m_concept, &characterCriteria );
return true;
@ -364,7 +418,12 @@ bool CResponseQueue::DispatchOneResponse_ThenANY( CDeferredResponse &response, A
continue; // too far
}
#ifdef MAPBASE
CAI_ExpresserSink *pSink = NULL;
pEx = InferExpresserFromBaseEntity( pTarget, &pSink );
#else
pEx = InferExpresserFromBaseEntity(pTarget);
#endif
if ( !pEx )
continue;
@ -376,6 +435,11 @@ bool CResponseQueue::DispatchOneResponse_ThenANY( CDeferredResponse &response, A
{
characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", sqrt(distIssuerToTargetSq) ) );
}
#ifdef MAPBASE
AppendFollowupCriteria( response.m_concept, characterCriteria, pEx, pSink, pTarget, pIssuer, kDRT_ANY );
#endif
AI_Response prospectiveResponse;
#ifdef MAPBASE

View File

@ -116,6 +116,11 @@ public:
inline int GetNumExpresserTargets() const;
inline CBaseEntity *GetExpresserHost(int which) const;
#ifdef MAPBASE
void AppendFollowupCriteria( AIConcept_t concept, AI_CriteriaSet &set, CAI_Expresser *pEx,
CAI_ExpresserSink *pSink, CBaseEntity *pTarget, CBaseEntity *pIssuer, DeferredResponseTarget_t nTargetType );
#endif
protected:
/// Actually send off one response to a consumer
/// Return true if dispatch succeeded

View File

@ -335,6 +335,8 @@ BEGIN_ENT_SCRIPTDESC( CBaseAnimating, CBaseEntity, "Animating models" )
DEFINE_SCRIPTFUNC( FindBodygroupByName, "Finds a bodygroup by name" )
DEFINE_SCRIPTFUNC( GetBodygroupCount, "Gets the number of models in a bodygroup" )
DEFINE_SCRIPTFUNC( GetNumBodyGroups, "Gets the number of bodygroups" )
DEFINE_SCRIPTFUNC( GetModelScale, "Gets the model's scale" )
DEFINE_SCRIPTFUNC( SetModelScale, "Sets the model's scale with the specified change duration" )
DEFINE_SCRIPTFUNC( Dissolve, "Use 'sprites/blueglow1.vmt' for the default material, Time() for the default start time, false for npcOnly if you don't want it to check if the entity is a NPC first, 0 for the default dissolve type, Vector(0,0,0) for the default dissolver origin, and 0 for the default magnitude." )
DEFINE_SCRIPTFUNC( Ignite, "'NPCOnly' only lets this fall through if the entity is a NPC and 'CalledByLevelDesigner' determines whether to treat this like the Ignite input or just an internal ignition call." )
@ -1331,7 +1333,7 @@ void CBaseAnimating::HandleAnimEvent( animevent_t *pEvent )
#ifdef MAPBASE
else if ( pEvent->event == AE_NPC_RESPONSE )
{
if (!MyNPCPointer()->GetExpresser()->IsSpeaking())
if (MyNPCPointer() && MyNPCPointer()->GetExpresser() && !MyNPCPointer()->GetExpresser()->IsSpeaking())
{
DispatchResponse( pEvent->options );
}
@ -1342,6 +1344,18 @@ void CBaseAnimating::HandleAnimEvent( animevent_t *pEvent )
DispatchResponse( pEvent->options );
return;
}
else if ( pEvent->event == AE_VSCRIPT_RUN )
{
if (!RunScript( pEvent->options ))
Warning( "%s failed to run AE_VSCRIPT_RUN on server with \"%s\"\n", GetDebugName(), pEvent->options );
return;
}
else if ( pEvent->event == AE_VSCRIPT_RUN_FILE )
{
if (!RunScriptFile( pEvent->options ))
Warning( "%s failed to run AE_VSCRIPT_RUN_FILE on server with \"%s\"\n", GetDebugName(), pEvent->options );
return;
}
#endif
else if ( pEvent->event == AE_RAGDOLL )
{

View File

@ -28,6 +28,15 @@
IMPLEMENT_SERVERCLASS_ST( CBaseHLBludgeonWeapon, DT_BaseHLBludgeonWeapon )
END_SEND_TABLE()
#ifdef MAPBASE
BEGIN_DATADESC(CBaseHLBludgeonWeapon)
DEFINE_FIELD(m_flDelayedFire, FIELD_TIME),
DEFINE_FIELD(m_bShotDelayed, FIELD_BOOLEAN),
END_DATADESC()
#endif // MAPBASE
#define BLUDGEON_HULL_DIM 16
static const Vector g_bludgeonMins(-BLUDGEON_HULL_DIM,-BLUDGEON_HULL_DIM,-BLUDGEON_HULL_DIM);
@ -39,6 +48,9 @@ static const Vector g_bludgeonMaxs(BLUDGEON_HULL_DIM,BLUDGEON_HULL_DIM,BLUDGEON_
CBaseHLBludgeonWeapon::CBaseHLBludgeonWeapon()
{
m_bFiresUnderwater = true;
#ifdef MAPBASE
m_bShotDelayed = false;
#endif // MAPBASE
}
//-----------------------------------------------------------------------------
@ -96,11 +108,19 @@ void CBaseHLBludgeonWeapon::ItemPostFrame( void )
#ifdef MAPBASE
if (pOwner->HasSpawnFlags( SF_PLAYER_SUPPRESS_FIRING ))
{
m_bShotDelayed = false;
WeaponIdle();
return;
}
#endif
// See if we need to fire off our secondary round
if (m_bShotDelayed)
{
if (gpGlobals->curtime > m_flDelayedFire)
DelayedAttack();
}
else
#endif
if ( (pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
{
PrimaryAttack();
@ -162,7 +182,12 @@ void CBaseHLBludgeonWeapon::Hit( trace_t &traceHit, Activity nHitActivity, bool
pPlayer->EyeVectors( &hitDirection, NULL, NULL );
VectorNormalize( hitDirection );
#ifdef MAPBASE
CTakeDamageInfo info(GetOwner(), GetOwner(), GetDamageForActivity(nHitActivity), GetDamageType());
#else
CTakeDamageInfo info(GetOwner(), GetOwner(), GetDamageForActivity(nHitActivity), DMG_CLUB);
#endif // MAPBASE
if( pPlayer && pHitEntity->IsNPC() )
{
@ -234,7 +259,7 @@ Activity CBaseHLBludgeonWeapon::ChooseIntersectionPointAndActivity( trace_t &hit
}
return ACT_VM_HITCENTER;
return GetPrimaryAttackActivity();
}
//-----------------------------------------------------------------------------
@ -292,7 +317,6 @@ void CBaseHLBludgeonWeapon::ImpactEffect( trace_t &traceHit )
UTIL_ImpactTrace( &traceHit, DMG_CLUB );
}
//------------------------------------------------------------------------------
// Purpose : Starts the swing of the weapon and determines the animation
// Input : bIsSecondary - is this a secondary attack?
@ -315,10 +339,14 @@ void CBaseHLBludgeonWeapon::Swing( int bIsSecondary )
Vector swingEnd = swingStart + forward * GetRange();
UTIL_TraceLine( swingStart, swingEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &traceHit );
Activity nHitActivity = ACT_VM_HITCENTER;
Activity nHitActivity = GetPrimaryAttackActivity();
// Like bullets, bludgeon traces have to trace against triggers.
#ifdef MAPBASE
CTakeDamageInfo triggerInfo(GetOwner(), GetOwner(), GetDamageForActivity(nHitActivity), GetDamageType());
#else
CTakeDamageInfo triggerInfo(GetOwner(), GetOwner(), GetDamageForActivity(nHitActivity), DMG_CLUB);
#endif // MAPBASE
triggerInfo.SetDamagePosition( traceHit.startpos );
triggerInfo.SetDamageForce( forward );
TraceAttackToTriggers( triggerInfo, traceHit.startpos, traceHit.endpos, forward );
@ -369,31 +397,20 @@ void CBaseHLBludgeonWeapon::Swing( int bIsSecondary )
{
nHitActivity = bIsSecondary ? ACT_VM_MISSCENTER2 : ACT_VM_MISSCENTER;
#ifndef MAPBASE
// We want to test the first swing again
Vector testEnd = swingStart + forward * GetRange();
#ifdef MAPBASE
// Sound has been moved here since we're using the other melee sounds now
WeaponSound( SINGLE );
#endif
// See if we happened to hit water
ImpactWater(swingStart, testEnd);
#endif // !MAPBASE
}
#ifndef MAPBASE
else
{
#ifdef MAPBASE
// Other melee sounds
if (traceHit.m_pEnt && traceHit.m_pEnt->IsWorld())
WeaponSound(MELEE_HIT_WORLD);
else if (traceHit.m_pEnt && !traceHit.m_pEnt->PassesDamageFilter(triggerInfo))
WeaponSound(MELEE_MISS);
else
WeaponSound(MELEE_HIT);
#endif
Hit( traceHit, nHitActivity, bIsSecondary ? true : false );
}
#endif
// Send the anim
SendWeaponAnim( nHitActivity );
@ -409,5 +426,125 @@ void CBaseHLBludgeonWeapon::Swing( int bIsSecondary )
#ifdef MAPBASE
pOwner->SetAnimation( PLAYER_ATTACK1 );
if (GetHitDelay() > 0.f)
{
//Play swing sound
WeaponSound(SINGLE);
m_flDelayedFire = gpGlobals->curtime + GetHitDelay();
m_bShotDelayed = true;
}
else
{
if (traceHit.fraction == 1.0f)
{
// We want to test the first swing again
Vector testEnd = swingStart + forward * GetRange();
//Play swing sound
WeaponSound(SINGLE);
// See if we happened to hit water
ImpactWater(swingStart, testEnd);
}
else
{
// Other melee sounds
if (traceHit.m_pEnt && traceHit.m_pEnt->IsWorld())
WeaponSound(MELEE_HIT_WORLD);
else if (traceHit.m_pEnt && !traceHit.m_pEnt->PassesDamageFilter(triggerInfo))
WeaponSound(MELEE_MISS);
else
WeaponSound(MELEE_HIT);
Hit(traceHit, nHitActivity, bIsSecondary ? true : false);
}
}
#endif
}
#ifdef MAPBASE
void CBaseHLBludgeonWeapon::DelayedAttack(void)
{
m_bShotDelayed = false;
trace_t traceHit;
// Try a ray
CBasePlayer* pOwner = ToBasePlayer(GetOwner());
if (!pOwner)
return;
pOwner->RumbleEffect(RUMBLE_CROWBAR_SWING, 0, RUMBLE_FLAG_RESTART);
Vector swingStart = pOwner->Weapon_ShootPosition();
Vector forward;
forward = pOwner->GetAutoaimVector(AUTOAIM_SCALE_DEFAULT, GetRange());
Vector swingEnd = swingStart + forward * GetRange();
UTIL_TraceLine(swingStart, swingEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &traceHit);
if (traceHit.fraction == 1.0)
{
float bludgeonHullRadius = 1.732f * BLUDGEON_HULL_DIM; // hull is +/- 16, so use cuberoot of 2 to determine how big the hull is from center to the corner point
// Back off by hull "radius"
swingEnd -= forward * bludgeonHullRadius;
UTIL_TraceHull(swingStart, swingEnd, g_bludgeonMins, g_bludgeonMaxs, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &traceHit);
if (traceHit.fraction < 1.0 && traceHit.m_pEnt)
{
Vector vecToTarget = traceHit.m_pEnt->GetAbsOrigin() - swingStart;
VectorNormalize(vecToTarget);
float dot = vecToTarget.Dot(forward);
// YWB: Make sure they are sort of facing the guy at least...
if (dot < 0.70721f)
{
// Force amiss
traceHit.fraction = 1.0f;
}
else
{
ChooseIntersectionPointAndActivity(traceHit, g_bludgeonMins, g_bludgeonMaxs, pOwner);
}
}
}
if (traceHit.fraction == 1.0f)
{
// We want to test the first swing again
Vector testEnd = swingStart + forward * GetRange();
// See if we happened to hit water
ImpactWater(swingStart, testEnd);
}
else
{
CTakeDamageInfo triggerInfo(GetOwner(), GetOwner(), GetDamageForActivity(GetActivity()), GetDamageType());
triggerInfo.SetDamagePosition(traceHit.startpos);
triggerInfo.SetDamageForce(forward);
// Other melee sounds
if (traceHit.m_pEnt && traceHit.m_pEnt->IsWorld())
WeaponSound(MELEE_HIT_WORLD);
else if (traceHit.m_pEnt && !traceHit.m_pEnt->PassesDamageFilter(triggerInfo))
WeaponSound(MELEE_MISS);
else
WeaponSound(MELEE_HIT);
Hit(traceHit, GetActivity(), false);
}
}
bool CBaseHLBludgeonWeapon::CanHolster(void)
{
if (m_bShotDelayed)
return false;
return BaseClass::CanHolster();
}
#endif // MAPBASE

View File

@ -23,6 +23,9 @@ public:
CBaseHLBludgeonWeapon();
DECLARE_SERVERCLASS();
#ifdef MAPBASE
DECLARE_DATADESC();
#endif // MAPBASE
virtual void Spawn( void );
virtual void Precache( void );
@ -30,6 +33,9 @@ public:
//Attack functions
virtual void PrimaryAttack( void );
virtual void SecondaryAttack( void );
#ifdef MAPBASE
void DelayedAttack(void);
#endif // MAPBASE
virtual void ItemPostFrame( void );
@ -44,6 +50,12 @@ public:
virtual int CapabilitiesGet( void );
virtual int WeaponMeleeAttack1Condition( float flDot, float flDist );
#ifdef MAPBASE
virtual int GetDamageType() { return DMG_CLUB; }
virtual float GetHitDelay() { return 0.f; }
virtual bool CanHolster(void);
#endif // MAPBASE
protected:
virtual void ImpactEffect( trace_t &trace );
@ -52,6 +64,11 @@ private:
void Swing( int bIsSecondary );
void Hit( trace_t &traceHit, Activity nHitActivity, bool bIsSecondary );
Activity ChooseIntersectionPointAndActivity( trace_t &hitTrace, const Vector &mins, const Vector &maxs, CBasePlayer *pOwner );
#ifdef MAPBASE
float m_flDelayedFire;
bool m_bShotDelayed;
#endif // MAPBASE
};
#endif

View File

@ -175,6 +175,7 @@ BEGIN_ENT_SCRIPTDESC( CBaseCombatCharacter, CBaseFlex, "The base class shared by
DEFINE_SCRIPTFUNC_NAMED( ScriptRelationType, "GetRelationship", "Get a character's relationship to a specific entity." )
DEFINE_SCRIPTFUNC_NAMED( ScriptRelationPriority, "GetRelationPriority", "Get a character's relationship priority for a specific entity." )
DEFINE_SCRIPTFUNC_NAMED( ScriptSetRelationship, "SetRelationship", "Set a character's relationship with a specific entity." )
DEFINE_SCRIPTFUNC_NAMED( ScriptSetClassRelationship, "SetClassRelationship", "Set a character's relationship with a specific Classify() class." )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetVehicleEntity, "GetVehicleEntity", "Get the entity for a character's current vehicle if they're in one." )
@ -1264,6 +1265,11 @@ bool CTraceFilterMelee::ShouldHitEntity( IHandleEntity *pHandleEntity, int conte
if ( pEntity->m_takedamage == DAMAGE_NO )
return false;
#ifdef MAPBASE // Moved from CheckTraceHullAttack()
if( m_pPassEnt && !pEntity->CanBeHitByMeleeAttack( const_cast<CBaseEntity*>(EntityFromEntityHandle( m_pPassEnt ) ) ) )
return false;
#endif
// FIXME: Do not translate this to the driver because the driver only accepts damage from the vehicle
// Translate the vehicle into its driver for damage
/*
@ -1311,6 +1317,10 @@ bool CTraceFilterMelee::ShouldHitEntity( IHandleEntity *pHandleEntity, int conte
}
else
{
#ifdef MAPBASE
// Do not override an existing hit entity
if (!m_pHit)
#endif
m_pHit = pEntity;
// Make sure if the player is holding this, he drops it
@ -1386,11 +1396,13 @@ CBaseEntity *CBaseCombatCharacter::CheckTraceHullAttack( const Vector &vStart, c
pEntity = traceFilter.m_pHit;
}
#ifndef MAPBASE // Moved to CTraceFilterMelee
if( pEntity && !pEntity->CanBeHitByMeleeAttack(this) )
{
// If we touched something, but it shouldn't be hit, return nothing.
pEntity = NULL;
}
#endif
return pEntity;
@ -3239,6 +3251,16 @@ void CBaseCombatCharacter::SetDefaultRelationship(Class_T nClass, Class_T nClass
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose: Determine whether or not default relationships are loaded
// Input :
// Output :
//-----------------------------------------------------------------------------
bool CBaseCombatCharacter::DefaultRelationshipsLoaded()
{
return m_DefaultRelationship != NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Fetch the default (ignore ai_relationship changes) relationship
// Input :
@ -3466,7 +3488,7 @@ void CBaseCombatCharacter::AddRelationship( const char *pszRelationship, CBaseEn
}
else
{
#ifdef MAPBASE // I know the extra #ifdef is pointless, but it's there so you know this is new
// NEW: Classify class relationships
if (!Q_strnicmp(entityString, "CLASS_", 5))
{
// Go through all of the classes and find which one this is
@ -3487,7 +3509,6 @@ void CBaseCombatCharacter::AddRelationship( const char *pszRelationship, CBaseEn
}
if (!bFoundEntity)
#endif
DevWarning( "Couldn't set relationship to unknown entity or class (%s)!\n", entityString );
}
}
@ -3594,7 +3615,7 @@ CBaseEntity *CBaseCombatCharacter::Weapon_FindUsable( const Vector &range )
else if (hl2_episodic.GetBool() && !GetActiveWeapon())
{
// Unarmed citizens are conservative in their weapon finding...in Episode One
if (Classify() != CLASS_PLAYER_ALLY_VITAL && Q_strncmp(STRING(gpGlobals->mapname), "ep1_", 4))
if (Classify() != CLASS_PLAYER_ALLY_VITAL && Q_strncmp(STRING(gpGlobals->mapname), "ep1_", 4) == 0)
bConservative = true;
}
#endif
@ -4643,6 +4664,13 @@ void CBaseCombatCharacter::ScriptSetRelationship( HSCRIPT pTarget, int dispositi
AddEntityRelationship( ToEnt( pTarget ), (Disposition_t)disposition, priority );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CBaseCombatCharacter::ScriptSetClassRelationship( int classify, int disposition, int priority )
{
AddClassRelationship( (Class_T)classify, (Disposition_t)disposition, priority);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
HSCRIPT CBaseCombatCharacter::ScriptGetVehicleEntity()

View File

@ -263,6 +263,10 @@ public:
virtual bool CanBecomeServerRagdoll( void ) { return true; }
#ifdef MAPBASE
virtual void OnEnemyRangeAttackedMe( CBaseEntity *pEnemy, const Vector &vecDir, const Vector &vecEnd ) {}
#endif
// -----------------------
// Damage
// -----------------------
@ -440,6 +444,7 @@ public:
int ScriptRelationType( HSCRIPT pTarget );
int ScriptRelationPriority( HSCRIPT pTarget );
void ScriptSetRelationship( HSCRIPT pTarget, int disposition, int priority );
void ScriptSetClassRelationship( int classify, int disposition, int priority );
HSCRIPT ScriptGetVehicleEntity();
@ -462,6 +467,7 @@ public:
static void AllocateDefaultRelationships( );
static void SetDefaultRelationship( Class_T nClass, Class_T nClassTarget, Disposition_t nDisposition, int nPriority );
#ifdef MAPBASE
static bool DefaultRelationshipsLoaded();
static Disposition_t GetDefaultRelationshipDisposition( Class_T nClassSource, Class_T nClassTarget );
static int GetDefaultRelationshipPriority( Class_T nClassSource, Class_T nClassTarget );
int GetDefaultRelationshipPriority( Class_T nClassTarget );

View File

@ -2379,6 +2379,9 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities"
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" )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetGroundEntity, "GetGroundEntity", "Get the entity we're standing on." )
DEFINE_SCRIPTFUNC_NAMED( ScriptSetGroundEntity, "SetGroundEntity", "Set the entity we're standing on." )
DEFINE_SCRIPTFUNC_NAMED( ScriptFollowEntity, "FollowEntity", "Begin following the specified entity. This makes this entity non-solid, parents it to the target entity, and teleports it to the specified entity's origin. The second parameter is whether or not to use bonemerging while following." )
DEFINE_SCRIPTFUNC( StopFollowingEntity, "Stops following an entity if we're following one." )
DEFINE_SCRIPTFUNC( IsFollowingEntity, "Returns true if this entity is following another entity." )
@ -10297,8 +10300,9 @@ bool CBaseEntity::ScriptAddOutput( const char *pszOutputName, const char *pszTar
const char *CBaseEntity::ScriptGetKeyValue( const char *pszKeyName )
{
static char szValue[128];
GetKeyValue( pszKeyName, szValue, sizeof(szValue) );
if ( GetKeyValue( pszKeyName, szValue, sizeof(szValue) ) )
return szValue;
return NULL;
}
//-----------------------------------------------------------------------------

View File

@ -1446,6 +1446,11 @@ public:
CBaseEntity *GetGroundEntity( void );
CBaseEntity *GetGroundEntity( void ) const { return const_cast<CBaseEntity *>(this)->GetGroundEntity(); }
#ifdef MAPBASE_VSCRIPT
HSCRIPT ScriptGetGroundEntity();
void ScriptSetGroundEntity( HSCRIPT hGroundEnt );
#endif
// Gets the velocity we impart to a player standing on us
virtual void GetGroundVelocityToApply( Vector &vecGroundVel ) { vecGroundVel = vec3_origin; }
@ -1575,7 +1580,7 @@ public:
float flVolume, soundlevel_t iSoundlevel, int iFlags = 0, int iPitch = PITCH_NORM,
const Vector *pOrigin = NULL, const Vector *pDirection = NULL, bool bUpdatePositions = true, float soundtime = 0.0f
#ifdef MAPBASE
, int iSpecialDSP = 0, int iSpeakerIndex = 0 // Needed for env_microphone
, int iSpecialDSP = 0, int iSpeakerIndex = -1 // Needed for env_microphone
#endif
);

View File

@ -736,6 +736,12 @@ CBaseEntity *CGlobalEntityList::FindEntityCustomProcedural( CBaseEntity *pStartE
g_pScriptVM->ExecuteFunction( g_CustomProcedurals[i].hFunc, args, 5, &functionReturn, NULL, true );
if (pStartEntity && ToEnt( functionReturn.m_hScript ) == pStartEntity)
{
Warning( "WARNING: Custom procedural %s returned entity identical to start entity (%s), returning null\n", g_CustomProcedurals[i].szName, pStartEntity->GetDebugName() );
return NULL;
}
return ToEnt( functionReturn.m_hScript );
}
}

View File

@ -162,10 +162,10 @@ void CBaseFilter::InputSetField( inputdata_t& inputdata )
#ifdef MAPBASE_VSCRIPT
bool CBaseFilter::ScriptPassesFilter( HSCRIPT pCaller, HSCRIPT pEntity ) { return PassesFilter( ToEnt(pCaller), ToEnt(pEntity) ); }
bool CBaseFilter::ScriptPassesDamageFilter( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? PassesDamageFilter( ToEnt( pCaller ), *const_cast<const CTakeDamageInfo*>(HScriptToClass<CTakeDamageInfo>( pInfo )) ) : NULL; }
bool CBaseFilter::ScriptPassesFinalDamageFilter( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? PassesFinalDamageFilter( ToEnt( pCaller ), *const_cast<const CTakeDamageInfo*>(HScriptToClass<CTakeDamageInfo>( pInfo )) ) : NULL; }
bool CBaseFilter::ScriptBloodAllowed( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? BloodAllowed( ToEnt( pCaller ), *const_cast<const CTakeDamageInfo*>(HScriptToClass<CTakeDamageInfo>( pInfo )) ) : NULL; }
bool CBaseFilter::ScriptDamageMod( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? DamageMod( ToEnt( pCaller ), *HScriptToClass<CTakeDamageInfo>( pInfo ) ) : NULL; }
bool CBaseFilter::ScriptPassesDamageFilter( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? PassesDamageFilter( ToEnt( pCaller ), *const_cast<const CTakeDamageInfo*>(HScriptToClass<CTakeDamageInfo>( pInfo )) ) : false; }
bool CBaseFilter::ScriptPassesFinalDamageFilter( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? PassesFinalDamageFilter( ToEnt( pCaller ), *const_cast<const CTakeDamageInfo*>(HScriptToClass<CTakeDamageInfo>( pInfo )) ) : false; }
bool CBaseFilter::ScriptBloodAllowed( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? BloodAllowed( ToEnt( pCaller ), *const_cast<const CTakeDamageInfo*>(HScriptToClass<CTakeDamageInfo>( pInfo )) ) : false; }
bool CBaseFilter::ScriptDamageMod( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? DamageMod( ToEnt( pCaller ), *HScriptToClass<CTakeDamageInfo>( pInfo ) ) : false; }
#endif

View File

@ -190,6 +190,24 @@ int CAI_FuncTankBehavior::SelectSchedule()
return SCHED_IDLE_STAND;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CAI_FuncTankBehavior::ModifyOrAppendCriteria( AI_CriteriaSet &set )
{
BaseClass::ModifyOrAppendCriteria( set );
#ifdef MAPBASE
set.AppendCriteria( "ft_mounted", m_bMounted ? "1" : "0" );
if (m_hFuncTank)
{
set.AppendCriteria( "ft_classname", m_hFuncTank->GetClassname() );
m_hFuncTank->AppendContextToCriteria( set, "ft_" );
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : activity -

View File

@ -55,6 +55,8 @@ public:
bool CanManTank( CFuncTank *pTank, bool bForced );
#endif
void ModifyOrAppendCriteria( AI_CriteriaSet &set );
Activity NPC_TranslateActivity( Activity activity );
// Conditions:

View File

@ -35,6 +35,9 @@ CBugBaitSensor* GetBugBaitSensorList()
CBugBaitSensor::CBugBaitSensor( void )
{
g_BugBaitSensorList.Insert( this );
#ifdef MAPBASE
m_bUseRadius = true;
#endif
}
CBugBaitSensor::~CBugBaitSensor( void )
@ -50,10 +53,24 @@ BEGIN_DATADESC( CBugBaitSensor )
DEFINE_KEYFIELD( m_bEnabled, FIELD_BOOLEAN, "Enabled" ),
DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),
#ifdef MAPBASE
DEFINE_KEYFIELD( m_bUseRadius, FIELD_BOOLEAN, "useradius" ),
DEFINE_KEYFIELD( m_vecMins, FIELD_VECTOR, "bmins" ),
DEFINE_KEYFIELD( m_vecMaxs, FIELD_VECTOR, "bmaxs" ),
#endif
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
#ifdef MAPBASE
DEFINE_INPUTFUNC( FIELD_VOID, "EnableRadius", InputEnableRadius ),
DEFINE_INPUTFUNC( FIELD_VOID, "DisableRadius", InputDisableRadius ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetRadius", InputSetRadius ),
DEFINE_INPUTFUNC( FIELD_VECTOR, "SetMins", InputSetMins ),
DEFINE_INPUTFUNC( FIELD_VECTOR, "SetMaxs", InputSetMaxs ),
#endif
// Function Pointers
DEFINE_OUTPUT( m_OnBaited, "OnBaited" ),
@ -267,6 +284,9 @@ bool CGrenadeBugBait::ActivateBugbaitTargets( CBaseEntity *pOwner, Vector vecOri
continue;
//Make sure we're within range of the sensor
#ifdef MAPBASE
if ( pSensor->UsesRadius() ){
#endif
if ( pSensor->GetRadius() > (pSensor->GetAbsOrigin() - vecOrigin).Length() )
{
//Tell the sensor it's been hit
@ -279,6 +299,27 @@ bool CGrenadeBugBait::ActivateBugbaitTargets( CBaseEntity *pOwner, Vector vecOri
}
}
}
#ifdef MAPBASE
}
else{
Vector vMins = pSensor->GetAbsMins();
Vector vMaxs = pSensor->GetAbsMaxs();
bool inBox = ((vecOrigin.x >= vMins.x && vecOrigin.x <= vMaxs.x) &&
(vecOrigin.y >= vMins.y && vecOrigin.y <= vMaxs.y) &&
(vecOrigin.z >= vMins.z && vecOrigin.z <= vMaxs.z));
if ( inBox ){
//Tell the sensor it's been hit
if ( pSensor->Baited( pOwner ) )
{
//If we're suppressing the call to antlions, then don't make a bugbait sound
if ( pSensor->SuppressCall() )
{
suppressCall = true;
}
}
}
}
#endif
}
return suppressCall;

View File

@ -63,6 +63,28 @@ public:
m_bEnabled = !m_bEnabled;
}
#ifdef MAPBASE
void InputEnableRadius( inputdata_t &data ){
m_bUseRadius = true;
}
void InputDisableRadius( inputdata_t &data ){
m_bUseRadius = false;
}
void InputSetRadius( inputdata_t &data ){
m_flRadius = data.value.Int();
}
void InputSetMins( inputdata_t &data ){
data.value.Vector3D( m_vecMins );
}
void InputSetMaxs( inputdata_t &data ){
data.value.Vector3D( m_vecMaxs );
}
#endif
bool SuppressCall( void )
{
return ( HasSpawnFlags( SF_BUGBAIT_SUPPRESS_CALL ) );
@ -91,10 +113,28 @@ public:
return !m_bEnabled;
}
#ifdef MAPBASE
bool UsesRadius( void ) const {
return m_bUseRadius;
}
Vector GetAbsMins( void ) const {
return GetAbsOrigin() + m_vecMins;
}
Vector GetAbsMaxs( void ) const {
return GetAbsOrigin() + m_vecMaxs;
}
#endif
protected:
float m_flRadius;
bool m_bEnabled;
#ifdef MAPBASE
bool m_bUseRadius;
Vector m_vecMins;
Vector m_vecMaxs;
#endif
COutputEvent m_OnBaited;
public:

View File

@ -1814,7 +1814,7 @@ bool CHL2_Player::CommanderFindGoal( commandgoal_t *pGoal )
// Get either our +USE entity or the gravity gun entity
CBaseEntity *pHeldEntity = GetPlayerHeldEntity(this);
if ( !pHeldEntity )
PhysCannonGetHeldEntity( GetActiveWeapon() );
pHeldEntity = PhysCannonGetHeldEntity( GetActiveWeapon() );
CTraceFilterSkipTwoEntities filter( this, pHeldEntity, COLLISION_GROUP_INTERACTIVE_DEBRIS );
#else

View File

@ -159,6 +159,10 @@ ConVar zombie_decaymax( "zombie_decaymax", "0.4" );
ConVar zombie_ambushdist( "zombie_ambushdist", "16000" );
#ifdef MAPBASE
ConVar zombie_no_flinch_during_unique_anim( "zombie_no_flinch_during_unique_anim", "1", FCVAR_NONE, "Prevents zombies from flinching during actbusies and scripted sequences." );
#endif
//=========================================================
// For a couple of reasons, we keep a running count of how
// many zombies in the world are angry at any given time.
@ -1745,7 +1749,11 @@ void CNPC_BaseZombie::HandleAnimEvent( animevent_t *pEvent )
dmgInfo.SetDamagePosition( vecHeadCrabPosition );
#ifdef MAPBASE
ReleaseHeadcrab( vecHeadCrabPosition, vVelocity *iSpeed, true, false, true );
#else
ReleaseHeadcrab( EyePosition(), vVelocity * iSpeed, true, false, true );
#endif
GuessDamageForce( &dmgInfo, vVelocity, vecHeadCrabPosition, 0.5f );
TakeDamage( dmgInfo );
@ -1927,6 +1935,31 @@ void CNPC_BaseZombie::OnScheduleChange( void )
}
//---------------------------------------------------------
//---------------------------------------------------------
bool CNPC_BaseZombie::CanFlinch( void )
{
if (!BaseClass::CanFlinch())
return false;
#ifdef MAPBASE
if (zombie_no_flinch_during_unique_anim.GetBool())
{
// Don't flinch if currently playing actbusy animation (navigating to or from one is fine)
if (m_ActBusyBehavior.IsInsideActBusy())
return false;
// Don't flinch if currently playing scripted sequence (navigating to or from one is fine)
if (m_NPCState == NPC_STATE_SCRIPT && (IsCurSchedule( SCHED_SCRIPTED_WAIT, false ) || IsCurSchedule( SCHED_SCRIPTED_FACE, false )))
return false;
}
#endif
return true;
}
//---------------------------------------------------------
//---------------------------------------------------------
int CNPC_BaseZombie::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
@ -2435,6 +2468,20 @@ void CNPC_BaseZombie::RemoveHead( void )
}
//---------------------------------------------------------
//---------------------------------------------------------
void CNPC_BaseZombie::SetModel( const char *szModelName )
{
#ifdef MAPBASE
// Zombies setting the same model again is a problem when they should maintain their current sequence (e.g. during dynamic interactions)
if ( IsRunningDynamicInteraction() && GetModelIndex() != 0 && FStrEq( szModelName, STRING(GetModelName()) ) )
return;
#endif
BaseClass::SetModel( szModelName );
}
bool CNPC_BaseZombie::ShouldPlayFootstepMoan( void )
{
if( random->RandomInt( 1, zombie_stepfreq.GetInt() * s_iAngryZombies ) == 1 )
@ -2450,9 +2497,15 @@ bool CNPC_BaseZombie::ShouldPlayFootstepMoan( void )
#define CRAB_HULL_EXPAND 1.1f
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CNPC_BaseZombie::HeadcrabFits( CBaseAnimating *pCrab )
bool CNPC_BaseZombie::HeadcrabFits( CBaseAnimating *pCrab, const Vector *vecOrigin )
{
Vector vecSpawnLoc = pCrab->GetAbsOrigin();
Vector vecSpawnLoc;
#ifdef MAPBASE
if (vecOrigin)
vecSpawnLoc = *vecOrigin;
else
#endif
vecSpawnLoc = pCrab->GetAbsOrigin();
CTraceFilterSimpleList traceFilter( COLLISION_GROUP_NONE );
traceFilter.AddEntityToIgnore( pCrab );
@ -2535,7 +2588,12 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve
SetHeadcrabSpawnLocation( iCrabAttachment, pAnimatingGib );
}
#ifdef MAPBASE
// Server ragdolls don't have a valid origin on spawn, so we have to use the origin originally passed
if( !HeadcrabFits( pAnimatingGib, m_bForceServerRagdoll ? &vecOrigin : NULL ) )
#else
if( !HeadcrabFits(pAnimatingGib) )
#endif
{
UTIL_Remove(pGib);
return;
@ -2552,11 +2610,20 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve
if( UTIL_ShouldShowBlood(BLOOD_COLOR_YELLOW) )
{
UTIL_BloodImpact( pGib->WorldSpaceCenter(), Vector(0,0,1), BLOOD_COLOR_YELLOW, 1 );
Vector vecGibCenter;
#ifdef MAPBASE
// Server ragdolls don't have a valid origin on spawn, so we have to use the origin originally passed
if (m_bForceServerRagdoll)
vecGibCenter = vecOrigin;
else
#endif
vecGibCenter = pGib->WorldSpaceCenter();
UTIL_BloodImpact( vecGibCenter, Vector(0,0,1), BLOOD_COLOR_YELLOW, 1 );
for ( int i = 0 ; i < 3 ; i++ )
{
Vector vecSpot = pGib->WorldSpaceCenter();
Vector vecSpot = vecGibCenter;
vecSpot.x += random->RandomFloat( -8, 8 );
vecSpot.y += random->RandomFloat( -8, 8 );
@ -2590,6 +2657,9 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve
// Inherit some misc. properties
pCrab->m_bForceServerRagdoll = m_bForceServerRagdoll;
pCrab->m_iViewHideFlags = m_iViewHideFlags;
// Add response context for companion response (more reliable than checking for post-death zombie entity)
pCrab->AddContext( "from_zombie", "1", 2.0f );
#endif
// make me the crab's owner to avoid collision issues

View File

@ -151,6 +151,8 @@ public:
int OnTakeDamage_Alive( const CTakeDamageInfo &info );
virtual float GetReactionDelay( CBaseEntity *pEnemy ) { return 0.0; }
bool CanFlinch( void );
virtual int SelectSchedule ( void );
virtual int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode );
virtual void BuildScheduleTestBits( void );
@ -186,9 +188,10 @@ public:
// Headcrab releasing/breaking apart
void RemoveHead( void );
virtual void SetZombieModel( void ) { };
virtual void SetModel( const char *szModelName );
virtual void BecomeTorso( const Vector &vecTorsoForce, const Vector &vecLegsForce );
virtual bool CanBecomeLiveTorso() { return false; }
virtual bool HeadcrabFits( CBaseAnimating *pCrab );
virtual bool HeadcrabFits( CBaseAnimating *pCrab, const Vector *vecOrigin = NULL );
void ReleaseHeadcrab( const Vector &vecOrigin, const Vector &vecVelocity, bool fRemoveHead, bool fRagdollBody, bool fRagdollCrab = false );
void SetHeadcrabSpawnLocation( int iCrabAttachment, CBaseAnimating *pCrab );

View File

@ -1092,10 +1092,14 @@ void CNPC_Alyx::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &
//-----------------------------------------------------------------------------
void CNPC_Alyx::EnemyIgnited( CAI_BaseNPC *pVictim )
{
#ifdef MAPBASE
BaseClass::EnemyIgnited( pVictim );
#else
if ( FVisible( pVictim ) )
{
SpeakIfAllowed( TLK_ENEMY_BURNING );
}
#endif
}
//-----------------------------------------------------------------------------
@ -1252,6 +1256,7 @@ void CNPC_Alyx::DoCustomSpeechAI( void )
CBasePlayer *pPlayer = AI_GetSinglePlayer();
#ifndef MAPBASE // Ported to CNPC_PlayerCompanion
if ( HasCondition(COND_NEW_ENEMY) && GetEnemy() )
{
if ( GetEnemy()->Classify() == CLASS_HEADCRAB )
@ -1278,6 +1283,7 @@ void CNPC_Alyx::DoCustomSpeechAI( void )
}
}
}
#endif
// Darkness mode speech
ClearCondition( COND_ALYX_IN_DARK );
@ -1917,6 +1923,7 @@ int CNPC_Alyx::SelectSchedule( void )
//-----------------------------------------------------------------------------
int CNPC_Alyx::SelectScheduleDanger( void )
{
#ifndef MAPBASE
if( HasCondition( COND_HEAR_DANGER ) )
{
CSound *pSound;
@ -1929,6 +1936,7 @@ int CNPC_Alyx::SelectScheduleDanger( void )
SpeakIfAllowed( TLK_DANGER_ZOMBINE_GRENADE );
}
}
#endif
return BaseClass::SelectScheduleDanger();
}

View File

@ -412,7 +412,6 @@ ScriptHook_t CNPC_Citizen::g_Hook_SelectModel;
BEGIN_ENT_SCRIPTDESC( CNPC_Citizen, CAI_BaseActor, "npc_citizen from Half-Life 2" )
DEFINE_SCRIPTFUNC( IsMedic, "Returns true if this citizen is a medic." )
DEFINE_SCRIPTFUNC( IsAmmoResupplier, "Returns true if this citizen is an ammo resupplier." )
DEFINE_SCRIPTFUNC( CanHeal, "Returns true if this citizen is a medic or ammo resupplier currently able to heal/give ammo." )

View File

@ -47,6 +47,8 @@ ConVar npc_combine_protected_run( "npc_combine_protected_run", "0", FCVAR_NONE,
ConVar npc_combine_altfire_not_allies_only( "npc_combine_altfire_not_allies_only", "1", FCVAR_NONE, "Mapbase: Elites are normally only allowed to fire their alt-fire attack at the player and the player's allies; This allows elites to alt-fire at other enemies too." );
ConVar npc_combine_new_cover_behavior( "npc_combine_new_cover_behavior", "1", FCVAR_NONE, "Mapbase: Toggles small patches for parts of npc_combine AI related to soldiers failing to take cover. These patches are minimal and only change cases where npc_combine would otherwise look at an enemy without shooting or run up to the player to melee attack when they don't have to. Consult the Mapbase wiki for more information." );
ConVar npc_combine_fixed_shootpos( "npc_combine_fixed_shootpos", "0", FCVAR_NONE, "Mapbase: Toggles fixed Combine soldier shoot position." );
#endif
#define COMBINE_SKIN_DEFAULT 0
@ -2959,6 +2961,28 @@ Vector CNPC_Combine::Weapon_ShootPosition( )
// FIXME: rename this "estimated" since it's not based on animation
// FIXME: the orientation won't be correct when testing from arbitary positions for arbitary angles
#ifdef MAPBASE
// HACKHACK: This weapon shoot position code does not work properly when in close range, causing the aim
// to drift to the left as the enemy gets closer to it.
// This problem is usually bearable for regular combat, but it causes dynamic interaction yaw to be offset
// as well, preventing most from ever being triggered.
// Ideally, this should be fixed from the root cause, but due to the sensitivity of such a change, this is
// currently being tied to a cvar which is off by default.
//
// If the cvar is disabled but the soldier has valid interactions on its current enemy, then a separate hack
// will still attempt to correct the drift as the enemy gets closer.
if ( npc_combine_fixed_shootpos.GetBool() )
{
right *= 0.0f;
}
else if ( HasValidInteractionsOnCurrentEnemy() )
{
float flDistSqr = GetEnemy()->WorldSpaceCenter().DistToSqr( WorldSpaceCenter() );
if (flDistSqr < Square( 128.0f ))
right *= (flDistSqr / Square( 128.0f ));
}
#endif
if ( bStanding )
{
if( HasShotgun() )

View File

@ -35,6 +35,8 @@
#include "mapbase/GlobalStrings.h"
#include "world.h"
#include "vehicle_base.h"
#include "npc_headcrab.h"
#include "npc_BaseZombie.h"
#endif
ConVar ai_debug_readiness("ai_debug_readiness", "0" );
@ -640,6 +642,55 @@ void CNPC_PlayerCompanion::DoCustomSpeechAI( void )
{
SpeakIfAllowed( TLK_PLDEAD );
}
#ifdef MAPBASE
// Unique new enemy concepts ported from Alyx
// The casts have been changed to dynamic_cast due to the risk of non-CBaseHeadcrab/CNPC_BaseZombie enemies using those classes
if ( HasCondition(COND_NEW_ENEMY) && GetEnemy() )
{
int nClass = GetEnemy()->Classify();
if ( nClass == CLASS_HEADCRAB )
{
CBaseHeadcrab *pHC = dynamic_cast<CBaseHeadcrab*>(GetEnemy());
if ( pHC )
{
// If we see a headcrab for the first time as he's jumping at me, freak out!
if ( ( GetEnemy()->GetEnemy() == this ) && pHC->IsJumping() && gpGlobals->curtime - GetEnemies()->FirstTimeSeen(GetEnemy()) < 0.5 )
{
SpeakIfAllowed( "TLK_SPOTTED_INCOMING_HEADCRAB" );
}
else
{
// If we see a headcrab leaving a zombie that just died, mention it
// (Note that this is now a response context since some death types remove the zombie instantly)
int nContext = pHC->FindContextByName( "from_zombie" );
if ( nContext > -1 && !ContextExpired( nContext ) ) // pHC->GetOwnerEntity() && ( pHC->GetOwnerEntity()->Classify() == CLASS_ZOMBIE ) && !pHC->GetOwnerEntity()->IsAlive()
{
SpeakIfAllowed( "TLK_SPOTTED_HEADCRAB_LEAVING_ZOMBIE" );
}
}
}
}
else if ( nClass == CLASS_ZOMBIE )
{
CNPC_BaseZombie *pZombie = dynamic_cast<CNPC_BaseZombie*>(GetEnemy());
// If we see a zombie getting up, mention it
if ( pZombie && pZombie->IsGettingUp() )
{
SpeakIfAllowed( "TLK_SPOTTED_ZOMBIE_WAKEUP" );
}
}
if ( gpGlobals->curtime - GetEnemies()->TimeAtFirstHand( GetEnemy() ) <= 1.0f && nClass != CLASS_BULLSEYE )
{
// New concept which did not originate from Alyx, but is in the same category as the above concepts.
// This is meant to be used when a new enemy enters the arena while combat is already in progress.
// (Note that this can still trigger when combat begins, but unlike TLK_STARTCOMBAT, it has no delay
// between combat engagements.)
SpeakIfAllowed( TLK_NEW_ENEMY );
}
}
#endif
}
//-----------------------------------------------------------------------------
@ -910,8 +961,21 @@ int CNPC_PlayerCompanion::SelectScheduleDanger()
if ( pSound && (pSound->m_iType & SOUND_DANGER) )
{
#ifdef MAPBASE
if ( pSound->SoundChannel() == SOUNDENT_CHANNEL_ZOMBINE_GRENADE )
{
SetSpeechTarget( pSound->m_hOwner );
SpeakIfAllowed( TLK_DANGER_ZOMBINE_GRENADE );
}
else if (!(pSound->SoundContext() & (SOUND_CONTEXT_MORTAR | SOUND_CONTEXT_FROM_SNIPER)) || IsOkToCombatSpeak())
{
SetSpeechTarget( pSound->m_hOwner );
SpeakIfAllowed( TLK_DANGER );
}
#else
if ( !(pSound->SoundContext() & (SOUND_CONTEXT_MORTAR|SOUND_CONTEXT_FROM_SNIPER)) || IsOkToCombatSpeak() )
SpeakIfAllowed( TLK_DANGER );
#endif
if ( HasCondition( COND_PC_SAFE_FROM_MORTAR ) )
{
@ -4309,6 +4373,20 @@ void CNPC_PlayerCompanion::Event_KilledOther( CBaseEntity *pVictim, const CTakeD
}
}
//-----------------------------------------------------------------------------
// Purpose: Called by enemy NPC's when they are ignited
// Input : pVictim - entity that was ignited
//-----------------------------------------------------------------------------
void CNPC_PlayerCompanion::EnemyIgnited( CAI_BaseNPC *pVictim )
{
BaseClass::EnemyIgnited( pVictim );
if ( FVisible( pVictim ) )
{
SpeakIfAllowed( TLK_ENEMY_BURNING );
}
}
//-----------------------------------------------------------------------------
// Purpose: Handles custom combat speech stuff ported from Alyx.
//-----------------------------------------------------------------------------
@ -4376,6 +4454,21 @@ void CNPC_PlayerCompanion::DoCustomCombatAI( void )
{
SpeakIfAllowed( TLK_MANY_ENEMIES );
}
// If we're not currently attacking or vulnerable, try speaking
else if ( gpGlobals->curtime - GetLastAttackTime() > 1.0f && (!HasCondition( COND_SEE_ENEMY ) || IsCurSchedule( SCHED_RELOAD ) || IsCurSchedule( SCHED_HIDE_AND_RELOAD )) )
{
int chance = ( IsMoving() ) ? 20 : 3;
if ( ShouldSpeakRandom( TLK_COMBAT_IDLE, chance ) )
{
AI_CriteriaSet modifiers;
modifiers.AppendCriteria( "in_cover", HasMemory( bits_MEMORY_INCOVER ) ? "1" : "0" );
modifiers.AppendCriteria( "lastseenenemy", UTIL_VarArgs( "%f", gpGlobals->curtime - GetEnemyLastTimeSeen() ) );
SpeakIfAllowed( TLK_COMBAT_IDLE, modifiers );
}
}
}
#endif

View File

@ -242,6 +242,7 @@ public:
virtual void Event_Killed( const CTakeDamageInfo &info );
virtual void Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info );
virtual void EnemyIgnited( CAI_BaseNPC *pVictim );
virtual void DoCustomCombatAI( void );
#endif

View File

@ -121,6 +121,33 @@ acttable_t CWeaponAnnabelle::m_acttable[] =
{ ACT_RELOAD_LOW, ACT_RELOAD_ANNABELLE_LOW, false },
{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_ANNABELLE, false },
// Readiness activities (not aiming)
{ ACT_IDLE_RELAXED, ACT_IDLE_AR2_RELAXED, false },//never aims
{ ACT_IDLE_STIMULATED, ACT_IDLE_AR2_STIMULATED, false },
{ ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_AR2, false },//always aims
{ ACT_WALK_RELAXED, ACT_WALK_AR2_RELAXED, false },//never aims
{ ACT_WALK_STIMULATED, ACT_WALK_AR2_STIMULATED, false },
{ ACT_WALK_AGITATED, ACT_WALK_AIM_AR2, false },//always aims
{ ACT_RUN_RELAXED, ACT_RUN_AR2_RELAXED, false },//never aims
{ ACT_RUN_STIMULATED, ACT_RUN_AR2_STIMULATED, false },
{ ACT_RUN_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims
// Readiness activities (aiming)
{ ACT_IDLE_AIM_RELAXED, ACT_IDLE_AR2_RELAXED, false },//never aims
{ ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_AR2_STIMULATED, false },
{ ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_AR2, false },//always aims
{ ACT_WALK_AIM_RELAXED, ACT_WALK_AR2_RELAXED, false },//never aims
{ ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_AR2_STIMULATED, false },
{ ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_AR2, false },//always aims
{ ACT_RUN_AIM_RELAXED, ACT_RUN_AR2_RELAXED, false },//never aims
{ ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_AR2_STIMULATED, false },
{ ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims
//End readiness activities
{ ACT_ARM, ACT_ARM_RIFLE, true },
{ ACT_DISARM, ACT_DISARM_RIFLE, true },
#else
@ -143,6 +170,13 @@ acttable_t CWeaponAnnabelle::m_acttable[] =
{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, false },
#endif
#if EXPANDED_HL2_COVER_ACTIVITIES
{ ACT_COVER_WALL_R, ACT_COVER_WALL_R_RIFLE, false },
{ ACT_COVER_WALL_L, ACT_COVER_WALL_L_RIFLE, false },
{ ACT_COVER_WALL_LOW_R, ACT_COVER_WALL_LOW_R_RIFLE, false },
{ ACT_COVER_WALL_LOW_L, ACT_COVER_WALL_LOW_L_RIFLE, false },
#endif
#ifdef MAPBASE
// HL2:DM activities (for third-person animations in SP)
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_AR2, false },
@ -161,6 +195,18 @@ acttable_t CWeaponAnnabelle::m_acttable[] =
IMPLEMENT_ACTTABLE(CWeaponAnnabelle);
#ifdef MAPBASE
acttable_t* GetAnnabelleActtable()
{
return CWeaponAnnabelle::m_acttable;
}
int GetAnnabelleActtableCount()
{
return ARRAYSIZE(CWeaponAnnabelle::m_acttable);
}
#endif // MAPBASE
void CWeaponAnnabelle::Precache( void )
{
CBaseCombatWeapon::Precache();

View File

@ -346,7 +346,7 @@ void CWeaponAR2::DelayedAttack( void )
pOwner->SnapEyeAngles( angles );
pOwner->ViewPunch( QAngle( random->RandomInt( -8, -12 ), random->RandomInt( 1, 2 ), 0 ) );
pOwner->ViewPunch( QAngle( random->RandomInt( -12, -8 ), random->RandomInt( 1, 2 ), 0 ) );
// Decrease ammo
pOwner->RemoveAmmo( 1, m_iSecondaryAmmoType );

View File

@ -762,6 +762,16 @@ acttable_t CWeaponCrossbow::m_acttable[] =
};
IMPLEMENT_ACTTABLE(CWeaponCrossbow);
acttable_t* GetCrossbowActtable()
{
return CWeaponCrossbow::m_acttable;
}
int GetCrossbowActtableCount()
{
return ARRAYSIZE(CWeaponCrossbow::m_acttable);
}
#endif
//-----------------------------------------------------------------------------

View File

@ -68,6 +68,10 @@ ConVar player_throwforce( "player_throwforce", "1000" );
ConVar physcannon_dmg_glass( "physcannon_dmg_glass", "15" );
ConVar physcannon_right_turrets( "physcannon_right_turrets", "0" );
#ifdef MAPBASE
ConVar sv_player_enable_propsprint("sv_player_enable_propsprint", "0", FCVAR_NONE, "If enabled, allows the player to sprint while holding a physics object" );
ConVar sv_player_enable_gravgun_sprint("sv_player_enable_gravgun_sprint", "0", FCVAR_NONE, "Enables the player to sprint while holding a phys. object with the gravity gun" );
#endif
extern ConVar hl2_normspeed;
extern ConVar hl2_walkspeed;
@ -1043,10 +1047,20 @@ void CPlayerPickupController::Init( CBasePlayer *pPlayer, CBaseEntity *pObject )
CHL2_Player *pOwner = (CHL2_Player *)ToBasePlayer( pPlayer );
if ( pOwner )
{
#ifndef MAPBASE
pOwner->EnableSprint( false );
#else
if ( sv_player_enable_propsprint.GetBool() == false )
{
pOwner->EnableSprint( false );
}
else
{
pOwner->EnableSprint( true );
}
#endif
}
// If the target is debris, convert it to non-debris
if ( pObject->GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
{
@ -1102,10 +1116,17 @@ void CPlayerPickupController::Shutdown( bool bThrown )
if ( m_pPlayer )
{
CHL2_Player *pOwner = (CHL2_Player *)ToBasePlayer( m_pPlayer );
#ifndef MAPBASE
if ( pOwner )
{
pOwner->EnableSprint( true );
}
#else
if ( pOwner && sv_player_enable_propsprint.GetBool() == false )
{
pOwner->EnableSprint( true );
}
#endif
m_pPlayer->SetUseEntity( NULL );
if ( m_pPlayer->GetActiveWeapon() )
@ -2497,6 +2518,7 @@ bool CWeaponPhysCannon::AttachObject( CBaseEntity *pObject, const Vector &vPosit
// NVNT set the players constant force to simulate holding mass
HapticSetConstantForce(pOwner,clamp(m_grabController.GetLoadWeight()*0.05,1,5)*Vector(0,-1,0));
#endif
#ifndef MAPBASE
pOwner->EnableSprint( false );
float loadWeight = ( 1.0f - GetLoadPercentage() );
@ -2504,6 +2526,22 @@ bool CWeaponPhysCannon::AttachObject( CBaseEntity *pObject, const Vector &vPosit
//Msg( "Load perc: %f -- Movement speed: %f/%f\n", loadWeight, maxSpeed, hl2_normspeed.GetFloat() );
pOwner->SetMaxSpeed( maxSpeed );
#else
if ( sv_player_enable_gravgun_sprint.GetBool() == false )
{
pOwner->EnableSprint( false );
float loadWeight = ( 1.0f - GetLoadPercentage() );
float maxSpeed = hl2_walkspeed.GetFloat() + ( ( hl2_normspeed.GetFloat() - hl2_walkspeed.GetFloat() ) * loadWeight );
//Msg( "Load perc: %f -- Movement speed: %f/%f\n", loadWeight, maxSpeed, hl2_normspeed.GetFloat() );
pOwner->SetMaxSpeed( maxSpeed );
}
else
{
pOwner->EnableSprint( true );
}
#endif
}
// Don't drop again for a slight delay, in case they were pulling objects near them
@ -2950,9 +2988,17 @@ void CWeaponPhysCannon::DetachObject( bool playSound, bool wasLaunched )
CHL2_Player *pOwner = (CHL2_Player *)ToBasePlayer( GetOwner() );
if( pOwner != NULL )
{
#ifndef MAPBASE
pOwner->EnableSprint( true );
pOwner->SetMaxSpeed( hl2_normspeed.GetFloat() );
#else
if (sv_player_enable_gravgun_sprint.GetBool() == false)
{
pOwner->EnableSprint( true );
pOwner->SetMaxSpeed( hl2_normspeed.GetFloat() );
}
#endif
if( wasLaunched )
{
pOwner->RumbleEffect( RUMBLE_357, 0, RUMBLE_FLAG_RESTART );

View File

@ -526,6 +526,10 @@ bool CWeaponPistol::Reload( void )
return fRet;
}
#ifdef MAPBASE
ConVar weapon_pistol_upwards_viewkick( "weapon_pistol_upwards_viewkick", "0" );
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
@ -538,7 +542,11 @@ void CWeaponPistol::AddViewKick( void )
QAngle viewPunch;
#ifdef MAPBASE
viewPunch.x = weapon_pistol_upwards_viewkick.GetBool() ? random->RandomFloat( -0.5f, -0.25f ) : random->RandomFloat( 0.25f, 0.5f );
#else
viewPunch.x = random->RandomFloat( 0.25f, 0.5f );
#endif
viewPunch.y = random->RandomFloat( -.6f, .6f );
viewPunch.z = 0.0f;

View File

@ -3112,7 +3112,7 @@ void CLogicBranch::UpdateOnRemove()
CBaseEntity *pEntity = m_Listeners.Element( i ).Get();
if ( pEntity )
{
g_EventQueue.AddEvent( this, "_OnLogicBranchRemoved", 0, this, this );
g_EventQueue.AddEvent( pEntity, "_OnLogicBranchRemoved", 0, this, this );
}
}
@ -4059,7 +4059,7 @@ void CLogicFormat::FormatString(const char *szStringToFormat, char *szOutput, in
curparam = atoi(szToken);
if (curparam < MAX_LOGIC_FORMAT_PARAMETERS /*&& szParameters[curparam] != NULL*/) //if (curparam < MAX_FORMAT_PARAMETERS)
{
Q_snprintf(szFormatted, sizeof(szFormatted), "%s%s", szFormatted, szParameters[curparam]);
Q_strncat(szFormatted, szParameters[curparam], sizeof(szFormatted));
}
else
{
@ -4068,8 +4068,8 @@ void CLogicFormat::FormatString(const char *szStringToFormat, char *szOutput, in
// This might not be the best way to do this, but
// reaching it is supposed to be the result of a mistake anyway.
m_iszBackupParameter != NULL_STRING ?
Q_snprintf(szFormatted, sizeof(szFormatted), "%s%s", szFormatted, STRING(m_iszBackupParameter)) :
Q_snprintf(szFormatted, sizeof(szFormatted), "%s<null>", szFormatted);
Q_strncat( szFormatted, STRING(m_iszBackupParameter), sizeof( szFormatted ) ) :
Q_strncat( szFormatted, "<null>", sizeof( szFormatted ) );
}
inparam = false;
@ -4077,7 +4077,7 @@ void CLogicFormat::FormatString(const char *szStringToFormat, char *szOutput, in
}
else
{
Q_snprintf(szFormatted, sizeof(szFormatted), "%s%s", szFormatted, szToken);
Q_strncat( szFormatted, szToken, sizeof( szFormatted ) );
inparam = true;
szToken = strtok(NULL, "}");

View File

@ -23,6 +23,7 @@
#include "hl2_gamerules.h"
#include "weapon_physcannon.h"
#include "globalstate.h"
#include "ai_hint.h"
#define COMBINE_AE_GREN_TOSS ( 7 )
@ -138,6 +139,8 @@ public:
void ClearAttackConditions( void );
bool FValidateHintType( CAI_Hint *pHint );
Vector GetAltFireTarget() { return m_vecAltFireTarget; }
virtual bool CanAltFireEnemy( bool bUseFreeKnowledge );
void DelayAltFireAttack( float flDelay );
@ -558,6 +561,29 @@ bool CAI_GrenadeUser<BASE_NPC>::CanThrowGrenade( const Vector &vecTarget )
}
}
CHintCriteria hintCriteria;
hintCriteria.SetHintType( HINT_TACTICAL_GRENADE_THROW );
hintCriteria.SetFlag( bits_HINT_NPC_IN_NODE_FOV );
hintCriteria.SetGroup( this->GetHintGroup() );
hintCriteria.AddIncludePosition( this->GetAbsOrigin(), 1024 );
if (this->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)
hintCriteria.SetFlag( bits_HINT_NODE_REPORT_FAILURES );
// If there's a grenade throw hint nearby, try using it
CAI_Hint *pHint = CAI_HintManager::FindHint( this, vecTarget, hintCriteria );
if ( pHint )
{
if ( CheckCanThrowGrenade( pHint->GetAbsOrigin() ) )
{
return true;
}
else
{
DevMsg( this, "Unable to throw grenade at hint %s\n", pHint->GetDebugName() );
}
}
return CheckCanThrowGrenade( vecTarget );
}
@ -636,6 +662,18 @@ void CAI_GrenadeUser<BASE_NPC>::ClearAttackConditions()
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
template <class BASE_NPC>
bool CAI_GrenadeUser<BASE_NPC>::FValidateHintType( CAI_Hint *pHint )
{
if ( pHint->HintType() == HINT_TACTICAL_GRENADE_THROW )
return true;
return BaseClass::FValidateHintType( pHint );
}
//-----------------------------------------------------------------------------
// Purpose: Drops grenades and alt-fire items on death. Based on code from npc_combines.cpp and npc_combine.cpp
//-----------------------------------------------------------------------------

View File

@ -0,0 +1,208 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: The central manager of the custom weapons system.
//
// Author: Peter Covington (petercov@outlook.com)
//
//==================================================================================//
#include "cbase.h"
#include "custom_weapon_factory.h"
#define GENERIC_MANIFEST_FILE "scripts/mapbase_default_manifest.txt"
#define AUTOLOADED_MANIFEST_FILE UTIL_VarArgs("maps/%s_manifest.txt", STRING(gpGlobals->mapname))
#define GLOBAL_WEAPONS_MANIFEST "scripts/custom_weapon_manifest.txt"
extern ConVar mapbase_load_default_manifest;
IMPLEMENT_PRIVATE_SYMBOLTYPE(CustomWeaponSymbol);
CCustomWeaponSystem::CCustomWeaponSystem() : CAutoGameSystem("CustomWeaponFactorySystem")
{
}
void CCustomWeaponSystem::LevelInitPreEntity()
{
LoadCustomWeaponsManifest(GLOBAL_WEAPONS_MANIFEST);
// Check for a generic "mapname_manifest.txt" file and load it.
if (filesystem->FileExists(AUTOLOADED_MANIFEST_FILE, "GAME"))
{
AddManifestFile(AUTOLOADED_MANIFEST_FILE);
}
else
{
// Load the generic script instead.
ParseGenericManifest();
}
}
// Get a generic, hardcoded manifest with hardcoded names.
void CCustomWeaponSystem::ParseGenericManifest()
{
if (!mapbase_load_default_manifest.GetBool())
return;
KeyValues* pKV = new KeyValues("DefaultManifest");
pKV->LoadFromFile(filesystem, GENERIC_MANIFEST_FILE);
AddManifestFile(pKV/*, true*/);
pKV->deleteThis();
}
void CCustomWeaponSystem::AddManifestFile(const char* file)
{
KeyValues* pKV = new KeyValues(file);
if (!pKV->LoadFromFile(filesystem, file))
{
Warning("Mapbase Manifest: \"%s\" is unreadable or missing (can't load KV, check for syntax errors)\n", file);
pKV->deleteThis();
return;
}
CGMsg(1, CON_GROUP_MAPBASE_MISC, "===== Mapbase Manifest: Loading manifest file %s =====\n", file);
AddManifestFile(pKV, false);
CGMsg(1, CON_GROUP_MAPBASE_MISC, "==============================================================================\n");
pKV->deleteThis();
}
void CCustomWeaponSystem::AddManifestFile(KeyValues* pKV, bool bDontWarn)
{
KeyValues* pKey = pKV->FindKey("weapons");
if (pKey)
{
char value[MAX_PATH];
value[0] = '\0';
// Parse %mapname%, etc.
bool inparam = false;
CUtlStringList outStrings;
V_SplitString(pKey->GetString(), "%", outStrings);
for (int i = 0; i < outStrings.Count(); i++)
{
if (inparam)
{
if (FStrEq(outStrings[i], "mapname"))
{
Q_strncat(value, STRING(gpGlobals->mapname), sizeof(value));
}
else if (FStrEq(outStrings[i], "language"))
{
#ifdef CLIENT_DLL
char uilanguage[64];
engine->GetUILanguage(uilanguage, sizeof(uilanguage));
Q_strncat(value, uilanguage, sizeof(value));
#else
// Give up, use English
Q_strncat(value, "english", sizeof(value));
#endif
}
}
else
{
Q_strncat(value, outStrings[i], sizeof(value));
}
inparam = !inparam;
}
outStrings.PurgeAndDeleteElements();
bDontWarn = pKV->GetBool("NoErrors", bDontWarn);
LoadCustomWeaponsManifest(value, bDontWarn);
}
}
#define Factory CustomWeaponsFactoryDictionary()
void CCustomWeaponSystem::LoadCustomWeaponsManifest(const char* file, bool bDontWarn)
{
KeyValuesAD pKV("weapons_manifest");
if (pKV->LoadFromFile(filesystem, file, "GAME"))
{
for (KeyValues *pkvWeapon = pKV->GetFirstValue(); pkvWeapon != nullptr; pkvWeapon = pkvWeapon->GetNextValue())
{
const char* pszClassname = pkvWeapon->GetName();
KeyValuesAD pkvWeaponScript("WeaponData");
if (pkvWeaponScript->LoadFromFile(filesystem, pkvWeapon->GetString(), "GAME"))
{
const char* pszFactory = pkvWeaponScript->GetString("custom_factory", nullptr);
unsigned short FactoryIndex = Factory.Find(pszFactory);
if (Factory.IsValidIndex(FactoryIndex))
{
auto* pFactory = Factory.Element(FactoryIndex);
const void* pData = pFactory->ParseDataFromWeaponFile(pkvWeaponScript);
if (!pData)
continue;
unsigned short ClassIndex = m_ClassFactories.Find(pszClassname);
if (!m_ClassFactories.IsValidIndex(ClassIndex))
{
ClassIndex = m_ClassFactories.Insert(pszClassname);
m_ClassFactories[ClassIndex].pOldFactory = EntityFactoryDictionary()->FindFactory(pszClassname);
}
else
{
Assert(m_ClassFactories[ClassIndex].pNewFactory);
Assert(m_ClassFactories[ClassIndex].pData);
m_ClassFactories[ClassIndex].pNewFactory->ReleaseData(m_ClassFactories[ClassIndex].pData);
}
m_ClassFactories[ClassIndex].sDataFile = pkvWeapon->GetString();
m_ClassFactories[ClassIndex].pNewFactory = pFactory;
m_ClassFactories[ClassIndex].pData = pData;
EntityFactoryDictionary()->UninstallFactory(pszClassname);
EntityFactoryDictionary()->InstallFactory(m_ClassFactories[ClassIndex].pNewFactory, pszClassname);
}
}
}
}
}
#undef Factory
void CCustomWeaponSystem::LevelShutdownPostEntity()
{
for (unsigned short i = 0; i < m_ClassFactories.Count(); i++)
{
EntityFactoryDictionary()->UninstallFactory(m_ClassFactories.GetElementName(i));
const CustomClassName_t& entry = m_ClassFactories.Element(i);
if (entry.pOldFactory)
EntityFactoryDictionary()->InstallFactory(entry.pOldFactory, m_ClassFactories.GetElementName(i));
Assert(entry.pData);
entry.pNewFactory->ReleaseData(entry.pData);
}
m_ClassFactories.Purge();
g_CustomWeaponSymbolSymbolTable.RemoveAll();
}
void CCustomWeaponSystem::ParseWeapon(CBaseCombatWeapon* pWeapon, const char* pClassName)
{
ICustomWeapon* pCustom = dynamic_cast<ICustomWeapon*> (pWeapon);
if (!pCustom)
return;
unsigned short i = m_ClassFactories.Find(pClassName);
if (!m_ClassFactories.IsValidIndex(i))
return;
pCustom->InitCustomWeaponFromData(m_ClassFactories[i].pData, m_ClassFactories[i].sDataFile.String());
}
CUtlDict<ICustomWeaponDataLoader*, unsigned short>& CustomWeaponsFactoryDictionary()
{
static CUtlDict<ICustomWeaponDataLoader*, unsigned short> dict;
return dict;
}
static CCustomWeaponSystem g_CustomWeaponsSystem;
CCustomWeaponSystem* CustomWeaponSystem()
{
return &g_CustomWeaponsSystem;
}

View File

@ -0,0 +1,117 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: See custom_weapon_factory.cpp
//
// Author: Peter Covington (petercov@outlook.com)
//
//==================================================================================//
#ifndef CUSTOM_WEAPON_FACTORY_H
#define CUSTOM_WEAPON_FACTORY_H
#pragma once
#include "utldict.h"
#include "utlsymbol.h"
DECLARE_PRIVATE_SYMBOLTYPE(CustomWeaponSymbol);
class ICustomWeaponDataLoader : public IEntityFactory
{
public:
virtual const void* ParseDataFromWeaponFile(KeyValues* pKV) const = 0;
virtual void ReleaseData(const void* pData) const = 0;
};
class ICustomWeapon
{
public:
virtual void InitCustomWeaponFromData(const void* pData, const char *pszWeaponScript) = 0;
};
class CCustomWeaponSystem : public CAutoGameSystem
{
public:
CCustomWeaponSystem();
// Level init, shutdown
virtual void LevelInitPreEntity();
virtual void LevelShutdownPostEntity();
void ParseWeapon(CBaseCombatWeapon* pWeapon, const char* pClassName);
private:
void ParseGenericManifest();
void AddManifestFile(const char* file);
void AddManifestFile(KeyValues* pKV, bool bDontWarn = false);
void LoadCustomWeaponsManifest(const char* file, bool bDontWarn = false);
typedef struct CustomClassName_s
{
CustomWeaponSymbol sDataFile;
ICustomWeaponDataLoader* pNewFactory;
IEntityFactory* pOldFactory;
const void* pData;
} CustomClassName_t;
CUtlDict<CustomClassName_t, unsigned short> m_ClassFactories;
};
CCustomWeaponSystem* CustomWeaponSystem();
CUtlDict< ICustomWeaponDataLoader*, unsigned short >& CustomWeaponsFactoryDictionary();
template <class T>
class CCustomWeaponEntityFactoryBase : public ICustomWeaponDataLoader
{
public:
CCustomWeaponEntityFactoryBase(const char* pFactoryClass)
{
CustomWeaponsFactoryDictionary().Insert(pFactoryClass, this);
}
IServerNetworkable* Create(const char* pClassName)
{
T* pEnt = _CreateEntityTemplate((T*)NULL, pClassName);
CustomWeaponSystem()->ParseWeapon(pEnt, pClassName);
return pEnt->NetworkProp();
}
void Destroy(IServerNetworkable* pNetworkable)
{
if (pNetworkable)
{
pNetworkable->Release();
}
}
virtual size_t GetEntitySize()
{
return sizeof(T);
}
};
template <class Entity, class Data>
class CDefaultCustomWeaponEntityFactory : public CCustomWeaponEntityFactoryBase<Entity>
{
public:
CDefaultCustomWeaponEntityFactory(const char *pFactoryClass) : CCustomWeaponEntityFactoryBase<Entity>(pFactoryClass)
{}
virtual const void* ParseDataFromWeaponFile(KeyValues* pKV) const
{
Data* pData = new Data;
if (pData && pData->Parse(pKV))
return pData;
delete pData;
return nullptr;
}
virtual void ReleaseData(const void* pData) const
{
delete pData;
}
};
#define DEFINE_CUSTOM_WEAPON_FACTORY(factoryName, DLLClassName, DataStruct) \
static CDefaultCustomWeaponEntityFactory<DLLClassName, DataStruct> custom_weapon_##factoryName##_factory( #factoryName );
#endif // !CUSTOM_WEAPON_FACTORY_H

File diff suppressed because it is too large Load Diff

View File

@ -447,7 +447,8 @@ void CGameText::SetText( const char* pszStr )
for (int i = 1; i < vecLines.Count(); i++)
{
Q_snprintf( szMsg, sizeof( szMsg ), "%s\n%s", szMsg, vecLines[i] );
Q_strncat( szMsg, "\n", sizeof( szMsg ) );
Q_strncat( szMsg, vecLines[i], sizeof( szMsg ) );
}
m_iszMessage = AllocPooledString( szMsg );
}

View File

@ -1557,6 +1557,16 @@ CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, con
pRagdoll->CollisionProp()->SetCollisionBounds( mins, maxs );
#ifdef MAPBASE
// If this was a NPC running a dynamic interaction, disable collisions with the interaction partner
if (pAnimating->IsNPC() /*&& pAnimating->MyNPCPointer()->IsRunningDynamicInteraction()*/)
{
CAI_BaseNPC *pNPC = pAnimating->MyNPCPointer();
if (pNPC->GetInteractionPartner() && pNPC->GetInteractionPartner()->VPhysicsGetObject())
{
PhysDisableEntityCollisions( pRagdoll, pNPC->GetInteractionPartner() );
}
}
variant_t variant;
variant.SetEntity(pRagdoll);
pAnimating->FireNamedOutput("OnServerRagdoll", variant, pRagdoll, pAnimating);

View File

@ -7021,7 +7021,7 @@ bool CBasePlayer::BumpWeapon( CBaseCombatWeapon *pWeapon )
{
//Weapon_EquipAmmoOnly( pWeapon );
// I'm too lazy to make my own version of Weapon_EquipAmmoOnly that doesn't check if we already have the weapon first
// Weapon_EquipAmmoOnly checks the array again, which isn't necessary here
int primaryGiven = (pWeapon->UsesClipsForAmmo1()) ? pWeapon->m_iClip1 : pWeapon->GetPrimaryAmmoCount();
int secondaryGiven = (pWeapon->UsesClipsForAmmo2()) ? pWeapon->m_iClip2 : pWeapon->GetSecondaryAmmoCount();
@ -7734,6 +7734,93 @@ void CBasePlayer::ResetAutoaim( void )
m_fOnTarget = false;
}
#ifdef MAPBASE
ConVar player_debug_probable_aim_target( "player_debug_probable_aim_target", "0", FCVAR_CHEAT, "" );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseEntity *CBasePlayer::GetProbableAimTarget( const Vector &vecSrc, const Vector &vecDir )
{
trace_t tr;
CBaseEntity *pIgnore = NULL;
if (IsInAVehicle())
pIgnore = GetVehicleEntity();
CTraceFilterSkipTwoEntities traceFilter( this, pIgnore, COLLISION_GROUP_NONE );
// Based on dot product and distance
// If we aim directly at something, only return it if there's not a larger entity slightly off-center
// Should be weighted based on whether an entity is a NPC, etc.
CBaseEntity *pBestEnt = NULL;
float flBestWeight = 0.0f;
for (CBaseEntity *pEntity = UTIL_EntitiesInPVS( this, NULL ); pEntity; pEntity = UTIL_EntitiesInPVS( this, pEntity ))
{
// Combat characters can be unviewable if they just died
if (!pEntity->IsViewable() && !pEntity->IsCombatCharacter())
continue;
if (pEntity == this || pEntity->GetMoveParent() == this || pEntity == GetVehicleEntity())
continue;
Vector vecEntDir = (pEntity->EyePosition() - vecSrc);
float flDot = DotProduct( vecEntDir.Normalized(), vecDir);
if (flDot < m_flFieldOfView)
continue;
// Make sure we can see it
UTIL_TraceLine( vecSrc, pEntity->EyePosition(), MASK_SHOT, &traceFilter, &tr );
if (tr.m_pEnt != pEntity)
{
if (pEntity->IsCombatCharacter())
{
// Trace between centers as well just in case our eyes are blocked
UTIL_TraceLine( WorldSpaceCenter(), pEntity->WorldSpaceCenter(), MASK_SHOT, &traceFilter, &tr );
if (tr.m_pEnt != pEntity)
continue;
}
else
continue;
}
float flWeight = flDot - (vecEntDir.LengthSqr() / Square( 2048.0f ));
if (pEntity->IsCombatCharacter())
{
// Hostile NPCs are more likely targets
if (IRelationType( pEntity ) <= D_FR)
flWeight += 0.5f;
}
else if (pEntity->GetFlags() & FL_AIMTARGET)
{
// FL_AIMTARGET is often used for props like explosive barrels
flWeight += 0.25f;
}
if (player_debug_probable_aim_target.GetBool())
{
float flWeightClamped = 1.0f - RemapValClamped( flWeight, -2.0f, 2.0f, 0.0f, 1.0f );
pEntity->EntityText( 0, UTIL_VarArgs( "%f", flWeight ), 2.0f, flWeightClamped * 255.0f, 255.0f, flWeightClamped * 255.0f, 255 );
}
if (flWeight > flBestWeight)
{
pBestEnt = pEntity;
flBestWeight = flWeight;
}
}
if (player_debug_probable_aim_target.GetBool())
{
Msg( "Best probable aim target is %s\n", pBestEnt->GetDebugName() );
NDebugOverlay::EntityBounds( pBestEnt, 255, 100, 0, 0, 2.0f );
}
return pBestEnt;
}
#endif
// ==========================================================================
// > Weapon stuff
// ==========================================================================

View File

@ -608,6 +608,11 @@ public:
virtual bool ShouldAutoaim( void );
void SetTargetInfo( Vector &vecSrc, float flDist );
#ifdef MAPBASE
// Tries to figure out what the player is trying to aim at
CBaseEntity *GetProbableAimTarget( const Vector &vecSrc, const Vector &vecDir );
#endif
void SetViewEntity( CBaseEntity *pEntity );
CBaseEntity *GetViewEntity( void ) { return m_hViewEntity; }

View File

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

View File

@ -6308,14 +6308,12 @@ void CPropDoorRotating::Break( CBaseEntity *pBreaker, const CTakeDamageInfo &inf
}
#endif
#ifdef MAPBASE
void CPropDoorRotating::InputSetSpeed(inputdata_t &inputdata)
{
AssertMsg1(inputdata.value.Float() > 0.0f, "InputSetSpeed on %s called with negative parameter!", GetDebugName() );
m_flSpeed = inputdata.value.Float();
DoorResume();
}
#endif
// Debug sphere
class CPhysSphere : public CPhysicsProp
@ -6362,15 +6360,6 @@ BEGIN_DATADESC( CPhysSphere )
END_DATADESC()
#endif
#ifndef MAPBASE // Yes, all I'm doing is moving this up a few lines and I'm still using the preprocessor.
void CPropDoorRotating::InputSetSpeed(inputdata_t &inputdata)
{
AssertMsg1(inputdata.value.Float() > 0.0f, "InputSetSpeed on %s called with negative parameter!", GetDebugName() );
m_flSpeed = inputdata.value.Float();
DoorResume();
}
#endif
LINK_ENTITY_TO_CLASS( prop_sphere, CPhysSphere );

View File

@ -1396,11 +1396,31 @@ void CAI_ScriptedSequence::ModifyScriptedAutoMovement( Vector *vecNewPos )
}
}
VMatrix matInteractionPosition = m_matInteractionPosition;
#ifdef MAPBASE
// Account for our own sequence movement
pAnimating = m_hTargetEnt->GetBaseAnimating();
if (pAnimating)
{
Vector vecDeltaPos;
QAngle angDeltaAngles;
pAnimating->GetSequenceMovement( pAnimating->GetSequence(), 0.0f, pAnimating->GetCycle(), vecDeltaPos, angDeltaAngles );
if (!vecDeltaPos.IsZero())
{
VMatrix matLocalMovement;
matLocalMovement.SetupMatrixOrgAngles( vecDeltaPos, angDeltaAngles );
MatrixMultiply( m_matInteractionPosition, matLocalMovement, matInteractionPosition );
}
}
#endif
// We've been asked to maintain a specific position relative to the other NPC
// we're interacting with. Lerp towards the relative position.
VMatrix matMeToWorld, matLocalToWorld;
matMeToWorld.SetupMatrixOrgAngles( vecRelativeOrigin, angRelativeAngles );
MatrixMultiply( matMeToWorld, m_matInteractionPosition, matLocalToWorld );
MatrixMultiply( matMeToWorld, matInteractionPosition, matLocalToWorld );
// Get the desired NPC position in worldspace
Vector vecOrigin;

View File

@ -63,6 +63,8 @@ $Project
$File "mapbase\ai_grenade.h"
$File "mapbase\ai_monitor.cpp"
$File "mapbase\ai_weaponmodifier.cpp"
$File "mapbase\custom_weapon_factory.cpp"
$File "mapbase\custom_weapon_factory.h"
$File "mapbase\closecaption_entity.cpp"
$File "mapbase\datadesc_mod.cpp"
$File "mapbase\datadesc_mod.h"
@ -86,6 +88,7 @@ $Project
$File "mapbase\SystemConvarMod.h"
$File "mapbase\variant_tools.h"
$File "mapbase\vgui_text_display.cpp"
$File "mapbase\weapon_custom_hl2.cpp"
$File "mapbase\logic_eventlistener.cpp"
$File "mapbase\logic_register_activator.cpp"

View File

@ -96,7 +96,7 @@ public:
bool PointIsWithin( const Vector &vecPoint );
#ifdef MAPBASE_VSCRIPT
bool ScriptPassesTriggerFilters( HSCRIPT hOther ) { return ToEnt(hOther) ? PassesTriggerFilters( ToEnt(hOther) ) : NULL; }
bool ScriptPassesTriggerFilters( HSCRIPT hOther ) { return ToEnt(hOther) ? PassesTriggerFilters( ToEnt(hOther) ) : false; }
HSCRIPT ScriptGetTouchedEntityOfType( const char *sClassName ) { return ToHScript( GetTouchedEntityOfType(sClassName) ); }
void ScriptGetTouchingEntities( HSCRIPT hTable );

View File

@ -76,6 +76,10 @@ public:
virtual const char *GetCannonicalName( const char *pClassName );
void ReportEntitySizes();
#ifdef MAPBASE
virtual void UninstallFactory(const char* pClassName);
#endif // MAPBASE
private:
IEntityFactory *FindFactory( const char *pClassName );
public:
@ -203,6 +207,11 @@ void CEntityFactoryDictionary::ReportEntitySizes()
}
#ifdef MAPBASE
void CEntityFactoryDictionary::UninstallFactory(const char* pClassName)
{
m_Factories.Remove(pClassName);
}
int EntityFactory_AutoComplete( const char *cmdname, CUtlVector< CUtlString > &commands, CUtlRBTree< CUtlString > &symbols, char *substring, int checklen = 0 )
{
CEntityFactoryDictionary *pFactoryDict = (CEntityFactoryDictionary*)EntityFactoryDictionary();

View File

@ -100,6 +100,9 @@ public:
virtual void Destroy( const char *pClassName, IServerNetworkable *pNetworkable ) = 0;
virtual IEntityFactory *FindFactory( const char *pClassName ) = 0;
virtual const char *GetCannonicalName( const char *pClassName ) = 0;
#ifdef MAPBASE
virtual void UninstallFactory(const char* pClassName) = 0;
#endif // MAPBASE
};
IEntityFactoryDictionary *EntityFactoryDictionary();
@ -188,8 +191,7 @@ extern CGlobalVars *gpGlobals;
inline bool FStrEq(const char *sz1, const char *sz2)
{
#ifdef MAPBASE
// V_stricmp() already checks if the pointers are equal, so having a pointer comparison here is pointless.
// I'm not sure if this was already automatically phased out by the compiler, but if it wasn't, then this is a very good change.
// V_stricmp() already checks if the pointers are equal, so having a pointer comparison here is unnecessary.
return ( V_stricmp(sz1, sz2) == 0 );
#else
return ( sz1 == sz2 || V_stricmp(sz1, sz2) == 0 );

View File

@ -606,10 +606,10 @@ bool VScriptServerInit()
#ifdef MAPBASE_VSCRIPT
// MULTIPLAYER
// NOTE: 'PlayerInstanceFromIndex' and 'GetPlayerFromUserID' are used in L4D2 and Source 2,
// but the GetPlayerBy* names are more consistent.
// ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_PlayerByIndex, "GetPlayerByIndex", "PlayerInstanceFromIndex" );
// ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_PlayerByUserId, "GetPlayerByUserId", "GetPlayerFromUserID" );
// ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_PlayerByName, "GetPlayerByName", "" );
// ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGetPlayerByNetworkID, "GetPlayerByNetworkID", "" );
// ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_PlayerByUserId, "GetPlayerByUserID", "GetPlayerFromUserID" );
ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_ShowMessageAll, "ShowMessage", "Print a hud message on all clients" );
#else
@ -841,6 +841,8 @@ public:
virtual void LevelShutdownPostEntity( void )
{
#ifdef MAPBASE_VSCRIPT
g_ScriptEntityIterator.DisableEntityListening();
g_ScriptNetMsg->LevelShutdownPreVM();
GetScriptHookManager().OnShutdown();

View File

@ -239,7 +239,7 @@ const unsigned char *CBaseCombatWeapon::GetEncryptionKey( void )
void CBaseCombatWeapon::Precache( void )
{
#if defined( CLIENT_DLL )
Assert( Q_strlen( GetClassname() ) > 0 );
Assert( Q_strlen(GetWeaponScriptName() ) > 0 );
// Msg( "Client got %s\n", GetClassname() );
#endif
m_iPrimaryAmmoType = m_iSecondaryAmmoType = -1;
@ -321,7 +321,7 @@ void CBaseCombatWeapon::Precache( void )
else
{
// Couldn't read data file, remove myself
Warning( "Error reading weapon data file for: %s\n", GetClassname() );
Warning( "Error reading weapon data file for: %s\n", GetWeaponScriptName() );
// Remove( ); //don't remove, this gets released soon!
}
}
@ -2886,6 +2886,15 @@ bool CBaseCombatWeapon::IsLocked( CBaseEntity *pAsker )
return ( m_flUnlockTime > gpGlobals->curtime && m_hLocker != pAsker );
}
bool CBaseCombatWeapon::CanBePickedUpByNPCs(void)
{
#ifdef MAPBASE
return GetWpnData().m_nWeaponRestriction != WPNRESTRICT_PLAYER_ONLY;
#else
return true;
#endif // MAPBASE
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :

View File

@ -390,7 +390,7 @@ public:
bool IsLocked( CBaseEntity *pAsker );
//All weapons can be picked up by NPCs by default
virtual bool CanBePickedUpByNPCs( void ) { return true; }
virtual bool CanBePickedUpByNPCs(void);
virtual int GetSkinOverride() const { return -1; }

View File

@ -83,6 +83,10 @@ ConVar ai_shot_bias_min( "ai_shot_bias_min", "-1.0", FCVAR_REPLICATED );
ConVar ai_shot_bias_max( "ai_shot_bias_max", "1.0", FCVAR_REPLICATED );
ConVar ai_debug_shoot_positions( "ai_debug_shoot_positions", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
#if defined(MAPBASE) && defined(GAME_DLL)
ConVar ai_shot_notify_targets( "ai_shot_notify_targets", "0", FCVAR_NONE, "Allows fired bullets to notify the NPCs and players they are targeting, regardless of whether they hit them or not. Can be used for custom AI and speech." );
#endif
// Utility func to throttle rate at which the "reasonable position" spew goes out
static double s_LastEntityReasonableEmitTime;
bool CheckEmitReasonablePhysicsSpew()
@ -2081,6 +2085,25 @@ void CBaseEntity::FireBullets( const FireBulletsInfo_t &info )
CTakeDamageInfo dmgInfo( this, pAttacker, flCumulativeDamage, nDamageType );
gamestats->Event_WeaponHit( pPlayer, info.m_bPrimaryAttack, pPlayer->GetActiveWeapon()->GetClassname(), dmgInfo );
}
#ifdef MAPBASE
if ( ai_shot_notify_targets.GetBool() )
{
if ( IsPlayer() )
{
// Look for probable target to notify of attack
CBaseEntity *pAimTarget = static_cast<CBasePlayer*>(this)->GetProbableAimTarget( info.m_vecSrc, info.m_vecDirShooting );
if ( pAimTarget && pAimTarget->IsCombatCharacter() )
{
pAimTarget->MyCombatCharacterPointer()->OnEnemyRangeAttackedMe( this, vecDir, vecEnd );
}
}
else if ( GetEnemy() && GetEnemy()->IsCombatCharacter() )
{
GetEnemy()->MyCombatCharacterPointer()->OnEnemyRangeAttackedMe( this, vecDir, vecEnd );
}
}
#endif
#endif
}
@ -2736,6 +2759,18 @@ void CBaseEntity::SetScriptOwnerEntity(HSCRIPT pOwner)
SetOwnerEntity(ToEnt(pOwner));
}
#ifdef MAPBASE_VSCRIPT
HSCRIPT CBaseEntity::ScriptGetGroundEntity()
{
return ToHScript( m_hGroundEntity.Get() );
}
void CBaseEntity::ScriptSetGroundEntity( HSCRIPT hGroundEnt )
{
SetGroundEntity( ToEnt( hGroundEnt ) );
}
#endif
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
const Vector& CBaseEntity::ScriptGetColorVector()

View File

@ -233,6 +233,11 @@ void EventList_RegisterSharedEvents( void )
REGISTER_SHARED_ANIMEVENT( AE_SV_DUSTTRAIL, AE_TYPE_SERVER );
REGISTER_SHARED_ANIMEVENT( AE_CL_CREATE_PARTICLE_EFFECT, AE_TYPE_CLIENT );
#ifdef MAPBASE // From Alien Swarm SDK
REGISTER_SHARED_ANIMEVENT( AE_CL_STOP_PARTICLE_EFFECT, AE_TYPE_CLIENT );
REGISTER_SHARED_ANIMEVENT( AE_CL_ADD_PARTICLE_EFFECT_CP, AE_TYPE_CLIENT );
//REGISTER_SHARED_ANIMEVENT( AE_CL_CREATE_PARTICLE_BRASS, AE_TYPE_CLIENT );
#endif
REGISTER_SHARED_ANIMEVENT( AE_RAGDOLL, AE_TYPE_SERVER );
@ -252,5 +257,8 @@ void EventList_RegisterSharedEvents( void )
#ifdef MAPBASE
REGISTER_SHARED_ANIMEVENT( AE_NPC_RESPONSE, AE_TYPE_SERVER );
REGISTER_SHARED_ANIMEVENT( AE_NPC_RESPONSE_FORCED, AE_TYPE_SERVER );
REGISTER_SHARED_ANIMEVENT( AE_VSCRIPT_RUN, AE_TYPE_CLIENT | AE_TYPE_SERVER );
REGISTER_SHARED_ANIMEVENT( AE_VSCRIPT_RUN_FILE, AE_TYPE_CLIENT | AE_TYPE_SERVER );
#endif
}

View File

@ -69,6 +69,11 @@ typedef enum
AE_SV_DUSTTRAIL,
AE_CL_CREATE_PARTICLE_EFFECT,
#ifdef MAPBASE // From Alien Swarm SDK
AE_CL_STOP_PARTICLE_EFFECT,
AE_CL_ADD_PARTICLE_EFFECT_CP,
//AE_CL_CREATE_PARTICLE_BRASS,
#endif
AE_RAGDOLL,
@ -88,6 +93,9 @@ typedef enum
#ifdef MAPBASE
AE_NPC_RESPONSE, // Play a response system concept if we're not speaking
AE_NPC_RESPONSE_FORCED, // Always play a response system concept
AE_VSCRIPT_RUN, // Run vscript code (server + client)
AE_VSCRIPT_RUN_FILE, // Run vscript file (server + client)
#endif
LAST_SHARED_ANIMEVENT,

View File

@ -208,7 +208,7 @@ void CWeaponAR2::DelayedAttack( void )
// pOwner->SnapEyeAngles( angles );
pOwner->ViewPunch( QAngle( SharedRandomInt( "ar2pax", -8, -12 ), SharedRandomInt( "ar2pay", 1, 2 ), 0 ) );
pOwner->ViewPunch( QAngle( SharedRandomInt( "ar2pax", -12, -8 ), SharedRandomInt( "ar2pay", 1, 2 ), 0 ) );
// Decrease ammo
pOwner->RemoveAmmo( 1, m_iSecondaryAmmoType );

View File

@ -70,7 +70,7 @@ ConVar mapbase_version_client( "mapbase_version_client", MAPBASE_VERSION, FCVAR_
// This is from the vgui_controls library
extern vgui::HScheme g_iCustomClientSchemeOverride;
bool g_bUsingCustomHudAnimations = false;
extern bool g_bUsingCustomHudAnimations;
bool g_bUsingCustomHudLayout = false;
#endif

View File

@ -22,6 +22,7 @@
#include "filesystem.h"
#include "igameevents.h"
#include "engine/ivdebugoverlay.h"
#include "icommandline.h"
#ifdef CLIENT_DLL
#include "IEffects.h"
@ -1094,7 +1095,7 @@ const char *CScriptReadWriteFile::FileRead( const char *szFile )
char pszFullName[MAX_PATH];
V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile );
if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
if ( !CommandLine()->FindParm( "-script_dotslash_read" ) && !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
{
DevWarning( 2, "Invalid file location : %s\n", szFile );
return NULL;
@ -1143,7 +1144,7 @@ bool CScriptReadWriteFile::FileExists( const char *szFile )
char pszFullName[MAX_PATH];
V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile );
if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
if ( !CommandLine()->FindParm( "-script_dotslash_read" ) && !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
{
DevWarning( 2, "Invalid file location : %s\n", szFile );
return NULL;
@ -1224,7 +1225,7 @@ HSCRIPT CScriptReadWriteFile::KeyValuesRead( const char *szFile )
char pszFullName[MAX_PATH];
V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile );
if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
if ( !CommandLine()->FindParm( "-script_dotslash_read" ) && !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
{
DevWarning( 2, "Invalid file location : %s\n", szFile );
return NULL;
@ -1338,8 +1339,7 @@ static const char *HasNetMsgCollision( int hash, const char *ignore )
inline int CNetMsgScriptHelper::Hash( const char *key )
{
int hash = HashStringCaseless( key );
Assert( hash < (1 << SCRIPT_NETMSG_HEADER_BITS) );
int hash = CaselessStringHashFunctor()( key );
return hash;
}
@ -1445,7 +1445,7 @@ void CNetMsgScriptHelper::ReceiveMessage( bf_read &msg )
while ( count-- )
#endif
{
int hash = m_MsgIn_()ReadWord();
int hash = m_MsgIn_()ReadUBitLong( SCRIPT_NETMSG_HEADER_BITS );
#ifdef _DEBUG
const char *msgName = GetNetMsgName( hash );
@ -1514,7 +1514,7 @@ void CNetMsgScriptHelper::Start( const char *msg )
Reset();
#endif
m_MsgOut.WriteWord( Hash( msg ) );
m_MsgOut.WriteUBitLong( Hash( msg ), SCRIPT_NETMSG_HEADER_BITS );
}
#ifdef GAME_DLL
@ -1923,7 +1923,7 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CNetMsgScriptHelper, "CNetMsg", SCRIPT_SINGLETON "N
DEFINE_SCRIPTFUNC( Receive, "Set custom network message callback" )
DEFINE_SCRIPTFUNC_NAMED( Receive, "Recieve", SCRIPT_HIDE ) // This was a typo until v6.3
#ifdef GAME_DLL
DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the server to the client (max 252 bytes)" )
DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the server to the client (max 251 bytes)" )
#else
DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the client to the server (max 2044 bytes)" )
#endif
@ -2742,6 +2742,11 @@ public:
void CScriptConvarAccessor::RegisterCommand( const char *name, HSCRIPT fn, const char *helpString, int flags )
{
#if CLIENT_DLL
// FIXME: This crashes in engine when used as a hook (dispatched from CScriptConCommand::CommandCallback())
Assert( V_stricmp( name, "load" ) != 0 );
#endif
unsigned int hash = Hash(name);
int idx = g_ScriptConCommands.Find(hash);
if ( idx == g_ScriptConCommands.InvalidIndex() )

View File

@ -24,7 +24,7 @@ void RegisterScriptSingletons();
#endif
#define SCRIPT_NETMSG_QUEUE_BITS 3 // determines the number of custom messages client can write to a usercmd
#define SCRIPT_NETMSG_HEADER_BITS (sizeof(word) << 3)
#define SCRIPT_NETMSG_HEADER_BITS (sizeof(unsigned int) << 3)
#define SCRIPT_NETMSG_STRING_SIZE 512

View File

@ -7,6 +7,7 @@
#include "cbase.h"
#include "tier1/fmtstr.h"
#include "tier1/utlvector.h"
#include "weapon_custom_scripted.h"
// memdbgon must be the last include file in a .cpp file!!!
@ -179,7 +180,7 @@ bool CWeaponCustomScripted::RunWeaponHook( ScriptHook_t &hook, HSCRIPT &cached,
{
if ( !cached )
{
if ( hook.CanRunInScope( m_ScriptScope ) )
if ( m_ScriptScope.IsInitialized() && hook.CanRunInScope( m_ScriptScope ) )
{
cached = hook.m_hFunc;
}
@ -187,6 +188,7 @@ bool CWeaponCustomScripted::RunWeaponHook( ScriptHook_t &hook, HSCRIPT &cached,
if (cached)
{
hook.m_hFunc = cached;
return hook.Call( m_ScriptScope, retVal, pArgs, false );
}
@ -198,13 +200,6 @@ bool CWeaponCustomScripted::RunWeaponHook( ScriptHook_t &hook, HSCRIPT &cached,
//-----------------------------------------------------------------------------
void CWeaponCustomScripted::Spawn( void )
{
#ifdef CLIENT_DLL
if (m_iszClientScripts[0] != '\0' && ValidateScriptScope())
{
RunScriptFile( m_iszClientScripts );
}
#endif
BaseClass::Spawn();
}
@ -334,7 +329,7 @@ void CWeaponCustomScripted::ItemPreFrame( void )
{
SIMPLE_VOID_OVERRIDE( ItemPreFrame, NULL );
BaseClass::ItemPostFrame();
BaseClass::ItemPreFrame();
}
void CWeaponCustomScripted::ItemPostFrame( void )
@ -432,7 +427,7 @@ void CWeaponCustomScripted::SecondaryAttack( void )
// Purpose:
//-----------------------------------------------------------------------------
#define ACTIVITY_FUNC_OVERRIDE( name ) ScriptVariant_t retVal; \
if (RunWeaponHook( g_Hook_##name, m_Func_##name, &retVal ) && retVal.m_bool == false) \
if (RunWeaponHook( g_Hook_##name, m_Func_##name, &retVal ) && !retVal.IsNull()) \
{ \
if (retVal.m_type == FIELD_INTEGER) \
{ \
@ -586,6 +581,36 @@ int CWeaponCustomScripted::WeaponMeleeAttack2Condition( float flDot, float flDis
return BaseClass::WeaponMeleeAttack2Condition( flDot, flDist );
}
struct VScriptWeaponCustomData_s
{
char cScripts[256];
bool Parse(KeyValues* pKVWeapon)
{
Q_strncpy(cScripts, pKVWeapon->GetString("vscript_file"), 256);
return true;
}
};
DEFINE_CUSTOM_WEAPON_FACTORY(vscript, CWeaponCustomScripted, VScriptWeaponCustomData_s);
void CWeaponCustomScripted::InitCustomWeaponFromData(const void* pData, const char* pszWeaponScript)
{
Q_FileBase(pszWeaponScript, m_iszWeaponScriptName.GetForModify(), 256);
Q_strncpy(m_iszClientScripts.GetForModify(), static_cast<const VScriptWeaponCustomData_s *> (pData)->cScripts, 256);
}
extern ConVar sv_script_think_interval;
#else
void CWeaponCustomScripted::OnDataChanged(DataUpdateType_t type)
{
BaseClass::OnDataChanged(type);
if (!m_ScriptScope.IsInitialized())
{
RunVScripts();
}
}
#endif
//-----------------------------------------------------------------------------
@ -604,3 +629,96 @@ int CWeaponCustomScripted::ActivityListCount( void )
return BaseClass::ActivityListCount();
}
void CWeaponCustomScripted::RunVScripts()
{
#ifdef CLIENT_DLL
if (m_iszClientScripts[0] != '\0' && ValidateScriptScope())
{
RunScriptFile(m_iszClientScripts);
}
#else
if (m_iszVScripts == NULL_STRING && m_iszClientScripts[0] == '\0')
{
return;
}
#ifdef MAPBASE_VSCRIPT
if (g_pScriptVM == NULL)
{
return;
}
#endif
ValidateScriptScope();
// All functions we want to have call chained instead of overwritten
// by other scripts in this entities list.
static const char* sCallChainFunctions[] =
{
"OnPostSpawn",
"Precache"
};
ScriptLanguage_t language = g_pScriptVM->GetLanguage();
// Make a call chainer for each in this entities scope
for (int j = 0; j < ARRAYSIZE(sCallChainFunctions); ++j)
{
if (language == SL_PYTHON)
{
// UNDONE - handle call chaining in python
;
}
else if (language == SL_SQUIRREL)
{
//TODO: For perf, this should be precompiled and the %s should be passed as a parameter
HSCRIPT hCreateChainScript = g_pScriptVM->CompileScript(CFmtStr("%sCallChain <- CSimpleCallChainer(\"%s\", self.GetScriptScope(), true)", sCallChainFunctions[j], sCallChainFunctions[j]));
g_pScriptVM->Run(hCreateChainScript, (HSCRIPT)m_ScriptScope);
}
}
CUtlStringList szScripts;
if (m_iszVScripts != NULL_STRING)
{
V_SplitString(STRING(m_iszVScripts), " ", szScripts);
}
if (m_iszClientScripts[0] != '\0')
{
szScripts.AddToHead(strdup(m_iszClientScripts.Get()));
}
for (int i = 0; i < szScripts.Count(); i++)
{
#ifdef MAPBASE
CGMsg(0, CON_GROUP_VSCRIPT, "%s executing script: %s\n", GetDebugName(), szScripts[i]);
#else
Log("%s executing script: %s\n", GetDebugName(), szScripts[i]);
#endif
RunScriptFile(szScripts[i], IsWorld());
for (int j = 0; j < ARRAYSIZE(sCallChainFunctions); ++j)
{
if (language == SL_PYTHON)
{
// UNDONE - handle call chaining in python
;
}
else if (language == SL_SQUIRREL)
{
//TODO: For perf, this should be precompiled and the %s should be passed as a parameter.
HSCRIPT hRunPostScriptExecute = g_pScriptVM->CompileScript(CFmtStr("%sCallChain.PostScriptExecute()", sCallChainFunctions[j]));
g_pScriptVM->Run(hRunPostScriptExecute, (HSCRIPT)m_ScriptScope);
}
}
}
if (m_iszScriptThinkFunction != NULL_STRING)
{
SetContextThink(&CBaseEntity::ScriptThink, gpGlobals->curtime + sv_script_think_interval.GetFloat(), "ScriptThink");
}
#endif
}

View File

@ -14,6 +14,8 @@
#include "basecombatweapon_shared.h"
#ifdef CLIENT_DLL
#include "vscript_client.h"
#else
#include "mapbase/custom_weapon_factory.h"
#endif
// The base class of the scripted weapon is game-specific.
@ -32,6 +34,9 @@
HSCRIPT m_Func_##name;
class CWeaponCustomScripted : public SCRIPTED_WEAPON_DERIVED_FROM
#ifndef CLIENT_DLL
, public ICustomWeapon
#endif // !CLIENT_DLL
{
public:
DECLARE_CLASS( CWeaponCustomScripted, SCRIPTED_WEAPON_DERIVED_FROM );
@ -45,6 +50,8 @@ public:
bool KeyValue( const char *szKeyName, const char *szValue );
bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen );
void RunVScripts();
// Base script has a function for this
//void Precache( void );
@ -106,6 +113,11 @@ public:
int WeaponRangeAttack2Condition( float flDot, float flDist );
int WeaponMeleeAttack1Condition( float flDot, float flDist );
int WeaponMeleeAttack2Condition( float flDot, float flDist );
// Inherited via ICustomWeapon
virtual void InitCustomWeaponFromData(const void* pData, const char* pszWeaponScript);
#else
void OnDataChanged(DataUpdateType_t type);
#endif
ALLOW_SCRIPT_ACCESS();

View File

@ -453,8 +453,7 @@ void RunAddonScripts()
// mapspawn_addon
char fullpath[MAX_PATH];
Q_snprintf( fullpath, sizeof( fullpath ), "%sscripts/vscripts/mapspawn_addon", path );
Q_FixSlashes( fullpath );
Q_ComposeFileName( path, "scripts/vscripts/mapspawn_addon", fullpath, sizeof( fullpath ) );
VScriptRunScriptAbsolute( fullpath, NULL, false, folderName );
}

View File

@ -408,6 +408,7 @@ FileWeaponInfo_t::FileWeaponInfo_t()
m_flSwaySpeedScale = 1.0f;
szDroppedModel[0] = 0;
m_bUsesHands = false;
m_nWeaponRestriction = WPNRESTRICT_NONE;
#endif
}
@ -415,6 +416,14 @@ FileWeaponInfo_t::FileWeaponInfo_t()
extern ConVar hud_fastswitch;
#endif
#ifdef MAPBASE
const char* pWeaponRestrictions[NUM_WEAPON_RESTRICTION_TYPES] = {
"none",
"player_only",
"npc_only",
};
#endif // MAPBASE
void FileWeaponInfo_t::Parse( KeyValues *pKeyValuesData, const char *szWeaponName )
{
// Okay, we tried at least once to look this up...
@ -483,6 +492,19 @@ void FileWeaponInfo_t::Parse( KeyValues *pKeyValuesData, const char *szWeaponNam
Q_strncpy( szDroppedModel, pKeyValuesData->GetString( "droppedmodel" ), MAX_WEAPON_STRING );
m_bUsesHands = ( pKeyValuesData->GetInt( "uses_hands", 0 ) != 0 ) ? true : false;
const char* pszRestrictString = pKeyValuesData->GetString("usage_restriction", nullptr);
if (pszRestrictString)
{
for (int i = 0; i < NUM_WEAPON_RESTRICTION_TYPES; i++)
{
if (V_stricmp(pszRestrictString, pWeaponRestrictions[i]) == 0)
{
m_nWeaponRestriction = i;
break;
}
}
}
#endif
#ifndef MAPBASE // Mapbase makes weapons in the same slot & position swap each other out, which is a feature mods can intentionally use.

View File

@ -58,6 +58,17 @@ int GetWeaponSoundFromString( const char *pszString );
class CHudTexture;
class KeyValues;
#ifdef MAPBASE
enum WeaponUsageRestricions_e
{
WPNRESTRICT_NONE = 0,
WPNRESTRICT_PLAYER_ONLY,
WPNRESTRICT_NPCS_ONLY,
NUM_WEAPON_RESTRICTION_TYPES
};
#endif // MAPBASE
//-----------------------------------------------------------------------------
// Purpose: Contains the data read from the weapon's script file.
// It's cached so we only read each weapon's script file once.
@ -125,6 +136,8 @@ public:
char szDroppedModel[MAX_WEAPON_STRING]; // Model of this weapon when dropped on the ground
bool m_bUsesHands;
int m_nWeaponRestriction;
#endif
// CLIENT DLL

View File

@ -102,8 +102,22 @@ enum soundlevel_t
#define MAX_SNDLVL_VALUE ((1<<MAX_SNDLVL_BITS)-1)
#define ATTN_TO_SNDLVL( a ) (soundlevel_t)(int)((a) ? (50 + 20 / ((float)a)) : 0 )
#define SNDLVL_TO_ATTN( a ) ((a > 50) ? (20.0f / (float)(a - 50)) : 4.0 )
inline soundlevel_t ATTN_TO_SNDLVL(float a)
{
soundlevel_t sndlvl = soundlevel_t::SNDLVL_NONE;
if (a >= 0.0f)
{
sndlvl = soundlevel_t(float(soundlevel_t::SNDLVL_50dB) + float(soundlevel_t::SNDLVL_20dB) / a);
}
return sndlvl;
}
inline float SNDLVL_TO_ATTN(soundlevel_t s)
{
return (s > soundlevel_t::SNDLVL_50dB)? (20.0f / float(s - soundlevel_t::SNDLVL_50dB)) : 4.0f;
}
// This is a limit due to network encoding.
// It encodes attenuation * 64 in 8 bits, so the maximum is (255 / 64)

View File

@ -1281,8 +1281,8 @@ PLATFORM_INTERFACE bool Is64BitOS();
//-----------------------------------------------------------------------------
// General Mapbase version constants compiled into projects for versioning purposes
//-----------------------------------------------------------------------------
#define MAPBASE_VERSION "7.1"
#define MAPBASE_VER_INT 7100 // For use in #if in a similar fashion to macros like _MSC_VER
#define MAPBASE_VERSION "7.2"
#define MAPBASE_VER_INT 7200 // For use in #if in a similar fashion to macros like _MSC_VER
#endif

View File

@ -61,7 +61,11 @@ public:
int GetNumFrames();
void SetFrame( int nFrame );
#ifdef MAPBASE
void SetRotation( int iRotation );
#else
void SetRotation( int iRotation ) { m_iRotation = iRotation; }
#endif
protected:
virtual void PaintBackground();

View File

@ -33,7 +33,7 @@ static CUtlSymbolTable g_ScriptSymbols(0, 128, true);
#ifdef MAPBASE
// Allows animation sequences to be overridden by map-specific files
extern bool g_bUsingCustomHudAnimations;
bool g_bUsingCustomHudAnimations = false;
#endif
// singleton accessor for animation controller for use by the vgui controls

View File

@ -73,6 +73,13 @@ void ImagePanel::OnSizeChanged(int newWide, int newTall)
//-----------------------------------------------------------------------------
void ImagePanel::SetImage(IImage *image)
{
#ifdef MAPBASE
if ( image )
{
image->SetRotation( m_iRotation );
}
#endif
m_pImage = image;
Repaint();
}
@ -471,3 +478,15 @@ void ImagePanel::SetFrame( int nFrame )
return m_pImage->SetFrame( nFrame );
}
#ifdef MAPBASE
void ImagePanel::SetRotation( int iRotation )
{
m_iRotation = iRotation;
if ( m_pImage )
{
m_pImage->SetRotation( m_iRotation );
}
}
#endif

View File

@ -206,19 +206,28 @@ void CScriptKeyValues::ScriptReleaseKeyValues( )
m_pKeyValues = NULL;
}
void CScriptKeyValues::TableToSubKeys( HSCRIPT hTable )
void KeyValues_TableToSubKeys( HSCRIPT hTable, KeyValues *pKV )
{
int nIterator = -1;
ScriptVariant_t varKey, varValue;
while ((nIterator = g_pScriptVM->GetKeyValue( hTable, nIterator, &varKey, &varValue )) != -1)
{
if ( varKey.m_type == FIELD_CSTRING )
{
switch ( varValue.m_type )
{
case FIELD_CSTRING: m_pKeyValues->SetString( varKey.m_pszString, varValue.m_pszString ); break;
case FIELD_INTEGER: m_pKeyValues->SetInt( varKey.m_pszString, varValue.m_int ); break;
case FIELD_FLOAT: m_pKeyValues->SetFloat( varKey.m_pszString, varValue.m_float ); break;
case FIELD_BOOLEAN: m_pKeyValues->SetBool( varKey.m_pszString, varValue.m_bool ); break;
case FIELD_VECTOR: m_pKeyValues->SetString( varKey.m_pszString, CFmtStr( "%f %f %f", varValue.m_pVector->x, varValue.m_pVector->y, varValue.m_pVector->z ) ); break;
case FIELD_CSTRING: pKV->SetString( varKey.m_pszString, varValue.m_pszString ); break;
case FIELD_INTEGER: pKV->SetInt( varKey.m_pszString, varValue.m_int ); break;
case FIELD_FLOAT: pKV->SetFloat( varKey.m_pszString, varValue.m_float ); break;
case FIELD_BOOLEAN: pKV->SetBool( varKey.m_pszString, varValue.m_bool ); break;
case FIELD_VECTOR: pKV->SetString( varKey.m_pszString, CFmtStr( "%f %f %f", varValue.m_pVector->x, varValue.m_pVector->y, varValue.m_pVector->z ) ); break;
case FIELD_HSCRIPT:
{
KeyValues *subKey = pKV->FindKey( varKey.m_pszString, true );
KeyValues_TableToSubKeys( varValue, subKey );
break;
}
}
}
g_pScriptVM->ReleaseValue( varKey );
@ -226,18 +235,37 @@ void CScriptKeyValues::TableToSubKeys( HSCRIPT hTable )
}
}
void CScriptKeyValues::SubKeysToTable( HSCRIPT hTable )
void KeyValues_SubKeysToTable( KeyValues *pKV, HSCRIPT hTable )
{
FOR_EACH_SUBKEY( m_pKeyValues, key )
FOR_EACH_SUBKEY( pKV, key )
{
switch ( key->GetDataType() )
{
case KeyValues::TYPE_STRING: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetString() ); break;
case KeyValues::TYPE_INT: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetInt() ); break;
case KeyValues::TYPE_FLOAT: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetFloat() ); break;
case KeyValues::TYPE_NONE:
{
ScriptVariant_t subTable;
g_pScriptVM->CreateTable( subTable );
g_pScriptVM->SetValue( hTable, key->GetName(), subTable );
KeyValues_SubKeysToTable( key, subTable );
g_pScriptVM->ReleaseValue( subTable );
break;
}
}
}
}
void CScriptKeyValues::TableToSubKeys( HSCRIPT hTable )
{
KeyValues_TableToSubKeys( hTable, m_pKeyValues );
}
void CScriptKeyValues::SubKeysToTable( HSCRIPT hTable )
{
KeyValues_SubKeysToTable( m_pKeyValues, hTable );
}
HSCRIPT CScriptKeyValues::ScriptFindOrCreateKey( const char *pszName )
{

View File

@ -118,16 +118,15 @@ class CSimpleCallChainer
function PostScriptExecute()
{
local func;
try {
func = scope[prefix];
} catch(e) {
return;
}
if (typeof(func) != "function")
return;
if ( prefix in scope )
{
local func = scope[prefix];
if ( typeof func == "function" )
{
chain.push(func);
}
}
}
function Call()
{