You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
632 lines
13 KiB
632 lines
13 KiB
/*** |
|
* |
|
* 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. |
|
* |
|
****/ |
|
|
|
//========================================================= |
|
// Generic Monster - purely for scripted sequence work. |
|
//========================================================= |
|
#include "extdll.h" |
|
#include "util.h" |
|
#include "cbase.h" |
|
#include "monsters.h" |
|
#include "schedule.h" |
|
#include "player.h" |
|
#include "weapons.h" |
|
|
|
#define GENEWORM_LEVEL0 0 |
|
#define GENEWORM_LEVEL1 1 |
|
|
|
#define GENEWORM_LEVEL0_HEIGHT 244 |
|
#define GENEWORM_LEVEL1_HEIGHT 304 |
|
|
|
|
|
#define GENEWORM_SKIN_EYE_OPEN 0 |
|
#define GENEWORM_SKIN_EYE_LEFT 1 |
|
#define GENEWORM_SKIN_EYE_RIGHT 2 |
|
#define GENEWORM_SKIN_EYE_CLOSED 3 |
|
|
|
|
|
//========================================================= |
|
// Monster's Anim Events Go Here |
|
//========================================================= |
|
|
|
#define GENEWORM_AE_BEAM ( 0 ) // Toxic beam attack (sprite trail) |
|
#define GENEWORM_AE_PORTAL ( 2 ) // Create a portal that spawns an enemy. |
|
|
|
#define GENEWORM_AE_MELEE_LEFT1 ( 3 ) // Play hit sound |
|
#define GENEWORM_AE_MELEE_LEFT2 ( 4 ) // Activates the trigger_geneworm_hit |
|
|
|
#define GENEWORM_AE_MELEE_RIGHT1 ( 5 ) // Play hit sound |
|
#define GENEWORM_AE_MELEE_RIGHT2 ( 6 ) // Activates the trigger_geneworm_hit |
|
|
|
#define GENEWORM_AE_MELEE_FORWARD1 ( 7 ) // Play hit sound |
|
#define GENEWORM_AE_MELEE_FORWARD2 ( 8 ) // Activates the trigger_geneworm_hit |
|
|
|
#define GENEWORM_AE_MAD ( 9 ) // Room starts shaking! |
|
|
|
#define GENEWORM_AE_EYEPAIN ( 1012 ) // Still put here (In case we need to toggle eye pain status) |
|
|
|
//========================================================= |
|
// CGeneWorm |
|
//========================================================= |
|
class CGeneWorm : public CBaseMonster |
|
{ |
|
public: |
|
|
|
int Save(CSave &save); |
|
int Restore(CRestore &restore); |
|
static TYPEDESCRIPTION m_SaveData[]; |
|
|
|
void Spawn(void); |
|
void Precache(void); |
|
int Classify(void) { return CLASS_ALIEN_MONSTER; }; |
|
int BloodColor(void) { return BLOOD_COLOR_YELLOW; } |
|
void Killed(entvars_t *pevAttacker, int iGib); |
|
|
|
void SetObjectCollisionBox(void) |
|
{ |
|
pev->absmin = pev->origin + Vector(-95, -95, 0); |
|
pev->absmax = pev->origin + Vector(95, 95, 0); |
|
} |
|
|
|
void HandleAnimEvent(MonsterEvent_t *pEvent); |
|
|
|
void EXPORT StartupThink(void); |
|
void EXPORT HuntThink(void); |
|
void EXPORT CrashTouch(CBaseEntity *pOther); |
|
void EXPORT DyingThink(void); |
|
void EXPORT StartupUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); |
|
void EXPORT NullThink(void); |
|
void EXPORT CommandUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); |
|
|
|
void FloatSequence(void); |
|
void NextActivity(void); |
|
|
|
void Flight(void); |
|
|
|
int TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); |
|
|
|
void PainSound(void); |
|
void DeathSound(void); |
|
void IdleSound(void); |
|
|
|
int Level( float dz ); |
|
int MyEnemyLevel(void); |
|
float MyEnemyHeight(void); |
|
|
|
Vector m_vecTarget; |
|
Vector m_posTarget; |
|
|
|
Vector m_vecDesired; |
|
Vector m_posDesired; |
|
|
|
float m_flLastSeen; |
|
float m_flPrevSeen; |
|
|
|
int m_iLevel; |
|
EHANDLE m_hHitVolumes[3]; |
|
|
|
void PlaySequenceAttack( int side, BOOL bMelee ); |
|
void PlaySequencePain( int side ); |
|
|
|
// |
|
// SOUNDS |
|
// |
|
|
|
static const char *pAttackSounds[]; |
|
static const char *pDeathSounds[]; |
|
static const char *pEntrySounds[]; |
|
static const char *pPainSounds[]; |
|
static const char *pIdleSounds[]; |
|
static const char *pEyePainSounds[]; |
|
|
|
|
|
// |
|
// ANIMATIONS |
|
// |
|
|
|
static const char *pDeathAnims[]; |
|
static const char *pEntryAnims[]; |
|
static const char *pIdleAnims[]; |
|
|
|
static const char *pMadAnims[]; |
|
static const char *pScreamAnims[]; |
|
|
|
static const char *pBigPainAnims[]; |
|
static const char *pEyePainAnims[]; |
|
static const char *pIdlePainAnims[]; |
|
|
|
static const char *pMeleeAttackAnims[]; |
|
static const char *pBeamAttackAnims[]; |
|
|
|
// |
|
// HIT VOLUMES |
|
// |
|
static const char* pHitVolumes[]; |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS(monster_geneworm, CGeneWorm); |
|
|
|
TYPEDESCRIPTION CGeneWorm::m_SaveData[] = |
|
{ |
|
DEFINE_FIELD(CGeneWorm, m_vecTarget, FIELD_VECTOR), |
|
DEFINE_FIELD(CGeneWorm, m_posTarget, FIELD_POSITION_VECTOR), |
|
DEFINE_FIELD(CGeneWorm, m_vecDesired, FIELD_VECTOR), |
|
DEFINE_FIELD(CGeneWorm, m_posDesired, FIELD_POSITION_VECTOR), |
|
|
|
DEFINE_FIELD(CGeneWorm, m_flLastSeen, FIELD_TIME), |
|
DEFINE_FIELD(CGeneWorm, m_flPrevSeen, FIELD_TIME), |
|
|
|
DEFINE_FIELD(CGeneWorm, m_iLevel, FIELD_INTEGER), |
|
DEFINE_ARRAY(CGeneWorm, m_hHitVolumes, FIELD_EHANDLE, 3), |
|
}; |
|
IMPLEMENT_SAVERESTORE(CGeneWorm, CBaseMonster); |
|
|
|
|
|
//========================================================= |
|
//========================================================= |
|
|
|
const char *CGeneWorm::pAttackSounds[] = |
|
{ |
|
"geneworm/geneworm_attack_mounted_gun.wav", |
|
"geneworm/geneworm_attack_mounted_rocket.wav", |
|
"geneworm/geneworm_beam_attack.wav", |
|
"geneworm/geneworm_big_attack_forward.wav", |
|
}; |
|
|
|
const char *CGeneWorm::pDeathSounds[] = |
|
{ |
|
"geneworm/geneworm_death.wav", |
|
}; |
|
|
|
const char *CGeneWorm::pEntrySounds[] = |
|
{ |
|
"geneworm/geneworm_entry.wav", |
|
}; |
|
|
|
const char *CGeneWorm::pPainSounds[] = |
|
{ |
|
"geneworm/geneworm_final_pain1.wav", |
|
"geneworm/geneworm_final_pain2.wav", |
|
"geneworm/geneworm_final_pain3.wav", |
|
"geneworm/geneworm_final_pain4.wav", |
|
}; |
|
|
|
const char *CGeneWorm::pIdleSounds[] = |
|
{ |
|
"geneworm/geneworm_idle1.wav", |
|
"geneworm/geneworm_idle2.wav", |
|
"geneworm/geneworm_idle3.wav", |
|
"geneworm/geneworm_idle4.wav", |
|
}; |
|
|
|
const char *CGeneWorm::pEyePainSounds[] = |
|
{ |
|
"geneworm/geneworm_shot_in_eye.wav", |
|
}; |
|
|
|
const char* CGeneWorm::pHitVolumes[] = |
|
{ |
|
"GeneWormRightSlash", |
|
"GeneWormCenterSlash", |
|
"GeneWormLeftSlash", |
|
}; |
|
|
|
|
|
//========================================================= |
|
//========================================================= |
|
|
|
const char *CGeneWorm::pDeathAnims[] = |
|
{ |
|
"death", |
|
}; |
|
|
|
const char *CGeneWorm::pEntryAnims[] = |
|
{ |
|
"entry", |
|
}; |
|
|
|
const char *CGeneWorm::pIdleAnims[] = |
|
{ |
|
"idle", |
|
}; |
|
|
|
const char *CGeneWorm::pMadAnims[] = |
|
{ |
|
"mad", |
|
}; |
|
|
|
const char *CGeneWorm::pScreamAnims[] = |
|
{ |
|
"scream1", |
|
"scream2", |
|
}; |
|
|
|
const char *CGeneWorm::pBigPainAnims[] = |
|
{ |
|
"bigpain1", |
|
"bigpain2", |
|
"bigpain3", |
|
"bigpain4", |
|
}; |
|
|
|
const char *CGeneWorm::pEyePainAnims[] = |
|
{ |
|
"eyepain1", |
|
"eyepain2", |
|
}; |
|
|
|
const char *CGeneWorm::pIdlePainAnims[] = |
|
{ |
|
|
|
"idlepain", |
|
"idlepain2", |
|
"idlepain3", |
|
}; |
|
|
|
const char *CGeneWorm::pMeleeAttackAnims[] = |
|
{ |
|
"melee1", |
|
"melee2", |
|
"melee3", |
|
}; |
|
|
|
const char *CGeneWorm::pBeamAttackAnims[] = |
|
{ |
|
"dattack1", |
|
"dattack2", |
|
"dattack3", |
|
}; |
|
|
|
//========================================================= |
|
// Spawn |
|
//========================================================= |
|
void CGeneWorm::Spawn() |
|
{ |
|
Precache(); |
|
// motor |
|
pev->movetype = MOVETYPE_FLY; |
|
pev->solid = SOLID_BBOX; |
|
|
|
|
|
SET_MODEL(ENT(pev), "models/geneworm.mdl"); |
|
UTIL_SetSize(pev, Vector(-32, -32, 0), Vector(32, 32, 64)); |
|
UTIL_SetOrigin(pev, pev->origin); |
|
|
|
pev->flags |= FL_MONSTER; |
|
pev->takedamage = DAMAGE_AIM; |
|
pev->health = 999; |
|
pev->view_ofs = Vector(0, 0, 192); |
|
|
|
pev->sequence = 0; |
|
ResetSequenceInfo(); |
|
|
|
InitBoneControllers(); |
|
|
|
SetThink(&CGeneWorm::StartupThink); |
|
pev->nextthink = gpGlobals->time + 0.1; |
|
|
|
m_vecDesired = Vector(1, 0, 0); |
|
m_posDesired = Vector(pev->origin.x, pev->origin.y, 512); |
|
} |
|
|
|
//========================================================= |
|
// Precache - precaches all resources this monster needs |
|
//========================================================= |
|
void CGeneWorm::Precache() |
|
{ |
|
PRECACHE_MODEL("models/geneworm.mdl"); |
|
|
|
PRECACHE_SOUND_ARRAY(pAttackSounds); |
|
PRECACHE_SOUND_ARRAY(pDeathSounds); |
|
PRECACHE_SOUND_ARRAY(pEntrySounds); |
|
PRECACHE_SOUND_ARRAY(pPainSounds); |
|
PRECACHE_SOUND_ARRAY(pIdleSounds); |
|
PRECACHE_SOUND_ARRAY(pEyePainSounds); |
|
} |
|
|
|
//========================================================= |
|
// NullThink |
|
//========================================================= |
|
void CGeneWorm::NullThink(void) |
|
{ |
|
StudioFrameAdvance(); |
|
pev->nextthink = gpGlobals->time + 0.5; |
|
} |
|
|
|
//========================================================= |
|
// StartupUse |
|
//========================================================= |
|
void CGeneWorm::StartupUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) |
|
{ |
|
SetThink(&CGeneWorm::HuntThink); |
|
pev->nextthink = gpGlobals->time + 0.1; |
|
SetUse(&CGeneWorm::CommandUse); |
|
} |
|
|
|
//========================================================= |
|
// |
|
//========================================================= |
|
void CGeneWorm::StartupThink(void) |
|
{ |
|
CBaseEntity *pEntity = NULL; |
|
|
|
int i; |
|
for (i = 0; i < 3; i++) |
|
{ |
|
pEntity = UTIL_FindEntityByTargetname(NULL, pHitVolumes[i]); |
|
if (pEntity) |
|
m_hHitVolumes[i] = pEntity; |
|
} |
|
|
|
SetThink(&CGeneWorm::HuntThink); |
|
SetUse(&CGeneWorm::CommandUse); |
|
pev->nextthink = gpGlobals->time + 0.1; |
|
} |
|
|
|
|
|
void CGeneWorm::Killed(entvars_t *pevAttacker, int iGib) |
|
{ |
|
CBaseMonster::Killed(pevAttacker, iGib); |
|
} |
|
|
|
void CGeneWorm::DyingThink(void) |
|
{ |
|
pev->nextthink = gpGlobals->time + 0.1; |
|
DispatchAnimEvents(); |
|
StudioFrameAdvance(); |
|
|
|
if (pev->deadflag == DEAD_NO) |
|
{ |
|
FireTargets(STRING(pev->target), this, this, USE_ON, 1.0); |
|
pev->velocity = Vector(0, 0, 0); |
|
|
|
DeathSound(); |
|
pev->deadflag = DEAD_DYING; |
|
} |
|
|
|
if (pev->deadflag == DEAD_DYING) |
|
{ |
|
Flight(); |
|
pev->deadflag = DEAD_DEAD; |
|
} |
|
|
|
if (m_fSequenceFinished) |
|
{ |
|
pev->sequence = LookupSequence("death"); |
|
pev->framerate = 0.5f; |
|
} |
|
|
|
if (m_hHitVolumes) |
|
{ |
|
int i; |
|
for (i = 0; i < 3; i++) |
|
{ |
|
UTIL_Remove(m_hHitVolumes[i]); |
|
m_hHitVolumes[i] = NULL; |
|
} |
|
} |
|
|
|
return; |
|
} |
|
|
|
//========================================================= |
|
//========================================================= |
|
void CGeneWorm::FloatSequence(void) |
|
{ |
|
pev->sequence = LookupSequence("idle"); |
|
} |
|
|
|
//========================================================= |
|
//========================================================= |
|
void CGeneWorm::NextActivity(void) |
|
{ |
|
UTIL_MakeAimVectors(pev->angles); |
|
|
|
float flDist = (m_posDesired - pev->origin).Length(); |
|
float flDot = DotProduct(m_vecDesired, gpGlobals->v_forward); |
|
|
|
if (m_hEnemy != 0 && !m_hEnemy->IsAlive()) |
|
{ |
|
m_hEnemy = NULL; |
|
} |
|
|
|
if (m_flLastSeen + 15 < gpGlobals->time) |
|
{ |
|
m_hEnemy = NULL; |
|
} |
|
|
|
if (m_hEnemy == 0) |
|
{ |
|
Look(4096); |
|
m_hEnemy = BestVisibleEnemy(); |
|
|
|
if (!m_hEnemy) |
|
{ |
|
m_hEnemy = UTIL_PlayerByIndex( 1 ); |
|
ASSERT( m_hEnemy ); |
|
} |
|
} |
|
|
|
if (m_hEnemy != 0) |
|
{ |
|
if (m_flLastSeen + 5 > gpGlobals->time) |
|
{ |
|
return; |
|
} |
|
} |
|
|
|
|
|
if (m_iLevel != -1) |
|
{ |
|
ASSERT( m_hEnemy ); |
|
} |
|
|
|
FloatSequence(); |
|
} |
|
|
|
void CGeneWorm::HuntThink(void) |
|
{ |
|
pev->nextthink = gpGlobals->time + 0.1; |
|
DispatchAnimEvents(); |
|
StudioFrameAdvance(); |
|
|
|
// if dead, force cancelation of current animation |
|
if (pev->health <= 0) |
|
{ |
|
SetThink(&CGeneWorm::DyingThink); |
|
m_fSequenceFinished = TRUE; |
|
return; |
|
} |
|
|
|
// get new sequence |
|
if (m_fSequenceFinished) |
|
{ |
|
pev->frame = 0; |
|
NextActivity(); |
|
ResetSequenceInfo(); |
|
} |
|
|
|
// look for current enemy |
|
if (m_hEnemy != 0) |
|
{ |
|
// Update level. |
|
m_iLevel = Level( m_hEnemy->pev->origin.z ); |
|
|
|
if (FVisible(m_hEnemy)) |
|
{ |
|
if (m_flLastSeen < gpGlobals->time - 5) |
|
m_flPrevSeen = gpGlobals->time; |
|
m_flLastSeen = gpGlobals->time; |
|
m_posTarget = m_hEnemy->pev->origin; |
|
m_vecTarget = (m_posTarget - pev->origin).Normalize(); |
|
m_vecDesired = m_vecTarget; |
|
m_posDesired = Vector(pev->origin.x, pev->origin.y, pev->origin.z); |
|
} |
|
else |
|
{ |
|
//m_flAdj = min(m_flAdj + 10, 1000); |
|
} |
|
} |
|
else |
|
{ |
|
m_iLevel = -1; |
|
} |
|
|
|
#if 0 |
|
// don't go too high |
|
if (m_posDesired.z > m_flMaxZ) |
|
m_posDesired.z = m_flMaxZ; |
|
|
|
// don't go too low |
|
if (m_posDesired.z < m_flMinZ) |
|
m_posDesired.z = m_flMinZ; |
|
#endif |
|
|
|
Flight(); |
|
} |
|
|
|
|
|
|
|
//========================================================= |
|
//========================================================= |
|
void CGeneWorm::Flight(void) |
|
{ |
|
|
|
} |
|
|
|
void CGeneWorm::HandleAnimEvent(MonsterEvent_t *pEvent) |
|
{ |
|
switch (pEvent->event) |
|
{ |
|
case 1: // shoot |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
void CGeneWorm::CommandUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) |
|
{ |
|
switch (useType) |
|
{ |
|
case USE_OFF: |
|
break; |
|
case USE_ON: |
|
break; |
|
case USE_SET: |
|
break; |
|
case USE_TOGGLE: |
|
break; |
|
} |
|
} |
|
|
|
int CGeneWorm::TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) |
|
{ |
|
if (pevInflictor->owner == edict()) |
|
return 0; |
|
|
|
if (flDamage >= pev->health) |
|
{ |
|
pev->health = 1; |
|
} |
|
|
|
PainSound(); |
|
|
|
pev->health -= flDamage; |
|
return 0; |
|
} |
|
|
|
void CGeneWorm::PainSound(void) |
|
{ |
|
|
|
} |
|
|
|
void CGeneWorm::DeathSound(void) |
|
{ |
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), 2, ATTN_NORM); |
|
} |
|
|
|
void CGeneWorm::IdleSound(void) |
|
{ |
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), VOL_NORM, ATTN_NORM); |
|
} |
|
|
|
int CGeneWorm::Level(float dz) |
|
{ |
|
if (dz < GENEWORM_LEVEL1_HEIGHT) |
|
return GENEWORM_LEVEL0; |
|
|
|
return GENEWORM_LEVEL1; |
|
} |
|
int CGeneWorm::MyEnemyLevel(void) |
|
{ |
|
if (!m_hEnemy) |
|
return -1; |
|
|
|
return Level(m_hEnemy->pev->origin.z); |
|
} |
|
|
|
float CGeneWorm::MyEnemyHeight(void) |
|
{ |
|
switch (m_iLevel) |
|
{ |
|
case GENEWORM_LEVEL0: |
|
return GENEWORM_LEVEL0_HEIGHT; |
|
case GENEWORM_LEVEL1: |
|
return GENEWORM_LEVEL1_HEIGHT; |
|
} |
|
|
|
return GENEWORM_LEVEL1_HEIGHT; |
|
}
|
|
|