mirror of
https://github.com/YGGverse/hlsdk-portable.git
synced 2025-02-09 21:44:15 +00:00
667 lines
20 KiB
C++
667 lines
20 KiB
C++
/***
|
|
*
|
|
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
|
|
*
|
|
* This product contains software technology licensed from Id
|
|
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
|
* All Rights Reserved.
|
|
*
|
|
* This source code contains proprietary and confidential information of
|
|
* Valve LLC and its suppliers. Access to this code is restricted to
|
|
* persons who have executed a written SDK license with Valve. Any access,
|
|
* use or distribution of this code by or to any unlicensed person is illegal.
|
|
*
|
|
****/
|
|
//=========================================================
|
|
// SPForce
|
|
//=========================================================
|
|
|
|
//=========================================================
|
|
// Hit groups!
|
|
//=========================================================
|
|
/*
|
|
|
|
1 - Head
|
|
2 - Stomach
|
|
3 - Gun
|
|
|
|
*/
|
|
|
|
|
|
#include "extdll.h"
|
|
#include "plane.h"
|
|
#include "util.h"
|
|
#include "cbase.h"
|
|
#include "monsters.h"
|
|
#include "schedule.h"
|
|
#include "animation.h"
|
|
#include "squadmonster.h"
|
|
#include "weapons.h"
|
|
#include "talkmonster.h"
|
|
#include "soundent.h"
|
|
#include "effects.h"
|
|
#include "customentity.h"
|
|
#include "hgrunt.h"
|
|
|
|
int g_fSPForceQuestion; // true if an idle SPForce asked a question. Cleared when someone answers.
|
|
|
|
extern DLL_GLOBAL int g_iSkillLevel;
|
|
|
|
//=========================================================
|
|
// monster-specific DEFINE's
|
|
//=========================================================
|
|
#define SPFORCE_CLIP_SIZE 36 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x!
|
|
#define SPFORCE_VOL 0.35 // volume of SPForce sounds
|
|
#define SPFORCE_ATTN ATTN_NORM // attenutation of SPForce sentences
|
|
#define SPFORCE_LIMP_HEALTH 20
|
|
#define SPFORCE_DMG_HEADSHOT ( DMG_BULLET | DMG_CLUB ) // damage types that can kill a SPForce with a single headshot.
|
|
#define SPFORCE_NUM_HEADS 2 // how many SPForce heads are there?
|
|
#define SPFORCE_MINIMUM_HEADSHOT_DAMAGE 15 // must do at least this much damage in one shot to head to score a headshot kill
|
|
#define SPFORCE_SENTENCE_VOLUME (float)0.35 // volume of SPForce sentences
|
|
|
|
#define SPFORCE_M41A ( 1 << 0)
|
|
#define SPFORCE_HANDGRENADE ( 1 << 1)
|
|
#define SPFORCE_GRENADELAUNCHER ( 1 << 2)
|
|
#define SPFORCE_BERETTA ( 1 << 3)
|
|
|
|
#define HEAD_GROUP 1
|
|
#define HEAD_SPForce 0
|
|
#define HEAD_COMMANDER 1
|
|
#define HEAD_BERETTA 2
|
|
#define HEAD_M203 3
|
|
#define GUN_GROUP 2
|
|
#define GUN_M41A 0
|
|
#define GUN_BERETTA 1
|
|
#define GUN_NONE 2
|
|
|
|
//=========================================================
|
|
// Monster's Anim Events Go Here
|
|
//=========================================================
|
|
#define SPFORCE_AE_RELOAD ( 2 )
|
|
#define SPFORCE_AE_KICK ( 3 )
|
|
#define SPFORCE_AE_BURST1 ( 4 )
|
|
#define SPFORCE_AE_BURST2 ( 5 )
|
|
#define SPFORCE_AE_BURST3 ( 6 )
|
|
#define SPFORCE_AE_GREN_TOSS ( 7 )
|
|
#define SPFORCE_AE_GREN_LAUNCH ( 8 )
|
|
#define SPFORCE_AE_GREN_DROP ( 9 )
|
|
#define SPFORCE_AE_CAUGHT_ENEMY ( 10) // SPForce established sight with an enemy (player only) that had previously eluded the squad.
|
|
#define SPFORCE_AE_DROP_GUN ( 11) // SPForce (probably dead) is dropping his mp5.
|
|
|
|
//=========================================================
|
|
// monster-specific schedule types
|
|
//=========================================================
|
|
enum
|
|
{
|
|
SCHED_SPFORCE_SUPPRESS = LAST_COMMON_SCHEDULE + 1,
|
|
SCHED_SPFORCE_ESTABLISH_LINE_OF_FIRE,// move to a location to set up an attack against the enemy. (usually when a friendly is in the way).
|
|
SCHED_SPFORCE_COVER_AND_RELOAD,
|
|
SCHED_SPFORCE_SWEEP,
|
|
SCHED_SPFORCE_FOUND_ENEMY,
|
|
SCHED_SPFORCE_REPEL,
|
|
SCHED_SPFORCE_REPEL_ATTACK,
|
|
SCHED_SPFORCE_REPEL_LAND,
|
|
SCHED_SPFORCE_WAIT_FACE_ENEMY,
|
|
SCHED_SPFORCE_TAKECOVER_FAILED,// special schedule type that forces analysis of conditions and picks the best possible schedule to recover from this type of failure.
|
|
SCHED_SPFORCE_ELOF_FAIL,
|
|
};
|
|
|
|
//=========================================================
|
|
// monster-specific tasks
|
|
//=========================================================
|
|
enum
|
|
{
|
|
TASK_SPFORCE_FACE_TOSS_DIR = LAST_COMMON_TASK + 1,
|
|
TASK_SPFORCE_SPEAK_SENTENCE,
|
|
TASK_SPFORCE_CHECK_FIRE,
|
|
};
|
|
|
|
//=========================================================
|
|
// monster-specific conditions
|
|
//=========================================================
|
|
#define bits_COND_SPFORCE_NOFIRE ( bits_COND_SPECIAL1 )
|
|
|
|
class CSPForce : public CHGrunt
|
|
{
|
|
public:
|
|
void Spawn( void );
|
|
void Precache( void );
|
|
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
|
void Shoot ( void );
|
|
void Beretta ( void );
|
|
void GibMonster( void );
|
|
|
|
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
|
|
|
int IRelationship ( CBaseEntity *pTarget );
|
|
};
|
|
|
|
LINK_ENTITY_TO_CLASS( monster_human_spforce, CSPForce );
|
|
|
|
//=========================================================
|
|
// IRelationship - overridden because Alien Grunts are
|
|
// Human grunt's nemesis.
|
|
//=========================================================
|
|
int CSPForce::IRelationship ( CBaseEntity *pTarget )
|
|
{
|
|
if ( FClassnameIs( pTarget->pev, "monster_alien_grunt" ) || ( FClassnameIs( pTarget->pev, "monster_gargantua" ) ) )
|
|
{
|
|
return R_NM;
|
|
}
|
|
else if( FClassnameIs( pTarget->pev, "monster_human_grunt" ) )
|
|
{
|
|
return R_HT;
|
|
}
|
|
|
|
return CSquadMonster::IRelationship( pTarget );
|
|
}
|
|
|
|
//=========================================================
|
|
// GibMonster - make gun fly through the air.
|
|
//=========================================================
|
|
void CSPForce :: GibMonster ( void )
|
|
{
|
|
Vector vecGunPos;
|
|
Vector vecGunAngles;
|
|
|
|
if ( GetBodygroup( 2 ) != 2 )
|
|
{// throw a gun if the SPForce has one
|
|
GetAttachment( 0, vecGunPos, vecGunAngles );
|
|
|
|
CBaseEntity *pGun;
|
|
//Alex994 begin - for beretta spawning
|
|
if (FBitSet( pev->weapons, SPFORCE_BERETTA ))
|
|
{
|
|
pGun = DropItem( "weapon_beretta", vecGunPos, vecGunAngles );
|
|
}
|
|
//Alex994 end
|
|
else
|
|
{
|
|
pGun = DropItem( "weapon_9mmm41a", vecGunPos, vecGunAngles );
|
|
}
|
|
if ( pGun )
|
|
{
|
|
pGun->pev->velocity = Vector (RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300));
|
|
pGun->pev->avelocity = Vector ( 0, RANDOM_FLOAT( 200, 400 ), 0 );
|
|
}
|
|
|
|
if (FBitSet( pev->weapons, SPFORCE_GRENADELAUNCHER ))
|
|
{
|
|
pGun = DropItem( "ammo_ARgrenades", vecGunPos, vecGunAngles );
|
|
if ( pGun )
|
|
{
|
|
pGun->pev->velocity = Vector (RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300));
|
|
pGun->pev->avelocity = Vector ( 0, RANDOM_FLOAT( 200, 400 ), 0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
CBaseMonster :: GibMonster();
|
|
}
|
|
|
|
//=========================================================
|
|
// TakeDamage - overridden for the SPForce because the SPForce
|
|
// needs to forget that he is in cover if he's hurt. (Obviously
|
|
// not in a safe place anymore).
|
|
//=========================================================
|
|
int CSPForce :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
|
{
|
|
Forget( bits_MEMORY_INCOVER );
|
|
|
|
return CSquadMonster :: TakeDamage ( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
|
}
|
|
|
|
//=========================================================
|
|
// Shoot
|
|
//=========================================================
|
|
void CSPForce :: Shoot ( void )
|
|
{
|
|
if (m_hEnemy == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Vector vecShootOrigin = GetGunPosition();
|
|
Vector vecShootDir = ShootAtEnemy( vecShootOrigin );
|
|
|
|
UTIL_MakeVectors ( pev->angles );
|
|
|
|
Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40,90) + gpGlobals->v_up * RANDOM_FLOAT(75,200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40);
|
|
EjectBrass ( vecShootOrigin - vecShootDir * 24, vecShellVelocity, pev->angles.y, m_iBrassShell, TE_BOUNCE_SHELL);
|
|
FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_10DEGREES, 2048, BULLET_MONSTER_MP5); // shoot +-5 degrees
|
|
|
|
pev->effects |= EF_MUZZLEFLASH;
|
|
|
|
m_cAmmoLoaded--;// take away a bullet!
|
|
|
|
Vector angDir = UTIL_VecToAngles( vecShootDir );
|
|
SetBlending( 0, angDir.x );
|
|
}
|
|
|
|
//=========================================================
|
|
// Shoot
|
|
//=========================================================
|
|
void CSPForce :: Beretta ( void )
|
|
{
|
|
if (m_hEnemy == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Vector vecShootOrigin = GetGunPosition();
|
|
Vector vecShootDir = ShootAtEnemy( vecShootOrigin );
|
|
|
|
UTIL_MakeVectors ( pev->angles );
|
|
|
|
Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40,90) + gpGlobals->v_up * RANDOM_FLOAT(75,200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40);
|
|
EjectBrass ( vecShootOrigin - vecShootDir * 24, vecShellVelocity, pev->angles.y, m_iBrassShell, TE_BOUNCE_SHELL);
|
|
FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_4DEGREES, 2048, BULLET_MONSTER_9MM, 0 ); // shoot +-7.5 degrees
|
|
|
|
pev->effects |= EF_MUZZLEFLASH;
|
|
|
|
m_cAmmoLoaded--;// take away a bullet!
|
|
|
|
Vector angDir = UTIL_VecToAngles( vecShootDir );
|
|
SetBlending( 0, angDir.x );
|
|
}
|
|
|
|
//=========================================================
|
|
// HandleAnimEvent - catches the monster-specific messages
|
|
// that occur when tagged animation frames are played.
|
|
//=========================================================
|
|
void CSPForce :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
|
{
|
|
Vector vecShootDir;
|
|
Vector vecShootOrigin;
|
|
|
|
switch( pEvent->event )
|
|
{
|
|
case SPFORCE_AE_DROP_GUN:
|
|
{
|
|
Vector vecGunPos;
|
|
Vector vecGunAngles;
|
|
|
|
GetAttachment( 0, vecGunPos, vecGunAngles );
|
|
|
|
// switch to body group with no gun.
|
|
SetBodygroup( GUN_GROUP, GUN_NONE );
|
|
|
|
//Alex begin
|
|
if (FBitSet( pev->weapons, SPFORCE_BERETTA ))
|
|
{
|
|
DropItem( "weapon_beretta", vecGunPos, vecGunAngles );
|
|
}
|
|
//Alex end
|
|
else
|
|
{
|
|
DropItem( "weapon_9mmm41a", vecGunPos, vecGunAngles );
|
|
}
|
|
if (FBitSet( pev->weapons, SPFORCE_GRENADELAUNCHER ))
|
|
{
|
|
DropItem( "ammo_ARgrenades", BodyTarget( pev->origin ), vecGunAngles );
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case SPFORCE_AE_RELOAD:
|
|
// the first round of the three round burst plays the sound and puts a sound in the world sound list.
|
|
if ( FBitSet( pev->weapons, SPFORCE_BERETTA ) )
|
|
{
|
|
EMIT_SOUND( ENT(pev), CHAN_WEAPON, "spforce/spf_reload2.wav", 1, ATTN_NORM );
|
|
}
|
|
else
|
|
{
|
|
EMIT_SOUND( ENT(pev), CHAN_WEAPON, "spforce/spf_reload1.wav", 1, ATTN_NORM );
|
|
}
|
|
|
|
m_cAmmoLoaded = m_cClipSize;
|
|
ClearConditions(bits_COND_NO_AMMO_LOADED);
|
|
break;
|
|
|
|
case SPFORCE_AE_GREN_TOSS:
|
|
{
|
|
UTIL_MakeVectors( pev->angles );
|
|
// CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32), m_vecTossVelocity, 3.5 );
|
|
CGrenade::ShootTimed( pev, GetGunPosition(), m_vecTossVelocity, 3.5 );
|
|
|
|
m_fThrowGrenade = FALSE;
|
|
m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown.
|
|
// !!!LATER - when in a group, only try to throw grenade if ordered.
|
|
}
|
|
break;
|
|
|
|
case SPFORCE_AE_GREN_LAUNCH:
|
|
{
|
|
//EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/m41aglauncher.wav", 0.8, ATTN_NORM);
|
|
if ( RANDOM_LONG(0,1) )
|
|
{
|
|
EMIT_SOUND( ENT(pev), CHAN_WEAPON, "weapons/m41aglauncher.wav", 0.8, ATTN_NORM );
|
|
}
|
|
else
|
|
{
|
|
EMIT_SOUND( ENT(pev), CHAN_WEAPON, "weapons/m41aglauncher2.wav", 0.8, ATTN_NORM );
|
|
}
|
|
CGrenade::ShootContact( pev, GetGunPosition(), m_vecTossVelocity, gSkillData.plrDmgM41AGrenade );
|
|
m_fThrowGrenade = FALSE;
|
|
if (g_iSkillLevel == SKILL_HARD)
|
|
m_flNextGrenadeCheck = gpGlobals->time + RANDOM_FLOAT( 2, 5 );// wait a random amount of time before shooting again
|
|
else
|
|
m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown.
|
|
}
|
|
break;
|
|
|
|
case SPFORCE_AE_GREN_DROP:
|
|
{
|
|
UTIL_MakeVectors( pev->angles );
|
|
CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 17 - gpGlobals->v_right * 27 + gpGlobals->v_up * 6, g_vecZero, 3 );
|
|
}
|
|
break;
|
|
|
|
case SPFORCE_AE_BURST1:
|
|
{
|
|
if ( FBitSet( pev->weapons, SPFORCE_M41A ))
|
|
{
|
|
Shoot();
|
|
|
|
// the first round of the three round burst plays the sound and puts a sound in the world sound list.
|
|
if ( RANDOM_LONG(0,1) )
|
|
{
|
|
EMIT_SOUND( ENT(pev), CHAN_WEAPON, "weapons/m41ahks1.wav", 1, ATTN_NORM );
|
|
}
|
|
else
|
|
{
|
|
EMIT_SOUND( ENT(pev), CHAN_WEAPON, "weapons/m41ahks2.wav", 1, ATTN_NORM );
|
|
}
|
|
}
|
|
//Alex begin
|
|
else
|
|
{
|
|
Beretta();
|
|
|
|
// the first round of the three round burst plays the sound and puts a sound in the world sound list.
|
|
EMIT_SOUND( ENT(pev), CHAN_WEAPON, "weapons/beretta_fire1.wav", 1, ATTN_NORM );
|
|
|
|
}
|
|
|
|
CSoundEnt::InsertSound ( bits_SOUND_COMBAT, pev->origin, 384, 0.3 );
|
|
}
|
|
break;
|
|
|
|
case SPFORCE_AE_BURST2:
|
|
case SPFORCE_AE_BURST3:
|
|
Shoot();
|
|
break;
|
|
|
|
case SPFORCE_AE_KICK:
|
|
{
|
|
CBaseEntity *pHurt = Kick();
|
|
|
|
if ( pHurt )
|
|
{
|
|
// SOUND HERE!
|
|
EMIT_SOUND( ENT(pev), CHAN_WEAPON, "common/kick.wav", 1, ATTN_NORM );
|
|
UTIL_MakeVectors( pev->angles );
|
|
pHurt->pev->punchangle.x = 15;
|
|
pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50;
|
|
pHurt->TakeDamage( pev, pev, gSkillData.hgruntDmgKick, DMG_CLUB );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SPFORCE_AE_CAUGHT_ENEMY:
|
|
{
|
|
if ( FOkToSpeak() )
|
|
{
|
|
SENTENCEG_PlayRndSz(ENT(pev), "HG_ALERT", SPFORCE_SENTENCE_VOLUME, SPFORCE_ATTN, 0, m_voicePitch);
|
|
JustSpoke();
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
CSquadMonster::HandleAnimEvent( pEvent );
|
|
break;
|
|
}
|
|
}
|
|
|
|
//=========================================================
|
|
// Spawn
|
|
//=========================================================
|
|
void CSPForce :: Spawn()
|
|
{
|
|
Precache( );
|
|
|
|
SET_MODEL(ENT(pev), "models/spforce.mdl");
|
|
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
|
|
|
pev->solid = SOLID_SLIDEBOX;
|
|
pev->movetype = MOVETYPE_STEP;
|
|
m_bloodColor = BLOOD_COLOR_RED;
|
|
pev->effects = 0;
|
|
pev->health = gSkillData.hgruntHealth;
|
|
m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
|
m_MonsterState = MONSTERSTATE_NONE;
|
|
m_flNextGrenadeCheck = gpGlobals->time + 1;
|
|
m_flNextPainTime = gpGlobals->time;
|
|
m_iSentence = HGRUNT_SENT_NONE;
|
|
|
|
m_afCapability = bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
|
|
|
|
m_fEnemyEluded = FALSE;
|
|
m_fFirstEncounter = TRUE;// this is true when the SPForce spawns, because he hasn't encountered an enemy yet.
|
|
|
|
m_HackedGunPos = Vector ( 0, 0, 55 );
|
|
|
|
if (pev->weapons == 0)
|
|
{
|
|
// initialize to original values
|
|
pev->weapons = SPFORCE_M41A | SPFORCE_HANDGRENADE;
|
|
// pev->weapons = SPFORCE_SHOTGUN;
|
|
// pev->weapons = SPFORCE_9MMAR | SPFORCE_GRENADELAUNCHER;
|
|
}
|
|
|
|
if (FBitSet( pev->weapons, SPFORCE_BERETTA ))
|
|
{
|
|
SetBodygroup( GUN_GROUP, GUN_BERETTA );
|
|
m_cClipSize = 15;
|
|
}
|
|
else
|
|
{
|
|
m_cClipSize = 45;
|
|
}
|
|
m_cAmmoLoaded = m_cClipSize;
|
|
|
|
if (FBitSet( pev->weapons, SPFORCE_BERETTA ))
|
|
{
|
|
SetBodygroup( HEAD_GROUP, HEAD_BERETTA);
|
|
}
|
|
else if (FBitSet( pev->weapons, SPFORCE_GRENADELAUNCHER ))
|
|
{
|
|
SetBodygroup( HEAD_GROUP, HEAD_M203 );
|
|
}
|
|
|
|
CTalkMonster::g_talkWaitTime = 0;
|
|
|
|
MonsterInit();
|
|
}
|
|
|
|
//=========================================================
|
|
// Precache - precaches all resources this monster needs
|
|
//=========================================================
|
|
void CSPForce :: Precache()
|
|
{
|
|
PRECACHE_MODEL( "models/spforce.mdl" );
|
|
|
|
PRECACHE_SOUND( "weapons/m41ahks1.wav" );
|
|
PRECACHE_SOUND( "weapons/m41ahks2.wav" );
|
|
|
|
PRECACHE_SOUND( "hgrunt/gr_die1.wav" );
|
|
PRECACHE_SOUND( "hgrunt/gr_die2.wav" );
|
|
PRECACHE_SOUND( "hgrunt/gr_die3.wav" );
|
|
|
|
PRECACHE_SOUND( "hgrunt/gr_pain1.wav" );
|
|
PRECACHE_SOUND( "hgrunt/gr_pain2.wav" );
|
|
PRECACHE_SOUND( "hgrunt/gr_pain3.wav" );
|
|
PRECACHE_SOUND( "hgrunt/gr_pain4.wav" );
|
|
PRECACHE_SOUND( "hgrunt/gr_pain5.wav" );
|
|
|
|
PRECACHE_SOUND( "spforce/spf_reload1.wav" );
|
|
PRECACHE_SOUND( "spforce/spf_reload2.wav" );
|
|
|
|
PRECACHE_SOUND( "weapons/m41aglauncher.wav" );
|
|
PRECACHE_SOUND( "weapons/m41aglauncher2.wav" );
|
|
|
|
PRECACHE_SOUND( "weapons/beretta_fire1.wav" );
|
|
PRECACHE_SOUND( "zombie/claw_miss2.wav" );// because we use the basemonster SWIPE animation event
|
|
PRECACHE_SOUND( "common/kick.wav" );
|
|
|
|
// get voice pitch
|
|
m_voicePitch = 92 + RANDOM_LONG( 0, 6 );
|
|
|
|
m_iBrassShell = PRECACHE_MODEL( "models/shell.mdl" );// brass shell
|
|
}
|
|
|
|
//=========================================================
|
|
// CSPForceRepel - when triggered, spawns a monster_human_spforce
|
|
// repelling down a line.
|
|
//=========================================================
|
|
|
|
class CSPForceRepel : public CBaseMonster
|
|
{
|
|
public:
|
|
void Spawn( void );
|
|
void Precache( void );
|
|
void EXPORT RepelUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
|
int m_iSpriteTexture; // Don't save, precache
|
|
};
|
|
|
|
LINK_ENTITY_TO_CLASS( monster_spforce_repel, CSPForceRepel );
|
|
|
|
void CSPForceRepel::Spawn( void )
|
|
{
|
|
Precache( );
|
|
pev->solid = SOLID_NOT;
|
|
|
|
SetUse( &CSPForceRepel::RepelUse );
|
|
}
|
|
|
|
void CSPForceRepel::Precache( void )
|
|
{
|
|
UTIL_PrecacheOther( "monster_human_spforce" );
|
|
m_iSpriteTexture = PRECACHE_MODEL( "sprites/rope.spr" );
|
|
}
|
|
|
|
void CSPForceRepel::RepelUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
|
{
|
|
TraceResult tr;
|
|
UTIL_TraceLine( pev->origin, pev->origin + Vector( 0, 0, -4096.0), dont_ignore_monsters, ENT(pev), &tr);
|
|
/*
|
|
if ( tr.pHit && Instance( tr.pHit )->pev->solid != SOLID_BSP)
|
|
return NULL;
|
|
*/
|
|
|
|
CBaseEntity *pEntity = Create( "monster_human_spforce", pev->origin, pev->angles );
|
|
CBaseMonster *pSPForce = pEntity->MyMonsterPointer( );
|
|
pSPForce->pev->movetype = MOVETYPE_FLY;
|
|
pSPForce->pev->velocity = Vector( 0, 0, RANDOM_FLOAT( -196, -128 ) );
|
|
pSPForce->SetActivity( ACT_GLIDE );
|
|
// UNDONE: position?
|
|
pSPForce->m_vecLastPosition = tr.vecEndPos;
|
|
|
|
CBeam *pBeam = CBeam::BeamCreate( "sprites/rope.spr", 10 );
|
|
pBeam->PointEntInit( pev->origin + Vector(0,0,112), pSPForce->entindex() );
|
|
pBeam->SetFlags( BEAM_FSOLID );
|
|
pBeam->SetColor( 255, 255, 255 );
|
|
pBeam->SetThink( &CBeam::SUB_Remove );
|
|
pBeam->pev->nextthink = gpGlobals->time + -4096.0 * tr.flFraction / pSPForce->pev->velocity.z + 0.5;
|
|
|
|
UTIL_Remove( this );
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
// DEAD SPFORCE PROP
|
|
//=========================================================
|
|
class CDeadSPForce : public CBaseMonster
|
|
{
|
|
public:
|
|
void Spawn( void );
|
|
int Classify ( void ) { return CLASS_HUMAN_MILITARY; }
|
|
|
|
void KeyValue( KeyValueData *pkvd );
|
|
|
|
int m_iPose;// which sequence to display -- temporary, don't need to save
|
|
static const char *m_szPoses[3];
|
|
};
|
|
|
|
const char *CDeadSPForce::m_szPoses[] = { "deadstomach", "deadside", "deadsitting" };
|
|
|
|
void CDeadSPForce::KeyValue( KeyValueData *pkvd )
|
|
{
|
|
if (FStrEq(pkvd->szKeyName, "pose"))
|
|
{
|
|
m_iPose = atoi(pkvd->szValue);
|
|
pkvd->fHandled = TRUE;
|
|
}
|
|
else
|
|
CBaseMonster::KeyValue( pkvd );
|
|
}
|
|
|
|
LINK_ENTITY_TO_CLASS( monster_spforce_dead, CDeadSPForce );
|
|
|
|
//=========================================================
|
|
// ********** DeadSPForce SPAWN **********
|
|
//=========================================================
|
|
void CDeadSPForce :: Spawn( void )
|
|
{
|
|
PRECACHE_MODEL("models/spforce.mdl");
|
|
SET_MODEL(ENT(pev), "models/spforce.mdl");
|
|
|
|
pev->effects = 0;
|
|
pev->yaw_speed = 8;
|
|
pev->sequence = 0;
|
|
m_bloodColor = BLOOD_COLOR_RED;
|
|
|
|
pev->sequence = LookupSequence( m_szPoses[m_iPose] );
|
|
|
|
if (pev->sequence == -1)
|
|
{
|
|
ALERT ( at_console, "Dead SPForce with bad pose\n" );
|
|
}
|
|
|
|
// Corpses have less health
|
|
pev->health = 8;
|
|
|
|
// map old bodies onto new bodies
|
|
switch( pev->body )
|
|
{
|
|
case 0: // SPForce with Gun
|
|
pev->body = 0;
|
|
pev->skin = 0;
|
|
SetBodygroup( HEAD_GROUP, HEAD_SPForce );
|
|
SetBodygroup( GUN_GROUP, GUN_M41A );
|
|
break;
|
|
case 1: // Commander with Gun
|
|
pev->body = 0;
|
|
pev->skin = 0;
|
|
SetBodygroup( HEAD_GROUP, HEAD_COMMANDER );
|
|
SetBodygroup( GUN_GROUP, GUN_M41A );
|
|
break;
|
|
case 2: // SPForce no Gun
|
|
pev->body = 0;
|
|
pev->skin = 0;
|
|
SetBodygroup( HEAD_GROUP, HEAD_SPForce );
|
|
SetBodygroup( GUN_GROUP, GUN_NONE );
|
|
break;
|
|
case 3: // Commander no Gun
|
|
pev->body = 0;
|
|
pev->skin = 0;
|
|
SetBodygroup( HEAD_GROUP, HEAD_COMMANDER );
|
|
SetBodygroup( GUN_GROUP, GUN_NONE );
|
|
break;
|
|
}
|
|
|
|
MonsterInitDead();
|
|
}
|