/*------------------------------------------------------------------- Procedure : AI for a Crawling Enemy while its SCANNING.. Output : ENEMY * Enemy Output : Nothing -------------------------------------------------------------------*/ void AI_CRAWL_SCAN( register ENEMY * Enemy ) { if( Enemy->Object.Animating ) { return; } if ( Enemy->Timer ) { Enemy->Timer -= framelag; if ( Enemy->Timer < 0.0F ) { //------------------------------------------------------------------------------ // Switch to AIMODE_IDLE //------------------------------------------------------------------------------ Enemy->Object.AI_Mode = AIMODE_IDLE; Enemy->Timer = RESET_SCAN_TIME + (float) Random_Range( (uint16) RESET_SCAN_TIME ); return; } } }
void BOTAI_GetNextNodeAlpha() { // random choice to go left or right at the rocks if( CurrentNode == 11 && ReverseNetwork ) { if(Random_Range(2) > 0) CurrentNode = 4; else CurrentNode--; return; } // random choice to go down or straight at the light room if( CurrentNode == 4 && !ReverseNetwork ) { if(Random_Range(2) > 0) CurrentNode = 11; else CurrentNode++; return; } CurrentNode++; // following the main network in reverse if( ReverseNetwork ) CurrentNode -= 2; // in the lower pit if( CurrentNode > ALPHA_NUM_NODES_MAIN ) CurrentNode = ALPHA_TOP_OF_PIT_NODE; // looping over the node network else if( CurrentNode > ALPHA_NUM_NODES_MAIN-1 ) { ReverseNetwork = true; CurrentNode = ALPHA_NUM_NODES_MAIN-1; } else if( CurrentNode < 0 ) { ReverseNetwork = false; CurrentNode = 0; } }
/*=================================================================== 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; }
void Particles_RainSnowEffect(Vector3 pos) { Vector3 startPos = pos; Int32 i; for (i = 0; i < 2; i++) { Real32 velX = Random_Float(&rnd) * 0.8f - 0.4f; /* [-0.4, 0.4] */ Real32 velZ = Random_Float(&rnd) * 0.8f - 0.4f; Real32 velY = Random_Float(&rnd) + 0.4f; Vector3 velocity = Vector3_Create3(velX, velY, velZ); Vector3 offset; offset.X = Random_Float(&rnd); /* [0.0, 1.0] */ offset.Y = Random_Float(&rnd) * 0.1f + 0.01f; offset.Z = Random_Float(&rnd); if (Rain_Count == PARTICLES_MAX) Rain_RemoveAt(0); RainParticle* p = &Rain_Particles[Rain_Count++]; Vector3_Add(&pos, &startPos, &offset); Particle_Reset(&p->Base, pos, velocity, 40.0f); Int32 type = Random_Range(&rnd, 0, 30); p->Base.Size = (UInt8)(type >= 28 ? 2 : (type >= 25 ? 4 : 3)); } }
/*------------------------------------------------------------------- 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 : Check if im in an Active Teleport.... Input : void Output : true/false ===================================================================*/ bool TeleportsAreaCheck( VECTOR * NewPos , VECTOR * OldPos ,u_int16_t Group, OBJECT *obj ) { TELEPORT * TPpnt; TELEPORT * newTPpnt; TPpnt = TeleportsGroupLink[Group]; while( TPpnt ) { if( TPpnt->Status == TELEPORTACTIVE ) { TeleportsZoneCheck( OldPos , NewPos , TPpnt ); if( Entry ) { PlaySfx( SFX_Teleport, 1.0F ); // StartShipScreenShake( 10.0F ); // Were in the Zone.... newTPpnt = Teleports; newTPpnt += TPpnt->Links[Random_Range(TPpnt->num_links)]; obj->Group = newTPpnt->Group; obj->Pos = newTPpnt->Pos; #if TELEPORTS_VERSION_NUMBER >= 2 QuatFromDirAndUp( &newTPpnt->Dir, &newTPpnt->Up, &obj->Quat); QuatToMatrix( &obj->Quat, &obj->Mat ); #endif return true; } } TPpnt = TPpnt->NextInGroup; } return false; }
/*------------------------------------------------------------------- Procedure : Update a trigger var..and then checks to see if all the triggers associated with any condition that this trigger is used in is complete if it is Set the events off... Input : TRIGGERVAR * TrigVar int Op int Val Output : nothing -------------------------------------------------------------------*/ void ModifyTriggerVar( TRIGGERVAR * TrigVar , int Op , int Val ) { BOOL Doit; int i; TRIGGER * Trig; int old_state; uint32 flags; int count; old_state = TrigVar->State; switch( Op ) { case TRIGGEROP_Set: TrigVar->State = Val; break; case TRIGGEROP_Reset: TrigVar->State = TrigVar->InitState; break; case TRIGGEROP_Inc: TrigVar->State += Val; break; case TRIGGEROP_Dec: TrigVar->State -= Val; break; case TRIGGEROP_Or: TrigVar->State |= Val; break; case TRIGGEROP_And: TrigVar->State &= Val; break; case TRIGGEROP_Xor: TrigVar->State ^= Val; break; case TRIGGEROP_Multiply: TrigVar->State *= Val; break; case TRIGGEROP_Divide: TrigVar->State /= Val; break; case TRIGGEROP_Random: TrigVar->State = (int) Random_Range( (uint16) Val ); break; case TRIGGEROP_Setflag: TrigVar->State |= FLAG_MASK( Val ); break; case TRIGGEROP_Clearflag: TrigVar->State &= ~FLAG_MASK( Val ); break; } if( TrigVar->State == old_state ) return; #ifdef DEBUG_TRIGGERS DebugPrintf( "modifytrigvar: var=%s(=%d) op=%d val=%d -> %d\n", TrigVar->Name, old_state, Op, Val, TrigVar->State ); #endif for( i = 0 ; i < TrigVar->NumOfTriggers ; i++ ) { Trig = TrigVar->Triggers[i]; Doit = FALSE; switch( Trig->Type ) { case TRIGGERTYPE_Equal: if( TrigVar->State == Trig->ActiveState ) Doit = TRUE; break; case TRIGGERTYPE_NotEqual: if( TrigVar->State != Trig->ActiveState ) Doit = TRUE; break; case TRIGGERTYPE_Less: if( TrigVar->State < Trig->ActiveState ) Doit = TRUE; break; case TRIGGERTYPE_Greater: if( TrigVar->State > Trig->ActiveState ) Doit = TRUE; break; case TRIGGERTYPE_LessEqual: if( TrigVar->State <= Trig->ActiveState ) Doit = TRUE; break; case TRIGGERTYPE_GreaterEqual: if( TrigVar->State >= Trig->ActiveState ) Doit = TRUE; break; case TRIGGERTYPE_FlagCount: count = 0; for ( flags = TrigVar->State; flags; flags >>= 1 ) { if ( flags & 1 ) count++; } if ( count == Trig->ActiveState ) Doit = TRUE; break; case TRIGGERTYPE_FlagTest: if ( TrigVar->State & FLAG_MASK( Trig->ActiveState ) ) Doit = TRUE; break; } if ( Doit ) { #ifdef DEBUG_TRIGGERS DebugPrintf( "modifytrigvar: trigger %d (type=%d, activestate=%d) activated (was %d)\n", Trig - Triggers, (int) Trig->Type, Trig->ActiveState, Trig->Active ); #endif if( !(_strnicmp( "secret" , &TrigVar->Name[0], 6 ) ) ) { PlaySfx( SFX_Secret, 1.0F ); } } Trig->Active = Doit; if( Trig->Active ) { TestAllConditions( Trig ); } } }
/*=================================================================== Procedure : AIR Formation Flying Input : ENEMY * Enemy Output : Nothing ===================================================================*/ void AI_AIR_FORMATION( register ENEMY * Enemy ) { float Dist; OBJECT * TObject; OBJECT * SObject; VECTOR NewPos; VECTOR TempVector; VECTOR TempUpVector; VECTOR TempOffset = { 0.0F, 0.0F, 0.0F }; VECTOR Offset; VECTOR TargetPos; VECTOR TempDir; ENEMY * LinkEnemy; SObject = &Enemy->Object; // Is it time to think??? AI_THINK( Enemy , false ,false); LinkEnemy = Enemy->FormationLink; if( !LinkEnemy || ( LinkEnemy->Object.AI_Mode == AIMODE_RETREAT ) ) { if( Enemy->AIFlags & ( AI_ICANSEEPLAYER + AI_ICANHEARPLAYER ) ) { AI_SetDOGFIGHT( Enemy ); return; }else{ AI_SetFOLLOWPATH( Enemy ); return; } } TObject = (OBJECT*) Enemy->TShip; Enemy->Timer -= framelag; if( Enemy->Timer < 0.0F ) Enemy->Timer = 0.0F; Enemy->PrimaryFireTimer -= framelag; if( Enemy->PrimaryFireTimer < 0.0F ) Enemy->PrimaryFireTimer = 0.0F; Enemy->SecondaryFireTimer -= framelag; if( Enemy->SecondaryFireTimer < 0.0F ) Enemy->SecondaryFireTimer = 0.0F; if( Enemy->AIFlags & ( AI_ICANSEEPLAYER + AI_ICANHEARPLAYER ) ) { Enemy->TNode = NULL; if( !(Enemy->Object.Flags & SHIP_Scattered ) && (Enemy->AIFlags & AI_ICANSEEPLAYER) && !(Enemy->AIFlags & AI_FRIENDLYFIRE) ) { ApplyMatrix( &SObject->Mat, &Forward, &TempVector ); ApplyMatrix( &SObject->Mat, &SlideUp, &TempUpVector ); if( (Enemy->PrimaryFireTimer == 0.0F) && ( EnemyTypes[Enemy->Type].PrimaryWeaponType != NO_PRIMARY ) ) { Enemy->PrimaryFireTimer = EnemyTypes[Enemy->Type].PrimaryFireRate + (float) Random_Range( (u_int16_t) EnemyTypes[Enemy->Type].PrimaryFireRate ); EnemyFirePrimary( OWNER_ENEMY, Enemy->Index, ++Enemy->BulletID, EnemyTypes[Enemy->Type].PrimaryWeaponType, Enemy->Object.Group, &Enemy->Object.Pos, &TempOffset, &TempVector, &TempUpVector, EnemyTypes[Enemy->Type].PowerLevel, 0.0F, false, NULL ); } } } if( (Enemy->Object.Flags & SHIP_Scattered ) || ( (EnemyTypes[Enemy->Type].Behave.Flags & AI_BEHAVIOUR_RETREAT) && ( Enemy->Object.Shield <= (EnemyTypes[Enemy->Type].Shield * 0.15 ) ) ) ) { // we should get out of hear.. Enemy->FormationLink = NULL; AI_SetRETREAT( Enemy ); return; } AI_UPDATEGUNS( Enemy ); // Aim at target if( TObject && ( Enemy->AIFlags & AI_ICANSEEPLAYER ) ) { if( EnemyTypes[Enemy->Type].PrimaryWeaponType != NO_PRIMARY ) { AI_LookAhead( EnemyTypes[Enemy->Type].Behave.Anticipate_Move , &Enemy->Object.Pos , TObject , &NewPos , PrimaryWeaponAttribs[EnemyTypes[Enemy->Type].PrimaryWeaponType].Speed[0] ); }else{ NewPos = TObject->Pos; } AI_AimAtTarget( &Enemy->Object.InvMat , &Enemy->Object.Pos, &NewPos ); if( !( EnemyTypes[Enemy->Type].Behave.Flags & AI_BEHAVIOUR_ICANTPITCH ) ) { Enemy->AIMoveFlags |= AimData.Flags; }else{ //This enemy cant look up or down so it has to move up and down to compensate.... Enemy->AIMoveFlags |= ( AimData.Flags & ( AI_CONTROL_TURNLEFT + AI_CONTROL_TURNRIGHT ) ); if( AimData.Flags & AI_CONTROL_TURNUP ) { Enemy->AIMoveFlags |= AI_CONTROL_UP; }else if( AimData.Flags & AI_CONTROL_TURNDOWN ) { Enemy->AIMoveFlags |= AI_CONTROL_DOWN; } } Enemy->AI_Angle = AimData.Angle; }else{ ApplyMatrix( &LinkEnemy->Object.Mat, &Forward, &TempDir ); TempDir.x *= 1024.0F * GLOBAL_SCALE * 5.0F; TempDir.y *= 1024.0F * GLOBAL_SCALE * 5.0F; TempDir.z *= 1024.0F * GLOBAL_SCALE * 5.0F; NewPos.x = LinkEnemy->Object.Pos.x + TempDir.x; NewPos.y = LinkEnemy->Object.Pos.y + TempDir.y; NewPos.z = LinkEnemy->Object.Pos.z + TempDir.z; AI_AimAtTarget( &Enemy->Object.InvMat , &Enemy->Object.Pos, &NewPos ); if( !( EnemyTypes[Enemy->Type].Behave.Flags & AI_BEHAVIOUR_ICANTPITCH ) ) { Enemy->AIMoveFlags |= AimData.Flags; }else{ //This enemy cant look up or down so it has to move up and down to compensate.... Enemy->AIMoveFlags |= ( AimData.Flags & ( AI_CONTROL_TURNLEFT + AI_CONTROL_TURNRIGHT ) ); if( AimData.Flags & AI_CONTROL_TURNUP ) { Enemy->AIMoveFlags |= AI_CONTROL_UP; }else if( AimData.Flags & AI_CONTROL_TURNDOWN ) { Enemy->AIMoveFlags |= AI_CONTROL_DOWN; } } Enemy->AI_Angle = AimData.Angle; } if( Enemy->AvoidTimer ) { Enemy->AIMoveFlags = Enemy->AvoidType; }else{ 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; Dist = DistanceVector2Vector( &TargetPos , &Enemy->Object.Pos); if( Dist > (EnemyTypes[Enemy->Type].Radius * 0.75F) ) { Offset.x = TargetPos.x - Enemy->Object.Pos.x; Offset.y = TargetPos.y - Enemy->Object.Pos.y; Offset.z = TargetPos.z - Enemy->Object.Pos.z; ApplyMatrix( &Enemy->Object.InvMat, &Offset, &TargetPos ); if( TargetPos.x < -(EnemyTypes[Enemy->Type].Radius * 0.25F) ) { Enemy->AIMoveFlags |= AI_CONTROL_LEFT; } if( TargetPos.x > (EnemyTypes[Enemy->Type].Radius * 0.25F) ) { Enemy->AIMoveFlags |= AI_CONTROL_RIGHT; } if( TargetPos.y < -(EnemyTypes[Enemy->Type].Radius * 0.25F) ) { Enemy->AIMoveFlags |= AI_CONTROL_DOWN; } if( TargetPos.y > (EnemyTypes[Enemy->Type].Radius * 0.25F) ) { Enemy->AIMoveFlags |= AI_CONTROL_UP; } if( TargetPos.z < -(EnemyTypes[Enemy->Type].Radius * 0.25F) ) { Enemy->AIMoveFlags |= AI_CONTROL_BACK; } if( TargetPos.z > (EnemyTypes[Enemy->Type].Radius * 0.25F) ) { Enemy->AIMoveFlags |= AI_CONTROL_FORWARD; } } } }
void BOTAI_UpdateSensors() { int CurrentDeaths = GetTotalDeaths(WhoIAm); int CurrentKills = GetTotalKills(WhoIAm); int i; // i died or killed someone if( CurrentDeaths > PrevDeaths || CurrentKills > PrevKills ) { // mix up the route if(Random_Range(2) > 0) ReverseNetwork = true; else ReverseNetwork = false; // i died, reset flags if( CurrentDeaths > PrevDeaths ) { //DebugPrintf("i died\n"); CurrentNode = -1; GettingPickup = -1; IFiredTitan = -1.0F; TargetMine = -1; HomingMissile = -1; PrevHomingMissile = -1; MissileAvoidanceSet = false; PrevHull = MAX_HULL+1.0F; PrevShield = MAX_SHIELD+1.0F; } PrevDeaths = CurrentDeaths; PrevKills = CurrentKills; } // i got hit if( Ships[WhoIAm].Object.Hull < PrevHull || Ships[WhoIAm].Object.Shield < PrevShield ) { PrevHull = Ships[WhoIAm].Object.Hull; PrevShield = Ships[WhoIAm].Object.Shield; // debugging stuff if(!initialised) initialised = true; else DEBUG_AVOIDANCE = false; } // -- update sensors // up, down, left, right, forward, backward DistWall[0] = BOTAI_DistanceToWall(false,false,false,true,false,false); // right DistWall[1] = BOTAI_DistanceToWall(false,false,true,false,false,false); // left DistWall[2] = BOTAI_DistanceToWall(true,false,false,false,false,false); // up DistWall[3] = BOTAI_DistanceToWall(false,true,false,false,false,false); // down DistWall[4] = BOTAI_DistanceToWall(true,false,false,true,false,false); // up right DistWall[5] = BOTAI_DistanceToWall(true,false,true,false,false,false); // up left DistWall[6] = BOTAI_DistanceToWall(false,true,false,true,false,false); // down right DistWall[7] = BOTAI_DistanceToWall(false,true,true,false,false,false); // down left DistWall[8] = BOTAI_DistanceToWall(false,false,false,false,true,false); // forward DistWall[9] = BOTAI_DistanceToWall(false,false,false,false,false,true); // backward DistWall[10] = BOTAI_DistanceToWall(false,false,false,true,false,true); // right backward DistWall[11] = BOTAI_DistanceToWall(false,false,true,false,false,true); // left backward DistWall[12] = BOTAI_DistanceToWall(true,false,false,false,false,true); // up backward DistWall[13] = BOTAI_DistanceToWall(false,true,false,false,false,true); // down backward DistWall[14] = BOTAI_DistanceToWall(true,false,false,true,false,true); // up right backward DistWall[15] = BOTAI_DistanceToWall(true,false,true,false,false,true); // up left backward DistWall[16] = BOTAI_DistanceToWall(false,true,false,true,false,true); // down right backward DistWall[17] = BOTAI_DistanceToWall(false,true,true,false,false,true); // down left backward DistWall[18] = BOTAI_DistanceToWall(false,false,false,true,true,false); // right forward DistWall[19] = BOTAI_DistanceToWall(false,false,true,false,true,false); // left forward DistWall[20] = BOTAI_DistanceToWall(true,false,false,false,true,false); // up forward DistWall[21] = BOTAI_DistanceToWall(false,true,false,false,true,false); // down forward DistWall[22] = BOTAI_DistanceToWall(true,false,false,true,true,false); // up right forward DistWall[23] = BOTAI_DistanceToWall(true,false,true,false,true,false); // up left forward DistWall[24] = BOTAI_DistanceToWall(false,true,false,true,true,false); // down right forward DistWall[25] = BOTAI_DistanceToWall(false,true,true,false,true,false); // down left forward AllSensorsClear = true; WhenWillHitSlide[0] = BOTAI_WhenWillBulletHitSlide(false,false,false,true,false,false); // r WhenWillHitSlide[1] = BOTAI_WhenWillBulletHitSlide(false,false,true,false,false,false); // l WhenWillHitSlide[2] = BOTAI_WhenWillBulletHitSlide(true,false,false,false,false,false); // u WhenWillHitSlide[3] = BOTAI_WhenWillBulletHitSlide(false,true,false,false,false,false); // d WhenWillHitSlide[4] = BOTAI_WhenWillBulletHitSlide(true,false,false,true,false,false); // u r WhenWillHitSlide[5] = BOTAI_WhenWillBulletHitSlide(true,false,true,false,false,false); // u l WhenWillHitSlide[6] = BOTAI_WhenWillBulletHitSlide(false,true,false,true,false,false); // d r WhenWillHitSlide[7] = BOTAI_WhenWillBulletHitSlide(false,true,true,false,false,false); // d l WhenWillHitSlide[8] = BOTAI_WhenWillBulletHitSlide(false,false,false,false,true,false); // f WhenWillHitSlide[9] = BOTAI_WhenWillBulletHitSlide(false,false,false,false,false,true); // b WhenWillHitSlide[10] = BOTAI_WhenWillBulletHitSlide(false,false,false,true,false,true); // r b WhenWillHitSlide[11] = BOTAI_WhenWillBulletHitSlide(false,false,true,false,false,true); // l b WhenWillHitSlide[12] = BOTAI_WhenWillBulletHitSlide(true,false,false,false,false,true); // u b WhenWillHitSlide[13] = BOTAI_WhenWillBulletHitSlide(false,true,false,false,false,true); // d b WhenWillHitSlide[14] = BOTAI_WhenWillBulletHitSlide(true,false,false,true,false,true); // u r b WhenWillHitSlide[15] = BOTAI_WhenWillBulletHitSlide(true,false,true,false,false,true); // u l b WhenWillHitSlide[16] = BOTAI_WhenWillBulletHitSlide(false,true,false,true,false,true); // d r b WhenWillHitSlide[17] = BOTAI_WhenWillBulletHitSlide(false,true,true,false,false,true); // d l b WhenWillHitSlide[18] = BOTAI_WhenWillBulletHitSlide(false,false,false,true,true,false); // r f WhenWillHitSlide[19] = BOTAI_WhenWillBulletHitSlide(false,false,true,false,true,false); // l f WhenWillHitSlide[20] = BOTAI_WhenWillBulletHitSlide(true,false,false,false,true,false); // u f WhenWillHitSlide[21] = BOTAI_WhenWillBulletHitSlide(false,true,false,false,true,false); // d f WhenWillHitSlide[22] = BOTAI_WhenWillBulletHitSlide(true,false,false,true,true,false); // u r f WhenWillHitSlide[23] = BOTAI_WhenWillBulletHitSlide(true,false,true,false,true,false); // u l f WhenWillHitSlide[24] = BOTAI_WhenWillBulletHitSlide(false,true,false,true,true,false); // d r f WhenWillHitSlide[25] = BOTAI_WhenWillBulletHitSlide(false,true,true,false,true,false); // d l f // time since i last fired titan // used so i don't move into the blast IFiredTitan -= framelag; BOTAI_GetNearestPickup(); FriendlyFire = BOTAI_FriendlyFireCheck(); BOTAI_GetSHIPTarget(); //DebugPrintf("r: %.1f l: %.1f u: %.1f d: %.1f f: %.1f b: %.1f\n", DistWall[0], DistWall[1], DistWall[2], DistWall[3], DistWall[8], DistWall[9]); //DebugPrintf("ur: %.1f ul: %.1f dr: %.1f dl: %.1f\n", DistWall[22], DistWall[23], DistWall[24], DistWall[25]); //DebugPrintf("%f %f %f\n", Ships[WhoIAm].Object.Pos.x, Ships[WhoIAm].Object.Pos.y, Ships[WhoIAm].Object.Pos.z); //DebugPrintf("Level: %s\n", ShortLevelNames[LevelNum]); //DebugPrintf("nearest node = %d\n", BOTAI_GetNearestNode(&Ships[WhoIAm].Object)); // if there is a target if(TargetShipID > - 1) { // distance to target TargetShipDistance = DistanceVector2Vector(&Ships[WhoIAm].Object.Pos, &Ships[TargetShipID].Object.Pos); // health of target TargetShipHealth = PlayerHealths[TargetShipID].Shield + PlayerHealths[TargetShipID].Hull; } }
/*=================================================================== Procedure : AI Spline Movement.... Input : ENEMY * Enemy Output : Nothing ===================================================================*/ void AI_SPLINE_FOLLOWPATH( register ENEMY * Enemy ) { NODE * TNode; NODE * Node1; NODE * Node2; NODE * Node3; NODE * Node4; VECTOR OldPos; VECTOR MoveOffset; float Distance; float WantedDistance; float Time; float Tstep; VECTOR TempPos; int Count; OldPos = Enemy->Object.Pos; if( !Enemy->SplineNode1 ) { //We have to start TNode = Enemy->Object.NearestNode; Enemy->SplineNode1 = (void*) TNode; Enemy->SplineNode2 = (void*) TNode; Node1 = (NODE*)Enemy->SplineNode1; Node2 = (NODE*)Enemy->SplineNode2; Enemy->SplineNode3 = (void*) FindSuitableSplineNode( Enemy->Object.NodeNetwork ,Node2 , Node1 , Node2 , NULL , NULL ); Node3 = (NODE*)Enemy->SplineNode3; Enemy->SplineNode4 = (void*) FindSuitableSplineNode( Enemy->Object.NodeNetwork ,Node3 , Node1 , Node2 , Node3 , NULL ); } Node1 = (NODE*)Enemy->SplineNode1; Node2 = (NODE*)Enemy->SplineNode2; Node3 = (NODE*)Enemy->SplineNode3; Node4 = (NODE*)Enemy->SplineNode4; WantedDistance = (EnemyTypes[Enemy->Type].MaxMoveRate*0.65F) * framelag; Tstep = ( WantedDistance / DistanceVector2Vector( &Node2->Pos , &Node3->Pos ) ) * 0.1F; Distance = DistanceVector2Vector( &Node2->Pos , &Node3->Pos ); Time = (Distance / (EnemyTypes[Enemy->Type].MaxMoveRate * 0.65F) ); Distance = 0.0F; Count = 0; do { Enemy->Timer += Tstep; if( Enemy->Timer >= 1.0F ) { Enemy->SplineNode1 = Enemy->SplineNode2; Enemy->SplineNode2 = Enemy->SplineNode3; Enemy->SplineNode3 = Enemy->SplineNode4; Node1 = (NODE*)Enemy->SplineNode1; Node2 = (NODE*)Enemy->SplineNode2; Node3 = (NODE*)Enemy->SplineNode3; Enemy->Timer = ((Enemy->Timer - 1.0F) * Time); Distance = DistanceVector2Vector( &Node2->Pos , &Node3->Pos ); Time = (Distance / (EnemyTypes[Enemy->Type].MaxMoveRate*0.65F) ); Enemy->Timer /= Time; Enemy->SplineNode4 = (void*) FindSuitableSplineNodeRandom( Enemy->Object.NodeNetwork ,Node3 , Node1 , Node2 , Node3 , NULL ); Node4 = (NODE*)Enemy->SplineNode4; Tstep = ( WantedDistance / DistanceVector2Vector( &Node2->Pos , &Node3->Pos ) ) * 0.1F; if( Node3 && (Node3->Flags&NODE_TERMINATE) ) { KillUsedEnemy( Enemy ); return; } } TempPos = Enemy->Object.Pos; spline(&Enemy->Object.Pos, Enemy->Timer, &Node1->Pos, &Node2->Pos, &Node3->Pos, &Node4->Pos); Distance += DistanceVector2Vector( &Enemy->Object.Pos , &TempPos); Count++; }while( ( Distance < WantedDistance ) && (Count < 100) ); AI_THINK( Enemy , true , true); if( (Enemy->AIFlags & AI_ANYPLAYERINRANGE) ) { Enemy->PrimaryFireTimer -= framelag; if( Enemy->PrimaryFireTimer < 0.0F ) Enemy->PrimaryFireTimer = 0.0F; if(Enemy->PrimaryFireTimer == 0.0F) { if( !Enemy->TShip ) { AI_DO_SCAN( Enemy ); } Enemy->PrimaryFireTimer = RESET_VALIDATE_TIME + (float) Random_Range( (u_int16_t) RESET_VALIDATE_TIME ); } } AI_UPDATEGUNS( Enemy ); MoveOffset.x = Enemy->Object.Pos.x - OldPos.x; MoveOffset.y = Enemy->Object.Pos.y - OldPos.y; MoveOffset.z = Enemy->Object.Pos.z - OldPos.z; Enemy->Object.Group = MoveGroup( &Mloadheader, &OldPos, Enemy->Object.Group, &MoveOffset ); }
static void Builder_DrawSprite(Int32 count) { TextureLoc texLoc = Block_GetTexLoc(Builder_Block, FACE_XMAX); Int32 i = Atlas1D_Index(texLoc); Real32 vOrigin = Atlas1D_RowId(texLoc) * Atlas1D_InvTileSize; Real32 X = (Real32)Builder_X, Y = (Real32)Builder_Y, Z = (Real32)Builder_Z; #define u1 0.0f #define u2 UV2_Scale Real32 x1 = (Real32)X + 2.50f / 16.0f, y1 = (Real32)Y, z1 = (Real32)Z + 2.50f / 16.0f; Real32 x2 = (Real32)X + 13.5f / 16.0f, y2 = (Real32)Y + 1.0f, z2 = (Real32)Z + 13.5f / 16.0f; Real32 v1 = vOrigin, v2 = vOrigin + Atlas1D_InvTileSize * UV2_Scale; UInt8 offsetType = Block_SpriteOffset[Builder_Block]; if (offsetType >= 6 && offsetType <= 7) { Random_SetSeed(&spriteRng, (Builder_X + 1217 * Builder_Z) & 0x7fffffff); Real32 valX = Random_Range(&spriteRng, -3, 3 + 1) / 16.0f; Real32 valY = Random_Range(&spriteRng, 0, 3 + 1) / 16.0f; Real32 valZ = Random_Range(&spriteRng, -3, 3 + 1) / 16.0f; #define stretch 1.7f / 16.0f x1 += valX - stretch; x2 += valX + stretch; z1 += valZ - stretch; z2 += valZ + stretch; if (offsetType == 7) { y1 -= valY; y2 -= valY; } } Builder1DPart* part = &Builder_Parts[i]; PackedCol white = PACKEDCOL_WHITE; PackedCol col = Builder_FullBright ? white : Lighting_Col_Sprite_Fast(Builder_X, Builder_Y, Builder_Z); Block_Tint(col, Builder_Block); VertexP3fT2fC4b v; v.Col = col; /* Draw Z axis */ Int32 index = part->sOffset; v.X = x1; v.Y = y1; v.Z = z1; v.U = u2; v.V = v2; Builder_Vertices[index + 0] = v; v.Y = y2; v.V = v1; Builder_Vertices[index + 1] = v; v.X = x2; v.Z = z2; v.U = u1; Builder_Vertices[index + 2] = v; v.Y = y1; v.V = v2; Builder_Vertices[index + 3] = v; /* Draw Z axis mirrored */ index += part->sAdvance; v.X = x2; v.Y = y1; v.Z = z2; v.U = u2; Builder_Vertices[index + 0] = v; v.Y = y2; v.V = v1; Builder_Vertices[index + 1] = v; v.X = x1; v.Z = z1; v.U = u1; Builder_Vertices[index + 2] = v; v.Y = y1; v.V = v2; Builder_Vertices[index + 3] = v; /* Draw X axis */ index += part->sAdvance; v.X = x1; v.Y = y1; v.Z = z2; v.U = u2; Builder_Vertices[index + 0] = v; v.Y = y2; v.V = v1; Builder_Vertices[index + 1] = v; v.X = x2; v.Z = z1; v.U = u1; Builder_Vertices[index + 2] = v; v.Y = y1; v.V = v2; Builder_Vertices[index + 3] = v; /* Draw X axis mirrored */ index += part->sAdvance; v.X = x2; v.Y = y1; v.Z = z1; v.U = u2; Builder_Vertices[index + 0] = v; v.Y = y2; v.V = v1; Builder_Vertices[index + 1] = v; v.X = x1; v.Z = z2; v.U = u1; Builder_Vertices[index + 2] = v; v.Y = y1; v.V = v2; Builder_Vertices[index + 3] = v; part->sOffset += 4; }
void Particles_BreakBlockEffect(Vector3I coords, BlockID oldBlock, BlockID block) { if (block != BLOCK_AIR || Block_Draw[oldBlock] == DRAW_GAS) return; block = oldBlock; Vector3 worldPos; Vector3I_ToVector3(&worldPos, &coords); TextureLoc texLoc = Block_GetTexLoc(block, FACE_XMIN); Int32 texIndex; TextureRec baseRec = Atlas1D_TexRec(texLoc, 1, &texIndex); Real32 uScale = (1.0f / 16.0f), vScale = (1.0f / 16.0f) * Atlas1D_InvTileSize; Vector3 minBB = Block_MinBB[block]; Vector3 maxBB = Block_MaxBB[block]; Int32 minX = (Int32)(minBB.X * 16), minZ = (Int32)(minBB.Z * 16); Int32 maxX = (Int32)(maxBB.X * 16), maxZ = (Int32)(maxBB.Z * 16); Int32 minU = min(minX, minZ), maxU = min(maxX, maxZ); Int32 minV = (Int32)(16 - maxBB.Y * 16), maxV = (Int32)(16 - minBB.Y * 16); Int32 maxUsedU = maxU, maxUsedV = maxV; /* This way we can avoid creating particles which outside the bounds and need to be clamped */ if (minU < 12 && maxU > 12) maxUsedU = 12; if (minV < 12 && maxV > 12) maxUsedV = 12; #define GRID_SIZE 4 /* gridOffset gives the centre of the cell on a grid */ #define CELL_CENTRE ((1.0f / GRID_SIZE) * 0.5f) Int32 x, y, z; Real32 maxU2 = baseRec.U1 + maxU * uScale; Real32 maxV2 = baseRec.V1 + maxV * vScale; for (x = 0; x < GRID_SIZE; x++) { for (y = 0; y < GRID_SIZE; y++) { for (z = 0; z < GRID_SIZE; z++) { Real32 cellX = (Real32)x / GRID_SIZE, cellY = (Real32)y / GRID_SIZE, cellZ = (Real32)z / GRID_SIZE; Vector3 cell = Vector3_Create3(CELL_CENTRE + cellX, CELL_CENTRE / 2 + cellY, CELL_CENTRE + cellZ); if (cell.X < minBB.X || cell.X > maxBB.X || cell.Y < minBB.Y || cell.Y > maxBB.Y || cell.Z < minBB.Z || cell.Z > maxBB.Z) continue; Vector3 velocity; /* centre random offset around [-0.2, 0.2] */ velocity.X = CELL_CENTRE + (cellX - 0.5f) + (Random_Float(&rnd) * 0.4f - 0.2f); velocity.Y = CELL_CENTRE + (cellY - 0.0f) + (Random_Float(&rnd) * 0.4f - 0.2f); velocity.Z = CELL_CENTRE + (cellZ - 0.5f) + (Random_Float(&rnd) * 0.4f - 0.2f); TextureRec rec = baseRec; rec.U1 = baseRec.U1 + Random_Range(&rnd, minU, maxUsedU) * uScale; rec.V1 = baseRec.V1 + Random_Range(&rnd, minV, maxUsedV) * vScale; rec.U2 = rec.U1 + 4 * uScale; rec.V2 = rec.V1 + 4 * vScale; rec.U2 = min(rec.U2, maxU2) - 0.01f * uScale; rec.V2 = min(rec.V2, maxV2) - 0.01f * vScale; if (Terrain_Count == PARTICLES_MAX) Terrain_RemoveAt(0); TerrainParticle* p = &Terrain_Particles[Terrain_Count++]; Real32 life = 0.3f + Random_Float(&rnd) * 1.2f; Vector3 pos; Vector3_Add(&pos, &worldPos, &cell); Particle_Reset(&p->Base, pos, velocity, life); p->Rec = rec; p->TexLoc = (TextureLoc)texLoc; p->Block = block; Int32 type = Random_Range(&rnd, 0, 30); p->Base.Size = (UInt8)(type >= 28 ? 12 : (type >= 25 ? 10 : 8)); } } } }
void BOTAI_GetNextNodeFourball() { int random; if( !ReverseNetwork ) { switch( CurrentNode ) { case 0: CurrentNode = 1; break; case 1: CurrentNode = 5; break; case 2: CurrentNode = 1; break; case 3: CurrentNode = 2; break; case 4: CurrentNode = 0; break; case 5: CurrentNode = 10; break; case 6: CurrentNode = 2; break; case 7: CurrentNode = 3; break; case 8: CurrentNode = 4; break; case 9: CurrentNode = 8; break; case 10: CurrentNode = 11; break; case 11: CurrentNode = 12; break; case 12: random = Random_Range(3); if(random == 0) CurrentNode = 20; else if(random == 1) CurrentNode = 13; else CurrentNode = 16; break; case 13: CurrentNode = 14; break; case 14: CurrentNode = 15; break; case 15: CurrentNode = 6; break; case 16: CurrentNode = 17; break; case 17: CurrentNode = 18; break; case 18: CurrentNode = 9; break; case 19: CurrentNode = 18; break; case 20: CurrentNode = 19; break; } } else { switch( CurrentNode ) { case 0: CurrentNode = 4; break; case 1: CurrentNode = 0; break; case 2: CurrentNode = 1; break; case 3: CurrentNode = 2; break; case 4: CurrentNode = 8; break; case 5: CurrentNode = 1; break; case 6: CurrentNode = 2; break; case 7: CurrentNode = 15; break; case 8: random = Random_Range(2); if(random == 0) CurrentNode = 9; else CurrentNode = 7; break; case 9: CurrentNode = 18; break; case 10: CurrentNode = 5; break; case 11: CurrentNode = 10; break; case 12: CurrentNode = 11; break; case 13: CurrentNode = 12; break; case 14: CurrentNode = 13; break; case 15: CurrentNode = 14; break; case 16: CurrentNode = 12; break; case 17: CurrentNode = 16; break; case 18: random = Random_Range(2); if(random == 0) CurrentNode = 19; else CurrentNode = 17; break; case 19: CurrentNode = 20; break; case 20: CurrentNode = 12; break; } } }
/*=================================================================== Procedure : AIR Follow Path Input : ENEMY * Enemy Output : Nothing ===================================================================*/ void AI_AIR_FOLLOWPATH( register ENEMY * Enemy ) { OBJECT * SObject; NODE * TNode; VECTOR TempVector = { 0.0F , 0.0F , 0.0F }; VECTOR TempUpVector; VECTOR TempForwardVector; u_int16_t MineIndex; SObject = &Enemy->Object; AI_THINK( Enemy , false , false ); if( !(Enemy->AIFlags & AI_ANYPLAYERINRANGE) ) return; if( EnemyTypes[Enemy->Type].Behave.Flags & AI_BEHAVIOUR_ATTACK_ONSITE ) { Tinfo->Flags = 0; SET_TARGET_PLAYERS; AI_GetDistToNearestTarget( Enemy ); Enemy->TShip = Tinfo->TObject; } if( Enemy->TShip && ( EnemyTypes[Enemy->Type].Behave.Flags & AI_BEHAVIOUR_ATTACK_ONSITE ) ) { if( Enemy->AIFlags&AI_ICANSEEPLAYER ) { AI_SetDOGFIGHT( Enemy ); return; }else if( Enemy->AIFlags&AI_ICANHEARPLAYER ) { AI_SetMOVETOTARGET( Enemy ); return; } } if( (Enemy->AIFlags&AI_MINEAVOID) && (EnemyTypes[Enemy->Type].Behave.Flags&AI_BEHAVIOUR_ATTACKMINES) ) { Enemy->AvoidTimer = 0.0F; Enemy->AvoidType = 0; AI_SetKILLMINE( Enemy ); return; } if( TNode = (NODE*) Enemy->TNode ) { if( !(Enemy->Object.NodeNetwork&TNode->NetMask) ) { // The node Im Targetting is not on my network...Better try and find one... if( Enemy->Object.LastNearestNode ) { TNode = (NODE*) ( Enemy->TNode = WhichNode( 1 , Enemy->Object.NearestNode , Enemy->Object.LastNearestNode ) ); if( !TNode ) { AI_SetSCAN( Enemy ); return; } if( !AI_ClearLOSNonZero( &Enemy->Object, &TNode->Pos , EnemyTypes[Enemy->Type].Radius ) ) { // couldnt find a node that will take me to my target so go back to my nearest... TNode = (NODE*) ( Enemy->TNode = Enemy->Object.NearestNode ); } } } AI_AimAtTarget( &Enemy->Object.InvMat , &Enemy->Object.Pos, &TNode->Pos ); Enemy->AI_Angle = AimData.Angle; if( !( EnemyTypes[Enemy->Type].Behave.Flags & AI_BEHAVIOUR_ICANTPITCH ) ) { Enemy->AIMoveFlags |= AimData.Flags; if( ( EnemyTypes[Enemy->Type].Behave.Flags & AI_BEHAVIOUR_DONTSTOPANDTURN ) || ( (AimData.Angle.x < 15.0F) && (AimData.Angle.x > -15.0F) && (AimData.Angle.y < 15.0F) && (AimData.Angle.y > -15.0F) ) ) Enemy->AIMoveFlags |= AI_CONTROL_FORWARD; }else{ //This enemy cant look up or down so it has to move up and down to compensate.... if( ( EnemyTypes[Enemy->Type].Behave.Flags & AI_BEHAVIOUR_DONTSTOPANDTURN ) || ( (AimData.Angle.x < 15.0F) && (AimData.Angle.x > -15.0F) && (AimData.Angle.y < 15.0F) && (AimData.Angle.y > -15.0F) && (AimData.Angle.y < 3.0F) && (AimData.Angle.y > -3.0F)) ) Enemy->AIMoveFlags |= AI_CONTROL_FORWARD; Enemy->AIMoveFlags |= ( AimData.Flags & ( AI_CONTROL_TURNLEFT + AI_CONTROL_TURNRIGHT ) ); if( AimData.Flags & AI_CONTROL_TURNUP ) { Enemy->AIMoveFlags |= AI_CONTROL_UP; }else if( AimData.Flags & AI_CONTROL_TURNDOWN ) { Enemy->AIMoveFlags |= AI_CONTROL_DOWN; } } if( DistanceVector2Vector( &Enemy->Object.Pos , &TNode->Pos ) < 64.0F ) { Tinfo->Flags = 0; SET_TARGET_NODES; AI_GetDistToNearestTarget( Enemy ); Enemy->TNode = Tinfo->TObject; } // MINES........ if( (Enemy->AIFlags&AI_MINEAVOID) && (EnemyTypes[Enemy->Type].Behave.Flags&AI_BEHAVIOUR_AVOIDMINES) ) { // A Mine is close....And I can See it... TNode = ChooseAlternateNode( Enemy->Object.NodeNetwork , Enemy->Object.NearestNode , Enemy->TNode ); Enemy->TNode = TNode; Enemy->AIFlags &= ~AI_MINEAVOID; Enemy->AIMoveFlags &= ~AI_CONTROL_FORWARD; } if( !Enemy->AvoidTimer) { TNode = Enemy->Object.NearestNode; if( (TNode->Flags&NODE_DROPMINES) && (EnemyTypes[Enemy->Type].Behave.Flags&AI_BEHAVIOUR_DROPMINES) ) { Enemy->SecondaryFireTimer -= framelag; if( Enemy->SecondaryFireTimer < 0.0F ) Enemy->SecondaryFireTimer = 0.0F; if( (Enemy->SecondaryFireTimer == 0.0F) ) { Enemy->SecondaryFireTimer = EnemyTypes[Enemy->Type].SecondaryFireRate + (float) Random_Range( (u_int16_t) EnemyTypes[Enemy->Type].SecondaryFireRate ); //This is where we Lay Mines.... ApplyMatrix( &Enemy->Object.Mat, &Forward, &TempForwardVector ); ApplyMatrix( &Enemy->Object.Mat, &SlideUp, &TempUpVector ); MineIndex = InitOneSecBull( OWNER_ENEMY, Enemy->Index, ++Enemy->BulletID, Enemy->Object.Group, &Enemy->Object.Pos, &TempVector, &TempForwardVector, &TempUpVector, &TempVector, EnemyTypes[Enemy->Type].SecondaryWeaponType, false ); if( MineIndex != (u_int16_t) -1 ) { SecBulls[MineIndex].LifeSpan = 10.0F * 60.0F; } } } } }else{ // If no target node has been found yet go to the nearest one... Enemy->TNode = Enemy->Object.NearestNode; } }
/*=================================================================== 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; } }