@@ -270,6 +270,16 @@ void MissileAIUpdate::projectileLaunchAtObjectOrPosition(
270270
271271#define APPROACH_HEIGHT 10 .0f
272272
273+ static Real getTorpedoTargetHeight (const Coord3D & pos, Locomotor* loco) {
274+ Real waterZ{ 0 };
275+ Real ret = pos.z ;
276+ bool underwater = TheTerrainLogic && TheTerrainLogic->isUnderwater (pos.x , pos.y , &waterZ);
277+ if (underwater && loco) {
278+ ret= waterZ + loco->getPreferredHeight ();
279+ }
280+ return ret;
281+ }
282+
273283// -------------------------------------------------------------------------------------------------
274284// The actual firing of the missile once setup.
275285// -------------------------------------------------------------------------------------------------
@@ -351,12 +361,23 @@ void MissileAIUpdate::projectileFireAtObjectOrPosition( const Object *victim, co
351361 // instead of Attacking the target.
352362 if (victim && d->m_tryToFollowTarget && (!TheGlobalData->m_dynamicTargeting || !victim->isKindOf (KINDOF_STRUCTURE)))
353363 {
354- getStateMachine ()->setGoalPosition (victim->getPosition ());
364+ Coord3D targetPos = *victim->getPosition ();
365+ if (d->m_isTorpedo ) {
366+ Locomotor* loco = getCurLocomotor ();
367+ if (loco) {
368+ targetPos.z = getTorpedoTargetHeight (targetPos, loco);
369+ }
370+ }
371+ getStateMachine ()->setGoalPosition (&targetPos);
355372 // ick. const-cast is evil. fix. (srj)
356- // aiMoveToObject(const_cast<Object*>(victim), CMD_FROM_AI );
357- // / IamInnocent - Edited... I'm gonna get fired but I'm unemployed.
358- aiMoveToObject (TheGameLogic->findObjectByID ( victim->getID () ), CMD_FROM_AI );
359- m_originalTargetPos = *victim->getPosition ();
373+ if (!d->m_isTorpedo ) {
374+ // / IamInnocent - Edited... I'm gonna get fired but I'm unemployed.
375+ aiMoveToObject (TheGameLogic->findObjectByID ( victim->getID () ), CMD_FROM_AI );
376+ }
377+ else {
378+ aiMoveToPosition (&targetPos, CMD_FROM_AI);
379+ }
380+ m_originalTargetPos = targetPos;
360381 m_isTrackingTarget = TRUE ;// Remember that I was originally shot at a moving object, so if the
361382 // target dies I can do something cool.
362383 m_victimID = victim->getID ();
@@ -639,10 +660,24 @@ void MissileAIUpdate::doAttackState(Bool turnOK, Bool randomPath)
639660 {
640661 DEBUG_LOG ((" >>> MissileAI - EndRandomPath: victim is not null.\n " ));
641662
642- getStateMachine ()->setGoalPosition (victim->getPosition ());
663+ Coord3D targetPos = *victim->getPosition ();
664+ if (d->m_isTorpedo ) {
665+ Locomotor* curLoco = getCurLocomotor ();
666+ if (curLoco)
667+ {
668+ targetPos.z = getTorpedoTargetHeight (targetPos, curLoco);
669+ }
670+ }
671+
672+ getStateMachine ()->setGoalPosition (&targetPos);
643673 getStateMachine ()->setGoalObject (victim);
644- aiMoveToObject (victim, CMD_FROM_AI);
645- m_originalTargetPos = *victim->getPosition ();
674+ if (!d->m_isTorpedo ) {
675+ aiMoveToObject (victim, CMD_FROM_AI);
676+ }
677+ else {
678+ aiMoveToPosition (&targetPos, CMD_FROM_AI);
679+ }
680+ m_originalTargetPos = targetPos;
646681 m_isTrackingTarget = TRUE ;// Remember that I was originally shot at a moving object, so if the
647682 // target dies I can do something cool.
648683 m_victimID = victim->getID ();
@@ -704,7 +739,7 @@ void MissileAIUpdate::doAttackState(Bool turnOK, Bool randomPath)
704739 }
705740 }
706741
707- if (curLoco && (curLoco->getPreferredHeight () > 0 || curLoco->getPreferredHeight () < 0 ) )
742+ if (curLoco && (curLoco->getPreferredHeight () > 0 )) // || curLoco->getPreferredHeight() < 0) )
708743 {
709744 // Am I close enough to the target to ignore my preferred height setting?
710745 Real distanceToTargetSquared = ThePartitionManager->getDistanceSquared ( getObject (), getGoalPosition (), FROM_CENTER_2D );
@@ -766,10 +801,19 @@ void MissileAIUpdate::doAttackState(Bool turnOK, Bool randomPath)
766801
767802 targetPos.add (&offset);
768803
769- // Make sure Z is above ground
770- PathfindLayerEnum layer = TheTerrainLogic->getHighestLayerForDestination (&targetPos);
771- Real minHeight = TheTerrainLogic->getLayerHeight (targetPos.x , targetPos.y , layer) + APPROACH_HEIGHT;
772- targetPos.z = __max (targetPos.z , minHeight);
804+ if (!d->m_isTorpedo ) {
805+ // Make sure Z is above ground
806+ PathfindLayerEnum layer = TheTerrainLogic->getHighestLayerForDestination (&targetPos);
807+ Real minHeight = TheTerrainLogic->getLayerHeight (targetPos.x , targetPos.y , layer) + APPROACH_HEIGHT;
808+ targetPos.z = __max (targetPos.z , minHeight);
809+ }
810+ else {
811+ Locomotor* curLoco = getCurLocomotor ();
812+ if (curLoco)
813+ {
814+ targetPos.z = getTorpedoTargetHeight (targetPos, curLoco);
815+ }
816+ }
773817
774818 getStateMachine ()->setGoalPosition (&targetPos);
775819 getStateMachine ()->setGoalObject (NULL );
@@ -797,20 +841,24 @@ void MissileAIUpdate::doAttackState(Bool turnOK, Bool randomPath)
797841
798842 // If I was fired at a flyer and have lost target (most likely they died), then I need to do something better
799843 // than cloverleaf around their last spot.
800- if ( m_isTrackingTarget && (getGoalObject () == NULL ) )
844+ if ( m_isTrackingTarget && (getGoalObject () == NULL ) && !d->m_isTorpedo )
845+ airborneTargetGone ();
846+
847+ if (m_isTrackingTarget && (getGoalPosition () == NULL ) && d->m_isTorpedo )
801848 airborneTargetGone ();
802849}
803850
804851// -------------------------------------------------------------------------------------------------
805852void MissileAIUpdate::doKillState (void )
806853{
807- if ( m_fuelExpirationDate && getMissileAIUpdateModuleData ()->m_fuelLifetime && m_fuelExpirationDate >= TheGameLogic->getFrame () && ( !m_nextWakeUpTime || m_nextWakeUpTime > m_fuelExpirationDate ))
854+ const MissileAIUpdateModuleData* d = getMissileAIUpdateModuleData ();
855+ if ( m_fuelExpirationDate && d->m_fuelLifetime && m_fuelExpirationDate >= TheGameLogic->getFrame () && ( !m_nextWakeUpTime || m_nextWakeUpTime > m_fuelExpirationDate ))
808856 {
809857 m_nextWakeUpTime = m_fuelExpirationDate;
810858 }
811859 if (TheGameLogic->getFrame () >= m_fuelExpirationDate)
812860 {
813- if ( getMissileAIUpdateModuleData () ->m_detonateOnNoFuel )
861+ if ( d ->m_detonateOnNoFuel )
814862 {
815863 detonate ();
816864 return ;
@@ -840,29 +888,53 @@ void MissileAIUpdate::doKillState(void)
840888 }
841889 if (isIdle ()) {
842890 // we finished the move
843- if (getGoalObject ()!= NULL ) {
891+ if (getGoalObject () != NULL ) {
844892 Locomotor* curLoco = getCurLocomotor ();
845893 Real closeEnough = 1 .0f ;
846894 if (curLoco)
847895 {
848896 closeEnough = curLoco->getMaxSpeedForCondition (BODY_PRISTINE);
849897 }
850- Real distanceToTargetSq = ThePartitionManager->getDistanceSquared ( getObject (), getGoalObject (), FROM_BOUNDINGSPHERE_3D);
898+ Real distanceToTargetSq = ThePartitionManager->getDistanceSquared (getObject (), getGoalObject (), FROM_BOUNDINGSPHERE_3D);
851899 // DEBUG_LOG((">>> MissileAI KILL (Idle) - Distance to target %f, closeEnough %f\n", sqrt(distanceToTargetSq), closeEnough));
852- if (distanceToTargetSq < closeEnough* closeEnough) {
900+ if (distanceToTargetSq < closeEnough * closeEnough) {
853901 Coord3D pos = *getGoalObject ()->getPosition ();
854902 getObject ()->setPosition (&pos);
855903 detonate ();
856- } else {
857- aiMoveToObject (getGoalObject (), CMD_FROM_AI );
858904 }
859- } else {
905+ else {
906+ aiMoveToObject (getGoalObject (), CMD_FROM_AI);
907+ }
908+ }
909+ else if (d->m_isTorpedo && getGoalPosition () != nullptr )
910+ {
911+ Locomotor* curLoco = getCurLocomotor ();
912+ Real closeEnough = 1 .0f ;
913+ if (curLoco)
914+ {
915+ closeEnough = curLoco->getMaxSpeedForCondition (BODY_PRISTINE);
916+ }
917+ Real distanceToTargetSq = ThePartitionManager->getDistanceSquared (getObject (), getGoalPosition (), FROM_BOUNDINGSPHERE_2D);
918+ if (distanceToTargetSq < closeEnough * closeEnough) {
919+ Coord3D pos = *getGoalPosition ();
920+ pos.z = getTorpedoTargetHeight (pos, curLoco);
921+ getObject ()->setPosition (&pos);
922+ detonate ();
923+ }
924+ else {
925+ aiMoveToObject (getGoalObject (), CMD_FROM_AI);
926+ }
927+ }
928+ else {
860929 detonate ();
861930 }
862931 }
863932 // If I was fired at a flyer and have lost target (most likely they died), then I need to do something better
864933 // than cloverleaf around their last spot.
865- if ( m_isTrackingTarget && (getGoalObject () == NULL ) )
934+ if ( m_isTrackingTarget && (getGoalObject () == NULL ) && !d->m_isTorpedo )
935+ airborneTargetGone ();
936+
937+ if (m_isTrackingTarget && (getGoalPosition () == nullptr ) && d->m_isTorpedo )
866938 airborneTargetGone ();
867939}
868940
@@ -895,6 +967,8 @@ UpdateSleepTime MissileAIUpdate::update()
895967 m_nextWakeUpTime = 1 ;
896968 }
897969
970+ const MissileAIUpdateModuleData* d = getMissileAIUpdateModuleData ();
971+
898972 // If this missile has been marked to divert to countermeasures, check when
899973 // that will occur, then do it when the timer expires.
900974 if ( m_framesTillDecoyed && m_framesTillDecoyed <= TheGameLogic->getFrame () )
@@ -964,9 +1038,22 @@ UpdateSleepTime MissileAIUpdate::update()
9641038 if ( targetID != INVALID_ID )
9651039 {
9661040 victim = TheGameLogic->findObjectByID ( targetID );
967- getStateMachine ()->setGoalPosition (victim->getPosition ());
968- aiMoveToObject (victim, CMD_FROM_AI );
969- m_originalTargetPos = *victim->getPosition ();
1041+ Coord3D targetPos = *victim->getPosition ();
1042+ if (d->m_isTorpedo ) {
1043+ Locomotor* curLoco = getCurLocomotor ();
1044+ if (curLoco)
1045+ {
1046+ targetPos.z = getTorpedoTargetHeight (targetPos, curLoco);
1047+ }
1048+ }
1049+ getStateMachine ()->setGoalPosition (&targetPos);
1050+ if (!d->m_isTorpedo ) {
1051+ aiMoveToObject (victim, CMD_FROM_AI );
1052+ }
1053+ else {
1054+ aiMoveToPosition (&targetPos, CMD_FROM_AI);
1055+ }
1056+ m_originalTargetPos = targetPos;
9701057 m_isTrackingTarget = TRUE ;// Remember that I was originally shot at a moving object, so if the
9711058 // target dies I can do something cool.
9721059 m_victimID = victim->getID ();
@@ -984,11 +1071,28 @@ UpdateSleepTime MissileAIUpdate::update()
9841071 }
9851072
9861073 // If treated as torpedo, explode when not over water
987- const MissileAIUpdateModuleData* d = getMissileAIUpdateModuleData ();
9881074 if (d->m_isTorpedo && !getObject ()->isOverWater ()) {
9891075 detonate ();
9901076 }
9911077
1078+ // If torpedo, update target location
1079+ if (d->m_isTorpedo && m_isTrackingTarget) {
1080+ Object * targetUnit = TheGameLogic->findObjectByID (m_victimID);
1081+ if (targetUnit != nullptr && !targetUnit->isEffectivelyDead ()) {
1082+ Coord3D targetPos = *targetUnit->getPosition ();
1083+ Locomotor* curLoco = getCurLocomotor ();
1084+ if (curLoco)
1085+ {
1086+ targetPos.z = getTorpedoTargetHeight (targetPos, curLoco);
1087+ }
1088+ getStateMachine ()->setGoalPosition (&targetPos);
1089+ getStateMachine ()->setGoalObject (targetUnit);
1090+ aiMoveToPosition (&targetPos, CMD_FROM_AI);
1091+ m_originalTargetPos = targetPos;
1092+ m_isTrackingTarget = true ;
1093+ }
1094+ }
1095+
9921096 switch ( m_state )
9931097 {
9941098 case PRELAUNCH:
0 commit comments