//------------------------------ void Howler_Attack( void ) { if ( !TIMER_Exists( NPC, "attacking" )) { // Going to do ATTACK1 TIMER_Set( NPC, "attacking", 1700 + random() * 200 ); NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); TIMER_Set( NPC, "attack_dmg", 200 ); // level two damage } // Need to do delayed damage since the attack animations encapsulate multiple mini-attacks if ( TIMER_Done2( NPC, "attack_dmg", qtrue )) { Howler_TryDamage( NPC->enemy, 5 ); } // Just using this to remove the attacking flag at the right time TIMER_Done2( NPC, "attacking", qtrue ); }
//---------------------------------- //replaced with SP version. static void Howler_Combat( void ) { qboolean faced = qfalse; float distance; qboolean advance = qfalse; if ( NPC->client->ps.groundEntityNum == ENTITYNUM_NONE ) {//not on the ground if ( NPC->client->ps.legsAnim == BOTH_JUMP1 || NPC->client->ps.legsAnim == BOTH_INAIR1 ) {//flying through the air with the greatest of ease, etc Howler_TryDamage( 10, qfalse, qfalse ); } } else {//not in air, see if we should attack or advance // If we cannot see our target or we have somewhere to go, then do that if ( !NPC_ClearLOS4( NPC->enemy ) )//|| UpdateGoal( )) { NPCInfo->goalEntity = NPC->enemy; NPCInfo->goalRadius = MAX_DISTANCE; // just get us within combat range if ( NPCInfo->localState == LSTATE_BERZERK ) { NPC_Howler_Move( 3 ); } else { NPC_Howler_Move( 10 ); } NPC_UpdateAngles( qfalse, qtrue ); return; } distance = DistanceHorizontal( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ); if ( NPC->enemy && NPC->enemy->client && PM_InKnockDown( &NPC->enemy->client->ps ) ) {//get really close to knocked down enemies advance = (qboolean)( distance > MIN_DISTANCE ? qtrue : qfalse ); } else { advance = (qboolean)( distance > MAX_DISTANCE ? qtrue : qfalse );//MIN_DISTANCE } 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 if ( TIMER_Done( NPC, "standing" ) ) { faced = Howler_Move( qtrue ); } } else { Howler_Attack( distance, qfalse ); } } if ( !faced ) { if ( //TIMER_Done( NPC, "standing" ) //not just standing there //!advance //not moving TIMER_Done( NPC, "attacking" ) )// not attacking {//not standing around // Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb NPC_FaceEnemy( qtrue ); } else { NPC_UpdateAngles( qfalse, qtrue ); } } }
static void Howler_Attack( float enemyDist, qboolean howl ) { int dmg = (NPCInfo->localState==LSTATE_BERZERK)?5:2; vec3_t boltOrg; vec3_t fwd; if ( !TIMER_Exists( NPC, "attacking" )) { int attackAnim = BOTH_GESTURE1; // Going to do an attack if ( NPC->enemy && NPC->enemy->client && PM_InKnockDown( &NPC->enemy->client->ps ) && enemyDist <= MIN_DISTANCE ) { attackAnim = BOTH_ATTACK2; } else if ( !Q_irand( 0, 4 ) || howl ) {//howl attack //G_SoundOnEnt( NPC, CHAN_VOICE, "sound/chars/howler/howl.mp3" ); } else if ( enemyDist > MIN_DISTANCE && Q_irand( 0, 1 ) ) {//lunge attack //jump foward vec3_t fwd, yawAng; VectorSet( yawAng, 0, NPC->client->ps.viewangles[YAW], 0 ); AngleVectors( yawAng, fwd, NULL, NULL ); VectorScale( fwd, (enemyDist*3.0f), NPC->client->ps.velocity ); NPC->client->ps.velocity[2] = 200; NPC->client->ps.groundEntityNum = ENTITYNUM_NONE; attackAnim = BOTH_ATTACK1; } else {//tongue attack attackAnim = BOTH_ATTACK2; } NPC_SetAnim( NPC, SETANIM_BOTH, attackAnim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_RESTART ); if ( NPCInfo->localState == LSTATE_BERZERK ) {//attack again right away TIMER_Set( NPC, "attacking", NPC->client->ps.legsTimer ); } else { TIMER_Set( NPC, "attacking", NPC->client->ps.legsTimer + Q_irand( 0, 1500 ) );//FIXME: base on skill TIMER_Set( NPC, "standing", -level.time ); TIMER_Set( NPC, "walking", -level.time ); TIMER_Set( NPC, "running", NPC->client->ps.legsTimer + 5000 ); } TIMER_Set( NPC, "attack_dmg", 200 ); // level two damage } // Need to do delayed damage since the attack animations encapsulate multiple mini-attacks switch ( NPC->client->ps.legsAnim ) { case BOTH_ATTACK1: case BOTH_MELEE1: if ( NPC->client->ps.legsTimer > 650//more than 13 frames left && BG_AnimLength( NPC->localAnimIndex, (animNumber_t)NPC->client->ps.legsAnim ) - NPC->client->ps.legsTimer >= 800 )//at least 16 frames into anim { Howler_TryDamage( dmg, qfalse, qfalse ); } break; case BOTH_ATTACK2: case BOTH_MELEE2: if ( NPC->client->ps.legsTimer > 350//more than 7 frames left && BG_AnimLength( NPC->localAnimIndex, (animNumber_t)NPC->client->ps.legsAnim ) - NPC->client->ps.legsTimer >= 550 )//at least 11 frames into anim { Howler_TryDamage( dmg, qtrue, qfalse ); } break; case BOTH_GESTURE1: { if ( NPC->client->ps.legsTimer > 1800//more than 36 frames left && BG_AnimLength( NPC->localAnimIndex, (animNumber_t)NPC->client->ps.legsAnim ) - NPC->client->ps.legsTimer >= 950 )//at least 19 frames into anim { Howler_Howl(); if ( !NPC->count ) { //RAFIXME - this probably won't work correctly. G_GetBoltPosition(NPC, NPC->NPC->genericBolt1, boltOrg, 0); AngleVectors( NPC->client->ps.viewangles, fwd, NULL, NULL ); G_PlayEffectID( G_EffectIndex( "howler/sonic" ), boltOrg, fwd); //G_PlayEffect( G_EffectIndex( "howler/sonic" ), NPC->ghoul2, NPC->NPC->genericBolt1, NPC->s.number, NPC->currentOrigin, 4750, qtrue ); G_SoundOnEnt( NPC, CHAN_VOICE, "sound/chars/howler/howl.mp3" ); NPC->count = 1; } } } break; default: //anims seem to get reset after a load, so just stop attacking and it will restart as needed. TIMER_Remove( NPC, "attacking" ); break; } // Just using this to remove the attacking flag at the right time TIMER_Done2( NPC, "attacking", qtrue ); }