/* ================ AIFunc_WarriorZombieMelee ================ */ char *AIFunc_WarriorZombieMelee( 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 ( cs->enemyNum < 0 ) { return NULL; } if ( ecs ) { anim = ( ent->client->ps.torsoAnim & ~ANIM_TOGGLEBIT ) - BG_AnimationIndexForString( "attack1", cs->entityNum ); if ( anim < 0 || anim >= NUM_WARRIOR_ANIMS ) { // animation interupted return AIFunc_DefaultStart( cs ); } if ( warriorHitTimes[anim][cs->animHitCount] >= 0 && cs->animHitCount < 3 ) { if ( !cs->animHitCount ) { hitDelay = warriorHitTimes[anim][cs->animHitCount]; } else { hitDelay = warriorHitTimes[anim][cs->animHitCount] - warriorHitTimes[anim][cs->animHitCount - 1]; } // check for inflicting damage if ( level.time - cs->weaponFireTimes[cs->weaponNum] > hitDelay ) { // do melee damage if ( ( tr = CheckMeleeAttack( ent, 44, qfalse ) ) && ( tr->entityNum == cs->enemyNum ) ) { G_Damage( &g_entities[tr->entityNum], ent, ent, vec3_origin, tr->endpos, warriorHitDamage[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 ( anim < 3 ) { // back handed-swinging, dont allow legs to move // 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 ( 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 ( cs->castScriptStatus.scriptNoMoveTime < level.time ) { trap_EA_MoveForward( cs->entityNum ); } } } } return NULL; }
/* ================ AIFunc_WarriorZombieDefense ================ */ char *AIFunc_WarriorZombieDefense( cast_state_t *cs ) { gentity_t *ent, *enemy; vec3_t enemyDir, vec; float dist; ent = &g_entities[cs->entityNum]; if ( !( ent->flags & FL_DEFENSE_GUARD ) ) { if ( cs->weaponFireTimes[cs->weaponNum] < level.time - 100 ) { return AIFunc_DefaultStart( cs ); } return NULL; } if ( ( cs->enemyNum < 0 ) || ( cs->dangerEntityValidTime >= level.time ) ) { ent->flags &= ~FL_DEFENSE_GUARD; ent->client->ps.torsoTimer = 0; ent->client->ps.legsTimer = 0; return NULL; } enemy = &g_entities[cs->enemyNum]; if ( cs->thinkFuncChangeTime < level.time - 1500 ) { // if we cant see them if ( !AICast_EntityVisible( cs, cs->enemyNum, qtrue ) ) { ent->flags &= ~FL_DEFENSE_GUARD; ent->client->ps.torsoTimer = 0; ent->client->ps.legsTimer = 0; return NULL; } // if our enemy isn't using a dangerous weapon if ( enemy->client->ps.weapon < WP_LUGER || enemy->client->ps.weapon > WP_CLASS_SPECIAL ) { ent->flags &= ~FL_DEFENSE_GUARD; ent->client->ps.torsoTimer = 0; ent->client->ps.legsTimer = 0; return NULL; } // if our enemy isn't looking right at us, abort VectorSubtract( ent->client->ps.origin, enemy->client->ps.origin, vec ); dist = VectorNormalize( vec ); if ( dist > 512 ) { dist = 512; } AngleVectors( enemy->client->ps.viewangles, enemyDir, NULL, NULL ); if ( DotProduct( vec, enemyDir ) < ( 0.98 - 0.2 * ( dist / 512 ) ) ) { ent->flags &= ~FL_DEFENSE_GUARD; ent->client->ps.torsoTimer = 0; ent->client->ps.legsTimer = 0; return NULL; } } cs->weaponFireTimes[cs->weaponNum] = level.time; if ( !ent->client->ps.torsoTimer ) { ent->flags &= ~FL_DEFENSE_GUARD; ent->client->ps.torsoTimer = 0; ent->client->ps.legsTimer = 0; return NULL; } // face them AICast_AimAtEnemy( cs ); // crouching position, use smaller bounding box trap_EA_Crouch( cs->bs->client ); return NULL; }
char *AIFunc_StimSoldierAttack1( cast_state_t *cs ) { gentity_t *ent; vec3_t vec; static vec3_t up = {0,0,1}; // ent = &g_entities[cs->entityNum]; cs->weaponFireTimes[WP_MONSTER_ATTACK1] = level.time; // face them AICast_AimAtEnemy( cs ); // // are we done with this attack? if ( cs->thinkFuncChangeTime < level.time - STIMSOLDIER_FLYJUMP_DELAY ) { // have we hit the ground yet? if ( ent->s.groundEntityNum != ENTITYNUM_NONE ) { // we are on something, have we started the landing animation? if ( !( cs->aiFlags & AIFL_LAND_ANIM_PLAYED ) ) { ent->client->ps.legsAnim = ( ( ent->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | STIMSOLDIER_FLYLAND_ANIM; ent->client->ps.legsTimer = STIMSOLDIER_FLYLAND_DURATION; // stay down until attack is finished // cs->noAttackTime = level.time + STIMSOLDIER_FLYLAND_DURATION; cs->aiFlags |= AIFL_LAND_ANIM_PLAYED; } else { if ( !ent->client->ps.legsTimer ) { // animation has finished, resume AI return AIFunc_DefaultStart( cs ); } } } else { // still flying } return NULL; } // // are we ready to start flying? if ( cs->thinkFuncChangeTime < ( level.time - STIMSOLDIER_STARTJUMP_DELAY ) ) { if ( !ent->client->ps.powerups[PW_FLIGHT] ) { // play a special ignition sound? } ent->client->ps.powerups[PW_FLIGHT] = 1; // let them fly ent->s.loopSound = level.stimSoldierFlySound; ent->client->ps.eFlags |= EF_MONSTER_EFFECT; // client-side stim engine effect if ( ent->s.effect1Time != ( cs->thinkFuncChangeTime + STIMSOLDIER_STARTJUMP_DELAY ) ) { ent->s.effect1Time = ( cs->thinkFuncChangeTime + STIMSOLDIER_STARTJUMP_DELAY ); // start the hovering animation ent->client->ps.legsAnim = ( ( ent->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | STIMSOLDIER_FLYHOVER_ANIM; } // give us some upwards velocity? if ( cs->thinkFuncChangeTime > level.time - STIMSOLDIER_FLYJUMP_DURATION * 0.9 ) { trap_EA_Move( cs->entityNum, up, 300 ); //trap_EA_Jump(cs->entityNum); VectorCopy( cs->bs->origin, cs->stimFlyAttackPos ); } else { // attack them // // if we can't attack, abort if ( AICast_CheckAttack( cs, cs->enemyNum, qfalse ) ) { // apply weapons.. trap_EA_Attack( cs->entityNum ); } // we're done here cs->thinkFuncChangeTime = -9999; } } else { // still on ground, so move forward to account for stepping animation AngleVectors( cs->viewangles, vec, NULL, NULL ); trap_EA_Move( cs->entityNum, vec, 300 ); } // if ( ent->client->ps.legsTimer < 1000 ) { ent->client->ps.legsTimer = 1000; // stay down until effect is done } // return NULL; }
char *AIFunc_ZombieFlameAttack( cast_state_t *cs ) { bot_state_t *bs; gentity_t *ent; // ent = &g_entities[cs->entityNum]; bs = cs->bs; // ent->s.onFireEnd = level.time + 2000; // if ( ent->health < 0 ) { ent->s.onFireEnd = 0; return AIFunc_DefaultStart( cs ); } // if ( cs->enemyNum < 0 ) { ent->s.onFireEnd = level.time + 1500; ent->client->ps.torsoTimer = 0; ent->client->ps.legsTimer = 0; return AIFunc_DefaultStart( cs ); } /* disabled, keep going so they cant come back for the easy kill // // if we can't see them anymore, abort immediately if (cs->vislist[cs->enemyNum].real_visible_timestamp != cs->vislist[cs->enemyNum].real_update_timestamp) { ent->s.onFireEnd = level.time + 1500; ent->client->ps.torsoTimer = 0; ent->client->ps.legsTimer = 0; return AIFunc_DefaultStart( cs ); } */ // if outside range, move closer if ( VectorDistance( cs->bs->origin, cs->vislist[cs->enemyNum].visible_pos ) > ZOMBIE_FLAME_RADIUS ) { ent->s.onFireEnd = level.time + 1500; ent->client->ps.torsoTimer = 0; ent->client->ps.legsTimer = 0; return AIFunc_DefaultStart( cs ); } // we are firing this weapon, so record it cs->weaponFireTimes[WP_MONSTER_ATTACK1] = level.time; // once an attack has started, only abort once the player leaves our view, or time runs out if ( cs->thinkFuncChangeTime < level.time - ZOMBIE_FLAME_DURATION ) { // finish this attack ent->client->ps.torsoTimer = 0; ent->client->ps.legsTimer = 0; return AIFunc_DefaultStart( cs ); } else { //ent->client->ps.torsoTimer = 400; //ent->client->ps.legsTimer = 400; // draw the client-side effect ent->client->ps.eFlags |= EF_MONSTER_EFFECT3; // inform the client of our enemies position //VectorCopy( g_entities[cs->enemyNum].client->ps.origin, ent->s.origin2 ); //ent->s.origin2[2] += g_entities[cs->enemyNum].client->ps.viewheight; // keep facing them AICast_AimAtEnemy( cs ); // look slightly downwards since animation is facing upwards slightly cs->ideal_viewangles[PITCH] += 20; } // // return NULL; }