/*=================================================================== Procedure : TURRET Fire At Target.. Input : ENEMY * Enemy Output : Nothing ===================================================================*/ void AI_TURRET_FIREATTARGET( register ENEMY * Enemy ) { OBJECT * TObject; TObject = (OBJECT*) Enemy->TShip; if( (Enemy->Type != ENEMY_MissileTurret) ) { if( Enemy->Object.Animating && !(Enemy->Object.CurAnimSeq == TURRETSEQ_Fire) ) { return; } }else{ if( Enemy->Object.Animating && (Enemy->Object.CurAnimSeq == TURRETSEQ_Opening ) ) { }else{ if( !Enemy->Object.Animating ) { SetCurAnimSeq( TURRETSEQ_Open, &Enemy->Object ); } } } AI_THINK( Enemy , true , true ); // Is it time to validate target ? if ( Enemy->Timer == 0.0F ) { if( !( Enemy->AIFlags & AI_ANYPLAYERINRANGE ) || !AI_ClearLOS( &Enemy->Object.Pos ,Enemy->Object.Group , &TObject->Pos ) ) { Enemy->AIFlags &= ~AI_ICANSEEPLAYER; SetCurAnimSeq( TURRETSEQ_Closing, &Enemy->Object ); AI_SetSCAN( Enemy ); return; } Enemy->Timer = RESET_VALIDATE_TIME + (float) Random_Range( (u_int16_t) RESET_VALIDATE_TIME ); }else{ Enemy->Timer -= framelag; if( Enemy->Timer < 0.0F ) Enemy->Timer = 0.0F; } Enemy->AIFlags |= AI_ICANSEEPLAYER; AI_UPDATEGUNS( Enemy ); }
/*------------------------------------------------------------------- Procedure : Exogenon Idle Input : ENEMY * Enemy Output : Nothing -------------------------------------------------------------------*/ void AI_EXOGENON_IDLE( register ENEMY * Enemy ) { int i; int startpos = 0; float Distance = 0.0F; AI_THINK( Enemy , TRUE , TRUE ); if( Enemy->Object.CurAnimSeq != EXOGENONSEQ_Stop_Up ) { SetCurAnimSeq( EXOGENONSEQ_Stop_Up, &Enemy->Object ); return; } Enemy->Object.Pos = Exogenon_StartPos[0]; Enemy->Object.Pos.y += 256.0F * 10.0F; if( !(Enemy->AIFlags & AI_ANYPLAYERINRANGE) ) return; if( Enemy->Timer ) { Enemy->Timer -= framelag; if( Enemy->Timer <= 0.0F ) { Enemy->Timer = 0.0F; } return; } startpos = Random_Range( (uint16) Exogenon_Num_StartPos); i = Exogenon_Num_StartPos; while( Distance < (SHIP_RADIUS * 3.0F ) && i >= 0 ) { Enemy->Object.Pos = Exogenon_StartPos[startpos]; startpos++; if( startpos >= Exogenon_Num_StartPos ) startpos = 0; Distance = DistanceVector2Vector( &Enemy->Object.Pos,&Ships[WhoIAm].Object.Pos ); i--; } SetCurAnimSeq( EXOGENONSEQ_Move_Down, &Enemy->Object ); Enemy->Object.AI_Mode = AIMODE_EXOGENON_MOVEDOWN; }
/*=================================================================== Procedure : AIR Formation Flying Input : ENEMY * Enemy Output : Nothing ===================================================================*/ void AI_LITTLEGEEK_FORMATION( register ENEMY * Enemy ) { OBJECT * SObject; VECTOR Offset; VECTOR TargetPos; ENEMY * LinkEnemy; SObject = &Enemy->Object; // Is it time to think??? AI_THINK( Enemy , false ,false); LinkEnemy = Enemy->FormationLink; if( !LinkEnemy || ( LinkEnemy->Object.Shield <= (EnemyTypes[LinkEnemy->Type].Shield * 0.5F ) ) ) { Enemy->Object.ControlType = ENEMY_CONTROLTYPE_FLY_AI; EnemyTypes[ENEMY_Boss_LittleGeek].Radius = 800 * GLOBAL_SCALE; EnemyTypes[ENEMY_Boss_BigGeek].Radius = 900.0F * GLOBAL_SCALE; AI_SetDOGFIGHT( Enemy ); SetCurAnimSeq( LITTLEGEEKSEQ_Open, &Enemy->Object ); Enemy->FormationLink = NULL; return; } SetCurAnimSeq( LITTLEGEEKSEQ_Stop, &Enemy->Object ); Offset = Enemy->FormationOffset; ApplyMatrix( &LinkEnemy->Object.Mat, &Offset, &TargetPos ); TargetPos.x += LinkEnemy->Object.Pos.x; TargetPos.y += LinkEnemy->Object.Pos.y; TargetPos.z += LinkEnemy->Object.Pos.z; Enemy->Object.Pos = TargetPos; Enemy->Object.Quat = LinkEnemy->Object.Quat; Enemy->Object.Mat = LinkEnemy->Object.Mat; Enemy->Object.InvMat = LinkEnemy->Object.InvMat; Enemy->Object.FinalMat = LinkEnemy->Object.FinalMat; Enemy->Object.FinalInvMat = LinkEnemy->Object.FinalInvMat; EnemyTypes[ENEMY_Boss_LittleGeek].Radius = 0.0F; }
/*------------------------------------------------------------------- Procedure : Exogenon Scan Input : ENEMY * Enemy Output : Nothing -------------------------------------------------------------------*/ void AI_EXOGENON_SCAN( register ENEMY * Enemy ) { ExogenonAim( Enemy ); Enemy->Object.AnimSpeed = 2.0F; if( Enemy->Object.Animating ) { return; } if( !AI_ClearLOS( &Enemy->Object.Pos,Enemy->Object.Group, &Ships[WhoIAm].Object.Pos ) ) { Enemy->Object.AI_Mode = AIMODE_EXOGENON_MOVEUP; SetCurAnimSeq( EXOGENONSEQ_Move_Up, &Enemy->Object ); ExogenonFireLeftRight( Enemy ); return; } Enemy->Object.AI_Mode = AIMODE_EXOGENON_FIRE; SetCurAnimSeq( EXOGENONSEQ_Fire, &Enemy->Object ); Enemy->Timer = 60.0F * 2.5F; }
/*------------------------------------------------------------------- Procedure : Exogenon Move Down Input : ENEMY * Enemy Output : Nothing -------------------------------------------------------------------*/ void AI_EXOGENON_MOVEDOWN( register ENEMY * Enemy ) { VECTOR TempUpVector; VECTOR FireDir; BOOL ClearLos = TRUE; VECTOR TempOffset = { 0.0F, 0.0F, 180.0F }; ExogenonAim( Enemy ); Enemy->Object.AnimSpeed = 1.5F; if( Enemy->Object.Animating ) { return; } if( !(ClearLos = AI_ClearLOSNonZeroNonObject( &Enemy->Object.Pos,Enemy->Object.Group, &Ships[WhoIAm].Object.Pos , SHIP_RADIUS)) || (Random_Range(10) > 5 ) ) { Enemy->Object.AI_Mode = AIMODE_EXOGENON_MOVEUP; SetCurAnimSeq( EXOGENONSEQ_Move_Up, &Enemy->Object ); if( ClearLos ) { ApplyMatrix( &Enemy->Object.Mat, &SlideUp, &TempUpVector ); ApplyMatrix( &Enemy->Object.Mat, &Forward, &FireDir ); // Fire a homing missile Forward... InitOneSecBull( OWNER_ENEMY, Enemy->Index, ++Enemy->BulletID, Enemy->Object.Group, &Enemy->Object.Pos, &TempOffset, &FireDir, &TempUpVector, &TempOffset, ENEMYBLUEHOMINGMISSILE, FALSE ); } ExogenonFireLeftRight( Enemy ); }else{ Enemy->Object.AI_Mode = AIMODE_EXOGENON_SCAN; SetCurAnimSeq( EXOGENONSEQ_Idle, &Enemy->Object ); } }
/*------------------------------------------------------------------- Procedure : AI for a turret while its SCANNING.. Output : ENEMY * Enemy Output : Nothing -------------------------------------------------------------------*/ void AI_TURRET_SCAN( register ENEMY * Enemy ) { OBJECT * TObject; Enemy->Timer -= framelag; if( Enemy->Object.Animating ) { return; } // Scan for target if ( Enemy->Timer <= 0.0F ) { AI_THINK( Enemy , TRUE , TRUE ); if( Enemy->AIFlags & AI_ANYPLAYERINRANGE ) { SET_TARGET_PLAYERS; AI_GetDistToNearestTarget( Enemy ); switch( Tinfo->TFlags ) { case _TARGET_PLAYERS: TObject = Tinfo->TObject; if( AI_InViewCone( &Enemy->Object.Pos ,&Enemy->Object.Mat , &TObject->Pos, Enemy->Viewcone ) ) { if( AI_ClearLOS( &Enemy->Object.Pos, Enemy->Object.Group , &TObject->Pos ) ) { Enemy->AIFlags |= AI_ICANSEEPLAYER; SetCurAnimSeq( TURRETSEQ_Opening, &Enemy->Object ); Enemy->TShip = TObject; AI_SetFIREATTARGET( Enemy ); } } break; default: // Switch to AIMODE_IDLE Enemy->Object.AI_Mode = AIMODE_IDLE; Enemy->Timer = RESET_IDLE_TIME; break; } }else{ Enemy->Object.AI_Mode = AIMODE_IDLE; Enemy->Timer = RESET_IDLE_TIME; } } }
/*------------------------------------------------------------------- Procedure : Exogenon Sweep Input : ENEMY * Enemy Output : Nothing -------------------------------------------------------------------*/ void AI_EXOGENON_SWEEP( register ENEMY * Enemy ) { if( Enemy->Timer ) { Enemy->Timer -= framelag; if( Enemy->Timer <= 0.0F ) { Enemy->Timer = 0.0F; } Enemy->AIMoveFlags = ExogenonSweepDir; Enemy->AI_Angle = ExogenonSweepAngle; ExogenonAim( Enemy ); } if( Enemy->Object.Animating ) { return; } Enemy->Object.AI_Mode = AIMODE_EXOGENON_MOVEUP; SetCurAnimSeq( EXOGENONSEQ_Move_Up, &Enemy->Object ); }
/*=================================================================== Procedure : Update an Enemies Guns... Input : ENEMY * Enemy Output : Nothing ===================================================================*/ void AI_UPDATEGUNS( register ENEMY * Enemy ) { OBJECT * TObject; GUNOBJECT * GObject; VECTOR TempVector; VECTOR TempUpVector; VECTOR TempOffset = { 0.0F, 0.0F, 0.0F }; VECTOR NewPos; VECTOR FireOffset; FIREPOS * FirePosPnt; VECTOR AimOffset; u_int16_t i; BYTE Weapon; TObject = (OBJECT*) Enemy->TShip; if( !TObject || TObject->Mode != NORMAL_MODE ) return; GObject = Enemy->Object.FirstGun; while( GObject ) { // if( TObject && ( (Enemy->AIFlags & AI_ICANSEEPLAYER) || (Enemy->Object.ControlType == ENEMY_CONTROLTYPE_TURRET_AI) || (Enemy->Object.ControlType == ENEMY_CONTROLTYPE_SPLINE) ) ) if( TObject && ( (Enemy->AIFlags & AI_ICANSEEPLAYER) || (Enemy->Object.ControlType == ENEMY_CONTROLTYPE_SPLINE) ) ) { FirePosPnt = EnemyTypes[Enemy->Type].GunFirePoints[GObject->GunNum>>1]; ApplyMatrix( &GObject->Mat , &FirePosPnt->Points[GObject->FirePosCount] , &FireOffset ); FireOffset.x += GObject->FirePos.x; FireOffset.y += GObject->FirePos.y; FireOffset.z += GObject->FirePos.z; FireOffset.x -= Enemy->Object.Pos.x; FireOffset.y -= Enemy->Object.Pos.y; FireOffset.z -= Enemy->Object.Pos.z; ApplyMatrix( &GObject->Mat, EnemyTypes[ Enemy->Type ].GunAimPos[GObject->GunNum>>1], &AimOffset ); AimOffset.x += Enemy->Object.Pos.x; AimOffset.y += Enemy->Object.Pos.y; AimOffset.z += Enemy->Object.Pos.z; if( !GunTypes[GObject->Type].BurstMasterCount ) { // Normal gun that just fires when it can.. if( GunTypes[GObject->Type].PrimarySecondary ) { AI_LookAhead( EnemyTypes[Enemy->Type].Behave.Anticipate_Move ,&AimOffset, TObject , &NewPos , SecondaryWeaponAttribs[GunTypes[GObject->Type].WeaponType].Speed); }else{ AI_LookAhead( EnemyTypes[Enemy->Type].Behave.Anticipate_Move ,&AimOffset, TObject , &NewPos , PrimaryWeaponAttribs[GunTypes[GObject->Type].WeaponType].Speed[GunTypes[GObject->Type].PowerLevel]); } AI_AimAtTarget( &GObject->InvMat , &AimOffset, &NewPos ); GObject->AIMoveFlags |= AimData.Flags; GObject->AI_Angle = AimData.Angle; if( GObject->Type == GUN_MetaTankMain ) { if( (GObject->ReloadTime <= (0.5F*60.0F) ) && !Enemy->Object.Animating ) { SetCurAnimSeq( 0, &Enemy->Object ); } } if( GObject->ReloadTime <= 0.0F && ( (( ( GObject->AI_Angle.x < GunTypes[GObject->Type].BurstAngle && GObject->AI_Angle.x > -GunTypes[GObject->Type].BurstAngle ) && ( GObject->AI_Angle.y < GunTypes[GObject->Type].BurstAngle && GObject->AI_Angle.y > -GunTypes[GObject->Type].BurstAngle ) ) ) || GObject->Type == GUN_MetaTankMain ) ) { ApplyMatrix( &GObject->Mat, &Forward, &TempVector ); ApplyMatrix( &GObject->Mat, &SlideUp, &TempUpVector ); if( !(Enemy->Object.Flags & SHIP_Scattered ) && (!GunTypes[GObject->Type].Range || ( DistanceVector2Vector( &TObject->Pos , &Enemy->Object.Pos) < GunTypes[GObject->Type].Range ) )) { if(Enemy->Object.ControlType == ENEMY_CONTROLTYPE_TURRET_AI) { if( !Enemy->Object.Animating && (Enemy->Type != ENEMY_MissileTurret) ) { SetCurAnimSeq( TURRETSEQ_Fire, &Enemy->Object ); } } if( GunTypes[GObject->Type].PrimarySecondary ) { InitOneSecBull( OWNER_ENEMY, Enemy->Index, ++Enemy->BulletID, Enemy->Object.Group, &Enemy->Object.Pos, &FireOffset, &TempVector, &TempUpVector, &TempOffset, GunTypes[GObject->Type].WeaponType, false ); }else{ Weapon = BodgePrimaryWeapon( GunTypes[GObject->Type].WeaponType, Enemy->PickupHeld ); i = EnemyFirePrimary( OWNER_ENEMY, Enemy->Index, ++Enemy->BulletID, Weapon, Enemy->Object.Group, &Enemy->Object.Pos, &FireOffset, &TempVector, &TempUpVector, GunTypes[GObject->Type].PowerLevel, (GunTypes[GObject->Type].PowerLevel +1) * 33.0F, false, GObject ); if( i != (u_int16_t) -1 ) { PrimBulls[i].FirePoint = GObject->FirePosCount; } } GObject->FirePosCount++; if( GObject->FirePosCount >= FirePosPnt->NumPoints ) GObject->FirePosCount = 0; GObject->ReloadTime =GunTypes[GObject->Type].ReloadTime; } } }else{ // A Gun That Does a Burst... if( GObject->BurstCount ) { if( GunTypes[GObject->Type].PrimarySecondary ) { AI_LookAhead( EnemyTypes[Enemy->Type].Behave.Anticipate_Move ,&AimOffset, TObject , &NewPos , SecondaryWeaponAttribs[GunTypes[GObject->Type].WeaponType].Speed); }else{ AI_LookAhead( EnemyTypes[Enemy->Type].Behave.Anticipate_Move ,&AimOffset, TObject , &NewPos , PrimaryWeaponAttribs[GunTypes[GObject->Type].WeaponType].Speed[GunTypes[GObject->Type].PowerLevel]); } AI_AimAtTarget( &GObject->InvMat , &AimOffset, &NewPos ); GObject->AIMoveFlags |= AimData.Flags; GObject->AIMoveFlags &= ~(AI_CONTROL_TURNLEFT + AI_CONTROL_TURNRIGHT ); GObject->AI_Angle = AimData.Angle; if( GObject->BurstStartSign < 0.0F ) { GObject->AIMoveFlags |= AI_CONTROL_TURNLEFT; }else{ GObject->AIMoveFlags |= AI_CONTROL_TURNRIGHT; } if( !(Enemy->Object.Flags & SHIP_Scattered ) &&GObject->ReloadTime <= 0.0F ) { ApplyMatrix( &GObject->Mat, &Forward, &TempVector ); ApplyMatrix( &GObject->Mat, &SlideUp, &TempUpVector ); if(Enemy->Object.ControlType == ENEMY_CONTROLTYPE_TURRET_AI) { if( !Enemy->Object.Animating && (Enemy->Type != ENEMY_MissileTurret) ) { SetCurAnimSeq( TURRETSEQ_Fire, &Enemy->Object ); } } if( GunTypes[GObject->Type].PrimarySecondary ) { InitOneSecBull( OWNER_ENEMY, Enemy->Index, ++Enemy->BulletID, Enemy->Object.Group, &Enemy->Object.Pos, &FireOffset, &TempVector, &TempUpVector, &TempOffset, GunTypes[GObject->Type].WeaponType, false ); }else{ Weapon = BodgePrimaryWeapon( GunTypes[GObject->Type].WeaponType, Enemy->PickupHeld ); i = EnemyFirePrimary( OWNER_ENEMY, Enemy->Index, ++Enemy->BulletID, Weapon, Enemy->Object.Group, &Enemy->Object.Pos, &FireOffset, &TempVector, &TempUpVector, GunTypes[GObject->Type].PowerLevel, (GunTypes[GObject->Type].PowerLevel +1) * 33.0F, false, GObject ); if( i != (u_int16_t) -1 ) { PrimBulls[i].FirePoint = GObject->FirePosCount; } } GObject->FirePosCount++; if( GObject->FirePosCount >= FirePosPnt->NumPoints ) GObject->FirePosCount = 0; GObject->ReloadTime = GunTypes[GObject->Type].ReloadTime; GObject->BurstCount--; } } if( !GObject->BurstCount && GunTypes[GObject->Type].BurstMasterCount ) { if( GunTypes[GObject->Type].PrimarySecondary ) { AI_LookAhead( EnemyTypes[Enemy->Type].Behave.Anticipate_Move ,&AimOffset, TObject , &NewPos , SecondaryWeaponAttribs[GunTypes[GObject->Type].WeaponType].Speed); }else{ AI_LookAhead( EnemyTypes[Enemy->Type].Behave.Anticipate_Move ,&AimOffset, TObject , &NewPos , PrimaryWeaponAttribs[GunTypes[GObject->Type].WeaponType].Speed[GunTypes[GObject->Type].PowerLevel]); } AI_AimAtTarget( &GObject->InvMat , &AimOffset, &NewPos ); GObject->AIMoveFlags |= AimData.Flags; GObject->AI_Angle = AimData.Angle; if( GObject->BurstTime == 0.0F ) { if( (GObject->AI_Angle.y <= GunTypes[GObject->Type].BurstAngle) && (GObject->AI_Angle.y >= -GunTypes[GObject->Type].BurstAngle) ) { GObject->BurstCount = GunTypes[GObject->Type].BurstMasterCount; GObject->BurstTime = GunTypes[GObject->Type].BurstMasterTime; GObject->BurstStartSign = GObject->AI_Angle.y; } } } } } GObject = GObject->Next; }
/*=================================================================== Procedure : CRAWL Follow Path Input : ENEMY * Enemy Output : Nothing ===================================================================*/ void AI_CRAWL_FOLLOWPATH( register ENEMY * Enemy ) { OBJECT * SObject; NODE * TNode; SObject = &Enemy->Object; AI_THINK( Enemy , false , false); if( !(Enemy->AIFlags & AI_ANYPLAYERINRANGE) ) return; Enemy->Timer -= framelag; if( Enemy->Timer < 0.0F ) Enemy->Timer = 0.0F; if(Enemy->Timer == 0.0F) { if( !(Enemy->AIFlags & AI_ICANSEEPLAYER )) { if( !Enemy->TShip ) { AI_DO_SCAN( Enemy ); } } Enemy->Timer = RESET_VALIDATE_TIME + (float) Random_Range( (u_int16_t) RESET_VALIDATE_TIME ); } AI_UPDATEGUNS( Enemy ); if( (Enemy->Type == ENEMY_Legz) || (Enemy->Type == ENEMY_LEADER_Legz) ) { if( !Enemy->Object.Animating ) { SetCurAnimSeq( 0, &Enemy->Object ); } Enemy->Object.AnimSpeed = 0.5F + ( ( Enemy->Object.Speed.z / EnemyTypes[Enemy->Type].MaxMoveRate ) * 2.0F ); } if( TNode = (NODE*) Enemy->TNode ) { if( !(EnemyTypes[Enemy->Type].Behave.Flags & AI_BEHAVIOUR_NOTURN) ) { AI_AimAtTarget( &Enemy->Object.InvMat , &Enemy->Object.Pos, &TNode->SolidPos ); Enemy->AIMoveFlags |= AimData.Flags; Enemy->AI_Angle = AimData.Angle; if( ( EnemyTypes[Enemy->Type].Behave.Flags & AI_BEHAVIOUR_DONTSTOPANDTURN ) || ( (AimData.Angle.y < 10.0F) && (AimData.Angle.y > -10.0F) ) ) { Enemy->AIMoveFlags |= AI_CONTROL_FORWARD; } }else{ Enemy->AIMoveFlags |= AI_CONTROL_FORWARD; } if(Enemy->PickNewNodeNow) { Enemy->PickNewNodeNow = false; Enemy->LastTNode = TNode; if( !Enemy->NextTNode ) { Enemy->Object.NearestNode = TNode; Enemy->NextTNode = FindSuitableSplineNode( Enemy->Object.NodeNetwork, Enemy->Object.NearestNode , Enemy->Object.NearestNode , Enemy->LastTNode , Enemy->NextTNode , Enemy->TNode ); } TNode = Enemy->NextTNode; Enemy->TNode = TNode; if( TNode ) { Enemy->Object.NearestNode = TNode; Enemy->NextTNode = FindSuitableSplineNode( Enemy->Object.NodeNetwork, Enemy->Object.NearestNode , Enemy->Object.NearestNode , Enemy->LastTNode , Enemy->NextTNode , Enemy->TNode ); } return; } }else{ // If no target node has been found yet go to the nearest one... Enemy->TNode = Enemy->Object.NearestNode; Enemy->NextTNode = NULL; } }