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
BaseVector::beginBaseVector::emptyBaseVector::endCivHandIsPluginControlledPlayer- CvAStar::ForceReset()
- CvBaseInfo::GetDescription() const
- CvGame::getActivePlayer() const
- CvGame::getGameTurn()
- CvGame::IsForceEndingTurn() const
- CvGame::isNetworkMultiPlayer() const
- CvGame::isOption(const char *) const
- CvGame::isOption(GameOptionTypes) const
- CvGame::SetForceEndingTurn(bool)
- CvGame::SetLastTurnAICivsProcessed()
- CvGlobals::getDLLIFace()
- CvGlobals::getGame()
- CvGlobals::getPathFinder()
- CvGlobals::getPLOT_UNIT_LIMIT()
- CvGlobals::getUnitInfo(UnitTypes)
- CvPlayer::AI_unitUpdate()
- CvPlayer::checkInitialTurnAIProcessed()
- CvPlayer::EndTurnsForReadyUnits()
- CvPlayer::firstUnit(int *, bool)
- CvPlayer::GetCountReadyUnits() const
- CvPlayer::GetFirstReadyUnit() const
- CvPlayer::GetID() const
- CvPlayer::GetLastSliceMoved() const
- CvPlayer::getName() const
- CvPlayer::GetTacticalAI() const
- CvPlayer::hasBusyUnitOrCity() const
- CvPlayer::hasBusyUnitUpdatesRemaining() const
- CvPlayer::hasUnitsThatNeedAIUpdate() const
- CvPlayer::isAlive() const
- CvPlayer::isAutoMoves() const
- CvPlayer::isEndTurn() const
- CvPlayer::isHuman() const
- CvPlayer::isLocalPlayer() const
- CvPlayer::isObserver() const
- CvPlayer::isSimultaneousTurns() const
- CvPlayer::isTurnActive() const
- CvPlayer::nextUnit(int *, bool)
- CvPlayer::setAutoMoves(bool)
- CvPlayer::setEndTurn(bool)
- CvPlayer::SetLastSliceMoved(int)
- CvPlayer::setProcessedAutoMoves(bool)
- CvPlayer::setTurnActive(bool, bool)
- CvPlayerAI::getPlayer(PlayerTypes)
- CvPlot::getNumFriendlyUnitsOfType(const CvUnit *, bool) const
- CvPlot::headUnitNode()
- CvPlot::nextUnitNode(IDInfo *)
- CvPreGame::slotStatus(PlayerTypes)
CvString::CvStringCvString::CvStringCvString::FormatCvString::GetCString- CvTacticalAI::IsInQueuedAttack(const CvUnit *)
- CvUnit::AreUnitsOfSameType(const CvUnit &, const bool) const
- CvUnit::AutoMission()
- CvUnit::canMove() const
- CvUnit::doDelayedDeath()
- CvUnit::GetActivityType() const
- CvUnit::getFortifyTurns() const
- CvUnit::GetID() const
- CvUnit::GetLengthMissionQueue() const
- CvUnit::GetMissionAIType()
- CvUnit::getName() const
- CvUnit::getOwner() const
- CvUnit::getUnitType() const
- CvUnit::getX() const
- CvUnit::getY() const
- CvUnit::IsAutomated() const
- CvUnit::IsDoingPartialMove() const
- CvUnit::jumpToNearestValidPlotWithinRange(int)
- CvUnit::kill(bool, PlayerTypes)
- CvUnit::plot() const
- CvUnitMission::HasCompletedMoveMission(UnitHandle)
FObjectHandle::FObjectHandleFObjectHandle::FObjectHandleFObjectHandle::ignoreDestructionFObjectHandle::operator boolFObjectHandle::operator!=FObjectHandle::operator->FObjectHandle::operator=FObjectHandle::pointerFStaticVector::FStaticVectorFStaticVector::push_back- getMissionAIString(CvString &, MissionAITypes)
- getUnit(const IDInfo &)
ICvEngineUtility1::HasReceivedTurnCompleteICvEngineUtility1::netMessageDebugLogICvEngineUtility1::sendTurnCompleteICvEngineUtility1::sendTurnUnreadyICvEngineUtility3::allAICivsProcessedThisTurnstd::basic_ostream::operator<<std::basic_ostringstream::basic_ostringstreamstd::basic_ostringstream::strstd::operator<<std::operator<<