/* ================ AIFunc_Helga_Melee ================ */ const char *AIFunc_Helga_Melee( cast_state_t *cs ) { gentity_t *ent = &g_entities[cs->entityNum]; gentity_t *enemy; cast_state_t *ecs; int hitDelay = -1, anim; trace_t tr; float enemyDist; aicast_predictmove_t move; vec3_t vec; cs->aiFlags |= AIFL_SPECIAL_FUNC; if ( !ent->client->ps.torsoTimer || !ent->client->ps.legsTimer ) { cs->aiFlags &= ~AIFL_SPECIAL_FUNC; return AIFunc_DefaultStart( cs ); } if ( cs->enemyNum < 0 ) { ent->client->ps.legsTimer = 0; // allow legs us to move ent->client->ps.torsoTimer = 0; // allow legs us to move cs->aiFlags &= ~AIFL_SPECIAL_FUNC; return AIFunc_DefaultStart( cs ); } ecs = AICast_GetCastState( cs->enemyNum ); enemy = &g_entities[cs->enemyNum]; anim = ( ent->client->ps.torsoAnim & ~ANIM_TOGGLEBIT ) - BG_AnimationIndexForString( "attack3", cs->entityNum ); if ( anim < 0 || anim >= NUM_HELGA_ANIMS ) { // animation interupted cs->aiFlags &= ~AIFL_SPECIAL_FUNC; return AIFunc_DefaultStart( cs ); //G_Error( "AIFunc_HelgaZombieMelee: helgaBoss using invalid or unknown attack anim" ); } if ( cs->animHitCount < MAX_HELGA_IMPACTS && helgaHitTimes[anim][cs->animHitCount] >= 0 ) { // face them VectorCopy( cs->bs->origin, vec ); vec[2] += ent->client->ps.viewheight; VectorSubtract( enemy->client->ps.origin, vec, vec ); VectorNormalize( vec ); vectoangles( vec, cs->ideal_viewangles ); cs->ideal_viewangles[PITCH] = AngleNormalize180( cs->ideal_viewangles[PITCH] ); // get hitDelay if ( !cs->animHitCount ) { hitDelay = helgaHitTimes[anim][cs->animHitCount]; } else { hitDelay = helgaHitTimes[anim][cs->animHitCount] - helgaHitTimes[anim][cs->animHitCount - 1]; } // check for inflicting damage if ( level.time - cs->weaponFireTimes[cs->weaponNum] > hitDelay ) { // do melee damage enemyDist = VectorDistance( enemy->r.currentOrigin, ent->r.currentOrigin ); enemyDist -= g_entities[cs->enemyNum].r.maxs[0]; enemyDist -= ent->r.maxs[0]; if ( enemyDist < 10 + AICast_WeaponRange( cs, cs->weaponNum ) ) { trap_Trace( &tr, ent->r.currentOrigin, NULL, NULL, enemy->r.currentOrigin, ent->s.number, MASK_SHOT ); if ( tr.entityNum == cs->enemyNum ) { G_Damage( &g_entities[tr.entityNum], ent, ent, vec3_origin, tr.endpos, helgaHitDamage[anim], 0, MOD_GAUNTLET ); G_AddEvent( enemy, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[ent->aiCharacter].soundScripts[STAYSOUNDSCRIPT] ) ); } } cs->weaponFireTimes[cs->weaponNum] = level.time; cs->animHitCount++; } } // if they are outside range, move forward AICast_PredictMovement( ecs, 2, 0.3, &move, &g_entities[cs->enemyNum].client->pers.cmd, -1 ); VectorSubtract( move.endpos, cs->bs->origin, vec ); vec[2] = 0; enemyDist = VectorLength( vec ); enemyDist -= g_entities[cs->enemyNum].r.maxs[0]; enemyDist -= ent->r.maxs[0]; if ( enemyDist > 8 ) { // we can get closer //if (!ent->client->ps.legsTimer) { // cs->castScriptStatus.scriptNoMoveTime = 0; trap_EA_MoveForward( cs->entityNum ); //} //ent->client->ps.legsTimer = 0; // allow legs us to move } return NULL; }
char *AIFunc_ZombieMelee( cast_state_t *cs ) { gentity_t *ent = &g_entities[cs->entityNum]; int hitDelay = -1, anim; trace_t *tr; cast_state_t *ecs = AICast_GetCastState( cs->enemyNum ); aicast_predictmove_t move; float enemyDist; if ( !ent->client->ps.torsoTimer ) { return AIFunc_DefaultStart( cs ); } if ( ecs ) { anim = ( ent->client->ps.torsoAnim & ~ANIM_TOGGLEBIT ) - BG_AnimationIndexForString( "attack1", cs->entityNum ); if ( anim < 0 || anim >= NUM_ZOMBIE_ANIMS ) { // animation interupted return AIFunc_DefaultStart( cs ); } if ( zombieHitTimes[anim][cs->animHitCount] >= 0 && cs->animHitCount < 3 ) { if ( !cs->animHitCount ) { hitDelay = zombieHitTimes[anim][cs->animHitCount]; } else { hitDelay = zombieHitTimes[anim][cs->animHitCount] - zombieHitTimes[anim][cs->animHitCount - 1]; } // check for inflicting damage if ( level.time - cs->weaponFireTimes[cs->weaponNum] > hitDelay ) { // do melee damage if ( ( tr = CheckMeleeAttack( ent, AICast_WeaponRange( cs, cs->weaponNum ) + 4.0, qfalse ) ) && ( tr->entityNum == cs->enemyNum ) ) { G_Damage( &g_entities[tr->entityNum], ent, ent, vec3_origin, tr->endpos, zombieHitDamage[anim], 0, MOD_GAUNTLET ); G_AddEvent( ent, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[ent->aiCharacter].soundScripts[STAYSOUNDSCRIPT] ) ); } else { G_AddEvent( ent, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[ent->aiCharacter].soundScripts[FOLLOWSOUNDSCRIPT] ) ); } cs->weaponFireTimes[cs->weaponNum] = level.time; cs->animHitCount++; } } // face them AICast_AimAtEnemy( cs ); if ( !ent->client->ps.legsTimer || zombieHitTimes[anim][cs->animHitCount] < 0 || cs->animHitCount >= 3 ) { // if they are outside range, move forward AICast_PredictMovement( ecs, 2, 0.5, &move, &g_entities[cs->enemyNum].client->pers.cmd, -1 ); enemyDist = Distance( move.endpos, cs->bs->origin ); enemyDist -= g_entities[cs->enemyNum].r.maxs[0]; enemyDist -= ent->r.maxs[0]; if ( /*anim != 4 &&*/ ( enemyDist > 16 ) ) { // we can get closer if ( ent->client->ps.legsTimer ) { ent->client->ps.legsTimer = 0; // allow legs us to move if ( cs->castScriptStatus.scriptNoMoveTime < level.time + 200 ) { // dont move until the legs are done lerping out of attack anim cs->castScriptStatus.scriptNoMoveTime = level.time + 200; } } if ( !ent->client->ps.legsTimer && cs->castScriptStatus.scriptNoMoveTime < level.time ) { trap_EA_MoveForward( cs->entityNum ); } } } } return NULL; }