00001 #include "b_local.h"
00002
00003
00004 #define MIN_DISTANCE 54
00005 #define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
00006
00007 #define MAX_DISTANCE 128
00008 #define MAX_DISTANCE_SQR ( MAX_DISTANCE * MAX_DISTANCE )
00009
00010 #define LSTATE_CLEAR 0
00011 #define LSTATE_WAITING 1
00012
00013
00014
00015
00016
00017
00018 void NPC_MineMonster_Precache( void )
00019 {
00020 int i;
00021
00022 for ( i = 0; i < 4; i++ )
00023 {
00024 G_SoundIndex( va("sound/chars/mine/misc/bite%i.wav", i+1 ));
00025 G_SoundIndex( va("sound/chars/mine/misc/miss%i.wav", i+1 ));
00026 }
00027 }
00028
00029
00030
00031
00032
00033
00034
00035 void MineMonster_Idle( void )
00036 {
00037 if ( UpdateGoal() )
00038 {
00039 ucmd.buttons &= ~BUTTON_WALKING;
00040 NPC_MoveToGoal( qtrue );
00041 }
00042 }
00043
00044
00045
00046
00047
00048
00049
00050 void MineMonster_Patrol( void )
00051 {
00052 vec3_t dif;
00053
00054 NPCInfo->localState = LSTATE_CLEAR;
00055
00056
00057 if ( UpdateGoal() )
00058 {
00059 ucmd.buttons &= ~BUTTON_WALKING;
00060 NPC_MoveToGoal( qtrue );
00061 }
00062 else
00063 {
00064 if ( TIMER_Done( NPC, "patrolTime" ))
00065 {
00066 TIMER_Set( NPC, "patrolTime", crandom() * 5000 + 5000 );
00067 }
00068 }
00069
00070
00071 VectorSubtract( g_entities[0].r.currentOrigin, NPC->r.currentOrigin, dif );
00072
00073 if ( VectorLengthSquared( dif ) < 256 * 256 )
00074 {
00075 G_SetEnemy( NPC, &g_entities[0] );
00076 }
00077
00078 if ( NPC_CheckEnemyExt( qtrue ) == qfalse )
00079 {
00080 MineMonster_Idle();
00081 return;
00082 }
00083 }
00084
00085
00086
00087
00088
00089
00090 void MineMonster_Move( qboolean visible )
00091 {
00092 if ( NPCInfo->localState != LSTATE_WAITING )
00093 {
00094 NPCInfo->goalEntity = NPC->enemy;
00095 NPC_MoveToGoal( qtrue );
00096 NPCInfo->goalRadius = MAX_DISTANCE;
00097 }
00098 }
00099
00100
00101 void MineMonster_TryDamage( gentity_t *enemy, int damage )
00102 {
00103 vec3_t end, dir;
00104 trace_t tr;
00105
00106 if ( !enemy )
00107 {
00108 return;
00109 }
00110
00111 AngleVectors( NPC->client->ps.viewangles, dir, NULL, NULL );
00112 VectorMA( NPC->r.currentOrigin, MIN_DISTANCE, dir, end );
00113
00114
00115 trap_Trace( &tr, NPC->r.currentOrigin, vec3_origin, vec3_origin, end, NPC->s.number, MASK_SHOT );
00116
00117 if ( tr.entityNum >= 0 && tr.entityNum < ENTITYNUM_NONE )
00118 {
00119 G_Damage( &g_entities[tr.entityNum], NPC, NPC, dir, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_MELEE );
00120 G_Sound( NPC, CHAN_AUTO, G_EffectIndex(va("sound/chars/mine/misc/bite%i.wav", Q_irand(1,4))));
00121 }
00122 else
00123 {
00124 G_Sound( NPC, CHAN_AUTO, G_EffectIndex(va("sound/chars/mine/misc/miss%i.wav", Q_irand(1,4))));
00125 }
00126 }
00127
00128
00129 void MineMonster_Attack( void )
00130 {
00131 if ( !TIMER_Exists( NPC, "attacking" ))
00132 {
00133
00134 if ( NPC->enemy && ((NPC->enemy->r.currentOrigin[2] - NPC->r.currentOrigin[2] > 10 && random() > 0.1f )
00135 || random() > 0.8f ))
00136 {
00137
00138 TIMER_Set( NPC, "attacking", 1750 + random() * 200 );
00139 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK4, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
00140
00141 TIMER_Set( NPC, "attack2_dmg", 950 );
00142 }
00143 else if ( random() > 0.5f )
00144 {
00145 if ( random() > 0.8f )
00146 {
00147
00148 TIMER_Set( NPC, "attacking", 850 );
00149 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
00150
00151 TIMER_Set( NPC, "attack2_dmg", 400 );
00152 }
00153 else
00154 {
00155
00156 TIMER_Set( NPC, "attacking", 850 );
00157 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
00158
00159 TIMER_Set( NPC, "attack1_dmg", 450 );
00160 }
00161 }
00162 else
00163 {
00164
00165 TIMER_Set( NPC, "attacking", 1250 );
00166 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
00167
00168 TIMER_Set( NPC, "attack1_dmg", 700 );
00169 }
00170 }
00171 else
00172 {
00173
00174 if ( TIMER_Done2( NPC, "attack1_dmg", qtrue ))
00175 {
00176 MineMonster_TryDamage( NPC->enemy, 5 );
00177 }
00178 else if ( TIMER_Done2( NPC, "attack2_dmg", qtrue ))
00179 {
00180 MineMonster_TryDamage( NPC->enemy, 10 );
00181 }
00182 }
00183
00184
00185 TIMER_Done2( NPC, "attacking", qtrue );
00186 }
00187
00188
00189 void MineMonster_Combat( void )
00190 {
00191 float distance;
00192 qboolean advance;
00193
00194
00195 if ( !NPC_ClearLOS4( NPC->enemy ) || UpdateGoal( ))
00196 {
00197 NPCInfo->combatMove = qtrue;
00198 NPCInfo->goalEntity = NPC->enemy;
00199 NPCInfo->goalRadius = MAX_DISTANCE;
00200
00201 NPC_MoveToGoal( qtrue );
00202 return;
00203 }
00204
00205
00206 NPC_FaceEnemy( qtrue );
00207
00208 distance = DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
00209
00210 advance = (qboolean)( distance > MIN_DISTANCE_SQR ? qtrue : qfalse );
00211
00212 if (( advance || NPCInfo->localState == LSTATE_WAITING ) && TIMER_Done( NPC, "attacking" ))
00213 {
00214 if ( TIMER_Done2( NPC, "takingPain", qtrue ))
00215 {
00216 NPCInfo->localState = LSTATE_CLEAR;
00217 }
00218 else
00219 {
00220 MineMonster_Move( 1 );
00221 }
00222 }
00223 else
00224 {
00225 MineMonster_Attack();
00226 }
00227 }
00228
00229
00230
00231
00232
00233
00234 void NPC_MineMonster_Pain(gentity_t *self, gentity_t *attacker, int damage)
00235 {
00236 G_AddEvent( self, EV_PAIN, floor((float)self->health/self->client->pers.maxHealth*100.0f) );
00237
00238 if ( damage >= 10 )
00239 {
00240 TIMER_Remove( self, "attacking" );
00241 TIMER_Remove( self, "attacking1_dmg" );
00242 TIMER_Remove( self, "attacking2_dmg" );
00243 TIMER_Set( self, "takingPain", 1350 );
00244
00245 VectorCopy( self->NPC->lastPathAngles, self->s.angles );
00246
00247 NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
00248
00249 if ( self->NPC )
00250 {
00251 self->NPC->localState = LSTATE_WAITING;
00252 }
00253 }
00254 }
00255
00256
00257
00258
00259
00260
00261
00262 void NPC_BSMineMonster_Default( void )
00263 {
00264 if ( NPC->enemy )
00265 {
00266 MineMonster_Combat();
00267 }
00268 else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
00269 {
00270 MineMonster_Patrol();
00271 }
00272 else
00273 {
00274 MineMonster_Idle();
00275 }
00276
00277 NPC_UpdateAngles( qtrue, qtrue );
00278 }