/*============================================================================= Name : MissileDestroyer.c Purpose : Specifics for the MissileDestroyer Created 6/30/1997 by gshaw Copyright Relic Entertainment, Inc. All rights reserved. =============================================================================*/ #include "types.h" #include "debug.h" #include "spaceobj.h" #include "MissileDestroyer.h" #include "statscript.h" #include "gun.h" #include "attack.h" #include "DefaultShip.h" #include "universe.h" #include "soundevent.h" #include "battle.h" #include "aiship.h" #include "aitrack.h" #include "collision.h" typedef struct { real32 missiledestroyerGunRange[NUM_TACTICS_TYPES]; real32 missiledestroyerTooCloseRange[NUM_TACTICS_TYPES]; real32 missileRegenerateTime; real32 missileVolleyTime; real32 missileLagVolleyTime; } MissileDestroyerStatics; MissileDestroyerStatics MissileDestroyerStaticRace1; MissileDestroyerStatics MissileDestroyerStaticRace2; scriptStructEntry MissileDestroyerScriptTable[] = { { "MissileRegenerateTime",scriptSetReal32CB,(udword) &(MissileDestroyerStaticRace1.missileRegenerateTime),(udword) &(MissileDestroyerStaticRace1) }, { "MissileVolleyTime",scriptSetReal32CB,(udword) &(MissileDestroyerStaticRace1.missileVolleyTime),(udword) &(MissileDestroyerStaticRace1) }, { "MissileLagVolleyTime",scriptSetReal32CB,(udword) &(MissileDestroyerStaticRace1.missileLagVolleyTime),(udword) &(MissileDestroyerStaticRace1) }, { NULL,NULL,0,0 } }; void MissileDestroyerStaticInit(char *directory,char *filename,struct ShipStaticInfo *statinfo) { udword i; MissileDestroyerStatics *mdestroyerstat = (statinfo->shiprace == R1) ? &MissileDestroyerStaticRace1 : &MissileDestroyerStaticRace2; statinfo->custstatinfo = mdestroyerstat; scriptSetStruct(directory,filename,MissileDestroyerScriptTable,(ubyte *)mdestroyerstat); for(i=0;imissiledestroyerGunRange[i] = statinfo->bulletRange[i]; mdestroyerstat->missiledestroyerTooCloseRange[i] = statinfo->minBulletRange[i] * 0.9f; } } void MissileDestroyerInit(Ship *ship) { MissileDestroyerSpec *spec = (MissileDestroyerSpec *)ship->ShipSpecifics; spec->lasttimeRegeneratedMissiles = 0.0f; spec->lasttimeFiredVolley = 0.0f; spec->lasttimeDidSpecialTargeting = -100000.0f; spec->curTargetIndex = 0; spec->volleyState = VOLLEY_BEGIN; } void MissileDestroyerAttack(Ship *ship,SpaceObjRotImpTarg *target,real32 maxdist) { ShipStaticInfo *shipstaticinfo = (ShipStaticInfo *)ship->staticinfo; MissileDestroyerStatics *mdestroyerstat = (MissileDestroyerStatics *)shipstaticinfo->custstatinfo; attackStraightForward(ship,target,mdestroyerstat->missiledestroyerGunRange[ship->tacticstype],mdestroyerstat->missiledestroyerTooCloseRange[ship->tacticstype]); } void MissileDestroyerAttackPassive(Ship *ship,Ship *target,bool rotate) { if ((rotate) & ((bool)((ShipStaticInfo *)(ship->staticinfo))->rotateToRetaliate)) { attackPassiveRotate(ship,target); } else { attackPassive(ship,target); } } void MissileDestroyerHousekeep(Ship *ship) { ShipStaticInfo *shipstaticinfo = (ShipStaticInfo *)ship->staticinfo; MissileDestroyerSpec *spec = (MissileDestroyerSpec *)ship->ShipSpecifics; MissileDestroyerStatics *mdestroyerstat = (MissileDestroyerStatics *)shipstaticinfo->custstatinfo; sdword numGuns; sdword i; Gun *gun; GunStatic *gunstatic; sdword minMissiles; sdword minIndex; if ((universe.totaltimeelapsed - spec->lasttimeDidSpecialTargeting) > mdestroyerstat->missileLagVolleyTime) { if ((universe.totaltimeelapsed - spec->lasttimeRegeneratedMissiles) > mdestroyerstat->missileRegenerateTime) { spec->lasttimeRegeneratedMissiles = universe.totaltimeelapsed; numGuns = ship->gunInfo->numGuns; // find missile launcher with least missiles, and give it a missile for (i=0,minIndex=-1,minMissiles=100000;igunStaticInfo->gunstatics[i]; if (gunstatic->guntype == GUN_MissileLauncher) { gun = &ship->gunInfo->guns[i]; if (gun->numMissiles < gunstatic->maxMissiles) { if (gun->numMissiles < minMissiles) { minMissiles = gun->numMissiles; minIndex = i; } } } } if (minIndex != -1) { ship->gunInfo->guns[minIndex].numMissiles++; } } } } bool MissileDestroyerSpecialTarget(Ship *ship,void *custom) { SelectAnyCommand *targets; sdword i; sdword numShipsToTarget; sdword numGuns; Gun *gun; real32 range; GunStatic *gunstatic; SpaceObjRotImpTarg *target; ShipStaticInfo *shipstaticinfo; MissileDestroyerSpec *spec; MissileDestroyerStatics *mdestroyerstat; bool firedSomeMissiles,triedToFire; vector trajectory; spec = (MissileDestroyerSpec *)ship->ShipSpecifics; shipstaticinfo = (ShipStaticInfo *)ship->staticinfo; mdestroyerstat = (MissileDestroyerStatics *)shipstaticinfo->custstatinfo; spec->lasttimeDidSpecialTargeting = universe.totaltimeelapsed; if ((universe.totaltimeelapsed - spec->lasttimeFiredVolley) > mdestroyerstat->missileVolleyTime) { spec->lasttimeFiredVolley = universe.totaltimeelapsed; } else { return FALSE; } targets = (SelectAnyCommand *)custom; numShipsToTarget = targets->numTargets; if (numShipsToTarget == 0) { // have destroyed targets, so we are done spec->curTargetIndex = 0; spec->volleyState = VOLLEY_BEGIN; return TRUE; } if(spec->volleyState == VOLLEY_BEGIN) { spec->volleyState = VOLLEY_NOT_BEGIN; ////////////////////// //Volley attack speech event! //event num: COMM_MissleDest_VolleyAttack //use battle chatter if(ship->playerowner->playerIndex == universe.curPlayerIndex) { if (battleCanChatterAtThisTime(BCE_COMM_MissleDest_VolleyAttack, ship)) { battleChatterAttempt(SOUND_EVENT_DEFAULT, BCE_COMM_MissleDest_VolleyAttack, ship, SOUND_EVENT_DEFAULT); } } ////////////////////// } if (spec->curTargetIndex >= numShipsToTarget) { spec->curTargetIndex = 0; } numGuns = ship->gunInfo->numGuns; firedSomeMissiles = FALSE; triedToFire=FALSE; for (i=0;igunStaticInfo->gunstatics[i]; if (gunstatic->guntype == GUN_MissileLauncher) { vecSub(trajectory,ship->collInfo.collPosition,targets->TargetPtr[spec->curTargetIndex]->collInfo.collPosition); range = RangeToTarget(ship,targets->TargetPtr[spec->curTargetIndex],&trajectory); gun = &ship->gunInfo->guns[i]; if(range < (gun->gunstatic->bulletrange*0.9f)) { triedToFire=TRUE; if (gunHasMissiles(gun)) { firedSomeMissiles = TRUE; target = targets->TargetPtr[spec->curTargetIndex]; missileShoot(ship,gun,target); spec->curTargetIndex++; if (spec->curTargetIndex >= numShipsToTarget) { spec->curTargetIndex = 0; } } } } } if (firedSomeMissiles) { aitrackZeroVelocity(ship); return FALSE; } else if(triedToFire) { // all empty of missiles, so we are done spec->curTargetIndex = 0; spec->volleyState = VOLLEY_BEGIN; return TRUE; } else { //didn't try to fire...so lets fly towards a target...lets pick...0 aishipFlyToShipAvoidingObjs(ship,(Ship *)targets->TargetPtr[0],AISHIP_FastAsPossible|AISHIP_PointInDirectionFlying,0.0f); } } CustShipHeader MissileDestroyerHeader = { MissileDestroyer, sizeof(MissileDestroyerSpec), MissileDestroyerStaticInit, NULL, MissileDestroyerInit, NULL, MissileDestroyerAttack, DefaultShipFire, MissileDestroyerAttackPassive, NULL, MissileDestroyerSpecialTarget, MissileDestroyerHousekeep, NULL, NULL, NULL, NULL, NULL, NULL };