Files
monstermod-redo-repo-copy/src/dlls/hornet.cpp

385 lines
10 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.
*
* 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.
*
****/
//=========================================================
// Hornets
//=========================================================
#include "extdll.h"
#include "util.h"
#include "cmbase.h"
#include "cmbasemonster.h"
#include "monsters.h"
#include "weapons.h"
#include "hornet.h"
int iHornetTrail;
int iHornetPuff;
//=========================================================
// don't let hornets gib, ever.
//=========================================================
int CMHornet :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
// filter these bits a little.
bitsDamageType &= ~ ( DMG_ALWAYSGIB );
bitsDamageType |= DMG_NEVERGIB;
return CMBaseMonster :: TakeDamage ( pevInflictor, pevAttacker, flDamage, bitsDamageType );
}
//=========================================================
//=========================================================
void CMHornet :: Spawn( void )
{
Precache();
pev->movetype = MOVETYPE_FLY;
pev->solid = SOLID_BBOX;
pev->takedamage = DAMAGE_YES;
pev->flags |= FL_MONSTER; // I have a bad feeling about this
pev->health = 1;// weak!
// hornets don't live as long in multiplayer
m_flStopAttack = gpGlobals->time + 3.5;
m_flFieldOfView = 0.9; // +- 25 degrees
if ( RANDOM_LONG ( 1, 5 ) <= 2 )
{
m_iHornetType = HORNET_TYPE_RED;
m_flFlySpeed = HORNET_RED_SPEED;
}
else
{
m_iHornetType = HORNET_TYPE_ORANGE;
m_flFlySpeed = HORNET_ORANGE_SPEED;
}
SET_MODEL(ENT( pev ), "models/hornet.mdl");
UTIL_SetSize( pev, Vector( -4, -4, -4 ), Vector( 4, 4, 4 ) );
SetTouch( &CMHornet::DieTouch );
SetThink( &CMHornet::StartTrack );
edict_t *pSoundEnt = pev->owner;
if ( !pSoundEnt )
pSoundEnt = edict();
// no real owner, or owner isn't a client.
pev->dmg = gSkillData.monDmgHornet;
pev->nextthink = gpGlobals->time + 0.1;
ResetSequenceInfo( );
pev->classname = MAKE_STRING( "hornet" );
}
void CMHornet :: Precache()
{
PRECACHE_MODEL("models/hornet.mdl");
PRECACHE_SOUND( "agrunt/ag_fire1.wav" );
PRECACHE_SOUND( "agrunt/ag_fire2.wav" );
PRECACHE_SOUND( "agrunt/ag_fire3.wav" );
PRECACHE_SOUND( "hornet/ag_buzz1.wav" );
PRECACHE_SOUND( "hornet/ag_buzz2.wav" );
PRECACHE_SOUND( "hornet/ag_buzz3.wav" );
PRECACHE_SOUND( "hornet/ag_hornethit1.wav" );
PRECACHE_SOUND( "hornet/ag_hornethit2.wav" );
PRECACHE_SOUND( "hornet/ag_hornethit3.wav" );
iHornetPuff = PRECACHE_MODEL( "sprites/muz1.spr" );
iHornetTrail = PRECACHE_MODEL("sprites/laserbeam.spr");
}
//=========================================================
// hornets will never get mad at each other, no matter who the owner is.
//=========================================================
int CMHornet::IRelationship ( CMBaseEntity *pTarget )
{
if ( pTarget->pev->modelindex == pev->modelindex )
{
return R_NO;
}
return CMBaseMonster :: IRelationship( pTarget );
}
//=========================================================
// ID's Hornet as their owner
//=========================================================
int CMHornet::Classify ( void )
{
/*
if ( pev->owner && pev->owner->v.flags & FL_CLIENT)
{
return CLASS_PLAYER_BIOWEAPON;
}
return CLASS_ALIEN_BIOWEAPON;
*/
// Ensure classify is consistent with the owner, in the event
// it's classification was overriden.
if ( pev->owner == NULL )
return CLASS_ALIEN_BIOWEAPON;
// Ain't this going to make the hornets code "slow"?
CMBaseMonster *pOwner = GetClassPtr((CMBaseMonster *)VARS(pev->owner));
return pOwner->Classify();
}
//=========================================================
// StartTrack - starts a hornet out tracking its target
//=========================================================
void CMHornet :: StartTrack ( void )
{
IgniteTrail();
SetTouch( &CMHornet::TrackTouch );
SetThink( &CMHornet::TrackTarget );
pev->nextthink = gpGlobals->time + 0.1;
}
//=========================================================
// StartDart - starts a hornet out just flying straight.
//=========================================================
void CMHornet :: StartDart ( void )
{
IgniteTrail();
SetTouch( &CMHornet::DartTouch );
SetThink( &CMHornet::SUB_Remove );
pev->nextthink = gpGlobals->time + 4;
}
void CMHornet::IgniteTrail( void )
{
/*
ted's suggested trail colors:
r161
g25
b97
r173
g39
b14
old colors
case HORNET_TYPE_RED:
WRITE_BYTE( 255 ); // r, g, b
WRITE_BYTE( 128 ); // r, g, b
WRITE_BYTE( 0 ); // r, g, b
break;
case HORNET_TYPE_ORANGE:
WRITE_BYTE( 0 ); // r, g, b
WRITE_BYTE( 100 ); // r, g, b
WRITE_BYTE( 255 ); // r, g, b
break;
*/
// trail
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( TE_BEAMFOLLOW );
WRITE_SHORT( entindex() ); // entity
WRITE_SHORT( iHornetTrail ); // model
WRITE_BYTE( 10 ); // life
WRITE_BYTE( 2 ); // width
switch ( m_iHornetType )
{
case HORNET_TYPE_RED:
WRITE_BYTE( 179 ); // r, g, b
WRITE_BYTE( 39 ); // r, g, b
WRITE_BYTE( 14 ); // r, g, b
break;
case HORNET_TYPE_ORANGE:
WRITE_BYTE( 255 ); // r, g, b
WRITE_BYTE( 128 ); // r, g, b
WRITE_BYTE( 0 ); // r, g, b
break;
}
WRITE_BYTE( 128 ); // brightness
MESSAGE_END();
}
//=========================================================
// Hornet is flying, gently tracking target
//=========================================================
void CMHornet :: TrackTarget ( void )
{
Vector vecFlightDir;
Vector vecDirToEnemy;
float flDelta;
StudioFrameAdvance( );
if (gpGlobals->time > m_flStopAttack)
{
SetTouch( NULL );
SetThink( &CMHornet::SUB_Remove );
pev->nextthink = gpGlobals->time + 0.1;
return;
}
// UNDONE: The player pointer should come back after returning from another level
if ( m_hEnemy == NULL )
{// enemy is dead.
Look( 512 );
m_hEnemy = BestVisibleEnemy( );
}
if ( m_hEnemy != NULL && UTIL_FVisible( m_hEnemy, ENT(pev) ))
{
m_vecEnemyLKP = UTIL_BodyTarget( m_hEnemy, pev->origin );
}
else
{
m_vecEnemyLKP = m_vecEnemyLKP + pev->velocity * m_flFlySpeed * 0.1;
}
vecDirToEnemy = ( m_vecEnemyLKP - pev->origin ).Normalize();
if (pev->velocity.Length() < 0.1)
vecFlightDir = vecDirToEnemy;
else
vecFlightDir = pev->velocity.Normalize();
// measure how far the turn is, the wider the turn, the slow we'll go this time.
flDelta = DotProduct ( vecFlightDir, vecDirToEnemy );
if ( flDelta < 0.5 )
{// hafta turn wide again. play sound
switch (RANDOM_LONG(0,2))
{
case 0: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz1.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break;
case 1: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz2.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break;
case 2: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz3.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break;
}
}
if ( flDelta <= 0 && m_iHornetType == HORNET_TYPE_RED )
{// no flying backwards, but we don't want to invert this, cause we'd go fast when we have to turn REAL far.
flDelta = 0.25;
}
pev->velocity = ( vecFlightDir + vecDirToEnemy).Normalize();
if ( pev->owner && (pev->owner->v.flags & FL_MONSTER) )
{
// random pattern only applies to hornets fired by monsters, not players.
pev->velocity.x += RANDOM_FLOAT ( -0.10, 0.10 );// scramble the flight dir a bit.
pev->velocity.y += RANDOM_FLOAT ( -0.10, 0.10 );
pev->velocity.z += RANDOM_FLOAT ( -0.10, 0.10 );
}
switch ( m_iHornetType )
{
case HORNET_TYPE_RED:
pev->velocity = pev->velocity * ( m_flFlySpeed * flDelta );// scale the dir by the ( speed * width of turn )
pev->nextthink = gpGlobals->time + RANDOM_FLOAT( 0.1, 0.3 );
break;
case HORNET_TYPE_ORANGE:
pev->velocity = pev->velocity * m_flFlySpeed;// do not have to slow down to turn.
pev->nextthink = gpGlobals->time + 0.1;// fixed think time
break;
}
pev->angles = UTIL_VecToAngles (pev->velocity);
pev->solid = SOLID_BBOX;
}
//=========================================================
// Tracking Hornet hit something
//=========================================================
void CMHornet :: TrackTouch ( edict_t *pOther )
{
if ( (pOther == pev->owner) || pOther->v.modelindex == pev->modelindex )
{// bumped into the guy that shot it.
pev->solid = SOLID_NOT;
return;
}
// is this NOT a player and IS a monster?
if (!UTIL_IsPlayer(pOther) && (pOther->v.euser4 != NULL))
{
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
if ( IRelationship( pMonster ) <= R_NO )
{
// hit something we don't want to hurt, so turn around.
pev->velocity = pev->velocity.Normalize();
pev->velocity.x *= -1;
pev->velocity.y *= -1;
pev->origin = pev->origin + pev->velocity * 4; // bounce the hornet off a bit.
pev->velocity = pev->velocity * m_flFlySpeed;
return;
}
}
DieTouch( pOther );
}
void CMHornet::DartTouch( edict_t *pOther )
{
DieTouch( pOther );
}
void CMHornet::DieTouch ( edict_t *pOther )
{
if ( pOther && pOther->v.takedamage )
{// do the damage
switch (RANDOM_LONG(0,2))
{// buzz when you plug someone
case 0: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_hornethit1.wav", 1, ATTN_NORM); break;
case 1: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_hornethit2.wav", 1, ATTN_NORM); break;
case 2: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_hornethit3.wav", 1, ATTN_NORM); break;
}
if (UTIL_IsPlayer(pOther))
UTIL_TakeDamage( pOther, pev, VARS( pev->owner ), pev->dmg, DMG_BULLET );
else if (pOther->v.euser4 != NULL)
{
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TakeDamage( pev, VARS( pev->owner ), pev->dmg, DMG_BULLET );
}
}
pev->modelindex = 0;// so will disappear for the 0.1 secs we wait until NEXTTHINK gets rid
pev->solid = SOLID_NOT;
SetThink ( &CMHornet::SUB_Remove );
pev->nextthink = gpGlobals->time + 1;// stick around long enough for the sound to finish!
}