Bazaar developer reference

CvUnitMission::ContinueMission(UnitHandle, int, int)

Defined in
CvUnitMission.cpp, line 480
Class
CvUnitMission
Hook name
CivHooks::CvUnitMission_ContinueMission
Lua events
GameEvents.CustomMissionDoStep, GameEvents.CustomMissionCompleted, GameEvents.CustomMissionCameraTime

Hand-placed hooks inside

A replacement for this function that does not call original stops these hooks from running.

Hooks

static bool BeforeCvUnitMission_ContinueMission(UnitHandle hUnit, int iSteps, int iETA);static void AfterCvUnitMission_ContinueMission(UnitHandle hUnit, int iSteps, int iETA);static void ReplaceCvUnitMission_ContinueMission(void (*original)(UnitHandle, int, int), UnitHandle hUnit, int iSteps, int iETA);CivRegisterBefore<CivHooks::CvUnitMission_ContinueMission>(host, &BeforeCvUnitMission_ContinueMission);CivRegisterAfter<CivHooks::CvUnitMission_ContinueMission>(host, &AfterCvUnitMission_ContinueMission);CivRegisterReplace<CivHooks::CvUnitMission_ContinueMission>(host, &ReplaceCvUnitMission_ContinueMission);

Code

479/// Yes, please hit me again. I like pain.480void CvUnitMission::ContinueMission(UnitHandle hUnit, int iSteps, int iETA)481{482	hUnit->ClearPathCache();		// At the start of the continue, clear any cached path.  The cache will be rebuilt and reused while the mission loops in this method483484	bool bContinueMissionRestart = true;	// to make this function no longer recursive485	while(bContinueMissionRestart)486	{487		bContinueMissionRestart = false;488489		bool bDone = false;   // are we done with mission?490		bool bAction = false; // are we taking an action this turn?491492		// slewis - important modification!493		// This function may call the pathfinder multiple times.494		// That can cause partial moves to be impossible in certain circumstances.495		// We need a way to determine if the pathfinder can be used without breaking the currently built path.496		// I added unit flags to accomplish this. (While this flag does not necessarily need to be serialized,497		// the variable is serialized for future proofing.)498		hUnit->m_iFlags = hUnit->m_iFlags | CvUnit::UNITFLAG_EVALUATING_MISSION;499500		// slewis - more important info!501		// I replaced all the "return"s in this function with "goto"s.502		// This ensures that every way this function is exited, it always clears out the unit flag evaluation mission.503		// I know gotos are frowned upon, but if anyone else wants to re-write and test this function, be my guest.504505		CvAssert(!hUnit->isInCombat());506		CvAssert(hUnit->HeadMissionQueueNode() != NULL);507		CvAssert(hUnit->getOwner() != NO_PLAYER);508		CvAssert(hUnit->GetActivityType() == ACTIVITY_MISSION);509510		if(HeadMissionQueueNode(hUnit->m_missionQueue) == NULL)511		{512			// just in case...513			hUnit->SetActivityType(ACTIVITY_AWAKE);514			goto ContinueMissionExit;515		}516517		CvAssert(iSteps < 100);518		if(iSteps >= 100)519			goto ContinueMissionExit;520521		const MissionData* pkMissionData = (HeadMissionQueueNode(hUnit->m_missionQueue));522523		if(pkMissionData->iPushTurn == GC.getGame().getGameTurn() || (pkMissionData->iFlags & MOVE_UNITS_THROUGH_ENEMY))524		{525			if(pkMissionData->eMissionType == CvTypes::getMISSION_MOVE_TO() && !hUnit->IsDoingPartialMove() && hUnit->canMove() && hUnit->m_unitMoveLocs.size() == 0)526			{527				CvPlot* pPlot = GC.getMap().plot(pkMissionData->iData1, pkMissionData->iData2);528				if(hUnit->IsAutomated() && pPlot->isVisible(hUnit->getTeam()) && hUnit->canMoveInto(*pPlot, CvUnit::MOVEFLAG_ATTACK))529				{530					// if we're automated and try to attack, consider this move OVAH531					bDone = true;532				}533				else534				{535					bool bCityAttackInterrupt = gDLL->GetAdvisorCityAttackInterrupt();536					bool bBadAttackInterrupt = gDLL->GetAdvisorBadAttackInterrupt();537					if(hUnit->isHuman() && !CvPreGame::isNetworkMultiplayerGame() && !GC.getGame().IsCombatWarned() && (bCityAttackInterrupt || bBadAttackInterrupt)538#if defined(CIV_HOST_API)539						&& !CivHandIsPluginControlledPlayer(hUnit->getOwner())hand-placed hook: IsPluginControlledPlayer540#endif541						)542					{543						if(hUnit->canMoveInto(*pPlot, CvUnit::MOVEFLAG_ATTACK) && pPlot->isVisible(hUnit->getTeam()))544						{545							if(pPlot->isCity())546							{547								if(bCityAttackInterrupt)548								{549									GC.GetEngineUserInterface()->SetDontShowPopups(false);550551									if(!GC.getGame().isOption(GAMEOPTION_NO_TUTORIAL))552									{553										// do city alert554										CvPopupInfo kPopup(BUTTONPOPUP_ADVISOR_MODAL);555										kPopup.iData1 = ADVISOR_MILITARY;556										kPopup.iData2 = pPlot->GetPlotIndex();557										kPopup.iData3 = hUnit->plot()->GetPlotIndex();558										strcpy_s(kPopup.szText, "TXT_KEY_ADVISOR_CITY_ATTACK_BODY");559										kPopup.bOption1 = true;560										GC.GetEngineUserInterface()->AddPopup(kPopup);561										goto ContinueMissionExit;562									}563								}564							}565							else if(bBadAttackInterrupt)566							{567								CvUnit* pDefender = pPlot->getVisibleEnemyDefender(hUnit->getOwner());568								if(pDefender)569								{570									CombatPredictionTypes ePrediction = GC.getGame().GetCombatPrediction(hUnit.pointer(), pDefender);571									if(ePrediction == COMBAT_PREDICTION_TOTAL_DEFEAT || ePrediction == COMBAT_PREDICTION_MAJOR_DEFEAT)572									{573										if(!GC.getGame().isOption(GAMEOPTION_NO_TUTORIAL))574										{575											GC.GetEngineUserInterface()->SetDontShowPopups(false);576											CvPopupInfo kPopup(BUTTONPOPUP_ADVISOR_MODAL);577											kPopup.iData1 = ADVISOR_MILITARY;578											kPopup.iData2 = pPlot->GetPlotIndex();579											kPopup.iData3 = hUnit->plot()->GetPlotIndex();580											strcpy_s(kPopup.szText, "TXT_KEY_ADVISOR_BAD_ATTACK_BODY");581											kPopup.bOption1 = false;582											GC.GetEngineUserInterface()->AddPopup(kPopup);583											goto ContinueMissionExit;584										}585586									}587								}588							}589						}590					}591592					if(hUnit->UnitAttack(pkMissionData->iData1, pkMissionData->iData2, pkMissionData->iFlags, iSteps))593					{594						bDone = true;595					}596				}597			}598		}599600		// extra crash protection, should never happen (but a previous bug in groupAttack was causing a NULL here)601		// while that bug is fixed, no reason to not be a little more careful602		if(HeadMissionQueueNode(hUnit->m_missionQueue) == NULL)603		{604			hUnit->SetActivityType(ACTIVITY_AWAKE);605			goto ContinueMissionExit;606		}607608		const MissionQueue& kMissionQueue = hUnit->m_missionQueue;609		// If there are units in the selection group, they can all move, and we're not done610		//   then try to follow the misision611		if(!bDone && hUnit->canMove() && !hUnit->IsDoingPartialMove())612		{613			const MissionData& kMissionData = *HeadMissionQueueNode(kMissionQueue);614615			if(kMissionData.eMissionType == CvTypes::getMISSION_MOVE_TO() ||616			        kMissionData.eMissionType == CvTypes::getMISSION_EMBARK() ||617			        kMissionData.eMissionType == CvTypes::getMISSION_DISEMBARK())618			{619				if(hUnit->getDomainType() == DOMAIN_AIR)620				{621					hUnit->UnitPathTo(kMissionData.iData1, kMissionData.iData2, kMissionData.iFlags);622					bDone = true;623				}624				else625				{626					int iThisETA = hUnit->UnitPathTo(kMissionData.iData1, kMissionData.iData2, kMissionData.iFlags, iETA);627					if(iThisETA > 0)628					{629						bAction = true;630					}631					else632					{633						bDone = true;634					}635636					// Save off the initial ETA, we will feed it back into the UnitPathTo so it can check to see if our ETA grows while we are in the loop.637					// This can happen as terrain gets revealed.638					if(iSteps == 0)639					{640						iETA = iThisETA;641					}642				}643			}644645			else if(kMissionData.eMissionType == CvTypes::getMISSION_ROUTE_TO())646			{647				if(hUnit->UnitRoadTo(kMissionData.iData1, kMissionData.iData2, kMissionData.iFlags))648				{649					bAction = true;650				}651				else652				{653					bDone = true;654				}655			}656657			else if(kMissionData.eMissionType == CvTypes::getMISSION_SWAP_UNITS())658			{659				CvPlot* pOriginationPlot;660				CvPlot* pTargetPlot;661662				// Get target plot663				pTargetPlot = GC.getMap().plot(kMissionData.iData1, kMissionData.iData2);664665				if(pTargetPlot != NULL)666				{667					pOriginationPlot = hUnit->plot();668669					if(pTargetPlot->getNumUnits() < 1)670					{671						bAction = false;672						bDone = true;673						break;674					}675676					// Find unit to move out677					for(int iI = 0; iI < pTargetPlot->getNumUnits(); iI++)678					{679						CvUnit* pUnit2 = pTargetPlot->getUnitByIndex(iI);680681						if(pUnit2->AreUnitsOfSameType(*(hUnit)) && pUnit2->ReadyToMove())682						{683							// Start the swap684							hUnit->UnitPathTo(HeadMissionQueueNode(kMissionQueue)->iData1, HeadMissionQueueNode(kMissionQueue)->iData2, MOVE_IGNORE_STACKING);685686							// Move the other unit back out687							pUnit2->UnitPathTo(pOriginationPlot->getX(), pOriginationPlot->getY(), 0);688							bDone = true;689						}690					}691				}692			}693694			else if(kMissionData.eMissionType == CvTypes::getMISSION_MOVE_TO_UNIT())695			{696				if((hUnit->AI_getUnitAIType() == UNITAI_DEFENSE) && hUnit->plot()->isCity() && (hUnit->plot()->getTeam() == hUnit->getTeam()))697				{698					if(hUnit->plot()->getBestDefender(hUnit->getOwner()) == hUnit.pointer())699					{700						bAction = false;701						bDone = true;702						break;703					}704				}705				UnitHandle pTargetUnit = GET_PLAYER((PlayerTypes)kMissionData.iData1).getUnit(kMissionData.iData2);706				if(pTargetUnit)707				{708					if(hUnit->GetMissionAIType() != MISSIONAI_SHADOW && hUnit->GetMissionAIType() != MISSIONAI_GROUP)709					{710						if(!hUnit->plot()->isOwned() || hUnit->plot()->getOwner() == hUnit->getOwner())711						{712							CvPlot* pMissionPlot = pTargetUnit->GetMissionAIPlot();713							if(pMissionPlot != NULL && NO_TEAM != pMissionPlot->getTeam())714							{715								if(pMissionPlot->isOwned() && pTargetUnit->isPotentialEnemy(pMissionPlot->getTeam(), pMissionPlot))716								{717									bAction = false;718									bDone = true;719									break;720								}721							}722						}723					}724725					if(hUnit->UnitPathTo(pTargetUnit->getX(), pTargetUnit->getY(), kMissionData.iFlags) > 0)726					{727						bAction = true;728					}729					else730					{731						bDone = true;732					}733				}734				else735				{736					bDone = true;737				}738			}739740			else if(kMissionData.eMissionType == CvTypes::getMISSION_RANGE_ATTACK())741			{742				if(CvUnitCombat::AttackRanged(*hUnit, kMissionData.iData1, kMissionData.iData2, (kMissionData.iFlags &  MISSION_MODIFIER_NO_DEFENSIVE_SUPPORT)?CvUnitCombat::ATTACK_OPTION_NO_DEFENSIVE_SUPPORT:CvUnitCombat::ATTACK_OPTION_NONE) != CvUnitCombat::ATTACK_ABORTED)743				{744					bDone = true;745				}746			}747748			else if(kMissionData.eMissionType == CvTypes::getMISSION_NUKE())749			{750				if(CvUnitCombat::AttackNuclear(*hUnit, kMissionData.iData1, kMissionData.iData2, (kMissionData.iFlags &  MISSION_MODIFIER_NO_DEFENSIVE_SUPPORT)?CvUnitCombat::ATTACK_OPTION_NO_DEFENSIVE_SUPPORT:CvUnitCombat::ATTACK_OPTION_NONE) != CvUnitCombat::ATTACK_ABORTED)751				{752					bDone = true;753				}754			}755756			else if(kMissionData.eMissionType == CvTypes::getMISSION_BUILD())757			{758				if(!hUnit->UnitBuild((BuildTypes)(kMissionData.iData1)))759				{760					bDone = true;761				}762			}763764#if defined(MOD_EVENTS_CUSTOM_MISSIONS)765			else if (MOD_EVENTS_CUSTOM_MISSIONS) {766				int iValue = 0;767				if (GAMEEVENTINVOKE_VALUE(iValue, GAMEEVENT_CustomMissionDoStep, hUnit->getOwner(), hUnit->GetID(), kMissionData.eMissionType, kMissionData.iData1, kMissionData.iData2, kMissionData.iFlags, kMissionData.iPushTurn) == GAMEEVENTRETURN_VALUE) {768					if (iValue == CUSTOM_MISSION_ACTION ) {769						bAction = true;770					} else if (iValue == CUSTOM_MISSION_DONE ) {771						bDone = true;772					} else if (iValue == CUSTOM_MISSION_ACTION_AND_DONE ) {773						bAction = true;774						bDone = true;775					}776				}777			}778#endif779		}780781		// check to see if mission is done782		if(!bDone && (hUnit->HeadMissionQueueNode() != NULL))783		{784			const MissionData& kMissionData = *HeadMissionQueueNode(kMissionQueue);785786			if(kMissionData.eMissionType == CvTypes::getMISSION_MOVE_TO() ||787			        kMissionData.eMissionType == CvTypes::getMISSION_SWAP_UNITS() ||788			        kMissionData.eMissionType == CvTypes::getMISSION_EMBARK() ||789			        kMissionData.eMissionType == CvTypes::getMISSION_DISEMBARK())790			{791				if(hUnit->at(kMissionData.iData1, kMissionData.iData2))792				{793					bDone = true;794#ifdef LOG_UNIT_MOVES795					std::ostringstream updateMsg;796					updateMsg << "ContinueMission() : player ";797					updateMsg << GET_PLAYER(hUnit->getOwner()).getName();798					updateMsg << hUnit->getName() << " id=" << hUnit->GetID() << " reached " << kMissionData.iData1 << ", " << kMissionData.iData2;799					gDLL->netMessageDebugLog(updateMsg.str());800#endif801				}802			}803804			else if(kMissionData.eMissionType == CvTypes::getMISSION_ROUTE_TO())805			{806				if(hUnit->at(kMissionData.iData1, kMissionData.iData2))807				{808					if(hUnit->GetBestBuildRoute(hUnit->plot()) == NO_ROUTE)809					{810						bDone = true;811					}812				}813			}814815			else if(kMissionData.eMissionType == CvTypes::getMISSION_MOVE_TO_UNIT())816			{817				UnitHandle pTargetUnit = GET_PLAYER((PlayerTypes)kMissionData.iData1).getUnit(kMissionData.iData2);818				if((!pTargetUnit) ||hUnit-> plot() == pTargetUnit->plot())819				{820					bDone = true;821				}822			}823824			else if(kMissionData.eMissionType == CvTypes::getMISSION_SET_UP_FOR_RANGED_ATTACK() ||825					kMissionData.eMissionType == CvTypes::getMISSION_AIRLIFT() ||826					kMissionData.eMissionType == CvTypes::getMISSION_NUKE() ||827					kMissionData.eMissionType == CvTypes::getMISSION_PARADROP() ||828					kMissionData.eMissionType == CvTypes::getMISSION_AIR_SWEEP() ||829					kMissionData.eMissionType == CvTypes::getMISSION_REBASE() ||830			        kMissionData.eMissionType == CvTypes::getMISSION_RANGE_ATTACK() ||831			        kMissionData.eMissionType == CvTypes::getMISSION_PILLAGE() ||832			        kMissionData.eMissionType == CvTypes::getMISSION_FOUND() ||833			        kMissionData.eMissionType == CvTypes::getMISSION_JOIN() ||834			        kMissionData.eMissionType == CvTypes::getMISSION_CONSTRUCT() ||835			        kMissionData.eMissionType == CvTypes::getMISSION_DISCOVER() ||836			        kMissionData.eMissionType == CvTypes::getMISSION_HURRY() ||837					kMissionData.eMissionType == CvTypes::getMISSION_TRADE() ||838					kMissionData.eMissionType == CvTypes::getMISSION_BUY_CITY_STATE() ||839					kMissionData.eMissionType == CvTypes::getMISSION_REPAIR_FLEET() ||840			        kMissionData.eMissionType == CvTypes::getMISSION_SPACESHIP() ||841			        kMissionData.eMissionType == CvTypes::getMISSION_CULTURE_BOMB() ||842			        kMissionData.eMissionType == CvTypes::getMISSION_FOUND_RELIGION() ||843			        kMissionData.eMissionType == CvTypes::getMISSION_GOLDEN_AGE() ||844			        kMissionData.eMissionType == CvTypes::getMISSION_LEAD() ||845			        kMissionData.eMissionType == CvTypes::getMISSION_DIE_ANIMATION() ||846			        kMissionData.eMissionType == CvTypes::getMISSION_SPREAD_RELIGION() ||847			        kMissionData.eMissionType == CvTypes::getMISSION_ENHANCE_RELIGION() ||848			        kMissionData.eMissionType == CvTypes::getMISSION_REMOVE_HERESY() ||849					kMissionData.eMissionType == CvTypes::getMISSION_ESTABLISH_TRADE_ROUTE() ||850					kMissionData.eMissionType == CvTypes::getMISSION_PLUNDER_TRADE_ROUTE() ||851					kMissionData.eMissionType == CvTypes::getMISSION_GREAT_WORK() ||852					kMissionData.eMissionType == CvTypes::getMISSION_CHANGE_TRADE_UNIT_HOME_CITY() ||853					kMissionData.eMissionType == CvTypes::getMISSION_SELL_EXOTIC_GOODS() ||854					kMissionData.eMissionType == CvTypes::getMISSION_GIVE_POLICIES() ||855					kMissionData.eMissionType == CvTypes::getMISSION_ONE_SHOT_TOURISM() ||856					kMissionData.eMissionType == CvTypes::getMISSION_CHANGE_ADMIRAL_PORT())857			{858				bDone = true;859			}860861			else if(kMissionData.eMissionType == CvTypes::getMISSION_WAIT_FOR())862			{863				CvUnit* pkWaitingFor = GET_PLAYER((PlayerTypes)kMissionData.iData1).getUnit(kMissionData.iData2);864				if(!pkWaitingFor || !pkWaitingFor->IsBusy())865				{866					bDone = true;867				}868				else869				{870					// Set the mission timer to 1 so we will get another UpdateMission call871					hUnit->ChangeMissionTimer(1);872				}873			}874875#if defined(MOD_EVENTS_CUSTOM_MISSIONS)876			else if (MOD_EVENTS_CUSTOM_MISSIONS) {877				if (GAMEEVENTINVOKE_TESTANY(GAMEEVENT_CustomMissionCompleted, hUnit->getOwner(), hUnit->GetID(), kMissionData.eMissionType, kMissionData.iData1, kMissionData.iData2, kMissionData.iFlags, kMissionData.iPushTurn) == GAMEEVENTRETURN_TRUE) {878					bDone = true;879				}880			}881#endif882		}883884		if(HeadMissionQueueNode(kMissionQueue) != NULL)885		{886			// if there is an action, if it's done or there are not moves left, and a player is watching, watch the movement887			if(bAction && (bDone || !hUnit->canMove()) && hUnit->plot()->isVisibleToWatchingHuman())888			{889				UpdateMissionTimer(hUnit, iSteps);890891				if(hUnit->ShowMoves() && GC.getGame().getActivePlayer() != NO_PLAYER && hUnit->getOwner() != GC.getGame().getActivePlayer() && hUnit->plot()->isActiveVisible(false))892				{893					auto_ptr<ICvPlot1> pDllPlot = GC.WrapPlotPointer(hUnit->plot());894					GC.GetEngineUserInterface()->lookAt(pDllPlot.get(), CAMERALOOKAT_NORMAL);895				}896			}897898			if(bDone)899			{900				if(hUnit->IsWork())901				{902					auto_ptr<ICvUnit1> pDllUnit(new CvDllUnit(hUnit.pointer()));903					gDLL->GameplayUnitWork(pDllUnit.get(), -1);904				}905906				if(hUnit->GetMissionTimer() == 0 && !hUnit->isInCombat())	// Was hUnit->IsBusy(), but its ok to clear the mission if the unit is just completing a move visualization907				{908					if(hUnit->getOwner() == GC.getGame().getActivePlayer() && hUnit->IsSelected())909					{910						const MissionData& kMissionData = *HeadMissionQueueNode(kMissionQueue);911912						if((kMissionData.eMissionType == CvTypes::getMISSION_MOVE_TO()) ||913						        (kMissionData.eMissionType == CvTypes::getMISSION_ROUTE_TO()) ||914						        (kMissionData.eMissionType == CvTypes::getMISSION_MOVE_TO_UNIT()))915						{916							// How long does the camera wait before jumping to the next item?917							int iCameraTime;918919							if(GET_PLAYER(hUnit->getOwner()).isOption(PLAYEROPTION_QUICK_MOVES))920							{921								iCameraTime = 1;922							}923							// If our move revealed a Plot, camera jumps slower924							else if(GC.GetEngineUserInterface()->IsSelectedUnitRevealingNewPlots())925							{926								iCameraTime = 10;927							}928							// No plots revealed by this move, go quicker929							else930							{931								iCameraTime = 5;932							}933934							GC.GetEngineUserInterface()->changeCycleSelectionCounter(iCameraTime);935						}936#if defined(MOD_EVENTS_CUSTOM_MISSIONS)937						else if (MOD_EVENTS_CUSTOM_MISSIONS) {938							int iCameraTime = 0;939							if (GAMEEVENTINVOKE_VALUE(iCameraTime, GAMEEVENT_CustomMissionCameraTime, hUnit->getOwner(), hUnit->GetID(), kMissionData.eMissionType, kMissionData.iData1, kMissionData.iData2, kMissionData.iFlags, kMissionData.iPushTurn) == GAMEEVENTRETURN_VALUE) {940								if (iCameraTime > 0 && iCameraTime <= 10) {941									if(GET_PLAYER(hUnit->getOwner()).isOption(PLAYEROPTION_QUICK_MOVES)) {942										iCameraTime = 1;943									}944945									GC.GetEngineUserInterface()->changeCycleSelectionCounter(iCameraTime);946								}947							}948						}949#endif950					}951952					if(hUnit->m_unitMoveLocs.size() > 0)953					{954						hUnit->PublishQueuedVisualizationMoves();955					}956957					DeleteMissionQueueNode(hUnit, HeadMissionQueueNode(hUnit->m_missionQueue));958				}959			}960			else961			{962				// if we can still act, process the mission again963				if(hUnit->canMove() && !hUnit->IsDoingPartialMove())964				{965					iSteps++;966					bContinueMissionRestart = true;	// keep looping967				}968				else if(!hUnit->IsBusy() && hUnit->getOwner() == GC.getGame().getActivePlayer() && hUnit->IsSelected())969				{970					GC.GetEngineUserInterface()->changeCycleSelectionCounter(1);971				}972			}973		}974	}975ContinueMissionExit: // goto destination to clean up the flag value976	hUnit->m_iFlags =hUnit-> m_iFlags & ~CvUnit::UNITFLAG_EVALUATING_MISSION;977	GC.getGame().SetCombatWarned(false);978}

Calls

Called by