#include "Enemies.h" #include "WorldManager.h" #define EnemySpeed 0.001 static void EnemyCheckCollisions(VEnemy * e, VWorld * w) { VObjectWrapper * objectList; VObject * object; float shields; Boolean wasHit = 0; for (objectList = w->Objects; objectList != NULL; objectList = objectList->next) { if (objectList->Type != VENEMY && objectList->Type != VOBJECT) continue; if (objectList->Type == VENEMY && e == objectList->e) continue; WObjectObject(objectList, &object); if (object != NULL && fabs(((e->Object.Position.x - object->Position.x) * (e->Object.Position.x - object->Position.x)) + ((e->Object.Position.y - object->Position.y) * (e->Object.Position.y - object->Position.y)) + ((e->Object.Position.z - object->Position.z) * (e->Object.Position.z - object->Position.z))) <= (e->Object.Radius + object->Radius)) { wasHit = 1; if (objectList->Type == VENEMY) { shields = objectList->e->Shields; HitEnemy(objectList->e, e->Shields); HitEnemy(e, shields); if (objectList->e->Shields <= 0) { objectList->taggedForRemoval = true; objectList->exploded = 1; } else { objectList->exploded = 2; } } else { shields = objectList->o->Durability; objectList->o->Durability -= e->Shields; e->Shields -= shields; if (objectList->o->Durability <= 0) { objectList->taggedForRemoval = true; objectList->exploded = 1; } else { objectList->exploded = 2; } } break; } } if (fabs(((e->Object.Position.x - w->Camera.Position.x) * (e->Object.Position.x - w->Camera.Position.x)) + ((e->Object.Position.y - w->Camera.Position.y) * (e->Object.Position.y - w->Camera.Position.y)) + ((e->Object.Position.z - w->Camera.Position.z) * (e->Object.Position.z - w->Camera.Position.z))) <= (e->Object.Radius + PLAYERSHIPRADIUS)) { wasHit = 1; shields = w->Player.Shields; DamagePlayer(w, e->Shields); HitEnemy(e, shields); } if (!InBounds(w, e->Object.Position, sqrt(e->Object.Radius))) { wasHit = 1; HitEnemy(e, e->Shields); } if (wasHit) { VObjectWrapper * anotherObject; anotherObject = GetWrapperByEnemy(w->Objects, e); if (anotherObject != NULL) { if (e->Shields <= 0) { anotherObject->exploded = 1; anotherObject->taggedForRemoval = 1; } else { anotherObject->exploded = 2; } } } } /* static void RepelEnemy(VEnemy * e, VWorld * w, Vector * Angle) { VObjectWrapper * objectList; VObject * object; for (objectList = w->Objects; objectList != NULL; objectList = objectList->next) { if (objectList->Type != VENEMY || e == objectList->e) continue; WObjectObject(objectList, &object); } } */ void DumbShooterAI(void * Enemy, AIEvent Event, float interval) { VEnemy * e; VWorld * w; Vector Angle; e = (VEnemy *) Enemy; switch (Event) { case AIEventRun: break; case AIEventCopyData: e->next->Data = e->Data; return; default: return; } w = (VWorld *) e->Data; SubtractVector(&w->Camera.Position, &e->Object.Position, &Angle); NormalizeVector(&Angle); Angle.x *= PYRAMIDROTSPEED / interval; Angle.y *= PYRAMIDROTSPEED / interval; Angle.z *= PYRAMIDROTSPEED / interval; AddVector(&e->Object.Front, &Angle, &e->Object.Front); NormalizeVector(&e->Object.Front); CrossProduct(&e->Object.Front, &e->Object.Right, &e->Object.Up); CrossProduct(&e->Object.Up, &e->Object.Front, &e->Object.Right); NormalizeVector(&e->Object.Right); NormalizeVector(&e->Object.Up); if(fabs( ((e->Object.Position.x - w->Camera.Position.x) * (e->Object.Position.x - w->Camera.Position.x) + (e->Object.Position.y - w->Camera.Position.y) * (e->Object.Position.y - w->Camera.Position.y) + (e->Object.Position.z - w->Camera.Position.z) * (e->Object.Position.z - w->Camera.Position.z)) > PYRAMIDRANGE)) { Angle = e->Object.Front; NormalizeVector(&Angle); Angle.x *= PYRAMIDMOVESPEED / interval; Angle.y *= PYRAMIDMOVESPEED / interval; Angle.z *= PYRAMIDMOVESPEED / interval; AddVector(&e->Object.Position, &Angle, &e->Object.Position); } else { e->Idle += (1.0 / interval); if(e->Idle > PYRAMIDSHOOTSPEED) { VWeapon * theWeapon; VObjectWrapper * theWObject; e->Idle = 0; theWeapon = (VWeapon *) NewPtr(sizeof(VWeapon)); if (theWeapon != NULL) { InitWeapon(theWeapon, WEAPONTYPEBULLET); AllocateWObject(&theWObject); theWObject->w = theWeapon; theWObject->Type = VWEAPON; theWObject->next = w->Objects; if (w->Objects != NULL) w->Objects->previous = theWObject; w->Objects = theWObject; StartWeapon(theWeapon, &e->Object.Position, 0.0, &e->Object.Front, &e->Object.Up); theWeapon->Speed = BULLETSPEED / 2; } } } EnemyCheckCollisions(e, w); } void KamikazeAI(void * Enemy, AIEvent Event, float interval) { VEnemy * e; VWorld * w; Vector Angle, Axis; float acceleration; Quaternion Quat; e = (VEnemy *) Enemy; switch (Event) { case AIEventRun: break; case AIEventCopyData: e->next->Data = e->Data; return; default: return; } w = (VWorld *) e->Data; SubtractVector(&w->Camera.Position, &e->Object.Position, &Angle); NormalizeVector(&Angle); /* The more directly the enemy faces the player, the more they accelerate. */ acceleration = ((e->Object.Front.x * Angle.x) + (e->Object.Front.y * Angle.y) + (e->Object.Front.z * Angle.z)); if (acceleration < 0.0) acceleration = 0.0; /* Cube acceleration */ acceleration *= (acceleration * acceleration); CrossProduct(&e->Object.Front, &Angle, &Axis); QuaternionRotateVector(KAMIKAZEROTSPEED, interval, &Axis, &e->Object.Front, &Quat); e->Object.Front.x = Quat.v.x; e->Object.Front.y = Quat.v.y; e->Object.Front.z = Quat.v.z; NormalizeVector(&e->Object.Front); CrossProduct(&e->Object.Front, &e->Object.Right, &e->Object.Up); CrossProduct(&e->Object.Up, &e->Object.Front, &e->Object.Right); NormalizeVector(&e->Object.Right); NormalizeVector(&e->Object.Up); if (fabs( ((e->Object.Position.x - w->Camera.Position.x) * (e->Object.Position.x - w->Camera.Position.x) + (e->Object.Position.y - w->Camera.Position.y) * (e->Object.Position.y - w->Camera.Position.y) + (e->Object.Position.z - w->Camera.Position.z) * (e->Object.Position.z - w->Camera.Position.z)) < KAMIKAZERANGE)) { Angle = e->Object.Front; NormalizeVector(&Angle); Angle.x *= ((SHIPACCELERATION / interval) * acceleration); Angle.y *= ((SHIPACCELERATION / interval) * acceleration); Angle.z *= ((SHIPACCELERATION / interval) * acceleration); PushShip(&e->Physics, &Angle); } EnemyCheckCollisions(e, w); } void SmartShooterAI(void * Enemy, AIEvent Event, float interval) { VEnemy * e; VWorld * w; SmartShooterData * data; Vector Angle, Axis; float acceleration; Quaternion Quat; switch (Event) { case AIEventInit: e = (VEnemy *) Enemy; w = (VWorld *) e->Data; /* Allocate a SmartShooterData and link it into Enemy->Data */ data = (SmartShooterData *) NewPtr(sizeof(SmartShooterData)); data->Data = e->Data; e->Data = data; /* Initialize the SmartShooterData */ data->State = SMARTSHOOTERSTATEFLY; data->Idle2 = 0.0; SubtractVector(&w->Camera.Position, &e->Object.Position, &Angle); NormalizeVector(&Angle); CrossProduct(&w->Camera.Up, &Angle, &data->Angle); if (Random() % 2) { /* Turn data->Angle from a right vector into an up vector */ NormalizeVector(&data->Angle); CrossProduct(&Angle, &data->Angle, &data->Angle); } if (Random() % 2) { data->Angle.x *= -1; data->Angle.y *= -1; data->Angle.z *= -1; } NormalizeVector(&data->Angle); break; case AIEventCleanUp: /* Dispose and unlink SmartShooterData */ e = (VEnemy *) Enemy; data = (SmartShooterData *) e->Data; DisposePtr(e->Data); e->Data = w; break; case AIEventCopyData: e = (VEnemy *) Enemy; /* Copy SmartShooterData to next->Data */ data = (SmartShooterData *) NewPtr(sizeof(SmartShooterData)); BlockMove(e->Data, data, sizeof(SmartShooterData)); e->next->Data = data; break; case AIEventRun: e = (VEnemy *) Enemy; data = (SmartShooterData *) e->Data; w = (VWorld *) data->Data; switch (data->State) { case SMARTSHOOTERSTATEFLY: data->Idle2 += (1.0 / interval); acceleration = ((e->Object.Front.x * data->Angle.x) + (e->Object.Front.y * data->Angle.y) + (e->Object.Front.z * data->Angle.z)); if (acceleration < 0.0) acceleration = 0.0; /* Cube acceleration */ acceleration *= (acceleration * acceleration); Angle = data->Angle; Angle.x *= (SMARTSHOOTERROTSPEED / interval); Angle.y *= (SMARTSHOOTERROTSPEED / interval); Angle.z *= (SMARTSHOOTERROTSPEED / interval); AddVector(&e->Object.Front, &Angle, &e->Object.Front); NormalizeVector(&e->Object.Front); CrossProduct(&e->Object.Front, &e->Object.Right, &e->Object.Up); CrossProduct(&e->Object.Up, &e->Object.Front, &e->Object.Right); NormalizeVector(&e->Object.Right); NormalizeVector(&e->Object.Up); Angle = e->Object.Front; Angle.x *= ((SHIPACCELERATION / interval) * acceleration); Angle.y *= ((SHIPACCELERATION / interval) * acceleration); Angle.z *= ((SHIPACCELERATION / interval) * acceleration); PushShip(&e->Physics, &Angle); if (data->Idle2 > SMARTSHOOTERFLYTIME) { data->State = SMARTSHOOTERSTATESHOOT; data->Idle2 = 0.0; e->Idle = 0; } break; case SMARTSHOOTERSTATESHOOT: SubtractVector(&w->Camera.Position, &e->Object.Position, &Angle); NormalizeVector(&Angle); CrossProduct(&e->Object.Front, &Angle, &Axis); QuaternionRotateVector(KAMIKAZEROTSPEED, interval, &Axis, &e->Object.Front, &Quat); e->Object.Front.x = Quat.v.x; e->Object.Front.y = Quat.v.y; e->Object.Front.z = Quat.v.z; NormalizeVector(&e->Object.Front); CrossProduct(&e->Object.Front, &e->Object.Right, &e->Object.Up); CrossProduct(&e->Object.Up, &e->Object.Front, &e->Object.Right); NormalizeVector(&e->Object.Right); NormalizeVector(&e->Object.Up); StopShip(&e->Physics, ((SHIPACCELERATION / 2) / interval)); if (fabs( ((e->Object.Position.x - w->Camera.Position.x) * (e->Object.Position.x - w->Camera.Position.x) + (e->Object.Position.y - w->Camera.Position.y) * (e->Object.Position.y - w->Camera.Position.y) + (e->Object.Position.z - w->Camera.Position.z) * (e->Object.Position.z - w->Camera.Position.z)) < SMARTSHOOTERRANGE)) { e->Idle += (1.0 / interval); data->Idle2 += (1.0 / interval); } if (e->Idle > SMARTSHOOTERSHOOTSPEED) { VWeapon * theWeapon; VObjectWrapper * theWObject; e->Idle = 0; theWeapon = (VWeapon *) NewPtr(sizeof(VWeapon)); if (theWeapon != NULL) { InitWeapon(theWeapon, WEAPONTYPEBULLET); AllocateWObject(&theWObject); theWObject->w = theWeapon; theWObject->Type = VWEAPON; theWObject->next = w->Objects; if (w->Objects != NULL) w->Objects->previous = theWObject; w->Objects = theWObject; StartWeapon(theWeapon, &e->Object.Position, 0.0, &e->Object.Front, &e->Object.Up); theWeapon->Speed = BULLETSPEED / 2; } } if (data->Idle2 > SMARTSHOOTERSHOOTTIME) { data->State = SMARTSHOOTERSTATEFLY; data->Idle2 = 0.0; SubtractVector(&w->Camera.Position, &e->Object.Position, &Angle); NormalizeVector(&Angle); CrossProduct(&w->Camera.Up, &Angle, &data->Angle); if (Random() % 2) { /* Turn data->Angle from a right vector into an up vector */ NormalizeVector(&data->Angle); CrossProduct(&Angle, &data->Angle, &data->Angle); } if (Random() % 2) { data->Angle.x *= -1; data->Angle.y *= -1; data->Angle.z *= -1; } NormalizeVector(&data->Angle); } break; } EnemyCheckCollisions(e, w); break; } } void StationaryAI(void * Enemy, AIEvent Event, float interval) { VEnemy * e; VWorld * w; Vector Angle; e = (VEnemy *) Enemy; switch (Event) { case AIEventRun: break; case AIEventCopyData: e->next->Data = e->Data; return; default: return; } w = (VWorld *) e->Data; SubtractVector(&w->Camera.Position, &e->Object.Position, &Angle); NormalizeVector(&Angle); Angle.x *= STATIONARYSHOOTERROTSPEED / interval; Angle.y *= STATIONARYSHOOTERROTSPEED / interval; Angle.z *= STATIONARYSHOOTERROTSPEED / interval; AddVector(&e->Object.Front, &Angle, &e->Object.Front); NormalizeVector(&e->Object.Front); CrossProduct(&e->Object.Front, &e->Object.Right, &e->Object.Up); CrossProduct(&e->Object.Up, &e->Object.Front, &e->Object.Right); NormalizeVector(&e->Object.Right); NormalizeVector(&e->Object.Up); if(fabs( ((e->Object.Position.x - w->Camera.Position.x) * (e->Object.Position.x - w->Camera.Position.x) + (e->Object.Position.y - w->Camera.Position.y) * (e->Object.Position.y - w->Camera.Position.y) + (e->Object.Position.z - w->Camera.Position.z) * (e->Object.Position.z - w->Camera.Position.z)) > STATIONARYSHOOTERRANGE)) { e->Idle += (1.0 / interval); if(e->Idle > STATIONARYSHOOTERSHOOTSPEED) { VWeapon * theWeapon; VObjectWrapper * theWObject; e->Idle = 0; theWeapon = (VWeapon *) NewPtr(sizeof(VWeapon)); if (theWeapon != NULL) { InitWeapon(theWeapon, WEAPONTYPEBULLET); AllocateWObject(&theWObject); theWObject->w = theWeapon; theWObject->Type = VWEAPON; theWObject->next = w->Objects; if (w->Objects != NULL) w->Objects->previous = theWObject; w->Objects = theWObject; StartWeapon(theWeapon, &e->Object.Position, 0.0, &e->Object.Front, &e->Object.Up); theWeapon->Speed = BULLETSPEED / 2; } } } EnemyCheckCollisions(e, w); } void HitEnemy(VEnemy * e, int Damage) { e->Shields -= Damage; } void InitEnemy(VEnemy * e, void * Data) { e->Callback = DumbShooterAI; e->Data = Data; e->Idle = 0.0; e->next = NULL; e->taggedForRemoval = false; InitObject(&e->Object); BuildEnemyModel(&e->Object.Model); CalculateRadius(&e->Object); e->Object.Front.z = -1.0; e->Object.Position.z = -10.0; e->Object.Position.x = -2.0; e->Object.Position.y = -2.0; e->Shields = 5; InitPhysics(&e->Physics); e->Callback(e, AIEventInit, 1.0); } void SetEnemyAI(VEnemy * e, EnemyAIModule aiMod) { e->Callback(e, AIEventCleanUp, 1.0); e->Callback = aiMod; e->Callback(e, AIEventInit, 1.0); } void CleanUpEnemy(VEnemy * e) { e->Callback(e, AIEventCleanUp, 1.0); CleanUpObject(&e->Object); } void DrawEnemy(VEnemy * e) { DrawObject(&e->Object); } void RunEnemy(VEnemy * e, float interval) { e->Callback(e, AIEventRun, interval); RunPhysics(&e->Physics, &e->Object.Position, &e->Object.Front, &e->Object.Up, &e->Object.Right, interval); } void CopyEnemy(VEnemy * src, VEnemy * dest) { VEnemy * oldNext; oldNext = src->next; src->next = dest; src->Callback(src, AIEventCopyData, 1.0); src->next = oldNext; dest->Callback = src->Callback; CopyObject(&src->Object, &dest->Object); dest->Idle = src->Idle; dest->next = NULL; /* Setting up this list is up to the caller */ dest->taggedForRemoval = src->taggedForRemoval; dest->Physics = src->Physics; dest->Shields = src->Shields; }