Bazaar developer reference

CvTacticalAI::ExecuteAttack(CvTacticalTarget *, CvPlot *, bool, bool)

Defined in
CvTacticalAI.cpp, line 6295
Class
CvTacticalAI
Hook name
CivHooks::CvTacticalAI_ExecuteAttack

Hooks

static bool BeforeCvTacticalAI_ExecuteAttack(CvTacticalAI* self, CvTacticalTarget* pTarget, CvPlot* pTargetPlot, bool bInflictWhatWeTake, bool bMustSurviveAttack);static void AfterCvTacticalAI_ExecuteAttack(CvTacticalAI* self, CvTacticalTarget* pTarget, CvPlot* pTargetPlot, bool bInflictWhatWeTake, bool bMustSurviveAttack);static void ReplaceCvTacticalAI_ExecuteAttack(void (*original)(CvTacticalAI*, CvTacticalTarget*, CvPlot*, bool, bool), CvTacticalAI* self, CvTacticalTarget* pTarget, CvPlot* pTargetPlot, bool bInflictWhatWeTake, bool bMustSurviveAttack);CivRegisterBefore<CivHooks::CvTacticalAI_ExecuteAttack>(host, &BeforeCvTacticalAI_ExecuteAttack);CivRegisterAfter<CivHooks::CvTacticalAI_ExecuteAttack>(host, &AfterCvTacticalAI_ExecuteAttack);CivRegisterReplace<CivHooks::CvTacticalAI_ExecuteAttack>(host, &ReplaceCvTacticalAI_ExecuteAttack);

Code

6294/// Attack a defended space6295void CvTacticalAI::ExecuteAttack(CvTacticalTarget* pTarget, CvPlot* pTargetPlot, bool bInflictWhatWeTake, bool bMustSurviveAttack)6296{6297	void* pFirstAttacker = NULL;6298	bool bFirstAttackRanged = false;6299	bool bFirstAttackCity = false;63006301	if(PlotAlreadyTargeted(pTargetPlot) != -1)6302	{6303		return;6304	}63056306	// How much damage do we still need to inflict?6307	int iDamageRemaining = (pTarget->GetAuxIntData() * (100 + GC.getAI_TACTICAL_OVERKILL_PERCENT())) / 100;63086309#if defined(MOD_AI_SMART_V3)6310	if (MOD_AI_SMART_V3)6311	{6312		// Start by sending possible air sweeps6313		for(unsigned int iI = 0; iI < m_CurrentAirUnits.size(); iI++)6314		{6315			UnitHandle pUnit = m_pPlayer->getUnit(m_CurrentAirUnits[iI].GetID());63166317			if(pUnit && !pUnit->TurnProcessed())6318			{6319				if(pUnit->canAirSweep())6320				{6321					if (pTarget)6322					{6323						pUnit->PushMission(CvTypes::getMISSION_AIR_SWEEP(), pTargetPlot->getX(), pTargetPlot->getY());6324						UnitProcessed(m_CurrentAirUnits[iI].GetID(), false /*bMarkTacticalMap*/);63256326						if(GC.getLogging() && GC.getAILogging())6327						{6328							CvString strMsg;6329							strMsg.Format("Sending %s to air sweep prior to attack to Target X: %d, Y: %d", pUnit->getName().GetCString(), pTargetPlot->getX(), pTargetPlot->getY());6330							LogTacticalMessage(strMsg);6331						}6332					}6333				}6334			}6335		}6336	}6337#endif63386339	// Start by applying damage from city bombards6340	for(unsigned int iI = 0; iI < m_CurrentMoveCities.size() && iDamageRemaining > 0; iI++)6341	{6342		CvCity* pCity = m_pPlayer->getCity(m_CurrentMoveCities[iI].GetID());6343		if(pCity != NULL)6344		{6345			if(QueueAttack(pCity, pTarget, true /*bRanged*/, true /*bCity*/))6346			{6347				pFirstAttacker = (void*)pCity;6348				bFirstAttackRanged = true;6349				bFirstAttackCity = true;6350			}63516352			// Subtract off expected damage6353			iDamageRemaining -= m_CurrentMoveCities[iI].GetExpectedTargetDamage();6354		}6355	}63566357#if defined(MOD_AI_SMART_V3)6358	if (MOD_AI_SMART_V3)6359	{6360		// Make a list of adjacent plots6361		std::vector<CvPlot *> plotList;6362		for(int iDirectionLoop = 0; iDirectionLoop < NUM_DIRECTION_TYPES; ++iDirectionLoop)6363		{6364			CvPlot* pAdjacentPlot = plotDirection(pTargetPlot->getX(), pTargetPlot->getY(), ((DirectionTypes)iDirectionLoop));6365			if (pAdjacentPlot)6366			{6367				int iPlotIndex = GC.getMap().plotNum(pAdjacentPlot->getX(), pAdjacentPlot->getY());6368				CvTacticalAnalysisCell *pCell = m_pMap->GetCell(iPlotIndex);6369				if(pAdjacentPlot != NULL && pAdjacentPlot->getNumDefenders(pTarget->GetTargetPlayer()) == 0 && !pCell->IsEnemyCity())6370				{6371					plotList.push_back(pAdjacentPlot);6372				}6373			}6374		}63756376		// First loop for melee units just to reposition.6377		for(unsigned int iI = 0; iI < m_CurrentMoveUnits.size(); iI++)6378		{6379			if(!bInflictWhatWeTake || m_CurrentMoveUnits[iI].GetExpectedTargetDamage() >= m_CurrentMoveUnits[iI].GetExpectedSelfDamage())6380			{6381				UnitHandle pUnit = m_pPlayer->getUnit(m_CurrentMoveUnits[iI].GetID());63826383				if(pUnit)6384				{6385					if(pUnit->getMoves() > 0 && (!bMustSurviveAttack || ((m_CurrentMoveUnits[iI].GetExpectedSelfDamage() + pUnit->getDamage()) < pUnit->GetMaxHitPoints())))6386					{6387						// Are we a melee unit6388						if(!pUnit->IsCanAttackRanged())6389						{6390							bool unitHasMoved = false;63916392							// Not adjacent6393							if (plotDistance(pUnit->getX(), pUnit->getY(), pTargetPlot->getX(), pTargetPlot->getY()) > 1)6394							{6395								// Find spaces adjacent to target we can move into with MP left6396								std::vector<CvPlot*>::iterator it;63976398								for (it = plotList.begin(); it != plotList.end(); it++)6399								{6400									if (TurnsToReachTarget(pUnit, *it, false /*bReusePaths*/, false /*bIgnoreUnits*/, true /*bIgnoreStacking*/) == 0)6401									{6402										// Move up there6403										pUnit->PushMission(CvTypes::getMISSION_MOVE_TO(), (*it)->getX(), (*it)->getY());6404										plotList.erase(it);6405										unitHasMoved = true;6406										break;6407									}6408								}64096410								// Didn't find an open hex6411								if (!unitHasMoved)6412								{6413									if(GC.getLogging() && GC.getAILogging())6414									{6415										CvString strMsg;6416										strMsg.Format("Not enough free hexes next for all melee units to attack, Target X: %d, Y: %d", pTargetPlot->getX(), pTargetPlot->getY());6417										LogTacticalMessage(strMsg);6418									}64196420									// Move up if we can6421									if (MoveToEmptySpaceNearTarget(pUnit, pTargetPlot, (pUnit->getDomainType() == DOMAIN_LAND)))6422									{6423										if(GC.getLogging() && GC.getAILogging())6424										{6425											CvString strMsg;6426											strMsg.Format("Moving adjacent for attack next turn, Now at X: %d, Y: %d", pUnit->getX(), pUnit->getY());6427											LogTacticalMessage(strMsg);6428										}6429									}6430								}6431							}6432						}6433					}6434				}6435			}6436			else6437			{6438				if(GC.getLogging() && GC.getAILogging())6439				{6440					CvString strMsg;6441					strMsg.Format("Not attacking with unit. Can't generate a good damage ratio.");6442					LogTacticalMessage(strMsg);6443				}6444			}6445		}6446	}6447#endif64486449	// First loop is ranged units only6450	for(unsigned int iI = 0; iI < m_CurrentMoveUnits.size() && iDamageRemaining > 0; iI++)6451	{6452		if(!bInflictWhatWeTake || m_CurrentMoveUnits[iI].GetExpectedTargetDamage() >= m_CurrentMoveUnits[iI].GetExpectedSelfDamage())6453		{6454			UnitHandle pUnit = m_pPlayer->getUnit(m_CurrentMoveUnits[iI].GetID());6455			if(pUnit)6456			{6457				if(pUnit->getMoves() > 0)6458				{6459#if defined(MOD_AI_SMART_V3)6460					if (MOD_AI_SMART_V3)6461					{6462						// Are we a ranged unit6463						if(pUnit->IsCanAttackRanged())6464						{6465							bool bQueueTryRangedAttack = false;64666467							// Are we in range and do we have LOS to the target?6468							if((plotDistance(pUnit->getX(), pUnit->getY(), pTargetPlot->getX(), pTargetPlot->getY()) <= pUnit->GetRange()) && (pUnit->canEverRangeStrikeAt(pTargetPlot->getX(), pTargetPlot->getY())))6469							{6470								bQueueTryRangedAttack = true;6471							}6472							else6473							{6474								// Not in range/ not LOS: then lets try to reposition unit.6475								CvPlot* repositionPlot = GetBestRepositionPlot(pUnit, pTargetPlot);64766477								if (repositionPlot != NULL)6478								{6479									if(GC.getLogging() && GC.getAILogging())6480									{6481										CvString strMsg;6482										strMsg.Format("Repositioning ranged unit from X: %d, Y: %d to X: %d, Y: %d", pUnit->getX(), pUnit->getY(), repositionPlot->getX(), repositionPlot->getY());6483										LogTacticalMessage(strMsg);6484									}64856486									pUnit->PushMission(CvTypes::getMISSION_MOVE_TO(), repositionPlot->getX(), repositionPlot->getY());6487									bQueueTryRangedAttack = true;6488								}6489							}64906491							if (bQueueTryRangedAttack)6492							{6493								// Do we have LOS to the target?6494								if(pUnit->canEverRangeStrikeAt(pTargetPlot->getX(), pTargetPlot->getY()))6495								{6496									// Do we need to set up to make a ranged attack?6497									if(pUnit->canSetUpForRangedAttack(NULL))6498									{6499										pUnit->setSetUpForRangedAttack(true);6500										if(GC.getLogging() && GC.getAILogging())6501										{6502											CvString strTemp;6503											CvString strMsg;6504											strTemp = GC.getUnitInfo(pUnit->getUnitType())->GetDescription();6505											strMsg.Format("Set up %s for ranged attack", strTemp.GetCString());6506											LogTacticalMessage(strMsg);6507										}6508										if(!pUnit->canMove())6509										{6510											pUnit->SetTacticalAIPlot(NULL);6511											UnitProcessed(pUnit->GetID());6512										}6513									}65146515									// Can we hit it with a ranged attack?  If so, that gets first priority6516									if(pUnit->canMove() && pUnit->canRangeStrikeAt(pTargetPlot->getX(), pTargetPlot->getY()))6517									{6518										// Queue up this attack6519										if(QueueAttack((void*)pUnit.pointer(), pTarget, true /*bRanged*/, false /*bCity*/))6520										{6521											pFirstAttacker = (void*)pUnit.pointer();6522											bFirstAttackRanged = true;6523										}6524										pUnit->SetTacticalAIPlot(NULL);6525										UnitProcessed(m_CurrentMoveUnits[iI].GetID());65266527										// Subtract off expected damage6528										iDamageRemaining -= m_CurrentMoveUnits[iI].GetExpectedTargetDamage();6529									}6530								}6531							}6532						}6533					}6534					else6535					{6536#endif6537						if(!bMustSurviveAttack || ((m_CurrentMoveUnits[iI].GetExpectedSelfDamage() + pUnit->getDamage()) < pUnit->GetMaxHitPoints()))6538						{6539							// Are we a ranged unit6540							if(pUnit->IsCanAttackRanged())6541							{6542								// Are we in range?6543								if(plotDistance(pUnit->getX(), pUnit->getY(), pTargetPlot->getX(), pTargetPlot->getY()) <= pUnit->GetRange())6544								{6545									// Do we have LOS to the target?6546									if(pUnit->canEverRangeStrikeAt(pTargetPlot->getX(), pTargetPlot->getY()))6547									{6548										// Do we need to set up to make a ranged attack?6549										if(pUnit->canSetUpForRangedAttack(NULL))6550										{6551											pUnit->setSetUpForRangedAttack(true);6552											if(GC.getLogging() && GC.getAILogging())6553											{6554												CvString strTemp;6555												CvString strMsg;6556												strTemp = GC.getUnitInfo(pUnit->getUnitType())->GetDescription();6557												strMsg.Format("Set up %s for ranged attack", strTemp.GetCString());6558												LogTacticalMessage(strMsg);6559											}6560											if(!pUnit->canMove())6561											{6562												pUnit->SetTacticalAIPlot(NULL);6563												UnitProcessed(pUnit->GetID());6564											}6565										}65666567										// Can we hit it with a ranged attack?  If so, that gets first priority6568										if(pUnit->canMove() && pUnit->canRangeStrikeAt(pTargetPlot->getX(), pTargetPlot->getY()))6569										{6570											// Queue up this attack6571											if(QueueAttack((void*)pUnit.pointer(), pTarget, true /*bRanged*/, false /*bCity*/))6572											{6573												pFirstAttacker = (void*)pUnit.pointer();6574												bFirstAttackRanged = true;6575											}6576											pUnit->SetTacticalAIPlot(NULL);6577											UnitProcessed(m_CurrentMoveUnits[iI].GetID());65786579											// Subtract off expected damage6580											iDamageRemaining -= m_CurrentMoveUnits[iI].GetExpectedTargetDamage();6581										}6582									}6583								}6584							}6585						}6586						else6587						{6588							if(GC.getLogging() && GC.getAILogging())6589							{6590								CvString strMsg;6591								strMsg.Format("Not attacking with unit. We'll destroy ourself, %s, X: %d, Y: %d", pUnit->getName().GetCString(), pUnit->getX(), pUnit->getY());6592								LogTacticalMessage(strMsg);6593							}6594						}6595#if defined(MOD_AI_SMART_V3)6596					}6597#endif6598				}6599			}6600		}6601		else6602		{6603			if(GC.getLogging() && GC.getAILogging())6604			{6605				CvString strMsg;6606				strMsg.Format("Not attacking with unit. Can't generate a good damage ratio.");6607				LogTacticalMessage(strMsg);6608			}6609		}6610	}66116612	// If target is city, want to get in one melee attack, so set damage remaining to 16613	if(pTarget->GetTargetType() == AI_TACTICAL_TARGET_CITY && iDamageRemaining < 1)6614	{6615		iDamageRemaining = 1;6616	}66176618	// Make a list of adjacent plots6619	std::vector<CvPlot *> plotList;6620#if defined(MOD_AI_SMART_V3)6621	if (!MOD_AI_SMART_V3)6622	{6623#endif6624	for(int iDirectionLoop = 0; iDirectionLoop < NUM_DIRECTION_TYPES; ++iDirectionLoop)6625	{6626		CvPlot* pAdjacentPlot = plotDirection(pTargetPlot->getX(), pTargetPlot->getY(), ((DirectionTypes)iDirectionLoop));6627		if (pAdjacentPlot)6628		{6629			int iPlotIndex = GC.getMap().plotNum(pAdjacentPlot->getX(), pAdjacentPlot->getY());6630			CvTacticalAnalysisCell *pCell = m_pMap->GetCell(iPlotIndex);6631			if(pAdjacentPlot != NULL && pAdjacentPlot->getNumDefenders(pTarget->GetTargetPlayer()) == 0 && !pCell->IsEnemyCity())6632			{6633				plotList.push_back(pAdjacentPlot);6634			}6635		}6636	}6637#if defined(MOD_AI_SMART_V3)6638	}6639#endif66406641	// Second loop are only melee units6642	for(unsigned int iI = 0; iI < m_CurrentMoveUnits.size() && iDamageRemaining > 0; iI++)6643	{6644		if(!bInflictWhatWeTake || m_CurrentMoveUnits[iI].GetExpectedTargetDamage() >= m_CurrentMoveUnits[iI].GetExpectedSelfDamage())6645		{6646			UnitHandle pUnit = m_pPlayer->getUnit(m_CurrentMoveUnits[iI].GetID());6647			if(pUnit)6648			{6649				if(pUnit->getMoves() > 0 && (!bMustSurviveAttack || ((m_CurrentMoveUnits[iI].GetExpectedSelfDamage() + pUnit->getDamage()) < pUnit->GetMaxHitPoints())))6650				{6651					// Are we a melee unit6652					if(!pUnit->IsCanAttackRanged())6653					{6654#if defined(MOD_AI_SMART_V3)6655						if (MOD_AI_SMART_V3)6656						{6657							// Already adjacent?6658							if (plotDistance(pUnit->getX(), pUnit->getY(), pTargetPlot->getX(), pTargetPlot->getY()) <= 1)6659							{6660								// Queue up this attack6661								if(QueueAttack((void*)pUnit.pointer(), pTarget, false /*bRanged*/, false /*bCity*/))6662								{6663									pFirstAttacker = (void*)pUnit.pointer();6664								}6665								pUnit->SetTacticalAIPlot(NULL);6666								UnitProcessed(m_CurrentMoveUnits[iI].GetID(), false /*bMarkTacticalMap*/);66676668								// Subtract off expected damage6669								iDamageRemaining -= m_CurrentMoveUnits[iI].GetExpectedTargetDamage();6670							}6671						}6672						else6673						{6674#endif6675						bool bQueueAttackNow = false;6676						6677						// Already adjacent?6678							if (plotDistance(pUnit->getX(), pUnit->getY(), pTargetPlot->getX(), pTargetPlot->getY()) <= 1)6679							{6680								bQueueAttackNow = true;6681							}6682							else6683							{6684								// Find spaces adjacent to target we can move into with MP left6685								std::vector<CvPlot *>::iterator it;6686								for (it = plotList.begin(); it != plotList.end(); it++)6687								{6688									if (TurnsToReachTarget(pUnit, *it, false /*bReusePaths*/, false /*bIgnoreUnits*/, true /*bIgnoreStacking*/) == 0)6689									{6690										// Move up there6691										pUnit->PushMission(CvTypes::getMISSION_MOVE_TO(), (*it)->getX(), (*it)->getY());6692										bQueueAttackNow = true;66936694										plotList.erase(it);6695										break;6696									}6697								}66986699								// Didn't find an open hex6700								if (!bQueueAttackNow)6701								{6702									if(GC.getLogging() && GC.getAILogging())6703									{6704										CvString strMsg;6705										strMsg.Format("Not enough free hexes next for all melee units to attack, Target X: %d, Y: %d", pTargetPlot->getX(), pTargetPlot->getY());6706										LogTacticalMessage(strMsg);6707									}6708								6709									// Move up if we can6710									if (MoveToEmptySpaceNearTarget(pUnit, pTargetPlot, (pUnit->getDomainType() == DOMAIN_LAND)))6711									{6712										if(GC.getLogging() && GC.getAILogging())6713										{6714											CvString strMsg;6715											strMsg.Format("Moving adjacent for attack next turn, Now at X: %d, Y: %d", pUnit->getX(), pUnit->getY());6716											LogTacticalMessage(strMsg);6717										}6718									}6719								}6720							}67216722						if (bQueueAttackNow)6723						{6724							// Queue up this attack6725							if(QueueAttack((void*)pUnit.pointer(), pTarget, false /*bRanged*/, false /*bCity*/))6726							{6727								pFirstAttacker = (void*)pUnit.pointer();6728							}6729							pUnit->SetTacticalAIPlot(NULL);6730							UnitProcessed(m_CurrentMoveUnits[iI].GetID(), false /*bMarkTacticalMap*/);67316732							// Subtract off expected damage6733							iDamageRemaining -= m_CurrentMoveUnits[iI].GetExpectedTargetDamage();6734						}6735#if defined(MOD_AI_SMART_V3)6736						}6737#endif6738					}6739				}6740				else6741				{6742					if(GC.getLogging() && GC.getAILogging())6743					{6744						CvString strMsg;6745						strMsg.Format("Not attacking with unit. We'll destroy ourself, %s, X: %d, Y: %d", pUnit->getName().GetCString(), pUnit->getX(), pUnit->getY());6746						LogTacticalMessage(strMsg);6747					}6748				}6749			}6750		}6751		else6752		{6753			if(GC.getLogging() && GC.getAILogging())6754			{6755				CvString strMsg;6756				strMsg.Format("Not attacking with unit. Can't generate a good damage ratio.");6757				LogTacticalMessage(strMsg);6758			}6759		}6760	}67616762	// Start up first attack6763	if(pFirstAttacker)6764	{6765		LaunchAttack(pFirstAttacker, pTarget, true /*bFirst*/, bFirstAttackRanged, bFirstAttackCity);6766	}6767}

Calls

Called by