mirror of
https://github.com/mapbase-source/source-sdk-2013.git
synced 2025-03-03 17:25:27 +03:00
Merge pull request #183 from Petercov/mapbase-feature/custom-weapons
Enhanced custom weapons support
This commit is contained in:
commit
6915787c80
@ -151,6 +151,13 @@ int C_BaseCombatWeapon::GetWorldModelIndex( void )
|
||||
//-----------------------------------------------------------------------------
|
||||
void C_BaseCombatWeapon::OnDataChanged( DataUpdateType_t updateType )
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
if (updateType == DATA_UPDATE_CREATED)
|
||||
{
|
||||
Precache();
|
||||
}
|
||||
#endif // MAPBASE
|
||||
|
||||
BaseClass::OnDataChanged(updateType);
|
||||
|
||||
CHandle< C_BaseCombatWeapon > handle = this;
|
||||
|
@ -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"
|
||||
}
|
||||
|
||||
|
67
sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp
Normal file
67
sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
//========= 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();
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
|
||||
|
||||
class C_HLCustomWeaponGun : public C_BaseHLCombatWeapon
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS(C_HLCustomWeaponGun, C_BaseHLCombatWeapon);
|
||||
DECLARE_CLIENTCLASS();
|
||||
DECLARE_PREDICTABLE();
|
||||
|
||||
C_HLCustomWeaponGun();
|
||||
|
||||
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';
|
||||
}
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 );
|
||||
|
||||
CTakeDamageInfo info( GetOwner(), GetOwner(), GetDamageForActivity( nHitActivity ), DMG_CLUB );
|
||||
#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.
|
||||
CTakeDamageInfo triggerInfo( GetOwner(), GetOwner(), GetDamageForActivity( nHitActivity ), DMG_CLUB );
|
||||
#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 );
|
||||
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
|
||||
|
@ -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
|
@ -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();
|
||||
|
@ -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
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
208
sp/src/game/server/mapbase/custom_weapon_factory.cpp
Normal file
208
sp/src/game/server/mapbase/custom_weapon_factory.cpp
Normal 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;
|
||||
}
|
117
sp/src/game/server/mapbase/custom_weapon_factory.h
Normal file
117
sp/src/game/server/mapbase/custom_weapon_factory.h
Normal 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(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
|
1546
sp/src/game/server/mapbase/weapon_custom_hl2.cpp
Normal file
1546
sp/src/game/server/mapbase/weapon_custom_hl2.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -164,7 +164,9 @@ void CBaseCombatWeapon::GiveDefaultAmmo( void )
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseCombatWeapon::Spawn( void )
|
||||
{
|
||||
#if !defined(CLIENT_DLL) || !defined(MAPBASE)
|
||||
Precache();
|
||||
#endif // !defined(CLIENT_DLL) || !defined(MAPBASE)
|
||||
|
||||
BaseClass::Spawn();
|
||||
|
||||
@ -239,7 +241,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 +323,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 +2888,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 :
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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!!!
|
||||
@ -198,13 +199,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();
|
||||
}
|
||||
|
||||
@ -432,7 +426,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 +580,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 +628,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
|
||||
}
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user