123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- // leave this line at the top of all AI_xxxx.cpp files for PCH reasons...
- #include "g_headers.h"
-
- #include "b_local.h"
- // These define the working combat range for these suckers
- #define MIN_DISTANCE 54
- #define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
- #define MAX_DISTANCE 128
- #define MAX_DISTANCE_SQR ( MAX_DISTANCE * MAX_DISTANCE )
- #define LSTATE_CLEAR 0
- #define LSTATE_WAITING 1
- /*
- -------------------------
- NPC_MineMonster_Precache
- -------------------------
- */
- void NPC_MineMonster_Precache( void )
- {
- for ( int i = 0; i < 4; i++ )
- {
- G_SoundIndex( va("sound/chars/mine/misc/bite%i.wav", i+1 ));
- G_SoundIndex( va("sound/chars/mine/misc/miss%i.wav", i+1 ));
- }
- }
- /*
- -------------------------
- MineMonster_Idle
- -------------------------
- */
- void MineMonster_Idle( void )
- {
- if ( UpdateGoal() )
- {
- ucmd.buttons &= ~BUTTON_WALKING;
- NPC_MoveToGoal( qtrue );
- }
- }
- /*
- -------------------------
- MineMonster_Patrol
- -------------------------
- */
- void MineMonster_Patrol( void )
- {
- NPCInfo->localState = LSTATE_CLEAR;
- //If we have somewhere to go, then do that
- if ( UpdateGoal() )
- {
- ucmd.buttons &= ~BUTTON_WALKING;
- NPC_MoveToGoal( qtrue );
- }
- vec3_t dif;
- VectorSubtract( g_entities[0].currentOrigin, NPC->currentOrigin, dif );
- if ( VectorLengthSquared( dif ) < 256 * 256 )
- {
- G_SetEnemy( NPC, &g_entities[0] );
- }
- if ( NPC_CheckEnemyExt( qtrue ) == qfalse )
- {
- MineMonster_Idle();
- return;
- }
- }
-
- /*
- -------------------------
- MineMonster_Move
- -------------------------
- */
- void MineMonster_Move( qboolean visible )
- {
- if ( NPCInfo->localState != LSTATE_WAITING )
- {
- NPCInfo->goalEntity = NPC->enemy;
- NPC_MoveToGoal( qtrue );
- NPCInfo->goalRadius = MAX_DISTANCE; // just get us within combat range
- }
- }
- //---------------------------------------------------------
- void MineMonster_TryDamage( gentity_t *enemy, int damage )
- {
- vec3_t end, dir;
- trace_t tr;
- if ( !enemy )
- {
- return;
- }
- AngleVectors( NPC->client->ps.viewangles, dir, NULL, NULL );
- VectorMA( NPC->currentOrigin, MIN_DISTANCE, dir, end );
- // Should probably trace from the mouth, but, ah well.
- gi.trace( &tr, NPC->currentOrigin, vec3_origin, vec3_origin, end, NPC->s.number, MASK_SHOT );
- if ( tr.entityNum >= 0 && tr.entityNum < ENTITYNUM_NONE )
- {
- G_Damage( &g_entities[tr.entityNum], NPC, NPC, dir, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_MELEE );
- G_SoundOnEnt( NPC, CHAN_VOICE_ATTEN, va("sound/chars/mine/misc/bite%i.wav", Q_irand(1,4)));
- }
- else
- {
- G_SoundOnEnt( NPC, CHAN_VOICE_ATTEN, va("sound/chars/mine/misc/miss%i.wav", Q_irand(1,4)));
- }
- }
- //------------------------------
- void MineMonster_Attack( void )
- {
- if ( !TIMER_Exists( NPC, "attacking" ))
- {
- // usually try and play a jump attack if the player somehow got above them....or just really rarely
- if ( NPC->enemy && ((NPC->enemy->currentOrigin[2] - NPC->currentOrigin[2] > 10 && random() > 0.1f )
- || random() > 0.8f ))
- {
- // Going to do ATTACK4
- TIMER_Set( NPC, "attacking", 1750 + random() * 200 );
- NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK4, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
- TIMER_Set( NPC, "attack2_dmg", 950 ); // level two damage
- }
- else if ( random() > 0.5f )
- {
- if ( random() > 0.8f )
- {
- // Going to do ATTACK3, (rare)
- TIMER_Set( NPC, "attacking", 850 );
- NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
- TIMER_Set( NPC, "attack2_dmg", 400 ); // level two damage
- }
- else
- {
- // Going to do ATTACK1
- TIMER_Set( NPC, "attacking", 850 );
- NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
- TIMER_Set( NPC, "attack1_dmg", 450 ); // level one damage
- }
- }
- else
- {
- // Going to do ATTACK2
- TIMER_Set( NPC, "attacking", 1250 );
- NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
- TIMER_Set( NPC, "attack1_dmg", 700 ); // level one damage
- }
- }
- else
- {
- // Need to do delayed damage since the attack animations encapsulate multiple mini-attacks
- if ( TIMER_Done2( NPC, "attack1_dmg", qtrue ))
- {
- MineMonster_TryDamage( NPC->enemy, 5 );
- }
- else if ( TIMER_Done2( NPC, "attack2_dmg", qtrue ))
- {
- MineMonster_TryDamage( NPC->enemy, 10 );
- }
- }
- // Just using this to remove the attacking flag at the right time
- TIMER_Done2( NPC, "attacking", qtrue );
- }
- //----------------------------------
- void MineMonster_Combat( void )
- {
- // If we cannot see our target or we have somewhere to go, then do that
- if ( !NPC_ClearLOS( NPC->enemy ) || UpdateGoal( ))
- {
- NPCInfo->combatMove = qtrue;
- NPCInfo->goalEntity = NPC->enemy;
- NPCInfo->goalRadius = MAX_DISTANCE; // just get us within combat range
- NPC_MoveToGoal( qtrue );
- return;
- }
- // Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
- NPC_FaceEnemy( qtrue );
- float distance = DistanceHorizontalSquared( NPC->currentOrigin, NPC->enemy->currentOrigin );
- qboolean advance = (qboolean)( distance > MIN_DISTANCE_SQR ? qtrue : qfalse );
- if (( advance || NPCInfo->localState == LSTATE_WAITING ) && TIMER_Done( NPC, "attacking" )) // waiting monsters can't attack
- {
- if ( TIMER_Done2( NPC, "takingPain", qtrue ))
- {
- NPCInfo->localState = LSTATE_CLEAR;
- }
- else
- {
- MineMonster_Move( 1 );
- }
- }
- else
- {
- MineMonster_Attack();
- }
- }
- /*
- -------------------------
- NPC_MineMonster_Pain
- -------------------------
- */
- void NPC_MineMonster_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc )
- {
- G_AddEvent( self, EV_PAIN, floor((float)self->health/self->max_health*100.0f) );
- if ( damage >= 10 )
- {
- TIMER_Remove( self, "attacking" );
- TIMER_Remove( self, "attacking1_dmg" );
- TIMER_Remove( self, "attacking2_dmg" );
- TIMER_Set( self, "takingPain", 1350 );
- VectorCopy( self->NPC->lastPathAngles, self->s.angles );
- NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
- if ( self->NPC )
- {
- self->NPC->localState = LSTATE_WAITING;
- }
- }
- }
- /*
- -------------------------
- NPC_BSMineMonster_Default
- -------------------------
- */
- void NPC_BSMineMonster_Default( void )
- {
- if ( NPC->enemy )
- {
- MineMonster_Combat();
- }
- else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
- {
- MineMonster_Patrol();
- }
- else
- {
- MineMonster_Idle();
- }
- NPC_UpdateAngles( qtrue, qtrue );
- }
|