Bazaar developer reference

CvGame::updateMoves()

Defined in
CvGame.cpp, line 8292
Class
CvGame
Hook name
CivHooks::CvGame_updateMoves

Hand-placed hooks inside

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

Hooks

static bool BeforeCvGame_updateMoves(CvGame* self);static void AfterCvGame_updateMoves(CvGame* self);static void ReplaceCvGame_updateMoves(void (*original)(CvGame*), CvGame* self);CivRegisterBefore<CivHooks::CvGame_updateMoves>(host, &BeforeCvGame_updateMoves);CivRegisterAfter<CivHooks::CvGame_updateMoves>(host, &AfterCvGame_updateMoves);CivRegisterReplace<CivHooks::CvGame_updateMoves>(host, &ReplaceCvGame_updateMoves);

Code

8292void CvGame::updateMoves()8293{8294	UnitHandle pLoopUnit;8295	pLoopUnit.ignoreDestruction(true); // It's acceptable for the unit to become invalid during AutoMoves()82968297	int iLoop;8298	int iI;82998300	static bool processPlayerAutoMoves = false;83018302	// Process all AI first, then process players.8303	// Processing of the AI 'first' only occurs when the AI are activated first8304	// in doTurn, when MPSIMULTANEOUS_TURNS is set.  If the turns are sequential,8305	// only one human or AI is active at one time and this will process them in order.8306	FStaticVector<PlayerTypes, MAX_PLAYERS, true, c_eCiv5GameplayDLL, 0> playersToProcess;83078308	for(iI = 0; iI < MAX_PLAYERS; iI++)8309	{8310		CvPlayer& player = GET_PLAYER((PlayerTypes)iI);8311		if(player.isAlive() && player.isTurnActive() && !player.isHuman())8312		{8313			playersToProcess.push_back(static_cast<PlayerTypes>(iI));8314			processPlayerAutoMoves = false;8315			// Notice the break.  Even if there is more than one AI with an active turn, we do them sequentially.8316			break;8317		}8318	}831983208321	int currentTurn = getGameTurn();8322	bool activatePlayers = playersToProcess.empty() && m_lastTurnAICivsProcessed != currentTurn;8323	// If no AI with an active turn, check humans.8324	if(playersToProcess.empty())8325	{8326		SetLastTurnAICivsProcessed();8327		if(gDLL->allAICivsProcessedThisTurn())8328		{//everyone is finished processing the AI civs.8329			PlayerTypes eActivePlayer = getActivePlayer();8330			if(eActivePlayer != NO_PLAYER && CvPreGame::slotStatus(eActivePlayer) == SS_OBSERVER)8331			{//if the active player is an observer, send a turn complete so we don't hold up the game.8332				//We wait until allAICivsProcessedThisTurn to prevent a race condition where an observer could send turn complete,8333				//before all clients have cleared the netbarrier locally.8334#if !defined(NO_ACHIEVEMENTS)8335				CvPlayer& kActivePlayer = GET_PLAYER(eActivePlayer);8336				kActivePlayer.GetPlayerAchievements().EndTurn();8337#endif8338				gDLL->sendTurnComplete();8339#if !defined(NO_ACHIEVEMENTS)8340				CvAchievementUnlocker::EndTurn();8341#endif8342			}83438344			if(!processPlayerAutoMoves)8345			{8346				if(!GC.getGame().isOption(GAMEOPTION_DYNAMIC_TURNS) && GC.getGame().isOption(GAMEOPTION_SIMULTANEOUS_TURNS))8347				{//fully simultaneous turns.8348					// All humans must be ready for auto moves8349					bool readyForAutoMoves = true;8350					for(iI = 0; iI < MAX_PLAYERS; iI++)8351					{8352						CvPlayer& player = GET_PLAYER((PlayerTypes)iI);8353						if(player.isHuman() && !player.isObserver() && !player.isAutoMoves())8354							readyForAutoMoves = false;8355					}8356					processPlayerAutoMoves = readyForAutoMoves;8357				}8358				else8359					processPlayerAutoMoves = true;8360			}83618362			for(iI = 0; iI < MAX_PLAYERS; iI++)8363			{8364				CvPlayer& player = GET_PLAYER((PlayerTypes)iI);83658366				player.checkInitialTurnAIProcessed();8367				if(player.isTurnActive() && player.isHuman())8368				{8369					playersToProcess.push_back(static_cast<PlayerTypes>(iI));8370				}8371			}8372		}8373	}83748375	FStaticVector<PlayerTypes, MAX_PLAYERS, true, c_eCiv5GameplayDLL, 0>::const_iterator i;83768377	for(i = playersToProcess.begin(); i != playersToProcess.end(); ++i)8378	{8379		GC.getPathFinder().ForceReset();8380		CvPlayer& player = GET_PLAYER((PlayerTypes)*i);8381		int iReadyUnitsBeforeMoves = player.GetCountReadyUnits();83828383		if(player.isAlive())8384		{8385			bool needsAIUpdate = player.hasUnitsThatNeedAIUpdate();8386			if(player.isTurnActive() || needsAIUpdate)8387			{8388				if(!(player.isAutoMoves()) || needsAIUpdate)8389				{8390					if(needsAIUpdate || !player.isHuman())8391					{8392						player.AI_unitUpdate();83938394						NET_MESSAGE_DEBUG_OSTR_ALWAYS("UpdateMoves() : player.AI_unitUpdate() called for player " << player.GetID() << " " << player.getName()); 8395					}83968397					int iReadyUnitsNow = player.GetCountReadyUnits();83988399					// Was a move completed, if so save off which turn slice this was8400					if(iReadyUnitsNow < iReadyUnitsBeforeMoves)8401					{8402						player.SetLastSliceMoved(m_iTurnSlice);8403					}84048405					if(!(player.isHuman()) && !(player.hasBusyUnitOrCity()))8406					{8407						if(iReadyUnitsNow == 0)8408						{8409							player.setAutoMoves(true);8410							NET_MESSAGE_DEBUG_OSTR_ALWAYS("UpdateMoves() : player.setAutoMoves(true) called for player " << player.GetID() << " " << player.getName()); 8411						}8412						else8413						{8414							const CvUnit* pReadyUnit = player.GetFirstReadyUnit();8415							if(pReadyUnit && !player.GetTacticalAI()->IsInQueuedAttack(pReadyUnit))8416							{8417								int iWaitTime = 100;8418								if(!isNetworkMultiPlayer())8419								{8420									iWaitTime = 10;8421								}8422								if(m_iTurnSlice - player.GetLastSliceMoved() > iWaitTime)8423								{8424									CvUnitEntry* entry = GC.getUnitInfo(pReadyUnit->getUnitType());8425									if(entry)8426									{8427										CvString strTemp = entry->GetDescription();8428										CvString szAssertMessage;8429										szAssertMessage.Format(8430										    "GAME HANG - Please show Ed and send save. Stuck units will have their turn ended so game can advance. [DETAILS: Player %i %s. First stuck unit is %s at (%d, %d)]",8431										    player.GetID(), player.getName(), strTemp.GetCString(), pReadyUnit->getX(), pReadyUnit->getY());8432										CvAssertMsg(false, szAssertMessage);8433										NET_MESSAGE_DEBUG_OSTR_ALWAYS(szAssertMessage);8434									}8435									player.EndTurnsForReadyUnits();8436								}8437							}8438						}8439					}8440				}8441844284438444				if(player.isAutoMoves() && (!player.isHuman() || processPlayerAutoMoves))8445				{8446					bool bRepeatAutomoves = false;8447					int iRepeatPassCount = 2;	// Prevent getting stuck in a loop8448					do8449					{8450						for(pLoopUnit = player.firstUnit(&iLoop); pLoopUnit; pLoopUnit = player.nextUnit(&iLoop))8451						{8452							CvString tempString;8453							getMissionAIString(tempString, pLoopUnit->GetMissionAIType());8454							NET_MESSAGE_DEBUG_OSTR_ALWAYS("UpdateMoves() : player " << player.GetID() << " " << player.getName()8455																							<< " running AutoMission (" << tempString << ") on " 8456																							<< pLoopUnit->getName() << " id=" << pLoopUnit->GetID());84578458							pLoopUnit->AutoMission();84598460							// Does the unit still have movement points left over?8461							if(player.isHuman() && CvUnitMission::HasCompletedMoveMission(pLoopUnit) && pLoopUnit->canMove() && !pLoopUnit->IsDoingPartialMove() && !pLoopUnit->IsAutomated())8462							{8463								if(player.isEndTurn())8464								{8465									bRepeatAutomoves = true;	// Do another pass.8466									NET_MESSAGE_DEBUG_OSTR_ALWAYS("UpdateMoves() : player " << player.GetID() << " " << player.getName()8467																									<< " AutoMission did not use up all movement points for " 8468																									<< pLoopUnit->getName() << " id=" << pLoopUnit->GetID());84698470									if(player.isLocalPlayer() && gDLL->sendTurnUnready())8471										player.setEndTurn(false);8472#if defined(CIV_HOST_API)8473									else if(!player.isLocalPlayer() && CivHandIsPluginControlledPlayer(player.GetID()))hand-placed hook: IsPluginControlledPlayer8474										player.setEndTurn(false);8475#endif8476								}8477							}84788479							// slewis sez:84808481							// This is a short-term solution to a problem where a unit with an auto-mission (a queued, multi-turn) move order cannot reach its destination, but8482							//  does not re-enter the "need order" list because this code is processed at the end of turns. The result is that the player could easily "miss" moving8483							//  the unit this turn because it displays "next turn" rather than "skip unit turn" and the unit is not added to the "needs orders" list.8484							// To correctly fix this problem, we would need some way to determine if any of the auto-missions are invalid before the player can end the turn and8485							//  activate the units that have a problem.8486							// The problem with evaluating this is that, with one unit per tile, we don't know what is a valid move until other units have moved.8487							// (For example, if one unit was to follow another, we would want the unit in the lead to move first and then have the following unit move, in order8488							//  to prevent the following unit from constantly waking up because it can't move into the next tile. This is currently not supported.)84898490							// This short-term solution will reactivate a unit after the player clicks "next turn". It will appear strange, because the player will be asked to move8491							// a unit after they clicked "next turn", but it is to give the player a chance to move all of their units.84928493							// jrandall sez: In MP matches, let's not OOS or stall the game.8494							if(!isNetworkMultiPlayer() && !isOption(GAMEOPTION_END_TURN_TIMER_ENABLED))8495							{8496								if(pLoopUnit && player.isEndTurn() && pLoopUnit->GetLengthMissionQueue() == 0 && pLoopUnit->GetActivityType() == ACTIVITY_AWAKE && pLoopUnit->canMove() && !pLoopUnit->IsDoingPartialMove() && !pLoopUnit->IsAutomated())8497								{8498									if(IsForceEndingTurn())8499									{8500										SetForceEndingTurn(false);8501									}8502									else8503									{8504										CvAssertMsg(GC.getGame().getActivePlayer() == player.GetID(), "slewis - We should not need to resolve ambiguous end turns for the AI or remotely.");8505										if(player.isLocalPlayer() && gDLL->sendTurnUnready())8506											player.setEndTurn(false);8507#if defined(CIV_HOST_API)8508										else if(!player.isLocalPlayer() && CivHandIsPluginControlledPlayer(player.GetID()))hand-placed hook: IsPluginControlledPlayer8509											player.setEndTurn(false);8510#endif8511									}8512								}8513							}8514						}8515					}8516					while(bRepeatAutomoves && iRepeatPassCount--);85178518					// check if the (for now human) player is overstacked and move the units8519					//if (player.isHuman())85208521					// slewis - I changed this to only be the AI because human players should have the tools to deal with this now8522					if(!player.isHuman())8523					{8524						for(pLoopUnit = player.firstUnit(&iLoop); pLoopUnit; pLoopUnit = player.nextUnit(&iLoop))8525						{8526							if(pLoopUnit)8527							{8528								bool bMoveMe  = false;8529								int iNumTurnsFortified = pLoopUnit->getFortifyTurns();8530								IDInfo* pUnitNodeInner;8531								pUnitNodeInner = pLoopUnit->plot()->headUnitNode();8532								while(pUnitNodeInner != NULL && !bMoveMe)8533								{8534									CvUnit* pLoopUnitInner = ::getUnit(*pUnitNodeInner);8535									if(pLoopUnitInner && pLoopUnit != pLoopUnitInner)8536									{8537										if(pLoopUnit->getOwner() == pLoopUnitInner->getOwner())	// Could be a dying Unit from another player here8538										{8539											if(pLoopUnit->AreUnitsOfSameType(*pLoopUnitInner) && pLoopUnit->plot()->getNumFriendlyUnitsOfType(pLoopUnit.pointer()) > GC.getPLOT_UNIT_LIMIT())8540											{8541												if(pLoopUnitInner->getFortifyTurns() >= iNumTurnsFortified)8542												{8543													bMoveMe = true;8544												}8545											}8546										}8547									}8548									pUnitNodeInner = pLoopUnit->plot()->nextUnitNode(pUnitNodeInner);8549								}8550								if(bMoveMe)8551								{8552									if (!pLoopUnit->jumpToNearestValidPlotWithinRange(1))8553										pLoopUnit->kill(false);8554									break;8555								}8556								pLoopUnit->doDelayedDeath();8557							}8558						}8559					}85608561					// If we completed the processing of the auto-moves, flag it.8562					if(player.isEndTurn() || !player.isHuman())8563					{8564						player.setProcessedAutoMoves(true);8565					}8566				}85678568				// KWG: This code should go into CheckPlayerTurnDeactivate8569				if(!player.isEndTurn() && gDLL->HasReceivedTurnComplete(player.GetID()) && player.isHuman() /* && (isNetworkMultiPlayer() || (!isNetworkMultiPlayer() && player.GetID() != getActivePlayer())) */)8570				{8571					if(!player.hasBusyUnitOrCity())8572					{8573						player.setEndTurn(true);8574						if(player.isEndTurn())8575						{//If the player's turn ended, indicate it in the log.  We only do so when the end turn state has changed to prevent useless log spamming in multiplayer. 8576							NET_MESSAGE_DEBUG_OSTR_ALWAYS("UpdateMoves() : player.setEndTurn(true) called for player " << player.GetID() << " " << player.getName());8577						}8578					}8579					else8580					{8581						if(!player.hasBusyUnitUpdatesRemaining())8582						{8583							NET_MESSAGE_DEBUG_OSTR_ALWAYS("Received turn complete for player "  << player.GetID() << " " << player.getName() << " but there is a busy unit. Forcing the turn to advance");8584							player.setEndTurn(true);8585						}8586					}8587				}8588			}8589		}8590	}85918592	if(activatePlayers)8593	{8594		if (isOption(GAMEOPTION_DYNAMIC_TURNS) || isOption(GAMEOPTION_SIMULTANEOUS_TURNS))8595		{//Activate human players who are playing simultaneous turns now that we've finished moves for the AI.8596			// KWG: This code should go into CheckPlayerTurnDeactivate8597			for(iI = 0; iI < MAX_PLAYERS; iI++)8598			{8599				CvPlayer& player = GET_PLAYER((PlayerTypes)iI);8600				if(!player.isTurnActive() && player.isHuman() && player.isAlive() && player.isSimultaneousTurns())8601				{8602					player.setTurnActive(true);8603				}8604			}8605		}8606	}8607}

Calls

Called by