Hooks
static bool BeforeCvTacticalAI_PlotNavalEscortOperationMoves(CvTacticalAI* self, CvAINavalEscortedOperation* pOperation);static void AfterCvTacticalAI_PlotNavalEscortOperationMoves(CvTacticalAI* self, CvAINavalEscortedOperation* pOperation);static void ReplaceCvTacticalAI_PlotNavalEscortOperationMoves(void (*original)(CvTacticalAI*, CvAINavalEscortedOperation*), CvTacticalAI* self, CvAINavalEscortedOperation* pOperation);CivRegisterBefore<CivHooks::CvTacticalAI_PlotNavalEscortOperationMoves>(host, &BeforeCvTacticalAI_PlotNavalEscortOperationMoves);CivRegisterAfter<CivHooks::CvTacticalAI_PlotNavalEscortOperationMoves>(host, &AfterCvTacticalAI_PlotNavalEscortOperationMoves);CivRegisterReplace<CivHooks::CvTacticalAI_PlotNavalEscortOperationMoves>(host, &ReplaceCvTacticalAI_PlotNavalEscortOperationMoves);
Code
4510/// Move a large army to its destination against an enemy target4511void CvTacticalAI::PlotNavalEscortOperationMoves(CvAINavalEscortedOperation* pOperation)4512{4513 CvUnit* pCivilian = 0, *pEscort = 0;4514 int iUnitID;45154516 // Simplification - assume only 1 army per operation now4517 if(pOperation->GetFirstArmyID() == -1)4518 {4519 return;4520 }4521 CvArmyAI* pThisArmy = m_pPlayer->getArmyAI(pOperation->GetFirstArmyID());4522 iUnitID = pThisArmy->GetFirstUnitID();4523 if(iUnitID != -1)4524 {4525 pCivilian = m_pPlayer->getUnit(iUnitID);45264527 iUnitID = pThisArmy->GetNextUnitID();4528 if(iUnitID != -1)4529 {4530 pEscort = m_pPlayer->getUnit(iUnitID);4531 if(pEscort->getDomainType() != DOMAIN_LAND)4532 {4533 pEscort = NULL; // Second unit wasn't the escort4534 }4535 }4536 }45374538 if(!pCivilian && pOperation->IsCivilianRequired())4539 {4540 return;4541 }45424543 m_OperationUnits.clear();4544 pThisArmy->UpdateCheckpointTurns();45454546 // RECRUITING4547 if(pThisArmy->GetArmyAIState() == ARMYAISTATE_WAITING_FOR_UNITS_TO_REINFORCE)4548 {4549 // If no estimate for when recruiting will end, let the rest of the AI use these units4550 if(pThisArmy->GetTurnAtNextCheckpoint() == ARMYSLOT_UNKNOWN_TURN_AT_CHECKPOINT)4551 {4552 return;4553 }4554 else4555 {4556 for(int iI = 0; iI < pThisArmy->GetNumFormationEntries(); iI++)4557 {4558 CvArmyFormationSlot* pSlot = pThisArmy->GetFormationSlot(iI);4559 if(pSlot->GetUnitID() != NO_UNIT)4560 {4561 // See if we are just able to get to muster point in time. If so, time for us to head over there4562 UnitHandle pUnit = m_pPlayer->getUnit(pSlot->GetUnitID());4563 if(pUnit && !pUnit->TurnProcessed())4564 {4565 CvMultiUnitFormationInfo* pkUnitFormationInfo = GC.getMultiUnitFormationInfo(pThisArmy->GetFormationIndex());4566 if (pkUnitFormationInfo)4567 {4568 const CvFormationSlotEntry& thisSlotEntry = pkUnitFormationInfo->getFormationSlotEntry(iI);45694570 // Continue moving to target4571 if(pSlot->HasStartedOnOperation())4572 {4573 MoveWithFormation(pUnit, thisSlotEntry.m_ePositionType);4574 }45754576 else4577 {4578 // See if we are just able to get to muster point in time. If so, time for us to head over there4579 int iTurns = TurnsToReachTarget(pUnit, pOperation->GetMusterPlot(), true /*bReusePaths*/, true, true);4580 if(iTurns + GC.getGame().getGameTurn() >= pThisArmy->GetTurnAtNextCheckpoint())4581 {4582 pSlot->SetStartedOnOperation(true);4583 MoveWithFormation(pUnit, thisSlotEntry.m_ePositionType);4584 }4585 }4586 }4587 }4588 }4589 }4590 ExecuteNavalFormationMoves(pThisArmy, pOperation->GetMusterPlot());4591 }4592 }45934594 // GATHERING FORCES4595 else if(pThisArmy->GetArmyAIState() == ARMYAISTATE_WAITING_FOR_UNITS_TO_CATCH_UP)4596 {4597 ExecuteFleetMoveToTarget(pThisArmy, pOperation->GetMusterPlot());4598 }45994600 // MOVING TO TARGET with a civilian4601 else if(pThisArmy->GetArmyAIState() == ARMYAISTATE_MOVING_TO_DESTINATION && pOperation->IsCivilianRequired())4602 {4603 // Update army's current location4604 CvPlot* pThisTurnTarget;4605 pThisTurnTarget = pThisArmy->GetCenterOfMass(DOMAIN_SEA);4606 if(pThisTurnTarget != NULL)4607 {4608 pThisArmy->SetXY(pThisTurnTarget->getX(), pThisTurnTarget->getY());4609 }46104611 // Settler at the target?4612 if(!pCivilian->plot()->isWater() && pCivilian->plot() == pThisArmy->GetGoalPlot())4613 {4614 if(pCivilian->canFound(pCivilian->plot()))4615 {4616 pCivilian->PushMission(CvTypes::getMISSION_FOUND());4617 if(GC.getLogging() && GC.getAILogging())4618 {4619 CvString strMsg;4620 strMsg.Format("City founded, At X=%d, At Y=%d", pCivilian->plot()->getX(), pCivilian->plot()->getY());4621 pOperation->LogOperationSpecialMessage(strMsg);4622 }4623 pOperation->SetToAbort(AI_ABORT_SUCCESS);4624 }4625 }46264627 // Is our settler and his escort both one move from the destination? If so, targeted moves to finish up4628 else if(TurnsToReachTarget(pCivilian, pOperation->GetTargetPlot()) <= 1 &&4629 (!pEscort || TurnsToReachTarget(pEscort, pOperation->GetTargetPlot()) <= 1))4630 {4631 ExecuteMoveToPlot(pCivilian, pOperation->GetTargetPlot(), true);4632 if(GC.getLogging() && GC.getAILogging())4633 {4634 CvString strTemp;4635 CvString strLogString;4636 strTemp = GC.getUnitInfo(pCivilian->getUnitType())->GetDescription();4637 strLogString.Format("Moving %s to target, X: %d, Y: %d", strTemp.GetCString(), pCivilian->getX(), pCivilian->getY());4638 LogTacticalMessage(strLogString);4639 }46404641 if(pEscort)4642 {4643 ExecuteMoveToPlot(pEscort, pOperation->GetTargetPlot(), false);4644 if(GC.getLogging() && GC.getAILogging())4645 {4646 CvString strTemp;4647 CvString strLogString;4648 strTemp = GC.getUnitInfo(pEscort->getUnitType())->GetDescription();4649 strLogString.Format("Moving %s to target, X: %d, Y: %d", strTemp.GetCString(), pEscort->getX(), pEscort->getY());4650 LogTacticalMessage(strLogString);4651 }4652 }46534654 // Move naval escorts in close4655 for(int iI = 2; iI < pThisArmy->GetNumFormationEntries(); iI++)4656 {4657 CvArmyFormationSlot* pSlot = pThisArmy->GetFormationSlot(iI);4658 if(pSlot->GetUnitID() != NO_UNIT)4659 {4660 UnitHandle pUnit = m_pPlayer->getUnit(pSlot->GetUnitID());4661 if(pUnit && !pUnit->TurnProcessed())4662 {4663 MoveToEmptySpaceNearTarget(pUnit, pOperation->GetTargetPlot(), false /*bLand */);4664 if(GC.getLogging() && GC.getAILogging())4665 {4666 CvString strTemp;4667 CvString strLogString;4668 strTemp = GC.getUnitInfo(pUnit->getUnitType())->GetDescription();4669 strLogString.Format("Moving %s near target, Now at X: %d, Y: %d", strTemp.GetCString(), pCivilian->getX(), pCivilian->getY());4670 LogTacticalMessage(strLogString);4671 }4672 }4673 }4674 }4675 }46764677 // Still further away, move as formation4678 else4679 {4680 int iBestDistance = MAX_INT;4681 CvPlot* pBestPlot = NULL;46824683 // Request moves for all units4684 for(int iI = 0; iI < pThisArmy->GetNumFormationEntries(); iI++)4685 {4686 CvArmyFormationSlot* pSlot = pThisArmy->GetFormationSlot(iI);4687 if(pSlot->GetUnitID() != NO_UNIT)4688 {4689 UnitHandle pUnit = m_pPlayer->getUnit(pSlot->GetUnitID());4690 if(pUnit && !pUnit->TurnProcessed())4691 {4692 const CvFormationSlotEntry& thisSlotEntry = GC.getMultiUnitFormationInfo(pThisArmy->GetFormationIndex())->getFormationSlotEntry(iI);4693 MoveWithFormation(pUnit, thisSlotEntry.m_ePositionType);4694 }4695 }4696 }46974698 // Plot one turn of movement for the first unit in the army we find out in the water4699 UnitHandle pUnitAtSea = pThisArmy->GetFirstUnitInDomain(DOMAIN_SEA);4700 if(!pUnitAtSea)4701 {4702 pOperation->RetargetCivilian(pCivilian, pThisArmy);4703 for(int iI = 0; iI < pThisArmy->GetNumFormationEntries(); iI++)4704 {4705 CvArmyFormationSlot* pSlot = pThisArmy->GetFormationSlot(iI);4706 if(pSlot->GetUnitID() != NO_UNIT)4707 {4708 UnitHandle pUnit = m_pPlayer->getUnit(pSlot->GetUnitID());4709 if(pUnit)4710 {4711 pUnit->finishMoves();4712 }4713 }4714 }4715 if(GC.getLogging() && GC.getAILogging())4716 {4717 CvString strLogString;4718 strLogString.Format("Retargeting naval escort operation (no unit in water!), X: %d, Y: %d", pOperation->GetTargetPlot()->getX(), pOperation->GetTargetPlot()->getY());4719 LogTacticalMessage(strLogString);4720 }4721 }4722 else4723 {4724 int iFlags;4725 if(pThisArmy->GetUnitsOfType((MultiunitPositionTypes)m_CachedInfoTypes[eMUPOSITION_NAVAL_ESCORT]) > 0)4726 {4727 iFlags = MOVE_UNITS_IGNORE_DANGER;4728 }47294730 // Goal should be a water tile one hex shy of our target4731 for(int iI = 0; iI < NUM_DIRECTION_TYPES; iI++)4732 {4733 CvPlot* pAdjacentPlot = plotDirection(pThisArmy->GetGoalX(), pThisArmy->GetGoalY(), ((DirectionTypes)iI));4734 if(pAdjacentPlot != NULL && pAdjacentPlot->isWater())4735 {4736 if(pUnitAtSea->plot() == pAdjacentPlot)4737 {4738 pBestPlot = pAdjacentPlot;4739 break;4740 }4741 else4742 {4743 // Using step finder could get tripped up by ocean hexes (since they are in the area but not valid movement targets for coastal vessels. Watch this!4744 int iDistance = GC.getStepFinder().GetStepDistanceBetweenPoints(m_pPlayer->GetID(), pOperation->GetEnemy(), pUnitAtSea->plot(), pAdjacentPlot);4745 if(iDistance > 0 && iDistance < iBestDistance)4746 {4747 iBestDistance = iDistance;4748 int iDistanceToMove = min(4, iDistance);4749 PlayerTypes eEnemy = pOperation->GetEnemy();4750 pBestPlot = GC.getStepFinder().GetXPlotsFromEnd(m_pPlayer->GetID(), eEnemy, pUnitAtSea->plot(), pAdjacentPlot, (iDistance - iDistanceToMove), false);4751 }4752 }4753 }4754 }47554756 if(pBestPlot == NULL)4757 {4758 pOperation->RetargetCivilian(pCivilian, pThisArmy);4759 for(int iI = 0; iI < pThisArmy->GetNumFormationEntries(); iI++)4760 {4761 CvArmyFormationSlot* pSlot = pThisArmy->GetFormationSlot(iI);4762 if(pSlot->GetUnitID() != NO_UNIT)4763 {4764 UnitHandle pUnit = m_pPlayer->getUnit(pSlot->GetUnitID());4765 if(pUnit)4766 {4767 pUnit->finishMoves();4768 }4769 }4770 }4771 if(GC.getLogging() && GC.getAILogging())4772 {4773 CvString strLogString;4774 strLogString.Format("Retargeting naval escort operation (path lost to target), X: %d, Y: %d", pOperation->GetTargetPlot()->getX(), pOperation->GetTargetPlot()->getY());4775 LogTacticalMessage(strLogString);4776 }4777 }4778 else4779 {4780 ExecuteNavalFormationMoves(pThisArmy, pBestPlot);4781 }4782 }4783 }4784 }47854786 // MOVING TO TARGET as an army4787 else if(pThisArmy->GetArmyAIState() == ARMYAISTATE_MOVING_TO_DESTINATION && !pOperation->IsCivilianRequired())4788 {4789 int iBestDistance = MAX_INT;4790 int iDistance;4791 CvPlot *pBestPlot = NULL;4792 int iSlowestMovementRate = MAX_INT;4793 UnitHandle pClosestUnitAtSea;4794 pBestPlot = m_pPlayer->GetMilitaryAI()->GetCoastalPlotAdjacentToTarget(pThisArmy->GetGoalPlot(), pThisArmy);47954796 // Error handling: couldn't find path to plot next to target4797 if (pBestPlot == NULL)4798 {4799 pOperation->RetargetCivilian(pCivilian, pThisArmy);4800 for (int iI = 0; iI < pThisArmy->GetNumFormationEntries(); iI++)4801 {4802 CvArmyFormationSlot *pSlot = pThisArmy->GetFormationSlot(iI);4803 if (pSlot->GetUnitID() != NO_UNIT)4804 {4805 UnitHandle pUnit = m_pPlayer->getUnit(pSlot->GetUnitID());4806 if (pUnit) 4807 {4808 pUnit->finishMoves();4809 }4810 }4811 }4812 if (GC.getLogging() && GC.getAILogging())4813 {4814 CvString strLogString;4815 strLogString.Format("Retargeting naval escort operation (path lost to target), X: %d, Y: %d", pOperation->GetTargetPlot()->getX(), pOperation->GetTargetPlot()->getY());4816 LogTacticalMessage(strLogString);4817 }4818 }48194820 else4821 {4822 // Request moves for all units, getting the slowest movement rate and the closest unit4823 iBestDistance = MAX_INT;4824 for (int iI = 0; iI < pThisArmy->GetNumFormationEntries(); iI++)4825 {4826 CvArmyFormationSlot *pSlot = pThisArmy->GetFormationSlot(iI);4827 if (pSlot->GetUnitID() != NO_UNIT)4828 {4829 UnitHandle pUnit = m_pPlayer->getUnit(pSlot->GetUnitID());4830 if (pUnit && !pUnit->TurnProcessed())4831 {4832 CvMultiUnitFormationInfo* pMultiUnitFormationInfo = GC.getMultiUnitFormationInfo(pThisArmy->GetFormationIndex());4833 if (pMultiUnitFormationInfo)4834 {4835 const CvFormationSlotEntry& thisSlotEntry = pMultiUnitFormationInfo->getFormationSlotEntry(iI);4836 MoveWithFormation(pUnit, thisSlotEntry.m_ePositionType);48374838 int iMoves = pUnit->getMoves() / GC.getMOVE_DENOMINATOR();4839 if (iMoves < iSlowestMovementRate)4840 {4841 iSlowestMovementRate = iMoves;4842 }48434844 // At sea?4845 iDistance = GC.getStepFinder().GetStepDistanceBetweenPoints(m_pPlayer->GetID(), pOperation->GetEnemy(), pUnit->plot(), pBestPlot);4846 if (iDistance > 0 && iDistance < iBestDistance)4847 {4848 iBestDistance = iDistance;4849 pClosestUnitAtSea = pUnit;4850 }4851 }4852 }4853 }4854 } 48554856 // Error handling: no one at sea, retarget4857 if (!pClosestUnitAtSea)4858 {4859 pOperation->RetargetCivilian(pCivilian, pThisArmy);4860 for (int iI = 0; iI < pThisArmy->GetNumFormationEntries(); iI++)4861 {4862 CvArmyFormationSlot *pSlot = pThisArmy->GetFormationSlot(iI);4863 if (pSlot->GetUnitID() != NO_UNIT)4864 {4865 UnitHandle pUnit = m_pPlayer->getUnit(pSlot->GetUnitID());4866 if (pUnit) 4867 {4868 pUnit->finishMoves();4869 }4870 }4871 }4872 if (GC.getLogging() && GC.getAILogging())4873 {4874 CvString strLogString;4875 strLogString.Format("Retargeting naval escort operation (no unit in water!), X: %d, Y: %d", pOperation->GetTargetPlot()->getX(), pOperation->GetTargetPlot()->getY());4876 LogTacticalMessage(strLogString);4877 }4878 }4879 else4880 {4881 // If not close yet, find best plot for this turn's movement along path to ultimate best plot4882 if (iBestDistance > iSlowestMovementRate)4883 {4884 pBestPlot = GC.getStepFinder().GetXPlotsFromEnd(m_pPlayer->GetID(), pOperation->GetEnemy(), pClosestUnitAtSea->plot(), pBestPlot, (iBestDistance - iSlowestMovementRate), true); 4885 }4886 if (pBestPlot)4887 {4888 pThisArmy->SetXY(pBestPlot->getX(), pBestPlot->getY());4889 ExecuteNavalFormationMoves(pThisArmy, pBestPlot);4890 }4891 else4892 {4893 CvAssertMsg (false, "Naval operation logic error.");4894 }4895 }4896 }4897 }4898}