Add monster_shocktrooper.
This commit is contained in:
12
README.md
12
README.md
@@ -43,6 +43,18 @@ You can recompile the source code under g++ 4.8 and use the newly generated bina
|
||||
|
||||
Alternatively, you can "remove" the outdated library to force HLDS to use the libstdc++ provided by the linux distro, which is generally more up to date. You might need to install GCC/G++ on the operating system if it doesn't work.
|
||||
|
||||
## Known Bugs
|
||||
|
||||
There are a few bugs that to this day I'm unable to find out why it happens:
|
||||
|
||||
- Human Grunts are completely unable to reload their weapons. As a workaround, infinite ammo has been given to them.
|
||||
|
||||
- Male Assassins share the same AI as HGrunts, so their ammo problem is still a thing, and the same workaround is used.
|
||||
|
||||
- Shock Troopers have it worst. They also share HGrunts AI code, and despite them automatically increasing their "bullets", they just eventually stop firing. Worse, taking cover is absolutely broken, and remain completely frozen in place when it happens.
|
||||
|
||||
Any hint that helps me fix these issues are greatly appreciated.
|
||||
|
||||
## Milestones
|
||||
|
||||
Attempting to recreate everything in one go is a daunting task.
|
||||
|
||||
@@ -31,3 +31,4 @@
|
||||
//monster_male_assassin
|
||||
//monster_otis
|
||||
//monster_pitdrone
|
||||
//monster_shocktrooper
|
||||
|
||||
@@ -109,6 +109,18 @@ sk_pitdrone_dmg_bite 25
|
||||
sk_pitdrone_dmg_whip 35
|
||||
sk_pitdrone_dmg_spit 10
|
||||
|
||||
// Shock Roach
|
||||
sk_shockroach_health 10
|
||||
sk_shockroach_lifespan 10
|
||||
|
||||
// ShockTrooper
|
||||
sk_shocktrooper_health 50
|
||||
sk_shocktrooper_kick 10
|
||||
sk_shocktrooper_maxcharge 8
|
||||
sk_shocktrooper_rchgspeed 1
|
||||
sk_shock_dmg 15
|
||||
sk_spore_dmg 50
|
||||
|
||||
// MONSTER WEAPON DAMAGE
|
||||
sk_9mm_bullet 5
|
||||
sk_9mmAR_bullet 4
|
||||
|
||||
@@ -39,9 +39,13 @@ OBJ = \
|
||||
otis.o \
|
||||
pitdrone.o \
|
||||
scientist.o \
|
||||
shock.o \
|
||||
shockroach.o \
|
||||
skill.o \
|
||||
sound.o \
|
||||
sporegrenade.o \
|
||||
squeakgrenade.o \
|
||||
strooper.o \
|
||||
subs.o \
|
||||
talkmonster.o \
|
||||
turret.o \
|
||||
|
||||
@@ -347,7 +347,7 @@ public:
|
||||
|
||||
virtual float GetDamageAmount( void ) { return gSkillData.headcrabDmgBite; }
|
||||
virtual int GetVoicePitch( void ) { return 100; }
|
||||
virtual float GetSoundVolue( void ) { return 1.0; }
|
||||
virtual float GetSoundVolume( void ) { return 1.0; }
|
||||
Schedule_t* GetScheduleOfType ( int Type );
|
||||
|
||||
CUSTOM_SCHEDULES;
|
||||
@@ -370,7 +370,7 @@ public:
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
Schedule_t* GetScheduleOfType ( int Type );
|
||||
virtual int GetVoicePitch( void ) { return PITCH_NORM + RANDOM_LONG(40,50); }
|
||||
virtual float GetSoundVolue( void ) { return 0.8; }
|
||||
virtual float GetSoundVolume( void ) { return 0.8; }
|
||||
};
|
||||
|
||||
|
||||
@@ -1416,4 +1416,78 @@ public:
|
||||
static const char *pAttackMissSounds[];
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Shock Roach
|
||||
//=========================================================
|
||||
class CMShockRoach : public CMHeadCrab
|
||||
{
|
||||
public:
|
||||
void Spawn(void);
|
||||
void Precache(void);
|
||||
void EXPORT LeapTouch(edict_t *pOther);
|
||||
void PainSound(void);
|
||||
void DeathSound(void);
|
||||
void IdleSound(void);
|
||||
void AlertSound(void);
|
||||
void MonsterThink(void);
|
||||
void StartTask(Task_t* pTask);
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
|
||||
static const char *pIdleSounds[];
|
||||
static const char *pAlertSounds[];
|
||||
static const char *pPainSounds[];
|
||||
static const char *pAttackSounds[];
|
||||
static const char *pDeathSounds[];
|
||||
static const char *pBiteSounds[];
|
||||
|
||||
float m_flBirthTime;
|
||||
BOOL m_fRoachSolid;
|
||||
|
||||
protected:
|
||||
void AttackSound();
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Shock Trooper
|
||||
//=========================================================
|
||||
class CMStrooper : public CMHGrunt
|
||||
{
|
||||
public:
|
||||
void Spawn(void);
|
||||
void MonsterThink();
|
||||
void Precache(void);
|
||||
int Classify(void);
|
||||
BOOL CheckRangeAttack1(float flDot, float flDist);
|
||||
BOOL CheckRangeAttack2(float flDot, float flDist);
|
||||
void HandleAnimEvent(MonsterEvent_t *pEvent);
|
||||
void SetObjectCollisionBox( void )
|
||||
{
|
||||
pev->absmin = pev->origin + Vector( -24, -24, 0 );
|
||||
pev->absmax = pev->origin + Vector( 24, 24, 72 );
|
||||
}
|
||||
|
||||
void SetActivity(Activity NewActivity);
|
||||
|
||||
void DeathSound(void);
|
||||
void PainSound(void);
|
||||
void IdleSound(void);
|
||||
void GibMonster(void);
|
||||
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
|
||||
void DropShockRoach(bool gibbed);
|
||||
|
||||
Schedule_t *GetSchedule(void);
|
||||
Schedule_t *GetScheduleOfType(int Type);
|
||||
|
||||
void SpeakSentence();
|
||||
|
||||
BOOL m_fRightClaw;
|
||||
float m_rechargeTime;
|
||||
float m_blinkTime;
|
||||
float m_eyeChangeTime;
|
||||
|
||||
static const char *pGruntSentences[];
|
||||
};
|
||||
|
||||
#endif // BASEMONSTER_H
|
||||
|
||||
@@ -156,6 +156,8 @@ monster_type_t monster_types[]=
|
||||
"monster_male_assassin", FALSE,
|
||||
"monster_otis", FALSE,
|
||||
"monster_pitdrone", FALSE,
|
||||
"monster_shockroach", FALSE,
|
||||
"monster_shocktrooper", FALSE,
|
||||
"info_node", FALSE, // Nodes
|
||||
"info_node_air", FALSE,
|
||||
"", FALSE
|
||||
@@ -620,6 +622,8 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
|
||||
case 19: monsters[monster_index].pMonster = CreateClassPtr((CMMassn *)NULL); break;
|
||||
case 20: monsters[monster_index].pMonster = CreateClassPtr((CMOtis *)NULL); break;
|
||||
case 21: monsters[monster_index].pMonster = CreateClassPtr((CMPitdrone *)NULL); break;
|
||||
case 22: monsters[monster_index].pMonster = CreateClassPtr((CMShockRoach *)NULL); break;
|
||||
case 23: monsters[monster_index].pMonster = CreateClassPtr((CMStrooper *)NULL); break;
|
||||
}
|
||||
|
||||
if (monsters[monster_index].pMonster == NULL)
|
||||
@@ -711,6 +715,7 @@ void check_respawn(void)
|
||||
|
||||
DLL_GLOBAL short g_sModelIndexFireball;// holds the index for the fireball
|
||||
DLL_GLOBAL short g_sModelIndexSmoke;// holds the index for the smoke cloud
|
||||
DLL_GLOBAL short g_sModelIndexTinySpit;// holds the index for the spore grenade explosion
|
||||
DLL_GLOBAL short g_sModelIndexWExplosion;// holds the index for the underwater explosion
|
||||
DLL_GLOBAL short g_sModelIndexBubbles;// holds the index for the bubbles model
|
||||
DLL_GLOBAL short g_sModelIndexBloodDrop;// holds the sprite index for the initial blood
|
||||
@@ -723,6 +728,7 @@ void world_precache(void)
|
||||
{
|
||||
g_sModelIndexFireball = PRECACHE_MODEL ("sprites/zerogxplode.spr");// fireball
|
||||
g_sModelIndexSmoke = PRECACHE_MODEL ("sprites/steam1.spr");// smoke
|
||||
g_sModelIndexTinySpit = PRECACHE_MODEL ("sprites/tinyspit.spr");// spore
|
||||
g_sModelIndexWExplosion = PRECACHE_MODEL ("sprites/WXplo1.spr");// underwater fireball
|
||||
g_sModelIndexBubbles = PRECACHE_MODEL ("sprites/bubble.spr");//bubbles
|
||||
g_sModelIndexBloodSpray = PRECACHE_MODEL ("sprites/bloodspray.spr"); // initial blood
|
||||
@@ -1299,6 +1305,8 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
|
||||
CMMassn massn;
|
||||
CMOtis otis;
|
||||
CMPitdrone pitdrone;
|
||||
CMShockRoach shockroach;
|
||||
CMStrooper strooper;
|
||||
|
||||
g_psv_gravity = CVAR_GET_POINTER( "sv_gravity" );
|
||||
|
||||
@@ -1338,6 +1346,8 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
|
||||
case 19: massn.Precache(); break;
|
||||
case 20: otis.Precache(); break;
|
||||
case 21: pitdrone.Precache(); break;
|
||||
case 22: shockroach.Precache(); break;
|
||||
case 23: strooper.Precache(); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
#define SF_ENVEXPLOSION_NODECAL ( 1 << 4 ) // don't make a scorch mark
|
||||
#define SF_ENVEXPLOSION_NOSPARKS ( 1 << 5 ) // don't make a scorch mark
|
||||
|
||||
extern DLL_GLOBAL short g_sModelIndexFireball;
|
||||
extern DLL_GLOBAL short g_sModelIndexSmoke;
|
||||
|
||||
extern DLL_GLOBAL short g_sModelIndexFireball;
|
||||
extern DLL_GLOBAL short g_sModelIndexSmoke;
|
||||
extern DLL_GLOBAL short g_sModelIndexTinySpit;
|
||||
|
||||
extern void ExplosionCreate( const Vector ¢er, const Vector &angles, edict_t *pOwner, int magnitude, int flags, float delay );
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ void CMHeadCrab :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
|
||||
int iSound = RANDOM_LONG(0,2);
|
||||
if ( iSound != 0 )
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, pAttackSounds[iSound], GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, pAttackSounds[iSound], GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
|
||||
pev->velocity = vecJumpDir;
|
||||
m_flNextAttack = gpGlobals->time + 2;
|
||||
@@ -325,7 +325,7 @@ void CMHeadCrab :: LeapTouch ( edict_t *pOther )
|
||||
// Don't hit if back on ground
|
||||
if ( !FBitSet( pev->flags, FL_ONGROUND ) )
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
|
||||
if (UTIL_IsPlayer(pOther))
|
||||
UTIL_TakeDamage( pOther, pev, pev, GetDamageAmount(), DMG_SLASH );
|
||||
@@ -359,7 +359,7 @@ void CMHeadCrab :: StartTask ( Task_t *pTask )
|
||||
{
|
||||
case TASK_RANGE_ATTACK1:
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, pAttackSounds[0], GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, pAttackSounds[0], GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
m_IdealActivity = ACT_RANGE_ATTACK1;
|
||||
SetTouch ( &CMHeadCrab::LeapTouch );
|
||||
break;
|
||||
@@ -415,7 +415,7 @@ int CMHeadCrab :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, f
|
||||
#define CRAB_ATTN_IDLE (float)1.5
|
||||
void CMHeadCrab :: IdleSound ( void )
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
@@ -423,7 +423,7 @@ void CMHeadCrab :: IdleSound ( void )
|
||||
//=========================================================
|
||||
void CMHeadCrab :: AlertSound ( void )
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
@@ -431,7 +431,7 @@ void CMHeadCrab :: AlertSound ( void )
|
||||
//=========================================================
|
||||
void CMHeadCrab :: PainSound ( void )
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
@@ -439,7 +439,7 @@ void CMHeadCrab :: PainSound ( void )
|
||||
//=========================================================
|
||||
void CMHeadCrab :: DeathSound ( void )
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
}
|
||||
|
||||
Schedule_t* CMHeadCrab :: GetScheduleOfType ( int Type )
|
||||
|
||||
233
src/dlls/shock.cpp
Normal file
233
src/dlls/shock.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
|
||||
|
||||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// shock - projectile shot from shockrifles.
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cmbase.h"
|
||||
#include "cmbasemonster.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "effects.h"
|
||||
#include "decals.h"
|
||||
#include "weapons.h"
|
||||
#include "customentity.h"
|
||||
#include "shock.h"
|
||||
|
||||
|
||||
void CMShock::Spawn()
|
||||
{
|
||||
Precache();
|
||||
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
pev->solid = SOLID_BBOX;
|
||||
pev->classname = MAKE_STRING("shock_beam");
|
||||
SET_MODEL(ENT(pev), "models/shock_effect.mdl");
|
||||
UTIL_SetOrigin(pev, pev->origin);
|
||||
pev->dmg = gSkillData.monDmgShockroach;
|
||||
UTIL_SetSize(pev, Vector(-4, -4, -4), Vector(4, 4, 4));
|
||||
|
||||
CreateEffects();
|
||||
SetThink( &CMShock::FlyThink );
|
||||
pev->nextthink = gpGlobals->time;
|
||||
}
|
||||
|
||||
void CMShock::Precache()
|
||||
{
|
||||
PRECACHE_MODEL("sprites/flare3.spr");
|
||||
PRECACHE_MODEL("sprites/lgtning.spr");
|
||||
PRECACHE_MODEL("models/shock_effect.mdl");
|
||||
PRECACHE_SOUND("weapons/shock_impact.wav");
|
||||
}
|
||||
|
||||
void CMShock::FlyThink()
|
||||
{
|
||||
if (pev->waterlevel == 3)
|
||||
{
|
||||
entvars_t *pevOwner = VARS(pev->owner);
|
||||
EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/shock_impact.wav", VOL_NORM, ATTN_NORM);
|
||||
RadiusDamage(pev->origin, pev, pevOwner ? pevOwner : pev, pev->dmg * 3, 144, CLASS_NONE, DMG_SHOCK | DMG_ALWAYSGIB );
|
||||
ClearEffects();
|
||||
SetThink( &CMBaseEntity::SUB_Remove );
|
||||
pev->nextthink = gpGlobals->time;
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->nextthink = gpGlobals->time + 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
edict_t *CMShock::Shoot(entvars_t *pevOwner, const Vector angles, const Vector vecStart, const Vector vecVelocity)
|
||||
{
|
||||
CMShock *pShock = CreateClassPtr((CMShock *)NULL);
|
||||
|
||||
if (pShock == NULL)
|
||||
return NULL;
|
||||
|
||||
UTIL_SetOrigin(pShock->pev, vecStart);
|
||||
pShock->Spawn();
|
||||
|
||||
pShock->pev->velocity = vecVelocity;
|
||||
pShock->pev->owner = ENT(pevOwner);
|
||||
pShock->pev->angles = angles;
|
||||
|
||||
pShock->pev->nextthink = gpGlobals->time;
|
||||
|
||||
return pShock->edict();
|
||||
}
|
||||
|
||||
void CMShock::Touch(edict_t *pOther)
|
||||
{
|
||||
// Do not collide with the owner.
|
||||
if (pOther == pev->owner)
|
||||
return;
|
||||
|
||||
TraceResult tr = UTIL_GetGlobalTrace( );
|
||||
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
|
||||
WRITE_BYTE(TE_DLIGHT);
|
||||
WRITE_COORD(pev->origin.x); // X
|
||||
WRITE_COORD(pev->origin.y); // Y
|
||||
WRITE_COORD(pev->origin.z); // Z
|
||||
WRITE_BYTE( 8 ); // radius * 0.1
|
||||
WRITE_BYTE( 0 ); // r
|
||||
WRITE_BYTE( 255 ); // g
|
||||
WRITE_BYTE( 255 ); // b
|
||||
WRITE_BYTE( 10 ); // time * 10
|
||||
WRITE_BYTE( 10 ); // decay * 0.1
|
||||
MESSAGE_END( );
|
||||
|
||||
ClearEffects();
|
||||
if (!pOther->v.takedamage)
|
||||
{
|
||||
// make a splat on the wall
|
||||
const int baseDecal = DECAL_SCORCH1;
|
||||
UTIL_DecalTrace(&tr, baseDecal + RANDOM_LONG(0, 1));
|
||||
|
||||
int iContents = UTIL_PointContents(pev->origin);
|
||||
|
||||
// Create sparks
|
||||
if (iContents != CONTENTS_WATER)
|
||||
{
|
||||
UTIL_Sparks(tr.vecEndPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int damageType = DMG_SHOCK;
|
||||
ClearMultiDamage();
|
||||
entvars_t *pevOwner = VARS(pev->owner);
|
||||
entvars_t *pevAttacker = pevOwner ? pevOwner : pev;
|
||||
|
||||
if ( UTIL_IsPlayer( pOther ) )
|
||||
UTIL_TraceAttack( pOther, pevAttacker, pev->dmg, pev->velocity.Normalize(), &tr, damageType );
|
||||
else if ( pOther->v.euser4 != NULL )
|
||||
{
|
||||
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
|
||||
pMonster->TraceAttack( pevAttacker, pev->dmg, pev->velocity.Normalize(), &tr, damageType );
|
||||
}
|
||||
|
||||
ApplyMultiDamage(pev, pevAttacker);
|
||||
}
|
||||
|
||||
// splat sound
|
||||
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/shock_impact.wav", VOL_NORM, ATTN_NORM);
|
||||
|
||||
pev->modelindex = 0;
|
||||
pev->solid = SOLID_NOT;
|
||||
SetThink( &CMBaseEntity::SUB_Remove );
|
||||
pev->nextthink = gpGlobals->time + 0.01; // let the sound play
|
||||
}
|
||||
|
||||
void CMShock::CreateEffects()
|
||||
{
|
||||
m_pSprite = CMSprite::SpriteCreate( "sprites/flare3.spr", pev->origin, FALSE );
|
||||
m_pSprite->SetAttachment( edict(), 0 );
|
||||
m_pSprite->pev->scale = 0.35;
|
||||
m_pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 170, kRenderFxNoDissipation );
|
||||
//m_pSprite->pev->spawnflags |= SF_SPRITE_TEMPORARY;
|
||||
//m_pSprite->pev->flags |= FL_SKIPLOCALHOST;
|
||||
|
||||
m_pBeam = CMBeam::BeamCreate( "sprites/lgtning.spr", 30 );
|
||||
|
||||
if (m_pBeam)
|
||||
{
|
||||
m_pBeam->EntsInit( entindex(), entindex() );
|
||||
m_pBeam->SetStartAttachment( 1 );
|
||||
m_pBeam->SetEndAttachment( 2 );
|
||||
m_pBeam->SetBrightness( 180 );
|
||||
m_pBeam->SetScrollRate( 10 );
|
||||
m_pBeam->SetNoise( 0 );
|
||||
m_pBeam->SetFlags( BEAM_FSHADEOUT );
|
||||
m_pBeam->SetColor( 0, 255, 255 );
|
||||
//m_pBeam->pev->spawnflags = SF_BEAM_TEMPORARY;
|
||||
m_pBeam->RelinkBeam();
|
||||
}
|
||||
else
|
||||
{
|
||||
ALERT(at_console, "Could not create shockbeam beam!\n");
|
||||
}
|
||||
|
||||
m_pNoise = CMBeam::BeamCreate( "sprites/lgtning.spr", 30 );
|
||||
|
||||
if (m_pNoise)
|
||||
{
|
||||
m_pNoise->EntsInit( entindex(), entindex() );
|
||||
m_pNoise->SetStartAttachment( 1 );
|
||||
m_pNoise->SetEndAttachment( 2 );
|
||||
m_pNoise->SetBrightness( 180 );
|
||||
m_pNoise->SetScrollRate( 30 );
|
||||
m_pNoise->SetNoise( 30 );
|
||||
m_pNoise->SetFlags( BEAM_FSHADEOUT );
|
||||
m_pNoise->SetColor( 255, 255, 173 );
|
||||
//m_pNoise->pev->spawnflags = SF_BEAM_TEMPORARY;
|
||||
m_pNoise->RelinkBeam();
|
||||
}
|
||||
else
|
||||
{
|
||||
ALERT(at_console, "Could not create shockbeam noise!\n");
|
||||
}
|
||||
}
|
||||
|
||||
void CMShock::ClearEffects()
|
||||
{
|
||||
if (m_pBeam)
|
||||
{
|
||||
UTIL_Remove( m_pBeam->edict() );
|
||||
m_pBeam = NULL;
|
||||
}
|
||||
|
||||
if (m_pNoise)
|
||||
{
|
||||
UTIL_Remove( m_pNoise->edict() );
|
||||
m_pNoise = NULL;
|
||||
}
|
||||
|
||||
if (m_pSprite)
|
||||
{
|
||||
UTIL_Remove( m_pSprite->edict() );
|
||||
m_pSprite = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CMShock::UpdateOnRemove()
|
||||
{
|
||||
CMBaseAnimating::UpdateOnRemove();
|
||||
ClearEffects();
|
||||
}
|
||||
25
src/dlls/shock.h
Normal file
25
src/dlls/shock.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef SHOCKBEAM_H
|
||||
#define SHOCKBEAM_H
|
||||
|
||||
//=========================================================
|
||||
// Shockrifle projectile
|
||||
//=========================================================
|
||||
class CMShock : public CMBaseAnimating
|
||||
{
|
||||
public:
|
||||
void Spawn(void);
|
||||
void Precache(void);
|
||||
|
||||
static edict_t *Shoot(entvars_t *pevOwner, const Vector angles, const Vector vecStart, const Vector vecVelocity);
|
||||
void Touch(edict_t *pOther);
|
||||
void EXPORT FlyThink();
|
||||
|
||||
void CreateEffects();
|
||||
void ClearEffects();
|
||||
void UpdateOnRemove();
|
||||
|
||||
CMBeam *m_pBeam;
|
||||
CMBeam *m_pNoise;
|
||||
CMSprite *m_pSprite;
|
||||
};
|
||||
#endif
|
||||
223
src/dlls/shockroach.cpp
Normal file
223
src/dlls/shockroach.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
|
||||
|
||||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// shockroach.cpp
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cmbase.h"
|
||||
#include "cmbasemonster.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "weapons.h"
|
||||
|
||||
const char *CMShockRoach::pIdleSounds[] =
|
||||
{
|
||||
"shockroach/shock_idle1.wav",
|
||||
"shockroach/shock_idle2.wav",
|
||||
"shockroach/shock_idle3.wav",
|
||||
};
|
||||
const char *CMShockRoach::pAlertSounds[] =
|
||||
{
|
||||
"shockroach/shock_angry.wav",
|
||||
};
|
||||
const char *CMShockRoach::pPainSounds[] =
|
||||
{
|
||||
"shockroach/shock_flinch.wav",
|
||||
};
|
||||
const char *CMShockRoach::pAttackSounds[] =
|
||||
{
|
||||
"shockroach/shock_jump1.wav",
|
||||
"shockroach/shock_jump2.wav",
|
||||
};
|
||||
|
||||
const char *CMShockRoach::pDeathSounds[] =
|
||||
{
|
||||
"shockroach/shock_die.wav",
|
||||
};
|
||||
|
||||
const char *CMShockRoach::pBiteSounds[] =
|
||||
{
|
||||
"shockroach/shock_bite.wav",
|
||||
};
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CMShockRoach::Spawn()
|
||||
{
|
||||
Precache();
|
||||
|
||||
SET_MODEL(ENT(pev), "models/w_shock_rifle.mdl");
|
||||
UTIL_SetOrigin(pev, pev->origin);
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
|
||||
pev->effects = 0;
|
||||
pev->health = gSkillData.roachHealth;
|
||||
pev->view_ofs = Vector(0, 0, 20);// position of the eyes relative to monster's origin.
|
||||
pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim?
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
|
||||
m_fRoachSolid = 0;
|
||||
m_flBirthTime = gpGlobals->time;
|
||||
|
||||
MonsterInit();
|
||||
|
||||
pev->classname = MAKE_STRING( "monster_shockroach" );
|
||||
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
|
||||
{
|
||||
// default name
|
||||
m_szMonsterName = MAKE_STRING( "Shock Roach" );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CMShockRoach::Precache()
|
||||
{
|
||||
PRECACHE_SOUND_ARRAY(pIdleSounds);
|
||||
PRECACHE_SOUND_ARRAY(pAlertSounds);
|
||||
PRECACHE_SOUND_ARRAY(pPainSounds);
|
||||
PRECACHE_SOUND_ARRAY(pAttackSounds);
|
||||
PRECACHE_SOUND_ARRAY(pDeathSounds);
|
||||
PRECACHE_SOUND_ARRAY(pBiteSounds);
|
||||
|
||||
PRECACHE_SOUND("shockroach/shock_walk.wav");
|
||||
|
||||
PRECACHE_MODEL("models/w_shock_rifle.mdl");
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// LeapTouch - this is the headcrab's touch function when it
|
||||
// is in the air
|
||||
//=========================================================
|
||||
void CMShockRoach::LeapTouch(edict_t *pOther)
|
||||
{
|
||||
if (!pOther->v.takedamage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't hit if back on ground
|
||||
if (!FBitSet(pev->flags, FL_ONGROUND))
|
||||
{
|
||||
EMIT_SOUND_DYN(edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
|
||||
|
||||
if (UTIL_IsPlayer(pOther))
|
||||
UTIL_TakeDamage( pOther, pev, pev, GetDamageAmount(), DMG_SLASH );
|
||||
else if (pOther->v.euser4 != NULL)
|
||||
{
|
||||
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
|
||||
pMonster->TakeDamage( pev, pev, GetDamageAmount(), DMG_SLASH );
|
||||
}
|
||||
}
|
||||
|
||||
SetTouch(NULL);
|
||||
}
|
||||
//=========================================================
|
||||
// PrescheduleThink
|
||||
//=========================================================
|
||||
void CMShockRoach::MonsterThink(void)
|
||||
{
|
||||
float lifeTime = (gpGlobals->time - m_flBirthTime);
|
||||
if (lifeTime >= 0.2)
|
||||
{
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
}
|
||||
if (!m_fRoachSolid && lifeTime >= 2.0 ) {
|
||||
m_fRoachSolid = TRUE;
|
||||
UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 4));
|
||||
}
|
||||
if (lifeTime >= gSkillData.roachLifespan)
|
||||
{
|
||||
pev->health = -1;
|
||||
Killed(pev, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
CMHeadCrab::MonsterThink();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// IdleSound
|
||||
//=========================================================
|
||||
void CMShockRoach::IdleSound(void)
|
||||
{
|
||||
EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AlertSound
|
||||
//=========================================================
|
||||
void CMShockRoach::AlertSound(void)
|
||||
{
|
||||
EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AlertSound
|
||||
//=========================================================
|
||||
void CMShockRoach::PainSound(void)
|
||||
{
|
||||
EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// DeathSound
|
||||
//=========================================================
|
||||
void CMShockRoach::DeathSound(void)
|
||||
{
|
||||
EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
|
||||
}
|
||||
|
||||
|
||||
void CMShockRoach::StartTask(Task_t *pTask)
|
||||
{
|
||||
m_iTaskStatus = TASKSTATUS_RUNNING;
|
||||
|
||||
switch (pTask->iTask)
|
||||
{
|
||||
case TASK_RANGE_ATTACK1:
|
||||
{
|
||||
m_IdealActivity = ACT_RANGE_ATTACK1;
|
||||
SetTouch(&CMShockRoach::LeapTouch);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
CMHeadCrab::StartTask(pTask);
|
||||
}
|
||||
}
|
||||
|
||||
int CMShockRoach::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
if ( gpGlobals->time - m_flBirthTime < 2.0 )
|
||||
flDamage = 0.0;
|
||||
// Skip headcrab's TakeDamage to avoid unwanted immunity to acid.
|
||||
return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
void CMShockRoach::AttackSound()
|
||||
{
|
||||
EMIT_SOUND_DYN(edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
|
||||
}
|
||||
@@ -83,6 +83,12 @@ skill_cfg_t skill_cfg[] = {
|
||||
{"sk_pitdrone_dmg_spit", &gSkillData.pitdroneDmgSpit},
|
||||
{"sk_pitdrone_dmg_whip", &gSkillData.pitdroneDmgWhip},
|
||||
{"sk_pitdrone_dmg_bite", &gSkillData.pitdroneDmgBite},
|
||||
{"sk_shockroach_health", &gSkillData.roachHealth},
|
||||
{"sk_shockroach_lifespan", &gSkillData.roachLifespan},
|
||||
{"sk_shocktrooper_health", &gSkillData.strooperHealth},
|
||||
{"sk_shocktrooper_kick", &gSkillData.strooperDmgKick},
|
||||
{"sk_shocktrooper_maxcharge", &gSkillData.strooperMaxCharge},
|
||||
{"sk_shocktrooper_rchgspeed", &gSkillData.strooperRchgSpeed},
|
||||
{"sk_12mm_bullet", &gSkillData.monDmg9MM},
|
||||
{"sk_9mmAR_bullet", &gSkillData.monDmgMP5},
|
||||
{"sk_9mm_bullet", &gSkillData.monDmg12MM},
|
||||
@@ -90,6 +96,8 @@ skill_cfg_t skill_cfg[] = {
|
||||
{"sk_762_bullet", &gSkillData.monDmg762},
|
||||
{"sk_357_bullet", &gSkillData.monDmg357},
|
||||
{"sk_hornet_dmg", &gSkillData.monDmgHornet},
|
||||
{"sk_shock_dmg", &gSkillData.monDmgShockroach},
|
||||
{"sk_spore_dmg", &gSkillData.monDmgSpore},
|
||||
{"", NULL}
|
||||
};
|
||||
|
||||
@@ -244,6 +252,16 @@ void monster_skill_init(void)
|
||||
gSkillData.pitdroneDmgWhip = 35.0f;
|
||||
gSkillData.pitdroneDmgBite = 25.0f;
|
||||
|
||||
// Shock Roach
|
||||
gSkillData.roachHealth = 10.0f;
|
||||
gSkillData.roachLifespan = 10.0f;
|
||||
|
||||
// Shock Trooper
|
||||
gSkillData.strooperHealth = 50.0f;
|
||||
gSkillData.strooperDmgKick = 10.0f;
|
||||
gSkillData.strooperMaxCharge = 8.0f;
|
||||
gSkillData.strooperRchgSpeed = 1.0f;
|
||||
|
||||
// MONSTER WEAPONS
|
||||
gSkillData.monDmg9MM = 5.0f;
|
||||
gSkillData.monDmgMP5 = 4.0f;
|
||||
@@ -254,8 +272,13 @@ void monster_skill_init(void)
|
||||
|
||||
// HORNET
|
||||
gSkillData.monDmgHornet = 5.0f;
|
||||
|
||||
|
||||
|
||||
// SHOCK ROACH
|
||||
gSkillData.monDmgShockroach = 15.0f;
|
||||
|
||||
// SPORE GRENADE
|
||||
gSkillData.monDmgSpore = 50.0f;
|
||||
|
||||
// find the directory name of the currently running MOD...
|
||||
(*g_engfuncs.pfnGetGameDir)(game_dir);
|
||||
|
||||
|
||||
@@ -102,6 +102,14 @@ struct skilldata_t
|
||||
float pitdroneDmgWhip;
|
||||
float pitdroneDmgBite;
|
||||
|
||||
float roachHealth;
|
||||
float roachLifespan;
|
||||
|
||||
float strooperHealth;
|
||||
float strooperDmgKick;
|
||||
float strooperMaxCharge;
|
||||
float strooperRchgSpeed;
|
||||
|
||||
// weapons shared by monsters
|
||||
float monDmg9MM;
|
||||
float monDmgMP5;
|
||||
@@ -111,6 +119,8 @@ struct skilldata_t
|
||||
float monDmg357;
|
||||
|
||||
float monDmgHornet;
|
||||
float monDmgShockroach;
|
||||
float monDmgSpore;
|
||||
};
|
||||
|
||||
extern DLL_GLOBAL skilldata_t gSkillData;
|
||||
|
||||
345
src/dlls/sporegrenade.cpp
Normal file
345
src/dlls/sporegrenade.cpp
Normal file
@@ -0,0 +1,345 @@
|
||||
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
|
||||
|
||||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cmbase.h"
|
||||
#include "cmbasemonster.h"
|
||||
#include "monsters.h"
|
||||
#include "weapons.h"
|
||||
#include "decals.h"
|
||||
#include "explode.h"
|
||||
|
||||
int gSporeExplode, gSporeExplodeC;
|
||||
|
||||
void CMSporeGrenade::Precache()
|
||||
{
|
||||
PRECACHE_MODEL("models/spore.mdl");
|
||||
PRECACHE_MODEL("sprites/glow02.spr");
|
||||
g_sModelIndexTinySpit = PRECACHE_MODEL("sprites/tinyspit.spr");
|
||||
gSporeExplode = PRECACHE_MODEL("sprites/spore_exp_01.spr");
|
||||
gSporeExplodeC = PRECACHE_MODEL("sprites/spore_exp_c_01.spr");
|
||||
PRECACHE_SOUND("weapons/splauncher_bounce.wav");
|
||||
PRECACHE_SOUND("weapons/splauncher_impact.wav");
|
||||
}
|
||||
|
||||
void CMSporeGrenade::Explode(TraceResult *pTrace)
|
||||
{
|
||||
pev->solid = SOLID_NOT;// intangible
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
|
||||
// Pull out of the wall a bit
|
||||
if (pTrace->flFraction != 1.0)
|
||||
{
|
||||
pev->origin = pTrace->vecEndPos + (pTrace->vecPlaneNormal * (pev->dmg - 24) * 0.6);
|
||||
}
|
||||
|
||||
Vector vecSpraySpot = pTrace->vecEndPos;
|
||||
float flSpraySpeed = RANDOM_LONG(10, 15);
|
||||
|
||||
// If the trace is pointing up, then place
|
||||
// spawn position a few units higher.
|
||||
if (pTrace->vecPlaneNormal.z > 0)
|
||||
{
|
||||
vecSpraySpot = vecSpraySpot + (pTrace->vecPlaneNormal * 8);
|
||||
flSpraySpeed *= 2; // Double the speed to make them fly higher
|
||||
// in the air.
|
||||
}
|
||||
|
||||
// Spawn small particles at the explosion origin.
|
||||
SpawnExplosionParticles(
|
||||
vecSpraySpot, // position
|
||||
pTrace->vecPlaneNormal, // direction
|
||||
g_sModelIndexTinySpit, // modelindex
|
||||
RANDOM_LONG(40, 50), // count
|
||||
flSpraySpeed, // speed
|
||||
RANDOM_FLOAT(600, 640)); // noise
|
||||
|
||||
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
|
||||
WRITE_BYTE( TE_SPRITE );
|
||||
WRITE_COORD( pev->origin.x );
|
||||
WRITE_COORD( pev->origin.y );
|
||||
WRITE_COORD( pev->origin.z );
|
||||
WRITE_SHORT( RANDOM_LONG( 0, 1 ) ? gSporeExplode : gSporeExplodeC );
|
||||
WRITE_BYTE( 25 ); // scale * 10
|
||||
WRITE_BYTE( 155 ); // framerate
|
||||
MESSAGE_END();
|
||||
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
|
||||
WRITE_BYTE(TE_DLIGHT);
|
||||
WRITE_COORD( pev->origin.x ); // X
|
||||
WRITE_COORD( pev->origin.y ); // Y
|
||||
WRITE_COORD( pev->origin.z ); // Z
|
||||
WRITE_BYTE( 12 ); // radius * 0.1
|
||||
WRITE_BYTE( 0 ); // r
|
||||
WRITE_BYTE( 180 ); // g
|
||||
WRITE_BYTE( 0 ); // b
|
||||
WRITE_BYTE( 20 ); // time * 10
|
||||
WRITE_BYTE( 20 ); // decay * 0.1
|
||||
MESSAGE_END( );
|
||||
|
||||
// Play explode sound.
|
||||
EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/splauncher_impact.wav", 1, ATTN_NORM);
|
||||
|
||||
entvars_t *pevOwner;
|
||||
if (pev->owner)
|
||||
pevOwner = VARS(pev->owner);
|
||||
else
|
||||
pevOwner = NULL;
|
||||
|
||||
pev->owner = NULL; // can't traceline attack owner if this is set
|
||||
|
||||
RadiusDamage(pev, pevOwner, pev->dmg, CLASS_NONE, DMG_BLAST);
|
||||
|
||||
// Place a decal on the surface that was hit.
|
||||
UTIL_DecalTrace(pTrace, DECAL_SPIT1 + RANDOM_LONG(0, 1));
|
||||
|
||||
UpdateOnRemove();
|
||||
UTIL_Remove( this->edict() );
|
||||
}
|
||||
|
||||
void CMSporeGrenade::Detonate()
|
||||
{
|
||||
TraceResult tr;
|
||||
Vector vecSpot = pev->origin + Vector(0, 0, 8);
|
||||
UTIL_TraceLine(vecSpot, vecSpot + Vector(0, 0, -40), ignore_monsters, ENT(pev), &tr);
|
||||
|
||||
Explode(&tr);
|
||||
}
|
||||
|
||||
|
||||
void CMSporeGrenade::BounceSound()
|
||||
{
|
||||
EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/splauncher_bounce.wav", 0.25, ATTN_NORM);
|
||||
}
|
||||
|
||||
void CMSporeGrenade::TumbleThink()
|
||||
{
|
||||
if (!IsInWorld())
|
||||
{
|
||||
UpdateOnRemove();
|
||||
UTIL_Remove( this->edict() );
|
||||
return;
|
||||
}
|
||||
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
|
||||
if (pev->dmgtime <= gpGlobals->time)
|
||||
{
|
||||
SetThink(&CMSporeGrenade::Detonate);
|
||||
}
|
||||
|
||||
// Spawn particles.
|
||||
SpawnTrailParticles(
|
||||
pev->origin, // position
|
||||
-pev->velocity.Normalize(), // dir
|
||||
g_sModelIndexTinySpit, // modelindex
|
||||
RANDOM_LONG( 2, 4 ), // count
|
||||
RANDOM_FLOAT(10, 15), // speed
|
||||
RANDOM_FLOAT(2, 3) * 100); // noise ( client will divide by 100 )
|
||||
}
|
||||
|
||||
//
|
||||
// Contact grenade, explode when it touches something
|
||||
//
|
||||
void CMSporeGrenade::ExplodeTouch(edict_t *pOther)
|
||||
{
|
||||
TraceResult tr;
|
||||
Vector vecSpot;// trace starts here!
|
||||
|
||||
pev->enemy = pOther;
|
||||
|
||||
vecSpot = pev->origin - pev->velocity.Normalize() * 32;
|
||||
UTIL_TraceLine(vecSpot, vecSpot + pev->velocity.Normalize() * 64, ignore_monsters, ENT(pev), &tr);
|
||||
|
||||
Explode(&tr);
|
||||
}
|
||||
|
||||
void CMSporeGrenade::DangerSoundThink()
|
||||
{
|
||||
if (!IsInWorld())
|
||||
{
|
||||
UpdateOnRemove();
|
||||
UTIL_Remove( this->edict() );
|
||||
return;
|
||||
}
|
||||
|
||||
pev->nextthink = gpGlobals->time + 0.2;
|
||||
|
||||
// Spawn particles.
|
||||
SpawnTrailParticles(
|
||||
pev->origin, // position
|
||||
-pev->velocity.Normalize(), // dir
|
||||
g_sModelIndexTinySpit, // modelindex
|
||||
RANDOM_LONG( 5, 10), // count
|
||||
RANDOM_FLOAT(10, 15), // speed
|
||||
RANDOM_FLOAT(2, 3) * 100); // noise ( client will divide by 100 )
|
||||
}
|
||||
|
||||
void CMSporeGrenade::BounceTouch(edict_t *pOther)
|
||||
{
|
||||
if ( !pOther->v.takedamage )
|
||||
{
|
||||
if (!(pev->flags & FL_ONGROUND)) {
|
||||
if (pev->dmg_save < gpGlobals->time) {
|
||||
BounceSound();
|
||||
pev->dmg_save = gpGlobals->time + 0.1;
|
||||
}
|
||||
} else {
|
||||
pev->velocity = pev->velocity * 0.9;
|
||||
}
|
||||
if (pev->flags & FL_SWIM)
|
||||
{
|
||||
pev->velocity = pev->velocity * 0.5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceResult tr = UTIL_GetGlobalTrace();
|
||||
Explode(&tr);
|
||||
}
|
||||
}
|
||||
|
||||
void CMSporeGrenade::Spawn()
|
||||
{
|
||||
Precache();
|
||||
pev->classname = MAKE_STRING("spore");
|
||||
pev->movetype = MOVETYPE_BOUNCE;
|
||||
|
||||
pev->solid = SOLID_BBOX;
|
||||
|
||||
SET_MODEL(ENT(pev), "models/spore.mdl");
|
||||
UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0));
|
||||
|
||||
//pev->gravity = 0.5;
|
||||
|
||||
pev->dmg = gSkillData.monDmgSpore;
|
||||
|
||||
m_pSporeGlow = CMSprite::SpriteCreate("sprites/glow02.spr", pev->origin, FALSE);
|
||||
|
||||
if (m_pSporeGlow)
|
||||
{
|
||||
m_pSporeGlow->SetTransparency(kRenderGlow, 150, 158, 19, 155, kRenderFxNoDissipation);
|
||||
m_pSporeGlow->SetAttachment(edict(), 0);
|
||||
m_pSporeGlow->SetScale(.75f);
|
||||
}
|
||||
}
|
||||
|
||||
CMSporeGrenade* CMSporeGrenade::ShootTimed(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, bool ai)
|
||||
{
|
||||
CMSporeGrenade *pGrenade = CreateClassPtr((CMSporeGrenade *)NULL);
|
||||
|
||||
if (pGrenade == NULL)
|
||||
return NULL;
|
||||
|
||||
UTIL_SetOrigin(pGrenade->pev, vecStart);
|
||||
pGrenade->Spawn();
|
||||
pGrenade->pev->velocity = vecVelocity;
|
||||
pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity);
|
||||
pGrenade->pev->owner = ENT(pevOwner);
|
||||
|
||||
pGrenade->SetTouch(&CMSporeGrenade::BounceTouch); // Bounce if touched
|
||||
|
||||
float lifetime = 2.0;
|
||||
if (ai) {
|
||||
lifetime = 4.0;
|
||||
pGrenade->pev->gravity = 0.5;
|
||||
pGrenade->pev->friction = 0.9;
|
||||
}
|
||||
pGrenade->pev->dmgtime = gpGlobals->time + lifetime;
|
||||
pGrenade->SetThink(&CMSporeGrenade::TumbleThink);
|
||||
pGrenade->pev->nextthink = gpGlobals->time + 0.1;
|
||||
if (lifetime < 0.1)
|
||||
{
|
||||
pGrenade->pev->nextthink = gpGlobals->time;
|
||||
pGrenade->pev->velocity = Vector(0, 0, 0);
|
||||
}
|
||||
|
||||
return pGrenade;
|
||||
}
|
||||
|
||||
CMSporeGrenade *CMSporeGrenade::ShootContact(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity)
|
||||
{
|
||||
CMSporeGrenade *pGrenade = CreateClassPtr((CMSporeGrenade *)NULL);
|
||||
|
||||
if (pGrenade == NULL)
|
||||
return NULL;
|
||||
|
||||
UTIL_SetOrigin(pGrenade->pev, vecStart);
|
||||
pGrenade->Spawn();
|
||||
pGrenade->pev->movetype = MOVETYPE_FLY;
|
||||
pGrenade->pev->velocity = vecVelocity;
|
||||
pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity);
|
||||
pGrenade->pev->owner = ENT(pevOwner);
|
||||
|
||||
// make monsters afraid of it while in the air
|
||||
pGrenade->SetThink(&CMSporeGrenade::DangerSoundThink);
|
||||
pGrenade->pev->nextthink = gpGlobals->time;
|
||||
|
||||
// Explode on contact
|
||||
pGrenade->SetTouch(&CMSporeGrenade::ExplodeTouch);
|
||||
|
||||
pGrenade->pev->gravity = 0.5;
|
||||
pGrenade->pev->friction = 0.7;
|
||||
|
||||
return pGrenade;
|
||||
}
|
||||
|
||||
void CMSporeGrenade::SpawnTrailParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise)
|
||||
{
|
||||
MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, origin);
|
||||
WRITE_BYTE(TE_SPRITE_SPRAY);
|
||||
WRITE_COORD(origin.x); // pos
|
||||
WRITE_COORD(origin.y);
|
||||
WRITE_COORD(origin.z);
|
||||
WRITE_COORD(direction.x); // dir
|
||||
WRITE_COORD(direction.y);
|
||||
WRITE_COORD(direction.z);
|
||||
WRITE_SHORT(modelindex); // model
|
||||
WRITE_BYTE(count); // count
|
||||
WRITE_BYTE(speed); // speed
|
||||
WRITE_BYTE(noise); // noise ( client will divide by 100 )
|
||||
MESSAGE_END();
|
||||
}
|
||||
|
||||
void CMSporeGrenade::SpawnExplosionParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise)
|
||||
{
|
||||
MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, origin);
|
||||
WRITE_BYTE(TE_SPRITE_SPRAY);
|
||||
WRITE_COORD(origin.x); // pos
|
||||
WRITE_COORD(origin.y);
|
||||
WRITE_COORD(origin.z);
|
||||
WRITE_COORD(direction.x); // dir
|
||||
WRITE_COORD(direction.y);
|
||||
WRITE_COORD(direction.z);
|
||||
WRITE_SHORT(modelindex); // model
|
||||
WRITE_BYTE(count); // count
|
||||
WRITE_BYTE(speed); // speed
|
||||
WRITE_BYTE(noise); // noise ( client will divide by 100 )
|
||||
MESSAGE_END();
|
||||
}
|
||||
|
||||
void CMSporeGrenade::UpdateOnRemove()
|
||||
{
|
||||
CMBaseMonster::UpdateOnRemove();
|
||||
if (m_pSporeGlow)
|
||||
{
|
||||
UTIL_Remove(m_pSporeGlow->edict());
|
||||
m_pSporeGlow = NULL;
|
||||
}
|
||||
}
|
||||
899
src/dlls/strooper.cpp
Normal file
899
src/dlls/strooper.cpp
Normal file
@@ -0,0 +1,899 @@
|
||||
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
|
||||
|
||||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// shocktrooper
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "plane.h"
|
||||
#include "util.h"
|
||||
#include "cmbase.h"
|
||||
#include "cmbasemonster.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "animation.h"
|
||||
#include "weapons.h"
|
||||
#include "cmtalkmonster.h"
|
||||
#include "effects.h"
|
||||
#include "customentity.h"
|
||||
#include "shock.h"
|
||||
|
||||
int g_fStrooperQuestion; // true if an idle grunt asked a question. Cleared when someone answers.
|
||||
|
||||
extern Schedule_t slGruntTakeCover[];
|
||||
extern Schedule_t slGruntGrenadeCover[];
|
||||
|
||||
//=========================================================
|
||||
// monster-specific DEFINE's
|
||||
//=========================================================
|
||||
#define STROOPER_CLIP_SIZE 10 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x!
|
||||
#define STROOPER_VOL 0.35 // volume of grunt sounds
|
||||
#define STROOPER_ATTN ATTN_NORM // attenutation of grunt sentences
|
||||
#define STROOPER_LIMP_HEALTH 20
|
||||
#define STROOPER_DMG_HEADSHOT ( DMG_BULLET | DMG_CLUB ) // damage types that can kill a grunt with a single headshot.
|
||||
#define STROOPER_NUM_HEADS 2 // how many grunt heads are there?
|
||||
#define STROOPER_MINIMUM_HEADSHOT_DAMAGE 15 // must do at least this much damage in one shot to head to score a headshot kill
|
||||
#define STROOPER_SENTENCE_VOLUME (float)0.35 // volume of grunt sentences
|
||||
#define STROOPER_MUZZLEFLASH "sprites/muzzle_shock.spr"
|
||||
|
||||
#define STROOPER_SHOCKRIFLE (1 << 0)
|
||||
#define STROOPER_HANDGRENADE (1 << 1)
|
||||
|
||||
#define GUN_GROUP 1
|
||||
#define GUN_SHOCKRIFLE 0
|
||||
#define GUN_NONE 1
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define STROOPER_AE_RELOAD ( 2 )
|
||||
#define STROOPER_AE_KICK ( 3 )
|
||||
#define STROOPER_AE_BURST1 ( 4 )
|
||||
#define STROOPER_AE_BURST2 ( 5 )
|
||||
#define STROOPER_AE_BURST3 ( 6 )
|
||||
#define STROOPER_AE_GREN_TOSS ( 7 )
|
||||
#define STROOPER_AE_GREN_LAUNCH ( 8 )
|
||||
#define STROOPER_AE_GREN_DROP ( 9 )
|
||||
#define STROOPER_AE_CAUGHT_ENEMY ( 10 ) // shocktrooper established sight with an enemy (player only) that had previously eluded the squad.
|
||||
#define STROOPER_AE_DROP_GUN ( 11 ) // shocktrooper (probably dead) is dropping his shockrifle.
|
||||
|
||||
|
||||
//=========================================================
|
||||
// monster-specific schedule types
|
||||
//=========================================================
|
||||
enum
|
||||
{
|
||||
SCHED_STROOPER_SUPPRESS = LAST_COMMON_SCHEDULE + 1,
|
||||
SCHED_STROOPER_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_STROOPER_COVER_AND_RELOAD,
|
||||
SCHED_STROOPER_SWEEP,
|
||||
SCHED_STROOPER_FOUND_ENEMY,
|
||||
SCHED_STROOPER_REPEL,
|
||||
SCHED_STROOPER_REPEL_ATTACK,
|
||||
SCHED_STROOPER_REPEL_LAND,
|
||||
SCHED_STROOPER_WAIT_FACE_ENEMY,
|
||||
SCHED_STROOPER_TAKECOVER_FAILED,// special schedule type that forces analysis of conditions and picks the best possible schedule to recover from this type of failure.
|
||||
SCHED_STROOPER_ELOF_FAIL,
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// monster-specific tasks
|
||||
//=========================================================
|
||||
enum
|
||||
{
|
||||
TASK_STROOPER_FACE_TOSS_DIR = LAST_COMMON_TASK + 1,
|
||||
TASK_STROOPER_SPEAK_SENTENCE,
|
||||
TASK_STROOPER_CHECK_FIRE,
|
||||
};
|
||||
|
||||
int iStrooperMuzzleFlash;
|
||||
|
||||
const char *CMStrooper::pGruntSentences[] =
|
||||
{
|
||||
"ST_GREN", // grenade scared grunt
|
||||
"ST_ALERT", // sees player
|
||||
"ST_MONST", // sees monster
|
||||
"ST_COVER", // running to cover
|
||||
"ST_THROW", // about to throw grenade
|
||||
"ST_CHARGE", // running out to get the enemy
|
||||
"ST_TAUNT", // say rude things
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
STROOPER_SENT_NONE = -1,
|
||||
STROOPER_SENT_GREN = 0,
|
||||
STROOPER_SENT_ALERT,
|
||||
STROOPER_SENT_MONSTER,
|
||||
STROOPER_SENT_COVER,
|
||||
STROOPER_SENT_THROW,
|
||||
STROOPER_SENT_CHARGE,
|
||||
STROOPER_SENT_TAUNT,
|
||||
} STROOPER_SENTENCE_TYPES;
|
||||
|
||||
void CMStrooper::SpeakSentence()
|
||||
{
|
||||
if( m_iSentence == STROOPER_SENT_NONE )
|
||||
{
|
||||
// no sentence cued up.
|
||||
return;
|
||||
}
|
||||
|
||||
if( FOkToSpeak() )
|
||||
{
|
||||
SENTENCEG_PlayRndSz( ENT( pev ), pGruntSentences[m_iSentence], STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch );
|
||||
JustSpoke();
|
||||
}
|
||||
}
|
||||
|
||||
#define STROOPER_GIB_COUNT 8
|
||||
//=========================================================
|
||||
// GibMonster - make gun fly through the air.
|
||||
//=========================================================
|
||||
void CMStrooper::GibMonster()
|
||||
{
|
||||
if (GetBodygroup(GUN_GROUP) != GUN_NONE)
|
||||
{
|
||||
DropShockRoach(true);
|
||||
}
|
||||
|
||||
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM );
|
||||
|
||||
if( CVAR_GET_FLOAT( "violence_agibs" ) != 0 ) // Should never get here, but someone might call it directly
|
||||
{
|
||||
CMGib::SpawnRandomGibs( pev, STROOPER_GIB_COUNT, "models/strooper_gibs.mdl", 0 ); // Throw alien gibs
|
||||
}
|
||||
SetThink( &CMBaseEntity::SUB_Remove );
|
||||
pev->nextthink = gpGlobals->time;
|
||||
}
|
||||
|
||||
void CMStrooper::IdleSound()
|
||||
{
|
||||
if (FOkToSpeak() && (g_fStrooperQuestion || RANDOM_LONG(0, 1)))
|
||||
{
|
||||
if (!g_fStrooperQuestion)
|
||||
{
|
||||
// ask question or make statement
|
||||
switch (RANDOM_LONG(0, 2))
|
||||
{
|
||||
case 0: // check in
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "ST_CHECK", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
|
||||
g_fStrooperQuestion = 1;
|
||||
break;
|
||||
case 1: // question
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "ST_QUEST", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
|
||||
g_fStrooperQuestion = 2;
|
||||
break;
|
||||
case 2: // statement
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "ST_IDLE", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (g_fStrooperQuestion)
|
||||
{
|
||||
case 1: // check in
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "ST_CLEAR", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
|
||||
break;
|
||||
case 2: // question
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "ST_ANSWER", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
|
||||
break;
|
||||
}
|
||||
g_fStrooperQuestion = 0;
|
||||
}
|
||||
JustSpoke();
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CMStrooper::Classify()
|
||||
{
|
||||
if ( m_iClassifyOverride == -1 ) // helper
|
||||
return CLASS_NONE;
|
||||
else if ( m_iClassifyOverride > 0 )
|
||||
return m_iClassifyOverride; // override
|
||||
|
||||
return CLASS_RACEX_SHOCK;
|
||||
}
|
||||
|
||||
BOOL CMStrooper::CheckRangeAttack1(float flDot, float flDist)
|
||||
{
|
||||
return m_cAmmoLoaded >= 1;// && CMHGrunt::CheckRangeAttack1(flDot, flDist);
|
||||
}
|
||||
|
||||
BOOL CMStrooper::CheckRangeAttack2( float flDot, float flDist )
|
||||
{
|
||||
if( !FBitSet( pev->weapons, STROOPER_HANDGRENADE ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
return CMHGrunt::CheckRangeAttack2(flDot, flDist);
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CMStrooper::HandleAnimEvent(MonsterEvent_t *pEvent)
|
||||
{
|
||||
switch (pEvent->event)
|
||||
{
|
||||
case STROOPER_AE_DROP_GUN:
|
||||
{
|
||||
if (GetBodygroup(GUN_GROUP) != GUN_NONE)
|
||||
{
|
||||
DropShockRoach(false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case STROOPER_AE_RELOAD:
|
||||
m_cAmmoLoaded = m_cClipSize;
|
||||
ClearConditions(bits_COND_NO_AMMO_LOADED);
|
||||
break;
|
||||
|
||||
case STROOPER_AE_GREN_TOSS:
|
||||
{
|
||||
UTIL_MakeVectors(pev->angles);
|
||||
// CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32), m_vecTossVelocity, 3.5 );
|
||||
CMSporeGrenade::ShootTimed(pev, pev->origin + Vector(0,0,98), 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 STROOPER_AE_GREN_LAUNCH:
|
||||
case STROOPER_AE_GREN_DROP:
|
||||
break;
|
||||
|
||||
case STROOPER_AE_BURST1:
|
||||
{
|
||||
if (m_hEnemy)
|
||||
{
|
||||
Vector vecGunPos;
|
||||
Vector vecGunAngles;
|
||||
|
||||
GetAttachment(0, vecGunPos, vecGunAngles);
|
||||
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecGunPos );
|
||||
WRITE_BYTE( TE_SPRITE );
|
||||
WRITE_COORD( vecGunPos.x ); // pos
|
||||
WRITE_COORD( vecGunPos.y );
|
||||
WRITE_COORD( vecGunPos.z );
|
||||
WRITE_SHORT( iStrooperMuzzleFlash ); // model
|
||||
WRITE_BYTE( 4 ); // size * 10
|
||||
WRITE_BYTE( 128 ); // brightness
|
||||
MESSAGE_END();
|
||||
|
||||
UTIL_MakeVectors(pev->angles);
|
||||
Vector vecShootOrigin = vecGunPos + gpGlobals->v_forward * 32;
|
||||
Vector vecShootDir = ShootAtEnemy( vecShootOrigin );
|
||||
vecGunAngles = UTIL_VecToAngles(vecShootDir);
|
||||
|
||||
//CBaseEntity *pShock = CBaseEntity::Create("shock_beam", vecShootOrigin, vecGunAngles, edict());
|
||||
CMShock *pShock = CreateClassPtr((CMShock *)NULL);
|
||||
|
||||
if (pShock != NULL)
|
||||
{
|
||||
pShock->pev->origin = vecShootOrigin;
|
||||
|
||||
vecGunAngles.z += RANDOM_FLOAT( -0.05, 0 );
|
||||
pShock->pev->angles = UTIL_VecToAngles( vecGunAngles );
|
||||
pShock->pev->owner = edict();
|
||||
|
||||
// Initialize these for entities who don't link to the world
|
||||
pShock->pev->absmin = pShock->pev->origin - Vector(1,1,1);
|
||||
pShock->pev->absmax = pShock->pev->origin + Vector(1,1,1);
|
||||
|
||||
pShock->Spawn();
|
||||
|
||||
pShock->pev->velocity = vecShootDir * 2000;
|
||||
pShock->pev->nextthink = gpGlobals->time;
|
||||
}
|
||||
|
||||
m_cAmmoLoaded--;
|
||||
SetBlending( 0, vecGunAngles.x );
|
||||
|
||||
// Play fire sound.
|
||||
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/shock_fire.wav", 1, ATTN_NORM);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case STROOPER_AE_KICK:
|
||||
{
|
||||
EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "zombie/claw_miss2.wav", 1.0, ATTN_NORM, 0, PITCH_NORM + RANDOM_LONG( -5, 5 ) );
|
||||
edict_t *pHurt = Kick();
|
||||
|
||||
if (pHurt)
|
||||
{
|
||||
// SOUND HERE!
|
||||
UTIL_MakeVectors(pev->angles);
|
||||
pHurt->v.punchangle.x = 15;
|
||||
pHurt->v.punchangle.z = (m_fRightClaw) ? -10 : 10;
|
||||
pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50;
|
||||
|
||||
if ( UTIL_IsPlayer( pHurt ) )
|
||||
UTIL_TakeDamage( pHurt, pev, pev, gSkillData.strooperDmgKick, DMG_CLUB );
|
||||
else if ( pHurt->v.euser4 != NULL )
|
||||
{
|
||||
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt));
|
||||
pMonster->TakeDamage( pev, pev, gSkillData.strooperDmgKick, DMG_CLUB );
|
||||
}
|
||||
}
|
||||
|
||||
m_fRightClaw = !m_fRightClaw;
|
||||
}
|
||||
break;
|
||||
|
||||
case STROOPER_AE_CAUGHT_ENEMY:
|
||||
{
|
||||
if (FOkToSpeak())
|
||||
{
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "ST_ALERT", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
|
||||
JustSpoke();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
default:
|
||||
CMHGrunt::HandleAnimEvent(pEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CMStrooper::Spawn()
|
||||
{
|
||||
Precache();
|
||||
|
||||
SET_MODEL(ENT(pev), "models/strooper.mdl");
|
||||
UTIL_SetSize( pev, Vector(-24, -24, 0), Vector(24, 24, 72) );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
pev->effects = 0;
|
||||
pev->health = gSkillData.strooperHealth;
|
||||
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 = STROOPER_SENT_NONE;
|
||||
|
||||
m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
|
||||
|
||||
//m_fEnemyEluded = FALSE;
|
||||
m_fFirstEncounter = TRUE;// this is true when the grunt 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 = STROOPER_SHOCKRIFLE | STROOPER_HANDGRENADE;
|
||||
}
|
||||
|
||||
m_cClipSize = gSkillData.strooperMaxCharge;
|
||||
|
||||
m_cAmmoLoaded = m_cClipSize;
|
||||
|
||||
m_fRightClaw = FALSE;
|
||||
|
||||
CMTalkMonster::g_talkWaitTime = 0;
|
||||
m_rechargeTime = gpGlobals->time + gSkillData.strooperRchgSpeed;
|
||||
m_blinkTime = gpGlobals->time + RANDOM_FLOAT(3.0f, 7.0f);
|
||||
|
||||
MonsterInit();
|
||||
|
||||
pev->classname = MAKE_STRING( "monster_shocktrooper" );
|
||||
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
|
||||
{
|
||||
// default name
|
||||
m_szMonsterName = MAKE_STRING( "Shock Trooper" );
|
||||
}
|
||||
}
|
||||
|
||||
void CMStrooper::MonsterThink()
|
||||
{
|
||||
if (m_cAmmoLoaded < m_cClipSize)
|
||||
{
|
||||
if (m_rechargeTime < gpGlobals->time)
|
||||
{
|
||||
m_cAmmoLoaded++;
|
||||
m_rechargeTime = gpGlobals->time + gSkillData.strooperRchgSpeed;
|
||||
}
|
||||
}
|
||||
if (m_blinkTime <= gpGlobals->time && pev->skin == 0) {
|
||||
pev->skin = 1;
|
||||
m_blinkTime = gpGlobals->time + RANDOM_FLOAT(3.0f, 7.0f);
|
||||
m_eyeChangeTime = gpGlobals->time + 0.1;
|
||||
}
|
||||
if (pev->skin != 0) {
|
||||
if (m_eyeChangeTime <= gpGlobals->time) {
|
||||
m_eyeChangeTime = gpGlobals->time + 0.1;
|
||||
pev->skin++;
|
||||
if (pev->skin > 3) {
|
||||
pev->skin = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
CMHGrunt::MonsterThink();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CMStrooper::Precache()
|
||||
{
|
||||
PRECACHE_MODEL("models/strooper.mdl");
|
||||
PRECACHE_MODEL("models/strooper_gibs.mdl");
|
||||
iStrooperMuzzleFlash = PRECACHE_MODEL(STROOPER_MUZZLEFLASH);
|
||||
PRECACHE_SOUND("shocktrooper/shock_trooper_attack.wav");
|
||||
|
||||
PRECACHE_SOUND("shocktrooper/shock_trooper_die1.wav");
|
||||
PRECACHE_SOUND("shocktrooper/shock_trooper_die2.wav");
|
||||
PRECACHE_SOUND("shocktrooper/shock_trooper_die3.wav");
|
||||
PRECACHE_SOUND("shocktrooper/shock_trooper_die4.wav");
|
||||
|
||||
PRECACHE_SOUND("shocktrooper/shock_trooper_pain1.wav");
|
||||
PRECACHE_SOUND("shocktrooper/shock_trooper_pain2.wav");
|
||||
PRECACHE_SOUND("shocktrooper/shock_trooper_pain3.wav");
|
||||
PRECACHE_SOUND("shocktrooper/shock_trooper_pain4.wav");
|
||||
PRECACHE_SOUND("shocktrooper/shock_trooper_pain5.wav");
|
||||
|
||||
PRECACHE_SOUND("weapons/shock_fire.wav");
|
||||
PRECACHE_SOUND("weapons/shock_impact.wav");
|
||||
|
||||
PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event
|
||||
|
||||
// shock_beam
|
||||
CMShock shock;
|
||||
shock.Precache();
|
||||
|
||||
// spore
|
||||
CMSporeGrenade spore;
|
||||
spore.Precache();
|
||||
|
||||
// shockroach
|
||||
CMShockRoach shockroach;
|
||||
shockroach.Precache();
|
||||
|
||||
// get voice pitch
|
||||
if (RANDOM_LONG(0, 1))
|
||||
m_voicePitch = 109 + RANDOM_LONG(0, 7);
|
||||
else
|
||||
m_voicePitch = 100;
|
||||
|
||||
m_iBrassShell = PRECACHE_MODEL("models/shell.mdl");// brass shell
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// PainSound
|
||||
//=========================================================
|
||||
void CMStrooper::PainSound()
|
||||
{
|
||||
if (gpGlobals->time > m_flNextPainTime)
|
||||
{
|
||||
#if 0
|
||||
if (RANDOM_LONG(0, 99) < 5)
|
||||
{
|
||||
// pain sentences are rare
|
||||
if (FOkToSpeak())
|
||||
{
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "HG_PAIN", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, PITCH_NORM);
|
||||
JustSpoke();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
switch (RANDOM_LONG(0, 4))
|
||||
{
|
||||
case 0:
|
||||
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain1.wav", 1, ATTN_NORM);
|
||||
break;
|
||||
case 1:
|
||||
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain2.wav", 1, ATTN_NORM);
|
||||
break;
|
||||
case 2:
|
||||
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain3.wav", 1, ATTN_NORM);
|
||||
break;
|
||||
case 3:
|
||||
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain4.wav", 1, ATTN_NORM);
|
||||
break;
|
||||
case 4:
|
||||
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain5.wav", 1, ATTN_NORM);
|
||||
break;
|
||||
}
|
||||
|
||||
m_flNextPainTime = gpGlobals->time + 1;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// DeathSound
|
||||
//=========================================================
|
||||
void CMStrooper::DeathSound()
|
||||
{
|
||||
switch (RANDOM_LONG(0, 3))
|
||||
{
|
||||
case 0:
|
||||
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die1.wav", 1, ATTN_IDLE);
|
||||
break;
|
||||
case 1:
|
||||
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die2.wav", 1, ATTN_IDLE);
|
||||
break;
|
||||
case 2:
|
||||
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die3.wav", 1, ATTN_IDLE);
|
||||
break;
|
||||
case 3:
|
||||
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die4.wav", 1, ATTN_IDLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// TraceAttack - reimplemented in shock trooper because they never have helmets
|
||||
//=========================================================
|
||||
void CMStrooper::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
|
||||
{
|
||||
CMBaseMonster::TraceAttack(pevAttacker, flDamage, vecDir, ptr, bitsDamageType);
|
||||
}
|
||||
|
||||
void CMStrooper::DropShockRoach(bool gibbed)
|
||||
{
|
||||
Vector vecGunPos;
|
||||
Vector vecGunAngles;
|
||||
|
||||
GetAttachment(0, vecGunPos, vecGunAngles);
|
||||
SetBodygroup(GUN_GROUP, GUN_NONE);
|
||||
|
||||
Vector vecDropAngles;
|
||||
|
||||
// Remove any pitch.
|
||||
vecDropAngles.x = 0;
|
||||
vecDropAngles.y = vecGunAngles.y;
|
||||
vecDropAngles.z = 0;
|
||||
|
||||
Vector vecPos = pev->origin;
|
||||
if (gibbed)
|
||||
vecPos.z += 32;
|
||||
else
|
||||
vecPos.z += 48;
|
||||
|
||||
// now spawn a shockroach.
|
||||
//CBaseEntity* roach = CBaseEntity::Create( "monster_shockroach", vecPos, vecDropAngles );
|
||||
CMShockRoach *roach = CreateClassPtr((CMShockRoach *)NULL);
|
||||
if (roach != NULL)
|
||||
{
|
||||
roach->pev->origin = vecPos;
|
||||
roach->pev->angles = UTIL_VecToAngles( vecDropAngles );
|
||||
|
||||
// Initialize these for entities who don't link to the world
|
||||
roach->pev->absmin = roach->pev->origin - Vector(1,1,1);
|
||||
roach->pev->absmax = roach->pev->origin + Vector(1,1,1);
|
||||
|
||||
roach->Spawn();
|
||||
|
||||
if (ShouldFadeOnDeath())
|
||||
roach->pev->spawnflags |= SF_MONSTER_FADECORPSE;
|
||||
if (gibbed)
|
||||
{
|
||||
roach->pev->velocity = Vector(RANDOM_FLOAT(-100.0f, 100.0f), RANDOM_FLOAT(-100.0f, 100.0f), RANDOM_FLOAT(200.0f, 300.0f));
|
||||
roach->pev->avelocity = Vector(0, RANDOM_FLOAT(200.0f, 300.0f), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
roach->pev->velocity = Vector(RANDOM_FLOAT(-20.0f, 20.0f) , RANDOM_FLOAT(-20.0f, 20.0f), RANDOM_FLOAT(20.0f, 30.0f));
|
||||
roach->pev->avelocity = Vector(0, RANDOM_FLOAT(20.0f, 40.0f), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// SetActivity
|
||||
//=========================================================
|
||||
void CMStrooper::SetActivity(Activity NewActivity)
|
||||
{
|
||||
int iSequence = ACTIVITY_NOT_AVAILABLE;
|
||||
void *pmodel = GET_MODEL_PTR(ENT(pev));
|
||||
|
||||
switch (NewActivity)
|
||||
{
|
||||
case ACT_RANGE_ATTACK1:
|
||||
// shocktrooper is either shooting standing or shooting crouched
|
||||
if (m_fStanding)
|
||||
{
|
||||
// get aimable sequence
|
||||
iSequence = LookupSequence("standing_mp5");
|
||||
}
|
||||
else
|
||||
{
|
||||
// get crouching shoot
|
||||
iSequence = LookupSequence("crouching_mp5");
|
||||
}
|
||||
break;
|
||||
case ACT_RANGE_ATTACK2:
|
||||
// shocktrooper is going to throw a grenade.
|
||||
|
||||
// get toss anim
|
||||
iSequence = LookupSequence("throwgrenade");
|
||||
break;
|
||||
|
||||
case ACT_RUN:
|
||||
if (pev->health <= STROOPER_LIMP_HEALTH)
|
||||
{
|
||||
// limp!
|
||||
iSequence = LookupActivity(ACT_RUN_HURT);
|
||||
}
|
||||
else
|
||||
{
|
||||
iSequence = LookupActivity(NewActivity);
|
||||
}
|
||||
break;
|
||||
case ACT_WALK:
|
||||
if (pev->health <= STROOPER_LIMP_HEALTH)
|
||||
{
|
||||
// limp!
|
||||
iSequence = LookupActivity(ACT_WALK_HURT);
|
||||
}
|
||||
else
|
||||
{
|
||||
iSequence = LookupActivity(NewActivity);
|
||||
}
|
||||
break;
|
||||
case ACT_IDLE:
|
||||
if (m_MonsterState == MONSTERSTATE_COMBAT)
|
||||
{
|
||||
NewActivity = ACT_IDLE_ANGRY;
|
||||
}
|
||||
iSequence = LookupActivity(NewActivity);
|
||||
break;
|
||||
default:
|
||||
iSequence = LookupActivity(NewActivity);
|
||||
break;
|
||||
}
|
||||
|
||||
m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present
|
||||
|
||||
// Set to the desired anim, or default anim if the desired is not present
|
||||
if (iSequence > ACTIVITY_NOT_AVAILABLE)
|
||||
{
|
||||
if (pev->sequence != iSequence || !m_fSequenceLoops)
|
||||
{
|
||||
pev->frame = 0;
|
||||
}
|
||||
|
||||
pev->sequence = iSequence; // Set to the reset anim (if it's there)
|
||||
ResetSequenceInfo();
|
||||
SetYawSpeed();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not available try to get default anim
|
||||
ALERT(at_console, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity);
|
||||
pev->sequence = 0; // Set to the reset anim (if it's there)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Get Schedule!
|
||||
//=========================================================
|
||||
Schedule_t *CMStrooper::GetSchedule(void)
|
||||
{
|
||||
|
||||
// clear old sentence
|
||||
m_iSentence = STROOPER_SENT_NONE;
|
||||
|
||||
// flying? If PRONE, barnacle has me. IF not, it's assumed I am rapelling.
|
||||
if (pev->movetype == MOVETYPE_FLY && m_MonsterState != MONSTERSTATE_PRONE)
|
||||
{
|
||||
if (pev->flags & FL_ONGROUND)
|
||||
{
|
||||
// just landed
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
return GetScheduleOfType(SCHED_STROOPER_REPEL_LAND);
|
||||
}
|
||||
else
|
||||
{
|
||||
// repel down a rope,
|
||||
if (m_MonsterState == MONSTERSTATE_COMBAT)
|
||||
return GetScheduleOfType(SCHED_STROOPER_REPEL_ATTACK);
|
||||
else
|
||||
return GetScheduleOfType(SCHED_STROOPER_REPEL);
|
||||
}
|
||||
}
|
||||
|
||||
switch (m_MonsterState)
|
||||
{
|
||||
case MONSTERSTATE_COMBAT:
|
||||
{
|
||||
// dead enemy
|
||||
if (HasConditions(bits_COND_ENEMY_DEAD))
|
||||
{
|
||||
// call base class, all code to handle dead enemies is centralized there.
|
||||
return CMBaseMonster::GetSchedule();
|
||||
}
|
||||
|
||||
// new enemy
|
||||
if (HasConditions(bits_COND_NEW_ENEMY))
|
||||
{
|
||||
//!!!KELLY - the leader of a squad of grunts has just seen the player or a
|
||||
// monster and has made it the squad's enemy. You
|
||||
// can check pev->flags for FL_CLIENT to determine whether this is the player
|
||||
// or a monster. He's going to immediately start
|
||||
// firing, though. If you'd like, we can make an alternate "first sight"
|
||||
// schedule where the leader plays a handsign anim
|
||||
// that gives us enough time to hear a short sentence or spoken command
|
||||
// before he starts pluggin away.
|
||||
if (FOkToSpeak())// && RANDOM_LONG(0,1))
|
||||
{
|
||||
if ((m_hEnemy != 0) && UTIL_IsPlayer( m_hEnemy ))
|
||||
// player
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "ST_ALERT", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
|
||||
/*
|
||||
else if ((m_hEnemy != 0) &&
|
||||
(m_hEnemy->Classify() != CLASS_PLAYER_ALLY) &&
|
||||
(m_hEnemy->Classify() != CLASS_HUMAN_PASSIVE) &&
|
||||
(m_hEnemy->Classify() != CLASS_MACHINE))
|
||||
// monster
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "ST_MONST", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
|
||||
*/
|
||||
JustSpoke();
|
||||
}
|
||||
|
||||
if (HasConditions(bits_COND_CAN_RANGE_ATTACK1))
|
||||
{
|
||||
return GetScheduleOfType(SCHED_STROOPER_SUPPRESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetScheduleOfType(SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE);
|
||||
}
|
||||
}
|
||||
// no ammo
|
||||
else if (HasConditions(bits_COND_NO_AMMO_LOADED))
|
||||
{
|
||||
//!!!KELLY - this individual just realized he's out of bullet ammo.
|
||||
// He's going to try to find cover to run to and reload, but rarely, if
|
||||
// none is available, he'll drop and reload in the open here.
|
||||
return GetScheduleOfType(SCHED_STROOPER_COVER_AND_RELOAD);
|
||||
}
|
||||
|
||||
// damaged just a little
|
||||
else if (HasConditions(bits_COND_LIGHT_DAMAGE))
|
||||
{
|
||||
// if hurt:
|
||||
// 90% chance of taking cover
|
||||
// 10% chance of flinch.
|
||||
int iPercent = RANDOM_LONG(0, 99);
|
||||
|
||||
if (iPercent <= 90 && m_hEnemy != 0)
|
||||
{
|
||||
// only try to take cover if we actually have an enemy!
|
||||
|
||||
//!!!KELLY - this grunt was hit and is going to run to cover.
|
||||
if (FOkToSpeak()) // && RANDOM_LONG(0,1))
|
||||
{
|
||||
//SENTENCEG_PlayRndSz( ENT(pev), "HG_COVER", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
|
||||
m_iSentence = STROOPER_SENT_COVER;
|
||||
//JustSpoke();
|
||||
}
|
||||
return GetScheduleOfType(SCHED_TAKE_COVER_FROM_ENEMY);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetScheduleOfType(SCHED_SMALL_FLINCH);
|
||||
}
|
||||
}
|
||||
// can kick
|
||||
else if (HasConditions(bits_COND_CAN_MELEE_ATTACK1))
|
||||
{
|
||||
return GetScheduleOfType(SCHED_MELEE_ATTACK1);
|
||||
}
|
||||
|
||||
// can shoot
|
||||
if (HasConditions(bits_COND_CAN_RANGE_ATTACK1))
|
||||
{
|
||||
if (HasConditions(bits_COND_CAN_RANGE_ATTACK2))
|
||||
{
|
||||
// throw a grenade if can and no engage slots are available
|
||||
return GetScheduleOfType(SCHED_RANGE_ATTACK2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// hide!
|
||||
return GetScheduleOfType(SCHED_TAKE_COVER_FROM_ENEMY);
|
||||
}
|
||||
}
|
||||
// can't see enemy
|
||||
else if (HasConditions(bits_COND_ENEMY_OCCLUDED))
|
||||
{
|
||||
if (HasConditions(bits_COND_CAN_RANGE_ATTACK2))
|
||||
{
|
||||
//!!!KELLY - this grunt is about to throw or fire a grenade at the player. Great place for "fire in the hole" "frag out" etc
|
||||
if (FOkToSpeak())
|
||||
{
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "ST_THROW", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
|
||||
JustSpoke();
|
||||
}
|
||||
return GetScheduleOfType(SCHED_RANGE_ATTACK2);
|
||||
}
|
||||
else
|
||||
{
|
||||
//!!!KELLY - grunt is going to stay put for a couple seconds to see if
|
||||
// the enemy wanders back out into the open, or approaches the
|
||||
// grunt's covered position. Good place for a taunt, I guess?
|
||||
if (FOkToSpeak() && RANDOM_LONG(0, 1))
|
||||
{
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "ST_TAUNT", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
|
||||
JustSpoke();
|
||||
}
|
||||
return GetScheduleOfType(SCHED_STANDOFF);
|
||||
}
|
||||
}
|
||||
|
||||
if (HasConditions(bits_COND_SEE_ENEMY) && !HasConditions(bits_COND_CAN_RANGE_ATTACK1))
|
||||
{
|
||||
return GetScheduleOfType(SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no special cases here, call the base class
|
||||
return CMBaseMonster::GetSchedule();
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
Schedule_t* CMStrooper::GetScheduleOfType(int Type)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case SCHED_TAKE_COVER_FROM_ENEMY:
|
||||
{
|
||||
if (RANDOM_LONG(0, 1))
|
||||
{
|
||||
return &slGruntTakeCover[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return &slGruntGrenadeCover[0];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
return CMHGrunt::GetScheduleOfType(Type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,33 @@ public:
|
||||
BOOL m_fRegisteredSound;// whether or not this grenade has issued its DANGER sound to the world sound list yet.
|
||||
};
|
||||
|
||||
// Contact/Timed spore grenade
|
||||
class CMSporeGrenade : public CMBaseMonster
|
||||
{
|
||||
public:
|
||||
void Precache(void);
|
||||
void Spawn(void);
|
||||
|
||||
static CMSporeGrenade *ShootTimed(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, bool ai);
|
||||
static CMSporeGrenade *ShootContact(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity);
|
||||
|
||||
void Explode(TraceResult *pTrace);
|
||||
|
||||
void EXPORT BounceTouch(edict_t *pOther);
|
||||
void EXPORT ExplodeTouch(edict_t *pOther);
|
||||
void EXPORT DangerSoundThink(void);
|
||||
void EXPORT Detonate(void);
|
||||
void EXPORT TumbleThink(void);
|
||||
|
||||
void BounceSound(void);
|
||||
void DangerSound();
|
||||
static void SpawnTrailParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise);
|
||||
static void SpawnExplosionParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise);
|
||||
|
||||
void UpdateOnRemove();
|
||||
|
||||
CMSprite* m_pSporeGlow;
|
||||
};
|
||||
|
||||
// constant items
|
||||
#define ITEM_HEALTHKIT 1
|
||||
|
||||
Reference in New Issue
Block a user