/* ------------------------------------------------------------------------------------------------------- © 1991-2012 Take-Two Interactive Software and its subsidiaries. Developed by Firaxis Games. Sid Meier's Civilization V, Civ, Civilization, 2K Games, Firaxis Games, Take-Two Interactive Software and their respective logos are all trademarks of Take-Two interactive Software, Inc. All other marks and trademarks are the property of their respective owners. All rights reserved. ------------------------------------------------------------------------------------------------------- */ #include "CvGameCoreDLLPCH.h" #include "ICvDLLUserInterface.h" #include "CvDiplomacyAIEnums.h" #include "CvDiplomacyAI.h" #include "CvGrandStrategyAI.h" #include "CvEconomicAI.h" #include "CvMilitaryAI.h" #include "CvMinorCivAI.h" #include "CvCitySpecializationAI.h" #include "CvDealClasses.h" #include "CvDealAI.h" #include "CvGameCoreUtils.h" #include "CvNotifications.h" #include "CvDiplomacyRequests.h" // must be included after all other headers #include "LintFree.h" #ifdef _MSC_VER # pragma warning ( disable : 4351 ) // default initialization of arrays #endif //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // CLASS: CvDiplomacyAI //! \brief Drives the diplomatic interaction of a player // //! Author: Jon Shafer // //! Key Attributes: //! - Object created by CvPlayer //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ CvDiplomacyAI::CvDiplomacyAI() { } CvDiplomacyAI::~CvDiplomacyAI(void) { Uninit(); } void CvDiplomacyAI::Uninit() { } // ************************************ // Initialization & Serialization // ************************************ /// Init & reset everything to default state void CvDiplomacyAI::Init(CvPlayer* pPlayer) { // Store off the pointer to the Player active for this game // Store the Player and Team IDs too because they're called about a million times m_pPlayer = pPlayer; m_eID = pPlayer->GetID(); SetTeam(pPlayer->getTeam()); // Personality Values m_iVictoryCompetitiveness = 5; m_iWonderCompetitiveness = 5; m_iMinorCivCompetitiveness = 5; m_iBoldness = 5; m_iDiploBalance = 5; m_iWarmongerHate = 5; m_iDoFWillingness = 5; m_iDenounceWillingness = 5; m_iWorkWithWillingness = 5; m_iWorkAgainstWillingness = 5; m_iLoyalty = 5; m_iForgiveness = 5; m_iNeediness = 5; m_iMeanness = 5; m_iChattiness = 5; for (int iI = 0; iI < NUM_CIV_APPROACHES; iI++) { m_aiMajorCivApproachBiases[iI] = 5; } m_iMinorCivWarBias = 5; m_iMinorCivHostileBias = 5; m_iMinorCivNeutralBias = 5; m_iMinorCivFriendlyBias = 5; // Key Players m_eMostValuableFriend = NO_PLAYER; m_eMostValuableAlly = NO_PLAYER; m_eBiggestCompetitor = NO_PLAYER; m_ePrimeLeagueAlly = NO_PLAYER; m_ePrimeLeagueCompetitor = NO_PLAYER; m_eDemandTargetPlayer = NO_PLAYER; m_eCSWarTarget = NO_PLAYER; m_eCSBullyTarget = NO_PLAYER; // Other Global Memory m_bWasHumanLastUpdate = false; m_bEndedFriendshipThisTurn = false; m_bUpdatedWarProgressThisTurn = false; m_iNumReevaluations = 0; m_bBackstabber = false; m_bCompetingForVictory = false; m_ePrimaryVictoryPursuit = NO_VICTORY_PURSUIT; m_eSecondaryVictoryPursuit = NO_VICTORY_PURSUIT; m_eCurrentVictoryPursuit = NO_VICTORY_PURSUIT; m_eStateAllWars = STATE_ALL_WARS_NEUTRAL; m_eTestStatement = NO_DIPLO_STATEMENT_TYPE; m_eTestToPlayer = NO_PLAYER; m_iTestStatementArg1 = 0; for (int iI = 0; iI < MAX_PLAYERS; iI++) { if (iI < MAX_MINOR_CIVS) { m_abWantToRouteToMinor[iI] = false; } if (iI < MAX_MAJOR_CIVS) { // Diplomatic Interactions for (int iJ = 0; iJ < NUM_DIPLO_STATEMENT_TYPES; iJ++) { m_aaiTurnStatementLastSent[iI][iJ] = -1; } for (int iJ = 0; iJ < MAX_MINOR_CIVS; iJ++) { m_aabSentAttackMessageToMinorCivProtector[iI][iJ] = false; } // Opinion & Approach m_aeCivOpinion[iI] = CIV_OPINION_NEUTRAL; m_aiCachedOpinionWeight[iI] = 0; m_aeCivStrategicApproach[iI] = CIV_APPROACH_NEUTRAL; m_aeCachedSurfaceApproach[iI] = -1; for (int iJ = 0; iJ < NUM_CIV_APPROACHES; iJ++) { m_aaiApproachValues[iI][iJ] = 0; m_aaiStrategicApproachValues[iI][iJ] = 0; } // Planning Exchanges m_abMajorCompetitor[iI] = false; m_abStrategicTradePartner[iI] = false; m_abWantsDoFWithPlayer[iI] = false; m_abWantsDefensivePactWithPlayer[iI] = false; m_abWantsToEndDoFWithPlayer[iI] = false; m_abWantsToEndDefensivePactWithPlayer[iI] = false; m_abWantsResearchAgreementWithPlayer[iI] = false; // Exchanges m_aiDoFAcceptedTurn[iI] = -1; m_aeDoFType[iI] = DOF_TYPE_NEW; m_aiDenouncedPlayerTurn[iI] = -1; m_abCantMatchDeal[iI] = false; m_aiNumDemandsMade[iI] = 0; m_aiDemandMadeTurn[iI] = -1; m_aiDemandTooSoonNumTurns[iI] = -1; m_aiNumConsecutiveDemandsTheyAccepted[iI] = 0; m_aiDemandAcceptedTurn[iI] = -1; m_aiTradeValue[iI] = 0; m_aiCommonFoeValue[iI] = 0; m_aiAssistValue[iI] = 0; // Coop Wars for (int iJ = 0; iJ < MAX_MAJOR_CIVS; iJ++) { m_aaeCoopWarState[iI][iJ] = NO_COOP_WAR_STATE; m_aaiCoopWarStateChangeTurn[iI][iJ] = -1; } m_aiCoopWarAgreementScore[iI] = 0; // War m_aiNumWarsDeclaredOnUs[iI] = 0; m_aiCivilianKillerValue[iI] = 0; // Peace m_aePeaceTreatyWillingToOffer[iI] = NO_PEACE_TREATY_TYPE; m_aePeaceTreatyWillingToAccept[iI] = NO_PEACE_TREATY_TYPE; // Backstabbing Penalties m_abUntrustworthyFriend[iI] = false; m_abEverBackstabbedBy[iI] = false; m_aiDoFBrokenTurn[iI] = -1; m_aiFriendDenouncedUsTurn[iI] = -1; m_aiFriendDeclaredWarOnUsTurn[iI] = -1; // Warmongering Penalties m_aiNumMinorsAttacked[iI] = 0; m_aiNumMinorsConquered[iI] = 0; m_aiNumMajorsAttacked[iI] = 0; m_aiNumMajorsConquered[iI] = 0; m_aiWarmongerAmountTimes100[iI] = 0; // Aggressive Postures m_aePlotBuyingAggressivePosture[iI] = AGGRESSIVE_POSTURE_NONE; // Dispute Levels m_abEndgameAggressiveTo[iI] = false; m_abRecklessExpander[iI] = false; m_abWonderSpammer[iI] = false; m_aeVictoryDisputeLevel[iI] = DISPUTE_LEVEL_NONE; m_aeVictoryBlockLevel[iI] = BLOCK_LEVEL_NONE; m_aeWonderDisputeLevel[iI] = DISPUTE_LEVEL_NONE; m_aeMinorCivDisputeLevel[iI] = DISPUTE_LEVEL_NONE; m_aeTechBlockLevel[iI] = BLOCK_LEVEL_NONE; m_aePolicyBlockLevel[iI] = BLOCK_LEVEL_NONE; // Threat Levels m_aeWarmongerThreat[iI] = THREAT_NONE; // PROMISES // Military Promise m_aeMilitaryPromiseState[iI] = NO_PROMISE_STATE; m_aiMilitaryPromiseTurn[iI] = -1; // Expansion Promise m_aeExpansionPromiseState[iI] = NO_PROMISE_STATE; m_aiExpansionPromiseTurn[iI] = -1; m_abAngryAboutExpansion[iI] = false; m_abEverRequestedExpansionPromise[iI] = false; // Border Promise m_aeBorderPromiseState[iI] = NO_PROMISE_STATE; m_aeBorderPromisePosture[iI] = AGGRESSIVE_POSTURE_NONE; m_aiBorderPromiseTurn[iI] = -1; m_abEverMadeBorderPromise[iI] = false; // Bully City-State Promise m_aeBullyCityStatePromiseState[iI] = NO_PROMISE_STATE; m_aiBullyCityStatePromiseTurn[iI] = -1; // Attack City-State Promise m_aeAttackCityStatePromiseState[iI] = NO_PROMISE_STATE; m_aiAttackCityStatePromiseTurn[iI] = -1; // Spy Promise m_aeSpyPromiseState[iI] = NO_PROMISE_STATE; m_aiSpyPromiseTurn[iI] = -1; // No Convert Promise m_aeNoConvertPromiseState[iI] = NO_PROMISE_STATE; m_aiNoConvertPromiseTurn[iI] = -1; m_abAskedNotToConvert[iI] = false; m_abEverConvertedCity[iI] = false; // No Digging Promise m_aeNoDiggingPromiseState[iI] = NO_PROMISE_STATE; m_aiNoDiggingPromiseTurn[iI] = -1; m_abAskedNotToDig[iI] = false; // Coop War Promise m_aiBrokenCoopWarPromiseTurn[iI] = -1; // END PROMISES // Event Flags m_abReturnedCapital[iI] = false; m_abReturnedHolyCity[iI] = false; m_abLiberatedCapital[iI] = false; m_abLiberatedHolyCity[iI] = false; m_abCapturedCapital[iI] = false; m_abCapturedHolyCity[iI] = false; m_abEverCapturedCapital[iI] = false; m_abEverCapturedHolyCity[iI] = false; m_abResurrectorAttackedUs[iI] = false; m_abEverSanctionedUs[iI] = false; m_abEverUnsanctionedUs[iI] = false; // # of times/points counters m_aiNumCitiesLiberated[iI] = 0; m_aiNumCitiesEverLiberated[iI] = 0; m_aiNumCiviliansReturnedToMe[iI] = 0; m_aiNumTimesIntrigueSharedBy[iI] = 0; m_aiNumLandmarksBuiltForMe[iI] = 0; m_aiTheyPlottedAgainstUs[iI] = 0; m_aiNumTradeRoutesPlundered[iI] = 0; m_aiNumWondersBeatenTo[iI] = 0; m_aiNumTimesCultureBombed[iI] = 0; m_aiTheyLoweredOurInfluence[iI] = 0; m_aiNumProtectedMinorsBullied[iI] = 0; m_aiNumProtectedMinorsAttacked[iI] = 0; m_aiNumProtectedMinorsKilled[iI] = 0; m_aiNegativeReligiousConversionPoints[iI] = 0; m_aiNumTimesRobbedBy[iI] = 0; m_aiPerformedCoupAgainstUs[iI] = 0; m_aiLikedTheirProposalValue[iI] = 0; m_aiSupportedOurProposalValue[iI] = 0; m_aiVotingHistoryScore[iI] = 0; m_aiSupportedOurHostingValue[iI] = 0; m_aiNegativeArchaeologyPoints[iI] = 0; m_aiArtifactsEverDugUp[iI] = 0; m_aiNumTimesNuked[iI] = 0; // Turn counters m_aiResurrectedOnTurn[iI] = -1; m_aiLiberatedCitiesTurn[iI] = -1; m_aiCiviliansReturnedToMeTurn[iI] = -1; m_aiIntrigueSharedTurn[iI] = -1; m_aiPlayerForgaveForSpyingTurn[iI] = -1; m_aiLandmarksBuiltForMeTurn[iI] = -1; m_aiPlottedAgainstUsTurn[iI] = -1; m_aiPlunderedTradeRouteTurn[iI] = -1; m_aiBeatenToWonderTurn[iI] = -1; m_aiLoweredOurInfluenceTurn[iI] = -1; m_aiSidedWithProtectedMinorTurn[iI] = -1; m_aiBulliedProtectedMinorTurn[iI] = -1; m_aiAttackedProtectedMinorTurn[iI] = -1; m_aiKilledProtectedMinorTurn[iI] = -1; m_aiReligiousConversionTurn[iI] = -1; m_aiTimesRobbedTurn[iI] = -1; m_aiPerformedCoupTurn[iI] = -1; m_aiStoleArtifactTurn[iI] = -1; m_aiWeLikedTheirProposalTurn[iI] = -1; m_aiWeDislikedTheirProposalTurn[iI] = -1; m_aiTheySupportedOurProposalTurn[iI] = -1; m_aiTheyFoiledOurProposalTurn[iI] = -1; m_aiTheySanctionedUsTurn[iI] = -1; m_aiTheyUnsanctionedUsTurn[iI] = -1; m_aiTheySupportedOurHostingTurn[iI] = -1; // Player-Specific Memory Values m_aeProtectedMinorBullied[iI] = NO_PLAYER; m_aeProtectedMinorAttacked[iI] = NO_PLAYER; m_aeProtectedMinorKilled[iI] = NO_PLAYER; // Guesses about other players' feelings towards us //m_aeOpinionTowardsUsGuess[iI] = CIV_OPINION_NEUTRAL; //m_aeApproachTowardsUsGuess[iI] = CIV_APPROACH_NEUTRAL; //m_aeApproachTowardsUsGuessCounter[iI] = 0; // C4DF Values m_aeShareApproachResponse[iI] = NO_SHARE_APPROACH_RESPONSE; m_aiHelpRequestAcceptedTurn[iI] = -1; m_aiHelpRequestTooSoonNumTurns[iI] = -1; m_aiVassalProtectValue[iI] = 0; m_aiPlayerVassalagePeacefullyRevokedTurn[iI] = -1; m_aiPlayerVassalageForcefullyRevokedTurn[iI] = -1; m_aiBrokeVassalAgreementTurn[iI] = -1; m_abMoveTroopsRequestAccepted[iI] = false; m_abOfferingGift[iI] = false; m_abOfferedGift[iI] = false; m_abMasterLiberatedMeFromVassalage[iI] = false; m_abVassalTaxRaised[iI] = false; m_abVassalTaxLowered[iI] = false; m_aiVassalGoldPerTurnTaxedSinceVassalStarted[iI] = 0; m_aiVassalGoldPerTurnCollectedSinceVassalStarted[iI] = 0; m_aTradePriority[iI] = 0.0f; } if (iI < MAX_CIV_PLAYERS) { // Opinion & Approach m_aeCivApproach[iI] = CIV_APPROACH_NEUTRAL; // War m_abSaneDiplomaticTarget[iI] = true; m_abPotentialWarTarget[iI] = false; m_abArmyInPlaceForAttack[iI] = false; m_abAggressor[iI] = false; m_aiNumWarsFought[iI] = 0; m_aeWarState[iI] = NO_WAR_STATE_TYPE; m_aiWarProgressScore[iI] = 0; // Aggressive Postures m_aeMilitaryAggressivePosture[iI] = AGGRESSIVE_POSTURE_NONE; // Dispute Levels m_aeLandDisputeLevel[iI] = DISPUTE_LEVEL_NONE; // Strength Assessments m_aeEconomicStrengthComparedToUs[iI] = STRENGTH_PATHETIC; m_aeMilitaryStrengthComparedToUs[iI] = STRENGTH_PATHETIC; m_aeRawMilitaryStrengthComparedToUs[iI] = STRENGTH_PATHETIC; m_aeTargetValue[iI] = TARGET_VALUE_CAKEWALK; m_aeRawTargetValue[iI] = TARGET_VALUE_CAKEWALK; m_abEasyTarget[iI] = false; } // Init this value for all players, Barbarians included m_aiNumCitiesCaptured[iI] = 0; } // Non-serialized memory m_bAvoidDeals = false; m_bIgnoreWarmonger = false; m_eVassalPlayerToLiberate = NO_PLAYER; // MOD_ACTIVE_DIPLOMACY m_aGreetPlayers.clear(); m_eDiploMode = DIPLO_ALL_PLAYERS; m_eTargetPlayer = NO_PLAYER; } template void CvDiplomacyAI::Serialize(DiplomacyAI& diplomacyAI, Visitor& visitor) { visitor(diplomacyAI.m_eID); visitor(diplomacyAI.m_eTeam); // Personality Values visitor(diplomacyAI.m_iVictoryCompetitiveness); visitor(diplomacyAI.m_iWonderCompetitiveness); visitor(diplomacyAI.m_iMinorCivCompetitiveness); visitor(diplomacyAI.m_iBoldness); visitor(diplomacyAI.m_iDiploBalance); visitor(diplomacyAI.m_iWarmongerHate); visitor(diplomacyAI.m_iDoFWillingness); visitor(diplomacyAI.m_iDenounceWillingness); visitor(diplomacyAI.m_iWorkWithWillingness); visitor(diplomacyAI.m_iWorkAgainstWillingness); visitor(diplomacyAI.m_iLoyalty); visitor(diplomacyAI.m_iForgiveness); visitor(diplomacyAI.m_iNeediness); visitor(diplomacyAI.m_iMeanness); visitor(diplomacyAI.m_iChattiness); visitor(diplomacyAI.m_aiMajorCivApproachBiases); visitor(diplomacyAI.m_iMinorCivWarBias); visitor(diplomacyAI.m_iMinorCivHostileBias); visitor(diplomacyAI.m_iMinorCivNeutralBias); visitor(diplomacyAI.m_iMinorCivFriendlyBias); // Key Players visitor(diplomacyAI.m_eMostValuableFriend); visitor(diplomacyAI.m_eMostValuableAlly); visitor(diplomacyAI.m_eBiggestCompetitor); visitor(diplomacyAI.m_ePrimeLeagueAlly); visitor(diplomacyAI.m_ePrimeLeagueCompetitor); visitor(diplomacyAI.m_eDemandTargetPlayer); visitor(diplomacyAI.m_eCSWarTarget); visitor(diplomacyAI.m_eCSBullyTarget); // Other Global Memory visitor(diplomacyAI.m_bWasHumanLastUpdate); visitor(diplomacyAI.m_bEndedFriendshipThisTurn); visitor(diplomacyAI.m_bUpdatedWarProgressThisTurn); visitor(diplomacyAI.m_iNumReevaluations); visitor(diplomacyAI.m_bBackstabber); visitor(diplomacyAI.m_bCompetingForVictory); visitor(diplomacyAI.m_ePrimaryVictoryPursuit); visitor(diplomacyAI.m_eSecondaryVictoryPursuit); visitor(diplomacyAI.m_eCurrentVictoryPursuit); visitor(diplomacyAI.m_eStateAllWars); // Diplomatic Interactions visitor(diplomacyAI.m_aaiTurnStatementLastSent); visitor(diplomacyAI.m_aabSentAttackMessageToMinorCivProtector); // Opinion & Approach visitor(diplomacyAI.m_aeCivOpinion); visitor(diplomacyAI.m_aiCachedOpinionWeight); visitor(diplomacyAI.m_aeCivApproach); visitor(diplomacyAI.m_aeCivStrategicApproach); visitor(diplomacyAI.m_aeCachedSurfaceApproach); visitor(diplomacyAI.m_aaiApproachValues); visitor(diplomacyAI.m_aaiStrategicApproachValues); // Minor Civs visitor(diplomacyAI.m_abWantToRouteToMinor); // Planning Exchanges visitor(diplomacyAI.m_abMajorCompetitor); visitor(diplomacyAI.m_abStrategicTradePartner); visitor(diplomacyAI.m_abWantsDoFWithPlayer); visitor(diplomacyAI.m_abWantsDefensivePactWithPlayer); visitor(diplomacyAI.m_abWantsToEndDoFWithPlayer); visitor(diplomacyAI.m_abWantsToEndDefensivePactWithPlayer); visitor(diplomacyAI.m_abWantsResearchAgreementWithPlayer); // Exchanges visitor(diplomacyAI.m_aiDoFAcceptedTurn); visitor(diplomacyAI.m_aeDoFType); visitor(diplomacyAI.m_aiDenouncedPlayerTurn); visitor(diplomacyAI.m_abCantMatchDeal); visitor(diplomacyAI.m_aiNumDemandsMade); visitor(diplomacyAI.m_aiDemandMadeTurn); visitor(diplomacyAI.m_aiDemandTooSoonNumTurns); visitor(diplomacyAI.m_aiNumConsecutiveDemandsTheyAccepted); visitor(diplomacyAI.m_aiDemandAcceptedTurn); visitor(diplomacyAI.m_aiTradeValue); visitor(diplomacyAI.m_aiCommonFoeValue); visitor(diplomacyAI.m_aiAssistValue); // Coop Wars visitor(diplomacyAI.m_aaeCoopWarState); visitor(diplomacyAI.m_aaiCoopWarStateChangeTurn); visitor(diplomacyAI.m_aiCoopWarAgreementScore); // War visitor(diplomacyAI.m_abSaneDiplomaticTarget); visitor(diplomacyAI.m_abPotentialWarTarget); visitor(diplomacyAI.m_abArmyInPlaceForAttack); visitor(diplomacyAI.m_abAggressor); visitor(diplomacyAI.m_aiNumWarsFought); visitor(diplomacyAI.m_aiNumWarsDeclaredOnUs); visitor(diplomacyAI.m_aiCivilianKillerValue); visitor(diplomacyAI.m_aiNumCitiesCaptured); visitor(diplomacyAI.m_aeWarState); visitor(diplomacyAI.m_aiWarProgressScore); // Peace visitor(diplomacyAI.m_aePeaceTreatyWillingToOffer); visitor(diplomacyAI.m_aePeaceTreatyWillingToAccept); // Backstabbing Penalties visitor(diplomacyAI.m_abUntrustworthyFriend); visitor(diplomacyAI.m_abEverBackstabbedBy); visitor(diplomacyAI.m_aiDoFBrokenTurn); visitor(diplomacyAI.m_aiFriendDenouncedUsTurn); visitor(diplomacyAI.m_aiFriendDeclaredWarOnUsTurn); // Warmongering Penalties visitor(diplomacyAI.m_aiNumMinorsAttacked); visitor(diplomacyAI.m_aiNumMinorsConquered); visitor(diplomacyAI.m_aiNumMajorsAttacked); visitor(diplomacyAI.m_aiNumMajorsConquered); visitor(diplomacyAI.m_aiWarmongerAmountTimes100); // Aggressive Postures visitor(diplomacyAI.m_aeMilitaryAggressivePosture); visitor(diplomacyAI.m_aePlotBuyingAggressivePosture); // Dispute Levels visitor(diplomacyAI.m_abEndgameAggressiveTo); visitor(diplomacyAI.m_abRecklessExpander); visitor(diplomacyAI.m_abWonderSpammer); visitor(diplomacyAI.m_aeLandDisputeLevel); visitor(diplomacyAI.m_aeVictoryDisputeLevel); visitor(diplomacyAI.m_aeVictoryBlockLevel); visitor(diplomacyAI.m_aeWonderDisputeLevel); visitor(diplomacyAI.m_aeMinorCivDisputeLevel); visitor(diplomacyAI.m_aeTechBlockLevel); visitor(diplomacyAI.m_aePolicyBlockLevel); // Threat Levels visitor(diplomacyAI.m_aeWarmongerThreat); // Strength Assessments visitor(diplomacyAI.m_aeEconomicStrengthComparedToUs); visitor(diplomacyAI.m_aeMilitaryStrengthComparedToUs); visitor(diplomacyAI.m_aeRawMilitaryStrengthComparedToUs); visitor(diplomacyAI.m_aeTargetValue); visitor(diplomacyAI.m_aeRawTargetValue); visitor(diplomacyAI.m_abEasyTarget); // PROMISES // Military Promise visitor(diplomacyAI.m_aeMilitaryPromiseState); visitor(diplomacyAI.m_aiMilitaryPromiseTurn); // Expansion Promise visitor(diplomacyAI.m_aeExpansionPromiseState); visitor(diplomacyAI.m_aiExpansionPromiseTurn); visitor(diplomacyAI.m_abAngryAboutExpansion); visitor(diplomacyAI.m_abEverRequestedExpansionPromise); // Border Promise visitor(diplomacyAI.m_aeBorderPromiseState); visitor(diplomacyAI.m_aeBorderPromisePosture); visitor(diplomacyAI.m_aiBorderPromiseTurn); visitor(diplomacyAI.m_abEverMadeBorderPromise); // Bully City-State Promise visitor(diplomacyAI.m_aeBullyCityStatePromiseState); visitor(diplomacyAI.m_aiBullyCityStatePromiseTurn); // Attack City-State Promise visitor(diplomacyAI.m_aeAttackCityStatePromiseState); visitor(diplomacyAI.m_aiAttackCityStatePromiseTurn); // Spy Promise visitor(diplomacyAI.m_aeSpyPromiseState); visitor(diplomacyAI.m_aiSpyPromiseTurn); // No Convert Promise visitor(diplomacyAI.m_aeNoConvertPromiseState); visitor(diplomacyAI.m_aiNoConvertPromiseTurn); visitor(diplomacyAI.m_abAskedNotToConvert); visitor(diplomacyAI.m_abEverConvertedCity); // No Digging Promise visitor(diplomacyAI.m_aeNoDiggingPromiseState); visitor(diplomacyAI.m_aiNoDiggingPromiseTurn); visitor(diplomacyAI.m_abAskedNotToDig); // Coop War Promise visitor(diplomacyAI.m_aiBrokenCoopWarPromiseTurn); // END PROMISES // Event Flags visitor(diplomacyAI.m_abReturnedCapital); visitor(diplomacyAI.m_abReturnedHolyCity); visitor(diplomacyAI.m_abLiberatedCapital); visitor(diplomacyAI.m_abLiberatedHolyCity); visitor(diplomacyAI.m_abCapturedCapital); visitor(diplomacyAI.m_abCapturedHolyCity); visitor(diplomacyAI.m_abEverCapturedCapital); visitor(diplomacyAI.m_abEverCapturedHolyCity); visitor(diplomacyAI.m_abResurrectorAttackedUs); visitor(diplomacyAI.m_abEverSanctionedUs); visitor(diplomacyAI.m_abEverUnsanctionedUs); // # of times/points counters visitor(diplomacyAI.m_aiNumCitiesLiberated); visitor(diplomacyAI.m_aiNumCitiesEverLiberated); visitor(diplomacyAI.m_aiNumCiviliansReturnedToMe); visitor(diplomacyAI.m_aiNumTimesIntrigueSharedBy); visitor(diplomacyAI.m_aiNumLandmarksBuiltForMe); visitor(diplomacyAI.m_aiTheyPlottedAgainstUs); visitor(diplomacyAI.m_aiNumTradeRoutesPlundered); visitor(diplomacyAI.m_aiNumWondersBeatenTo); visitor(diplomacyAI.m_aiNumTimesCultureBombed); visitor(diplomacyAI.m_aiTheyLoweredOurInfluence); visitor(diplomacyAI.m_aiNumProtectedMinorsBullied); visitor(diplomacyAI.m_aiNumProtectedMinorsAttacked); visitor(diplomacyAI.m_aiNumProtectedMinorsKilled); visitor(diplomacyAI.m_aiNegativeReligiousConversionPoints); visitor(diplomacyAI.m_aiNumTimesRobbedBy); visitor(diplomacyAI.m_aiPerformedCoupAgainstUs); visitor(diplomacyAI.m_aiLikedTheirProposalValue); visitor(diplomacyAI.m_aiSupportedOurProposalValue); visitor(diplomacyAI.m_aiVotingHistoryScore); visitor(diplomacyAI.m_aiSupportedOurHostingValue); visitor(diplomacyAI.m_aiNegativeArchaeologyPoints); visitor(diplomacyAI.m_aiArtifactsEverDugUp); visitor(diplomacyAI.m_aiNumTimesNuked); // Turn counters visitor(diplomacyAI.m_aiResurrectedOnTurn); visitor(diplomacyAI.m_aiLiberatedCitiesTurn); visitor(diplomacyAI.m_aiCiviliansReturnedToMeTurn); visitor(diplomacyAI.m_aiIntrigueSharedTurn); visitor(diplomacyAI.m_aiPlayerForgaveForSpyingTurn); visitor(diplomacyAI.m_aiLandmarksBuiltForMeTurn); visitor(diplomacyAI.m_aiPlottedAgainstUsTurn); visitor(diplomacyAI.m_aiPlunderedTradeRouteTurn); visitor(diplomacyAI.m_aiBeatenToWonderTurn); visitor(diplomacyAI.m_aiLoweredOurInfluenceTurn); visitor(diplomacyAI.m_aiSidedWithProtectedMinorTurn); visitor(diplomacyAI.m_aiBulliedProtectedMinorTurn); visitor(diplomacyAI.m_aiAttackedProtectedMinorTurn); visitor(diplomacyAI.m_aiKilledProtectedMinorTurn); visitor(diplomacyAI.m_aiReligiousConversionTurn); visitor(diplomacyAI.m_aiTimesRobbedTurn); visitor(diplomacyAI.m_aiPerformedCoupTurn); visitor(diplomacyAI.m_aiStoleArtifactTurn); visitor(diplomacyAI.m_aiWeLikedTheirProposalTurn); visitor(diplomacyAI.m_aiWeDislikedTheirProposalTurn); visitor(diplomacyAI.m_aiTheySupportedOurProposalTurn); visitor(diplomacyAI.m_aiTheyFoiledOurProposalTurn); visitor(diplomacyAI.m_aiTheySanctionedUsTurn); visitor(diplomacyAI.m_aiTheyUnsanctionedUsTurn); visitor(diplomacyAI.m_aiTheySupportedOurHostingTurn); // Player-Specific Memory visitor(diplomacyAI.m_aeProtectedMinorBullied); visitor(diplomacyAI.m_aeProtectedMinorAttacked); visitor(diplomacyAI.m_aeProtectedMinorKilled); // GUESSES // Guesses about other players' feelings towards us //visitor(diplomacyAI.m_aeOpinionTowardsUsGuess); //visitor(diplomacyAI.m_aeApproachTowardsUsGuess); //visitor(diplomacyAI.m_aiApproachTowardsUsGuessCounter); // C4DF Values visitor(diplomacyAI.m_aeShareApproachResponse); visitor(diplomacyAI.m_aiHelpRequestAcceptedTurn); visitor(diplomacyAI.m_aiHelpRequestTooSoonNumTurns); visitor(diplomacyAI.m_aiVassalProtectValue); visitor(diplomacyAI.m_aiPlayerVassalagePeacefullyRevokedTurn); visitor(diplomacyAI.m_aiPlayerVassalageForcefullyRevokedTurn); visitor(diplomacyAI.m_aiBrokeVassalAgreementTurn); visitor(diplomacyAI.m_abMoveTroopsRequestAccepted); visitor(diplomacyAI.m_abOfferingGift); visitor(diplomacyAI.m_abOfferedGift); visitor(diplomacyAI.m_abMasterLiberatedMeFromVassalage); visitor(diplomacyAI.m_abVassalTaxRaised); visitor(diplomacyAI.m_abVassalTaxLowered); visitor(diplomacyAI.m_aiVassalGoldPerTurnTaxedSinceVassalStarted); visitor(diplomacyAI.m_aiVassalGoldPerTurnCollectedSinceVassalStarted); } /// Serialization read void CvDiplomacyAI::Read(FDataStream& kStream) { CvStreamLoadVisitor serialVisitor(kStream); CvDiplomacyAI::Serialize(*this, serialVisitor); } /// Serialization write void CvDiplomacyAI::Write(FDataStream& kStream) const { CvStreamSaveVisitor serialVisitor(kStream); CvDiplomacyAI::Serialize(*this, serialVisitor); } FDataStream& operator>>(FDataStream& stream, CvDiplomacyAI& diplomacyAI) { diplomacyAI.Read(stream); return stream; } FDataStream& operator<<(FDataStream& stream, const CvDiplomacyAI& diplomacyAI) { diplomacyAI.Write(stream); return stream; } // ----------------------------------------------------------------------------------------------- void CvDiplomacyAI::update() { if (!GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { if (!m_aGreetPlayers.empty()) { PlayerTypes eActivePlayer = GC.getGame().getActivePlayer(); // In out list? PlayerTypesArray::iterator itr = std::find(m_aGreetPlayers.begin(), m_aGreetPlayers.end(), eActivePlayer); if (itr != m_aGreetPlayers.end()) { m_aGreetPlayers.erase(itr); const char* szText = GetDiploStringForMessage(DIPLO_MESSAGE_INTRO); if (szText) { CvDiplomacyRequests::SendRequest(GetID(), eActivePlayer, DIPLO_UI_STATE_DEFAULT_ROOT, szText, LEADERHEAD_ANIM_INTRO); } } } } } // ----------------------------------------------------------------------------------------------- /// Player slot state changed (player was killed or switched between AI and human status) void CvDiplomacyAI::SlotStateChange() { const PlayerTypes eID = (PlayerTypes)GetID(); // Player was killed if (!GetPlayer()->isAlive()) { CancelAllCoopWars(); // Reset global memory SetMostValuableFriend(NO_PLAYER); SetMostValuableAlly(NO_PLAYER); SetBiggestCompetitor(NO_PLAYER); SetPrimeLeagueAlly(NO_PLAYER); SetPrimeLeagueCompetitor(NO_PLAYER); SetDemandTargetPlayer(NO_PLAYER); SetCSWarTargetPlayer(NO_PLAYER); SetCSBullyTargetPlayer(NO_PLAYER); SetEndedFriendshipThisTurn(false); SetBackstabber(false); SetCompetingForVictory(false); SetCurrentVictoryPursuit(GetPrimaryVictoryPursuit()); SetStateAllWars(STATE_ALL_WARS_NEUTRAL); for (int iI = 0; iI < MAX_CIV_PLAYERS; iI++) { PlayerTypes ePlayer = (PlayerTypes)iI; if (ePlayer == GetID() || !GET_PLAYER(ePlayer).isAlive()) continue; bool bTeammates = IsTeammate(ePlayer); bool bAlwaysAtWar = IsAlwaysAtWar(ePlayer); // Reset values for majors if (iI < MAX_MAJOR_CIVS) { CvDiplomacyAI* pOther = GET_PLAYER(ePlayer).GetDiplomacyAI(); for (int iMinorCivLoop = MAX_MAJOR_CIVS; iMinorCivLoop < MAX_CIV_PLAYERS; iMinorCivLoop++) { PlayerTypes eMinor = (PlayerTypes)iMinorCivLoop; SetWantToRouteConnectToMinor(eMinor, false); // Reset "attacked your protected minor" taunts in both directions if (!bTeammates && !bAlwaysAtWar) { SetSentAttackProtectedMinorTaunt(ePlayer, eMinor, false); pOther->SetSentAttackProtectedMinorTaunt(eID, eMinor, false); } } if (!bAlwaysAtWar) { // Reset exchange type values SetRecentTradeValue(ePlayer, 0); SetRecentAssistValue(ePlayer, 0); SetCommonFoeValue(ePlayer, 0); SetCantMatchDeal(ePlayer, false); SetOfferingGift(ePlayer, false); SetOfferedGift(ePlayer, false); pOther->SetRecentTradeValue(eID, 0); pOther->SetRecentAssistValue(eID, 0); pOther->SetCommonFoeValue(eID, 0); pOther->SetCantMatchDeal(eID, false); pOther->SetOfferingGift(eID, false); pOther->SetOfferedGift(eID, false); } if (!bTeammates) { // Reset warmonger values SetWarmongerThreat(ePlayer, THREAT_NONE); SetCivilianKillerValue(ePlayer, 0); pOther->SetOtherPlayerWarmongerAmountTimes100(eID, 0); // only reset the killed player's warmongering pOther->SetWarmongerThreat(eID, THREAT_NONE); pOther->SetCivilianKillerValue(eID, 0); // Reset aggressive postures SetMilitaryAggressivePosture(ePlayer, AGGRESSIVE_POSTURE_NONE); SetPlotBuyingAggressivePosture(ePlayer, AGGRESSIVE_POSTURE_NONE); pOther->SetMilitaryAggressivePosture(eID, AGGRESSIVE_POSTURE_NONE); pOther->SetPlotBuyingAggressivePosture(eID, AGGRESSIVE_POSTURE_NONE); // Reset dispute values SetMajorCompetitor(ePlayer, false); SetRecklessExpander(ePlayer, false); SetWonderSpammer(ePlayer, false); SetEndgameAggressiveTo(ePlayer, false); SetWonderDisputeLevel(ePlayer, DISPUTE_LEVEL_NONE); SetMinorCivDisputeLevel(ePlayer, DISPUTE_LEVEL_NONE); SetVictoryDisputeLevel(ePlayer, DISPUTE_LEVEL_NONE); SetVictoryBlockLevel(ePlayer, BLOCK_LEVEL_NONE); SetTechBlockLevel(ePlayer, BLOCK_LEVEL_NONE); SetPolicyBlockLevel(ePlayer, BLOCK_LEVEL_NONE); pOther->SetMajorCompetitor(eID, false); pOther->SetRecklessExpander(eID, false); pOther->SetWonderSpammer(eID, false); pOther->SetEndgameAggressiveTo(eID, false); pOther->SetLandDisputeLevel(eID, DISPUTE_LEVEL_NONE); // only set this for other majors, since it's a value towards all civs pOther->SetWonderDisputeLevel(eID, DISPUTE_LEVEL_NONE); pOther->SetNumWondersBeatenTo(eID, 0); // only reset the killed player's wonder completions pOther->SetMinorCivDisputeLevel(eID, DISPUTE_LEVEL_NONE); pOther->SetNumTimesTheyLoweredOurInfluence(eID, 0); // only reset the killed player's influence lowering pOther->SetVictoryDisputeLevel(eID, DISPUTE_LEVEL_NONE); pOther->SetVictoryBlockLevel(eID, BLOCK_LEVEL_NONE); pOther->SetTechBlockLevel(eID, BLOCK_LEVEL_NONE); pOther->SetPolicyBlockLevel(eID, BLOCK_LEVEL_NONE); if (!bAlwaysAtWar) { SetVassalProtectValue(ePlayer, 0); pOther->SetVassalProtectValue(eID, 0); // Cancel coop wars against this player pOther->CancelCoopWarsAgainstPlayer(eID, false); // Reset approach to NEUTRAL SetCivStrategicApproach(ePlayer, CIV_APPROACH_NEUTRAL); SetCachedSurfaceApproach(ePlayer, -1); pOther->SetCivStrategicApproach(eID, CIV_APPROACH_NEUTRAL); pOther->SetCachedSurfaceApproach(eID, -1); for (int iJ = 0; iJ < NUM_CIV_APPROACHES; iJ++) { CivApproachTypes eApproach = (CivApproachTypes)iJ; SetPlayerApproachValue(ePlayer, eApproach, 0); SetPlayerStrategicApproachValue(ePlayer, eApproach, 0); pOther->SetPlayerApproachValue(eID, eApproach, 0); pOther->SetPlayerStrategicApproachValue(eID, eApproach, 0); } pOther->SetCivApproach(eID, CIV_APPROACH_NEUTRAL); // only set this for other majors, since it's a value towards all civs // Clear war status SetSaneDiplomaticTarget(ePlayer, true); SetPotentialWarTarget(ePlayer, false); SetTreatyWillingToOffer(ePlayer, NO_PEACE_TREATY_TYPE); SetTreatyWillingToAccept(ePlayer, NO_PEACE_TREATY_TYPE); pOther->SetSaneDiplomaticTarget(eID, true); pOther->SetPotentialWarTarget(eID, false); pOther->SetTreatyWillingToOffer(eID, NO_PEACE_TREATY_TYPE); pOther->SetTreatyWillingToAccept(eID, NO_PEACE_TREATY_TYPE); pOther->SetArmyInPlaceForAttack(eID, false); // only set this for other majors, since it's a value towards all civs // Reset planning exchanges SetWantsDoFWithPlayer(ePlayer, false); SetWantsToEndDoFWithPlayer(ePlayer, false); SetWantsDefensivePactWithPlayer(ePlayer, false); SetWantsToEndDefensivePactWithPlayer(ePlayer, false); SetWantsResearchAgreementWithPlayer(ePlayer, false); SetStrategicTradePartner(ePlayer, false); pOther->SetWantsDoFWithPlayer(eID, false); pOther->SetWantsToEndDoFWithPlayer(eID, false); pOther->SetWantsDefensivePactWithPlayer(eID, false); pOther->SetWantsToEndDefensivePactWithPlayer(eID, false); pOther->SetWantsResearchAgreementWithPlayer(eID, false); pOther->SetStrategicTradePartner(eID, false); // Terminate promises in both directions SetMilitaryPromiseState(ePlayer, NO_PROMISE_STATE); SetExpansionPromiseState(ePlayer, NO_PROMISE_STATE); SetBorderPromiseState(ePlayer, NO_PROMISE_STATE); SetBullyCityStatePromiseState(ePlayer, NO_PROMISE_STATE); SetAttackCityStatePromiseState(ePlayer, NO_PROMISE_STATE); SetSpyPromiseState(ePlayer, NO_PROMISE_STATE); SetNoConvertPromiseState(ePlayer, NO_PROMISE_STATE); SetNoDiggingPromiseState(ePlayer, NO_PROMISE_STATE); SetBrokeCoopWarPromise(ePlayer, false); SetPlayerAskedNotToConvert(ePlayer, false); SetPlayerAskedNotToDig(ePlayer, false); pOther->SetMilitaryPromiseState(eID, NO_PROMISE_STATE); pOther->SetExpansionPromiseState(eID, NO_PROMISE_STATE); pOther->SetBorderPromiseState(eID, NO_PROMISE_STATE); pOther->SetBullyCityStatePromiseState(eID, NO_PROMISE_STATE); pOther->SetAttackCityStatePromiseState(eID, NO_PROMISE_STATE); pOther->SetSpyPromiseState(eID, NO_PROMISE_STATE); pOther->SetNoConvertPromiseState(eID, NO_PROMISE_STATE); pOther->SetNoDiggingPromiseState(eID, NO_PROMISE_STATE); pOther->SetBrokeCoopWarPromise(eID, false); pOther->SetPlayerAskedNotToConvert(eID, false); pOther->SetPlayerAskedNotToDig(eID, false); } } // Reset strength evaluations SetEconomicStrengthComparedToUs(ePlayer, STRENGTH_IMMENSE); SetMilitaryStrengthComparedToUs(ePlayer, STRENGTH_IMMENSE); SetRawMilitaryStrengthComparedToUs(ePlayer, STRENGTH_IMMENSE); SetTargetValue(ePlayer, TARGET_VALUE_IMPOSSIBLE); SetRawTargetValue(ePlayer, TARGET_VALUE_IMPOSSIBLE); pOther->SetEconomicStrengthComparedToUs(eID, STRENGTH_PATHETIC); pOther->SetMilitaryStrengthComparedToUs(eID, STRENGTH_PATHETIC); pOther->SetRawMilitaryStrengthComparedToUs(eID, STRENGTH_PATHETIC); pOther->SetTargetValue(eID, TARGET_VALUE_CAKEWALK); pOther->SetRawTargetValue(eID, TARGET_VALUE_CAKEWALK); // Reset voting history score SetVotingHistoryScore(ePlayer, 0); pOther->SetVotingHistoryScore(eID, 0); // Reset share approach response SetShareApproachResponse(ePlayer, NO_SHARE_APPROACH_RESPONSE); pOther->SetShareApproachResponse(eID, NO_SHARE_APPROACH_RESPONSE); } // Reset values for all civs if (!bTeammates) { SetLandDisputeLevel(ePlayer, DISPUTE_LEVEL_NONE); if (!bAlwaysAtWar) { SetArmyInPlaceForAttack(ePlayer, false); SetCivApproach(ePlayer, CIV_APPROACH_NEUTRAL); } } } return; } bool bWasHuman = WasHumanLastUpdate(); bool bIsHuman = GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY); m_bWasHumanLastUpdate = bIsHuman; // Are we now an AI player? if (bWasHuman && !bIsHuman) { // Pick flavors and a victory focus DoInitializePersonality(false); // Turn on the backstabber flag, if appropriate if (GetWeDeclaredWarOnFriendCount(GetID()) > 0) SetBackstabber(true); } // Are we now a human player? else if (bIsHuman && !bWasHuman) { // Reset flavors/victory focus to default m_iVictoryCompetitiveness = 5; m_iWonderCompetitiveness = 5; m_iMinorCivCompetitiveness = 5; m_iBoldness = 5; m_iDiploBalance = 5; m_iWarmongerHate = 5; m_iDoFWillingness = 5; m_iDenounceWillingness = 5; m_iWorkWithWillingness = 5; m_iWorkAgainstWillingness = 5; m_iLoyalty = 5; m_iForgiveness = 5; m_iNeediness = 5; m_iMeanness = 5; m_iChattiness = 5; for (int iI = 0; iI < NUM_CIV_APPROACHES; iI++) { m_aiMajorCivApproachBiases[iI] = 5; } m_iMinorCivWarBias = 5; m_iMinorCivHostileBias = 5; m_iMinorCivNeutralBias = 5; m_iMinorCivFriendlyBias = 5; SelectDefaultVictoryPursuits(); LogPersonality(); // Reset AI-only values SetMostValuableFriend(NO_PLAYER); SetMostValuableAlly(NO_PLAYER); SetBiggestCompetitor(NO_PLAYER); SetPrimeLeagueAlly(NO_PLAYER); SetPrimeLeagueCompetitor(NO_PLAYER); SetDemandTargetPlayer(NO_PLAYER); SetCSWarTargetPlayer(NO_PLAYER); SetCSBullyTargetPlayer(NO_PLAYER); SetBackstabber(false); SetCompetingForVictory(true); for (int iMinorCivLoop = MAX_MAJOR_CIVS; iMinorCivLoop < MAX_CIV_PLAYERS; iMinorCivLoop++) { PlayerTypes eMinor = (PlayerTypes)iMinorCivLoop; ResetSentAttackProtectedMinorTaunts(eMinor); SetWantToRouteConnectToMinor(eMinor, false); } for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes)iPlayerLoop; if (ePlayer == GetID() || !GET_PLAYER(ePlayer).isAlive()) continue; bool bTeammates = IsTeammate(ePlayer); bool bAlwaysAtWar = IsAlwaysAtWar(ePlayer); if (iPlayerLoop < MAX_MAJOR_CIVS) { if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) { CvDiplomacyAI* pOther = GET_PLAYER(ePlayer).GetDiplomacyAI(); // Terminate any coop war agreements with other humans CancelCoopWarsWithPlayer(ePlayer, false); // Terminate promises made with other humans if (!bTeammates && !bAlwaysAtWar) { SetMilitaryPromiseState(ePlayer, NO_PROMISE_STATE); SetExpansionPromiseState(ePlayer, NO_PROMISE_STATE); SetBorderPromiseState(ePlayer, NO_PROMISE_STATE); SetBullyCityStatePromiseState(ePlayer, NO_PROMISE_STATE); SetAttackCityStatePromiseState(ePlayer, NO_PROMISE_STATE); SetSpyPromiseState(ePlayer, NO_PROMISE_STATE); SetNoConvertPromiseState(ePlayer, NO_PROMISE_STATE); SetNoDiggingPromiseState(ePlayer, NO_PROMISE_STATE); SetBrokeCoopWarPromise(ePlayer, false); SetPlayerAskedNotToConvert(ePlayer, false); SetPlayerAskedNotToDig(ePlayer, false); pOther->SetMilitaryPromiseState(eID, NO_PROMISE_STATE); pOther->SetExpansionPromiseState(eID, NO_PROMISE_STATE); pOther->SetBorderPromiseState(eID, NO_PROMISE_STATE); pOther->SetBullyCityStatePromiseState(eID, NO_PROMISE_STATE); pOther->SetAttackCityStatePromiseState(eID, NO_PROMISE_STATE); pOther->SetSpyPromiseState(eID, NO_PROMISE_STATE); pOther->SetNoConvertPromiseState(eID, NO_PROMISE_STATE); pOther->SetNoDiggingPromiseState(eID, NO_PROMISE_STATE); pOther->SetBrokeCoopWarPromise(eID, false); pOther->SetPlayerAskedNotToConvert(eID, false); pOther->SetPlayerAskedNotToDig(eID, false); } } if (!bTeammates) { // Reset exchange type values SetWantsDoFWithPlayer(ePlayer, false); SetWantsToEndDoFWithPlayer(ePlayer, false); SetWantsDefensivePactWithPlayer(ePlayer, false); SetWantsToEndDefensivePactWithPlayer(ePlayer, false); SetWantsResearchAgreementWithPlayer(ePlayer, false); SetSaneDiplomaticTarget(ePlayer, true); SetPotentialWarTarget(ePlayer, true); // Reset dispute values SetRecklessExpander(ePlayer, false); SetWonderSpammer(ePlayer, false); SetEndgameAggressiveTo(ePlayer, false); SetMajorCompetitor(ePlayer, false); SetPlotBuyingAggressivePosture(ePlayer, AGGRESSIVE_POSTURE_NONE); SetWonderDisputeLevel(ePlayer, DISPUTE_LEVEL_NONE); SetMinorCivDisputeLevel(ePlayer, DISPUTE_LEVEL_NONE); SetVictoryDisputeLevel(ePlayer, DISPUTE_LEVEL_NONE); SetVictoryBlockLevel(ePlayer, BLOCK_LEVEL_NONE); SetTechBlockLevel(ePlayer, BLOCK_LEVEL_NONE); SetPolicyBlockLevel(ePlayer, BLOCK_LEVEL_NONE); if (!bAlwaysAtWar) { // Reset opinion/approach to neutral SetCivOpinion(ePlayer, CIV_OPINION_NEUTRAL); SetCachedOpinionWeight(ePlayer, 0); SetCivStrategicApproach(ePlayer, CIV_APPROACH_NEUTRAL); SetCachedSurfaceApproach(ePlayer, -1); // Reset peace willingness SetTreatyWillingToOffer(ePlayer, NO_PEACE_TREATY_TYPE); SetTreatyWillingToAccept(ePlayer, NO_PEACE_TREATY_TYPE); // Reset exchange type values SetStrategicTradePartner(ePlayer, false); } } // Reset other AI-only values SetRecentAssistValue(ePlayer, 0); SetVotingHistoryScore(ePlayer, 0); SetShareApproachResponse(ePlayer, NO_SHARE_APPROACH_RESPONSE); SetCantMatchDeal(ePlayer, false); SetOfferingGift(ePlayer, false); SetOfferedGift(ePlayer, false); } if (!bTeammates) { SetLandDisputeLevel(ePlayer, DISPUTE_LEVEL_NONE); if (!bAlwaysAtWar) { SetArmyInPlaceForAttack(ePlayer, false); SelectHumanApproach(ePlayer); } } } } TestBackstabberFlag(); } // ----------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------- // ************************************ // Helper Functions // ************************************ /// Is this a valid player to be looking at for diplomacy purposes? (e.g. are they alive, do we know them, etc.) bool CvDiplomacyAI::IsPlayerValid(PlayerTypes eOtherPlayer, bool bMyTeamIsValid /* = false = */ ) const { if (eOtherPlayer < 0 || eOtherPlayer >= MAX_CIV_PLAYERS) return false; // Alive? if (!GET_PLAYER(eOtherPlayer).isAlive()) return false; // REALLY Alive? (For some reason a player can be "alive" but have no Cities, Units, etc... grrrr) if (GET_PLAYER(eOtherPlayer).getNumCities() <= 0) return false; // A player we've met? if (!GET_TEAM(GetTeam()).isHasMet(GET_PLAYER(eOtherPlayer).getTeam())) return false; // On our team? if (!bMyTeamIsValid && GET_PLAYER(eOtherPlayer).getTeam() == GetTeam()) return false; return true; } /// Returns the number of valid major civs int CvDiplomacyAI::GetNumValidMajorCivs() const { int iCount = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = static_cast(iPlayerLoop); iCount += IsPlayerValid(ePlayer) ? 1 : 0; } return iCount; } /// Returns a vector containing pointers to all valid major civs vector CvDiplomacyAI::GetAllValidMajorCivs() const { vector result; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = static_cast(iPlayerLoop); if (IsPlayerValid(ePlayer)) result.push_back(ePlayer); } return result; } // ----------------------------------------------------------------------------------------------- /// Determine if we're at war with a player bool CvDiplomacyAI::IsAtWar(PlayerTypes eOtherPlayer) const { return GET_TEAM(GetTeam()).isAtWar(GET_PLAYER(eOtherPlayer).getTeam()); } /// Determine if we're always at war with a player bool CvDiplomacyAI::IsAlwaysAtWar(PlayerTypes eOtherPlayer) const { if (GetTeam() == GET_PLAYER(eOtherPlayer).getTeam()) return false; if (eOtherPlayer == BARBARIAN_PLAYER) return true; if (GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR)) return true; if (GET_PLAYER(eOtherPlayer).isMinorCiv() && GET_PLAYER(eOtherPlayer).GetMinorCivAI()->IsPermanentWar(GetTeam())) return true; return GC.getGame().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE) && IsAtWar(eOtherPlayer); } /// Determine if a player is a teammate (returns false for ourselves!) bool CvDiplomacyAI::IsTeammate(PlayerTypes eOtherPlayer) const { return eOtherPlayer != GetID() && GetTeam() == GET_PLAYER(eOtherPlayer).getTeam(); } /// Determine if a player is on a team we've met bool CvDiplomacyAI::IsHasMet(PlayerTypes eOtherPlayer, bool bMyTeamIsValid /* = false */) const { if (eOtherPlayer == NO_PLAYER || eOtherPlayer == BARBARIAN_PLAYER) return false; return GetTeam() == GET_PLAYER(eOtherPlayer).getTeam() ? bMyTeamIsValid : GET_TEAM(GetTeam()).isHasMet(GET_PLAYER(eOtherPlayer).getTeam()); } /// Determine if a player's team has a Defensive Pact with our team bool CvDiplomacyAI::IsHasDefensivePact(PlayerTypes eOtherPlayer) const { return GET_TEAM(GetTeam()).IsHasDefensivePact(GET_PLAYER(eOtherPlayer).getTeam()); } /// Determine if a player's team has a Research Agreement with our team bool CvDiplomacyAI::IsHasResearchAgreement(PlayerTypes eOtherPlayer) const { return GET_TEAM(GetTeam()).IsHasResearchAgreement(GET_PLAYER(eOtherPlayer).getTeam()); } /// Determine if we have an embassy with a player's team bool CvDiplomacyAI::IsHasEmbassy(PlayerTypes eOtherPlayer) const { return GET_TEAM(GetTeam()).HasEmbassyAtTeam(GET_PLAYER(eOtherPlayer).getTeam()); } /// Determine if we have Open Borders with a player's team (we can enter their territory) bool CvDiplomacyAI::IsHasOpenBorders(PlayerTypes eOtherPlayer) const { return GET_PLAYER(eOtherPlayer).isMinorCiv() ? GET_PLAYER(eOtherPlayer).GetMinorCivAI()->IsPlayerHasOpenBorders(GetID()) : GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).IsAllowsOpenBordersToTeam(GetTeam()); } /// Determine if we're a player's vassal bool CvDiplomacyAI::IsVassal(PlayerTypes eOtherPlayer) const { return GET_TEAM(GetTeam()).IsVassal(GET_PLAYER(eOtherPlayer).getTeam()); } /// Determine if we're a player's master bool CvDiplomacyAI::IsMaster(PlayerTypes eOtherPlayer) const { return GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).IsVassal(GetTeam()); } /// Determine if our current vassalage agreement with this player is voluntary (functions in either direction) bool CvDiplomacyAI::IsVoluntaryVassalage(PlayerTypes eOtherPlayer) const { if (IsVassal(eOtherPlayer) && GET_TEAM(GetTeam()).IsVoluntaryVassal(GET_PLAYER(eOtherPlayer).getTeam())) return true; if (IsMaster(eOtherPlayer) && GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).IsVoluntaryVassal(GetTeam())) return true; return false; } /// Which allies do we have if we declare war on eOtherPlayer / in our ongoing war against eOtherPlayer? /// bReverseMode = Which allies does eOtherPlayer have if they declare war on us / in their ongoing war against us? vector CvDiplomacyAI::GetOffensiveWarAllies(PlayerTypes eOtherPlayer, bool bIncludeMinors, bool bReverseMode) const { return GetWarAllies(eOtherPlayer, /*bDefensive*/ false, bIncludeMinors, bReverseMode, /*bNewWarsOnly*/ false); } /// Which allies do we have if eOtherPlayer declares war on us / in our ongoing war against them? /// bReverseMode = Which allies does eOtherPlayer have if we declare war on them / in their ongoing war against us? /// bNewWarOnly = Only consider new wars we (or they) would get into if war were declared. Returns an empty vector if called while already at war. vector CvDiplomacyAI::GetDefensiveWarAllies(PlayerTypes eOtherPlayer, bool bIncludeMinors, bool bReverseMode, bool bNewWarsOnly) const { return GetWarAllies(eOtherPlayer, /*bDefensive*/ true, bIncludeMinors, bReverseMode, bNewWarsOnly); } /// Which allies do we have in a war with eOtherPlayer? /// See the two helper functions above for usage notes. vector CvDiplomacyAI::GetWarAllies(PlayerTypes eOtherPlayer, bool bDefensive, bool bIncludeMinors, bool bReverseMode, bool bNewWarsOnly) const { vector result; vector vMinorsToCheck; vector vCheckVassals; vector vValidPlayers; if (GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR)) bIncludeMinors = false; if (!bDefensive) bNewWarsOnly = false; if (bNewWarsOnly && (IsAtWar(eOtherPlayer) || GC.getGame().isOption(GAMEOPTION_ALWAYS_PEACE) || GC.getGame().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE) || GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR))) return result; // Not valid for City-States. if (bReverseMode && !GET_PLAYER(eOtherPlayer).isMajorCiv()) return result; int iLoopUntil = bIncludeMinors ? MAX_CIV_PLAYERS : MAX_MAJOR_CIVS; vector vOurTeam = GET_TEAM(GetTeam()).getPlayers(); vector vTheirTeam = GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).getPlayers(); if (!bReverseMode) { for (int iPlayerLoop = 0; iPlayerLoop < iLoopUntil; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (eLoopPlayer == GetID()) continue; if (GET_PLAYER(eLoopPlayer).getTeam() == GET_PLAYER(eOtherPlayer).getTeam()) continue; if (!GET_PLAYER(eLoopPlayer).isAlive() || GET_PLAYER(eLoopPlayer).getNumCities() <= 0) continue; if (!IsHasMet(eLoopPlayer, true)) continue; if (IsAtWar(eLoopPlayer)) continue; if (IsAtWar(eOtherPlayer) && !GET_PLAYER(eLoopPlayer).IsAtWarWith(eOtherPlayer)) continue; if (GET_PLAYER(eLoopPlayer).isMinorCiv()) { if (bIncludeMinors && GET_PLAYER(eLoopPlayer).GetMinorCivAI()->GetAlly() != NO_PLAYER) vMinorsToCheck.push_back(eLoopPlayer); continue; } // Our teammate? if (IsTeammate(eLoopPlayer)) { result.push_back(eLoopPlayer); continue; } if (GC.getGame().isOption(GAMEOPTION_ALWAYS_PEACE) || GC.getGame().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE) || GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR)) continue; vValidPlayers.push_back(eLoopPlayer); // Our vassal? if (IsMaster(eLoopPlayer)) { result.push_back(eLoopPlayer); continue; } // Our master? if (IsVassal(eLoopPlayer)) { result.push_back(eLoopPlayer); continue; } // Our Defensive Pact? Only applies if they're currently at war or we're looking at defensive allies. if (IsHasDefensivePact(eLoopPlayer) && (bDefensive || GET_PLAYER(eLoopPlayer).IsAtWarWith(eOtherPlayer))) { result.push_back(eLoopPlayer); vCheckVassals.push_back(eLoopPlayer); continue; } // Our coop war ally? bool bBreak = false; bool bAvoidExchanges = AvoidExchangesWithPlayer(eLoopPlayer); vector vLoopTeam = GET_TEAM(GET_PLAYER(eLoopPlayer).getTeam()).getPlayers(); for (size_t i=0; iIsDoFAccepted(eLoopPlayer) || pTeamDiplo->WasResurrectedBy(eLoopPlayer) || pTeamDiplo->IsLiberator(eLoopPlayer, true, false)) { result.push_back(eLoopPlayer); vCheckVassals.push_back(eLoopPlayer); bBreak = true; break; } else if (!bAvoidExchanges && !GET_PLAYER(vOurTeam[i]).isHuman(ISHUMAN_AI_DIPLOMACY) && pTeamDiplo->GetBiggestCompetitor() != eLoopPlayer && (pTeamDiplo->GetCivOpinion(eLoopPlayer) == CIV_OPINION_ALLY || pTeamDiplo->GetDoFType(eLoopPlayer) == DOF_TYPE_ALLIES)) { result.push_back(eLoopPlayer); vCheckVassals.push_back(eLoopPlayer); bBreak = true; break; } } if (GetPlayer()->IsVassalOfSomeone() || GET_PLAYER(eOtherPlayer).isMinorCiv()) continue; for (size_t j=0; jGetCoopWarState(vLoopTeam[j], vTheirTeam[k]) >= COOP_WAR_STATE_PREPARING) { result.push_back(eLoopPlayer); vCheckVassals.push_back(eLoopPlayer); bBreak = true; break; } } if (bBreak) break; // Coop wars against a DP of theirs, master of theirs, or DP of their master also count (if defensive, the latter two only if ongoing) for (int iPlayerLoopTwo = 0; iPlayerLoopTwo < MAX_MAJOR_CIVS; iPlayerLoopTwo++) { PlayerTypes eLoopOtherPlayer = (PlayerTypes) iPlayerLoopTwo; if (!GET_PLAYER(eLoopOtherPlayer).isAlive() || !GET_PLAYER(eLoopOtherPlayer).isMajorCiv() || GET_PLAYER(eLoopOtherPlayer).getNumCities() <= 0) continue; TeamTypes eLoopOtherTeam = GET_PLAYER(eLoopOtherPlayer).getTeam(); if (eLoopOtherTeam == GET_PLAYER(eOtherPlayer).getTeam() || !IsHasMet(eLoopOtherPlayer)) continue; if (GET_TEAM(eLoopOtherTeam).IsHasDefensivePact(GET_PLAYER(eOtherPlayer).getTeam())) { CoopWarStates eCoopWarState = pTeamDiplo->GetCoopWarState(vLoopTeam[j], eLoopOtherPlayer); if (eCoopWarState == COOP_WAR_STATE_ONGOING || (!bDefensive && eCoopWarState == COOP_WAR_STATE_PREPARING)) { result.push_back(eLoopPlayer); vCheckVassals.push_back(eLoopPlayer); bBreak = true; break; } } else { TeamTypes eMasterTeam = GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).GetMaster(); if (eMasterTeam != NO_TEAM) { if (eLoopOtherTeam == eMasterTeam) { if (pTeamDiplo->GetCoopWarState(vLoopTeam[j], eLoopOtherPlayer) >= COOP_WAR_STATE_PREPARING) { result.push_back(eLoopPlayer); vCheckVassals.push_back(eLoopPlayer); bBreak = true; break; } } else if (GET_TEAM(eLoopOtherTeam).IsHasDefensivePact(eMasterTeam)) { CoopWarStates eCoopWarState = pTeamDiplo->GetCoopWarState(vLoopTeam[j], eLoopOtherPlayer); if (eCoopWarState == COOP_WAR_STATE_ONGOING || (!bDefensive && eCoopWarState == COOP_WAR_STATE_PREPARING)) { result.push_back(eLoopPlayer); vCheckVassals.push_back(eLoopPlayer); bBreak = true; break; } } } } } if (bBreak) break; } if (bBreak) break; } } if (!GC.getGame().isOption(GAMEOPTION_ALWAYS_PEACE) && !GC.getGame().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE) && !GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR)) { // Do we have a master? Let's include their vassals and any applicable DPs. if (GetPlayer()->IsVassalOfSomeone()) { TeamTypes eMasterTeam = GET_TEAM(GetTeam()).GetMaster(); vector vMasterTeam = GET_TEAM(eMasterTeam).getPlayers(); PlayerTypes eMasterPlayer = NO_PLAYER; for (size_t i=0; i::iterator iter = vValidPlayers.begin(); iter != vValidPlayers.end(); ++iter) { PlayerTypes eLoopPlayer = *iter; if (GET_PLAYER(eLoopPlayer).getTeam() == GET_PLAYER(eMasterPlayer).getTeam()) continue; if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsVassal(eMasterPlayer)) { // Make sure not to include them more than once! if (std::find(result.begin(), result.end(), eLoopPlayer) == result.end()) { result.push_back(eLoopPlayer); } } else if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsHasDefensivePact(eMasterPlayer)) { // Make sure not to include them more than once! if (std::find(result.begin(), result.end(), eLoopPlayer) == result.end()) { result.push_back(eLoopPlayer); } if (std::find(vCheckVassals.begin(), vCheckVassals.end(), eLoopPlayer) == vCheckVassals.end()) { vCheckVassals.push_back(eLoopPlayer); } } } } // For all relevant allies, check if they have vassals and include them too! for (std::vector::iterator it = vCheckVassals.begin(); it != vCheckVassals.end(); ++it) { for (std::vector::iterator iter = vValidPlayers.begin(); iter != vValidPlayers.end(); ++iter) { PlayerTypes eLoopPlayer = *iter; if (GET_PLAYER(eLoopPlayer).getTeam() == GET_PLAYER(*it).getTeam()) continue; if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsVassal(*it)) { // Make sure not to include them more than once! if (std::find(result.begin(), result.end(), eLoopPlayer) == result.end()) { result.push_back(eLoopPlayer); } } } } // Include any City-State allies of our major allies! if (bIncludeMinors) { for (std::vector::iterator it = vMinorsToCheck.begin(); it != vMinorsToCheck.end(); ++it) { PlayerTypes eAlly = GET_PLAYER(*it).GetMinorCivAI()->GetAlly(); if (eAlly == GetID() || std::find(result.begin(), result.end(), eAlly) != result.end()) { result.push_back(*it); } } } } } else { for (int iPlayerLoop = 0; iPlayerLoop < iLoopUntil; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (eLoopPlayer == eOtherPlayer) continue; if (GET_PLAYER(eLoopPlayer).getTeam() == GetTeam()) continue; if (!GET_PLAYER(eLoopPlayer).isAlive() || GET_PLAYER(eLoopPlayer).getNumCities() <= 0) continue; if (!IsHasMet(eLoopPlayer)) continue; if (GET_PLAYER(eOtherPlayer).IsAtWarWith(eLoopPlayer)) continue; if (IsAtWar(eOtherPlayer) && !IsAtWar(eLoopPlayer)) continue; if (GET_PLAYER(eLoopPlayer).isMinorCiv()) { if (bIncludeMinors && GET_PLAYER(eLoopPlayer).GetMinorCivAI()->GetAlly() != NO_PLAYER) vMinorsToCheck.push_back(eLoopPlayer); continue; } CvDiplomacyAI* pDiplo = GET_PLAYER(eOtherPlayer).GetDiplomacyAI(); // Their teammate? if (pDiplo->IsTeammate(eLoopPlayer)) { result.push_back(eLoopPlayer); continue; } if (GC.getGame().isOption(GAMEOPTION_ALWAYS_PEACE) || GC.getGame().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE) || GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR)) continue; vValidPlayers.push_back(eLoopPlayer); // Their vassal? if (pDiplo->IsMaster(eLoopPlayer)) { result.push_back(eLoopPlayer); continue; } // Their master? if (pDiplo->IsVassal(eLoopPlayer)) { result.push_back(eLoopPlayer); continue; } // Their Defensive Pact? if (pDiplo->IsHasDefensivePact(eLoopPlayer) && (bDefensive || IsAtWar(eLoopPlayer))) { result.push_back(eLoopPlayer); vCheckVassals.push_back(eLoopPlayer); continue; } // Their coop war ally? bool bBreak = false; vector vLoopTeam = GET_TEAM(GET_PLAYER(eLoopPlayer).getTeam()).getPlayers(); for (size_t i=0; iIsDoFAccepted(eLoopPlayer) || pTeamDiplo->WasResurrectedBy(eLoopPlayer) || pTeamDiplo->IsLiberator(eLoopPlayer, true, false)) { result.push_back(eLoopPlayer); vCheckVassals.push_back(eLoopPlayer); bBreak = true; break; } } if (GET_PLAYER(eOtherPlayer).IsVassalOfSomeone()) continue; for (size_t j=0; jGetCoopWarState(vLoopTeam[j], vOurTeam[k]) == COOP_WAR_STATE_ONGOING) { result.push_back(eLoopPlayer); vCheckVassals.push_back(eLoopPlayer); bBreak = true; break; } } if (bBreak) break; // Coop wars against a DP of ours, master of ours, or DP of our master also count... for (int iPlayerLoopTwo = 0; iPlayerLoopTwo < MAX_MAJOR_CIVS; iPlayerLoopTwo++) { PlayerTypes eLoopOtherPlayer = (PlayerTypes) iPlayerLoopTwo; if (!GET_PLAYER(eLoopOtherPlayer).isAlive() || !GET_PLAYER(eLoopOtherPlayer).isMajorCiv() || GET_PLAYER(eLoopOtherPlayer).getNumCities() <= 0) continue; TeamTypes eLoopOtherTeam = GET_PLAYER(eLoopOtherPlayer).getTeam(); if (!IsHasMet(eLoopOtherPlayer)) continue; if (GET_TEAM(eLoopOtherTeam).IsHasDefensivePact(GetTeam())) { if (pTeamDiplo->GetCoopWarState(vLoopTeam[j], eLoopOtherPlayer) == COOP_WAR_STATE_ONGOING) { result.push_back(eLoopPlayer); vCheckVassals.push_back(eLoopPlayer); bBreak = true; break; } } else { TeamTypes eMasterTeam = GET_TEAM(GetTeam()).GetMaster(); if (eMasterTeam != NO_TEAM && (eLoopOtherTeam == eMasterTeam || GET_TEAM(eLoopOtherTeam).IsHasDefensivePact(eMasterTeam))) { if (pTeamDiplo->GetCoopWarState(vLoopTeam[j], eLoopOtherPlayer) == COOP_WAR_STATE_ONGOING) { result.push_back(eLoopPlayer); vCheckVassals.push_back(eLoopPlayer); bBreak = true; break; } } } } if (bBreak) break; } if (bBreak) break; } } if (!GC.getGame().isOption(GAMEOPTION_ALWAYS_PEACE) && !GC.getGame().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE) && !GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR)) { // Do they have a master? Let's include their vassals and any applicable DPs. if (GET_PLAYER(eOtherPlayer).IsVassalOfSomeone()) { TeamTypes eMasterTeam = GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).GetMaster(); vector vMasterTeam = GET_TEAM(eMasterTeam).getPlayers(); PlayerTypes eMasterPlayer = NO_PLAYER; for (size_t i=0; i::iterator iter = vValidPlayers.begin(); iter != vValidPlayers.end(); ++iter) { PlayerTypes eLoopPlayer = *iter; if (GET_PLAYER(eLoopPlayer).getTeam() == GET_PLAYER(eMasterPlayer).getTeam()) continue; if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsVassal(eMasterPlayer)) { // Make sure not to include them more than once! if (std::find(result.begin(), result.end(), eLoopPlayer) == result.end()) { result.push_back(eLoopPlayer); } } else if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsHasDefensivePact(eMasterPlayer)) { // Make sure not to include them more than once! if (std::find(result.begin(), result.end(), eLoopPlayer) == result.end()) { result.push_back(eLoopPlayer); } if (std::find(vCheckVassals.begin(), vCheckVassals.end(), eLoopPlayer) == vCheckVassals.end()) { vCheckVassals.push_back(eLoopPlayer); } } } } // For all relevant allies, check if they have vassals and include them too! for (std::vector::iterator it = vCheckVassals.begin(); it != vCheckVassals.end(); ++it) { for (std::vector::iterator iter = vValidPlayers.begin(); iter != vValidPlayers.end(); ++iter) { PlayerTypes eLoopPlayer = *iter; if (GET_PLAYER(eLoopPlayer).getTeam() == GET_PLAYER(*it).getTeam()) continue; if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsVassal(*it)) { // Make sure not to include them more than once! if (std::find(result.begin(), result.end(), eLoopPlayer) == result.end()) { result.push_back(eLoopPlayer); } } } } // Include any City-State allies of their major allies! if (bIncludeMinors) { for (std::vector::iterator it = vMinorsToCheck.begin(); it != vMinorsToCheck.end(); ++it) { PlayerTypes eAlly = GET_PLAYER(*it).GetMinorCivAI()->GetAlly(); if (eAlly == eOtherPlayer || std::find(result.begin(), result.end(), eAlly) != result.end()) { result.push_back(*it); } } } } } if (bNewWarsOnly) { vector vNewWars; for (std::vector::iterator it = result.begin(); it != result.end(); ++it) { if (!bReverseMode && !GET_PLAYER(eOtherPlayer).IsAtWarWith(*it)) vNewWars.push_back(*it); else if (bReverseMode && !IsAtWar(*it)) vNewWars.push_back(*it); } return vNewWars; } return result; } /// Determine if we are Nuclear Gandhi :) bool CvDiplomacyAI::IsNuclearGandhi(bool bPotentially) const { // Nuclear Gandhi must be enabled if (!MOD_DIPLOAI_ENABLE_NUCLEAR_GANDHI || GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isNoNukes()) return false; bool bGandhiPersonality = false; if (GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { int iRandomnessSetting = gCustomMods.getOption("DIPLOAI_NUCLEAR_GANDHI_RANDOM_PERSONALITIES_SETTING", 1); if (iRandomnessSetting == 1) // Don't proceed if Random Personalities is enabled return false; if (iRandomnessSetting == 3) // Check if Gandhi was selected as the Random Personality instead of checking Leaders.Type { if (GetPlayer()->getPersonalityType() == (LeaderHeadTypes)GD_INT_GET(GANDHI_LEADER)) bGandhiPersonality = true; else return false; } } // Must be Gandhi if (!bGandhiPersonality && GetPlayer()->getLeaderType() != (LeaderHeadTypes)GD_INT_GET(GANDHI_LEADER)) return false; if (bPotentially) return true; // Don't go crazy if we're a vassal if (GetPlayer()->IsVassalOfSomeone()) return false; // Must have nukes OR have nuked a major civ already bool bNukeHappy = GetPlayer()->getNumNukeUnits() > 0; if (!bNukeHappy) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsNukedBy(GetID())) { bNukeHappy = true; break; } } } return bNukeHappy; } /// Is the AI forced to accept all Discuss menu requests from human players? bool CvDiplomacyAI::IsAIMustAcceptHumanDiscussRequests() const { if (MOD_DIPLO_DEBUG_MODE) { int iSetting = gCustomMods.getOption("DIPLO_DEBUG_MODE_SETTING", 1); return iSetting == 2; } return false; } // ************************************ // Personality Values // ************************************ /// Returns a personality weight with a small random element int CvDiplomacyAI::RandomizePersonalityFlavor(int iOriginalValue, const CvSeeder& seed) { int iPlusMinus = range(/*2*/ GD_INT_GET(FLAVOR_RANDOMIZATION_RANGE), 0, (INT_MAX - 1) / 2); // Diplo AI Option: Disable this randomization! if (iPlusMinus == 0 || MOD_DIPLOAI_NO_FLAVOR_RANDOMIZATION) return range(iOriginalValue, 1, 10); // Randomize! return range(GC.getGame().randRangeInclusive(iOriginalValue - iPlusMinus, iOriginalValue + iPlusMinus, seed), 1, 10); } // ----------------------------------------------------------------------------------------------- /// Initializes Personality Values for this player (XML value + random element) void CvDiplomacyAI::DoInitializePersonality(bool bFirstInit) { int ID = (int) GetID(); // AI Players only if (!GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { LeaderHeadTypes leader = GetPlayer()->getPersonalityType(); if (leader != NO_LEADER) { CvLeaderHeadInfo* pkLeaderHeadInfo = GC.getLeaderHeadInfo(leader); if (pkLeaderHeadInfo) { // Four flavors can be set to -12 to guarantee that the AI will pursue a specific victory condition, if it is enabled. // These should be treated as a 10 for all other purposes. m_iBoldness = pkLeaderHeadInfo->GetBoldness() == -12 ? 10 : RandomizePersonalityFlavor(pkLeaderHeadInfo->GetBoldness(), CvSeeder::fromRaw(0x0ba129a6).mix(ID)); m_iMinorCivCompetitiveness = pkLeaderHeadInfo->GetMinorCivCompetitiveness() == -12 ? 10 : RandomizePersonalityFlavor(pkLeaderHeadInfo->GetMinorCivCompetitiveness(), CvSeeder::fromRaw(0x4f28f23d).mix(ID)); m_iWonderCompetitiveness = pkLeaderHeadInfo->GetWonderCompetitiveness() == -12 ? 10 : RandomizePersonalityFlavor(pkLeaderHeadInfo->GetWonderCompetitiveness(), CvSeeder::fromRaw(0x69d26397).mix(ID)); m_iWarmongerHate = pkLeaderHeadInfo->GetWarmongerHate() == -12 ? 10 : RandomizePersonalityFlavor(pkLeaderHeadInfo->GetWarmongerHate(), CvSeeder::fromRaw(0x8e1f1b1f).mix(ID)); m_iVictoryCompetitiveness = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetVictoryCompetitiveness(), CvSeeder::fromRaw(0x5bea8e77).mix(ID)); m_iDiploBalance = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetDiploBalance(), CvSeeder::fromRaw(0x2f543039).mix(ID)); m_iDoFWillingness = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetDoFWillingness(), CvSeeder::fromRaw(0x2a36ee5b).mix(ID)); m_iDenounceWillingness = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetDenounceWillingness(), CvSeeder::fromRaw(0x84da8912).mix(ID)); m_iWorkWithWillingness = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetWorkWithWillingness(), CvSeeder::fromRaw(0x5f5224db).mix(ID)); m_iWorkAgainstWillingness = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetWorkAgainstWillingness(), CvSeeder::fromRaw(0xbe85dbdb).mix(ID)); m_iLoyalty = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetLoyalty(), CvSeeder::fromRaw(0x52c2cdd2).mix(ID)); m_iForgiveness = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetForgiveness(), CvSeeder::fromRaw(0x81d9b8ec).mix(ID)); m_iNeediness = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetNeediness(), CvSeeder::fromRaw(0xc0dd3363).mix(ID)); m_iMeanness = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetMeanness(), CvSeeder::fromRaw(0x24ca13c9).mix(ID)); m_iChattiness = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetChattiness(), CvSeeder::fromRaw(0x5227bf3a).mix(ID)); m_aiMajorCivApproachBiases[CIV_APPROACH_WAR] = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetWarBias(false), CvSeeder::fromRaw(0xe48d2e04).mix(ID)); m_aiMajorCivApproachBiases[CIV_APPROACH_HOSTILE] = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetHostileBias(false), CvSeeder::fromRaw(0x49b046ba).mix(ID)); m_aiMajorCivApproachBiases[CIV_APPROACH_DECEPTIVE] = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetDeceptiveBias(), CvSeeder::fromRaw(0xa9bc4713).mix(ID)); m_aiMajorCivApproachBiases[CIV_APPROACH_GUARDED] = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetGuardedBias(), CvSeeder::fromRaw(0xab8795d6).mix(ID)); m_aiMajorCivApproachBiases[CIV_APPROACH_AFRAID] = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetAfraidBias(), CvSeeder::fromRaw(0x9a90c196).mix(ID)); m_aiMajorCivApproachBiases[CIV_APPROACH_NEUTRAL] = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetNeutralBias(false), CvSeeder::fromRaw(0xf7729147).mix(ID)); m_aiMajorCivApproachBiases[CIV_APPROACH_FRIENDLY] = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetFriendlyBias(false), CvSeeder::fromRaw(0x7fecee06).mix(ID)); m_iMinorCivWarBias = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetWarBias(true), CvSeeder::fromRaw(0x0be73829).mix(ID)); m_iMinorCivHostileBias = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetHostileBias(true), CvSeeder::fromRaw(0xc44465ee).mix(ID)); m_iMinorCivNeutralBias = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetNeutralBias(true), CvSeeder::fromRaw(0x4087c177).mix(ID)); m_iMinorCivFriendlyBias = RandomizePersonalityFlavor(pkLeaderHeadInfo->GetFriendlyBias(true), CvSeeder::fromRaw(0x4565d312).mix(ID)); // Minimal loyalty? We're willing to backstab. if (GetLoyalty() <= 2) SetBackstabber(true); } } } else m_bWasHumanLastUpdate = true; // Now that we've picked our flavors, select our default Victory Pursuit. SelectDefaultVictoryPursuits(); LogPersonality(); // Initialize a few static values here if (bFirstInit) { m_aeCivApproach[ID] = CIV_APPROACH_FRIENDLY; m_aeCivStrategicApproach[ID] = CIV_APPROACH_FRIENDLY; m_aeCivOpinion[ID] = CIV_OPINION_ALLY; m_aiCachedOpinionWeight[ID] = SHRT_MIN; m_aeEconomicStrengthComparedToUs[ID] = STRENGTH_AVERAGE; m_aeMilitaryStrengthComparedToUs[ID] = STRENGTH_AVERAGE; m_aeRawMilitaryStrengthComparedToUs[ID] = STRENGTH_AVERAGE; m_aeTargetValue[ID] = TARGET_VALUE_AVERAGE; m_aeRawTargetValue[ID] = TARGET_VALUE_AVERAGE; for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes)iPlayerLoop; if (IsTeammate(eLoopPlayer)) { m_aeCivApproach[eLoopPlayer] = CIV_APPROACH_FRIENDLY; if (GET_PLAYER(eLoopPlayer).isMajorCiv()) { m_aeCivStrategicApproach[eLoopPlayer] = CIV_APPROACH_FRIENDLY; m_aeCivOpinion[eLoopPlayer] = CIV_OPINION_ALLY; m_aiCachedOpinionWeight[eLoopPlayer] = SHRT_MIN; } } else if (IsAlwaysAtWar(eLoopPlayer) && GET_PLAYER(eLoopPlayer).isMajorCiv()) { m_aeCivOpinion[eLoopPlayer] = CIV_OPINION_UNFORGIVABLE; m_aiCachedOpinionWeight[eLoopPlayer] = SHRT_MAX; } } } } // ----------------------------------------------------------------------------------------------- /// Select our Default Victory Pursuits for this game (the victory condition we go for early in the game, prior to grand strategy AI taking effect; has other effects throughout the game as well) /// May also pick a Secondary Victory Pursuit which has less significant effects on AI diplomacy and strategy. /// Intention here is to add some variability to AI behavior, especially for generalist civs void CvDiplomacyAI::SelectDefaultVictoryPursuits() { // Check if this leader will ALWAYS go for a specific victory condition if they can. switch (GetEternalVictoryPursuit()) { case VICTORY_PURSUIT_DOMINATION: SetPrimaryVictoryPursuit(VICTORY_PURSUIT_DOMINATION); SetSecondaryVictoryPursuit(NO_VICTORY_PURSUIT); return; case VICTORY_PURSUIT_DIPLOMACY: SetPrimaryVictoryPursuit(VICTORY_PURSUIT_DIPLOMACY); SetSecondaryVictoryPursuit(NO_VICTORY_PURSUIT); return; case VICTORY_PURSUIT_CULTURE: SetPrimaryVictoryPursuit(VICTORY_PURSUIT_CULTURE); SetSecondaryVictoryPursuit(NO_VICTORY_PURSUIT); return; case VICTORY_PURSUIT_SCIENCE: SetPrimaryVictoryPursuit(VICTORY_PURSUIT_SCIENCE); SetSecondaryVictoryPursuit(NO_VICTORY_PURSUIT); return; default: break; } CvPlayerTraits* pTraits = GetPlayer()->GetPlayerTraits(); bool bCanUnlockTechs = !GC.getGame().isOption(GAMEOPTION_NO_SCIENCE); bool bCanUnlockPolicies = !GC.getGame().isOption(GAMEOPTION_NO_POLICIES); bool bCanUseLeague = !GC.getGame().isOption(GAMEOPTION_NO_LEAGUES); bool bCanConquer = GC.getGame().CanPlayerAttemptDominationVictory(GetID(), NO_PLAYER, true); int iNumValidOptions = 0; if (bCanUnlockTechs) iNumValidOptions++; if (bCanUnlockPolicies) iNumValidOptions++; if (bCanUseLeague) iNumValidOptions++; if (bCanConquer) iNumValidOptions++; // If nothing is valid, we can't do much here, can we...? if (iNumValidOptions == 0) { SetPrimaryVictoryPursuit(NO_VICTORY_PURSUIT); SetSecondaryVictoryPursuit(NO_VICTORY_PURSUIT); return; } // If only ONE option is valid, we have no other choice! if (iNumValidOptions == 1) { if (bCanUnlockTechs) SetPrimaryVictoryPursuit(VICTORY_PURSUIT_SCIENCE); else if (bCanUnlockPolicies) SetPrimaryVictoryPursuit(VICTORY_PURSUIT_CULTURE); else if (bCanUseLeague) SetPrimaryVictoryPursuit(VICTORY_PURSUIT_DIPLOMACY); else if (bCanConquer) SetPrimaryVictoryPursuit(VICTORY_PURSUIT_DOMINATION); SetSecondaryVictoryPursuit(NO_VICTORY_PURSUIT); return; } // Civs are given AI victory pursuit hints/biases in the Leaders table, so retrieve those now VictoryPursuitTypes ePrimaryVictory = NO_VICTORY_PURSUIT; VictoryPursuitTypes eSecondaryVictory = NO_VICTORY_PURSUIT; // Ignore Random Personalities when the DiploAI option is active or when retrieving hints for highly specialized scenarios LeaderHeadTypes leader = (iNumValidOptions > 2 && !MOD_DIPLOAI_LIMIT_VICTORY_PURSUIT_RANDOMIZATION) ? GetPlayer()->getPersonalityType() : GetPlayer()->getLeaderType(); if (leader != NO_LEADER) { CvLeaderHeadInfo* pkLeaderHeadInfo = GC.getLeaderHeadInfo(leader); if (pkLeaderHeadInfo) { ePrimaryVictory = pkLeaderHeadInfo->GetPrimaryVictoryPursuit(); eSecondaryVictory = pkLeaderHeadInfo->GetSecondaryVictoryPursuit(); } } // If only TWO options are valid, we have only two choices...though at least we can use the pursuit hints to prioritize this time. if (iNumValidOptions == 2) { VictoryPursuitTypes eOptionOne = NO_VICTORY_PURSUIT; VictoryPursuitTypes eOptionTwo = NO_VICTORY_PURSUIT; if (bCanUnlockTechs) { eOptionOne = VICTORY_PURSUIT_SCIENCE; } if (bCanUnlockPolicies) { if (eOptionOne == NO_VICTORY_PURSUIT) eOptionOne = VICTORY_PURSUIT_CULTURE; else eOptionTwo = VICTORY_PURSUIT_CULTURE; } if (bCanUseLeague) { if (eOptionOne == NO_VICTORY_PURSUIT) eOptionOne = VICTORY_PURSUIT_DIPLOMACY; else eOptionTwo = VICTORY_PURSUIT_DIPLOMACY; } if (bCanConquer) { eOptionTwo = VICTORY_PURSUIT_DOMINATION; } if (eOptionOne == ePrimaryVictory) { SetPrimaryVictoryPursuit(eOptionOne); SetSecondaryVictoryPursuit(eOptionTwo); return; } else if (eOptionTwo == ePrimaryVictory) { SetPrimaryVictoryPursuit(eOptionTwo); SetSecondaryVictoryPursuit(eOptionOne); return; } else if (eOptionOne == eSecondaryVictory) { SetPrimaryVictoryPursuit(eOptionOne); SetSecondaryVictoryPursuit(eOptionTwo); return; } else if (eOptionTwo == eSecondaryVictory) { SetPrimaryVictoryPursuit(eOptionOne); SetSecondaryVictoryPursuit(eOptionTwo); return; } // We have no guidance on how to proceed? Let's see if player traits has any guidance. // This is obviously a highly specialized scenario, so we need to use our strengths if we have them! if (bCanConquer && pTraits->IsWarmonger()) { SetPrimaryVictoryPursuit(eOptionOne == VICTORY_PURSUIT_DOMINATION ? eOptionOne : eOptionTwo); SetSecondaryVictoryPursuit(eOptionOne == VICTORY_PURSUIT_DOMINATION ? eOptionTwo : eOptionOne); return; } else if (bCanUseLeague && pTraits->IsDiplomat()) { SetPrimaryVictoryPursuit(eOptionOne == VICTORY_PURSUIT_DIPLOMACY ? eOptionOne : eOptionTwo); SetSecondaryVictoryPursuit(eOptionOne == VICTORY_PURSUIT_DIPLOMACY ? eOptionTwo : eOptionOne); return; } else if (bCanUnlockPolicies && pTraits->IsTourism()) { SetPrimaryVictoryPursuit(eOptionOne == VICTORY_PURSUIT_CULTURE ? eOptionOne : eOptionTwo); SetSecondaryVictoryPursuit(eOptionOne == VICTORY_PURSUIT_CULTURE ? eOptionTwo : eOptionOne); return; } else if (bCanUnlockTechs && pTraits->IsNerd()) { SetPrimaryVictoryPursuit(eOptionOne == VICTORY_PURSUIT_SCIENCE ? eOptionOne : eOptionTwo); SetSecondaryVictoryPursuit(eOptionOne == VICTORY_PURSUIT_SCIENCE ? eOptionTwo : eOptionOne); return; } else if (pTraits->IsExpansionist()) { if (bCanConquer) { SetPrimaryVictoryPursuit(eOptionOne == VICTORY_PURSUIT_DOMINATION ? eOptionOne : eOptionTwo); SetSecondaryVictoryPursuit(eOptionOne == VICTORY_PURSUIT_DOMINATION ? eOptionTwo : eOptionOne); return; } if (bCanUseLeague) { SetPrimaryVictoryPursuit(eOptionOne == VICTORY_PURSUIT_DIPLOMACY ? eOptionOne : eOptionTwo); SetSecondaryVictoryPursuit(eOptionOne == VICTORY_PURSUIT_DIPLOMACY ? eOptionTwo : eOptionOne); return; } } else if (pTraits->IsSmaller()) { if (bCanUnlockPolicies) { SetPrimaryVictoryPursuit(eOptionOne == VICTORY_PURSUIT_CULTURE ? eOptionOne : eOptionTwo); SetSecondaryVictoryPursuit(eOptionOne == VICTORY_PURSUIT_CULTURE ? eOptionTwo : eOptionOne); return; } if (bCanUnlockTechs) { SetPrimaryVictoryPursuit(eOptionOne == VICTORY_PURSUIT_SCIENCE ? eOptionOne : eOptionTwo); SetSecondaryVictoryPursuit(eOptionOne == VICTORY_PURSUIT_SCIENCE ? eOptionTwo : eOptionOne); return; } } // If we still have no clue what to do, then just sort by enum order. SetPrimaryVictoryPursuit(eOptionOne < eOptionTwo ? eOptionOne : eOptionTwo); SetSecondaryVictoryPursuit(eOptionOne < eOptionTwo ? eOptionTwo : eOptionOne); return; } // If we made it this far, at least three options must be valid! // Human player if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { // First handle any exclusions if (!bCanUnlockTechs) { if (eSecondaryVictory == VICTORY_PURSUIT_SCIENCE) eSecondaryVictory = NO_VICTORY_PURSUIT; if (ePrimaryVictory == VICTORY_PURSUIT_SCIENCE) ePrimaryVictory = eSecondaryVictory; } if (!bCanUnlockPolicies) { if (eSecondaryVictory == VICTORY_PURSUIT_CULTURE) eSecondaryVictory = NO_VICTORY_PURSUIT; if (ePrimaryVictory == VICTORY_PURSUIT_CULTURE) ePrimaryVictory = eSecondaryVictory; } if (!bCanUseLeague) { if (eSecondaryVictory == VICTORY_PURSUIT_DIPLOMACY) eSecondaryVictory = NO_VICTORY_PURSUIT; if (ePrimaryVictory == VICTORY_PURSUIT_DIPLOMACY) ePrimaryVictory = eSecondaryVictory; } if (!bCanConquer) { if (eSecondaryVictory == VICTORY_PURSUIT_DOMINATION) eSecondaryVictory = NO_VICTORY_PURSUIT; if (ePrimaryVictory == VICTORY_PURSUIT_DOMINATION) ePrimaryVictory = eSecondaryVictory; } // Now if we have a victory pursuit hint, use it! // Humans don't use secondary victory pursuits unless specified in the database if (ePrimaryVictory != NO_VICTORY_PURSUIT) { SetPrimaryVictoryPursuit(ePrimaryVictory); SetSecondaryVictoryPursuit(eSecondaryVictory); return; } else { SetSecondaryVictoryPursuit(NO_VICTORY_PURSUIT); // No hint? Let's see if leader traits can provide some advice. if (bCanConquer && pTraits->IsWarmonger()) { SetPrimaryVictoryPursuit(VICTORY_PURSUIT_DOMINATION); return; } else if (bCanUseLeague && pTraits->IsDiplomat()) { SetPrimaryVictoryPursuit(VICTORY_PURSUIT_DIPLOMACY); return; } else if (bCanUnlockPolicies && pTraits->IsTourism()) { SetPrimaryVictoryPursuit(VICTORY_PURSUIT_CULTURE); return; } else if (bCanUnlockTechs && pTraits->IsNerd()) { SetPrimaryVictoryPursuit(VICTORY_PURSUIT_SCIENCE); return; } else { if (pTraits->IsExpansionist()) { if (bCanConquer) { SetPrimaryVictoryPursuit(VICTORY_PURSUIT_DOMINATION); return; } else if (bCanUseLeague) { SetPrimaryVictoryPursuit(VICTORY_PURSUIT_DIPLOMACY); return; } } if (pTraits->IsSmaller()) { if (bCanUnlockPolicies) { SetPrimaryVictoryPursuit(VICTORY_PURSUIT_CULTURE); return; } else if (bCanUnlockTechs) { SetPrimaryVictoryPursuit(VICTORY_PURSUIT_SCIENCE); return; } } // Still haven't come up with anything? Use this order of priority. if (bCanConquer) { SetPrimaryVictoryPursuit(VICTORY_PURSUIT_DOMINATION); return; } if (MOD_BALANCE_VP) // VP makes Culture Victory easier than Science Victory { if (bCanUnlockPolicies) { SetPrimaryVictoryPursuit(VICTORY_PURSUIT_CULTURE); return; } if (bCanUnlockTechs) { SetPrimaryVictoryPursuit(VICTORY_PURSUIT_SCIENCE); return; } } else { if (bCanUnlockTechs) { SetPrimaryVictoryPursuit(VICTORY_PURSUIT_SCIENCE); return; } if (bCanUnlockPolicies) { SetPrimaryVictoryPursuit(VICTORY_PURSUIT_CULTURE); return; } } } } } // AI player - we score victory pursuits based on flavors... CvFlavorManager* pFlavorMgr = GetPlayer()->GetFlavorManager(); vector VictoryScores(NUM_VICTORY_PURSUITS, 100); // ...and some randomness const PlayerTypes ID = GetID(); // Used to randomize values between different AIs const uint uRandom = GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES) ? 20 : 10; // Max. random weight added to each victory condition // Base likelihood depends on the difference between this civ's major approach biases VictoryScores[VICTORY_PURSUIT_DOMINATION] += max(GetMajorCivApproachBias(CIV_APPROACH_WAR), GetMajorCivApproachBias(CIV_APPROACH_HOSTILE)) - max(GetMajorCivApproachBias(CIV_APPROACH_FRIENDLY), GetMajorCivApproachBias(CIV_APPROACH_AFRAID)); VictoryScores[VICTORY_PURSUIT_DIPLOMACY] += max(GetMajorCivApproachBias(CIV_APPROACH_DECEPTIVE), GetMajorCivApproachBias(CIV_APPROACH_FRIENDLY)) - max(GetMajorCivApproachBias(CIV_APPROACH_WAR), GetMajorCivApproachBias(CIV_APPROACH_HOSTILE)); VictoryScores[VICTORY_PURSUIT_CULTURE] += max(GetMajorCivApproachBias(CIV_APPROACH_NEUTRAL), GetMajorCivApproachBias(CIV_APPROACH_FRIENDLY)) - max(GetMajorCivApproachBias(CIV_APPROACH_HOSTILE), GetMajorCivApproachBias(CIV_APPROACH_GUARDED)); VictoryScores[VICTORY_PURSUIT_SCIENCE] += max(GetMajorCivApproachBias(CIV_APPROACH_NEUTRAL), GetMajorCivApproachBias(CIV_APPROACH_GUARDED)) - max(GetMajorCivApproachBias(CIV_APPROACH_WAR), GetMajorCivApproachBias(CIV_APPROACH_DECEPTIVE)); // Weight for conquest VictoryScores[VICTORY_PURSUIT_DOMINATION] += GetBoldness(); VictoryScores[VICTORY_PURSUIT_DOMINATION] += GetMeanness(); VictoryScores[VICTORY_PURSUIT_DOMINATION] += pFlavorMgr->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_OFFENSE")) / 2; VictoryScores[VICTORY_PURSUIT_DOMINATION] += pFlavorMgr->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_EXPANSION")) / 2; VictoryScores[VICTORY_PURSUIT_DOMINATION] += static_cast(GC.getGame().urandRangeInclusive(1, uRandom, CvSeeder::fromRaw(0x905ce137).mix(ID))); // Weight for diplomacy VictoryScores[VICTORY_PURSUIT_DIPLOMACY] += GetMinorCivCompetitiveness(); VictoryScores[VICTORY_PURSUIT_DIPLOMACY] += GetWorkWithWillingness(); VictoryScores[VICTORY_PURSUIT_DIPLOMACY] += GetDiploBalance() / 2; VictoryScores[VICTORY_PURSUIT_DIPLOMACY] += pFlavorMgr->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_DIPLOMACY")) / 2; VictoryScores[VICTORY_PURSUIT_DIPLOMACY] += static_cast(GC.getGame().urandRangeInclusive(1, uRandom, CvSeeder::fromRaw(0xea5ade8b).mix(ID))); // Weight for culture VictoryScores[VICTORY_PURSUIT_CULTURE] += GetDoFWillingness(); VictoryScores[VICTORY_PURSUIT_CULTURE] += GetWonderCompetitiveness(); VictoryScores[VICTORY_PURSUIT_CULTURE] += pFlavorMgr->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_WONDER")) / 2; VictoryScores[VICTORY_PURSUIT_CULTURE] += pFlavorMgr->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_CULTURE")) / 2; VictoryScores[VICTORY_PURSUIT_CULTURE] += static_cast(GC.getGame().urandRangeInclusive(1, uRandom, CvSeeder::fromRaw(0xbccbe5fc).mix(ID))); // Weight for science VictoryScores[VICTORY_PURSUIT_SCIENCE] += GetLoyalty(); VictoryScores[VICTORY_PURSUIT_SCIENCE] += GetWarmongerHate(); VictoryScores[VICTORY_PURSUIT_SCIENCE] += pFlavorMgr->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_SCIENCE")) / 2; VictoryScores[VICTORY_PURSUIT_SCIENCE] += pFlavorMgr->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_GROWTH")) / 2; VictoryScores[VICTORY_PURSUIT_SCIENCE] += static_cast(GC.getGame().urandRangeInclusive(1, uRandom, CvSeeder::fromRaw(0x26353d52).mix(ID))); // Add leader bias for default primary pursuit if (ePrimaryVictory != NO_VICTORY_PURSUIT) VictoryScores[ePrimaryVictory] += eSecondaryVictory == NO_VICTORY_PURSUIT ? 8 : 5; // Default secondary pursuit adds a smaller amount of bias, if it exists // Note: If default primary pursuit is NO_VICTORY_PURSUIT, there will also be no default secondary pursuit if (eSecondaryVictory != NO_VICTORY_PURSUIT) VictoryScores[eSecondaryVictory] += 3; // Not many City-States? City-States aren't 100% essential but without them a Diplomatic Victory becomes much harder. int iNumMinorsEver = GC.getGame().GetNumMinorCivsEver(); int iNumMajorsEver = GC.getGame().GetNumMajorCivsEver(); if (iNumMinorsEver < iNumMajorsEver) VictoryScores[VICTORY_PURSUIT_DIPLOMACY] -= iNumMinorsEver <= iNumMajorsEver / 2 ? 10 : 5; // Reduce weight significantly if the associated victory condition is disabled. // But not entirely, because this is a civ's ROUTE to victory, not necessarily the victory condition they will achieve. // Increased reduction for Random Personalities to account for the increased random factor (don't want the option to make the AI stupid!) bool bDominationVictoryEnabled = GC.getGame().isVictoryValid((VictoryTypes) GC.getInfoTypeForString("VICTORY_DOMINATION", true)); bool bDiploVictoryEnabled = GC.getGame().isVictoryValid((VictoryTypes) GC.getInfoTypeForString("VICTORY_DIPLOMATIC", true)); bool bCultureVictoryEnabled = GC.getGame().isVictoryValid((VictoryTypes) GC.getInfoTypeForString("VICTORY_CULTURAL", true)); bool bScienceVictoryEnabled = GC.getGame().isVictoryValid((VictoryTypes) GC.getInfoTypeForString("VICTORY_SPACE_RACE", true)); if (!bDominationVictoryEnabled) VictoryScores[VICTORY_PURSUIT_DOMINATION] -= GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES) ? 15 : 10; if (!bDiploVictoryEnabled) VictoryScores[VICTORY_PURSUIT_DIPLOMACY] -= GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES) ? 15 : 10; if (!bCultureVictoryEnabled) VictoryScores[VICTORY_PURSUIT_CULTURE] -= GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES) ? 15 : 10; if (!bScienceVictoryEnabled) VictoryScores[VICTORY_PURSUIT_SCIENCE] -= GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES) ? 15 : 10; // Zero out all invalid scores if (!bCanConquer) VictoryScores[VICTORY_PURSUIT_DOMINATION] = 0; if (!bCanUseLeague) VictoryScores[VICTORY_PURSUIT_DIPLOMACY] = 0; if (!bCanUnlockPolicies) VictoryScores[VICTORY_PURSUIT_CULTURE] = 0; if (!bCanUnlockTechs) VictoryScores[VICTORY_PURSUIT_SCIENCE] = 0; // Sort the scores to find the highest two CvWeightedVector SortedVictoryScores; for (int iVictoryLoop = 0; iVictoryLoop < NUM_VICTORY_PURSUITS; iVictoryLoop++) { VictoryPursuitTypes eVictoryPursuit = (VictoryPursuitTypes) iVictoryLoop; SortedVictoryScores.push_back(eVictoryPursuit, VictoryScores[eVictoryPursuit]); } SortedVictoryScores.StableSortItems(); VictoryPursuitTypes eHighestScore = SortedVictoryScores.GetElement(0); VictoryPursuitTypes eRunnerUp = SortedVictoryScores.GetElement(1); VictoryPursuitTypes eChosenPrimary = NO_VICTORY_PURSUIT; if (SortedVictoryScores.GetWeight(0) > SortedVictoryScores.GetWeight(1)) { eChosenPrimary = eHighestScore; } // Do we have a tie between the top two? Use victory pursuit hints to resolve the tie. else { if (eHighestScore == ePrimaryVictory) { eChosenPrimary = eHighestScore; } else if (eRunnerUp == ePrimaryVictory) { eChosenPrimary = eRunnerUp; } else if (eHighestScore == eSecondaryVictory) { eChosenPrimary = eHighestScore; } else if (eRunnerUp == eSecondaryVictory) { eChosenPrimary = eRunnerUp; } // In this unusual case, let's see if player traits can help us out else { if (eHighestScore == VICTORY_PURSUIT_DOMINATION && pTraits->IsWarmonger()) eChosenPrimary = eHighestScore; else if (eHighestScore == VICTORY_PURSUIT_DIPLOMACY && pTraits->IsDiplomat()) eChosenPrimary = eHighestScore; else if (eHighestScore == VICTORY_PURSUIT_CULTURE && pTraits->IsTourism()) eChosenPrimary = eHighestScore; else if (eHighestScore == VICTORY_PURSUIT_SCIENCE && pTraits->IsNerd()) eChosenPrimary = eHighestScore; else if (eRunnerUp == VICTORY_PURSUIT_DOMINATION && pTraits->IsWarmonger()) eChosenPrimary = eRunnerUp; else if (eRunnerUp == VICTORY_PURSUIT_DIPLOMACY && pTraits->IsDiplomat()) eChosenPrimary = eRunnerUp; else if (eRunnerUp == VICTORY_PURSUIT_CULTURE && pTraits->IsTourism()) eChosenPrimary = eRunnerUp; else if (eRunnerUp == VICTORY_PURSUIT_SCIENCE && pTraits->IsNerd()) eChosenPrimary = eRunnerUp; // No? How about expansionist/smaller? else if ((eHighestScore == VICTORY_PURSUIT_DOMINATION || eHighestScore == VICTORY_PURSUIT_DIPLOMACY) && pTraits->IsExpansionist()) eChosenPrimary = eHighestScore; else if ((eHighestScore == VICTORY_PURSUIT_CULTURE || eHighestScore == VICTORY_PURSUIT_SCIENCE) && pTraits->IsSmaller()) eChosenPrimary = eHighestScore; else if ((eRunnerUp == VICTORY_PURSUIT_DOMINATION || eRunnerUp == VICTORY_PURSUIT_DIPLOMACY) && pTraits->IsExpansionist()) eChosenPrimary = eRunnerUp; else if ((eRunnerUp == VICTORY_PURSUIT_CULTURE || eRunnerUp == VICTORY_PURSUIT_SCIENCE) && pTraits->IsSmaller()) eChosenPrimary = eRunnerUp; // If we made it this far and still haven't selected something, there's no breaking this tie, just go with the first option lol else eChosenPrimary = eHighestScore; } } // Override from game options? Only apply if valid. if (MOD_DIPLOAI_LIMIT_VICTORY_PURSUIT_RANDOMIZATION) { bool bValid = false; switch (ePrimaryVictory) { case NO_VICTORY_PURSUIT: break; case VICTORY_PURSUIT_DOMINATION: bValid = bCanConquer && bDominationVictoryEnabled; break; case VICTORY_PURSUIT_DIPLOMACY: bValid = bCanUseLeague && bDiploVictoryEnabled; break; case VICTORY_PURSUIT_CULTURE: bValid = bCanUnlockPolicies && bCultureVictoryEnabled; break; case VICTORY_PURSUIT_SCIENCE: bValid = bCanUnlockTechs && bScienceVictoryEnabled; break; } if (bValid) eChosenPrimary = ePrimaryVictory; } SetPrimaryVictoryPursuit(eChosenPrimary); // Now we pick a secondary pursuit. VictoryPursuitTypes eCandidate = NO_VICTORY_PURSUIT; // Case 1: The default primary pursuit is the one we chose. Pick the default secondary pursuit (if any). if (eChosenPrimary == ePrimaryVictory) { eCandidate = eSecondaryVictory; } // Case 2: "No Secondary Victory Pursuit Randomization" is enabled. Don't pick anything. else if (MOD_DIPLOAI_LIMIT_VICTORY_PURSUIT_RANDOMIZATION && gCustomMods.getOption("DIPLOAI_LIMIT_VICTORY_PURSUIT_SETTING", 1) == 2) { SetSecondaryVictoryPursuit(NO_VICTORY_PURSUIT); return; } // Case 3: The default primary pursuit is NOT the one we chose, but there is a default primary pursuit. Pick it! else if (eChosenPrimary != ePrimaryVictory && ePrimaryVictory != NO_VICTORY_PURSUIT) { eCandidate = ePrimaryVictory; } // Check that our candidate is valid if (!GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { bool bValid = false; switch (eCandidate) { case NO_VICTORY_PURSUIT: break; case VICTORY_PURSUIT_DOMINATION: bValid = bCanConquer && bDominationVictoryEnabled; break; case VICTORY_PURSUIT_DIPLOMACY: bValid = bCanUseLeague && bDiploVictoryEnabled; break; case VICTORY_PURSUIT_CULTURE: bValid = bCanUnlockPolicies && bCultureVictoryEnabled; break; case VICTORY_PURSUIT_SCIENCE: bValid = bCanUnlockTechs && bScienceVictoryEnabled; break; } if (bValid) { SetSecondaryVictoryPursuit(eCandidate); return; } } // Case 4: No default primary or secondary pursuit in the database (generalist civ, or the modder didn't specify it for a custom civ). // We might also end up here if, in Cases 1 or 3, the pursuit hint is invalid or Random Personalities is enabled. // In any of these cases, we use the second highest scoring pursuit. SetSecondaryVictoryPursuit(eChosenPrimary == eHighestScore ? eRunnerUp : eHighestScore); } // ----------------------------------------------------------------------------------------------- /// How much importance does this AI leader place on competing for Victory? int CvDiplomacyAI::GetVictoryCompetitiveness() const { return m_iVictoryCompetitiveness; } /// How much importance does this AI leader place on obtaining World Wonders? int CvDiplomacyAI::GetWonderCompetitiveness() const { return m_iWonderCompetitiveness; } /// How possessive is this AI leader's attitude towards "their" City-States? int CvDiplomacyAI::GetMinorCivCompetitiveness() const { return m_iMinorCivCompetitiveness; } /// How likely is this AI leader to take risks / go for World Conquest? int CvDiplomacyAI::GetBoldness() const { return m_iBoldness; } /// How much does this AI leader want to maintain a balance of power in the world? int CvDiplomacyAI::GetDiploBalance() const { return m_iDiploBalance; } /// How much does this AI leader value peace and hate others warmongering? int CvDiplomacyAI::GetWarmongerHate() const { return m_iWarmongerHate; } /// How much is this AI leader willing to befriend other players? int CvDiplomacyAI::GetDoFWillingness() const { return m_iDoFWillingness; } /// How much is this AI leader willing to badmouth and deceive other players? int CvDiplomacyAI::GetDenounceWillingness() const { return m_iDenounceWillingness; } /// How much is this AI leader willing to form strategic alliances WITH others? int CvDiplomacyAI::GetWorkWithWillingness() const { return m_iWorkWithWillingness; } /// How much is this AI leader willing to form strategic alliances AGAINST others? int CvDiplomacyAI::GetWorkAgainstWillingness() const { return m_iWorkAgainstWillingness; } /// How likely is this AI leader to refrain from backstabbing their friends? int CvDiplomacyAI::GetLoyalty() const { return m_iLoyalty; } /// How willing is this AI leader to overlook transgressions against them? int CvDiplomacyAI::GetForgiveness() const { return m_iForgiveness; } /// How much does this AI leader want the support of its friends in rough times? int CvDiplomacyAI::GetNeediness() const { return m_iNeediness; } /// How much does this AI leader like to bully others? int CvDiplomacyAI::GetMeanness() const { return m_iMeanness; } /// How much does this AI leader like to pop up and talk? int CvDiplomacyAI::GetChattiness() const { return m_iChattiness; } /// What is this AI leader's bias towards a particular Major Civ Approach? int CvDiplomacyAI::GetMajorCivApproachBias(CivApproachTypes eApproach) const { PRECONDITION(eApproach >= 0 && eApproach < NUM_CIV_APPROACHES, "Approach index out of bounds"); return m_aiMajorCivApproachBiases[eApproach]; } /// What is this AI leader's bias towards a particular Minor Civ Approach? int CvDiplomacyAI::GetMinorCivApproachBias(CivApproachTypes eApproach, bool bHideAssert) const { PRECONDITION(eApproach >= 0 && eApproach < NUM_CIV_APPROACHES, "Approach index out of bounds"); switch (eApproach) { case CIV_APPROACH_WAR: return m_iMinorCivWarBias; case CIV_APPROACH_HOSTILE: return m_iMinorCivHostileBias; case CIV_APPROACH_NEUTRAL: return m_iMinorCivNeutralBias; case CIV_APPROACH_FRIENDLY: return m_iMinorCivFriendlyBias; default: ASSERT(bHideAssert, "GetMinorCivApproachBias() called for major Approach"); return 0; } } /// At what warscore threshold does the AI consider itself to be winning a war? int CvDiplomacyAI::GetWarscoreThresholdPositive() const { return WARSCORE_THRESHOLD_POSITIVE + 5 - GetDiploBalance(); } /// At what warscore threshold does the AI consider itself to be losing a war? int CvDiplomacyAI::GetWarscoreThresholdNegative() const { return WARSCORE_THRESHOLD_NEGATIVE + 5 - GetMeanness(); } // ----------------------------------------------------------------------------------------------- /// What is this AI leader's default Primary Victory Pursuit (after randomization)? VictoryPursuitTypes CvDiplomacyAI::GetPrimaryVictoryPursuit() const { return (VictoryPursuitTypes) m_ePrimaryVictoryPursuit; } void CvDiplomacyAI::SetPrimaryVictoryPursuit(VictoryPursuitTypes eVictoryPursuit) { PRECONDITION(eVictoryPursuit >= NO_VICTORY_PURSUIT && eVictoryPursuit < NUM_VICTORY_PURSUITS, "Invalid VictoryPursuitType"); m_ePrimaryVictoryPursuit = eVictoryPursuit; } /// Is this AI leader naturally inclined to be aggressive and covetous of others' possessions? bool CvDiplomacyAI::IsConqueror() const { return GetPrimaryVictoryPursuit() == VICTORY_PURSUIT_DOMINATION; } /// Is this AI leader naturally focused on forging friendships and allying City-States? bool CvDiplomacyAI::IsDiplomat() const { return GetPrimaryVictoryPursuit() == VICTORY_PURSUIT_DIPLOMACY; } /// Is this AI leader naturally focused on spreading their culture across the world? bool CvDiplomacyAI::IsCultural() const { return GetPrimaryVictoryPursuit() == VICTORY_PURSUIT_CULTURE; } /// Is this AI leader naturally focused on their civilization's technological advancement? bool CvDiplomacyAI::IsScientist() const { return GetPrimaryVictoryPursuit() == VICTORY_PURSUIT_SCIENCE; } /// What is this AI leader's default Secondary Victory Pursuit (after randomization)? This is not as important as the Primary Victory Pursuit, but still has an effect. VictoryPursuitTypes CvDiplomacyAI::GetSecondaryVictoryPursuit() const { return (VictoryPursuitTypes) m_eSecondaryVictoryPursuit; } void CvDiplomacyAI::SetSecondaryVictoryPursuit(VictoryPursuitTypes eVictoryPursuit) { PRECONDITION(eVictoryPursuit >= NO_VICTORY_PURSUIT && eVictoryPursuit < NUM_VICTORY_PURSUITS, "Invalid VictoryPursuitType"); m_eSecondaryVictoryPursuit = eVictoryPursuit; } /// Is this AI leader secondarily inclined to be aggressive and covetous of others' possessions? bool CvDiplomacyAI::IsSecondaryConqueror() const { return GetSecondaryVictoryPursuit() == VICTORY_PURSUIT_DOMINATION; } /// Is this AI leader secondarily focused on forging friendships and allying City-States? bool CvDiplomacyAI::IsSecondaryDiplomat() const { return GetSecondaryVictoryPursuit() == VICTORY_PURSUIT_DIPLOMACY; } /// Is this AI leader secondarily focused on spreading their culture across the world? bool CvDiplomacyAI::IsSecondaryCultural() const { return GetSecondaryVictoryPursuit() == VICTORY_PURSUIT_CULTURE; } /// Is this AI leader secondarily focused on their civilization's technological advancement? bool CvDiplomacyAI::IsSecondaryScientist() const { return GetSecondaryVictoryPursuit() == VICTORY_PURSUIT_SCIENCE; } /// Will this leader *ALWAYS* go for a specific victory condition? VictoryPursuitTypes CvDiplomacyAI::GetEternalVictoryPursuit() const { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return NO_VICTORY_PURSUIT; bool bDominationVictoryEnabled = GC.getGame().CanPlayerAttemptDominationVictory(GetID(), NO_PLAYER, true); bool bDiploVictoryEnabled = !GC.getGame().isOption(GAMEOPTION_NO_LEAGUES) && GC.getGame().isVictoryValid((VictoryTypes) GC.getInfoTypeForString("VICTORY_DIPLOMATIC", true)); bool bCultureVictoryEnabled = !GC.getGame().isOption(GAMEOPTION_NO_POLICIES) && GC.getGame().isVictoryValid((VictoryTypes) GC.getInfoTypeForString("VICTORY_CULTURAL", true)); bool bScienceVictoryEnabled = !GC.getGame().isOption(GAMEOPTION_NO_SCIENCE) && GC.getGame().isVictoryValid((VictoryTypes) GC.getInfoTypeForString("VICTORY_SPACE_RACE", true)); LeaderHeadTypes leader = GetPlayer()->getLeaderType(); if (leader != NO_LEADER) { CvLeaderHeadInfo* pkLeaderHeadInfo = GC.getLeaderHeadInfo(leader); if (pkLeaderHeadInfo) { // General override active for all leaders? if (MOD_DIPLOAI_LIMIT_VICTORY_PURSUIT_RANDOMIZATION) { int iLimitSetting = gCustomMods.getOption("DIPLOAI_LIMIT_VICTORY_PURSUIT_SETTING", 1); if (iLimitSetting == 3) { VictoryPursuitTypes ePrimaryVictory = pkLeaderHeadInfo->GetPrimaryVictoryPursuit(); VictoryPursuitTypes eSecondaryVictory = pkLeaderHeadInfo->GetSecondaryVictoryPursuit(); if (ePrimaryVictory == VICTORY_PURSUIT_DOMINATION && bDominationVictoryEnabled) return VICTORY_PURSUIT_DOMINATION; if (ePrimaryVictory == VICTORY_PURSUIT_DIPLOMACY && bDiploVictoryEnabled) return VICTORY_PURSUIT_DIPLOMACY; if (ePrimaryVictory == VICTORY_PURSUIT_CULTURE && bCultureVictoryEnabled) return VICTORY_PURSUIT_CULTURE; if (ePrimaryVictory == VICTORY_PURSUIT_SCIENCE && bScienceVictoryEnabled) return VICTORY_PURSUIT_SCIENCE; if (eSecondaryVictory == VICTORY_PURSUIT_DOMINATION && bDominationVictoryEnabled) return VICTORY_PURSUIT_DOMINATION; if (eSecondaryVictory == VICTORY_PURSUIT_DIPLOMACY && bDiploVictoryEnabled) return VICTORY_PURSUIT_DIPLOMACY; if (eSecondaryVictory == VICTORY_PURSUIT_CULTURE && bCultureVictoryEnabled) return VICTORY_PURSUIT_CULTURE; if (eSecondaryVictory == VICTORY_PURSUIT_SCIENCE && bScienceVictoryEnabled) return VICTORY_PURSUIT_SCIENCE; } } // Individual override active for this leader? if (pkLeaderHeadInfo->GetBoldness() == -12 && bDominationVictoryEnabled) return VICTORY_PURSUIT_DOMINATION; // Follow the white cloak. if (pkLeaderHeadInfo->GetMinorCivCompetitiveness() == -12 && bDiploVictoryEnabled) return VICTORY_PURSUIT_DIPLOMACY; // The benefits of being able to force a dialogue in place of a duel. if (pkLeaderHeadInfo->GetWonderCompetitiveness() == -12 && bCultureVictoryEnabled) return VICTORY_PURSUIT_CULTURE; // Now, the sky had alighted in glorious colour! They were feeling nostalgic for a place they were still experiencing, and it made them happy. if (pkLeaderHeadInfo->GetWarmongerHate() == -12 && bScienceVictoryEnabled) return VICTORY_PURSUIT_SCIENCE; // It'd worked exactly as he'd thought. The scientist's brains over the god's brawn had found a way forward. } } return NO_VICTORY_PURSUIT; } // ************************************ // Memory Management // ************************************ // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Diplomatic Interactions // ------------------------------------ /// When did we last send eDiploLogStatement? int CvDiplomacyAI::GetTurnStatementLastSent(PlayerTypes ePlayer, DiploStatementTypes eDiploStatement) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS && eDiploStatement >= 0 && eDiploStatement < NUM_DIPLO_STATEMENT_TYPES, "Array index out of bounds"); return m_aaiTurnStatementLastSent[ePlayer][eDiploStatement]; } void CvDiplomacyAI::SetTurnStatementLastSent(PlayerTypes ePlayer, DiploStatementTypes eDiploStatement, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS && eDiploStatement >= 0 && eDiploStatement < NUM_DIPLO_STATEMENT_TYPES, "Array index out of bounds"); ASSERT(iValue >= -1, "Setting TurnStatementLastSent to invalid value"); m_aaiTurnStatementLastSent[ePlayer][eDiploStatement] = iValue; } /// How long has it been since we sent eDiploLogStatement? int CvDiplomacyAI::GetNumTurnsSinceStatementSent(PlayerTypes ePlayer, DiploStatementTypes eDiploStatement) const { int iTurn = GetTurnStatementLastSent(ePlayer, eDiploStatement); if (iTurn == -1) return INT_MAX; return GC.getGame().getGameTurn() - iTurn; } /// How long has it been since we sent something to ePlayer? int CvDiplomacyAI::GetNumTurnsSinceSomethingSent(PlayerTypes ePlayer) const { int iMostRecentTurn = -1; for (int iI = 0; iI < NUM_DIPLO_STATEMENT_TYPES; iI++) { int iTurn = GetTurnStatementLastSent(ePlayer, (DiploStatementTypes)iI); if (iTurn > iMostRecentTurn) iMostRecentTurn = iTurn; } if (iMostRecentTurn == -1) return 0; return GC.getGame().getGameTurn() - iMostRecentTurn; } /// Have we approached another civ about attacking their protected minor? bool CvDiplomacyAI::HasSentAttackProtectedMinorTaunt(PlayerTypes ePlayer, PlayerTypes eMinor) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS && eMinor >= MAX_MAJOR_CIVS && eMinor < MAX_CIV_PLAYERS, "Array index out of bounds"); return m_aabSentAttackMessageToMinorCivProtector[ePlayer][eMinor - MAX_MAJOR_CIVS]; } void CvDiplomacyAI::SetSentAttackProtectedMinorTaunt(PlayerTypes ePlayer, PlayerTypes eMinor, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS && eMinor >= MAX_MAJOR_CIVS && eMinor < MAX_CIV_PLAYERS, "Array index out of bounds"); ASSERT(NotTeam(ePlayer), "Changing SentAttackMessageToMinorCivProtector for own team"); m_aabSentAttackMessageToMinorCivProtector[ePlayer][eMinor - MAX_MAJOR_CIVS] = bValue; } /// Reset whether we have approached another civ about attacking a protected minor (i.e., once peace is made with the minor) void CvDiplomacyAI::ResetSentAttackProtectedMinorTaunts(PlayerTypes eMinor) { for (int iI = 0; iI < MAX_MAJOR_CIVS; iI++) { PlayerTypes ePlayer = (PlayerTypes)iI; if (GET_PLAYER(ePlayer).getTeam() != GetTeam()) SetSentAttackProtectedMinorTaunt(ePlayer, eMinor, false); } } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Victory Competition // ------------------------------------ /// Are we competing to win this game? bool CvDiplomacyAI::IsCompetingForVictory() const { return m_bCompetingForVictory; } void CvDiplomacyAI::SetCompetingForVictory(bool bValue) { m_bCompetingForVictory = bValue; } /// What victory condition are we currently focusing on? VictoryPursuitTypes CvDiplomacyAI::GetCurrentVictoryPursuit() const { return (VictoryPursuitTypes) m_eCurrentVictoryPursuit; } void CvDiplomacyAI::SetCurrentVictoryPursuit(VictoryPursuitTypes eVictoryPursuit) { PRECONDITION(eVictoryPursuit >= NO_VICTORY_PURSUIT && eVictoryPursuit < NUM_VICTORY_PURSUITS, "Invalid VictoryPursuitType"); m_eCurrentVictoryPursuit = eVictoryPursuit; } /// Does this player want to conquer the world? bool CvDiplomacyAI::IsGoingForWorldConquest() const { return GetCurrentVictoryPursuit() == VICTORY_PURSUIT_DOMINATION; } /// Does this player want to win by diplomacy? bool CvDiplomacyAI::IsGoingForDiploVictory() const { return GetCurrentVictoryPursuit() == VICTORY_PURSUIT_DIPLOMACY; } /// Does this player want to win by culture? bool CvDiplomacyAI::IsGoingForCultureVictory() const { return GetCurrentVictoryPursuit() == VICTORY_PURSUIT_CULTURE; } /// Does this player want to win by science? bool CvDiplomacyAI::IsGoingForSpaceshipVictory() const { return GetCurrentVictoryPursuit() == VICTORY_PURSUIT_SCIENCE; } /// Are we extra aggressive towards this player because they're close to victory? bool CvDiplomacyAI::IsEndgameAggressiveTo(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abEndgameAggressiveTo[ePlayer]; } void CvDiplomacyAI::SetEndgameAggressiveTo(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(!bValue || NotTeam(ePlayer), "Setting EndgameAggressive to true for own team"); m_abEndgameAggressiveTo[ePlayer] = bValue; } /// Is this player expanding recklessly? bool CvDiplomacyAI::IsRecklessExpander(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abRecklessExpander[ePlayer]; } void CvDiplomacyAI::SetRecklessExpander(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(!bValue || NotTeam(ePlayer), "Setting RecklessExpander to true for own team"); m_abRecklessExpander[ePlayer] = bValue; } /// Is this player spamming World Wonders? bool CvDiplomacyAI::IsWonderSpammer(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abWonderSpammer[ePlayer]; } void CvDiplomacyAI::SetWonderSpammer(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(!bValue || NotTeam(ePlayer), "Setting WonderSpammer to true for own team"); m_abWonderSpammer[ePlayer] = bValue; } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Victory Progress // ------------------------------------ /// How close are we to achieving a Time victory? /// FIXME: Needs a "is time victory disabled?" check before this function can be of much use int CvDiplomacyAI::GetScoreVictoryProgress() const { if (!m_pPlayer->isAlive() || GC.getGame().getWinner() != NO_TEAM) return 0; int iProgress = 0; int iOurScore = 0; int iHighestScore = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes)iPlayerLoop; if (GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv()) { int iScore = GET_PLAYER(eLoopPlayer).GetScore(); if (iScore > iHighestScore) { iHighestScore = iScore; } if (eLoopPlayer == GetPlayer()->GetID()) { iOurScore = iScore; } } } iProgress = (iOurScore * 100) / max(1, iHighestScore); iProgress *= GC.getGame().getGameTurn(); iProgress /= max(1,GC.getGame().getMaxTurns()); return iProgress; } /// How close are we to achieving a Domination victory? int CvDiplomacyAI::GetDominationVictoryProgress() const { if (!m_pPlayer->isAlive() || !GC.getGame().CanPlayerAttemptDominationVictory(GetID(), NO_PLAYER, false)) return 0; int iCapitalsProgress = 0; int iTotalCapitals = 0; int iOurMight = 0; int iTotalMight = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; if (!GET_PLAYER(ePlayer).isMajorCiv()) continue; CvPlot* pOriginalCapitalPlot = GC.getMap().plot(GET_PLAYER(ePlayer).GetOriginalCapitalX(), GET_PLAYER(ePlayer).GetOriginalCapitalY()); if (!pOriginalCapitalPlot || !pOriginalCapitalPlot->isCity()) continue; iTotalCapitals++; if (GET_PLAYER(ePlayer).isAlive() && GET_PLAYER(ePlayer).getNumCities() > 0) { int iMight = GET_PLAYER(ePlayer).GetMilitaryMight(); iTotalMight += iMight; if (GET_PLAYER(ePlayer).getTeam() == GetTeam() || IsMaster(ePlayer)) iOurMight += iMight; } if (pOriginalCapitalPlot->getPlotCity()->GetOwnerForDominationVictory() == GetID()) iCapitalsProgress += 1; } iOurMight = (iOurMight * 100) / max(1, iTotalMight); iCapitalsProgress = (iCapitalsProgress * 100) / max(1, iTotalCapitals); return max(iOurMight, iCapitalsProgress); } /// How close are we to achieving a Diplomatic victory? int CvDiplomacyAI::GetDiplomaticVictoryProgress() const { CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (!pLeague || !m_pPlayer->isAlive() || GC.getGame().isOption(GAMEOPTION_NO_LEAGUES) || !GC.getGame().isVictoryValid((VictoryTypes)GC.getInfoTypeForString("VICTORY_DIPLOMATIC", true)) || GC.getGame().getWinner() != NO_TEAM) return 0; int iOurVotes = pLeague->CalculateStartingVotesForMember(GetPlayer()->GetID(), /*bFakeUN*/ true); int iNeededVotes = GC.getGame().GetVotesNeededForDiploVictory(); int iProgress = (iOurVotes * 100) / max(1, iNeededVotes); int iExtra = 0; if (pLeague->IsUnitedNations()) { iExtra += 1; } if (MOD_BALANCE_VP) { for (int i = 0; i < GC.getNumLeagueSpecialSessionInfos(); i++) { LeagueSpecialSessionTypes e = (LeagueSpecialSessionTypes)i; CvLeagueSpecialSessionEntry* pSessionInfo = GC.getLeagueSpecialSessionInfo(e); if (pSessionInfo->IsUnitedNations()) { ResolutionTypes eResolution = LeagueHelpers::IsResolutionForTriggerActive(pSessionInfo->GetResolutionTrigger()); if (eResolution != NO_RESOLUTION) { iExtra += 1; } } } } if (iExtra == 0) { iProgress = min(iProgress, 59); } else { iProgress = min(iProgress, 59 + 20 * iExtra); } return iProgress; } /// How close are we to achieving a Science victory? int CvDiplomacyAI::GetScienceVictoryProgress() const { VictoryTypes eSpaceVictory = (VictoryTypes)GC.getInfoTypeForString("VICTORY_SPACE_RACE", true); if (!m_pPlayer->isAlive() || GC.getGame().isOption(GAMEOPTION_NO_SCIENCE) || !GC.getGame().isVictoryValid(eSpaceVictory) || GC.getGame().getWinner() != NO_TEAM) return 0; int iProjectsRequired = 0; int iProjectsCompleted = 0; for (int iK = 0; iK < GC.getNumProjectInfos(); iK++) { const ProjectTypes eProject = static_cast(iK); CvProjectEntry* pkProjectInfo = GC.getProjectInfo(eProject); if (pkProjectInfo) { iProjectsRequired += pkProjectInfo->GetVictoryMinThreshold(eSpaceVictory); iProjectsCompleted += GET_TEAM(m_pPlayer->getTeam()).getProjectCount(eProject); } } ProjectTypes eApollo = (ProjectTypes)GC.getInfoTypeForString("PROJECT_APOLLO_PROGRAM", true); if (eApollo != NO_PROJECT) { iProjectsRequired++; if (GET_TEAM(m_pPlayer->getTeam()).getProjectCount(eApollo) > 0) { iProjectsCompleted++; } } int iScienceProgress = (GET_TEAM(m_pPlayer->getTeam()).GetTeamTechs()->GetNumTechsKnown() * 100) / max(1, GC.getNumTechInfos() - 1); int iSpaceshipProgress = (21 * iProjectsCompleted) / max(1, iProjectsRequired); int iProgress = min(iScienceProgress, 78 + iSpaceshipProgress); return iProgress; } /// How close are we to achieving a Culture victory? int CvDiplomacyAI::GetCultureVictoryProgress() const { if (!m_pPlayer->isAlive() || GC.getGame().isOption(GAMEOPTION_NO_POLICIES) || !GC.getGame().isVictoryValid((VictoryTypes)GC.getInfoTypeForString("VICTORY_CULTURAL", true)) || GC.getGame().getWinner() != NO_TEAM) return 0; int iLowestPercent = GetLowestTourismInfluence(); int iProgress = min(99,iLowestPercent); if (MOD_BALANCE_CULTURE_VICTORY_CHANGES) { int iPolicies = GetPlayer()->GetPlayerPolicies()->GetNumPoliciesOwned(true, true, true); iPolicies = min(iPolicies, 27); iProgress = min(iLowestPercent, 18 + iPolicies * 3); } return iProgress; } int CvDiplomacyAI::GetLowestTourismInfluence() const { int iLowestPercent = /*100*/ GD_INT_GET(CULTURE_LEVEL_INFLUENTIAL); // Don't want to target civs if already influential for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; CvPlayer& kPlayer = GET_PLAYER(eLoopPlayer); if (eLoopPlayer != GetID() && kPlayer.isAlive() && kPlayer.isMajorCiv()) { long long lInfluenceOn = GetPlayer()->GetCulture()->GetInfluenceOnTimes100(eLoopPlayer); long long lLifetimeCulture = kPlayer.GetJONSCultureEverGeneratedTimes100(); int iPercent = 0; if (lInfluenceOn > 0) { if (lLifetimeCulture > 0) { iPercent = (int)(lInfluenceOn * 100 / lLifetimeCulture); } } if (iPercent < 0) { iPercent = 0; } if (iPercent < iLowestPercent) { iLowestPercent = iPercent; } } } return iLowestPercent; } // ----------------------------------------------------------------------------------------------- /// Is this player close to ANY victory condition? bool CvDiplomacyAI::IsCloseToAnyVictoryCondition() const { if (GC.getGame().getWinner() != NO_TEAM) return false; if (IsCloseToWorldConquest() || IsCloseToDiploVictory() || IsCloseToSpaceshipVictory() || IsCloseToCultureVictory()) { return true; } return false; } /// Is this player close to a domination victory? bool CvDiplomacyAI::IsCloseToWorldConquest() const { return GetDominationVictoryProgress() >= /*50*/ GD_INT_GET(CLOSE_TO_DOMINATION_VICTORY_THRESHOLD); } /// Is this player close to a diplomatic victory? bool CvDiplomacyAI::IsCloseToDiploVictory() const { return GetDiplomaticVictoryProgress() >= /*55*/ GD_INT_GET(CLOSE_TO_DIPLOMATIC_VICTORY_THRESHOLD); } /// Is this player close to a science victory? bool CvDiplomacyAI::IsCloseToSpaceshipVictory() const { return GetScienceVictoryProgress() >= /*80*/ GD_INT_GET(CLOSE_TO_SCIENCE_VICTORY_THRESHOLD); } /// Is this player close to a cultural victory? bool CvDiplomacyAI::IsCloseToCultureVictory() const { return GetCultureVictoryProgress() >= /*60*/ GD_INT_GET(CLOSE_TO_CULTURE_VICTORY_THRESHOLD); } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Opinion & Approach // ------------------------------------ /// What is our Diplomatic Opinion of this Major Civ? CivOpinionTypes CvDiplomacyAI::GetCivOpinion(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_PLAYERS, "Player index out of bounds"); if (ePlayer == BARBARIAN_PLAYER) return CIV_OPINION_UNFORGIVABLE; // If called for a City-State, map to approach if (ePlayer >= MAX_MAJOR_CIVS) { switch (GetCivApproach(ePlayer)) { case CIV_APPROACH_WAR: return CIV_OPINION_ENEMY; case CIV_APPROACH_HOSTILE: return CIV_OPINION_COMPETITOR; case CIV_APPROACH_FRIENDLY: return GET_PLAYER(ePlayer).GetMinorCivAI()->IsAllies(GetID()) ? CIV_OPINION_ALLY : CIV_OPINION_FRIEND; default: return CIV_OPINION_NEUTRAL; } } return (CivOpinionTypes) m_aeCivOpinion[ePlayer]; } void CvDiplomacyAI::SetCivOpinion(PlayerTypes ePlayer, CivOpinionTypes eOpinion) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(eOpinion >= 0 && eOpinion < NUM_CIV_OPINIONS, "Setting CivOpinion to invalid value"); ASSERT(NotTeam(ePlayer) && !IsAlwaysAtWar(ePlayer), "Updating static CivOpinion value"); m_aeCivOpinion[ePlayer] = eOpinion; } /// What is our cached opinion weight for this Major Civ? int CvDiplomacyAI::GetCachedOpinionWeight(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiCachedOpinionWeight[ePlayer]; } void CvDiplomacyAI::SetCachedOpinionWeight(PlayerTypes ePlayer, int iWeight) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer) && !IsAlwaysAtWar(ePlayer), "Updating static CachedOpinionWeight value"); m_aiCachedOpinionWeight[ePlayer] = range(iWeight, SHRT_MIN, SHRT_MAX); } /// What is our Diplomatic Approach towards this Civilization? CivApproachTypes CvDiplomacyAI::GetCivApproach(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_PLAYERS, "Player index out of bounds"); return ePlayer != BARBARIAN_PLAYER ? (CivApproachTypes) m_aeCivApproach[ePlayer] : CIV_APPROACH_WAR; } void CvDiplomacyAI::SetCivApproach(PlayerTypes ePlayer, CivApproachTypes eApproach, bool bResetAttackOperations) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); PRECONDITION(eApproach >= 0 && eApproach < NUM_CIV_APPROACHES, "Setting CivApproach to invalid value"); PRECONDITION((ePlayer < MAX_MAJOR_CIVS) || (eApproach != CIV_APPROACH_DECEPTIVE && eApproach != CIV_APPROACH_GUARDED && eApproach != CIV_APPROACH_AFRAID), "Setting CivApproach towards City-State to a major approach"); ASSERT(NotTeam(ePlayer), "Updating static CivApproach value"); m_aeCivApproach[ePlayer] = eApproach; // Planning war? Pick a surface approach to disguise our war plans. if (eApproach == CIV_APPROACH_WAR) { // No surface approaches for City-States! if (ePlayer >= MAX_MAJOR_CIVS) return; // If we're targeting them for a demand, cancel that if (bResetAttackOperations && GetDemandTargetPlayer() == ePlayer) SetDemandTargetPlayer(NO_PLAYER); // We don't need a surface approach while *at* war. Approaches are updated in DoWeMadePeaceWithSomeone(), so there's no need to store one for later. if (IsAtWar(ePlayer)) { SetCachedSurfaceApproach(ePlayer, -1); return; } CivApproachTypes eSurfaceApproach = CIV_APPROACH_NEUTRAL; int iCurrentSurfaceApproach = GetCachedSurfaceApproach(ePlayer); if (iCurrentSurfaceApproach != -1) { eSurfaceApproach = (CivApproachTypes)iCurrentSurfaceApproach; // If we were just denounced or they ended our friendship, can't be better than GUARDED if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncingPlayer(GetID()) || (IsDoFBroken(ePlayer) && GetTurnsSinceDoFBroken(ePlayer) <= 1)) { if (eSurfaceApproach > CIV_APPROACH_GUARDED) { eSurfaceApproach = CIV_APPROACH_GUARDED; } } if (eSurfaceApproach == CIV_APPROACH_HOSTILE) { if (IsLiberator(ePlayer, false, true) || (IsCityRecentlyLiberatedBy(ePlayer) && GetPlayer()->getCitiesLost() > 0 && !GET_PLAYER(ePlayer).GetDiplomacyAI()->IsCloseToWorldConquest() && !IsEndgameAggressiveTo(ePlayer))) { eSurfaceApproach = CIV_APPROACH_GUARDED; } } else if (eSurfaceApproach == CIV_APPROACH_FRIENDLY) { if (IsDenouncedPlayer(ePlayer) || IsDenouncedByPlayer(ePlayer) || IsUntrustworthy(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsRecentDemandAccepted(GetID()) || CvDiplomacyAIHelpers::ProposedSanctionsBlockingDiplomacy(GetID(), ePlayer)) { eSurfaceApproach = GetCivOpinion(ePlayer) <= CIV_OPINION_ENEMY ? CIV_APPROACH_GUARDED : CIV_APPROACH_NEUTRAL; } } GetPlayer()->GetDiplomacyAI()->SetCachedSurfaceApproach(ePlayer, (int)eSurfaceApproach); return; } else { eSurfaceApproach = GetHighestValueApproach(ePlayer, /*bExcludeWar*/ true, /*bIncludeOverrides*/ true); // If we were just denounced or they ended our friendship, can't be better than GUARDED if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncingPlayer(GetID()) || (IsDoFBroken(ePlayer) && GetTurnsSinceDoFBroken(ePlayer) <= 1)) { // Reset to GUARDED if approach is FRIENDLY, DECEPTIVE, or higher than GUARDED if (eSurfaceApproach > CIV_APPROACH_GUARDED || eSurfaceApproach == CIV_APPROACH_DECEPTIVE) { eSurfaceApproach = CIV_APPROACH_GUARDED; } } // Don't pretend to be afraid if we're not, and Deceptive = Friendly if (eSurfaceApproach == CIV_APPROACH_AFRAID) GetPlayer()->GetDiplomacyAI()->SetCachedSurfaceApproach(ePlayer, (int)CIV_APPROACH_GUARDED); else if (eSurfaceApproach == CIV_APPROACH_DECEPTIVE) GetPlayer()->GetDiplomacyAI()->SetCachedSurfaceApproach(ePlayer, (int)CIV_APPROACH_FRIENDLY); else GetPlayer()->GetDiplomacyAI()->SetCachedSurfaceApproach(ePlayer, (int)eSurfaceApproach); return; } } // If our new approach is something other than WAR, cancel any sneak attack operations else if (!IsAtWar(ePlayer) && GetDemandTargetPlayer() != ePlayer && (GetPlayer()->IsNoNewWars() || GetCSBullyTargetPlayer() != ePlayer)) { CvAIOperation* pCurrentSneakAttackOperation = GetPlayer()->getFirstOffensiveAIOperation(ePlayer); if (pCurrentSneakAttackOperation) { if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) { GetPlayer()->StopAllLandOffensiveOperationsAgainstPlayer(ePlayer,AI_ABORT_TARGET_NOT_VALID); GetPlayer()->StopAllSeaOffensiveOperationsAgainstPlayer(ePlayer,AI_ABORT_TARGET_NOT_VALID); } // This parameter exists because SelectBestApproachTowardsMajorCiv() might update approach to something other than WAR // However, DoUpdateWarTargets() overrides this logic sometimes, so we don't ALWAYS want to cancel the operation immediately else if (bResetAttackOperations) { pCurrentSneakAttackOperation->LogOperationSpecialMessage("War goal changed, probably another war is more important"); GetPlayer()->StopAllLandOffensiveOperationsAgainstPlayer(ePlayer,AI_ABORT_DIPLO_OPINION_CHANGE); GetPlayer()->StopAllSeaOffensiveOperationsAgainstPlayer(ePlayer,AI_ABORT_DIPLO_OPINION_CHANGE); } } } if (ePlayer < MAX_MAJOR_CIVS) SetCachedSurfaceApproach(ePlayer, -1); } /// What is our Strategic Diplomatic Approach towards this Major Civ? CivApproachTypes CvDiplomacyAI::GetCivStrategicApproach(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (CivApproachTypes) m_aeCivStrategicApproach[ePlayer]; } void CvDiplomacyAI::SetCivStrategicApproach(PlayerTypes ePlayer, CivApproachTypes eApproach) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(eApproach >= 0 && eApproach < NUM_CIV_APPROACHES, "Setting CivStrategicApproach to invalid value"); ASSERT(NotTeam(ePlayer), "Updating static CivStrategicApproach value"); m_aeCivStrategicApproach[ePlayer] = eApproach; } /// What is our cached surface-level approach towards ePlayer? This is used to stay consistent during war plans. /// Return value is int instead of CivApproachTypes to permit a value of -1 (i.e., no approach, to circumvent needing to actually have an enum for that.) int CvDiplomacyAI::GetCachedSurfaceApproach(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aeCachedSurfaceApproach[ePlayer]; } void CvDiplomacyAI::SetCachedSurfaceApproach(PlayerTypes ePlayer, int iApproach) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(iApproach >= -1 && iApproach < NUM_CIV_APPROACHES, "Setting CachedSurfaceApproach to invalid value"); ASSERT(NotTeam(ePlayer), "AI is disguising approach towards own team!"); m_aeCachedSurfaceApproach[ePlayer] = iApproach; } /// What is our surface-level approach towards ePlayer (i.e. how we are acting towards them right now)? CivApproachTypes CvDiplomacyAI::GetSurfaceApproach(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_PLAYERS, "Player index out of bounds"); if (ePlayer == BARBARIAN_PLAYER || IsAtWar(ePlayer)) return CIV_APPROACH_WAR; // Approach towards City-States is always honest if (ePlayer >= MAX_MAJOR_CIVS) return GetCivApproach(ePlayer); // Always friendly if we have a Declaration of Friendship if (IsDoFAccepted(ePlayer)) return CIV_APPROACH_FRIENDLY; CivApproachTypes eRealApproach = GetCivApproach(ePlayer); // Use a surface approach to disguise our war plans (this approach is cached to prevent erratic behavior) if (eRealApproach == CIV_APPROACH_WAR) { // In theory a return value of -1 here should not happen, but because the gamecore and UI run separately, it's possible. int iCachedSurfaceApproach = GetCachedSurfaceApproach(ePlayer); if (iCachedSurfaceApproach == -1) { ASSERT(!gDLL->IsGameCoreThread(), "GetSurfaceApproach() has invalid return value (-1) from gamecore thread"); return CIV_APPROACH_NEUTRAL; } return (CivApproachTypes) iCachedSurfaceApproach; } // Deceptive = Friendly else if (eRealApproach == CIV_APPROACH_DECEPTIVE) { return CIV_APPROACH_FRIENDLY; } return eRealApproach; } /// How is trade deal valuation modified based on our surface-level approach towards ePlayer? int CvDiplomacyAI::GetSurfaceApproachDealModifier(PlayerTypes ePlayer, bool bFromMe) const { if (bFromMe) { // How much is OUR stuff worth? switch (GetSurfaceApproach(ePlayer)) { case CIV_APPROACH_FRIENDLY: return /*80*/ GD_INT_GET(APPROACH_FRIENDLY_SELLING_PRICE_MODIFIER); case CIV_APPROACH_AFRAID: return /*80*/ GD_INT_GET(APPROACH_AFRAID_SELLING_PRICE_MODIFIER); case CIV_APPROACH_NEUTRAL: return /*100*/ GD_INT_GET(APPROACH_NEUTRAL_SELLING_PRICE_MODIFIER); case CIV_APPROACH_GUARDED: return /*125*/ GD_INT_GET(APPROACH_GUARDED_SELLING_PRICE_MODIFIER); case CIV_APPROACH_HOSTILE: return /*200*/ GD_INT_GET(APPROACH_HOSTILE_SELLING_PRICE_MODIFIER); default: return 100; // Peace deals should always be evaluated fairly for sanity } } else { switch (GetSurfaceApproach(ePlayer)) { case CIV_APPROACH_FRIENDLY: return /*125*/ GD_INT_GET(APPROACH_FRIENDLY_BUYING_PRICE_MODIFIER); case CIV_APPROACH_AFRAID: return /*125*/ GD_INT_GET(APPROACH_AFRAID_BUYING_PRICE_MODIFIER); case CIV_APPROACH_NEUTRAL: return /*100*/ GD_INT_GET(APPROACH_NEUTRAL_BUYING_PRICE_MODIFIER); case CIV_APPROACH_GUARDED: return /*80*/ GD_INT_GET(APPROACH_GUARDED_BUYING_PRICE_MODIFIER); case CIV_APPROACH_HOSTILE: return /*50*/ GD_INT_GET(APPROACH_HOSTILE_BUYING_PRICE_MODIFIER); default: return 100; // Peace deals should always be evaluated fairly for sanity } } } /// Returns ePlayer's visible Diplomatic Approach towards us CivApproachTypes CvDiplomacyAI::GetVisibleApproachTowardsUs(PlayerTypes ePlayer) const { return (GET_PLAYER(ePlayer).isAlive() && !GET_PLAYER(ePlayer).isObserver()) ? GET_PLAYER(ePlayer).GetDiplomacyAI()->GetSurfaceApproach(GetID()) : CIV_APPROACH_NEUTRAL; } /// Does this AI want to sneak attack ePlayer? bool CvDiplomacyAI::IsWantsSneakAttack(PlayerTypes ePlayer) const { return GetCivApproach(ePlayer) == CIV_APPROACH_WAR && !IsAtWar(ePlayer); } /// Returns the value for a specific approach from the last SelectBestApproachTowardsMajorCiv() NORMAL (non-strategic) update int CvDiplomacyAI::GetPlayerApproachValue(PlayerTypes ePlayer, CivApproachTypes eApproach) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS && eApproach >= 0 && eApproach < NUM_CIV_APPROACHES, "Array index out of bounds"); return m_aaiApproachValues[ePlayer][eApproach]; } void CvDiplomacyAI::SetPlayerApproachValue(PlayerTypes ePlayer, CivApproachTypes eApproach, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS && eApproach >= 0 && eApproach < NUM_CIV_APPROACHES, "Array index out of bounds"); ASSERT(iValue >= 0, "Trying to set approach score to a negative value"); ASSERT(NotTeam(ePlayer), "Updating static ApproachValue"); m_aaiApproachValues[ePlayer][eApproach] = iValue; } /// Which approach has the highest value for ePlayer? CivApproachTypes CvDiplomacyAI::GetHighestValueApproach(PlayerTypes ePlayer, bool bExcludeWar, bool bIncludeOverrides) const { if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) return CIV_APPROACH_FRIENDLY; CivApproachTypes eBestApproach = CIV_APPROACH_NEUTRAL; int iHighestValue = 0; for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eLoopApproach = (CivApproachTypes) iApproachLoop; if (bExcludeWar && eLoopApproach == CIV_APPROACH_WAR) continue; int iValue = GetPlayerApproachValue(ePlayer, eLoopApproach); if (iValue > iHighestValue) { eBestApproach = eLoopApproach; iHighestValue = iValue; } } if (bIncludeOverrides) { if (eBestApproach == CIV_APPROACH_WAR || eBestApproach == CIV_APPROACH_HOSTILE) { if (WasResurrectedBy(ePlayer) || (IsCityRecentlyLiberatedBy(ePlayer) && GetPlayer()->getCitiesLost() > 0 && !GET_PLAYER(ePlayer).GetDiplomacyAI()->IsCloseToWorldConquest() && !IsEndgameAggressiveTo(ePlayer))) { eBestApproach = CIV_APPROACH_GUARDED; } else if (eBestApproach == CIV_APPROACH_HOSTILE && IsLiberator(ePlayer, false, true)) { eBestApproach = CIV_APPROACH_GUARDED; } } if (eBestApproach == CIV_APPROACH_FRIENDLY || eBestApproach == CIV_APPROACH_DECEPTIVE) { if (IsDenouncedPlayer(ePlayer) || IsDenouncedByPlayer(ePlayer) || IsUntrustworthy(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsRecentDemandAccepted(GetID()) || CvDiplomacyAIHelpers::ProposedSanctionsBlockingDiplomacy(GetID(), ePlayer)) { eBestApproach = (GetCivOpinion(ePlayer) <= CIV_OPINION_ENEMY) ? CIV_APPROACH_GUARDED : CIV_APPROACH_NEUTRAL; } } } return eBestApproach; } /// Which other player has the highest NORMAL (non-strategic) value for this approach? PlayerTypes CvDiplomacyAI::GetPlayerWithHighestApproachValue(CivApproachTypes eApproach, vector& vPlayersToExclude) const { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) || GetPlayer()->getCapitalCity() == NULL || GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR)) return NO_PLAYER; PlayerTypes eBestPlayer = NO_PLAYER; int iHighestValue = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GetTeam() == GET_PLAYER(eLoopPlayer).getTeam()) continue; if (IsAlwaysAtWar(eLoopPlayer)) continue; if (std::find(vPlayersToExclude.begin(), vPlayersToExclude.end(), eLoopPlayer) != vPlayersToExclude.end()) continue; if (IsPlayerValid(eLoopPlayer)) { int iValue = GetPlayerApproachValue(eLoopPlayer, eApproach); if (iValue > iHighestValue) { eBestPlayer = eLoopPlayer; iHighestValue = iValue; } } } return eBestPlayer; } /// Returns the value for a specific approach from the last SelectBestApproachTowardsMajorCiv() STRATEGIC update int CvDiplomacyAI::GetPlayerStrategicApproachValue(PlayerTypes ePlayer, CivApproachTypes eApproach) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS && eApproach >= 0 && eApproach < NUM_CIV_APPROACHES, "Array index out of bounds"); return m_aaiStrategicApproachValues[ePlayer][eApproach]; } void CvDiplomacyAI::SetPlayerStrategicApproachValue(PlayerTypes ePlayer, CivApproachTypes eApproach, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS && eApproach >= 0 && eApproach < NUM_CIV_APPROACHES, "Array index out of bounds"); ASSERT(iValue >= 0, "Trying to set strategic approach score to a negative value"); ASSERT(NotTeam(ePlayer), "Updating static StrategicApproachValue"); m_aaiStrategicApproachValues[ePlayer][eApproach] = iValue; } /// Which other player has the highest STRATEGIC value for this approach? PlayerTypes CvDiplomacyAI::GetPlayerWithHighestStrategicApproachValue(CivApproachTypes eApproach, vector& vPlayersToExclude) const { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) || GetPlayer()->getCapitalCity() == NULL || GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR)) return NO_PLAYER; PlayerTypes eBestPlayer = NO_PLAYER; int iHighestValue = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eLoopPlayer).getTeam() == GetTeam()) continue; if (GET_PLAYER(eLoopPlayer).IsVassalOfSomeone()) continue; if (IsAlwaysAtWar(eLoopPlayer)) continue; if (std::find(vPlayersToExclude.begin(), vPlayersToExclude.end(), eLoopPlayer) != vPlayersToExclude.end()) continue; if (IsPlayerValid(eLoopPlayer)) { int iValue = GetPlayerStrategicApproachValue(eLoopPlayer, eApproach); if (iValue > iHighestValue) { eBestPlayer = eLoopPlayer; iHighestValue = iValue; } } } return eBestPlayer; } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Minor Civs // ------------------------------------ /// Is there a City-State we're targeting for bullying, backed with force? PlayerTypes CvDiplomacyAI::GetCSBullyTargetPlayer() const { return (PlayerTypes) m_eCSBullyTarget; } void CvDiplomacyAI::SetCSBullyTargetPlayer(PlayerTypes ePlayer) { PRECONDITION((ePlayer == NO_PLAYER) || (ePlayer >= MAX_MAJOR_CIVS && ePlayer < MAX_CIV_PLAYERS), "Setting non-CS as CS bullying target"); m_eCSBullyTarget = ePlayer; } /// Is there a City-State we're targeting for war? PlayerTypes CvDiplomacyAI::GetCSWarTargetPlayer() const { return (PlayerTypes) m_eCSWarTarget; } void CvDiplomacyAI::SetCSWarTargetPlayer(PlayerTypes ePlayer) { PRECONDITION((ePlayer == NO_PLAYER) || (ePlayer >= MAX_MAJOR_CIVS && ePlayer < MAX_CIV_PLAYERS), "Setting non-CS as CS war target"); m_eCSWarTarget = ePlayer; } /// Does this AI want to connect to a minor with a route? bool CvDiplomacyAI::IsWantToRouteConnectToMinor(PlayerTypes eMinor) { PRECONDITION(eMinor >= MAX_MAJOR_CIVS && eMinor < MAX_CIV_PLAYERS, "Player index out of bounds"); return m_abWantToRouteToMinor[eMinor - MAX_MAJOR_CIVS]; } void CvDiplomacyAI::SetWantToRouteConnectToMinor(PlayerTypes eMinor, bool bValue) { PRECONDITION(eMinor >= MAX_MAJOR_CIVS && eMinor < MAX_CIV_PLAYERS, "Player index out of bounds"); m_abWantToRouteToMinor[eMinor - MAX_MAJOR_CIVS] = bValue; } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Planning Exchanges // ------------------------------------ /// Does this AI consider ePlayer a major competitor? bool CvDiplomacyAI::IsMajorCompetitor(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abMajorCompetitor[ePlayer]; } void CvDiplomacyAI::SetMajorCompetitor(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(!bValue || NotTeam(ePlayer), "Setting MajorCompetitor to true for own team"); m_abMajorCompetitor[ePlayer] = bValue; } /// Does this AI consider ePlayer a strategic trade partner? bool CvDiplomacyAI::IsStrategicTradePartner(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abStrategicTradePartner[ePlayer]; } void CvDiplomacyAI::SetStrategicTradePartner(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(!bValue || NotTeam(ePlayer), "Setting StrategicTradePartner to true for own team"); m_abStrategicTradePartner[ePlayer] = bValue; } /// Does this AI want to make a Declaration of Friendship with ePlayer? bool CvDiplomacyAI::IsWantsDoFWithPlayer(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abWantsDoFWithPlayer[ePlayer]; } void CvDiplomacyAI::SetWantsDoFWithPlayer(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(!bValue || NotTeam(ePlayer), "Setting WantsDoFWithPlayer to true for own team"); m_abWantsDoFWithPlayer[ePlayer] = bValue; } /// Does this AI want to make a Defensive Pact with ePlayer? bool CvDiplomacyAI::IsWantsDefensivePactWithPlayer(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abWantsDefensivePactWithPlayer[ePlayer]; } void CvDiplomacyAI::SetWantsDefensivePactWithPlayer(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(!bValue || NotTeam(ePlayer), "Setting WantsDefensivePactWithPlayer to true for own team"); m_abWantsDefensivePactWithPlayer[ePlayer] = bValue; } /// Does this AI want to cancel its Declaration of Friendship with ePlayer? bool CvDiplomacyAI::IsWantsToEndDoFWithPlayer(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abWantsToEndDoFWithPlayer[ePlayer]; } void CvDiplomacyAI::SetWantsToEndDoFWithPlayer(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(!bValue || NotTeam(ePlayer), "Setting WantsToEndDoFWithPlayer to true for own team"); ASSERT(!bValue || IsDoFAccepted(ePlayer), "Setting WantsToEndDoFWithPlayer to true, but we don't have a DoF with them"); m_abWantsToEndDoFWithPlayer[ePlayer] = bValue; } /// Does this AI want to cancel its Defensive Pact with ePlayer? bool CvDiplomacyAI::IsWantsToEndDefensivePactWithPlayer(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abWantsToEndDefensivePactWithPlayer[ePlayer]; } void CvDiplomacyAI::SetWantsToEndDefensivePactWithPlayer(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(!bValue || NotTeam(ePlayer), "Setting WantsToEndDefensivePactWithPlayer to true for own team"); ASSERT(!bValue || IsHasDefensivePact(ePlayer), "Setting WantsToEndDefensivePactWithPlayer to true, but we don't have a DP with them"); m_abWantsToEndDefensivePactWithPlayer[ePlayer] = bValue; } /// Does this AI want to make a Research Agreement with ePlayer? bool CvDiplomacyAI::IsWantsResearchAgreementWithPlayer(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abWantsResearchAgreementWithPlayer[ePlayer]; } void CvDiplomacyAI::SetWantsResearchAgreementWithPlayer(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(!bValue || NotTeam(ePlayer), "Setting WantsResearchAgreementWithPlayer to true for own team"); if (IsHasResearchAgreement(ePlayer)) { m_abWantsResearchAgreementWithPlayer[ePlayer] = false; return; } if (bValue != IsWantsResearchAgreementWithPlayer(ePlayer)) { m_abWantsResearchAgreementWithPlayer[ePlayer] = bValue; if (bValue) { // Start saving the Gold int iGoldAmount = GC.getGame().GetGameDeals().GetTradeItemGoldCost(TRADE_ITEM_RESEARCH_AGREEMENT, GetID(), ePlayer); GetPlayer()->GetEconomicAI()->StartSaveForPurchase(PURCHASE_TYPE_MAJOR_CIV_TRADE, iGoldAmount, /*Priority*/ 1); LogWantRA(ePlayer); } else { GetPlayer()->GetEconomicAI()->CancelSaveForPurchase(PURCHASE_TYPE_MAJOR_CIV_TRADE); } } } /// Who is this AI's most valuable potential friend? PlayerTypes CvDiplomacyAI::GetMostValuableFriend() const { return m_eMostValuableFriend; } void CvDiplomacyAI::SetMostValuableFriend(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= NO_PLAYER && ePlayer < MAX_MAJOR_CIVS && NotTeam(ePlayer), "Setting MostValuableFriend to invalid value"); m_eMostValuableFriend = ePlayer; } /// Who is this AI's most valuable potential ally? PlayerTypes CvDiplomacyAI::GetMostValuableAlly() const { return m_eMostValuableAlly; } void CvDiplomacyAI::SetMostValuableAlly(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= NO_PLAYER && ePlayer < MAX_MAJOR_CIVS && NotTeam(ePlayer), "Setting MostValuableAlly to invalid value"); m_eMostValuableAlly = ePlayer; } /// Who is this AI's biggest competitor? PlayerTypes CvDiplomacyAI::GetBiggestCompetitor() const { return m_eBiggestCompetitor; } void CvDiplomacyAI::SetBiggestCompetitor(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= NO_PLAYER && ePlayer < MAX_MAJOR_CIVS && NotTeam(ePlayer), "Setting BiggestCompetitor to invalid value"); m_eBiggestCompetitor = ePlayer; } /// Who is this AI's biggest World Congress ally? PlayerTypes CvDiplomacyAI::GetPrimeLeagueAlly() const { return m_ePrimeLeagueAlly; } void CvDiplomacyAI::SetPrimeLeagueAlly(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= NO_PLAYER && ePlayer < MAX_MAJOR_CIVS && NotTeam(ePlayer), "Setting PrimeLeagueAlly to invalid value"); m_ePrimeLeagueAlly = ePlayer; } /// Who is this AI's biggest World Congress rival? PlayerTypes CvDiplomacyAI::GetPrimeLeagueCompetitor() const { return m_ePrimeLeagueCompetitor; } void CvDiplomacyAI::SetPrimeLeagueCompetitor(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= NO_PLAYER && ePlayer < MAX_MAJOR_CIVS && NotTeam(ePlayer), "Setting PrimeLeagueCompetitor to invalid value"); m_ePrimeLeagueCompetitor = ePlayer; } /// Is there a player we're targeting to make a demand from, backed with force? PlayerTypes CvDiplomacyAI::GetDemandTargetPlayer() const { return m_eDemandTargetPlayer; } void CvDiplomacyAI::SetDemandTargetPlayer(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= NO_PLAYER && ePlayer < MAX_MAJOR_CIVS && NotTeam(ePlayer), "Setting DemandTargetPlayer to invalid value"); m_eDemandTargetPlayer = ePlayer; } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Exchanges // ------------------------------------ /// Do we have a Declaration of Friendship with ePlayer? bool CvDiplomacyAI::IsDoFAccepted(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return GetDoFAcceptedTurn(ePlayer) != -1; } /// We made a Declaration of Friendship with someone, handle everything that means void CvDiplomacyAI::SetDoFAccepted(PlayerTypes ePlayer, bool bValue) { if (bValue != IsDoFAccepted(ePlayer)) { // Someone made a DoF, send out notifications to everyone if (bValue) { SetDoFAcceptedTurn(ePlayer, GC.getGame().getGameTurn()); // These shouldn't be true, but if a DoF is forced somehow (IGE, multiplayer), reset them to false to avoid strange behavior. SetDenouncedPlayer(ePlayer, false); SetDoFBroken(ePlayer, false, true); if (!GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAccepted(GetID())) // Only run this once! { DoFLevelTypes eHighestDoF = (DoFLevelTypes)max((int)GetDoFType(ePlayer), (int)GET_PLAYER(ePlayer).GetDiplomacyAI()->GetDoFType(GetID())); switch (eHighestDoF) { case DOF_TYPE_UNTRUSTWORTHY: case DOF_TYPE_NEW: SetDoFType(ePlayer, DOF_TYPE_FRIENDS); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFType(GetID(), DOF_TYPE_FRIENDS); break; case DOF_TYPE_FRIENDS: SetDoFType(ePlayer, DOF_TYPE_ALLIES); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFType(GetID(), DOF_TYPE_ALLIES); break; case DOF_TYPE_ALLIES: case DOF_TYPE_BATTLE_BROTHERS: SetDoFType(ePlayer, DOF_TYPE_BATTLE_BROTHERS); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFType(GetID(), DOF_TYPE_BATTLE_BROTHERS); break; } Localization::String strText = Localization::Lookup("TXT_KEY_NOTIFICATION_DOF"); Localization::String strSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_DOF_S"); for (int iCurPlayer = 0; iCurPlayer < MAX_MAJOR_CIVS; ++iCurPlayer) { PlayerTypes eCurPlayer = (PlayerTypes) iCurPlayer; CvPlayerAI& kCurPlayer = GET_PLAYER(eCurPlayer); CvNotifications* pNotifications = GET_PLAYER(eCurPlayer).GetNotifications(); if (iCurPlayer != ePlayer && iCurPlayer != GetID() && pNotifications) { const char* strThisPlayerName = NULL; const char* strOtherPlayerName = NULL; CvTeam* pCurTeam = &GET_TEAM(kCurPlayer.getTeam()); // Have we met these guys yet? bool bHasMetThisTeam = pCurTeam->isHasMet(GetTeam()) || GET_PLAYER(eCurPlayer).isObserver(); if (bHasMetThisTeam) strThisPlayerName = GetPlayer()->getCivilizationShortDescriptionKey(); else strThisPlayerName = "TXT_KEY_UNMET_PLAYER"; bool bHasMetOtherTeam = pCurTeam->isHasMet(GET_PLAYER(ePlayer).getTeam()) || GET_PLAYER(eCurPlayer).isObserver(); if (bHasMetOtherTeam) strOtherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescriptionKey(); else strOtherPlayerName = "TXT_KEY_UNMET_PLAYER"; //Only display notification if we've met both of the players. if (bHasMetThisTeam && bHasMetOtherTeam) { Localization::String tempInfoStr = strText; tempInfoStr << strThisPlayerName << strOtherPlayerName; Localization::String tempSummaryStr = strSummary; tempSummaryStr << strThisPlayerName << strOtherPlayerName; pNotifications->Add(NOTIFICATION_DIPLOMACY_DECLARATION, tempInfoStr.toUTF8(), tempSummaryStr.toUTF8(), -1, -1, GetID(), ePlayer); } } } } // Cancel any coop wars against the player we've befriended for (int iThirdPartyLoop = 0; iThirdPartyLoop < MAX_MAJOR_CIVS; iThirdPartyLoop++) { PlayerTypes eThirdParty = (PlayerTypes) iThirdPartyLoop; if (IsPlayerValid(eThirdParty, true) && eThirdParty != GetID() && GetCoopWarState(eThirdParty, ePlayer) == COOP_WAR_STATE_PREPARING) { GET_PLAYER(eThirdParty).GetDiplomacyAI()->SetBrokeCoopWarPromise(GetID(), true); GET_PLAYER(eThirdParty).GetDiplomacyAI()->ChangeCoopWarAgreementScore(GetID(), -2); GET_PLAYER(eThirdParty).GetDiplomacyAI()->ChangeRecentAssistValue(GetID(), -300); CvNotifications* pNotify = GetPlayer()->GetNotifications(); if (pNotify) { Localization::String strSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_S"); Localization::String strText = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_DOF"); strText << GET_PLAYER(ePlayer).getCivilizationShortDescriptionKey(); strText << GET_PLAYER(eThirdParty).getCivilizationShortDescriptionKey(); pNotify->Add(NOTIFICATION_DIPLOMACY_DECLARATION, strText.toUTF8(), strSummary.toUTF8(), -1, -1, -1); } pNotify = GET_PLAYER(eThirdParty).GetNotifications(); if (pNotify) { Localization::String strSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_S"); Localization::String strText = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_DOF"); strText << GET_PLAYER(ePlayer).getCivilizationShortDescriptionKey(); strText << GetPlayer()->getCivilizationShortDescriptionKey(); pNotify->Add(NOTIFICATION_DIPLOMACY_DECLARATION, strText.toUTF8(), strSummary.toUTF8(), -1, -1, -1); } SetCoopWarState(eThirdParty, ePlayer, NO_COOP_WAR_STATE); GET_PLAYER(eThirdParty).GetDiplomacyAI()->SetCoopWarState(GetID(), ePlayer, NO_COOP_WAR_STATE); } } } else { SetDoFAcceptedTurn(ePlayer, -1); } m_pPlayer->recomputeGreatPeopleModifiers(); } } /// How many Declarations of Friendship do we currently have with other players? int CvDiplomacyAI::GetNumDoF(bool bVassalageException) const { int iRtnValue = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (bVassalageException) { if (GET_PLAYER(eLoopPlayer).IsVassalOfSomeone()) { continue; } if (IsVassal(eLoopPlayer)) { continue; } } if (IsPlayerValid(eLoopPlayer) && IsDoFAccepted(eLoopPlayer)) { iRtnValue++; } } return iRtnValue; } /// If true, this player can't ask us to make a DoF (used in the UI) bool CvDiplomacyAI::IsDoFMessageTooSoon(PlayerTypes ePlayer) const { // Already have a DoF? if (IsDoFAccepted(ePlayer)) return true; // Observer can't ask this if (!GET_PLAYER(ePlayer).isAlive() || GET_PLAYER(ePlayer).isObserver()) return true; // Can't ask this while at war if (IsAtWar(ePlayer)) return true; return false; } /// On what turn did we most recently make a Declaration of Friendship with ePlayer? int CvDiplomacyAI::GetDoFAcceptedTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiDoFAcceptedTurn[ePlayer]; } void CvDiplomacyAI::SetDoFAcceptedTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting DoFAcceptedTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting DoFAcceptedTurn for own team"); m_aiDoFAcceptedTurn[ePlayer] = iTurn; } /// How many turns have passed since we most recently made a Declaration of Friendship with ePlayer? int CvDiplomacyAI::GetTurnsSinceBefriendedPlayer(PlayerTypes ePlayer) const { if (!IsDoFAccepted(ePlayer)) return -1; return (GC.getGame().getGameTurn() - GetDoFAcceptedTurn(ePlayer)); } /// Returns our current "level" of friendship with ePlayer DoFLevelTypes CvDiplomacyAI::GetDoFType(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (DoFLevelTypes) m_aeDoFType[ePlayer]; } void CvDiplomacyAI::SetDoFType(PlayerTypes ePlayer, DoFLevelTypes eDoFLevel) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(eDoFLevel >= 0 && eDoFLevel < NUM_DOF_TYPES, "Invalid DoFLevelType"); ASSERT(NotTeam(ePlayer), "Setting DoFType for own team"); m_aeDoFType[ePlayer] = eDoFLevel; } /// Have we denounced ePlayer? bool CvDiplomacyAI::IsDenouncedPlayer(PlayerTypes ePlayer) const { return GetDenouncedPlayerTurn(ePlayer) != -1; } void CvDiplomacyAI::SetDenouncedPlayer(PlayerTypes ePlayer, bool bValue) { if (bValue) { SetDenouncedPlayerTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetDenouncedPlayerTurn(ePlayer, -1); } m_pPlayer->recomputeGreatPeopleModifiers(); } /// Have we been denounced by ePlayer? bool CvDiplomacyAI::IsDenouncedByPlayer(PlayerTypes ePlayer) const { return GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID()); } /// If true, this player can't denounce us (used in the UI) bool CvDiplomacyAI::IsDenounceMessageTooSoon(PlayerTypes ePlayer) const { // They already denounced us? if (IsDenouncedByPlayer(ePlayer)) return true; // Observers can't do this! if (!GET_PLAYER(ePlayer).isAlive() || GET_PLAYER(ePlayer).isObserver()) return true; // Can't denounce if always at war if (IsAlwaysAtWar(ePlayer)) return true; return false; } /// On what turn did we most recently denounce ePlayer? int CvDiplomacyAI::GetDenouncedPlayerTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiDenouncedPlayerTurn[ePlayer]; } void CvDiplomacyAI::SetDenouncedPlayerTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting DenouncedPlayerTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting DenouncedPlayerTurn for own team"); m_aiDenouncedPlayerTurn[ePlayer] = iTurn; } /// How many turns have passed since we most recently denounced ePlayer? int CvDiplomacyAI::GetTurnsSinceDenouncedPlayer(PlayerTypes ePlayer) const { if (!IsDenouncedPlayer(ePlayer)) return -1; return (GC.getGame().getGameTurn() - GetDenouncedPlayerTurn(ePlayer)); } /// Denouncing this turn? bool CvDiplomacyAI::IsDenouncingPlayer(PlayerTypes ePlayer) const { int iTurn = GC.getGame().getGameTurn(); if (GetDenouncedPlayerTurn(ePlayer) == iTurn) return true; if ((GetDenouncedPlayerTurn(ePlayer) == (iTurn - 1)) && (iTurn > 0)) return true; return false; } /// How many players have we currently denounced? int CvDiplomacyAI::GetNumDenouncements() const { int iRtnValue = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer) && IsDenouncedPlayer(eLoopPlayer)) { iRtnValue++; } } return iRtnValue; } /// How many players have denounced US? int CvDiplomacyAI::GetNumDenouncementsOfPlayer() const { int iRtnValue = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer) && IsDenouncedByPlayer(eLoopPlayer)) { iRtnValue++; } } return iRtnValue; } /// Is this AI unable to match the deal offer of ePlayer? bool CvDiplomacyAI::IsCantMatchDeal(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abCantMatchDeal[ePlayer]; } void CvDiplomacyAI::SetCantMatchDeal(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotMe(ePlayer), "AI is making a deal with itself?"); m_abCantMatchDeal[ePlayer] = bValue; } /// Returns the number of trade demands ePlayer has made of us int CvDiplomacyAI::GetNumDemandsMade(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumDemandsMade[ePlayer]; } void CvDiplomacyAI::SetNumDemandsMade(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumDemandsMade to a negative value"); ASSERT(NotTeam(ePlayer), "Setting NumDemandsMade for own team"); m_aiNumDemandsMade[ePlayer] = min(iValue, UCHAR_MAX); if (iValue > 0) { SetDemandMadeTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetDemandMadeTurn(ePlayer, -1); SetDemandTooSoonNumTurns(ePlayer, -1); } } void CvDiplomacyAI::ChangeNumDemandsMade(PlayerTypes ePlayer, int iChange) { SetNumDemandsMade(ePlayer, GetNumDemandsMade(ePlayer) + iChange); } bool CvDiplomacyAI::IsDemandMade(PlayerTypes ePlayer) const { return GetNumDemandsMade(ePlayer) > 0; } /// On what turn did ePlayer most recently make a demand of us? int CvDiplomacyAI::GetDemandMadeTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiDemandMadeTurn[ePlayer]; } void CvDiplomacyAI::SetDemandMadeTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting DemandMadeTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting DemandMadeTurn for own team"); m_aiDemandMadeTurn[ePlayer] = iTurn; } /// Returns the amount of turns required before the next demand might be accepted int CvDiplomacyAI::GetDemandTooSoonNumTurns(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiDemandTooSoonNumTurns[ePlayer]; } void CvDiplomacyAI::SetDemandTooSoonNumTurns(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= -1, "Setting DemandTooSoonNumTurns to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting DemandTooSoonNumTurns for own team"); m_aiDemandTooSoonNumTurns[ePlayer] = min(iValue, CHAR_MAX); } /// Has it been too soon since the last demand made? bool CvDiplomacyAI::IsDemandTooSoon(PlayerTypes ePlayer) const { int iDemandTooSoonNumTurns = GetDemandTooSoonNumTurns(ePlayer); // Haven't gotten a demand before if (iDemandTooSoonNumTurns == -1 || GetDemandMadeTurn(ePlayer) == -1) return false; int iTurnDifference = GC.getGame().getGameTurn() - GetDemandMadeTurn(ePlayer); return iTurnDifference < iDemandTooSoonNumTurns; } /// Returns the number of consecutive trade demands ePlayer accepted from us (resets to 0 on refusal) int CvDiplomacyAI::GetNumConsecutiveDemandsTheyAccepted(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumConsecutiveDemandsTheyAccepted[ePlayer]; } void CvDiplomacyAI::SetNumConsecutiveDemandsTheyAccepted(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumConsecutiveDemandsTheyAccepted to a negative value"); ASSERT(NotTeam(ePlayer), "Setting NumConsecutiveDemandsTheyAccepted for own team"); m_aiNumConsecutiveDemandsTheyAccepted[ePlayer] = min(iValue, UCHAR_MAX); if (iValue > 0) { SetDemandAcceptedTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetDemandAcceptedTurn(ePlayer, -1); } } void CvDiplomacyAI::ChangeNumConsecutiveDemandsTheyAccepted(PlayerTypes ePlayer, int iChange) { SetNumConsecutiveDemandsTheyAccepted(ePlayer, GetNumConsecutiveDemandsTheyAccepted(ePlayer) + iChange); } /// On what turn did ePlayer most recently accept a trade demand from us? int CvDiplomacyAI::GetDemandAcceptedTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiDemandAcceptedTurn[ePlayer]; } void CvDiplomacyAI::SetDemandAcceptedTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting DemandAcceptedTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting DemandAcceptedTurn for own team"); m_aiDemandAcceptedTurn[ePlayer] = iTurn; } /// Has ePlayer recently accepted a trade demand from us that we're still reaping benefits from? bool CvDiplomacyAI::IsRecentDemandAccepted(PlayerTypes ePlayer) const { if (GetDemandAcceptedTurn(ePlayer) == -1) return false; int iTurnDifference = GC.getGame().getGameTurn() - GetDemandAcceptedTurn(ePlayer); return iTurnDifference < GC.getGame().getGameSpeedInfo().GetDealDuration(); } /// Returns the value of recent trades int CvDiplomacyAI::GetMaxRecentTradeValue() const { /*200 on Standard, 134 on Quick, 300 on Epic, 600 on Marathon*/ return range(((GD_INT_GET(DEAL_VALUE_PER_OPINION_WEIGHT) * -GD_INT_GET(OPINION_WEIGHT_TRADE_MAX) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100), 0, USHRT_MAX); } int CvDiplomacyAI::GetRecentTradeValue(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiTradeValue[ePlayer]; } void CvDiplomacyAI::SetRecentTradeValue(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotMe(ePlayer), "Setting RecentTradeValue for self"); m_aiTradeValue[ePlayer] = range(iValue, 0, GetMaxRecentTradeValue()); } void CvDiplomacyAI::ChangeRecentTradeValue(PlayerTypes ePlayer, int iChange) { if (iChange > 0) { int iScaledAmount = iChange * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent() / 100; SetRecentTradeValue(ePlayer, GetRecentTradeValue(ePlayer) + iScaledAmount); } else { SetRecentTradeValue(ePlayer, GetRecentTradeValue(ePlayer) + iChange); } } /// Returns the value of combat damage inflicted on a common enemy int CvDiplomacyAI::GetMaxCommonFoeValue() const { /*5000 on Standard, 3350 on Quick, 7500 on Epic, 15000 on Marathon*/ return range(((GD_INT_GET(COMMON_FOE_VALUE_PER_OPINION_WEIGHT) * -GD_INT_GET(OPINION_WEIGHT_COMMON_FOE_MAX) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100), 0, USHRT_MAX); } int CvDiplomacyAI::GetCommonFoeValue(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiCommonFoeValue[ePlayer]; } void CvDiplomacyAI::SetCommonFoeValue(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotMe(ePlayer), "Setting CommonFoeValue for self"); m_aiCommonFoeValue[ePlayer] = range(iValue, 0, GetMaxCommonFoeValue()); } void CvDiplomacyAI::ChangeCommonFoeValue(PlayerTypes ePlayer, int iChange) { if (iChange > 0) { int iScaledAmount = iChange * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent() / 100; SetCommonFoeValue(ePlayer, GetCommonFoeValue(ePlayer) + iScaledAmount); } else { SetCommonFoeValue(ePlayer, GetCommonFoeValue(ePlayer) + iChange); } } /// Returns the value of recent diplomatic actions by ePlayer int CvDiplomacyAI::GetMaxRecentAssistValue() const { /*150 on Standard, 100 on Quick, 225 on Epic, 450 on Marathon*/ return range(((GD_INT_GET(ASSIST_VALUE_PER_OPINION_WEIGHT) * -GD_INT_GET(OPINION_WEIGHT_ASSIST_MAX) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100), 0, SHRT_MAX); } int CvDiplomacyAI::GetMaxRecentFailedAssistValue() const { /*-150 on Standard, -100 on Quick, -225 on Epic, -450 on Marathon*/ return range(((GD_INT_GET(ASSIST_VALUE_PER_OPINION_WEIGHT) * -GD_INT_GET(OPINION_WEIGHT_FAILED_ASSIST_MAX) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100), SHRT_MIN, 0); } int CvDiplomacyAI::GetRecentAssistValue(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiAssistValue[ePlayer]; } void CvDiplomacyAI::SetRecentAssistValue(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotMe(ePlayer), "Setting RecentAssistValue for self"); m_aiAssistValue[ePlayer] = range(iValue, IsTeammate(ePlayer) ? 0 : GetMaxRecentFailedAssistValue(), GetMaxRecentAssistValue()); } void CvDiplomacyAI::ChangeRecentAssistValue(PlayerTypes ePlayer, int iChange, bool bDecay) { // Humans can handle their own diplomacy, and don't update this if always at war. if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) || IsAlwaysAtWar(ePlayer)) return; if (!bDecay) { // Don't care if we're at war. if (iChange < 0 && IsAtWar(ePlayer)) return; int iScaledAmount = iChange * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent() / 100; SetRecentAssistValue(ePlayer, GetRecentAssistValue(ePlayer) + iScaledAmount); } else { // Decay cannot cause value to go past 0! int iCurrentValue = GetRecentAssistValue(ePlayer); if (iCurrentValue > 0 && (iCurrentValue + iChange) < 0) { SetRecentAssistValue(ePlayer, 0); return; } else if (iCurrentValue < 0 && (iCurrentValue + iChange) > 0) { SetRecentAssistValue(ePlayer, 0); return; } SetRecentAssistValue(ePlayer, GetRecentAssistValue(ePlayer) + iChange); } } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Coop Wars // ------------------------------------ /// What is the current state of the coop war between us and eAllyPlayer against eTargetPlayer? CoopWarStates CvDiplomacyAI::GetCoopWarState(PlayerTypes eAllyPlayer, PlayerTypes eTargetPlayer) const { PRECONDITION(eAllyPlayer >= 0 && eAllyPlayer < MAX_MAJOR_CIVS && eTargetPlayer >= 0 && eTargetPlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (CoopWarStates) m_aaeCoopWarState[eAllyPlayer][eTargetPlayer]; } void CvDiplomacyAI::SetCoopWarState(PlayerTypes eAllyPlayer, PlayerTypes eTargetPlayer, CoopWarStates eNewState, bool bSkipLogging) { PRECONDITION(eAllyPlayer >= 0 && eAllyPlayer < MAX_MAJOR_CIVS && eTargetPlayer >= 0 && eTargetPlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(NotMe(eAllyPlayer) && NotTeam(eTargetPlayer), "Invalid players for coop war"); if (eNewState != GetCoopWarState(eAllyPlayer, eTargetPlayer)) { m_aaeCoopWarState[eAllyPlayer][eTargetPlayer] = eNewState; if (eNewState != NO_COOP_WAR_STATE) { SetCoopWarStateChangeTurn(eAllyPlayer, eTargetPlayer, GC.getGame().getGameTurn()); if (eNewState == COOP_WAR_STATE_ONGOING) { ChangeCoopWarAgreementScore(eAllyPlayer, 1); } // If not declaring war immediately, reset exchange desires to prevent accidental betrayal. if (eNewState == COOP_WAR_STATE_PREPARING && !bSkipLogging) { vector vOurTeam = GET_TEAM(GetTeam()).getPlayers(); vector vTargetTeam = GET_TEAM(GET_PLAYER(eTargetPlayer).getTeam()).getPlayers(); for (size_t i=0; iSetWantsDoFWithPlayer(vTargetTeam[j], false); GET_PLAYER(vOurTeam[i]).GetDiplomacyAI()->SetWantsDefensivePactWithPlayer(vTargetTeam[j], false); GET_PLAYER(vOurTeam[i]).GetDiplomacyAI()->SetWantsResearchAgreementWithPlayer(vTargetTeam[j], false); } } } } else { SetCoopWarStateChangeTurn(eAllyPlayer, eTargetPlayer, -1); } if (!bSkipLogging) LogCoopWar(eAllyPlayer, eTargetPlayer, eNewState); } } /// Are we locked into a war with ePlayer? bool CvDiplomacyAI::IsLockedIntoCoopWar(PlayerTypes ePlayer) const { if (GET_TEAM(GetTeam()).GetNumTurnsLockedIntoWar(GET_PLAYER(ePlayer).getTeam()) > 0) return true; return GetGlobalCoopWarAgainstState(ePlayer) == COOP_WAR_STATE_PREPARING; } /// What is our highest coop war state WITH this player against another player? CoopWarStates CvDiplomacyAI::GetGlobalCoopWarWithState(PlayerTypes ePlayer, bool bExcludeOngoing) const { CoopWarStates eBestState = NO_COOP_WAR_STATE; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer)) { CoopWarStates eCoopWarState = GetCoopWarState(ePlayer, eLoopPlayer); if (bExcludeOngoing && eCoopWarState == COOP_WAR_STATE_ONGOING) continue; if (eCoopWarState >= COOP_WAR_STATE_PREPARING && eCoopWarState > eBestState) { eBestState = eCoopWarState; } } } return eBestState; } /// What is our highest coop war state with another player AGAINST this player? CoopWarStates CvDiplomacyAI::GetGlobalCoopWarAgainstState(PlayerTypes ePlayer) const { CoopWarStates eBestState = NO_COOP_WAR_STATE; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (NotMe(eLoopPlayer) && IsPlayerValid(eLoopPlayer, true)) { CoopWarStates eCoopWarState = GetCoopWarState(eLoopPlayer, ePlayer); if (eCoopWarState >= COOP_WAR_STATE_PREPARING && eCoopWarState > eBestState) { eBestState = eCoopWarState; } } } return eBestState; } /// How many players are we currently planning a coop war against? int CvDiplomacyAI::GetNumCoopWarTargets() const { int iCount = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GetGlobalCoopWarAgainstState(eLoopPlayer) >= COOP_WAR_STATE_PREPARING) { iCount++; } } return iCount; } /// On what turn was the current state of the coop war between us and eAllyPlayer against eTargetPlayer last updated? int CvDiplomacyAI::GetCoopWarStateChangeTurn(PlayerTypes eAllyPlayer, PlayerTypes eTargetPlayer) const { PRECONDITION(eAllyPlayer >= 0 && eAllyPlayer < MAX_MAJOR_CIVS && eTargetPlayer >= 0 && eTargetPlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aaiCoopWarStateChangeTurn[eAllyPlayer][eTargetPlayer]; } void CvDiplomacyAI::SetCoopWarStateChangeTurn(PlayerTypes eAllyPlayer, PlayerTypes eTargetPlayer, int iTurn) { PRECONDITION(eAllyPlayer >= 0 && eAllyPlayer < MAX_MAJOR_CIVS && eTargetPlayer >= 0 && eTargetPlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(NotMe(eAllyPlayer) && NotTeam(eTargetPlayer), "Invalid players for coop war"); ASSERT(iTurn >= -1, "Setting CoopWarStateChangeTurn to an invalid value"); m_aaiCoopWarStateChangeTurn[eAllyPlayer][eTargetPlayer] = iTurn; } /// Has ePlayer asked to start a coop war against eTargetPlayer lately? bool CvDiplomacyAI::IsCoopWarMessageTooSoon(PlayerTypes eAskingPlayer, PlayerTypes eTargetPlayer) const { CoopWarStates eCoopWarState = GetCoopWarState(eAskingPlayer, eTargetPlayer); return eCoopWarState == COOP_WAR_STATE_REJECTED || eCoopWarState == COOP_WAR_STATE_WARNED_TARGET; } /// Returns this AI's "score" of ePlayer for accepting or denying their coop war requests against other players (a penalty is applied if a coop war agreement is broken, too) int CvDiplomacyAI::GetCoopWarAgreementScore(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiCoopWarAgreementScore[ePlayer]; } void CvDiplomacyAI::SetCoopWarAgreementScore(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotMe(ePlayer), "Setting CoopWarAgreementScore for self"); ASSERT(iValue >= 0 || !IsTeammate(ePlayer), "Setting CoopWarAgreementScore to a negative value for a teammate"); m_aiCoopWarAgreementScore[ePlayer] = range(iValue, CHAR_MIN, CHAR_MAX); } void CvDiplomacyAI::ChangeCoopWarAgreementScore(PlayerTypes ePlayer, int iChange) { SetCoopWarAgreementScore(ePlayer, GetCoopWarAgreementScore(ePlayer) + iChange); } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // War // ------------------------------------ /// Does this AI believe it's sane to attack ePlayer? bool CvDiplomacyAI::IsSaneDiplomaticTarget(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); return m_abSaneDiplomaticTarget[ePlayer]; } void CvDiplomacyAI::SetSaneDiplomaticTarget(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting SaneDiplomaticTarget for own team"); m_abSaneDiplomaticTarget[ePlayer] = bValue; } bool CvDiplomacyAI::IsWarSane(PlayerTypes ePlayer) const { vector vOurTeam = GET_TEAM(GetTeam()).getPlayers(); for (size_t i=0; iisAlive() && !pTeamMember->GetDiplomacyAI()->IsSaneDiplomaticTarget(ePlayer)) return false; } return true; } /// Is this AI potentially willing to attack ePlayer if someone else asks them to? bool CvDiplomacyAI::IsPotentialWarTarget(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); return GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && NotTeam(ePlayer) ? true : m_abPotentialWarTarget[ePlayer]; } void CvDiplomacyAI::SetPotentialWarTarget(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting PotentialWarTarget for own team"); m_abPotentialWarTarget[ePlayer] = bValue; } /// Are we building up for an attack on ePlayer? bool CvDiplomacyAI::IsArmyInPlaceForAttack(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); return m_abArmyInPlaceForAttack[ePlayer]; } void CvDiplomacyAI::SetArmyInPlaceForAttack(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); ASSERT(!bValue || NotTeam(ePlayer), "AI attempting to declare war on itself"); ASSERT(NotTeam(ePlayer), "Setting ArmyInPlaceForAttack for own team"); m_abArmyInPlaceForAttack[ePlayer] = bValue; } /// Did this AI want to start the war it's currently in with ePlayer? bool CvDiplomacyAI::IsAggressor(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); return m_abAggressor[ePlayer]; } void CvDiplomacyAI::SetAggressor(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting Aggressor for own team"); m_abAggressor[ePlayer] = bValue; } /// How many times have we gone to war with ePlayer? int CvDiplomacyAI::GetNumWarsFought(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); return m_aiNumWarsFought[ePlayer]; } void CvDiplomacyAI::SetNumWarsFought(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumWarsFought to a negative value"); ASSERT(NotTeam(ePlayer), "Setting NumWarsFought for own team"); m_aiNumWarsFought[ePlayer] = min(iValue, UCHAR_MAX); } void CvDiplomacyAI::ChangeNumWarsFought(PlayerTypes ePlayer, int iChange) { SetNumWarsFought(ePlayer, GetNumWarsFought(ePlayer) + iChange); } /// How many times has ePlayer declared war on us? int CvDiplomacyAI::GetNumWarsDeclaredOnUs(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumWarsDeclaredOnUs[ePlayer]; } void CvDiplomacyAI::SetNumWarsDeclaredOnUs(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumWarsDeclaredOnUs to a negative value"); ASSERT(NotTeam(ePlayer), "Setting NumWarsDeclaredOnUs for own team"); m_aiNumWarsDeclaredOnUs[ePlayer] = min(iValue, UCHAR_MAX); } void CvDiplomacyAI::ChangeNumWarsDeclaredOnUs(PlayerTypes ePlayer, int iChange) { SetNumWarsDeclaredOnUs(ePlayer, GetNumWarsDeclaredOnUs(ePlayer) + iChange); } /// How much do we hate this player for killing or capturing our civilians? int CvDiplomacyAI::GetMaxCivilianKillerValue() const { /*5000 on Standard, 3350 on Quick, 7500 on Epic, 15000 on Marathon*/ return range(((GD_INT_GET(CIVILIAN_KILLER_VALUE_PER_OPINION_WEIGHT) * GD_INT_GET(OPINION_WEIGHT_CIVILIAN_KILLER_MAX) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100), 0, USHRT_MAX); } int CvDiplomacyAI::GetCivilianKillerValue(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiCivilianKillerValue[ePlayer]; } void CvDiplomacyAI::SetCivilianKillerValue(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting CivilianKillerValue for own team"); m_aiCivilianKillerValue[ePlayer] = range(iValue, 0, GetMaxCivilianKillerValue()); } void CvDiplomacyAI::ChangeCivilianKillerValue(PlayerTypes ePlayer, int iChange) { if (iChange > 0) { int iScaledAmount = iChange * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent() / 100; SetCivilianKillerValue(ePlayer, GetCivilianKillerValue(ePlayer) + iScaledAmount); } else if (!IsTeammate(ePlayer)) { SetCivilianKillerValue(ePlayer, GetCivilianKillerValue(ePlayer) + iChange); } } /// How many times has this player captured one of our cities? int CvDiplomacyAI::GetNumCitiesCapturedBy(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_PLAYERS, "Player index out of bounds"); return m_aiNumCitiesCaptured[ePlayer]; } void CvDiplomacyAI::SetNumCitiesCapturedBy(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_PLAYERS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumCitiesCaptured to a negative value"); ASSERT(NotTeam(ePlayer), "Setting NumCitiesCaptured for own team"); m_aiNumCitiesCaptured[ePlayer] = min(iValue, UCHAR_MAX); } void CvDiplomacyAI::ChangeNumCitiesCapturedBy(PlayerTypes ePlayer, int iChange) { SetNumCitiesCapturedBy(ePlayer, GetNumCitiesCapturedBy(ePlayer) + iChange); } /// What is the state of war with this player? WarStateTypes CvDiplomacyAI::GetWarState(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); return (WarStateTypes) m_aeWarState[ePlayer]; } void CvDiplomacyAI::SetWarState(PlayerTypes ePlayer, WarStateTypes eWarState) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); ASSERT(eWarState == NO_WAR_STATE_TYPE || NotTeam(ePlayer), "Diplo AI thinks team is at war with itself"); m_aeWarState[ePlayer] = eWarState; } /// Overall assessment of whether we're winning or losing all the wars we are in StateAllWars CvDiplomacyAI::GetStateAllWars() const { return m_eStateAllWars; } void CvDiplomacyAI::SetStateAllWars(StateAllWars eState) { PRECONDITION(eState >= 0 && eState < NUM_STATES_ALL_WARS, "Invalid StateAllWars value"); m_eStateAllWars = eState; } /// How much progress (or lack thereof) have we made in this war? /// Used by the AI to determine whether they should continue or stop. int CvDiplomacyAI::GetWarProgressScore(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); return m_aiWarProgressScore[ePlayer]; } void CvDiplomacyAI::SetWarProgressScore(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); ASSERT(iValue == 0 || NotTeam(ePlayer), "Setting WarProgressScore to non-zero value for own team"); m_aiWarProgressScore[ePlayer] = range(iValue, SHRT_MIN, SHRT_MAX); } void CvDiplomacyAI::ChangeWarProgressScore(PlayerTypes ePlayer, int iChange) { SetWarProgressScore(ePlayer, GetWarProgressScore(ePlayer) + iChange); } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Peace // ------------------------------------ /// What are we willing to give up to ePlayer to make peace? PeaceTreatyTypes CvDiplomacyAI::GetTreatyWillingToOffer(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (PeaceTreatyTypes) m_aePeaceTreatyWillingToOffer[ePlayer]; } void CvDiplomacyAI::SetTreatyWillingToOffer(PlayerTypes ePlayer, PeaceTreatyTypes eTreaty) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(eTreaty == NO_PEACE_TREATY_TYPE || NotTeam(ePlayer), "AI offering peace to own team"); m_aePeaceTreatyWillingToOffer[ePlayer] = eTreaty; } /// What are we willing to accept from ePlayer to make peace? PeaceTreatyTypes CvDiplomacyAI::GetTreatyWillingToAccept(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (PeaceTreatyTypes) m_aePeaceTreatyWillingToAccept[ePlayer]; } void CvDiplomacyAI::SetTreatyWillingToAccept(PlayerTypes ePlayer, PeaceTreatyTypes eTreaty) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(eTreaty == NO_PEACE_TREATY_TYPE || NotTeam(ePlayer), "AI accepting peace from own team"); m_aePeaceTreatyWillingToAccept[ePlayer] = eTreaty; } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Backstabbing Penalties // ------------------------------------ /// Have we ever betrayed one of our friends? bool CvDiplomacyAI::IsBackstabber() const { return m_bBackstabber; } void CvDiplomacyAI::SetBackstabber(bool bValue) { m_bBackstabber = bValue; } /// Do we personally think this player a backstabber? If so, then his word isn't worth much bool CvDiplomacyAI::IsUntrustworthyFriend(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abUntrustworthyFriend[ePlayer]; } void CvDiplomacyAI::SetUntrustworthyFriend(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(!bValue || NotTeam(ePlayer), "Setting UntrustworthyFriend to true for own team"); m_abUntrustworthyFriend[ePlayer] = bValue; } /// Is this player a backstabber according to anyone on our team? bool CvDiplomacyAI::IsUntrustworthy(PlayerTypes ePlayer) const { vector vOurTeam = GET_TEAM(GetTeam()).getPlayers(); for (size_t i=0; iisAlive() && pTeamMember->GetDiplomacyAI()->IsUntrustworthyFriend(ePlayer)) return true; } return false; } /// Did this player EVER do something that would make him untrustworthy to us? bool CvDiplomacyAI::WasEverBackstabbedBy(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abEverBackstabbedBy[ePlayer]; } void CvDiplomacyAI::SetEverBackstabbedBy(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting EverBackstabbedBy for own team"); m_abEverBackstabbedBy[ePlayer] = bValue; } /// Apply a backstabbing mark for ePlayer and their teammates to our entire team void CvDiplomacyAI::SetBackstabbedBy(PlayerTypes ePlayer, bool bValue, bool bSkipReevaluation) { vector vOurTeam = GET_TEAM(GetTeam()).getPlayers(); vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); for (size_t i=0; iSetEverBackstabbedBy((PlayerTypes)vTheirTeam[j], bValue); } } if (bValue && !bSkipReevaluation) { // All AI players must re-evaluate the trustworthiness of other players. for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eLoopPlayer).getTeam() != GET_PLAYER(ePlayer).getTeam()) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->DoReevaluatePlayers(vTheirTeam); } else { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->DoReevaluateEveryone(); } } } } /// Returns if ePlayer broke our Declaration of Friendship bool CvDiplomacyAI::IsDoFBroken(PlayerTypes ePlayer) const { return GetDoFBrokenTurn(ePlayer) != -1; } /// Our Declaration of Friendship with ePlayer has been broken, handle everything that means void CvDiplomacyAI::SetDoFBroken(PlayerTypes ePlayer, bool bValue, bool bSkipTraitorUpdate) { if (bValue != IsDoFBroken(ePlayer)) { // Someone broke a DoF, send out notifications to everyone if (bValue) { GET_PLAYER(ePlayer).GetDiplomacyAI()->SetEndedFriendshipThisTurn(true); Localization::String strText = Localization::Lookup("TXT_KEY_NOTIFICATION_DOF_BROKEN"); Localization::String strSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_DOF_S_BROKEN"); for (int iCurPlayer = 0; iCurPlayer < MAX_MAJOR_CIVS; ++iCurPlayer) { PlayerTypes eCurPlayer = (PlayerTypes) iCurPlayer; CvPlayerAI& kCurPlayer = GET_PLAYER(eCurPlayer); CvNotifications* pNotifications = GET_PLAYER(eCurPlayer).GetNotifications(); if (iCurPlayer != ePlayer && iCurPlayer !=GetID() && pNotifications) { const char* strThisPlayerName = NULL; const char* strOtherPlayerName = NULL; CvTeam* pCurTeam = &GET_TEAM(kCurPlayer.getTeam()); // Have we met these guys yet? bool bHasMetThisTeam = pCurTeam->isHasMet(GetTeam()) || GET_PLAYER(eCurPlayer).isObserver(); if (bHasMetThisTeam) strThisPlayerName = GetPlayer()->getCivilizationShortDescriptionKey(); else strThisPlayerName = "TXT_KEY_UNMET_PLAYER"; bool bHasMetOtherTeam = pCurTeam->isHasMet(GET_PLAYER(ePlayer).getTeam()) || GET_PLAYER(eCurPlayer).isObserver(); if (bHasMetOtherTeam) strOtherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescriptionKey(); else strOtherPlayerName = "TXT_KEY_UNMET_PLAYER"; //Only display notification if we've met both of the players. if (bHasMetThisTeam && bHasMetOtherTeam) { Localization::String tempInfoStr = strText; tempInfoStr << strThisPlayerName << strOtherPlayerName; Localization::String tempSummaryStr = strSummary; tempSummaryStr << strThisPlayerName << strOtherPlayerName; pNotifications->Add(NOTIFICATION_DIPLOMACY_DECLARATION, tempInfoStr.toUTF8(), tempSummaryStr.toUTF8(), -1, -1, GetID(), ePlayer); } } } if (GET_PLAYER(ePlayer).IsVassalOfSomeone()) return; if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; SetDoFBrokenTurn(ePlayer, GC.getGame().getGameTurn()); if (!bSkipTraitorUpdate) { vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); // All AI players must re-evaluate the trustworthiness of other players. for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eLoopPlayer).isMajorCiv()) { if (GET_PLAYER(eLoopPlayer).getTeam() != GET_PLAYER(ePlayer).getTeam()) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->DoReevaluatePlayers(vTheirTeam); } else { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->DoReevaluateEveryone(); } } } } } else { SetDoFBrokenTurn(ePlayer, -1); } } } /// On what turn did ePlayer most recently break our Declaration of Friendship? int CvDiplomacyAI::GetDoFBrokenTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiDoFBrokenTurn[ePlayer]; } void CvDiplomacyAI::SetDoFBrokenTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting DoFBrokenTurn to invalid value"); ASSERT(NotTeam(ePlayer), "Setting DoFBrokenTurn for own team"); m_aiDoFBrokenTurn[ePlayer] = iTurn; } /// How many turns has it been since ePlayer most recently broke our Declaration of Friendship? int CvDiplomacyAI::GetTurnsSinceDoFBroken(PlayerTypes ePlayer) const { if (!IsDoFBroken(ePlayer)) return -1; return GC.getGame().getGameTurn() - GetDoFBrokenTurn(ePlayer); } /// Did this player denounce us while we had a DoF? bool CvDiplomacyAI::IsFriendDenouncedUs(PlayerTypes ePlayer) const { return GetFriendDenouncedUsTurn(ePlayer) != -1; } void CvDiplomacyAI::SetFriendDenouncedUs(PlayerTypes ePlayer, bool bValue) { if (bValue) { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if (GET_PLAYER(ePlayer).IsVassalOfSomeone()) return; SetFriendDenouncedUsTurn(ePlayer, GC.getGame().getGameTurn()); SetBackstabbedBy(ePlayer, true, true); } else { SetFriendDenouncedUsTurn(ePlayer, -1); } } /// On what turn did this player most recently denounce us while we had a DoF? int CvDiplomacyAI::GetFriendDenouncedUsTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiFriendDenouncedUsTurn[ePlayer]; } void CvDiplomacyAI::SetFriendDenouncedUsTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting FriendDenouncedUsTurn to invalid value"); ASSERT(NotTeam(ePlayer), "Setting FriendDenouncedUsTurn for own team"); m_aiFriendDenouncedUsTurn[ePlayer] = iTurn; } /// How many former friends have denounced US??? int CvDiplomacyAI::GetNumFriendsDenouncedBy() const { int iNum = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsFriendDenouncedUs(eLoopPlayer)) iNum++; } return iNum; } /// How many friends have WE denounced? int CvDiplomacyAI::GetWeDenouncedFriendCount() { int iNum = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsFriendDenouncedUs(GetID())) iNum++; } return iNum; } /// Did this player declare war on us while we had a DoF? bool CvDiplomacyAI::IsFriendDeclaredWarOnUs(PlayerTypes ePlayer) const { return GetFriendDeclaredWarOnUsTurn(ePlayer) != -1; } void CvDiplomacyAI::SetFriendDeclaredWarOnUs(PlayerTypes ePlayer, bool bValue) { ASSERT(NotTeam(ePlayer)); if (bValue) { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if (GET_PLAYER(ePlayer).IsVassalOfSomeone()) return; SetFriendDeclaredWarOnUsTurn(ePlayer, GC.getGame().getGameTurn()); SetBackstabbedBy(ePlayer, true, true); // If they didn't backstab in reaction to our backstabbing, mark them as a serial backstabber if (!GET_PLAYER(ePlayer).GetDiplomacyAI()->IsBackstabber() && !GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && !GET_PLAYER(ePlayer).GetDiplomacyAI()->WasEverBackstabbedBy(GetID()) && !GET_PLAYER(ePlayer).GetDiplomacyAI()->IsUntrustworthy(GetID())) GET_PLAYER(ePlayer).GetDiplomacyAI()->SetBackstabber(true); } else { SetFriendDeclaredWarOnUsTurn(ePlayer, -1); // If all backstabbing penalties for this player have expired and Loyalty > 2 and not Nuclear Gandhi, reset the backstabber flag. if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsBackstabber() && GET_PLAYER(ePlayer).GetDiplomacyAI()->GetLoyalty() > 2 && GET_PLAYER(ePlayer).GetDiplomacyAI()->GetWeDeclaredWarOnFriendCount(ePlayer) <= 0 && !GET_PLAYER(ePlayer).GetDiplomacyAI()->IsNuclearGandhi()) GET_PLAYER(ePlayer).GetDiplomacyAI()->SetBackstabber(false); } } /// On what turn did this player most recently declare war on us when we had a DoF? int CvDiplomacyAI::GetFriendDeclaredWarOnUsTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiFriendDeclaredWarOnUsTurn[ePlayer]; } void CvDiplomacyAI::SetFriendDeclaredWarOnUsTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting FriendDeclaredWarOnUsTurn to invalid value"); ASSERT(NotTeam(ePlayer), "Setting FriendDeclaredWarOnUsTurn for own team"); m_aiFriendDeclaredWarOnUsTurn[ePlayer] = iTurn; } /// How many friends have WE Declared War on? int CvDiplomacyAI::GetWeDeclaredWarOnFriendCount(PlayerTypes eObserver, bool bExcludeOtherBackstabbers) { int iNum = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; TeamTypes eLoopTeam = GET_PLAYER(eLoopPlayer).getTeam(); if (eLoopTeam == GetTeam()) continue; if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsFriendDeclaredWarOnUs(GetID())) { // If we're observing ourselves (to set the backstabber flag), we know whether we backstabbed because we considered the other player untrustworthy // However, other players may not know this, so use their own estimation instead - and also ignore anyone they haven't met because that's cheating // It is possible that we didn't consider them untrustworthy when we chose to backstab, but if we changed our mind that's OK if (!bExcludeOtherBackstabbers) iNum++; else if (eObserver == NO_PLAYER || eObserver == GetID() || GET_PLAYER(eObserver).getTeam() == eLoopTeam) iNum += (!WasEverBackstabbedBy(eLoopPlayer) && !IsUntrustworthy(eLoopPlayer)) ? 1 : 0; else if (GET_PLAYER(eObserver).GetDiplomacyAI()->IsHasMet(eLoopPlayer)) iNum += (!WasEverBackstabbedBy(eLoopPlayer) && !GET_PLAYER(eObserver).GetDiplomacyAI()->IsUntrustworthy(eLoopPlayer)) ? 1 : 0; } } return iNum; } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Warmongering Penalties // ------------------------------------ /// How many Minors have we seen this Player attack? int CvDiplomacyAI::GetOtherPlayerNumMinorsAttacked(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumMinorsAttacked[ePlayer]; } void CvDiplomacyAI::SetOtherPlayerNumMinorsAttacked(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumMinorsAttacked to negative value"); ASSERT(NotTeam(ePlayer), "Setting NumMinorsAttacked for own team"); m_aiNumMinorsAttacked[ePlayer] = min(iValue, UCHAR_MAX); } void CvDiplomacyAI::ChangeOtherPlayerNumMinorsAttacked(PlayerTypes ePlayer, int iChange, TeamTypes eAttackedTeam) { // Flag has been set to ignore warmongering? (Set when this player has purchased third party war, or a declaration of independence occurs.) if (IsIgnoreWarmonger()) return; // Disregard our own warmongering if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) return; // Disregard if we're also at war with this team if (GET_TEAM(GetTeam()).isAtWar(eAttackedTeam)) return; // Don't apply warmongering if we haven't met the attacker or the attacked team (otherwise that's cheating) if (!IsHasMet(ePlayer) || !GET_TEAM(GetTeam()).isHasMet(eAttackedTeam)) return; // Ignore our master's warmongering if (IsVassal(ePlayer)) return; // Don't count this if the player declaring war is a vassal because he can't declare war himself if (GET_PLAYER(ePlayer).IsVassalOfSomeone()) return; SetOtherPlayerNumMinorsAttacked(ePlayer, GetOtherPlayerNumMinorsAttacked(ePlayer) + iChange); int iWarmongerValueTimes100 = CvDiplomacyAIHelpers::GetWarmongerTriggerPenalty(ePlayer, eAttackedTeam, GetID(), WARMONGER_MINOR_ATTACKED) * 100; ChangeOtherPlayerWarmongerAmountTimes100(ePlayer, iWarmongerValueTimes100); DoUpdateWarmongerThreats(true); } /// How many Minors have we seen this Player conquer? int CvDiplomacyAI::GetPlayerNumMinorsConquered(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumMinorsConquered[ePlayer]; } void CvDiplomacyAI::SetPlayerNumMinorsConquered(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumMinorsConquered to negative value"); m_aiNumMinorsConquered[ePlayer] = min(iValue, UCHAR_MAX); } /// How many Majors have we seen this Player attack? int CvDiplomacyAI::GetOtherPlayerNumMajorsAttacked(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumMajorsAttacked[ePlayer]; } void CvDiplomacyAI::SetOtherPlayerNumMajorsAttacked(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumMajorsAttacked to negative value"); ASSERT(NotTeam(ePlayer), "Setting NumMajorsAttacked for own team"); m_aiNumMajorsAttacked[ePlayer] = min(iValue, UCHAR_MAX); } void CvDiplomacyAI::ChangeOtherPlayerNumMajorsAttacked(PlayerTypes ePlayer, int iChange, TeamTypes eAttackedTeam) { // Flag has been set to ignore warmongering? (Set when this player has purchased third party war, or a declaration of independence occurs.) if (IsIgnoreWarmonger()) return; // Disregard our own warmongering if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) return; // Disregard if we're also at war with this team if (GET_TEAM(GetTeam()).isAtWar(eAttackedTeam)) return; // Don't apply warmongering if we haven't met the attacker or the attacked team (otherwise that's cheating) if (!IsHasMet(ePlayer) || !GET_TEAM(GetTeam()).isHasMet(eAttackedTeam)) return; // War declarations between humans don't apply warmongering except for the attacked team and their DPs/vassals (prevents exploit) if (eAttackedTeam != GetTeam() && !GET_TEAM(GetTeam()).IsHasDefensivePact(eAttackedTeam) && !GET_TEAM(GetTeam()).IsVassal(eAttackedTeam) && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && GET_TEAM(eAttackedTeam).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // Ignore our master's warmongering if (IsVassal(ePlayer)) return; // Don't count this if the player declaring war is a vassal because he can't declare war himself if (GET_PLAYER(ePlayer).IsVassalOfSomeone()) return; // If we're planning a coop war with this guy against someone on the attacked team (or this guy is a vassal/master/DP of that team), we don't care. for (int iAttackedPlayer = 0; iAttackedPlayer < MAX_MAJOR_CIVS; iAttackedPlayer++) { PlayerTypes eAttackedPlayer = (PlayerTypes) iAttackedPlayer; TeamTypes eTeam = GET_PLAYER(eAttackedPlayer).getTeam(); if (eTeam == eAttackedTeam || GET_TEAM(eTeam).IsHasDefensivePact(eAttackedTeam) || GET_TEAM(eTeam).IsVassal(eAttackedTeam) || GET_TEAM(eAttackedTeam).IsVassal(eTeam)) { if (!GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { if (GetCivApproach(eAttackedPlayer) == CIV_APPROACH_WAR) return; if (eTeam == eAttackedTeam && IsUntrustworthyFriend(eAttackedPlayer)) return; } if (IsLockedIntoCoopWar(eAttackedPlayer)) return; } } SetOtherPlayerNumMajorsAttacked(ePlayer, GetOtherPlayerNumMajorsAttacked(ePlayer) + iChange); int iWarmongerValueTimes100 = CvDiplomacyAIHelpers::GetWarmongerTriggerPenalty(ePlayer, eAttackedTeam, GetID(), WARMONGER_MAJOR_ATTACKED) * 100; ChangeOtherPlayerWarmongerAmountTimes100(ePlayer, iWarmongerValueTimes100); DoUpdateWarmongerThreats(true); } /// How many Majors have we seen this Player conquer? int CvDiplomacyAI::GetPlayerNumMajorsConquered(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumMajorsConquered[ePlayer]; } void CvDiplomacyAI::SetPlayerNumMajorsConquered(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumMajorsConquered to negative value"); m_aiNumMajorsConquered[ePlayer] = min(iValue, UCHAR_MAX); } /// Get the amount of warmongerishness felt toward this player int CvDiplomacyAI::GetOtherPlayerWarmongerAmountTimes100(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiWarmongerAmountTimes100[ePlayer]; } int CvDiplomacyAI::GetOtherPlayerWarmongerAmount(PlayerTypes ePlayer) const { return GetOtherPlayerWarmongerAmountTimes100(ePlayer) / 100; } void CvDiplomacyAI::SetOtherPlayerWarmongerAmountTimes100(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting WarmongerAmountTimes100 for own team"); m_aiWarmongerAmountTimes100[ePlayer] = max(iValue, 0); } void CvDiplomacyAI::ChangeOtherPlayerWarmongerAmountTimes100(PlayerTypes ePlayer, int iChange) { SetOtherPlayerWarmongerAmountTimes100(ePlayer, GetOtherPlayerWarmongerAmountTimes100(ePlayer) + iChange); } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Aggressive Postures // ------------------------------------ /// How aggressively are this player's military Units positioned in relation to us? AggressivePostureTypes CvDiplomacyAI::GetMilitaryAggressivePosture(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); return (AggressivePostureTypes) m_aeMilitaryAggressivePosture[ePlayer]; } void CvDiplomacyAI::SetMilitaryAggressivePosture(PlayerTypes ePlayer, AggressivePostureTypes ePosture) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); PRECONDITION(ePosture >= 0 && ePosture < NUM_AGGRESSIVE_POSTURE_TYPES, "Setting MilitaryAggressivePosture to invalid value"); ASSERT(ePosture == AGGRESSIVE_POSTURE_NONE || NotTeam(ePlayer), "Setting MilitaryAggressivePosture for own team"); m_aeMilitaryAggressivePosture[ePlayer] = ePosture; } /// How aggressively has this player settled in relation to us? /// This is rarely called, so we can just calculate it as needed rather than updating and storing it every turn AggressivePostureTypes CvDiplomacyAI::GetExpansionAggressivePosture(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); if (!IsPlayerValid(ePlayer) || GetPlayer()->getNumCities() <= 0) return AGGRESSIVE_POSTURE_NONE; int iCityLoop = 0; int iCityLoop2 = 0; int iMinDistance = INT_MAX; CvCity* pTheirCapital = GET_PLAYER(ePlayer).getCapitalCity(); if (!pTheirCapital || !pTheirCapital->plot()) return AGGRESSIVE_POSTURE_NONE; for (CvCity* pOtherCity = GET_PLAYER(ePlayer).firstCity(&iCityLoop); pOtherCity != NULL; pOtherCity = GET_PLAYER(ePlayer).nextCity(&iCityLoop)) { CvPlot* pCityPlot = pOtherCity->plot(); if (!pCityPlot) continue; // Ignore their capital if (pOtherCity->IsOriginalCapitalForPlayer(ePlayer)) continue; // Can we actually see this city? No cheating! if (!pCityPlot->isRevealed(GetTeam())) continue; // Only count settled cities, not conquered ones if (GET_PLAYER(pOtherCity->getOriginalOwner()).getTeam() != GET_PLAYER(ePlayer).getTeam()) continue; int iTurnFounded = pOtherCity->getGameTurnFounded(); int iDistanceFromTheirCapital = plotDistance(*pCityPlot, *pTheirCapital->plot()); // Which city of ours (acquired earlier than this one) is closest? for (CvCity* pOurLoopCity = m_pPlayer->firstCity(&iCityLoop2); pOurLoopCity != NULL; pOurLoopCity = m_pPlayer->nextCity(&iCityLoop2)) { CvPlot* pOurCityPlot = pOurLoopCity->plot(); if (!pOurCityPlot) continue; // If we acquired our city after they founded theirs, they're not being aggressive int iGameTurnAcquired = pOurLoopCity->getGameTurnAcquired(); if (iGameTurnAcquired > iTurnFounded) continue; else if (iGameTurnAcquired == iTurnFounded) { // If they're earlier in the player order, they're not being aggressive, they just got there first! if ((int)ePlayer < (int)GetID()) continue; } // Must be on same land area if (pCityPlot->getLandmass() != pOurCityPlot->getLandmass()) continue; // Must be closer to our city than to their capital int iDistance = plotDistance(*pCityPlot, *pOurCityPlot); if (iDistanceFromTheirCapital <= iDistance) continue; if (iDistance < iMinDistance) { iMinDistance = iDistance; } } } if (iMinDistance <= /*5*/ GD_INT_GET(EXPANSION_BICKER_RANGE_LOW)) return AGGRESSIVE_POSTURE_INCREDIBLE; else if (iMinDistance <= /*7*/ GD_INT_GET(EXPANSION_BICKER_RANGE_HIGH)) return AGGRESSIVE_POSTURE_HIGH; else if (iMinDistance <= /*9*/ GD_INT_GET(EXPANSION_BICKER_RANGE_HIGH) + 2) return AGGRESSIVE_POSTURE_MEDIUM; else if (iMinDistance <= /*13*/ GD_INT_GET(EXPANSION_BICKER_RANGE_HIGH) + 6) return AGGRESSIVE_POSTURE_LOW; return AGGRESSIVE_POSTURE_NONE; } /// How aggressively is ePlayer buying land near us? AggressivePostureTypes CvDiplomacyAI::GetPlotBuyingAggressivePosture(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (AggressivePostureTypes) m_aePlotBuyingAggressivePosture[ePlayer]; } void CvDiplomacyAI::SetPlotBuyingAggressivePosture(PlayerTypes ePlayer, AggressivePostureTypes ePosture) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(ePosture >= 0 && ePosture < NUM_AGGRESSIVE_POSTURE_TYPES, "Setting PlotBuyingAggressivePosture to invalid value"); ASSERT(ePosture == AGGRESSIVE_POSTURE_NONE || NotTeam(ePlayer), "Setting PlotBuyingAggressivePosture for own team"); m_aePlotBuyingAggressivePosture[ePlayer] = ePosture; } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Dispute Levels // ------------------------------------ /// What is our level of Dispute with a player over Land? DisputeLevelTypes CvDiplomacyAI::GetLandDisputeLevel(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); return (DisputeLevelTypes) m_aeLandDisputeLevel[ePlayer]; } void CvDiplomacyAI::SetLandDisputeLevel(PlayerTypes ePlayer, DisputeLevelTypes eDisputeLevel) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); PRECONDITION(eDisputeLevel >= 0 && eDisputeLevel < NUM_DISPUTE_LEVELS, "Setting LandDisputeLevel to invalid value"); ASSERT(eDisputeLevel == DISPUTE_LEVEL_NONE || NotTeam(ePlayer), "Setting LandDisputeLevel for own team"); m_aeLandDisputeLevel[ePlayer] = eDisputeLevel; } /// What is our level of Dispute with a player over Victory? DisputeLevelTypes CvDiplomacyAI::GetVictoryDisputeLevel(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (DisputeLevelTypes) m_aeVictoryDisputeLevel[ePlayer]; } void CvDiplomacyAI::SetVictoryDisputeLevel(PlayerTypes ePlayer, DisputeLevelTypes eDisputeLevel) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(eDisputeLevel >= 0 && eDisputeLevel < NUM_DISPUTE_LEVELS, "Setting VictoryDisputeLevel to invalid value"); ASSERT(eDisputeLevel == DISPUTE_LEVEL_NONE || NotTeam(ePlayer), "Setting VictoryDisputeLevel for own team"); m_aeVictoryDisputeLevel[ePlayer] = eDisputeLevel; } /// What is our level of Desire to block this player over Victory? BlockLevelTypes CvDiplomacyAI::GetVictoryBlockLevel(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (BlockLevelTypes) m_aeVictoryBlockLevel[ePlayer]; } void CvDiplomacyAI::SetVictoryBlockLevel(PlayerTypes ePlayer, BlockLevelTypes eBlockLevel) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(eBlockLevel >= 0 && eBlockLevel < NUM_BLOCK_LEVELS, "Setting VictoryBlockLevel to invalid value"); ASSERT(eBlockLevel == BLOCK_LEVEL_NONE || NotTeam(ePlayer), "Setting VictoryBlockLevel for own team"); m_aeVictoryBlockLevel[ePlayer] = eBlockLevel; } /// What is our level of Dispute with a player over World Wonders? DisputeLevelTypes CvDiplomacyAI::GetWonderDisputeLevel(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (DisputeLevelTypes) m_aeWonderDisputeLevel[ePlayer]; } void CvDiplomacyAI::SetWonderDisputeLevel(PlayerTypes ePlayer, DisputeLevelTypes eDisputeLevel) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(eDisputeLevel >= 0 && eDisputeLevel < NUM_DISPUTE_LEVELS, "Setting WonderDisputeLevel to invalid value"); ASSERT(eDisputeLevel == DISPUTE_LEVEL_NONE || NotTeam(ePlayer), "Setting WonderDisputeLevel for own team"); m_aeWonderDisputeLevel[ePlayer] = eDisputeLevel; } /// What is our level of Dispute with a player over Minor Civ Friendship? DisputeLevelTypes CvDiplomacyAI::GetMinorCivDisputeLevel(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (DisputeLevelTypes) m_aeMinorCivDisputeLevel[ePlayer]; } void CvDiplomacyAI::SetMinorCivDisputeLevel(PlayerTypes ePlayer, DisputeLevelTypes eDisputeLevel) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(eDisputeLevel >= 0 && eDisputeLevel < NUM_DISPUTE_LEVELS, "Setting MinorCivDisputeLevel to invalid value"); ASSERT(eDisputeLevel == DISPUTE_LEVEL_NONE || NotTeam(ePlayer), "Setting MinorCivDisputeLevel for own team"); m_aeMinorCivDisputeLevel[ePlayer] = eDisputeLevel; } /// What is our level of Desire to block this player over Technology? BlockLevelTypes CvDiplomacyAI::GetTechBlockLevel(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (BlockLevelTypes) m_aeTechBlockLevel[ePlayer]; } void CvDiplomacyAI::SetTechBlockLevel(PlayerTypes ePlayer, BlockLevelTypes eBlockLevel) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(eBlockLevel >= 0 && eBlockLevel < NUM_BLOCK_LEVELS, "Setting TechBlockLevel to invalid value"); ASSERT(eBlockLevel == BLOCK_LEVEL_NONE || NotTeam(ePlayer), "Setting TechBlockLevel for own team"); m_aeTechBlockLevel[ePlayer] = eBlockLevel; } /// What is our level of Desire to block this player over Social Policies? BlockLevelTypes CvDiplomacyAI::GetPolicyBlockLevel(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (BlockLevelTypes) m_aePolicyBlockLevel[ePlayer]; } void CvDiplomacyAI::SetPolicyBlockLevel(PlayerTypes ePlayer, BlockLevelTypes eBlockLevel) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(eBlockLevel >= 0 && eBlockLevel < NUM_BLOCK_LEVELS, "Setting PolicyBlockLevel to invalid value"); ASSERT(eBlockLevel == BLOCK_LEVEL_NONE || NotTeam(ePlayer), "Setting PolicyBlockLevel for own team"); m_aePolicyBlockLevel[ePlayer] = eBlockLevel; } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Threat Levels // ------------------------------------ /// How much of a threat are these guys to run amok and break everything? ThreatTypes CvDiplomacyAI::GetWarmongerThreat(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (ThreatTypes) m_aeWarmongerThreat[ePlayer]; } void CvDiplomacyAI::SetWarmongerThreat(PlayerTypes ePlayer, ThreatTypes eWarmongerThreat) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(eWarmongerThreat >= 0 && eWarmongerThreat < NUM_THREAT_VALUES, "Setting WarmongerThreat to invalid value"); ASSERT(eWarmongerThreat == THREAT_NONE || NotTeam(ePlayer), "Setting WarmongerThreat for own team"); m_aeWarmongerThreat[ePlayer] = eWarmongerThreat; } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Strength Assessments // ------------------------------------ /// What is our assessment of this player's overall Economic Strength? StrengthTypes CvDiplomacyAI::GetEconomicStrengthComparedToUs(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); return (StrengthTypes) m_aeEconomicStrengthComparedToUs[ePlayer]; } void CvDiplomacyAI::SetEconomicStrengthComparedToUs(PlayerTypes ePlayer, StrengthTypes eEconomicStrength) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); PRECONDITION(eEconomicStrength >= 0 && eEconomicStrength < NUM_STRENGTH_VALUES, "Setting EconomicStrengthComparedToUs to invalid value"); ASSERT(NotMe(ePlayer), "Updating static EconomicStrengthComparedToUs value"); m_aeEconomicStrengthComparedToUs[ePlayer] = eEconomicStrength; } /// What is our assessment of this player's overall Military Strength? StrengthTypes CvDiplomacyAI::GetMilitaryStrengthComparedToUs(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); return (StrengthTypes) m_aeMilitaryStrengthComparedToUs[ePlayer]; } void CvDiplomacyAI::SetMilitaryStrengthComparedToUs(PlayerTypes ePlayer, StrengthTypes eMilitaryStrength) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); PRECONDITION(eMilitaryStrength >= 0 && eMilitaryStrength < NUM_STRENGTH_VALUES, "Setting MilitaryStrengthComparedToUs to invalid value"); ASSERT(NotMe(ePlayer), "Updating static MilitaryStrengthComparedToUs value"); m_aeMilitaryStrengthComparedToUs[ePlayer] = eMilitaryStrength; } /// What is our unbiased assessment of this player's overall Military Strength? (No reduction from Boldness) StrengthTypes CvDiplomacyAI::GetRawMilitaryStrengthComparedToUs(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); return (StrengthTypes) m_aeRawMilitaryStrengthComparedToUs[ePlayer]; } void CvDiplomacyAI::SetRawMilitaryStrengthComparedToUs(PlayerTypes ePlayer, StrengthTypes eMilitaryStrength) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); PRECONDITION(eMilitaryStrength >= 0 && eMilitaryStrength < NUM_STRENGTH_VALUES, "Setting RawMilitaryStrengthComparedToUs to invalid value"); ASSERT(NotMe(ePlayer), "Updating static RawMilitaryStrengthComparedToUs value"); m_aeRawMilitaryStrengthComparedToUs[ePlayer] = eMilitaryStrength; } /// What is our assessment of this player's value as a military target? TargetValueTypes CvDiplomacyAI::GetTargetValue(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); return (TargetValueTypes) m_aeTargetValue[ePlayer]; } void CvDiplomacyAI::SetTargetValue(PlayerTypes ePlayer, TargetValueTypes eTargetValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); PRECONDITION(eTargetValue >= 0 && eTargetValue < NUM_TARGET_VALUES, "Setting TargetValue to invalid value"); ASSERT(NotMe(ePlayer), "Updating static TargetValue"); m_aeTargetValue[ePlayer] = eTargetValue; } /// What is our unbiased assessment of this player's value as a military target? (No reduction from Boldness) TargetValueTypes CvDiplomacyAI::GetRawTargetValue(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); return (TargetValueTypes) m_aeRawTargetValue[ePlayer]; } void CvDiplomacyAI::SetRawTargetValue(PlayerTypes ePlayer, TargetValueTypes eTargetValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); PRECONDITION(eTargetValue >= 0 && eTargetValue < NUM_TARGET_VALUES, "Setting RawTargetValue to invalid value"); ASSERT(NotMe(ePlayer), "Updating static RawTargetValue"); m_aeRawTargetValue[ePlayer] = eTargetValue; } /// Is this player an easy attack target? /// If we're someone's vassal, they're not an easy target (unless we're at war). bool CvDiplomacyAI::IsEasyTarget(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); return (!IsAtWar(ePlayer) && GetPlayer()->IsVassalOfSomeone()) ? false : m_abEasyTarget[ePlayer]; } void CvDiplomacyAI::SetEasyTarget(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_CIV_PLAYERS, "Player index out of bounds"); ASSERT(!bValue || NotTeam(ePlayer), "Setting EasyTarget to true for own team"); m_abEasyTarget[ePlayer] = bValue; } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Promises // ------------------------------------ // //////////////////////////////////// // Military Promise // //////////////////////////////////// /// What is the state of ePlayer's military promise to us? PromiseStates CvDiplomacyAI::GetMilitaryPromiseState(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (PromiseStates) m_aeMilitaryPromiseState[ePlayer]; } void CvDiplomacyAI::SetMilitaryPromiseState(PlayerTypes ePlayer, PromiseStates ePromiseState) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(ePromiseState >= NO_PROMISE_STATE && ePromiseState < NUM_PROMISE_STATES, "Setting MilitaryPromiseState to invalid value"); ASSERT(NotTeam(ePlayer), "Setting MilitaryPromiseState for own team"); PromiseStates eCurrentState = GetMilitaryPromiseState(ePlayer); if (eCurrentState != ePromiseState) { if (ePromiseState != NO_PROMISE_STATE) { if (GET_PLAYER(ePlayer).IsVassalOfSomeone()) { return; } if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) { return; } } m_aeMilitaryPromiseState[ePlayer] = ePromiseState; if (ePromiseState != NO_PROMISE_STATE) { SetMilitaryPromiseTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetMilitaryPromiseTurn(ePlayer, -1); } if (ePromiseState != PROMISE_STATE_MADE) SetPlayerMoveTroopsRequestAccepted(ePlayer, false); } } /// On what turn did the state of ePlayer's military promise to us most recently change? int CvDiplomacyAI::GetMilitaryPromiseTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiMilitaryPromiseTurn[ePlayer]; } void CvDiplomacyAI::SetMilitaryPromiseTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting MilitaryPromiseTurn to invalid value"); ASSERT(NotTeam(ePlayer), "Setting MilitaryPromiseTurn for own team"); m_aiMilitaryPromiseTurn[ePlayer] = iTurn; } bool CvDiplomacyAI::MadeMilitaryPromise(PlayerTypes ePlayer) const { return GetMilitaryPromiseState(ePlayer) == PROMISE_STATE_MADE; } bool CvDiplomacyAI::IgnoredMilitaryPromise(PlayerTypes ePlayer) const { return GetMilitaryPromiseState(ePlayer) == PROMISE_STATE_IGNORED; } bool CvDiplomacyAI::BrokeMilitaryPromise(PlayerTypes ePlayer) const { return GetMilitaryPromiseState(ePlayer) == PROMISE_STATE_BROKEN; } /// Return the number of turns since ePlayer has made a military promise to us int CvDiplomacyAI::GetNumTurnsMilitaryPromise(PlayerTypes ePlayer) const { // Did they make a military promise? if (!MadeMilitaryPromise(ePlayer)) return -1; int iTurnDifference = GC.getGame().getGameTurn() - GetMilitaryPromiseTurn(ePlayer); // this promise does not scale with gamespeed! return std::max(/*20*/ GD_INT_GET(MOVE_TROOPS_MEMORY_TURN_EXPIRATION) - iTurnDifference, 0); } // //////////////////////////////////// // Expansion Promise // //////////////////////////////////// /// What is the state of ePlayer's expansion promise to us? PromiseStates CvDiplomacyAI::GetExpansionPromiseState(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (PromiseStates) m_aeExpansionPromiseState[ePlayer]; } void CvDiplomacyAI::SetExpansionPromiseState(PlayerTypes ePlayer, PromiseStates ePromiseState) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(ePromiseState >= NO_PROMISE_STATE && ePromiseState < NUM_PROMISE_STATES, "Setting ExpansionPromiseState to invalid value"); ASSERT(NotTeam(ePlayer), "Setting ExpansionPromiseState for own team"); PromiseStates eCurrentState = GetExpansionPromiseState(ePlayer); if (eCurrentState != ePromiseState) { m_aeExpansionPromiseState[ePlayer] = ePromiseState; if (ePromiseState != NO_PROMISE_STATE) { SetExpansionPromiseTurn(ePlayer, GC.getGame().getGameTurn()); // They ignored or broke an expansion promise! Cancel any commitment not to settle near them! if (ePromiseState != PROMISE_STATE_MADE && GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeExpansionPromise(GetID())) { GET_PLAYER(ePlayer).GetDiplomacyAI()->SetExpansionPromiseState(GetID(), NO_PROMISE_STATE); } } else { SetExpansionPromiseTurn(ePlayer, -1); } } } bool CvDiplomacyAI::MadeExpansionPromise(PlayerTypes ePlayer) const { return GetExpansionPromiseState(ePlayer) == PROMISE_STATE_MADE; } bool CvDiplomacyAI::IgnoredExpansionPromise(PlayerTypes ePlayer) const { return GetExpansionPromiseState(ePlayer) == PROMISE_STATE_IGNORED; } bool CvDiplomacyAI::BrokeExpansionPromise(PlayerTypes ePlayer) const { return GetExpansionPromiseState(ePlayer) == PROMISE_STATE_BROKEN; } /// Which players have we agreed to avoid settling near? vector CvDiplomacyAI::GetPlayersWithNoSettlePolicy() const { // If AI has no cities, ignore the promise - they need a capital vector result; if (GetPlayer()->getNumCities() == 0) return result; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = static_cast(iPlayerLoop); if (GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeExpansionPromise(GetID())) result.push_back(ePlayer); else if (IsTeammate(ePlayer) && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) result.push_back(ePlayer); } return result; } /// On what turn did the state of ePlayer's expansion promise to us most recently change? int CvDiplomacyAI::GetExpansionPromiseTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiExpansionPromiseTurn[ePlayer]; } void CvDiplomacyAI::SetExpansionPromiseTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting ExpansionPromiseTurn to invalid value"); ASSERT(NotTeam(ePlayer), "Setting ExpansionPromiseTurn for own team"); m_aiExpansionPromiseTurn[ePlayer] = iTurn; } /// Return the number of turns since ePlayer has made an expansion promise to us int CvDiplomacyAI::GetNumTurnsExpansionPromise(PlayerTypes ePlayer) const { if (!MadeExpansionPromise(ePlayer)) return -1; int iTurnDifference = GC.getGame().getGameTurn() - GetExpansionPromiseTurn(ePlayer); int iTimeOutTurns = (/*50*/ GD_INT_GET(EXPANSION_PROMISE_TURNS_EFFECTIVE) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100; int iValue = (iTimeOutTurns - iTurnDifference); if (iValue >= 0) { return iValue; } return -1; } /// If true, this player can't ask us not to settle near them (used in the UI) bool CvDiplomacyAI::IsDontSettleMessageTooSoon(PlayerTypes ePlayer) const { if (IsTeammate(ePlayer)) return true; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeExpansionPromise(GetID())) return true; // Observer can't ask this if (!GET_PLAYER(ePlayer).isAlive() || GET_PLAYER(ePlayer).isObserver()) return true; // Can't ask this while at war if (IsAtWar(ePlayer)) return true; return false; } /// Are we angry about this player settling too near us? bool CvDiplomacyAI::IsAngryAboutExpansion(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abAngryAboutExpansion[ePlayer]; } void CvDiplomacyAI::SetAngryAboutExpansion(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting AngryAboutExpansion for own team"); m_abAngryAboutExpansion[ePlayer] = bValue; } /// Did we ever request an expansion promise from this player? bool CvDiplomacyAI::EverRequestedExpansionPromise(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abEverRequestedExpansionPromise[ePlayer]; } void CvDiplomacyAI::SetEverRequestedExpansionPromise(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting EverRequestedExpansionPromise for own team"); m_abEverRequestedExpansionPromise[ePlayer] = bValue; } // //////////////////////////////////// // Border Promise // //////////////////////////////////// /// What is the state of ePlayer's border promise to us? PromiseStates CvDiplomacyAI::GetBorderPromiseState(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (PromiseStates) m_aeBorderPromiseState[ePlayer]; } void CvDiplomacyAI::SetBorderPromiseState(PlayerTypes ePlayer, PromiseStates ePromiseState) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(ePromiseState >= NO_PROMISE_STATE && ePromiseState < NUM_PROMISE_STATES, "Setting BorderPromiseState to invalid value"); ASSERT(NotTeam(ePlayer), "Setting BorderPromiseState for own team"); PromiseStates eCurrentState = GetBorderPromiseState(ePlayer); if (eCurrentState != ePromiseState) { m_aeBorderPromiseState[ePlayer] = ePromiseState; if (ePromiseState != NO_PROMISE_STATE) { SetBorderPromiseTurn(ePlayer, GC.getGame().getGameTurn()); if (ePromiseState == PROMISE_STATE_MADE) { SetBorderPromisePosture(ePlayer, GetPlotBuyingAggressivePosture(ePlayer)); SetEverMadeBorderPromise(ePlayer, true); } else { SetBorderPromisePosture(ePlayer, AGGRESSIVE_POSTURE_NONE); // They ignored or broke a border promise! Cancel any commitment not to buy land near them! if (GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeBorderPromise(GetID())) { GET_PLAYER(ePlayer).GetDiplomacyAI()->SetBorderPromiseState(GetID(), NO_PROMISE_STATE); } } } else { SetBorderPromiseTurn(ePlayer, -1); SetBorderPromisePosture(ePlayer, AGGRESSIVE_POSTURE_NONE); } } } bool CvDiplomacyAI::MadeBorderPromise(PlayerTypes ePlayer) const { return GetBorderPromiseState(ePlayer) == PROMISE_STATE_MADE; } bool CvDiplomacyAI::IgnoredBorderPromise(PlayerTypes ePlayer) const { return GetBorderPromiseState(ePlayer) == PROMISE_STATE_IGNORED; } bool CvDiplomacyAI::BrokeBorderPromise(PlayerTypes ePlayer) const { return GetBorderPromiseState(ePlayer) == PROMISE_STATE_BROKEN; } /// On what turn did the state of ePlayer's border promise to us most recently change? int CvDiplomacyAI::GetBorderPromiseTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiBorderPromiseTurn[ePlayer]; } void CvDiplomacyAI::SetBorderPromiseTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting BorderPromiseTurn to invalid value"); ASSERT(NotTeam(ePlayer), "Setting BorderPromiseTurn for own team"); m_aiBorderPromiseTurn[ePlayer] = iTurn; } /// Return the number of turns since ePlayer has made a border promise to us int CvDiplomacyAI::GetNumTurnsBorderPromise(PlayerTypes ePlayer) const { if (!MadeBorderPromise(ePlayer)) return -1; int iTurnDifference = GC.getGame().getGameTurn() - GetBorderPromiseTurn(ePlayer); int iTimeOutTurns = (/*50*/ GD_INT_GET(BORDER_PROMISE_TURNS_EFFECTIVE) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100; int iValue = (iTimeOutTurns - iTurnDifference); if (iValue >= 0) { return iValue; } return -1; } /// What was the plot buying aggressive posture of ePlayer when they made a border promise to us? AggressivePostureTypes CvDiplomacyAI::GetBorderPromisePosture(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (AggressivePostureTypes) m_aeBorderPromisePosture[ePlayer]; } void CvDiplomacyAI::SetBorderPromisePosture(PlayerTypes ePlayer, AggressivePostureTypes ePosture) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(ePosture >= AGGRESSIVE_POSTURE_NONE && ePosture < NUM_AGGRESSIVE_POSTURE_TYPES, "Setting BorderPromisePosture to invalid value"); ASSERT(NotTeam(ePlayer), "Setting BorderPromisePosture for own team"); m_aeBorderPromisePosture[ePlayer] = ePosture; } /// Did this player ever make a border promise to us? bool CvDiplomacyAI::EverMadeBorderPromise(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abEverMadeBorderPromise[ePlayer]; } void CvDiplomacyAI::SetEverMadeBorderPromise(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting EverMadeBorderPromise for own team"); m_abEverMadeBorderPromise[ePlayer] = bValue; } // //////////////////////////////////// // Bully City-State Promise // //////////////////////////////////// /// What is the state of ePlayer's bully city-state promise to us PromiseStates CvDiplomacyAI::GetBullyCityStatePromiseState(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (PromiseStates) m_aeBullyCityStatePromiseState[ePlayer]; } void CvDiplomacyAI::SetBullyCityStatePromiseState(PlayerTypes ePlayer, PromiseStates ePromiseState) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(ePromiseState >= NO_PROMISE_STATE && ePromiseState < NUM_PROMISE_STATES, "Setting BullyCityStatePromiseState to invalid value"); ASSERT(NotTeam(ePlayer), "Setting BullyCityStatePromiseState for own team"); PromiseStates eCurrentState = GetBullyCityStatePromiseState(ePlayer); if (eCurrentState != ePromiseState) { m_aeBullyCityStatePromiseState[ePlayer] = ePromiseState; if (ePromiseState != NO_PROMISE_STATE) { SetBullyCityStatePromiseTurn(ePlayer, GC.getGame().getGameTurn()); // They ignored or broke a promise to stop bullying our protected City-States! Cancel any commitment not to bully theirs! if (ePromiseState != PROMISE_STATE_MADE && GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeBullyCityStatePromise(GetID())) { GET_PLAYER(ePlayer).GetDiplomacyAI()->SetBullyCityStatePromiseState(GetID(), NO_PROMISE_STATE); } } else { SetBullyCityStatePromiseTurn(ePlayer, -1); } } } /// On what turn did the state of ePlayer's bully city-state promise to us most recently change? int CvDiplomacyAI::GetBullyCityStatePromiseTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiBullyCityStatePromiseTurn[ePlayer]; } void CvDiplomacyAI::SetBullyCityStatePromiseTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting BullyCityStatePromiseTurn to invalid value"); ASSERT(NotTeam(ePlayer), "Setting BullyCityStatePromiseTurn for own team"); m_aiBullyCityStatePromiseTurn[ePlayer] = iTurn; } bool CvDiplomacyAI::MadeBullyCityStatePromise(PlayerTypes ePlayer) const { return GetBullyCityStatePromiseState(ePlayer) == PROMISE_STATE_MADE; } bool CvDiplomacyAI::IgnoredBullyCityStatePromise(PlayerTypes ePlayer) const { return GetBullyCityStatePromiseState(ePlayer) == PROMISE_STATE_IGNORED; } bool CvDiplomacyAI::BrokeBullyCityStatePromise(PlayerTypes ePlayer) const { return GetBullyCityStatePromiseState(ePlayer) == PROMISE_STATE_BROKEN; } // //////////////////////////////////// // Attack City-State Promise // //////////////////////////////////// /// What is the state of ePlayer's attack city-state promise to us PromiseStates CvDiplomacyAI::GetAttackCityStatePromiseState(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (PromiseStates) m_aeAttackCityStatePromiseState[ePlayer]; } void CvDiplomacyAI::SetAttackCityStatePromiseState(PlayerTypes ePlayer, PromiseStates ePromiseState) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(ePromiseState >= NO_PROMISE_STATE && ePromiseState < NUM_PROMISE_STATES, "Setting AttackCityStatePromiseState to invalid value"); ASSERT(NotTeam(ePlayer), "Setting AttackCityStatePromiseState for own team"); PromiseStates eCurrentState = GetAttackCityStatePromiseState(ePlayer); if (eCurrentState != ePromiseState) { // Vassals can't backstab...and humans can't backstab other humans if (ePromiseState == PROMISE_STATE_BROKEN) { if (GET_PLAYER(ePlayer).IsVassalOfSomeone()) { return; } if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) { return; } } m_aeAttackCityStatePromiseState[ePlayer] = ePromiseState; if (ePromiseState != NO_PROMISE_STATE) { SetAttackCityStatePromiseTurn(ePlayer, GC.getGame().getGameTurn()); // They ignored or broke a promise to stop attacking our protected City-States! Cancel any commitment not to attack theirs! if (ePromiseState != PROMISE_STATE_MADE && GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeAttackCityStatePromise(GetID())) { GET_PLAYER(ePlayer).GetDiplomacyAI()->SetAttackCityStatePromiseState(GetID(), NO_PROMISE_STATE); } } else { SetAttackCityStatePromiseTurn(ePlayer, -1); } } } /// On what turn did the state of ePlayer's attack city-state promise to us most recently change? int CvDiplomacyAI::GetAttackCityStatePromiseTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiAttackCityStatePromiseTurn[ePlayer]; } void CvDiplomacyAI::SetAttackCityStatePromiseTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting AttackCityStatePromiseTurn to invalid value"); ASSERT(NotTeam(ePlayer), "Setting AttackCityStatePromiseTurn for own team"); m_aiAttackCityStatePromiseTurn[ePlayer] = iTurn; } bool CvDiplomacyAI::MadeAttackCityStatePromise(PlayerTypes ePlayer) const { return GetAttackCityStatePromiseState(ePlayer) == PROMISE_STATE_MADE; } bool CvDiplomacyAI::IgnoredAttackCityStatePromise(PlayerTypes ePlayer) const { return GetAttackCityStatePromiseState(ePlayer) == PROMISE_STATE_IGNORED; } bool CvDiplomacyAI::BrokeAttackCityStatePromise(PlayerTypes ePlayer) const { return GetAttackCityStatePromiseState(ePlayer) == PROMISE_STATE_BROKEN; } // //////////////////////////////////// // Spy Promise // //////////////////////////////////// /// What is the state of ePlayer's spy promise to us PromiseStates CvDiplomacyAI::GetSpyPromiseState(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (PromiseStates) m_aeSpyPromiseState[ePlayer]; } void CvDiplomacyAI::SetSpyPromiseState(PlayerTypes ePlayer, PromiseStates ePromiseState) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(ePromiseState >= NO_PROMISE_STATE && ePromiseState < NUM_PROMISE_STATES, "Setting SpyPromiseState to invalid value"); ASSERT(NotTeam(ePlayer), "Setting SpyPromiseState for own team"); PromiseStates eCurrentState = GetSpyPromiseState(ePlayer); if (eCurrentState != ePromiseState) { m_aeSpyPromiseState[ePlayer] = ePromiseState; if (ePromiseState != NO_PROMISE_STATE) { SetSpyPromiseTurn(ePlayer, GC.getGame().getGameTurn()); // They ignored or broke a promise to stop spying on us! Cancel any commitment not to spy on them! if (ePromiseState != PROMISE_STATE_MADE && GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeSpyPromise(GetID())) { GET_PLAYER(ePlayer).GetDiplomacyAI()->SetSpyPromiseState(GetID(), NO_PROMISE_STATE); } } else { SetSpyPromiseTurn(ePlayer, -1); } } } /// On what turn did the state of ePlayer's spy promise to us most recently change? int CvDiplomacyAI::GetSpyPromiseTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiSpyPromiseTurn[ePlayer]; } void CvDiplomacyAI::SetSpyPromiseTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting SpyPromiseTurn to invalid value"); ASSERT(NotTeam(ePlayer), "Setting SpyPromiseTurn for own team"); m_aiSpyPromiseTurn[ePlayer] = iTurn; } bool CvDiplomacyAI::MadeSpyPromise(PlayerTypes ePlayer) const { return GetSpyPromiseState(ePlayer) == PROMISE_STATE_MADE; } bool CvDiplomacyAI::IgnoredSpyPromise(PlayerTypes ePlayer) const { return GetSpyPromiseState(ePlayer) == PROMISE_STATE_IGNORED; } bool CvDiplomacyAI::BrokeSpyPromise(PlayerTypes ePlayer) const { return GetSpyPromiseState(ePlayer) == PROMISE_STATE_BROKEN; } /// If true, this player can't ask us not to spy on them (used in the UI) bool CvDiplomacyAI::IsStopSpyingMessageTooSoon(PlayerTypes ePlayer) const { if (IsTeammate(ePlayer)) return true; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeSpyPromise(GetID())) return true; // Observer can't ask this if (!GET_PLAYER(ePlayer).isAlive() || GET_PLAYER(ePlayer).isObserver()) return true; // Can't ask this while at war if (IsAtWar(ePlayer)) return true; return false; } // //////////////////////////////////// // Religious Conversion Promise // //////////////////////////////////// /// What is the state of ePlayer's no convert promise to us PromiseStates CvDiplomacyAI::GetNoConvertPromiseState(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (PromiseStates) m_aeNoConvertPromiseState[ePlayer]; } void CvDiplomacyAI::SetNoConvertPromiseState(PlayerTypes ePlayer, PromiseStates ePromiseState) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(ePromiseState >= NO_PROMISE_STATE && ePromiseState < NUM_PROMISE_STATES, "Setting NoConvertPromiseState to invalid value"); ASSERT(NotTeam(ePlayer), "Setting NoConvertPromiseState for own team"); PromiseStates eCurrentState = GetNoConvertPromiseState(ePlayer); if (eCurrentState != ePromiseState) { m_aeNoConvertPromiseState[ePlayer] = ePromiseState; // Reset the ability to ask the promise again if (eCurrentState == PROMISE_STATE_MADE) { GET_PLAYER(ePlayer).GetDiplomacyAI()->SetPlayerAskedNotToConvert(GetID(), false); } if (ePromiseState != NO_PROMISE_STATE) { SetNoConvertPromiseTurn(ePlayer, GC.getGame().getGameTurn()); // They ignored or broke a promise to stop converting our cities! Cancel any commitment not to convert theirs! if (ePromiseState != PROMISE_STATE_MADE && GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeNoConvertPromise(GetID())) { GET_PLAYER(ePlayer).GetDiplomacyAI()->SetNoConvertPromiseState(GetID(), NO_PROMISE_STATE); } } else { SetNoConvertPromiseTurn(ePlayer, -1); } } } /// On what turn did the state of ePlayer's promise to us to stop converting our cities most recently change? int CvDiplomacyAI::GetNoConvertPromiseTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNoConvertPromiseTurn[ePlayer]; } void CvDiplomacyAI::SetNoConvertPromiseTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting NoConvertPromiseTurn to invalid value"); ASSERT(NotTeam(ePlayer), "Setting NoConvertPromiseTurn for own team"); m_aiNoConvertPromiseTurn[ePlayer] = iTurn; } bool CvDiplomacyAI::MadeNoConvertPromise(PlayerTypes ePlayer) const { return GetNoConvertPromiseState(ePlayer) == PROMISE_STATE_MADE; } bool CvDiplomacyAI::IgnoredNoConvertPromise(PlayerTypes ePlayer) const { return GetNoConvertPromiseState(ePlayer) == PROMISE_STATE_IGNORED; } bool CvDiplomacyAI::BrokeNoConvertPromise(PlayerTypes ePlayer) const { return GetNoConvertPromiseState(ePlayer) == PROMISE_STATE_BROKEN; } /// If true, this player can't ask us not to send missionaries and prophets to their cities (used in the UI) bool CvDiplomacyAI::IsPlayerAskedNotToConvert(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); // Can't ask if we've already promised if (GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeNoConvertPromise(GetID())) return true; // Observer can't ask this if (!GET_PLAYER(ePlayer).isAlive() || GET_PLAYER(ePlayer).isObserver()) return true; // Can't ask this while at war if (IsAtWar(ePlayer)) return true; // Teammates and vassals automatically won't convert cities if the player owns a religion if (GET_PLAYER(ePlayer).GetReligions()->OwnsReligion() && (IsTeammate(ePlayer) || IsVassal(ePlayer))) return true; return m_abAskedNotToConvert[ePlayer]; } void CvDiplomacyAI::SetPlayerAskedNotToConvert(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting AskedNotToConvert for own team"); m_abAskedNotToConvert[ePlayer] = bValue; } /// Has this player ever converted one of our cities (if we care)? bool CvDiplomacyAI::HasEverConvertedCity(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abEverConvertedCity[ePlayer]; } void CvDiplomacyAI::SetEverConvertedCity(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting EverConvertedCity for own team"); m_abEverConvertedCity[ePlayer] = bValue; } // //////////////////////////////////// // Digging Promise // //////////////////////////////////// /// What is the state of ePlayer's no digging promise to us PromiseStates CvDiplomacyAI::GetNoDiggingPromiseState(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (PromiseStates) m_aeNoDiggingPromiseState[ePlayer]; } void CvDiplomacyAI::SetNoDiggingPromiseState(PlayerTypes ePlayer, PromiseStates ePromiseState) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(ePromiseState >= NO_PROMISE_STATE && ePromiseState < NUM_PROMISE_STATES, "Setting NoDiggingPromiseState to invalid value"); ASSERT(NotTeam(ePlayer), "Setting NoDiggingPromiseState for own team"); PromiseStates eCurrentState = GetNoDiggingPromiseState(ePlayer); if (eCurrentState != ePromiseState) { m_aeNoDiggingPromiseState[ePlayer] = ePromiseState; // Reset the ability to ask the promise again if (eCurrentState == PROMISE_STATE_MADE) { GET_PLAYER(ePlayer).GetDiplomacyAI()->SetPlayerAskedNotToDig(GetID(), false); } if (ePromiseState != NO_PROMISE_STATE) { SetNoDiggingPromiseTurn(ePlayer, GC.getGame().getGameTurn()); // They ignored or broke a promise to stop digging up our artifacts! Cancel any commitment not to dig up theirs! if (ePromiseState != PROMISE_STATE_MADE && GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeNoDiggingPromise(GetID()) && !GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { GET_PLAYER(ePlayer).GetDiplomacyAI()->SetNoDiggingPromiseState(GetID(), NO_PROMISE_STATE); } } else { SetNoDiggingPromiseTurn(ePlayer, -1); } } } /// On what turn did the state of ePlayer's promise to us to stop digging up our artifacts most recently change? int CvDiplomacyAI::GetNoDiggingPromiseTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNoDiggingPromiseTurn[ePlayer]; } void CvDiplomacyAI::SetNoDiggingPromiseTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting NoDiggingPromiseTurn to invalid value"); ASSERT(NotTeam(ePlayer), "Setting NoDiggingPromiseTurn for own team"); m_aiNoDiggingPromiseTurn[ePlayer] = iTurn; } bool CvDiplomacyAI::MadeNoDiggingPromise(PlayerTypes ePlayer) const { return GetNoDiggingPromiseState(ePlayer) == PROMISE_STATE_MADE; } bool CvDiplomacyAI::IgnoredNoDiggingPromise(PlayerTypes ePlayer) const { return GetNoDiggingPromiseState(ePlayer) == PROMISE_STATE_IGNORED; } bool CvDiplomacyAI::BrokeNoDiggingPromise(PlayerTypes ePlayer) const { return GetNoDiggingPromiseState(ePlayer) == PROMISE_STATE_BROKEN; } /// If true, this player can't ask us not to dig up their artifacts (used in the UI) bool CvDiplomacyAI::IsPlayerAskedNotToDig(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); // Can't ask if we've already promised if (GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeNoDiggingPromise(GetID())) return true; // Can't ask if we're their teammate or vassal - promise is automatic if (IsTeammate(ePlayer) || IsVassal(ePlayer)) return true; // Observer can't ask this if (!GET_PLAYER(ePlayer).isAlive() || GET_PLAYER(ePlayer).isObserver()) return true; // Can't ask this while at war if (IsAtWar(ePlayer)) return true; return m_abAskedNotToDig[ePlayer]; } void CvDiplomacyAI::SetPlayerAskedNotToDig(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting AskedNotToDig for own team"); m_abAskedNotToDig[ePlayer] = bValue; } // //////////////////////////////////// // Coop War Promise // //////////////////////////////////// /// Did this player break a coop war promise to us? bool CvDiplomacyAI::BrokeCoopWarPromise(PlayerTypes ePlayer) const { return GetBrokeCoopWarPromiseTurn(ePlayer) != -1; } void CvDiplomacyAI::SetBrokeCoopWarPromise(PlayerTypes ePlayer, bool bValue) { if (bValue) { SetBrokeCoopWarPromiseTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetBrokeCoopWarPromiseTurn(ePlayer, -1); } } /// On what turn did ePlayer most recently break a coop war promise to us? int CvDiplomacyAI::GetBrokeCoopWarPromiseTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiBrokenCoopWarPromiseTurn[ePlayer]; } void CvDiplomacyAI::SetBrokeCoopWarPromiseTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting BrokenCoopWarPromiseTurn to invalid value"); ASSERT(NotTeam(ePlayer), "Setting BrokenCoopWarPromiseTurn for own team"); m_aiBrokenCoopWarPromiseTurn[ePlayer] = iTurn; } // //////////////////////////////////// // Global Promise Checks // //////////////////////////////////// bool CvDiplomacyAI::BrokeAnyPromise(PlayerTypes ePlayer, int iWithinXTurns /* = -1 */) const { bool bCheckDuration = iWithinXTurns > -1; int iGameTurn = GC.getGame().getGameTurn(); if (BrokeMilitaryPromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetMilitaryPromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } if (BrokeExpansionPromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetExpansionPromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } if (BrokeBorderPromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetBorderPromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } if (BrokeBullyCityStatePromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetBullyCityStatePromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } if (BrokeAttackCityStatePromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetAttackCityStatePromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } if (BrokeSpyPromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetSpyPromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } if (BrokeNoConvertPromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetNoConvertPromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } if (BrokeNoDiggingPromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetNoDiggingPromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } if (BrokeCoopWarPromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetBrokeCoopWarPromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } return false; } bool CvDiplomacyAI::IgnoredAnyPromise(PlayerTypes ePlayer, int iWithinXTurns /* = -1 */) const { bool bCheckDuration = iWithinXTurns > -1; int iGameTurn = GC.getGame().getGameTurn(); if (IgnoredMilitaryPromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetMilitaryPromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } if (IgnoredExpansionPromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetExpansionPromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } if (IgnoredBorderPromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetBorderPromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } if (IgnoredBullyCityStatePromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetBullyCityStatePromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } if (IgnoredAttackCityStatePromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetAttackCityStatePromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } if (IgnoredSpyPromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetSpyPromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } if (IgnoredNoConvertPromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetNoConvertPromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } if (IgnoredNoDiggingPromise(ePlayer)) { if (bCheckDuration) { int iTurnDifference = iGameTurn - GetNoDiggingPromiseTurn(ePlayer); if (iTurnDifference <= iWithinXTurns) return true; } else return true; } return false; } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Event Flags // ------------------------------------ /// Did this player return our original capital to us? If bForPenaltyReduction == false, then don't apply a bonus if they've ever captured our capital. bool CvDiplomacyAI::IsPlayerReturnedCapital(PlayerTypes ePlayer, bool bForPenaltyReduction) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (!bForPenaltyReduction && IsCapitalCapturedBy(ePlayer, false, true, true)) ? false : m_abReturnedCapital[ePlayer]; } void CvDiplomacyAI::SetPlayerReturnedCapital(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotMe(ePlayer), "Setting ReturnedCapital for self"); m_abReturnedCapital[ePlayer] = bValue; } /// Did this player return our Holy City to us? If bForPenaltyReduction == false, then don't apply a bonus if they've ever captured our capital or Holy City. bool CvDiplomacyAI::IsPlayerReturnedHolyCity(PlayerTypes ePlayer, bool bForPenaltyReduction) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (!bForPenaltyReduction && (IsCapitalCapturedBy(ePlayer, false, true, true) || IsHolyCityCapturedBy(ePlayer, false, true, true))) ? false : m_abReturnedHolyCity[ePlayer]; } void CvDiplomacyAI::SetPlayerReturnedHolyCity(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotMe(ePlayer), "Setting ReturnedHolyCity for self"); m_abReturnedHolyCity[ePlayer] = bValue; } /// Did this player liberate our original capital? bool CvDiplomacyAI::IsPlayerLiberatedCapital(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return IsCapitalCapturedBy(ePlayer, false, true, true) ? false : m_abLiberatedCapital[ePlayer]; } void CvDiplomacyAI::SetPlayerLiberatedCapital(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotMe(ePlayer), "Setting LiberatedCapital for self"); m_abLiberatedCapital[ePlayer] = bValue; } /// Did this player liberate our Holy City? bool CvDiplomacyAI::IsPlayerLiberatedHolyCity(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (IsCapitalCapturedBy(ePlayer, false, true, true) || IsHolyCityCapturedBy(ePlayer, false, true, true)) ? false : m_abLiberatedHolyCity[ePlayer]; } void CvDiplomacyAI::SetPlayerLiberatedHolyCity(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotMe(ePlayer), "Setting LiberatedHolyCity for self"); m_abLiberatedHolyCity[ePlayer] = bValue; } /// Did this player capture our original capital? bool CvDiplomacyAI::IsPlayerCapturedCapital(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abCapturedCapital[ePlayer]; } void CvDiplomacyAI::SetPlayerCapturedCapital(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting CapturedCapital for own team"); m_abCapturedCapital[ePlayer] = bValue; if (bValue) SetPlayerEverCapturedCapital(ePlayer, true); } bool CvDiplomacyAI::IsPlayerEverCapturedCapital(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abEverCapturedCapital[ePlayer]; } void CvDiplomacyAI::SetPlayerEverCapturedCapital(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting EverCapturedCapital for own team"); m_abEverCapturedCapital[ePlayer] = bValue; } /// Returns if this player's original capital was captured by ePlayer bool CvDiplomacyAI::IsCapitalCapturedBy(PlayerTypes ePlayer, bool bCurrently, bool bTeammates, bool bCheckEver) const { if (GET_PLAYER(ePlayer).isMinorCiv()) bCurrently = true; if (!bCurrently && (IsPlayerCapturedCapital(ePlayer) || (bCheckEver && IsPlayerEverCapturedCapital(ePlayer)))) return true; CvPlot *pOriginalCapitalPlot = GC.getMap().plot(m_pPlayer->GetOriginalCapitalX(), m_pPlayer->GetOriginalCapitalY()); if (pOriginalCapitalPlot != NULL && pOriginalCapitalPlot->isCity()) { if (bTeammates) { if (GET_PLAYER(pOriginalCapitalPlot->getOwner()).getTeam() == GET_PLAYER(ePlayer).getTeam()) return true; } else { if (pOriginalCapitalPlot->getOwner() == ePlayer) return true; } } if (bTeammates && !bCurrently) { vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); for (size_t i=0; i= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abCapturedHolyCity[ePlayer]; } void CvDiplomacyAI::SetPlayerCapturedHolyCity(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting CapturedHolyCity for own team"); m_abCapturedHolyCity[ePlayer] = bValue; if (bValue) SetPlayerEverCapturedHolyCity(ePlayer, true); } bool CvDiplomacyAI::IsPlayerEverCapturedHolyCity(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abEverCapturedHolyCity[ePlayer]; } void CvDiplomacyAI::SetPlayerEverCapturedHolyCity(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting EverCapturedHolyCity for own team"); m_abEverCapturedHolyCity[ePlayer] = bValue; } /// Returns if this player's Holy City was captured by ePlayer bool CvDiplomacyAI::IsHolyCityCapturedBy(PlayerTypes ePlayer, bool bCurrently, bool bTeammates, bool bCheckEver) const { if (GET_PLAYER(ePlayer).isMinorCiv()) bCurrently = true; if (!bCurrently && (IsPlayerCapturedHolyCity(ePlayer) || (bCheckEver && IsPlayerEverCapturedHolyCity(ePlayer)))) return true; CvPlot *pHolyCityPlot = GC.getMap().plot(m_pPlayer->GetLostHolyCityX(), m_pPlayer->GetLostHolyCityY()); if (pHolyCityPlot != NULL && pHolyCityPlot->isCity() && pHolyCityPlot->getPlotCity()->GetCityReligions()->IsHolyCityForReligion(GetPlayer()->GetReligions()->GetOriginalReligionCreatedByPlayer())) { if (bTeammates) { if (GET_PLAYER(pHolyCityPlot->getOwner()).getTeam() == GET_PLAYER(ePlayer).getTeam()) return true; } else { if (pHolyCityPlot->getOwner() == ePlayer) return true; } } if (bTeammates && !bCurrently) { vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); for (size_t i=0; i GetNumCitiesEverLiberatedBy(ePlayer)) return false; // Not if currently at war or untrustworthy. if (IsAtWar(ePlayer) || IsUntrustworthy(ePlayer)) return false; if (WasResurrectedBy(ePlayer) || IsPlayerLiberatedCapital(ePlayer) || IsPlayerLiberatedHolyCity(ePlayer)) return true; if (!bIgnoreReturns) { if (IsPlayerReturnedCapital(ePlayer) || IsPlayerReturnedHolyCity(ePlayer)) return true; } if (!bOnlyMajorCities) { if (GetNumCitiesLiberatedBy(ePlayer) > 0 && GetNumCitiesEverLiberatedBy(ePlayer) > GetNumCitiesCapturedBy(ePlayer)) return true; } return false; } /// Did this player resurrect us and then attack us? bool CvDiplomacyAI::IsResurrectorAttackedUs(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abResurrectorAttackedUs[ePlayer]; } void CvDiplomacyAI::SetResurrectorAttackedUs(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting ResurrectorAttackedUs for own team"); m_abResurrectorAttackedUs[ePlayer] = bValue; } /// Did this player ever successfully pass sanctions against us in the World Congress? bool CvDiplomacyAI::HasEverSanctionedUs(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abEverSanctionedUs[ePlayer]; } void CvDiplomacyAI::SetEverSanctionedUs(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotMe(ePlayer), "Setting EverSanctionedUs for self"); m_abEverSanctionedUs[ePlayer] = bValue; } /// Did this player ever successfully repeal sanctions against us in the World Congress? bool CvDiplomacyAI::HasEverUnsanctionedUs(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abEverUnsanctionedUs[ePlayer]; } void CvDiplomacyAI::SetEverUnsanctionedUs(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotMe(ePlayer), "Setting EverUnsanctionedUs for self"); m_abEverUnsanctionedUs[ePlayer] = bValue; } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // # of times/points counters // ------------------------------------ /// Returns the number of cities liberated by ePlayer int CvDiplomacyAI::GetNumCitiesLiberatedBy(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumCitiesLiberated[ePlayer]; } void CvDiplomacyAI::SetNumCitiesLiberatedBy(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumCitiesLiberated to a negative value"); ASSERT(NotMe(ePlayer), "Setting NumCitiesLiberated for self"); m_aiNumCitiesLiberated[ePlayer] = min(iValue, UCHAR_MAX); if (iValue > 0) { SetLiberatedCitiesTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetLiberatedCitiesTurn(ePlayer, -1); } } void CvDiplomacyAI::ChangeNumCitiesLiberatedBy(PlayerTypes ePlayer, int iChange) { SetNumCitiesLiberatedBy(ePlayer, GetNumCitiesLiberatedBy(ePlayer) + iChange); } int CvDiplomacyAI::GetNumCitiesEverLiberatedBy(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumCitiesEverLiberated[ePlayer]; } void CvDiplomacyAI::SetNumCitiesEverLiberatedBy(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumCitiesEverLiberated to a negative value"); ASSERT(NotMe(ePlayer), "Setting NumCitiesEverLiberated for self"); m_aiNumCitiesEverLiberated[ePlayer] = min(iValue, UCHAR_MAX); } void CvDiplomacyAI::ChangeNumCitiesEverLiberatedBy(PlayerTypes ePlayer, int iChange) { SetNumCitiesEverLiberatedBy(ePlayer, GetNumCitiesEverLiberatedBy(ePlayer) + iChange); } /// How many civilians has this player returned to us? int CvDiplomacyAI::GetNumCiviliansReturnedToMe(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumCiviliansReturnedToMe[ePlayer]; } void CvDiplomacyAI::SetNumCiviliansReturnedToMe(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumCiviliansReturnedToMe to a negative value"); ASSERT(NotMe(ePlayer), "Setting NumCiviliansReturnedToMe for self"); m_aiNumCiviliansReturnedToMe[ePlayer] = min(iValue, UCHAR_MAX); if (iValue > 0) { SetCiviliansReturnedToMeTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetCiviliansReturnedToMeTurn(ePlayer, -1); } } void CvDiplomacyAI::ChangeNumCiviliansReturnedToMe(PlayerTypes ePlayer, int iChange) { SetNumCiviliansReturnedToMe(ePlayer, GetNumCiviliansReturnedToMe(ePlayer) + iChange); if (iChange > 0) { if (!MOD_DIPLOAI_SHUT_UP && !MOD_DIPLOAI_SHUT_UP_COMPLIMENTS) { if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { const char* strText = GetDiploStringForMessage(DIPLO_MESSAGE_RETURNED_CIVILIAN); // TODO: what about GC.GetEngineUserInterface()->SetForceDiscussionModeQuitOnBack(true)? CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE, -1); } else { if (!GC.getGame().isNetworkMultiPlayer()) // KWG: Candidate for !GC.getGame().IsOption(GAMEOPTION_SIMULTANEOUS_TURNS) { if (GC.getGame().getActivePlayer() == ePlayer) { GC.GetEngineUserInterface()->SetForceDiscussionModeQuitOnBack(true); // Set force quit so that when discuss mode pops up the Back button won't go to leader root const char* strText = GetDiploStringForMessage(DIPLO_MESSAGE_RETURNED_CIVILIAN); gDLL->GameplayDiplomacyAILeaderMessage(GetID(), DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } } } } } /// How many times has this player shared intrigue with us? int CvDiplomacyAI::GetNumTimesIntrigueSharedBy(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumTimesIntrigueSharedBy[ePlayer]; } void CvDiplomacyAI::SetNumTimesIntrigueSharedBy(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumTimesIntrigueSharedBy to a negative value"); ASSERT(NotMe(ePlayer), "Setting NumTimesIntrigueSharedBy for self"); m_aiNumTimesIntrigueSharedBy[ePlayer] = min(iValue, UCHAR_MAX); if (iValue > 0) { SetIntrigueSharedTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetIntrigueSharedTurn(ePlayer, -1); } } void CvDiplomacyAI::ChangeNumTimesIntrigueSharedBy(PlayerTypes ePlayer, int iChange) { SetNumTimesIntrigueSharedBy(ePlayer, GetNumTimesIntrigueSharedBy(ePlayer) + iChange); } /// How many landmarks has this player built in my territory? int CvDiplomacyAI::GetNumLandmarksBuiltForMe(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumLandmarksBuiltForMe[ePlayer]; } void CvDiplomacyAI::SetNumLandmarksBuiltForMe(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumLandmarksBuiltForMe to a negative value"); ASSERT(NotMe(ePlayer), "Setting NumLandmarksBuiltForMe for self"); m_aiNumLandmarksBuiltForMe[ePlayer] = min(iValue, UCHAR_MAX); if (iValue > 0) { SetLandmarksBuiltForMeTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetLandmarksBuiltForMeTurn(ePlayer, -1); } } void CvDiplomacyAI::ChangeNumLandmarksBuiltForMe(PlayerTypes ePlayer, int iChange) { SetNumLandmarksBuiltForMe(ePlayer, GetNumLandmarksBuiltForMe(ePlayer) + iChange); } /// How many times has ePlayer been caught plotting against us? int CvDiplomacyAI::GetNumTimesTheyPlottedAgainstUs(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiTheyPlottedAgainstUs[ePlayer]; } void CvDiplomacyAI::SetNumTimesTheyPlottedAgainstUs(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting TheyPlottedAgainstUs to a negative value"); ASSERT(NotTeam(ePlayer), "Setting TheyPlottedAgainstUs for own team"); m_aiTheyPlottedAgainstUs[ePlayer] = min(iValue, UCHAR_MAX); if (iValue > 0) { SetPlottedAgainstUsTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetPlottedAgainstUsTurn(ePlayer, -1); } } void CvDiplomacyAI::ChangeNumTimesTheyPlottedAgainstUs(PlayerTypes ePlayer, int iChange) { SetNumTimesTheyPlottedAgainstUs(ePlayer, GetNumTimesTheyPlottedAgainstUs(ePlayer) + iChange); if (iChange > 0 && GetRecentAssistValue(ePlayer) < 0) { SetRecentAssistValue(ePlayer, 0); } } /// How many times has this player plundered one of our Trade Routes? int CvDiplomacyAI::GetNumTradeRoutesPlundered(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumTradeRoutesPlundered[ePlayer]; } void CvDiplomacyAI::SetNumTradeRoutesPlundered(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumTradeRoutesPlundered to a negative value"); ASSERT(NotTeam(ePlayer), "Setting NumTradeRoutesPlundered for own team"); m_aiNumTradeRoutesPlundered[ePlayer] = min(iValue, UCHAR_MAX); if (iValue > 0) { SetPlunderedTradeRouteTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetPlunderedTradeRouteTurn(ePlayer, -1); } } void CvDiplomacyAI::ChangeNumTradeRoutesPlundered(PlayerTypes ePlayer, int iChange) { SetNumTradeRoutesPlundered(ePlayer, GetNumTradeRoutesPlundered(ePlayer) + iChange); } /// How many World Wonders has ePlayer beaten us to? int CvDiplomacyAI::GetNumWondersBeatenTo(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumWondersBeatenTo[ePlayer]; } void CvDiplomacyAI::SetNumWondersBeatenTo(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumWondersBeatenTo to a negative value"); ASSERT(NotMe(ePlayer), "Setting NumWondersBeatenTo for self"); m_aiNumWondersBeatenTo[ePlayer] = min(iValue, UCHAR_MAX); if (iValue > 0) { SetBeatenToWonderTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetBeatenToWonderTurn(ePlayer, -1); } } void CvDiplomacyAI::ChangeNumWondersBeatenTo(PlayerTypes ePlayer, int iChange) { SetNumWondersBeatenTo(ePlayer, GetNumWondersBeatenTo(ePlayer) + iChange); } /// How many times has this player stolen our territory? int CvDiplomacyAI::GetNumTimesCultureBombed(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumTimesCultureBombed[ePlayer]; } void CvDiplomacyAI::SetNumTimesCultureBombed(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumTimesCultureBombed to a negative value"); ASSERT(NotMe(ePlayer), "Setting NumTimesCultureBombed for self"); m_aiNumTimesCultureBombed[ePlayer] = min(iValue, UCHAR_MAX); } void CvDiplomacyAI::ChangeNumTimesCultureBombed(PlayerTypes ePlayer, int iChange) { if (iChange > 0 && !GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { // No new penalties for stealing territory while at war (but existing peacetime penalties persist until peace is signed). if (IsAtWar(ePlayer)) return; } SetNumTimesCultureBombed(ePlayer, GetNumTimesCultureBombed(ePlayer) + iChange); if (iChange > 0 && !GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { // Time to reevaluate this thief! DoReevaluatePlayer(ePlayer, true); } } /// How many times has ePlayer lowered our influence with a Minor Civ? int CvDiplomacyAI::GetNumTimesTheyLoweredOurInfluence(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiTheyLoweredOurInfluence[ePlayer]; } void CvDiplomacyAI::SetNumTimesTheyLoweredOurInfluence(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting TheyLoweredOurInfluence to a negative value"); ASSERT(NotMe(ePlayer), "Setting TheyLoweredOurInfluence for self"); m_aiTheyLoweredOurInfluence[ePlayer] = min(iValue, UCHAR_MAX); if (iValue > 0) { SetLoweredOurInfluenceTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetLoweredOurInfluenceTurn(ePlayer, -1); } } void CvDiplomacyAI::ChangeNumTimesTheyLoweredOurInfluence(PlayerTypes ePlayer, int iChange) { SetNumTimesTheyLoweredOurInfluence(ePlayer, GetNumTimesTheyLoweredOurInfluence(ePlayer) + iChange); } /// How many protected Minors have we seen this Player bully? int CvDiplomacyAI::GetOtherPlayerNumProtectedMinorsBullied(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumProtectedMinorsBullied[ePlayer]; } void CvDiplomacyAI::SetOtherPlayerNumProtectedMinorsBullied(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumProtectedMinorsBullied to a negative value"); ASSERT(NotTeam(ePlayer), "Setting NumProtectedMinorsBullied for own team"); m_aiNumProtectedMinorsBullied[ePlayer] = min(iValue, UCHAR_MAX); } void CvDiplomacyAI::ChangeOtherPlayerNumProtectedMinorsBullied(PlayerTypes ePlayer, int iChange) { SetOtherPlayerNumProtectedMinorsBullied(ePlayer, GetOtherPlayerNumProtectedMinorsBullied(ePlayer) + iChange); } /// How many protected Minors have we seen this player attack? int CvDiplomacyAI::GetOtherPlayerNumProtectedMinorsAttacked(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumProtectedMinorsAttacked[ePlayer]; } void CvDiplomacyAI::SetOtherPlayerNumProtectedMinorsAttacked(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumProtectedMinorsAttacked to a negative value"); ASSERT(NotTeam(ePlayer), "Setting NumProtectedMinorsAttacked for own team"); m_aiNumProtectedMinorsAttacked[ePlayer] = min(iValue, UCHAR_MAX); } void CvDiplomacyAI::ChangeOtherPlayerNumProtectedMinorsAttacked(PlayerTypes ePlayer, int iChange) { SetOtherPlayerNumProtectedMinorsAttacked(ePlayer, GetOtherPlayerNumProtectedMinorsAttacked(ePlayer) + iChange); } /// How many protected Minors have we seen this Player attack? int CvDiplomacyAI::GetOtherPlayerNumProtectedMinorsKilled(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumProtectedMinorsKilled[ePlayer]; } void CvDiplomacyAI::SetOtherPlayerNumProtectedMinorsKilled(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumProtectedMinorsKilled to a negative value"); ASSERT(NotTeam(ePlayer), "Setting NumProtectedMinorsKilled for own team"); m_aiNumProtectedMinorsKilled[ePlayer] = min(iValue, UCHAR_MAX); } void CvDiplomacyAI::ChangeOtherPlayerNumProtectedMinorsKilled(PlayerTypes ePlayer, int iChange) { SetOtherPlayerNumProtectedMinorsKilled(ePlayer, GetOtherPlayerNumProtectedMinorsKilled(ePlayer) + iChange); } /// How many times has this player converted the religion of our cities? (if we care) int CvDiplomacyAI::GetNegativeReligiousConversionPoints(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNegativeReligiousConversionPoints[ePlayer]; } void CvDiplomacyAI::SetNegativeReligiousConversionPoints(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NegativeReligiousConversionPoints to a negative value"); ASSERT(NotTeam(ePlayer), "Setting NegativeReligiousConversionPoints for own team"); m_aiNegativeReligiousConversionPoints[ePlayer] = min(iValue, USHRT_MAX); if (iValue > 0) { SetReligiousConversionTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetReligiousConversionTurn(ePlayer, -1); } } void CvDiplomacyAI::ChangeNegativeReligiousConversionPoints(PlayerTypes ePlayer, int iChange) { SetNegativeReligiousConversionPoints(ePlayer, GetNegativeReligiousConversionPoints(ePlayer) + iChange); if (iChange > 0) { SetEverConvertedCity(ePlayer, true); // Reset the ability to ask for a promise GET_PLAYER(ePlayer).GetDiplomacyAI()->SetPlayerAskedNotToConvert(GetID(), false); // You broke the promise you made! if (MadeNoConvertPromise(ePlayer)) { SetNoConvertPromiseState(ePlayer, PROMISE_STATE_BROKEN); } if (!GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { // Time to reevaluate this thief! DoReevaluatePlayer(ePlayer); } } } /// How many times has this player robbed us? int CvDiplomacyAI::GetNumTimesRobbedBy(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumTimesRobbedBy[ePlayer]; } void CvDiplomacyAI::SetNumTimesRobbedBy(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumTimesRobbedBy to a negative value"); ASSERT(NotTeam(ePlayer), "Setting NumTimesRobbedBy for own team"); m_aiNumTimesRobbedBy[ePlayer] = min(iValue, UCHAR_MAX); if (iValue > 0) { SetRobbedTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetRobbedTurn(ePlayer, -1); } } void CvDiplomacyAI::ChangeNumTimesRobbedBy(PlayerTypes ePlayer, int iChange) { SetNumTimesRobbedBy(ePlayer, GetNumTimesRobbedBy(ePlayer) + iChange); if (iChange > 0 && !GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { // Time to reevaluate this thief! DoReevaluatePlayer(ePlayer); } } /// How many times has ePlayer lowered our Minor Civ influence in a coup? int CvDiplomacyAI::GetNumTimesPerformedCoupAgainstUs(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiPerformedCoupAgainstUs[ePlayer]; } void CvDiplomacyAI::SetNumTimesPerformedCoupAgainstUs(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting PerformedCoupAgainstUs to a negative value"); ASSERT(NotMe(ePlayer), "Setting PerformedCoupAgainstUs for self"); m_aiPerformedCoupAgainstUs[ePlayer] = min(iValue, UCHAR_MAX); if (iValue > 0) { SetPerformedCoupTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetPerformedCoupTurn(ePlayer, -1); } } void CvDiplomacyAI::ChangeNumTimesPerformedCoupAgainstUs(PlayerTypes ePlayer, int iChange) { SetNumTimesPerformedCoupAgainstUs(ePlayer, GetNumTimesPerformedCoupAgainstUs(ePlayer) + iChange); if (iChange > 0 && !GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { // Time to reevaluate this thief! DoReevaluatePlayer(ePlayer); } } /// How much support or opposition did we have for their most recent World Congress proposal? int CvDiplomacyAI::GetLikedTheirProposalValue(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiLikedTheirProposalValue[ePlayer]; } void CvDiplomacyAI::SetLikedTheirProposalValue(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); m_aiLikedTheirProposalValue[ePlayer] = range(iValue, CHAR_MIN, CHAR_MAX); } /// How much support or opposition did they give to our most recent World Congress proposal? int CvDiplomacyAI::GetSupportedOurProposalValue(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiSupportedOurProposalValue[ePlayer]; } void CvDiplomacyAI::SetSupportedOurProposalValue(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= -100 && iValue <= 100, "Setting SupportedOurProposalValue to an invalid value"); int iSetValue = iValue; int iCurrentValue = GetSupportedOurProposalValue(ePlayer); // The easy cases if (iCurrentValue == 0 || IsTeammate(ePlayer)) { m_aiSupportedOurProposalValue[ePlayer] = iSetValue; } // Remember previous support or opposition! This will get a little complicated, but it makes the AI smarter. else if (iSetValue != 0) { int iTurn = 0; int iDuration = 0; // Previously opposed us if (iCurrentValue > 0) { iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(OPINION_WEIGHT_THEY_FOILED_OUR_PROPOSAL_NUM_TURNS), GetWorkAgainstWillingness()); iTurn = IsFoiledOurProposalAndThenSupportedUs(ePlayer) ? GetTheySupportedOurProposalTurn(ePlayer) : GetTheyFoiledOurProposalTurn(ePlayer); if (iTurn < 0) return; } // Previously supported us else { iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_PROPOSAL_NUM_TURNS), GetWorkWithWillingness()); iTurn = IsSupportedOurProposalAndThenFoiledUs(ePlayer) ? GetTheyFoiledOurProposalTurn(ePlayer) : GetTheySupportedOurProposalTurn(ePlayer); if (iTurn < 0) return; } // Reduction to duration? Do not apply the forgiveness mod if the new value is positive! if (iCurrentValue > 0 && IsFoiledOurProposalAndThenSupportedUs(ePlayer) && iSetValue < 0) { int iDurationMod = (GetForgiveness() - 5) * 2; iDurationMod *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDurationMod /= 100; if (iDurationMod > 0) { iDuration -= iDurationMod; } } // Likewise, do not apply the neediness mod if the new value is negative! else if (iCurrentValue < 0 && IsSupportedOurProposalAndThenFoiledUs(ePlayer) && iSetValue > 0) { int iDurationMod = (GetNeediness() - 5) * 2; iDurationMod *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDurationMod /= 100; if (iDurationMod > 0) { iDuration -= iDurationMod; } } int iTurnsPassed = (GC.getGame().getGameTurn()+1) - iTurn; int iDurationPercent = (iTurnsPassed * 100) / max(iDuration,1); int iPercentageLeft = 100 - iDurationPercent; // Calculate the opinion modifier! int iMinOpinionWeight = iCurrentValue > 0 ? max(0, /*10*/ GD_INT_GET(OPINION_WEIGHT_THEY_FOILED_OUR_PROPOSAL)) : min(0, /*-10*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_PROPOSAL)); int iMaxOpinionWeight = iCurrentValue > 0 ? max(0, /*60*/ GD_INT_GET(OPINION_WEIGHT_THEY_FOILED_OUR_PROPOSAL_MAX)) : min(0, /*-60*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_PROPOSAL_MAX)); int iDifference = iMaxOpinionWeight - iMinOpinionWeight; int iAwardPercentage = iCurrentValue > 0 ? min(100, (iCurrentValue * max(1, /*2*/ GD_INT_GET(OPINION_WEIGHT_PER_VOTE_PERCENT)))) : min(100, (-iCurrentValue * max(1, /*2*/ GD_INT_GET(OPINION_WEIGHT_PER_VOTE_PERCENT)))); int iOpinionWeight = iMinOpinionWeight; iOpinionWeight += (iDifference * iAwardPercentage) / 100; // Now we scale the value by how much of the modifier timer has passed iOpinionWeight *= iPercentageLeft; iOpinionWeight /= 100; // Now reverse the calculation to find the corresponding current value % int iNewCurrentValue = 0; iOpinionWeight -= iMinOpinionWeight; // Did this bring us down to zero? if (iOpinionWeight <= 0 && iCurrentValue > 0) { iNewCurrentValue = 0; } else if (iOpinionWeight >= 0 && iCurrentValue < 0) { iNewCurrentValue = 0; } else { int iNewAwardPercentage = (iOpinionWeight * 100) / max(1, iDifference); iNewCurrentValue = iNewAwardPercentage / max(1, /*2*/ GD_INT_GET(OPINION_WEIGHT_PER_VOTE_PERCENT)); } // Then add the scaled current value to the new value to get the sum iSetValue += iNewCurrentValue; m_aiSupportedOurProposalValue[ePlayer] = range(iSetValue, -100, 100); } } /// Helper functions so the AI can remember recent World Congress diplomacy bool CvDiplomacyAI::IsSupportedOurProposalAndThenFoiledUs(PlayerTypes ePlayer) const { return GetTheyFoiledOurProposalTurn(ePlayer) > -1 && GetSupportedOurProposalValue(ePlayer) < 0; } bool CvDiplomacyAI::IsFoiledOurProposalAndThenSupportedUs(PlayerTypes ePlayer) const { return GetTheySupportedOurProposalTurn(ePlayer) > -1 && GetSupportedOurProposalValue(ePlayer) > 0; } /// How much do we like or dislike their voting history on proposals in the World Congress? int CvDiplomacyAI::GetVotingHistoryScore(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiVotingHistoryScore[ePlayer]; } void CvDiplomacyAI::SetVotingHistoryScore(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); int iMaxValue = range(/*2400*/ GD_INT_GET(VOTING_HISTORY_SCORE_MAX), 0, SHRT_MAX); int iMinValue = range(/*-2400*/ -GD_INT_GET(VOTING_HISTORY_SCORE_MAX), SHRT_MIN, 0); m_aiVotingHistoryScore[ePlayer] = range(iValue, iMinValue, iMaxValue); } void CvDiplomacyAI::ChangeVotingHistoryScore(PlayerTypes ePlayer, int iChange) { SetVotingHistoryScore(ePlayer, GetVotingHistoryScore(ePlayer) + iChange); } /// How much support did they give to us when we succeeded in becoming World Congress host? int CvDiplomacyAI::GetSupportedOurHostingValue(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiSupportedOurHostingValue[ePlayer]; } void CvDiplomacyAI::SetSupportedOurHostingValue(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0 && iValue <= 100, "Setting SupportedOurHostingValue to an invalid value"); m_aiSupportedOurHostingValue[ePlayer] = iValue; } /// How many negative points does this player have for digging up our artifacts? int CvDiplomacyAI::GetNegativeArchaeologyPoints(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNegativeArchaeologyPoints[ePlayer]; } void CvDiplomacyAI::SetNegativeArchaeologyPoints(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NegativeArchaeologyPoints to a negative value"); ASSERT(NotTeam(ePlayer), "Setting NegativeArchaeologyPoints for own team"); m_aiNegativeArchaeologyPoints[ePlayer] = min(iValue, UCHAR_MAX); if (iValue > 0) { SetStoleArtifactTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetStoleArtifactTurn(ePlayer, -1); } } void CvDiplomacyAI::ChangeNegativeArchaeologyPoints(PlayerTypes ePlayer, int iChange) { SetNegativeArchaeologyPoints(ePlayer, GetNegativeArchaeologyPoints(ePlayer) + iChange); if (iChange > 0) { ChangeNumArtifactsEverDugUp(ePlayer, 1); // Reset the ability to ask for a promise GET_PLAYER(ePlayer).GetDiplomacyAI()->SetPlayerAskedNotToDig(GetID(), false); // You broke the promise you made! if (MadeNoDiggingPromise(ePlayer)) { SetNoDiggingPromiseState(ePlayer, PROMISE_STATE_BROKEN); } if (!GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { // Time to reevaluate this thief! DoReevaluatePlayer(ePlayer); } } } /// How many times has this player dug up our artifacts? int CvDiplomacyAI::GetNumArtifactsEverDugUp(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiArtifactsEverDugUp[ePlayer]; } void CvDiplomacyAI::SetNumArtifactsEverDugUp(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting ArtifactsEverDugUp to a negative value"); ASSERT(NotMe(ePlayer), "Setting ArtifactsEverDugUp for self"); m_aiArtifactsEverDugUp[ePlayer] = min(iValue, UCHAR_MAX); if (MOD_ENABLE_ACHIEVEMENTS && !GC.getGame().isGameMultiPlayer() && GET_PLAYER(ePlayer).isHuman(ISHUMAN_ACHIEVEMENTS) && ePlayer == GC.getGame().getActivePlayer()) { if (iValue >= 5) gDLL->UnlockAchievement(ACHIEVEMENT_XP2_34); } } void CvDiplomacyAI::ChangeNumArtifactsEverDugUp(PlayerTypes ePlayer, int iChange) { SetNumArtifactsEverDugUp(ePlayer, GetNumArtifactsEverDugUp(ePlayer) + iChange); } /// How many times has this player nuked us? int CvDiplomacyAI::GetNumTimesNuked(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiNumTimesNuked[ePlayer]; } void CvDiplomacyAI::SetNumTimesNuked(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting NumTimesNuked to a negative value"); ASSERT(NotMe(ePlayer), "Setting NumTimesNuked for self"); m_aiNumTimesNuked[ePlayer] = min(iValue, UCHAR_MAX); } void CvDiplomacyAI::ChangeNumTimesNuked(PlayerTypes ePlayer, int iChange) { SetNumTimesNuked(ePlayer, GetNumTimesNuked(ePlayer) + iChange); // and do diplo... if (iChange > 0) { SetBackstabbedBy(ePlayer, true, false); int iWarmongerValueTimes100 = CvDiplomacyAIHelpers::GetWarmongerTriggerPenalty(ePlayer, GetTeam(), GetID(), WARMONGER_NUKED_PLAYER) * 100; ChangeOtherPlayerWarmongerAmountTimes100(ePlayer, iWarmongerValueTimes100); DoUpdateWarmongerThreats(true); // Global warmongering penalty for this! for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (eLoopPlayer == GetID() || GET_PLAYER(eLoopPlayer).getTeam() == GET_PLAYER(ePlayer).getTeam()) continue; if (!IsHasMet(eLoopPlayer) || !GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasMet(eLoopPlayer)) continue; iWarmongerValueTimes100 = CvDiplomacyAIHelpers::GetWarmongerTriggerPenalty(ePlayer, GetTeam(), eLoopPlayer, WARMONGER_NUKED_PLAYER) * 100; GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeOtherPlayerWarmongerAmountTimes100(ePlayer, iWarmongerValueTimes100); GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->DoUpdateWarmongerThreats(true); } } } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Turn Counters // ------------------------------------ /// Did ePlayer bring us back to life? bool CvDiplomacyAI::WasResurrectedBy(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiResurrectedOnTurn[ePlayer] != -1; } void CvDiplomacyAI::SetResurrectedBy(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotMe(ePlayer), "Setting ResurrectedOnTurn for self"); if (bValue) { m_aiResurrectedOnTurn[ePlayer] = GC.getGame().getGameTurn(); } else { m_aiResurrectedOnTurn[ePlayer] = -1; } } /// Did ePlayer bring us back to life on this turn? bool CvDiplomacyAI::WasResurrectedThisTurnBy(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiResurrectedOnTurn[ePlayer] == GC.getGame().getGameTurn(); } /// Were we resurrected by anyone? bool CvDiplomacyAI::WasResurrectedByAnyone() const { return GET_TEAM(GetTeam()).GetLiberatedByTeam() != NO_TEAM; } /// On what turn did this player most recently liberate one of our cities? int CvDiplomacyAI::GetLiberatedCitiesTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS); return m_aiLiberatedCitiesTurn[ePlayer]; } void CvDiplomacyAI::SetLiberatedCitiesTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting LiberatedCitiesTurn to an invalid value"); ASSERT(NotMe(ePlayer), "Setting LiberatedCitiesTurn for self"); m_aiLiberatedCitiesTurn[ePlayer] = iTurn; } /// Did this player recently liberate one of our cities? bool CvDiplomacyAI::IsCityRecentlyLiberatedBy(PlayerTypes ePlayer) const { int iTurn = GetLiberatedCitiesTurn(ePlayer); if (iTurn <= -1) return false; // Anti-exploit - no bonuses under certain conditions! if (!IsLiberator(ePlayer, false, false)) return false; int iTurnDifference = GC.getGame().getGameTurn() - iTurn; return iTurnDifference < 20; } /// On what turn did this player most recently return a civilian to us? int CvDiplomacyAI::GetCiviliansReturnedToMeTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiCiviliansReturnedToMeTurn[ePlayer]; } void CvDiplomacyAI::SetCiviliansReturnedToMeTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting CiviliansReturnedToMeTurn to an invalid value"); ASSERT(NotMe(ePlayer), "Setting CiviliansReturnedToMeTurn for self"); m_aiCiviliansReturnedToMeTurn[ePlayer] = iTurn; } /// On what turn did this player most recently share intrigue with us? int CvDiplomacyAI::GetIntrigueSharedTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiIntrigueSharedTurn[ePlayer]; } void CvDiplomacyAI::SetIntrigueSharedTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting IntrigueSharedTurn to an invalid value"); ASSERT(NotMe(ePlayer), "Setting IntrigueSharedTurn for self"); m_aiIntrigueSharedTurn[ePlayer] = iTurn; } /// Did this player forgive us for spying on them? bool CvDiplomacyAI::IsPlayerForgaveForSpying(PlayerTypes ePlayer) const { return GetForgaveForSpyingTurn(ePlayer) != -1; } void CvDiplomacyAI::SetPlayerForgaveForSpying(PlayerTypes ePlayer, bool bValue) { if (bValue) { SetForgaveForSpyingTurn(ePlayer, GC.getGame().getGameTurn()); } else { SetForgaveForSpyingTurn(ePlayer, -1); } } /// On what turn did this player most recently forgive us for spying? int CvDiplomacyAI::GetForgaveForSpyingTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiPlayerForgaveForSpyingTurn[ePlayer]; } void CvDiplomacyAI::SetForgaveForSpyingTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting PlayerForgaveForSpyingTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting PlayerForgaveForSpyingTurn for own team"); m_aiPlayerForgaveForSpyingTurn[ePlayer] = iTurn; } /// On what turn did this player most recently build a landmark for me? int CvDiplomacyAI::GetLandmarksBuiltForMeTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiLandmarksBuiltForMeTurn[ePlayer]; } void CvDiplomacyAI::SetLandmarksBuiltForMeTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting LandmarksBuiltForMeTurn to an invalid value"); ASSERT(NotMe(ePlayer), "Setting LandmarksBuiltForMeTurn for self"); m_aiLandmarksBuiltForMeTurn[ePlayer] = iTurn; } /// On what turn did we most recently catch this player plotting against us? int CvDiplomacyAI::GetPlottedAgainstUsTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiPlottedAgainstUsTurn[ePlayer]; } void CvDiplomacyAI::SetPlottedAgainstUsTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting PlottedAgainstUsTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting PlottedAgainstUsTurn for own team"); m_aiPlottedAgainstUsTurn[ePlayer] = iTurn; } /// On what turn did this player most recently plunder one of our Trade Routes? int CvDiplomacyAI::GetPlunderedTradeRouteTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiPlunderedTradeRouteTurn[ePlayer]; } void CvDiplomacyAI::SetPlunderedTradeRouteTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting PlunderedTradeRouteTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting PlunderedTradeRouteTurn for own team"); m_aiPlunderedTradeRouteTurn[ePlayer] = iTurn; } /// On what turn did this player most recently beat us to a World Wonder? int CvDiplomacyAI::GetBeatenToWonderTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiBeatenToWonderTurn[ePlayer]; } void CvDiplomacyAI::SetBeatenToWonderTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting BeatenToWonderTurn to an invalid value"); ASSERT(NotMe(ePlayer), "Setting BeatenToWonderTurn for self"); m_aiBeatenToWonderTurn[ePlayer] = iTurn; } /// On what turn did this player most recently lower our Influence with a City-State? int CvDiplomacyAI::GetLoweredOurInfluenceTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiLoweredOurInfluenceTurn[ePlayer]; } void CvDiplomacyAI::SetLoweredOurInfluenceTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting SidedWithProtectedMinorTurn to an invalid value"); ASSERT(NotMe(ePlayer), "Setting SidedWithProtectedMinorTurn for self"); m_aiLoweredOurInfluenceTurn[ePlayer] = iTurn; } /// On what turn did this player most recently side with one of their protected minors that we attacked/bullied? int CvDiplomacyAI::GetOtherPlayerSidedWithProtectedMinorTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiSidedWithProtectedMinorTurn[ePlayer]; } void CvDiplomacyAI::SetOtherPlayerSidedWithProtectedMinorTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting SidedWithProtectedMinorTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting SidedWithProtectedMinorTurn for own team"); m_aiSidedWithProtectedMinorTurn[ePlayer] = iTurn; } /// Are we angry about ePlayer choosing to side with one of their protected minors that we attacked/bullied? bool CvDiplomacyAI::IsAngryAboutSidedWithProtectedMinor(PlayerTypes ePlayer) const { return GetOtherPlayerSidedWithProtectedMinorTurn(ePlayer) != -1; } int CvDiplomacyAI::GetOtherPlayerBulliedProtectedMinorTurn(PlayerTypes ePlayer) const { if (GetOtherPlayerProtectedMinorBullied(ePlayer) == NO_PLAYER) return -1; return m_aiBulliedProtectedMinorTurn[ePlayer]; } void CvDiplomacyAI::SetOtherPlayerBulliedProtectedMinorTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting BulliedProtectedMinorTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting BulliedProtectedMinorTurn for own team"); m_aiBulliedProtectedMinorTurn[ePlayer] = iTurn; } /// Are we angry about ePlayer bullying one of our protected Minors? bool CvDiplomacyAI::IsAngryAboutProtectedMinorBullied(PlayerTypes ePlayer) const { // Anger over attacked/killed city-states trumps this, don't stack if (IsAngryAboutProtectedMinorKilled(ePlayer) || IsAngryAboutProtectedMinorAttacked(ePlayer)) return false; int iMostRecentBullyTurn = GetOtherPlayerBulliedProtectedMinorTurn(ePlayer); if (iMostRecentBullyTurn == -1) return false; int iTurnDifference = GC.getGame().getGameTurn() - iMostRecentBullyTurn; if (iTurnDifference < AdjustModifierDuration(/*30*/ GD_INT_GET(OPINION_WEIGHT_BULLIED_PROTECTED_MINOR_NUM_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true)) { return true; } return false; } int CvDiplomacyAI::GetOtherPlayerAttackedProtectedMinorTurn(PlayerTypes ePlayer) const { if (GetOtherPlayerProtectedMinorAttacked(ePlayer) == NO_PLAYER) return -1; return m_aiAttackedProtectedMinorTurn[ePlayer]; } void CvDiplomacyAI::SetOtherPlayerAttackedProtectedMinorTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting AttackedProtectedMinorTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting AttackedProtectedMinorTurn for own team"); m_aiAttackedProtectedMinorTurn[ePlayer] = iTurn; if (iTurn == -1) SetOtherPlayerProtectedMinorAttacked(ePlayer, NO_PLAYER); } /// Are we angry about ePlayer attacking one of our protected Minors? bool CvDiplomacyAI::IsAngryAboutProtectedMinorAttacked(PlayerTypes ePlayer) const { // Anger over killed city-states trumps this, don't stack if (IsAngryAboutProtectedMinorKilled(ePlayer)) return false; return GetOtherPlayerAttackedProtectedMinorTurn(ePlayer) != -1; } int CvDiplomacyAI::GetOtherPlayerKilledProtectedMinorTurn(PlayerTypes ePlayer) const { if (GetOtherPlayerProtectedMinorKilled(ePlayer) == NO_PLAYER) return -1; return m_aiKilledProtectedMinorTurn[ePlayer]; } void CvDiplomacyAI::SetOtherPlayerKilledProtectedMinorTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting KilledProtectedMinorTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting KilledProtectedMinorTurn for own team"); m_aiKilledProtectedMinorTurn[ePlayer] = iTurn; if (iTurn == -1) SetOtherPlayerProtectedMinorKilled(ePlayer, NO_PLAYER); } /// Are we angry about ePlayer killing one of our protected Minors? bool CvDiplomacyAI::IsAngryAboutProtectedMinorKilled(PlayerTypes ePlayer) const { int iTurn = GetOtherPlayerKilledProtectedMinorTurn(ePlayer); if (iTurn <= -1) return false; int iTurnDifference = GC.getGame().getGameTurn() - iTurn; if (iTurnDifference < /*50*/ GD_INT_GET(OPINION_WEIGHT_KILLED_PROTECTED_MINOR_NUM_TURNS_UNTIL_FORGIVEN)) return true; return false; } /// On what turn did this player most recently convert one of our cities? int CvDiplomacyAI::GetReligiousConversionTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiReligiousConversionTurn[ePlayer]; } void CvDiplomacyAI::SetReligiousConversionTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting ReligiousConversionTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting ReligiousConversionTurn for own team"); m_aiReligiousConversionTurn[ePlayer] = iTurn; } /// On what turn did this player most recently steal from us? int CvDiplomacyAI::GetRobbedTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiTimesRobbedTurn[ePlayer]; } void CvDiplomacyAI::SetRobbedTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting TimesRobbedTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting TimesRobbedTurn for own team"); m_aiTimesRobbedTurn[ePlayer] = iTurn; } /// On what turn did this player most recently lower our Minor Civ influence in a coup? int CvDiplomacyAI::GetPerformedCoupTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiPerformedCoupTurn[ePlayer]; } void CvDiplomacyAI::SetPerformedCoupTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting PerformedCoupTurn to an invalid value"); ASSERT(NotMe(ePlayer), "Setting PerformedCoupTurn for self"); m_aiPerformedCoupTurn[ePlayer] = iTurn; } /// On what turn did this player most recently steal one of our cultural artifacts? int CvDiplomacyAI::GetStoleArtifactTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiStoleArtifactTurn[ePlayer]; } void CvDiplomacyAI::SetStoleArtifactTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting StoleArtifactTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting StoleArtifactTurn for own team"); m_aiStoleArtifactTurn[ePlayer] = iTurn; } /// How many turns has it been since we really liked this player's proposal to the World Congress? int CvDiplomacyAI::GetWeLikedTheirProposalTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiWeLikedTheirProposalTurn[ePlayer]; } void CvDiplomacyAI::SetWeLikedTheirProposalTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting WeLikedTheirProposalTurn to an invalid value"); ASSERT(NotMe(ePlayer), "Setting WeLikedTheirProposalTurn for self"); m_aiWeLikedTheirProposalTurn[ePlayer] = iTurn; if (iTurn == -1) SetLikedTheirProposalValue(ePlayer, 0); } bool CvDiplomacyAI::WeLikedTheirProposal(PlayerTypes ePlayer) const { return GetWeLikedTheirProposalTurn(ePlayer) > -1; } /// How many turns has it been since we really disliked this player's proposal to the World Congress? int CvDiplomacyAI::GetWeDislikedTheirProposalTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiWeDislikedTheirProposalTurn[ePlayer]; } void CvDiplomacyAI::SetWeDislikedTheirProposalTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting WeDislikedTheirProposalTurn to an invalid value"); ASSERT(NotMe(ePlayer), "Setting WeDislikedTheirProposalTurn for self"); m_aiWeDislikedTheirProposalTurn[ePlayer] = iTurn; if (iTurn == -1) SetLikedTheirProposalValue(ePlayer, 0); } bool CvDiplomacyAI::WeDislikedTheirProposal(PlayerTypes ePlayer) const { return GetWeDislikedTheirProposalTurn(ePlayer) > -1; } /// How many turns has it been since they supported our proposal to the World Congress? int CvDiplomacyAI::GetTheySupportedOurProposalTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiTheySupportedOurProposalTurn[ePlayer]; } void CvDiplomacyAI::SetTheySupportedOurProposalTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting TheySupportedOurProposalTurn to an invalid value"); ASSERT(NotMe(ePlayer), "Setting TheySupportedOurProposalTurn for self"); m_aiTheySupportedOurProposalTurn[ePlayer] = iTurn; } /// How many turns has it been since they foiled our proposal to the World Congress? int CvDiplomacyAI::GetTheyFoiledOurProposalTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiTheyFoiledOurProposalTurn[ePlayer]; } void CvDiplomacyAI::SetTheyFoiledOurProposalTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting TheyFoiledOurProposalTurn to an invalid value"); ASSERT(NotMe(ePlayer), "Setting TheyFoiledOurProposalTurn for self"); m_aiTheyFoiledOurProposalTurn[ePlayer] = iTurn; } /// How many turns has it been since they proposed or voted to sanction us? int CvDiplomacyAI::GetTheySanctionedUsTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiTheySanctionedUsTurn[ePlayer]; } void CvDiplomacyAI::SetTheySanctionedUsTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting TheySanctionedUsTurn to an invalid value"); ASSERT(NotMe(ePlayer), "Setting TheySanctionedUsTurn for self"); m_aiTheySanctionedUsTurn[ePlayer] = iTurn; } bool CvDiplomacyAI::HasTriedToSanctionUs(PlayerTypes ePlayer) const { return GetTheySanctionedUsTurn(ePlayer) != -1; } /// How many turns has it been since they proposed or voted to remove a sanction against us? int CvDiplomacyAI::GetTheyUnsanctionedUsTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiTheyUnsanctionedUsTurn[ePlayer]; } void CvDiplomacyAI::SetTheyUnsanctionedUsTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting TheyUnsanctionedUsTurn to an invalid value"); ASSERT(NotMe(ePlayer), "Setting TheyUnsanctionedUsTurn for self"); m_aiTheyUnsanctionedUsTurn[ePlayer] = iTurn; } bool CvDiplomacyAI::HasTriedToUnsanctionUs(PlayerTypes ePlayer) const { return GetTheyUnsanctionedUsTurn(ePlayer) != -1; } /// How many turns has it been since they supported relocating the World Congress to our lands? int CvDiplomacyAI::GetTheySupportedOurHostingTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiTheySupportedOurHostingTurn[ePlayer]; } void CvDiplomacyAI::SetTheySupportedOurHostingTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting TheySupportedOurHostingTurn to an invalid value"); ASSERT(NotMe(ePlayer), "Setting TheySupportedOurHostingTurn for self"); m_aiTheySupportedOurHostingTurn[ePlayer] = iTurn; if (iTurn == -1) SetSupportedOurHostingValue(ePlayer, 0); } bool CvDiplomacyAI::TheySupportedOurHosting(PlayerTypes ePlayer) const { return GetTheySupportedOurHostingTurn(ePlayer) > -1; } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Player-Specific Memory Values // ------------------------------------ /// Were we a human player last time we called SlotStateChange()? bool CvDiplomacyAI::WasHumanLastUpdate() const { return m_bWasHumanLastUpdate; } /// Did we end a Declaration of Friendship with someone early this turn? bool CvDiplomacyAI::HasEndedFriendshipThisTurn() const { return m_bEndedFriendshipThisTurn; } void CvDiplomacyAI::SetEndedFriendshipThisTurn(bool bValue) { m_bEndedFriendshipThisTurn = bValue; } bool CvDiplomacyAI::UpdatedWarProgressThisTurn() const { return m_bUpdatedWarProgressThisTurn; } void CvDiplomacyAI::SetUpdatedWarProgressThisTurn(bool bValue) { m_bUpdatedWarProgressThisTurn = bValue; } /// How many times have we reevaluated our approach towards any player? Only used for RNG. int CvDiplomacyAI::GetNumReevaluations() const { return m_iNumReevaluations; } void CvDiplomacyAI::SetNumReevaluations(int iValue) { ASSERT(iValue >= 0, "Setting NumReevaluations to a negative value"); m_iNumReevaluations = iValue; } void CvDiplomacyAI::ChangeNumReevaluations(int iChange) { SetNumReevaluations(m_iNumReevaluations + iChange); } /// Are we avoiding deals? Temporary non-serialized value, used to avoid constant iterating over players... bool CvDiplomacyAI::IsAvoidDeals() const { return m_bAvoidDeals; } void CvDiplomacyAI::SetAvoidDeals(bool bValue) { m_bAvoidDeals = bValue; } /// Are we ignoring warmongering? Temporary non-serialized value. bool CvDiplomacyAI::IsIgnoreWarmonger() const { return m_bIgnoreWarmonger; } void CvDiplomacyAI::SetIgnoreWarmonger(bool bValue) { m_bIgnoreWarmonger = bValue; } // Do we want to liberate this player on this turn? Temporary non-serialized value. PlayerTypes CvDiplomacyAI::GetVassalPlayerToLiberate() const { return m_eVassalPlayerToLiberate; } void CvDiplomacyAI::SetVassalPlayerToLiberate(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= NO_PLAYER && ePlayer < MAX_MAJOR_CIVS && NotTeam(ePlayer), "Invalid VassalPlayerToLiberate"); m_eVassalPlayerToLiberate = ePlayer; } /// Who was the last Minor ePlayer bullied that we were protecting? PlayerTypes CvDiplomacyAI::GetOtherPlayerProtectedMinorBullied(PlayerTypes ePlayer) const { return (PlayerTypes) m_aeProtectedMinorBullied[ePlayer]; } void CvDiplomacyAI::SetOtherPlayerProtectedMinorBullied(PlayerTypes ePlayer, PlayerTypes eBulliedPlayer) { ASSERT(NotTeam(ePlayer), "Setting ProtectedMinorBullied for own team"); PRECONDITION((eBulliedPlayer == NO_PLAYER || eBulliedPlayer >= MAX_MAJOR_CIVS) && eBulliedPlayer < MAX_CIV_PLAYERS, "Invalid ProtectedMinorBullied"); m_aeProtectedMinorBullied[ePlayer] = eBulliedPlayer; } /// Who was the last Minor ePlayer attacked that we were protecting? PlayerTypes CvDiplomacyAI::GetOtherPlayerProtectedMinorAttacked(PlayerTypes ePlayer) const { return (PlayerTypes) m_aeProtectedMinorAttacked[ePlayer]; } void CvDiplomacyAI::SetOtherPlayerProtectedMinorAttacked(PlayerTypes ePlayer, PlayerTypes eAttackedPlayer) { ASSERT(NotTeam(ePlayer), "Setting ProtectedMinorAttacked for own team"); PRECONDITION((eAttackedPlayer == NO_PLAYER || eAttackedPlayer >= MAX_MAJOR_CIVS) && eAttackedPlayer < MAX_CIV_PLAYERS, "Invalid ProtectedMinorAttacked"); m_aeProtectedMinorAttacked[ePlayer] = eAttackedPlayer; } /// Who was the last Minor ePlayer killed that we were protecting? PlayerTypes CvDiplomacyAI::GetOtherPlayerProtectedMinorKilled(PlayerTypes ePlayer) const { return (PlayerTypes) m_aeProtectedMinorKilled[ePlayer]; } void CvDiplomacyAI::SetOtherPlayerProtectedMinorKilled(PlayerTypes ePlayer, PlayerTypes eKilledPlayer) { ASSERT(NotTeam(ePlayer), "Setting ProtectedMinorKilled for own team"); PRECONDITION((eKilledPlayer == NO_PLAYER || eKilledPlayer >= MAX_MAJOR_CIVS) && eKilledPlayer < MAX_CIV_PLAYERS, "Invalid ProtectedMinorKilled"); m_aeProtectedMinorKilled[ePlayer] = eKilledPlayer; } // ----------------------------------------------------------------------------------------------- // ------------------------------------ // Guesses // ------------------------------------ // //////////////////////////////////// // Guesses about other players' feelings towards us // //////////////////////////////////// /* /// Returns our guess as to another player's Diplomatic Opinion towards us CivOpinionTypes CvDiplomacyAI::GetOpinionTowardsUsGuess(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aeOpinionTowardsUsGuess[ePlayer]; } void CvDiplomacyAI::SetOpinionTowardsUsGuess(PlayerTypes ePlayer, CivOpinionTypes eOpinion) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(eOpinion >= 0 && eOpinion < NUM_CIV_OPINIONS, "Setting OpinionTowardsUsGuess to invalid value"); ASSERT(NotTeam(ePlayer), "Updating static OpinionTowardsUsGuess value"); m_aeOpinionTowardsUsGuess[ePlayer] = eOpinion; } /// Returns our guess as to another player's true Diplomatic Approach towards us CivApproachTypes CvDiplomacyAI::GetApproachTowardsUsGuess(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aeApproachTowardsUsGuess[ePlayer]; } void CvDiplomacyAI::SetApproachTowardsUsGuess(PlayerTypes ePlayer, CivApproachTypes eApproach) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(eApproach >= 0 && eApproach < NUM_CIV_APPROACHES, "Setting ApproachTowardsUsGuess to invalid value"); ASSERT(NotTeam(ePlayer), "Updating static ApproachTowardsUsGuess value"); m_aeApproachTowardsUsGuess[ePlayer] = eApproach; } /// Returns how long we've thought ePlayer has had his true Diplomatic Approach towards us int CvDiplomacyAI::GetApproachTowardsUsGuessCounter(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aeApproachTowardsUsGuessCounter[ePlayer]; } void CvDiplomacyAI::SetApproachTowardsUsGuessCounter(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= -1, "Setting ApproachTowardsUsGuessCounter to invalid value"); ASSERT(NotTeam(ePlayer), "Setting ApproachTowardsUsGuessCounter for own team"); m_aeApproachTowardsUsGuessCounter[ePlayer] = min(iValue, CHAR_MAX); } void CvDiplomacyAI::ChangeApproachTowardsUsGuessCounter(PlayerTypes ePlayer, int iChange) { SetApproachTowardsUsGuessCounter(ePlayer, GetApproachTowardsUsGuessCounter(ePlayer) + iChange); } */ // ----------------------------------------------------------------------------------------------- // ------------------------------------ // C4DF Values // ------------------------------------ /// Have we accepted or refused ePlayer's request to share our Diplomatic Approach towards other players? ShareApproachResponseTypes CvDiplomacyAI::GetShareApproachResponse(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return (ShareApproachResponseTypes) m_aeShareApproachResponse[ePlayer]; } void CvDiplomacyAI::SetShareApproachResponse(PlayerTypes ePlayer, ShareApproachResponseTypes eResponse) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); PRECONDITION(eResponse >= NO_SHARE_APPROACH_RESPONSE && eResponse < NUM_SHARE_APPROACH_RESPONSES, "Setting ShareApproachResponse to invalid value"); ASSERT(NotMe(ePlayer), "Setting ShareApproachResponse for self"); m_aeShareApproachResponse[ePlayer] = eResponse; } /// Have we agreed to move our troops away from ePlayer's borders? bool CvDiplomacyAI::IsPlayerMoveTroopsRequestAccepted(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abMoveTroopsRequestAccepted[ePlayer]; } void CvDiplomacyAI::SetPlayerMoveTroopsRequestAccepted(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting MoveTroopsRequestAccepted for own team"); m_abMoveTroopsRequestAccepted[ePlayer] = bValue; } /// If true, this player can't ask us to move our troops from their borders (used in the UI) bool CvDiplomacyAI::IsTooSoonForMoveTroopsRequest(PlayerTypes ePlayer) const { if (IsPlayerMoveTroopsRequestAccepted(ePlayer)) return true; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeMilitaryPromise(GetID())) return true; // Can't ask this of your teammate if (IsTeammate(ePlayer)) return true; // Observer can't ask this if (!GET_PLAYER(ePlayer).isAlive() || GET_PLAYER(ePlayer).isObserver()) return true; // Can't ask this while at war if (IsAtWar(ePlayer)) return true; // We must be able to declare war on each other - in both directions, since the promise is mutual TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam(); if (!GET_TEAM(eTeam).canDeclareWar(GetTeam(), ePlayer) || !GET_TEAM(GetTeam()).canDeclareWar(eTeam, GetID())) return true; // Exploit avoidance: No giving humans indirect backstabbing penalties if (GetPlayer()->IsAITeammateOfHuman()) return true; return false; } /// Is this AI offering a gift to ePlayer? bool CvDiplomacyAI::IsOfferingGift(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abOfferingGift[ePlayer]; } void CvDiplomacyAI::SetOfferingGift(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotMe(ePlayer), "Setting OfferingGift for self"); m_abOfferingGift[ePlayer] = bValue; } /// Did this AI offer a gift to ePlayer? bool CvDiplomacyAI::IsOfferedGift(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abOfferedGift[ePlayer]; } void CvDiplomacyAI::SetOfferedGift(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotMe(ePlayer), "Setting OfferedGift for self"); m_abOfferedGift[ePlayer] = bValue; } /// On what turn did we most recently accept a Help Request from ePlayer? int CvDiplomacyAI::GetHelpRequestAcceptedTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiHelpRequestAcceptedTurn[ePlayer]; } void CvDiplomacyAI::SetHelpRequestAcceptedTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting HelpRequestAcceptedTurn to an invalid value"); ASSERT(NotMe(ePlayer), "Setting HelpRequestAcceptedTurn for self"); m_aiHelpRequestAcceptedTurn[ePlayer] = iTurn; } /// Returns how long it will be before the next Help Request might be accepted int CvDiplomacyAI::GetHelpRequestTooSoonNumTurns(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiHelpRequestTooSoonNumTurns[ePlayer]; } void CvDiplomacyAI::SetHelpRequestTooSoonNumTurns(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= -1, "Setting HelpRequestTooSoonNumTurns to an invalid value"); ASSERT(NotMe(ePlayer), "Setting HelpRequestTooSoonNumTurns for self"); m_aiHelpRequestTooSoonNumTurns[ePlayer] = min(iValue, CHAR_MAX); } /// Too soon for another Help Request from ePlayer? bool CvDiplomacyAI::IsHelpRequestTooSoon(PlayerTypes ePlayer) const { int iHelpRequestAcceptedTurn = GetHelpRequestAcceptedTurn(ePlayer); int iHelpRequestTooSoonNumTurns = GetHelpRequestTooSoonNumTurns(ePlayer); // Haven't gotten a help request before if (iHelpRequestAcceptedTurn == -1 || iHelpRequestTooSoonNumTurns == -1) return false; int iTurnDifference = GC.getGame().getGameTurn() - iHelpRequestAcceptedTurn; return iTurnDifference < iHelpRequestTooSoonNumTurns; } /// Returns value of vassal protection given int CvDiplomacyAI::GetMaxVassalProtectValue() const { /*2500 on Standard, 1675 on Quick, 3750 on Epic, 7500 on Marathon*/ return range(((GD_INT_GET(VASSALAGE_PROTECT_VALUE_PER_OPINION_WEIGHT) * -GD_INT_GET(OPINION_WEIGHT_VASSALAGE_PROTECT_MAX) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100), 0, SHRT_MAX); } int CvDiplomacyAI::GetMaxVassalFailedProtectValue() const { /*-2500 on Standard, -1675 on Quick, -3750 on Epic, -7500 on Marathon*/ return range(((GD_INT_GET(VASSALAGE_PROTECT_VALUE_PER_OPINION_WEIGHT) * -GD_INT_GET(OPINION_WEIGHT_VASSALAGE_FAILED_PROTECT_MAX) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100), SHRT_MIN, 0); } int CvDiplomacyAI::GetVassalProtectValue(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiVassalProtectValue[ePlayer]; } void CvDiplomacyAI::SetVassalProtectValue(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting VassalProtectValue for own team"); m_aiVassalProtectValue[ePlayer] = range(iValue, GetMaxVassalFailedProtectValue(), GetMaxVassalProtectValue()); } void CvDiplomacyAI::ChangeVassalProtectValue(PlayerTypes ePlayer, int iChange, bool bDecay) { if (!bDecay) { int iScaledAmount = iChange * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent() / 100; SetVassalProtectValue(ePlayer, GetVassalProtectValue(ePlayer) + iScaledAmount); } else if (!IsTeammate(ePlayer)) { // Decay cannot cause value to go past 0! int iCurrentValue = GetVassalProtectValue(ePlayer); if (iCurrentValue > 0 && (iCurrentValue + iChange) < 0) { SetVassalProtectValue(ePlayer, 0); return; } else if (iCurrentValue < 0 && (iCurrentValue + iChange) > 0) { SetVassalProtectValue(ePlayer, 0); return; } SetVassalProtectValue(ePlayer, GetVassalProtectValue(ePlayer) + iChange); } } /// Is ePlayer a former master who liberated us from vassalage? bool CvDiplomacyAI::IsMasterLiberatedMeFromVassalage(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abMasterLiberatedMeFromVassalage[ePlayer]; } void CvDiplomacyAI::SetMasterLiberatedMeFromVassalage(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting MasterLiberatedMeFromVassalage for own team"); m_abMasterLiberatedMeFromVassalage[ePlayer] = bValue; // If we regained our capital or Holy City, forgive a master who liberated us for capturing it in the first place. if (!GetPlayer()->IsHasLostCapital()) SetPlayerCapturedCapital(ePlayer, false); if (!GetPlayer()->IsHasLostHolyCity()) SetPlayerCapturedHolyCity(ePlayer, false); } /// Did ePlayer agree to give us our independence when we asked for it? int CvDiplomacyAI::GetVassalagePeacefullyRevokedTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiPlayerVassalagePeacefullyRevokedTurn[ePlayer]; } void CvDiplomacyAI::SetVassalagePeacefullyRevokedTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting PlayerVassalagePeacefullyRevokedTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting PlayerVassalagePeacefullyRevokedTurn for own team"); m_aiPlayerVassalagePeacefullyRevokedTurn[ePlayer] = iTurn; if (iTurn > -1) { // If we regained our capital or Holy City, forgive a master who liberated us for capturing it in the first place. if (!GetPlayer()->IsHasLostCapital()) SetPlayerCapturedCapital(ePlayer, false); if (!GetPlayer()->IsHasLostHolyCity()) SetPlayerCapturedHolyCity(ePlayer, false); } } /// Did ePlayer refuse to give us our independence when we asked for it? int CvDiplomacyAI::GetVassalageForcefullyRevokedTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiPlayerVassalageForcefullyRevokedTurn[ePlayer]; } void CvDiplomacyAI::SetVassalageForcefullyRevokedTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting PlayerVassalageForcefullyRevokedTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting PlayerVassalageForcefullyRevokedTurn for own team"); m_aiPlayerVassalageForcefullyRevokedTurn[ePlayer] = iTurn; } /// Did this player declare war on us while we were their vassal? bool CvDiplomacyAI::BrokeVassalAgreement(PlayerTypes ePlayer) const { return GetBrokeVassalAgreementTurn(ePlayer) != -1; } void CvDiplomacyAI::SetBrokeVassalAgreement(PlayerTypes ePlayer, bool bValue) { if (bValue) { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; SetBrokeVassalAgreementTurn(ePlayer, GC.getGame().getGameTurn()); SetBackstabbedBy(ePlayer, true, true); } else { SetBrokeVassalAgreementTurn(ePlayer, -1); } } /// On what turn did this player most recently declare war on us while we were his vassal? int CvDiplomacyAI::GetBrokeVassalAgreementTurn(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiBrokeVassalAgreementTurn[ePlayer]; } void CvDiplomacyAI::SetBrokeVassalAgreementTurn(PlayerTypes ePlayer, int iTurn) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iTurn >= -1, "Setting BrokeVassalAgreementTurn to an invalid value"); ASSERT(NotTeam(ePlayer), "Setting BrokeVassalAgreementTurn for own team"); m_aiBrokeVassalAgreementTurn[ePlayer] = iTurn; } /// If true, AI should send a "we raised your taxes" message bool CvDiplomacyAI::IsVassalTaxRaised(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abVassalTaxRaised[ePlayer]; } void CvDiplomacyAI::SetVassalTaxRaised(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting VassalTaxRaised for own team"); m_abVassalTaxRaised[ePlayer] = bValue; } /// If true, AI should send a "we lowered your taxes" message bool CvDiplomacyAI::IsVassalTaxLowered(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_abVassalTaxLowered[ePlayer]; } void CvDiplomacyAI::SetVassalTaxLowered(PlayerTypes ePlayer, bool bValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(NotTeam(ePlayer), "Setting VassalTaxLowered for own team"); m_abVassalTaxLowered[ePlayer] = bValue; } /// How much Gold have we collected since our vassalage with ePlayer began? int CvDiplomacyAI::GetVassalGoldPerTurnCollectedSinceVassalStarted(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiVassalGoldPerTurnCollectedSinceVassalStarted[ePlayer]; } void CvDiplomacyAI::SetVassalGoldPerTurnCollectedSinceVassalStarted(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting VassalGoldPerTurnCollectedSinceVassalStarted to a negative value"); ASSERT(NotTeam(ePlayer), "Setting VassalGoldPerTurnCollectedSinceVassalStarted for own team"); m_aiVassalGoldPerTurnCollectedSinceVassalStarted[ePlayer] = max(iValue, 0); } void CvDiplomacyAI::ChangeVassalGoldPerTurnCollectedSinceVassalStarted(PlayerTypes ePlayer, int iChange) { SetVassalGoldPerTurnCollectedSinceVassalStarted(ePlayer, GetVassalGoldPerTurnCollectedSinceVassalStarted(ePlayer) + iChange); } /// How much Gold has ePlayer taxed from us since our vassalage with them began? int CvDiplomacyAI::GetVassalGoldPerTurnTaxedSinceVassalStarted(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); return m_aiVassalGoldPerTurnTaxedSinceVassalStarted[ePlayer]; } void CvDiplomacyAI::SetVassalGoldPerTurnTaxedSinceVassalStarted(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); ASSERT(iValue >= 0, "Setting VassalGoldPerTurnTaxedSinceVassalStarted to a negative value"); ASSERT(NotTeam(ePlayer), "Setting VassalGoldPerTurnTaxedSinceVassalStarted for own team"); m_aiVassalGoldPerTurnTaxedSinceVassalStarted[ePlayer] = iValue; } void CvDiplomacyAI::ChangeVassalGoldPerTurnTaxedSinceVassalStarted(PlayerTypes ePlayer, int iChange) { SetVassalGoldPerTurnTaxedSinceVassalStarted(ePlayer, GetVassalGoldPerTurnTaxedSinceVassalStarted(ePlayer) + iChange); } // ----------------------------------------------------------------------------------------------- // ************************************ // Turn Stuff // ************************************ /// Runs every turn! The order matters for a lot of this stuff, so be VERY careful about moving anything around (!) void CvDiplomacyAI::DoTurn(DiplomacyMode eDiploMode, PlayerTypes ePlayer) { //set this for one iteration, reset below m_eDiploMode = eDiploMode; m_eTargetPlayer = ePlayer; // Test if the backstabber flag should be enabled or disabled TestBackstabberFlag(); // Conquest Stats DoUpdateConquestStats(); // Coop Wars DoUpdateCoopWarStates(); // Victory Competition DoUpdateCompetingForVictory(); DoUpdateCurrentVictoryPursuit(); DoUpdateRecklessExpanders(); DoUpdateWonderSpammers(); DoUpdateTechBlockLevels(); DoUpdatePolicyBlockLevels(); DoUpdateVictoryDisputeLevels(); DoUpdateVictoryBlockLevels(); // Military Stuff DoUpdateWarStates(); GetPlayer()->DoTestEmpireInBadShapeForWar(); DoUpdatePlayerStrengthEstimates(); DoUpdateWarProgressScores(); DoUpdateWarmongerThreats(); GetPlayer()->DoTestEmpireInBadShapeForWar(); // intentionally called twice because it updates based on data from DoUpdatePlayerStrengthEstimates() DoUpdateEasyTargets(); // Aggressive Postures DoUpdateMilitaryAggressivePostures(); DoExpansionBickering(); DoUpdatePlotBuyingAggressivePostures(); // Dispute Levels DoUpdateLandDisputeLevels(); DoUpdateWonderDisputeLevels(); DoUpdateMinorCivDisputeLevels(); // Look at the situation TestBackstabbingPenalties(); // Have any backstabbing penalties expired? TestUntrustworthyFriends(); // Do we consider any players backstabbers? // Sanity Checks DoUpdateSaneDiplomaticTargets(); // Player Opinion & Approach TestOpinionModifiers(); DoUpdateOpinions(); DoUpdateGlobalPolitics(); // Peace Treaty Willingness DoUpdatePeaceTreatyWillingness(true); // These functions actually DO things, and we don't want the shadow AI behind a human player doing things for him if (!GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { DetermineVassalTaxRates(); DoUpdateDemands(); MakeWar(); DoContactMinorCivs(); DoContactMajorCivs(); if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().DoCancelAllProposedMPDealsWithPlayer(GetID(), DIPLO_AI_PLAYERS); } else { GC.getGame().GetGameDeals().DoCancelAllProposedDealsWithPlayer(GetID()); // Proposed deals with AI players are purely transitional. } } // Update Counters DoCounters(); // Logging LogStatus(); LogWarStatus(); //reset to default m_eDiploMode = DIPLO_ALL_PLAYERS; m_eTargetPlayer = NO_PLAYER; } // ----------------------------------------------------------------------------------------------- // //////////////////////////////////// // TEST BACKSTABBER FLAG // //////////////////////////////////// /// Test if the backstabber flag should be enabled or disabled /// This code makes turning off Nuclear Gandhi savegame compatible void CvDiplomacyAI::TestBackstabberFlag() { if (IsBackstabber()) { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) || GetPlayer()->IsVassalOfSomeone()) { SetBackstabber(false); } else if (GetLoyalty() > 2 && !IsNuclearGandhi() && GetWeDeclaredWarOnFriendCount(GetID()) == 0) { SetBackstabber(false); } } else if (!GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && !GetPlayer()->IsVassalOfSomeone() && IsNuclearGandhi()) { SetBackstabber(true); } } // //////////////////////////////////// // CONQUEST STATS // //////////////////////////////////// /// How many players have other major civilizations conquered? void CvDiplomacyAI::DoUpdateConquestStats() { // What players do we know exist? vector vKnownPlayers; int iLoop = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsHasMet(eLoopPlayer, true)) { if (std::find(vKnownPlayers.begin(), vKnownPlayers.end(), eLoopPlayer) == vKnownPlayers.end()) { vKnownPlayers.push_back(eLoopPlayer); } } for (CvCity* pLoopCity = GET_PLAYER(eLoopPlayer).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(eLoopPlayer).nextCity(&iLoop)) { CvPlot* pCityPlot = pLoopCity->plot(); if (pCityPlot == NULL || !pLoopCity->isRevealed(GetTeam(),false,false)) continue; PlayerTypes eCityOwner = pCityPlot->getOwner(); if (eCityOwner == NO_PLAYER || eCityOwner == BARBARIAN_PLAYER || eCityOwner == GetID()) continue; if (std::find(vKnownPlayers.begin(), vKnownPlayers.end(), eCityOwner) == vKnownPlayers.end()) { vKnownPlayers.push_back(eCityOwner); } PlayerTypes eOriginalOwner = pLoopCity->getOriginalOwner(); if (eOriginalOwner == eCityOwner || eOriginalOwner == NO_PLAYER || eOriginalOwner == BARBARIAN_PLAYER || eOriginalOwner == GetID()) continue; if (std::find(vKnownPlayers.begin(), vKnownPlayers.end(), eOriginalOwner) == vKnownPlayers.end()) { vKnownPlayers.push_back(eOriginalOwner); } } } for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam(); if (IsPlayerValid(ePlayer, true)) { int iNumMajorsConquered = 0; int iNumMinorsConquered = 0; for (std::vector::iterator it = vKnownPlayers.begin(); it != vKnownPlayers.end(); ++it) { TeamTypes eLoopTeam = GET_PLAYER(*it).getTeam(); if (eLoopTeam == eTeam) continue; // Killed them? if (!GET_PLAYER(*it).isAlive() && GET_TEAM(eLoopTeam).GetKilledByTeam() == eTeam) { if (GET_PLAYER(*it).isMajorCiv()) { iNumMajorsConquered++; } else if (GET_PLAYER(*it).isMinorCiv()) { iNumMinorsConquered++; } } // Forcibly vassalized them? else if (GET_PLAYER(*it).isMajorCiv() && GET_TEAM(eLoopTeam).IsVassal(eTeam) && !GET_TEAM(eLoopTeam).IsVoluntaryVassal(eTeam)) { iNumMajorsConquered++; } // Captured their original capital? else if (GET_PLAYER(*it).IsHasLostCapital()) { CvPlot *pOriginalCapitalPlot = GC.getMap().plot(GET_PLAYER(*it).GetOriginalCapitalX(), GET_PLAYER(*it).GetOriginalCapitalY()); if (pOriginalCapitalPlot != NULL && pOriginalCapitalPlot->isCity()) { if (GET_PLAYER(pOriginalCapitalPlot->getOwner()).getTeam() == eTeam) { if (GET_PLAYER(*it).isMajorCiv()) { iNumMajorsConquered++; } else if (GET_PLAYER(*it).isMinorCiv()) { iNumMinorsConquered++; } } } } } SetPlayerNumMajorsConquered(ePlayer, iNumMajorsConquered); SetPlayerNumMinorsConquered(ePlayer, iNumMinorsConquered); } else { SetPlayerNumMajorsConquered(ePlayer, 0); SetPlayerNumMinorsConquered(ePlayer, 0); } } } // ----------------------------------------------------------------------------------------------- // //////////////////////////////////// // COOP WARS // //////////////////////////////////// /// Updates our coop war states for the turn void CvDiplomacyAI::DoUpdateCoopWarStates() { int iGameTurn = GC.getGame().getGameTurn(); for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (eLoopPlayer == GetID()) continue; if (!GET_PLAYER(eLoopPlayer).isAlive()) continue; if (!IsHasMet(eLoopPlayer, true)) continue; for (int iThirdPartyLoop = 0; iThirdPartyLoop < MAX_MAJOR_CIVS; iThirdPartyLoop++) { PlayerTypes eThirdParty = (PlayerTypes) iThirdPartyLoop; if (!GET_PLAYER(eThirdParty).isAlive()) continue; if (GET_PLAYER(eThirdParty).getTeam() == GET_PLAYER(eLoopPlayer).getTeam()) continue; if (!IsHasMet(eThirdParty)) continue; CoopWarStates eCoopWarState = GetCoopWarState(eLoopPlayer, eThirdParty); int iTurn = GetCoopWarStateChangeTurn(eLoopPlayer, eThirdParty); if (iTurn < 0 || eCoopWarState == NO_COOP_WAR_STATE || eCoopWarState == COOP_WAR_STATE_ONGOING) continue; int iTurnDifference = iGameTurn - iTurn; if (eCoopWarState == COOP_WAR_STATE_REJECTED || eCoopWarState == COOP_WAR_STATE_WARNED_TARGET) { if (iTurnDifference >= 30) { SetCoopWarState(eLoopPlayer, eThirdParty, NO_COOP_WAR_STATE); } } else if (eCoopWarState == COOP_WAR_STATE_PREPARING) { if (CanStartCoopWar(eLoopPlayer, eThirdParty)) { if (iTurnDifference >= /*10*/ GD_INT_GET(COOP_WAR_SOON_COUNTER)) { DoStartCoopWar(eLoopPlayer, eThirdParty); } } else { CvNotifications* pNotify = GetPlayer()->GetNotifications(); if (pNotify) { Localization::String strSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_S"); Localization::String strText = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_INVALID_TARGET"); strText << GET_PLAYER(eThirdParty).getCivilizationShortDescriptionKey(); strText << GET_PLAYER(eLoopPlayer).getCivilizationShortDescriptionKey(); pNotify->Add(NOTIFICATION_DIPLOMACY_DECLARATION, strText.toUTF8(), strSummary.toUTF8(), -1, -1, -1); } pNotify = GET_PLAYER(eLoopPlayer).GetNotifications(); if (pNotify) { Localization::String strSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_S"); Localization::String strText = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_INVALID_TARGET"); strText << GET_PLAYER(eThirdParty).getCivilizationShortDescriptionKey(); strText << GetPlayer()->getCivilizationShortDescriptionKey(); pNotify->Add(NOTIFICATION_DIPLOMACY_DECLARATION, strText.toUTF8(), strSummary.toUTF8(), -1, -1, -1); } SetCoopWarState(eLoopPlayer, eThirdParty, NO_COOP_WAR_STATE); GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->SetCoopWarState(GetID(), eThirdParty, NO_COOP_WAR_STATE); } } } } } /// Can we initiate our planned coop war with eAllyPlayer against eTargetPlayer? bool CvDiplomacyAI::CanStartCoopWar(PlayerTypes eAllyPlayer, PlayerTypes eTargetPlayer) { // Do we even have a coop war plan to begin with? if (GetCoopWarState(eAllyPlayer, eTargetPlayer) != COOP_WAR_STATE_PREPARING) return false; // If we somehow got here and we're at war with our ally or our friendship was broken, no dice. (failsafe) if (IsAtWar(eAllyPlayer) || IsDoFBroken(eAllyPlayer) || GET_PLAYER(eAllyPlayer).GetDiplomacyAI()->IsDoFBroken(GetID())) return false; // Make sure the target is still valid if (!IsValidCoopWarTarget(eTargetPlayer, true)) return false; if (!GET_PLAYER(eAllyPlayer).GetDiplomacyAI()->IsValidCoopWarTarget(eTargetPlayer, true)) return false; return true; } /// Starts a coop war void CvDiplomacyAI::DoStartCoopWar(PlayerTypes eAllyPlayer, PlayerTypes eTargetPlayer) { // Our declaration if (IsAtWar(eTargetPlayer) || DeclareWar(eTargetPlayer)) { if (!GetPlayer()->isHuman(ISHUMAN_AI_UNITS)) { GetPlayer()->GetMilitaryAI()->RequestCityAttack(eTargetPlayer, 3, false); } // Their war declaration if (GET_PLAYER(eAllyPlayer).IsAtWarWith(eTargetPlayer) || GET_PLAYER(eAllyPlayer).GetDiplomacyAI()->DeclareWar(eTargetPlayer)) { if (!GET_PLAYER(eAllyPlayer).isHuman(ISHUMAN_AI_UNITS)) { GET_PLAYER(eAllyPlayer).GetMilitaryAI()->RequestCityAttack(eTargetPlayer, 3, false); } int iMyTurnsAtWar = GET_TEAM(GetTeam()).GetNumTurnsAtWar(GET_PLAYER(eTargetPlayer).getTeam()); int iTheirTurnsAtWar = GET_TEAM(GET_PLAYER(eAllyPlayer).getTeam()).GetNumTurnsAtWar(GET_PLAYER(eTargetPlayer).getTeam()); int iLockedTurns = /*15*/ GD_INT_GET(COOP_WAR_LOCKED_LENGTH) - max(iMyTurnsAtWar, iTheirTurnsAtWar); if (iLockedTurns > 0) { GET_TEAM(GetTeam()).ChangeNumTurnsLockedIntoWar(GET_PLAYER(eTargetPlayer).getTeam(), iLockedTurns); GET_TEAM(GET_PLAYER(eAllyPlayer).getTeam()).ChangeNumTurnsLockedIntoWar(GET_PLAYER(eTargetPlayer).getTeam(), iLockedTurns); } SetCoopWarState(eAllyPlayer, eTargetPlayer, COOP_WAR_STATE_ONGOING); GET_PLAYER(eAllyPlayer).GetDiplomacyAI()->SetCoopWarState(GetID(), eTargetPlayer, COOP_WAR_STATE_ONGOING); } else { CvNotifications* pNotify = GetPlayer()->GetNotifications(); if (pNotify) { Localization::String strSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_S"); Localization::String strText = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_INVALID_TARGET"); strText << GET_PLAYER(eTargetPlayer).getCivilizationShortDescriptionKey(); strText << GET_PLAYER(eAllyPlayer).getCivilizationShortDescriptionKey(); pNotify->Add(NOTIFICATION_DIPLOMACY_DECLARATION, strText.toUTF8(), strSummary.toUTF8(), -1, -1, -1); } pNotify = GET_PLAYER(eAllyPlayer).GetNotifications(); if (pNotify) { Localization::String strSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_S"); Localization::String strText = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_INVALID_TARGET"); strText << GET_PLAYER(eTargetPlayer).getCivilizationShortDescriptionKey(); strText << GetPlayer()->getCivilizationShortDescriptionKey(); pNotify->Add(NOTIFICATION_DIPLOMACY_DECLARATION, strText.toUTF8(), strSummary.toUTF8(), -1, -1, -1); } SetCoopWarState(eAllyPlayer, eTargetPlayer, NO_COOP_WAR_STATE); GET_PLAYER(eAllyPlayer).GetDiplomacyAI()->SetCoopWarState(GetID(), eTargetPlayer, NO_COOP_WAR_STATE); } } else { CvNotifications* pNotify = GetPlayer()->GetNotifications(); if (pNotify) { Localization::String strSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_S"); Localization::String strText = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_INVALID_TARGET"); strText << GET_PLAYER(eTargetPlayer).getCivilizationShortDescriptionKey(); strText << GET_PLAYER(eAllyPlayer).getCivilizationShortDescriptionKey(); pNotify->Add(NOTIFICATION_DIPLOMACY_DECLARATION, strText.toUTF8(), strSummary.toUTF8(), -1, -1, -1); } pNotify = GET_PLAYER(eAllyPlayer).GetNotifications(); if (pNotify) { Localization::String strSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_S"); Localization::String strText = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_INVALID_TARGET"); strText << GET_PLAYER(eTargetPlayer).getCivilizationShortDescriptionKey(); strText << GetPlayer()->getCivilizationShortDescriptionKey(); pNotify->Add(NOTIFICATION_DIPLOMACY_DECLARATION, strText.toUTF8(), strSummary.toUTF8(), -1, -1, -1); } SetCoopWarState(eAllyPlayer, eTargetPlayer, NO_COOP_WAR_STATE); GET_PLAYER(eAllyPlayer).GetDiplomacyAI()->SetCoopWarState(GetID(), eTargetPlayer, NO_COOP_WAR_STATE); } } // ----------------------------------------------------------------------------------------------- // //////////////////////////////////// // VICTORY COMPETITION // //////////////////////////////////// /// Is this player currently competing to win? Determines whether Victory Dispute/Block penalties apply. void CvDiplomacyAI::DoUpdateCompetingForVictory() { SetCompetingForVictory(true); // Vassals do not compete for victory. if (GetPlayer()->IsVassalOfSomeone()) { SetCompetingForVictory(false); } // If we were resurrected by anyone, we're not competing for victory. else if (WasResurrectedByAnyone()) { SetCompetingForVictory(false); } // If we've lost our original capital, we're not competing for victory. else if (GetPlayer()->IsHasLostCapital()) { SetCompetingForVictory(false); } // Victory competition is disabled by game options else if (!GC.getGame().IsVictoryCompetitionEnabled()) { SetCompetingForVictory(false); } if (IsEndgameAggressive()) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; SetEndgameAggressiveTo(eLoopPlayer, IsPlayerValid(eLoopPlayer) && GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsCloseToAnyVictoryCondition()); } } else { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; SetEndgameAggressiveTo(eLoopPlayer, false); } } } /// Are we extra aggressive towards players that are close to victory in general? bool CvDiplomacyAI::IsEndgameAggressive() const { if (!IsCompetingForVictory()) return false; return GC.getGame().IsEndgameAggressionEnabled(); } /// What victory condition are we currently going for? void CvDiplomacyAI::DoUpdateCurrentVictoryPursuit() { // Check if this leader will ALWAYS go for a specific victory condition if they can. switch (GetEternalVictoryPursuit()) { case VICTORY_PURSUIT_DOMINATION: SetCurrentVictoryPursuit(VICTORY_PURSUIT_DOMINATION); return; case VICTORY_PURSUIT_DIPLOMACY: SetCurrentVictoryPursuit(VICTORY_PURSUIT_DIPLOMACY); return; case VICTORY_PURSUIT_CULTURE: SetCurrentVictoryPursuit(VICTORY_PURSUIT_CULTURE); return; case VICTORY_PURSUIT_SCIENCE: SetCurrentVictoryPursuit(VICTORY_PURSUIT_SCIENCE); return; default: break; } // Always War scenarios - always Domination if (GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR) || GC.getGame().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE)) { SetCurrentVictoryPursuit(VICTORY_PURSUIT_DOMINATION); return; } // Human player? Use the primary victory pursuit. if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { switch (GetPrimaryVictoryPursuit()) { case VICTORY_PURSUIT_DOMINATION: SetCurrentVictoryPursuit(VICTORY_PURSUIT_DOMINATION); break; case VICTORY_PURSUIT_DIPLOMACY: SetCurrentVictoryPursuit(VICTORY_PURSUIT_DIPLOMACY); break; case VICTORY_PURSUIT_CULTURE: SetCurrentVictoryPursuit(VICTORY_PURSUIT_CULTURE); break; case VICTORY_PURSUIT_SCIENCE: SetCurrentVictoryPursuit(VICTORY_PURSUIT_SCIENCE); break; default: break; } return; } // Let's check if we're close to winning. if (IsCloseToSpaceshipVictory()) { SetCurrentVictoryPursuit(VICTORY_PURSUIT_SCIENCE); return; } else if (IsCloseToCultureVictory()) { SetCurrentVictoryPursuit(VICTORY_PURSUIT_CULTURE); return; } else if (IsCloseToDiploVictory()) { SetCurrentVictoryPursuit(VICTORY_PURSUIT_DIPLOMACY); return; } else if (IsCloseToWorldConquest()) { SetCurrentVictoryPursuit(VICTORY_PURSUIT_DOMINATION); return; } AIGrandStrategyTypes eMyGrandStrategy = GetPlayer()->GetGrandStrategyAI()->GetActiveGrandStrategy(); AIGrandStrategyTypes eDomination = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_CONQUEST"); AIGrandStrategyTypes eDiplomacy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_UNITED_NATIONS"); AIGrandStrategyTypes eCulture = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_CULTURE"); AIGrandStrategyTypes eScience = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_SPACESHIP"); // If we don't care about winning, use our primary victory pursuit based on flavors if (!IsSeriousAboutVictory()) { switch (GetPrimaryVictoryPursuit()) { case VICTORY_PURSUIT_DIPLOMACY: SetCurrentVictoryPursuit(VICTORY_PURSUIT_DIPLOMACY); break; case VICTORY_PURSUIT_CULTURE: SetCurrentVictoryPursuit(VICTORY_PURSUIT_CULTURE); break; case VICTORY_PURSUIT_SCIENCE: SetCurrentVictoryPursuit(VICTORY_PURSUIT_SCIENCE); break; default: SetCurrentVictoryPursuit(VICTORY_PURSUIT_DOMINATION); break; } return; } // We care about winning, so Grand Strategy AI decides if (eMyGrandStrategy == eDomination) { SetCurrentVictoryPursuit(VICTORY_PURSUIT_DOMINATION); return; } else if (eMyGrandStrategy == eDiplomacy) { SetCurrentVictoryPursuit(VICTORY_PURSUIT_DIPLOMACY); return; } else if (eMyGrandStrategy == eCulture) { SetCurrentVictoryPursuit(VICTORY_PURSUIT_CULTURE); return; } else if (eMyGrandStrategy == eScience) { SetCurrentVictoryPursuit(VICTORY_PURSUIT_SCIENCE); return; } // By default we go for conquest SetCurrentVictoryPursuit(VICTORY_PURSUIT_DOMINATION); } bool CvDiplomacyAI::IsSeriousAboutVictory() const { AIGrandStrategyTypes eDomination = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_CONQUEST"); AIGrandStrategyTypes eMyGrandStrategy = GetPlayer()->GetGrandStrategyAI()->GetActiveGrandStrategy(); int iGameEra = GC.getGame().getCurrentEra(); // AI isn't too focused on victory in the early game, if victory competition is disabled, or if it hasn't picked a grand strategy with a high enough priority bool bDontCareAboutWinning = eMyGrandStrategy == NO_AIGRANDSTRATEGY; bDontCareAboutWinning = bDontCareAboutWinning || GetPlayer()->GetGrandStrategyAI()->GetGrandStrategyPriority(eMyGrandStrategy) <= 500; bDontCareAboutWinning = bDontCareAboutWinning || !IsCompetingForVictory(); bDontCareAboutWinning = bDontCareAboutWinning || (eMyGrandStrategy != eDomination && iGameEra < GD_INT_GET(RENAISSANCE_ERA)); bDontCareAboutWinning = bDontCareAboutWinning || (eMyGrandStrategy == eDomination && iGameEra < GD_INT_GET(MEDIEVAL_ERA) && GetPlayer()->GetNumCapitalCities() <= 0); return !bDontCareAboutWinning; } /// Do we consider any players to be expanding too recklessly? void CvDiplomacyAI::DoUpdateRecklessExpanders() { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return; // We're not competing for victory, so we don't care. if (!IsCompetingForVictory() || GC.getGame().GetNumMajorCivsAlive() < 2) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; SetRecklessExpander(ePlayer, false); } return; } // Calculate the global averages int iMedianNumCities = GC.getGame().CalculateMedianNumCities(); int iMedianNumPlots = GC.getGame().CalculateMedianNumPlots(); double fTotalNumCities = 0; double fTotalNumPlots = 0; int iNumPlayers = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; CvPlayer* pPlayer = &GET_PLAYER(eLoopPlayer); // Dead or no cities if (!pPlayer->isMajorCiv() || !pPlayer->isAlive() || pPlayer->getNumCities() == 0) continue; iNumPlayers++; fTotalNumCities += pPlayer->getNumCities(); fTotalNumPlots += pPlayer->getTotalLand(); } for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(ePlayer)) { // If the player has too few cities, don't worry about it int iNumCities = GET_PLAYER(ePlayer).getNumCities(); int iNumPlots = GET_PLAYER(ePlayer).getTotalLand(); if (iNumCities < 4) { SetRecklessExpander(ePlayer, false); continue; } // If the player is too far away from us, we don't care if (GetPlayer()->GetProximityToPlayer(ePlayer) < PLAYER_PROXIMITY_CLOSE) { SetRecklessExpander(ePlayer, false); continue; } // If we have at least as many cities OR land as them, don't worry about it if (iNumCities <= GetPlayer()->getNumCities()) { SetRecklessExpander(ePlayer, false); continue; } if (iNumPlots <= GetPlayer()->getTotalLand()) { SetRecklessExpander(ePlayer, false); continue; } // If this guy's military is as strong as ours, then it probably means he's just stronger than us if (GetTargetValue(ePlayer) <= TARGET_VALUE_DIFFICULT) { SetRecklessExpander(ePlayer, false); continue; } // What's the global average, not counting this player? double dAverageNumCities = ((fTotalNumCities - iNumCities) / max(1, iNumPlayers - 1)); double dAverageNumPlots = ((fTotalNumPlots - iNumPlots) / max(1, iNumPlayers - 1)); // Do they have way more cities than the average player in the game? if ((iNumCities*100) > (iMedianNumCities * /*200*/ GD_INT_GET(RECKLESS_EXPANDER_CITIES_THRESHOLD))) { // Must also have at least 50% more than the global average, just to prevent anything stupid if (iNumCities > (dAverageNumCities * 1.5)) { SetRecklessExpander(ePlayer, true); continue; } } // Do they have way more land than the average player in the game? if ((iNumPlots*100) > (iMedianNumPlots * /*250*/ GD_INT_GET(RECKLESS_EXPANDER_LAND_THRESHOLD))) { // Must also have at least 50% more than the global average, just to prevent anything stupid if (iNumPlots > (dAverageNumPlots * 1.5)) { SetRecklessExpander(ePlayer, true); continue; } } SetRecklessExpander(ePlayer, false); } else { SetRecklessExpander(ePlayer, false); } } } void CvDiplomacyAI::DoUpdateWonderSpammers() { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return; // We're not competing for victory, so we don't care. if (!IsCompetingForVictory() || GC.getGame().GetNumMajorCivsAlive() < 2) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; SetWonderSpammer(ePlayer, false); } return; } int iMedianNumWonders = GC.getGame().CalculateMedianNumWondersConstructed(); double dAverageNumWonders = 0; int iNumPlayers = 0; // Calculate the global average for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; CvPlayer* pPlayer = &GET_PLAYER(eLoopPlayer); // Only major civs who have built Wonders are counted if (!pPlayer->isMajorCiv() || pPlayer->GetWondersConstructed() <= 0) continue; iNumPlayers++; dAverageNumWonders += pPlayer->GetWondersConstructed(); } // Find the mean value dAverageNumWonders /= max(1, iNumPlayers); bool bCultural = GetPlayer()->GetPlayerTraits()->IsTourism() || IsGoingForCultureVictory(); bool bConqueror = GetPlayer()->GetPlayerTraits()->IsWarmonger() || IsGoingForWorldConquest(); bool bCanCrossOcean = GetPlayer()->CanCrossOcean(); for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(ePlayer)) { // Up to two Wonders is always okay. int iNumWonders = GET_PLAYER(ePlayer).GetWondersConstructed(); if (iNumWonders <= 2) { SetWonderSpammer(ePlayer, false); continue; } // We've constructed at least as many as them? Ignore. if (GetPlayer()->GetWondersConstructed() >= iNumWonders) { SetWonderSpammer(ePlayer, false); continue; } // If we're not a cultural civ, we only care about this if they're nearby PlayerProximityTypes eProximity = GetPlayer()->GetProximityToPlayer(ePlayer); if (!bCultural) { if (bConqueror) { if (bCanCrossOcean) { if (eProximity < PLAYER_PROXIMITY_FAR) { SetWonderSpammer(ePlayer, false); continue; } } else { if (eProximity < PLAYER_PROXIMITY_CLOSE) { SetWonderSpammer(ePlayer, false); continue; } } } else { if (bCanCrossOcean) { if (eProximity < PLAYER_PROXIMITY_CLOSE) { SetWonderSpammer(ePlayer, false); continue; } } else { if (eProximity < PLAYER_PROXIMITY_NEIGHBORS) { SetWonderSpammer(ePlayer, false); continue; } } } } int iWonderBlockMod = GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getWonderBlockMod() : GC.getGame().getHandicapInfo().getWonderBlockMod(); // 0 or negative // Must have built several more Wonders than the median player in this game if (iNumWonders > (iMedianNumWonders + /*3*/ GD_INT_GET(WONDER_SPAMMER_THRESHOLD) - iWonderBlockMod)) { // Must also have at least 50% more than the global average, just to prevent anything stupid if (iNumWonders >= (dAverageNumWonders * 1.5)) { SetWonderSpammer(ePlayer, true); continue; } } SetWonderSpammer(ePlayer, false); } else { SetWonderSpammer(ePlayer, false); } } } void CvDiplomacyAI::DoUpdateTechBlockLevels() { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return; if (GC.getGame().isOption(GAMEOPTION_NO_SCIENCE)) return; // We must be competing for victory to care about this. if (!IsCompetingForVictory()) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; SetTechBlockLevel(ePlayer, BLOCK_LEVEL_NONE); } return; } int iOurTechs = GET_TEAM(GetTeam()).GetTeamTechs()->GetNumTechsKnown(); int iDiploBalance = GetDiploBalance(); int iTechMod = 0; if (IsScientist() || GetPlayer()->GetPlayerTraits()->IsNerd()) { iTechMod++; } VictoryTypes eSpaceshipVictory = (VictoryTypes) GC.getInfoTypeForString("VICTORY_SPACE_RACE", true); bool bSpaceshipValid = (eSpaceshipVictory != NO_VICTORY && GC.getGame().isVictoryValid(eSpaceshipVictory)); if (!bSpaceshipValid) { iTechMod--; } else if (GetPlayer()->GetCurrentEra() >= 3 && IsGoingForSpaceshipVictory()) { iTechMod++; } for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; // Don't do this until the Classical Era. if (GetPlayer()->GetCurrentEra() <= 0 && GET_PLAYER(ePlayer).GetCurrentEra() <= 0) continue; if (!IsPlayerValid(ePlayer)) { SetTechBlockLevel(ePlayer, BLOCK_LEVEL_NONE); continue; } if (bSpaceshipValid && IsEndgameAggressiveTo(ePlayer) && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsCloseToSpaceshipVictory()) { SetTechBlockLevel(ePlayer, BLOCK_LEVEL_FIERCE); continue; } int iTechDifference = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetTeamTechs()->GetNumTechsKnown() - iOurTechs; // Tied pre-Medieval era? Disregard. if (iTechDifference == 0 && GetPlayer()->GetCurrentEra() < 2) { SetTechBlockLevel(ePlayer, BLOCK_LEVEL_NONE); continue; } // Anything that modifies tech competitiveness? iTechDifference += iTechMod; if (!IsUntrustworthy(ePlayer) && !IsDenouncedPlayer(ePlayer) && !IsDenouncedByPlayer(ePlayer)) { // Reduce if we have a Research Agreement if (IsHasResearchAgreement(ePlayer)) { iTechDifference--; } // Reduce if we're friends and they can trade tech to us if (!GC.getGame().isOption(GAMEOPTION_NO_TECH_TRADING) && IsDoFAccepted(ePlayer) && IsHasEmbassy(ePlayer) && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasEmbassy(GetID()) && GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isTechTrading()) { iTechDifference--; } // Reduce if we have a Defensive Pact - we value military protection if (IsHasDefensivePact(ePlayer)) { iTechDifference--; } } if (iTechDifference > 0) { BlockLevelTypes eBlockLevel = BLOCK_LEVEL_NONE; int DifficultyModifier = GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getTechBlockMod() : GC.getGame().getHandicapInfo().getTechBlockMod(); // Multiply by DiploBalance flavor - we want to maintain a balance of power in the world...with us in the lead, of course iTechDifference *= iDiploBalance; iTechDifference += DifficultyModifier; if (iTechDifference >= /*30*/ GD_INT_GET(TECH_BLOCK_FIERCE_THRESHOLD)) eBlockLevel = BLOCK_LEVEL_FIERCE; else if (iTechDifference >= /*20*/ GD_INT_GET(TECH_BLOCK_STRONG_THRESHOLD)) eBlockLevel = BLOCK_LEVEL_STRONG; else if (iTechDifference >= /*10*/ GD_INT_GET(TECH_BLOCK_WEAK_THRESHOLD)) eBlockLevel = BLOCK_LEVEL_WEAK; SetTechBlockLevel(ePlayer, eBlockLevel); } else { SetTechBlockLevel(ePlayer, BLOCK_LEVEL_NONE); } } } void CvDiplomacyAI::DoUpdatePolicyBlockLevels() { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return; if (GC.getGame().isOption(GAMEOPTION_NO_POLICIES)) return; // We must be competing for victory to care about this. if (!IsCompetingForVictory()) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; SetPolicyBlockLevel(ePlayer, BLOCK_LEVEL_NONE); } return; } int iOurPolicies = GetPlayer()->GetPlayerPolicies()->GetNumPoliciesOwned(true, true, true); int iDiploBalance = GetDiploBalance(); int iPolicyMod = 0; if (IsCultural() || GetPlayer()->GetPlayerTraits()->IsTourism()) { iPolicyMod++; } VictoryTypes eCulturalVictory = (VictoryTypes) GC.getInfoTypeForString("VICTORY_CULTURAL", true); bool bCultureValid = (eCulturalVictory != NO_VICTORY && GC.getGame().isVictoryValid(eCulturalVictory)); if (!bCultureValid) { iPolicyMod--; } else if (GetPlayer()->GetCurrentEra() >= 3 && IsGoingForCultureVictory()) { iPolicyMod++; } for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; // Don't do this until the Classical Era. if (GetPlayer()->GetCurrentEra() <= 0 && GET_PLAYER(ePlayer).GetCurrentEra() <= 0) continue; if (!IsPlayerValid(ePlayer)) { SetPolicyBlockLevel(ePlayer, BLOCK_LEVEL_NONE); continue; } if (bCultureValid && IsEndgameAggressiveTo(ePlayer) && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsCloseToCultureVictory()) { SetPolicyBlockLevel(ePlayer, BLOCK_LEVEL_FIERCE); continue; } int iPolicyDifference = GET_PLAYER(ePlayer).GetPlayerPolicies()->GetNumPoliciesOwned(true, true, true) - iOurPolicies; // For Policies, if we're ahead, never apply penalties. if (iPolicyDifference < 0) { SetPolicyBlockLevel(ePlayer, BLOCK_LEVEL_NONE); continue; } // Tied pre-Medieval era? Disregard. else if (iPolicyDifference == 0 && GetPlayer()->GetCurrentEra() < 2) { SetPolicyBlockLevel(ePlayer, BLOCK_LEVEL_NONE); continue; } // Sanity check for early game. else if (iOurPolicies <= 1 && iPolicyDifference <= 1) { SetPolicyBlockLevel(ePlayer, BLOCK_LEVEL_NONE); continue; } // Anything that modifies policy competitiveness? iPolicyDifference += iPolicyMod; if (iPolicyDifference > 0) { BlockLevelTypes eBlockLevel = BLOCK_LEVEL_NONE; int DifficultyModifier = GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getPolicyBlockMod() : GC.getGame().getHandicapInfo().getPolicyBlockMod(); // Multiply by DiploBalance flavor - we want to maintain a balance of power in the world...with us in the lead, of course iPolicyDifference *= iDiploBalance; iPolicyDifference += DifficultyModifier; if (iPolicyDifference >= /*24*/ GD_INT_GET(POLICY_BLOCK_FIERCE_THRESHOLD)) eBlockLevel = BLOCK_LEVEL_FIERCE; else if (iPolicyDifference >= /*16*/ GD_INT_GET(POLICY_BLOCK_STRONG_THRESHOLD)) eBlockLevel = BLOCK_LEVEL_STRONG; else if (iPolicyDifference >= /*8*/ GD_INT_GET(POLICY_BLOCK_WEAK_THRESHOLD)) eBlockLevel = BLOCK_LEVEL_WEAK; SetPolicyBlockLevel(ePlayer, eBlockLevel); } else { SetPolicyBlockLevel(ePlayer, BLOCK_LEVEL_NONE); } } } /// Updates what our level of Dispute is with all players over Victory (for the SAME victory condition) void CvDiplomacyAI::DoUpdateVictoryDisputeLevels() { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return; //Don't do this at the start of the game. if (GC.getGame().getGameTurn() <= 150) return; if (!IsSeriousAboutVictory()) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; SetVictoryDisputeLevel(eLoopPlayer, DISPUTE_LEVEL_NONE); } return; } AIGrandStrategyTypes eMyGrandStrategy = GetPlayer()->GetGrandStrategyAI()->GetActiveGrandStrategy(); // Loop through all (valid) Players for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer) && GET_PLAYER(eLoopPlayer).isMajorCiv()) { AIGrandStrategyTypes eTheirGrandStrategy = GetPlayer()->GetGrandStrategyAI()->GetGuessOtherPlayerActiveGrandStrategy(eLoopPlayer); if (eTheirGrandStrategy == NO_AIGRANDSTRATEGY || eTheirGrandStrategy != eMyGrandStrategy) { SetVictoryDisputeLevel(eLoopPlayer, DISPUTE_LEVEL_NONE); continue; } if (!IsAtWar(eLoopPlayer) && GetCivOpinion(eLoopPlayer) == CIV_OPINION_ALLY) { SetVictoryDisputeLevel(eLoopPlayer, DISPUTE_LEVEL_NONE); continue; } DisputeLevelTypes eDisputeLevel = DISPUTE_LEVEL_NONE; int iVictoryDisputeWeight = 0; switch (GetPlayer()->GetGrandStrategyAI()->GetGuessOtherPlayerActiveGrandStrategyConfidence(eLoopPlayer)) { case GUESS_CONFIDENCE_POSITIVE: iVictoryDisputeWeight = /*25*/ GD_INT_GET(VICTORY_DISPUTE_GRAND_STRATEGY_MATCH_POSITIVE); break; case GUESS_CONFIDENCE_LIKELY: iVictoryDisputeWeight = /*15*/ GD_INT_GET(VICTORY_DISPUTE_GRAND_STRATEGY_MATCH_LIKELY); break; case GUESS_CONFIDENCE_UNSURE: iVictoryDisputeWeight = /*5*/ GD_INT_GET(VICTORY_DISPUTE_GRAND_STRATEGY_MATCH_UNSURE); break; } // Reduce competitiveness in earlier eras iVictoryDisputeWeight -= (6 - GC.getGame().getCurrentEra()); if (iVictoryDisputeWeight > 0) { int DifficultyModifier = GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(eLoopPlayer).getHandicapInfo().getVictoryDisputeMod() : GC.getGame().getHandicapInfo().getVictoryDisputeMod(); // Add weight for Player's competitiveness (1 - 10) iVictoryDisputeWeight *= GetVictoryCompetitiveness(); iVictoryDisputeWeight += DifficultyModifier; // Now see what our new Dispute Level should be if (iVictoryDisputeWeight >= /*80*/ GD_INT_GET(VICTORY_DISPUTE_FIERCE_THRESHOLD)) eDisputeLevel = DISPUTE_LEVEL_FIERCE; else if (iVictoryDisputeWeight >= /*50*/ GD_INT_GET(VICTORY_DISPUTE_STRONG_THRESHOLD)) eDisputeLevel = DISPUTE_LEVEL_STRONG; else if (iVictoryDisputeWeight >= /*30*/ GD_INT_GET(VICTORY_DISPUTE_WEAK_THRESHOLD)) eDisputeLevel = DISPUTE_LEVEL_WEAK; } // Actually set the Level SetVictoryDisputeLevel(eLoopPlayer, eDisputeLevel); } else { SetVictoryDisputeLevel(eLoopPlayer, DISPUTE_LEVEL_NONE); } } } /// Updates what our level of Dispute is with all players over Victory (for a DIFFERENT victory condition) void CvDiplomacyAI::DoUpdateVictoryBlockLevels() { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return; //Don't do this at the start of the game. if (GC.getGame().getGameTurn() <= 150) return; if (!IsSeriousAboutVictory()) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; SetVictoryBlockLevel(eLoopPlayer, BLOCK_LEVEL_NONE); } return; } AIGrandStrategyTypes eMyGrandStrategy = GetPlayer()->GetGrandStrategyAI()->GetActiveGrandStrategy(); AIGrandStrategyTypes eConquestGrandStrategy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_CONQUEST"); AIGrandStrategyTypes eCultureGrandStrategy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_CULTURE"); AIGrandStrategyTypes eUNGrandStrategy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_UNITED_NATIONS"); AIGrandStrategyTypes eSpaceshipGrandStrategy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_SPACESHIP"); // Loop through all (valid) Players for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer) && GET_PLAYER(eLoopPlayer).isMajorCiv()) { AIGrandStrategyTypes eTheirGrandStrategy = GetPlayer()->GetGrandStrategyAI()->GetGuessOtherPlayerActiveGrandStrategy(eLoopPlayer); if (eTheirGrandStrategy == NO_AIGRANDSTRATEGY || eTheirGrandStrategy == eMyGrandStrategy) { SetVictoryBlockLevel(eLoopPlayer, BLOCK_LEVEL_NONE); continue; } if (!IsAtWar(eLoopPlayer) && GetCivOpinion(eLoopPlayer) >= CIV_OPINION_FRIEND) { SetVictoryBlockLevel(eLoopPlayer, BLOCK_LEVEL_NONE); continue; } // Exclusions: Exclude anyone who isn't a competitor in this realm... int iVictoryBlockWeight = 0; if (eConquestGrandStrategy == eTheirGrandStrategy) { if (GetWarmongerThreat(eLoopPlayer) < THREAT_SEVERE && GetPlayerNumMajorsConquered(eLoopPlayer) >= GC.getGame().GetNumMajorCivsEver() / 3) { SetVictoryBlockLevel(eLoopPlayer, BLOCK_LEVEL_NONE); continue; } } if (eUNGrandStrategy == eTheirGrandStrategy) { CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (pLeague == NULL) { SetVictoryBlockLevel(eLoopPlayer, BLOCK_LEVEL_NONE); continue; } int iVotes = pLeague->CalculateStartingVotesForMember(eLoopPlayer, /*bFakeUN*/ true); int iNeededVotes = GC.getGame().GetVotesNeededForDiploVictory(); // 33% there? Close! if (iVotes < iNeededVotes / 3) { SetVictoryBlockLevel(eLoopPlayer, BLOCK_LEVEL_NONE); continue; } } if (eCultureGrandStrategy == eTheirGrandStrategy) { if (!IsWonderSpammer(eLoopPlayer) && GetPolicyBlockLevel(eLoopPlayer) < BLOCK_LEVEL_STRONG && GET_PLAYER(eLoopPlayer).GetCulture()->GetNumCivsInfluentialOn() > 1) { SetVictoryBlockLevel(eLoopPlayer, BLOCK_LEVEL_NONE); continue; } } if (eSpaceshipGrandStrategy == eTheirGrandStrategy) { bool bSpaceRace = false; int iProjectCount = GET_TEAM(GET_PLAYER(eLoopPlayer).getTeam()).GetSSProjectCount(); if (iProjectCount > 0) { bSpaceRace = true; iVictoryBlockWeight += iProjectCount * 10; } else if (GetTechBlockLevel(eLoopPlayer) >= BLOCK_LEVEL_STRONG) { bSpaceRace = true; } else { int iTheirTechNum = GET_TEAM(GET_PLAYER(eLoopPlayer).getTeam()).GetTeamTechs()->GetNumTechsKnown(); int iNumOtherPlayers = 0; int iNumPlayersAheadInTech = 0; for (int iOtherPlayerLoop = 0; iOtherPlayerLoop < MAX_MAJOR_CIVS; iOtherPlayerLoop++) { PlayerTypes eOtherPlayer = (PlayerTypes) iOtherPlayerLoop; if (GET_PLAYER(eOtherPlayer).getTeam() == GET_PLAYER(eLoopPlayer).getTeam()) continue; if (!IsPlayerValid(eOtherPlayer, true)) continue; iNumOtherPlayers++; int iNumTechs = GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).GetTeamTechs()->GetNumTechsKnown(); if (iTheirTechNum > iNumTechs) { iNumPlayersAheadInTech++; } } if (iNumPlayersAheadInTech >= iNumOtherPlayers / 2) { bSpaceRace = true; } } if (!bSpaceRace) { SetVictoryBlockLevel(eLoopPlayer, BLOCK_LEVEL_NONE); continue; } } switch (GetPlayer()->GetGrandStrategyAI()->GetGuessOtherPlayerActiveGrandStrategyConfidence(eLoopPlayer)) { case GUESS_CONFIDENCE_POSITIVE: iVictoryBlockWeight += /*20*/ GD_INT_GET(VICTORY_BLOCK_GRAND_STRATEGY_DIFFERENCE_POSITIVE); break; case GUESS_CONFIDENCE_LIKELY: iVictoryBlockWeight += /*15*/ GD_INT_GET(VICTORY_BLOCK_GRAND_STRATEGY_DIFFERENCE_LIKELY); break; case GUESS_CONFIDENCE_UNSURE: iVictoryBlockWeight += /*5*/ GD_INT_GET(VICTORY_BLOCK_GRAND_STRATEGY_DIFFERENCE_UNSURE); break; } BlockLevelTypes eBlockLevel = BLOCK_LEVEL_NONE; if (iVictoryBlockWeight > 0) { int DifficultyModifier = GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(eLoopPlayer).getHandicapInfo().getVictoryBlockMod() : GC.getGame().getHandicapInfo().getVictoryBlockMod(); // Add weight for Player's victory competitiveness, meanness and diplobalance desires (1 - 10) // Average of each is 5, and era goes up by one throughout game. iVictoryBlockWeight += GetVictoryCompetitiveness() + GetMeanness() + GetDiploBalance() + GC.getGame().getCurrentEra(); iVictoryBlockWeight += DifficultyModifier; // Now see what our new Block Level should be if (iVictoryBlockWeight >= /*40*/ GD_INT_GET(VICTORY_BLOCK_FIERCE_THRESHOLD)) eBlockLevel = BLOCK_LEVEL_FIERCE; else if (iVictoryBlockWeight >= /*30*/ GD_INT_GET(VICTORY_BLOCK_STRONG_THRESHOLD)) eBlockLevel = BLOCK_LEVEL_STRONG; else if (iVictoryBlockWeight >= /*20*/ GD_INT_GET(VICTORY_BLOCK_WEAK_THRESHOLD)) eBlockLevel = BLOCK_LEVEL_WEAK; } // Actually set the new level SetVictoryBlockLevel(eLoopPlayer, eBlockLevel); } else { SetVictoryBlockLevel(eLoopPlayer, BLOCK_LEVEL_NONE); } } } // ----------------------------------------------------------------------------------------------- // //////////////////////////////////// // MILITARY STUFF // //////////////////////////////////// /// Updates what the state of war is with all players void CvDiplomacyAI::DoUpdateWarStates() { // Reset overall war state int iStateAllWars = 0; // Used to assess overall war state in this function SetStateAllWars(STATE_ALL_WARS_NEUTRAL); ReligionTypes eMyReligion = GetPlayer()->GetReligions()->GetOwnedReligion(); int iLoop = 0; PlayerTypes eHighestWarWearinessPlayer = GetPlayer()->GetHighestWarWearinessPlayer(true); int iUnhappinessFromWarWeariness = eHighestWarWearinessPlayer != NO_PLAYER ? GetPlayer()->GetUnhappinessFromWarWearinessWithTeam(GET_PLAYER(eHighestWarWearinessPlayer).getTeam(), true) : 0; // Loop through all (known) Players for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // War? if (GET_PLAYER(eLoopPlayer).isAlive() && IsAtWar(eLoopPlayer)) { WarStateTypes eWarState = NO_WAR_STATE_TYPE; ReligionTypes eTheirReligion = GET_PLAYER(eLoopPlayer).isMinorCiv() ? NO_RELIGION : GET_PLAYER(eLoopPlayer).GetReligions()->GetOwnedReligion(); // Evaluate our danger and their danger from this war int iNumOurCities = 0; int iNumOurCitiesInDanger = 0; int iNumTheirCities = 0; int iNumTheirCitiesInDanger = 0; int iOurDanger = 0; int iTheirDanger = 0; bool bSeriousDangerUs = false; bool bSeriousDangerThem = false; vector vOurWarAllies = GetOffensiveWarAllies(eLoopPlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ false); vector vTheirWarAllies = GetOffensiveWarAllies(eLoopPlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ true); vOurWarAllies.push_back(GetID()); vTheirWarAllies.push_back(eLoopPlayer); for (CvCity* pLoopCity = m_pPlayer->firstCity(&iLoop); pLoopCity != NULL; pLoopCity = m_pPlayer->nextCity(&iLoop)) { iNumOurCities++; int iDangerMod = 0; //look at the tactical map (is it up to date?) CvTacticalDominanceZone* pLandZone = m_pPlayer->GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,false); CvTacticalDominanceZone* pWaterZone = m_pPlayer->GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,true); if (pLoopCity->IsInDangerFromPlayers(vTheirWarAllies)) { iNumOurCitiesInDanger++; iDangerMod++; if (pLandZone && pLandZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY) iDangerMod++; if (pWaterZone && pWaterZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY) iDangerMod++; if (pLoopCity->isUnderSiege()) { if (pLoopCity->isInDangerOfFalling(true)) iDangerMod += 3; else iDangerMod++; if (pLoopCity->isInDangerOfFalling(true) || pLoopCity->getDamage() >= (pLoopCity->GetMaxHitPoints()/2)) { if (pLoopCity->getOriginalOwner() == GetID()) { bSeriousDangerUs = true; } else if (pLoopCity->isCapital() || pLoopCity->IsOriginalMajorCapital() || (eMyReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eMyReligion)) || (eTheirReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eTheirReligion)) || pLoopCity->HasAnyWonder() || pLoopCity->getNumNationalWonders() > 0) { bSeriousDangerUs = true; } } } } else { if (pLandZone && pLandZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY) { iNumOurCitiesInDanger++; } else if (pWaterZone && pWaterZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY) { iNumOurCitiesInDanger++; } else if (pLoopCity->isUnderSiege()) { iNumOurCitiesInDanger++; } } if (iDangerMod > 0) { if (pLoopCity->isCapital()) iDangerMod *= 3; else if (pLoopCity->IsOriginalMajorCapital() || (eMyReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eMyReligion)) || (eTheirReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eTheirReligion)) || pLoopCity->HasAnyWonder()) iDangerMod *= 2; else if (pLoopCity->GetCityReligions()->IsHolyCityAnyReligion() || pLoopCity->getNumNationalWonders() > 0) iDangerMod++; } iOurDanger += iDangerMod; } for (CvCity* pLoopCity = GET_PLAYER(eLoopPlayer).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(eLoopPlayer).nextCity(&iLoop)) { iNumTheirCities++; int iDangerMod = 0; //look at the tactical map (is it up to date?) CvTacticalDominanceZone* pLandZone = GET_PLAYER(eLoopPlayer).GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,false); CvTacticalDominanceZone* pWaterZone = GET_PLAYER(eLoopPlayer).GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,true); if (pLoopCity->IsInDangerFromPlayers(vOurWarAllies)) { iNumTheirCitiesInDanger++; iDangerMod++; if (pLandZone && pLandZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY) iDangerMod++; if (pWaterZone && pWaterZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY) iDangerMod++; if (pLoopCity->isInDangerOfFalling(true) || pLoopCity->isUnderSiege()) { if (pLoopCity->isInDangerOfFalling(true)) iDangerMod += 3; else iDangerMod++; if (pLoopCity->isInDangerOfFalling(true) || pLoopCity->getDamage() >= (pLoopCity->GetMaxHitPoints()/2)) { bSeriousDangerThem = true; } } } else if (pLoopCity->isRevealed(GetTeam(), false, true)) { if (pLandZone && pLandZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY) { iNumTheirCitiesInDanger++; } else if (pWaterZone && pWaterZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY) { iNumTheirCitiesInDanger++; } else if (pLoopCity->isUnderSiege()) { iNumTheirCitiesInDanger++; } } if (iDangerMod > 0) { if (pLoopCity->isCapital()) iDangerMod *= 3; else if (pLoopCity->IsOriginalMajorCapital() || (eMyReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eMyReligion)) || (eTheirReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eTheirReligion)) || pLoopCity->HasAnyWonder()) iDangerMod *= 2; else if (pLoopCity->GetCityReligions()->IsHolyCityAnyReligion()) iDangerMod++; } iTheirDanger += iDangerMod; } if (iNumOurCities == 0) bSeriousDangerUs = true; if (iNumTheirCities == 0) bSeriousDangerThem = true; int WarScore = GetWarScore(eLoopPlayer); bool bWeAreLosing = WarScore <= -75; bool bTheyAreLosing = WarScore >= 75; if (iUnhappinessFromWarWeariness > 0 && GET_PLAYER(eHighestWarWearinessPlayer).getTeam() == GET_PLAYER(eLoopPlayer).getTeam() && GetPlayer()->IsEmpireVeryUnhappy()) { if (WarScore < 0) { WarScore *= 2; } else { WarScore *= 75; WarScore /= 100; } } // First consider serious danger - overrides danger percent comparison! if (bSeriousDangerUs && !bSeriousDangerThem) { eWarState = WAR_STATE_DEFENSIVE; } else if (!bSeriousDangerUs && bSeriousDangerThem) { if (WarScore > GetWarscoreThresholdPositive()) eWarState = WAR_STATE_OFFENSIVE; else eWarState = WAR_STATE_STALEMATE; } else if (bSeriousDangerUs && bSeriousDangerThem) { if (WarScore > GetWarscoreThresholdNegative() && WarScore < GetWarscoreThresholdPositive()) eWarState = WAR_STATE_STALEMATE; } // If no override from serious danger and raw score is high/low enough (+/- 75), also ignore danger percent if (eWarState == NO_WAR_STATE_TYPE) { if (bWeAreLosing) eWarState = WAR_STATE_DEFENSIVE; else if (bTheyAreLosing) eWarState = WAR_STATE_OFFENSIVE; } // Which of us has more city danger? Also consider the war score! if (eWarState == NO_WAR_STATE_TYPE) { // Adjust danger levels based on # of safe cities compared to the other player. int iNumOurSafeCities = iNumOurCities - iNumOurCitiesInDanger; int iNumTheirSafeCities = iNumTheirCities - iNumTheirCitiesInDanger; if (iNumOurSafeCities > iNumTheirSafeCities) { int iPercentage = (max(iNumTheirSafeCities, 1) * 100) / iNumOurSafeCities; iOurDanger *= max(iPercentage, 34); iOurDanger /= 100; } else if (iNumTheirSafeCities > iNumOurSafeCities) { int iPercentage = (max(iNumOurSafeCities, 1) * 100) / iNumTheirSafeCities; iTheirDanger *= max(iPercentage, 34); iTheirDanger /= 100; } bool bDangerValid = iNumOurCities > 0 && iNumTheirCities > 0 && (iTheirDanger > 0 || iOurDanger > 0); int iDangerPercent = (iTheirDanger * 100) / max(iOurDanger, 1); // Assessment time! if (iDangerPercent < 100 && bDangerValid) { if (WarScore >= GetWarscoreThresholdPositive()) eWarState = WAR_STATE_STALEMATE; else if (WarScore > 0) eWarState = WAR_STATE_TROUBLED; else eWarState = WAR_STATE_DEFENSIVE; } else if (iDangerPercent > 100 && bDangerValid) { if (WarScore <= GetWarscoreThresholdNegative()) eWarState = WAR_STATE_TROUBLED; else if (WarScore < 0) eWarState = WAR_STATE_STALEMATE; else eWarState = WAR_STATE_OFFENSIVE; } else { bool bWeAreNomadic = iNumOurCities == 0 && iNumTheirCities > 0; bool bTheyAreNomadic = iNumTheirCities == 0 && iNumOurCities > 0; if (WarScore >= GetWarscoreThresholdPositive()) eWarState = bWeAreNomadic ? WAR_STATE_STALEMATE : WAR_STATE_OFFENSIVE; else if (WarScore <= GetWarscoreThresholdNegative() || bWeAreNomadic) eWarState = bWeAreNomadic ? WAR_STATE_DEFENSIVE : WAR_STATE_TROUBLED; else eWarState = bTheyAreNomadic ? WAR_STATE_OFFENSIVE : WAR_STATE_STALEMATE; } } // If winning and war score high enough, or losing and war score low enough, the war state intensifies if (WarScore >= GetWarscoreThresholdPositive()*2 && eWarState == WAR_STATE_OFFENSIVE) { eWarState = WAR_STATE_NEARLY_WON; } else if (WarScore <= GetWarscoreThresholdNegative()*2 && eWarState == WAR_STATE_DEFENSIVE) { eWarState = WAR_STATE_NEARLY_DEFEATED; } //Exceptions? //If low warscore, no serious danger, and it has been a while since either side captured a city, let's bring it down to calm. if (!bSeriousDangerThem && !bSeriousDangerUs && WarScore <= GetWarscoreThresholdPositive() && WarScore >= GetWarscoreThresholdNegative()) { if (GetPlayer()->GetNumTurnsSinceCityCapture(eLoopPlayer) >= 10 && GET_PLAYER(eLoopPlayer).GetNumTurnsSinceCityCapture(GetID()) >= 10) eWarState = WAR_STATE_CALM; } // If this is a major power, determine what the impact of this war is on our global situation if (GET_PLAYER(eLoopPlayer).isMajorCiv()) { if (eWarState == WAR_STATE_NEARLY_WON) { iStateAllWars += 4; } else if (eWarState == WAR_STATE_OFFENSIVE) { iStateAllWars += (bSeriousDangerThem && !bSeriousDangerUs) ? 4 : 2; } else if (eWarState == WAR_STATE_TROUBLED) { iStateAllWars -= bSeriousDangerUs ? 2 : 1; } else if (eWarState == WAR_STATE_DEFENSIVE) { iStateAllWars -= bSeriousDangerUs ? 4 : 2; // If we are defensive in any war and we don't have a capital, or our capital has been damaged to 75% or lower, overall state should be defensive CvCity *pCapital = m_pPlayer->getCapitalCity(); if (!pCapital || pCapital->getDamage() >= (pCapital->GetMaxHitPoints()/4)) { SetStateAllWars(STATE_ALL_WARS_LOSING); } } // If nearly defeated in any war, overall state should be defensive else if (eWarState == WAR_STATE_NEARLY_DEFEATED) { SetStateAllWars(STATE_ALL_WARS_LOSING); } } SetWarState(eLoopPlayer, eWarState); } // Not at war else { SetWarState(eLoopPlayer, NO_WAR_STATE_TYPE); } } // Finalize overall assessment if (iStateAllWars < -2 || GetStateAllWars() == STATE_ALL_WARS_LOSING) { SetStateAllWars(STATE_ALL_WARS_LOSING); } else if (iStateAllWars > 2) { SetStateAllWars(STATE_ALL_WARS_WINNING); } } /// What is the integer value of how well we think the war with ePlayer is going? int CvDiplomacyAI::GetWarScore(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_CIV_PLAYERS) return 0; if (!IsAtWar(ePlayer)) return 0; int iWarDamageWeInflicted = GET_PLAYER(ePlayer).GetWarDamageValue(GetID()); int iWarDamageTheyInflicted = GetPlayer()->GetWarDamageValue(ePlayer); int iWarScore = (iWarDamageWeInflicted - iWarDamageTheyInflicted) * 2; return range(iWarScore, -100, 100); } int CvDiplomacyAI::GetHighestWarscore() { int iHighestWarscore = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer)) { int iWarscore = GetWarScore(eLoopPlayer); if (iWarscore > iHighestWarscore) { iHighestWarscore = iWarscore; } } } iHighestWarscore *= GetPlayer()->GetPositiveWarScoreTourismMod(); iHighestWarscore /= 100; return iHighestWarscore; } PlayerTypes CvDiplomacyAI::GetHighestWarscorePlayer() { int iHighestWarscore = 0; PlayerTypes eBestPlayer = NO_PLAYER; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer)) { int iWarscore = GetWarScore(eLoopPlayer); if (iWarscore >= iHighestWarscore) { if (iWarscore == 100 && iHighestWarscore == 100 && eBestPlayer != NO_PLAYER) { // At 100 with multiple civs? if (IsCapitalCapturedBy(eLoopPlayer, true, false)) { eBestPlayer = eLoopPlayer; continue; } else if (IsHolyCityCapturedBy(eLoopPlayer, true, false) && !IsCapitalCapturedBy(eBestPlayer, true, false)) { eBestPlayer = eLoopPlayer; continue; } else if (IsEndgameAggressiveTo(eLoopPlayer) && !IsEndgameAggressiveTo(eBestPlayer) && !IsCapitalCapturedBy(eBestPlayer, true, false) && !IsHolyCityCapturedBy(eBestPlayer, true, false)) { eBestPlayer = eLoopPlayer; continue; } if (IsCapitalCapturedBy(eBestPlayer, true, false) || IsHolyCityCapturedBy(eBestPlayer, true, false) || (IsEndgameAggressiveTo(eBestPlayer) && !IsEndgameAggressiveTo(eLoopPlayer))) continue; // The weakest civ is the one we are most willing to stay at war with. if (GetRawTargetValue(eLoopPlayer) > GetRawTargetValue(eBestPlayer)) { eBestPlayer = eLoopPlayer; } else if (GetRawTargetValue(eLoopPlayer) == GetRawTargetValue(eBestPlayer)) { if (GetRawMilitaryStrengthComparedToUs(eLoopPlayer) < GetRawMilitaryStrengthComparedToUs(eBestPlayer)) { eBestPlayer = eLoopPlayer; } } } else if (iWarscore > iHighestWarscore) { eBestPlayer = eLoopPlayer; iHighestWarscore = iWarscore; } } } } return eBestPlayer; } /// Updates what our assessment is of all players' overall Economic Strength, Military Strength (Attack Power) and Target Value (Defense Power) void CvDiplomacyAI::DoUpdatePlayerStrengthEstimates() { int iEconomicStrength = GetPlayer()->getNumCities() > 0 ? max(GetPlayer()->GetEconomicMight(), 1) : 1; int iBase = /*30*/ GD_INT_GET(MILITARY_STRENGTH_BASE); int iMilitaryStrength = max(iBase + GetPlayer()->GetMilitaryMight() - GetPlayer()->GetNuclearMight(), 1); // Remove strength from nukes, added later int iOurCityDefense = 0; int iOurBombShelterPercent = 0; int iOurAvgInterceptionChance = 0; int iOurAvgNukeModifier = 0; if (GetPlayer()->getNumCities() > 0) { int iNumOurCities = 0; int iNumOurShelteredCities = 0; int iLoop = 0; for (CvCity* pLoopCity = GetPlayer()->firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GetPlayer()->nextCity(&iLoop)) { // Add city defensive strength to us int iHitpoints = pLoopCity->GetMaxHitPoints() - pLoopCity->getDamage(); int iPower = (pLoopCity->GetPower() * iHitpoints) / pLoopCity->GetMaxHitPoints(); iOurCityDefense += (iPower * /*33*/ GD_INT_GET(MILITARY_STRENGTH_CITY_MOD)) / 100; // Factor in how well our cities are protected against nuclear attacks iNumOurCities += pLoopCity->isCapital() ? 2 : 1; // Capital is 2x as important int iInterceptionChance = pLoopCity->getNukeInterceptionChance(); int iNukeModifier = pLoopCity->getNukeModifier() * -1; if (iNukeModifier > 0 || iInterceptionChance > 0) { iNumOurShelteredCities += pLoopCity->isCapital() ? 2 : 1; // Capital is 2x as important iOurAvgNukeModifier += iNukeModifier; iOurAvgInterceptionChance += iInterceptionChance; } } iOurAvgNukeModifier /= max(iNumOurShelteredCities, 1); iOurAvgInterceptionChance /= max(iNumOurShelteredCities, 1); iOurBombShelterPercent = iNumOurShelteredCities * 100 / max(iNumOurCities, 1); } PlayerTypes eOurPlayer = GetID(); TeamTypes eOurTeam = GetTeam(); vector vOurTeam = GET_TEAM(eOurTeam).getPlayers(); vector vValidPlayers; for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; if (ePlayer == GetID() || !GET_PLAYER(ePlayer).isAlive() || !IsHasMet(ePlayer, true)) continue; vValidPlayers.push_back(ePlayer); // First we update economic strength, that's easy int iTheirEconomicStrength = GET_PLAYER(ePlayer).getNumCities() > 0 ? max(GET_PLAYER(ePlayer).GetEconomicMight(), 1) : 1; int iEconomicRatio = iTheirEconomicStrength * /*100*/ GD_INT_GET(ECONOMIC_STRENGTH_RATIO_MULTIPLIER) / iEconomicStrength; // Now do the final assessment StrengthTypes eEconomicStrength = STRENGTH_PATHETIC; if (iEconomicRatio >= /*300*/ GD_INT_GET(ECONOMIC_STRENGTH_IMMENSE_THRESHOLD)) eEconomicStrength = STRENGTH_IMMENSE; else if (iEconomicRatio >= /*200*/ GD_INT_GET(ECONOMIC_STRENGTH_POWERFUL_THRESHOLD)) eEconomicStrength = STRENGTH_POWERFUL; else if (iEconomicRatio >= /*126*/ GD_INT_GET(ECONOMIC_STRENGTH_STRONG_THRESHOLD)) eEconomicStrength = STRENGTH_STRONG; else if (iEconomicRatio >= /*75*/ GD_INT_GET(ECONOMIC_STRENGTH_AVERAGE_THRESHOLD)) eEconomicStrength = STRENGTH_AVERAGE; else if (iEconomicRatio >= /*50*/ GD_INT_GET(ECONOMIC_STRENGTH_POOR_THRESHOLD)) eEconomicStrength = STRENGTH_POOR; else if (iEconomicRatio >= /*33*/ GD_INT_GET(ECONOMIC_STRENGTH_WEAK_THRESHOLD)) eEconomicStrength = STRENGTH_WEAK; // Set the value SetEconomicStrengthComparedToUs(ePlayer, eEconomicStrength); int iOurMilitaryStrength = iMilitaryStrength; int iTheirMilitaryStrength = max(iBase + GET_PLAYER(ePlayer).GetMilitaryMight() - GET_PLAYER(ePlayer).GetNuclearMight(), 1); // Remove strength from nukes, added later // One of us has no cities and the other doesn't? Don't bother with all of this then. if (GetPlayer()->getNumCities() <= 0 && GET_PLAYER(ePlayer).getNumCities() > 0 && iOurMilitaryStrength < iTheirMilitaryStrength * 10 && GetPlayer()->getNumNukeUnits() == 0) { SetMilitaryStrengthComparedToUs(ePlayer, STRENGTH_IMMENSE); SetRawMilitaryStrengthComparedToUs(ePlayer, STRENGTH_IMMENSE); SetTargetValue(ePlayer, TARGET_VALUE_IMPOSSIBLE); SetRawTargetValue(ePlayer, TARGET_VALUE_IMPOSSIBLE); continue; } else if (GET_PLAYER(ePlayer).getNumCities() <= 0 && GetPlayer()->getNumCities() > 0 && iTheirMilitaryStrength < iOurMilitaryStrength * 10 && GET_PLAYER(ePlayer).getNumNukeUnits() == 0) { SetMilitaryStrengthComparedToUs(ePlayer, STRENGTH_PATHETIC); SetRawMilitaryStrengthComparedToUs(ePlayer, STRENGTH_PATHETIC); SetTargetValue(ePlayer, TARGET_VALUE_CAKEWALK); SetRawTargetValue(ePlayer, TARGET_VALUE_CAKEWALK); continue; } // Also handle edge cases with nukes and no cities else if (GET_PLAYER(ePlayer).getNumNukeUnits() > 0 && GetPlayer()->getNumCities() <= 0 && (GET_PLAYER(ePlayer).getNumCities() > 0 || GetPlayer()->getNumNukeUnits() == 0)) { SetMilitaryStrengthComparedToUs(ePlayer, STRENGTH_IMMENSE); SetRawMilitaryStrengthComparedToUs(ePlayer, STRENGTH_IMMENSE); SetTargetValue(ePlayer, TARGET_VALUE_IMPOSSIBLE); SetRawTargetValue(ePlayer, TARGET_VALUE_IMPOSSIBLE); continue; } else if (GetPlayer()->getNumNukeUnits() > 0 && GET_PLAYER(ePlayer).getNumCities() <= 0 && (GetPlayer()->getNumCities() > 0 || GET_PLAYER(ePlayer).getNumNukeUnits() == 0)) { SetMilitaryStrengthComparedToUs(ePlayer, STRENGTH_PATHETIC); SetRawMilitaryStrengthComparedToUs(ePlayer, STRENGTH_PATHETIC); SetTargetValue(ePlayer, TARGET_VALUE_CAKEWALK); SetRawTargetValue(ePlayer, TARGET_VALUE_CAKEWALK); continue; } // Factor in dynamic combat modifiers iOurMilitaryStrength *= 100 + ComputeDynamicStrengthModifier(eOurPlayer, ePlayer); iOurMilitaryStrength /= 100; iTheirMilitaryStrength *= 100 + ComputeDynamicStrengthModifier(ePlayer, eOurPlayer); iTheirMilitaryStrength /= 100; // Factor in military rating (combat skill) int iOurRatingModifier = GC.getGame().ComputeRatingStrengthAdjustment(eOurPlayer, eOurPlayer); int iTheirRatingModifier = 100; iOurMilitaryStrength *= iOurRatingModifier; iOurMilitaryStrength /= 100; // If we're an AI evaluating a human, modify their strength estimate based on difficulty level if they're reasonably strong if (!GetPlayer()->isHuman(ISHUMAN_HANDICAP) && GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) && (GET_PLAYER(ePlayer).getNumNukeUnits() > 0 || (!GET_PLAYER(ePlayer).IsVassalOfSomeone() && !GET_PLAYER(ePlayer).IsInTerribleShapeForWar() && GetWarState(ePlayer) < WAR_STATE_OFFENSIVE))) { int iStartingRating = GC.getGame().GetStartingMilitaryRating(); int iMinimumHumanRating = GC.getGame().GetMinimumHumanMilitaryRating(); int iCurrentRating = GET_PLAYER(ePlayer).GetMilitaryRating(); int iHumanStrengthMod = max(0, GET_PLAYER(ePlayer).getHandicapInfo().getHumanStrengthPerceptionMod()); int iSkillRatingMod = GC.getGame().ComputeRatingStrengthAdjustment(ePlayer, eOurPlayer); // If they're above the starting rating or are performing better than average, apply the highest of the human perception mod and the skill rating mod if (iSkillRatingMod >= 100 || iCurrentRating >= iStartingRating || iStartingRating == 0) { iTheirRatingModifier = 100 + max(iHumanStrengthMod, iSkillRatingMod - 100); } // If the human hasn't lost 20% or more of the starting rating, they still get some of the strength perception bonus else if (iCurrentRating >= iMinimumHumanRating && GD_INT_GET(MILITARY_RATING_HUMAN_BUFFER_VALUE_PERCENT) < 100) { int iMaxPercentageLost = 100 - /*80*/ max(GD_INT_GET(MILITARY_RATING_HUMAN_BUFFER_VALUE_PERCENT), 0); int iPercentageLost = (iCurrentRating * 100 - iStartingRating * 100) / -iStartingRating; int iDifference = (100 + iHumanStrengthMod) - iSkillRatingMod; // Do not exceed human strength mod! // example 1: if human strength mod is 10, skill rating mod is 80, and percentage lost is 10 (modifier of 95%), that'd be a 15% bonus; we cap the modifier to 90% (10% bonus) to avoid exceeding the XML value. // example 2: if human strength mod is 30, skill rating mod is 80, and percentage lost is 10 (modifier of 105%), that'd be a 25% bonus, which is less than 30, so that's OK. int iFinalModifier = 100 + iHumanStrengthMod - (iDifference * iPercentageLost / iMaxPercentageLost); if (iFinalModifier - iSkillRatingMod > iHumanStrengthMod) iFinalModifier = iSkillRatingMod + iHumanStrengthMod; iTheirRatingModifier = iFinalModifier; } else { iTheirRatingModifier = iSkillRatingMod; } } else if (GET_PLAYER(ePlayer).isMajorCiv()) { iTheirRatingModifier = GC.getGame().ComputeRatingStrengthAdjustment(ePlayer, eOurPlayer); } iTheirMilitaryStrength *= iTheirRatingModifier; iTheirMilitaryStrength /= 100; // Factor in nuclear power int iTheirCityDefense = 0; int iTheirBombShelterPercent = 0; int iTheirAvgInterceptionChance = 0; int iTheirAvgNukeModifier = 0; if (GET_PLAYER(ePlayer).getNumCities() > 0) { int iNumTheirCities = 0; int iNumTheirShelteredCities = 0; int iLoop = 0; for (CvCity* pLoopCity = GET_PLAYER(ePlayer).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iLoop)) { // Add city defensive strength to them int iHitpoints = pLoopCity->GetMaxHitPoints() - pLoopCity->getDamage(); int iPower = (pLoopCity->GetPower() * iHitpoints) / pLoopCity->GetMaxHitPoints(); iTheirCityDefense += (iPower * /*33*/ GD_INT_GET(MILITARY_STRENGTH_CITY_MOD)) / 100; // Factor in how well their cities are protected against nuclear attacks iNumTheirCities += pLoopCity->isCapital() ? 2 : 1; // Capital is 2x as important int iInterceptionChance = pLoopCity->getNukeInterceptionChance(); int iNukeModifier = pLoopCity->getNukeModifier() * -1; if (iNukeModifier > 0 || iInterceptionChance > 0) { iNumTheirShelteredCities += pLoopCity->isCapital() ? 2 : 1; // Capital is 2x as important iTheirAvgNukeModifier += iNukeModifier; iTheirAvgInterceptionChance += iInterceptionChance; } } iTheirAvgNukeModifier /= max(iNumTheirShelteredCities, 1); iTheirAvgInterceptionChance /= max(iNumTheirShelteredCities, 1); iTheirBombShelterPercent = iNumTheirShelteredCities * 100 / max(iNumTheirCities, 1); } iOurMilitaryStrength += GetPlayer()->calculateNuclearMight(ePlayer, false, iTheirBombShelterPercent, iTheirAvgNukeModifier, iTheirAvgInterceptionChance); iTheirMilitaryStrength += GET_PLAYER(ePlayer).calculateNuclearMight(eOurPlayer, false, iOurBombShelterPercent, iOurAvgNukeModifier, iOurAvgInterceptionChance); // Factor in war allies // If this is a City-State, we instead factor in the allies of their ally (if any) vector vOurAllies; vector vOurDefensiveAllies = GetDefensiveWarAllies(ePlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ false, /*bNewWarsOnly*/ false); vector vOurOffensiveAllies = GetOffensiveWarAllies(ePlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ false); vector vTheirAllies; vector vTheirDefensiveAllies; vector vTheirOffensiveAllies; PlayerTypes eEvaluate = ePlayer; if (GET_PLAYER(ePlayer).isMinorCiv()) { eEvaluate = GET_PLAYER(ePlayer).GetMinorCivAI()->GetAlly(); if (eEvaluate != NO_PLAYER) { // Vassals can't declare war and the master usually won't care if the vassal's City-State ally is attacked. if (GET_PLAYER(eEvaluate).IsVassalOfSomeone() && !IsAtWar(eEvaluate)) eEvaluate = NO_PLAYER; } } if (eEvaluate != NO_PLAYER) { vTheirDefensiveAllies = GetDefensiveWarAllies(eEvaluate, /*bIncludeMinors*/ true, /*bReverseMode*/ true, /*bNewWarsOnly*/ false); // City-States won't be declaring war on us of their own initiative, and even if Permanent War is enabled, they won't bring in allies. if (GET_PLAYER(ePlayer).isMajorCiv()) { vTheirOffensiveAllies = GetOffensiveWarAllies(eEvaluate, /*bIncludeMinors*/ true, /*bReverseMode*/ true); } // But do include their ally in the list, since GetDefensiveWarAllies() won't include it // And exclude the City-State player we're already looking at! else if (GET_PLAYER(ePlayer).isMinorCiv()) { vTheirDefensiveAllies.push_back(eEvaluate); vTheirDefensiveAllies.erase(std::remove(vTheirDefensiveAllies.begin(), vTheirDefensiveAllies.end(), ePlayer), vTheirDefensiveAllies.end()); } } // Go through the lists of allies and see which of them are relevant for ATTACK and DEFENSE evaluations for (std::vector::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); ++it) { PlayerTypes eLoopPlayer = *it; if (eLoopPlayer == ePlayer) continue; // If this player is doing terribly, assume they won't play a role in any conflicts if (GET_PLAYER(eLoopPlayer).IsInTerribleShapeForWar()) continue; // Assume they can't provide any backup if they're far away and can't cross the ocean. bool bCanCrossOcean = GET_PLAYER(eLoopPlayer).isMajorCiv() && GET_PLAYER(eLoopPlayer).CanCrossOcean(); bool bCanMoveToUs = bCanCrossOcean || GET_PLAYER(eLoopPlayer).GetProximityToPlayer(eOurPlayer) >= PLAYER_PROXIMITY_CLOSE; bool bCanMoveToThem = bCanCrossOcean || GET_PLAYER(eLoopPlayer).GetProximityToPlayer(ePlayer) >= PLAYER_PROXIMITY_CLOSE; // City-States must be neighbors to be a factor in any war if (GET_PLAYER(eLoopPlayer).isMinorCiv()) { bCanMoveToUs = GET_PLAYER(eLoopPlayer).GetProximityToPlayer(eOurPlayer) == PLAYER_PROXIMITY_NEIGHBORS; bCanMoveToThem = GET_PLAYER(eLoopPlayer).GetProximityToPlayer(ePlayer) == PLAYER_PROXIMITY_NEIGHBORS; } if (bCanMoveToUs && std::find(vOurDefensiveAllies.begin(), vOurDefensiveAllies.end(), eLoopPlayer) != vOurDefensiveAllies.end()) vOurAllies.push_back(eLoopPlayer); else if (bCanMoveToThem && std::find(vOurOffensiveAllies.begin(), vOurOffensiveAllies.end(), eLoopPlayer) != vOurOffensiveAllies.end()) vOurAllies.push_back(eLoopPlayer); if (bCanMoveToThem && std::find(vTheirDefensiveAllies.begin(), vTheirDefensiveAllies.end(), eLoopPlayer) != vTheirDefensiveAllies.end()) vTheirAllies.push_back(eLoopPlayer); else if (bCanMoveToUs && std::find(vTheirOffensiveAllies.begin(), vTheirOffensiveAllies.end(), eLoopPlayer) != vTheirOffensiveAllies.end()) vTheirAllies.push_back(eLoopPlayer); } int iAllyAttackBonus = 0; int iAllyDefenseBonus = 0; int iEnemyAttackBonus = 0; int iEnemyDefenseBonus = 0; // Our allies for (std::vector::iterator it = vOurAllies.begin(); it != vOurAllies.end(); ++it) { PlayerTypes eAllyPlayer = *it; int iAllyMight = max(iBase + GET_PLAYER(eAllyPlayer).GetMilitaryMight() - GET_PLAYER(eAllyPlayer).GetNuclearMight(), 1); int iTheirMight = max(iBase + GET_PLAYER(ePlayer).GetMilitaryMight() - GET_PLAYER(ePlayer).GetNuclearMight(), 1); // Factor in dynamic combat modifiers iAllyMight *= 100 + ComputeDynamicStrengthModifier(eAllyPlayer, ePlayer); iAllyMight /= 100; int iTheirDynamicModifier = ComputeDynamicStrengthModifier(ePlayer, eAllyPlayer); iTheirMight *= 100 + iTheirDynamicModifier; iTheirMight /= 100; // Factor in military rating (combat skill) // If we're an AI evaluating a human, modify the ally's strength estimate based on difficulty level if they're reasonably strong if (!GetPlayer()->isHuman(ISHUMAN_HANDICAP) && GET_PLAYER(eAllyPlayer).isHuman(ISHUMAN_HANDICAP) && (GET_PLAYER(eAllyPlayer).getNumNukeUnits() > 0 || (!GET_PLAYER(eAllyPlayer).IsVassalOfSomeone() && GET_PLAYER(ePlayer).GetDiplomacyAI()->GetWarState(eAllyPlayer) < WAR_STATE_OFFENSIVE))) { int iStartingRating = GC.getGame().GetStartingMilitaryRating(); int iMinimumHumanRating = GC.getGame().GetMinimumHumanMilitaryRating(); int iCurrentRating = GET_PLAYER(eAllyPlayer).GetMilitaryRating(); int iHumanStrengthMod = max(0, GET_PLAYER(eAllyPlayer).getHandicapInfo().getHumanStrengthPerceptionMod()); int iSkillRatingMod = GC.getGame().ComputeRatingStrengthAdjustment(eAllyPlayer, eOurPlayer); // If they're above the starting rating or are performing better than average, apply the highest of the human perception mod and the skill rating mod if (iSkillRatingMod >= 100 || iCurrentRating >= iStartingRating || iStartingRating == 0) { iAllyMight *= 100 + max(iHumanStrengthMod, iSkillRatingMod - 100); iAllyMight /= 100; } // If the human hasn't lost 20% or more of the starting rating, they still get some of the strength perception bonus else if (iCurrentRating >= iMinimumHumanRating && GD_INT_GET(MILITARY_RATING_HUMAN_BUFFER_VALUE_PERCENT) < 100) { int iMaxPercentageLost = 100 - /*80*/ max(GD_INT_GET(MILITARY_RATING_HUMAN_BUFFER_VALUE_PERCENT), 0); int iPercentageLost = (iCurrentRating * 100 - iStartingRating * 100) / -iStartingRating; int iDifference = (100 + iHumanStrengthMod) - iSkillRatingMod; // Do not exceed human strength mod! // example 1: if human strength mod is 10, skill rating mod is 80, and percentage lost is 10 (modifier of 95%), that'd be a 15% bonus; we cap the modifier to 90% (10% bonus) to avoid exceeding the XML value. // example 2: if human strength mod is 30, skill rating mod is 80, and percentage lost is 10 (modifier of 105%), that'd be a 25% bonus, which is less than 30, so that's OK. int iFinalModifier = 100 + iHumanStrengthMod - (iDifference * iPercentageLost / iMaxPercentageLost); if (iFinalModifier - iSkillRatingMod > iHumanStrengthMod) iFinalModifier = iSkillRatingMod + iHumanStrengthMod; iAllyMight *= iFinalModifier; iAllyMight /= 100; } else { iAllyMight *= iSkillRatingMod; iAllyMight /= 100; } } else if (GET_PLAYER(eAllyPlayer).isMajorCiv()) { iAllyMight *= GC.getGame().ComputeRatingStrengthAdjustment(eAllyPlayer, eOurPlayer); iAllyMight /= 100; } iTheirMight *= iTheirRatingModifier; iTheirMight /= 100; // Factor in nuclear power iAllyMight += GET_PLAYER(eAllyPlayer).calculateNuclearMight(ePlayer, false, iTheirBombShelterPercent, iTheirAvgNukeModifier, iTheirAvgInterceptionChance); iTheirMight += GET_PLAYER(ePlayer).calculateNuclearMight(eAllyPlayer, true); // Careful in the early game when the number of units is very low ... if (GET_PLAYER(eAllyPlayer).getNumMilitaryUnits() < 6 && GET_PLAYER(ePlayer).getNumMilitaryUnits() < 6 && iAllyMight < iTheirMight * 10 && iTheirMight < iAllyMight * 10 && GET_PLAYER(ePlayer).getNumNukeUnits() == 0 && GET_PLAYER(eAllyPlayer).getNumNukeUnits() == 0) { iAllyMight = iTheirMight; } // How much does this ally contribute to our ATTACK and DEFENSE? int iMilitaryRatio = iAllyMight * /*100*/ GD_INT_GET(MILITARY_STRENGTH_RATIO_MULTIPLIER) / max(iTheirMight, 1); StrengthTypes eMilitaryStrength = STRENGTH_PATHETIC; if (iMilitaryRatio >= /*300*/ GD_INT_GET(MILITARY_STRENGTH_IMMENSE_THRESHOLD)) eMilitaryStrength = STRENGTH_IMMENSE; else if (iMilitaryRatio >= /*200*/ GD_INT_GET(MILITARY_STRENGTH_POWERFUL_THRESHOLD)) eMilitaryStrength = STRENGTH_POWERFUL; else if (iMilitaryRatio >= /*126*/ GD_INT_GET(MILITARY_STRENGTH_STRONG_THRESHOLD)) eMilitaryStrength = STRENGTH_STRONG; else if (iMilitaryRatio >= /*75*/ GD_INT_GET(MILITARY_STRENGTH_AVERAGE_THRESHOLD)) eMilitaryStrength = STRENGTH_AVERAGE; else if (iMilitaryRatio >= /*50*/ GD_INT_GET(MILITARY_STRENGTH_POOR_THRESHOLD)) eMilitaryStrength = STRENGTH_POOR; else if (iMilitaryRatio >= /*33*/ GD_INT_GET(MILITARY_STRENGTH_WEAK_THRESHOLD)) eMilitaryStrength = STRENGTH_WEAK; int iThirdPartyAttackValue = std::find(vOurOffensiveAllies.begin(), vOurOffensiveAllies.end(), eAllyPlayer) != vOurOffensiveAllies.end() ? iAllyMight : 0; int iThirdPartyDefenseValue = std::find(vOurDefensiveAllies.begin(), vOurDefensiveAllies.end(), eAllyPlayer) != vOurDefensiveAllies.end() ? iAllyMight : 0; if (GET_PLAYER(eAllyPlayer).isMinorCiv()) { switch (eMilitaryStrength) { case STRENGTH_IMMENSE: iThirdPartyAttackValue *= /*50*/ GD_INT_GET(MILITARY_STRENGTH_MINOR_BACKUP_IMMENSE); iThirdPartyDefenseValue *= /*50*/ GD_INT_GET(TARGET_MINOR_BACKUP_IMMENSE); break; case STRENGTH_POWERFUL: iThirdPartyAttackValue *= /*35*/ GD_INT_GET(MILITARY_STRENGTH_MINOR_BACKUP_POWERFUL); iThirdPartyDefenseValue *= /*35*/ GD_INT_GET(TARGET_MINOR_BACKUP_POWERFUL); break; case STRENGTH_STRONG: iThirdPartyAttackValue *= /*25*/ GD_INT_GET(MILITARY_STRENGTH_MINOR_BACKUP_STRONG); iThirdPartyDefenseValue *= /*25*/ GD_INT_GET(TARGET_MINOR_BACKUP_STRONG); break; case STRENGTH_AVERAGE: iThirdPartyAttackValue *= /*15*/ GD_INT_GET(MILITARY_STRENGTH_MINOR_BACKUP_AVERAGE); iThirdPartyDefenseValue *= /*15*/ GD_INT_GET(TARGET_MINOR_BACKUP_AVERAGE); break; case STRENGTH_POOR: iThirdPartyAttackValue *= /*0*/ GD_INT_GET(MILITARY_STRENGTH_MINOR_BACKUP_POOR); iThirdPartyDefenseValue *= /*0*/ GD_INT_GET(TARGET_MINOR_BACKUP_POOR); break; case STRENGTH_WEAK: iThirdPartyAttackValue *= /*0*/ GD_INT_GET(MILITARY_STRENGTH_MINOR_BACKUP_WEAK); iThirdPartyDefenseValue *= /*0*/ GD_INT_GET(TARGET_MINOR_BACKUP_WEAK); break; case STRENGTH_PATHETIC: iThirdPartyAttackValue *= /*0*/ GD_INT_GET(MILITARY_STRENGTH_MINOR_BACKUP_PATHETIC); iThirdPartyDefenseValue *= /*0*/ GD_INT_GET(TARGET_MINOR_BACKUP_PATHETIC); break; default: UNREACHABLE(); } } else { switch (eMilitaryStrength) { case STRENGTH_IMMENSE: iThirdPartyAttackValue *= /*50*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_IMMENSE); iThirdPartyDefenseValue *= /*50*/ GD_INT_GET(TARGET_MAJOR_BACKUP_IMMENSE); break; case STRENGTH_POWERFUL: iThirdPartyAttackValue *= /*35*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_POWERFUL); iThirdPartyDefenseValue *= /*35*/ GD_INT_GET(TARGET_MAJOR_BACKUP_POWERFUL); break; case STRENGTH_STRONG: iThirdPartyAttackValue *= /*25*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_STRONG); iThirdPartyDefenseValue *= /*25*/ GD_INT_GET(TARGET_MAJOR_BACKUP_STRONG); break; case STRENGTH_AVERAGE: iThirdPartyAttackValue *= /*15*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_AVERAGE); iThirdPartyDefenseValue *= /*15*/ GD_INT_GET(TARGET_MAJOR_BACKUP_AVERAGE); break; case STRENGTH_POOR: iThirdPartyAttackValue *= /*10*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_POOR); iThirdPartyDefenseValue *= /*10*/ GD_INT_GET(TARGET_MAJOR_BACKUP_POOR); break; case STRENGTH_WEAK: iThirdPartyAttackValue *= /*5*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_WEAK); iThirdPartyDefenseValue *= /*5*/ GD_INT_GET(TARGET_MAJOR_BACKUP_WEAK); break; case STRENGTH_PATHETIC: iThirdPartyAttackValue *= /*0*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_PATHETIC); iThirdPartyDefenseValue *= /*0*/ GD_INT_GET(TARGET_MAJOR_BACKUP_PATHETIC); break; default: UNREACHABLE(); } } iThirdPartyAttackValue /= 100; iThirdPartyDefenseValue /= 100; // ATTACK: How close is this ally to them? if (GET_PLAYER(eAllyPlayer).isMinorCiv()) { iThirdPartyAttackValue *= /*100*/ GD_INT_GET(MILITARY_STRENGTH_MINOR_BACKUP_NEIGHBORS); } else { switch (GET_PLAYER(eAllyPlayer).GetProximityToPlayer(ePlayer)) { case PLAYER_PROXIMITY_NEIGHBORS: iThirdPartyAttackValue *= /*200*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_NEIGHBORS); break; case PLAYER_PROXIMITY_CLOSE: iThirdPartyAttackValue *= /*100*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_CLOSE); break; case PLAYER_PROXIMITY_FAR: iThirdPartyAttackValue *= /*50*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_FAR); break; default: iThirdPartyAttackValue *= /*0*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_DISTANT); break; } } // DEFENSE: How close is this ally to us? if (GET_PLAYER(eAllyPlayer).isMinorCiv()) { iThirdPartyDefenseValue *= /*100*/ GD_INT_GET(TARGET_MINOR_BACKUP_NEIGHBORS); } else { switch (GET_PLAYER(eAllyPlayer).GetProximityToPlayer(eOurPlayer)) { case PLAYER_PROXIMITY_NEIGHBORS: iThirdPartyDefenseValue *= /*200*/ GD_INT_GET(TARGET_MAJOR_BACKUP_NEIGHBORS); break; case PLAYER_PROXIMITY_CLOSE: iThirdPartyDefenseValue *= /*100*/ GD_INT_GET(TARGET_MAJOR_BACKUP_CLOSE); break; case PLAYER_PROXIMITY_FAR: iThirdPartyDefenseValue *= /*50*/ GD_INT_GET(TARGET_MAJOR_BACKUP_FAR); break; default: iThirdPartyDefenseValue *= /*0*/ GD_INT_GET(TARGET_MAJOR_BACKUP_DISTANT); break; } } iThirdPartyAttackValue /= 100; iThirdPartyDefenseValue /= 100; // Reduce if the third party is already at war with other players if (iThirdPartyAttackValue > 0 || iThirdPartyDefenseValue > 0) { int iThirdPartyWarCount = GET_PLAYER(eAllyPlayer).CountNumDangerousMajorsAtWarWith(false, true); if (GET_PLAYER(eAllyPlayer).IsAtWarWith(ePlayer)) iThirdPartyWarCount--; if (iThirdPartyWarCount > 0) { int iScale = max(/*20*/ max(GD_INT_GET(MILITARY_STRENGTH_BACKUP_ALREADY_WAR_MINIMUM), 0), 100 - iThirdPartyWarCount * /*30*/ GD_INT_GET(MILITARY_STRENGTH_BACKUP_ALREADY_WAR_EACH_PLAYER)); iThirdPartyAttackValue *= iScale; iThirdPartyAttackValue /= 100; iScale = max(/*20*/ max(GD_INT_GET(TARGET_ALREADY_WAR_MINIMUM), 0), 100 - iThirdPartyWarCount * /*30*/ GD_INT_GET(TARGET_ALREADY_WAR_EACH_PLAYER)); iThirdPartyDefenseValue *= iScale; iThirdPartyDefenseValue /= 100; } iAllyAttackBonus += iThirdPartyAttackValue; iAllyDefenseBonus += iThirdPartyDefenseValue; } } // Their allies for (std::vector::iterator it = vTheirAllies.begin(); it != vTheirAllies.end(); ++it) { PlayerTypes eEnemyPlayer = *it; int iOurMight = iMilitaryStrength; int iEnemyMight = max(iBase + GET_PLAYER(eEnemyPlayer).GetMilitaryMight() - GET_PLAYER(eEnemyPlayer).GetNuclearMight(), 1); // Factor in dynamic combat modifiers int iOurDynamicModifier = ComputeDynamicStrengthModifier(eOurPlayer, eEnemyPlayer); iOurMight *= 100 + iOurDynamicModifier; iOurMight /= 100; iEnemyMight *= 100 + ComputeDynamicStrengthModifier(eEnemyPlayer, eOurPlayer); iEnemyMight /= 100; // Factor in military rating (combat skill) iOurMight *= iOurRatingModifier; iOurMight /= 100; // If we're an AI evaluating a human, modify the ally's strength estimate based on difficulty level if they're reasonably strong if (!GetPlayer()->isHuman(ISHUMAN_HANDICAP) && GET_PLAYER(eEnemyPlayer).isHuman(ISHUMAN_HANDICAP) && (GET_PLAYER(eEnemyPlayer).getNumNukeUnits() > 0 || (!GET_PLAYER(eEnemyPlayer).IsVassalOfSomeone() && GetWarState(eEnemyPlayer) < WAR_STATE_OFFENSIVE))) { int iStartingRating = GC.getGame().GetStartingMilitaryRating(); int iMinimumHumanRating = GC.getGame().GetMinimumHumanMilitaryRating(); int iCurrentRating = GET_PLAYER(eEnemyPlayer).GetMilitaryRating(); int iHumanStrengthMod = max(0, GET_PLAYER(eEnemyPlayer).getHandicapInfo().getHumanStrengthPerceptionMod()); int iSkillRatingMod = GC.getGame().ComputeRatingStrengthAdjustment(eEnemyPlayer, eOurPlayer); // If they're above the starting rating or are performing better than average, apply the highest of the human perception mod and the skill rating mod if (iSkillRatingMod >= 100 || iCurrentRating >= iStartingRating || iStartingRating == 0) { iEnemyMight *= 100 + max(iHumanStrengthMod, iSkillRatingMod - 100); iEnemyMight /= 100; } // If the human hasn't lost 20% or more of the starting rating, they still get some of the strength perception bonus else if (iCurrentRating >= iMinimumHumanRating && GD_INT_GET(MILITARY_RATING_HUMAN_BUFFER_VALUE_PERCENT) < 100) { int iMaxPercentageLost = 100 - /*80*/ max(GD_INT_GET(MILITARY_RATING_HUMAN_BUFFER_VALUE_PERCENT), 0); int iPercentageLost = (iCurrentRating * 100 - iStartingRating * 100) / -iStartingRating; int iDifference = (100 + iHumanStrengthMod) - iSkillRatingMod; // Do not exceed human strength mod! // example 1: if human strength mod is 10, skill rating mod is 80, and percentage lost is 10 (modifier of 95%), that'd be a 15% bonus; we cap the modifier to 90% (10% bonus) to avoid exceeding the XML value. // example 2: if human strength mod is 30, skill rating mod is 80, and percentage lost is 10 (modifier of 105%), that'd be a 25% bonus, which is less than 30, so that's OK. int iFinalModifier = 100 + iHumanStrengthMod - (iDifference * iPercentageLost / iMaxPercentageLost); if (iFinalModifier - iSkillRatingMod > iHumanStrengthMod) iFinalModifier = iSkillRatingMod + iHumanStrengthMod; iEnemyMight *= iFinalModifier; iEnemyMight /= 100; } else { iEnemyMight *= iSkillRatingMod; iEnemyMight /= 100; } } else if (GET_PLAYER(eEnemyPlayer).isMajorCiv()) { iEnemyMight *= GC.getGame().ComputeRatingStrengthAdjustment(eEnemyPlayer, eOurPlayer); iEnemyMight /= 100; } // Factor in nuclear power iOurMight += GetPlayer()->calculateNuclearMight(eEnemyPlayer, true); iEnemyMight += GET_PLAYER(eEnemyPlayer).calculateNuclearMight(eOurPlayer, false, iOurBombShelterPercent, iOurAvgNukeModifier, iOurAvgInterceptionChance); // Careful in the early game when the number of units is very low ... if (GET_PLAYER(eEnemyPlayer).getNumMilitaryUnits() < 6 && GetPlayer()->getNumMilitaryUnits() < 6 && iOurMight < iEnemyMight * 10 && iEnemyMight < iOurMight * 10 && GetPlayer()->getNumNukeUnits() == 0 && GET_PLAYER(eEnemyPlayer).getNumNukeUnits() == 0) { iEnemyMight = iOurMight; } // How much does this enemy contribute to their ATTACK and DEFENSE? int iMilitaryRatio = iEnemyMight * /*100*/ GD_INT_GET(MILITARY_STRENGTH_RATIO_MULTIPLIER) / max(iOurMight, 1); StrengthTypes eMilitaryStrength = STRENGTH_PATHETIC; if (iMilitaryRatio >= /*300*/ GD_INT_GET(MILITARY_STRENGTH_IMMENSE_THRESHOLD)) eMilitaryStrength = STRENGTH_IMMENSE; else if (iMilitaryRatio >= /*200*/ GD_INT_GET(MILITARY_STRENGTH_POWERFUL_THRESHOLD)) eMilitaryStrength = STRENGTH_POWERFUL; else if (iMilitaryRatio >= /*126*/ GD_INT_GET(MILITARY_STRENGTH_STRONG_THRESHOLD)) eMilitaryStrength = STRENGTH_STRONG; else if (iMilitaryRatio >= /*75*/ GD_INT_GET(MILITARY_STRENGTH_AVERAGE_THRESHOLD)) eMilitaryStrength = STRENGTH_AVERAGE; else if (iMilitaryRatio >= /*50*/ GD_INT_GET(MILITARY_STRENGTH_POOR_THRESHOLD)) eMilitaryStrength = STRENGTH_POOR; else if (iMilitaryRatio >= /*33*/ GD_INT_GET(MILITARY_STRENGTH_WEAK_THRESHOLD)) eMilitaryStrength = STRENGTH_WEAK; int iThirdPartyAttackValue = std::find(vTheirOffensiveAllies.begin(), vTheirOffensiveAllies.end(), eEnemyPlayer) != vTheirOffensiveAllies.end() ? iEnemyMight : 0; int iThirdPartyDefenseValue = std::find(vTheirDefensiveAllies.begin(), vTheirDefensiveAllies.end(), eEnemyPlayer) != vTheirDefensiveAllies.end() ? iEnemyMight : 0; if (GET_PLAYER(eEnemyPlayer).isMinorCiv()) { switch (eMilitaryStrength) { case STRENGTH_IMMENSE: iThirdPartyAttackValue *= /*50*/ GD_INT_GET(MILITARY_STRENGTH_MINOR_BACKUP_IMMENSE); iThirdPartyDefenseValue *= /*50*/ GD_INT_GET(TARGET_MINOR_BACKUP_IMMENSE); break; case STRENGTH_POWERFUL: iThirdPartyAttackValue *= /*35*/ GD_INT_GET(MILITARY_STRENGTH_MINOR_BACKUP_POWERFUL); iThirdPartyDefenseValue *= /*35*/ GD_INT_GET(TARGET_MINOR_BACKUP_POWERFUL); break; case STRENGTH_STRONG: iThirdPartyAttackValue *= /*25*/ GD_INT_GET(MILITARY_STRENGTH_MINOR_BACKUP_STRONG); iThirdPartyDefenseValue *= /*25*/ GD_INT_GET(TARGET_MINOR_BACKUP_STRONG); break; case STRENGTH_AVERAGE: iThirdPartyAttackValue *= /*15*/ GD_INT_GET(MILITARY_STRENGTH_MINOR_BACKUP_AVERAGE); iThirdPartyDefenseValue *= /*15*/ GD_INT_GET(TARGET_MINOR_BACKUP_AVERAGE); break; case STRENGTH_POOR: iThirdPartyAttackValue *= /*0*/ GD_INT_GET(MILITARY_STRENGTH_MINOR_BACKUP_POOR); iThirdPartyDefenseValue *= /*0*/ GD_INT_GET(TARGET_MINOR_BACKUP_POOR); break; case STRENGTH_WEAK: iThirdPartyAttackValue *= /*0*/ GD_INT_GET(MILITARY_STRENGTH_MINOR_BACKUP_WEAK); iThirdPartyDefenseValue *= /*0*/ GD_INT_GET(TARGET_MINOR_BACKUP_WEAK); break; case STRENGTH_PATHETIC: iThirdPartyAttackValue *= /*0*/ GD_INT_GET(MILITARY_STRENGTH_MINOR_BACKUP_PATHETIC); iThirdPartyDefenseValue *= /*0*/ GD_INT_GET(TARGET_MINOR_BACKUP_PATHETIC); break; default: UNREACHABLE(); } } else { switch (eMilitaryStrength) { case STRENGTH_IMMENSE: iThirdPartyAttackValue *= /*50*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_IMMENSE); iThirdPartyDefenseValue *= /*50*/ GD_INT_GET(TARGET_MAJOR_BACKUP_IMMENSE); break; case STRENGTH_POWERFUL: iThirdPartyAttackValue *= /*35*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_POWERFUL); iThirdPartyDefenseValue *= /*35*/ GD_INT_GET(TARGET_MAJOR_BACKUP_POWERFUL); break; case STRENGTH_STRONG: iThirdPartyAttackValue *= /*25*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_STRONG); iThirdPartyDefenseValue *= /*25*/ GD_INT_GET(TARGET_MAJOR_BACKUP_STRONG); break; case STRENGTH_AVERAGE: iThirdPartyAttackValue *= /*15*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_AVERAGE); iThirdPartyDefenseValue *= /*15*/ GD_INT_GET(TARGET_MAJOR_BACKUP_AVERAGE); break; case STRENGTH_POOR: iThirdPartyAttackValue *= /*10*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_POOR); iThirdPartyDefenseValue *= /*10*/ GD_INT_GET(TARGET_MAJOR_BACKUP_POOR); break; case STRENGTH_WEAK: iThirdPartyAttackValue *= /*5*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_WEAK); iThirdPartyDefenseValue *= /*5*/ GD_INT_GET(TARGET_MAJOR_BACKUP_WEAK); break; case STRENGTH_PATHETIC: iThirdPartyAttackValue *= /*0*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_PATHETIC); iThirdPartyDefenseValue *= /*0*/ GD_INT_GET(TARGET_MAJOR_BACKUP_PATHETIC); break; default: UNREACHABLE(); } } iThirdPartyAttackValue /= 100; iThirdPartyDefenseValue /= 100; // ATTACK: How close is this enemy to us? if (GET_PLAYER(eEnemyPlayer).isMinorCiv()) { iThirdPartyAttackValue *= /*100*/ GD_INT_GET(MILITARY_STRENGTH_MINOR_BACKUP_NEIGHBORS); } else { switch (GET_PLAYER(eEnemyPlayer).GetProximityToPlayer(eOurPlayer)) { case PLAYER_PROXIMITY_NEIGHBORS: iThirdPartyAttackValue *= /*200*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_NEIGHBORS); break; case PLAYER_PROXIMITY_CLOSE: iThirdPartyAttackValue *= /*100*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_CLOSE); break; case PLAYER_PROXIMITY_FAR: iThirdPartyAttackValue *= /*50*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_FAR); break; default: iThirdPartyAttackValue *= /*0*/ GD_INT_GET(MILITARY_STRENGTH_MAJOR_BACKUP_DISTANT); break; } } // DEFENSE: How close is this enemy to them? if (GET_PLAYER(eEnemyPlayer).isMinorCiv()) { iThirdPartyDefenseValue *= /*100*/ GD_INT_GET(TARGET_MINOR_BACKUP_NEIGHBORS); } else { switch (GET_PLAYER(eEnemyPlayer).GetProximityToPlayer(ePlayer)) { case PLAYER_PROXIMITY_NEIGHBORS: iThirdPartyDefenseValue *= /*200*/ GD_INT_GET(TARGET_MAJOR_BACKUP_NEIGHBORS); break; case PLAYER_PROXIMITY_CLOSE: iThirdPartyDefenseValue *= /*100*/ GD_INT_GET(TARGET_MAJOR_BACKUP_CLOSE); break; case PLAYER_PROXIMITY_FAR: iThirdPartyDefenseValue *= /*50*/ GD_INT_GET(TARGET_MAJOR_BACKUP_FAR); break; default: iThirdPartyDefenseValue *= /*0*/ GD_INT_GET(TARGET_MAJOR_BACKUP_DISTANT); break; } } iThirdPartyAttackValue /= 100; iThirdPartyDefenseValue /= 100; // Reduce if the third party is already at war with other players if (iThirdPartyAttackValue > 0 || iThirdPartyDefenseValue > 0) { int iThirdPartyWarCount = GET_PLAYER(eEnemyPlayer).CountNumDangerousMajorsAtWarWith(false, true); if (IsAtWar(eEnemyPlayer)) iThirdPartyWarCount--; if (iThirdPartyWarCount > 0) { int iScale = max(/*20*/ max(GD_INT_GET(MILITARY_STRENGTH_BACKUP_ALREADY_WAR_MINIMUM), 0), 100 - iThirdPartyWarCount * /*30*/ GD_INT_GET(MILITARY_STRENGTH_BACKUP_ALREADY_WAR_EACH_PLAYER)); iThirdPartyAttackValue *= iScale; iThirdPartyAttackValue /= 100; iScale = max(/*20*/ max(GD_INT_GET(TARGET_ALREADY_WAR_MINIMUM), 0), 100 - iThirdPartyWarCount * /*30*/ GD_INT_GET(TARGET_ALREADY_WAR_EACH_PLAYER)); iThirdPartyDefenseValue *= iScale; iThirdPartyDefenseValue /= 100; } iEnemyAttackBonus += iThirdPartyAttackValue; iEnemyDefenseBonus += iThirdPartyDefenseValue; } } // Calculate their overall ATTACK vs. our overall DEFENSE int iTheirOverallAttack = iTheirMilitaryStrength + iEnemyAttackBonus; int iOurOverallDefense = iOurMilitaryStrength + iOurCityDefense; // Careful in the early game when the number of units is very low ... bool bEarlyGameCaution = false; if (GET_PLAYER(ePlayer).getNumMilitaryUnits() < 6 && GetPlayer()->getNumMilitaryUnits() < 6 && iOurMilitaryStrength < iTheirMilitaryStrength * 10 && iTheirMilitaryStrength < iOurMilitaryStrength * 10 && GetPlayer()->getNumNukeUnits() == 0 && GET_PLAYER(ePlayer).getNumNukeUnits() == 0) { bEarlyGameCaution = true; iOurOverallDefense = iTheirMilitaryStrength; } // Reduce if we're already at war with other players int iOurWarCount = GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) ? GetPlayer()->CountNumDangerousMajorsAtWarWith(false, true) : GetPlayer()->CountNumDangerousMajorsAtWarWith(true, true); if (IsAtWar(ePlayer)) iOurWarCount--; if (iOurWarCount > 0) { iOurOverallDefense *= max(/*20*/ GD_INT_GET(TARGET_ALREADY_WAR_MINIMUM), 100 - iOurWarCount * /*30*/ GD_INT_GET(TARGET_ALREADY_WAR_EACH_PLAYER)); iOurOverallDefense /= 100; } iOurOverallDefense += iAllyDefenseBonus; // ATTACK: Military Strength estimate int iBoldnessReduction = GetBoldness() * /*-3*/ GD_INT_GET(MILITARY_STRENGTH_REDUCTION_PER_BOLDNESS); // AI underestimates opponents' military strength by 3% per point of Boldness flavor int iRatioMultiplier = GetPlayer()->isHuman(ISHUMAN_AI_UNITS) ? 100 : max(iBoldnessReduction + /*100*/ GD_INT_GET(MILITARY_STRENGTH_RATIO_MULTIPLIER), 0); int iMilitaryRatio = iTheirOverallAttack * iRatioMultiplier / max(iOurOverallDefense, 1); StrengthTypes eMilitaryStrength = STRENGTH_PATHETIC; if (iMilitaryRatio >= /*300*/ GD_INT_GET(MILITARY_STRENGTH_IMMENSE_THRESHOLD)) eMilitaryStrength = STRENGTH_IMMENSE; else if (iMilitaryRatio >= /*200*/ GD_INT_GET(MILITARY_STRENGTH_POWERFUL_THRESHOLD)) eMilitaryStrength = STRENGTH_POWERFUL; else if (iMilitaryRatio >= /*126*/ GD_INT_GET(MILITARY_STRENGTH_STRONG_THRESHOLD)) eMilitaryStrength = STRENGTH_STRONG; else if (iMilitaryRatio >= /*75*/ GD_INT_GET(MILITARY_STRENGTH_AVERAGE_THRESHOLD)) eMilitaryStrength = STRENGTH_AVERAGE; else if (iMilitaryRatio >= /*50*/ GD_INT_GET(MILITARY_STRENGTH_POOR_THRESHOLD)) eMilitaryStrength = STRENGTH_POOR; else if (iMilitaryRatio >= /*33*/ GD_INT_GET(MILITARY_STRENGTH_WEAK_THRESHOLD)) eMilitaryStrength = STRENGTH_WEAK; // Set the value SetMilitaryStrengthComparedToUs(ePlayer, eMilitaryStrength); // Now do it again but with the unbiased value iMilitaryRatio = iTheirOverallAttack * 100 / max(iOurOverallDefense, 1); eMilitaryStrength = STRENGTH_PATHETIC; if (iMilitaryRatio >= /*300*/ GD_INT_GET(MILITARY_STRENGTH_IMMENSE_THRESHOLD)) eMilitaryStrength = STRENGTH_IMMENSE; else if (iMilitaryRatio >= /*200*/ GD_INT_GET(MILITARY_STRENGTH_POWERFUL_THRESHOLD)) eMilitaryStrength = STRENGTH_POWERFUL; else if (iMilitaryRatio >= /*126*/ GD_INT_GET(MILITARY_STRENGTH_STRONG_THRESHOLD)) eMilitaryStrength = STRENGTH_STRONG; else if (iMilitaryRatio >= /*75*/ GD_INT_GET(MILITARY_STRENGTH_AVERAGE_THRESHOLD)) eMilitaryStrength = STRENGTH_AVERAGE; else if (iMilitaryRatio >= /*50*/ GD_INT_GET(MILITARY_STRENGTH_POOR_THRESHOLD)) eMilitaryStrength = STRENGTH_POOR; else if (iMilitaryRatio >= /*33*/ GD_INT_GET(MILITARY_STRENGTH_WEAK_THRESHOLD)) eMilitaryStrength = STRENGTH_WEAK; SetRawMilitaryStrengthComparedToUs(ePlayer, eMilitaryStrength); // Calculate our overall ATTACK vs. their overall DEFENSE int iOurOverallAttack = iOurMilitaryStrength + iAllyAttackBonus + iEconomicStrength * /*100*/ GD_INT_GET(TARGET_ECONOMIC_MOD) / 100; int iTheirOverallDefense = iTheirMilitaryStrength + iTheirEconomicStrength * /*100*/ GD_INT_GET(TARGET_ECONOMIC_MOD) / 100; // Careful in the early game when the number of units is very low ... if (bEarlyGameCaution) iOurOverallAttack = iTheirOverallDefense + iAllyAttackBonus; // Add in defense from their cities iTheirOverallDefense += iTheirCityDefense; // Decrease target value if the player is already at war with other players int iTheirWarCount = GET_PLAYER(ePlayer).CountNumDangerousMajorsAtWarWith(false, true); // Reduce by 1 if he's already at war with us if (IsAtWar(ePlayer)) iTheirWarCount--; if (iTheirWarCount > 0) { iTheirOverallDefense *= max(/*20*/ GD_INT_GET(TARGET_ALREADY_WAR_MINIMUM), 100 - iTheirWarCount * /*30*/ GD_INT_GET(TARGET_ALREADY_WAR_EACH_PLAYER)); iTheirOverallDefense /= 100; } iTheirOverallDefense += iEnemyDefenseBonus; // DEFENSE: Target Value estimate iBoldnessReduction = GetBoldness() * /*-3*/ GD_INT_GET(TARGET_VALUE_REDUCTION_PER_BOLDNESS); // AI underestimates opponents' target values by 3% per point of Boldness flavor iRatioMultiplier = GetPlayer()->isHuman(ISHUMAN_AI_UNITS) ? 100 : max(iBoldnessReduction + /*100*/ GD_INT_GET(MILITARY_STRENGTH_RATIO_MULTIPLIER), 0); int iTargetValueRatio = iTheirOverallDefense * iRatioMultiplier / max(iOurOverallAttack, 1); // Factor in distance switch (GetPlayer()->GetProximityToPlayer(ePlayer)) { case PLAYER_PROXIMITY_NEIGHBORS: iTargetValueRatio *= /*4 in early game, 3 otherwise*/ (bEarlyGameCaution && GD_INT_GET(TARGET_NEIGHBORS) < GD_INT_GET(TARGET_NEIGHBORS_DIVISOR)) ? max(GD_INT_GET(TARGET_NEIGHBORS_DIVISOR), 1) : max(GD_INT_GET(TARGET_NEIGHBORS), 1); iTargetValueRatio /= /*4*/ max(GD_INT_GET(TARGET_NEIGHBORS_DIVISOR), 1); break; case PLAYER_PROXIMITY_CLOSE: iTargetValueRatio *= /*1*/ max(GD_INT_GET(TARGET_CLOSE), 1); iTargetValueRatio /= /*1*/ max(GD_INT_GET(TARGET_CLOSE_DIVISOR), 1); break; case PLAYER_PROXIMITY_FAR: iTargetValueRatio *= /*3*/ max(GD_INT_GET(TARGET_FAR), 1); iTargetValueRatio /= /*2*/ max(GD_INT_GET(TARGET_FAR_DIVISOR), 1); break; default: iTargetValueRatio *= /*2*/ max(GD_INT_GET(TARGET_DISTANT), 1); iTargetValueRatio /= /*1*/ max(GD_INT_GET(TARGET_DISTANT_DIVISOR), 1); break; } // Now do the final assessment TargetValueTypes eTargetValue = TARGET_VALUE_CAKEWALK; if (iTargetValueRatio >= /*300*/ GD_INT_GET(TARGET_IMPOSSIBLE_THRESHOLD)) eTargetValue = TARGET_VALUE_IMPOSSIBLE; else if (iTargetValueRatio >= /*200*/ GD_INT_GET(TARGET_BAD_THRESHOLD)) eTargetValue = TARGET_VALUE_BAD; else if (iTargetValueRatio >= /*126*/ GD_INT_GET(TARGET_DIFFICULT_THRESHOLD)) eTargetValue = TARGET_VALUE_DIFFICULT; else if (iTargetValueRatio >= /*75*/ GD_INT_GET(TARGET_AVERAGE_THRESHOLD)) eTargetValue = TARGET_VALUE_AVERAGE; else if (iTargetValueRatio >= /*50*/ GD_INT_GET(TARGET_FAVORABLE_THRESHOLD)) eTargetValue = TARGET_VALUE_FAVORABLE; else if (iTargetValueRatio >= /*33*/ GD_INT_GET(TARGET_SOFT_THRESHOLD)) eTargetValue = TARGET_VALUE_SOFT; // Set the value SetTargetValue(ePlayer, eTargetValue); // Now do it again but with the unbiased value iTargetValueRatio = iTheirOverallDefense * 100 / max(iOurOverallAttack, 1); // Factor in distance switch (GetPlayer()->GetProximityToPlayer(ePlayer)) { case PLAYER_PROXIMITY_NEIGHBORS: iTargetValueRatio *= /*4 in early game, 3 otherwise*/ (bEarlyGameCaution && GD_INT_GET(TARGET_NEIGHBORS) < GD_INT_GET(TARGET_NEIGHBORS_DIVISOR)) ? max(GD_INT_GET(TARGET_NEIGHBORS_DIVISOR), 1) : max(GD_INT_GET(TARGET_NEIGHBORS), 1); iTargetValueRatio /= /*4*/ max(GD_INT_GET(TARGET_NEIGHBORS_DIVISOR), 1); break; case PLAYER_PROXIMITY_CLOSE: iTargetValueRatio *= /*1*/ max(GD_INT_GET(TARGET_CLOSE), 1); iTargetValueRatio /= /*1*/ max(GD_INT_GET(TARGET_CLOSE_DIVISOR), 1); break; case PLAYER_PROXIMITY_FAR: iTargetValueRatio *= /*3*/ max(GD_INT_GET(TARGET_FAR), 1); iTargetValueRatio /= /*2*/ max(GD_INT_GET(TARGET_FAR_DIVISOR), 1); break; default: iTargetValueRatio *= /*2*/ max(GD_INT_GET(TARGET_DISTANT), 1); iTargetValueRatio /= /*1*/ max(GD_INT_GET(TARGET_DISTANT_DIVISOR), 1); break; } eTargetValue = TARGET_VALUE_CAKEWALK; if (iTargetValueRatio >= /*300*/ GD_INT_GET(TARGET_IMPOSSIBLE_THRESHOLD)) eTargetValue = TARGET_VALUE_IMPOSSIBLE; else if (iTargetValueRatio >= /*200*/ GD_INT_GET(TARGET_BAD_THRESHOLD)) eTargetValue = TARGET_VALUE_BAD; else if (iTargetValueRatio >= /*126*/ GD_INT_GET(TARGET_DIFFICULT_THRESHOLD)) eTargetValue = TARGET_VALUE_DIFFICULT; else if (iTargetValueRatio >= /*75*/ GD_INT_GET(TARGET_AVERAGE_THRESHOLD)) eTargetValue = TARGET_VALUE_AVERAGE; else if (iTargetValueRatio >= /*50*/ GD_INT_GET(TARGET_FAVORABLE_THRESHOLD)) eTargetValue = TARGET_VALUE_FAVORABLE; else if (iTargetValueRatio >= /*33*/ GD_INT_GET(TARGET_SOFT_THRESHOLD)) eTargetValue = TARGET_VALUE_SOFT; SetRawTargetValue(ePlayer, eTargetValue); } } /// Calculates strength bonuses for a player which DO change based on who they're up against int CvDiplomacyAI::ComputeDynamicStrengthModifier(PlayerTypes ePlayer, PlayerTypes eAgainstPlayer) { if (!GET_PLAYER(ePlayer).isMajorCiv()) return 0; int iModifier = 0; TeamTypes eOurTeam = GetTeam(); TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam(); CvPlayerTraits* pTraits = GET_PLAYER(ePlayer).GetPlayerTraits(); // Rome / BNW Mongolia UA if (GET_PLAYER(eAgainstPlayer).isMinorCiv()) iModifier += pTraits->GetCityStateCombatModifier(); // Ethiopia UA iModifier += GET_PLAYER(ePlayer).getNumCities() < GET_PLAYER(eAgainstPlayer).getNumCities() ? pTraits->GetCombatBonusVsLargerCiv() : 0; // Bonus VS Higher Pop iModifier += GET_PLAYER(ePlayer).getTotalPopulation() < GET_PLAYER(eAgainstPlayer).getTotalPopulation() ? pTraits->GetCombatBonusVsHigherPop() : 0; // Sweden UA / BNW China UA ... count GG bonus if at least one of them is active if (pTraits->GetGreatGeneralExtraBonus() > 0) { int iLoop = 0; for (CvUnit* pLoopUnit = GET_PLAYER(ePlayer).firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = GET_PLAYER(ePlayer).nextUnit(&iLoop)) { if (pLoopUnit->IsGreatGeneral() && !pLoopUnit->isDelayedDeath() && !pLoopUnit->isProjectedToDieNextTurn()) { // Must be close to one of the players CvPlot* pUnitPlot = pLoopUnit->plot(); if (!pUnitPlot) continue; // Can we actually see this Unit? if (eTeam != eOurTeam) { if (!pUnitPlot->isVisible(eOurTeam) || pLoopUnit->isInvisible(eOurTeam, false) || (pLoopUnit->isCargo() && pLoopUnit->getTransportUnit()->isInvisible(eOurTeam, false))) continue; } // Minor cheating here (doesn't check whether the city is visible to eOurTeam) if (!pUnitPlot->IsCloseToCity(ePlayer) && !pUnitPlot->IsCloseToCity(eAgainstPlayer)) continue; iModifier += pTraits->GetGreatGeneralExtraBonus(); break; } } } return iModifier; } /// Updates war progress scores (per turn decay) void CvDiplomacyAI::DoUpdateWarProgressScores() { if (UpdatedWarProgressThisTurn()) return; for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(ePlayer)) { StrengthTypes eMilitaryStrength = GetMilitaryStrengthComparedToUs(ePlayer); if (eMilitaryStrength > STRENGTH_AVERAGE) { ChangeWarProgressScore(ePlayer, /*-5*/ GD_INT_GET(WAR_PROGRESS_DECAY_VS_STRONGER)); } else if (eMilitaryStrength < STRENGTH_AVERAGE) { ChangeWarProgressScore(ePlayer, /*-3*/ GD_INT_GET(WAR_PROGRESS_DECAY_VS_WEAKER)); } else { ChangeWarProgressScore(ePlayer, /*-4*/ GD_INT_GET(WAR_PROGRESS_DECAY_VS_EQUAL)); } } else if (GET_PLAYER(ePlayer).isAlive() && GET_PLAYER(ePlayer).getTeam() != GetTeam()) { SetWarProgressScore(ePlayer, /*100*/ GD_INT_GET(WAR_PROGRESS_INITIAL_VALUE)); } else { SetWarProgressScore(ePlayer, 0); } } } /// Updates how much of a threat each player is to run amok and break everything void CvDiplomacyAI::DoUpdateWarmongerThreats(bool bUpdateOnly) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer)) { // Our masters and vassals don't pose any warmongering threat to us. if (IsVassal(eLoopPlayer) || IsMaster(eLoopPlayer)) { SetWarmongerThreat(eLoopPlayer, THREAT_NONE); continue; } // Do warmonger decay int iDecayValue = !bUpdateOnly ? (/*200*/ GD_INT_GET(WARMONGER_THREAT_PER_TURN_DECAY) * 100) : 0; if (!bUpdateOnly) { int iDecayModifier = 100; // Decay slower if he's stronger than us, decay faster if he's not. switch (GetRawMilitaryStrengthComparedToUs(eLoopPlayer)) { case STRENGTH_IMMENSE: iDecayModifier = /*50*/ GD_INT_GET(WARMONGER_THREAT_STRENGTH_DECAY_IMMENSE); break; case STRENGTH_POWERFUL: iDecayModifier = /*75*/ GD_INT_GET(WARMONGER_THREAT_STRENGTH_DECAY_POWERFUL); break; case STRENGTH_STRONG: iDecayModifier = /*100*/ GD_INT_GET(WARMONGER_THREAT_STRENGTH_DECAY_STRONG); break; case STRENGTH_AVERAGE: iDecayModifier = /*150*/ GD_INT_GET(WARMONGER_THREAT_STRENGTH_DECAY_AVERAGE); break; case STRENGTH_POOR: iDecayModifier = /*200*/ GD_INT_GET(WARMONGER_THREAT_STRENGTH_DECAY_POOR); break; case STRENGTH_WEAK: iDecayModifier = /*250*/ GD_INT_GET(WARMONGER_THREAT_STRENGTH_DECAY_WEAK); break; case STRENGTH_PATHETIC: iDecayModifier = /*300*/ GD_INT_GET(WARMONGER_THREAT_STRENGTH_DECAY_PATHETIC); break; } // Decay faster if we have a good relationship with this player. if (IsDoFAccepted(eLoopPlayer) || IsHasDefensivePact(eLoopPlayer) || GetDoFType(eLoopPlayer) >= DOF_TYPE_FRIENDS) { iDecayModifier *= 2; } if (GC.getGame().GetGameLeagues()->IsWorldWar(GetID()) > 0) { iDecayModifier *= /*200*/ GD_INT_GET(WARMONGER_THREAT_PER_TURN_DECAY_INCREASED); iDecayModifier /= 100; } else if (GC.getGame().GetGameLeagues()->GetUnitMaintenanceMod(GetID()) > 0) { iDecayModifier *= /*50*/ GD_INT_GET(WARMONGER_THREAT_PER_TURN_DECAY_DECREASED); iDecayModifier /= 100; } iDecayValue *= iDecayModifier; iDecayValue /= 100; ChangeOtherPlayerWarmongerAmountTimes100(eLoopPlayer, min(iDecayValue, -100)); // Make sure it decays by at least -1 every turn } ThreatTypes eThreatType = THREAT_NONE; int iThreatValue = GetOtherPlayerWarmongerScore(eLoopPlayer); // Now do the final assessment if (iThreatValue >= /*200*/ GD_INT_GET(WARMONGER_THREAT_CRITICAL_THRESHOLD)) eThreatType = THREAT_CRITICAL; else if (iThreatValue >= /*140*/ GD_INT_GET(WARMONGER_THREAT_SEVERE_THRESHOLD)) eThreatType = THREAT_SEVERE; else if (iThreatValue >= /*80*/ GD_INT_GET(WARMONGER_THREAT_MAJOR_THRESHOLD)) eThreatType = THREAT_MAJOR; else if (iThreatValue >= /*20*/ GD_INT_GET(WARMONGER_THREAT_MINOR_THRESHOLD)) eThreatType = THREAT_MINOR; // Also test % of players killed int iNumPlayersEver = GC.getGame().GetNumMajorCivsEver(); int iNumPlayersKilled = GetPlayerNumMajorsConquered(eLoopPlayer) + (GetPlayerNumMinorsConquered(eLoopPlayer)/2); if (eThreatType != THREAT_CRITICAL) { int iPlayersKilledPercent = iNumPlayersKilled * 100 / max(1, iNumPlayersEver); if (iPlayersKilledPercent >= /*33*/ GD_INT_GET(WARMONGER_THREAT_CRITICAL_PERCENT_THRESHOLD)) eThreatType = THREAT_CRITICAL; else if (iPlayersKilledPercent >= /*25*/ GD_INT_GET(WARMONGER_THREAT_SEVERE_PERCENT_THRESHOLD)) eThreatType = THREAT_SEVERE; } // Set the Threat bool bUpdateLogsSpecial = false; if (eThreatType != GetWarmongerThreat(eLoopPlayer)) { SetWarmongerThreat(eLoopPlayer, eThreatType); bUpdateLogsSpecial = true; } // Skip updating the logs if threat is zero and nothing has changed if (!bUpdateLogsSpecial && eThreatType == THREAT_NONE && GetOtherPlayerWarmongerAmount(eLoopPlayer) == 0) continue; LogMajorCivWarmongerUpdate(eLoopPlayer, iDecayValue, bUpdateLogsSpecial); } else { SetWarmongerThreat(eLoopPlayer, THREAT_NONE); } } } int CvDiplomacyAI::GetNumberOfThreatenedCities(PlayerTypes ePlayer) { if (!IsAtWar(ePlayer)) return 0; int iCountCitiesInDanger = 0; int iCityLoop = 0; vector vTheirWarAllies = GetOffensiveWarAllies(ePlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ true); vTheirWarAllies.push_back(ePlayer); for (const CvCity* pFriendlyCity = GetPlayer()->firstCity(&iCityLoop); pFriendlyCity != NULL; pFriendlyCity = GetPlayer()->nextCity(&iCityLoop)) { if (pFriendlyCity->IsInDangerFromPlayers(vTheirWarAllies)) iCountCitiesInDanger++; } return iCountCitiesInDanger; } /// Updates whether all players are easy attack targets void CvDiplomacyAI::DoUpdateEasyTargets() { // How many people are we already at war with? int iCurrentWars = GetPlayer()->CountNumDangerousMajorsAtWarWith(true, true); // Personality bool bTheAssyriaException = GetPlayer()->GetPlayerTraits()->IsTechFromCityConquer(); bool bMoreAggressive = IsConqueror() || IsSecondaryConqueror() || GetPlayer()->GetPlayerTraits()->IsWarmonger() || GetPlayer()->GetPlayerTraits()->IsExpansionist(); bool bLessAggressive = ((IsScientist() && !bTheAssyriaException) || (GetPlayer()->GetPlayerTraits()->IsNerd() && !bTheAssyriaException) || GetPlayer()->GetPlayerTraits()->IsSmaller()); if (bMoreAggressive && bLessAggressive) { bMoreAggressive = false; bLessAggressive = false; } for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(ePlayer)) { // First check the war state WarStateTypes eWarState = GetWarState(ePlayer); if (eWarState == WAR_STATE_NEARLY_WON) { SetEasyTarget(ePlayer, true); continue; } if (eWarState < WAR_STATE_STALEMATE) { SetEasyTarget(ePlayer, false); continue; } // If we're doing very badly, they can't be an easy target if (GetPlayer()->IsInTerribleShapeForWar()) { SetEasyTarget(ePlayer, false); continue; } // Nuclear Gandhi sees vulnerability in his opponents... if (IsNuclearGandhi()) { SetEasyTarget(ePlayer, true); continue; } // If they're too far away, they can't be an easy target if (GetPlayer()->GetProximityToPlayer(ePlayer) <= PLAYER_PROXIMITY_FAR) { SetEasyTarget(ePlayer, false); continue; } // If we've been at war for a while without capturing any of their cities, they can't be an easy target if (GET_TEAM(GetTeam()).GetNumTurnsAtWar(GET_PLAYER(ePlayer).getTeam()) >= 30 && GetPlayer()->GetNumTurnsSinceCityCapture(ePlayer) >= 30) { SetEasyTarget(ePlayer, false); continue; } // City-State: Easy evaluation if (GET_PLAYER(ePlayer).isMinorCiv()) { SetEasyTarget(ePlayer, GetTargetValue(ePlayer) >= TARGET_VALUE_FAVORABLE); continue; } // If we're at a stalemate or defensive in a war against any other players, they can't be an easy target bool bValueSet = false; for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // Don't look at the guy we're already thinking about if (IsPlayerValid(eLoopPlayer) && ePlayer != eLoopPlayer && IsAtWar(eLoopPlayer)) { // Ignore phony wars if (IsPhonyWar(ePlayer)) { continue; } if (GET_PLAYER(ePlayer).isMinorCiv()) // caution requirement is lower for City-States { if (GetWarState(eLoopPlayer) <= WAR_STATE_TROUBLED) { SetEasyTarget(ePlayer, false); bValueSet = true; break; } } else { if (GetWarState(eLoopPlayer) <= WAR_STATE_TROUBLED) { SetEasyTarget(ePlayer, false); bValueSet = true; break; } else if (!GET_PLAYER(eLoopPlayer).IsNoNewWars() && GetWarState(eLoopPlayer) <= WAR_STATE_CALM) // if we're at a stalemate in this war, don't open ourselves up to dogpiling { SetEasyTarget(ePlayer, false); bValueSet = true; break; } } } } if (bValueSet) continue; // They're doing very badly! Let's strike! if (GET_PLAYER(ePlayer).IsInTerribleShapeForWar()) { SetEasyTarget(ePlayer, true); continue; } // Deterrents bool bAlreadyAtWar = !IsAtWar(ePlayer) && iCurrentWars > 0; bool bUnhappinessPenalty = GetPlayer()->IsEmpireUnhappy(); bool bCapturedOurCities = GetNumCitiesCapturedBy(ePlayer) > GET_PLAYER(ePlayer).GetDiplomacyAI()->GetNumCitiesCapturedBy(GetID()); // Aggravators bool bCoopWar = GetGlobalCoopWarAgainstState(ePlayer) >= COOP_WAR_STATE_PREPARING; // More likely to view players as weak if we've agreed to a coop war bool bUnhappinessBoost = !bUnhappinessPenalty && GET_PLAYER(ePlayer).IsEmpireUnhappy(); // More likely to attack players who are unhappy, if we're not. bool bCapturedTheirCities = GET_PLAYER(ePlayer).GetDiplomacyAI()->GetNumCitiesCapturedBy(GetID()) > GetNumCitiesCapturedBy(ePlayer); // Military Rating (Combat Skill) - can be either a bonus or a penalty bool bSkillRatingPenalty = false; bool bSkillRatingBoost = false; if (GetPlayer()->GetMilitaryRating() > GC.getGame().GetStartingMilitaryRating() && GetPlayer()->GetMilitaryRating() > GET_PLAYER(ePlayer).GetMilitaryRating()) { if (GetPlayer()->GetMilitaryRating() * 2 > GET_PLAYER(ePlayer).GetMilitaryRating()) bSkillRatingBoost = true; if (GET_PLAYER(ePlayer).GetMilitaryRating() < GC.getGame().GetStartingMilitaryRating()) bSkillRatingBoost = true; } else if (GET_PLAYER(ePlayer).GetMilitaryRating() > GC.getGame().GetStartingMilitaryRating() && GET_PLAYER(ePlayer).GetMilitaryRating() > GetPlayer()->GetMilitaryRating()) { if (GET_PLAYER(ePlayer).GetMilitaryRating() * 2 > GetPlayer()->GetMilitaryRating()) bSkillRatingPenalty = true; if (GetPlayer()->GetMilitaryRating() < GC.getGame().GetStartingMilitaryRating()) bSkillRatingPenalty = true; } // Do we particularly want to conquer them? bool bWantsConquest = IsGoingForWorldConquest() || IsCloseToWorldConquest(); bWantsConquest |= GetWarState(ePlayer) == WAR_STATE_OFFENSIVE; bWantsConquest |= IsRecklessExpander(ePlayer); bWantsConquest |= IsWonderSpammer(ePlayer); bWantsConquest |= IsEndgameAggressiveTo(ePlayer); bWantsConquest |= GetBiggestCompetitor() == ePlayer; bWantsConquest |= GetCivOpinion(ePlayer) == CIV_OPINION_UNFORGIVABLE; bWantsConquest |= GetWarmongerThreat(ePlayer) >= THREAT_SEVERE; bWantsConquest |= IsLockedIntoCoopWar(ePlayer); bWantsConquest |= IsEarlyGameCompetitor(ePlayer); int iNumDeterrents = 0; if (bAlreadyAtWar) iNumDeterrents++; if (bUnhappinessPenalty) iNumDeterrents++; if (bSkillRatingPenalty) iNumDeterrents++; if (bCapturedOurCities) iNumDeterrents++; if (bLessAggressive) iNumDeterrents++; if (!bWantsConquest) iNumDeterrents++; if (GetPlayer()->GetMilitaryAI()->IsExposedToEnemy(NULL, ePlayer)) iNumDeterrents++; int iNumAggravators = 0; if (bCoopWar) iNumAggravators++; if (bUnhappinessBoost) iNumAggravators++; if (bSkillRatingBoost) iNumAggravators++; if (bCapturedTheirCities) iNumAggravators++; if (bMoreAggressive) iNumAggravators++; if (bWantsConquest) iNumAggravators++; if (GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(ePlayer)) iNumAggravators++; // Okay, now let's compare our strengths... bool bNoNewWars = GetPlayer()->IsNoNewWars() && !IsAtWar(ePlayer); TargetValueTypes eTargetValue = (bNoNewWars || iNumDeterrents > iNumAggravators) ? GetRawTargetValue(ePlayer) : GetTargetValue(ePlayer); StrengthTypes eMilitaryStrength = (bNoNewWars || iNumDeterrents > iNumAggravators) ? GetRawMilitaryStrengthComparedToUs(ePlayer) : GetMilitaryStrengthComparedToUs(ePlayer); StrengthTypes eEconomicStrength = GetEconomicStrengthComparedToUs(ePlayer); if (eTargetValue <= TARGET_VALUE_BAD || eMilitaryStrength == STRENGTH_IMMENSE || eEconomicStrength == STRENGTH_IMMENSE) { SetEasyTarget(ePlayer, false); continue; } if (eTargetValue >= TARGET_VALUE_SOFT || eMilitaryStrength == STRENGTH_PATHETIC || eEconomicStrength == STRENGTH_PATHETIC) { SetEasyTarget(ePlayer, true); continue; } // Too many reasons why they're not favorable? Abort! if ((iNumAggravators < 3 && iNumDeterrents >= iNumAggravators + 2) || (iNumAggravators >= 3 && iNumDeterrents >= iNumAggravators + 3)) { SetEasyTarget(ePlayer, false); continue; } bool bFavorableForOpportunityAttack = iNumAggravators > iNumDeterrents; // Compare military and economic strengths to look for opportunities to strike if (bFavorableForOpportunityAttack) { if (eMilitaryStrength <= STRENGTH_POOR && eEconomicStrength <= STRENGTH_STRONG) { SetEasyTarget(ePlayer, true); continue; } if (eMilitaryStrength <= STRENGTH_WEAK && eEconomicStrength <= STRENGTH_POWERFUL) { SetEasyTarget(ePlayer, true); continue; } if (eEconomicStrength <= STRENGTH_POOR && eMilitaryStrength <= STRENGTH_STRONG) { SetEasyTarget(ePlayer, true); continue; } if (eEconomicStrength <= STRENGTH_WEAK && eMilitaryStrength <= STRENGTH_POWERFUL) { SetEasyTarget(ePlayer, true); continue; } if (eMilitaryStrength <= STRENGTH_POOR && eTargetValue >= TARGET_VALUE_FAVORABLE) { SetEasyTarget(ePlayer, true); continue; } if (eMilitaryStrength <= STRENGTH_WEAK && eTargetValue >= TARGET_VALUE_AVERAGE) { SetEasyTarget(ePlayer, true); continue; } if (eEconomicStrength <= STRENGTH_POOR && eTargetValue >= TARGET_VALUE_FAVORABLE) { SetEasyTarget(ePlayer, true); continue; } if (eEconomicStrength <= STRENGTH_WEAK && eTargetValue >= TARGET_VALUE_AVERAGE) { SetEasyTarget(ePlayer, true); continue; } } if (!bLessAggressive && !bNoNewWars && iNumAggravators == iNumDeterrents && iNumAggravators > 1 && eTargetValue >= TARGET_VALUE_FAVORABLE && eMilitaryStrength <= STRENGTH_POOR && eEconomicStrength <= STRENGTH_POOR) { SetEasyTarget(ePlayer, true); continue; } if (bMoreAggressive && eTargetValue >= TARGET_VALUE_AVERAGE && iNumAggravators + 1 >= iNumDeterrents && !bNoNewWars) { if (eMilitaryStrength <= STRENGTH_POOR) { if (iNumAggravators + 1 > iNumDeterrents && eEconomicStrength <= STRENGTH_STRONG) { SetEasyTarget(ePlayer, true); continue; } if (eEconomicStrength <= STRENGTH_AVERAGE || eTargetValue >= TARGET_VALUE_FAVORABLE) { SetEasyTarget(ePlayer, true); continue; } } if (eEconomicStrength <= STRENGTH_POOR && eMilitaryStrength <= STRENGTH_AVERAGE) { SetEasyTarget(ePlayer, true); continue; } } if (eMilitaryStrength <= STRENGTH_WEAK) { if (bLessAggressive && eEconomicStrength <= STRENGTH_POOR) { SetEasyTarget(ePlayer, true); continue; } if (!bLessAggressive && eEconomicStrength <= STRENGTH_AVERAGE) { SetEasyTarget(ePlayer, true); continue; } if (bMoreAggressive && eEconomicStrength <= STRENGTH_STRONG) { SetEasyTarget(ePlayer, true); continue; } if (eTargetValue >= TARGET_VALUE_FAVORABLE) { SetEasyTarget(ePlayer, true); continue; } } if (eEconomicStrength <= STRENGTH_WEAK) { if (bLessAggressive && eMilitaryStrength <= STRENGTH_POOR) { SetEasyTarget(ePlayer, true); continue; } if (!bLessAggressive && eMilitaryStrength <= STRENGTH_AVERAGE) { SetEasyTarget(ePlayer, true); continue; } if (bMoreAggressive && eMilitaryStrength <= STRENGTH_STRONG) { SetEasyTarget(ePlayer, true); continue; } if (eTargetValue >= TARGET_VALUE_FAVORABLE) { SetEasyTarget(ePlayer, true); continue; } } SetEasyTarget(ePlayer, false); } else { SetEasyTarget(ePlayer, false); } } } // ----------------------------------------------------------------------------------------------- // //////////////////////////////////// // AGGRESSIVE POSTURES // //////////////////////////////////// int CvDiplomacyAI::CountAggressiveMilitaryScore(PlayerTypes ePlayer, bool bHalveDefenders) { CvPlayerAI& kPlayer = GET_PLAYER(ePlayer); CvTeam& kTeam = GET_TEAM(kPlayer.getTeam()); TeamTypes eOurTeam = GetTeam(); if (!IsAtWar(ePlayer)) { // Don't be frightened of City-States or vassals, they can't declare war on their own. if (kPlayer.isMinorCiv() || kPlayer.IsVassalOfSomeone()) return 0; } // Vassals count units for the purposes of determining deterrence against independence if (kPlayer.isMajorCiv() && !IsVassal(ePlayer)) { // We're allowing them Open Borders? We shouldn't care. if (GET_TEAM(GetTeam()).IsAllowsOpenBordersToTeam(kPlayer.getTeam())) return 0; // We're working together, so don't worry about it if (IsDoFAccepted(ePlayer) || IsHasDefensivePact(ePlayer)) return 0; // They resurrected us, so don't worry about it if ((WasResurrectedBy(ePlayer) || IsMasterLiberatedMeFromVassalage(ePlayer)) && !IsAtWar(ePlayer)) return 0; } // Sometimes we ignore other wars the player may be waging, and always count their units as aggressive bool bIgnoreOtherWars = GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) || IsAtWar(ePlayer) || IsVassal(ePlayer); bool bIsAtWarWithSomeone = bIgnoreOtherWars ? false : kTeam.getAtWarCount(false) > 0; // Loop through the other guy's units int iScore = 0; int iLoop = 0; for (CvUnit* pLoopUnit = kPlayer.firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = kPlayer.nextUnit(&iLoop)) { // Don't be scared of noncombat Units! if (pLoopUnit->IsCivilianUnit() || pLoopUnit->getUnitInfo().GetDefaultUnitAIType() == UNITAI_EXPLORE || pLoopUnit->getUnitInfo().GetDefaultUnitAIType() == UNITAI_EXPLORE_SEA) continue; CvPlot* pUnitPlot = pLoopUnit->plot(); // Can we actually see this Unit? No cheating! if (!pUnitPlot->isVisible(eOurTeam) || pLoopUnit->isInvisible(eOurTeam, false) || (pLoopUnit->isCargo() && pLoopUnit->getTransportUnit()->isInvisible(eOurTeam, false))) continue; // Must be close to us if (!pUnitPlot->IsCloseToCity(GetID())) continue; // At war with someone? Because if this Unit is in the vicinity of another player he's already at war with, don't count this Unit as aggressive if (bIsAtWarWithSomeone) { // Loop through all players... for (int iOtherPlayerLoop = 0; iOtherPlayerLoop < MAX_PLAYERS; iOtherPlayerLoop++) { PlayerTypes eLoopOtherPlayer = (PlayerTypes) iOtherPlayerLoop; TeamTypes eLoopOtherTeam = (TeamTypes) GET_PLAYER(eLoopOtherPlayer).getTeam(); // At war with this player? Slight "cheating" to the player's advantage here, in that the AI counts players they haven't met. if (GET_PLAYER(eLoopOtherPlayer).isAlive() && kTeam.isAtWar(eLoopOtherTeam)) { // Is this an ally of ours? We're not going to ignore it then. bool bAlly = GET_PLAYER(eLoopOtherPlayer).isMinorCiv() ? (GET_PLAYER(eLoopOtherPlayer).GetMinorCivAI()->IsProtectedByMajor(GetID()) || GET_PLAYER(eLoopOtherPlayer).GetMinorCivAI()->GetAlly() == GetID()) : IsFriendOrAlly(eLoopOtherPlayer); // Is the unit close to the other player? if (!bAlly && pUnitPlot->IsCloseToCity(eLoopOtherPlayer)) continue; } } } // If the Unit is in the other team's territory, halve its "aggression value", since he may just be defending himself if (bHalveDefenders && pUnitPlot->getOwner() != NO_PLAYER && GET_PLAYER(pUnitPlot->getOwner()).getTeam() == GET_PLAYER(ePlayer).getTeam()) iScore++; else iScore += 2; } return iScore; } /// Updates how aggressively all players' military Units are positioned in relation to us void CvDiplomacyAI::DoUpdateMilitaryAggressivePostures() { vector vPlayersToReevaluate; // Loop through all (known) Players for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(ePlayer)) { AggressivePostureTypes eLastPosture = GetMilitaryAggressivePosture(ePlayer); AggressivePostureTypes eAggressivePosture = AGGRESSIVE_POSTURE_NONE; // So how threatening is he being? int iAggressionScore = CountAggressiveMilitaryScore(ePlayer, true); if (iAggressionScore >= /*12*/ GD_INT_GET(MILITARY_AGGRESSIVE_POSTURE_THRESHOLD_INCREDIBLE)) eAggressivePosture = AGGRESSIVE_POSTURE_INCREDIBLE; else if (iAggressionScore >= /*8*/ GD_INT_GET(MILITARY_AGGRESSIVE_POSTURE_THRESHOLD_HIGH)) eAggressivePosture = AGGRESSIVE_POSTURE_HIGH; else if (iAggressionScore >= /*6*/ GD_INT_GET(MILITARY_AGGRESSIVE_POSTURE_THRESHOLD_MEDIUM)) eAggressivePosture = AGGRESSIVE_POSTURE_MEDIUM; else if (iAggressionScore >= /*2*/ GD_INT_GET(MILITARY_AGGRESSIVE_POSTURE_THRESHOLD_LOW)) eAggressivePosture = AGGRESSIVE_POSTURE_LOW; SetMilitaryAggressivePosture(ePlayer, eAggressivePosture); if (GET_PLAYER(ePlayer).isMajorCiv() && eAggressivePosture > eLastPosture && eAggressivePosture >= AGGRESSIVE_POSTURE_MEDIUM) vPlayersToReevaluate.push_back(ePlayer); } else { SetMilitaryAggressivePosture(ePlayer, AGGRESSIVE_POSTURE_NONE); } } // If someone's aggressive posture rose, reevaluate our approach towards them immediately DoReevaluatePlayers(vPlayersToReevaluate); } /// Updates how pissy the AI is about other players settling too near them /// Also tests whether other players have broken any expansion or land buying promises since last turn void CvDiplomacyAI::DoExpansionBickering() { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return; int iLoop = 0; int iLoop2 = 0; int iGameTurn = GC.getGame().getGameTurn(); int iExpansionBickerRange = GetExpansionBickerRange(); for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; if (!IsPlayerValid(ePlayer)) continue; // No point in updating this while at war if (IsAtWar(ePlayer)) continue; // Test to see if a land buying promise was broken if (GetNumTurnsBorderPromise(ePlayer) > 0) { AggressivePostureTypes eOldPosture = GetBorderPromisePosture(ePlayer); if (GetPlotBuyingAggressivePosture(ePlayer) > eOldPosture) SetBorderPromiseState(ePlayer, PROMISE_STATE_BROKEN); } // Don't bother checking further if they ignored/broke a promise, if bicker range is 0, or if they're our master. if (GetExpansionPromiseState(ePlayer) >= PROMISE_STATE_IGNORED || iExpansionBickerRange <= 0 || IsVassal(ePlayer)) { SetAngryAboutExpansion(ePlayer, false); continue; } // If they haven't expanded or if it's been too long for us to complain about their expansion, forget about it. if (GET_PLAYER(ePlayer).getNumCities() <= 1 || GET_PLAYER(ePlayer).GetNumCitiesFounded() == 0 || GET_PLAYER(ePlayer).GetTurnsSinceSettledLastCity() >= /*30*/ GD_INT_GET(EXPANSION_BICKER_TIMEOUT)) { SetAngryAboutExpansion(ePlayer, false); continue; } CvCity* pTheirCapital = GET_PLAYER(ePlayer).getCapitalCity(); if (!pTheirCapital || !pTheirCapital->plot()) continue; bool bFoundOne = false; for (CvCity* pLoopCity = GET_PLAYER(ePlayer).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iLoop)) { CvPlot* pCityPlot = pLoopCity->plot(); if (!pCityPlot) continue; // Ignore their capital if (pLoopCity->IsOriginalCapitalForPlayer(ePlayer)) continue; // If this city already existed last time we got mad, don't get mad again over it // This flag also excludes City-States purchased by Venice/Austria if (pLoopCity->IsIgnoredForExpansionBickering(GetID())) continue; // Can we actually see this city? No cheating! if (!pCityPlot->isRevealed(GetTeam())) continue; // Only count settled cities, not conquered ones if (GET_PLAYER(pLoopCity->getOriginalOwner()).getTeam() != GET_PLAYER(ePlayer).getTeam()) continue; // Must have founded the city recently int iTurnFounded = pLoopCity->getGameTurnFounded(); if (iGameTurn - iTurnFounded >= /*30*/ GD_INT_GET(EXPANSION_BICKER_TIMEOUT)) continue; // Must be close to one of our cities if (GetPlayer()->GetCityDistanceInPlots(pCityPlot) > iExpansionBickerRange) continue; int iDistanceFromTheirCapital = plotDistance(*pCityPlot, *pTheirCapital->plot()); // Check to see if any of our cities within range of them were founded earlier than theirs was for (CvCity* pOurLoopCity = GetPlayer()->firstCity(&iLoop2); pOurLoopCity != NULL; pOurLoopCity = GetPlayer()->nextCity(&iLoop2)) { CvPlot* pOurCityPlot = pOurLoopCity->plot(); if (!pOurCityPlot) continue; // If we acquired our city after they founded theirs, they're not being aggressive int iGameTurnAcquired = pOurLoopCity->getGameTurnAcquired(); if (iGameTurnAcquired > iTurnFounded) continue; else if (iGameTurnAcquired == iTurnFounded) { // If they're earlier in the player order, they're not being aggressive, they just got there first! if ((int)ePlayer < (int)GetID()) continue; } // Must be on same land area if (pCityPlot->getLandmass() != pOurCityPlot->getLandmass()) continue; // Is this city near them? int iDistanceBetweenUs = plotDistance(*pCityPlot, *pOurCityPlot); if (iDistanceBetweenUs > iExpansionBickerRange) continue; // If their city is closer to (or equidistant from) their own capital, it's within their sphere of influence - ignore it if (iDistanceFromTheirCapital <= iDistanceBetweenUs) continue; // We've got a reason to be mad! bFoundOne = true; break; } if (bFoundOne) break; } // AI is pissed! if (bFoundOne) { if (GetNumTurnsExpansionPromise(ePlayer) > 0) // Call this function, because expansion bickering occurs before TestOpinionModifiers() { SetExpansionPromiseState(ePlayer, PROMISE_STATE_BROKEN); // You broke the promise you made! // Flag all of their cities as ignored for future bickering for (CvCity* pLoopCity = GET_PLAYER(ePlayer).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iLoop)) { pLoopCity->SetIgnoredForExpansionBickering(GetID(), true); } } else SetAngryAboutExpansion(ePlayer, true); // Let's give them a piece of our mind! } else SetAngryAboutExpansion(ePlayer, false); } } /// How close to one of our cities must a newly-settled city be in order to trigger our aggression? int CvDiplomacyAI::GetExpansionBickerRange() const { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) || GetPlayer()->GetPlayerTraits()->IsExpansionist() || GetBoldness() >= 8) return /*7*/ GD_INT_GET(EXPANSION_BICKER_RANGE_HIGH); if (GetBoldness() >= 4) return /*6*/ GD_INT_GET(EXPANSION_BICKER_RANGE_MEDIUM); return /*5*/ GD_INT_GET(EXPANSION_BICKER_RANGE_LOW); } /// Updates how aggressively all players have bought land near us void CvDiplomacyAI::DoUpdatePlotBuyingAggressivePostures() { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return; vector vPlayersToReevaluate; // Loop through all (known) Players for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(ePlayer)) { AggressivePostureTypes ePosture = AGGRESSIVE_POSTURE_NONE; AggressivePostureTypes eCurrentPosture = GetPlotBuyingAggressivePosture(ePlayer); int iAggressionScore = 0; int iCityLoop = 0; // Loop through all of our Cities to see if this player has bought land near them for (CvCity* pLoopCity = GetPlayer()->firstCity(&iCityLoop); pLoopCity != NULL; pLoopCity = GetPlayer()->nextCity(&iCityLoop)) { iAggressionScore += pLoopCity->AI_GetNumPlotsAcquiredByOtherPlayer(ePlayer); } // Now See what our new Dispute Level should be if (iAggressionScore >= /*12*/ GD_INT_GET(PLOT_BUYING_POSTURE_INCREDIBLE_THRESHOLD)) ePosture = AGGRESSIVE_POSTURE_INCREDIBLE; else if (iAggressionScore >= /*9*/ GD_INT_GET(PLOT_BUYING_POSTURE_HIGH_THRESHOLD)) ePosture = AGGRESSIVE_POSTURE_HIGH; else if (iAggressionScore >= /*5*/ GD_INT_GET(PLOT_BUYING_POSTURE_MEDIUM_THRESHOLD)) ePosture = AGGRESSIVE_POSTURE_MEDIUM; else if (iAggressionScore >= /*3*/ GD_INT_GET(PLOT_BUYING_POSTURE_LOW_THRESHOLD)) ePosture = AGGRESSIVE_POSTURE_LOW; SetPlotBuyingAggressivePosture(ePlayer, ePosture); if (ePosture > eCurrentPosture) vPlayersToReevaluate.push_back(ePlayer); } else { SetPlotBuyingAggressivePosture(ePlayer, AGGRESSIVE_POSTURE_NONE); } } // If someone's aggressive posture rose, reevaluate our approach towards them immediately DoReevaluatePlayers(vPlayersToReevaluate); } // ----------------------------------------------------------------------------------------------- // //////////////////////////////////// // DISPUTE LEVELS // //////////////////////////////////// /// Updates what our level of Dispute is with a player over Land void CvDiplomacyAI::DoUpdateLandDisputeLevels() { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return; int iEra = GetPlayer()->GetCurrentEra(); bool bCramped = GetPlayer()->IsCramped(); PlayerTypes eSettleSpotThief = GetPlayer()->GetPlayerWhoStoleMyFavoriteCitySite(); bool bBold = GetBoldness() > 7 || GetPlayer()->GetPlayerTraits()->IsWarmonger() || GetPlayer()->GetPlayerTraits()->IsExpansionist() || (IsCompetingForVictory() && IsGoingForWorldConquest()); bBold |= GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_EXPANSION")) > 7; vector vPlayersToReevaluate; // Loop through all (known) Players for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; // Update last turn's values DisputeLevelTypes eCurrentDisputeLevel = GetLandDisputeLevel(ePlayer); if (IsPlayerValid(ePlayer)) { // Look at our Proximity to the other Player PlayerProximityTypes eProximity = GetPlayer()->GetProximityToPlayer(ePlayer); if (eProximity < PLAYER_PROXIMITY_CLOSE) { SetLandDisputeLevel(ePlayer, DISPUTE_LEVEL_NONE); continue; } // Ignore masters and vassals if (IsVassal(ePlayer) || (IsMaster(ePlayer) && !GetPlayer()->OwnsOurCity(ePlayer))) { SetLandDisputeLevel(ePlayer, DISPUTE_LEVEL_NONE); continue; } int iContestedScore = 0; // Loop through all of this player's Cities int iCityLoop = 0; for (const CvCity* pLoopCity = m_pPlayer->firstCity(&iCityLoop); pLoopCity != NULL; pLoopCity = m_pPlayer->nextCity(&iCityLoop)) { iContestedScore += pLoopCity->GetContestedPlotScore(ePlayer); } // Is the player already cramped in on other sides? If so, bump up the score int iMultiplier = bCramped ? /*50*/ GD_INT_GET(LAND_DISPUTE_CRAMPED_MULTIPLIER) : 0; // Land disputes matter more if a warmonger, or in the early game. if (bBold) iMultiplier += 50; if (iEra <= GD_INT_GET(ANCIENT_ERA)) iMultiplier += 100; else if (iEra <= GD_INT_GET(CLASSICAL_ERA)) iMultiplier += 50; else if (iEra <= GD_INT_GET(MEDIEVAL_ERA)) iMultiplier += 25; iContestedScore *= 100 + iMultiplier; iContestedScore /= 100; // Now see what our new Dispute Level should be DisputeLevelTypes eDisputeLevel = DISPUTE_LEVEL_NONE; if (iContestedScore >= /*8*/ GD_INT_GET(LAND_DISPUTE_FIERCE_THRESHOLD)) eDisputeLevel = DISPUTE_LEVEL_FIERCE; else if (iContestedScore >= /*4*/ GD_INT_GET(LAND_DISPUTE_STRONG_THRESHOLD)) eDisputeLevel = DISPUTE_LEVEL_STRONG; else if (iContestedScore >= /*1*/ GD_INT_GET(LAND_DISPUTE_WEAK_THRESHOLD)) eDisputeLevel = DISPUTE_LEVEL_WEAK; // Did this player steal "our" favorite city settling spot? We should get mad! if (ePlayer == eSettleSpotThief) { if (bBold) { if (eDisputeLevel >= DISPUTE_LEVEL_WEAK) eDisputeLevel = DISPUTE_LEVEL_FIERCE; else eDisputeLevel = DISPUTE_LEVEL_STRONG; } else { if (eDisputeLevel >= DISPUTE_LEVEL_STRONG) eDisputeLevel = DISPUTE_LEVEL_FIERCE; else if (eDisputeLevel == DISPUTE_LEVEL_WEAK) eDisputeLevel = DISPUTE_LEVEL_STRONG; else eDisputeLevel = DISPUTE_LEVEL_WEAK; } } // Actually set the Level if (eCurrentDisputeLevel != eDisputeLevel) { SetLandDisputeLevel(ePlayer, eDisputeLevel); if (eCurrentDisputeLevel > eDisputeLevel) vPlayersToReevaluate.push_back(ePlayer); if (GC.getLogging() && GC.getAILogging()) { // Find the name of this civ and city CvString playerName = GetPlayer()->getCivilizationShortDescription(); CvString otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); // Open the log file CvString strLogName = GC.getPlayerAndCityAILogSplit() ? "DiplomacyAI_ExpansionLogic_Log_" + playerName + ".csv" : "DiplomacyAI_ExpansionLogic_Log.csv"; FILogFile* pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line CvString strBaseString; strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName + ", " + otherPlayerName + ", "; char const*names[] = { "DISPUTE_LEVEL_NONE", "DISPUTE_LEVEL_WEAK", "DISPUTE_LEVEL_STRONG", "DISPUTE_LEVEL_FIERCE" }; // Actual info CvString strOutBuf(strBaseString); CvString strTmp; strTmp.Format("%s, score %d", names[eDisputeLevel], iContestedScore); strOutBuf += strTmp; pLog->Msg(strOutBuf); } } } else { SetLandDisputeLevel(ePlayer, DISPUTE_LEVEL_NONE); } } // If someone's land dispute level increased, reevaluate that player's approach immediately DoReevaluatePlayers(vPlayersToReevaluate); } /// Updates what our level of Dispute is with all players over World Wonders void CvDiplomacyAI::DoUpdateWonderDisputeLevels() { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return; int iCompetitiveness = GetWonderCompetitiveness(); // Loop through all (known) Players for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(ePlayer)) { DisputeLevelTypes eDisputeLevel = DISPUTE_LEVEL_NONE; int iWonderDisputeWeight = GetNumWondersBeatenTo(ePlayer); // Add weight for player's competitiveness (1 - 10) iWonderDisputeWeight *= iCompetitiveness; if (iWonderDisputeWeight >= /*24*/ GD_INT_GET(WONDER_DISPUTE_FIERCE_THRESHOLD)) eDisputeLevel = DISPUTE_LEVEL_FIERCE; else if (iWonderDisputeWeight >= /*12*/ GD_INT_GET(WONDER_DISPUTE_STRONG_THRESHOLD)) eDisputeLevel = DISPUTE_LEVEL_STRONG; else if (iWonderDisputeWeight >= /*1*/ GD_INT_GET(WONDER_DISPUTE_WEAK_THRESHOLD)) eDisputeLevel = DISPUTE_LEVEL_WEAK; SetWonderDisputeLevel(ePlayer, eDisputeLevel); } else { SetWonderDisputeLevel(ePlayer, DISPUTE_LEVEL_NONE); } } } /// Updates what our level of Dispute is with all players over Minor Civ Friendship void CvDiplomacyAI::DoUpdateMinorCivDisputeLevels() { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return; // Personality factors in quite a bit here, which is why we square the value int iPersonalityMod = GetMinorCivCompetitiveness() * GetMinorCivCompetitiveness(); // Ranges from 1 to 100 int iThreshold = ((10 - GetMinorCivCompetitiveness()) + GC.getGame().getCurrentEra()); PlayerTypes eMyPlayer = GetID(); vector vMinorsToCheck; for (int iPlayerLoop = MAX_MAJOR_CIVS; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eMinor = (PlayerTypes) iPlayerLoop; // Don't evaluate City-States that are unmet/dead if (!IsPlayerValid(eMinor)) continue; if (!GET_PLAYER(eMinor).isMinorCiv()) continue; // Ignore if League resolutions make it irrelevant if (GET_PLAYER(eMinor).GetMinorCivAI()->IsNoAlly()) continue; if (GET_PLAYER(eMinor).GetMinorCivAI()->GetPermanentAlly() == eMyPlayer) continue; // Ignore if we're aggressive towards this minor if (GetCivApproach(eMinor) <= CIV_APPROACH_HOSTILE) continue; vMinorsToCheck.push_back(eMinor); } for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; if (!IsPlayerValid(ePlayer)) { SetMinorCivDisputeLevel(ePlayer, DISPUTE_LEVEL_NONE); continue; } DisputeLevelTypes eDisputeLevel = DISPUTE_LEVEL_NONE; int iDisputeWeight = 0; bool bThreatening = GetNumTimesTheyLoweredOurInfluence(ePlayer) > 0 && GetNumTimesTheyLoweredOurInfluence(ePlayer) >= iThreshold; for (std::vector::iterator it = vMinorsToCheck.begin(); it != vMinorsToCheck.end(); ++it) { if (bThreatening || GET_PLAYER(*it).GetMinorCivAI()->IsFriends(eMyPlayer)) { bool bOneIsAllied = false; // Other Player is Allies with this minor if (GET_PLAYER(*it).GetMinorCivAI()->IsAllies(ePlayer)) { if (GetCivApproach(*it) == CIV_APPROACH_FRIENDLY) { iDisputeWeight += (iPersonalityMod / 2); } bOneIsAllied = true; } // Other Player is Friends with this minor else if (GET_PLAYER(*it).GetMinorCivAI()->IsFriends(ePlayer)) { //We're allies? Grr... if (GET_PLAYER(*it).GetMinorCivAI()->IsAllies(eMyPlayer)) { iDisputeWeight += iPersonalityMod; bOneIsAllied = true; } else { iDisputeWeight -= iPersonalityMod; } } //Let's look at influence. int iOurInfluence = GET_PLAYER(*it).GetMinorCivAI()->GetEffectiveFriendshipWithMajor(eMyPlayer); int iTheirInfluence = GET_PLAYER(*it).GetMinorCivAI()->GetEffectiveFriendshipWithMajor(ePlayer); if (bOneIsAllied) { iDisputeWeight += (iOurInfluence / 5); iDisputeWeight += (GetNumTimesTheyLoweredOurInfluence(ePlayer) * 10); int iInfluenceStart = iPersonalityMod; if (iTheirInfluence > iOurInfluence) { int iDifference = iTheirInfluence - iOurInfluence; //Are our influences within 20 of each other? if (iDifference <= 20) { iInfluenceStart *= 40; iInfluenceStart /= 10; } //Are our influences within 40 of each other? else if (iDifference <= 40) { iInfluenceStart *= 35; iInfluenceStart /= 10; } //Are our influences within 60 of each other? else if (iDifference <= 60) { iInfluenceStart *= 30; iInfluenceStart /= 10; } //Are our influences within 80 of each other? else if (iDifference <= 80) { iInfluenceStart *= 20; iInfluenceStart /= 10; } //Are our influences within 100 of each other? else if (iDifference <= 100) { iInfluenceStart *= 10; iInfluenceStart /= 10; } //Are our influences within 120 of each other? else if (iDifference <= 120) { iInfluenceStart *= 5; iInfluenceStart /= 10; } //Are our influences within 150 of each other? else if (iDifference <= 150) { iInfluenceStart *= 3; iInfluenceStart /= 10; } //Are our influences within 200 of each other? else if (iDifference <= 200) { iInfluenceStart *= 2; iInfluenceStart /= 10; } } else if (iOurInfluence > iTheirInfluence) { int iDifference = iOurInfluence - iTheirInfluence; //Are our influences within 20 of each other? if (iDifference <= 20) { iInfluenceStart *= 40; iInfluenceStart /= 10; } //Are our influences within 40 of each other? else if (iDifference <= 40) { iInfluenceStart *= 30; iInfluenceStart /= 10; } //Are our influences within 60 of each other? else if (iDifference <= 60) { iInfluenceStart *= 20; iInfluenceStart /= 10; } //Are our influences within 80 of each other? else if (iDifference <= 80) { iInfluenceStart *= 15; iInfluenceStart /= 10; } //Are our influences within 100 of each other? else if (iDifference <= 100) { iInfluenceStart *= 10; iInfluenceStart /= 10; } //Are our influences within 120 of each other? else if (iDifference <= 120) { iInfluenceStart *= 8; iInfluenceStart /= 10; } //Are our influences within 150 of each other? else if (iDifference <= 150) { iInfluenceStart *= 6; iInfluenceStart /= 10; } //Are our influences within 200 of each other? else if (iDifference <= 200) { iInfluenceStart *= 4; iInfluenceStart /= 10; } } // Tied? Ramp it up! if (iOurInfluence == iTheirInfluence) { iDisputeWeight += iOurInfluence; } else { // We have a PtP with this minor - bump it up a little bit. if (GET_PLAYER(*it).GetMinorCivAI()->IsProtectedByMajor(eMyPlayer)) { iInfluenceStart *= 15; iInfluenceStart /= 10; } iDisputeWeight += iInfluenceStart; } } // Neither of us is allied - but have they been lowering our influence? else { iDisputeWeight += (GetNumTimesTheyLoweredOurInfluence(ePlayer) * 20); } } } // Now see what our new Dispute Level should be if (iDisputeWeight >= /*1400*/ GD_INT_GET(MINOR_CIV_DISPUTE_FIERCE_THRESHOLD)) eDisputeLevel = DISPUTE_LEVEL_FIERCE; else if (iDisputeWeight >= /*1000*/ GD_INT_GET(MINOR_CIV_DISPUTE_STRONG_THRESHOLD)) eDisputeLevel = DISPUTE_LEVEL_STRONG; else if (iDisputeWeight >= /*700*/ GD_INT_GET(MINOR_CIV_DISPUTE_WEAK_THRESHOLD)) eDisputeLevel = DISPUTE_LEVEL_WEAK; SetMinorCivDisputeLevel(ePlayer, eDisputeLevel); } } // ----------------------------------------------------------------------------------------------- // //////////////////////////////////// // PLAYER TRUSTWORTHINESS // //////////////////////////////////// static void checkAndUpdateBackstabbingPenalty( CvDiplomacyAI* pThis, PlayerTypes eLoopPlayer, int iTurn, bool (CvDiplomacyAI::*isPenalty)(PlayerTypes) const, int (CvDiplomacyAI::*getPenaltyTurn)(PlayerTypes) const, void (CvDiplomacyAI::*setPenalty)(PlayerTypes, bool), int baseTurns) { if ((pThis->*isPenalty)(eLoopPlayer)) { int iTurnDifference = iTurn - (pThis->*getPenaltyTurn)(eLoopPlayer); int iTimeOutTurns = (baseTurns * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100; if (iTurnDifference >= std::max(1, iTimeOutTurns)) (pThis->*setPenalty)(eLoopPlayer, false); } } static void checkAndUpdatePromise( CvDiplomacyAI* pThis, PlayerTypes eLoopPlayer, int iTurn, bool (CvDiplomacyAI::*isPromise)(PlayerTypes) const, int (CvDiplomacyAI::*getPromiseTurn)(PlayerTypes) const, void (CvDiplomacyAI::*setPromise)(PlayerTypes, PromiseStates), int baseTurns, bool bGamespeed = true) { if ((pThis->*isPromise)(eLoopPlayer)) { int iTurnDifference = iTurn - (pThis->*getPromiseTurn)(eLoopPlayer); int iTimeOutTurns = bGamespeed ? (baseTurns * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100 : baseTurns; if (iTurnDifference >= std::max(1, iTimeOutTurns)) (pThis->*setPromise)(eLoopPlayer, NO_PROMISE_STATE); } } /// Forces an update of expired backstabbing penalties /// This will be called for dead players as well, when other players are examining whether backstabbing took place. void CvDiplomacyAI::TestBackstabbingPenalties() { int iTurn = GC.getGame().getGameTurn(); for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = static_cast(iPlayerLoop); if (!IsHasMet(eLoopPlayer)) continue; // Denounced us while we were friends? checkAndUpdateBackstabbingPenalty(this, eLoopPlayer, iTurn, &CvDiplomacyAI::IsFriendDenouncedUs, &CvDiplomacyAI::GetFriendDenouncedUsTurn, &CvDiplomacyAI::SetFriendDenouncedUs, /*75*/ GD_INT_GET(FRIEND_DENOUNCED_US_TURNS_UNTIL_FORGIVEN)); // Declared war on us while we were friends? checkAndUpdateBackstabbingPenalty(this, eLoopPlayer, iTurn, &CvDiplomacyAI::IsFriendDeclaredWarOnUs, &CvDiplomacyAI::GetFriendDeclaredWarOnUsTurn, &CvDiplomacyAI::SetFriendDeclaredWarOnUs, /*100*/ GD_INT_GET(FRIEND_DECLARED_WAR_ON_US_TURNS_UNTIL_FORGIVEN)); // Declared war on us while we were their vassal? checkAndUpdateBackstabbingPenalty(this, eLoopPlayer, iTurn, &CvDiplomacyAI::BrokeVassalAgreement, &CvDiplomacyAI::GetBrokeVassalAgreementTurn, &CvDiplomacyAI::SetBrokeVassalAgreement, /*100*/ GD_INT_GET(MASTER_DECLARED_WAR_ON_US_TURNS_UNTIL_FORGIVEN)); // Broke a promise not to declare war on us? // This promise does not scale with game speed! checkAndUpdatePromise(this, eLoopPlayer, iTurn, &CvDiplomacyAI::BrokeMilitaryPromise, &CvDiplomacyAI::GetMilitaryPromiseTurn, &CvDiplomacyAI::SetMilitaryPromiseState, /*80*/ GD_INT_GET(MILITARY_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN), false); // Broke a promise not to conquer our protected City-State? checkAndUpdatePromise(this, eLoopPlayer, iTurn, &CvDiplomacyAI::BrokeAttackCityStatePromise, &CvDiplomacyAI::GetAttackCityStatePromiseTurn, &CvDiplomacyAI::SetAttackCityStatePromiseState, /*60*/ GD_INT_GET(ATTACK_CS_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN)); // Ended Declaration of Friendship with us prematurely? // This one needs special handling (two args, special condition for friends) if (IsDoFBroken(eLoopPlayer)) { int iTurnDifference = iTurn - GetDoFBrokenTurn(eLoopPlayer); int iTimeOutTurns = (/*50*/ GD_INT_GET(DOF_BROKEN_TURNS_UNTIL_FORGIVEN) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100; // If we've made amends, take away the "broken DoF" malus sooner than usual. if (!GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && GetCivOpinion(eLoopPlayer) >= CIV_OPINION_FRIEND) iTimeOutTurns = /*10*/ std::max(GD_INT_GET(DOF_BROKEN_TURNS_UNTIL_FORGIVEN_FRIENDS), GD_INT_GET(DOF_BROKEN_BACKSTAB_TIMER)); if (iTurnDifference >= std::max(1, iTimeOutTurns)) SetDoFBroken(eLoopPlayer, false, false); } } } /// Loop through all players and decide if we view any of them as backstabbers void CvDiplomacyAI::TestUntrustworthyFriends() { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = static_cast(iPlayerLoop); SetUntrustworthyFriend(ePlayer, GET_PLAYER(ePlayer).isAlive() && IsHasMet(ePlayer) && TestOnePlayerUntrustworthyFriend(ePlayer)); } } /// Do we view this player as a backstabber? bool CvDiplomacyAI::TestOnePlayerUntrustworthyFriend(PlayerTypes ePlayer) { if (IsAlwaysAtWar(ePlayer)) return true; // Vassals can't be untrustworthy, they have no rights. if (GET_PLAYER(ePlayer).IsVassalOfSomeone()) return false; // Stole our capital/Holy City? Unless we're your capitulated vassal, we don't care about anything you have to say... bool bCapitulatedVassal = IsVassal(ePlayer) && !IsVoluntaryVassalage(ePlayer); bool bResurrected = WasResurrectedBy(ePlayer); if (!bCapitulatedVassal && !bResurrected) { if (IsCapitalCapturedBy(ePlayer) || IsHolyCityCapturedBy(ePlayer)) return true; } vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); for (size_t i=0; iisHuman(ISHUMAN_AI_DIPLOMACY)) return false; int iNumBrokenAgreements = 0; int iNumFriendsDenounced = 0; int iNumFriendsAttacked = 0; int iNumCivsEver = GC.getGame().GetNumMajorCivsEver(); // Don't tolerate backstabbing if it could get us killed! bool bDangerous = (GC.getGame().GetNumMajorCivsAlive() <= iNumCivsEver / 2) || (GetPlayerNumMajorsConquered(ePlayer) >= iNumCivsEver / 3); // If true, ignore backstabbing against anyone who isn't too important. bool bBackstabber = IsBackstabber() && !bDangerous; // Loop through all other civs for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (eLoopPlayer == GetID() || !IsHasMet(eLoopPlayer, true)) continue; if (CvDiplomacyAIHelpers::IgnoresBackstabbing(GetID(), eLoopPlayer)) continue; // Clear any expired penalties since those won't be updated automatically if the player is dead if (!GET_PLAYER(eLoopPlayer).isAlive()) GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->TestBackstabbingPenalties(); // Do we particularly care about this player's opinion? bool bPriority = IsFriendOrAlly(eLoopPlayer); // If they're not a priority and it's not dangerous, we might ignore any backstabbing towards them. if (!bPriority && !bDangerous) { if (bBackstabber) continue; if (IsAtWar(eLoopPlayer)) continue; if (IsDenouncedPlayer(eLoopPlayer)) continue; if (GetCivOpinion(eLoopPlayer) == CIV_OPINION_UNFORGIVABLE) continue; if (GetWarmongerThreat(eLoopPlayer) == THREAT_CRITICAL) continue; bool bLoopPlayerDangerous = GetPlayerNumMajorsConquered(eLoopPlayer) >= iNumCivsEver / 3; if (bLoopPlayerDangerous) continue; if (GetBiggestCompetitor() == eLoopPlayer) continue; } // This player is important to us - backstabbing is unacceptable! if (bPriority) { for (size_t i=0; iIsNukedBy(eTeamMember)) return true; } } // Normal check else { CvDiplomacyAI* pDiploAI = GET_PLAYER(eLoopPlayer).GetDiplomacyAI(); if (IsVassal(ePlayer) && pDiploAI->BrokeVassalAgreement(ePlayer)) return true; // If multiple players on ePlayer's team backstab the same player in the same way, it still only counts as one betrayal bool bFriendDenouncedUs = false; bool bFriendAttackedUs = false; bool bBrokeMilitaryPromise = false; bool bBrokeAttackCityStatePromise = false; bool bBrokeVassalAgreement = false; for (size_t i=0; iIsFriendDeclaredWarOnUs(eTeamMember) || pDiploAI->IsResurrectorAttackedUs(eTeamMember)) { if (bResurrected) { if (pDiploAI->IsResurrectorAttackedUs(eTeamMember)) return true; else continue; } if (!bFriendAttackedUs) { bFriendAttackedUs = true; iNumFriendsAttacked++; } } if (bResurrected) continue; if (pDiploAI->IsFriendDenouncedUs(eTeamMember) && !bFriendDenouncedUs) { bFriendDenouncedUs = true; iNumFriendsDenounced++; } if (pDiploAI->BrokeMilitaryPromise(eTeamMember) && !bBrokeMilitaryPromise) { bBrokeMilitaryPromise = true; iNumBrokenAgreements++; } if (pDiploAI->BrokeAttackCityStatePromise(eTeamMember) && !bBrokeAttackCityStatePromise) { bBrokeAttackCityStatePromise = true; iNumBrokenAgreements++; } if (pDiploAI->BrokeVassalAgreement(eTeamMember) && !bBrokeVassalAgreement) { bBrokeVassalAgreement = true; iNumBrokenAgreements++; } } } } // If we're a backstabber, we don't care who ePlayer has backstabbed if they aren't important to us (and ePlayer isn't dangerous) if (bBackstabber) return false; int iBrokenAgreementTolerance = (GetLoyalty() < 6) ? 3 : 2; int iFriendDenounceTolerance = (GetLoyalty() < 5) ? 2 : 1; int iFriendAttackedTolerance = (GetLoyalty() < 4) ? 1 : 0; if (GetForgiveness() > 8) { iBrokenAgreementTolerance += 2; iFriendDenounceTolerance++; } // Hard override in dangerous situations if (bDangerous) { iBrokenAgreementTolerance = 2; iFriendDenounceTolerance = 1; iFriendAttackedTolerance = 0; } // Be more tolerant if they've liberated one of our key cities. if (IsLiberator(ePlayer, true, true)) { iBrokenAgreementTolerance *= 2; iFriendDenounceTolerance++; iFriendAttackedTolerance++; } return (iNumBrokenAgreements > iBrokenAgreementTolerance) || (iNumFriendsDenounced > iFriendDenounceTolerance) || (iNumFriendsAttacked > iFriendAttackedTolerance); } // ----------------------------------------------------------------------------------------------- // //////////////////////////////////// // WAR SANITY CHECKS // //////////////////////////////////// /// Prevent the AI from being stupid and wrecking its empire/diplomatic relationships by declaring war on the wrong person void CvDiplomacyAI::DoUpdateSaneDiplomaticTargets() { // Never check for sanity if war scenario flags are active! if (GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR) || GC.getGame().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE)) return; // Are we in bad shape? If so, we should avoid entering additional wars. bool bNoNewWars = GetPlayer()->IsNoNewWars(); for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; if (ePlayer == NO_PLAYER || ePlayer == BARBARIAN_PLAYER) continue; if (!GET_PLAYER(ePlayer).isAlive()) continue; if (!IsHasMet(ePlayer)) continue; // If we're already at war, they're a sane target. if (IsAtWar(ePlayer)) { SetSaneDiplomaticTarget(ePlayer, true); continue; } // No new wars! Exception for endgame aggression and players who captured our key cities. if (bNoNewWars && !IsCapitalCapturedBy(ePlayer, true, false) && !IsHolyCityCapturedBy(ePlayer, true, false) && (!GET_PLAYER(ePlayer).isMajorCiv() || !IsEndgameAggressiveTo(ePlayer))) { SetSaneDiplomaticTarget(ePlayer, false); continue; } // Check if this is a sane financial decision. if (IsWarWouldBankruptUs(ePlayer)) { SetSaneDiplomaticTarget(ePlayer, false); continue; } // Check if this is a sane diplomatic decision for us. if (!DoUpdateOnePlayerSaneDiplomaticTarget(ePlayer, /*bImpulse*/ false)) { SetSaneDiplomaticTarget(ePlayer, false); continue; } // All seems in order...full steam ahead! SetSaneDiplomaticTarget(ePlayer, true); } } /// Would we go bankrupt soon if we went to war with ePlayer? bool CvDiplomacyAI::IsWarWouldBankruptUs(PlayerTypes ePlayer, int iMinimumIncome) { if (IsAtWar(ePlayer)) return false; int iTreasuryGold = GetPlayer()->GetTreasury()->GetGold(); int iIncome = GetPlayer()->getAvgGoldRate(); int iGoldPerTurnLostFromWar = CalculateGoldPerTurnLostFromWar(ePlayer); // Would we actually gain gold from this war declaration? if (iGoldPerTurnLostFromWar <= 0) return false; // If we're already bankrupt, don't bother with further checks. if (iTreasuryGold <= 0 && iIncome <= 0) return true; // If we set a minimum income threshold and we're below that, abort! if (iMinimumIncome > 0) { int iAdjustedGPT = iIncome - iGoldPerTurnLostFromWar; if (iAdjustedGPT < iMinimumIncome) { return true; } } int iTurnsUntilBankruptcy = GetPlayer()->getTurnsToBankruptcy(iGoldPerTurnLostFromWar); return iTurnsUntilBankruptcy <= 30; } /// How much Gold Per Turn would we lose if we went to war with this player? int CvDiplomacyAI::CalculateGoldPerTurnLostFromWar(PlayerTypes ePlayer) { if (IsAtWar(ePlayer)) return 0; int iGPT = 0; PlayerTypes eMyPlayer = GetID(); // City-State - easy calculation if (GET_PLAYER(ePlayer).isMinorCiv()) { iGPT += (GetPlayer()->GetTrade()->GetTradeGPTLostFromWarTimes100(ePlayer) / 100); iGPT += GET_PLAYER(ePlayer).GetMinorCivAI()->GetCurrentGoldBonus(eMyPlayer); return iGPT; } iGPT += GetPlayer()->GetTrade()->GetTradeGPTLostFromWarTimes100(ePlayer); iGPT += (GC.getGame().GetGameDeals().GetDealGPTLostFromWar(GetID(), ePlayer) * 100); // Vassal taxes? if (IsMaster(ePlayer)) { iGPT += GetPlayer()->GetTreasury()->GetMyShareOfVassalTaxes(GET_PLAYER(ePlayer).getTeam()); } vector vDefensiveWarAllies = GetDefensiveWarAllies(ePlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ true, /*bNewWarsOnly*/ true); for (std::vector::iterator it = vDefensiveWarAllies.begin(); it != vDefensiveWarAllies.end(); ++it) { iGPT += GetPlayer()->GetTrade()->GetTradeGPTLostFromWarTimes100(*it); if (GET_PLAYER(*it).isMajorCiv()) { iGPT += (GC.getGame().GetGameDeals().GetDealGPTLostFromWar(eMyPlayer, *it) * 100); } else { iGPT += (GET_PLAYER(*it).GetMinorCivAI()->GetCurrentGoldBonus(eMyPlayer) * 100); } } // Bring it out of hundreds iGPT /= 100; return iGPT; } /// Is it diplomatically sane to declare war on this player? bool CvDiplomacyAI::DoUpdateOnePlayerSaneDiplomaticTarget(PlayerTypes ePlayer, bool bImpulse) { if (ePlayer < 0 || ePlayer >= MAX_CIV_PLAYERS) return false; // Already at war? if (IsAtWar(ePlayer)) return true; // City-States have a much easier check if (GET_PLAYER(ePlayer).isMinorCiv()) { // Don't declare war if we've pledged protection! if (GET_PLAYER(ePlayer).GetMinorCivAI()->IsProtectedByMajor(GetID())) return false; PlayerTypes eAlly = GET_PLAYER(ePlayer).GetMinorCivAI()->GetAlly(); if (eAlly != NO_PLAYER) { // Don't declare war on our own ally! if (eAlly == GetID()) return false; // Don't declare war on our friends' allies, either. if (IsFriendOrAlly(eAlly)) return false; } // Check to see who's protecting this City-State... for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(ePlayer).GetMinorCivAI()->IsProtectedByMajor(eLoopPlayer)) { // Don't declare war on our friends' protectorates... if (IsFriendOrAlly(eLoopPlayer)) { return false; } } } return true; } // Don't start an impulse war if we have a coop war planned! if (bImpulse && GetGlobalCoopWarAgainstState(ePlayer) == COOP_WAR_STATE_PREPARING) return false; // For major civs we have a lot more to consider... vector vDefensiveWarAllies = GetDefensiveWarAllies(ePlayer, /*bIncludeMinors*/ false, /*bReverseMode*/ true, /*bNewWarsOnly*/ true); vDefensiveWarAllies.push_back(ePlayer); // Loop through each major and decide if A) declaring war would be backstabbing them and B) we're okay with doing so. for (std::vector::iterator it = vDefensiveWarAllies.begin(); it != vDefensiveWarAllies.end(); ++it) { bool bDirect = (GET_PLAYER(*it).getTeam() == GET_PLAYER(ePlayer).getTeam()); bDirect |= GET_PLAYER(*it).GetDiplomacyAI()->IsVassal(ePlayer); if (CanBackstab(*it) && !IsWillingToAttackFriend(*it, bDirect, bImpulse)) return false; } // If this is an impulsive decision, run this by our teammates too. if (bImpulse) { vector vOurTeam = GET_TEAM(GetTeam()).getPlayers(); for (size_t i=0; iIsSaneDiplomaticTarget(ePlayer)) return false; if (GET_PLAYER(vOurTeam[i]).GetDiplomacyAI()->GetGlobalCoopWarAgainstState(ePlayer) == COOP_WAR_STATE_PREPARING) return false; } } // Seems everything is okay - let's go! return true; } /// Would declaring war on this person be considered backstabbing or otherwise disadvantageous in some way? bool CvDiplomacyAI::CanBackstab(PlayerTypes ePlayer) const { if (IsDoFAccepted(ePlayer)) return true; if (IsHasDefensivePact(ePlayer)) return true; if (GetCivOpinion(ePlayer) == CIV_OPINION_ALLY) return true; if (WasResurrectedBy(ePlayer)) return true; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->WasResurrectedBy(GetID())) return true; if (GetDoFType(ePlayer) == DOF_TYPE_BATTLE_BROTHERS) return true; if (IsPlayerMoveTroopsRequestAccepted(ePlayer)) return true; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeMilitaryPromise(GetID())) return true; if (IsLiberator(ePlayer, false, true)) return true; if (IsCityRecentlyLiberatedBy(ePlayer)) return true; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFBroken(GetID()) && GET_PLAYER(ePlayer).GetDiplomacyAI()->GetTurnsSinceDoFBroken(GetID()) < /*10*/ GD_INT_GET(DOF_BROKEN_BACKSTAB_TIMER)) return true; if (GetGlobalCoopWarWithState(ePlayer) >= COOP_WAR_STATE_PREPARING) return true; // Only check this for humans...AI handles this in SelectApproachTowardsVassal() if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && IsMaster(ePlayer)) return true; return false; } /// Are we devious enough to declare war on our friend? bool CvDiplomacyAI::IsWillingToAttackFriend(PlayerTypes ePlayer, bool bDirect, bool bImpulse) { // If this is called for a human, always return no if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return false; // No backstabbing if we're not competitive. if (!IsCompetingForVictory()) return false; // No backstabbing in the early game unless the flag is set if (GetPlayer()->GetCurrentEra() <= 1 && !IsBackstabber()) return false; // Never backstab if they resurrected us or vice versa if (WasResurrectedBy(ePlayer)) return false; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->WasResurrectedBy(GetID())) return false; CivApproachTypes eApproach = GetCivApproach(ePlayer); CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); // Too scared of them? if (eApproach == CIV_APPROACH_AFRAID) return false; // Like them too much? if (eOpinion == CIV_OPINION_ALLY) return false; bool bEndgameAggressive = IsEndgameAggressiveTo(ePlayer); bool bUntrustworthy = IsUntrustworthy(ePlayer); if (!bImpulse && !bEndgameAggressive && !bUntrustworthy) { // Liberating our cities? if (IsLiberator(ePlayer, false, true) && bDirect) return false; if (IsCityRecentlyLiberatedBy(ePlayer) && GetPlayer()->getCitiesLost() > 0) return false; // Friends for too long? if (GetDoFType(ePlayer) == DOF_TYPE_BATTLE_BROTHERS) return false; // Went on multiple coop wars together? if (GetCoopWarAgreementScore(ePlayer) >= 2) return false; // Don't declare war if we agreed to start a coop war with them, that's dumb. if (GetGlobalCoopWarWithState(ePlayer) >= COOP_WAR_STATE_PREPARING) return false; // Don't declare war if we promised not to attack! if (bDirect) { if (IsPlayerMoveTroopsRequestAccepted(ePlayer)) return false; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeMilitaryPromise(GetID())) return false; } } // What kind of backstabbing is this? // Backstab timer? bool bBackstabTimer = (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFBroken(GetID()) && GET_PLAYER(ePlayer).GetDiplomacyAI()->GetTurnsSinceDoFBroken(GetID()) < /*10*/ GD_INT_GET(DOF_BROKEN_BACKSTAB_TIMER)); if (bBackstabTimer && bDirect) { // Only do this if there are no consequences for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer)) { if (!CvDiplomacyAIHelpers::IgnoresBackstabbing(eLoopPlayer, ePlayer) && !CvDiplomacyAIHelpers::IgnoresBackstabbing(eLoopPlayer, GetID(), true)) { if (IsFriendOrAlly(eLoopPlayer)) { return false; } else if (!bEndgameAggressive) // Lower bar if endgame aggressive, we may not have time to win otherwise { if (IsStrategicTradePartner(eLoopPlayer)) { return false; } else if (!IsEasyTarget(eLoopPlayer)) { return false; } else if (!IsBackstabber() && GetCivOpinion(eLoopPlayer) >= CIV_OPINION_FAVORABLE) { return false; } } } } } } // Declaration of Friendship or Defensive Pact? else if (IsDoFAccepted(ePlayer) || (IsHasDefensivePact(ePlayer) && !IsWantsToEndDefensivePactWithPlayer(ePlayer))) { // Don't backstab if we have 7+ Loyalty if (!IsBackstabber() && GetLoyalty() > 6) return false; if (bDirect && !bImpulse) { // No direct unprovoked backstabbing if approach is NEUTRAL or FRIENDLY if (eApproach >= CIV_APPROACH_NEUTRAL) return false; // No direct unprovoked backstabbing if opinion is too high if (eOpinion == CIV_OPINION_FRIEND) return false; if (!IsBackstabber()) { // Loyalty 5 or 6 and at least Neutral? if (GetLoyalty() > 4 && eOpinion >= CIV_OPINION_NEUTRAL) return false; // Loyalty 4 and at least Favorable? if (GetLoyalty() == 4 && eOpinion == CIV_OPINION_FAVORABLE) return false; } } // We must be stronger than them bool bAggressive = IsConqueror() || GetPlayer()->GetPlayerTraits()->IsWarmonger() || GetPlayer()->GetPlayerTraits()->IsExpansionist(); bool bOurCitiesOK = !GetPlayer()->GetMilitaryAI()->IsExposedToEnemy(NULL, ePlayer); bool bTheirCitiesVulnerable = GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(ePlayer); bool bEasyTarget = IsEasyTarget(ePlayer); if (bDirect) { bool bAtLeastTwo = (bOurCitiesOK && bTheirCitiesVulnerable) || (bOurCitiesOK && bEasyTarget) || (bTheirCitiesVulnerable && bEasyTarget); if (bAtLeastTwo) { if (bAggressive) { if (GetMilitaryStrengthComparedToUs(ePlayer) >= STRENGTH_POWERFUL) return false; if (GetEconomicStrengthComparedToUs(ePlayer) >= STRENGTH_POWERFUL) return false; } else { if (GetMilitaryStrengthComparedToUs(ePlayer) >= STRENGTH_STRONG) return false; if (GetEconomicStrengthComparedToUs(ePlayer) >= STRENGTH_STRONG) return false; } } else if (bAggressive) { if (GetMilitaryStrengthComparedToUs(ePlayer) >= STRENGTH_AVERAGE) return false; if (GetEconomicStrengthComparedToUs(ePlayer) >= STRENGTH_AVERAGE) return false; } else { if (GetMilitaryStrengthComparedToUs(ePlayer) >= STRENGTH_POOR) return false; if (GetEconomicStrengthComparedToUs(ePlayer) >= STRENGTH_POOR) return false; } } // Wars against neighbors that are too strong are a bad idea. else { PlayerProximityTypes eProximity = GET_PLAYER(ePlayer).GetProximityToPlayer(GetID()); StrengthTypes eStrength = GetMilitaryStrengthComparedToUs(ePlayer); bool bStrengthOK = false; if (bAggressive) { if (eProximity == PLAYER_PROXIMITY_NEIGHBORS) { if (eStrength <= STRENGTH_AVERAGE) bStrengthOK = true; } else if (eProximity == PLAYER_PROXIMITY_CLOSE) { if (eStrength <= STRENGTH_STRONG) bStrengthOK = true; } else bStrengthOK = true; } else { if (eProximity == PLAYER_PROXIMITY_NEIGHBORS) { if (eStrength <= STRENGTH_POOR) bStrengthOK = true; } else if (eProximity == PLAYER_PROXIMITY_CLOSE) { if (eStrength <= STRENGTH_AVERAGE) bStrengthOK = true; } else bStrengthOK = true; } bool bDefenseOK = bStrengthOK && (bEasyTarget || bOurCitiesOK || bTheirCitiesVulnerable); if (!bDefenseOK) return false; } // We need a good reason to even consider backstabbing a friend... bool bGoodReason = IsBackstabber(); // if we've already backstabbed one friend, more willing to backstab others bGoodReason |= bEndgameAggressive; bGoodReason |= bUntrustworthy; bGoodReason |= IsCloseToWorldConquest() && GET_PLAYER(ePlayer).GetCapitalConqueror() == NO_PLAYER; bGoodReason |= GET_PLAYER(ePlayer).GetDiplomacyAI()->GetWeDeclaredWarOnFriendCount(GetID()) > 0; // they also backstabbed people bGoodReason |= GetBiggestCompetitor() == ePlayer; bGoodReason |= GetWarmongerThreat(ePlayer) >= THREAT_SEVERE; if (!bDirect) { bGoodReason |= IsGoingForWorldConquest() && GET_PLAYER(ePlayer).GetCapitalConqueror() == NO_PLAYER; bGoodReason |= IsMajorCompetitor(ePlayer); } if (!bGoodReason) return false; // Further checks are only necessary for Declarations of Friendship...breaking a DP doesn't earn a global backstabbing penalty if (IsDoFAccepted(ePlayer)) { // Okay, so we have a good reason. Are there any consequences from doing this that we're unwilling to face? if (bDirect) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer)) { if (!CvDiplomacyAIHelpers::IgnoresBackstabbing(eLoopPlayer, ePlayer) && !CvDiplomacyAIHelpers::IgnoresBackstabbing(eLoopPlayer, GetID(), true)) { if (IsFriendOrAlly(eLoopPlayer)) { return false; } else if (IsStrategicTradePartner(eLoopPlayer)) { return false; } else if (!IsBackstabber() && GetCivOpinion(eLoopPlayer) >= CIV_OPINION_FRIEND) { return false; } else { // Hatred by neighbors that are too strong is a bad idea. PlayerProximityTypes eProximity = GET_PLAYER(eLoopPlayer).GetProximityToPlayer(GetID()); StrengthTypes eStrength = GetMilitaryStrengthComparedToUs(eLoopPlayer); bool bStrengthOK = (eProximity == PLAYER_PROXIMITY_NEIGHBORS && eStrength < STRENGTH_AVERAGE) || (eProximity == PLAYER_PROXIMITY_CLOSE && eStrength < STRENGTH_POWERFUL) || (eProximity < PLAYER_PROXIMITY_CLOSE); bool bDefenseOK = bStrengthOK && (IsEasyTarget(eLoopPlayer) || !GetPlayer()->GetMilitaryAI()->IsExposedToEnemy(NULL, eLoopPlayer)); if (!bDefenseOK) return false; } } } } } // Indirect backstab via Defensive Pact - much lower bar. Aim here is to prevent AIs from getting caught up in DP gridlock. else { // Ignore vassals! if (!GET_PLAYER(ePlayer).IsVassalOfSomeone()) { // Don't do it if we're very loyal if (!IsBackstabber() && !bEndgameAggressive && GetLoyalty() > 8) return false; // Don't do it if we'd lose a valuable trade partner if (IsStrategicTradePartner(ePlayer)) return false; // Impulse wars against people we like are a bad idea. if (bImpulse && eOpinion >= CIV_OPINION_FRIEND) return false; } } } } // If we've gone through all the hoops then we're okay with it. return true; } // ----------------------------------------------------------------------------------------------- // //////////////////////////////////// // OPINION // //////////////////////////////////// /// Determines our Diplomatic Opinions of all players we've met void CvDiplomacyAI::DoUpdateOpinions() { // Loop through all (known) Majors bool bMPDealUpdates = MOD_ACTIVE_DIPLOMACY && GC.getGame().isReallyNetworkMultiPlayer() && !GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY); for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = static_cast(iPlayerLoop); if (GET_PLAYER(eLoopPlayer).isAlive() && !IsAlwaysAtWar(eLoopPlayer) && eLoopPlayer != GetID() && IsHasMet(eLoopPlayer, bMPDealUpdates)) DoUpdateOnePlayerOpinion(eLoopPlayer); } } /// What is our basic opinion of the role a player has in our game? void CvDiplomacyAI::DoUpdateOnePlayerOpinion(PlayerTypes ePlayer) { // Human shadow AI skips the normal code if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { if (IsAtWar(ePlayer) || IsDenouncedPlayer(ePlayer)) SetCivOpinion(ePlayer, CIV_OPINION_ENEMY); else if (IsDoFAccepted(ePlayer) || IsHasDefensivePact(ePlayer)) SetCivOpinion(ePlayer, CIV_OPINION_FRIEND); else SetCivOpinion(ePlayer, CIV_OPINION_NEUTRAL); return; } else if (MOD_ACTIVE_DIPLOMACY && IsTeammate(ePlayer) && GC.getGame().isReallyNetworkMultiPlayer() && !GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { // JdH => calculate ai to human trade priority for multiplayer DoUpdateHumanTradePriority(ePlayer, GD_INT_GET(OPINION_THRESHOLD_ALLY)); } else { int iOpinionWeight = CalculateCivOpinionWeight(ePlayer); SetCachedOpinionWeight(ePlayer, iOpinionWeight); CivOpinionTypes eOpinion = CIV_OPINION_ALLY; if (iOpinionWeight >= /*160*/ GD_INT_GET(OPINION_THRESHOLD_UNFORGIVABLE)) eOpinion = CIV_OPINION_UNFORGIVABLE; else if (iOpinionWeight >= /*80*/ GD_INT_GET(OPINION_THRESHOLD_ENEMY)) eOpinion = CIV_OPINION_ENEMY; else if (iOpinionWeight >= /*30*/ GD_INT_GET(OPINION_THRESHOLD_COMPETITOR)) eOpinion = CIV_OPINION_COMPETITOR; else if (iOpinionWeight > /*-30*/ GD_INT_GET(OPINION_THRESHOLD_FAVORABLE)) eOpinion = CIV_OPINION_NEUTRAL; else if (iOpinionWeight > /*-80*/ GD_INT_GET(OPINION_THRESHOLD_FRIEND)) eOpinion = CIV_OPINION_FAVORABLE; else if (iOpinionWeight > /*-160*/ GD_INT_GET(OPINION_THRESHOLD_ALLY)) eOpinion = CIV_OPINION_FRIEND; // JdH => calculate ai to human trade priority for multiplayer if (MOD_ACTIVE_DIPLOMACY && GC.getGame().isReallyNetworkMultiPlayer()) { DoUpdateHumanTradePriority(ePlayer, iOpinionWeight); } // Finally, set the Opinion SetCivOpinion(ePlayer, eOpinion); } } /// What is the number value of our opinion towards ePlayer? int CvDiplomacyAI::CalculateCivOpinionWeight(PlayerTypes ePlayer) { int iOpinionWeight = GetBaseOpinionScore(ePlayer); ////////////////////////////////////// // DISPUTE MODIFIERS ////////////////////////////////////// iOpinionWeight += GetLandDisputeLevelScore(ePlayer); iOpinionWeight += GetWonderDisputeLevelScore(ePlayer); iOpinionWeight += GetMinorCivDisputeLevelScore(ePlayer); iOpinionWeight += GetTechBlockLevelScore(ePlayer); iOpinionWeight += GetPolicyBlockLevelScore(ePlayer); iOpinionWeight += GetVictoryDisputeLevelScore(ePlayer); iOpinionWeight += GetVictoryBlockLevelScore(ePlayer); iOpinionWeight += GetRecklessExpanderScore(ePlayer); iOpinionWeight += GetWonderSpammerScore(ePlayer); ////////////////////////////////////// // WAR STUFF ////////////////////////////////////// iOpinionWeight += GetMilitaryAggressivePostureScore(ePlayer); iOpinionWeight += GetWarmongerThreatScore(ePlayer); iOpinionWeight += GetTradeRoutesPlunderedScore(ePlayer); iOpinionWeight += max(GetCivilianKillerScore(ePlayer), GetCivilianKillerGlobalScore(ePlayer)); iOpinionWeight += GetNukedByScore(ePlayer); iOpinionWeight += GetHolyCityCapturedByScore(ePlayer); iOpinionWeight += GetCapitalCapturedByScore(ePlayer); ////////////////////////////////////// // Player has done nice stuff ////////////////////////////////////// iOpinionWeight += GetRecentTradeScore(ePlayer); iOpinionWeight += GetRecentAssistScore(ePlayer); iOpinionWeight += GetCommonFoeScore(ePlayer); iOpinionWeight += GetCiviliansReturnedToMeScore(ePlayer); iOpinionWeight += GetLandmarksBuiltForMeScore(ePlayer); iOpinionWeight += GetResurrectedScore(ePlayer); iOpinionWeight += GetLiberatedCapitalScore(ePlayer); iOpinionWeight += GetLiberatedHolyCityScore(ePlayer); iOpinionWeight += GetLiberatedCitiesScore(ePlayer); iOpinionWeight += GetReturnedCapitalScore(ePlayer); iOpinionWeight += GetReturnedHolyCityScore(ePlayer); iOpinionWeight += GetEmbassyScore(ePlayer); iOpinionWeight += GetDiplomatScore(ePlayer); iOpinionWeight += GetForgaveForSpyingScore(ePlayer); iOpinionWeight += GetTimesIntrigueSharedScore(ePlayer); ////////////////////////////////////// // Player has done mean stuff ////////////////////////////////////// iOpinionWeight += GetTimesCultureBombedScore(ePlayer); iOpinionWeight += GetTimesRobbedScore(ePlayer); iOpinionWeight += GetTimesPlottedAgainstUsScore(ePlayer); iOpinionWeight += GetTimesPerformedCoupScore(ePlayer); iOpinionWeight += GetStoleArtifactsScore(ePlayer); ////////////////////////////////////// // Player has asked us to do things we don't like ////////////////////////////////////// iOpinionWeight += GetNoSettleRequestScore(ePlayer); iOpinionWeight += GetStopSpyingRequestScore(ePlayer); iOpinionWeight += GetDemandMadeScore(ePlayer); ////////////////////////////////////// // DENOUNCING ////////////////////////////////////// iOpinionWeight += GetDenouncedScore(ePlayer); iOpinionWeight += GetDenouncedFriendScore(ePlayer); iOpinionWeight += GetDenouncedEnemyScore(ePlayer); iOpinionWeight += GetDenouncedByOurFriendScore(ePlayer); ////////////////////////////////////// // PROMISES ////////////////////////////////////// iOpinionWeight += GetBrokenMilitaryPromiseScore(ePlayer); iOpinionWeight += GetBrokenMilitaryPromiseWithAnybodyScore(ePlayer); iOpinionWeight += GetIgnoredMilitaryPromiseScore(ePlayer); iOpinionWeight += GetBrokenExpansionPromiseScore(ePlayer); iOpinionWeight += GetIgnoredExpansionPromiseScore(ePlayer); iOpinionWeight += GetBrokenBorderPromiseScore(ePlayer); iOpinionWeight += GetIgnoredBorderPromiseScore(ePlayer); iOpinionWeight += GetBrokenAttackCityStatePromiseScore(ePlayer); iOpinionWeight += GetBrokenAttackCityStatePromiseWithAnybodyScore(ePlayer); iOpinionWeight += GetIgnoredAttackCityStatePromiseScore(ePlayer); iOpinionWeight += GetBrokenBullyCityStatePromiseScore(ePlayer); iOpinionWeight += GetIgnoredBullyCityStatePromiseScore(ePlayer); iOpinionWeight += GetBrokenNoConvertPromiseScore(ePlayer); iOpinionWeight += GetIgnoredNoConvertPromiseScore(ePlayer); iOpinionWeight += GetBrokenNoDiggingPromiseScore(ePlayer); iOpinionWeight += GetIgnoredNoDiggingPromiseScore(ePlayer); iOpinionWeight += GetBrokenSpyPromiseScore(ePlayer); iOpinionWeight += GetIgnoredSpyPromiseScore(ePlayer); iOpinionWeight += GetBrokenCoopWarPromiseScore(ePlayer); ////////////////////////////////////// // RELIGION/IDEOLOGY ////////////////////////////////////// iOpinionWeight += GetPolicyScore(ePlayer); iOpinionWeight += GetReligionScore(ePlayer); iOpinionWeight += GetReligiousConversionPointsScore(ePlayer); iOpinionWeight += GetIdeologyScore(ePlayer); ////////////////////////////////////// // PROTECTED MINORS ////////////////////////////////////// iOpinionWeight += GetPtPSameCSScore(ePlayer); iOpinionWeight += GetAngryAboutProtectedMinorKilledScore(ePlayer); iOpinionWeight += GetAngryAboutProtectedMinorAttackedScore(ePlayer); iOpinionWeight += GetAngryAboutProtectedMinorBulliedScore(ePlayer); iOpinionWeight += GetAngryAboutSidedWithProtectedMinorScore(ePlayer); ////////////////////////////////////// // DECLARATION OF FRIENDSHIP ////////////////////////////////////// iOpinionWeight += GetDOFAcceptedScore(ePlayer); iOpinionWeight += GetDOFWithAnyFriendScore(ePlayer); iOpinionWeight += GetDOFWithAnyEnemyScore(ePlayer); ////////////////////////////////////// // TRADE AGREEMENTS ////////////////////////////////////// iOpinionWeight += GetDPAcceptedScore(ePlayer); iOpinionWeight += GetDPWithAnyFriendScore(ePlayer); iOpinionWeight += GetDPWithAnyEnemyScore(ePlayer); iOpinionWeight += GetOpenBordersScore(ePlayer); iOpinionWeight += GetResearchAgreementScore(ePlayer); ////////////////////////////////////// // TRAITOR OPINION ////////////////////////////////////// int iTraitorOpinion = GetFriendDenouncementScore(ePlayer); iTraitorOpinion = max(iTraitorOpinion, GetPlayerDenouncedFriendScore(ePlayer)); iTraitorOpinion = max(iTraitorOpinion, GetFriendDenouncedUsScore(ePlayer)); iTraitorOpinion = max(iTraitorOpinion, GetPlayerAttackedVassalScore(ePlayer)); iTraitorOpinion = max(iTraitorOpinion, GetMasterAttackedUsScore(ePlayer)); iTraitorOpinion = max(iTraitorOpinion, GetPlayerAttackedFriendScore(ePlayer)); iTraitorOpinion = max(iTraitorOpinion, GetFriendAttackedUsScore(ePlayer)); iTraitorOpinion = max(iTraitorOpinion, GetPlayerAttackedResurrectedCivScore(ePlayer)); iTraitorOpinion = max(iTraitorOpinion, GetResurrectorAttackedUsScore(ePlayer)); iOpinionWeight += iTraitorOpinion; ////////////////////////////////////// // WORLD CONGRESS ////////////////////////////////////// iOpinionWeight += GetVotingHistoryOpinionScore(ePlayer); iOpinionWeight += GetLikedTheirProposalScore(ePlayer); iOpinionWeight += GetDislikedTheirProposalScore(ePlayer); iOpinionWeight += GetSupportedOurProposalScore(ePlayer); iOpinionWeight += GetSupportedMyHostingScore(ePlayer); iOpinionWeight += GetSanctionedUsScore(ePlayer); ////////////////////////////////////// // VASSALAGE ////////////////////////////////////// iOpinionWeight += GetVassalScore(ePlayer); iOpinionWeight += GetVassalTreatedScore(ePlayer); iOpinionWeight += GetVassalProtectScore(ePlayer); iOpinionWeight += GetMasterScore(ePlayer); iOpinionWeight += GetTooManyVassalsScore(ePlayer); iOpinionWeight += GetSameMasterScore(ePlayer); iOpinionWeight += GetMasterLiberatedMeFromVassalageScore(ePlayer); iOpinionWeight += GetHappyAboutVassalagePeacefullyRevokedScore(ePlayer); iOpinionWeight += GetAngryAboutVassalageForcefullyRevokedScore(ePlayer); ////////////////////////////////////// // MODMOD MODIFIERS ////////////////////////////////////// if (MOD_EVENTS_DIPLO_MODIFIERS && !GC.getGame().isReallyNetworkMultiPlayer() && !GC.getGame().isNetworkMultiPlayer()) { std::vector aOpinions; iOpinionWeight += GetDiploModifiers(ePlayer, aOpinions); } return iOpinionWeight; } // JdH => calculate ai to human trade priority for multiplayer void CvDiplomacyAI::DoUpdateHumanTradePriority(PlayerTypes ePlayer, int iOpinionWeight) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS); if (!MOD_ACTIVE_DIPLOMACY) return; iOpinionWeight = max(iOpinionWeight, GD_INT_GET(OPINION_THRESHOLD_ALLY)); iOpinionWeight = min(iOpinionWeight, GD_INT_GET(OPINION_THRESHOLD_UNFORGIVABLE)); iOpinionWeight -= GD_INT_GET(OPINION_THRESHOLD_UNFORGIVABLE); // make it >= 0 if (iOpinionWeight < 0) { iOpinionWeight = 0; } float opinion = iOpinionWeight / (float)(GD_INT_GET(OPINION_THRESHOLD_ALLY) - GD_INT_GET(OPINION_THRESHOLD_UNFORGIVABLE)); int turnsPassed = GC.getGame().getGameTurn() - GetNumTurnsSinceSomethingSent(ePlayer); m_aTradePriority[ePlayer] = 10.0f * opinion + turnsPassed; // factor in turns since last contact and opinion of player } // ----------------------------------------------------------------------------------------------- // //////////////////////////////////// // GLOBAL POLITICS // //////////////////////////////////// /// Decide how we're going to handle other players in the world this turn void CvDiplomacyAI::DoUpdateGlobalPolitics() { vector v; DoUpdateMajorCivApproaches(v, /*bStrategic*/ true); DoUpdateMajorCompetitors(); DoRelationshipPairing(); DoUpdatePrimeLeagueAlly(); DoUpdateMajorCivApproaches(v, /*bStrategic*/ false); DoUpdatePlanningExchanges(); // called twice intentionally DoUpdatePrimeLeagueAlly(); // called twice intentionally DoUpdateMinorCivApproaches(); } /// Reevaluate our general Diplomatic Approach towards a single player void CvDiplomacyAI::DoReevaluatePlayer(PlayerTypes ePlayer, bool bMajorEvent, bool bCancelExchanges, bool bFromResurrection) { vector v(1, ePlayer); DoReevaluatePlayers(v, bMajorEvent, bCancelExchanges, bFromResurrection); } /// Reevaluate our general Diplomatic Approach towards all valid major civs void CvDiplomacyAI::DoReevaluateEveryone(bool bMajorEvent, bool bCancelExchanges, bool bFromResurrection) { vector v = GetAllValidMajorCivs(); DoReevaluatePlayers(v, bMajorEvent, bCancelExchanges, bFromResurrection); } /// Reevaluate our general Diplomatic Approach towards specified players void CvDiplomacyAI::DoReevaluatePlayers(vector& vTargetPlayers, bool bMajorEvent, bool bCancelExchanges, bool bFromResurrection) { if (vTargetPlayers.empty()) return; if (!GetPlayer()->isMajorCiv()) return; if (!GC.getGame().isFinalInitialized()) return; TestBackstabbingPenalties(); // If dead, halt here! if (!GetPlayer()->isAlive()) return; // Make sure the players we're supposed to reevaluate are valid vector vPlayersToReevaluate; for (std::vector::iterator it = vTargetPlayers.begin(); it != vTargetPlayers.end(); ++it) { PlayerTypes ePlayer = *it; if (ePlayer != NO_PLAYER && GET_PLAYER(ePlayer).isAlive() && IsHasMet(ePlayer) && GET_PLAYER(ePlayer).isMajorCiv() && std::find(vPlayersToReevaluate.begin(), vPlayersToReevaluate.end(), ePlayer) == vPlayersToReevaluate.end()) { vPlayersToReevaluate.push_back(ePlayer); } } if (vPlayersToReevaluate.empty()) return; // There is an unusual case in which a reevaluation update is requested by another function mid-war declaration // This can happen if a new player (Defensive Pact, CS ally) is met because of a war declaration, for example, which will trigger DoUpdateMilitaryAggressivePostures(), which can call DoReevaluatePlayers() // This can cause undefined behavior, as the AI will not know to update other functions first, since bMajorEvent == false // We can know that this occurred if bMajorEvent == false but a player we are currently at war with has NO_WAR_STATE_TYPE // The safest way to handle this edge case is to set bMajorEvent to true - not great for performance but oh well if (!bMajorEvent) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eLoopPlayer).isAlive() && IsAtWar(eLoopPlayer) && GetWarState(eLoopPlayer) == NO_WAR_STATE_TYPE) { bMajorEvent = true; break; } } } if (!GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { TestUntrustworthyFriends(); if (bFromResurrection) { DoUpdateCompetingForVictory(); DoUpdateRecklessExpanders(); DoUpdateWonderSpammers(); DoUpdateTechBlockLevels(); DoUpdatePolicyBlockLevels(); DoUpdateVictoryDisputeLevels(); DoUpdateVictoryBlockLevels(); DoUpdateOpinions(); } } else bCancelExchanges = false; // Major event (e.g., war declaration)? We have a lot more reevaluating to do. if (bMajorEvent) { GetPlayer()->DoUpdateWarDamageAndWeariness(/*bDamageOnly*/ true); DoUpdateWarStates(); GetPlayer()->DoTestEmpireInBadShapeForWar(); DoUpdatePlayerStrengthEstimates(); DoUpdateWarmongerThreats(); GetPlayer()->DoTestEmpireInBadShapeForWar(); // intentionally called twice because it updates based on data from DoUpdatePlayerStrengthEstimates() DoUpdateEasyTargets(); DoUpdateSaneDiplomaticTargets(); } for (std::vector::iterator it = vPlayersToReevaluate.begin(); it != vPlayersToReevaluate.end(); ++it) { DoUpdateOnePlayerOpinion(*it); // Reevaluate our exchange desires with this player next turn! if (bCancelExchanges) { SetWantsDoFWithPlayer(*it, false); SetWantsDefensivePactWithPlayer(*it, false); SetWantsResearchAgreementWithPlayer(*it, false); } } // Humans halt here! if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return; DoUpdatePrimeLeagueAlly(); DoUpdateMajorCivApproaches(vPlayersToReevaluate, /*bStrategic*/ true); DoUpdateMajorCivApproaches(vPlayersToReevaluate, /*bStrategic*/ false); DoUpdatePrimeLeagueAlly(); // called twice intentionally // Finally, we update peace treaty willingness DoUpdatePeaceTreatyWillingness(); } /// Updates our general Diplomatic Approach towards each major civilization we've met void CvDiplomacyAI::DoUpdateMajorCivApproaches(vector& vPlayersToReevaluate, bool bStrategic) { vector vValidPlayers; vector vPlayersToUpdate; vector vPostUpdatePlayers; std::map oldApproaches; bool bReevaluation = !vPlayersToReevaluate.empty(); bool bHuman = GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY); bool bNoCities = GetPlayer()->getCapitalCity() == NULL; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eLoopPlayer).isMajorCiv() && GET_PLAYER(eLoopPlayer).isAlive() && IsHasMet(eLoopPlayer, false)) { CivApproachTypes eOldApproach = GetCivApproach(eLoopPlayer); bool bPriorityUpdate = false; // Under certain circumstances, set the approach immediately // Prioritized approach updates are done first and don't depend on each other, therefore we can fairly use the new approach in the map ... // Only do this on the first pass, because we'll get the exact same result next time. if (IsAlwaysAtWar(eLoopPlayer)) { bPriorityUpdate = true; if (bStrategic) SelectAlwaysWarApproach(eLoopPlayer); } else if (bHuman) { bPriorityUpdate = true; if (bStrategic) SelectHumanApproach(eLoopPlayer); } else if (bNoCities) { bPriorityUpdate = true; if (bStrategic) SelectApproachIfWeHaveNoCities(eLoopPlayer); } else if (GET_PLAYER(eLoopPlayer).getCapitalCity() == NULL) { bPriorityUpdate = true; if (bStrategic) SelectApproachIfTheyHaveNoCities(eLoopPlayer); } else if (IsVassal(eLoopPlayer)) { bPriorityUpdate = true; if (bStrategic) SelectApproachTowardsMaster(eLoopPlayer); } if (bPriorityUpdate) { CivApproachTypes eUpdatedApproach = GetCivApproach(eLoopPlayer); oldApproaches.insert(std::make_pair(eLoopPlayer, eUpdatedApproach)); continue; } // Otherwise, add the old approach to the map ... oldApproaches.insert(std::make_pair(eLoopPlayer, eOldApproach)); // If this player is someone's vassal (and aren't a priority update), we'll deal with them after we're done with the regular approaches if (GET_PLAYER(eLoopPlayer).IsVassalOfSomeone()) { vPostUpdatePlayers.push_back(eLoopPlayer); } else { vValidPlayers.push_back(eLoopPlayer); // This player's opinion and approach scores should be compared for prioritization // If this is a between turns (reevaluation) update, we only reevaluate the players on the list (for performance reasons). if (bReevaluation && std::find(vPlayersToReevaluate.begin(), vPlayersToReevaluate.end(), eLoopPlayer) == vPlayersToReevaluate.end()) continue; // ... now we add this player to the list of civs to update approaches for this turn vPlayersToUpdate.push_back(eLoopPlayer); } } } // Now loop through each player and update their approach! for (std::vector::iterator it = vPlayersToUpdate.begin(); it != vPlayersToUpdate.end(); ++it) { SelectBestApproachTowardsMajorCiv(*it, bStrategic, vValidPlayers, vPlayersToReevaluate, oldApproaches); } // Lastly we handle any post update logic if (!bStrategic) { for (std::vector::iterator it = vPostUpdatePlayers.begin(); it != vPostUpdatePlayers.end(); ++it) { SelectApproachTowardsVassal(*it); } DoUpdateWarTargets(); } } /// Updates our Diplomatic Approach towards a Civ that we're at permanent war with void CvDiplomacyAI::SelectAlwaysWarApproach(PlayerTypes ePlayer) { if (GET_PLAYER(ePlayer).isMajorCiv()) { // Permanent war means ONLY WAR! vector vApproachScores(NUM_CIV_APPROACHES, 0); vApproachScores[CIV_APPROACH_WAR] = 9999; // Grab the old approach and scratch values for logging CivApproachTypes eOldApproach = GetCivApproach(ePlayer); vector vApproachScoresScratch; for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eLoopApproach = (CivApproachTypes) iApproachLoop; int iLastTurnValue = GetPlayerApproachValue(ePlayer, eLoopApproach); vApproachScoresScratch.push_back(iLastTurnValue); SetPlayerApproachValue(ePlayer, eLoopApproach, vApproachScores[iApproachLoop]); SetPlayerStrategicApproachValue(ePlayer, eLoopApproach, vApproachScores[iApproachLoop]); } // Update approach and log. SetCivApproach(ePlayer, CIV_APPROACH_WAR); SetCivStrategicApproach(ePlayer, CIV_APPROACH_WAR); LogMajorCivApproachUpdate(ePlayer, &vApproachScores[0], CIV_APPROACH_WAR, eOldApproach, GetSurfaceApproach(ePlayer)); LogApproachValueDeltas(ePlayer, &vApproachScores[0], &vApproachScoresScratch[0]); } else if (GET_PLAYER(ePlayer).isMinorCiv()) { SetCivApproach(ePlayer, CIV_APPROACH_WAR); } } /// Updates the human shadow AI's Diplomatic Approach towards a Civ void CvDiplomacyAI::SelectHumanApproach(PlayerTypes ePlayer) { if (GET_PLAYER(ePlayer).isMajorCiv()) { CivApproachTypes eApproach = CIV_APPROACH_NEUTRAL; vector vApproachScores(NUM_CIV_APPROACHES, 0); if (IsAtWar(ePlayer)) { eApproach = CIV_APPROACH_WAR; } else { if (IsDenouncedPlayer(ePlayer) || IsCapitalCapturedBy(ePlayer) || IsHolyCityCapturedBy(ePlayer)) { eApproach = CIV_APPROACH_HOSTILE; } else if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID())) { eApproach = CIV_APPROACH_GUARDED; } else if (IsDoFAccepted(ePlayer) || IsHasDefensivePact(ePlayer) || GetGlobalCoopWarWithState(ePlayer) >= COOP_WAR_STATE_PREPARING) { eApproach = CIV_APPROACH_FRIENDLY; } else { eApproach = CIV_APPROACH_NEUTRAL; } } // Grab the old approach and scratch values for logging CivApproachTypes eOldApproach = GetCivApproach(ePlayer); // Add some base weight to the approach we've selected vApproachScores[(int)eApproach] = 5; vector vApproachScoresScratch; for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eLoopApproach = (CivApproachTypes) iApproachLoop; int iLastTurnValue = GetPlayerApproachValue(ePlayer, eLoopApproach); vApproachScoresScratch.push_back(iLastTurnValue); SetPlayerApproachValue(ePlayer, eLoopApproach, vApproachScores[iApproachLoop]); SetPlayerStrategicApproachValue(ePlayer, eLoopApproach, vApproachScores[iApproachLoop]); } // Update approach and log. SetCivApproach(ePlayer, eApproach); SetCivStrategicApproach(ePlayer, eApproach); LogMajorCivApproachUpdate(ePlayer, &vApproachScores[0], eApproach, eOldApproach, GetSurfaceApproach(ePlayer)); LogApproachValueDeltas(ePlayer, &vApproachScores[0], &vApproachScoresScratch[0]); } else if (GET_PLAYER(ePlayer).isMinorCiv()) { if (IsAtWar(ePlayer)) { SetCivApproach(ePlayer, CIV_APPROACH_WAR); } else if (GET_PLAYER(ePlayer).GetMinorCivAI()->IsAllies(GetID()) || GET_PLAYER(ePlayer).GetMinorCivAI()->IsFriends(GetID()) || GET_PLAYER(ePlayer).GetMinorCivAI()->IsProtectedByMajor(GetID())) { SetCivApproach(ePlayer, CIV_APPROACH_FRIENDLY); } else { SetCivApproach(ePlayer, CIV_APPROACH_NEUTRAL); } } } /// Updates our Diplomatic Approach towards a Civ if we have no cities void CvDiplomacyAI::SelectApproachIfWeHaveNoCities(PlayerTypes ePlayer) { if (GET_PLAYER(ePlayer).isMajorCiv()) { CivApproachTypes eApproach = CIV_APPROACH_NEUTRAL; vector vApproachScores(NUM_CIV_APPROACHES, 0); if (IsAtWar(ePlayer)) { eApproach = CIV_APPROACH_WAR; } else { if (IsDenouncedPlayer(ePlayer) || IsCapitalCapturedBy(ePlayer) || IsHolyCityCapturedBy(ePlayer) || IsUntrustworthy(ePlayer)) { eApproach = CIV_APPROACH_HOSTILE; } else if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID())) { eApproach = CIV_APPROACH_GUARDED; } else if (WasResurrectedBy(ePlayer) || IsDoFAccepted(ePlayer) || IsHasDefensivePact(ePlayer)) { eApproach = CIV_APPROACH_FRIENDLY; } else if (GetNumCitiesCapturedBy(ePlayer) > 0) { eApproach = CIV_APPROACH_GUARDED; } else { eApproach = CIV_APPROACH_NEUTRAL; } } // Grab the old approach and scratch values for logging CivApproachTypes eOldApproach = GetCivApproach(ePlayer); // Add some base weight to the approach we've selected vApproachScores[(int)eApproach] = GetMajorCivApproachBias(eApproach); vector vApproachScoresScratch; for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eLoopApproach = (CivApproachTypes) iApproachLoop; int iLastTurnValue = GetPlayerApproachValue(ePlayer, eLoopApproach); vApproachScoresScratch.push_back(iLastTurnValue); SetPlayerApproachValue(ePlayer, eLoopApproach, vApproachScores[iApproachLoop]); SetPlayerStrategicApproachValue(ePlayer, eLoopApproach, vApproachScores[iApproachLoop]); } // Update approach and log. SetCivApproach(ePlayer, eApproach); SetCivStrategicApproach(ePlayer, eApproach); LogMajorCivApproachUpdate(ePlayer, &vApproachScores[0], eApproach, eOldApproach, GetSurfaceApproach(ePlayer)); LogApproachValueDeltas(ePlayer, &vApproachScores[0], &vApproachScoresScratch[0]); } else if (GET_PLAYER(ePlayer).isMinorCiv()) { SetCivApproach(ePlayer, CIV_APPROACH_NEUTRAL); } } /// Updates our Diplomatic Approach towards a Civ that has no cities void CvDiplomacyAI::SelectApproachIfTheyHaveNoCities(PlayerTypes ePlayer) { if (GET_PLAYER(ePlayer).isMajorCiv()) { CivApproachTypes eApproach = CIV_APPROACH_NEUTRAL; vector vApproachScores(NUM_CIV_APPROACHES, 0); if (IsAtWar(ePlayer)) { eApproach = CIV_APPROACH_WAR; } else { if (IsDenouncedPlayer(ePlayer) || IsUntrustworthy(ePlayer)) { eApproach = CIV_APPROACH_HOSTILE; } else if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID())) { eApproach = CIV_APPROACH_GUARDED; } else if (WasResurrectedBy(ePlayer) || IsDoFAccepted(ePlayer) || IsHasDefensivePact(ePlayer)) { eApproach = CIV_APPROACH_FRIENDLY; } else { eApproach = CIV_APPROACH_NEUTRAL; } } // Grab the old approach and scratch values for logging CivApproachTypes eOldApproach = GetCivApproach(ePlayer); // Add some base weight to the approach we've selected vApproachScores[(int)eApproach] = GetMajorCivApproachBias(eApproach); vector vApproachScoresScratch; for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eLoopApproach = (CivApproachTypes) iApproachLoop; int iLastTurnValue = GetPlayerApproachValue(ePlayer, eLoopApproach); vApproachScoresScratch.push_back(iLastTurnValue); SetPlayerApproachValue(ePlayer, eLoopApproach, vApproachScores[iApproachLoop]); SetPlayerStrategicApproachValue(ePlayer, eLoopApproach, vApproachScores[iApproachLoop]); } // Update approach and log. SetCivApproach(ePlayer, eApproach); SetCivStrategicApproach(ePlayer, eApproach); LogMajorCivApproachUpdate(ePlayer, &vApproachScores[0], eApproach, eOldApproach, GetSurfaceApproach(ePlayer)); LogApproachValueDeltas(ePlayer, &vApproachScores[0], &vApproachScoresScratch[0]); } else if (GET_PLAYER(ePlayer).isMinorCiv()) { if (IsAtWar(ePlayer)) { SetCivApproach(ePlayer, CIV_APPROACH_WAR); } else { SetCivApproach(ePlayer, CIV_APPROACH_NEUTRAL); } } } /// Updates our Diplomatic Approach towards a Master of ours void CvDiplomacyAI::SelectApproachTowardsMaster(PlayerTypes ePlayer) { if (GET_PLAYER(ePlayer).isMajorCiv()) { CivApproachTypes eApproach = CIV_APPROACH_NEUTRAL; vector vApproachScores(NUM_CIV_APPROACHES, 0); VassalTreatmentTypes eTreatmentLevel = GetVassalTreatmentLevel(ePlayer); if (IsVoluntaryVassalage(ePlayer)) { switch (eTreatmentLevel) { case NO_VASSAL_TREATMENT: UNREACHABLE(); case VASSAL_TREATMENT_CONTENT: case VASSAL_TREATMENT_DISAGREE: eApproach = CIV_APPROACH_FRIENDLY; break; case VASSAL_TREATMENT_MISTREATED: eApproach = CIV_APPROACH_NEUTRAL; break; case VASSAL_TREATMENT_UNHAPPY: eApproach = WasResurrectedBy(ePlayer) ? CIV_APPROACH_GUARDED : CIV_APPROACH_HOSTILE; break; case VASSAL_TREATMENT_ENSLAVED: eApproach = CIV_APPROACH_HOSTILE; break; } } else { switch (eTreatmentLevel) { case NO_VASSAL_TREATMENT: UNREACHABLE(); case VASSAL_TREATMENT_CONTENT: eApproach = CIV_APPROACH_FRIENDLY; break; case VASSAL_TREATMENT_DISAGREE: eApproach = CIV_APPROACH_NEUTRAL; break; case VASSAL_TREATMENT_MISTREATED: eApproach = CIV_APPROACH_GUARDED; break; case VASSAL_TREATMENT_UNHAPPY: case VASSAL_TREATMENT_ENSLAVED: eApproach = CIV_APPROACH_HOSTILE; break; } } CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); if (IsUntrustworthy(ePlayer) || eOpinion == CIV_OPINION_UNFORGIVABLE) { eApproach = CIV_APPROACH_HOSTILE; } else if (IsDenouncedPlayer(ePlayer) || IsDenouncedByPlayer(ePlayer) || eOpinion == CIV_OPINION_ENEMY) { if (eApproach != CIV_APPROACH_HOSTILE) { eApproach = CIV_APPROACH_GUARDED; } } // If they're strong enough, consider being AFRAID if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam(), ePlayer)) { int iAfraidScore = GetMajorCivApproachBias(CIV_APPROACH_AFRAID) + (GetPlayer()->GetNumOurCitiesOwnedBy(ePlayer)*2) - GetBoldness(); // If friendly or hostile, less likely to be afraid if (eApproach == CIV_APPROACH_FRIENDLY || eApproach == CIV_APPROACH_HOSTILE) iAfraidScore -= 10; // If conqueror, less likely to be afraid if (IsGoingForWorldConquest() || GetPlayer()->GetPlayerTraits()->IsWarmonger()) iAfraidScore -= 10; // Factor in military strength switch (GetMilitaryStrengthComparedToUs(ePlayer)) { case STRENGTH_IMMENSE: iAfraidScore += 30; break; case STRENGTH_POWERFUL: iAfraidScore += 20; break; case STRENGTH_STRONG: iAfraidScore += 10; break; case STRENGTH_AVERAGE: case STRENGTH_POOR: case STRENGTH_WEAK: case STRENGTH_PATHETIC: iAfraidScore -= 1000; break; } // Factor in proximity switch (GET_PLAYER(ePlayer).GetProximityToPlayer(GetID())) { case PLAYER_PROXIMITY_NEIGHBORS: iAfraidScore += 10; break; case PLAYER_PROXIMITY_CLOSE: iAfraidScore += 5; break; case PLAYER_PROXIMITY_FAR: iAfraidScore -= 10; break; case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: iAfraidScore -= 20; break; } // Factor in military posturing switch (GetMilitaryAggressivePosture(ePlayer)) { case AGGRESSIVE_POSTURE_INCREDIBLE: iAfraidScore += 20; break; case AGGRESSIVE_POSTURE_HIGH: iAfraidScore += 10; break; case AGGRESSIVE_POSTURE_MEDIUM: iAfraidScore += 5; break; case AGGRESSIVE_POSTURE_LOW: iAfraidScore -= 5; break; case AGGRESSIVE_POSTURE_NONE: iAfraidScore -= 10; break; } // Easy target? Nope! if (IsEasyTarget(ePlayer) || GetTargetValue(ePlayer) >= TARGET_VALUE_FAVORABLE) iAfraidScore -= 1000; if (iAfraidScore >= 25) eApproach = CIV_APPROACH_AFRAID; } // Grab the old approach and scratch values for logging CivApproachTypes eOldApproach = GetCivApproach(ePlayer); // Add some base weight to the approach we've selected vApproachScores[(int)eApproach] = GetMajorCivApproachBias(eApproach); vector vApproachScoresScratch; for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eLoopApproach = (CivApproachTypes) iApproachLoop; int iLastTurnValue = GetPlayerApproachValue(ePlayer, eLoopApproach); vApproachScoresScratch.push_back(iLastTurnValue); SetPlayerApproachValue(ePlayer, eLoopApproach, vApproachScores[iApproachLoop]); SetPlayerStrategicApproachValue(ePlayer, eLoopApproach, vApproachScores[iApproachLoop]); } // Update approach and log. SetCivApproach(ePlayer, eApproach); SetCivStrategicApproach(ePlayer, eApproach); LogMajorCivApproachUpdate(ePlayer, &vApproachScores[0], eApproach, eOldApproach, GetSurfaceApproach(ePlayer)); LogApproachValueDeltas(ePlayer, &vApproachScores[0], &vApproachScoresScratch[0]); } } /// Updates our Diplomatic Approach towards a Vassal void CvDiplomacyAI::SelectApproachTowardsVassal(PlayerTypes ePlayer) { if (GET_PLAYER(ePlayer).isMajorCiv()) { CivApproachTypes eApproach = CIV_APPROACH_NEUTRAL; vector vApproachScores(NUM_CIV_APPROACHES, 0); if (IsAtWar(ePlayer)) { eApproach = CIV_APPROACH_WAR; } else { // Towards our own vassals, we are friendly by default if (IsMaster(ePlayer)) { eApproach = CIV_APPROACH_FRIENDLY; } else { CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); // Are we vassals of the same master? if (GET_TEAM(GetTeam()).GetMaster() == GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetMaster()) { eApproach = CIV_APPROACH_FRIENDLY; } // Friends? else if (IsDoFAccepted(ePlayer) && eOpinion > CIV_OPINION_ENEMY) { eApproach = CIV_APPROACH_FRIENDLY; } // Favorable opinion? else if (eOpinion >= CIV_OPINION_FAVORABLE) { eApproach = CIV_APPROACH_FRIENDLY; } // Don't like them? if (IsDenouncedPlayer(ePlayer) || IsDenouncedByPlayer(ePlayer) || eOpinion <= CIV_OPINION_ENEMY) { eApproach = CIV_APPROACH_HOSTILE; } // Planning war towards this vassal's master? bool bWarPlans = false; if (!GetPlayer()->IsVassalOfSomeone()) { TeamTypes eMaster = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetMaster(); vector vOurTeam = GET_TEAM(GetTeam()).getPlayers(); vector vMasterTeam = GET_TEAM(eMaster).getPlayers(); for (size_t i=0; iGetGlobalCoopWarAgainstState(vMasterTeam[j]) >= COOP_WAR_STATE_PREPARING) { bWarPlans = true; break; } if (!GET_PLAYER(vOurTeam[i]).isHuman(ISHUMAN_AI_DIPLOMACY) && pDiploAI->GetCivApproach(vMasterTeam[j]) == CIV_APPROACH_WAR) { bWarPlans = true; break; } } if (bWarPlans) break; } if (bWarPlans) { eApproach = CIV_APPROACH_WAR; } } } } if (eApproach != CIV_APPROACH_WAR && GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) { bool bConsiderWar = GetPlayer()->OwnsOurCity(ePlayer); // Consider war if they have one of our cities if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsCloseToAnyVictoryCondition()) // Consider war if they're close to winning { bConsiderWar = true; } if (IsCompetingForVictory() && !IsMaster(ePlayer)) // Consider war if they have original major capitals and we're not their master { if (IsGoingForWorldConquest() || IsCloseToWorldConquest()) { if (GET_PLAYER(ePlayer).GetCapitalConqueror() != NO_PLAYER) { bConsiderWar = true; } else if (GET_PLAYER(ePlayer).GetNumCapitalCities() > 0) { bConsiderWar = true; } } } if (bConsiderWar) { if (!GetPlayer()->IsNoNewWars() && GetPlayer()->GetProximityToPlayer(ePlayer) >= PLAYER_PROXIMITY_CLOSE && GetTargetValue(ePlayer) >= TARGET_VALUE_AVERAGE) { if (IsWarSane(ePlayer)) { if (IsMaster(ePlayer)) { eApproach = CIV_APPROACH_WAR; } else { TeamTypes eMaster = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetMaster(); vector vMasterTeam = GET_TEAM(eMaster).getPlayers(); bool bAbort = false; bool bAllEasyTargets = true; StrengthTypes eHighestStrength = STRENGTH_PATHETIC; for (size_t i=0; i eHighestStrength) { eHighestStrength = eStrength; } } if (eHighestStrength == STRENGTH_IMMENSE || (eHighestStrength == STRENGTH_POWERFUL && (!bAllEasyTargets || GetBoldness() < 7)) || !GET_TEAM(GetTeam()).canDeclareWar(eMaster, GetID())) { bAbort = true; } if (!bAbort) { eApproach = CIV_APPROACH_WAR; for (size_t i=0; i vApproachScoresScratch; for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eLoopApproach = (CivApproachTypes) iApproachLoop; int iLastTurnValue = GetPlayerApproachValue(ePlayer, eLoopApproach); vApproachScoresScratch.push_back(iLastTurnValue); SetPlayerApproachValue(ePlayer, eLoopApproach, vApproachScores[iApproachLoop]); SetPlayerStrategicApproachValue(ePlayer, eLoopApproach, vApproachScores[iApproachLoop]); } // Update approach and log. SetCivApproach(ePlayer, eApproach); SetCivStrategicApproach(ePlayer, eApproach); LogMajorCivApproachUpdate(ePlayer, &vApproachScores[0], eApproach, eOldApproach, GetSurfaceApproach(ePlayer)); LogApproachValueDeltas(ePlayer, &vApproachScores[0], &vApproachScoresScratch[0]); } } int ApplyPercentageModifier(int iValue, int iModifier, bool bDecrease = false) { PRECONDITION(!bDecrease || iModifier != 0); long long lValue = static_cast(iValue); return bDecrease ? static_cast((lValue * 100) / iModifier) : static_cast((lValue * iModifier) / 100); } /// What is the best Diplomatic Approach to take towards this major civilization? /// This is the most important calculation in determining AI diplomatic behavior! void CvDiplomacyAI::SelectBestApproachTowardsMajorCiv(PlayerTypes ePlayer, bool bStrategic, vector& vValidPlayers, vector& vPlayersToReevaluate, std::map& oldApproaches) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS && NotTeam(ePlayer) && !IsAlwaysAtWar(ePlayer) && GetPlayer()->getNumCities() > 0 && GET_PLAYER(ePlayer).getNumCities() > 0); // Are we reevaluating our approach towards this player? bool bReevaluation = std::find(vPlayersToReevaluate.begin(), vPlayersToReevaluate.end(), ePlayer) != vPlayersToReevaluate.end(); // Initialize some variables that are called repeatedly here, just for convenience PlayerTypes eMyPlayer = GetID(); TeamTypes eMyTeam = GetTeam(); TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam(); PlayerProximityTypes eOurProximity = GetPlayer()->GetProximityToPlayer(ePlayer); PlayerProximityTypes eTheirProximity = GET_PLAYER(ePlayer).GetProximityToPlayer(eMyPlayer); PlayerProximityTypes eClosestProximity = (PlayerProximityTypes)(max((int)eOurProximity, (int)eTheirProximity)); CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); bool bMetValidMinor = GetPlayer()->HasMetValidMinorCiv(); CvDiplomacyAI* pTheirDiplo = GET_PLAYER(ePlayer).GetDiplomacyAI(); // Turn/Era int iMyEra = GetPlayer()->GetCurrentEra(); int iTheirEra = GET_PLAYER(ePlayer).GetCurrentEra(); int iGameEra = GC.getGame().getCurrentEra(); int iGameTurn = GC.getGame().getGameTurn(); bool bWeHaveUUTech = GetPlayer()->HasUUPeriod() && GetPlayer()->GetPlayerTechs()->HasUUTech(); bool bWeHaveUUActive = bWeHaveUUTech && GetPlayer()->HasUUActive(); // Player traits CvPlayerTraits* pTraits = GetPlayer()->GetPlayerTraits(); bool bConquerorTraits = pTraits->IsWarmonger(); bool bDiplomatTraits = pTraits->IsDiplomat(); bool bCulturalTraits = pTraits->IsTourism(); bool bScientistTraits = pTraits->IsNerd(); // Victory stuff bool bCloseToWorldConquest = IsCloseToWorldConquest(); bool bCloseToDiploVictory = IsCloseToDiploVictory(); bool bCloseToScienceVictory = IsCloseToSpaceshipVictory(); bool bCloseToCultureVictory = IsCloseToCultureVictory(); bool bCloseToAnyVictory = bCloseToWorldConquest || bCloseToDiploVictory || bCloseToScienceVictory || bCloseToCultureVictory; bool bTheyAreCloseToWorldConquest = pTheirDiplo->IsCloseToWorldConquest(); bool bTheyAreCloseToDiploVictory = pTheirDiplo->IsCloseToDiploVictory(); bool bTheyAreCloseToScienceVictory = pTheirDiplo->IsCloseToSpaceshipVictory(); bool bTheyAreCloseToCultureVictory = pTheirDiplo->IsCloseToCultureVictory(); // Possessions int iNumOurTechs = GET_TEAM(GetTeam()).GetTeamTechs()->GetNumTechsKnown(); int iNumTheirTechs = GET_TEAM(eTeam).GetTeamTechs()->GetNumTechsKnown(); bool bWeLostCapital = GetPlayer()->IsHasLostCapital(); bool bTheyLostCapital = GET_PLAYER(ePlayer).IsHasLostCapital(); bool bCapturedOurCapital = IsCapitalCapturedBy(ePlayer, true, false); bool bCapturedOurHolyCity = IsHolyCityCapturedBy(ePlayer, true, false); bool bCapturedTheirCapital = pTheirDiplo->IsCapitalCapturedBy(eMyPlayer, true, false); bool bCapturedTheirHolyCity = pTheirDiplo->IsHolyCityCapturedBy(eMyPlayer, true, false); bool bEverCapturedKeyCity = IsCapitalCapturedBy(ePlayer) || IsHolyCityCapturedBy(ePlayer); // this also checks teammates int iNumOurCitiesTheyOwn = GetPlayer()->GetNumOurCitiesOwnedBy(ePlayer); // Evaluations StrengthTypes eMilitaryStrength = GetMilitaryStrengthComparedToUs(ePlayer); StrengthTypes eEconomicStrength = GetEconomicStrengthComparedToUs(ePlayer); bool bUntrustworthy = IsUntrustworthy(ePlayer); bool bEarlyGameCompetitor = IsEarlyGameCompetitor(ePlayer); bool bEasyTarget = IsEasyTarget(ePlayer); bool bGoodAttackTarget = GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(ePlayer); CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); bool bColdWar = false; bool bDoEasyTargetCheck = bEasyTarget && !IsAtWar(ePlayer); // They're only an easy target if we're already at war with them, OR not already at war with somebody else. if (bDoEasyTargetCheck || pLeague) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes)iPlayerLoop; if (IsPlayerValid(eLoopPlayer) && GET_PLAYER(eLoopPlayer).isMajorCiv()) { // Cold war - increases emphasis for ideologies if (pLeague && GC.getGame().GetGameLeagues()->IsIdeologyEmbargoed(eMyPlayer, eLoopPlayer)) bColdWar = true; if (!bDoEasyTargetCheck) { if (bColdWar) break; else continue; } if (IsAtWar(eLoopPlayer)) { if (!GetPlayer()->IsNoNewWars() && GetWarState(eLoopPlayer) > WAR_STATE_TROUBLED) { // Ignore players who aren't a serious threat. if (IsEasyTarget(eLoopPlayer) || GET_PLAYER(eLoopPlayer).IsInTerribleShapeForWar()) continue; // Disregard phony wars if (IsPhonyWar(eLoopPlayer, true)) continue; } bEasyTarget = false; bDoEasyTargetCheck = false; if (bColdWar || !pLeague) break; } } } } bool bSanctioningUsNow = false; bool bEmbargoingUsNow = false; bool bSanctioningThemNow = false; bool bEmbargoingThemNow = false; PlayerTypes eOtherPlayerWeAreSanctioning = NO_PLAYER; PlayerTypes eOtherPlayerTheyAreSanctioning = NO_PLAYER; if (pLeague) { for (EnactProposalList::iterator it = pLeague->m_vEnactProposals.begin(); it != pLeague->m_vEnactProposals.end(); ++it) { PlayerTypes eProposer = it->GetProposerDecision()->GetProposer(); if (eProposer != NO_PLAYER && GetPlayer()->GetLeagueAI()->IsSanctionProposal(&(*it), NO_PLAYER)) { PlayerTypes eTarget = (PlayerTypes)it->GetProposerDecision()->GetDecision(); TeamTypes eProposerTeam = GET_PLAYER(eProposer).getTeam(); TeamTypes eTargetTeam = GET_PLAYER(eTarget).getTeam(); if (eTargetTeam == GetTeam() && eProposerTeam == eTeam) { bSanctioningUsNow = true; if (it->GetEffects()->bEmbargoPlayer) bEmbargoingUsNow = true; } else if (eProposerTeam == GetTeam() && eTargetTeam == eTeam) { bSanctioningThemNow = true; if (it->GetEffects()->bEmbargoPlayer) bEmbargoingThemNow = true; } else if (eProposerTeam == GetTeam()) eOtherPlayerWeAreSanctioning = eTarget; else if (eProposerTeam == eTeam) eOtherPlayerTheyAreSanctioning = eTarget; } } } // Previous approach bool bFirstUpdate = GET_TEAM(eMyTeam).GetTurnsSinceMeetingTeam(eTeam) == 0; CivApproachTypes eOldApproach = CIV_APPROACH_NEUTRAL; std::map::iterator oldApproachPointer = oldApproaches.find(ePlayer); if (oldApproachPointer != oldApproaches.end()) { eOldApproach = oldApproachPointer->second; } else { eOldApproach = GetCivApproach(ePlayer); } //--------------------------------// // [PART 1: INITIAL WEIGHTS] // //--------------------------------// // This vector is what we'll stuff the values into first, and pass it into our logging function (which can't take a CvWeightedVector, which we need to sort...) vector vApproachScores(NUM_CIV_APPROACHES, 0); //////////////////////////////////// // PERSONALITY WEIGHTS - x100 for greater fidelity //////////////////////////////////// vector vApproachBias; for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eLoopApproach = (CivApproachTypes) iApproachLoop; int iBias = GetMajorCivApproachBias(eLoopApproach) * 100; vApproachBias.push_back(iBias); // Add 1x bias for each approach to reflect personality weight vApproachScores[iApproachLoop] += iBias; } // Only apply some DECEPTIVE bonuses if that's a valid approach for this AI to shift into WAR from (see DoUpdateWarTargets()) bool bApplyDeception = bCloseToWorldConquest || IsEndgameAggressiveTo(ePlayer); if (!bApplyDeception && (eOurProximity == PLAYER_PROXIMITY_NEIGHBORS || (GetPlayer()->CanCrossOcean() && eOurProximity >= PLAYER_PROXIMITY_CLOSE))) { if (IsBackstabber() || GetDenounceWillingness() > 8 || vApproachBias[CIV_APPROACH_DECEPTIVE] > vApproachBias[CIV_APPROACH_FRIENDLY] + 100 || IsGoingForWorldConquest()) bApplyDeception = true; } // Also only apply it if it'd be worth our time if (IsDenouncedPlayer(ePlayer) || IsDenouncedByPlayer(ePlayer) || bUntrustworthy || bEverCapturedKeyCity || bSanctioningUsNow || bSanctioningThemNow || HasEverSanctionedUs(ePlayer)) bApplyDeception = false; //////////////////////////////////// // NEUTRAL DEFAULT WEIGHT //////////////////////////////////// vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * /*3*/ GD_INT_GET(APPROACH_NEUTRAL_DEFAULT); //////////////////////////////////// // LEADER TRAITS WEIGHT //////////////////////////////////// // Conquerors get enough war bonuses later on; non-warmonger UAs get a bonus here. if (bDiplomatTraits || bCulturalTraits) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; } if (bScientistTraits) { vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; } // Default victory focus should also count! if (IsDiplomat() || IsCultural()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; } else if (IsScientist()) { vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; } //////////////////////////////////// // LAST TURN APPROACH BIASES //////////////////////////////////// // Add a bias for our current approach, to make it less likely to flip from turn to turn if (!bFirstUpdate && !bReevaluation) { vApproachScores[eOldApproach] += vApproachBias[eOldApproach] * /*2*/ GD_INT_GET(APPROACH_BIAS_FOR_CURRENT); // If we're planning a war (or want to wipe them off the planet) then add WAR bias so that we don't get away from it too easily if (eOldApproach == CIV_APPROACH_WAR) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * /*4*/ GD_INT_GET(APPROACH_WAR_CURRENTLY_WAR); // Ready to attack? if (IsArmyInPlaceForAttack(ePlayer)) { vApproachScores[CIV_APPROACH_WAR] += 10000; } } // Same for demand else if (GetDemandTargetPlayer() == ePlayer) { vApproachScores[eOldApproach] += vApproachBias[eOldApproach] * 4; } } //////////////////////////////////// // WORLD CONQUEST //////////////////////////////////// // Conquest bias: must be a stalemate or better to apply (or not at war yet) if (IsGoingForWorldConquest() || bCloseToWorldConquest) { if (GetWarState(ePlayer) == NO_WAR_STATE_TYPE || GetWarState(ePlayer) > WAR_STATE_TROUBLED) { vApproachScores[CIV_APPROACH_WAR] += bCloseToWorldConquest ? vApproachBias[CIV_APPROACH_WAR] * 2 : vApproachBias[CIV_APPROACH_WAR]; if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += bCloseToWorldConquest ? vApproachBias[CIV_APPROACH_WAR] * 4 : vApproachBias[CIV_APPROACH_WAR] * 2; } } } if (bMetValidMinor) { // If we were given a quest to attack/denounce this player, it probably means he's a total jerk... int iNumWarQuests = 0; int iNumHostileQuests = 0; int iNumDeceptiveQuests = 0; int iNumFriendlyQuests = 0; // Are there any quests that should influence our decision? for (int iMinorLoop = MAX_MAJOR_CIVS; iMinorLoop < MAX_CIV_PLAYERS; iMinorLoop++) { PlayerTypes eMinor = (PlayerTypes) iMinorLoop; if (IsPlayerValid(eMinor) && GET_PLAYER(eMinor).isMinorCiv() && !IsAtWar(eMinor) && GetCivApproach(eMinor) > CIV_APPROACH_HOSTILE) { CvPlayer* pMinor = &GET_PLAYER(eMinor); CvMinorCivAI* pMinorCivAI = pMinor->GetMinorCivAI(); // Quests increasing war likelihood if (pMinorCivAI->IsActiveQuestForPlayer(eMyPlayer, MINOR_CIV_QUEST_WAR) && pMinorCivAI->GetQuestData1(eMyPlayer, MINOR_CIV_QUEST_WAR) == ePlayer) { iNumWarQuests++; } if (pMinorCivAI->IsActiveQuestForPlayer(eMyPlayer, MINOR_CIV_QUEST_ACQUIRE_CITY)) { int iX = pMinorCivAI->GetQuestData1(eMyPlayer, MINOR_CIV_QUEST_ACQUIRE_CITY); int iY = pMinorCivAI->GetQuestData2(eMyPlayer, MINOR_CIV_QUEST_ACQUIRE_CITY); CvPlot* pPlot = GC.getMap().plot(iX, iY); if (pPlot->isCity() && pPlot->getOwner() == ePlayer) { iNumWarQuests++; } } if (pMinorCivAI->IsActiveQuestForPlayer(eMyPlayer, MINOR_CIV_QUEST_LIBERATION)) { int iX = pMinorCivAI->GetQuestData1(eMyPlayer, MINOR_CIV_QUEST_LIBERATION); int iY = pMinorCivAI->GetQuestData2(eMyPlayer, MINOR_CIV_QUEST_LIBERATION); CvPlot* pPlot = GC.getMap().plot(iX, iY); if (pPlot->isCity() && pPlot->getOwner() == ePlayer) { iNumWarQuests++; } } // Quests increasing hostile likelihood if (pMinorCivAI->IsActiveQuestForPlayer(eMyPlayer, MINOR_CIV_QUEST_DENOUNCE_MAJOR) && pMinorCivAI->GetQuestData1(eMyPlayer, MINOR_CIV_QUEST_DENOUNCE_MAJOR) == ePlayer) { iNumHostileQuests++; } // Quests increasing hostile AND deceptive likelihood if (pMinorCivAI->IsActiveQuestForPlayer(eMyPlayer, MINOR_CIV_QUEST_SPY_ON_MAJOR) && pMinorCivAI->GetQuestData1(eMyPlayer, MINOR_CIV_QUEST_SPY_ON_MAJOR) == ePlayer) { iNumHostileQuests++; iNumDeceptiveQuests++; } if (pMinorCivAI->IsActiveQuestForPlayer(eMyPlayer, MINOR_CIV_QUEST_COUP)) { PlayerTypes eTargetMinor = (PlayerTypes)pMinorCivAI->GetQuestData1(eMyPlayer, MINOR_CIV_QUEST_COUP); if (GET_PLAYER(eTargetMinor).GetMinorCivAI()->GetAlly() == ePlayer) { iNumHostileQuests++; iNumDeceptiveQuests++; } } // Quests increasing friendly likelihood if (pMinorCivAI->IsActiveQuestForPlayer(eMyPlayer, MINOR_CIV_QUEST_FIND_PLAYER) && pMinorCivAI->GetQuestData1(eMyPlayer, MINOR_CIV_QUEST_FIND_PLAYER) == ePlayer) { iNumFriendlyQuests++; } if (pMinorCivAI->IsActiveQuestForPlayer(eMyPlayer, MINOR_CIV_QUEST_FIND_CITY)) { // Minor cheating: Allow the AI to know who owns the city // Humans can do the same in many cases by memorizing the city names int iX = pMinorCivAI->GetQuestData1(eMyPlayer, MINOR_CIV_QUEST_FIND_CITY); int iY = pMinorCivAI->GetQuestData2(eMyPlayer, MINOR_CIV_QUEST_FIND_CITY); CvPlot* pPlot = GC.getMap().plot(iX, iY); if (pPlot->isCity() && pPlot->getOwner() == ePlayer) { iNumFriendlyQuests++; } } if (pMinorCivAI->IsActiveQuestForPlayer(eMyPlayer, MINOR_CIV_QUEST_CONNECT_RESOURCE)) { ResourceTypes eResource = (ResourceTypes) pMinorCivAI->GetQuestData1(eMyPlayer, MINOR_CIV_QUEST_CONNECT_RESOURCE); if (GET_PLAYER(eMyPlayer).getNumResourceAvailable(eResource, /*bIncludeImport*/ true) <= 0 && GET_PLAYER(ePlayer).getNumResourceAvailable(eResource, /*bIncludeImport*/ false) > 0) { iNumFriendlyQuests++; } } } } // Multipliers? if (!bCloseToDiploVictory && !bCloseToScienceVictory && !bCloseToCultureVictory) { if (IsGoingForDiploVictory()) { iNumWarQuests *= 2; iNumHostileQuests *= 2; iNumDeceptiveQuests *= 2; iNumFriendlyQuests *= 2; } if (bDiplomatTraits) { iNumWarQuests *= 2; iNumHostileQuests *= 2; iNumDeceptiveQuests *= 2; iNumFriendlyQuests *= 2; } } if (iNumWarQuests > 0) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iNumWarQuests; } if (iNumHostileQuests > 0) { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iNumHostileQuests; } if (iNumDeceptiveQuests > 0) { vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * iNumDeceptiveQuests; } if (iNumFriendlyQuests > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iNumFriendlyQuests; } } //--------------------------------// // [PART 2: FRIENDSHIP MODS] // //--------------------------------// //////////////////////////////////// // DECLARATION OF FRIENDSHIP //////////////////////////////////// if (IsDoFAccepted(ePlayer)) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * max(GetLoyalty(), 5); vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; vApproachScores[CIV_APPROACH_GUARDED] = 0; vApproachScores[CIV_APPROACH_AFRAID] = 0; } //////////////////////////////////// // DEFENSIVE PACT //////////////////////////////////// if (IsHasDefensivePact(ePlayer)) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * max(GetLoyalty(), 5); vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; vApproachScores[CIV_APPROACH_GUARDED] = 0; vApproachScores[CIV_APPROACH_AFRAID] = 0; } //////////////////////////////////// // RESEARCH AGREEMENT //////////////////////////////////// if (IsHasResearchAgreement(ePlayer)) { vApproachScores[CIV_APPROACH_FRIENDLY] += (IsScientist() || IsGoingForSpaceshipVictory()) ? vApproachBias[CIV_APPROACH_FRIENDLY] * 5 : vApproachBias[CIV_APPROACH_FRIENDLY] * 3; vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; vApproachScores[CIV_APPROACH_GUARDED] = 0; vApproachScores[CIV_APPROACH_AFRAID] = 0; } //////////////////////////////////// // EXCHANGE DESIRES //////////////////////////////////// // Deliberately ignore first pass check here to make it more likely that the AI will maintain long-term relationships... if (IsWantsDoFWithPlayer(ePlayer)) { // However don't add this weight if we were being deceptive if ((bStrategic && eOldApproach > CIV_APPROACH_DECEPTIVE) || (!bStrategic && GetCivStrategicApproach(ePlayer) > CIV_APPROACH_DECEPTIVE)) { vApproachScores[CIV_APPROACH_FRIENDLY] += (GetMostValuableFriend() == ePlayer || GetMostValuableAlly() == ePlayer) ? vApproachBias[CIV_APPROACH_FRIENDLY] * 5 : vApproachBias[CIV_APPROACH_FRIENDLY] * 2; vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; vApproachScores[CIV_APPROACH_GUARDED] = 0; vApproachScores[CIV_APPROACH_AFRAID] = 0; } // Instead add some deceptive weight else { vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 3; } } if (IsWantsDefensivePactWithPlayer(ePlayer)) { vApproachScores[CIV_APPROACH_FRIENDLY] += (GetMostValuableFriend() == ePlayer || GetMostValuableAlly() == ePlayer) ? vApproachBias[CIV_APPROACH_FRIENDLY] * 5 : vApproachBias[CIV_APPROACH_FRIENDLY] * 2; vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; vApproachScores[CIV_APPROACH_GUARDED] = 0; vApproachScores[CIV_APPROACH_AFRAID] = 0; } //////////////////////////////////// // COOP WARS //////////////////////////////////// // Coop War Accepted? if (GetCoopWarAgreementScore(ePlayer) > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * GetCoopWarAgreementScore(ePlayer) * 2; } // Coop War Denied? else if (GetCoopWarAgreementScore(ePlayer) < 0) { if (BrokeCoopWarPromise(ePlayer)) { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * -GetCoopWarAgreementScore(ePlayer); } else if (IsDoFAccepted(ePlayer) || GetMeanness() < 6) { vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * -GetCoopWarAgreementScore(ePlayer); } else { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * -GetCoopWarAgreementScore(ePlayer); } } // Loop through all (known) Players - did we agree to go to war with this player against someone else? bool bCoopWar = false; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer) && GetCoopWarState(ePlayer, eLoopPlayer) >= COOP_WAR_STATE_PREPARING) { bCoopWar = true; int iStrengthMod = (int)GetMilitaryStrengthComparedToUs(eLoopPlayer) - 2; vApproachScores[CIV_APPROACH_FRIENDLY] += iStrengthMod > 0 ? vApproachBias[CIV_APPROACH_FRIENDLY] * iStrengthMod : vApproachBias[CIV_APPROACH_FRIENDLY]; } } if (bCoopWar) { vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; vApproachScores[CIV_APPROACH_GUARDED] = 0; vApproachScores[CIV_APPROACH_AFRAID] = 0; } //////////////////////////////////// // LIBERATED THEM FROM VASSALAGE //////////////////////////////////// if (pTheirDiplo->IsMasterLiberatedMeFromVassalage(eMyPlayer)) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * max(GetLoyalty(), 5); vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; vApproachScores[CIV_APPROACH_GUARDED] = 0; vApproachScores[CIV_APPROACH_AFRAID] = 0; } //////////////////////////////////// // LIBERATED CITIES //////////////////////////////////// bool bLiberatedCapital = false; bool bLiberatedHolyCity = false; bool bRecentLiberation = IsCityRecentlyLiberatedBy(ePlayer) && GetPlayer()->getCitiesLost() > 0 && !bTheyAreCloseToWorldConquest && !IsEndgameAggressiveTo(ePlayer); if (!bEverCapturedKeyCity && !bUntrustworthy && !IsAtWar(ePlayer)) { // Liberated the capital or Holy City? bLiberatedCapital = IsPlayerLiberatedCapital(ePlayer); bLiberatedHolyCity = IsPlayerLiberatedHolyCity(ePlayer); int iLiberationMod = GetNumCitiesEverLiberatedBy(ePlayer) - max(iNumOurCitiesTheyOwn, GetNumCitiesCapturedBy(ePlayer)); if (bLiberatedCapital) iLiberationMod--; if (bLiberatedHolyCity) iLiberationMod--; if (iLiberationMod > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iLiberationMod * 2; vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; vApproachScores[CIV_APPROACH_GUARDED] = 0; vApproachScores[CIV_APPROACH_AFRAID] = 0; vApproachScores[CIV_APPROACH_NEUTRAL] = 0; } if (bLiberatedCapital || bLiberatedHolyCity) { // Each of these set all non-FRIENDLY approaches to -1x their bias value (-2x for both) if (bLiberatedCapital && bLiberatedHolyCity) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 15; vApproachScores[CIV_APPROACH_WAR] = -vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] = -vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] = -vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; vApproachScores[CIV_APPROACH_GUARDED] = -vApproachBias[CIV_APPROACH_GUARDED] * 2; vApproachScores[CIV_APPROACH_AFRAID] = -vApproachBias[CIV_APPROACH_AFRAID] * 2; vApproachScores[CIV_APPROACH_NEUTRAL] = -vApproachBias[CIV_APPROACH_NEUTRAL] * 2; } else { vApproachScores[CIV_APPROACH_FRIENDLY] += bLiberatedCapital ? vApproachBias[CIV_APPROACH_FRIENDLY] * 10 : vApproachBias[CIV_APPROACH_FRIENDLY] * 5; vApproachScores[CIV_APPROACH_WAR] = -vApproachBias[CIV_APPROACH_WAR]; vApproachScores[CIV_APPROACH_HOSTILE] = -vApproachBias[CIV_APPROACH_HOSTILE]; vApproachScores[CIV_APPROACH_DECEPTIVE] = -vApproachBias[CIV_APPROACH_DECEPTIVE]; vApproachScores[CIV_APPROACH_GUARDED] = -vApproachBias[CIV_APPROACH_GUARDED]; vApproachScores[CIV_APPROACH_AFRAID] = -vApproachBias[CIV_APPROACH_AFRAID]; vApproachScores[CIV_APPROACH_NEUTRAL] = -vApproachBias[CIV_APPROACH_NEUTRAL]; } } // Returned the capital? if (!bLiberatedCapital && IsPlayerReturnedCapital(ePlayer)) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5; } // Returned the Holy City? if (!bLiberatedHolyCity && IsPlayerReturnedHolyCity(ePlayer)) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 3; } } //////////////////////////////////// // RESURRECTION //////////////////////////////////// bool bResurrectedUs = WasResurrectedBy(ePlayer); bool bResurrectedThem = pTheirDiplo->WasResurrectedBy(eMyPlayer); if (bResurrectedUs || bResurrectedThem) { vApproachScores[CIV_APPROACH_FRIENDLY] += bResurrectedUs && bResurrectedThem ? vApproachBias[CIV_APPROACH_FRIENDLY] * max(20, 10 + GetLoyalty() * 2) : vApproachBias[CIV_APPROACH_FRIENDLY] * max(10, GetLoyalty() + 5); // Subtract instead of setting if there's a liberation bonus above if (bLiberatedCapital || bLiberatedHolyCity) { vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * 2; vApproachScores[CIV_APPROACH_AFRAID] -= vApproachBias[CIV_APPROACH_AFRAID] * 2; vApproachScores[CIV_APPROACH_NEUTRAL] -= vApproachBias[CIV_APPROACH_NEUTRAL] * 2; } else { vApproachScores[CIV_APPROACH_WAR] = -vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] = -vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] = -vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; vApproachScores[CIV_APPROACH_GUARDED] = -vApproachBias[CIV_APPROACH_GUARDED] * 2; vApproachScores[CIV_APPROACH_AFRAID] = -vApproachBias[CIV_APPROACH_AFRAID] * 2; vApproachScores[CIV_APPROACH_NEUTRAL] = -vApproachBias[CIV_APPROACH_NEUTRAL] * 2; } } //--------------------------------// // [PART 3: PROVOCATIONS] // //--------------------------------// bool bProvokedUs = false; //////////////////////////////////// // DEMANDS //////////////////////////////////// if (GetNumDemandsMade(ePlayer) > 0) { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * GetNumDemandsMade(ePlayer); vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * GetNumDemandsMade(ePlayer) / 2; if (pTheirDiplo->IsRecentDemandAccepted(eMyPlayer) || GetNumDemandsMade(ePlayer) > 1 || IsAtWar(ePlayer)) { bProvokedUs = true; } if (bEasyTarget || eMilitaryStrength < STRENGTH_AVERAGE) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetNumDemandsMade(ePlayer); } else { vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * GetNumDemandsMade(ePlayer); } } //////////////////////////////////// // DENOUNCEMENTS //////////////////////////////////// // We denounced them if (IsDenouncedPlayer(ePlayer)) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_FRIENDLY] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2; } } // They denounced us if (IsDenouncedByPlayer(ePlayer)) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 2; vApproachScores[CIV_APPROACH_FRIENDLY] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; bProvokedUs = true; } //////////////////////////////////// // SANCTIONS //////////////////////////////////// // We tried to sanction them RECENTLY - stick with it, unless they are liberating us or they're a vassal if ((pTheirDiplo->HasTriedToSanctionUs(eMyPlayer) || bSanctioningThemNow) && !IsLiberator(ePlayer, false, true) && !bRecentLiberation) { bool bWaitingForDiminishedPower = bEmbargoingThemNow && !bTheyAreCloseToWorldConquest && !IsEndgameAggressiveTo(ePlayer) && !IsBackstabber() && iNumOurCitiesTheyOwn == 0; vApproachScores[CIV_APPROACH_WAR] += bWaitingForDiminishedPower ? 0 : vApproachBias[CIV_APPROACH_WAR] * 4; vApproachScores[CIV_APPROACH_HOSTILE] += bWaitingForDiminishedPower ? vApproachBias[CIV_APPROACH_HOSTILE] * 4 : vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_GUARDED] += bWaitingForDiminishedPower ? vApproachBias[CIV_APPROACH_GUARDED] * 4 : vApproachBias[CIV_APPROACH_GUARDED] * 2; vApproachScores[CIV_APPROACH_FRIENDLY] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += bWaitingForDiminishedPower ? 0 : vApproachBias[CIV_APPROACH_WAR] * 4; vApproachScores[CIV_APPROACH_HOSTILE] += bWaitingForDiminishedPower ? vApproachBias[CIV_APPROACH_HOSTILE] * 4 : vApproachBias[CIV_APPROACH_HOSTILE] * 2; } } // They tried to sanction us RECENTLY if (HasTriedToSanctionUs(ePlayer) || bSanctioningUsNow) { bool bWaitingForDiminishedPower = bEmbargoingUsNow && !bCloseToWorldConquest && pTheirDiplo->IsEndgameAggressiveTo(eMyPlayer) && !bUntrustworthy && GET_PLAYER(ePlayer).GetNumOurCitiesOwnedBy(eMyPlayer) == 0; vApproachScores[CIV_APPROACH_WAR] += (bWaitingForDiminishedPower || bEasyTarget) ? vApproachBias[CIV_APPROACH_WAR] * 5 : vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; vApproachScores[CIV_APPROACH_GUARDED] += bWaitingForDiminishedPower ? vApproachBias[CIV_APPROACH_GUARDED] * 2 : vApproachBias[CIV_APPROACH_GUARDED] * 5; vApproachScores[CIV_APPROACH_FRIENDLY] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; vApproachScores[CIV_APPROACH_AFRAID] = 0; bProvokedUs = true; } //////////////////////////////////// // TRAITOR OPINION //////////////////////////////////// // Do we think they're a good-for-nothing backstabber?! if (bUntrustworthy) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 2; vApproachScores[CIV_APPROACH_FRIENDLY] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; bProvokedUs = true; // Extra penalties if they betrayed us personally! if (CvDiplomacyAIHelpers::BackstabbedPlayer(ePlayer, eMyPlayer, false)) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 3; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 3; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 3; if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; } } } //////////////////////////////////// // VENGEANCE! Grrrr.... //////////////////////////////////// int iFlavorReligion = m_pPlayer->GetFlavorManager()->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_RELIGION")); // Note: This is in addition to the weight from being an untrustworthy friend! if (bCapturedOurCapital) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 10; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 10; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 10; vApproachScores[CIV_APPROACH_FRIENDLY] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; bProvokedUs = true; // Easy target? Get our capital back! if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += 20000; } } if (bCapturedOurHolyCity) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iFlavorReligion; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iFlavorReligion; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iFlavorReligion; vApproachScores[CIV_APPROACH_FRIENDLY] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; bProvokedUs = true; // Easy target? Get our Holy City back! if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iFlavorReligion; } } // Stole our territory? if (GetNumTimesCultureBombed(ePlayer) > 0) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetNumTimesCultureBombed(ePlayer) / 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * GetNumTimesCultureBombed(ePlayer) / 2; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * GetNumTimesCultureBombed(ePlayer) / 4; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; bProvokedUs = true; if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetNumTimesCultureBombed(ePlayer) / 2; } } // Are we angry about what they've done to our protected City-States? if (IsAngryAboutProtectedMinorKilled(ePlayer)) { vApproachScores[CIV_APPROACH_FRIENDLY] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; bProvokedUs = true; if (IsGoingForDiploVictory()) { int iPenalty = max((GetOtherPlayerNumProtectedMinorsKilled(ePlayer) * 2), GetOtherPlayerNumProtectedMinorsAttacked(ePlayer)); vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iPenalty; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iPenalty; } else { int iPenalty = max(GetOtherPlayerNumProtectedMinorsKilled(ePlayer), (GetOtherPlayerNumProtectedMinorsAttacked(ePlayer)/2)); vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iPenalty; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iPenalty; } } else if (IsAngryAboutProtectedMinorAttacked(ePlayer)) { int iPenalty = (GetOtherPlayerNumProtectedMinorsKilled(ePlayer) + GetOtherPlayerNumProtectedMinorsAttacked(ePlayer)); bProvokedUs |= !MadeAttackCityStatePromise(ePlayer); if (IsGoingForDiploVictory()) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iPenalty; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iPenalty; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iPenalty / 2; vApproachScores[CIV_APPROACH_FRIENDLY] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; } else { vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * iPenalty; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iPenalty / 2; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iPenalty / 2; } } else if (IsAngryAboutProtectedMinorBullied(ePlayer)) { if (IsDoFAccepted(ePlayer) || IsHasDefensivePact(ePlayer) || IsHasResearchAgreement(ePlayer)) { vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * GetOtherPlayerNumProtectedMinorsBullied(ePlayer) / 2; } else { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * GetOtherPlayerNumProtectedMinorsBullied(ePlayer) / 2; } } // Are they standing in the way of our bullying? How nasty am I...? if (IsAngryAboutSidedWithProtectedMinor(ePlayer) && !IsDoFAccepted(ePlayer) && !IsHasDefensivePact(ePlayer) && !IsHasResearchAgreement(ePlayer)) { if (GetBoldness() > 7 || GetMeanness() > 7) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR]; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE]; } else if (pTraits->GetBullyMilitaryStrengthModifier() != 0 || pTraits->GetBullyValueModifier() != 0 || pTraits->GetCityStateCombatModifier() != 0 || pTraits->IsBullyAnnex() || pTraits->IgnoreBullyPenalties() || GetPlayer()->IsCanBullyFriendlyCS()) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR]; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE]; } } //////////////////////////////////// // CAPTURED CITIES //////////////////////////////////// int iCityDifference = (GET_PLAYER(ePlayer).GetNumOurCitiesOwnedBy(eMyPlayer) - iNumOurCitiesTheyOwn) * 2; // Only apply weight for this if they're nearby (if we conquered a city far away from the rest of their empire, adding weight here is not helpful). if (eClosestProximity >= PLAYER_PROXIMITY_CLOSE) { // If we've captured cities from them before, we're more likely to finish the job. if (iCityDifference > 0) { // As strong as them or stronger? Let's continue our conquest, especially if they're an easy target. if (bEasyTarget) { if (eOurProximity >= PLAYER_PROXIMITY_CLOSE) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iCityDifference * 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iCityDifference * 2; } } else if (eMilitaryStrength <= STRENGTH_AVERAGE && !GetPlayer()->IsNoNewWars()) { if (eOurProximity >= PLAYER_PROXIMITY_CLOSE) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iCityDifference; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iCityDifference; } } // If they're stronger than us, they're going to want revenge... else if (eTheirProximity >= PLAYER_PROXIMITY_CLOSE) { vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iCityDifference; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * iCityDifference * 2; } } // If they've captured cities from us before, they're more likely to finish the job. else if (iCityDifference < 0) { // Flip it! iCityDifference *= -1; bProvokedUs = true; // Easy target? Get our cities back! if (bEasyTarget) { if (eOurProximity >= PLAYER_PROXIMITY_CLOSE) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iCityDifference * 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iCityDifference * 2; vApproachScores[CIV_APPROACH_FRIENDLY] = 0; } } // Weaker than us? Let's get revenge! else if (eMilitaryStrength < STRENGTH_AVERAGE && !GetPlayer()->IsNoNewWars()) { if (eOurProximity >= PLAYER_PROXIMITY_CLOSE) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iCityDifference; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iCityDifference; vApproachScores[CIV_APPROACH_FRIENDLY] = 0; } } // As strong as us or stronger? Decrease desire for war, they've proven their might. else { if (eTheirProximity >= PLAYER_PROXIMITY_CLOSE) { vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * iCityDifference; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * iCityDifference; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iCityDifference; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * iCityDifference; } } } } //--------------------------------// // [PART 4: KEY PLAYERS] // //--------------------------------// bool bVictoryConcern = bTheyAreCloseToWorldConquest || GetPlayerNumMajorsConquered(ePlayer) >= 3; if (IsCompetingForVictory()) { bVictoryConcern |= GetVictoryDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG; bVictoryConcern |= GetVictoryBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG; bVictoryConcern |= GetTechBlockLevel(ePlayer) == BLOCK_LEVEL_FIERCE; bVictoryConcern |= GetPolicyBlockLevel(ePlayer) == BLOCK_LEVEL_FIERCE; bVictoryConcern |= IsEndgameAggressiveTo(ePlayer); bVictoryConcern |= IsGoingForSpaceshipVictory() && GetTechBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG; bVictoryConcern |= IsGoingForWorldConquest() && GetLandDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG; bVictoryConcern |= IsGoingForDiploVictory() && GetMinorCivDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG; bVictoryConcern |= IsGoingForCultureVictory() && (GetWonderDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG || GetPolicyBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG); } //////////////////////////////////// // OPPORTUNITY ATTACK DESIRE //////////////////////////////////// // This value determines whether the AI wants to exploit any weaknesses in the other player's defenses bool bWantsOpportunityAttack = false; int iAttackMultiplier = bEasyTarget ? 2 : 1; int iTheirAttackMultiplier = pTheirDiplo->IsEasyTarget(eMyPlayer) ? 2 : 1; // Also take good city attack targets into consideration... if (bGoodAttackTarget) { iAttackMultiplier++; } if (GetPlayer()->GetMilitaryAI()->IsExposedToEnemy(NULL, ePlayer)) { iTheirAttackMultiplier++; } // Certain things require a higher threshold for an opportunity attack desire // If we don't moderate our aggressive desire, we'll lose friends and be hated by the world. bool bModerateAggressiveDesire = false; bool bBold = GetBoldness() > 7 || bConquerorTraits || (IsCompetingForVictory() && IsGoingForWorldConquest()); // If they're far away, moderate aggressive desire no matter what. if (eOurProximity <= PLAYER_PROXIMITY_FAR) { bModerateAggressiveDesire = true; } else if (!bUntrustworthy && !bEverCapturedKeyCity && !GC.getGame().IsAIAggressiveMode() && !IsBackstabber()) { // Ally to us now? if (IsFriendOrAlly(ePlayer)) { if (!bBold || !bEasyTarget || !bGoodAttackTarget) { bModerateAggressiveDesire = true; } } // Friends previously, and not major competitors? else if (GetDoFType(ePlayer) >= DOF_TYPE_FRIENDS) { if (!IsMajorCompetitor(ePlayer) && !bEarlyGameCompetitor && eOpinion >= CIV_OPINION_COMPETITOR) { if (!bBold || !(bEasyTarget || bGoodAttackTarget)) { bModerateAggressiveDesire = true; } } } } // Always want an opportunity attack if we have a coop war planned. if (IsLockedIntoCoopWar(ePlayer)) { bWantsOpportunityAttack = true; } // No opportunity attacks if we're doing terribly already, unless things are dire. else if (!GetPlayer()->IsNoNewWars() || IsEndgameAggressiveTo(ePlayer) || bEverCapturedKeyCity || bTheyAreCloseToWorldConquest) { // Don't be aggressive towards our friends or distant players without a good reason. if (bModerateAggressiveDesire) { bWantsOpportunityAttack = bCloseToWorldConquest || bProvokedUs || bEverCapturedKeyCity || GetWarmongerThreat(ePlayer) >= THREAT_SEVERE || bEarlyGameCompetitor || GetBiggestCompetitor() == ePlayer || GetPrimeLeagueCompetitor() == ePlayer || bVictoryConcern || bTheyAreCloseToWorldConquest || GC.getGame().IsAIAggressiveMode(); } else { bWantsOpportunityAttack = (IsGoingForWorldConquest() && iMyEra >= GD_INT_GET(MEDIEVAL_ERA)) || bEverCapturedKeyCity || bVictoryConcern || GetWarmongerThreat(ePlayer) >= THREAT_SEVERE || bCloseToWorldConquest || bTheyAreCloseToWorldConquest || IsMajorCompetitor(ePlayer) || GC.getGame().IsAIAggressiveMode(); // If they're nearby, more reasons are valid to want to attack them! if (eOurProximity >= PLAYER_PROXIMITY_CLOSE) { bWantsOpportunityAttack |= IsConqueror() || bConquerorTraits || bProvokedUs || (bEasyTarget && eOpinion <= CIV_OPINION_COMPETITOR) || eOpinion <= CIV_OPINION_ENEMY || bEarlyGameCompetitor || IsRecklessExpander(ePlayer) || IsWonderSpammer(ePlayer); } } } // Loop through other players and look for anything interesting for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; TeamTypes eLoopTeam = (TeamTypes) GET_PLAYER(eLoopPlayer).getTeam(); if (!IsPlayerValid(eLoopPlayer, true)) continue; if (eLoopPlayer == ePlayer || eLoopPlayer == eMyPlayer) continue; if (GET_PLAYER(eLoopPlayer).IsVassalOfSomeone()) continue; // Has this guy denounced the other player? if (!bStrategic && pTheirDiplo->IsDenouncedPlayer(eLoopPlayer)) { // Is this loop player one of our biggest competitors? We should like this guy! if (GetBiggestCompetitor() == eLoopPlayer || GetPrimeLeagueCompetitor() == eLoopPlayer || eOtherPlayerWeAreSanctioning == eLoopPlayer) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * GetWorkAgainstWillingness() / 4; } // Is this loop player our most valuable DOF or DP? We hate this other guy! else if (GetMostValuableFriend() == eLoopPlayer || GetMostValuableAlly() == eLoopPlayer) { if (IsDoFAccepted(eLoopPlayer) || (GetMostValuableAlly() == eLoopPlayer && IsHasDefensivePact(eLoopPlayer))) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetWorkWithWillingness() / 4; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * GetWorkWithWillingness() / 4; } else { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetWorkWithWillingness() / 8; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * GetWorkWithWillingness() / 8; } } } // Is this guy at war with the other player? if (!bStrategic && GET_TEAM(eLoopTeam).isAtWar(eTeam)) { // Is this loop player one of our biggest competitors? We should like this guy! if (GetBiggestCompetitor() == eLoopPlayer || GetPrimeLeagueCompetitor() == eLoopPlayer || eOtherPlayerWeAreSanctioning == eLoopPlayer) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * GetWorkAgainstWillingness() / 2; } // Is this loop player our most valuable DOF or DP? We hate this other guy! else if (GetMostValuableFriend() == eLoopPlayer || GetMostValuableAlly() == eLoopPlayer) { if (IsDoFAccepted(eLoopPlayer) || (GetMostValuableAlly() == eLoopPlayer && IsHasDefensivePact(eLoopPlayer))) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetWorkWithWillingness() / 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * GetWorkWithWillingness() / 2; } else { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetWorkWithWillingness() / 4; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * GetWorkWithWillingness() / 4; } } } // Has this guy proposed to sanction the other guy? if (!bStrategic && eOtherPlayerTheyAreSanctioning == eLoopPlayer) { // Is this loop player our biggest competitor? We should like this guy! if (GetBiggestCompetitor() == eLoopPlayer || GetPrimeLeagueCompetitor() == eLoopPlayer || eOtherPlayerWeAreSanctioning == eLoopPlayer) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * GetWorkAgainstWillingness() / 2; } // Is this loop player our most valuable DOF or DP? We hate this other guy! else if (GetMostValuableFriend() == eLoopPlayer || GetMostValuableAlly() == eLoopPlayer) { if (IsDoFAccepted(eLoopPlayer) || (GetMostValuableAlly() == eLoopPlayer && IsHasDefensivePact(eLoopPlayer))) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetWorkWithWillingness() / 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * GetWorkWithWillingness() / 2; } else { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetWorkWithWillingness() / 4; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * GetWorkWithWillingness() / 4; } } } } if (!bStrategic) // Don't update this on the first pass - we need the updated values, which are updated after the first pass of DoUpdateMajorCivApproaches() { if (GetBiggestCompetitor() == ePlayer) { vApproachScores[CIV_APPROACH_WAR] += iMyEra > GD_INT_GET(CLASSICAL_ERA) ? vApproachBias[CIV_APPROACH_WAR] * 5 : vApproachBias[CIV_APPROACH_WAR] * 3; vApproachScores[CIV_APPROACH_HOSTILE] += iMyEra > GD_INT_GET(CLASSICAL_ERA) ? vApproachBias[CIV_APPROACH_HOSTILE] * 5 : vApproachBias[CIV_APPROACH_HOSTILE] * 3; vApproachScores[CIV_APPROACH_GUARDED] += iMyEra > GD_INT_GET(CLASSICAL_ERA) ? vApproachBias[CIV_APPROACH_GUARDED] * 5 : 0; vApproachScores[CIV_APPROACH_DECEPTIVE] += iMyEra > GD_INT_GET(CLASSICAL_ERA) ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 5 : 0; if (IsGoingForWorldConquest() || eOurProximity == PLAYER_PROXIMITY_NEIGHBORS || bVictoryConcern || bProvokedUs || bWantsOpportunityAttack) { // Easy target? Let's get 'em. if (bEasyTarget && iMyEra > GD_INT_GET(ANCIENT_ERA)) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; } } } // Focus our attention on major competitors. else if (IsMajorCompetitor(ePlayer) || IsEarlyGameCompetitor(ePlayer)) { vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 3; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 2; if (IsGoingForWorldConquest() || eOurProximity == PLAYER_PROXIMITY_NEIGHBORS || bVictoryConcern || bProvokedUs) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 3; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 3; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 : 0; // Easy target? Let's get 'em. if (bEasyTarget && iMyEra > GD_INT_GET(ANCIENT_ERA)) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 3; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 3; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 : 0; } } } // Not a major competitor in the Renaissance or later? We have bigger fish to fry. else if (iMyEra >= GD_INT_GET(RENAISSANCE_ERA) && !bVictoryConcern && IsCompetingForVictory()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * GetVictoryCompetitiveness() / 2; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * (11 - GetVictoryCompetitiveness()) / 2; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * GetVictoryCompetitiveness(); } // Is this our prime competitor in the World Congress? if (GetPrimeLeagueCompetitor() == ePlayer) { if (IsGoingForDiploVictory() || bProvokedUs) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 5; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 5; if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 5 : 0; } } } // No? Then our diplomacy is glad for it. else if (pLeague && !bUntrustworthy && (IsGoingForDiploVictory() || !bProvokedUs)) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; } if (!bUntrustworthy) { if (GetMostValuableFriend() == ePlayer) { if (IsDoFAccepted(ePlayer) && !bVictoryConcern && !bProvokedUs) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 10; vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; vApproachScores[CIV_APPROACH_AFRAID] = 0; vApproachScores[CIV_APPROACH_GUARDED] = 0; } else if (!bProvokedUs) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5; if (IsDoFAccepted(ePlayer) || !bVictoryConcern) { vApproachScores[CIV_APPROACH_WAR] /= 2; vApproachScores[CIV_APPROACH_HOSTILE] /= 2; vApproachScores[CIV_APPROACH_DECEPTIVE] /= 2; vApproachScores[CIV_APPROACH_AFRAID] /= 2; vApproachScores[CIV_APPROACH_GUARDED] /= 2; } } else { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; } } if (GetMostValuableAlly() == ePlayer) { if (IsHasDefensivePact(ePlayer) && !bVictoryConcern && !bProvokedUs) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 10; vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; vApproachScores[CIV_APPROACH_AFRAID] = 0; vApproachScores[CIV_APPROACH_GUARDED] = 0; } else if (!bProvokedUs) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5; if (IsDoFAccepted(ePlayer) || !bVictoryConcern) { vApproachScores[CIV_APPROACH_WAR] /= 2; vApproachScores[CIV_APPROACH_HOSTILE] /= 2; vApproachScores[CIV_APPROACH_DECEPTIVE] /= 2; vApproachScores[CIV_APPROACH_AFRAID] /= 2; vApproachScores[CIV_APPROACH_GUARDED] /= 2; } } else { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; } } if (GetPrimeLeagueAlly() == ePlayer) { if (!bVictoryConcern && !bProvokedUs) { vApproachScores[CIV_APPROACH_FRIENDLY] += IsGoingForDiploVictory() ? vApproachBias[CIV_APPROACH_FRIENDLY] * 10 : vApproachBias[CIV_APPROACH_FRIENDLY] * GetDiploBalance(); vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; vApproachScores[CIV_APPROACH_AFRAID] = 0; vApproachScores[CIV_APPROACH_GUARDED] = 0; } else if (!bProvokedUs) { vApproachScores[CIV_APPROACH_FRIENDLY] += IsGoingForDiploVictory() ? vApproachBias[CIV_APPROACH_FRIENDLY] * 5 : vApproachBias[CIV_APPROACH_FRIENDLY] * GetDiploBalance() / 2; if (IsDoFAccepted(ePlayer) || !bVictoryConcern) { vApproachScores[CIV_APPROACH_WAR] /= 2; vApproachScores[CIV_APPROACH_HOSTILE] /= 2; vApproachScores[CIV_APPROACH_DECEPTIVE] /= 2; vApproachScores[CIV_APPROACH_AFRAID] /= 2; vApproachScores[CIV_APPROACH_GUARDED] /= 2; } } else // We need our World Congress allies... so apply the full FRIENDLY weight, but also apply equal DECEPTIVE weight if we've been provoked { vApproachScores[CIV_APPROACH_FRIENDLY] += IsGoingForDiploVictory() ? vApproachBias[CIV_APPROACH_FRIENDLY] * 5 : vApproachBias[CIV_APPROACH_FRIENDLY] * GetDiploBalance() / 2; vApproachScores[CIV_APPROACH_DECEPTIVE] += IsGoingForDiploVictory() ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 5 : vApproachBias[CIV_APPROACH_DECEPTIVE] * GetDiploBalance() / 2; } } } } //--------------------------------// // [PART 5: EVALUATIONS] // //--------------------------------// //////////////////////////////////// // WARMONGER THREAT //////////////////////////////////// switch (GetWarmongerThreat(ePlayer)) { case THREAT_CRITICAL: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetWarmongerHate() * 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * GetWarmongerHate() * 2; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * GetWarmongerHate() * iTheirAttackMultiplier / 2; vApproachScores[CIV_APPROACH_AFRAID] += vApproachBias[CIV_APPROACH_AFRAID] * (11 - GetWarmongerHate()) * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * GetWarmongerHate() / iTheirAttackMultiplier; break; case THREAT_SEVERE: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetWarmongerHate(); vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * GetWarmongerHate(); vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * GetWarmongerHate() * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_AFRAID] += vApproachBias[CIV_APPROACH_AFRAID] * (11 - GetWarmongerHate()) * iTheirAttackMultiplier / 2; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * GetWarmongerHate() / iTheirAttackMultiplier / 2; break; case THREAT_MAJOR: vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * GetWarmongerHate() * iTheirAttackMultiplier / 2; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * GetWarmongerHate() * iTheirAttackMultiplier / 2; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * GetWarmongerHate() / iTheirAttackMultiplier / 3; break; case THREAT_MINOR: case THREAT_NONE: vApproachScores[CIV_APPROACH_NEUTRAL] += bProvokedUs ? 0 : vApproachBias[CIV_APPROACH_NEUTRAL] * GetWarmongerHate() * iTheirAttackMultiplier / 2; vApproachScores[CIV_APPROACH_FRIENDLY] += bProvokedUs ? 0 : vApproachBias[CIV_APPROACH_FRIENDLY] * GetWarmongerHate() * iTheirAttackMultiplier / 2; break; } //////////////////////////////////// // STRENGTH COMPARED TO US //////////////////////////////////// switch (eMilitaryStrength) { case STRENGTH_PATHETIC: vApproachScores[CIV_APPROACH_WAR] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 6 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR] * 3; vApproachScores[CIV_APPROACH_HOSTILE] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_HOSTILE] * 6 * iAttackMultiplier : vApproachBias[CIV_APPROACH_HOSTILE] * 3; vApproachScores[CIV_APPROACH_DECEPTIVE] += (bWantsOpportunityAttack && bApplyDeception) ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 6 * iAttackMultiplier : vApproachBias[CIV_APPROACH_DECEPTIVE] * 3; vApproachScores[CIV_APPROACH_AFRAID] -= vApproachBias[CIV_APPROACH_AFRAID] * 6 * iAttackMultiplier; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * 6 * iAttackMultiplier; break; case STRENGTH_WEAK: vApproachScores[CIV_APPROACH_WAR] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 4 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_HOSTILE] * 4 * iAttackMultiplier : vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] += (bWantsOpportunityAttack && bApplyDeception) ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 4 * iAttackMultiplier : vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; vApproachScores[CIV_APPROACH_AFRAID] -= vApproachBias[CIV_APPROACH_AFRAID] * 4 * iAttackMultiplier; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * 4 * iAttackMultiplier; break; case STRENGTH_POOR: vApproachScores[CIV_APPROACH_WAR] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 2 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR]; vApproachScores[CIV_APPROACH_DECEPTIVE] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 * iAttackMultiplier : vApproachBias[CIV_APPROACH_DECEPTIVE]; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; vApproachScores[CIV_APPROACH_AFRAID] -= vApproachBias[CIV_APPROACH_AFRAID] * 2 * iAttackMultiplier; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * 2 * iAttackMultiplier; break; case STRENGTH_AVERAGE: vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; break; case STRENGTH_STRONG: vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iTheirAttackMultiplier; break; case STRENGTH_POWERFUL: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 3 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 3 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_AFRAID] += vApproachBias[CIV_APPROACH_AFRAID] * 4 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 4 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 4 * iTheirAttackMultiplier; break; case STRENGTH_IMMENSE: vApproachScores[CIV_APPROACH_AFRAID] += vApproachBias[CIV_APPROACH_AFRAID] * 8 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 6 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 6 * iTheirAttackMultiplier; break; } // For high economic strength, do not add AFRAID weight or remove WAR/HOSTILE weight unless military strength is also that high ... military power should remain a larger factor switch (eEconomicStrength) { case STRENGTH_PATHETIC: vApproachScores[CIV_APPROACH_WAR] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 6 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR] * 3; vApproachScores[CIV_APPROACH_HOSTILE] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_HOSTILE] * 6 * iAttackMultiplier : vApproachBias[CIV_APPROACH_HOSTILE] * 3; vApproachScores[CIV_APPROACH_DECEPTIVE] += (bWantsOpportunityAttack && bApplyDeception) ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 6 * iAttackMultiplier : vApproachBias[CIV_APPROACH_DECEPTIVE] * 3; vApproachScores[CIV_APPROACH_AFRAID] -= vApproachBias[CIV_APPROACH_AFRAID] * 6 * iAttackMultiplier; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * 6 * iAttackMultiplier; break; case STRENGTH_WEAK: vApproachScores[CIV_APPROACH_WAR] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 4 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_HOSTILE] * 4 * iAttackMultiplier : vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] += (bWantsOpportunityAttack && bApplyDeception) ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 * iAttackMultiplier : vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; vApproachScores[CIV_APPROACH_AFRAID] -= vApproachBias[CIV_APPROACH_AFRAID] * 4 * iAttackMultiplier; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * 4 * iAttackMultiplier; break; case STRENGTH_POOR: vApproachScores[CIV_APPROACH_WAR] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 2 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR]; vApproachScores[CIV_APPROACH_DECEPTIVE] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 * iAttackMultiplier : vApproachBias[CIV_APPROACH_DECEPTIVE]; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; vApproachScores[CIV_APPROACH_AFRAID] -= vApproachBias[CIV_APPROACH_AFRAID] * 2 * iAttackMultiplier; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * 2 * iAttackMultiplier; break; case STRENGTH_AVERAGE: vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL]; vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY]; break; case STRENGTH_STRONG: { vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iTheirAttackMultiplier; if (eMilitaryStrength >= STRENGTH_STRONG) { vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iTheirAttackMultiplier; } break; } case STRENGTH_POWERFUL: { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 2 * iTheirAttackMultiplier; if (eMilitaryStrength > STRENGTH_STRONG) { vApproachScores[CIV_APPROACH_AFRAID] += vApproachBias[CIV_APPROACH_AFRAID] * 4 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 4 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 4 * iTheirAttackMultiplier; } else if (eMilitaryStrength == STRENGTH_STRONG) { vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iTheirAttackMultiplier; } break; } case STRENGTH_IMMENSE: { switch (eMilitaryStrength) { case STRENGTH_STRONG: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iTheirAttackMultiplier; break; case STRENGTH_POWERFUL: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_AFRAID] += vApproachBias[CIV_APPROACH_AFRAID] * 4 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 4 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 4 * iTheirAttackMultiplier; break; case STRENGTH_IMMENSE: vApproachScores[CIV_APPROACH_AFRAID] += vApproachBias[CIV_APPROACH_AFRAID] * 8 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 6 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 6 * iTheirAttackMultiplier; break; default: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 2 * iTheirAttackMultiplier; break; } break; } } //////////////////////////////////// // DOF HISTORY //////////////////////////////////// int iDiploMultiplier = IsCompetingForVictory() && IsGoingForDiploVictory() ? 2 : 1; if (GetDiploBalance() >= 8) iDiploMultiplier += 1; else if (GetDiploBalance() <= 3) iDiploMultiplier -= 1; // Our prime league competitor isn't considered aligned with our interests... if (IsCompetingForVictory() && GetDoFType(ePlayer) > DOF_TYPE_UNTRUSTWORTHY) { if (GetPrimeLeagueCompetitor() == ePlayer) { iDiploMultiplier--; } if (bTheyAreCloseToDiploVictory) { iDiploMultiplier--; } } iDiploMultiplier = max(iDiploMultiplier, 0); // Encourage long-term friendships and alliances // Enemies made later in the game are more likely to remain enemies switch (GetDoFType(ePlayer)) { case DOF_TYPE_UNTRUSTWORTHY: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * max(iMyEra, 1) * iDiploMultiplier; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * max(iMyEra, 1) * iDiploMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * max(iMyEra, 1) * iDiploMultiplier; break; case DOF_TYPE_NEW: vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * max(iMyEra, 1) * iDiploMultiplier; vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * max(iMyEra, 1) * iDiploMultiplier; break; case DOF_TYPE_FRIENDS: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * max(iMyEra, 1) * iDiploMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * max(iMyEra, 1) * iDiploMultiplier; break; case DOF_TYPE_ALLIES: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * max(iMyEra, 1) * 2 * iDiploMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * max(iMyEra, 1) * 2 * iDiploMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * max(iMyEra, 1); break; case DOF_TYPE_BATTLE_BROTHERS: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * max(iMyEra, 1) * 3 * iDiploMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * max(iMyEra, 1) * 3 * iDiploMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * max(iMyEra, 1) * 2 * iDiploMultiplier; break; } //////////////////////////////////// // WORLD CONGRESS ALIGNMENT //////////////////////////////////// if (pLeague != NULL) { iDiploMultiplier = IsCompetingForVictory() && IsGoingForDiploVictory() ? AdjustConditionalModifier(600, GetDiploBalance()) : AdjustConditionalModifier(200, GetDiploBalance()); CvLeagueAI::AlignmentLevels eAlignment = (GetPlayer()->GetLeagueAI()->EvaluateAlignment(ePlayer, /*bIgnoreWar*/ true)); // Our prime league competitor isn't considered aligned with our interests... if (IsCompetingForVictory() && eAlignment > CvLeagueAI::ALIGNMENT_RIVAL) { if (GetPrimeLeagueCompetitor() == ePlayer) { if (IsGoingForDiploVictory()) iDiploMultiplier = 0; else iDiploMultiplier -= AdjustConditionalModifier(100, GetWorkAgainstWillingness()); } if (bTheyAreCloseToDiploVictory) { iDiploMultiplier -= AdjustConditionalModifier(100, GetWorkAgainstWillingness()); } iDiploMultiplier = max(iDiploMultiplier, 0); } switch (eAlignment) { case CvLeagueAI::ALIGNMENT_SELF: case CvLeagueAI::ALIGNMENT_TEAMMATE: case CvLeagueAI::ALIGNMENT_LEADER: case CvLeagueAI::ALIGNMENT_WAR: UNREACHABLE(); case CvLeagueAI::ALIGNMENT_ENEMY: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetWorkAgainstWillingness() * iDiploMultiplier / 100; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * GetWorkAgainstWillingness() * iDiploMultiplier / 100; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * GetWorkAgainstWillingness() * iDiploMultiplier / 100 : 0; break; case CvLeagueAI::ALIGNMENT_HATRED: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetWorkAgainstWillingness() * iDiploMultiplier / 200; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * GetWorkAgainstWillingness() * iDiploMultiplier / 200; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * GetWorkAgainstWillingness() * iDiploMultiplier / 200; break; case CvLeagueAI::ALIGNMENT_RIVAL: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetWorkAgainstWillingness() * iDiploMultiplier / 300; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * GetWorkAgainstWillingness() * iDiploMultiplier / 300; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * GetWorkAgainstWillingness() * iDiploMultiplier / 300; break; case CvLeagueAI::ALIGNMENT_NEUTRAL: vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * GetDiploBalance() * iDiploMultiplier / 200; break; case CvLeagueAI::ALIGNMENT_FRIEND: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * GetWorkWithWillingness() * iDiploMultiplier / 300; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * GetWorkWithWillingness() * iDiploMultiplier / 300; break; case CvLeagueAI::ALIGNMENT_CONFIDANT: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * GetWorkWithWillingness() * iDiploMultiplier / 200; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * GetWorkWithWillingness() * iDiploMultiplier / 200; break; case CvLeagueAI::ALIGNMENT_ALLY: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * GetWorkWithWillingness() * iDiploMultiplier / 100; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * GetWorkWithWillingness() * iDiploMultiplier / 100; break; case CvLeagueAI::ALIGNMENT_LIBERATOR: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * GetWorkWithWillingness() * iDiploMultiplier / 50; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * GetWorkWithWillingness() * iDiploMultiplier / 50; break; } } //////////////////////////////////// // VICTORY ISSUES //////////////////////////////////// int DifficultyModifier = 0; int iMaxVictoryDispute = 0; if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP)) DifficultyModifier = max(GET_PLAYER(ePlayer).getHandicapInfo().getVictoryDisputePercent(), GET_PLAYER(ePlayer).getHandicapInfo().getVictoryBlockPercent()); else DifficultyModifier = max(GC.getGame().getHandicapInfo().getVictoryDisputePercent(), GC.getGame().getHandicapInfo().getVictoryBlockPercent()); if (IsCompetingForVictory() && iGameTurn > 150) { iMaxVictoryDispute = max((int)GetVictoryDisputeLevel(ePlayer), (int)GetVictoryBlockLevel(ePlayer)); DisputeLevelTypes eMaxVictoryDispute = (DisputeLevelTypes) iMaxVictoryDispute; switch (eMaxVictoryDispute) { case DISPUTE_LEVEL_NONE: vApproachScores[CIV_APPROACH_FRIENDLY] += bProvokedUs ? 0 : vApproachBias[CIV_APPROACH_FRIENDLY] * max(iTheirEra, 1) * GetVictoryCompetitiveness() * DifficultyModifier / 500; vApproachScores[CIV_APPROACH_NEUTRAL] += bProvokedUs ? 0 : vApproachBias[CIV_APPROACH_NEUTRAL] * max(iTheirEra, 1) * GetVictoryCompetitiveness() * DifficultyModifier / 500; break; case DISPUTE_LEVEL_WEAK: vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * max(iTheirEra, 1) * GetVictoryCompetitiveness() * DifficultyModifier / 1000; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * max(iTheirEra, 1) * GetVictoryCompetitiveness() * DifficultyModifier / 1000; break; case DISPUTE_LEVEL_STRONG: vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * max(iTheirEra, 1) * GetVictoryCompetitiveness() * DifficultyModifier / 750; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * max(iTheirEra, 1) * GetVictoryCompetitiveness() * DifficultyModifier / 750; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * max(iTheirEra, 1) * GetVictoryCompetitiveness() * DifficultyModifier / 750; break; case DISPUTE_LEVEL_FIERCE: vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * max(iTheirEra, 1) * GetVictoryCompetitiveness() * DifficultyModifier / 500; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * max(iTheirEra, 1) * GetVictoryCompetitiveness() * DifficultyModifier / 500; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * max(iTheirEra, 1) * GetVictoryCompetitiveness() * DifficultyModifier / 500; break; } } //////////////////////////////////// // TERRITORIAL DISPUTES //////////////////////////////////// DisputeLevelTypes eDisputeLevel = GetLandDisputeLevel(ePlayer); DifficultyModifier = GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getLandDisputePercent() : GC.getGame().getHandicapInfo().getLandDisputePercent(); int iMultiplier = 1; int iModifier = 0; bool bBonus = true; // territorial disputes should always play a major role ... bool bVictoryCompetitor = false; if (IsConqueror() || IsSecondaryConqueror()) { iMultiplier++; } if (bConquerorTraits) { iMultiplier++; } if (iMyEra <= GD_INT_GET(CLASSICAL_ERA)) { iMultiplier += 2; } if (iMyEra >= GD_INT_GET(MEDIEVAL_ERA) && IsGoingForWorldConquest()) { iMultiplier += 2; bVictoryCompetitor = true; } if (bCloseToWorldConquest) { iMultiplier += 2; bVictoryCompetitor = true; } if (bTheyAreCloseToWorldConquest || GetPlayerNumMajorsConquered(ePlayer) > 2) { iMultiplier++; bBonus = false; bVictoryCompetitor = true; } if (IsRecklessExpander(ePlayer)) { iMultiplier++; bBonus = false; } // Additional multiplier increase if dispute level is fierce if (eDisputeLevel == DISPUTE_LEVEL_FIERCE) { if (bVictoryCompetitor) { iMultiplier *= 2; } else if (IsConqueror() || bConquerorTraits) { iMultiplier++; } } // No non-competition bonuses if they've provoked us. if (bProvokedUs) { bBonus = false; } // Scale based on flavors and difficulty level if (eDisputeLevel > DISPUTE_LEVEL_NONE) { iModifier = GetBoldness() * iMultiplier * DifficultyModifier; iModifier /= 5; } else if (eDisputeLevel == DISPUTE_LEVEL_NONE && bBonus) { iModifier = (GetBoldness() + GetNeediness()) * iMultiplier * DifficultyModifier; iModifier /= 10; } switch (eDisputeLevel) { case DISPUTE_LEVEL_NONE: vApproachScores[CIV_APPROACH_FRIENDLY] += bBonus ? vApproachBias[CIV_APPROACH_FRIENDLY] * iModifier / 100 : 0; vApproachScores[CIV_APPROACH_NEUTRAL] += bBonus ? vApproachBias[CIV_APPROACH_NEUTRAL] * iModifier / 100 : 0; break; case DISPUTE_LEVEL_WEAK: vApproachScores[CIV_APPROACH_WAR] += (bVictoryCompetitor || IsConqueror() || bConquerorTraits) ? vApproachBias[CIV_APPROACH_WAR] * iModifier / 100 : 0; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iModifier / 100; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * iModifier / 100; break; case DISPUTE_LEVEL_STRONG: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iModifier / 100; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iModifier / 100; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iModifier / 100; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * iModifier / 100 : 0; break; case DISPUTE_LEVEL_FIERCE: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iModifier / 100; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iModifier / 100; break; } //////////////////////////////////// // WORLD WONDER COMPETITION //////////////////////////////////// eDisputeLevel = GetWonderDisputeLevel(ePlayer); DifficultyModifier = GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getWonderDisputePercent() : GC.getGame().getHandicapInfo().getWonderDisputePercent(); iMultiplier = 1; bBonus = false; bVictoryCompetitor = false; if (IsCultural() || IsSecondaryCultural()) { iMultiplier++; bBonus = true; } if (bCulturalTraits) { iMultiplier++; bBonus = true; } if (iMyEra >= GD_INT_GET(RENAISSANCE_ERA) && IsGoingForCultureVictory()) { iMultiplier++; bBonus = true; bVictoryCompetitor = true; } if (bCloseToCultureVictory) { iMultiplier++; bBonus = true; bVictoryCompetitor = true; } if (bTheyAreCloseToCultureVictory && IsEndgameAggressiveTo(ePlayer)) { iMultiplier++; bBonus = false; bVictoryCompetitor = true; } if (GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(eMyPlayer) >= INFLUENCE_LEVEL_INFLUENTIAL) { iMultiplier++; bBonus = !bVictoryCompetitor && IsCompetingForVictory(); } else if (GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(eMyPlayer) == INFLUENCE_LEVEL_POPULAR && GET_PLAYER(ePlayer).GetCulture()->GetInfluenceTrend(eMyPlayer) == INFLUENCE_TREND_RISING) { iMultiplier++; bBonus = !bVictoryCompetitor && IsCompetingForVictory(); } if (IsWonderSpammer(ePlayer)) { iMultiplier++; bBonus = false; // Special: If they're spamming World Wonders and we're cultural, treat this as a fierce dispute level if (IsCultural() || bCulturalTraits) eDisputeLevel = DISPUTE_LEVEL_FIERCE; } // Additional multiplier increase if dispute level is fierce if (eDisputeLevel == DISPUTE_LEVEL_FIERCE) { if (bVictoryCompetitor) { iMultiplier *= 2; } else if (IsCultural() || bCulturalTraits) { iMultiplier++; } } // No non-competition bonuses if they've provoked us. if (bProvokedUs) { bBonus = false; } // Scale based on flavors and difficulty level if (eDisputeLevel > DISPUTE_LEVEL_NONE) { iModifier = GetWonderCompetitiveness() * iMultiplier * DifficultyModifier; iModifier /= 5; } else if (eDisputeLevel == DISPUTE_LEVEL_NONE && bBonus) { iModifier = (GetWonderCompetitiveness() + GetNeediness()) * iMultiplier * DifficultyModifier; iModifier /= 10; } switch (eDisputeLevel) { case DISPUTE_LEVEL_NONE: vApproachScores[CIV_APPROACH_FRIENDLY] += bBonus ? vApproachBias[CIV_APPROACH_FRIENDLY] * iModifier / 100 : 0; vApproachScores[CIV_APPROACH_NEUTRAL] += bBonus ? vApproachBias[CIV_APPROACH_NEUTRAL] * iModifier / 100 : 0; break; case DISPUTE_LEVEL_WEAK: vApproachScores[CIV_APPROACH_WAR] += (bVictoryCompetitor || IsCultural() || bCulturalTraits) ? vApproachBias[CIV_APPROACH_WAR] * iModifier / 100 : 0; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iModifier / 100; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * iModifier / 100; break; case DISPUTE_LEVEL_STRONG: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iModifier / 100; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iModifier / 100; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iModifier / 100; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * iModifier / 100 : 0; break; case DISPUTE_LEVEL_FIERCE: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iModifier / 100; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iModifier / 100; break; } //////////////////////////////////// // CITY-STATE COMPETITION //////////////////////////////////// if (bMetValidMinor) { eDisputeLevel = GetMinorCivDisputeLevel(ePlayer); DifficultyModifier = GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getMinorCivDisputePercent() : GC.getGame().getHandicapInfo().getMinorCivDisputePercent(); iMultiplier = 1; bBonus = false; bVictoryCompetitor = false; if (IsDiplomat() || IsSecondaryDiplomat()) { iMultiplier++; bBonus = true; } if (bDiplomatTraits) { iMultiplier++; bBonus = true; } if (iMyEra >= GD_INT_GET(RENAISSANCE_ERA) && IsGoingForDiploVictory()) { iMultiplier++; bBonus = true; bVictoryCompetitor = true; } if (bCloseToDiploVictory) { iMultiplier++; bBonus = true; bVictoryCompetitor = true; } if (bTheyAreCloseToDiploVictory && IsEndgameAggressiveTo(ePlayer)) { iMultiplier++; bBonus = false; bVictoryCompetitor = true; } if (GetNumTimesPerformedCoupAgainstUs(ePlayer) > 0) { iMultiplier++; bBonus = false; } if (IsAngryAboutProtectedMinorAttacked(ePlayer) || IsAngryAboutProtectedMinorKilled(ePlayer) || IgnoredAttackCityStatePromise(ePlayer) || BrokeAttackCityStatePromise(ePlayer)) { iMultiplier++; bBonus = false; } // Additional multiplier increase if dispute level is fierce if (eDisputeLevel == DISPUTE_LEVEL_FIERCE) { if (bVictoryCompetitor) { iMultiplier *= 2; } else if (IsDiplomat() || bDiplomatTraits) { iMultiplier++; } } // No non-competition bonuses if they've provoked us. if (bProvokedUs) { bBonus = false; } // Scale based on flavors and difficulty level if (eDisputeLevel > DISPUTE_LEVEL_NONE) { iModifier = GetMinorCivCompetitiveness() * iMultiplier * DifficultyModifier; iModifier /= 5; } else if (eDisputeLevel == DISPUTE_LEVEL_NONE && bBonus) { iModifier = (GetMinorCivCompetitiveness() + GetNeediness()) * iMultiplier * DifficultyModifier; iModifier /= 10; } switch (eDisputeLevel) { case DISPUTE_LEVEL_NONE: vApproachScores[CIV_APPROACH_FRIENDLY] += bBonus ? vApproachBias[CIV_APPROACH_FRIENDLY] * iModifier / 100 : 0; vApproachScores[CIV_APPROACH_NEUTRAL] += bBonus ? vApproachBias[CIV_APPROACH_NEUTRAL] * iModifier / 100 : 0; break; case DISPUTE_LEVEL_WEAK: vApproachScores[CIV_APPROACH_WAR] += (bVictoryCompetitor || IsDiplomat() || bDiplomatTraits) ? vApproachBias[CIV_APPROACH_WAR] * iModifier / 100 : 0; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iModifier / 100; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * iModifier / 100; break; case DISPUTE_LEVEL_STRONG: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iModifier / 100; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iModifier / 100; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iModifier / 100; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * iModifier / 100 : 0; break; case DISPUTE_LEVEL_FIERCE: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iModifier / 100; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iModifier / 100; break; } } if (IsCompetingForVictory() && (iMyEra > GD_INT_GET(ANCIENT_ERA) || iTheirEra > GD_INT_GET(ANCIENT_ERA))) { int iBlockEra = max(0, max(iMyEra, iTheirEra) - 1); //////////////////////////////////// // TECH BLOCK //////////////////////////////////// BlockLevelTypes eBlockLevel = GetTechBlockLevel(ePlayer); DifficultyModifier = GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getTechBlockPercent() : GC.getGame().getHandicapInfo().getTechBlockPercent(); iMultiplier = 1; bBonus = false; bVictoryCompetitor = false; if (IsScientist() || IsSecondaryScientist()) { iMultiplier++; bBonus = true; } if (bScientistTraits) { iMultiplier++; bBonus = true; } if (iMyEra >= GD_INT_GET(RENAISSANCE_ERA) && IsGoingForSpaceshipVictory()) { iMultiplier++; bVictoryCompetitor = true; } if (bCloseToScienceVictory) { iMultiplier++; bVictoryCompetitor = true; } if (bTheyAreCloseToScienceVictory && IsEndgameAggressiveTo(ePlayer)) { iMultiplier++; bBonus = false; bVictoryCompetitor = true; } if (GetNumTimesRobbedBy(ePlayer) > 0 || BrokeSpyPromise(ePlayer) || IgnoredSpyPromise(ePlayer)) { iMultiplier++; bBonus = false; } // Additional multiplier increase if block level is fierce if (eBlockLevel == BLOCK_LEVEL_FIERCE) { if (bVictoryCompetitor) { iMultiplier *= 2; } else if (IsScientist() || bScientistTraits) { iMultiplier++; } } // No non-competition bonuses if they've provoked us. if (bProvokedUs) { bBonus = false; } // Scale based on flavors and difficulty level if (eBlockLevel > BLOCK_LEVEL_NONE) { iModifier = GetDiploBalance() * iMultiplier * DifficultyModifier * 100; iModifier /= max(500, 1000 - (iBlockEra * 125)); } else if (eBlockLevel == BLOCK_LEVEL_NONE && bBonus) { iModifier = (GetDiploBalance() + GetNeediness()) * iMultiplier * DifficultyModifier; iModifier /= 10; } switch (eBlockLevel) { case BLOCK_LEVEL_NONE: vApproachScores[CIV_APPROACH_FRIENDLY] += bBonus ? vApproachBias[CIV_APPROACH_FRIENDLY] * iModifier / 100 : 0; vApproachScores[CIV_APPROACH_NEUTRAL] += bBonus ? vApproachBias[CIV_APPROACH_NEUTRAL] * iModifier / 100 : 0; break; case BLOCK_LEVEL_WEAK: vApproachScores[CIV_APPROACH_WAR] += (bVictoryCompetitor || IsScientist() || bScientistTraits) ? vApproachBias[CIV_APPROACH_WAR] * iModifier / 100 : 0; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iModifier / 100; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * iModifier / 100; break; case BLOCK_LEVEL_STRONG: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iModifier / 100; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iModifier / 100; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iModifier / 100; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * iModifier / 100 : 0; break; case BLOCK_LEVEL_FIERCE: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iModifier / 100; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iModifier / 100; break; } //////////////////////////////////// // POLICY BLOCK //////////////////////////////////// eBlockLevel = GetPolicyBlockLevel(ePlayer); DifficultyModifier = GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getPolicyBlockPercent() : GC.getGame().getHandicapInfo().getPolicyBlockPercent(); iMultiplier = 1; bBonus = false; bVictoryCompetitor = false; if (IsCultural() || IsSecondaryCultural()) { iMultiplier++; bBonus = true; } if (bCulturalTraits) { iMultiplier++; bBonus = true; } if (iMyEra >= GD_INT_GET(RENAISSANCE_ERA) && IsGoingForCultureVictory()) { iMultiplier++; bVictoryCompetitor = true; } if (bCloseToCultureVictory) { iMultiplier++; bVictoryCompetitor = true; } if (bTheyAreCloseToCultureVictory && IsEndgameAggressiveTo(ePlayer)) { iMultiplier++; bBonus = false; bVictoryCompetitor = true; } if (GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(eMyPlayer) >= INFLUENCE_LEVEL_INFLUENTIAL) { iMultiplier++; bBonus = !bVictoryCompetitor && IsCompetingForVictory(); } else if (GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(eMyPlayer) == INFLUENCE_LEVEL_POPULAR && GET_PLAYER(ePlayer).GetCulture()->GetInfluenceTrend(eMyPlayer) == INFLUENCE_TREND_RISING) { iMultiplier++; bBonus = !bVictoryCompetitor && IsCompetingForVictory(); } if (IsWonderSpammer(ePlayer)) { iMultiplier++; bBonus = false; } // Additional multiplier increase if block level is fierce if (eBlockLevel == BLOCK_LEVEL_FIERCE) { if (bVictoryCompetitor) { iMultiplier *= 2; } else if (IsCultural() || bCulturalTraits) { iMultiplier++; } } // No non-competition bonuses if they've provoked us. if (bProvokedUs) { bBonus = false; } // Scale based on flavors and difficulty level if (eBlockLevel > BLOCK_LEVEL_NONE) { iModifier = GetDiploBalance() * iMultiplier * DifficultyModifier * 100; iModifier /= max(500, 1000 - (iBlockEra * 125)); } else if (eBlockLevel == BLOCK_LEVEL_NONE && bBonus) { iModifier = (GetDiploBalance() + GetNeediness()) * iMultiplier * DifficultyModifier; iModifier /= 10; } switch (eBlockLevel) { case BLOCK_LEVEL_NONE: vApproachScores[CIV_APPROACH_FRIENDLY] += bBonus ? vApproachBias[CIV_APPROACH_FRIENDLY] * iModifier / 100 : 0; vApproachScores[CIV_APPROACH_NEUTRAL] += bBonus ? vApproachBias[CIV_APPROACH_NEUTRAL] * iModifier / 100 : 0; break; case BLOCK_LEVEL_WEAK: vApproachScores[CIV_APPROACH_WAR] += (bVictoryCompetitor || IsCultural() || bCulturalTraits) ? vApproachBias[CIV_APPROACH_WAR] * iModifier / 100 : 0; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iModifier / 100; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * iModifier / 100; break; case BLOCK_LEVEL_STRONG: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iModifier / 100; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iModifier / 100; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iModifier / 100; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * iModifier / 100 : 0; break; case BLOCK_LEVEL_FIERCE: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iModifier / 100; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iModifier / 100; break; } } //////////////////////////////////// // WAR STATE - if we're at war, how's the war going? //////////////////////////////////// switch (GetWarState(ePlayer)) { case NO_WAR_STATE_TYPE: break; // Not at war, do nothing. case WAR_STATE_NEARLY_DEFEATED: vApproachScores[CIV_APPROACH_AFRAID] += vApproachBias[CIV_APPROACH_AFRAID] * 5 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 5 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_FRIENDLY] += bProvokedUs ? 0 : vApproachBias[CIV_APPROACH_FRIENDLY] * 5 * iTheirAttackMultiplier; break; case WAR_STATE_DEFENSIVE: case WAR_STATE_TROUBLED: vApproachScores[CIV_APPROACH_AFRAID] += vApproachBias[CIV_APPROACH_AFRAID] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_FRIENDLY] += bProvokedUs ? 0 : vApproachBias[CIV_APPROACH_FRIENDLY] * 2 * iTheirAttackMultiplier; break; case WAR_STATE_STALEMATE: case WAR_STATE_CALM: vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; break; case WAR_STATE_OFFENSIVE: vApproachScores[CIV_APPROACH_WAR] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 2 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR] * iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iAttackMultiplier : vApproachBias[CIV_APPROACH_HOSTILE] * iAttackMultiplier; break; case WAR_STATE_NEARLY_WON: vApproachScores[CIV_APPROACH_WAR] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 5 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR] * 2 * iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_HOSTILE] * 5 * iAttackMultiplier : vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iAttackMultiplier; break; } //////////////////////////////////// // MILITARY AGGRESSIVE POSTURE - if not at war, how aggressively has ePlayer deployed their units near our borders? //////////////////////////////////// if (!IsAtWar(ePlayer) && !GET_TEAM(eMyTeam).isForcePeace(eTeam)) { switch (GetMilitaryAggressivePosture(ePlayer)) { case AGGRESSIVE_POSTURE_NONE: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; break; case AGGRESSIVE_POSTURE_LOW: vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; break; case AGGRESSIVE_POSTURE_MEDIUM: vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; break; case AGGRESSIVE_POSTURE_HIGH: vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 3; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 3; break; case AGGRESSIVE_POSTURE_INCREDIBLE: vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 3; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 3; break; } } //////////////////////////////////// // VASSALAGE //////////////////////////////////// // They refused to give us our independence when we asked (or they betrayed us)! if (IsAngryAboutPlayerVassalageForcefullyRevoked(ePlayer) || BrokeVassalAgreement(ePlayer)) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * /*4*/ GD_INT_GET(APPROACH_WAR_VASSAL_FORCEFULLY_REVOKED); vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * /*-10*/ GD_INT_GET(APPROACH_FRIENDLY_VASSAL_FORCEFULLY_REVOKED); vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * /*-10*/ GD_INT_GET(APPROACH_DECEPTIVE_VASSAL_FORCEFULLY_REVOKED); } // They gave us our independence! else if (IsHappyAboutPlayerVassalagePeacefullyRevoked(ePlayer) || IsMasterLiberatedMeFromVassalage(ePlayer)) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * /*-4*/ GD_INT_GET(APPROACH_WAR_VASSAL_PEACEFULLY_REVOKED); vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * /*5*/ GD_INT_GET(APPROACH_FRIENDLY_VASSAL_PEACEFULLY_REVOKED); vApproachScores[CIV_APPROACH_DECEPTIVE] += IsMasterLiberatedMeFromVassalage(ePlayer) ? 0 : vApproachBias[CIV_APPROACH_DECEPTIVE] * /*2*/ GD_INT_GET(APPROACH_DECEPTIVE_VASSAL_PEACEFULLY_REVOKED); } // Do we have a master? What does our master think about this player? // For human masters, shadow AI approach will be used if (GetPlayer()->IsVassalOfSomeone()) { TeamTypes eMaster = GET_TEAM(GetTeam()).GetMaster(); const vector& vMasterTeam = GET_TEAM(eMaster).getPlayers(); for (size_t i=0; iGetSurfaceApproach(ePlayer); switch (eMasterApproach) { case CIV_APPROACH_WAR: case CIV_APPROACH_HOSTILE: vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iMasterOpinionValue; break; case CIV_APPROACH_GUARDED: vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iMasterOpinionValue; break; case CIV_APPROACH_AFRAID: vApproachScores[CIV_APPROACH_AFRAID] += vApproachBias[CIV_APPROACH_AFRAID] * iMasterOpinionValue; break; case CIV_APPROACH_FRIENDLY: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iMasterOpinionValue; break; default: vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * iMasterOpinionValue; break; } } // Our master is treating us poorly, so we will act against their interests! else { CivApproachTypes ePlayerApproachTowardsMaster = pTheirDiplo->GetSurfaceApproach(eMasterPlayer); switch (ePlayerApproachTowardsMaster) { case CIV_APPROACH_WAR: case CIV_APPROACH_HOSTILE: case CIV_APPROACH_GUARDED: case CIV_APPROACH_AFRAID: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iMasterOpinionValue; break; case CIV_APPROACH_FRIENDLY: vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iMasterOpinionValue; break; default: vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * iMasterOpinionValue; break; } } } } //--------------------------------// // [PART 6: STRATEGIC DIPLOMACY] // //--------------------------------// bool bIgnorePolicyDifferences = IsIgnorePolicyDifferences(ePlayer); bool bIgnoreReligionDifferences = IsIgnoreReligionDifferences(ePlayer); bool bIgnoreIdeologyDifferences = IsIgnoreIdeologyDifferences(ePlayer); //////////////////////////////////// // SOCIAL POLICIES //////////////////////////////////// // Similar government types get along better int iPolicyScore = GetNumSamePolicies(ePlayer); if (iPolicyScore > 0 && !bUntrustworthy) { vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * iPolicyScore; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * iPolicyScore; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * iPolicyScore; } // Different government types get along worse else if (iPolicyScore < 0) { if (bEarlyGameCompetitor) { iPolicyScore *= 2; } if (!bIgnorePolicyDifferences) { if (eMilitaryStrength < STRENGTH_AVERAGE) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iPolicyScore; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iPolicyScore; } else { vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iPolicyScore; } } } //////////////////////////////////// // RELIGION //////////////////////////////////// int iReligiosityScore = 0; bool bSameReligion = false; bool bDifferentReligions = false; if (!GC.getGame().isOption(GAMEOPTION_NO_RELIGION)) { iReligiosityScore = max(iFlavorReligion, GC.getEraInfo((EraTypes)iGameEra)->getDiploEmphasisReligion()); if (iFlavorReligion < 5) { iReligiosityScore = max(0, iReligiosityScore - 2); } else if (iFlavorReligion > 7) { iReligiosityScore += 2; } iReligiosityScore += pTraits->IsReligious() ? 2 : 0; iReligiosityScore *= 100; if (iGameEra < GD_INT_GET(MEDIEVAL_ERA) || iGameEra > GD_INT_GET(RENAISSANCE_ERA)) iReligiosityScore /= 2; // Have they been converting our cities? Grr... if (GetNegativeReligiousConversionPoints(ePlayer) > 0 && !MadeNoConvertPromise(ePlayer)) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] + iReligiosityScore + (GetNegativeReligiousConversionPoints(ePlayer) * 100); vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] + iReligiosityScore + (GetNegativeReligiousConversionPoints(ePlayer) * 100); } ReligionTypes eOurOwnedReligion = GetPlayer()->GetReligions()->GetOwnedReligion(); ReligionTypes eOurStateReligion = GetPlayer()->GetReligions()->GetStateReligion(false); ReligionTypes eTheirOwnedReligion = GET_PLAYER(ePlayer).GetReligions()->GetOwnedReligion(); ReligionTypes eTheirStateReligion = GET_PLAYER(ePlayer).GetReligions()->GetStateReligion(false); // We didn't found or conquer, but have a state religion if (eOurOwnedReligion == NO_RELIGION && eOurStateReligion != NO_RELIGION) { if (GetNegativeReligiousConversionPoints(ePlayer) <= 0 && !bEverCapturedKeyCity && !bUntrustworthy) { // We adopted their owned religion if (eOurStateReligion == eTheirOwnedReligion) { bSameReligion = true; vApproachScores[CIV_APPROACH_FRIENDLY] += (vApproachBias[CIV_APPROACH_FRIENDLY] * 2) + iReligiosityScore; // If it's the World Religion and they're its controller, support them since we get extra League votes from it if (GC.getGame().GetGameLeagues()->GetReligionSpreadStrengthModifier(ePlayer, eTheirOwnedReligion) > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += (vApproachBias[CIV_APPROACH_FRIENDLY] * 2) + iReligiosityScore; } } // Same state religions? else if (eOurStateReligion == eTheirStateReligion) { bSameReligion = true; vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] + iReligiosityScore; } } // Different majority religions? if (eOurStateReligion != eTheirStateReligion && eTheirStateReligion != NO_RELIGION) { if (!bIgnoreReligionDifferences) { bDifferentReligions = true; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] + iReligiosityScore; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] + iReligiosityScore; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] + iReligiosityScore; } } } // We founded or conquered else if (eOurOwnedReligion != NO_RELIGION) { // Did they also found or conquer? We don't like that! if (eTheirOwnedReligion != NO_RELIGION) { if (!bIgnoreReligionDifferences) { bDifferentReligions = true; vApproachScores[CIV_APPROACH_WAR] += (vApproachBias[CIV_APPROACH_WAR] * 2) + iReligiosityScore; vApproachScores[CIV_APPROACH_DECEPTIVE] += (vApproachBias[CIV_APPROACH_DECEPTIVE] * 2) + iReligiosityScore; vApproachScores[CIV_APPROACH_GUARDED] += (vApproachBias[CIV_APPROACH_GUARDED] * 2) + iReligiosityScore; // If it's the World Religion and they control its Holy City, we should work against them if (GC.getGame().GetGameLeagues()->GetReligionSpreadStrengthModifier(ePlayer, eTheirOwnedReligion) > 0) { vApproachScores[CIV_APPROACH_WAR] += (vApproachBias[CIV_APPROACH_WAR] * 2) + iReligiosityScore; vApproachScores[CIV_APPROACH_HOSTILE] += (vApproachBias[CIV_APPROACH_HOSTILE] * 2) + iReligiosityScore; vApproachScores[CIV_APPROACH_GUARDED] += (vApproachBias[CIV_APPROACH_GUARDED] * 2) + iReligiosityScore; } } } // No? Well, do they have a state religion? else if (eTheirStateReligion != NO_RELIGION) { // Ours? if (eTheirStateReligion == eOurOwnedReligion) { bSameReligion = true; vApproachScores[CIV_APPROACH_FRIENDLY] += (vApproachBias[CIV_APPROACH_FRIENDLY] * 3) + iReligiosityScore; // If it's the World Religion and we control its Holy City, we should work together if (GC.getGame().GetGameLeagues()->GetReligionSpreadStrengthModifier(eMyPlayer, eOurOwnedReligion) > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += (vApproachBias[CIV_APPROACH_FRIENDLY] * 2) + iReligiosityScore; } } // Someone else's? else if (!bIgnoreReligionDifferences) { // Who controls the Holy City? PlayerTypes eController = NO_PLAYER; const CvReligion* pReligion = GC.getGame().GetGameReligions()->GetReligion(eTheirStateReligion, NO_PLAYER); if (MOD_BALANCE_VP) { if (pReligion) { CvCity* pHolyCity = pReligion->GetHolyCity(); if (pHolyCity != NULL) { eController = pHolyCity->getOwner(); } } } else if (pReligion) { eController = pReligion->m_eFounder; } // If the religion's controller is our teammate, don't apply a penalty. if (eController == NO_PLAYER || !IsTeammate(eController)) { bDifferentReligions = true; // If the religion's founder is THEIR teammate, treat it like an opposing state religion. if (eController != NO_PLAYER && pTheirDiplo->IsTeammate(eController)) { vApproachScores[CIV_APPROACH_WAR] += (vApproachBias[CIV_APPROACH_WAR] * 2) + iReligiosityScore; vApproachScores[CIV_APPROACH_DECEPTIVE] += (vApproachBias[CIV_APPROACH_DECEPTIVE] * 2) + iReligiosityScore; vApproachScores[CIV_APPROACH_GUARDED] += (vApproachBias[CIV_APPROACH_GUARDED] * 2) + iReligiosityScore; // If it's the World Religion and their teammate controls its Holy City, we should work against them if (GC.getGame().GetGameLeagues()->GetReligionSpreadStrengthModifier(eController, eTheirStateReligion) > 0) { vApproachScores[CIV_APPROACH_WAR] += (vApproachBias[CIV_APPROACH_WAR] * 2) + iReligiosityScore; vApproachScores[CIV_APPROACH_HOSTILE] += (vApproachBias[CIV_APPROACH_HOSTILE] * 2) + iReligiosityScore; vApproachScores[CIV_APPROACH_GUARDED] += (vApproachBias[CIV_APPROACH_GUARDED] * 2) + iReligiosityScore; } } // Otherwise, apply a penalty for different majority religions. else { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] + iReligiosityScore; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] + iReligiosityScore; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] + iReligiosityScore; } } } } } } //////////////////////////////////// // IDEOLOGY //////////////////////////////////// int iFlavorCulture = m_pPlayer->GetFlavorManager()->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_CULTURE")); int iIdeologueScore = max(iFlavorCulture, GC.getEraInfo((EraTypes)iGameEra)->getDiploEmphasisLatePolicies()); if (iFlavorCulture < 5) { iIdeologueScore = max(0, iIdeologueScore - 2); } else if (iFlavorCulture > 7) { iIdeologueScore += 2; } iIdeologueScore += IsCultural() || bCulturalTraits ? 2 : 0; iIdeologueScore += IsGoingForCultureVictory() || bCloseToCultureVictory ? 2 : 0; iIdeologueScore *= 100; if (iGameEra >= GD_INT_GET(INFORMATION_ERA)) iIdeologueScore /= 2; PolicyBranchTypes eMyBranch = m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree(); PolicyBranchTypes eTheirBranch = GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree(); bool bSameIdeology = false; bool bDifferentIdeologies = false; if (eMyBranch != NO_POLICY_BRANCH_TYPE && eTheirBranch != NO_POLICY_BRANCH_TYPE) { if (eMyBranch == eTheirBranch && !bEverCapturedKeyCity && !bUntrustworthy) { bSameIdeology = true; vApproachScores[CIV_APPROACH_FRIENDLY] += (vApproachBias[CIV_APPROACH_FRIENDLY] * 3) + iIdeologueScore; vApproachScores[CIV_APPROACH_WAR] -= (vApproachBias[CIV_APPROACH_WAR] * 3) + iIdeologueScore; vApproachScores[CIV_APPROACH_HOSTILE] -= (vApproachBias[CIV_APPROACH_HOSTILE] * 3) + iIdeologueScore; vApproachScores[CIV_APPROACH_GUARDED] -= (vApproachBias[CIV_APPROACH_GUARDED] * 3) + iIdeologueScore; // World Ideology modifier? if (pLeague != NULL && pLeague->GetPressureForIdeology(eMyBranch) > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += (vApproachBias[CIV_APPROACH_FRIENDLY] * 2) + iIdeologueScore; vApproachScores[CIV_APPROACH_WAR] -= (vApproachBias[CIV_APPROACH_WAR] * 2) + iIdeologueScore; vApproachScores[CIV_APPROACH_HOSTILE] -= (vApproachBias[CIV_APPROACH_HOSTILE] * 2) + iIdeologueScore; vApproachScores[CIV_APPROACH_GUARDED] -= (vApproachBias[CIV_APPROACH_GUARDED] * 2) + iIdeologueScore; } // Cold War? if (bColdWar) { vApproachScores[CIV_APPROACH_FRIENDLY] += (vApproachBias[CIV_APPROACH_FRIENDLY] * 2) + iIdeologueScore; vApproachScores[CIV_APPROACH_WAR] -= (vApproachBias[CIV_APPROACH_WAR] * 2) + iIdeologueScore; vApproachScores[CIV_APPROACH_HOSTILE] -= (vApproachBias[CIV_APPROACH_HOSTILE] * 2) + iIdeologueScore; vApproachScores[CIV_APPROACH_GUARDED] -= (vApproachBias[CIV_APPROACH_GUARDED] * 2) + iIdeologueScore; } } else if (eMyBranch != eTheirBranch) { if (!bIgnoreIdeologyDifferences) { bDifferentIdeologies = true; vApproachScores[CIV_APPROACH_FRIENDLY] -= (vApproachBias[CIV_APPROACH_FRIENDLY] * 3) + iIdeologueScore; vApproachScores[CIV_APPROACH_WAR] += (vApproachBias[CIV_APPROACH_WAR] * 3) + iIdeologueScore; vApproachScores[CIV_APPROACH_HOSTILE] += (vApproachBias[CIV_APPROACH_HOSTILE] * 3) + iIdeologueScore; vApproachScores[CIV_APPROACH_GUARDED] += (vApproachBias[CIV_APPROACH_GUARDED] * 3) + iIdeologueScore; // World Ideology modifier? if (pLeague != NULL && (pLeague->GetPressureForIdeology(eMyBranch) > 0 || pLeague->GetPressureForIdeology(eTheirBranch) > 0)) { vApproachScores[CIV_APPROACH_FRIENDLY] -= (vApproachBias[CIV_APPROACH_FRIENDLY] * 2) + iIdeologueScore; vApproachScores[CIV_APPROACH_WAR] += (vApproachBias[CIV_APPROACH_WAR] * 2) + iIdeologueScore; vApproachScores[CIV_APPROACH_HOSTILE] += (vApproachBias[CIV_APPROACH_HOSTILE] * 2) + iIdeologueScore; vApproachScores[CIV_APPROACH_GUARDED] += (vApproachBias[CIV_APPROACH_GUARDED] * 2) + iIdeologueScore; } // Cold War? if (bColdWar) { vApproachScores[CIV_APPROACH_FRIENDLY] += (vApproachBias[CIV_APPROACH_FRIENDLY] * 2) + iIdeologueScore; vApproachScores[CIV_APPROACH_WAR] -= (vApproachBias[CIV_APPROACH_WAR] * 2) + iIdeologueScore; vApproachScores[CIV_APPROACH_HOSTILE] -= (vApproachBias[CIV_APPROACH_HOSTILE] * 2) + iIdeologueScore; vApproachScores[CIV_APPROACH_GUARDED] -= (vApproachBias[CIV_APPROACH_GUARDED] * 2) + iIdeologueScore; } } } } //////////////////////////////////// // ALLIANCES //////////////////////////////////// int iReligionMod = iReligiosityScore; int iIdeologyMod = iIdeologueScore; // Let's play good guys versus bad guys! // Good alliances if (!bUntrustworthy) { int iSameEnemyNeutralBonus = 0; int iSameEnemyFriendlyBonus = 0; int iSameFriendWarReduction = 0; int iSameFriendHostileReduction = 0; int iSameFriendGuardedReduction = 0; int iSameFriendFriendlyBonus = 0; int iSameDPWarReduction = 0; int iSameDPHostileReduction = 0; int iSameDPGuardedReduction = 0; int iSameDPFriendlyBonus = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; bool bIgnore = GET_PLAYER(eLoopPlayer).IsVassalOfSomeone() && !GetPlayer()->IsVassalOfSomeone(); // Vassals aren't important unless we're one too. if (IsPlayerValid(eLoopPlayer) && eTeam != GET_PLAYER(eLoopPlayer).getTeam()) { if (IsAtWar(eLoopPlayer) || IsDenouncedPlayer(eLoopPlayer) || IsUntrustworthy(eLoopPlayer) || eOtherPlayerWeAreSanctioning == eLoopPlayer) { // At war with an enemy of ours if (GET_TEAM(eTeam).isAtWar(GET_PLAYER(eLoopPlayer).getTeam())) { if (!bIgnore) { iSameEnemyNeutralBonus += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; } // Is this guy helping us take down a HERETIC?!?! if (!bDifferentReligions && IsPlayerOpposingReligion(eLoopPlayer) && !IsIgnoreReligionDifferences(eLoopPlayer)) { iSameEnemyFriendlyBonus += iReligionMod; } // Is this guy helping to take down our ideological opponents? if (!bDifferentIdeologies && IsPlayerOpposingIdeology(eLoopPlayer) && !IsIgnoreIdeologyDifferences(eLoopPlayer)) { iSameEnemyFriendlyBonus += iIdeologyMod; } } // Proposed to sanction an enemy of ours if (eOtherPlayerTheyAreSanctioning == eLoopPlayer) { if (!bIgnore) { iSameEnemyNeutralBonus += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; } // Is this guy helping us take down a HERETIC?!?! if (!bDifferentReligions && IsPlayerOpposingReligion(eLoopPlayer) && !IsIgnoreReligionDifferences(eLoopPlayer)) { iSameEnemyFriendlyBonus += iReligionMod; } // Is this guy helping to take down our ideological opponents? if (!bDifferentIdeologies && IsPlayerOpposingIdeology(eLoopPlayer) && !IsIgnoreIdeologyDifferences(eLoopPlayer)) { iSameEnemyFriendlyBonus += iIdeologyMod; } } // Denounced an enemy of ours if (pTheirDiplo->IsDenouncedPlayer(eLoopPlayer)) { if (!bIgnore) { iSameEnemyFriendlyBonus += vApproachBias[CIV_APPROACH_FRIENDLY]; iSameEnemyNeutralBonus += vApproachBias[CIV_APPROACH_NEUTRAL]; } // Is this a brother of the faith who denounced a HERETIC?!?! if (bSameReligion && IsPlayerOpposingReligion(eLoopPlayer) && !IsIgnoreReligionDifferences(eLoopPlayer)) { iSameEnemyFriendlyBonus += iReligionMod; } // Is this an ideological partner who denounced an ideological opponent? if (bSameIdeology && IsPlayerOpposingIdeology(eLoopPlayer) && !IsIgnoreIdeologyDifferences(eLoopPlayer)) { iSameEnemyFriendlyBonus += iIdeologyMod; } } } else if (!bIgnore) // Ignore mutual friends who are vassals, unless we're one too! { // Befriended a friend of ours if (IsDoFAccepted(eLoopPlayer) && pTheirDiplo->IsDoFAccepted(eLoopPlayer)) { iSameFriendFriendlyBonus += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; iSameFriendWarReduction += vApproachBias[CIV_APPROACH_WAR]; iSameFriendHostileReduction += vApproachBias[CIV_APPROACH_HOSTILE]; iSameFriendGuardedReduction += vApproachBias[CIV_APPROACH_GUARDED]; // Are we all brothers of the faith? if (bSameReligion && IsPlayerSameReligion(eLoopPlayer)) { iSameFriendFriendlyBonus += iReligionMod; } // Are we all of the same ideology? if (bSameIdeology && IsPlayerSameIdeology(eLoopPlayer)) { iSameFriendFriendlyBonus += iIdeologyMod; } } // Befriended a DP of ours else if (IsHasDefensivePact(eLoopPlayer) && pTheirDiplo->IsDoFAccepted(eLoopPlayer)) { iSameFriendFriendlyBonus += vApproachBias[CIV_APPROACH_FRIENDLY]; } // Made a DP with a DP of ours if (IsHasDefensivePact(eLoopPlayer) && pTheirDiplo->IsHasDefensivePact(eLoopPlayer)) { iSameDPFriendlyBonus += vApproachBias[CIV_APPROACH_FRIENDLY]; iSameDPWarReduction += vApproachBias[CIV_APPROACH_WAR]; iSameDPHostileReduction += vApproachBias[CIV_APPROACH_HOSTILE]; iSameDPGuardedReduction += vApproachBias[CIV_APPROACH_GUARDED]; // Are we all brothers of the faith? if (bSameReligion && IsPlayerSameReligion(eLoopPlayer)) { iSameDPFriendlyBonus += iReligionMod; } // Are we all of the same ideology? if (bSameIdeology && IsPlayerSameIdeology(eLoopPlayer)) { iSameDPFriendlyBonus += iIdeologyMod; } } // Made a DP with a friend of ours else if (IsDoFAccepted(eLoopPlayer) && pTheirDiplo->IsHasDefensivePact(eLoopPlayer)) { iSameDPFriendlyBonus += vApproachBias[CIV_APPROACH_FRIENDLY]; } } } } vApproachScores[CIV_APPROACH_NEUTRAL] += AdjustConditionalModifier(iSameEnemyNeutralBonus, GetWorkAgainstWillingness()); vApproachScores[CIV_APPROACH_FRIENDLY] += AdjustConditionalModifier(iSameEnemyFriendlyBonus, GetWorkAgainstWillingness()) + AdjustConditionalModifier(iSameFriendFriendlyBonus, GetWorkWithWillingness()) + AdjustConditionalModifier(iSameDPFriendlyBonus, GetWarmongerHate()); vApproachScores[CIV_APPROACH_WAR] -= AdjustConditionalModifier(iSameFriendWarReduction, GetWorkWithWillingness()) + AdjustConditionalModifier(iSameDPWarReduction, GetWarmongerHate()); vApproachScores[CIV_APPROACH_HOSTILE] -= AdjustConditionalModifier(iSameFriendHostileReduction, GetWorkWithWillingness()) + AdjustConditionalModifier(iSameDPHostileReduction, GetWarmongerHate()); vApproachScores[CIV_APPROACH_GUARDED] -= AdjustConditionalModifier(iSameFriendGuardedReduction, GetWorkWithWillingness()) + AdjustConditionalModifier(iSameDPGuardedReduction, GetWarmongerHate()); } // Reduce global penalties for religion/ideology, if appropriate. if (bIgnoreReligionDifferences) { iReligionMod = 0; } else if (IsDoFAccepted(ePlayer) || IsHasDefensivePact(ePlayer) || GetDoFType(ePlayer) >= DOF_TYPE_ALLIES) { iReligionMod /= 2; } if (bIgnoreIdeologyDifferences || GetPlayer()->IsVassalOfSomeone()) { iIdeologyMod = 0; } else if (IsDoFAccepted(ePlayer)) { iIdeologyMod /= 2; } // Bad alliances { int iFriendsWithEnemyWarPenalty = 0; int iFriendsWithEnemyHostilePenalty = 0; int iFriendsWithEnemyDeceptivePenalty = 0; int iFriendsWithEnemyGuardedPenalty = 0; int iFriendsWithEnemyFriendlyReduction = 0; int iFriendsWithEnemyNeutralReduction = 0; int iEnemiesWithFriendWarPenalty = 0; int iEnemiesWithFriendHostilePenalty = 0; int iEnemiesWithFriendDeceptiveReduction = 0; int iEnemiesWithFriendFriendlyReduction = 0; int iEnemiesWithFriendNeutralReduction = 0; int iDenouncedByFriendWarPenalty = 0; int iDenouncedByFriendHostilePenalty = 0; int iDenouncedByFriendDeceptivePenalty = 0; int iDenouncedByFriendFriendlyReduction = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; bool bIgnore = GET_PLAYER(eLoopPlayer).IsVassalOfSomeone() && !GetPlayer()->IsVassalOfSomeone(); // Vassals aren't important unless we're one too. if (IsPlayerValid(eLoopPlayer, /*bMyTeamIsValid*/ true) && eLoopPlayer != GetID() && eTeam != GET_PLAYER(eLoopPlayer).getTeam()) { if (!IsTeammate(eLoopPlayer) && (IsAtWar(eLoopPlayer) || IsDenouncedPlayer(eLoopPlayer) || IsUntrustworthy(eLoopPlayer) || eOtherPlayerWeAreSanctioning == eLoopPlayer)) { // Made a DoF with an enemy of ours if (pTheirDiplo->IsDoFAccepted(eLoopPlayer)) { if (!bIgnore) { iFriendsWithEnemyDeceptivePenalty += (IsAtWar(eLoopPlayer) && !IsAtWar(ePlayer)) ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 : vApproachBias[CIV_APPROACH_DECEPTIVE]; iFriendsWithEnemyHostilePenalty += vApproachBias[CIV_APPROACH_HOSTILE]; iFriendsWithEnemyGuardedPenalty += vApproachBias[CIV_APPROACH_GUARDED]; iFriendsWithEnemyFriendlyReduction += vApproachBias[CIV_APPROACH_FRIENDLY]; iFriendsWithEnemyNeutralReduction += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; } // Are they brothers of a hostile faith? Let's try to slow down their religion game. if (bDifferentReligions && pTheirDiplo->IsPlayerSameReligion(eLoopPlayer)) { iFriendsWithEnemyWarPenalty += iReligionMod; iFriendsWithEnemyDeceptivePenalty += iReligionMod; } // Are they allied ideological foes? Let's try to slow down their ideological spread. if (bDifferentIdeologies && pTheirDiplo->IsPlayerSameIdeology(eLoopPlayer)) { iFriendsWithEnemyWarPenalty += iIdeologyMod; iFriendsWithEnemyDeceptivePenalty += iIdeologyMod; } } // Made a DP with an enemy of ours if (pTheirDiplo->IsHasDefensivePact(eLoopPlayer)) { if (!bIgnore) { iFriendsWithEnemyDeceptivePenalty += (IsAtWar(eLoopPlayer) && !IsAtWar(ePlayer)) ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 : vApproachBias[CIV_APPROACH_DECEPTIVE]; iFriendsWithEnemyHostilePenalty += vApproachBias[CIV_APPROACH_HOSTILE]; iFriendsWithEnemyGuardedPenalty += vApproachBias[CIV_APPROACH_GUARDED]; iFriendsWithEnemyFriendlyReduction += vApproachBias[CIV_APPROACH_FRIENDLY]; iFriendsWithEnemyNeutralReduction += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; } // Are they brothers of a hostile faith? Let's try to slow down their religion game. if (bDifferentReligions && pTheirDiplo->IsPlayerSameReligion(eLoopPlayer)) { iFriendsWithEnemyWarPenalty += iReligionMod; iFriendsWithEnemyDeceptivePenalty += iReligionMod; iFriendsWithEnemyGuardedPenalty += iReligionMod; } // Are they allied ideological foes? Let's try to slow down their ideological spread. if (bDifferentIdeologies && pTheirDiplo->IsPlayerSameIdeology(eLoopPlayer)) { iFriendsWithEnemyWarPenalty += iIdeologyMod; iFriendsWithEnemyDeceptivePenalty += iIdeologyMod; iFriendsWithEnemyGuardedPenalty += iIdeologyMod; } } } else if (IsTeammate(eLoopPlayer) || IsDoFAccepted(eLoopPlayer) || IsHasDefensivePact(eLoopPlayer)) { // At war with a friend of ours if (!bIgnore && GET_TEAM(eTeam).isAtWar(GET_PLAYER(ePlayer).getTeam())) { iEnemiesWithFriendWarPenalty += vApproachBias[CIV_APPROACH_WAR]; iEnemiesWithFriendHostilePenalty += vApproachBias[CIV_APPROACH_HOSTILE]; iEnemiesWithFriendDeceptiveReduction += vApproachBias[CIV_APPROACH_DECEPTIVE]; iEnemiesWithFriendFriendlyReduction += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; iEnemiesWithFriendNeutralReduction += vApproachBias[CIV_APPROACH_NEUTRAL]; if (IsPlayerSameReligion(eLoopPlayer)) { // Is this person attacking our brother of the faith as a HERETIC?!?! How dare they! if (bDifferentReligions) { iEnemiesWithFriendWarPenalty += iReligionMod; iEnemiesWithFriendFriendlyReduction += iReligionMod; } } if (IsPlayerSameIdeology(eLoopPlayer)) { // Same for ideology if (bDifferentIdeologies) { iEnemiesWithFriendWarPenalty += iIdeologyMod; iEnemiesWithFriendFriendlyReduction += iIdeologyMod; } } } // Proposed sanctions against a friend of ours // AI is still mad if a vassal friend of theirs is targeted with sanctions, because that's going out of your way to be nasty. // It is possible the sanctions were proposed prior to vassalage beginning, but that's OK because it's still applicable. if (eOtherPlayerTheyAreSanctioning == eLoopPlayer) { iEnemiesWithFriendWarPenalty += vApproachBias[CIV_APPROACH_WAR]; iEnemiesWithFriendHostilePenalty += vApproachBias[CIV_APPROACH_HOSTILE]; iEnemiesWithFriendDeceptiveReduction += vApproachBias[CIV_APPROACH_DECEPTIVE]; iEnemiesWithFriendFriendlyReduction += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; iEnemiesWithFriendNeutralReduction += vApproachBias[CIV_APPROACH_NEUTRAL]; if (IsPlayerSameReligion(eLoopPlayer)) { // Is this person attacking our brother of the faith as a HERETIC?!?! How dare they! if (bDifferentReligions) { iEnemiesWithFriendWarPenalty += iReligionMod; iEnemiesWithFriendFriendlyReduction += iReligionMod; } } if (IsPlayerSameIdeology(eLoopPlayer)) { // Same for ideology if (bDifferentIdeologies) { iEnemiesWithFriendWarPenalty += iIdeologyMod; iEnemiesWithFriendFriendlyReduction += iIdeologyMod; } } } // Denounced a friend of ours // AI is still mad if a vassal friend of theirs is denounced, because that's going out of your way to be nasty. if (pTheirDiplo->IsDenouncedPlayer(eLoopPlayer)) { iEnemiesWithFriendWarPenalty += vApproachBias[CIV_APPROACH_WAR]; iEnemiesWithFriendHostilePenalty += vApproachBias[CIV_APPROACH_HOSTILE]; iEnemiesWithFriendDeceptiveReduction += vApproachBias[CIV_APPROACH_DECEPTIVE]; iEnemiesWithFriendFriendlyReduction += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; iEnemiesWithFriendNeutralReduction += vApproachBias[CIV_APPROACH_NEUTRAL]; if (IsPlayerSameReligion(eLoopPlayer)) { // Is this person denouncing our brother of the faith as a HERETIC?!?! How dare they! if (bDifferentReligions) { iEnemiesWithFriendWarPenalty += iReligionMod; iEnemiesWithFriendFriendlyReduction += iReligionMod; } } if (IsPlayerSameIdeology(eLoopPlayer)) { // Same for ideology if (bDifferentIdeologies) { iEnemiesWithFriendWarPenalty += iIdeologyMod; iEnemiesWithFriendFriendlyReduction += iIdeologyMod; } } } // Denounced BY a friend of ours // AI is still mad if a vassal friend of theirs has denounced this player if (pTheirDiplo->IsDenouncedByPlayer(eLoopPlayer)) { // No war penalty here - dogpiling adequately covers that scenario iDenouncedByFriendHostilePenalty += bEarlyGameCompetitor ? vApproachBias[CIV_APPROACH_HOSTILE] : 2; iDenouncedByFriendDeceptivePenalty += (pTheirDiplo->IsAtWar(eLoopPlayer) || pTheirDiplo->IsDenouncedPlayer(eLoopPlayer)) ? 0 : vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; // No retaliation yet? Be more DECEPTIVE. iDenouncedByFriendFriendlyReduction += bEarlyGameCompetitor ? vApproachBias[CIV_APPROACH_FRIENDLY] * 2 : vApproachBias[CIV_APPROACH_FRIENDLY]; // Reduce GUARDED and AFRAID approaches depending on comparative military strength...but only if the friend is both close to the enemy, and as close to the enemy as the enemy is to us! if (GET_PLAYER(eLoopPlayer).GetProximityToPlayer(ePlayer) >= PLAYER_PROXIMITY_CLOSE && GET_PLAYER(eLoopPlayer).GetProximityToPlayer(ePlayer) >= GET_PLAYER(ePlayer).GetProximityToPlayer(eMyPlayer)) { int iDenouncedByFriendGuardedReduction = 0; int iDenouncedByFriendAfraidReduction = 0; if (pTheirDiplo->GetMilitaryStrengthComparedToUs(eLoopPlayer) > STRENGTH_AVERAGE) { iDenouncedByFriendGuardedReduction += vApproachBias[CIV_APPROACH_GUARDED] * 3; if (eMilitaryStrength < STRENGTH_AVERAGE) iDenouncedByFriendAfraidReduction += vApproachBias[CIV_APPROACH_AFRAID] * 3; else if (eMilitaryStrength == STRENGTH_AVERAGE) iDenouncedByFriendAfraidReduction += vApproachBias[CIV_APPROACH_AFRAID] * 15 / 10; else iDenouncedByFriendAfraidReduction += vApproachBias[CIV_APPROACH_AFRAID]; } else if (pTheirDiplo->GetMilitaryStrengthComparedToUs(eLoopPlayer) == STRENGTH_AVERAGE) { iDenouncedByFriendGuardedReduction += vApproachBias[CIV_APPROACH_GUARDED] * 2; if (eMilitaryStrength < STRENGTH_AVERAGE) iDenouncedByFriendAfraidReduction += vApproachBias[CIV_APPROACH_AFRAID] * 2; else if (eMilitaryStrength == STRENGTH_AVERAGE) iDenouncedByFriendAfraidReduction += vApproachBias[CIV_APPROACH_AFRAID]; else iDenouncedByFriendAfraidReduction += vApproachBias[CIV_APPROACH_AFRAID] / 2; } else { iDenouncedByFriendGuardedReduction += vApproachBias[CIV_APPROACH_GUARDED]; } // Need to add these ones now as they depend on the OTHER player's WorkAgainstWillingness flavor int iLoopWorkAgainstWillingness = EstimateWorkAgainstWillingness(eLoopPlayer); vApproachScores[CIV_APPROACH_GUARDED] -= bEarlyGameCompetitor && GET_PLAYER(eLoopPlayer).GetCurrentEra() <= 2 ? AdjustConditionalModifier(iDenouncedByFriendGuardedReduction * 2, iLoopWorkAgainstWillingness) : AdjustConditionalModifier(iDenouncedByFriendGuardedReduction, iLoopWorkAgainstWillingness); vApproachScores[CIV_APPROACH_AFRAID] -= bEarlyGameCompetitor && GET_PLAYER(eLoopPlayer).GetCurrentEra() <= 2 ? AdjustConditionalModifier(iDenouncedByFriendAfraidReduction * 2, iLoopWorkAgainstWillingness) : AdjustConditionalModifier(iDenouncedByFriendAfraidReduction, iLoopWorkAgainstWillingness); } if (!pTheirDiplo->IsDenouncedPlayer(eLoopPlayer)) // Do not stack with above modifier to avoid excessive religious/ideological fervor. { if (IsPlayerSameReligion(eLoopPlayer)) { // Has our brother of the faith denounced a HERETIC?!?! BURN THE WITCH! if (bDifferentReligions) { iDenouncedByFriendWarPenalty += iReligionMod; iDenouncedByFriendFriendlyReduction += iReligionMod; } } if (IsPlayerSameIdeology(eLoopPlayer)) { // Same for ideology if (bDifferentIdeologies) { iDenouncedByFriendWarPenalty += iIdeologyMod; iDenouncedByFriendFriendlyReduction += iIdeologyMod; } } } } } } } vApproachScores[CIV_APPROACH_WAR] += AdjustConditionalModifier(iFriendsWithEnemyWarPenalty, GetNeediness()) + AdjustConditionalModifier(iEnemiesWithFriendWarPenalty, GetLoyalty()) + AdjustConditionalModifier(iDenouncedByFriendWarPenalty, GetWorkAgainstWillingness()); vApproachScores[CIV_APPROACH_HOSTILE] += AdjustConditionalModifier(iFriendsWithEnemyHostilePenalty, GetNeediness()) + AdjustConditionalModifier(iEnemiesWithFriendHostilePenalty, GetLoyalty()) + AdjustConditionalModifier(iDenouncedByFriendHostilePenalty, GetWorkAgainstWillingness()); vApproachScores[CIV_APPROACH_DECEPTIVE] += AdjustConditionalModifier(iFriendsWithEnemyDeceptivePenalty, GetNeediness()) - AdjustConditionalModifier(iEnemiesWithFriendDeceptiveReduction, GetLoyalty()) + AdjustConditionalModifier(iDenouncedByFriendDeceptivePenalty, GetWorkAgainstWillingness()); vApproachScores[CIV_APPROACH_GUARDED] += AdjustConditionalModifier(iFriendsWithEnemyGuardedPenalty, GetNeediness()); vApproachScores[CIV_APPROACH_FRIENDLY] -= AdjustConditionalModifier(iFriendsWithEnemyFriendlyReduction, GetNeediness()) + AdjustConditionalModifier(iEnemiesWithFriendFriendlyReduction, GetLoyalty()) + AdjustConditionalModifier(iDenouncedByFriendFriendlyReduction, GetWorkAgainstWillingness()); vApproachScores[CIV_APPROACH_NEUTRAL] -= AdjustConditionalModifier(iFriendsWithEnemyNeutralReduction, GetNeediness()) + AdjustConditionalModifier(iEnemiesWithFriendNeutralReduction, GetLoyalty()); } //////////////////////////////////// // Are we getting yields from trade with them? //////////////////////////////////// // Compare trade route value for us VS. trade route value for them int iCurrentGoldIn = GetPlayer()->GetTrade()->GetAllTradeValueFromPlayerTimes100(YIELD_GOLD, ePlayer); int iCurrentGoldOut = GET_PLAYER(ePlayer).GetTrade()->GetAllTradeValueFromPlayerTimes100(YIELD_GOLD, eMyPlayer); int iCurrentScienceIn = GetPlayer()->GetTrade()->GetAllTradeValueFromPlayerTimes100(YIELD_SCIENCE, ePlayer); int iCurrentScienceOut = GET_PLAYER(ePlayer).GetTrade()->GetAllTradeValueFromPlayerTimes100(YIELD_SCIENCE, eMyPlayer); int iCurrentCultureIn = GetPlayer()->GetTrade()->GetAllTradeValueFromPlayerTimes100(YIELD_CULTURE, ePlayer); int iCurrentCultureOut = GET_PLAYER(ePlayer).GetTrade()->GetAllTradeValueFromPlayerTimes100(YIELD_CULTURE, eMyPlayer); int iGDPEstimate = GetPlayer()->GetTreasury()->GetGoldFromCitiesTimes100(false); int iScienceEstimate = GetPlayer()->GetScienceFromCitiesTimes100(false); int iCultureEstimate = GetPlayer()->GetYieldRateFromCitiesTimes100(YIELD_CULTURE) + (GetPlayer()->GetJONSCulturePerTurnForFree() * 100); // Scale factor is hard to guess ... int iGoldDelta = (5 * (iCurrentGoldIn - iCurrentGoldOut)) / max(iGDPEstimate,1); int iScienceDelta = (5 * (iCurrentScienceIn - iCurrentScienceOut)) / max(iScienceEstimate,1); int iCultureDelta = (5 * (iCurrentCultureIn - iCurrentCultureOut)) / max(iCultureEstimate,1); // Now add in value from ongoing trade deals (if we're on the lookout for an opportunity attack, we should exclude demands and peace treaties) int iTradeDealValue = GC.getGame().GetGameDeals().GetDealValueWithPlayer(eMyPlayer, ePlayer, false, bWantsOpportunityAttack); // Scale based on personality - how much do we care about trade loyalty? // Netherlands always has maximum trade loyalty! iTradeDealValue *= pTraits->IsImportsCountTowardsMonopolies() ? 20 : GetLoyalty() + GetWorkWithWillingness(); iTradeDealValue /= 20; // Trade deal value is more important in earlier eras if (iMyEra <= GD_INT_GET(MEDIEVAL_ERA)) { iTradeDealValue /= 3; } else { iTradeDealValue /= 5; } int iTradeDelta = iGoldDelta + iScienceDelta + iCultureDelta + iTradeDealValue; if (iTradeDelta > 0) { if (bUntrustworthy) { iTradeDelta /= 2; } else if (IsStrategicTradePartner(ePlayer)) { iTradeDelta *= 2; } vApproachScores[CIV_APPROACH_FRIENDLY] += iTradeDelta * 100; } ////////////////////////////////////////////// // WOULD YOU BE INTERESTED IN A TRADE DEMAND? ////////////////////////////////////////////// // Only consider emphasizing a demand if we're not already targeting someone else, want an opportunity attack, and (performance shortcut) if they don't have any of our cities int iDemandValueScore = 0; if (bWantsOpportunityAttack && iNumOurCitiesTheyOwn == 0 && (GetDemandTargetPlayer() == NO_PLAYER || GetDemandTargetPlayer() == ePlayer)) { // How much could we get from making a trade demand? // NOTE: iDemandValueScore will only be > 0 if a demand is valid (this checks a lot of conditions) and would net at least half of the AI's desired demand value if (IsValidDemandTarget(ePlayer, iDemandValueScore)) { if (iDemandValueScore > 0) { iDemandValueScore *= GetBoldness() + GetMeanness(); iDemandValueScore *= GetNumConsecutiveDemandsTheyAccepted(ePlayer) + 1; // Able to make a demand now or soon, or our previous demand was rebuffed? Add hostility weight. // Otherwise, add guarded weight, since they're likely to want revenge. int iTooSoonTurns = /*20*/ GD_INT_GET(DEMAND_TURN_LIMIT_MIN) + /*10*/ GD_INT_GET(DEMAND_TURN_LIMIT_RAND); if (!IsRecentDemandAccepted(ePlayer) || GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_DEMAND) > iTooSoonTurns - 3) { vApproachScores[CIV_APPROACH_HOSTILE] += iDemandValueScore; } else { vApproachScores[CIV_APPROACH_GUARDED] += max(iDemandValueScore, vApproachBias[CIV_APPROACH_GUARDED] * 2 * iTheirAttackMultiplier); } } } else iDemandValueScore = 0; } // If we didn't add any demand weight, but they accepted a demand recently, add guarded weight, since they're likely to want revenge. if (iDemandValueScore == 0 && IsRecentDemandAccepted(ePlayer)) vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 2 * iTheirAttackMultiplier; //////////////////////////////////// // BANKRUPTCY //////////////////////////////////// bool bBankrupt = GetPlayer()->GetTreasury()->GetGold() <= 0 && GetPlayer()->getAvgGoldRate() <= 0; int iLostGoldPerTurn = CalculateGoldPerTurnLostFromWar(ePlayer) * 100; // We're bankrupt! if (bBankrupt) { if (iLostGoldPerTurn < 0) // We're bankrupt, and we'd gain GPT by going to war with them? Well, there's one way to end that. { vApproachScores[CIV_APPROACH_WAR] += max(vApproachBias[CIV_APPROACH_WAR] - iLostGoldPerTurn, vApproachBias[CIV_APPROACH_WAR] * -iLostGoldPerTurn / 5); vApproachScores[CIV_APPROACH_HOSTILE] += max(vApproachBias[CIV_APPROACH_HOSTILE] - iLostGoldPerTurn, vApproachBias[CIV_APPROACH_HOSTILE] * -iLostGoldPerTurn / 5); } else // Let's not go to war! { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 5; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 5; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * 5; } } if (bWeLostCapital) { // We captured a major city from them? Keep our guard up! if (bCapturedTheirCapital || bCapturedTheirHolyCity) { vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 10; } else if (!bEverCapturedKeyCity && !bUntrustworthy) { if (GetNumCitiesCapturedBy(ePlayer) <= 0 && GetNumWarsDeclaredOnUs(ePlayer) <= 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 10; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * 10; } else if (GetNumCitiesCapturedBy(ePlayer) <= 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * 5; } else { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; } // Is this a neighbor? if (eClosestProximity == PLAYER_PROXIMITY_NEIGHBORS) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * 5; } else if (eClosestProximity == PLAYER_PROXIMITY_CLOSE) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; } // We have someone else's capital? Doubly important we stay on others' good sides! if (GetPlayer()->GetNumCapitalCities() > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * GetPlayer()->GetNumCapitalCities() * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * GetPlayer()->GetNumCapitalCities() * 2; } } } //////////////////////////////////// // AT WAR RIGHT NOW //////////////////////////////////// PlayerTypes eHighestWarWearinessPlayer = GetPlayer()->GetHighestWarWearinessPlayer(true); int iUnhappinessFromWarWeariness = eHighestWarWearinessPlayer != NO_PLAYER ? GetPlayer()->GetUnhappinessFromWarWearinessWithTeam(GET_PLAYER(eHighestWarWearinessPlayer).getTeam(), true) : 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; TeamTypes eLoopTeam = GET_PLAYER(eLoopPlayer).getTeam(); // Don't look at the guy we're already thinking about or anyone on his team if (eTeam == eLoopTeam || !IsPlayerValid(eLoopPlayer) || !IsAtWar(eLoopPlayer)) continue; // Fighting together against a common foe? if (GET_TEAM(eTeam).isAtWar(eLoopTeam)) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; } // Is this guy a neighbor? We want neighbors on our good side if we're going to war with others if (eTheirProximity == PLAYER_PROXIMITY_NEIGHBORS) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL]; } // Is the target a neighbor? We want others on our good side if we're going to war with neighbors if (GET_PLAYER(eLoopPlayer).GetProximityToPlayer(eMyPlayer) == PLAYER_PROXIMITY_NEIGHBORS) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL]; } //////////////////////////////////// // WAR STATE //////////////////////////////////// WarStateTypes eLoopWarState = GetWarState(eLoopPlayer); bool bWarWearinessPenalty = iUnhappinessFromWarWeariness >= 10 && GET_PLAYER(eHighestWarWearinessPlayer).getTeam() == eLoopTeam; // Not significantly war weary from this guy? We should ignore them for the war state check. if (!GetPlayer()->IsNoNewWars() && eLoopWarState > WAR_STATE_TROUBLED && !bWarWearinessPenalty) { // Ignore players who aren't a serious threat. if (IsEasyTarget(eLoopPlayer) || GET_PLAYER(eLoopPlayer).IsInTerribleShapeForWar()) { continue; } // Ignore phony wars if (IsPhonyWar(eLoopPlayer, true)) { continue; } } // Let's not get into too many wars ... vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 5; // Not winning? Let's be even nicer. switch (eLoopWarState) { case NO_WAR_STATE_TYPE: UNREACHABLE(); // If we're at war we're supposed to have a war state. case WAR_STATE_NEARLY_WON: case WAR_STATE_OFFENSIVE: break; case WAR_STATE_CALM: vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR]; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE]; break; case WAR_STATE_STALEMATE: vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * iTheirAttackMultiplier; break; case WAR_STATE_TROUBLED: case WAR_STATE_DEFENSIVE: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 2 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iTheirAttackMultiplier; break; case WAR_STATE_NEARLY_DEFEATED: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 5 * iTheirAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 5 * iTheirAttackMultiplier; break; } } //////////////////////////////////// // DANGEROUS NEIGHBORS - Be careful about being dogpiled! //////////////////////////////////// for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // Only consider potential threats that aren't on this player's team if (IsPlayerValid(eLoopPlayer) && GET_PLAYER(eLoopPlayer).getTeam() != eTeam && IsPotentialMilitaryTargetOrThreat(eLoopPlayer, true)) { // If we're winning against them or they're losing all their wars, don't count this player as a deterrent. if (!GetPlayer()->IsNoNewWars()) { if (IsEasyTarget(eLoopPlayer) || GetWarState(eLoopPlayer) >= WAR_STATE_OFFENSIVE || GET_PLAYER(eLoopPlayer).IsInTerribleShapeForWar()) continue; } bool bNeighbors = GetPlayer()->GetProximityToPlayer(eLoopPlayer) == PLAYER_PROXIMITY_NEIGHBORS || GET_PLAYER(eLoopPlayer).GetProximityToPlayer(eMyPlayer) == PLAYER_PROXIMITY_NEIGHBORS; int iProximityFactor = bNeighbors ? 2 : 1; int iStrengthFactor = (int)GetMilitaryStrengthComparedToUs(eLoopPlayer) - 3; // Ranges from -1 (poor) to 3 (immense) int iCitiesCapturedFactor = GetNumCitiesCapturedBy(eLoopPlayer) - GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumCitiesCapturedBy(eMyPlayer); int iDeterrence = (iProximityFactor + iStrengthFactor + iCitiesCapturedFactor) * 100; if (iDeterrence > 0) { if (!bNeighbors) { iDeterrence /= 2; } // If we're already at war but not with the other nearby player, halve the effect of deterrence. if (IsAtWar(ePlayer) && !IsAtWar(eLoopPlayer)) { iDeterrence /= 2; } vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * iDeterrence / 100; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * iDeterrence / 100; vApproachScores[CIV_APPROACH_AFRAID] += vApproachBias[CIV_APPROACH_AFRAID] * iDeterrence / 100; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iDeterrence / 100; } } } //////////////////////////////////// // THEY'RE CLOSE TO WINNING! //////////////////////////////////// if (bTheyAreCloseToWorldConquest) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 5; } // Are they conquering the world? We could be next! int iDangerScore = GetPlayerNumMajorsConquered(ePlayer); if (iDangerScore > 0 && eClosestProximity >= PLAYER_PROXIMITY_CLOSE) { // Be extremely wary of early game conquerors if (iGameEra <= GD_INT_GET(ANCIENT_ERA)) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 3 * iDangerScore; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 3 * iDangerScore; if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iDangerScore * 3; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iDangerScore * 3; // Easy target and we have war bonuses? Attack! if (bCloseToWorldConquest || bConquerorTraits || bWeHaveUUActive) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; } } } else if (iGameEra <= GD_INT_GET(CLASSICAL_ERA)) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2 * iDangerScore; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 2 * iDangerScore; if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iDangerScore * 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iDangerScore * 2; // Easy target and we have war bonuses? Attack! if (bCloseToWorldConquest || bConquerorTraits || bWeHaveUUActive) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 3; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 3; } } } else { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iDangerScore; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iDangerScore; if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iDangerScore; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iDangerScore; // Easy target and we have war bonuses? Attack! if (IsConqueror() || IsGoingForWorldConquest() || bCloseToWorldConquest || bConquerorTraits || bWeHaveUUActive) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 3; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 3; } } } } if (IsEndgameAggressiveTo(ePlayer)) { if (bTheyAreCloseToScienceVictory) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; } if (bTheyAreCloseToDiploVictory) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; } if (bTheyAreCloseToCultureVictory) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; } // Are we both close to a victory condition? if (bCloseToAnyVictory) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; } if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; } } //////////////////////////////////// // Is this player already in a war with someone who isn't us? //////////////////////////////////// int iBonus = 0; bool bThinkingAboutDogpiling = false; bool bFriendUnderAttack = false; bool bCompetitorUnderAttack = false; bool bAllowDogpiling = true; bool bOtherWarPlayerCloseToTarget = false; if (bWantsOpportunityAttack) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // Is this a player we have relations with? if (eLoopPlayer != eMyPlayer && IsPlayerValid(eLoopPlayer, true) && pTheirDiplo->IsPlayerValid(eLoopPlayer) && GET_PLAYER(eLoopPlayer).isMajorCiv()) { if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsAtWar(ePlayer)) { bThinkingAboutDogpiling = false; bOtherWarPlayerCloseToTarget = GET_PLAYER(eLoopPlayer).GetProximityToPlayer(ePlayer) >= PLAYER_PROXIMITY_CLOSE; if (IsMajorCompetitor(ePlayer) || bEarlyGameCompetitor || iDangerScore > 0) { bThinkingAboutDogpiling = true; } if (GET_PLAYER(ePlayer).GetWarDamageValue(eLoopPlayer) >= 40 && GET_PLAYER(ePlayer).GetWarDamageValue(eLoopPlayer) > GET_PLAYER(eLoopPlayer).GetWarDamageValue(ePlayer)) { bThinkingAboutDogpiling = true; vApproachScores[CIV_APPROACH_AFRAID] -= vApproachBias[CIV_APPROACH_AFRAID] * 5; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * 5; } else if (pTheirDiplo->GetWarState(eLoopPlayer) >= WAR_STATE_OFFENSIVE) { bThinkingAboutDogpiling = true; vApproachScores[CIV_APPROACH_AFRAID] -= vApproachBias[CIV_APPROACH_AFRAID] * 3; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * 3; } else if (bOtherWarPlayerCloseToTarget && GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetWarState(ePlayer) >= WAR_STATE_OFFENSIVE) { bThinkingAboutDogpiling = true; vApproachScores[CIV_APPROACH_AFRAID] -= vApproachBias[CIV_APPROACH_AFRAID] * 2; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * 2; } // On the flipside, is this guy suffering from being at war with this player? else if (!bStrategic && !IsEndgameAggressiveTo(ePlayer) && GET_PLAYER(eLoopPlayer).GetWarDamageValue(ePlayer) >= 20 && GET_PLAYER(eLoopPlayer).GetWarDamageValue(ePlayer) > GET_PLAYER(ePlayer).GetWarDamageValue(eLoopPlayer)) { // Do we like them? Then we should defend our ally, if we can! if ((IsFriendOrAlly(eLoopPlayer) || GetMostValuableFriend() == eLoopPlayer || GetMostValuableAlly() == eLoopPlayer || IsLiberator(eLoopPlayer, false, false) || IsMaster(eLoopPlayer) || (IsVassal(eLoopPlayer) && GetVassalTreatmentLevel(eLoopPlayer) <= VASSAL_TREATMENT_DISAGREE)) && !IsUntrustworthy(eLoopPlayer) && GetCivApproach(eLoopPlayer) > CIV_APPROACH_GUARDED) { bFriendUnderAttack = true; if (eMilitaryStrength < STRENGTH_POWERFUL) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetLoyalty() / 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * GetLoyalty() / 2; bThinkingAboutDogpiling = true; } else if (eMilitaryStrength < STRENGTH_AVERAGE) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetLoyalty(); vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * GetLoyalty(); bThinkingAboutDogpiling = true; } } // Do we hate the other guy? Then let's be more reluctant to go to war. else if (!bUntrustworthy && !IsDenouncedByPlayer(ePlayer) && GetNumCitiesCapturedBy(ePlayer) <= 0 && GetWarScore(ePlayer) < GetWarscoreThresholdPositive() && (!IsAtWar(ePlayer) || IsPhonyWar(ePlayer, true) || GetWarScore(ePlayer) > GetWarscoreThresholdNegative()/2)) { if (GetBiggestCompetitor() == eLoopPlayer || GetPrimeLeagueCompetitor() == eLoopPlayer || IsEndgameAggressiveTo(eLoopPlayer)) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * GetVictoryCompetitiveness(); vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * GetVictoryCompetitiveness() * 2; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * GetVictoryCompetitiveness(); bCompetitorUnderAttack = true; } else if (GetCivOpinion(eLoopPlayer) < GetCivOpinion(ePlayer) && GetBiggestCompetitor() != ePlayer && GetPrimeLeagueCompetitor() != ePlayer) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * GetVictoryCompetitiveness() / 2; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * GetVictoryCompetitiveness() / 4; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * GetVictoryCompetitiveness() / 4; bCompetitorUnderAttack = true; } } } // If we can't take them, we don't want them attacking us after finishing with this guy if (bThinkingAboutDogpiling && bOtherWarPlayerCloseToTarget && GetMilitaryStrengthComparedToUs(eLoopPlayer) >= STRENGTH_POWERFUL && !IsTeammate(eLoopPlayer) && !IsDoFAccepted(eLoopPlayer) && !IsHasDefensivePact(eLoopPlayer)) { bAllowDogpiling = false; break; } if (bThinkingAboutDogpiling) { int iBonusMod = max((int)GetVictoryBlockLevel(ePlayer), (int)GetVictoryDisputeLevel(ePlayer)) + (int)GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetWarState(ePlayer); if (IsAtWar(ePlayer)) iBonusMod += (int)GetWarState(ePlayer) - 4; if (iBonusMod > 0) { if (bOtherWarPlayerCloseToTarget && (bEarlyGameCompetitor || iDangerScore > 0)) { // All three of us pre-Renaissance? Add a little extra punch! if (iTheirEra < GD_INT_GET(RENAISSANCE_ERA) && GET_PLAYER(eLoopPlayer).GetCurrentEra() < GD_INT_GET(RENAISSANCE_ERA)) { iBonusMod *= 5; iBonusMod /= 2; } else iBonusMod *= 2; } // They're not close to the target? else if (!bOtherWarPlayerCloseToTarget) { if (iMyEra < GD_INT_GET(RENAISSANCE_ERA)) iBonusMod = 0; else iBonusMod /= 2; } } iBonus += iBonusMod; } } } } // Let's let our enemy fall before we take them on. if (!bFriendUnderAttack && bCompetitorUnderAttack) { bAllowDogpiling = false; } if (bAllowDogpiling && iBonus > 0) { vApproachScores[CIV_APPROACH_WAR] += AdjustConditionalModifier(vApproachBias[CIV_APPROACH_WAR] * iBonus, GetWorkAgainstWillingness()) / 2; vApproachScores[CIV_APPROACH_HOSTILE] += AdjustConditionalModifier(vApproachBias[CIV_APPROACH_HOSTILE] * iBonus, GetWorkAgainstWillingness()) / 2; } } //////////////////////////////////// // EARLY GAME STRATEGY - How should we approach players before the Renaissance? //////////////////////////////////// if (iMyEra < GD_INT_GET(RENAISSANCE_ERA)) { // If we've been on a coop war together, let's keep this relationship going. if (GetCoopWarAgreementScore(ePlayer) > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2 * GetCoopWarAgreementScore(ePlayer); } // Early game competition modifier if (bEarlyGameCompetitor) { // Only favor nearby targets before the Renaissance if (eOurProximity >= PLAYER_PROXIMITY_CLOSE) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 : 0; // Bold/mean AIs and those with war bonuses don't like early game competitors if (GetBoldness() > 7 || GetMeanness() > 7 || IsConqueror() || bConquerorTraits) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 : 0; } // Denouncements are a catalyst for early game aggression. if (IsDenouncedPlayer(ePlayer) || IsDenouncedByPlayer(ePlayer)) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2; } } } // Are they staying out of our way? else { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; // Scientific civs are more likely to be neutral; all others are more likely to be friendly if (IsScientist()) { vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; } else { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; } // More friendliness if not neighbors switch (eClosestProximity) { case PLAYER_PROXIMITY_NEIGHBORS: break; case PLAYER_PROXIMITY_CLOSE: vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR]; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE]; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE]; break; case PLAYER_PROXIMITY_FAR: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY]; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; break; case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 4; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 4; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * 4; break; } } } //////////////////////////////////// // GRAND STRATEGY WEIGHT - What victory condition am I aiming for? //////////////////////////////////// if (IsCompetingForVictory()) { // Must be at least in the Medieval Era (or with few players / close to winning / already conquered a capital) to add weight for Domination Victory if (iMyEra >= GD_INT_GET(MEDIEVAL_ERA) || bCloseToWorldConquest || GC.getGame().GetNumMajorCivsAlive() <= 4 || GetPlayerNumMajorsConquered(eMyPlayer) > 0) { if (IsGoingForWorldConquest() || GetPlayerNumMajorsConquered(eMyPlayer) > 0) { // Add some weight for leader flavors vApproachScores[CIV_APPROACH_WAR] += (GetBoldness() + GetWorkAgainstWillingness()) * 50; vApproachScores[CIV_APPROACH_HOSTILE] += (GetMeanness() + GetDenounceWillingness()) * 50; // More likely to declare war for each original capital we own if (GetPlayerNumMajorsConquered(eMyPlayer) > 0) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetPlayerNumMajorsConquered(eMyPlayer) * 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * GetPlayerNumMajorsConquered(eMyPlayer) * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * GetPlayerNumMajorsConquered(eMyPlayer) : 0; } // They have another player's original capital? if (GET_PLAYER(ePlayer).GetNumCapitalCities() > 0) { // We're close to winning? Give us that, my precioussss... if (bCloseToWorldConquest) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5 * GET_PLAYER(ePlayer).GetNumCapitalCities(); vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5 * GET_PLAYER(ePlayer).GetNumCapitalCities(); } // They have their own capital too (making them a competitor)? That makes them a worthwhile ally, but let's also be defensive. else if (!bTheyLostCapital) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetPlayerNumMajorsConquered(ePlayer); vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * GetPlayerNumMajorsConquered(ePlayer); vApproachScores[CIV_APPROACH_FRIENDLY] += (vApproachBias[CIV_APPROACH_FRIENDLY] * 5) - (vApproachBias[CIV_APPROACH_FRIENDLY] * GET_PLAYER(ePlayer).GetNumCapitalCities()); } // Easy target? Score! if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; } } // We must control all capitals to win a Domination Victory, including theirs if (bCloseToWorldConquest) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; } } // Not close to victory yet? Let's seek friendship with strong civs. else { if (GetRawMilitaryStrengthComparedToUs(ePlayer) > STRENGTH_AVERAGE) { int iStrengthFactor = ((int)GetRawMilitaryStrengthComparedToUs(ePlayer) - 3) * 2; if (IsHasDefensivePact(ePlayer)) { iStrengthFactor++; } if (GetRawTargetValue(ePlayer) <= TARGET_VALUE_DIFFICULT) // They're a difficult target for now...aim to build up our strength { iStrengthFactor++; } if (IsBackstabber() && pTheirDiplo->GetWeDeclaredWarOnFriendCount(GetID()) > 0 && !bUntrustworthy) // Backstabbing conquerors like other backstabbers { iStrengthFactor += 2; } vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iStrengthFactor; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * iStrengthFactor; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iStrengthFactor; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * iStrengthFactor; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * iStrengthFactor; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * iStrengthFactor; } else { int iStrengthFactor = (int)GetRawMilitaryStrengthComparedToUs(ePlayer) - 3; iStrengthFactor *= -2; vApproachScores[CIV_APPROACH_FRIENDLY] -= vApproachBias[CIV_APPROACH_FRIENDLY] * iStrengthFactor; vApproachScores[CIV_APPROACH_NEUTRAL] -= vApproachBias[CIV_APPROACH_NEUTRAL] * iStrengthFactor; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * iStrengthFactor; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iStrengthFactor; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iStrengthFactor; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * iStrengthFactor; } // We really want friendship with wealthy civs who aren't close to us (so we can request help and trade) if (IsStrategicTradePartner(ePlayer)) { if (eOpinion >= CIV_OPINION_NEUTRAL) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 5; } else { vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 5; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 5; } if (IsDoFAccepted(ePlayer)) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 10; } } } // They're giving us Open Borders, and no contested borders? Add some friendliness weight. if (IsHasOpenBorders(ePlayer) && GetLandDisputeLevel(ePlayer) == DISPUTE_LEVEL_NONE) { if (eOpinion >= CIV_OPINION_FAVORABLE) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; } else { vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; } } // Value coop wars if (GetCoopWarAgreementScore(ePlayer) != 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += GetCoopWarAgreementScore(ePlayer) * 800; vApproachScores[CIV_APPROACH_HOSTILE] -= GetCoopWarAgreementScore(ePlayer) * 800; vApproachScores[CIV_APPROACH_DECEPTIVE] -= GetCoopWarAgreementScore(ePlayer) * 800; } } } // Renaissance Era or later (or close to winning) for other victories if (iMyEra >= GD_INT_GET(RENAISSANCE_ERA) || bCloseToAnyVictory) { if (IsGoingForDiploVictory() || bCloseToDiploVictory) { // Increase friendship willingness with all civs (that aren't close to winning) if (!IsEndgameAggressiveTo(ePlayer)) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] + GetWorkWithWillingness() * 100 + GetDoFWillingness() * 100; // Larger increase if we're close to winning if (bCloseToDiploVictory) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; } } if (bMetValidMinor) { int iLoweredInfluenceCount = GetNumTimesTheyLoweredOurInfluence(ePlayer)/2 + GetNumTimesPerformedCoupAgainstUs(ePlayer); if (iLoweredInfluenceCount > 0) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iLoweredInfluenceCount; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iLoweredInfluenceCount; } // Are they protecting our allied/protected City-States? if (!IsMinorCivTroublemaker(ePlayer)) { int iSamePtP = 0; for (int iPlayerLoop = MAX_MAJOR_CIVS; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eMinorLoopPlayer = (PlayerTypes) iPlayerLoop; if (eMinorLoopPlayer == NO_PLAYER) continue; if (GET_PLAYER(eMinorLoopPlayer).isMinorCiv()) { if (GET_PLAYER(eMinorLoopPlayer).GetMinorCivAI()->IsProtectedByMajor(ePlayer)) { if (GET_PLAYER(eMinorLoopPlayer).GetMinorCivAI()->IsProtectedByMajor(GetID()) || GET_PLAYER(eMinorLoopPlayer).GetMinorCivAI()->GetAlly() == GetID()) iSamePtP++; } } } if (iSamePtP > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iSamePtP / 2; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * iSamePtP / 2; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * iSamePtP / 2; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * iSamePtP / 2; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * iSamePtP / 2; } } } // Is this one of our primary League competitors? if (pLeague != NULL) { bool bSanctionAgainstUs = HasEverSanctionedUs(ePlayer) || HasTriedToSanctionUs(ePlayer); // Prime competitor? if (GetPrimeLeagueCompetitor() == ePlayer) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 5; // Easy target? if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2; } // Especially so if we're close to winning if (bCloseToDiploVictory) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; } } // More votes than us or tried to sanction us? else if (bSanctionAgainstUs || pLeague->CalculateStartingVotesForMember(ePlayer) > pLeague->CalculateStartingVotesForMember(eMyPlayer)) { // Especially so if we're close to winning if (bCloseToDiploVictory) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; } // Easy target? else if (bEasyTarget && (GetPrimeLeagueCompetitor() == NO_PLAYER || !IsEasyTarget(GetPrimeLeagueCompetitor()))) { vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 3 * iAttackMultiplier; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 3 * iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 3 * iAttackMultiplier; } else if (bTheyAreCloseToDiploVictory || bSanctionAgainstUs || (GetPlayer()->GetLeagueAI()->EvaluateAlignment(ePlayer) <= CvLeagueAI::ALIGNMENT_NEUTRAL)) { vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 3; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 3 * iAttackMultiplier; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 3 * iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 3 * iAttackMultiplier; } else { vApproachScores[CIV_APPROACH_FRIENDLY] += (eOpinion > CIV_OPINION_ENEMY && GetWarmongerThreat(ePlayer) < THREAT_SEVERE) ? vApproachBias[CIV_APPROACH_FRIENDLY] * 5 : vApproachBias[CIV_APPROACH_FRIENDLY] * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] += (eOpinion > CIV_OPINION_ENEMY && GetWarmongerThreat(ePlayer) < THREAT_SEVERE) ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 5 : vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; vApproachScores[CIV_APPROACH_WAR] -= (eOpinion > CIV_OPINION_ENEMY && GetWarmongerThreat(ePlayer) < THREAT_SEVERE) ? vApproachBias[CIV_APPROACH_WAR] * 5 : vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] -= (eOpinion > CIV_OPINION_ENEMY && GetWarmongerThreat(ePlayer) < THREAT_SEVERE) ? vApproachBias[CIV_APPROACH_HOSTILE] * 5 : vApproachBias[CIV_APPROACH_HOSTILE] * 2; } } // Have they tried to unsanction us? HUGE boost to relations even if we don't normally like them. else if (HasEverUnsanctionedUs(ePlayer) || HasTriedToUnsanctionUs(ePlayer)) { int iMultiplier = HasEverUnsanctionedUs(ePlayer) ? 200 : 100; if (bUntrustworthy || GetWarmongerThreat(ePlayer) >= THREAT_SEVERE) iMultiplier /= 2; vApproachScores[CIV_APPROACH_FRIENDLY] += (vApproachBias[CIV_APPROACH_FRIENDLY] * 10 * iMultiplier) / 100; vApproachScores[CIV_APPROACH_WAR] -= (vApproachBias[CIV_APPROACH_WAR] * 10 * iMultiplier) / 100; vApproachScores[CIV_APPROACH_HOSTILE] -= (vApproachBias[CIV_APPROACH_HOSTILE] * 10 * iMultiplier) / 100; vApproachScores[CIV_APPROACH_DECEPTIVE] -= (vApproachBias[CIV_APPROACH_DECEPTIVE] * 5 * iMultiplier) / 100; vApproachScores[CIV_APPROACH_GUARDED] -= (vApproachBias[CIV_APPROACH_GUARDED] * 5 * iMultiplier) / 100; vApproachScores[CIV_APPROACH_AFRAID] -= (vApproachBias[CIV_APPROACH_AFRAID] * 5 * iMultiplier) / 100; } // No? Well, we should still be much more reluctant to go to war then! else { vApproachScores[CIV_APPROACH_FRIENDLY] += (eOpinion > CIV_OPINION_ENEMY && GetWarmongerThreat(ePlayer) < THREAT_SEVERE) ? vApproachBias[CIV_APPROACH_FRIENDLY] * 10 : vApproachBias[CIV_APPROACH_FRIENDLY] * 5; vApproachScores[CIV_APPROACH_WAR] -= (eOpinion > CIV_OPINION_ENEMY && GetWarmongerThreat(ePlayer) < THREAT_SEVERE) ? vApproachBias[CIV_APPROACH_WAR] * 10 : vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] -= (eOpinion > CIV_OPINION_ENEMY && GetWarmongerThreat(ePlayer) < THREAT_SEVERE) ? vApproachBias[CIV_APPROACH_HOSTILE] * 10 : vApproachBias[CIV_APPROACH_HOSTILE] * 5; } } } if (IsGoingForCultureVictory() || bCloseToCultureVictory) { // They have influence over us if (GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(eMyPlayer) >= INFLUENCE_LEVEL_POPULAR) { if (GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(eMyPlayer) >= INFLUENCE_LEVEL_INFLUENTIAL || GET_PLAYER(ePlayer).GetCulture()->GetInfluenceTrend(eMyPlayer) == INFLUENCE_TREND_RISING) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2 * iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iAttackMultiplier; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 2 * iTheirAttackMultiplier; // Easy target? Tourism begone! if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5 * iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5 * iAttackMultiplier; } } } else { // Weight for Open Borders if ((MOD_BALANCE_FLIPPED_OPEN_BORDERS_TOURISM && pTheirDiplo->IsHasOpenBorders(eMyPlayer)) || (!MOD_BALANCE_FLIPPED_OPEN_BORDERS_TOURISM && IsHasOpenBorders(ePlayer))) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; // Additional weight if we're already influential on half of the civs if (GetPlayer()->GetCulture()->GetNumCivsToBeInfluentialOn() <= GetPlayer()->GetCulture()->GetNumCivsInfluentialOn()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; } } } // The civ we have the lowest influence on is an easy target? Let's get 'em! if (GetPlayer()->GetCulture()->GetCivLowestInfluence(false) == ePlayer && bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5 * iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5 * iAttackMultiplier; // Especially if we're close to winning if (bCloseToCultureVictory) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5 * iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5 * iAttackMultiplier; } } else if (GetPlayer()->GetCulture()->GetCivLowestInfluence(false) == ePlayer) { vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 10; if (bCloseToCultureVictory) { vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 10; } } else { if (bCloseToCultureVictory) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; } // If we need influence over them, we should be more friendly, but they're also a greater competitor switch (GetPlayer()->GetCulture()->GetInfluenceLevel(ePlayer)) { case NO_INFLUENCE_LEVEL: case INFLUENCE_LEVEL_UNKNOWN: vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 4; vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; break; case INFLUENCE_LEVEL_EXOTIC: vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; break; case INFLUENCE_LEVEL_FAMILIAR: vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 4; break; case INFLUENCE_LEVEL_POPULAR: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 4; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; break; case INFLUENCE_LEVEL_INFLUENTIAL: { if (GetPlayer()->GetCulture()->GetInfluenceTrend(ePlayer) != INFLUENCE_TREND_RISING) // don't let our influence slip! { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 4; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; } else if (eOpinion >= CIV_OPINION_FAVORABLE) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; } else if (eOpinion <= CIV_OPINION_COMPETITOR && eOurProximity >= PLAYER_PROXIMITY_CLOSE) // we've already got our influence, now conquering them will be easier { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2 * iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iAttackMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 * iAttackMultiplier : 0; if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 3 * iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 3 * iAttackMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 3 * iAttackMultiplier : 0; } } break; } case INFLUENCE_LEVEL_DOMINANT: { if (eOpinion >= CIV_OPINION_FAVORABLE) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY]; } else if (eOpinion <= CIV_OPINION_COMPETITOR && eOurProximity >= PLAYER_PROXIMITY_CLOSE) // we've already got our influence, now conquering them will be easier { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5 * iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5 * iAttackMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 5 * iAttackMultiplier : 0; if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5 * iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5 * iAttackMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 5 * iAttackMultiplier : 0; } } break; } } } } if (IsGoingForSpaceshipVictory() || bCloseToScienceVictory) { // Spaceship competitor? if (GET_TEAM(eTeam).GetSSProjectCount() > 0 || (!bCloseToScienceVictory && bTheyAreCloseToScienceVictory)) { if (IsEndgameAggressiveTo(ePlayer)) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 10 * iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 10 * iAttackMultiplier; } } else if (bCloseToScienceVictory) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 10; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 10; } else { int iTechDifference = min(10, max(-10, (iNumTheirTechs - iNumOurTechs))); // They have a tech lead of 4 or more - enemy! if (iTechDifference >= 4) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iTechDifference; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iTechDifference; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iTechDifference; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * iTechDifference : 0; // Easy target? if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5 * iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5 * iAttackMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 5 * iAttackMultiplier : 0; } } // They have a tech lead of 2 to 3 - we want to befriend them for tech trading/trade routes, but they're also an enemy else if (iTechDifference >= 2) { vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 * iTechDifference; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 2 * iTechDifference; // Easy target? if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2 * iAttackMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 * iAttackMultiplier; } } // We have a tech lead of 2 or more - below average friend/enemy choice, but good DP choice if they're strong else if (iTechDifference <= -2) { vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * -iTechDifference; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * -iTechDifference; // We want other civs to guard our back if (eMilitaryStrength >= STRENGTH_AVERAGE && !GET_PLAYER(ePlayer).IsInTerribleShapeForWar() && !IsAtWar(ePlayer) && eTheirProximity >= PLAYER_PROXIMITY_CLOSE) { if (bVictoryConcern) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * -iTechDifference * GetWarmongerHate() / 2 / iAttackMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * -iTechDifference * GetWarmongerHate() / 2 / iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * -iTechDifference * GetWarmongerHate() / 2 / iAttackMultiplier; } else { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * -iTechDifference * GetWarmongerHate() / iAttackMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * -iTechDifference * GetWarmongerHate() / iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * -iTechDifference * GetWarmongerHate() / iAttackMultiplier; } } } // Equal, or within one tech of each other else { vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE]; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL]; // We want other civs to guard our back if (eMilitaryStrength >= STRENGTH_AVERAGE && !GET_PLAYER(ePlayer).IsInTerribleShapeForWar() && !IsAtWar(ePlayer) && eTheirProximity >= PLAYER_PROXIMITY_CLOSE) { if (bVictoryConcern) vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * GetWarmongerHate() / 2 / iAttackMultiplier; else vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * GetWarmongerHate() / iAttackMultiplier; } } // Be friendly to our DPs (if they don't have a large tech lead). if (IsHasDefensivePact(ePlayer) && iTechDifference <= 3) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 3; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 3; } // Be friendly to our RA civs (if they don't have a large tech lead). if (IsHasResearchAgreement(ePlayer) && iTechDifference <= 3 && !GET_TEAM(eMyTeam).GetTeamTechs()->HasResearchedAllTechs()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 3; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 3; } } } } } //--------------------------------// // [PART 7: ADDITIONAL FACTORS] // //--------------------------------// //////////////////////////////////// // NUKES //////////////////////////////////// int iHowLikelyAreTheyToNukeUs = GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_UNITS) ? 100 : 0; // assume humans will use 'em if they've got 'em if (iHowLikelyAreTheyToNukeUs == 0) { if (IsNukedBy(ePlayer) || pTheirDiplo->IsNukedBy(eMyPlayer)) // nukes have been used already { iHowLikelyAreTheyToNukeUs = 100; } // they are surely going to lose a war with us, so they will nuke us else if (pTheirDiplo->GetRawMilitaryStrengthComparedToUs(eMyPlayer) == STRENGTH_IMMENSE || pTheirDiplo->GetWarState(eMyPlayer) == WAR_STATE_NEARLY_DEFEATED) { iHowLikelyAreTheyToNukeUs = 100; } else { int iFlavorNuke = GET_PLAYER(ePlayer).GetFlavorManager()->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_USE_NUKE")); iHowLikelyAreTheyToNukeUs = iFlavorNuke * 10; } } // Do we have nukes and they don't? if (GET_PLAYER(ePlayer).getNumNukeUnits() == 0 && GetPlayer()->getNumNukeUnits() > 0) { vApproachScores[CIV_APPROACH_WAR] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 3 * iAttackMultiplier : 0; vApproachScores[CIV_APPROACH_HOSTILE] += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_HOSTILE] * 3 * iAttackMultiplier : 0; if (bWantsOpportunityAttack) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5 * iAttackMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5 * iAttackMultiplier; } } // Do they have nukes and we don't? else if (GET_PLAYER(ePlayer).getNumNukeUnits() > 0 && GetPlayer()->getNumNukeUnits() == 0 && iHowLikelyAreTheyToNukeUs >= 60) { vApproachScores[CIV_APPROACH_AFRAID] += vApproachBias[CIV_APPROACH_AFRAID] * 5 * iTheirAttackMultiplier; } // Do we both have nukes? else if (GET_PLAYER(ePlayer).getNumNukeUnits() > 0 && GetPlayer()->getNumNukeUnits() > 0 && iHowLikelyAreTheyToNukeUs >= 30) { vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 2 * iTheirAttackMultiplier; } // THEY NUKED US! if (IsNukedBy(ePlayer)) { int iTimesNuked = min(GetNumTimesNuked(ePlayer), 10); vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iTimesNuked / 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iTimesNuked / 2; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iTimesNuked; vApproachScores[CIV_APPROACH_AFRAID] += vApproachBias[CIV_APPROACH_AFRAID] * iTimesNuked * iTheirAttackMultiplier; } //////////////////////////////////// // UNIQUE UNITS //////////////////////////////////// // We've got a UU? Let's see if it is time to go. if (GetPlayer()->HasUUPeriod()) { // We got it? Let's strike! if (bWeHaveUUTech && bWeHaveUUActive) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 3; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 3; // Going for world conquest? if (IsGoingForWorldConquest() || bCloseToWorldConquest) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; } // Easy target? if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; } } // Have tech but not UU? Hurry up! else if (bWeHaveUUTech && !bWeHaveUUActive) { vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 5; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; } // Don't have it but will have it soon? Delay. else if (GetPlayer()->GetPlayerTechs()->WillHaveUUTechSoon()) { vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; } } //////////////////////////////////// // EMPIRE CRAMPED //////////////////////////////////// if (GetPlayer()->IsCramped() && eClosestProximity == PLAYER_PROXIMITY_NEIGHBORS) { vApproachScores[CIV_APPROACH_WAR] += eOurProximity == PLAYER_PROXIMITY_NEIGHBORS ? vApproachBias[CIV_APPROACH_WAR] * 3 : 0; vApproachScores[CIV_APPROACH_GUARDED] += eTheirProximity == PLAYER_PROXIMITY_NEIGHBORS ? vApproachBias[CIV_APPROACH_GUARDED] * 3 : 0; } //////////////////////////////////// // BONUSES FROM BEING AT WAR //////////////////////////////////// if (GetPlayer()->getMilitaryProductionModPerMajorWar() > 0) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetPlayer()->getMilitaryProductionModPerMajorWar() / 4; } if (GetPlayer()->getHappinessPerMajorWar() > 0 && GetPlayer()->IsEmpireUnhappy()) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * GetPlayer()->getHappinessPerMajorWar() / 2; } //////////////////////////////////// // WAR WEARINESS //////////////////////////////////// if (iUnhappinessFromWarWeariness > 0 && GET_PLAYER(eHighestWarWearinessPlayer).getTeam() == eTeam) { if (GetPlayer()->IsEmpireSuperUnhappy()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iUnhappinessFromWarWeariness * 2; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * iUnhappinessFromWarWeariness * 2; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * iUnhappinessFromWarWeariness * 2; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * iUnhappinessFromWarWeariness * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * iUnhappinessFromWarWeariness * 2; } else if (GetPlayer()->IsEmpireVeryUnhappy()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iUnhappinessFromWarWeariness; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * iUnhappinessFromWarWeariness; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * iUnhappinessFromWarWeariness; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * iUnhappinessFromWarWeariness; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * iUnhappinessFromWarWeariness; } else if (GetPlayer()->IsEmpireUnhappy()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iUnhappinessFromWarWeariness / 2; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * iUnhappinessFromWarWeariness / 2; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * iUnhappinessFromWarWeariness / 2; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * iUnhappinessFromWarWeariness / 2; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * iUnhappinessFromWarWeariness / 2; } } //////////////////////////////////// // Is this player a reckless expander? //////////////////////////////////// DifficultyModifier = GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getLandDisputePercent() : GC.getGame().getHandicapInfo().getLandDisputePercent(); if (IsRecklessExpander(ePlayer)) { int iWarIncrease = vApproachBias[CIV_APPROACH_WAR] * 2; int iHostileIncrease = vApproachBias[CIV_APPROACH_HOSTILE] * 2; int iDeceptiveIncrease = vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; if (IsConqueror() || IsSecondaryConqueror() || bConquerorTraits) { iWarIncrease += vApproachBias[CIV_APPROACH_WAR] * 2; iHostileIncrease += vApproachBias[CIV_APPROACH_HOSTILE] * 2; iDeceptiveIncrease += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 : 0; } if ((bEasyTarget && bGoodAttackTarget) || bWeHaveUUActive) { iWarIncrease += vApproachBias[CIV_APPROACH_WAR] * 3; } vApproachScores[CIV_APPROACH_WAR] += iWarIncrease * GetBoldness() * DifficultyModifier / 500; vApproachScores[CIV_APPROACH_HOSTILE] += iHostileIncrease * GetBoldness() * DifficultyModifier / 500; vApproachScores[CIV_APPROACH_DECEPTIVE] += iDeceptiveIncrease * GetBoldness() * DifficultyModifier / 500; } //////////////////////////////////// // Is this player spamming World Wonders? //////////////////////////////////// DifficultyModifier = GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getWonderBlockPercent() : GC.getGame().getHandicapInfo().getWonderBlockPercent(); if (IsWonderSpammer(ePlayer)) { int iWarIncrease = vApproachBias[CIV_APPROACH_WAR] * 2; int iHostileIncrease = vApproachBias[CIV_APPROACH_HOSTILE] * 2; int iDeceptiveIncrease = vApproachBias[CIV_APPROACH_DECEPTIVE] * 2; if (IsCultural() || IsConqueror() || IsSecondaryConqueror() || IsSecondaryCultural() || bCulturalTraits || bConquerorTraits) { iWarIncrease += vApproachBias[CIV_APPROACH_WAR] * 2; iHostileIncrease += vApproachBias[CIV_APPROACH_HOSTILE] * 2; iDeceptiveIncrease += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 : 0; } if ((bEasyTarget && bGoodAttackTarget) || bWeHaveUUActive) { iWarIncrease += vApproachBias[CIV_APPROACH_WAR] * 3; } vApproachScores[CIV_APPROACH_WAR] += iWarIncrease * GetWonderCompetitiveness() * DifficultyModifier / 500; vApproachScores[CIV_APPROACH_HOSTILE] += iHostileIncrease * GetWonderCompetitiveness() * DifficultyModifier / 500; vApproachScores[CIV_APPROACH_DECEPTIVE] += iDeceptiveIncrease * GetWonderCompetitiveness() * DifficultyModifier / 500; } //////////////////////////////////// // DUEL - There's only 2 players in this game //////////////////////////////////// if (GC.getGame().GetNumMajorCivsAlive() == 2) { if (IsCompetingForVictory()) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; if (IsEndgameAggressiveTo(ePlayer) && bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += 100000; } } if (IsGoingForWorldConquest() || bCloseToWorldConquest) { vApproachScores[CIV_APPROACH_WAR] += bCloseToWorldConquest ? 100000 : 10000; } } //////////////////////////////////// // VOX POPULI CHANGES //////////////////////////////////// //////////////////////////////////// // Difficulty Bonus //////////////////////////////////// int iAggressionIncrease = GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getAggressionIncrease() : GC.getGame().getHandicapInfo().getAggressionIncrease(); // If we don't view them favorably, increase hostility based on game difficulty if (eOpinion < CIV_OPINION_NEUTRAL) { int iDifficultyBonus = iAggressionIncrease - ((int)eOpinion * 2); // Unforgivable: -0, Enemy: -2, Competitor: -4 if (iDifficultyBonus > 0) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iDifficultyBonus / 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iDifficultyBonus / 2; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * iDifficultyBonus / 2 : 0; if (bWantsOpportunityAttack) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iDifficultyBonus; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iDifficultyBonus; } // Easy target? if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iDifficultyBonus / 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iDifficultyBonus / 2; } } } //////////////////////////////////// // War Bonus //////////////////////////////////// // Do we have bonuses towards war? int iWarBonus = 0; int iHostileBonus = 0; int iDeceptiveBonus = 0; // Warmonger or expansionist? if (IsConqueror() || bConquerorTraits || pTraits->IsExpansionist()) { iWarBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 2 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR] * iAttackMultiplier; iHostileBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iAttackMultiplier : vApproachBias[CIV_APPROACH_HOSTILE] * iAttackMultiplier; iDeceptiveBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 * iAttackMultiplier : vApproachBias[CIV_APPROACH_DECEPTIVE] * iAttackMultiplier; // Unique unit active? Slay them all! if (GetPlayer()->HasUUPeriod() && bWeHaveUUTech && bWeHaveUUActive) { iWarBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 2 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR] * iAttackMultiplier; iHostileBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iAttackMultiplier : vApproachBias[CIV_APPROACH_HOSTILE] * iAttackMultiplier; iDeceptiveBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 * iAttackMultiplier : vApproachBias[CIV_APPROACH_DECEPTIVE] * iAttackMultiplier; // AND we're going for world conquest? if (IsGoingForWorldConquest() || bCloseToWorldConquest) { iWarBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 4 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR] * 2 * iAttackMultiplier; iHostileBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_HOSTILE] * 4 * iAttackMultiplier : vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iAttackMultiplier; iDeceptiveBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 4 * iAttackMultiplier : vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 * iAttackMultiplier; } } } // If we picked offensive policy trees, war is a lot better for us. PolicyBranchTypes eAuthority = (PolicyBranchTypes)GC.getInfoTypeForString("POLICY_BRANCH_HONOR", true); PolicyBranchTypes eImperialism = (PolicyBranchTypes)GC.getInfoTypeForString("POLICY_BRANCH_EXPLORATION", true); bool bCanCrossOcean = GetPlayer()->CanCrossOcean() && iMyEra >= GD_INT_GET(MEDIEVAL_ERA); if (GetPlayer()->GetPlayerPolicies()->IsPolicyBranchUnlocked(eAuthority)) { iWarBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 3 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR] * 2 * iAttackMultiplier; iHostileBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_HOSTILE] * 3 * iAttackMultiplier : vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iAttackMultiplier; iDeceptiveBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 3 * iAttackMultiplier : vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 * iAttackMultiplier; if (GetPlayer()->GetPlayerPolicies()->IsPolicyBranchFinished(eAuthority)) { iWarBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 3 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR] * 2 * iAttackMultiplier; iHostileBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_HOSTILE] * 3 * iAttackMultiplier : vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iAttackMultiplier; iDeceptiveBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 3 * iAttackMultiplier : vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 * iAttackMultiplier; } } if (GetPlayer()->GetPlayerPolicies()->IsPolicyBranchUnlocked(eImperialism)) { if (MOD_BALANCE_VP || (bCanCrossOcean && GetPlayer()->GetNumEffectiveCoastalCities() > 1 && GET_PLAYER(ePlayer).GetNumEffectiveCoastalCities() > 0)) { iWarBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 3 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR] * 2 * iAttackMultiplier; iHostileBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_HOSTILE] * 3 * iAttackMultiplier : vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iAttackMultiplier; iDeceptiveBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 3 * iAttackMultiplier : vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 * iAttackMultiplier; if (GetPlayer()->GetPlayerPolicies()->IsPolicyBranchFinished(eImperialism)) { iWarBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 3 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR] * 2 * iAttackMultiplier; iHostileBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_HOSTILE] * 3 * iAttackMultiplier : vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iAttackMultiplier; iDeceptiveBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 3 * iAttackMultiplier : vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 * iAttackMultiplier; } } } if (eMyBranch == GD_INT_GET(POLICY_BRANCH_AUTOCRACY)) { iWarBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 6 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR] * 3 * iAttackMultiplier; iHostileBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_HOSTILE] * 6 * iAttackMultiplier : vApproachBias[CIV_APPROACH_HOSTILE] * 3 * iAttackMultiplier; iDeceptiveBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 6 * iAttackMultiplier : vApproachBias[CIV_APPROACH_DECEPTIVE] * 3 * iAttackMultiplier; } // Temporary attack bonus? if (GetPlayer()->GetAttackBonusTurns() > 0) { iWarBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_WAR] * 3 * iAttackMultiplier : vApproachBias[CIV_APPROACH_WAR] * 2 * iAttackMultiplier; iHostileBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_HOSTILE] * 3 * iAttackMultiplier : vApproachBias[CIV_APPROACH_HOSTILE] * 2 * iAttackMultiplier; iDeceptiveBonus += bWantsOpportunityAttack ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 3 * iAttackMultiplier : vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 * iAttackMultiplier; } // Scale based on personality (boldness), victory issues, and difficulty level if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP)) DifficultyModifier = max(GET_PLAYER(ePlayer).getHandicapInfo().getVictoryDisputePercent(), GET_PLAYER(ePlayer).getHandicapInfo().getVictoryBlockPercent()); else DifficultyModifier = max(GC.getGame().getHandicapInfo().getVictoryDisputePercent(), GC.getGame().getHandicapInfo().getVictoryBlockPercent()); iWarBonus *= (GetBoldness() + iMaxVictoryDispute) * DifficultyModifier; iWarBonus /= 500; iHostileBonus *= (GetMeanness() + iMaxVictoryDispute) * DifficultyModifier; iHostileBonus /= 500; if (!bApplyDeception) iDeceptiveBonus = 0; else { iDeceptiveBonus *= (GetDenounceWillingness() + max((int)GetVictoryDisputeLevel(ePlayer), (int)GetVictoryBlockLevel(ePlayer))) * DifficultyModifier; iDeceptiveBonus /= 500; } if (iWarBonus > 0 || iHostileBonus > 0 || iDeceptiveBonus > 0) { bool bNoHiding = bCloseToWorldConquest || (bEasyTarget && bGoodAttackTarget); vApproachScores[CIV_APPROACH_WAR] += iWarBonus; vApproachScores[CIV_APPROACH_HOSTILE] += iHostileBonus; vApproachScores[CIV_APPROACH_DECEPTIVE] += bNoHiding ? 0 : iDeceptiveBonus; if (bConquerorTraits || bCloseToWorldConquest || IsGoingForWorldConquest()) { vApproachScores[CIV_APPROACH_WAR] += iWarBonus; vApproachScores[CIV_APPROACH_HOSTILE] += iHostileBonus; vApproachScores[CIV_APPROACH_DECEPTIVE] += bNoHiding ? 0 : iDeceptiveBonus; } } //////////////////////////////////// // Non-War Social Policy Bonuses //////////////////////////////////// PolicyBranchTypes eTradition = (PolicyBranchTypes)GC.getInfoTypeForString("POLICY_BRANCH_TRADITION", true); PolicyBranchTypes eProgress = (PolicyBranchTypes)GC.getInfoTypeForString("POLICY_BRANCH_LIBERTY", true); PolicyBranchTypes eFealty = (PolicyBranchTypes)GC.getInfoTypeForString("POLICY_BRANCH_PIETY", true); PolicyBranchTypes eStatecraft = (PolicyBranchTypes)GC.getInfoTypeForString("POLICY_BRANCH_PATRONAGE", true); PolicyBranchTypes eArtistry = (PolicyBranchTypes)GC.getInfoTypeForString("POLICY_BRANCH_AESTHETICS", true); PolicyBranchTypes eIndustry = (PolicyBranchTypes)GC.getInfoTypeForString("POLICY_BRANCH_COMMERCE", true); PolicyBranchTypes eRationalism = (PolicyBranchTypes)GC.getInfoTypeForString("POLICY_BRANCH_RATIONALISM", true); // Tradition if (GetPlayer()->GetPlayerPolicies()->IsPolicyBranchUnlocked(eTradition)) { iMultiplier = GetPlayer()->GetPlayerPolicies()->IsPolicyBranchFinished(eTradition) ? 3 : 2; iMultiplier += IsCultural() || IsSecondaryCultural() ? 2 : 0; iMultiplier += bCulturalTraits ? 2 : 0; iMultiplier += pTraits->IsSmaller() ? 1 : 0; // Additional bonus if we're geared towards being smaller iMultiplier += GetWonderCompetitiveness() > 6 ? 1 : 0; // More likely to be friendly if not competing for Wonders, more aggressive if competing for Wonders if (GetWonderDisputeLevel(ePlayer) == DISPUTE_LEVEL_FIERCE) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * iMultiplier; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iMultiplier; } else if (!bUntrustworthy) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iMultiplier; } } // Progress if (GetPlayer()->GetPlayerPolicies()->IsPolicyBranchUnlocked(eProgress)) { iMultiplier = GetPlayer()->GetPlayerPolicies()->IsPolicyBranchFinished(eProgress) ? 3 : 2; iMultiplier += IsScientist() || IsSecondaryScientist() ? 2 : 0; iMultiplier += bScientistTraits ? 2 : 0; iMultiplier += pTraits->IsSmaller() ? 1 : 0; // Additional bonus if we're geared towards being smaller iMultiplier += bEarlyGameCompetitor ? -2 : 1; // Disincentivize early war - we want to work on our infrastructure if (iMyEra <= GD_INT_GET(MEDIEVAL_ERA)) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iMultiplier; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * iMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * iMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * iMultiplier; // Be wary of strong neighbors if (eClosestProximity >= PLAYER_PROXIMITY_CLOSE && eMilitaryStrength > STRENGTH_AVERAGE) { iMultiplier += bEarlyGameCompetitor ? 3 : 0; // 3 to undo the earlier change and add 1 vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * iMultiplier; } } } // Fealty if (GetPlayer()->GetPlayerPolicies()->IsPolicyBranchUnlocked(eFealty)) { iMultiplier = GetPlayer()->GetPlayerPolicies()->IsPolicyBranchFinished(eFealty) ? 2 : 1; iMultiplier += IsConqueror() || IsSecondaryConqueror() ? 1 : 0; iMultiplier += bConquerorTraits ? 1 : 0; iMultiplier += pTraits->IsReligious() ? 2 : 0; // Additional bonus if we're geared towards being religious iMultiplier += IsGoingForWorldConquest() ? 1 : 0; iMultiplier += iFlavorReligion < 5 ? -1 : 0; iMultiplier += iFlavorReligion > 7 ? 1 : 0; // Favor players who we share a religion with if (bSameReligion) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * iMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * iMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * iMultiplier; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * iMultiplier; } // Pre-Modern Era, let's be more intense about religious fervor else if (bDifferentReligions && iMyEra <= GD_INT_GET(INDUSTRIAL_ERA)) { vApproachScores[CIV_APPROACH_FRIENDLY] -= vApproachBias[CIV_APPROACH_FRIENDLY] * iMultiplier; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * iMultiplier; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iMultiplier; } } // Statecraft if (GetPlayer()->GetPlayerPolicies()->IsPolicyBranchUnlocked(eStatecraft)) { iMultiplier = GetPlayer()->GetPlayerPolicies()->IsPolicyBranchFinished(eStatecraft) ? 2 : 1; iMultiplier += pTraits->IsExpansionist() ? 2 : 0; // Larger civs care more, since they have more enforcement ability, resources and City-State allies iMultiplier += bDiplomatTraits ? 2 : 0; iMultiplier += IsDiplomat() || IsSecondaryDiplomat() ? 2 : 0; // Extra hatred for those who mess with our City-States (also our prime league competitor!) if ((bMetValidMinor && IsMinorCivTroublemaker(ePlayer)) || GetPrimeLeagueCompetitor() == ePlayer) { vApproachScores[CIV_APPROACH_FRIENDLY] -= vApproachBias[CIV_APPROACH_FRIENDLY] * iMultiplier; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * iMultiplier; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iMultiplier; } // Extra friendliness for others! else if (bMetValidMinor && !bUntrustworthy) { // Especially our prime league ally! if (GetPrimeLeagueAlly() == ePlayer) iMultiplier *= 2; vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * iMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * iMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * iMultiplier; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * iMultiplier; } } // Artistry if (GetPlayer()->GetPlayerPolicies()->IsPolicyBranchUnlocked(eArtistry)) { iMultiplier = GetPlayer()->GetPlayerPolicies()->IsPolicyBranchFinished(eArtistry) ? 2 : 1; iMultiplier += pTraits->IsSmaller() ? 2 : 0; // Smaller civs really care about cultural competition if they've chosen Artistry iMultiplier += bCulturalTraits ? 2 : 0; iMultiplier += IsCultural() || IsSecondaryCultural() ? 2 : 0; bool bStolenArtifacts = GetNumArtifactsEverDugUp(ePlayer) > 0; bool bInfluenceOverUs = false; // They have influence over us if (GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(eMyPlayer) >= INFLUENCE_LEVEL_POPULAR) { if (GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(eMyPlayer) >= INFLUENCE_LEVEL_INFLUENTIAL || GET_PLAYER(ePlayer).GetCulture()->GetInfluenceTrend(eMyPlayer) == INFLUENCE_TREND_RISING) { bInfluenceOverUs = true; } } // More aggressive if competing for Influence, dig sites or Wonders, more friendly otherwise if (bInfluenceOverUs || bStolenArtifacts || GetWonderDisputeLevel(ePlayer) == DISPUTE_LEVEL_FIERCE || IsWonderSpammer(ePlayer)) { vApproachScores[CIV_APPROACH_FRIENDLY] -= vApproachBias[CIV_APPROACH_FRIENDLY] * iMultiplier; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * iMultiplier; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iMultiplier; } else if (!bUntrustworthy) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * iMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * iMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * iMultiplier; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * iMultiplier; // If they've built landmarks for us, be happy! if (GetNumLandmarksBuiltForMe(ePlayer) > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * GetNumLandmarksBuiltForMe(ePlayer); } } } // Industry - more friendliness for trade partners if (GetPlayer()->GetPlayerPolicies()->IsPolicyBranchUnlocked(eIndustry)) { iMultiplier = GetPlayer()->GetPlayerPolicies()->IsPolicyBranchFinished(eIndustry) ? 2 : 1; iMultiplier += pTraits->IsExpansionist() ? 2 : 0; // Larger civs care more, since they have more trade resources iMultiplier += bDiplomatTraits ? 2 : 0; iMultiplier += IsDiplomat() || IsSecondaryDiplomat() ? 2 : 0; iMultiplier += IsMajorCompetitor(ePlayer) ? -2 : 1; iMultiplier += pTraits->IsImportsCountTowardsMonopolies() ? 2 : 0; // The Netherlands REALLY likes strategic trade partners if they've chosen Industry iMultiplier += pTraits->GetNumTradeRoutesModifier() > 0 ? 2 : 0; // ditto for Venice iMultiplier += pTraits->GetTradeRouteResourceModifier() > 0 ? 2 : 0; // ditto for Carthage for (int i = 0; i < NUM_YIELD_TYPES; i++) { YieldTypes e = static_cast(i); if (pTraits->GetYieldChangeIncomingTradeRoute(e) > 0 || pTraits->GetTradeRouteEndYieldInternational(e) > 0) { iMultiplier += 2; // ditto for Morocco/Ottomans break; } } // Trade partner that we're getting trade value from? if (iTradeDelta > 0) { if (IsHasDefensivePact(ePlayer) || GetPrimeLeagueAlly() == ePlayer || IsStrategicTradePartner(ePlayer)) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * iMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * iMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * iMultiplier; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * iMultiplier; } else { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iMultiplier / 2; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * iMultiplier / 2; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * iMultiplier / 2; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * iMultiplier / 2; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * iMultiplier / 2; } } } // Rationalism - more friendliness if ahead in science, more aggression if behind if (GetPlayer()->GetPlayerPolicies()->IsPolicyBranchUnlocked(eRationalism)) { iMultiplier = GetPlayer()->GetPlayerPolicies()->IsPolicyBranchFinished(eRationalism) ? 2 : 1; iMultiplier += pTraits->IsSmaller() ? 2 : 0; // Smaller civs really care about tech levels if they've chosen Rationalism iMultiplier += bScientistTraits ? 2 : 0; iMultiplier += IsScientist() || IsSecondaryScientist() ? 2 : 0; if (GetTechBlockLevel(ePlayer) > BLOCK_LEVEL_NONE) { vApproachScores[CIV_APPROACH_FRIENDLY] -= vApproachBias[CIV_APPROACH_FRIENDLY] * iMultiplier; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * iMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] += vApproachBias[CIV_APPROACH_DECEPTIVE] * iMultiplier; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * iMultiplier; } else { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iMultiplier; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * iMultiplier; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * iMultiplier; vApproachScores[CIV_APPROACH_DECEPTIVE] -= vApproachBias[CIV_APPROACH_DECEPTIVE] * iMultiplier; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * iMultiplier; } } //////////////////////////////////// // LEADER PERSONALITY - Each leader has their own tendencies // (currently experimenting with this) //////////////////////////////////// // Assyria - Attitude depends on tech lead if (pTraits->IsTechFromCityConquer() && !GET_TEAM(eMyTeam).GetTeamTechs()->HasResearchedAllTechs()) { int iTechDifference = iNumTheirTechs - iNumOurTechs; if (iTechDifference > 0) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iTechDifference * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * iTechDifference * 2 : 0; } else if (iTechDifference < 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * -iTechDifference * 2; } else { vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; } } // Aztecs - More likely to declare war than any other civ, everything else equal, plus an extra bonus for war when not in a Golden Age (or in the early game) else if (pTraits->GetGoldenAgeFromVictory() > 0) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2; if (bEarlyGameCompetitor || (!GetPlayer()->isGoldenAge() && eOurProximity == PLAYER_PROXIMITY_NEIGHBORS)) { if (bEasyTarget && bGoodAttackTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 10; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 10; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 10 : 0; } else if (bEasyTarget || bGoodAttackTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 5 : 0; } else if (GetTargetValue(ePlayer) >= TARGET_VALUE_FAVORABLE) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_DECEPTIVE] += bApplyDeception ? vApproachBias[CIV_APPROACH_DECEPTIVE] * 2 : 0; } } } // India - More likely to be friendly than any other civ, everything else equal, plus an extra bonus for players with no warmongering penalty else if (pTraits->IsProphetFervor()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; if (GetOtherPlayerWarmongerScore(ePlayer) <= 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; } } // ... unless Gandhi has nukes if (IsNuclearGandhi()) { vApproachScores[CIV_APPROACH_WAR] += 20000; vApproachScores[CIV_APPROACH_HOSTILE] += 10000; vApproachScores[CIV_APPROACH_FRIENDLY] = -100000; vApproachScores[CIV_APPROACH_NEUTRAL] = -100000; vApproachScores[CIV_APPROACH_AFRAID] = -100000; vApproachScores[CIV_APPROACH_DECEPTIVE] = -100000; } //////////////////////////////////// // WORLD CONGRESS - Are there any resolutions we should take into consideration? //////////////////////////////////// if (pLeague != NULL) { // UN active? Be more friendly if we're trying to win by diplomacy. if (pLeague->IsUnitedNations()) { if (IsGoingForDiploVictory() || bCloseToDiploVictory) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 5; } } // If either of us is sanctioned, we should usually be more hostile. if (pLeague->IsTradeEmbargoed(eMyPlayer, ePlayer)) { bool bIgnore = IsLiberator(ePlayer, false, false) || GetCoopWarAgreementScore(ePlayer) > 0 || GetCivOpinion(ePlayer) == CIV_OPINION_ALLY || GetDoFType(ePlayer) == DOF_TYPE_BATTLE_BROTHERS || HasTriedToUnsanctionUs(ePlayer) || HasEverUnsanctionedUs(ePlayer); if (bIgnore) bIgnore = !bWantsOpportunityAttack && !HasEverSanctionedUs(ePlayer) && !HasTriedToSanctionUs(ePlayer) && !pTheirDiplo->HasEverSanctionedUs(eMyPlayer) && !pTheirDiplo->HasTriedToSanctionUs(eMyPlayer); if (!bIgnore) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 5; } } // Casus Belli = more war, less friendly if (GC.getGame().GetGameLeagues()->IsWorldWar(eMyPlayer) > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] -= vApproachBias[CIV_APPROACH_FRIENDLY] * 5; vApproachScores[CIV_APPROACH_NEUTRAL] -= vApproachBias[CIV_APPROACH_NEUTRAL] * 5; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; vApproachScores[CIV_APPROACH_GUARDED] += vApproachBias[CIV_APPROACH_GUARDED] * 3; } // Global Peace Accords = less war, more friendly if (GC.getGame().GetGameLeagues()->GetUnitMaintenanceMod(eMyPlayer) > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 5; vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 5; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 5; vApproachScores[CIV_APPROACH_GUARDED] -= vApproachBias[CIV_APPROACH_GUARDED] * 3; } } //--------------------------------// // [PART 8: MODDER WEIGHT - FLAT] // //--------------------------------// //////////////////////////////////// // CUSTOM DLL MOD BONUS/PENALTY //////////////////////////////////// // Modders can add flat weight to approaches based on custom conditions (if a certain Custom Mod is active) here. //////////////////////////////////// // LUA BONUS/PENALTY //////////////////////////////////// // Modders can add flat weight to approaches based on custom conditions here. if (MOD_EVENTS_DIPLO_MODIFIERS) { for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { int iChange = 0; if (GAMEEVENTINVOKE_VALUE(iChange, GAMEEVENT_AiFlatApproachChange, GetID(), iApproachLoop) == GAMEEVENTRETURN_VALUE) { vApproachScores[iApproachLoop] += iChange; } } } //////////////////////////////////// // XML BONUS/PENALTY FROM DIFFICULTY LEVEL //////////////////////////////////// // Additional weight to approaches (flat +/-; configurable in DiploApproachWeights.sql) if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP)) { vApproachScores[CIV_APPROACH_NEUTRAL] += GET_PLAYER(ePlayer).getHandicapInfo().getHumanNeutralApproachChangeFlat(); vApproachScores[CIV_APPROACH_FRIENDLY] += GET_PLAYER(ePlayer).getHandicapInfo().getHumanFriendlyApproachChangeFlat(); vApproachScores[CIV_APPROACH_AFRAID] += GET_PLAYER(ePlayer).getHandicapInfo().getHumanAfraidApproachChangeFlat(); vApproachScores[CIV_APPROACH_GUARDED] += GET_PLAYER(ePlayer).getHandicapInfo().getHumanGuardedApproachChangeFlat(); vApproachScores[CIV_APPROACH_DECEPTIVE] += GET_PLAYER(ePlayer).getHandicapInfo().getHumanDeceptiveApproachChangeFlat(); vApproachScores[CIV_APPROACH_HOSTILE] += GET_PLAYER(ePlayer).getHandicapInfo().getHumanHostileApproachChangeFlat(); vApproachScores[CIV_APPROACH_WAR] += GET_PLAYER(ePlayer).getHandicapInfo().getHumanWarApproachChangeFlat(); } else { vApproachScores[CIV_APPROACH_NEUTRAL] += GC.getGame().getHandicapInfo().getAINeutralApproachChangeFlat(); vApproachScores[CIV_APPROACH_FRIENDLY] += GC.getGame().getHandicapInfo().getAIFriendlyApproachChangeFlat(); vApproachScores[CIV_APPROACH_AFRAID] += GC.getGame().getHandicapInfo().getAIAfraidApproachChangeFlat(); vApproachScores[CIV_APPROACH_GUARDED] += GC.getGame().getHandicapInfo().getAIGuardedApproachChangeFlat(); vApproachScores[CIV_APPROACH_DECEPTIVE] += GC.getGame().getHandicapInfo().getAIDeceptiveApproachChangeFlat(); vApproachScores[CIV_APPROACH_HOSTILE] += GC.getGame().getHandicapInfo().getAIHostileApproachChangeFlat(); vApproachScores[CIV_APPROACH_WAR] += GC.getGame().getHandicapInfo().getAIWarApproachChangeFlat(); } //--------------------------------// // [PART 9: PRIORITIZATION] // //--------------------------------// int iWarMod = vApproachBias[CIV_APPROACH_WAR]/100 + GetBoldness(); int iHostileMod = vApproachBias[CIV_APPROACH_HOSTILE]/100 + GetDenounceWillingness(); int iDeceptiveMod = vApproachBias[CIV_APPROACH_DECEPTIVE]/100 + (11 - GetLoyalty()); int iGuardedMod = vApproachBias[CIV_APPROACH_GUARDED]/100 + (11 - GetWorkWithWillingness()); int iAfraidMod = vApproachBias[CIV_APPROACH_AFRAID]/100 + (11 - GetMeanness()); int iFriendlyMod = vApproachBias[CIV_APPROACH_FRIENDLY]/100 + GetDoFWillingness(); int iNeutralMod = vApproachBias[CIV_APPROACH_NEUTRAL]/100 + GetDiploBalance(); // Prioritize our approaches to avoid adopting the same approach towards too many players and allow more variance/strategy // Only do this on the second pass of the function, as we've already recorded the most recent values for this turn if (!bStrategic && vValidPlayers.size() > 1) { for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eLoopApproach = (CivApproachTypes) iApproachLoop; // Create a vector to store and rank the approach weights of each player from the first pass CvWeightedVector vePlayerApproachValues; for (std::vector::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); ++it) { // We use the strategic approach here for a better variance/response to circumstances. vePlayerApproachValues.push_back(*it, GetPlayerStrategicApproachValue(*it, eLoopApproach)); } // Sort the weights from highest to lowest vePlayerApproachValues.StableSortItems(); // Find this player's ranking (how far are they down the list?) for (int iPlayerRanking = 0; iPlayerRanking < vePlayerApproachValues.size(); iPlayerRanking++) { PlayerTypes eLoopPlayer = (PlayerTypes) vePlayerApproachValues.GetElement(iPlayerRanking); if (eLoopPlayer == ePlayer) { // If this is the highest rated player for this approach, we do nothing. if (iPlayerRanking == 0) break; // If this player's ranking is greater than 0 (i.e. the highest approach weight of all players) then subtract weight // Ranking of 1 = -1x bias, 2 = -2x bias, etc. int iReduction = vApproachBias[iApproachLoop] * iPlayerRanking; // Adjust the reduction based on flavors // Higher approach flavor = Lower reduction // Lower approach flavor = Higher reduction int iFlavorMod = 0; switch (eLoopApproach) { case CIV_APPROACH_WAR: iFlavorMod = iWarMod - 10; break; case CIV_APPROACH_HOSTILE: iFlavorMod = iHostileMod - 10; break; case CIV_APPROACH_DECEPTIVE: iFlavorMod = iDeceptiveMod - 10; break; case CIV_APPROACH_GUARDED: iFlavorMod = iGuardedMod - 10; break; case CIV_APPROACH_AFRAID: iFlavorMod = iAfraidMod - 10; break; case CIV_APPROACH_FRIENDLY: iFlavorMod = iFriendlyMod - 10; break; case CIV_APPROACH_NEUTRAL: iFlavorMod = iNeutralMod - 10; break; default: UNREACHABLE(); } iFlavorMod *= 10; if (iFlavorMod > 0) { iReduction *= 100; iReduction /= 100 + iFlavorMod; } else if (iFlavorMod < 0) { iReduction *= 100 + iFlavorMod; iReduction /= 100; } if (iReduction > 0) vApproachScores[iApproachLoop] -= iReduction; break; } } } } // Negative approach weights - cap at zero! for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { if (vApproachScores[iApproachLoop] <= 0) vApproachScores[iApproachLoop] = 0; } //--------------------------------// // [PART 10: MULTIPLIERS] // //--------------------------------// //////////////////////////////////// // TOO MANY VASSALS MULTIPLIER //////////////////////////////////// if (GET_PLAYER(ePlayer).GetNumVassals() > 1) { // Increase bad approach scores for each vassal they own, provided they have more than one vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], 100 + GET_PLAYER(ePlayer).GetNumVassals() * /*20*/ GD_INT_GET(APPROACH_WAR_TOO_MANY_VASSALS)); // 2 vassals = 140%, 3 vassals = 160% vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], 100 + GET_PLAYER(ePlayer).GetNumVassals() * /*20*/ GD_INT_GET(APPROACH_WAR_TOO_MANY_VASSALS)); vApproachScores[CIV_APPROACH_GUARDED] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_GUARDED], 100 + GET_PLAYER(ePlayer).GetNumVassals() * /*20*/ GD_INT_GET(APPROACH_GUARDED_TOO_MANY_VASSALS)); vApproachScores[CIV_APPROACH_AFRAID] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_AFRAID], 100 + GET_PLAYER(ePlayer).GetNumVassals() * /*20*/ GD_INT_GET(APPROACH_GUARDED_TOO_MANY_VASSALS)); } //////////////////////////////////// // MILITARY TARGET VALUE - how tough is this guy to kill? //////////////////////////////////// // Target capacity should matter! If we don't have a good attack target, we shouldn't want to attack them as much! bool bWantsConquest = bWantsOpportunityAttack && bGoodAttackTarget; switch (GetTargetValue(ePlayer)) { case TARGET_VALUE_IMPOSSIBLE: vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], bWantsConquest ? /*50*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_IMPOSSIBLE) : /*25*/ GD_INT_GET(MAJOR_WAR_MULTIPLIER_TARGET_IMPOSSIBLE)); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], bWantsConquest ? /*50*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_IMPOSSIBLE) : /*25*/ GD_INT_GET(MAJOR_WAR_MULTIPLIER_TARGET_IMPOSSIBLE)); break; case TARGET_VALUE_BAD: vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], bWantsConquest ? /*75*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_BAD) : /*50*/ GD_INT_GET(MAJOR_WAR_MULTIPLIER_TARGET_BAD)); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], bWantsConquest ? /*75*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_BAD) : /*50*/ GD_INT_GET(MAJOR_WAR_MULTIPLIER_TARGET_BAD)); break; case TARGET_VALUE_DIFFICULT: vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], bWantsConquest ? /*100*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_DIFFICULT) : /*75*/ GD_INT_GET(MAJOR_WAR_MULTIPLIER_TARGET_DIFFICULT)); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], bWantsConquest ? /*100*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_DIFFICULT) : /*75*/ GD_INT_GET(MAJOR_WAR_MULTIPLIER_TARGET_DIFFICULT)); break; case TARGET_VALUE_AVERAGE: vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], bWantsConquest ? /*125*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_AVERAGE) : /*100*/ GD_INT_GET(MAJOR_WAR_MULTIPLIER_TARGET_AVERAGE)); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], bWantsConquest ? /*125*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_AVERAGE) : /*100*/ GD_INT_GET(MAJOR_WAR_MULTIPLIER_TARGET_AVERAGE)); break; case TARGET_VALUE_FAVORABLE: vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], bWantsConquest ? /*150*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_FAVORABLE) : /*125*/ GD_INT_GET(MAJOR_WAR_MULTIPLIER_TARGET_FAVORABLE)); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], bWantsConquest ? /*150*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_FAVORABLE) : /*125*/ GD_INT_GET(MAJOR_WAR_MULTIPLIER_TARGET_FAVORABLE)); break; case TARGET_VALUE_SOFT: vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], bWantsConquest ? /*200*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_SOFT) : /*150*/ GD_INT_GET(MAJOR_WAR_MULTIPLIER_TARGET_SOFT)); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], bWantsConquest ? /*200*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_SOFT) : /*150*/ GD_INT_GET(MAJOR_WAR_MULTIPLIER_TARGET_SOFT)); break; case TARGET_VALUE_CAKEWALK: vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], bWantsConquest ? /*250*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_CAKEWALK) : /*200*/ GD_INT_GET(MAJOR_WAR_MULTIPLIER_TARGET_CAKEWALK)); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], bWantsConquest ? /*250*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_CAKEWALK) : /*200*/ GD_INT_GET(MAJOR_WAR_MULTIPLIER_TARGET_CAKEWALK)); break; } //////////////////////////////////// // PROXIMITY MULTIPLIER - the farther away a player is the less likely we are to care about them! //////////////////////////////////// if (bCanCrossOcean) { switch (eClosestProximity) { case PLAYER_PROXIMITY_NEIGHBORS: vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], (bWantsConquest && eOurProximity == PLAYER_PROXIMITY_NEIGHBORS) ? /*200*/ GD_INT_GET(APPROACH_WAR_PROXIMITY_NEIGHBORS) : /*150*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_NEIGHBORS)); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], (bWantsConquest && eOurProximity == PLAYER_PROXIMITY_NEIGHBORS) ? /*200*/ GD_INT_GET(APPROACH_WAR_PROXIMITY_NEIGHBORS) : /*150*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_NEIGHBORS)); vApproachScores[CIV_APPROACH_AFRAID] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_AFRAID], /*150*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_NEIGHBORS)); vApproachScores[CIV_APPROACH_FRIENDLY] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_FRIENDLY], /*150*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_NEIGHBORS)); vApproachScores[CIV_APPROACH_DECEPTIVE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_DECEPTIVE], /*150*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_NEIGHBORS)); vApproachScores[CIV_APPROACH_GUARDED] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_GUARDED], /*150*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_NEIGHBORS)); break; case PLAYER_PROXIMITY_CLOSE: vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], (bWantsConquest && eOurProximity == PLAYER_PROXIMITY_CLOSE) ? /*150*/ GD_INT_GET(APPROACH_WAR_PROXIMITY_CLOSE) : /*125*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_CLOSE)); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], (bWantsConquest && eOurProximity == PLAYER_PROXIMITY_CLOSE) ? /*150*/ GD_INT_GET(APPROACH_WAR_PROXIMITY_CLOSE) : /*125*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_CLOSE)); vApproachScores[CIV_APPROACH_AFRAID] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_AFRAID], /*125*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_CLOSE)); vApproachScores[CIV_APPROACH_FRIENDLY] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_FRIENDLY], /*125*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_CLOSE)); vApproachScores[CIV_APPROACH_DECEPTIVE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_DECEPTIVE], /*125*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_CLOSE)); vApproachScores[CIV_APPROACH_GUARDED] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_GUARDED], /*125*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_CLOSE)); break; case PLAYER_PROXIMITY_FAR: vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], (bWantsConquest && eOurProximity == PLAYER_PROXIMITY_FAR) ? /*100*/ GD_INT_GET(APPROACH_WAR_PROXIMITY_FAR) : /*75*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_FAR)); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], (bWantsConquest && eOurProximity == PLAYER_PROXIMITY_FAR) ? /*100*/ GD_INT_GET(APPROACH_WAR_PROXIMITY_FAR) : /*75*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_FAR)); vApproachScores[CIV_APPROACH_FRIENDLY] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_FRIENDLY], /*75*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_FAR)); vApproachScores[CIV_APPROACH_AFRAID] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_AFRAID], /*75*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_FAR)); vApproachScores[CIV_APPROACH_GUARDED] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_GUARDED], /*75*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_FAR)); vApproachScores[CIV_APPROACH_DECEPTIVE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_DECEPTIVE], /*75*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_FAR)); break; case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], bWantsConquest ? /*75*/ GD_INT_GET(APPROACH_WAR_PROXIMITY_DISTANT) : /*50*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_DISTANT)); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], bWantsConquest ? /*75*/ GD_INT_GET(APPROACH_WAR_PROXIMITY_DISTANT) : /*50*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_DISTANT)); vApproachScores[CIV_APPROACH_FRIENDLY] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_FRIENDLY], /*50*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_DISTANT)); vApproachScores[CIV_APPROACH_AFRAID] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_AFRAID], /*50*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_DISTANT)); vApproachScores[CIV_APPROACH_GUARDED] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_GUARDED], /*50*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_DISTANT)); vApproachScores[CIV_APPROACH_DECEPTIVE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_DECEPTIVE], /*50*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_DISTANT)); break; } } else { switch (eClosestProximity) { case PLAYER_PROXIMITY_NEIGHBORS: vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], (bWantsConquest && eOurProximity == PLAYER_PROXIMITY_NEIGHBORS) ? /*200*/ GD_INT_GET(APPROACH_WAR_PROXIMITY_NEIGHBORS) : /*150*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_NEIGHBORS)); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], (bWantsConquest && eOurProximity == PLAYER_PROXIMITY_NEIGHBORS) ? /*200*/ GD_INT_GET(APPROACH_WAR_PROXIMITY_NEIGHBORS) : /*150*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_NEIGHBORS)); vApproachScores[CIV_APPROACH_AFRAID] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_AFRAID], /*150*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_NEIGHBORS)); vApproachScores[CIV_APPROACH_FRIENDLY] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_FRIENDLY], /*150*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_NEIGHBORS)); vApproachScores[CIV_APPROACH_DECEPTIVE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_DECEPTIVE], /*150*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_NEIGHBORS)); vApproachScores[CIV_APPROACH_GUARDED] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_GUARDED], /*150*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_NEIGHBORS)); break; case PLAYER_PROXIMITY_CLOSE: vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], (bWantsConquest && eOurProximity == PLAYER_PROXIMITY_CLOSE) ? /*150*/ GD_INT_GET(APPROACH_WAR_PROXIMITY_CLOSE) : /*125*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_CLOSE)); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], (bWantsConquest && eOurProximity == PLAYER_PROXIMITY_CLOSE) ? /*150*/ GD_INT_GET(APPROACH_WAR_PROXIMITY_CLOSE) : /*125*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_CLOSE)); vApproachScores[CIV_APPROACH_AFRAID] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_AFRAID], /*125*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_CLOSE)); vApproachScores[CIV_APPROACH_FRIENDLY] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_FRIENDLY], /*125*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_CLOSE)); vApproachScores[CIV_APPROACH_DECEPTIVE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_DECEPTIVE], /*125*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_CLOSE)); vApproachScores[CIV_APPROACH_GUARDED] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_GUARDED], /*125*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_CLOSE)); break; case PLAYER_PROXIMITY_FAR: vApproachScores[CIV_APPROACH_FRIENDLY] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_FRIENDLY], /*75*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_FAR)); vApproachScores[CIV_APPROACH_AFRAID] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_AFRAID], /*75*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_FAR)); vApproachScores[CIV_APPROACH_GUARDED] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_GUARDED], /*75*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_FAR)); vApproachScores[CIV_APPROACH_DECEPTIVE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_DECEPTIVE], /*75*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_FAR)); break; case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: vApproachScores[CIV_APPROACH_FRIENDLY] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_FRIENDLY], /*50*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_DISTANT)); vApproachScores[CIV_APPROACH_AFRAID] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_AFRAID], /*50*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_DISTANT)); vApproachScores[CIV_APPROACH_GUARDED] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_GUARDED], /*50*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_DISTANT)); vApproachScores[CIV_APPROACH_DECEPTIVE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_DECEPTIVE], /*50*/ GD_INT_GET(APPROACH_MULTIPLIER_PROXIMITY_DISTANT)); break; } if (eOurProximity < PLAYER_PROXIMITY_CLOSE) { vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; } } //////////////////////////////////// // DOMINATION VICTORY DISABLED //////////////////////////////////// bool bDominationVictoryEnabled = GC.getGame().isVictoryValid((VictoryTypes) GC.getInfoTypeForString("VICTORY_DOMINATION", true)); // NO WAR? if (!bDominationVictoryEnabled) { if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_MECHANICS)) { if (!GC.getGame().IsAIAggressiveTowardsHumans()) { vApproachScores[CIV_APPROACH_WAR] /= 2; vApproachScores[CIV_APPROACH_HOSTILE] /= 2; } } else if (!GC.getGame().IsAIAggressiveMode()) { vApproachScores[CIV_APPROACH_WAR] /= 2; vApproachScores[CIV_APPROACH_HOSTILE] /= 2; } } //////////////////////////////////// // SANCTIONED - Less FRIENDLY //////////////////////////////////// if (pLeague && pLeague->IsTradeEmbargoed(eMyPlayer, ePlayer)) { // Did they participate in sanctioning us or vice versa? if (HasEverSanctionedUs(ePlayer) || HasTriedToSanctionUs(ePlayer)) { vApproachScores[CIV_APPROACH_FRIENDLY] /= 2; vApproachScores[CIV_APPROACH_DECEPTIVE] /= 2; } else if ((pTheirDiplo->HasEverSanctionedUs(eMyPlayer) || pTheirDiplo->HasTriedToSanctionUs(eMyPlayer)) && !IsLiberator(ePlayer, false, true) && !bRecentLiberation) { vApproachScores[CIV_APPROACH_FRIENDLY] /= 2; vApproachScores[CIV_APPROACH_DECEPTIVE] /= 2; } // No? Then let's not totally turn our backs on them if we have a good opinion. else if (!IsLiberator(ePlayer, false, false) && GetCoopWarAgreementScore(ePlayer) <= 0 && GetDoFType(ePlayer) != DOF_TYPE_BATTLE_BROTHERS && !HasTriedToUnsanctionUs(ePlayer) && !HasEverUnsanctionedUs(ePlayer)) { switch (GetCivOpinion(ePlayer)) { case CIV_OPINION_ALLY: break; case CIV_OPINION_FRIEND: vApproachScores[CIV_APPROACH_FRIENDLY] *= 75; vApproachScores[CIV_APPROACH_FRIENDLY] /= 100; break; default: vApproachScores[CIV_APPROACH_FRIENDLY] /= 2; break; } } } //////////////////////////////////// // AGGRESSIVE MODE MULTIPLIER //////////////////////////////////// // Only war? ONLY WAR!!!! // (This mode is automatically enabled if only Domination and/or Time Victories are enabled.) if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_MECHANICS)) { if (GC.getGame().IsAIAggressiveTowardsHumans()) { vApproachScores[CIV_APPROACH_WAR] *= 2; vApproachScores[CIV_APPROACH_HOSTILE] *= 2; vApproachScores[CIV_APPROACH_DECEPTIVE] *= bApplyDeception ? 2 : 1; } } else if (GC.getGame().IsAIAggressiveMode()) { vApproachScores[CIV_APPROACH_WAR] *= 2; vApproachScores[CIV_APPROACH_HOSTILE] *= 2; vApproachScores[CIV_APPROACH_DECEPTIVE] *= bApplyDeception ? 2 : 1; } //--------------------------------// // [PART 11: MODDER WEIGHT - %] // //--------------------------------// //////////////////////////////////// // CUSTOM DLL MOD BONUS/PENALTY //////////////////////////////////// // Modders can add % weight to approaches based on custom conditions (if a certain Custom Mod is active) here. //////////////////////////////////// // LUA BONUS/PENALTY //////////////////////////////////// // Modders can add % weight to approaches based on custom conditions here. if (MOD_EVENTS_DIPLO_MODIFIERS) { for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { int iMod = 0; if (GAMEEVENTINVOKE_VALUE(iMod, GAMEEVENT_AiPercentApproachMod, GetID(), iApproachLoop) == GAMEEVENTRETURN_VALUE) { vApproachScores[iApproachLoop] = ApplyPercentageModifier(vApproachScores[iApproachLoop], max(0, 100 + iMod)); } } } //////////////////////////////////// // XML BONUS/PENALTY FROM DIFFICULTY LEVEL //////////////////////////////////// // Now add the percentage weight. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP)) { vApproachScores[CIV_APPROACH_NEUTRAL] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_NEUTRAL], max(0, 100 + GET_PLAYER(ePlayer).getHandicapInfo().getHumanNeutralApproachChangePercent())); vApproachScores[CIV_APPROACH_FRIENDLY] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_FRIENDLY], max(0, 100 + GET_PLAYER(ePlayer).getHandicapInfo().getHumanFriendlyApproachChangePercent())); vApproachScores[CIV_APPROACH_AFRAID] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_AFRAID], max(0, 100 + GET_PLAYER(ePlayer).getHandicapInfo().getHumanAfraidApproachChangePercent())); vApproachScores[CIV_APPROACH_GUARDED] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_GUARDED], max(0, 100 + GET_PLAYER(ePlayer).getHandicapInfo().getHumanGuardedApproachChangePercent())); vApproachScores[CIV_APPROACH_DECEPTIVE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_DECEPTIVE], max(0, 100 + GET_PLAYER(ePlayer).getHandicapInfo().getHumanDeceptiveApproachChangePercent())); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], max(0, 100 + GET_PLAYER(ePlayer).getHandicapInfo().getHumanHostileApproachChangePercent())); vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], max(0, 100 + GET_PLAYER(ePlayer).getHandicapInfo().getHumanWarApproachChangePercent())); } else { vApproachScores[CIV_APPROACH_NEUTRAL] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_NEUTRAL], max(0, 100 + GC.getGame().getHandicapInfo().getAINeutralApproachChangePercent())); vApproachScores[CIV_APPROACH_FRIENDLY] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_FRIENDLY], max(0, 100 + GC.getGame().getHandicapInfo().getAIFriendlyApproachChangePercent())); vApproachScores[CIV_APPROACH_AFRAID] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_AFRAID], max(0, 100 + GC.getGame().getHandicapInfo().getAIAfraidApproachChangePercent())); vApproachScores[CIV_APPROACH_GUARDED] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_GUARDED], max(0, 100 + GC.getGame().getHandicapInfo().getAIGuardedApproachChangePercent())); vApproachScores[CIV_APPROACH_DECEPTIVE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_DECEPTIVE], max(0, 100 + GC.getGame().getHandicapInfo().getAIDeceptiveApproachChangePercent())); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], max(0, 100 + GC.getGame().getHandicapInfo().getAIHostileApproachChangePercent())); vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], max(0, 100 + GC.getGame().getHandicapInfo().getAIWarApproachChangePercent())); } //////////////////////////////////// // RANDOMNESS - Adjust each approach score by +/- n% // Only do this for the non-strategic update to avoid erratic changes in desired friends/enemies. //////////////////////////////////// if (!bStrategic) { int iRandomFactor = range(GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES) ? /*10*/ GD_INT_GET(APPROACH_RANDOM_PERSONALITIES_PERCENT) : /*5*/ GD_INT_GET(APPROACH_RANDOM_PERCENT), 0, 100); if (iRandomFactor != 0) { int MyID = (int)eMyPlayer; int TheirID = (int)ePlayer; if (bReevaluation) { int NumReevaluations = GetNumReevaluations(); vApproachScores[CIV_APPROACH_FRIENDLY] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_FRIENDLY], GC.getGame().randRangeInclusive(100 - iRandomFactor, 100 + iRandomFactor, CvSeeder::fromRaw(0xb09432c8).mix(MyID).mix(TheirID).mix(NumReevaluations))); vApproachScores[CIV_APPROACH_NEUTRAL] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_NEUTRAL], GC.getGame().randRangeInclusive(100 - iRandomFactor, 100 + iRandomFactor, CvSeeder::fromRaw(0x576eeb77).mix(MyID).mix(TheirID).mix(NumReevaluations))); vApproachScores[CIV_APPROACH_AFRAID] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_AFRAID], GC.getGame().randRangeInclusive(100 - iRandomFactor, 100 + iRandomFactor, CvSeeder::fromRaw(0x7a7a0b0a).mix(MyID).mix(TheirID).mix(NumReevaluations))); vApproachScores[CIV_APPROACH_GUARDED] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_GUARDED], GC.getGame().randRangeInclusive(100 - iRandomFactor, 100 + iRandomFactor, CvSeeder::fromRaw(0xbe08eb5a).mix(MyID).mix(TheirID).mix(NumReevaluations))); vApproachScores[CIV_APPROACH_DECEPTIVE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_DECEPTIVE], GC.getGame().randRangeInclusive(100 - iRandomFactor, 100 + iRandomFactor, CvSeeder::fromRaw(0x0b148c8b).mix(MyID).mix(TheirID).mix(NumReevaluations))); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], GC.getGame().randRangeInclusive(100 - iRandomFactor, 100 + iRandomFactor, CvSeeder::fromRaw(0xa6c53d5a).mix(MyID).mix(TheirID).mix(NumReevaluations))); vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], GC.getGame().randRangeInclusive(100 - iRandomFactor, 100 + iRandomFactor, CvSeeder::fromRaw(0x3dceb668).mix(MyID).mix(TheirID).mix(NumReevaluations))); } else { vApproachScores[CIV_APPROACH_FRIENDLY] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_FRIENDLY], GC.getGame().randRangeInclusive(100 - iRandomFactor, 100 + iRandomFactor, CvSeeder::fromRaw(0xdaeb36d6).mix(MyID).mix(TheirID))); vApproachScores[CIV_APPROACH_NEUTRAL] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_NEUTRAL], GC.getGame().randRangeInclusive(100 - iRandomFactor, 100 + iRandomFactor, CvSeeder::fromRaw(0x30271783).mix(MyID).mix(TheirID))); vApproachScores[CIV_APPROACH_AFRAID] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_AFRAID], GC.getGame().randRangeInclusive(100 - iRandomFactor, 100 + iRandomFactor, CvSeeder::fromRaw(0x01aace4a).mix(MyID).mix(TheirID))); vApproachScores[CIV_APPROACH_GUARDED] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_GUARDED], GC.getGame().randRangeInclusive(100 - iRandomFactor, 100 + iRandomFactor, CvSeeder::fromRaw(0xbee17684).mix(MyID).mix(TheirID))); vApproachScores[CIV_APPROACH_DECEPTIVE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_DECEPTIVE], GC.getGame().randRangeInclusive(100 - iRandomFactor, 100 + iRandomFactor, CvSeeder::fromRaw(0x13c7e7c3).mix(MyID).mix(TheirID))); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], GC.getGame().randRangeInclusive(100 - iRandomFactor, 100 + iRandomFactor, CvSeeder::fromRaw(0x8f2681a5).mix(MyID).mix(TheirID))); vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], GC.getGame().randRangeInclusive(100 - iRandomFactor, 100 + iRandomFactor, CvSeeder::fromRaw(0x320744a1).mix(MyID).mix(TheirID))); } } } // Negative approach weights - cap at zero! for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { if (vApproachScores[iApproachLoop] <= 0) vApproachScores[iApproachLoop] = 0; } //--------------------------------// // [PART 12: ZERO-OUT CONDITIONS] // //--------------------------------// vector vScratchValueOverrides(NUM_CIV_APPROACHES, -1); // No valid attack target? if (!GetPlayer()->GetMilitaryAI()->HavePossibleAttackTarget(ePlayer)) { vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; vScratchValueOverrides[CIV_APPROACH_WAR] = 0; vScratchValueOverrides[CIV_APPROACH_HOSTILE] = 0; } //////////////////////////////////// // BANKRUPTCY SANITY //////////////////////////////////// // Don't go to war if we would go bankrupt! if (IsWarWouldBankruptUs(ePlayer)) { vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; vScratchValueOverrides[CIV_APPROACH_WAR] = 0; vScratchValueOverrides[CIV_APPROACH_HOSTILE] = 0; } //////////////////////////////////// // PEACE TREATY - have we made peace with this player recently? If so, reduce war weight //////////////////////////////////// bool bRecentPeaceTreaty = false; if (GetNumWarsFought(ePlayer) > 0) { int iPeaceTreatyTurn = GET_TEAM(eMyTeam).GetTurnMadePeaceTreatyWithTeam(eTeam); if (iPeaceTreatyTurn > -1) { int iTurnsSincePeace = GC.getGame().getGameTurn() - iPeaceTreatyTurn; int iPeaceDampenerTurns = GC.getGame().getHandicapInfo().getPeaceTreatyDampenerTurns(); if (iTurnsSincePeace < iPeaceDampenerTurns) bRecentPeaceTreaty = true; if (bRecentPeaceTreaty && !bStrategic) // Don't zero out weight here if this is the strategic update, we don't want to make friends that we would prefer to attack { vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; vScratchValueOverrides[CIV_APPROACH_WAR] = 0; vScratchValueOverrides[CIV_APPROACH_HOSTILE] = 0; } } } //////////////////////////////////// // Disfavor war if we can't even attack them! //////////////////////////////////// if (!GET_TEAM(eMyTeam).canDeclareWar(eTeam, eMyPlayer) && !IsAtWar(ePlayer)) { if (!bStrategic || !bRecentPeaceTreaty) // For the same reason, don't zero out weight here during the strategic update if we can't attack them due to a recent peace treaty { vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; vScratchValueOverrides[CIV_APPROACH_WAR] = 0; vScratchValueOverrides[CIV_APPROACH_HOSTILE] = 0; } } // On the flipside, why be afraid of players who can't attack us? if (!GET_TEAM(eTeam).canDeclareWar(eMyTeam, ePlayer) && !IsAtWar(ePlayer)) { vApproachScores[CIV_APPROACH_AFRAID] = 0; vScratchValueOverrides[CIV_APPROACH_AFRAID] = 0; } //////////////////////////////////// // LIBERATOR - Don't be HOSTILE to a player who's liberating us! //////////////////////////////////// if (IsLiberator(ePlayer, false, true) || bRecentLiberation) { vApproachScores[CIV_APPROACH_HOSTILE] = 0; vScratchValueOverrides[CIV_APPROACH_HOSTILE] = 0; if (bResurrectedUs || bRecentLiberation) { vApproachScores[CIV_APPROACH_WAR] = 0; vScratchValueOverrides[CIV_APPROACH_WAR] = 0; } } //////////////////////////////////// // NO FRIENDLY - Don't be friendly if we hate them! //////////////////////////////////// bool bNoFriendlyObvious = IsDenouncedPlayer(ePlayer) || IsDenouncedByPlayer(ePlayer) || bUntrustworthy || pTheirDiplo->IsRecentDemandAccepted(eMyPlayer) || bSanctioningUsNow || (bSanctioningThemNow && !IsLiberator(ePlayer, false, true) && !bRecentLiberation); bool bNoFriendly = bNoFriendlyObvious ? true : bProvokedUs && bWantsOpportunityAttack && (bEasyTarget || (GetMilitaryStrengthComparedToUs(ePlayer) < STRENGTH_STRONG && bGoodAttackTarget)); if (bStrategic && bNoFriendly) { vApproachScores[CIV_APPROACH_FRIENDLY] = 0; } //////////////////////////////////// // Disfavor bad approaches vs. liberated vassals //////////////////////////////////// if (!bNoFriendly && pTheirDiplo->IsMasterLiberatedMeFromVassalage(eMyPlayer)) { bool bOverride = false; // If they own any of our cities, ignore this if (GetPlayer()->GetNumOurCitiesOwnedBy(ePlayer) > 0) bOverride = true; // If they have original capitals and we're going for World Conquest, ignore this if ((bCloseToWorldConquest || IsGoingForWorldConquest()) && (GET_PLAYER(ePlayer).GetCapitalConqueror() == NO_PLAYER || GET_PLAYER(ePlayer).GetNumCapitalCities() > 0)) bOverride = true; // If they're close to winning the game, ignore this if (bTheyAreCloseToWorldConquest || IsEndgameAggressiveTo(ePlayer)) bOverride = true; if (!bOverride) { vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; vApproachScores[CIV_APPROACH_DECEPTIVE] = 0; vApproachScores[CIV_APPROACH_GUARDED] = 0; vApproachScores[CIV_APPROACH_AFRAID] = 0; } } //--------------------------------// // [PART 13: OPINION] // //--------------------------------// // We use opinion weight as +/- %: more fluid than the switch table. // Opinion is applied after every other modifier because the NEUTRAL approach has special logic to ensure that a good opinion doesn't punish the player inadvertently. int iOpinionWeight = GetCachedOpinionWeight(ePlayer); int iCompetitorThreshold = /*30*/ max(GD_INT_GET(OPINION_THRESHOLD_COMPETITOR), 1); int iFavorableThreshold = /*-30*/ min(GD_INT_GET(OPINION_THRESHOLD_FAVORABLE), -1); if (iOpinionWeight > iCompetitorThreshold) { // Let's also see how much we don't like this guy compared to others that we hate. // If we hate a lot of people, we need to prioritize! if (vValidPlayers.size() > 1) { int iAverage = 0; int iNumBadOpinions = 0; for (std::vector::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); ++it) { if (ePlayer == *it) continue; if (GetCachedOpinionWeight(*it) > /*30*/ GD_INT_GET(OPINION_THRESHOLD_COMPETITOR)) { iAverage += GetCachedOpinionWeight(*it); iNumBadOpinions++; } } iAverage /= max(iNumBadOpinions, 1); if (iNumBadOpinions > 0) { int iPercentDifferenceFromAverage = ((iOpinionWeight * 100) - (iAverage * 100)) / max(iAverage, 1); iOpinionWeight = ApplyPercentageModifier(iOpinionWeight, range((100 + iPercentDifferenceFromAverage), (iHostileMod + 40), max(100,(iFriendlyMod * 10)))); } } // Increase vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], 100 + iWarMod + iOpinionWeight); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], 100 + iHostileMod + iOpinionWeight); vApproachScores[CIV_APPROACH_DECEPTIVE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_DECEPTIVE], 100 + iDeceptiveMod + iOpinionWeight); vApproachScores[CIV_APPROACH_GUARDED] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_GUARDED], 100 + iGuardedMod + iOpinionWeight); vApproachScores[CIV_APPROACH_AFRAID] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_AFRAID], 100 + iAfraidMod + iOpinionWeight); // Decrease vApproachScores[CIV_APPROACH_FRIENDLY] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_FRIENDLY], max(100, 100 - iFriendlyMod + iOpinionWeight), true); // Decrease NEUTRAL vApproachScores[CIV_APPROACH_NEUTRAL] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_NEUTRAL], max(100, 100 - iNeutralMod + iOpinionWeight), true); } else if (iOpinionWeight < iFavorableThreshold) { // Flip the sign! iOpinionWeight *= -1; // Let's also see how much we like this guy compared to others that we like. // If we like a lot of people, we need to prioritize! if (vValidPlayers.size() > 1) { int iAverage = 0; int iNumGoodOpinions = 0; for (std::vector::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); ++it) { if (ePlayer == *it) continue; if (GetCachedOpinionWeight(*it) < /*-30*/ GD_INT_GET(OPINION_THRESHOLD_FAVORABLE)) { iAverage -= GetCachedOpinionWeight(*it); iNumGoodOpinions++; } } iAverage /= max(iNumGoodOpinions, 1); if (iNumGoodOpinions > 0) { int iPercentDifferenceFromAverage = ((iOpinionWeight * 100) - (iAverage * 100)) / max(iAverage, 1); iOpinionWeight = ApplyPercentageModifier(iOpinionWeight, range((100 + iPercentDifferenceFromAverage), (iFriendlyMod + 40), max(100,(iHostileMod * 10)))); } } // Increase vApproachScores[CIV_APPROACH_FRIENDLY] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_FRIENDLY], 100 + iFriendlyMod + iOpinionWeight); // Decrease vApproachScores[CIV_APPROACH_WAR] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_WAR], max(100, 100 - iWarMod + iOpinionWeight), true); vApproachScores[CIV_APPROACH_HOSTILE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_HOSTILE], max(100, 100 - iHostileMod + iOpinionWeight), true); vApproachScores[CIV_APPROACH_DECEPTIVE] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_DECEPTIVE], max(100, 100 - iDeceptiveMod + iOpinionWeight), true); vApproachScores[CIV_APPROACH_GUARDED] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_GUARDED], max(100, 100 - iGuardedMod + iOpinionWeight), true); vApproachScores[CIV_APPROACH_AFRAID] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_AFRAID], max(100, 100 - iAfraidMod + iOpinionWeight), true); // Special logic for NEUTRAL approach! // Determine the maximum amount NEUTRAL can be raised up or down based on Opinion int iNeutralUpB = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_NEUTRAL], 100 + iNeutralMod + iOpinionWeight); int iNeutralDownB = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_NEUTRAL], max(100, 100 - iNeutralMod + iOpinionWeight), true); // Jump ahead and look what all the values will be after Part 14: The Approach Curve int iNeutralPostCurve = vApproachScores[CIV_APPROACH_NEUTRAL]; int iFriendlyPostCurve = vApproachScores[CIV_APPROACH_FRIENDLY]; int iHighestBadApproachPostCurve = 0; if (!bStrategic) { for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eLoopApproach = (CivApproachTypes) iApproachLoop; int iLastTurnValue = vScratchValueOverrides[iApproachLoop] >= 0 ? vScratchValueOverrides[iApproachLoop] : GetPlayerApproachValue(ePlayer, eLoopApproach); int iApproachValue = vApproachScores[iApproachLoop]; float fAlpha = /*0.30f*/ GD_FLOAT_GET(APPROACH_SHIFT_PERCENT); int iAverage = int(0.5f + (iApproachValue * fAlpha) + (iLastTurnValue * (1 - fAlpha))); // If the value changed, make sure it goes up/down by at least one if (iAverage == iLastTurnValue && iApproachValue != iLastTurnValue) { iAverage += (iApproachValue > iLastTurnValue) ? 1 : -1; } switch (eLoopApproach) { case CIV_APPROACH_NEUTRAL: iNeutralPostCurve = iAverage; break; case CIV_APPROACH_FRIENDLY: iFriendlyPostCurve = iAverage; break; default: iHighestBadApproachPostCurve = max(iHighestBadApproachPostCurve, iAverage); break; } } } else { iHighestBadApproachPostCurve = max(vApproachScores[CIV_APPROACH_WAR], vApproachScores[CIV_APPROACH_HOSTILE]); iHighestBadApproachPostCurve = max(iHighestBadApproachPostCurve, vApproachScores[CIV_APPROACH_DECEPTIVE]); iHighestBadApproachPostCurve = max(iHighestBadApproachPostCurve, vApproachScores[CIV_APPROACH_GUARDED]); iHighestBadApproachPostCurve = max(iHighestBadApproachPostCurve, vApproachScores[CIV_APPROACH_AFRAID]); } if (iFriendlyPostCurve > iHighestBadApproachPostCurve) { // We want the NEUTRAL approach score to be 1 below FRIENDLY *after* the approach curve is applied for this turn int iNeutralTarget = iFriendlyPostCurve - 1; // Unless the approach curve doesn't apply, we need to figure out what value - when combined with last turn's NEUTRAL value in the curve - will result in the target value if (!bStrategic) { float fAlpha = /*0.30f*/ GD_FLOAT_GET(APPROACH_SHIFT_PERCENT); int iLastTurnNeutralValue = vScratchValueOverrides[CIV_APPROACH_NEUTRAL] >= 0 ? vScratchValueOverrides[CIV_APPROACH_NEUTRAL] : GetPlayerApproachValue(ePlayer, CIV_APPROACH_NEUTRAL); iNeutralTarget = (int) ((iNeutralTarget - ((1 - fAlpha) * iLastTurnNeutralValue)) / fAlpha); } // Change the NEUTRAL approach to match the target as much as Opinion permits if (iNeutralTarget > vApproachScores[CIV_APPROACH_NEUTRAL]) { vApproachScores[CIV_APPROACH_NEUTRAL] = min(iNeutralTarget, iNeutralUpB); } else if (iNeutralTarget < vApproachScores[CIV_APPROACH_NEUTRAL]) { vApproachScores[CIV_APPROACH_NEUTRAL] = max(iNeutralDownB, iNeutralTarget); } } else { // FRIENDLY doesn't outscore the bad approaches, so raise NEUTRAL as much as possible vApproachScores[CIV_APPROACH_NEUTRAL] = iNeutralUpB; } } else { // Increase NEUTRAL if we're somewhere between the two thresholds, inclusive // The closer iOpinionWeight is to 0, the stronger the boost (2x iNeutralMod when at 0, 1x iNeutralMod when at either threshold) if (iOpinionWeight >= 0) { iNeutralMod *= (100 + 100 * (iCompetitorThreshold - iOpinionWeight) / iCompetitorThreshold); iNeutralMod /= 100; } else { iNeutralMod *= (100 + 100 * (iFavorableThreshold - iOpinionWeight) / -iFavorableThreshold); iNeutralMod /= 100; } vApproachScores[CIV_APPROACH_NEUTRAL] = ApplyPercentageModifier(vApproachScores[CIV_APPROACH_NEUTRAL], 100 + iNeutralMod); } //--------------------------------// // [PART 14: THE APPROACH CURVE] // //--------------------------------// bool bAllZero = true; // Save off the scratch value for logging! vector vApproachScoresScratch; if (!bStrategic) { // Let's make this a gradual process - no rapid jumping from value to value! for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eLoopApproach = (CivApproachTypes) iApproachLoop; int iLastTurnValue = GetPlayerApproachValue(ePlayer, eLoopApproach); vApproachScoresScratch.push_back(iLastTurnValue); // Certain circumstances call for using a different value than the actual last turn value (for the averaging) // This has to be done after pushing back the actual value for logging to work properly if (vScratchValueOverrides[iApproachLoop] >= 0) { iLastTurnValue = vScratchValueOverrides[iApproachLoop]; } int iApproachValue = vApproachScores[iApproachLoop]; if (!bReevaluation && !bFirstUpdate) { float fAlpha = /*0.30f*/ GD_FLOAT_GET(APPROACH_SHIFT_PERCENT); int iAverage = int(0.5f + (iApproachValue * fAlpha) + (iLastTurnValue * (1 - fAlpha))); // If the value changed, make sure it goes up/down by at least one if (iAverage == iLastTurnValue && iApproachValue != iLastTurnValue) { iAverage += (iApproachValue > iLastTurnValue) ? 1 : -1; } vApproachScores[iApproachLoop] = iAverage; if (iAverage > 0) bAllZero = false; // Set the new average for next turn (but not if we're doing a reevaluation!). SetPlayerApproachValue(ePlayer, eLoopApproach, iAverage); } else { if (iApproachValue > 0) bAllZero = false; // We're re-evaluating this player (or evaluating them for the first time), so use this turn's value as the average SetPlayerApproachValue(ePlayer, eLoopApproach, iApproachValue); } } } else { for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { if (vApproachScores[iApproachLoop] > 0) { bAllZero = false; break; } } } //--------------------------------// // [PART 15: APPROACH SELECTION] // //--------------------------------// // This vector is what we'll use to sort CvWeightedVector vApproachScoresForSorting; // Transfer values from our normal int vector (which we need for logging) to the Weighted Vector we can sort for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eLoopApproach = (CivApproachTypes) iApproachLoop; vApproachScoresForSorting.push_back(eLoopApproach, vApproachScores[iApproachLoop]); } vApproachScoresForSorting.StableSortItems(); // All at zero? Neutral is the default. CivApproachTypes eApproach = bAllZero ? CIV_APPROACH_NEUTRAL : vApproachScoresForSorting.GetElement(0); //////////////////////////////////// // APPROACH OVERRIDES //////////////////////////////////// if (bNoFriendlyObvious) { if (eApproach == CIV_APPROACH_FRIENDLY || eApproach == CIV_APPROACH_DECEPTIVE) eApproach = eOpinion <= CIV_OPINION_ENEMY ? CIV_APPROACH_GUARDED : CIV_APPROACH_NEUTRAL; } else if (bNoFriendly && eApproach == CIV_APPROACH_FRIENDLY) { eApproach = CIV_APPROACH_DECEPTIVE; } // If this is the first time ever updating approach towards this player, let's not be hostile right off the bat - neutral is fine, though. // Unless they've somehow managed to provoke us already! if (bFirstUpdate && !bProvokedUs && eApproach < CIV_APPROACH_NEUTRAL) { eApproach = CIV_APPROACH_NEUTRAL; } // If this was a strategic update, update the strategic approach values if (bStrategic) { for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eLoopApproach = (CivApproachTypes) iApproachLoop; SetPlayerStrategicApproachValue(ePlayer, eLoopApproach, vApproachScores[iApproachLoop]); } SetCivStrategicApproach(ePlayer, eApproach); } // Finally, update our approach if (!bStrategic) { SetCivApproach(ePlayer, eApproach, /*bResetAttackOperations*/ false); LogMajorCivApproachUpdate(ePlayer, &vApproachScores[0], eApproach, eOldApproach, GetSurfaceApproach(ePlayer)); LogApproachValueDeltas(ePlayer, &vApproachScores[0], &vApproachScoresScratch[0]); } if (bReevaluation) ChangeNumReevaluations(1); } /// Who are our major competitors? void CvDiplomacyAI::DoUpdateMajorCompetitors() { int iNumMajorsAlive = GC.getGame().GetNumMajorCivsAlive(); int iDangerThreshold = GC.getGame().GetNumMajorCivsEver() * 33 / 100; bool bCloseToDominationVictory = IsCloseToWorldConquest(); bool bCloseToDiploVictory = IsCloseToDiploVictory(); bool bCloseToCultureVictory = IsCloseToCultureVictory(); bool bCloseToScienceVictory = IsCloseToSpaceshipVictory(); int iEra = GetPlayer()->GetCurrentEra(); int iOurTechs = GET_TEAM(GetTeam()).GetTeamTechs()->GetNumTechsKnown(); CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); int iMyVotes = (pLeague != NULL) ? pLeague->CalculateStartingVotesForMember(GetID()) : 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; if (!IsPlayerValid(ePlayer) || !GET_PLAYER(ePlayer).isMajorCiv()) { SetMajorCompetitor(ePlayer, false); continue; } if (iNumMajorsAlive == 2 && IsCompetingForVictory()) { SetMajorCompetitor(ePlayer, true); continue; } if (GET_PLAYER(ePlayer).GetFractionOriginalCapitalsUnderControl() >= 33) { SetMajorCompetitor(ePlayer, true); continue; } if (GetPlayerNumMajorsConquered(ePlayer) >= iDangerThreshold) { SetMajorCompetitor(ePlayer, true); continue; } if (IsEndgameAggressiveTo(ePlayer)) { SetMajorCompetitor(ePlayer, true); continue; } if (IsCapitalCapturedBy(ePlayer) || IsHolyCityCapturedBy(ePlayer)) { SetMajorCompetitor(ePlayer, true); continue; } if (IsNukedBy(ePlayer) || GET_PLAYER(ePlayer).getNumNukeUnits() > 0) { SetMajorCompetitor(ePlayer, true); continue; } // Vassals aren't major competitors 99% of the time. if (GET_PLAYER(ePlayer).IsVassalOfSomeone()) { SetMajorCompetitor(ePlayer, false); continue; } if (GET_PLAYER(ePlayer).IsHasLostCapital()) { SetMajorCompetitor(ePlayer, false); continue; } if (bCloseToDominationVictory) { SetMajorCompetitor(ePlayer, true); continue; } if (IsUntrustworthy(ePlayer)) { SetMajorCompetitor(ePlayer, true); continue; } if (IsPlayerOpposingIdeology(ePlayer)) { SetMajorCompetitor(ePlayer, true); continue; } if (GetWarmongerThreat(ePlayer) >= THREAT_SEVERE) { SetMajorCompetitor(ePlayer, true); continue; } if (GetCivOpinion(ePlayer) == CIV_OPINION_UNFORGIVABLE) { SetMajorCompetitor(ePlayer, true); continue; } if (GetVictoryDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG || GetVictoryBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG) { SetMajorCompetitor(ePlayer, true); continue; } if (GetLandDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) { SetMajorCompetitor(ePlayer, true); continue; } if (IsRecklessExpander(ePlayer)) { SetMajorCompetitor(ePlayer, true); continue; } if (IsWonderSpammer(ePlayer)) { SetMajorCompetitor(ePlayer, true); continue; } if (pLeague != NULL) { // Is this our primary League competitor? if (GetPrimeLeagueCompetitor() == ePlayer) { SetMajorCompetitor(ePlayer, true); continue; } } if (IsCompetingForVictory()) { if (iEra >= GD_INT_GET(MEDIEVAL_ERA) && IsGoingForWorldConquest()) { if (GET_PLAYER(ePlayer).GetNumCapitalCities() > 0) { SetMajorCompetitor(ePlayer, true); continue; } if (GetLandDisputeLevel(ePlayer) > DISPUTE_LEVEL_NONE) { SetMajorCompetitor(ePlayer, true); continue; } if (GetPlayer()->GetProximityToPlayer(ePlayer) == PLAYER_PROXIMITY_NEIGHBORS) { SetMajorCompetitor(ePlayer, true); continue; } } if ((iEra >= GD_INT_GET(RENAISSANCE_ERA) && IsGoingForDiploVictory()) || bCloseToDiploVictory) { if (IsMinorCivTroublemaker(ePlayer, true)) { SetMajorCompetitor(ePlayer, true); continue; } if (pLeague != NULL) { // Sanctioned us? if (HasEverSanctionedUs(ePlayer)) { SetMajorCompetitor(ePlayer, true); continue; } // More votes than us and aligned against us? if (pLeague->CalculateStartingVotesForMember(ePlayer) > iMyVotes) { if (GetPlayer()->GetLeagueAI()->EvaluateAlignment(ePlayer) < CvLeagueAI::ALIGNMENT_NEUTRAL) { SetMajorCompetitor(ePlayer, true); continue; } } } } if ((iEra >= GD_INT_GET(RENAISSANCE_ERA) && IsGoingForCultureVictory()) || bCloseToCultureVictory) { if (GetWonderDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) { SetMajorCompetitor(ePlayer, true); continue; } if (GetPolicyBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG) { SetMajorCompetitor(ePlayer, true); continue; } if (GetPlayer()->GetCulture()->GetCivLowestInfluence(false) == ePlayer) { SetMajorCompetitor(ePlayer, true); continue; } if (GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(GetID()) >= INFLUENCE_LEVEL_POPULAR) { if (GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(GetID()) >= INFLUENCE_LEVEL_INFLUENTIAL || GET_PLAYER(ePlayer).GetCulture()->GetInfluenceTrend(GetID()) != INFLUENCE_TREND_FALLING) { SetMajorCompetitor(ePlayer, true); continue; } } if (iEra >= GD_INT_GET(MODERN_ERA)) { if (GetPlayer()->GetCulture()->GetInfluenceLevel(ePlayer) <= INFLUENCE_LEVEL_FAMILIAR || (GetPlayer()->GetCulture()->GetInfluenceLevel(ePlayer) == INFLUENCE_LEVEL_POPULAR && GetPlayer()->GetCulture()->GetInfluenceTrend(ePlayer) == INFLUENCE_TREND_FALLING)) { SetMajorCompetitor(ePlayer, true); continue; } } else { if (GetPlayer()->GetCulture()->GetInfluenceLevel(ePlayer) <= INFLUENCE_LEVEL_EXOTIC || (GetPlayer()->GetCulture()->GetInfluenceLevel(ePlayer) == INFLUENCE_LEVEL_FAMILIAR && GetPlayer()->GetCulture()->GetInfluenceTrend(ePlayer) == INFLUENCE_TREND_FALLING)) { SetMajorCompetitor(ePlayer, true); continue; } } } if ((iEra >= GD_INT_GET(RENAISSANCE_ERA) && IsGoingForSpaceshipVictory()) || bCloseToScienceVictory) { if (GetTechBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG) { SetMajorCompetitor(ePlayer, true); continue; } int iTheirTechs = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetTeamTechs()->GetNumTechsKnown(); if (iEra >= GD_INT_GET(ATOMIC_ERA) && iTheirTechs > iOurTechs) { SetMajorCompetitor(ePlayer, true); continue; } } } SetMajorCompetitor(ePlayer, false); } } /// Plan our relationships with other major civilizations void CvDiplomacyAI::DoRelationshipPairing() { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR)) return; vector vValidPlayers; // STEP 1: Identify our prime league competitor CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); bool bDiploVictoryEnabled = GC.getGame().isVictoryValid((VictoryTypes) GC.getInfoTypeForString("VICTORY_DIPLOMATIC", true)); bool bGoingForDiploVictory = bDiploVictoryEnabled && IsCompetingForVictory() && IsGoingForDiploVictory(); bool bCloseToDiploVictory = bDiploVictoryEnabled && IsCloseToDiploVictory(); // The variables below only matter for Step 1, the ones just above are used later in the function bool bPrimeIsCloseToWinning = false; PlayerTypes ePrimeLeagueCompetitor = NO_PLAYER; int iMyVotes = pLeague != NULL ? pLeague->CalculateStartingVotesForMember(GetID()) : 0; int iPrimeVotes = 0; int iMinimumScore = /*-2400*/ range(-GD_INT_GET(VOTING_HISTORY_SCORE_MAX), SHRT_MIN, 0); int iBadVotingHistoryThreshold = /*-200*/ iMinimumScore / max(1, GD_INT_GET(VOTING_HISTORY_SCORE_PRIME_COMPETITOR_THRESHOLD)); vector vPlayersGivingAChanceTo; CvWeightedVector veVoteCounts; CvWeightedVector veVotingHistoryScores; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (!IsPlayerValid(eLoopPlayer)) continue; // Add them to the evaluation list for later vValidPlayers.push_back(eLoopPlayer); // Don't proceed further unless the World Congress is active if (!pLeague) continue; if (WasResurrectedBy(eLoopPlayer)) continue; if (IsVassal(eLoopPlayer) && GetVassalTreatmentLevel(eLoopPlayer) <= VASSAL_TREATMENT_DISAGREE) continue; bool bTheyAreCloseToWinning = GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsCloseToDiploVictory(); if (IsMaster(eLoopPlayer) && !bTheyAreCloseToWinning) continue; if (bDiploVictoryEnabled && IsCompetingForVictory()) { if (bTheyAreCloseToWinning) { if (!bPrimeIsCloseToWinning) { ePrimeLeagueCompetitor = eLoopPlayer; bPrimeIsCloseToWinning = true; iPrimeVotes = pLeague->CalculateStartingVotesForMember(eLoopPlayer); } else { int iVotes = pLeague->CalculateStartingVotesForMember(eLoopPlayer); int iPrimeVotes = pLeague->CalculateStartingVotesForMember(ePrimeLeagueCompetitor); if (iVotes > iPrimeVotes) { ePrimeLeagueCompetitor = eLoopPlayer; iPrimeVotes = iVotes; } // Resolve any ties here using voting history score // Use an aggressive evaluation here because both players are close to Diplomatic Victory - no leniency threshold else if (iVotes == iPrimeVotes) { if (GetVotingHistoryScore(eLoopPlayer) < GetVotingHistoryScore(ePrimeLeagueCompetitor)) { ePrimeLeagueCompetitor = eLoopPlayer; } // If there's still a tie, resolve it using opinion weight else if (GetVotingHistoryScore(eLoopPlayer) == GetVotingHistoryScore(ePrimeLeagueCompetitor)) { if (GetCachedOpinionWeight(eLoopPlayer) > GetCachedOpinionWeight(ePrimeLeagueCompetitor)) { ePrimeLeagueCompetitor = eLoopPlayer; } } } } continue; } // Don't evaluate alignment or vote count if someone else is close to winning, and we're competing with them else if (bPrimeIsCloseToWinning) { continue; } } if (bGoingForDiploVictory || bCloseToDiploVictory) { int iVotes = pLeague->CalculateStartingVotesForMember(eLoopPlayer); veVoteCounts.push_back(eLoopPlayer, iVotes); // If they have less than 2/3rds of our votes, we don't hate them and their league alignment or opinion is high enough, let's give them a chance. bool bGiveAChance = iVotes < ((iMyVotes * 2) / 3) && !IsAtWar(eLoopPlayer) && GetWorkWithWillingness() >= (GetWorkAgainstWillingness() - 1); if (bGiveAChance) bGiveAChance = GetCivOpinion(eLoopPlayer) > CIV_OPINION_ENEMY && !HasEverSanctionedUs(eLoopPlayer) && !HasTriedToSanctionUs(eLoopPlayer) && !IsEndgameAggressiveTo(eLoopPlayer) && !IsUntrustworthy(eLoopPlayer); if (bGiveAChance) bGiveAChance = GetCivOpinion(eLoopPlayer) >= CIV_OPINION_FRIEND || HasEverUnsanctionedUs(eLoopPlayer) || HasTriedToUnsanctionUs(eLoopPlayer) || IsLiberator(eLoopPlayer, false, true) || GetPlayer()->GetLeagueAI()->EvaluateAlignment(eLoopPlayer, true) >= CvLeagueAI::ALIGNMENT_CONFIDANT; if (bGiveAChance) vPlayersGivingAChanceTo.push_back(eLoopPlayer); } else veVotingHistoryScores.push_back(eLoopPlayer, GetVotingHistoryScore(eLoopPlayer) - iMinimumScore); } // If we've already picked a prime league competitor who's close to winning via Diplomatic Victory, skip the following checks. Otherwise, proceed. if (ePrimeLeagueCompetitor == NO_PLAYER && (!veVoteCounts.empty() || !veVotingHistoryScores.empty())) { // If we're aiming for a diplomatic victory, we evaluate by highest vote total first, then by worst voting history score if (bGoingForDiploVictory || bCloseToDiploVictory) { iPrimeVotes = 0; veVoteCounts.StableSortItems(); for (int iVoteCountIndex = 0; iVoteCountIndex < veVoteCounts.size(); iVoteCountIndex++) { PlayerTypes eLoopPlayer = (PlayerTypes) veVoteCounts.GetElement(iVoteCountIndex); int iVotes = veVoteCounts.GetWeight(iVoteCountIndex); // No one yet? This is our guy. if (ePrimeLeagueCompetitor == NO_PLAYER) { ePrimeLeagueCompetitor = eLoopPlayer; iPrimeVotes = iVotes; continue; } // We've got somebody already. If we're not giving them a chance and the next player's vote count is lower, we're done. if (std::find(vPlayersGivingAChanceTo.begin(), vPlayersGivingAChanceTo.end(), ePrimeLeagueCompetitor) == vPlayersGivingAChanceTo.end() && iVotes < iPrimeVotes) break; // Okay, looks like we need to compare the two. // Let's check voting history scores first. int iLoopVotingHistoryScore = GetVotingHistoryScore(eLoopPlayer); int iPrimeVotingHistoryScore = GetVotingHistoryScore(ePrimeLeagueCompetitor); // This player's score is worse than the prime competitor's score if (iLoopVotingHistoryScore < iPrimeVotingHistoryScore) { // Within a certain threshold (less than 200 by default), voting history scores are considered equal-ish...if below that, we have a new prime competitor! if (iLoopVotingHistoryScore <= (iPrimeVotingHistoryScore + iBadVotingHistoryThreshold)) { ePrimeLeagueCompetitor = eLoopPlayer; iPrimeVotes = iVotes; } // Resolve ties using league alignment else { CvLeagueAI::AlignmentLevels eAlignment = GetPlayer()->GetLeagueAI()->EvaluateAlignment(eLoopPlayer, true); CvLeagueAI::AlignmentLevels ePrimeAlignment = GetPlayer()->GetLeagueAI()->EvaluateAlignment(ePrimeLeagueCompetitor, true); if (eAlignment < ePrimeAlignment) { ePrimeLeagueCompetitor = eLoopPlayer; iPrimeVotes = iVotes; } // Another tie? Resolve it using opinion weight. else if (eAlignment == ePrimeAlignment) { if (GetCachedOpinionWeight(eLoopPlayer) > GetCachedOpinionWeight(ePrimeLeagueCompetitor)) { ePrimeLeagueCompetitor = eLoopPlayer; iPrimeVotes = iVotes; } } } } // Prime competitor's score is worse than or equal to this player's score // Within a certain threshold (less than 200 by default), voting history scores are considered equal-ish...if above that, prime competitor remains in the lead. else if (iPrimeVotingHistoryScore <= (iLoopVotingHistoryScore + iBadVotingHistoryThreshold)) { // Resolve ties using league alignment CvLeagueAI::AlignmentLevels eAlignment = GetPlayer()->GetLeagueAI()->EvaluateAlignment(eLoopPlayer, true); CvLeagueAI::AlignmentLevels ePrimeAlignment = GetPlayer()->GetLeagueAI()->EvaluateAlignment(ePrimeLeagueCompetitor, true); if (eAlignment < ePrimeAlignment) { ePrimeLeagueCompetitor = eLoopPlayer; iPrimeVotes = iVotes; } // Another tie? Resolve it using opinion weight. else if (eAlignment == ePrimeAlignment) { if (GetCachedOpinionWeight(eLoopPlayer) > GetCachedOpinionWeight(ePrimeLeagueCompetitor)) { ePrimeLeagueCompetitor = eLoopPlayer; iPrimeVotes = iVotes; } } } } } // Not going for diplo victory - we should primarily use voting history score else { int iPrimeScore = 0; veVotingHistoryScores.StableSortItems(); for (int iVotingScoreIndex = veVotingHistoryScores.size() - 1; iVotingScoreIndex >= 0; iVotingScoreIndex--) { PlayerTypes eLoopPlayer = (PlayerTypes) veVotingHistoryScores.GetElement(iVotingScoreIndex); int iScore = GetVotingHistoryScore(eLoopPlayer); // Is this guy's score at or below the bad voting history threshold? if (iScore <= iBadVotingHistoryThreshold) { // No one yet? This is our guy. if (ePrimeLeagueCompetitor == NO_PLAYER) { ePrimeLeagueCompetitor = eLoopPlayer; iPrimeScore = iScore; continue; } // If the existing prime competitor isn't at the bad voting history threshold, they're not our prime competitor anymore. else if (iPrimeScore > iBadVotingHistoryThreshold) { ePrimeLeagueCompetitor = eLoopPlayer; iPrimeScore = iScore; continue; } // Within a certain threshold (less than 200 by default), voting history scores are considered equal-ish...if above that, prime competitor remains in the lead. if (iPrimeScore <= (iScore + iBadVotingHistoryThreshold)) { // Resolve ties using highest vote count int iPrimeVotes = pLeague->CalculateStartingVotesForMember(ePrimeLeagueCompetitor); int iLoopVotes = pLeague->CalculateStartingVotesForMember(eLoopPlayer); if (iLoopVotes > iPrimeVotes) { ePrimeLeagueCompetitor = eLoopPlayer; iPrimeScore = iScore; } // Resolve ties using league alignment else if (iLoopVotes == iPrimeVotes) { CvLeagueAI::AlignmentLevels eAlignment = GetPlayer()->GetLeagueAI()->EvaluateAlignment(eLoopPlayer, true); CvLeagueAI::AlignmentLevels ePrimeAlignment = GetPlayer()->GetLeagueAI()->EvaluateAlignment(ePrimeLeagueCompetitor, true); if (eAlignment < ePrimeAlignment) { ePrimeLeagueCompetitor = eLoopPlayer; iPrimeScore = iScore; } // Another tie? Resolve it using opinion weight. else if (eAlignment == ePrimeAlignment) { if (GetCachedOpinionWeight(eLoopPlayer) > GetCachedOpinionWeight(ePrimeLeagueCompetitor)) { ePrimeLeagueCompetitor = eLoopPlayer; iPrimeScore = iScore; } } } } } else { // Not above the bad voting history threshold - but do they have a bad alignment? CvLeagueAI::AlignmentLevels eAlignment = GetPlayer()->GetLeagueAI()->EvaluateAlignment(eLoopPlayer, true); if (eAlignment < CvLeagueAI::ALIGNMENT_NEUTRAL) { // No one yet? This is our guy. if (ePrimeLeagueCompetitor == NO_PLAYER) { ePrimeLeagueCompetitor = eLoopPlayer; iPrimeScore = iScore; continue; } // Existing competitor is below the threshold - skip this guy. else if (iPrimeScore <= iBadVotingHistoryThreshold) { continue; } // This guy has not been sabotaging us with his votes enough to be at or below the threshold, so let's sort by league alignment first. CvLeagueAI::AlignmentLevels ePrimeAlignment = GetPlayer()->GetLeagueAI()->EvaluateAlignment(ePrimeLeagueCompetitor, true); if (eAlignment < ePrimeAlignment) { ePrimeLeagueCompetitor = eLoopPlayer; iPrimeScore = iScore; } // If there is a tie, however, then we'll use vote count. else if (eAlignment == ePrimeAlignment) { int iPrimeVotes = pLeague->CalculateStartingVotesForMember(ePrimeLeagueCompetitor); int iLoopVotes = pLeague->CalculateStartingVotesForMember(eLoopPlayer); if (iLoopVotes > iPrimeVotes) { ePrimeLeagueCompetitor = eLoopPlayer; iPrimeScore = iScore; } // Another tie? Resolve it using opinion weight. else if (iLoopVotes == iPrimeVotes) { if (GetCachedOpinionWeight(eLoopPlayer) > GetCachedOpinionWeight(ePrimeLeagueCompetitor)) { ePrimeLeagueCompetitor = eLoopPlayer; iPrimeScore = iScore; } } } } } } } } SetPrimeLeagueCompetitor(ePrimeLeagueCompetitor); // STEP 2: Who is our biggest competitor? PlayerTypes eBiggestCompetitor = NO_PLAYER; int iCompetitorPriority = 0; vector vPotentialCompetitors; for (std::vector::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); ++it) { if (WasResurrectedBy(*it)) continue; if (IsMajorCompetitor(*it) || IsEarlyGameCompetitor(*it)) { vPotentialCompetitors.push_back(*it); } else if (GetCivStrategicApproach(*it) < CIV_APPROACH_FRIENDLY) { vPotentialCompetitors.push_back(*it); } else if (IsVassal(*it) && GetVassalTreatmentLevel(*it) > VASSAL_TREATMENT_DISAGREE) { vPotentialCompetitors.push_back(*it); } } // First let's examine any conditions which make some players the greatest competitors. for (std::vector::iterator it = vPotentialCompetitors.begin(); it != vPotentialCompetitors.end(); ++it) { PlayerTypes eLoopPlayer = *it; int iPriority = 0; bool bIgnoreCityConquests = false; if (IsVassal(eLoopPlayer) && !IsVoluntaryVassalage(eLoopPlayer)) { bIgnoreCityConquests = true; } // They captured our capital, currently? Biggest competitor! if (IsCapitalCapturedBy(eLoopPlayer, true, false) && !bIgnoreCityConquests) { eBiggestCompetitor = eLoopPlayer; iCompetitorPriority = 1; } // They're close to conquering the whole planet? else if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsCloseToWorldConquest()) { iPriority = 2; if ((iCompetitorPriority == 0) || (iPriority < iCompetitorPriority)) { eBiggestCompetitor = eLoopPlayer; iCompetitorPriority = 2; } } // We're mad because they're close to winning? else if (IsEndgameAggressiveTo(eLoopPlayer) && GetCivStrategicApproach(eLoopPlayer) < CIV_APPROACH_NEUTRAL) { iPriority = 3; if ((iCompetitorPriority == 0) || (iPriority < iCompetitorPriority)) { eBiggestCompetitor = eLoopPlayer; iCompetitorPriority = 3; } else if (iPriority == iCompetitorPriority) { // Okay, so we're endgame aggressive to two people... // We hate the people who are going for the same victory condition as us more. if (GetVictoryDisputeLevel(eLoopPlayer) > GetVictoryDisputeLevel(eBiggestCompetitor)) { eBiggestCompetitor = eLoopPlayer; } // Same victory dispute level? Then we hate whichever one is closer to us. else if (GetVictoryDisputeLevel(eLoopPlayer) == GetVictoryDisputeLevel(eBiggestCompetitor)) { if (GetPlayer()->GetProximityToPlayer(eLoopPlayer) > GetPlayer()->GetProximityToPlayer(eBiggestCompetitor)) { eBiggestCompetitor = eLoopPlayer; } // Same proximity? Then we hate whichever one has the worst opinion. else if (GetPlayer()->GetProximityToPlayer(eLoopPlayer) == GetPlayer()->GetProximityToPlayer(eBiggestCompetitor)) { if (GetCachedOpinionWeight(eLoopPlayer) > GetCachedOpinionWeight(eBiggestCompetitor)) { eBiggestCompetitor = eLoopPlayer; } } } } } // We're mad because they captured our Holy City, currently? else if (IsHolyCityCapturedBy(eLoopPlayer, true, false) && !bIgnoreCityConquests) { iPriority = 4; if ((iCompetitorPriority == 0) || (iPriority < iCompetitorPriority)) { eBiggestCompetitor = eLoopPlayer; iCompetitorPriority = 4; } } // We're mad because they captured our capital, previously? else if (IsCapitalCapturedBy(eLoopPlayer) && !bIgnoreCityConquests) { iPriority = 5; if ((iCompetitorPriority == 0) || (iPriority < iCompetitorPriority)) { eBiggestCompetitor = eLoopPlayer; iCompetitorPriority = 5; } // Multiple players on the same team can have this penalty else if (iPriority == iCompetitorPriority) { // Pick whoever actually did the deed if (IsPlayerCapturedCapital(eLoopPlayer) && !IsPlayerCapturedCapital(eBiggestCompetitor)) { eBiggestCompetitor = eLoopPlayer; } // They both did at different times? Go with whoever is the closest; failing that, whoever we hate the most else { if (GetPlayer()->GetProximityToPlayer(eLoopPlayer) > GetPlayer()->GetProximityToPlayer(eBiggestCompetitor)) { eBiggestCompetitor = eLoopPlayer; } else if (GetPlayer()->GetProximityToPlayer(eLoopPlayer) == GetPlayer()->GetProximityToPlayer(eBiggestCompetitor)) { if (GetCachedOpinionWeight(eLoopPlayer) > GetCachedOpinionWeight(eBiggestCompetitor)) { eBiggestCompetitor = eLoopPlayer; } } } } } // We're mad because they captured our Holy City, previously? else if (IsHolyCityCapturedBy(eLoopPlayer) && !bIgnoreCityConquests) { iPriority = 6; if ((iCompetitorPriority == 0) || (iPriority < iCompetitorPriority)) { eBiggestCompetitor = eLoopPlayer; iCompetitorPriority = 6; } // Multiple players on the same team can have this penalty else if (iPriority == iCompetitorPriority) { // Pick whoever actually did the deed if (IsPlayerCapturedHolyCity(eLoopPlayer) && !IsPlayerCapturedHolyCity(eBiggestCompetitor)) { eBiggestCompetitor = eLoopPlayer; } // They both did at different times? Go with whoever is the closest; failing that, whoever we hate the most else { if (GetPlayer()->GetProximityToPlayer(eLoopPlayer) > GetPlayer()->GetProximityToPlayer(eBiggestCompetitor)) { eBiggestCompetitor = eLoopPlayer; } else if (GetPlayer()->GetProximityToPlayer(eLoopPlayer) == GetPlayer()->GetProximityToPlayer(eBiggestCompetitor)) { if (GetCachedOpinionWeight(eLoopPlayer) > GetCachedOpinionWeight(eBiggestCompetitor)) { eBiggestCompetitor = eLoopPlayer; } } } } } // They're our master and they're treating us poorly? else if (IsVassal(eLoopPlayer) && GetVassalTreatmentLevel(eLoopPlayer) > VASSAL_TREATMENT_DISAGREE) { iPriority = 7; if (iCompetitorPriority == 0) { eBiggestCompetitor = eLoopPlayer; iCompetitorPriority = 7; } // Multiple masters? Which one is treating us worse? else if (iPriority == iCompetitorPriority) { if (GetVassalTreatmentLevel(eLoopPlayer) > GetVassalTreatmentLevel(eBiggestCompetitor)) { eBiggestCompetitor = eLoopPlayer; } // Tied? Go with whoever we hate the most. else if (GetVassalTreatmentLevel(eLoopPlayer) == GetVassalTreatmentLevel(eBiggestCompetitor)) { if (GetCachedOpinionWeight(eLoopPlayer) > GetCachedOpinionWeight(eBiggestCompetitor)) { eBiggestCompetitor = eLoopPlayer; } } } } } // Haven't found a biggest competitor yet? Now we start sorting by approach. if (eBiggestCompetitor == NO_PLAYER) { vector vWarExclusions; vector vHostileExclusions; vector vDeceptiveExclusions; vector vGuardedExclusions; vector vAfraidExclusions; PlayerTypes eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_WAR, vWarExclusions); while (eCandidate != NO_PLAYER && eBiggestCompetitor == NO_PLAYER) { if (GetCivStrategicApproach(eCandidate) < CIV_APPROACH_FRIENDLY && !WasResurrectedBy(eCandidate)) { eBiggestCompetitor = eCandidate; } else { vWarExclusions.push_back(eCandidate); eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_WAR, vWarExclusions); } } eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_HOSTILE, vHostileExclusions); while (eCandidate != NO_PLAYER && eBiggestCompetitor == NO_PLAYER) { if (GetCivStrategicApproach(eCandidate) < CIV_APPROACH_FRIENDLY && !WasResurrectedBy(eCandidate)) { eBiggestCompetitor = eCandidate; } else { vHostileExclusions.push_back(eCandidate); eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_HOSTILE, vHostileExclusions); } } eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_DECEPTIVE, vDeceptiveExclusions); while (eCandidate != NO_PLAYER && eBiggestCompetitor == NO_PLAYER) { if (GetCivStrategicApproach(eCandidate) < CIV_APPROACH_FRIENDLY && !WasResurrectedBy(eCandidate)) { eBiggestCompetitor = eCandidate; } else { vDeceptiveExclusions.push_back(eCandidate); eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_DECEPTIVE, vDeceptiveExclusions); } } eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_GUARDED, vGuardedExclusions); while (eCandidate != NO_PLAYER && eBiggestCompetitor == NO_PLAYER) { if (GetCivStrategicApproach(eCandidate) < CIV_APPROACH_FRIENDLY && !WasResurrectedBy(eCandidate)) { eBiggestCompetitor = eCandidate; } else { vGuardedExclusions.push_back(eCandidate); eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_GUARDED, vGuardedExclusions); } } eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_AFRAID, vAfraidExclusions); while (eCandidate != NO_PLAYER && eBiggestCompetitor == NO_PLAYER) { if (GetCivStrategicApproach(eCandidate) < CIV_APPROACH_FRIENDLY && !WasResurrectedBy(eCandidate)) { eBiggestCompetitor = eCandidate; } else { vAfraidExclusions.push_back(eCandidate); eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_AFRAID, vAfraidExclusions); } } } SetBiggestCompetitor(eBiggestCompetitor); // STEP 3: Select our friends and DPs...this is handled in DoUpdatePlanningExchanges(), so we'll call that now DoUpdatePlanningExchanges(); // STEP 4: Select our strategic trade partners bool bCloseToConquest = IsCloseToWorldConquest(); bool bGoingForConquest = IsCompetingForVictory() && IsGoingForWorldConquest(); PolicyBranchTypes eIndustry = (PolicyBranchTypes)GC.getInfoTypeForString("POLICY_BRANCH_COMMERCE", true); bool bTradeBonus = GetPlayer()->GetPlayerPolicies()->IsPolicyBranchUnlocked(eIndustry); // Certain unique abilities give trade bonuses bTradeBonus |= GetPlayer()->GetPlayerTraits()->IsImportsCountTowardsMonopolies(); bTradeBonus |= GetPlayer()->GetPlayerTraits()->GetNumTradeRoutesModifier() > 0; bTradeBonus |= GetPlayer()->GetPlayerTraits()->GetTradeRouteResourceModifier() > 0; if (!bTradeBonus) { for (int i = 0; i < NUM_YIELD_TYPES; i++) { YieldTypes e = static_cast(i); if (GetPlayer()->GetPlayerTraits()->GetYieldChangeIncomingTradeRoute(e) > 0 || GetPlayer()->GetPlayerTraits()->GetTradeRouteEndYieldInternational(e) > 0) { bTradeBonus = true; break; } } } for (std::vector::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); ++it) { PlayerTypes eLoopPlayer = *it; if (IsAtWar(eLoopPlayer) || GetCivApproach(eLoopPlayer) == CIV_APPROACH_WAR) { SetStrategicTradePartner(eLoopPlayer, false); continue; } if (GetDemandTargetPlayer() == eLoopPlayer) { SetStrategicTradePartner(eLoopPlayer, false); continue; } if (GetGlobalCoopWarAgainstState(eLoopPlayer) >= COOP_WAR_STATE_PREPARING) { SetStrategicTradePartner(eLoopPlayer, false); continue; } if (GetCivOpinion(eLoopPlayer) <= CIV_OPINION_ENEMY) { SetStrategicTradePartner(eLoopPlayer, false); continue; } if (GetWarmongerThreat(eLoopPlayer) >= THREAT_SEVERE) { SetStrategicTradePartner(eLoopPlayer, false); continue; } if (IsUntrustworthy(eLoopPlayer)) { SetStrategicTradePartner(eLoopPlayer, false); continue; } // Sanctioned? CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (pLeague != NULL && pLeague->IsTradeEmbargoed(GetID(), eLoopPlayer)) { SetStrategicTradePartner(eLoopPlayer, false); continue; } // Resurrector? if (WasResurrectedBy(eLoopPlayer)) { SetStrategicTradePartner(eLoopPlayer, true); continue; } // Our master? if (IsVassal(eLoopPlayer)) { if (GetVassalTreatmentLevel(eLoopPlayer) <= VASSAL_TREATMENT_DISAGREE) SetStrategicTradePartner(eLoopPlayer, true); else SetStrategicTradePartner(eLoopPlayer, false); continue; } if (GetVictoryDisputeLevel(eLoopPlayer) == DISPUTE_LEVEL_FIERCE) { SetStrategicTradePartner(eLoopPlayer, false); continue; } if (GetVictoryBlockLevel(eLoopPlayer) == BLOCK_LEVEL_FIERCE) { SetStrategicTradePartner(eLoopPlayer, false); continue; } if (IsEndgameAggressiveTo(eLoopPlayer)) { SetStrategicTradePartner(eLoopPlayer, false); continue; } // Vassals are usually strategic trade partners. if (IsMaster(eLoopPlayer)) { SetStrategicTradePartner(eLoopPlayer, true); continue; } if (GetNumTimesTheyPlottedAgainstUs(eLoopPlayer) > 0) { SetStrategicTradePartner(eLoopPlayer, false); continue; } // Not our biggest competitors! if (eBiggestCompetitor == eLoopPlayer || ePrimeLeagueCompetitor == eLoopPlayer) { SetStrategicTradePartner(eLoopPlayer, false); continue; } // Are we going for conquest? Check land dispute. if ((bGoingForConquest && !GetPlayer()->IsVassalOfSomeone()) || bCloseToConquest) { if (GetLandDisputeLevel(eLoopPlayer) > DISPUTE_LEVEL_WEAK) { SetStrategicTradePartner(eLoopPlayer, false); continue; } } // Are we going for diplo victory? Check minor civ dispute. if (bGoingForDiploVictory || bCloseToDiploVictory) { if (bCloseToDiploVictory) { if (IsMinorCivTroublemaker(eLoopPlayer, true)) { SetStrategicTradePartner(eLoopPlayer, false); continue; } } else { if (IsMinorCivTroublemaker(eLoopPlayer, false)) { SetStrategicTradePartner(eLoopPlayer, false); continue; } } } // Our most valuable friend and ally make good trade partners! if (GetMostValuableFriend() == eLoopPlayer || GetMostValuableAlly() == eLoopPlayer) { SetStrategicTradePartner(eLoopPlayer, true); continue; } // Liberator? if (IsLiberator(eLoopPlayer, false, false)) { SetStrategicTradePartner(eLoopPlayer, true); continue; } // If we're a vassal, pretty much everyone is a good trade partner if (GetPlayer()->IsVassalOfSomeone()) { SetStrategicTradePartner(eLoopPlayer, true); continue; } // If we have a bonus to trade and this isn't a major competitor, bump up trade value. if (bTradeBonus && !IsMajorCompetitor(eLoopPlayer)) { SetStrategicTradePartner(eLoopPlayer, true); continue; } // Must have at least equal economic strength if (GetEconomicStrengthComparedToUs(eLoopPlayer) < STRENGTH_AVERAGE) { SetStrategicTradePartner(eLoopPlayer, false); continue; } // If we're going for conquest or diplo victory, bump up trade value. if (bGoingForConquest || bCloseToConquest || bGoingForDiploVictory || bCloseToDiploVictory) { SetStrategicTradePartner(eLoopPlayer, true); continue; } SetStrategicTradePartner(eLoopPlayer, false); } } /// Updates our desire to make Declarations of Friendship, Defensive Pacts and Research Agreements with all players void CvDiplomacyAI::DoUpdatePlanningExchanges() { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR)) return; vector vValidPlayers; int iDefensePactLimit = GetPlayer()->CalculateDefensivePactLimit(); bool bCancelDPs = GetPlayer()->IsAITeammateOfHuman() || iDefensePactLimit <= 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer)) { vValidPlayers.push_back(eLoopPlayer); if (bCancelDPs) { SetWantsDefensivePactWithPlayer(eLoopPlayer, false); if (IsHasDefensivePact(eLoopPlayer) && GET_PLAYER(eLoopPlayer).isAlive()) { SetWantsToEndDefensivePactWithPlayer(eLoopPlayer, true); } else { SetWantsToEndDefensivePactWithPlayer(eLoopPlayer, false); } } } else { SetWantsDoFWithPlayer(eLoopPlayer, false); SetWantsDefensivePactWithPlayer(eLoopPlayer, false); SetWantsResearchAgreementWithPlayer(eLoopPlayer, false); if (IsDoFAccepted(eLoopPlayer) && GET_PLAYER(eLoopPlayer).isAlive()) { SetWantsToEndDoFWithPlayer(eLoopPlayer, true); } else { SetWantsToEndDoFWithPlayer(eLoopPlayer, false); } if (IsHasDefensivePact(eLoopPlayer) && GET_PLAYER(eLoopPlayer).isAlive()) { SetWantsToEndDefensivePactWithPlayer(eLoopPlayer, true); } else { SetWantsToEndDefensivePactWithPlayer(eLoopPlayer, false); } } } vector vAcceptableFriends; for (std::vector::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); ++it) { if (IsAtWar(*it)) continue; // Skip over our master and all vassals if (IsVassal(*it) || GET_PLAYER(*it).IsVassalOfSomeone()) continue; // We're unlikely to befriend our biggest competitors if (GetBiggestCompetitor() == *it || GetPrimeLeagueCompetitor() == *it) { CivOpinionTypes eOpinion = GetCivOpinion(*it); CivApproachTypes eApproach = GetCivApproach(*it); DoFLevelTypes eDoFLevel = GetDoFType(*it); bool bLiberator = IsLiberator(*it, false, false) && eOpinion > CIV_OPINION_ENEMY; // If we've worked together before and we're on fairly good terms, let's at least consider it... if ((eDoFLevel < DOF_TYPE_FRIENDS && eOpinion < CIV_OPINION_FRIEND) || (eDoFLevel >= DOF_TYPE_FRIENDS && eOpinion < CIV_OPINION_NEUTRAL)) { if (!bLiberator) continue; } // Don't befriend our prime league competitor unless we really like them. if (GetPrimeLeagueCompetitor() == *it && !bLiberator) { if (IsCompetingForVictory() && IsGoingForDiploVictory() && eOpinion < CIV_OPINION_ALLY) { continue; } else if (eOpinion < CIV_OPINION_FRIEND) { continue; } } // Global politics makes this a bad decision if (IsPlayerDenouncedFriend(*it) || IsPlayerDoFWithAnyEnemy(*it) || IsPlayerDPWithAnyEnemy(*it) || GetDenouncedByOurFriendScore(*it) > 0) { continue; } // Too much aggression in this relationship... if ((eApproach <= CIV_APPROACH_GUARDED && eApproach != CIV_APPROACH_DECEPTIVE) || GetVictoryDisputeLevel(*it) >= DISPUTE_LEVEL_STRONG || GetVictoryBlockLevel(*it) == BLOCK_LEVEL_FIERCE || GetWarmongerThreat(*it) >= THREAT_SEVERE || IsEndgameAggressiveTo(*it)) { continue; } // Being nasty to us if (GetNumTimesTheyPlottedAgainstUs(*it) > 0 || GetNumTimesPerformedCoupAgainstUs(*it) > 0 || GetCoopWarAgreementScore(*it) < -1 || GetRecentAssistValue(*it) > 75 || GetNumTimesCultureBombed(*it) > 0) { continue; } // Broken/ignored promises if (GetExpansionPromiseState(*it) >= PROMISE_STATE_IGNORED || GetBorderPromiseState(*it) >= PROMISE_STATE_IGNORED || GetSpyPromiseState(*it) >= PROMISE_STATE_IGNORED || GetBullyCityStatePromiseState(*it) >= PROMISE_STATE_IGNORED) { continue; } if (GetAttackCityStatePromiseState(*it) >= PROMISE_STATE_IGNORED || GetNoConvertPromiseState(*it) >= PROMISE_STATE_IGNORED || GetNoDiggingPromiseState(*it) >= PROMISE_STATE_IGNORED) { continue; } // Tried to sanction us if (HasEverSanctionedUs(*it) || HasTriedToSanctionUs(*it)) { continue; } } // This checks if they're otherwise okay to befriend if (IsGoodChoiceForDoF(*it)) { vAcceptableFriends.push_back(*it); } } for (std::vector::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); ++it) { // Not if we broke off a DoF with them recently if (GET_PLAYER(*it).GetDiplomacyAI()->IsDoFBroken(GetID()) && GET_PLAYER(*it).GetDiplomacyAI()->GetTurnsSinceDoFBroken(GetID()) < 30) { SetWantsDoFWithPlayer(*it, false); SetWantsToEndDoFWithPlayer(*it, false); continue; } // They're our master or someone's vassal? Okay to befriend if approach is AFRAID, FRIENDLY or NEUTRAL if (IsVassal(*it) || GET_PLAYER(*it).IsVassalOfSomeone()) { if (GetCivStrategicApproach(*it) >= CIV_APPROACH_AFRAID) { SetWantsDoFWithPlayer(*it, true); SetWantsToEndDoFWithPlayer(*it, false); } else { if (IsDoFAccepted(*it) && IsEndDoFAcceptable(*it)) { SetWantsToEndDoFWithPlayer(*it, true); } else { SetWantsToEndDoFWithPlayer(*it, false); } SetWantsDoFWithPlayer(*it, false); } continue; } // Currently friends? if (IsDoFAccepted(*it)) { // Uh oh! Do we want to end this friendship right away or let it expire? if (std::find(vAcceptableFriends.begin(), vAcceptableFriends.end(), *it) == vAcceptableFriends.end()) { if (IsEndDoFAcceptable(*it)) { SetWantsToEndDoFWithPlayer(*it, true); } else { SetWantsToEndDoFWithPlayer(*it, false); } SetWantsDoFWithPlayer(*it, false); } else { SetWantsDoFWithPlayer(*it, true); SetWantsToEndDoFWithPlayer(*it, false); } } // Reset DoF desire else { SetWantsDoFWithPlayer(*it, false); SetWantsToEndDoFWithPlayer(*it, false); } } PlayerTypes eMostValuableFriend = NO_PLAYER; int iFriends = GetNumDoF(true); int iBaseFriendLimit = 2; if (GetPlayer()->GetDoFToVotes() > 0) { iBaseFriendLimit = MAX_MAJOR_CIVS; } else if (GetDoFWillingness() > 8) { iBaseFriendLimit = 4; } else if (GetDoFWillingness() > 6) { iBaseFriendLimit = 3; } // Scale friend limit with map size int iFriendLimit = iBaseFriendLimit * vValidPlayers.size() / 8; iFriendLimit = max(iBaseFriendLimit, iFriendLimit); // Now examine all acceptable friends and decide who we want to befriend. vector vFriendlyExclusions; vector vNeutralExclusions; vector vAfraidExclusions; vector vDeceptiveExclusions; bool bLimitReached = false; bool bBestFriendPossible = vAcceptableFriends.size() > 0 && vValidPlayers.size() > 1; PlayerTypes eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_FRIENDLY, vFriendlyExclusions); while (eCandidate != NO_PLAYER) { if (std::find(vAcceptableFriends.begin(), vAcceptableFriends.end(), eCandidate) != vAcceptableFriends.end()) { if (eMostValuableFriend == NO_PLAYER && bBestFriendPossible) { eMostValuableFriend = eCandidate; } if (!IsDoFAccepted(eCandidate)) { if (iFriends < iFriendLimit) { SetWantsDoFWithPlayer(eCandidate, true); iFriends++; if (iFriends >= iFriendLimit && (eMostValuableFriend != NO_PLAYER || !bBestFriendPossible)) { bLimitReached = true; break; } } } } vFriendlyExclusions.push_back(eCandidate); eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_FRIENDLY, vFriendlyExclusions); } eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_NEUTRAL, vNeutralExclusions); while (eCandidate != NO_PLAYER && !bLimitReached) { if (std::find(vAcceptableFriends.begin(), vAcceptableFriends.end(), eCandidate) != vAcceptableFriends.end()) { if (eMostValuableFriend == NO_PLAYER && bBestFriendPossible) { eMostValuableFriend = eCandidate; } if (!IsDoFAccepted(eCandidate)) { if (iFriends < iFriendLimit) { SetWantsDoFWithPlayer(eCandidate, true); iFriends++; if (iFriends >= iFriendLimit && (eMostValuableFriend != NO_PLAYER || !bBestFriendPossible)) { bLimitReached = true; break; } } } } vNeutralExclusions.push_back(eCandidate); eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_NEUTRAL, vNeutralExclusions); } eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_AFRAID, vAfraidExclusions); while (eCandidate != NO_PLAYER && !bLimitReached) { if (std::find(vAcceptableFriends.begin(), vAcceptableFriends.end(), eCandidate) != vAcceptableFriends.end()) { if (eMostValuableFriend == NO_PLAYER && bBestFriendPossible) { eMostValuableFriend = eCandidate; } if (!IsDoFAccepted(eCandidate)) { if (iFriends < iFriendLimit) { SetWantsDoFWithPlayer(eCandidate, true); iFriends++; if (iFriends >= iFriendLimit && (eMostValuableFriend != NO_PLAYER || !bBestFriendPossible)) { bLimitReached = true; break; } } } } vAfraidExclusions.push_back(eCandidate); eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_AFRAID, vAfraidExclusions); } eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_DECEPTIVE, vDeceptiveExclusions); while (eCandidate != NO_PLAYER && !bLimitReached) { if (std::find(vAcceptableFriends.begin(), vAcceptableFriends.end(), eCandidate) != vAcceptableFriends.end()) { if (!IsDoFAccepted(eCandidate)) { if (iFriends < iFriendLimit) { SetWantsDoFWithPlayer(eCandidate, true); iFriends++; if (iFriends >= iFriendLimit) { bLimitReached = true; break; } } } } vDeceptiveExclusions.push_back(eCandidate); eCandidate = GetPlayerWithHighestStrategicApproachValue(CIV_APPROACH_DECEPTIVE, vDeceptiveExclusions); } SetMostValuableFriend(eMostValuableFriend); // Now let's examine who we want to form Defensive Pacts with! if (GET_TEAM(GetTeam()).isDefensivePactTradingAllowed() && !bCancelDPs) { vector vAcceptableDefensePacts; bool bCoastal = GetPlayer()->GetNumEffectiveCoastalCities() > 1; // If someone on our team was resurrected, must agree to a Defensive Pact with them & not allowed to make Defensive Pacts with others TeamTypes eLiberatedByTeam = GET_TEAM(GetTeam()).GetLiberatedByTeam(); if (eLiberatedByTeam != NO_TEAM && GET_TEAM(eLiberatedByTeam).isAlive() && GET_TEAM(eLiberatedByTeam).getNumCities() > 0 && GET_TEAM(eLiberatedByTeam).isDefensivePactTradingAllowed() && GET_TEAM(eLiberatedByTeam).getAliveCount() <= iDefensePactLimit) { // Edge case fix for the liberator dying and then being resurrected, being vassalized, etc. for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eLoopPlayer).getTeam() != eLiberatedByTeam) { if (IsHasDefensivePact(eLoopPlayer)) SetWantsToEndDefensivePactWithPlayer(eLoopPlayer, true); else SetWantsToEndDefensivePactWithPlayer(eLoopPlayer, false); SetWantsDefensivePactWithPlayer(eLoopPlayer, false); } } vector vLiberatedTeamPlayers = GET_TEAM(eLiberatedByTeam).getPlayers(); for (size_t i=0; i::iterator it = vAcceptableDefensePacts.begin(); it != vAcceptableDefensePacts.end(); ++it) { SetWantsDefensivePactWithPlayer(*it, true); SetWantsToEndDefensivePactWithPlayer(*it, false); int iScore = ScoreDefensivePactChoice(*it, bCoastal); if (iScore > iHighestScore) { iHighestScore = iScore; eMostValuableAlly = *it; } } SetMostValuableAlly(eMostValuableAlly); } // Normal selection procedure else { // If we're trying to win World Congress votes, use a less stringent evaluation if (GetPlayer()->GetDefensePactsToVotes() > 0) { for (std::vector::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); ++it) { if (GET_TEAM(GET_PLAYER(*it).getTeam()).getAliveCount() > iDefensePactLimit) continue; if (IsGoodChoiceForDefensivePact(*it)) { vAcceptableDefensePacts.push_back(*it); } } } // Normally, only consider acceptable friend choices for Defensive Pacts // And we must also have a Declaration of Friendship already, that we aren't planning on ending else { for (std::vector::iterator it = vAcceptableFriends.begin(); it != vAcceptableFriends.end(); ++it) { if (!IsDoFAccepted(*it)) continue; if (!IsWantsDoFWithPlayer(*it)) continue; if (GET_TEAM(GET_PLAYER(*it).getTeam()).getAliveCount() > iDefensePactLimit) continue; if (IsWantsToEndDoFWithPlayer(*it)) continue; if (IsGoodChoiceForDefensivePact(*it)) { vAcceptableDefensePacts.push_back(*it); } } } // First we see if there's any Defensive Pacts we want to end! for (std::vector::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); ++it) { // Do we currently have a DP? if (IsHasDefensivePact(*it)) { // Uh oh! if (std::find(vAcceptableDefensePacts.begin(), vAcceptableDefensePacts.end(), *it) == vAcceptableDefensePacts.end()) { SetWantsDefensivePactWithPlayer(*it, false); SetWantsToEndDefensivePactWithPlayer(*it, true); } else { SetWantsDefensivePactWithPlayer(*it, true); SetWantsToEndDefensivePactWithPlayer(*it, false); } } // Reset DP desire else { SetWantsDefensivePactWithPlayer(*it, false); SetWantsToEndDefensivePactWithPlayer(*it, false); } } int iNumDefensePacts = GetNumDefensePacts(); int iBestFriendScore = 0; // because the most valuable friend gets a special exemption to scoring, we need this value to track their score and determine if they're also the best ally PlayerTypes eMostValuableAlly = NO_PLAYER; // If we can make a Defensive Pact with our most valuable friend and they're a good choice, always do so. if (eMostValuableFriend != NO_PLAYER && IsDoFAccepted(eMostValuableFriend)) { if (std::find(vAcceptableDefensePacts.begin(), vAcceptableDefensePacts.end(), eMostValuableFriend) != vAcceptableDefensePacts.end()) { vector vTheirTeam = GET_TEAM(GET_PLAYER(eMostValuableFriend).getTeam()).getPlayers(); // Let's make a Defensive Pact! if (!IsHasDefensivePact(eMostValuableFriend)) { if (iNumDefensePacts < iDefensePactLimit) { for (size_t i=0; i iBestFriendScore) { iBestFriendScore = iScore; eMostValuableAlly = (PlayerTypes)vTheirTeam[i]; } } } } // Still need to score them... else { for (size_t i=0; i iBestFriendScore) { iBestFriendScore = iScore; eMostValuableAlly = (PlayerTypes)vTheirTeam[i]; } } } } } // Okay, now let's choose the most valuable DPs vector vDPExclusions; PlayerTypes eDPCandidate = GetHighestScoringDefensivePact(vAcceptableDefensePacts, vDPExclusions); while (eDPCandidate != NO_PLAYER) { if ((eMostValuableAlly == NO_PLAYER || iBestFriendScore > 0) && bBestFriendPossible && std::find(vAcceptableFriends.begin(), vAcceptableFriends.end(), eDPCandidate) != vAcceptableFriends.end()) { if (eMostValuableAlly != NO_PLAYER) { // Found a better scoring ally who isn't our most valued friend, so reset this. if (ScoreDefensivePactChoice(eDPCandidate, bCoastal) > iBestFriendScore) { iBestFriendScore = 0; eMostValuableAlly = eDPCandidate; } } else { eMostValuableAlly = eDPCandidate; } } if (!IsHasDefensivePact(eDPCandidate) && !IsWantsDefensivePactWithPlayer(eDPCandidate)) { if ((iNumDefensePacts + GET_TEAM(GET_PLAYER(eDPCandidate).getTeam()).getAliveCount()) <= iDefensePactLimit) { vector vTheirTeam = GET_TEAM(GET_PLAYER(eDPCandidate).getTeam()).getPlayers(); for (size_t i=0; i= iDefensePactLimit && iBestFriendScore == 0 && (eMostValuableAlly != NO_PLAYER || !bBestFriendPossible)) { break; } } } vDPExclusions.push_back(eDPCandidate); eDPCandidate = GetHighestScoringDefensivePact(vAcceptableDefensePacts, vDPExclusions); } SetMostValuableAlly(eMostValuableAlly); } } if (GET_TEAM(GetTeam()).IsResearchAgreementTradingAllowed() && !GetPlayer()->IsAITeammateOfHuman()) { for (std::vector::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); ++it) { if (IsGoodChoiceForResearchAgreement(*it)) { SetWantsResearchAgreementWithPlayer(*it, true); } else { SetWantsResearchAgreementWithPlayer(*it, false); } } } } /// Select our most valuable World Congress ally as of this turn (if any; there may not be one!) void CvDiplomacyAI::DoUpdatePrimeLeagueAlly() { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return; PlayerTypes ePrimeLeagueAlly = NO_PLAYER; CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (!pLeague) { SetPrimeLeagueAlly(NO_PLAYER); return; } int iGoodVotingHistoryThreshold = /*200*/ GD_INT_GET(VOTING_HISTORY_SCORE_MAX) / max(1, GD_INT_GET(VOTING_HISTORY_SCORE_PRIME_COMPETITOR_THRESHOLD)); int iBadVotingHistoryThreshold = iGoodVotingHistoryThreshold * -1; int iMyVotes = pLeague->CalculateStartingVotesForMember(GetID()); int iPrimeVotes = 0; CvLeagueAI::AlignmentLevels ePrimeAlignment = CvLeagueAI::ALIGNMENT_NEUTRAL; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; // Make sure this is a valid player to look at (alive, not planning war, etc.) if (!IsHasMet(ePlayer) || !GET_PLAYER(ePlayer).isAlive() || !GET_PLAYER(ePlayer).isMajorCiv()) continue; if (IsEndgameAggressiveTo(ePlayer) || AvoidExchangesWithPlayer(ePlayer, false) || IsUntrustworthy(ePlayer)) continue; if (IsVassal(ePlayer) && GetVassalTreatmentLevel(ePlayer) > VASSAL_TREATMENT_DISAGREE) continue; // Can't have recently tried to sanction us (or ever succeeded) if (HasEverSanctionedUs(ePlayer) || HasTriedToSanctionUs(ePlayer)) continue; // Must have no penalty for defeating our proposals if (GetSupportedOurProposalValue(ePlayer) > 0) continue; // Must not have strongly or overwhelmingly disliked their most recent proposal if (GetLikedTheirProposalValue(ePlayer) > /*30*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL)) continue; // Can't be below the bad voting history threshold if (GetVotingHistoryScore(ePlayer) <= iBadVotingHistoryThreshold) continue; // Can't have a negative league alignment CvLeagueAI::AlignmentLevels eAlignment = GetPlayer()->GetLeagueAI()->EvaluateAlignment(ePlayer); if (eAlignment < CvLeagueAI::ALIGNMENT_NEUTRAL) continue; int iVotes = pLeague->CalculateStartingVotesForMember(ePlayer); if (IsCompetingForVictory() && !WasResurrectedBy(ePlayer)) { // Can't be too close to a diplo victory! if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsCloseToDiploVictory()) continue; // Can't have more votes than we do if we're going for diplo victory if (IsGoingForDiploVictory() && iVotes > iMyVotes) continue; } // No existing ally? This is our guy! if (ePrimeLeagueAlly == NO_PLAYER) { ePrimeLeagueAlly = ePlayer; iPrimeVotes = iVotes; ePrimeAlignment = eAlignment; continue; } // Do they beat the existing guy by 200 or more? This is our guy! else if (GetVotingHistoryScore(ePlayer) > GetVotingHistoryScore(ePrimeLeagueAlly) && GetVotingHistoryScore(ePlayer) >= (GetVotingHistoryScore(ePrimeLeagueAlly) + iGoodVotingHistoryThreshold)) { ePrimeLeagueAlly = ePlayer; iPrimeVotes = iVotes; ePrimeAlignment = eAlignment; continue; } // Does the existing guy beat them by 200 or more? This is not our guy! else if (GetVotingHistoryScore(ePrimeLeagueAlly) > GetVotingHistoryScore(ePlayer) && GetVotingHistoryScore(ePrimeLeagueAlly) >= (GetVotingHistoryScore(ePlayer) + iGoodVotingHistoryThreshold)) { continue; } // In the event of a tie, we use league alignment if (eAlignment > ePrimeAlignment) { ePrimeLeagueAlly = ePlayer; iPrimeVotes = iVotes; ePrimeAlignment = eAlignment; } // In the event of another tie, sort by most votes else if (eAlignment == ePrimeAlignment) { if (iVotes > iPrimeVotes) { ePrimeLeagueAlly = ePlayer; iPrimeVotes = iVotes; ePrimeAlignment = eAlignment; } // In the event of yet another tie, sort by opinion score else if (iVotes == iPrimeVotes) { if (GetCachedOpinionWeight(ePlayer) < GetCachedOpinionWeight(ePrimeLeagueAlly)) { ePrimeLeagueAlly = ePlayer; iPrimeVotes = iVotes; ePrimeAlignment = eAlignment; } } } } SetPrimeLeagueAlly(ePrimeLeagueAlly); } /// Should we avoid making certain agreements with this player? bool CvDiplomacyAI::AvoidExchangesWithPlayer(PlayerTypes ePlayer, bool bWarOnly, bool bIgnoreSelfApproach) const { if (IsAtWar(ePlayer)) return true; vector vOurTeam = GET_TEAM(GetTeam()).getPlayers(); vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); for (size_t i=0; iGetVisibleApproachTowardsUs(vTheirTeam[j]) == CIV_APPROACH_HOSTILE) return true; // Has there been a denouncement in either direction? if (pDiploAI->IsDenouncedPlayer(vTheirTeam[j]) || pDiploAI->IsDenouncedByPlayer(vTheirTeam[j])) return true; } // Are we planning war? if (pDiploAI->GetGlobalCoopWarAgainstState(vTheirTeam[j]) >= COOP_WAR_STATE_PREPARING) return true; // Bad approach or opinion? if (GET_PLAYER(vOurTeam[i]).isHuman(ISHUMAN_AI_DIPLOMACY) || (bIgnoreSelfApproach && GET_PLAYER(vOurTeam[i]).GetID() == GetID())) continue; if (bWarOnly) { if (pDiploAI->GetCivApproach(vTheirTeam[j]) == CIV_APPROACH_WAR) return true; } else { if (pDiploAI->GetCivApproach(vTheirTeam[j]) <= CIV_APPROACH_HOSTILE) return true; if (pDiploAI->GetSurfaceApproach(vTheirTeam[j]) != CIV_APPROACH_FRIENDLY && pDiploAI->GetCivOpinion(vTheirTeam[j]) <= CIV_OPINION_ENEMY) return true; } } else if (GET_PLAYER(vTheirTeam[j]).isMinorCiv()) { // Planning to conquer this City-State? if (!GET_PLAYER(vOurTeam[i]).isHuman(ISHUMAN_AI_DIPLOMACY)) { if (pDiploAI->GetCSWarTargetPlayer() == vTheirTeam[j]) return true; if (pDiploAI->GetCivApproach(vTheirTeam[j]) == CIV_APPROACH_WAR) return true; } } } } return false; } /// Is this player a good choice for a Declaration of Friendship? bool CvDiplomacyAI::IsGoodChoiceForDoF(PlayerTypes ePlayer) { // At war or planning war? if (AvoidExchangesWithPlayer(ePlayer, true)) return false; // Haven't known this guy for long enough if (IsTooEarlyForDoF(ePlayer)) return false; // Untrustworthy? if (IsUntrustworthy(ePlayer)) return false; // Denouncement? if (IsDenouncedPlayer(ePlayer) || IsDenouncedByPlayer(ePlayer)) return false; // Approach can't be too negative (but deliberately don't check opinion here, to give relations a chance to heal) if (GetCivStrategicApproach(ePlayer) <= CIV_APPROACH_HOSTILE || GetCivApproach(ePlayer) <= CIV_APPROACH_HOSTILE) return false; // If we're willing to end our friendship with them, don't make friends with them! if (IsDenounceAcceptable(ePlayer) || IsEndDoFAcceptable(ePlayer, true) || IsDenounceFriendAcceptable(ePlayer)) return false; // Recent peace treaty? if (GetNumWarsFought(ePlayer) > 0) { int iPeaceTreatyTurn = GET_TEAM(GetTeam()).GetTurnMadePeaceTreatyWithTeam(GET_PLAYER(ePlayer).getTeam()); if (iPeaceTreatyTurn > -1) { int iTurnsSincePeace = GC.getGame().getGameTurn() - iPeaceTreatyTurn; int iPeaceDampenerTurns = GC.getGame().getHandicapInfo().getPeaceTreatyDampenerTurns(); if (iTurnsSincePeace < iPeaceDampenerTurns) return false; } } // It'd be odd to befriend someone after declaring independence recently... if (IsAngryAboutPlayerVassalageForcefullyRevoked(ePlayer)) return false; // Is there a sanction proposal in either direction? if (CvDiplomacyAIHelpers::ProposedSanctionsBlockingDiplomacy(GetID(), ePlayer)) return false; return true; } bool CvDiplomacyAI::IsGoodChoiceForDefensivePact(PlayerTypes ePlayer) { // At war or planning war? if (AvoidExchangesWithPlayer(ePlayer)) return false; // We need tech & embassy to make a DP if (!GET_TEAM(GetTeam()).isDefensivePactTradingAllowedWithTeam(GET_PLAYER(ePlayer).getTeam())) return false; if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isDefensivePactTradingAllowedWithTeam(GetTeam())) return false; // Humans on our team? if (GetPlayer()->IsAITeammateOfHuman()) return false; // Sanctioned? CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (pLeague && pLeague->IsTradeEmbargoed(GetID(), ePlayer)) return false; // Did we just meet them? Let's not make a DP quite yet. if (IsTooEarlyForDoF(ePlayer) && !IsDoFAccepted(ePlayer) && GetPlayer()->GetDefensePactsToVotes() <= 0) return false; //No DPs if last two! if (GC.getGame().GetNumMajorCivsAlive() <= 2) return false; // Untrustworthy? if (IsUntrustworthy(ePlayer)) return false; // Denouncement? if (IsDenouncedPlayer(ePlayer) || IsDenouncedByPlayer(ePlayer)) return false; // Approach/opinion can't be too negative if (GetCivApproach(ePlayer) <= CIV_APPROACH_DECEPTIVE || GetCivStrategicApproach(ePlayer) <= CIV_APPROACH_DECEPTIVE) return false; if (GetCivOpinion(ePlayer) <= CIV_OPINION_ENEMY) return false; // If we're willing to end our friendship with them, don't make a DP with them! if (IsWantsToEndDoFWithPlayer(ePlayer) || IsDenounceAcceptable(ePlayer) || IsEndDoFAcceptable(ePlayer, true) || IsDenounceFriendAcceptable(ePlayer)) return false; // Recent peace treaty? if (GetNumWarsFought(ePlayer) > 0) { int iPeaceTreatyTurn = GET_TEAM(GetTeam()).GetTurnMadePeaceTreatyWithTeam(GET_PLAYER(ePlayer).getTeam()); if (iPeaceTreatyTurn > -1) { int iTurnsSincePeace = GC.getGame().getGameTurn() - iPeaceTreatyTurn; int iPeaceDampenerTurns = GC.getGame().getHandicapInfo().getPeaceTreatyDampenerTurns(); if (iTurnsSincePeace < iPeaceDampenerTurns) return false; } } // It'd be odd to befriend someone after declaring independence recently... if (IsAngryAboutPlayerVassalageForcefullyRevoked(ePlayer)) return false; // Is there a sanction proposal in either direction? if (CvDiplomacyAIHelpers::ProposedSanctionsBlockingDiplomacy(GetID(), ePlayer)) return false; return true; } bool CvDiplomacyAI::IsGoodChoiceForResearchAgreement(PlayerTypes ePlayer) { if (IsAtWar(ePlayer)) return false; // Need a Declaration of Friendship to make a RA if (!IsDoFAccepted(ePlayer)) return false; // Already have a RA if (IsHasResearchAgreement(ePlayer)) return false; // We need tech & embassy to make a RA if (!GET_TEAM(GetTeam()).IsResearchAgreementTradingAllowedWithTeam(GET_PLAYER(ePlayer).getTeam())) return false; if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).IsResearchAgreementTradingAllowedWithTeam(GetTeam())) return false; // Sanctioned? CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (pLeague != NULL && pLeague->IsTradeEmbargoed(GetID(), ePlayer)) return false; // Humans on our team? if (GetPlayer()->IsAITeammateOfHuman()) return false; // Untrustworthy? if (IsUntrustworthy(ePlayer)) return false; // Denouncement? if (IsDenouncedPlayer(ePlayer) || IsDenouncedByPlayer(ePlayer)) return false; // If we're willing to end our friendship with them, don't make a RA with them! if (IsWantsToEndDoFWithPlayer(ePlayer) || IsEndDoFAcceptable(ePlayer) || IsDenounceFriendAcceptable(ePlayer)) return false; // One of us has already researched all techs if (GET_TEAM(GetTeam()).GetTeamTechs()->HasResearchedAllTechs() || GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetTeamTechs()->HasResearchedAllTechs()) return false; return true; } /// Are we able to make a Research Agreement with ePlayer right now? bool CvDiplomacyAI::IsCanMakeResearchAgreementRightNow(PlayerTypes ePlayer) { // We don't want a RA with this guy if (!IsWantsResearchAgreementWithPlayer(ePlayer)) return false; // Already have a RA? if (IsHasResearchAgreement(ePlayer)) return false; // Either side already has all techs? if (GET_TEAM(GetTeam()).GetTeamTechs()->HasResearchedAllTechs() || GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetTeamTechs()->HasResearchedAllTechs()) return false; // We need tech & embassy to make a RA if (!GET_TEAM(GetTeam()).IsResearchAgreementTradingAllowedWithTeam(GET_PLAYER(ePlayer).getTeam())) return false; if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).IsResearchAgreementTradingAllowedWithTeam(GetTeam())) return false; int iGoldAmount = GC.getGame().GetGameDeals().GetTradeItemGoldCost(TRADE_ITEM_RESEARCH_AGREEMENT, GetID(), ePlayer); // We don't have enough Gold if (GetPlayer()->GetTreasury()->GetGold() < iGoldAmount) return false; // They don't have enough Gold if (GET_PLAYER(ePlayer).GetTreasury()->GetGold() < iGoldAmount) return false; return true; } PlayerTypes CvDiplomacyAI::GetHighestScoringDefensivePact(vector& vAcceptableChoices, vector& vPlayersToExclude) { if (vAcceptableChoices.empty()) return NO_PLAYER; PlayerTypes eBestDP = NO_PLAYER; int iBestDPValue = 0; bool bCoastal = GetPlayer()->GetNumEffectiveCoastalCities() > 1; for (std::vector::iterator it = vAcceptableChoices.begin(); it != vAcceptableChoices.end(); ++it) { if (std::find(vPlayersToExclude.begin(), vPlayersToExclude.end(), *it) != vPlayersToExclude.end()) continue; PlayerTypes eChoice = *it; int iDPValue = ScoreDefensivePactChoice(*it, bCoastal); if (iDPValue > iBestDPValue) { eBestDP = eChoice; iBestDPValue = iDPValue; } } return eBestDP; } int CvDiplomacyAI::ScoreDefensivePactChoice(PlayerTypes eChoice, bool bCoastal) { int iDPValue = GetLoyalty(); switch (GetMilitaryStrengthComparedToUs(eChoice)) { case STRENGTH_PATHETIC: iDPValue += -60; break; case STRENGTH_WEAK: iDPValue += -40; break; case STRENGTH_POOR: iDPValue += -20; break; case STRENGTH_AVERAGE: iDPValue += 10; break; case STRENGTH_STRONG: iDPValue += 20; break; case STRENGTH_POWERFUL: iDPValue += 40; break; case STRENGTH_IMMENSE: iDPValue += 60; break; } switch (GetPlayer()->GetProximityToPlayer(eChoice)) { case PLAYER_PROXIMITY_NEIGHBORS: iDPValue += 5; break; case PLAYER_PROXIMITY_CLOSE: iDPValue += 0; break; case PLAYER_PROXIMITY_FAR: iDPValue -= 20; break; case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: iDPValue -= 50; break; } switch (GetCivOpinion(eChoice)) { case CIV_OPINION_UNFORGIVABLE: iDPValue -= 1000; break; case CIV_OPINION_ENEMY: iDPValue -= 100; break; case CIV_OPINION_COMPETITOR: iDPValue -= 20; break; case CIV_OPINION_NEUTRAL: iDPValue -= 10; break; case CIV_OPINION_FAVORABLE: iDPValue += 5; break; case CIV_OPINION_FRIEND: iDPValue += 10; break; case CIV_OPINION_ALLY: iDPValue += 20; break; } // Same or different continents? if (GET_PLAYER(eChoice).getCapitalCity() && GetPlayer()->getCapitalCity()) { // If we're coastal, consider coastal allies more... if (bCoastal && GET_PLAYER(eChoice).GetNumEffectiveCoastalCities() > 1) { if (GET_PLAYER(eChoice).getCapitalCity()->HasSharedLandmassWith(GetPlayer()->getCapitalCity(), true, false)) { iDPValue += 5; } else { iDPValue -= 10; } } else { if (GET_PLAYER(eChoice).getCapitalCity()->HasSharedLandmassWith(GetPlayer()->getCapitalCity(), true, false)) { iDPValue += 5; } else { iDPValue -= 20; } } } // Have they assisted us in wars? if (GetCoopWarAgreementScore(eChoice) > 0) { iDPValue += GetCoopWarAgreementScore(eChoice) * 20; } else if (GetCoopWarAgreementScore(eChoice) < 0) { iDPValue += GetCoopWarAgreementScore(eChoice) * 10; } iDPValue += GetCommonFoeScore(eChoice) / -2; // Have they liberated us? if (WasResurrectedBy(eChoice)) { iDPValue += 50; } if (IsPlayerLiberatedCapital(eChoice) || IsPlayerLiberatedHolyCity(eChoice)) { iDPValue += 25; } else if (IsPlayerReturnedCapital(eChoice) || IsPlayerReturnedHolyCity(eChoice)) { iDPValue += 10; } else if (IsHappyAboutPlayerVassalagePeacefullyRevoked(eChoice) || IsMasterLiberatedMeFromVassalage(eChoice)) { iDPValue += 5; } int iNumOurCitiesTheyOwn = GetPlayer()->GetNumOurCitiesOwnedBy(eChoice); if (iNumOurCitiesTheyOwn > 0) { iDPValue += iNumOurCitiesTheyOwn * -25; } else if (GetNumCitiesLiberatedBy(eChoice) > 0) { iDPValue += GetNumCitiesLiberatedBy(eChoice) * 25; } return iDPValue; } /// Update which major civs we're targeting for war. NOTE: City-State targets are handled in DoUpdateMinorCivApproaches() and SelectBestApproachTowardsMinorCiv(). void CvDiplomacyAI::DoUpdateWarTargets() { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR) || GC.getGame().isOption(GAMEOPTION_ALWAYS_PEACE) || GC.getGame().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE)) return; vector vMyTeam = GET_TEAM(GetTeam()).getPlayers(); vector vValidPlayers; vector vAtWarPlayers; vector vDirectTargets; vector vNotAtWarPlayers; vector vPlanningWarPlayers; vector vLastResortCandidates; // Which majors are we currently at war with? for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (!IsPlayerValid(eLoopPlayer)) continue; if (IsAlwaysAtWar(eLoopPlayer)) continue; if (!GET_PLAYER(eLoopPlayer).isMajorCiv()) continue; vValidPlayers.push_back(eLoopPlayer); if (IsAtWar(eLoopPlayer)) { vAtWarPlayers.push_back(eLoopPlayer); vDirectTargets.push_back(eLoopPlayer); } else { vNotAtWarPlayers.push_back(eLoopPlayer); } } // Who are we PLANNING war against? for (std::vector::iterator it = vNotAtWarPlayers.begin(); it != vNotAtWarPlayers.end(); ++it) { vector vTeam = GET_TEAM(GET_PLAYER(*it).getTeam()).getPlayers(); bool bWarPlans = false; for (size_t i=0; iGetGlobalCoopWarAgainstState(vTeam[j]) >= COOP_WAR_STATE_PREPARING) { bWarPlans = true; break; } if (GET_PLAYER(vMyTeam[i]).GetID() == GetID()) { // If we're planning war against our own vassal (!), trust the SelectApproachTowardsVassal() logic and treat this as an irrevocable commitment if (IsMaster(vTeam[j]) && GetCivApproach(vTeam[j]) == CIV_APPROACH_WAR) { bWarPlans = true; break; } continue; } // Also count any wars our AI teammates are planning if (!GET_PLAYER(vMyTeam[i]).isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(vMyTeam[i]).GetDiplomacyAI()->GetCivApproach(vTeam[j]) == CIV_APPROACH_WAR) { bWarPlans = true; break; } } if (bWarPlans) break; } if (bWarPlans) { SetCivApproach(*it, CIV_APPROACH_WAR); vPlanningWarPlayers.push_back(*it); continue; } // Check if we're targeting any vassals of theirs if (GET_PLAYER(*it).GetNumVassals() > 0) { bool bFoundTargetedVassal = false; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer) && GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsVassal(*it)) { if (GetCivApproach(eLoopPlayer) == CIV_APPROACH_WAR) { bFoundTargetedVassal = true; break; } } } if (bFoundTargetedVassal) { vPlanningWarPlayers.push_back(*it); // No need to set WAR approach here, it was just done in SelectApproachTowardsVassal() // We also trust the SelectApproachTowardsVassal() logic for war sanity and just consider war vs. the master to be an irrevocable commitment } } } // Combine all players into one array! Need to do it in this circuitous way to check linked war players, which only go to war if not already at war with the target... for (std::vector::iterator it = vPlanningWarPlayers.begin(); it != vPlanningWarPlayers.end(); ++it) { if (std::find(vAtWarPlayers.begin(), vAtWarPlayers.end(), *it) == vAtWarPlayers.end()) { vAtWarPlayers.push_back(*it); } vDirectTargets.push_back(*it); vector vDefensiveWarAllies = GetDefensiveWarAllies(*it, /*bIncludeMinors*/ false, /*bReverseMode*/ true, /*bNewWarsOnly*/ true); for (std::vector::iterator it2 = vDefensiveWarAllies.begin(); it2 != vDefensiveWarAllies.end(); ++it2) { if (std::find(vAtWarPlayers.begin(), vAtWarPlayers.end(), *it2) == vAtWarPlayers.end()) { vAtWarPlayers.push_back(*it2); } } } // Repopulate vNotAtWarPlayers... vNotAtWarPlayers.clear(); vPlanningWarPlayers.clear(); for (std::vector::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); ++it) { if (std::find(vAtWarPlayers.begin(), vAtWarPlayers.end(), *it) == vAtWarPlayers.end()) { vNotAtWarPlayers.push_back(*it); } } // Finally let's add up our total "conflict score" to see what our situation is at. int iConflictScore = 0; for (std::vector::iterator it = vAtWarPlayers.begin(); it != vAtWarPlayers.end(); ++it) { if (IsAtWar(*it) && IsPhonyWar(*it)) { iConflictScore += GetPlayer()->GetCurrentEra() <= 1 ? 10 : 5; } else if (IsEasyTarget(*it) && GetPlayer()->GetCurrentEra() > 1) { iConflictScore += 5; } else { iConflictScore += 10; } } int iConqueredCivs = GetPlayerNumMajorsConquered(GetID()); bool bCloseToWorldConquest = IsCloseToWorldConquest(); bool bGoingForWorldConquest = IsGoingForWorldConquest(); int iConflictLimit = 10 + (iConqueredCivs * 5); int iPotentialWarLimit = iConflictLimit + 5; // Limit our willingness for conflict in the very early game. if (GetPlayer()->GetCurrentEra() <= 1) { iConflictLimit = 10; iPotentialWarLimit = 10; } // At our conflict limit? Cancel all new war plans! if (iConflictScore >= iConflictLimit) { for (std::vector::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); ++it) { // Must not be in the list of existing wars or war commitments if (std::find(vAtWarPlayers.begin(), vAtWarPlayers.end(), *it) == vAtWarPlayers.end()) { SetCivApproach(*it, GetHighestValueApproach(*it, true, true)); } } } else { // Not at our conflict limit? Let's decide who we want to go to war against (if anyone)! // Loop through all players to find valid options bool bCanCrossOcean = GetPlayer()->CanCrossOcean(); bool bAnyPeaceTreaty = false; for (std::vector::iterator it = vNotAtWarPlayers.begin(); it != vNotAtWarPlayers.end(); ++it) { // Don't consider our own vassals here - cases where we would want to attack them are handled earlier in this function + they shouldn't count as recent peace treaties if (IsMaster(*it)) continue; // Recent peace treaty? Run this check first as if it's true for anyone, we won't select any last resort candidates if (GetNumWarsFought(*it) > 0) { int iPeaceTreatyTurn = GET_TEAM(GetTeam()).GetTurnMadePeaceTreatyWithTeam(GET_PLAYER(*it).getTeam()); if (iPeaceTreatyTurn > -1) { int iTurnsSincePeace = GC.getGame().getGameTurn() - iPeaceTreatyTurn; int iPeaceDampenerTurns = GC.getGame().getHandicapInfo().getPeaceTreatyDampenerTurns(); if (iTurnsSincePeace < iPeaceDampenerTurns) { bAnyPeaceTreaty = true; vLastResortCandidates.clear(); continue; } } } // If this war would take us over the limit, let's not! int iConflictValue = IsEasyTarget(*it) && GetPlayer()->GetCurrentEra() > 1 ? 5 : 10; int iTestValue = iConflictScore + iConflictValue; if (iTestValue > iConflictLimit) continue; // War weight must be > 0 if (GetPlayerApproachValue(*it, CIV_APPROACH_WAR) <= 0) continue; // Avoid war if we have no chance whatsoever of winning if (GetMilitaryStrengthComparedToUs(*it) == STRENGTH_IMMENSE && !(IsEasyTarget(*it) && GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(*it))) continue; // Are we not allowing new wars? if (GetPlayer()->IsNoNewWars() && !IsEndgameAggressiveTo(*it) && !IsCapitalCapturedBy(*it, true, false)) continue; // Can we declare war? if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(*it).getTeam(), GetID())) continue; // Recent liberation? bool bRecentLiberation = IsCityRecentlyLiberatedBy(*it) && GetPlayer()->getCitiesLost() > 0 && !IsEndgameAggressiveTo(*it); if (bRecentLiberation) continue; // Usually need a valid (bad) approach towards this player bool bValidApproach = false; CivApproachTypes eApproach = GetCivApproach(*it); PlayerProximityTypes eProximity = GetPlayer()->GetProximityToPlayer(*it); if (eApproach == CIV_APPROACH_WAR) { bValidApproach = true; } else if (eApproach == CIV_APPROACH_HOSTILE) { if (bCloseToWorldConquest || IsEndgameAggressiveTo(*it)) { bValidApproach = true; } else if (bCanCrossOcean) { if (eProximity >= PLAYER_PROXIMITY_CLOSE) { bValidApproach = true; } else if (IsCompetingForVictory() && bGoingForWorldConquest) { bValidApproach = true; } } else if (eProximity == PLAYER_PROXIMITY_NEIGHBORS) { bValidApproach = true; } } else if (eApproach == CIV_APPROACH_DECEPTIVE) { if (bCloseToWorldConquest || IsEndgameAggressiveTo(*it)) { bValidApproach = true; } else if (eProximity == PLAYER_PROXIMITY_NEIGHBORS || (bCanCrossOcean && eProximity >= PLAYER_PROXIMITY_CLOSE)) { if (IsBackstabber() || GetDenounceWillingness() > 8 || GetMajorCivApproachBias(CIV_APPROACH_DECEPTIVE) > GetMajorCivApproachBias(CIV_APPROACH_FRIENDLY) + 1 || bGoingForWorldConquest) { bValidApproach = true; } } } // If approach is invalid, not a sane war choice, or a recent demand was accepted, only attack this player as a last resort... bool bPossibleSanityOverride = !IsWarSane(*it); if (!bValidApproach || bPossibleSanityOverride || IsRecentDemandAccepted(*it)) { // Don't select any last resort candidates if not going for world conquest, if there's a recent peace treaty we can wait out, or if unhappy if (!bGoingForWorldConquest || bAnyPeaceTreaty || GetPlayer()->IsEmpireUnhappy()) continue; // They must have a capital to conquer if (GET_PLAYER(*it).GetCapitalConqueror() != NO_PLAYER && GET_PLAYER(*it).GetNumCapitalCities() == 0) continue; if (bPossibleSanityOverride) { // Don't do this if we or one of our teammates would go bankrupt // Also don't do this if teamed up with a human (don't want to give human teammates backstabbing penalties) bool bNoOverride = false; for (size_t i=0; iIsWarWouldBankruptUs(*it)) { bNoOverride = true; break; } } if (bNoOverride) continue; } if (bCanCrossOcean || eProximity == PLAYER_PROXIMITY_NEIGHBORS) vLastResortCandidates.push_back(*it); continue; } vPlanningWarPlayers.push_back(*it); } // Look at our existing sneak attacks. If they're still valid targets, score them for continuation. CvWeightedVector viExistingSneakAttacks; vector vContinuingSneakAttacks; for (std::vector::iterator it = vNotAtWarPlayers.begin(); it != vNotAtWarPlayers.end(); ++it) { if (std::find(vPlanningWarPlayers.begin(), vPlanningWarPlayers.end(), *it) != vPlanningWarPlayers.end() && GetDemandTargetPlayer() != *it) { // How much do we value this existing sneak attack? if (GetPlayer()->getFirstOffensiveAIOperation(*it) != NULL || IsArmyInPlaceForAttack(*it)) { int iSneakAttackValue = GetPlayerApproachValue(*it, CIV_APPROACH_WAR); if (IsArmyInPlaceForAttack(*it)) iSneakAttackValue += 100000000; viExistingSneakAttacks.push_back(*it, iSneakAttackValue); } } } viExistingSneakAttacks.StableSortItems(); // If we have an existing (valid) war planned, let's stick with it. for (int iSneakAttackLoop = 0; iSneakAttackLoop < (int) viExistingSneakAttacks.size(); iSneakAttackLoop++) { PlayerTypes eSneakAttackTarget = (PlayerTypes) viExistingSneakAttacks.GetElement(iSneakAttackLoop); int iConflictValue = IsEasyTarget(eSneakAttackTarget) && GetPlayer()->GetCurrentEra() > 1 ? 5 : 10; iConflictScore += iConflictValue; SetCivApproach(eSneakAttackTarget, CIV_APPROACH_WAR); vAtWarPlayers.push_back(eSneakAttackTarget); vDirectTargets.push_back(eSneakAttackTarget); vContinuingSneakAttacks.push_back(eSneakAttackTarget); vector vDefensiveWarAllies = GetDefensiveWarAllies(eSneakAttackTarget, /*bIncludeMinors*/ false, /*bReverseMode*/ true, /*bNewWarsOnly*/ true); for (std::vector::iterator it = vDefensiveWarAllies.begin(); it != vDefensiveWarAllies.end(); ++it) { // Don't double count them if already in the list of war commitments! if (std::find(vAtWarPlayers.begin(), vAtWarPlayers.end(), *it) == vAtWarPlayers.end()) { iConflictValue = IsEasyTarget(*it) && GetPlayer()->GetCurrentEra() > 1 ? 5 : 10; iConflictScore += iConflictValue; vAtWarPlayers.push_back(*it); } } // Reached our limit? We're done here. if (iConflictScore >= iConflictLimit) break; } if (iConflictScore < iConflictLimit) { // If we don't have enough existing targets, we select new ones here, sorting by highest WAR weight vector vWarExclusions; PlayerTypes eCandidate = GetPlayerWithHighestApproachValue(CIV_APPROACH_WAR, vWarExclusions); while (eCandidate != NO_PLAYER) { // Must be among our valid options if (std::find(vPlanningWarPlayers.begin(), vPlanningWarPlayers.end(), eCandidate) == vPlanningWarPlayers.end()) { vWarExclusions.push_back(eCandidate); eCandidate = GetPlayerWithHighestApproachValue(CIV_APPROACH_WAR, vWarExclusions); continue; } // Can't be in our existing list of sneak attacks if (std::find(vContinuingSneakAttacks.begin(), vContinuingSneakAttacks.end(), eCandidate) != vContinuingSneakAttacks.end()) { vWarExclusions.push_back(eCandidate); eCandidate = GetPlayerWithHighestApproachValue(CIV_APPROACH_WAR, vWarExclusions); continue; } int iConflictValue = IsEasyTarget(eCandidate) && GetPlayer()->GetCurrentEra() > 1 ? 5 : 10; iConflictScore += iConflictValue; SetCivApproach(eCandidate, CIV_APPROACH_WAR); vAtWarPlayers.push_back(eCandidate); vDirectTargets.push_back(eCandidate); vWarExclusions.push_back(eCandidate); vector vDefensiveWarAllies = GetDefensiveWarAllies(eCandidate, /*bIncludeMinors*/ false, /*bReverseMode*/ true, /*bNewWarsOnly*/ true); for (std::vector::iterator it = vDefensiveWarAllies.begin(); it != vDefensiveWarAllies.end(); ++it) { // Don't double count them if already in the list of war commitments! if (std::find(vAtWarPlayers.begin(), vAtWarPlayers.end(), *it) == vAtWarPlayers.end()) { iConflictValue = IsEasyTarget(*it) && GetPlayer()->GetCurrentEra() > 1 ? 5 : 10; iConflictScore += iConflictValue; vAtWarPlayers.push_back(*it); } } // Reached our limit? We're done here. if (iConflictScore >= iConflictLimit) break; // No? Pick another one! eCandidate = GetPlayerWithHighestApproachValue(CIV_APPROACH_WAR, vWarExclusions); } } // Going for world conquest and we don't have anyone we're at war with? That won't do. if (!vLastResortCandidates.empty() && vAtWarPlayers.empty()) { vector vWarExclusions; PlayerTypes eCandidate = GetPlayerWithHighestApproachValue(CIV_APPROACH_WAR, vWarExclusions); while (eCandidate != NO_PLAYER) { // Must be among our valid last resort options if (std::find(vLastResortCandidates.begin(), vLastResortCandidates.end(), eCandidate) == vLastResortCandidates.end()) { vWarExclusions.push_back(eCandidate); eCandidate = GetPlayerWithHighestApproachValue(CIV_APPROACH_WAR, vWarExclusions); continue; } int iConflictValue = IsEasyTarget(eCandidate) && GetPlayer()->GetCurrentEra() > 1 ? 5 : 10; iConflictScore += iConflictValue; SetCivApproach(eCandidate, CIV_APPROACH_WAR); vAtWarPlayers.push_back(eCandidate); vDirectTargets.push_back(eCandidate); vector vDefensiveWarAllies = GetDefensiveWarAllies(eCandidate, /*bIncludeMinors*/ false, /*bReverseMode*/ true, /*bNewWarsOnly*/ true); for (std::vector::iterator it = vDefensiveWarAllies.begin(); it != vDefensiveWarAllies.end(); ++it) { iConflictValue = IsEasyTarget(*it) && GetPlayer()->GetCurrentEra() > 1 ? 5 : 10; iConflictScore += iConflictValue; vAtWarPlayers.push_back(*it); } // Only add one last resort candidate. break; } } // We've selected all of our war targets! Now go through all players and make sure we don't have the WAR approach if we're not planning war. for (std::vector::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); ++it) { if (std::find(vAtWarPlayers.begin(), vAtWarPlayers.end(), *it) == vAtWarPlayers.end()) { SetCivApproach(*it, GetHighestValueApproach(*it, true, true)); } } } // Now update our potential MAJOR war targets. // If we're at or above the limit, no more wars for us! if (GetPlayer()->IsNoNewWars() || (iConflictScore >= iPotentialWarLimit)) { for (std::vector::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); ++it) { // Already at war? if (IsAtWar(*it)) { SetPotentialWarTarget(*it, false); continue; } // In our list of direct targets already, so we're willing to attack them. if (std::find(vDirectTargets.begin(), vDirectTargets.end(), *it) != vDirectTargets.end()) { SetPotentialWarTarget(*it, true); continue; } SetPotentialWarTarget(*it, false); } } // Otherwise, mark all majors we might want to attack as potential war targets // War sanity is not checked here (to allow for impulse wars and betrayals), but it will be before a potential war is declared else { for (std::vector::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); ++it) { PlayerTypes ePlayer = *it; // Already at war? if (IsAtWar(ePlayer)) { SetPotentialWarTarget(ePlayer, false); continue; } // In our list of direct targets already, so we're willing to attack them. if (std::find(vDirectTargets.begin(), vDirectTargets.end(), ePlayer) != vDirectTargets.end()) { SetPotentialWarTarget(ePlayer, true); continue; } int iConflictValue = IsEasyTarget(ePlayer) && GetPlayer()->GetCurrentEra() > 1 ? 5 : 10; int iTestValue = iConflictScore + iConflictValue; // Not if it would take us over the limit! if (iTestValue > iPotentialWarLimit) { SetPotentialWarTarget(ePlayer, false); continue; } // Scared of them? if (GetCivApproach(ePlayer) == CIV_APPROACH_AFRAID) { SetPotentialWarTarget(ePlayer, false); continue; } // Avoid war if we have no chance whatsoever of winning if (GetMilitaryStrengthComparedToUs(ePlayer) == STRENGTH_IMMENSE && !(IsEasyTarget(ePlayer) && GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(ePlayer))) { SetPotentialWarTarget(ePlayer, false); continue; } // No target? if (!GetPlayer()->GetMilitaryAI()->HavePossibleAttackTarget(ePlayer)) { SetPotentialWarTarget(ePlayer, false); continue; } // Recent peace treaty? if (GetNumWarsFought(ePlayer) > 0) { int iPeaceTreatyTurn = GET_TEAM(GetTeam()).GetTurnMadePeaceTreatyWithTeam(GET_PLAYER(ePlayer).getTeam()); if (iPeaceTreatyTurn > -1) { int iTurnsSincePeace = GC.getGame().getGameTurn() - iPeaceTreatyTurn; int iPeaceDampenerTurns = GC.getGame().getHandicapInfo().getPeaceTreatyDampenerTurns(); if (iTurnsSincePeace < iPeaceDampenerTurns) { SetPotentialWarTarget(ePlayer, false); continue; } } } if (!IsUntrustworthy(ePlayer)) { // Most valuable friend or ally? if (GetMostValuableFriend() == ePlayer || GetMostValuableAlly() == ePlayer) { SetPotentialWarTarget(ePlayer, false); continue; } // Avoid attacking early game friends unless we have extremely low Loyalty if (IsDoFAccepted(ePlayer) && GetPlayer()->GetCurrentEra() <= 1 && !IsBackstabber()) { SetPotentialWarTarget(ePlayer, false); continue; } // Strategic trade partner? if (IsStrategicTradePartner(ePlayer)) { SetPotentialWarTarget(ePlayer, false); continue; } // Friendly to them? if (GetCivApproach(ePlayer) == CIV_APPROACH_FRIENDLY) { SetPotentialWarTarget(ePlayer, false); continue; } // Like them too much? if (GetCivOpinion(ePlayer) >= CIV_OPINION_FRIEND) { SetPotentialWarTarget(ePlayer, false); continue; } // Major liberator, or recent liberation while we still have missing cities? if (IsLiberator(ePlayer, false, true) || (IsCityRecentlyLiberatedBy(ePlayer) && !IsEndgameAggressiveTo(ePlayer) && GetPlayer()->getCitiesLost() > 0)) { SetPotentialWarTarget(ePlayer, false); continue; } } // All clear...the potential is there! SetPotentialWarTarget(ePlayer, true); } } } /// Updates our general Diplomatic Approach towards each minor civilization (City-State) we've met void CvDiplomacyAI::DoUpdateMinorCivApproaches() { bool bHuman = GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY); bool bNoCities = GetPlayer()->getCapitalCity() == NULL; std::vector vPlayersToUpdate; for (int iPlayerLoop = MAX_MAJOR_CIVS; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eMinor = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eMinor).isMinorCiv() && GET_PLAYER(eMinor).isAlive() && IsHasMet(eMinor, false)) { if (IsAlwaysAtWar(eMinor)) { SelectAlwaysWarApproach(eMinor); SetPotentialWarTarget(eMinor, true); continue; } else if (bHuman) { SelectHumanApproach(eMinor); continue; } else if (GET_PLAYER(eMinor).getCapitalCity() == NULL) { SelectApproachIfTheyHaveNoCities(eMinor); SetPotentialWarTarget(eMinor, !bNoCities); continue; } else if (bNoCities) { SelectApproachIfWeHaveNoCities(eMinor); SetPotentialWarTarget(eMinor, false); continue; } vPlayersToUpdate.push_back(eMinor); } } if (bHuman || bNoCities) return; CvWeightedVector vePlayerApproachWeights; int iHighestWeight = 1000; // Loop through all (known) Minors and determine the order of who we pick our Approach for first based on PROXIMITY - this is different from Majors for (std::vector::iterator it = vPlayersToUpdate.begin(); it != vPlayersToUpdate.end(); ++it) { CvCity* pCapitalCity = GET_PLAYER(*it).getCapitalCity(); CvCity* pOurClosestCity = GetPlayer()->GetClosestCityByPathLength(pCapitalCity->plot()); if (!pOurClosestCity) continue; int iDistance = plotDistance(*pCapitalCity->plot(), *pOurClosestCity->plot()); iHighestWeight = 1000 - iDistance; vePlayerApproachWeights.push_back(*it, iHighestWeight); } // Now sort the list if there's anything in it if (vePlayerApproachWeights.empty()) return; vePlayerApproachWeights.StableSortItems(); // Now that Minors are sorted, ACTUALLY figure out what our Approach will be, taking everything into account for (int iPlayerVectorIndex = 0; iPlayerVectorIndex < vePlayerApproachWeights.size(); iPlayerVectorIndex++) { PlayerTypes eLoopPlayer = (PlayerTypes) vePlayerApproachWeights.GetElement(iPlayerVectorIndex); SelectBestApproachTowardsMinorCiv(eLoopPlayer); } // Now that we've set approaches, let's pick our war and bully targets (only one each). bool bBullySet = false; bool bWarSet = false; for (int iPlayerVectorIndex = 0; iPlayerVectorIndex < vePlayerApproachWeights.size(); iPlayerVectorIndex++) { PlayerTypes eLoopPlayer = (PlayerTypes) vePlayerApproachWeights.GetElement(iPlayerVectorIndex); // See which Approach is best CivApproachTypes eApproach = GetCivApproach(eLoopPlayer); //Our first bully? if (eApproach == CIV_APPROACH_HOSTILE) { if (!bBullySet) { SetCSBullyTargetPlayer(eLoopPlayer); bBullySet = true; } else { //second bully? Set as ignore. SetCivApproach(eLoopPlayer, CIV_APPROACH_NEUTRAL); } } //Our first war? if (eApproach == CIV_APPROACH_WAR) { if (!bWarSet) { SetCSWarTargetPlayer(eLoopPlayer); bWarSet = true; } else { //second war? Set as ignore. SetCivApproach(eLoopPlayer, CIV_APPROACH_NEUTRAL); } } } } /// What is the best Diplomatic Approach to take towards a minor civilization (City-State)? void CvDiplomacyAI::SelectBestApproachTowardsMinorCiv(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= MAX_MAJOR_CIVS && ePlayer < MAX_CIV_PLAYERS); PlayerTypes eMyPlayer = GetID(); TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam(); bool bEasyTarget = IsEasyTarget(ePlayer); bool bPotentialWarTarget = true; CvPlayerTraits* pTraits = GetPlayer()->GetPlayerTraits(); CivApproachTypes eOldApproach = GetCivApproach(ePlayer); bool bFirstUpdate = GET_TEAM(GetTeam()).GetTurnsSinceMeetingTeam(GET_PLAYER(ePlayer).getTeam()) <= 1; // This vector is what we'll stuff the values into first, and pass it into our logging function (which can't take a CvWeightedVector, which we need to sort...) vector vApproachScores(NUM_CIV_APPROACHES, 0); vector vApproachBias; for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eLoopApproach = (CivApproachTypes) iApproachLoop; int iBias = GetMinorCivApproachBias(eLoopApproach, true) * 100; // x100 for greater fidelity vApproachBias.push_back(iBias); // Add 1x bias for each approach to reflect personality weight vApproachScores[iApproachLoop] += iBias; } //////////////////////////////////// // NEUTRAL DEFAULT WEIGHT //////////////////////////////////// vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * /*2*/ GD_INT_GET(MINOR_APPROACH_IGNORE_DEFAULT); //////////////////////////////////// // PREVIOUS APPROACH BIAS //////////////////////////////////// if (!bFirstUpdate) { // If we're planning a war then give it a bias so that we don't get away from it too easily if (IsAtWar(ePlayer) || eOldApproach == CIV_APPROACH_WAR) { // Don't give this bias if the war is going poorly if ((GetWarState(ePlayer) != NO_WAR_STATE_TYPE || GetWarState(ePlayer) > WAR_STATE_CALM) && !GetPlayer()->IsNoNewWars()) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; } } if (eOldApproach == CIV_APPROACH_HOSTILE) { // Don't give this bias if they're a bad target if (bEasyTarget || GetTargetValue(ePlayer) >= TARGET_VALUE_AVERAGE) { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2; } } } //////////////////////////////////// // RESOURCES //////////////////////////////////// int iNumWeLack = GET_PLAYER(ePlayer).GetMinorCivAI()->GetNumResourcesMajorLacks(eMyPlayer); if (iNumWeLack > 0) { if (GetPlayer()->IsCSResourcesCountMonopolies()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iNumWeLack * 2; } else { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iNumWeLack; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * iNumWeLack; } } //////////////////////////////////// // FRIENDS WITH MINOR //////////////////////////////////// if (GET_PLAYER(ePlayer).GetMinorCivAI()->IsFriends(eMyPlayer)) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; } //////////////////////////////////// // PLEDGE TO PROTECT - have we pledged to protect this minor? //////////////////////////////////// if (GET_PLAYER(ePlayer).GetMinorCivAI()->IsProtectedByMajor(GetID())) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; } bool bIsGoodWarTarget = false; bool bCheckIfGoodWarTarget = true; //////////////////////////////////// // EASY TARGET //////////////////////////////////// if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2; } //////////////////////////////////// // TRAITS THAT AFFECT MINORS - These are heavy handed, but that is intentional //////////////////////////////////// bool bAnyFriendshipBonus = false; bool bAnyAggressionBonus = false; // UA that increases the benefits of City-State friendship if (pTraits->GetCityStateFriendshipModifier() > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5; bAnyFriendshipBonus = true; } if (pTraits->GetCityStateBonusModifier() > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5; bAnyFriendshipBonus = true; } if (pTraits->GetAllianceCSStrength() > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5; bAnyFriendshipBonus = true; } if (pTraits->GetAllianceCSDefense() > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5; bAnyFriendshipBonus = true; } if (pTraits->GetMinorInfluencePerGiftedUnit() > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5; bAnyFriendshipBonus = true; } if (pTraits->IsDiplomaticMarriage()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 10; bAnyFriendshipBonus = true; } if (pTraits->IsAbleToAnnexCityStates()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 10; bAnyFriendshipBonus = true; } for (int iI = 0; iI < NUM_YIELD_TYPES; iI++) { YieldTypes eYield = (YieldTypes) iI; if (eYield != NO_YIELD) { if (pTraits->GetYieldFromCSAlly(eYield) > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5; bAnyFriendshipBonus = true; } } } // Other Friendship Bonuses if (GetPlayer()->GetMinorFriendshipAnchorMod() > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; bAnyFriendshipBonus = true; } if (GetPlayer()->GetMinorFriendshipDecayMod() < 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; bAnyFriendshipBonus = true; } if (GetPlayer()->getMinorGoldFriendshipMod() > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; bAnyFriendshipBonus = true; } if (GetPlayer()->IsMinorScienceAllies()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; bAnyFriendshipBonus = true; } if (GetPlayer()->IsMinorResourceBonus()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; bAnyFriendshipBonus = true; } if (GetPlayer()->GetIncreasedQuestInfluence() > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; bAnyFriendshipBonus = true; } if (GetPlayer()->GetPlayerPolicies()->GetNumericModifier(POLICYMOD_RIGGING_ELECTION_MODIFIER) > 0 || GetPlayer()->GetPlayerPolicies()->GetNumericModifier(POLICYMOD_RIG_ELECTION_INFLUENCE_MODIFIER) > 0 || pTraits->GetSpyOffensiveStrengthModifier()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; bAnyFriendshipBonus = true; } // UA that increases the benefits of aggression towards City-States if (pTraits->GetCityStateCombatModifier() > 0) { vApproachScores[CIV_APPROACH_HOSTILE] += MOD_BALANCE_VP ? 0 : vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_WAR] += MOD_BALANCE_VP ? vApproachBias[CIV_APPROACH_WAR] * 5 : vApproachBias[CIV_APPROACH_WAR] * 2; bAnyAggressionBonus = true; } if (GetPlayer()->GetCityStateCombatModifier() > 0) { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; bAnyAggressionBonus = true; } if (pTraits->IsBullyAnnex()) { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; bAnyAggressionBonus = true; } if (pTraits->IgnoreBullyPenalties()) { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; bAnyAggressionBonus = true; } if (pTraits->GetBullyMilitaryStrengthModifier() != 0) { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; bAnyAggressionBonus = true; } if (pTraits->GetBullyValueModifier() != 0) { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; bAnyAggressionBonus = true; } for (int iI = 0; iI < NUM_YIELD_TYPES; iI++) { YieldTypes eYield = (YieldTypes)iI; if (GetPlayer()->GetYieldFromMinorDemand(eYield) > 0) { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; bAnyAggressionBonus = true; } } // Other Bullying Bonuses if (GetPlayer()->IsCanBullyFriendlyCS()) { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; } if (GetPlayer()->GetBullyGlobalCSReduction() > 0) { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 5; } //////////////////////////////////// // CONQUEST GRAND STRATEGY //////////////////////////////////// if (IsGoingForWorldConquest() || IsCloseToWorldConquest() || pTraits->IsWarmonger() || pTraits->IsExpansionist()) { // Minor is militaristic or mercantile if (GET_PLAYER(ePlayer).GetMinorCivAI()->GetTrait() == MINOR_CIV_TRAIT_MILITARISTIC || GET_PLAYER(ePlayer).GetMinorCivAI()->GetTrait() == MINOR_CIV_TRAIT_MERCANTILE) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; bCheckIfGoodWarTarget = false; } else if (IsCloseToWorldConquest() || (!pTraits->IsSmaller() && !IsGoingForDiploVictory())) { bAnyAggressionBonus = true; vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 4; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; } } //////////////////////////////////// // DIPLO GRAND STRATEGY //////////////////////////////////// bool bCloseToDiploVictory = IsCloseToDiploVictory(); if (IsGoingForDiploVictory() || bCloseToDiploVictory || pTraits->IsDiplomat()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; if (IsGoingForDiploVictory() || bCloseToDiploVictory || pTraits->IsDiplomat()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 4; bCheckIfGoodWarTarget = false; bAnyFriendshipBonus = true; } } //////////////////////////////////// // CULTURE GRAND STRATEGY //////////////////////////////////// if (IsGoingForCultureVictory() || IsCloseToCultureVictory() || pTraits->IsTourism() || pTraits->IsSmaller()) { // Minor is cultural or maritime if (GET_PLAYER(ePlayer).GetMinorCivAI()->GetTrait() == MINOR_CIV_TRAIT_CULTURED || GET_PLAYER(ePlayer).GetMinorCivAI()->GetTrait() == MINOR_CIV_TRAIT_MARITIME) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; bCheckIfGoodWarTarget = false; } // Smaller bonus for happiness else if (GET_PLAYER(ePlayer).GetMinorCivAI()->GetTrait() == MINOR_CIV_TRAIT_MERCANTILE) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY]; } else { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2; } } //////////////////////////////////// // SCIENCE GRAND STRATEGY //////////////////////////////////// if (IsGoingForSpaceshipVictory() || IsCloseToSpaceshipVictory() || pTraits->IsNerd() || pTraits->IsSmaller()) { // Minor is maritime or militaristic if (GET_PLAYER(ePlayer).GetMinorCivAI()->GetTrait() == MINOR_CIV_TRAIT_MARITIME || GET_PLAYER(ePlayer).GetMinorCivAI()->GetTrait() == MINOR_CIV_TRAIT_MILITARISTIC) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; bCheckIfGoodWarTarget = false; } // Smaller bonus for happiness else if (GET_PLAYER(ePlayer).GetMinorCivAI()->GetTrait() == MINOR_CIV_TRAIT_MERCANTILE) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY]; } else { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2; } } //////////////////////////////////// // RELIGIOUS MINORS //////////////////////////////////// if (GET_PLAYER(ePlayer).GetMinorCivAI()->GetTrait() == MINOR_CIV_TRAIT_RELIGIOUS && !GC.getGame().isOption(GAMEOPTION_NO_RELIGION)) { ReligionTypes eReligion = GetPlayer()->GetReligions()->GetStateReligion(false); // Do we already have a religion? if (eReligion != NO_RELIGION) { const CvReligion* pReligion = GC.getGame().GetGameReligions()->GetReligion(eReligion, NO_PLAYER); // But not yet enhanced? if (!pReligion->m_bEnhanced) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; bCheckIfGoodWarTarget = false; } // Enhanced? Be a bit more friendly (to benefit from faith quests etc), but it's no longer a huge issue. else { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY]; } } // Can we found a religion? else if (GC.getGame().GetGameReligions()->GetNumReligionsStillToFound() <= 0 || pTraits->IsAlwaysReligion()) { // We created a pantheon? We're on track, so keep these City-States close... if (GetPlayer()->GetReligions()->HasCreatedPantheon()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 4; bCheckIfGoodWarTarget = false; } // No one has founded a religion yet, so let's be more friendly... else if (GC.getGame().GetGameReligions()->GetNumReligionsFounded() <= 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; bCheckIfGoodWarTarget = false; } // Less interesting, but better conquest targets. else if (!pTraits->IsReligious()) { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR]; if (bEasyTarget) { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE]; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR]; } if (bAnyAggressionBonus) { vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE]; vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR]; } } } // Far less interesting. else { vApproachScores[CIV_APPROACH_WAR] += (bEasyTarget || bAnyAggressionBonus) ? vApproachBias[CIV_APPROACH_WAR] * 2 : 0; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 4; } // Religious civ - apply extra weight if ANY religion criteria is met if (pTraits->IsReligious()) { if (eReligion != NO_RELIGION || GetPlayer()->GetReligions()->HasCreatedPantheon()) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; bCheckIfGoodWarTarget = false; } } } int iNonPuppetCities = GetPlayer()->getNumCities() - GetPlayer()->GetNumPuppetCities(); if (bAnyFriendshipBonus && iNonPuppetCities < 4) { vApproachScores[CIV_APPROACH_HOSTILE] = 0; vApproachScores[CIV_APPROACH_WAR] = 0; bCheckIfGoodWarTarget = false; bAnyAggressionBonus = false; } // Does this City-State own an original major capital? bool bHasOriginalCapital = false; int iCityLoop = 0; for (CvCity* pCity = GET_PLAYER(ePlayer).firstCity(&iCityLoop); pCity != NULL; pCity = GET_PLAYER(ePlayer).nextCity(&iCityLoop)) { // Allying the City-State AND conquering it both count towards Domination Victory. if (pCity->IsOriginalMajorCapital()) { bAnyFriendshipBonus = true; if (GET_PLAYER(pCity->GetOwnerForDominationVictory()).getTeam() != GetTeam()) { bHasOriginalCapital = true; bAnyAggressionBonus = true; bCheckIfGoodWarTarget = true; } break; } } //////////////////////////////////// // AT WAR RIGHT NOW //////////////////////////////////// for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // Don't look at the guy we're already thinking about or anyone on his team if (GET_PLAYER(ePlayer).getTeam() != GET_PLAYER(eLoopPlayer).getTeam()) { if (IsPlayerValid(eLoopPlayer)) { if (IsAtWar(eLoopPlayer)) { vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL]; vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY]; //let's not worry about CSs as much if at war. if (GET_PLAYER(eLoopPlayer).isMajorCiv()) { vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_HOSTILE] -= vApproachBias[CIV_APPROACH_HOSTILE] * 2; } if (GetWarState(eLoopPlayer) <= WAR_STATE_TROUBLED) bPotentialWarTarget = false; } //for every nearby major civ, let's reduce our interest in CS conquest. We've got bigger fish to fry. if (GET_PLAYER(eLoopPlayer).isMajorCiv()) { if (GetPlayer()->GetProximityToPlayer(eLoopPlayer) == PLAYER_PROXIMITY_NEIGHBORS) { vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR] * 2; } else if (GetPlayer()->GetProximityToPlayer(eLoopPlayer) == PLAYER_PROXIMITY_CLOSE) { vApproachScores[CIV_APPROACH_WAR] -= vApproachBias[CIV_APPROACH_WAR]; } } } } } //////////////////////////////////// // PROXIMITY //////////////////////////////////// switch (GetPlayer()->GetProximityToPlayer(ePlayer)) { case PLAYER_PROXIMITY_NEIGHBORS: vApproachScores[CIV_APPROACH_FRIENDLY] += bAnyFriendshipBonus ? vApproachBias[CIV_APPROACH_FRIENDLY] * 3 : vApproachBias[CIV_APPROACH_FRIENDLY] * 2; vApproachScores[CIV_APPROACH_HOSTILE] += (bEasyTarget || bAnyAggressionBonus) ? vApproachBias[CIV_APPROACH_HOSTILE] : vApproachBias[CIV_APPROACH_HOSTILE] / 2; vApproachScores[CIV_APPROACH_WAR] += (bEasyTarget || bAnyAggressionBonus) ? vApproachBias[CIV_APPROACH_WAR] * 2 : vApproachBias[CIV_APPROACH_WAR]; break; case PLAYER_PROXIMITY_CLOSE: vApproachScores[CIV_APPROACH_FRIENDLY] += bAnyFriendshipBonus ? vApproachBias[CIV_APPROACH_FRIENDLY] * 2 : vApproachBias[CIV_APPROACH_FRIENDLY]; vApproachScores[CIV_APPROACH_HOSTILE] += (bEasyTarget || bAnyAggressionBonus) ? vApproachBias[CIV_APPROACH_HOSTILE] * 2 : vApproachBias[CIV_APPROACH_HOSTILE]; vApproachScores[CIV_APPROACH_WAR] += (bEasyTarget || bAnyAggressionBonus) ? vApproachBias[CIV_APPROACH_WAR] : vApproachBias[CIV_APPROACH_WAR] / 2; break; case PLAYER_PROXIMITY_FAR: vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 4; break; case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 8; break; } //////////////////////////////////// // MINOR PERSONALITY //////////////////////////////////// switch (GET_PLAYER(ePlayer).GetMinorCivAI()->GetPersonality()) { case NO_MINOR_CIV_PERSONALITY_TYPE: ASSERT(!GET_PLAYER(ePlayer).isMinorCiv(), "Minor missing personality"); break; // Not a minor civ. case MINOR_CIV_PERSONALITY_FRIENDLY: vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; break; case MINOR_CIV_PERSONALITY_NEUTRAL: vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL]; break; case MINOR_CIV_PERSONALITY_HOSTILE: vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; break; case MINOR_CIV_PERSONALITY_IRRATIONAL: vApproachScores[CIV_APPROACH_HOSTILE] += vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; break; } //////////////////////////////////// // TRIBUTE HISTORY - have we bullied this player before? If so, we are more likely to keep bullying //////////////////////////////////// if (GET_PLAYER(ePlayer).GetMinorCivAI()->IsEverBulliedByMajor(eMyPlayer)) { vApproachScores[CIV_APPROACH_HOSTILE] += (bEasyTarget || bAnyAggressionBonus) ? vApproachBias[CIV_APPROACH_HOSTILE] * 4 : vApproachBias[CIV_APPROACH_HOSTILE] * 2; vApproachScores[CIV_APPROACH_WAR] += (bEasyTarget || bAnyAggressionBonus) ? vApproachBias[CIV_APPROACH_WAR] * 2 : vApproachBias[CIV_APPROACH_WAR]; } //////////////////////////////////// // QUESTS - are there any active quests that might sway our decision? //////////////////////////////////// if (GET_PLAYER(ePlayer).GetMinorCivAI()->IsActiveQuestForPlayer(eMyPlayer, MINOR_CIV_QUEST_PLEDGE_TO_PROTECT)) { vApproachScores[CIV_APPROACH_FRIENDLY] += bAnyFriendshipBonus ? vApproachBias[CIV_APPROACH_FRIENDLY] * 2 : vApproachBias[CIV_APPROACH_FRIENDLY]; } // Check for kill CS quests if ((!MOD_BALANCE_QUEST_CHANGES && GET_PLAYER(ePlayer).GetMinorCivAI()->IsEnabledQuest(MINOR_CIV_QUEST_KILL_CITY_STATE)) || (MOD_BALANCE_QUEST_CHANGES && GET_PLAYER(ePlayer).GetMinorCivAI()->IsActiveQuestForPlayer(eMyPlayer, MINOR_CIV_QUEST_KILL_CITY_STATE))) { for (int iMinorCivLoop = MAX_MAJOR_CIVS; iMinorCivLoop < MAX_CIV_PLAYERS; iMinorCivLoop++) { PlayerTypes eOtherMinor = (PlayerTypes) iMinorCivLoop; if (IsPlayerValid(eOtherMinor) && GET_PLAYER(eOtherMinor).isMinorCiv() && GET_PLAYER(eOtherMinor).getTeam() != GET_PLAYER(ePlayer).getTeam()) { if (GET_PLAYER(eOtherMinor).GetMinorCivAI()->IsActiveQuestForPlayer(eMyPlayer, MINOR_CIV_QUEST_KILL_CITY_STATE)) { PlayerTypes eTarget = (PlayerTypes) GET_PLAYER(eOtherMinor).GetMinorCivAI()->GetQuestData1(eMyPlayer, MINOR_CIV_QUEST_KILL_CITY_STATE); if (eTarget == ePlayer) { bool bBadTarget = false; int iPTV = GetTargetValue(ePlayer); switch (iPTV) { //Is this guy a bad target? Let's diplo him instead. case TARGET_VALUE_IMPOSSIBLE: case TARGET_VALUE_BAD: case TARGET_VALUE_DIFFICULT: case TARGET_VALUE_AVERAGE: bBadTarget = true; break; //Good target? Let's kill him and get the prize! case TARGET_VALUE_FAVORABLE: case TARGET_VALUE_SOFT: case TARGET_VALUE_CAKEWALK: vApproachScores[CIV_APPROACH_WAR] += (bEasyTarget || bAnyAggressionBonus) ? vApproachBias[CIV_APPROACH_WAR] * 4 : vApproachBias[CIV_APPROACH_WAR] * 2; bAnyAggressionBonus = true; break; default: break; } // If this City-State has a kill CS quest, then it means another CS has one too... if (MOD_BALANCE_QUEST_CHANGES) { int iPTV2 = GetTargetValue(eOtherMinor); switch (iPTV2) { // Is the other guy also a bad target? Let's diplo them both instead. case TARGET_VALUE_IMPOSSIBLE: case TARGET_VALUE_BAD: case TARGET_VALUE_DIFFICULT: case TARGET_VALUE_AVERAGE: if (bBadTarget) { vApproachScores[CIV_APPROACH_FRIENDLY] += bAnyFriendshipBonus ? vApproachBias[CIV_APPROACH_FRIENDLY] * 4 : vApproachBias[CIV_APPROACH_FRIENDLY] * 2; } break; default: break; } // Break out of the loop here if it's the modified quest, since no one else will also be targeting this CS break; } } } } } } // Are we getting yields from trade with them? int iCurrentTradeValue = GetPlayer()->GetTrade()->GetAllTradeValueFromPlayerTimes100(YIELD_GOLD, ePlayer) + GetPlayer()->GetTrade()->GetAllTradeValueFromPlayerTimes100(YIELD_CULTURE, ePlayer) + GetPlayer()->GetTrade()->GetAllTradeValueFromPlayerTimes100(YIELD_SCIENCE, ePlayer); iCurrentTradeValue /= 300; iCurrentTradeValue += GET_PLAYER(ePlayer).GetMinorCivAI()->GetCurrentGoldBonus(eMyPlayer); if (iCurrentTradeValue > 0) { vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * iCurrentTradeValue; } else //not a trade partner { vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; } //////////////////////////////////// // WORLD CONGRESS //////////////////////////////////// if (GET_PLAYER(ePlayer).GetMinorCivAI()->IsNoAlly()) { vApproachScores[CIV_APPROACH_WAR] += (bEasyTarget || bAnyAggressionBonus) ? vApproachBias[CIV_APPROACH_WAR] * 2 : vApproachBias[CIV_APPROACH_WAR]; vApproachScores[CIV_APPROACH_HOSTILE] += (bEasyTarget || bAnyAggressionBonus) ? vApproachBias[CIV_APPROACH_HOSTILE] * 2 : vApproachBias[CIV_APPROACH_HOSTILE]; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; vApproachScores[CIV_APPROACH_FRIENDLY] /= 2; bCheckIfGoodWarTarget = true; bAnyFriendshipBonus = false; bAnyAggressionBonus = true; } if (GET_PLAYER(ePlayer).GetMinorCivAI()->GetPermanentAlly() != NO_PLAYER && GET_PLAYER(GET_PLAYER(ePlayer).GetMinorCivAI()->GetPermanentAlly()).getTeam() != GetTeam()) { vApproachScores[CIV_APPROACH_WAR] += (bEasyTarget || bAnyAggressionBonus) ? vApproachBias[CIV_APPROACH_WAR] * 2 : vApproachBias[CIV_APPROACH_WAR]; vApproachScores[CIV_APPROACH_HOSTILE] += (bEasyTarget || bAnyAggressionBonus) ? vApproachBias[CIV_APPROACH_HOSTILE] * 2 : vApproachBias[CIV_APPROACH_HOSTILE]; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; vApproachScores[CIV_APPROACH_FRIENDLY] /= 2; bCheckIfGoodWarTarget = true; bAnyFriendshipBonus = false; bAnyAggressionBonus = true; } // See if this minor is on the same continent as a major power we want to attack vector vGoodWarTargetMajors; if (bCheckIfGoodWarTarget) { CvCity *pkMinorCapital = GET_PLAYER(ePlayer).getCapitalCity(); // Validity was already checked in DoUpdateMinorCivApproaches() for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // Make sure it is another valid player and not already at war with them if (IsPlayerValid(eLoopPlayer)) { // Do we want to attack them? if (IsAtWar(eLoopPlayer) || GetCivApproach(eLoopPlayer) == CIV_APPROACH_WAR) { CvCity* pkLoopPlayerCity = GET_PLAYER(eLoopPlayer).getCapitalCity(); if (pkLoopPlayerCity) // It's possible this is not valid { // All of us neighbors on same landmass? if (GET_PLAYER(eLoopPlayer).GetProximityToPlayer(ePlayer) >= PLAYER_PROXIMITY_NEIGHBORS && GetPlayer()->GetProximityToPlayer(ePlayer) >= PLAYER_PROXIMITY_NEIGHBORS) { if (pkMinorCapital->HasSharedLandmassWith(GetPlayer()->getCapitalCity(),true,false) && pkMinorCapital->HasSharedLandmassWith(pkLoopPlayerCity,true,false)) { bIsGoodWarTarget = true; vGoodWarTargetMajors.push_back(eLoopPlayer); } } } } } } } if (bIsGoodWarTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; if (bEasyTarget) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; } if (bAnyAggressionBonus) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; } if (IsGoingForWorldConquest() || IsCloseToWorldConquest()) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 2; } } else if (GetPlayer()->GetProximityToPlayer(ePlayer) <= PLAYER_PROXIMITY_FAR) { vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; } //////////////////////////////////// // APPROACHES TOWARDS OTHER PLAYERS //////////////////////////////////// // Look at Approaches we've already adopted for higher priority major civs for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes)iPlayerLoop; if (IsPlayerValid(eLoopPlayer) && GET_PLAYER(eLoopPlayer).getTeam() != GET_PLAYER(ePlayer).getTeam()) { if (GET_PLAYER(eLoopPlayer).isMajorCiv() && std::find(vGoodWarTargetMajors.begin(), vGoodWarTargetMajors.end(), eLoopPlayer) == vGoodWarTargetMajors.end()) { if (IsAtWar(eLoopPlayer) || GetCivApproach(eLoopPlayer) == CIV_APPROACH_WAR) { if (!IsPhonyWar(eLoopPlayer) && GetPlayer()->GetProximityToPlayer(eLoopPlayer) >= PLAYER_PROXIMITY_CLOSE) { vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; } } } } } //////////////////////////////////// // AT WAR WITH MINOR? //////////////////////////////////// // No bullying while at war! if (IsAtWar(ePlayer)) { vApproachScores[CIV_APPROACH_HOSTILE] = 0; } //////////////////////////////////// // PREVIOUSLY CAPTURED OUR CITIES //////////////////////////////////// bool bTheyCapturedFromUs = GetPlayer()->GetNumOurCitiesOwnedBy(ePlayer) > 0; PlayerTypes eAlly = GET_PLAYER(ePlayer).GetMinorCivAI()->GetAlly(); // If we can't declare war, ignore any captures for the time being. if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), eMyPlayer) && !IsAtWar(ePlayer)) { bTheyCapturedFromUs = false; bHasOriginalCapital = false; } // Zero out all approaches except for WAR, and add 10x WAR bias. if (bTheyCapturedFromUs) { vApproachScores[CIV_APPROACH_WAR] += vApproachBias[CIV_APPROACH_WAR] * 10; vApproachScores[CIV_APPROACH_FRIENDLY] = 0; vApproachScores[CIV_APPROACH_NEUTRAL] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; } else { // Nuclear Gandhi doesn't typically attack City-States. He has bigger fish to fry. if (IsNuclearGandhi()) { if (!bTheyCapturedFromUs && !bHasOriginalCapital) { vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; bPotentialWarTarget = false; } // Unless they DARED to capture an original capital, or a city he founded! else vApproachScores[CIV_APPROACH_WAR] += 100000; } //////////////////////////////////// // ALLIES WITH MINOR? //////////////////////////////////// if (eAlly == eMyPlayer) { // Disfavor conquest and bullying if they are our ally vApproachScores[CIV_APPROACH_WAR] = 0; if (!GetPlayer()->IsCanBullyFriendlyCS()) { vApproachScores[CIV_APPROACH_HOSTILE] = 0; } vApproachScores[CIV_APPROACH_FRIENDLY] += bAnyFriendshipBonus ? vApproachBias[CIV_APPROACH_FRIENDLY] * 6 : vApproachBias[CIV_APPROACH_FRIENDLY] * 3; vApproachScores[CIV_APPROACH_NEUTRAL] = 0; bPotentialWarTarget = false; } else if (eAlly != NO_PLAYER && AvoidExchangesWithPlayer(eAlly, /*bWarOnly*/ true)) { // If we're at war with or planning war against their ally, don't try to bully their City-States vApproachScores[CIV_APPROACH_HOSTILE] = 0; } } //////////////////////////////////// // BULLY VIABILITY //////////////////////////////////// if (vApproachScores[CIV_APPROACH_HOSTILE] > 0) { int iBullyScore = GET_PLAYER(ePlayer).GetMinorCivAI()->CalculateBullyScore(eMyPlayer, false) * 100; if (iBullyScore > 0) vApproachScores[CIV_APPROACH_HOSTILE] += MOD_BALANCE_HEAVY_TRIBUTE ? iBullyScore / 5 : iBullyScore / 10; else vApproachScores[CIV_APPROACH_HOSTILE] = 0; } //////////////////////////////////// // MILITARY TARGET VALUE - how tough is this guy to kill? //////////////////////////////////// // Negative approach weights - cap at zero! for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { if (vApproachScores[iApproachLoop] <= 0) vApproachScores[iApproachLoop] = 0; } bool bGoodAttackTarget = GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(ePlayer) || bTheyCapturedFromUs; bool bWantsConquest = bAnyAggressionBonus && bGoodAttackTarget; switch (GetTargetValue(ePlayer)) { case TARGET_VALUE_IMPOSSIBLE: vApproachScores[CIV_APPROACH_WAR] *= bWantsConquest ? /*50*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_IMPOSSIBLE) : /*25*/ GD_INT_GET(MINOR_APPROACH_WAR_TARGET_IMPOSSIBLE); vApproachScores[CIV_APPROACH_HOSTILE] *= bWantsConquest ? /*50*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_IMPOSSIBLE) : /*25*/ GD_INT_GET(MINOR_APPROACH_WAR_TARGET_IMPOSSIBLE); vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 5; vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 5; break; case TARGET_VALUE_BAD: vApproachScores[CIV_APPROACH_WAR] *= bWantsConquest ? /*75*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_BAD) : /*33*/ GD_INT_GET(MINOR_APPROACH_WAR_TARGET_BAD); vApproachScores[CIV_APPROACH_HOSTILE] *= bWantsConquest ? /*75*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_BAD) : /*33*/ GD_INT_GET(MINOR_APPROACH_WAR_TARGET_BAD); vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 4; vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 4; break; case TARGET_VALUE_DIFFICULT: vApproachScores[CIV_APPROACH_WAR] *= bWantsConquest ? /*75*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_DIFFICULT) : /*50*/ GD_INT_GET(MINOR_APPROACH_WAR_TARGET_DIFFICULT); vApproachScores[CIV_APPROACH_HOSTILE] *= bWantsConquest ? /*75*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_DIFFICULT) : /*50*/ GD_INT_GET(MINOR_APPROACH_WAR_TARGET_DIFFICULT); vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 3; vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 3; break; case TARGET_VALUE_AVERAGE: vApproachScores[CIV_APPROACH_WAR] *= bWantsConquest ? /*125*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_AVERAGE) : /*75*/ GD_INT_GET(MINOR_APPROACH_WAR_TARGET_AVERAGE); vApproachScores[CIV_APPROACH_HOSTILE] *= bWantsConquest ? /*125*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_AVERAGE) : /*75*/ GD_INT_GET(MINOR_APPROACH_WAR_TARGET_AVERAGE); vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL] * 2; vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY] * 2; break; case TARGET_VALUE_FAVORABLE: vApproachScores[CIV_APPROACH_WAR] *= bWantsConquest ? /*150*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_FAVORABLE) : /*100*/ GD_INT_GET(MINOR_APPROACH_WAR_TARGET_FAVORABLE); vApproachScores[CIV_APPROACH_HOSTILE] *= bWantsConquest ? /*150*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_FAVORABLE) : /*100*/ GD_INT_GET(MINOR_APPROACH_WAR_TARGET_FAVORABLE); vApproachScores[CIV_APPROACH_NEUTRAL] += vApproachBias[CIV_APPROACH_NEUTRAL]; vApproachScores[CIV_APPROACH_FRIENDLY] += vApproachBias[CIV_APPROACH_FRIENDLY]; break; case TARGET_VALUE_SOFT: vApproachScores[CIV_APPROACH_WAR] *= bWantsConquest ? /*200*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_SOFT) : /*125*/ GD_INT_GET(MINOR_APPROACH_WAR_TARGET_SOFT); vApproachScores[CIV_APPROACH_HOSTILE] *= bWantsConquest ? /*200*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_SOFT) : /*125*/ GD_INT_GET(MINOR_APPROACH_WAR_TARGET_SOFT); break; case TARGET_VALUE_CAKEWALK: vApproachScores[CIV_APPROACH_WAR] *= bWantsConquest ? /*250*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_CAKEWALK) : /*150*/ GD_INT_GET(MINOR_APPROACH_WAR_TARGET_CAKEWALK); vApproachScores[CIV_APPROACH_HOSTILE] *= bWantsConquest ? /*250*/ GD_INT_GET(CONQUEST_WAR_MULTIPLIER_TARGET_CAKEWALK) : /*150*/ GD_INT_GET(MINOR_APPROACH_WAR_TARGET_CAKEWALK); break; default: break; } vApproachScores[CIV_APPROACH_WAR] /= 100; vApproachScores[CIV_APPROACH_HOSTILE] /= 100; //////////////////////////////////// // RECENTLY BULLIED // Avoid bullying/conquest if we recently bullied them //////////////////////////////////// if (GET_PLAYER(ePlayer).GetMinorCivAI()->IsRecentlyBulliedByMajor(eMyPlayer)) { vApproachScores[CIV_APPROACH_FRIENDLY] /= 2; vApproachScores[CIV_APPROACH_HOSTILE] = 0; if (!bIsGoodWarTarget) { vApproachScores[CIV_APPROACH_WAR] = 0; } } //////////////////////////////////// // CAN WE DECLARE WAR? // Disfavor conquest if we can't even do war with them! //////////////////////////////////// if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), eMyPlayer) && !IsAtWar(ePlayer)) { vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; bPotentialWarTarget = false; } // No target? if (!GetPlayer()->GetMilitaryAI()->HavePossibleAttackTarget(ePlayer)) { vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; bPotentialWarTarget = false; } // Don't waste our time trying to conquer City-States if they aren't even good targets... else if (!bIsGoodWarTarget && !bGoodAttackTarget) { vApproachScores[CIV_APPROACH_WAR] = 0; } if (MOD_GLOBAL_CS_OVERSEAS_TERRITORY) { // Don't bother going after City-States unless they don't have an ally or we're at war with the ally. if (eAlly != NO_PLAYER && !IsAtWar(eAlly)) { vApproachScores[CIV_APPROACH_WAR] = 0; bPotentialWarTarget = false; } } // If we're in bad shape, don't waste time trying to go after City-States. if (GetPlayer()->IsNoNewWars()) { vApproachScores[CIV_APPROACH_HOSTILE] = 0; if (!bTheyCapturedFromUs || GetPlayer()->IsInTerribleShapeForWar()) { vApproachScores[CIV_APPROACH_WAR] = 0; bPotentialWarTarget = false; } } // If we're already at war with this City-State and the war is going badly, abort! if (IsAtWar(ePlayer)) { if ((bTheyCapturedFromUs && GetStateAllWars() == STATE_ALL_WARS_LOSING) || (!bTheyCapturedFromUs && GetWarState(ePlayer) <= WAR_STATE_TROUBLED)) { vApproachScores[CIV_APPROACH_WAR] = 0; vApproachScores[CIV_APPROACH_HOSTILE] = 0; } } //////////////////////////////////// // PEACE TREATY - have we made peace with this player recently? If so, reduce war weight //////////////////////////////////// int iPeaceTreatyTurn = GET_TEAM(GetTeam()).GetTurnMadePeaceTreatyWithTeam(eTeam); if (iPeaceTreatyTurn > -1) { int iTurnsSincePeace = GC.getGame().getElapsedGameTurns() - iPeaceTreatyTurn; if (iTurnsSincePeace < GC.getGame().getHandicapInfo().getPeaceTreatyDampenerTurns()) { vApproachScores[CIV_APPROACH_HOSTILE] = 0; vApproachScores[CIV_APPROACH_WAR] = 0; } } //////////////////////////////////// // Teammates & Friends //////////////////////////////////// if (!bTheyCapturedFromUs && (vApproachScores[CIV_APPROACH_WAR] > 0 || vApproachScores[CIV_APPROACH_HOSTILE] > 0)) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer, true)) { if (eLoopPlayer == GetID()) continue; // Don't attack a minor that a teammate has allied/protected! // Let's not attack our friends' allied/protected City-States either. if (IsFriendOrAlly(eLoopPlayer)) { if (GET_PLAYER(ePlayer).GetMinorCivAI()->GetAlly() == eLoopPlayer) { vApproachScores[CIV_APPROACH_HOSTILE] = 0; vApproachScores[CIV_APPROACH_WAR] = 0; bPotentialWarTarget = false; break; } else if (GET_PLAYER(ePlayer).GetMinorCivAI()->IsProtectedByMajor(eLoopPlayer)) { vApproachScores[CIV_APPROACH_HOSTILE] = 0; vApproachScores[CIV_APPROACH_WAR] = 0; bPotentialWarTarget = false; break; } } // Did we make a promise? /* comment this out for now because this promise never resets, it currently only applies AI-to-AI, and the AI doesn't know when to break it else if (GET_PLAYER(ePlayer).GetMinorCivAI()->IsProtectedByMajor(eLoopPlayer)) { if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->MadeAttackCityStatePromise(GetID())) { vApproachScores[CIV_APPROACH_WAR] = 0; } if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->MadeBullyCityStatePromise(GetID())) { vApproachScores[CIV_APPROACH_HOSTILE] = 0; } } */ } } } //////////////////////////////////// // EMBASSIES // Don't sabotage our own embassies! //////////////////////////////////// if (!bTheyCapturedFromUs && vApproachScores[CIV_APPROACH_WAR] > 0) { int iCityLoop = 0; for (CvCity* pLoopCity = GET_PLAYER(ePlayer).firstCity(&iCityLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iCityLoop)) { std::set siPlots = pLoopCity->GetPlotList(); for (std::set::const_iterator it = siPlots.begin(); it != siPlots.end(); ++it) { CvPlot* pLoopPlot = GC.getMap().plotByIndex(*it); if (pLoopPlot && pLoopPlot->getOwner() == ePlayer && pLoopPlot->IsImprovementEmbassy()) { if (GET_PLAYER(pLoopPlot->GetPlayerThatBuiltImprovement()).getTeam() == GetTeam()) { vApproachScores[CIV_APPROACH_WAR] = 0; bPotentialWarTarget = false; break; } } } } } //////////////////////////////////// // TOO FAR TO BULLY // Don't bully if they're too far away //////////////////////////////////// if (vApproachScores[CIV_APPROACH_HOSTILE] > 0) { int iDistanceTurns = GetPlayer()->GetCityDistancePathLength(GET_PLAYER(ePlayer).getCapitalCity()->plot()); if (iDistanceTurns > 23) vApproachScores[CIV_APPROACH_HOSTILE] = 0; } //////////////////////////////////// // AGGRESSIVE MODE //////////////////////////////////// if (GC.getGame().IsAIAggressiveMode()) { vApproachScores[CIV_APPROACH_WAR] *= 2; vApproachScores[CIV_APPROACH_HOSTILE] *= 2; } // Negative approach weights - cap at zero! bool bAllZero = true; for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { if (vApproachScores[iApproachLoop] <= 0) vApproachScores[iApproachLoop] = 0; else bAllZero = false; } // This vector is what we'll use to sort CvWeightedVector< CivApproachTypes> vApproachScoresForSorting; // Transfer values from our normal int vector (which we need for logging) to the Weighted Vector we can sort for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eLoopApproach = (CivApproachTypes) iApproachLoop; vApproachScoresForSorting.push_back(eLoopApproach, vApproachScores[iApproachLoop]); } vApproachScoresForSorting.StableSortItems(); // All at zero? Neutral is the default. CivApproachTypes eApproach = bAllZero ? CIV_APPROACH_NEUTRAL : vApproachScoresForSorting.GetElement(0); SetCivApproach(ePlayer, eApproach); LogMinorCivApproachUpdate(ePlayer, &vApproachScores[0], eApproach, eOldApproach); // Set if they're a potential war target if (IsAtWar(ePlayer)) SetPotentialWarTarget(ePlayer, true); else if (bPotentialWarTarget) SetPotentialWarTarget(ePlayer, (IsGoingForDiploVictory() || bCloseToDiploVictory) ? eApproach < CIV_APPROACH_NEUTRAL : eApproach < CIV_APPROACH_FRIENDLY); else SetPotentialWarTarget(ePlayer, false); } // ----------------------------------------------------------------------------------------------- // //////////////////////////////////// // PEACE TREATY WILLINGNESS // //////////////////////////////////// void CvDiplomacyAI::DoUpdatePeaceTreatyWillingness(bool bMyTurn) { // AI is never allowed to make peace with anyone under these circumstances, no need to log it either if (GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR) || GC.getGame().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE) || GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) || GetPlayer()->IsAITeammateOfHuman() || GetPlayer()->IsVassalOfSomeone()) { CvString strEmpty; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; RefusePeaceTreaty(ePlayer, strEmpty); } return; } CvCity* pCapital = GetPlayer()->getCapitalCity(); bool bLog = bMyTurn && GC.getLogging() && GC.getAILogging(); // Only log peace willingness once per turn to prevent log spam vector vMyTeam = GET_TEAM(GetTeam()).getPlayers(); vector vPeaceBlockedMajors; vector vPeacePossibleMinors; vector vMajorTeamsAtWarWith; int iLoop = 0; //------------------------------------------------// // [PART 1: CHECK CRITICAL STATE & PEACE BLOCKS] // //------------------------------------------------// bool bCriticalState = GetPlayer()->IsEmpireSuperUnhappy() || !pCapital || pCapital->isInDangerOfFalling(true) || pCapital->getDamage() >= (pCapital->GetMaxHitPoints()/2); bool bMakePeaceWithAllMinors = bCriticalState; for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; bool bMajor = GET_PLAYER(eLoopPlayer).isMajorCiv(); if (GET_PLAYER(eLoopPlayer).isAlive() && IsAtWar(eLoopPlayer)) { if (bMajor) { TeamTypes eLoopTeam = GET_PLAYER(eLoopPlayer).getTeam(); if (std::find(vMajorTeamsAtWarWith.begin(), vMajorTeamsAtWarWith.end(), eLoopTeam) == vMajorTeamsAtWarWith.end()) vMajorTeamsAtWarWith.push_back(eLoopTeam); } PeaceBlockReasons ePeaceBlockReason = NUM_PEACE_BLOCK_REASONS; for (size_t i=0; iGetPeaceBlockReason(eLoopPlayer); if (ePotentialReason != NO_PEACE_BLOCK_REASON && ePotentialReason < ePeaceBlockReason) ePeaceBlockReason = ePotentialReason; } if (ePeaceBlockReason != NUM_PEACE_BLOCK_REASONS) { CvString strLogMessage; if (bLog) { CvString strPeaceBlockReason; FmtPeaceBlockReasonLogStr(strPeaceBlockReason, GetID(), eLoopPlayer, GetPeaceBlockReason(eLoopPlayer)); strLogMessage.Format("PEACE BLOCKED! "); strLogMessage += strPeaceBlockReason; if (GET_PLAYER(eLoopPlayer).isMinorCiv()) LogPeaceWillingnessReason(eLoopPlayer, strLogMessage); } if (bMajor) { RefusePeaceTreaty(eLoopPlayer, strLogMessage); vPeaceBlockedMajors.push_back(eLoopPlayer); } } else if (GET_PLAYER(eLoopPlayer).isMinorCiv()) vPeacePossibleMinors.push_back(eLoopPlayer); if (!bCriticalState) { WarStateTypes eWarState = GetWarState(eLoopPlayer); if (eWarState == WAR_STATE_NEARLY_DEFEATED) { bCriticalState = true; bMakePeaceWithAllMinors = true; } else if (eWarState <= WAR_STATE_TROUBLED) { if (!bMajor || GetPlayer()->IsNoNewWars() || !IsPhonyWar(eLoopPlayer) || GetWarScore(eLoopPlayer) <= WARSCORE_THRESHOLD_NEGATIVE) bMakePeaceWithAllMinors = true; } } } else if (bMajor) { CvString strEmpty; RefusePeaceTreaty(eLoopPlayer, strEmpty); } } // Extra logic for GLOBAL_CS_OVERSEAS_TERRITORY vector vEndangeredCityStates; bool bInTerribleShape = GetPlayer()->IsInTerribleShapeForWar(); if (MOD_GLOBAL_CS_OVERSEAS_TERRITORY && !bCriticalState && !bInTerribleShape) { for (int iPlayerLoop = MAX_MAJOR_CIVS; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes)iPlayerLoop; if (!GET_PLAYER(eLoopPlayer).isAlive()) continue; PlayerTypes eAlly = GET_PLAYER(eLoopPlayer).GetMinorCivAI()->GetAlly(); if (eAlly == NO_PLAYER || !IsAtWar(eAlly)) continue; // Failsafe so peace is *eventually* possible int iTurnsSinceCityCapture = GetPlayer()->GetNumTurnsSinceCityCapture(eLoopPlayer); int iMinorWarDuration = min(GET_TEAM(GetTeam()).GetNumTurnsAtWar(GET_PLAYER(eLoopPlayer).getTeam()), iTurnsSinceCityCapture); if (iMinorWarDuration >= 30) continue; // Need to check city danger here for a peace refusal check way below. for (CvCity* pLoopCity = GET_PLAYER(eLoopPlayer).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(eLoopPlayer).nextCity(&iLoop)) { if (pLoopCity->isCapital() || GET_PLAYER(pLoopCity->getOriginalOwner()).getTeam() == GetTeam() || IsTryingToLiberate(pLoopCity)) { if ((pLoopCity->isUnderSiege() || pLoopCity->IsBlockadedWaterAndLand()) && pLoopCity->IsInDangerFromPlayers(vMyTeam)) { vEndangeredCityStates.push_back(eLoopPlayer); break; } } } } } //-----------------------------------------// // [PART 2: EXTEND PEACE BLOCKS TO TEAMS] // //-----------------------------------------// vector vPotentiallyValidMajorTeams; // See if any teams have a peace blocked player and if so, eliminate those teams from consideration. for (std::vector::iterator it = vMajorTeamsAtWarWith.begin(); it != vMajorTeamsAtWarWith.end(); ++it) { PlayerTypes ePeaceBlocker = NO_PLAYER; vector vEnemyTeamMembers = GET_TEAM(*it).getPlayers(); for (size_t i=0; i::iterator it = vPeacePossibleMinors.begin(); it != vPeacePossibleMinors.end(); ++it) { PlayerTypes eMinor = *it; // Only consider peace with City-States if they aren't our war target or we're doing poorly (since there's no War Weariness) if (!bMakePeaceWithAllMinors && GetCSWarTargetPlayer() == eMinor) continue; if (!bCriticalState) { bool bProceed = false; // When not in a critical state, don't make peace with a City-State for the above reasons if a key city of theirs is in major danger from us specifically // Key cities: Their capital, a city they captured from us, or a city we want to liberate from them for (CvCity* pLoopCity = GET_PLAYER(eMinor).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(eMinor).nextCity(&iLoop)) { if (pLoopCity->isCapital() || GET_PLAYER(pLoopCity->getOriginalOwner()).getTeam() == GetTeam() || IsTryingToLiberate(pLoopCity)) { if ((pLoopCity->isUnderSiege() || pLoopCity->IsBlockadedWaterAndLand()) && pLoopCity->IsInDangerFromPlayers(vMyTeam)) { bProceed = true; break; } } } if (bProceed) continue; } GET_TEAM(GetTeam()).makePeace(GET_PLAYER(eMinor).getTeam(), true, false, GetID()); LogPeaceMade(eMinor); if (bLog) { CvString strLogMessage; if (bCriticalState) strLogMessage.Format("Making peace with all possible City-States because we're in a critical state!"); else if (bMakePeaceWithAllMinors) strLogMessage.Format("Making peace with all possible City-States because we're doing poorly in war!"); else strLogMessage.Format("This City-State is no longer our war target!"); LogPeaceWillingnessReason(eMinor, strLogMessage); } } } //--------------------------------------------// // [PART 4: AUTOMATIC PEACE: CRITICAL STATE] // //--------------------------------------------// if (vPotentiallyValidMajorTeams.empty()) return; vector vMakePeaceTeams; if (bCriticalState) { for (std::vector::iterator it = vPotentiallyValidMajorTeams.begin(); it != vPotentiallyValidMajorTeams.end(); ++it) { vMakePeaceTeams.push_back(*it); if (bLog) { vector vEnemyTeamMembers = GET_TEAM(*it).getPlayers(); for (size_t i=0; iGetPlayerTraits()->IsDiplomat(); bool bUABonusesFromCityConquest = false; CvPlayerTraits* pTraits = GetPlayer()->GetPlayerTraits(); if (!bInTerribleShape) { // Assyria, Japan, Indonesia, The Aztecs, Nuclear Gandhi, and anyone who keeps conquered buildings will prolong wars longer if they are winning and the enemy's cities are in danger from them. bUABonusesFromCityConquest = pTraits->IsTechFromCityConquer(); bUABonusesFromCityConquest |= pTraits->GetCityConquestGWAM() > 0; bUABonusesFromCityConquest |= pTraits->GetUniqueLuxuryQuantity() > 0; bUABonusesFromCityConquest |= pTraits->GetGoldenAgeFromVictory() > 0; bUABonusesFromCityConquest |= pTraits->IsKeepConqueredBuildings() || GetPlayer()->IsKeepConqueredBuildings(); bUABonusesFromCityConquest |= pTraits->GetCultureBonusModifierConquest() > 0; bUABonusesFromCityConquest |= pTraits->GetProductionBonusModifierConquest() > 0; bUABonusesFromCityConquest |= pTraits->IsFreeGreatWorkOnConquest(); bUABonusesFromCityConquest |= pTraits->IsConquestOfTheWorld(); bUABonusesFromCityConquest |= IsNuclearGandhi(); } PlayerTypes eHighestWarWearinessPlayer = GetPlayer()->GetHighestWarWearinessPlayer(true); int iUnhappinessFromWarWeariness = eHighestWarWearinessPlayer != NO_PLAYER ? GetPlayer()->GetUnhappinessFromWarWearinessWithTeam(GET_PLAYER(eHighestWarWearinessPlayer).getTeam(), true) : 0; int iWarProgressUnhappyPenalty = 0; int iWarProgressResourcePenalty = 0; if (MOD_BALANCE_VP) { int iHappiness = GetPlayer()->GetExcessHappiness(); if (iHappiness < /*50*/ GD_INT_GET(UNHAPPY_THRESHOLD)) { iWarProgressUnhappyPenalty += (GD_INT_GET(UNHAPPY_THRESHOLD) - iHappiness) * /*-2*/ GD_INT_GET(WAR_PROGRESS_PER_UNHAPPY); } } else if (GetPlayer()->GetExcessHappiness() < 0) iWarProgressUnhappyPenalty -= GetPlayer()->GetExcessHappiness() * /*-4*/ GD_INT_GET(WAR_PROGRESS_PER_UNHAPPY); for (int iResourceLoop = 0; iResourceLoop < GC.getNumResourceInfos(); iResourceLoop++) { ResourceTypes eResource = (ResourceTypes) iResourceLoop; if (eResource == NO_RESOURCE) continue; const CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource); if (pkResourceInfo == NULL) continue; if (pkResourceInfo->getResourceUsage() != RESOURCEUSAGE_STRATEGIC) continue; if (GetPlayer()->getResourceShortageValue(eResource) > 0) iWarProgressResourcePenalty += GetPlayer()->getResourceShortageValue(eResource) * /*-5*/ GD_INT_GET(WAR_PROGRESS_PER_STRATEGIC_DEFICIT); if (iWarProgressResourcePenalty <= -50) { iWarProgressResourcePenalty = -50; break; } } int iWarCount = max(GetPlayer()->CountNumDangerousMajorsAtWarWith(true, false) - 1, 0); //-------------------------------------------------------// // [PART 6: WAR STATE EVALUATION & AUTOMATIC DECISIONS] // //-------------------------------------------------------// for (std::vector::iterator it = vPotentiallyValidMajorTeams.begin(); it != vPotentiallyValidMajorTeams.end(); ++it) { // Data storage for later bool bPopulatedWarAllies = false; vector vEnemyTeamMembers = GET_TEAM(*it).getPlayers(); vector vOurWarAllies; vector vTheirWarAllies; vector vEnemyCitiesEndangered; vector vEnemyCitiesEndangeredByUs; std::map OurDangerAmounts; std::map TheirDangerAmounts; std::map OurSeriousDanger; std::map TheirSeriousDanger; // Good things >:) bool bCanVassalize = GET_TEAM(*it).canBecomeVassal(GetTeam()); bool bAnyCapitulationThresholdMet = false; bool bTheyAreInAnyDanger = false; bool bAnySeriousDangerThem = false; bool bNearlyWonWarWithNeighbor = false; bool bWantedPeaceLastTime = false; // Bad things >:( bool bDominationBlocked = false; bool bEndgameAggressive = false; bool bAnyBadWarState = false; bool bAnyNonHostileApproach = false; bool bCapturedKeyCity = false; bool bCapturedAnyCityFromUs = false; bool bCapturedAnyCityWeWantToLiberate = false; bool bWeAreInAnyDanger = false; bool bAnySeriousDangerUs = false; bool bAllWarsArePhony = false; bool bFailedAnyVassalagePrereq = false; int iLowestWarScore = 0; int iLowestTurnsSinceCityCapture = INT_MAX; for (size_t i=0; i= PEACE_TREATY_WHITE_PEACE || GetTreatyWillingToAccept(ePlayer) >= PEACE_TREATY_WHITE_PEACE; bCapturedKeyCity = IsCapitalCapturedBy(ePlayer, /*bTeammates*/ true, /*bCheckEver*/ true) || IsHolyCityCapturedBy(ePlayer, /*bTeammates*/ true, /*bCheckEver*/ true); bCapturedAnyCityFromUs = bCapturedKeyCity; vOurWarAllies = GetOffensiveWarAllies(ePlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ false); vOurWarAllies.push_back(GetID()); vTheirWarAllies = GetOffensiveWarAllies(ePlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ true); vTheirWarAllies.push_back(ePlayer); bPopulatedWarAllies = true; } if (!bInTerribleShape && bWorldConquest && GC.getGame().WouldMakingPeacePreventDominationVictory(GetID(), ePlayer)) bDominationBlocked = true; if (IsEndgameAggressiveTo(ePlayer)) bEndgameAggressive = true; if (GetCivApproach(ePlayer) > CIV_APPROACH_DECEPTIVE) bAnyNonHostileApproach = true; // Check war stats int iTurnsSinceCityCapture = GetPlayer()->GetNumTurnsSinceCityCapture(ePlayer); if (iTurnsSinceCityCapture < iLowestTurnsSinceCityCapture) iLowestTurnsSinceCityCapture = iTurnsSinceCityCapture; int iWarScore = GetWarScore(ePlayer); if (iWarScore < iLowestWarScore) iLowestWarScore = iWarScore; if (iWarScore >= 75) bAnyCapitulationThresholdMet = true; WarStateTypes eWarState = GetWarState(ePlayer); if (eWarState <= WAR_STATE_TROUBLED) { bAnyBadWarState = true; } else if (!bInTerribleShape && eWarState == WAR_STATE_NEARLY_WON && GetPlayer()->GetProximityToPlayer(ePlayer) >= PLAYER_PROXIMITY_CLOSE) { bNearlyWonWarWithNeighbor = true; } // Assess our comparative danger with each member of their team int iTheirDanger = 0; bool bSeriousDangerUs = false; bool bSeriousDangerThem = false; int iOurDanger = GetComparativeDanger(ePlayer, vOurWarAllies, vTheirWarAllies, /*passed by reference*/ iTheirDanger, bSeriousDangerUs, bSeriousDangerThem, vEnemyCitiesEndangered, vEnemyCitiesEndangeredByUs); OurDangerAmounts.insert(std::make_pair(ePlayer, iOurDanger)); TheirDangerAmounts.insert(std::make_pair(ePlayer, iTheirDanger)); OurSeriousDanger.insert(std::make_pair(ePlayer, bSeriousDangerUs)); TheirSeriousDanger.insert(std::make_pair(ePlayer, bSeriousDangerThem)); if (bSeriousDangerUs) bAnySeriousDangerUs = true; if (bSeriousDangerThem) bAnySeriousDangerThem = true; if (iOurDanger > 0) bWeAreInAnyDanger = true; if (iTheirDanger > 0) bTheyAreInAnyDanger = true; if (!IsPhonyWar(ePlayer)) bAllWarsArePhony = false; // Do we consider vassalizing this player? if (!bFailedAnyVassalagePrereq) { bool bConsiderVassalage = bCanVassalize && !bCapturedKeyCity && !bEndgameAggressive && !IsUntrustworthy(ePlayer); if (!bConsiderVassalage) bFailedAnyVassalagePrereq = true; } } // War duration for peace willingness resets when a city is captured. int iWarDuration = min(GET_TEAM(GetTeam()).GetNumTurnsAtWar(*it), iLowestTurnsSinceCityCapture); // Need at least 75 warscore with one civ on this team to be able to vassalize them if (!bAnyCapitulationThresholdMet) bFailedAnyVassalagePrereq = true; // No peace if they're in danger of losing a city and we're not. if (bAnySeriousDangerThem && !bAnySeriousDangerUs) { if (iWarDuration < 40) // Failsafe in case of a problem somewhere - limit the amount of time peace is impossible for { CvString strLogMessage; if (bLog) strLogMessage.Format("No peace! They're in serious danger of losing a city and we're not!"); for (size_t i=0; i= 40) { bool bAnyWarProgressSuccess = false; for (size_t i=0; i 0 && GET_PLAYER(eHighestWarWearinessPlayer).getTeam() == GET_PLAYER(ePlayer).getTeam()) iWarProgress -= iUnhappinessFromWarWeariness; if (iWarProgress > 0) { bAnyWarProgressSuccess = true; break; } } if (!bAnyWarProgressSuccess) { vMakePeaceTeams.push_back(*it); if (bLog) { CvString strLogMessage; strLogMessage.Format("Automatic peace offer: We're making no progress in this war!"); for (size_t i=0; igetOriginalOwner(); if (GET_PLAYER(eOriginalOwner).getTeam() == GET_TEAM(*it).GetID()) continue; CvPlot* pCityPlot = pLoopCity->plot(); if (!pCityPlot) continue; bool bEndangeredPeriod = std::find(vEnemyCitiesEndangered.begin(), vEnemyCitiesEndangered.end(), pLoopCity->GetID()) != vEnemyCitiesEndangered.end(); bool bEndangeredByOurTeam = bEndangeredPeriod && std::find(vEnemyCitiesEndangeredByUs.begin(), vEnemyCitiesEndangeredByUs.end(), pLoopCity->GetID()) != vEnemyCitiesEndangeredByUs.end(); // Ignore cities that are too far away from us (unless endangered). if (GetPlayer()->GetCityDistanceInPlots(pCityPlot) > 12) { if (!bEndangeredByOurTeam && (!bEndangeredPeriod || GET_PLAYER(eOriginalOwner).getTeam() != GetTeam())) continue; } if (GET_PLAYER(eOriginalOwner).getTeam() == GetTeam()) { bFailedAnyVassalagePrereq = true; bCapturedAnyCityFromUs = true; } // Is this a city that we want to liberate? else if (IsTryingToLiberate(pLoopCity)) { bFailedAnyVassalagePrereq = true; bCapturedAnyCityWeWantToLiberate = true; } if (bCapturedAnyCityFromUs && bCapturedAnyCityWeWantToLiberate) break; // Is this a valuable city endangered by our team? // Alternatively, if we're nasty/hateful or get bonuses from conquering cities, we're rarely willing to vassalize if ANY city is vulnerable. if (bEndangeredByOurTeam && !bFailedAnyVassalagePrereq) { bool bProceed = bUABonusesFromCityConquest || IsBackstabber() || GetMeanness() >= 8 || GetDiploBalance() <= 2; bProceed |= pLoopCity->IsOriginalMajorCapital() || pLoopCity->GetCityReligions()->IsHolyCityAnyReligion() || pLoopCity->HasAnyWonder(); if (bProceed) { if (GetPlayer()->GetMilitaryAI()->IsPreferredAttackTarget(pLoopCity)) { bFailedAnyVassalagePrereq = true; } else if (pLoopCity->isUnderSiege()) { bFailedAnyVassalagePrereq = true; } else { //look at the tactical map (is it up to date?) CvTacticalDominanceZone* pLandZone = GET_PLAYER(ePlayer).GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,false); CvTacticalDominanceZone* pWaterZone = GET_PLAYER(ePlayer).GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,true); if (pLandZone && pLandZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY) { bFailedAnyVassalagePrereq = true; } else if (pWaterZone && pWaterZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY) { bFailedAnyVassalagePrereq = true; } } } } } } } // If not ready for vassalage and we've nearly won a war with a neighbor, let's persevere. if (bFailedAnyVassalagePrereq && bNearlyWonWarWithNeighbor && iWarDuration < 30) { CvString strLogMessage; if (bLog) { strLogMessage.Format("No peace! We've almost won this war and "); CvString strNoVassalReason; if (!bCanVassalize) strNoVassalReason.Format("we're not able to make them a vassal!"); else if (bCapturedAnyCityFromUs) strNoVassalReason.Format("they have a nearby or endangered city they captured from us!"); else if (bEndgameAggressive) strNoVassalReason.Format("they're in danger of winning the game!"); else if (bCapturedAnyCityWeWantToLiberate) strNoVassalReason.Format("they have a nearby or endangered city we want to liberate!"); else if (!bAnyCapitulationThresholdMet) strNoVassalReason.Format("war score is less than 75!"); else strNoVassalReason.Format("we don't trust them to be obedient or we still want to capture cities from them!"); strLogMessage += strNoVassalReason; } for (size_t i=0; i::iterator iter = vEndangeredCityStates.begin(); iter != vEndangeredCityStates.end(); ++iter) { PlayerTypes eMinor = *iter; PlayerTypes eAlly = GET_PLAYER(eMinor).GetMinorCivAI()->GetAlly(); if (eAlly == NO_PLAYER || GET_PLAYER(eAlly).getTeam() != *it) continue; CvString strLogMessage; if (bLog) strLogMessage.Format("No peace! Making peace will lock us out of capturing their allied City-State's city!"); for (size_t i=0; i 0 && GET_PLAYER(eHighestWarWearinessPlayer).getTeam() == GET_TEAM(*it).GetID()) { bWarWearinessValid = true; int iPercentOfPop = iUnhappinessFromWarWeariness * 100 / max(1, GetPlayer()->getTotalPopulation()); if (iPercentOfPop >= 20) bWarWeary = true; } // Okay, so no automatic peace. But should we consider peace at all? bool bConsiderPeace = !bFailedAnyVassalagePrereq || bAllWarsArePhony || bAnySeriousDangerUs || bInTerribleShape || bWarWeary || bAnyNonHostileApproach || bAnyBadWarState || iWarDuration >= 30; // Not worth making peace with. Proceed. if (!bConsiderPeace) { CvString strLogMessage; if (bLog) strLogMessage.Format("No peace! We don't have a good enough reason to consider peace yet!"); for (size_t i=0; iGetPositiveWarScoreTourismMod() > 0 && GetHighestWarscorePlayer() != NO_PLAYER && GET_PLAYER(GetHighestWarscorePlayer()).getTeam() == GET_TEAM(*it).GetID() && !bInTerribleShape && !bAnySeriousDangerUs; if (bProlongAll) { iTooLongWarThreshold *= 2; iMinimumWarDuration *= 2; } int iDurationPenalty = iWarDuration - iMinimumWarDuration; bool bAnyAztecException = false; for (size_t i=0; igetNumCities() > 0 && GET_PLAYER(ePlayer).getNumCities() == 0 && GetRawMilitaryStrengthComparedToUs(ePlayer) < STRENGTH_POOR) continue; int iPeaceScore = 0; bool bProlong = bProlongAll; // War score is of course a significant factor // If we get a tourism bonus from high warscore or special conquest bonuses, let's not end early! int iWarScore = GetWarScore(ePlayer); if (iWarScore <= 0) { iPeaceScore += iWarScore / -2; bProlong = false; } else if (bInTerribleShape || bAnySeriousDangerUs) { iPeaceScore += iWarScore / 3; } else if (vEnemyCitiesEndangeredByUs.size() > 0) { bProlong |= bUABonusesFromCityConquest; if (!bProlong) iPeaceScore += iWarScore / 5; } else if (!bProlong) { iPeaceScore += iWarScore / 5; } // Lack of progress in war increases desire for peace (moreso if far away). if (iWarDuration > iTooLongWarThreshold) { switch (GetPlayer()->GetProximityToPlayer(ePlayer)) { case PLAYER_PROXIMITY_NEIGHBORS: iPeaceScore += iDurationPenalty; break; case PLAYER_PROXIMITY_CLOSE: iPeaceScore += (iDurationPenalty * 150) / 100; break; case PLAYER_PROXIMITY_FAR: iPeaceScore += iDurationPenalty * 2; break; case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: iPeaceScore += iDurationPenalty * 3; break; } } // War weariness if (bWarWearinessValid) { iPeaceScore += iUnhappinessFromWarWeariness / 4; if (bWarWeary || GetPlayer()->IsEmpireUnhappy()) { if (GetPlayer()->IsEmpireVeryUnhappy() || (GetPlayer()->IsEmpireUnhappy() && bWarWeary)) iPeaceScore += iUnhappinessFromWarWeariness / 2; else iPeaceScore += iUnhappinessFromWarWeariness / 3; } } // Danger considerations int iOurMultiplier = 1; int iTheirMultiplier = 1; if (!bInTerribleShape || GetWarState(ePlayer) == WAR_STATE_NEARLY_WON) { if (GetMilitaryStrengthComparedToUs(ePlayer) < STRENGTH_AVERAGE) { iOurMultiplier++; } if (IsEasyTarget(ePlayer)) { iOurMultiplier++; } if (GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(ePlayer)) { iOurMultiplier++; } if (GetWarState(ePlayer) == WAR_STATE_NEARLY_WON || GET_PLAYER(ePlayer).IsInTerribleShapeForWar()) { iOurMultiplier++; } } if (!GET_PLAYER(ePlayer).IsInTerribleShapeForWar()) { if (GetMilitaryStrengthComparedToUs(ePlayer) > STRENGTH_AVERAGE) { iTheirMultiplier++; } if (bInTerribleShape) { iTheirMultiplier++; } if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsEasyTarget(GetID())) { iTheirMultiplier++; } if (GetPlayer()->GetMilitaryAI()->IsExposedToEnemy(NULL, ePlayer)) { iTheirMultiplier++; } } // Grab stored danger level from the containers std::map::iterator OurDangerFinder = OurDangerAmounts.find(ePlayer); std::map::iterator TheirDangerFinder = TheirDangerAmounts.find(ePlayer); std::map::iterator OurSeriousDangerFinder = OurSeriousDanger.find(ePlayer); std::map::iterator TheirSeriousDangerFinder = TheirSeriousDanger.find(ePlayer); ASSERT(OurDangerFinder != OurDangerAmounts.end() && TheirDangerFinder != TheirDangerAmounts.end() && OurSeriousDangerFinder != OurSeriousDanger.end() && TheirSeriousDangerFinder != TheirSeriousDanger.end()); int iOurDanger = OurDangerFinder->second; int iTheirDanger = TheirDangerFinder->second; bool bSeriousDangerUs = OurSeriousDangerFinder->second; bool bSeriousDangerThem = TheirSeriousDangerFinder->second; // They're in danger and we're not? Let's hold out longer! if (iOurDanger == 0 && iTheirDanger > 0) { if (iWarDuration < 20 || iOurMultiplier > 2 || bSeriousDangerThem) { iPeaceScore -= (5 * iTheirDanger * iOurMultiplier); } else { iPeaceScore -= (2 * iTheirDanger * iOurMultiplier); } } // We're in danger and they're not? Let's hold out less! else if (iOurDanger > 0 && iTheirDanger == 0) { if (iWarScore <= GetWarscoreThresholdNegative()/2 || iTheirMultiplier > 2 || bSeriousDangerUs) { iPeaceScore += (5 * iOurDanger * iTheirMultiplier); } else { iPeaceScore += (2 * iOurDanger * iTheirMultiplier); } } // We're both in danger - how much? else { iPeaceScore -= (2 * iTheirDanger * iOurMultiplier); iPeaceScore += (2 * iOurDanger * iTheirMultiplier); } // Also consider the overall war state bool bLosingOverall = GetStateAllWars() == STATE_ALL_WARS_LOSING; switch (GetWarState(ePlayer)) { case NO_WAR_STATE_TYPE: UNREACHABLE(); // Being here would indicate we aren't at war with this player. case WAR_STATE_NEARLY_WON: iPeaceScore -= !bFailedAnyVassalagePrereq ? 0 : 10 * iOurMultiplier; break; case WAR_STATE_OFFENSIVE: iPeaceScore -= 5 * iOurMultiplier; break; case WAR_STATE_CALM: iPeaceScore -= bLosingOverall ? -5 * iTheirMultiplier : 2 * iOurMultiplier; break; case WAR_STATE_STALEMATE: case WAR_STATE_TROUBLED: iPeaceScore += bLosingOverall ? 5 * iTheirMultiplier : 2 * iTheirMultiplier; break; case WAR_STATE_DEFENSIVE: case WAR_STATE_NEARLY_DEFEATED: iPeaceScore += bLosingOverall ? 10 * iTheirMultiplier : 5 * iTheirMultiplier; break; } // Hold out for longer if we have military support from nearby allies int iAlliesMod = 0; if (!bInTerribleShape) { for (std::vector::iterator iter = vOurWarAllies.begin(); iter != vOurWarAllies.end(); ++iter) { if (*iter == GetID()) continue; if (!GET_PLAYER(*iter).isMajorCiv()) continue; if (GET_PLAYER(*iter).IsInTerribleShapeForWar()) continue; if (GET_PLAYER(*iter).GetProximityToPlayer(GetID()) < PLAYER_PROXIMITY_CLOSE) continue; if (GET_PLAYER(*iter).GetProximityToPlayer(ePlayer) < PLAYER_PROXIMITY_CLOSE) continue; if (GET_PLAYER(*iter).GetDiplomacyAI()->GetWarState(ePlayer) <= WAR_STATE_TROUBLED) continue; switch (GET_PLAYER(*iter).GetDiplomacyAI()->GetMilitaryStrengthComparedToUs(ePlayer)) { case STRENGTH_IMMENSE: case STRENGTH_POWERFUL: case STRENGTH_STRONG: break; case STRENGTH_AVERAGE: case STRENGTH_POOR: iAlliesMod -= 5; break; case STRENGTH_WEAK: iAlliesMod -= 10; break; case STRENGTH_PATHETIC: iAlliesMod -= 15; break; } } // Ongoing coop war? Minimum -10 modifier. if (iAlliesMod > -10 && GetGlobalCoopWarAgainstState(ePlayer) == COOP_WAR_STATE_ONGOING) { iAlliesMod = -10; } } if (iWarScore <= GetWarscoreThresholdNegative() && GetWarState(ePlayer) < WAR_STATE_OFFENSIVE) { iAlliesMod /= 2; } iPeaceScore += iAlliesMod; // If we want to conquer them, let's hold out longer. if (bFailedAnyVassalagePrereq && IsWantsToConquer(ePlayer)) { iPeaceScore -= 10; } // If they captured one of our key cities, let's hold out for significantly longer. if (bCapturedKeyCity) { iPeaceScore -= 15; } // If they're about to win the game, let's hold out for a lot longer. if (bEndgameAggressive) { iPeaceScore -= 20; } if (iPeaceScore > 0) { // If we're going for world conquest, we want to fight our wars until we get their capital or can vassalize them // We should also be more reluctant to make peace if they've captured cities from us, or cities that we want to liberate, // and also if we get a golden age when winning a war or if we get happiness or production modifiers from being at war // However, do not factor this in when losing if (!bInTerribleShape) { if (iWarScore >= 0 || (iWarScore > GetWarscoreThresholdNegative()/2 && GetRawTargetValue(ePlayer) >= TARGET_VALUE_AVERAGE && GetWarState(ePlayer) > WAR_STATE_TROUBLED)) { if (GetPlayer()->IsEmpireUnhappy()) { iPeaceScore -= GetPlayer()->getHappinessPerMajorWar(); } iPeaceScore -= GetPlayer()->getMilitaryProductionModPerMajorWar() / 2; iPeaceScore = max(iPeaceScore, 0); if (iWarScore > 0 && iWarScore < WARSCORE_THRESHOLD_POSITIVE && GetPlayer()->GetPlayerTraits()->GetGoldenAgeFromVictory() > 0) { bAnyAztecException = true; iPeaceScore /= 2; } else if (bCapturedAnyCityFromUs) { if (iWarScore > (iWarDuration / 2)) iPeaceScore /= 2; else { iPeaceScore *= 75; iPeaceScore /= 100; } } else if (bCapturedAnyCityWeWantToLiberate) { if ((bDiplomatic && iWarScore > 0) || iWarScore > iWarDuration) { if (bDiplomatic && iWarScore > iWarDuration) { iPeaceScore /= 2; } else { iPeaceScore *= 75; iPeaceScore /= 100; } } } else if (bWorldConquest) { if (!bFailedAnyVassalagePrereq || (GET_PLAYER(ePlayer).GetCapitalConqueror() != NO_PLAYER && GET_PLAYER(ePlayer).GetNumCapitalCities() <= 0)) { // If they're economically weak or a bad target, boost peace willingness if (GetEconomicStrengthComparedToUs(ePlayer) <= STRENGTH_WEAK || GetRawTargetValue(ePlayer) <= TARGET_VALUE_BAD) iPeaceScore *= 2; else if (GetTargetValue(ePlayer) == TARGET_VALUE_DIFFICULT) { iPeaceScore *= 3; iPeaceScore /= 2; } } else if (GET_PLAYER(ePlayer).GetCapitalConqueror() == NO_PLAYER || GET_PLAYER(ePlayer).GetNumCapitalCities() > 0) { iPeaceScore *= 75; iPeaceScore /= 100; } } } } // Overall state is bad and we're doing great in this war, or at least haven't lost any cities? Let's peace out! else if (!bCapturedAnyCityFromUs || !bFailedAnyVassalagePrereq || GetWarState(ePlayer) == WAR_STATE_NEARLY_WON) { iPeaceScore *= 2; // Both? Even more likely to make peace! if (!bCapturedAnyCityFromUs && (!bFailedAnyVassalagePrereq || GetWarState(ePlayer) == WAR_STATE_NEARLY_WON)) iPeaceScore *= 2; } // Modify based on leader flavors // High Meanness leaders will fight to the bitter end when losing, high Diplo Balance leaders like to return to status quo when winning if (iWarScore > 0) { iPeaceScore = AdjustConditionalModifier(iPeaceScore, GetDiploBalance()); } else if (iWarScore < 0) { iPeaceScore = AdjustConditionalModifier(iPeaceScore, GetMeanness(), true); } } if (iWarScore > 0) { switch (GetPlayer()->GetProximityToPlayer(ePlayer)) { case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: iPeaceScoreAggregate += iPeaceScore; iPeaceScoreDivisor += 1; break; case PLAYER_PROXIMITY_FAR: iPeaceScoreAggregate += iPeaceScore * 2; iPeaceScoreDivisor += 2; break; case PLAYER_PROXIMITY_CLOSE: iPeaceScoreAggregate += iPeaceScore * 3; iPeaceScoreDivisor += 3; break; case PLAYER_PROXIMITY_NEIGHBORS: iPeaceScoreAggregate += iPeaceScore * 4; iPeaceScoreDivisor += 4; break; } } else { switch (GET_PLAYER(ePlayer).GetProximityToPlayer(GetID())) { case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: iPeaceScoreAggregate += iPeaceScore; iPeaceScoreDivisor += 1; break; case PLAYER_PROXIMITY_FAR: iPeaceScoreAggregate += iPeaceScore * 2; iPeaceScoreDivisor += 2; break; case PLAYER_PROXIMITY_CLOSE: iPeaceScoreAggregate += iPeaceScore * 3; iPeaceScoreDivisor += 3; break; case PLAYER_PROXIMITY_NEIGHBORS: iPeaceScoreAggregate += iPeaceScore * 4; iPeaceScoreDivisor += 4; break; } } } //-----------------------------// // [PART 9: FINAL ASSESSMENT] // //-----------------------------// int iFinalPeaceScore = iPeaceScoreAggregate / iPeaceScoreDivisor; // Must be high enough to return a true desire for peace bool bLeeway = bWantedPeaceLastTime && (!bAnyAztecException || bAnyBadWarState || bAnySeriousDangerUs || bInTerribleShape); // if we were willing to make peace last update, add some leeway here to prevent flip-flopping int iThreshold = bLeeway ? /*17*/ GD_INT_GET(REQUEST_PEACE_LEEWAY_THRESHOLD) : /*20*/ GD_INT_GET(REQUEST_PEACE_TURN_THRESHOLD); int iThresholdReductionPerOtherWar = bLeeway ? /*3*/ GD_INT_GET(REQUEST_PEACE_LEEWAY_THRESHOLD_REDUCTION_PER_WAR) : /*2*/ GD_INT_GET(REQUEST_PEACE_THRESHOLD_REDUCTION_PER_WAR); iThreshold = max(iThreshold - max(iThresholdReductionPerOtherWar * iWarCount, 0), 1); if (iFinalPeaceScore >= iThreshold) { vMakePeaceTeams.push_back(*it); if (bLog) { CvString strLogMessage; strLogMessage.Format("Willing to make peace, Score for Peace: %d, Required Score: %d", iFinalPeaceScore, iThreshold); for (size_t i=0; i& vMakePeaceTeams, bool bCriticalState) { if (vMakePeaceTeams.empty()) return; // Do not give up too much or accept too little in a Peace Treaty, otherwise AI will routinely get ripped off. // We only factor in an additional penalty from War Weariness here if we're unhappy or doing terribly. PlayerTypes eHighestWarWearinessPlayer = NO_PLAYER; int iUnhappinessFromWarWeariness = 0; if (GetPlayer()->IsEmpireUnhappy() || GetPlayer()->IsInTerribleShapeForWar()) { eHighestWarWearinessPlayer = GetPlayer()->GetHighestWarWearinessPlayer(true); iUnhappinessFromWarWeariness = eHighestWarWearinessPlayer != NO_PLAYER ? GetPlayer()->GetUnhappinessFromWarWearinessWithTeam(GET_PLAYER(eHighestWarWearinessPlayer).getTeam(), true) : 0; } for (std::vector::iterator it = vMakePeaceTeams.begin(); it != vMakePeaceTeams.end(); ++it) { TeamTypes eTeam = GET_TEAM(*it).GetID(); int iWillingToOfferAggregate = 0; int iWillingToOfferDivisor = 0; int iWillingToAcceptAggregate = 0; int iWillingToAcceptDivisor = 0; int iWarWearinessPenalty = (iUnhappinessFromWarWeariness > 0 && GET_PLAYER(eHighestWarWearinessPlayer).getTeam() == eTeam) ? iUnhappinessFromWarWeariness : 0; vector vTheirTeam = GET_TEAM(eTeam).getPlayers(); for (size_t i=0; iGetProximityToPlayer(ePlayer)) { case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: iWillingToAcceptAggregate += iWarScore; iWillingToAcceptDivisor++; break; case PLAYER_PROXIMITY_FAR: iWillingToAcceptAggregate += iWarScore * 2; iWillingToAcceptDivisor += 2; break; case PLAYER_PROXIMITY_CLOSE: iWillingToAcceptAggregate += iWarScore * 3; iWillingToAcceptDivisor += 3; break; case PLAYER_PROXIMITY_NEIGHBORS: iWillingToAcceptAggregate += iWarScore * 4; iWillingToAcceptDivisor += 4; break; } } } // Now do the final assessment PeaceTreatyTypes eTreatyWillingToOffer = PEACE_TREATY_WHITE_PEACE; PeaceTreatyTypes eTreatyWillingToAccept = PEACE_TREATY_WHITE_PEACE; int iWillingToOfferScore = (iWillingToOfferAggregate > 0 && iWillingToOfferDivisor > 0) ? iWillingToOfferAggregate / iWillingToOfferDivisor : 0; int iWillingToAcceptScore = (iWillingToAcceptAggregate > 0 && iWillingToAcceptDivisor > 0) ? iWillingToAcceptAggregate / iWillingToAcceptDivisor : 0; if (iWillingToOfferScore >= /*100*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_UN_SURRENDER)) eTreatyWillingToOffer = PEACE_TREATY_UNCONDITIONAL_SURRENDER; else if (iWillingToOfferScore >= /*90*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_CAPITULATION)) eTreatyWillingToOffer = PEACE_TREATY_CAPITULATION; else if (iWillingToOfferScore >= /*80*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_CESSION)) eTreatyWillingToOffer = PEACE_TREATY_CESSION; else if (iWillingToOfferScore >= /*70*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_SURRENDER)) eTreatyWillingToOffer = PEACE_TREATY_SURRENDER; else if (iWillingToOfferScore >= /*60*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_SUBMISSION)) eTreatyWillingToOffer = PEACE_TREATY_SUBMISSION; else if (iWillingToOfferScore >= /*40*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_BACKDOWN)) eTreatyWillingToOffer = PEACE_TREATY_BACKDOWN; else if (iWillingToOfferScore >= /*30*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_SETTLEMENT)) eTreatyWillingToOffer = PEACE_TREATY_SETTLEMENT; else if (iWillingToOfferScore >= /*15*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_ARMISTICE)) eTreatyWillingToOffer = PEACE_TREATY_ARMISTICE; if (iWillingToAcceptScore >= /*100*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_UN_SURRENDER)) eTreatyWillingToAccept = PEACE_TREATY_UNCONDITIONAL_SURRENDER; else if (iWillingToAcceptScore >= /*90*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_CAPITULATION)) eTreatyWillingToAccept = PEACE_TREATY_CAPITULATION; else if (iWillingToAcceptScore >= /*80*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_CESSION)) eTreatyWillingToAccept = PEACE_TREATY_CESSION; else if (iWillingToAcceptScore >= /*70*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_SURRENDER)) eTreatyWillingToAccept = PEACE_TREATY_SURRENDER; else if (iWillingToAcceptScore >= /*60*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_SUBMISSION)) eTreatyWillingToAccept = PEACE_TREATY_SUBMISSION; else if (iWillingToAcceptScore >= /*40*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_BACKDOWN)) eTreatyWillingToAccept = PEACE_TREATY_BACKDOWN; else if (iWillingToAcceptScore >= /*30*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_SETTLEMENT)) eTreatyWillingToAccept = PEACE_TREATY_SETTLEMENT; else if (iWillingToAcceptScore >= /*15*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_ARMISTICE)) eTreatyWillingToAccept = PEACE_TREATY_ARMISTICE; // If we're in a critical state, always accept a white peace. if (bCriticalState) eTreatyWillingToAccept = PEACE_TREATY_WHITE_PEACE; // Make the chosen offer to the entire enemy team for (size_t i=0; i& vOurWarAllies, vector& vTheirWarAllies, int& iTheirDanger, bool& bSeriousDangerUs, bool& bSeriousDangerThem, vector& vEnemyCitiesEndangered, vector& vEnemyCitiesEndangeredByUs) { int iOurDanger = 0; ReligionTypes eMyReligion = GetPlayer()->GetReligions()->GetOwnedReligion(); ReligionTypes eTheirReligion = GET_PLAYER(ePlayer).GetReligions()->GetOwnedReligion(); vector vMyTeam = GET_TEAM(GetTeam()).getPlayers(); int iLoop = 0; for (CvCity* pLoopCity = m_pPlayer->firstCity(&iLoop); pLoopCity != NULL; pLoopCity = m_pPlayer->nextCity(&iLoop)) { if (pLoopCity->IsInDangerFromPlayers(vTheirWarAllies)) // Only care if we're in danger from them! { int iDangerMod = 1; //look at the tactical map (is it up to date?) CvTacticalDominanceZone* pLandZone = m_pPlayer->GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,false); CvTacticalDominanceZone* pWaterZone = m_pPlayer->GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,true); if (pLandZone && pLandZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY) iDangerMod++; if (pWaterZone && pWaterZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY) iDangerMod++; if (pLoopCity->isUnderSiege()) { if (pLoopCity->isInDangerOfFalling(true)) iDangerMod += 3; else iDangerMod++; if (pLoopCity->isInDangerOfFalling(true) || pLoopCity->getDamage() >= (pLoopCity->GetMaxHitPoints()/2)) { if (pLoopCity->getOriginalOwner() == GetID()) { bSeriousDangerUs = true; } else if (pLoopCity->isCapital() || pLoopCity->IsOriginalMajorCapital() || (eMyReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eMyReligion)) || (eTheirReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eTheirReligion)) || pLoopCity->HasAnyWonder() || pLoopCity->getNumNationalWonders() > 0) { bSeriousDangerUs = true; } } } if (iDangerMod > 1 && pLoopCity->IsInDanger(ePlayer) && GetPlayer()->GetMilitaryAI()->IsExposedToEnemy(pLoopCity, ePlayer)) { iDangerMod++; } if (pLoopCity->isCapital()) iDangerMod *= 3; else if (pLoopCity->IsOriginalMajorCapital() || (eMyReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eMyReligion)) || (eTheirReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eTheirReligion)) || pLoopCity->HasAnyWonder()) iDangerMod *= 2; else if (pLoopCity->GetCityReligions()->IsHolyCityAnyReligion() || pLoopCity->getNumNationalWonders() > 0) iDangerMod++; iOurDanger += iDangerMod; } } for (CvCity* pLoopCity = GET_PLAYER(ePlayer).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iLoop)) { if (pLoopCity->IsInDangerFromPlayers(vOurWarAllies)) // Only care if they're in danger from us! { int iDangerMod = 1; //look at the tactical map (is it up to date?) CvTacticalDominanceZone* pLandZone = GET_PLAYER(ePlayer).GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,false); CvTacticalDominanceZone* pWaterZone = GET_PLAYER(ePlayer).GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,true); if (pLandZone && pLandZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY) iDangerMod++; if (pWaterZone && pWaterZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY) iDangerMod++; if (pLoopCity->isUnderSiege()) { if (pLoopCity->isInDangerOfFalling(true)) iDangerMod += 3; else iDangerMod++; if (pLoopCity->isInDangerOfFalling(true) || pLoopCity->getDamage() >= (pLoopCity->GetMaxHitPoints()/2)) { bSeriousDangerThem = true; } } if (iDangerMod > 1) { vEnemyCitiesEndangered.push_back(pLoopCity->GetID()); if (pLoopCity->IsInDangerFromPlayers(vMyTeam)) { vEnemyCitiesEndangeredByUs.push_back(pLoopCity->GetID()); if (pLoopCity->IsInDanger(GetID()) && GetPlayer()->GetMilitaryAI()->IsPreferredAttackTarget(pLoopCity)) { iDangerMod++; } } } if (pLoopCity->isCapital()) iDangerMod *= 3; else if (pLoopCity->IsOriginalMajorCapital() || (eMyReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eMyReligion)) || (eTheirReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eTheirReligion)) || pLoopCity->HasAnyWonder()) iDangerMod *= 2; else if (pLoopCity->GetCityReligions()->IsHolyCityAnyReligion()) iDangerMod++; iTheirDanger += iDangerMod; } } return iOurDanger; } bool CvDiplomacyAI::IsWantsPeaceWithPlayer(PlayerTypes ePlayer) const { return IsAtWar(ePlayer) && !IsPeaceBlocked(ePlayer) && (GetTreatyWillingToOffer(ePlayer) > NO_PEACE_TREATY_TYPE || GetTreatyWillingToAccept(ePlayer) > NO_PEACE_TREATY_TYPE); } bool CvDiplomacyAI::IsPeaceBlocked(PlayerTypes ePlayer) const { return GetPeaceBlockReason(ePlayer) > NO_PEACE_BLOCK_REASON; } PeaceBlockReasons CvDiplomacyAI::GetPeaceBlockReason(PlayerTypes ePlayer) const { if (!IsAtWar(ePlayer)) return NO_PEACE_BLOCK_REASON; // We are always at war if (IsAlwaysAtWar(ePlayer)) return PEACE_BLOCK_REASON_ALWAYS_WAR; // One of us is someone's vassal! TeamTypes eMyTeam = GetTeam(); TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam(); if (GET_TEAM(eMyTeam).IsVassalOfSomeone() || GET_TEAM(eTeam).IsVassalOfSomeone()) return PEACE_BLOCK_REASON_VASSALAGE; // At war with City-State's ally? bool bMinor = GET_PLAYER(ePlayer).isMinorCiv(); if (bMinor && GET_PLAYER(ePlayer).GetMinorCivAI()->IsAllyAtWar(eMyTeam)) return PEACE_BLOCK_REASON_AT_WAR_WITH_ALLY; // Some game rule blocking this from occurring? if (!GET_TEAM(eMyTeam).canChangeWarPeace(eTeam) && GET_TEAM(eMyTeam).GetNumTurnsLockedIntoWar(eTeam) <= 0) return PEACE_BLOCK_REASON_SCENARIO; // Locked into war? if (GET_TEAM(eMyTeam).GetNumTurnsLockedIntoWar(eTeam) > 0) return PEACE_BLOCK_REASON_WAR_DEAL; // Too soon to make peace if (GET_PLAYER(ePlayer).isMajorCiv() && GET_TEAM(eMyTeam).GetNumTurnsAtWar(eTeam) < /*10*/ GD_INT_GET(WAR_MAJOR_MINIMUM_TURNS)) return PEACE_BLOCK_REASON_TOO_SOON; if (bMinor) { if (GET_TEAM(eMyTeam).GetNumTurnsAtWar(eTeam) < /*1*/ GD_INT_GET(WAR_MINOR_MINIMUM_TURNS)) return PEACE_BLOCK_REASON_TOO_SOON; if (GET_PLAYER(ePlayer).GetMinorCivAI()->GetPeaceBlockedTurns(eMyTeam) > 0) return PEACE_BLOCK_REASON_TOO_SOON; } // Planning a coop war against someone this guy has a Defensive Pact with? for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; TeamTypes eLoopTeam = GET_PLAYER(eLoopPlayer).getTeam(); if (eLoopTeam == GET_PLAYER(ePlayer).getTeam()) continue; if (IsPlayerValid(eLoopPlayer) && GET_TEAM(eLoopTeam).IsHasDefensivePact(eTeam)) { if (GetGlobalCoopWarAgainstState(eLoopPlayer) == COOP_WAR_STATE_PREPARING) return PEACE_BLOCK_REASON_COOP_WAR_AGAINST_DEFENSIVE_PACT; } } // Other player's team has no cities and aren't strong? We lose nothing from staying at war. if (GetPlayer()->getNumCities() > 0 && GET_TEAM(eTeam).getNumCities() == 0) { bool bConsiderPeace = false; vector vTheirTeam = GET_TEAM(eTeam).getPlayers(); for (size_t i=0; i= STRENGTH_POOR) { bConsiderPeace = true; break; } } if (!bConsiderPeace) return PEACE_BLOCK_REASON_NO_ENEMY_CITIES; } // Enemy captured a city and wants peace right away? Not if we can retaliate ... (sanity check: and our capital isn't about to fall) if (GetWarScore(ePlayer) > -100 && GET_PLAYER(ePlayer).GetNumTurnsSinceCityCapture(GetID()) <= 1 && CountUnitsAroundEnemyCities(ePlayer,3)>1) { CvCity* pCapital = GetPlayer()->getCapitalCity(); if (!pCapital || !pCapital->isInDangerOfFalling(true)) return PEACE_BLOCK_REASON_CITY_JUST_CAPTURED; } return NO_PEACE_BLOCK_REASON; } void CvDiplomacyAI::FmtPeaceBlockReasonLogStr(CvString& str, PlayerTypes eThisPlayer, PlayerTypes eAtWarPlayer, PeaceBlockReasons eReason) { switch (eReason) { case NO_PEACE_BLOCK_REASON: case PEACE_BLOCK_REASON_ALWAYS_WAR: UNREACHABLE(); // Peace is expected to be blocked, and this should not be called if we're always at war. case PEACE_BLOCK_REASON_VASSALAGE: str.Format("They are someone's vassal!"); break; case PEACE_BLOCK_REASON_AT_WAR_WITH_ALLY: str.Format("We're at war with this City-State's ally!"); break; case PEACE_BLOCK_REASON_SCENARIO: str.Format("Cannot change war/peace status due to special scenario rule!"); break; case PEACE_BLOCK_REASON_WAR_DEAL: str.Format("Locked into coop/3rd party war for %d more turns!", GET_TEAM(GET_PLAYER(eThisPlayer).getTeam()).GetNumTurnsLockedIntoWar(GET_PLAYER(eAtWarPlayer).getTeam())); break; case PEACE_BLOCK_REASON_TOO_SOON: str.Format("Too early to make peace!"); break; case PEACE_BLOCK_REASON_COOP_WAR_AGAINST_DEFENSIVE_PACT: str.Format("We're planning a coop war against someone they have a Defensive Pact with! Making peace would cancel the coop war!"); break; case PEACE_BLOCK_REASON_NO_ENEMY_CITIES: str.Format("Other player's team has no cities so we lose nothing from maintaining the war!"); break; case PEACE_BLOCK_REASON_CITY_JUST_CAPTURED: str.Format("Our city was just captured and we have units near the enemy's cities!"); break; } } /// How many of our units are near ePlayer's cities? int CvDiplomacyAI::CountUnitsAroundEnemyCities(PlayerTypes ePlayer, int iTurnRange) const { if (ePlayer == NO_PLAYER) return 0; int iCount = 0; int iUnitLoop = 0; for (CvUnit* pLoopUnit = m_pPlayer->firstUnit(&iUnitLoop); pLoopUnit != NULL; pLoopUnit = m_pPlayer->nextUnit(&iUnitLoop)) { if (!pLoopUnit->IsCanAttack() || pLoopUnit->isDelayedDeath() || pLoopUnit->isProjectedToDieNextTurn()) continue; if (GET_PLAYER(ePlayer).GetCityDistancePathLength(pLoopUnit->plot()) < iTurnRange) iCount++; } return iCount; } /// Disables the possibility of AI making peace with a major civ & logs the reason why void CvDiplomacyAI::RefusePeaceTreaty(PlayerTypes ePlayer, const CvString& strLogMessage) { SetTreatyWillingToOffer(ePlayer, NO_PEACE_TREATY_TYPE); SetTreatyWillingToAccept(ePlayer, NO_PEACE_TREATY_TYPE); if (!strLogMessage.empty()) LogPeaceWillingnessReason(ePlayer, strLogMessage); } /// Logs the reason for a peace refusal or automatic peace offer by this AI void CvDiplomacyAI::LogPeaceWillingnessReason(PlayerTypes ePlayer, const CvString& strLogMessage) { CvString strLogName = GC.getDiploPeaceLogFileName(GetPlayer()); CvString playerName = GetPlayer()->getCivilizationShortDescription(); CvString otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); FILogFile* pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line CvString strBaseString; strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName + " VS. " + otherPlayerName + ", "; strBaseString += strLogMessage; pLog->Msg(strBaseString); } // ----------------------------------------------------------------------------------------------- // //////////////////////////////////// // VASSAL TAXATION // //////////////////////////////////// /// Determine tax rates for all vassals, if we can void CvDiplomacyAI::DetermineVassalTaxRates() { if (!MOD_BALANCE_VP || GC.getGame().isOption(GAMEOPTION_NO_VASSALAGE) || GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR) || GetPlayer()->IsAITeammateOfHuman() || GetPlayer()->GetNumVassals() <= 0) return; vector vMyTeam = GET_TEAM(GetTeam()).getPlayers(); int iMyCurrentGPT = 0; int iAverageMeanness = 0; int iAverageDoFWillingness = 0; int iNumTeamMembers = 0; bool bNotLowestEconomicMight = false; bool bAnyoneInDireStraits = false; int iMinTaxRate = /*0*/ GD_INT_GET(VASSALAGE_VASSAL_TAX_PERCENT_MINIMUM); int iMaxTaxRate = /*25*/ GD_INT_GET(VASSALAGE_VASSAL_TAX_PERCENT_MAXIMUM); for (size_t i=0; iGetStateAllWars() == STATE_ALL_WARS_LOSING || GET_PLAYER(vMyTeam[i]).IsEmpireVeryUnhappy()) { bAnyoneInDireStraits = true; break; } // If another AI on our team is at least 20% weaker than us, we should let that AI decide if ((GetPlayer()->GetEconomicMight() * 80) > (GET_PLAYER(vMyTeam[i]).GetEconomicMight() * 100)) bNotLowestEconomicMight = true; iMyCurrentGPT += GET_PLAYER(vMyTeam[i]).getAvgGoldRate() * 100; iAverageMeanness += GET_PLAYER(vMyTeam[i]).GetDiplomacyAI()->GetMeanness(); iAverageDoFWillingness += GET_PLAYER(vMyTeam[i]).GetDiplomacyAI()->GetDoFWillingness(); iNumTeamMembers++; } if (!bAnyoneInDireStraits) { // If no one is in dire straits, let the player with the lowest economic might make this decision... if (bNotLowestEconomicMight) return; iAverageMeanness /= max(iNumTeamMembers, 1); iAverageDoFWillingness /= max(iNumTeamMembers, 1); } for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; if (!IsPlayerValid(ePlayer)) continue; if (!GET_TEAM(GetTeam()).CanSetVassalTax(ePlayer)) continue; vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); // Current tax rate int iTaxRate = GET_TEAM(GetTeam()).GetVassalTax(ePlayer); // Are we able to raise or lower? bool bCanLower = iTaxRate > iMinTaxRate; bool bCanRaise = iTaxRate < iMaxTaxRate; // If anyone is in dire straits, tax all vassals the maximum, their feelings be damned! if (bAnyoneInDireStraits) { if (bCanRaise) GET_TEAM(GetTeam()).DoApplyVassalTax(ePlayer, iMaxTaxRate); continue; } bool bTheyAreInDireStraits = GET_PLAYER(ePlayer).GetDiplomacyAI()->GetStateAllWars() == STATE_ALL_WARS_LOSING || GET_PLAYER(ePlayer).IsEmpireVeryUnhappy(); int iVassalCurrentGPT = GET_PLAYER(ePlayer).getAvgGoldRate(); int iVassalCurrentGross = GET_PLAYER(ePlayer).GetTreasury()->CalculateGrossGoldTimes100(); int iAverageOpinionScore = 0; int iNumOpinions = 0; for (size_t i=0; iGetCachedOpinionWeight(vTheirTeam[i]); iNumTeammateOpinions++; } iTeamOpinionScoreForPlayer /= max(iNumTeammateOpinions,1); iAverageOpinionScore += iTeamOpinionScoreForPlayer; iNumOpinions++; } iAverageOpinionScore /= max(iNumOpinions, 1); CivOpinionTypes eTeamOpinion = CIV_OPINION_ALLY; if (iAverageOpinionScore >= /*160*/ GD_INT_GET(OPINION_THRESHOLD_UNFORGIVABLE)) eTeamOpinion = CIV_OPINION_UNFORGIVABLE; else if (iAverageOpinionScore >= /*80*/ GD_INT_GET(OPINION_THRESHOLD_ENEMY)) eTeamOpinion = CIV_OPINION_ENEMY; else if (iAverageOpinionScore >= /*30*/ GD_INT_GET(OPINION_THRESHOLD_COMPETITOR)) eTeamOpinion = CIV_OPINION_COMPETITOR; else if (iAverageOpinionScore > /*-30*/ GD_INT_GET(OPINION_THRESHOLD_FAVORABLE)) eTeamOpinion = CIV_OPINION_NEUTRAL; else if (iAverageOpinionScore > /*-80*/ GD_INT_GET(OPINION_THRESHOLD_FRIEND)) eTeamOpinion = CIV_OPINION_FAVORABLE; else if (iAverageOpinionScore > /*-160*/ GD_INT_GET(OPINION_THRESHOLD_ALLY)) eTeamOpinion = CIV_OPINION_FRIEND; // Like him a lot? Tax the minimum! if (eTeamOpinion == CIV_OPINION_ALLY || GET_TEAM(GetTeam()).GetLiberatedByTeam() == GET_PLAYER(ePlayer).getTeam()) { if (iTaxRate > iMinTaxRate) GET_TEAM(GetTeam()).DoApplyVassalTax(ePlayer, iMinTaxRate); continue; } // Hate him? Tax the maximum! else if (eTeamOpinion <= CIV_OPINION_ENEMY) { if (iTaxRate < iMaxTaxRate) GET_TEAM(GetTeam()).DoApplyVassalTax(ePlayer, iMaxTaxRate); continue; } int iScoreForRaise = iAverageMeanness * 20; int iScoreForLower = iAverageDoFWillingness * 20; // Opinion matters switch (eTeamOpinion) { case CIV_OPINION_UNFORGIVABLE: case CIV_OPINION_ENEMY: case CIV_OPINION_ALLY: UNREACHABLE(); case CIV_OPINION_COMPETITOR: iScoreForRaise += 100; break; case CIV_OPINION_FAVORABLE: iScoreForLower += 50; break; case CIV_OPINION_FRIEND: iScoreForLower += 100; break; case CIV_OPINION_NEUTRAL: break; } // Strength compared to us matters switch (GetEconomicStrengthComparedToUs(ePlayer)) { case STRENGTH_IMMENSE: iScoreForRaise += 300; break; case STRENGTH_POWERFUL: iScoreForRaise += 200; break; case STRENGTH_STRONG: case STRENGTH_AVERAGE: iScoreForRaise += 100; break; case STRENGTH_POOR: iScoreForLower += 100; break; case STRENGTH_WEAK: iScoreForLower += 200; break; case STRENGTH_PATHETIC: iScoreForLower += 300; break; } switch (GetMilitaryStrengthComparedToUs(ePlayer)) { case STRENGTH_IMMENSE: iScoreForRaise += 300; break; case STRENGTH_POWERFUL: iScoreForRaise += 200; break; case STRENGTH_STRONG: case STRENGTH_AVERAGE: iScoreForRaise += 100; break; case STRENGTH_POOR: iScoreForLower += 50; break; case STRENGTH_WEAK: iScoreForLower += 100; break; case STRENGTH_PATHETIC: iScoreForLower += 150; break; } // Is our vassal's income worse than 60% of our GPT (or are they doing VERY poorly)? if (bTheyAreInDireStraits || (eTeamOpinion >= CIV_OPINION_NEUTRAL && (iVassalCurrentGPT * 100) < (iMyCurrentGPT * 60))) { iScoreForLower += 100; // Doing VERY poorly or worse than 40%? if (bTheyAreInDireStraits || (iVassalCurrentGPT * 100) < (iMyCurrentGPT * 40)) { iScoreForLower += 200; } } // Does our vassal have at least 60% of our GPT? else if ((iVassalCurrentGPT * 100) >= (iMyCurrentGPT * 60)) { iScoreForRaise += 150; } // At least 80%? else if ((iVassalCurrentGPT * 100) >= (iMyCurrentGPT * 80)) { iScoreForRaise += 225; } // Is our vassal doing better than us monetarily? else if (iVassalCurrentGPT >= iMyCurrentGPT) { iScoreForRaise += 300; } // Less likely to aggressively tax them if we liberated them, or if they're a voluntary vassal we like... if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetLiberatedByTeam() == GetTeam() || (IsVoluntaryVassalage(ePlayer) && eTeamOpinion == CIV_OPINION_FRIEND)) { iScoreForRaise /= 2; } int iNewTaxValue = 0; if (bCanRaise) bCanRaise = (iScoreForRaise > iScoreForLower); if (bCanLower) bCanLower = (iScoreForLower > iScoreForRaise); // Decided we might raise - by how much? if (bCanRaise) { if (iScoreForRaise >= 350) { iNewTaxValue = iMaxTaxRate; } else if (iScoreForRaise >= 250) { iNewTaxValue = (iMaxTaxRate * 80) / 100; iNewTaxValue *= 5; iNewTaxValue /= 5; } else if (iScoreForRaise >= 200) { iNewTaxValue = (iMaxTaxRate * 60) / 100; iNewTaxValue *= 5; iNewTaxValue /= 5; } else if (iScoreForRaise >= 125) { iNewTaxValue = (iMaxTaxRate * 40) / 100; iNewTaxValue *= 5; iNewTaxValue /= 5; } else { iNewTaxValue = (iMaxTaxRate * 20) / 100; iNewTaxValue *= 5; iNewTaxValue /= 5; } int iCurrentTaxAmount = iTaxRate * iVassalCurrentGross; int iNewTaxAmount = iNewTaxValue * iVassalCurrentGross; // Only apply the tax increase if we gain at least 3 GPT in profit if (iNewTaxValue > iTaxRate && ((iNewTaxAmount - iCurrentTaxAmount) >= 30000)) GET_TEAM(GetTeam()).DoApplyVassalTax(ePlayer, iNewTaxValue); } // Decided we might lower - by how much? else if (bCanLower) { if (iScoreForLower >= 350) { iNewTaxValue = iMinTaxRate; } else if (iScoreForLower >= 250) { iNewTaxValue = (iMaxTaxRate * 20) / 100; iNewTaxValue *= 5; iNewTaxValue /= 5; } else if (iScoreForLower >= 200) { iNewTaxValue = (iMaxTaxRate * 40) / 100; iNewTaxValue *= 5; iNewTaxValue /= 5; } else if (iScoreForLower >= 125) { iNewTaxValue = (iMaxTaxRate * 60) / 100; iNewTaxValue *= 5; iNewTaxValue /= 5; } else { iNewTaxValue = (iMaxTaxRate * 80) / 100; iNewTaxValue *= 5; iNewTaxValue /= 5; } int iCurrentTaxAmount = iTaxRate * iVassalCurrentGross; int iNewTaxAmount = iNewTaxValue * iVassalCurrentGross; // Only apply the tax decrease if they gain at least 1 GPT in profit if (iNewTaxValue < iTaxRate && ((iCurrentTaxAmount - iNewTaxAmount) >= 10000)) GET_TEAM(GetTeam()).DoApplyVassalTax(ePlayer, iNewTaxValue); } } } /// eMasterTeam has changed our taxes void CvDiplomacyAI::DoVassalTaxChanged(TeamTypes eMasterTeam, bool bTaxesLowered) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eLoopPlayer).getTeam() == eMasterTeam) { SetVassalTaxRaised(eLoopPlayer, !bTaxesLowered); SetVassalTaxLowered(eLoopPlayer, bTaxesLowered); } } } // ----------------------------------------------------------------------------------------------- // //////////////////////////////////// // DEMANDS // //////////////////////////////////// /// Updates our desire to make a demand from a player void CvDiplomacyAI::DoUpdateDemands() { if (GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR) || GC.getGame().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE) || GetPlayer()->IsVassalOfSomeone()) return; // Look at the World Congress sanctions situation before we begin bool bOnlyVassalTargets = false; vector vSanctionTargets; CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (pLeague) { for (ActiveResolutionList::iterator it = pLeague->m_vActiveResolutions.begin(); it != pLeague->m_vActiveResolutions.end(); ++it) { // Can't make demands if sanctioned, except with vassals if (it->GetEffects()->bEmbargoPlayer && (PlayerTypes)it->GetProposerDecision()->GetDecision() == GetID()) { if (GetPlayer()->GetNumVassals() == 0) return; bOnlyVassalTargets = true; } } if (!bOnlyVassalTargets) { for (EnactProposalList::iterator it = pLeague->m_vEnactProposals.begin(); it != pLeague->m_vEnactProposals.end(); ++it) { if (it->GetEffects()->bEmbargoPlayer) { PlayerTypes eTarget = (PlayerTypes)it->GetProposerDecision()->GetDecision(); // If we're being targeted with sanctions, don't bother with demands unless we have vassals - we have bigger fish to fry + the deal will be cancelled if the resolution passes if (eTarget == GetID()) { if (GetPlayer()->GetNumVassals() == 0) { SetDemandTargetPlayer(NO_PLAYER); return; } else { bOnlyVassalTargets = true; break; } } else vSanctionTargets.push_back(eTarget); } } } } CvWeightedVector vePotentialDemandTargets; bool bExistingValidTarget = false; int iWarCount = GetPlayer()->CountNumDangerousMajorsAtWarWith(true, false); for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; // Must be a valid player if (!IsPlayerValid(ePlayer) || !GET_PLAYER(ePlayer).isMajorCiv()) continue; // If we're planning a war, are friendly towards them, or are afraid of them, don't make a demand of them...doesn't mesh well CivApproachTypes eApproach = GetCivApproach(ePlayer); if (eApproach == CIV_APPROACH_WAR || eApproach == CIV_APPROACH_FRIENDLY || eApproach == CIV_APPROACH_AFRAID) continue; // If approach is NEUTRAL and opinion is better than ENEMY, don't demand if (eApproach == CIV_APPROACH_NEUTRAL && GetCivOpinion(ePlayer) > CIV_OPINION_ENEMY) continue; // Don't make demands of them too often int iTooSoonTurns = /*20*/ GD_INT_GET(DEMAND_TURN_LIMIT_MIN) + /*10*/ GD_INT_GET(DEMAND_TURN_LIMIT_RAND); if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_DEMAND) < max(iTooSoonTurns, 10)) continue; // Don't make demands if sanctions between us could be a problem (excluding master-vassal demands) if (!IsMaster(ePlayer)) { if (bOnlyVassalTargets) continue; if (std::find(vSanctionTargets.begin(), vSanctionTargets.end(), ePlayer) != vSanctionTargets.end()) continue; } // Must be a valid demand target // This checks a bunch of invalidating conditions; if something that's supposed to be checked here turns up below, it'll trigger an UNREACHABLE() crash int iDemandValueScore = 0; if (!IsValidDemandTarget(ePlayer, /*passed by reference*/ iDemandValueScore)) continue; int iWeight = (GetBoldness()/2); switch (GetPlayer()->GetProximityToPlayer(ePlayer)) { case PLAYER_PROXIMITY_NEIGHBORS: iWeight += 10; break; case PLAYER_PROXIMITY_CLOSE: iWeight -= 10; break; default: UNREACHABLE(); } // Bad approach? switch (eApproach) { case CIV_APPROACH_HOSTILE: iWeight += 20; break; case CIV_APPROACH_GUARDED: iWeight += 10; break; case CIV_APPROACH_DECEPTIVE: iWeight += 10; break; case CIV_APPROACH_NEUTRAL: iWeight += -10; break; default: UNREACHABLE(); } switch (GetCivOpinion(ePlayer)) { case CIV_OPINION_UNFORGIVABLE: iWeight += 20; break; case CIV_OPINION_ENEMY: iWeight += 15; break; case CIV_OPINION_COMPETITOR: iWeight += 10; break; case CIV_OPINION_NEUTRAL: iWeight -= 5; break; case CIV_OPINION_FAVORABLE: iWeight -= 15; break; default: UNREACHABLE(); } // Must be a favorable target. switch (GetTargetValue(ePlayer)) { case TARGET_VALUE_CAKEWALK: iWeight += 30; break; case TARGET_VALUE_SOFT: iWeight += 20; break; case TARGET_VALUE_FAVORABLE: iWeight += 10; break; default: UNREACHABLE(); } // Can't be stronger than us militarily. switch (GetMilitaryStrengthComparedToUs(ePlayer)) { case STRENGTH_PATHETIC: iWeight += 30; break; case STRENGTH_WEAK: iWeight += 20; break; case STRENGTH_POOR: iWeight += 10; break; case STRENGTH_AVERAGE: iWeight -= 10; break; default: UNREACHABLE(); } // Better for us if we're stronger than them economically. switch (GetEconomicStrengthComparedToUs(ePlayer)) { case STRENGTH_PATHETIC: iWeight += 20; break; case STRENGTH_WEAK: iWeight += 10; break; case STRENGTH_POOR: iWeight += 5; break; case STRENGTH_AVERAGE: iWeight -= 5; break; case STRENGTH_STRONG: iWeight -= 15; break; case STRENGTH_POWERFUL: iWeight -= 25; break; default: UNREACHABLE(); } // Easy target? if (IsEasyTarget(ePlayer)) { iWeight += 20; } // Already at war? if (iWarCount > 0) { iWeight -= (iWarCount * 10); } if (iWeight <= 0) { continue; } iWeight += iDemandValueScore; // Is this our existing target? if (GetDemandTargetPlayer() == ePlayer) { bExistingValidTarget = true; } vePotentialDemandTargets.push_back(ePlayer, iWeight); } vePotentialDemandTargets.StableSortItems(); // Any valid possibilities? if (vePotentialDemandTargets.size() > 0) { // If we already have a valid demand target, don't assign a new one // Otherwise, start the demand process against the player with the highest weight if (!bExistingValidTarget) { SetDemandTargetPlayer(vePotentialDemandTargets.GetElement(0)); } } else { SetDemandTargetPlayer(NO_PLAYER); } } /// Returns if ePlayer is a valid target to make a demand /// Only checks some of the conditions, as this is called by both SelectBestApproachTowardsMajorCiv() and DoUpdateDemands() for different reasons bool CvDiplomacyAI::IsValidDemandTarget(PlayerTypes ePlayer, int& iDemandValueScore) { // Are we doing badly? if (GetPlayer()->IsNoNewWars()) return false; // Have to be able to contact this player if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && (MOD_DIPLOAI_SHUT_UP_DEMANDS || GC.getGame().isReallyNetworkMultiPlayer())) return false; // Too friendly with them? if (IsDoFAccepted(ePlayer) || IsHasDefensivePact(ePlayer) || IsHasResearchAgreement(ePlayer) || GetCivOpinion(ePlayer) >= CIV_OPINION_FRIEND) return false; // Don't make a demand if endgame aggressive, could interfere with future war plans // And if we're endgame aggressive they're probably not weak enough to justify making a demand in the first place if (IsEndgameAggressiveTo(ePlayer)) return false; // Can't be stronger than us militarily, or too hard to attack. if (GetMilitaryStrengthComparedToUs(ePlayer) > STRENGTH_AVERAGE) return false; if (GetTargetValue(ePlayer) < TARGET_VALUE_FAVORABLE) return false; // Can't be extremely strong economically. if (GetEconomicStrengthComparedToUs(ePlayer) == STRENGTH_IMMENSE) return false; // Have to be able to declare war against this player if (IsAtWar(ePlayer) || !GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) return false; // Too far away? if (!GetPlayer()->getCapitalCity() || !GET_PLAYER(ePlayer).getCapitalCity()) return false; if (GetPlayer()->GetProximityToPlayer(ePlayer) < PLAYER_PROXIMITY_CLOSE) return false; // Can't make/accept demands if sanctioned CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (pLeague != NULL && pLeague->IsTradeEmbargoed(GetID(), ePlayer)) return false; // Resurrection / liberation in either direction? if (IsLiberator(ePlayer, false, false) || IsMasterLiberatedMeFromVassalage(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->WasResurrectedBy(GetID()) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsMasterLiberatedMeFromVassalage(GetID())) return false; // Is it sane to attack this player? if (!IsWarSane(ePlayer)) return false; // Good city attack target? if (!GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(ePlayer)) return false; // Do not make a demand if a coop war is planned, or if our AI teammates are planning war if (AvoidExchangesWithPlayer(ePlayer, /*bWarOnly*/ true, /*bIgnoreSelfApproach*/ true)) return false; // Do not make a demand if they own any of our cities - could interfere with future war plans if (GetPlayer()->GetNumOurCitiesOwnedBy(ePlayer) > 0) return false; // If they're a vassal, estimate if their master will protect them // This estimate can be inaccurate, and that's intended (to avoid AI cheating, to trigger the diplo bonus, and to make things interesting) TeamTypes eTheirMaster = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetMaster(); if (eTheirMaster != NO_TEAM && eTheirMaster != GetTeam() && GET_TEAM(GetTeam()).isHasMet(eTheirMaster)) { vector vMasterTeam = GET_TEAM(eTheirMaster).getPlayers(); for (size_t i=0; i < vMasterTeam.size(); i++) { CvPlayer* pMaster = &GET_PLAYER(vMasterTeam[i]); if (!pMaster->isAlive() || pMaster->getNumCities() == 0) continue; // We are afraid of the master - abort! // OK to check this because SelectBestApproachTowardsMajorCiv() is not called for vassals if (GetCivApproach(vMasterTeam[i]) == CIV_APPROACH_AFRAID) return false; // This master is not strong enough to protect them. // Note: Vassal will check raw military strength (w/o Boldness) while demander will check target value with a Boldness reduction // These values will not always match, allowing for deliberate inaccuracy if (GetTargetValue(vMasterTeam[i]) < TARGET_VALUE_AVERAGE) continue; // Is their master neighbors with us? if (pMaster->GetProximityToPlayer(GetID()) == PLAYER_PROXIMITY_NEIGHBORS) return false; // Master is at least as close to them as we are? if (pMaster->GetProximityToPlayer(ePlayer) >= GetPlayer()->GetProximityToPlayer(ePlayer)) return false; } } // Do they even have anything worthwhile to give us? iDemandValueScore = GetPlayerDemandValueScore(ePlayer); if (iDemandValueScore <= 0) return false; // Don't make demands if they're too far away... // Do this check last, for performance CvCity* pTheirClosestCity = GetPlayer()->GetClosestCityToUsByPlots(ePlayer); if (!pTheirClosestCity) return false; int iDistanceTurns = GetPlayer()->GetCityDistancePathLength(pTheirClosestCity->plot()); return iDistanceTurns <= 23; } /// How much value can we get from ePlayer if we made a demand of them? int CvDiplomacyAI::GetPlayerDemandValueScore(PlayerTypes ePlayer) { int iIdealValue = 25 * (GetPlayer()->GetDiplomacyAI()->GetMeanness() + GetPlayer()->GetCurrentEra()); int Value = NUM_STRENGTH_VALUES - (int)GetPlayer()->GetDiplomacyAI()->GetMilitaryStrengthComparedToUs(ePlayer); if (Value > 0) { iIdealValue *= Value; } else { return 0; } //End the gift exchange at the start of each round. GetPlayer()->GetDiplomacyAI()->SetOfferingGift(ePlayer, false); GetPlayer()->GetDiplomacyAI()->SetOfferedGift(ePlayer, false); // We can use this deal pointer to form a trade offer CvDeal* pDeal = GC.getGame().GetGameDeals().GetTempDeal(); pDeal->ClearItems(); pDeal->SetRequestingPlayer(NO_PLAYER); pDeal->SetFromPlayer(GetID()); pDeal->SetToPlayer(ePlayer); pDeal->SetDuration(GC.getGame().getGameSpeedInfo().GetDealDuration()); int iDemandValue = GetPlayer()->GetDealAI()->GetPotentialDemandValue(ePlayer, pDeal, iIdealValue); pDeal->ClearItems(); if (iDemandValue < (iIdealValue/2)) return 0; return iDemandValue; } // ----------------------------------------------------------------------------------------------- // //////////////////////////////////// // WAR! // //////////////////////////////////// /// Handles declarations of War for this AI void CvDiplomacyAI::MakeWar() { CvWeightedVector playerList; if (m_eDiploMode == DIPLO_SPECIFIC_PLAYER) { DoMakeWarOnPlayer(m_eTargetPlayer); } else { for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eTarget = (PlayerTypes)iPlayerLoop; if (IsValidUIDiplomacyTarget(eTarget) || (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY)) { if (IsPlayerValid(eTarget)) { int iWeight = GET_PLAYER(eTarget).isMajorCiv() ? GetPlayerStrategicApproachValue(eTarget, CIV_APPROACH_WAR) : (int)GetTargetValue(eTarget) + 1; // Square the distance enum to make it crucial iWeight *= (1 + (int)GetPlayer()->GetProximityToPlayer(eTarget)); iWeight *= (1 + (int)GetPlayer()->GetProximityToPlayer(eTarget)); playerList.push_back(eTarget, iWeight); } } } playerList.StableSortItems(); for (int iI = 0; iI < playerList.size(); iI++) { DoMakeWarOnPlayer(playerList.GetElement(iI)); } } } /// Handles declarations of War for this AI void CvDiplomacyAI::DoMakeWarOnPlayer(PlayerTypes eTargetPlayer) { //if we are already at war, there is not much to do if (IsAtWar(eTargetPlayer)) { SetArmyInPlaceForAttack(eTargetPlayer, false); return; } //sometimes we cannot declare war if (!IsPlayerValid(eTargetPlayer) || !GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(eTargetPlayer).getTeam(), GetID())) { SetArmyInPlaceForAttack(eTargetPlayer, false); if (GetPlayer()->getFirstOffensiveAIOperation(eTargetPlayer) != NULL) { GetPlayer()->StopAllLandOffensiveOperationsAgainstPlayer(eTargetPlayer,AI_ABORT_TARGET_NOT_VALID); GetPlayer()->StopAllSeaOffensiveOperationsAgainstPlayer(eTargetPlayer,AI_ABORT_TARGET_NOT_VALID); } return; } bool bWantToAttack = false; bool bWantShowOfForce = false; // Minor Civ if (GET_PLAYER(eTargetPlayer).isMinorCiv()) { bWantToAttack = GetCSWarTargetPlayer() == eTargetPlayer; bWantShowOfForce = GetCSBullyTargetPlayer() == eTargetPlayer; } // Major Civ else { bWantToAttack = GetCivApproach(eTargetPlayer) == CIV_APPROACH_WAR; bWantShowOfForce = GetDemandTargetPlayer() == eTargetPlayer; } if (IsArmyInPlaceForAttack(eTargetPlayer)) { SetArmyInPlaceForAttack(eTargetPlayer, false); // Our Approach with this player calls for war if (bWantToAttack) { // Don't declare war on a major until any coop war allies are ready! // If we're waiting due to a coop war, the AI operation will be disbanded in CvPlayerAI::AI_doTurnUnitsPre(), and we'll request a new one next turn in this function // The IsArmyInPlaceForAttack/SetArmyInPlaceForAttack loop is ugly and should probably be done better. if (GET_PLAYER(eTargetPlayer).isMinorCiv() || GetGlobalCoopWarAgainstState(eTargetPlayer) != COOP_WAR_STATE_PREPARING) { // FIXME: Okay, so we're ready to declare war...but, can we exploit Defensive Pacts to do so with fewer diplomatic penalties? DeclareWar(eTargetPlayer); } } else if (bWantShowOfForce) { //minors will be bullied automatically! if (GET_PLAYER(eTargetPlayer).isMajorCiv()) DoMakeDemand(eTargetPlayer); } else { //it's possible for an operation to be ongoing here if it was a demand, so cancel it //sneak attacks vs. major civs are aborted when DoUpdateWarTargets() calls SetCivApproach() //sneak attacks vs. minor civs are aborted in SelectBestApproachTowardsMinorCiv() or DoUpdateMinorCivApproaches() CvAIOperation* pCurrentDemandOperation = GetPlayer()->getFirstOffensiveAIOperation(eTargetPlayer); if (pCurrentDemandOperation) { pCurrentDemandOperation->LogOperationSpecialMessage("Show of force cancelled, probably preoccupied with something else"); GetPlayer()->StopAllLandOffensiveOperationsAgainstPlayer(eTargetPlayer,AI_ABORT_DIPLO_OPINION_CHANGE); GetPlayer()->StopAllSeaOffensiveOperationsAgainstPlayer(eTargetPlayer,AI_ABORT_DIPLO_OPINION_CHANGE); } } } else { //see if we can start a sneak attack if (bWantToAttack) { // Attack on minor if (GET_PLAYER(eTargetPlayer).isMinorCiv()) { bool bCareful = GetPlayer()->CountNumDangerousMajorsAtWarWith(true, false) > 0; GetPlayer()->GetMilitaryAI()->RequestCityAttack(eTargetPlayer, 1, bCareful); } // Attack on major else { bool bCareful = (GetPlayer()->IsNoNewWars() || GetPlayer()->CountNumDangerousMajorsAtWarWith(true, true) > 0) && GetGlobalCoopWarAgainstState(eTargetPlayer) < COOP_WAR_STATE_PREPARING; GetPlayer()->GetMilitaryAI()->RequestCityAttack(eTargetPlayer, 3, bCareful); } } //we just want to scare them else if (bWantShowOfForce) { GetPlayer()->GetMilitaryAI()->RequestBullyingOperation(eTargetPlayer); } else { //it's possible for an operation to be ongoing here if it was a demand, so cancel it //sneak attacks vs. major civs are aborted when DoUpdateWarTargets() calls SetCivApproach() //sneak attacks vs. minor civs are aborted in SelectBestApproachTowardsMinorCiv() or DoUpdateMinorCivApproaches() CvAIOperation* pCurrentDemandOperation = GetPlayer()->getFirstOffensiveAIOperation(eTargetPlayer); if (pCurrentDemandOperation) { pCurrentDemandOperation->LogOperationSpecialMessage("Show of force cancelled, probably preoccupied with something else"); GetPlayer()->StopAllLandOffensiveOperationsAgainstPlayer(eTargetPlayer,AI_ABORT_DIPLO_OPINION_CHANGE); GetPlayer()->StopAllSeaOffensiveOperationsAgainstPlayer(eTargetPlayer,AI_ABORT_DIPLO_OPINION_CHANGE); } } } } /// We've decided to declare war on someone bool CvDiplomacyAI::DeclareWar(PlayerTypes ePlayer) { CvTeam& kMyTeam = GET_TEAM(GetTeam()); TeamTypes eTheirTeam = GET_PLAYER(ePlayer).getTeam(); if (!kMyTeam.canDeclareWar(eTheirTeam, GetID())) return false; // Only do it if we are not already at war. if (!kMyTeam.isAtWar(eTheirTeam)) { kMyTeam.declareWar(eTheirTeam, false, GetID()); m_pPlayer->GetCitySpecializationAI()->SetSpecializationsDirty(SPECIALIZATION_UPDATE_NOW_AT_WAR); // Show scene to human if (!MOD_DIPLOAI_SHUT_UP) { if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { // JdH: deciding whether to send a notification or pop up directy is done in SendRequest if (CvPreGame::isHuman(ePlayer)) { const char* strText = GetDiploStringForMessage(DIPLO_MESSAGE_DOW_ROOT, ePlayer); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_AI_DECLARED_WAR, strText, LEADERHEAD_ANIM_DECLARE_WAR); } } else { if(!CvPreGame::isNetworkMultiplayerGame() && GC.getGame().getActivePlayer() == ePlayer) { const char* strText = GetDiploStringForMessage(DIPLO_MESSAGE_DOW_ROOT, ePlayer); gDLL->GameplayDiplomacyAILeaderMessage(GetID(), DIPLO_UI_STATE_AI_DECLARED_WAR, strText, LEADERHEAD_ANIM_DECLARE_WAR); } } } LogWarDeclaration(ePlayer); return true; } return false; } /// We've decided to declare war on someone bool CvDiplomacyAI::DeclareWar(TeamTypes eTeam) { for (int i = 0; i < MAX_CIV_PLAYERS; i++) { PlayerTypes ePlayer = (PlayerTypes)i; if (ePlayer == NO_PLAYER) continue; if (!GET_PLAYER(ePlayer).isAlive()) continue; if (GET_PLAYER(ePlayer).getTeam() != eTeam) continue; return DeclareWar(ePlayer); } return false; } // ----------------------------------------------------------------------------------------------- // ************************************ // City-State Diplomacy // ************************************ // ----------------------------------------------------------------------------------------------- // ************************************ // Diplomatic Interactions // ************************************ // ----------------------------------------------------------------------------------------------- // ************************************ // Counters // ************************************ /// Increment our turn counters void CvDiplomacyAI::DoCounters() { vector v; // Loop through all (known) Players for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (eLoopPlayer != GetID() && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv() && IsHasMet(eLoopPlayer, true)) { CvNotifications* pNotifications = GET_PLAYER(eLoopPlayer).GetNotifications(); // Are we ready to forget our denunciation? if (IsDenouncedPlayer(eLoopPlayer) && GetTurnsSinceDenouncedPlayer(eLoopPlayer) >= GC.getGame().getGameSpeedInfo().getRelationshipDuration()) { SetDenouncedPlayer(eLoopPlayer, false); // Let's reevaluate this guy if they haven't denounced us and aren't untrustworthy. // Rewards human appeasement strategies. if (!IsDenouncedByPlayer(eLoopPlayer) && !IsUntrustworthy(eLoopPlayer)) v.push_back(eLoopPlayer); // Notify the target of the denouncement that it has expired. if (pNotifications) { CvString strSummary = GetLocalizedText("TXT_KEY_NOTIFICATION_THEIR_DENUNCIATION_EXPIRED_S"); Localization::String strInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_THEIR_DENUNCIATION_EXPIRED"); Localization::String strTemp = strInfo; strTemp << GET_PLAYER(GetID()).getCivilizationShortDescriptionKey(); pNotifications->Add(NOTIFICATION_DENUNCIATION_EXPIRED, strTemp.toUTF8(), strSummary, -1, -1, GetID(), eLoopPlayer); } } // Has our Friendship expired? if (IsDoFAccepted(eLoopPlayer) && GetTurnsSinceBefriendedPlayer(eLoopPlayer) >= GC.getGame().getGameSpeedInfo().getRelationshipDuration()) { SetDoFAccepted(eLoopPlayer, false); GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->SetDoFAccepted(GetID(), false); // Notify both parties that our friendship has expired. if (pNotifications) { CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_FRIENDSHIP_EXPIRED", GET_PLAYER(GetID()).getCivilizationShortDescriptionKey()); CvString strSummary = GetLocalizedText("TXT_KEY_NOTIFICATION_FRIENDSHIP_EXPIRED_S"); pNotifications->Add(NOTIFICATION_FRIENDSHIP_EXPIRED, strBuffer, strSummary, -1, -1, GetID(), eLoopPlayer); } pNotifications = GET_PLAYER(GetID()).GetNotifications(); if (pNotifications) { CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_FRIENDSHIP_EXPIRED", GET_PLAYER(eLoopPlayer).getCivilizationShortDescriptionKey()); CvString strSummary = GetLocalizedText("TXT_KEY_NOTIFICATION_FRIENDSHIP_EXPIRED_S"); pNotifications->Add(NOTIFICATION_FRIENDSHIP_EXPIRED, strBuffer, strSummary, -1, -1, eLoopPlayer, GetID()); } } } } DoReevaluatePlayers(v); } /// Does this AI have a gold quest active with any minor civ? bool CvDiplomacyAI::IsHasActiveGoldQuest() { //antonjs: consider: optimize for (int iMinorLoop = MAX_MAJOR_CIVS; iMinorLoop < MAX_CIV_PLAYERS; iMinorLoop++) { PlayerTypes eMinor = (PlayerTypes) iMinorLoop; if (GET_PLAYER(eMinor).GetMinorCivAI()->IsActiveQuestForPlayer(GetID(), MINOR_CIV_QUEST_GIVE_GOLD)) return true; if (GET_PLAYER(eMinor).GetMinorCivAI()->IsActiveQuestForPlayer(GetID(), MINOR_CIV_QUEST_INVEST)) return true; } return false; } ///////////////////////////////////////////////////////// // Requests ///////////////////////////////////////////////////////// /// Is this AI willing to make a request of ePlayer? bool CvDiplomacyAI::IsMakeRequest(PlayerTypes ePlayer, CvDeal* pDeal, bool& bRandPassed) { bool bFriendly = GetSurfaceApproach(ePlayer) == CIV_APPROACH_FRIENDLY; if(bFriendly && IsDoFAccepted(ePlayer)) { // Is there something we want? bool bWantsSomething = false; // Is there a strong reason why we want something? (added to rand roll) int iWeightBias = 0; // Luxury Request if(!bWantsSomething) bWantsSomething = IsLuxuryRequest(ePlayer, pDeal, iWeightBias); // Gold Request if(!bWantsSomething) bWantsSomething = IsGoldRequest(ePlayer, pDeal, iWeightBias); if (bWantsSomething) { // Random element int iRand = GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x99eccf9f).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); iRand += iWeightBias; if (iRand >= 7) { bRandPassed = true; return true; } else { bRandPassed = false; return false; } } } return false; } /// Does this AI want a luxury resource gift from ePlayer? bool CvDiplomacyAI::IsLuxuryRequest(PlayerTypes ePlayer, CvDeal* pDeal, int& iWeightBias) { iWeightBias = 0; ResourceTypes eLuxuryToAskFor = NO_RESOURCE; int iResourceLoop = 0; // See if the other player has a Resource to trade for(iResourceLoop = 0; iResourceLoop < GC.getNumResourceInfos(); iResourceLoop++) { const ResourceTypes eResource = static_cast(iResourceLoop); CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource); if(pkResourceInfo) { // Only look at Luxuries if(pkResourceInfo->getResourceUsage() != RESOURCEUSAGE_LUXURY) continue; // Any extras? if(GET_PLAYER(ePlayer).getNumResourceAvailable(eResource, /*bIncludeImport*/ false) < 2) continue; // Can they actually give us this item if(!pDeal->IsPossibleToTradeItem(ePlayer, GetID(), TRADE_ITEM_RESOURCES, eResource, 1)) continue; eLuxuryToAskFor = eResource; break; } } // Didn't find something they could give us? if(eLuxuryToAskFor == NO_RESOURCE) return false; // See if there's any Luxuries WE can trade (because if there are then we shouldn't be asking for hand outs) for(iResourceLoop = 0; iResourceLoop < GC.getNumResourceInfos(); iResourceLoop++) { const ResourceTypes eResource = static_cast(iResourceLoop); CvResourceInfo* pkResource = GC.getResourceInfo(eResource); if(pkResource) { // Only look at Luxuries if(pkResource->getResourceUsage() != RESOURCEUSAGE_LUXURY) continue; // Any extras? if(GetPlayer()->getNumResourceAvailable(eResource, /*bIncludeImport*/ false) < 2) continue; // Can they actually give us this item if(!pDeal->IsPossibleToTradeItem(GetID(), ePlayer, TRADE_ITEM_RESOURCES, eResource, 1)) continue; // Found something we can trade to them, so abort return false; } } // Add a little something extra since we're in dire straits if(GetPlayer()->IsEmpireUnhappy()) iWeightBias += 5; // Now seed the deal pDeal->AddResourceTrade(ePlayer, eLuxuryToAskFor, 1, GC.getGame().GetDealDuration()); return true; } /// Does this AI want a gold gift from ePlayer? bool CvDiplomacyAI::IsGoldRequest(PlayerTypes ePlayer, CvDeal* pDeal, int& iWeightBias) { iWeightBias = 0; int iOurGold = GetPlayer()->GetTreasury()->GetGold(); int iOurGPT = GetPlayer()->calculateGoldRate(); int iOurExpenses = GetPlayer()->GetTreasury()->CalculateTotalCosts(); int iOurGrossIncome = iOurGPT + iOurExpenses; // If we have no expenses, don't ask (and also don't crash) if(iOurExpenses == 0) return false; // If we already have some gold saved up then don't bother if(iOurGold > 100) return false; // If we're making 35% more than we're spending then don't ask, we're doing alright if(iOurGrossIncome * 100 / iOurExpenses > 135) return false; int iTheirGold = GET_PLAYER(ePlayer).GetTreasury()->GetGold(); int iTheirGPT = GET_PLAYER(ePlayer).calculateGoldRate(); int iTheirExpenses = GET_PLAYER(ePlayer).GetTreasury()->CalculateTotalCosts(); int iTheirGrossIncome = iTheirGPT + iTheirExpenses; // Don't divide by zero please if(iTheirExpenses != 0) { // If they're making less than 35% more than they're spending then don't ask, they're not in great shape if(iTheirGrossIncome * 100 / iTheirExpenses < 135) return false; } else if(iTheirGPT <= iOurGPT) { return false; } // Add a little something extra since we're in dire straits if(iOurGPT < 0) iWeightBias += 5; // If we've made it this far we'd like to ask, so figure out how much we want to ask for int iGoldToAskFor = iTheirGPT * GC.getGame().GetDealDuration() / 5; int iGPTToAskFor = 0; if(iGoldToAskFor > iTheirGold) { iGoldToAskFor = 0; iGPTToAskFor = max(1, iTheirGPT / 6); } // Now seed the deal if(iGoldToAskFor > 0) pDeal->AddGoldTrade(ePlayer, iGoldToAskFor, true); else if(iGPTToAskFor > 0) pDeal->AddGoldPerTurnTrade(ePlayer, iGPTToAskFor, GC.getGame().GetDealDuration()); return true; } /// Are we willing to swap embassies with ePlayer? bool CvDiplomacyAI::IsEmbassyExchangeAcceptable(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); CivApproachTypes eApproach = GetSurfaceApproach(ePlayer); if (IsDenouncedPlayer(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID())) { return false; } if (IsUntrustworthy(ePlayer)) { return false; } switch (eApproach) { case CIV_APPROACH_WAR: case CIV_APPROACH_HOSTILE: case CIV_APPROACH_GUARDED: return false; default: return true; } } /// What are our opinions of this player's neigbors? CivOpinionTypes CvDiplomacyAI::GetNeighborOpinion(PlayerTypes ePlayer) const { if (ePlayer == NO_PLAYER) { return CIV_OPINION_NEUTRAL; } int iBad = 0; int iNeutral = 0; int iGood = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer, true) && GET_PLAYER(eLoopPlayer).isMajorCiv() && GET_PLAYER(eLoopPlayer).GetProximityToPlayer(ePlayer) == PLAYER_PROXIMITY_NEIGHBORS) { if (GetCivOpinion(eLoopPlayer) <= CIV_OPINION_COMPETITOR) { iBad++; } else if (GetCivOpinion(eLoopPlayer) == CIV_OPINION_NEUTRAL) { iNeutral++; } else if (GetCivOpinion(eLoopPlayer) >= CIV_OPINION_FAVORABLE) { iGood++; } } } if (iGood > iNeutral && iGood > iBad) { return CIV_OPINION_FRIEND; } else if (iNeutral > iGood && iNeutral > iBad) { return CIV_OPINION_NEUTRAL; } else if (iBad > iGood && iBad > iNeutral) { return CIV_OPINION_ENEMY; } else { return CIV_OPINION_NEUTRAL; } } bool CvDiplomacyAI::MusteringForNeighborAttack(PlayerTypes ePlayer) const { if (ePlayer == NO_PLAYER) { return false; } for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer) && GET_PLAYER(eLoopPlayer).isMajorCiv() && GET_PLAYER(eLoopPlayer).GetProximityToPlayer(ePlayer) == PLAYER_PROXIMITY_NEIGHBORS) { if (IsAtWar(eLoopPlayer) || GetCivApproach(eLoopPlayer) == CIV_APPROACH_WAR) { return true; } } } return false; } /// Do we want to have an embassy in the player's capital? - this is only used for when to trigger an AI request, not whether or not the AI will accept a deal period bool CvDiplomacyAI::WantsEmbassyAtPlayer(PlayerTypes ePlayer) const { // May want to make this logic more sophisticated eventually. This will do for now. return GetCivApproach(ePlayer) > CIV_APPROACH_HOSTILE; } /// Are we willing to accept Open Borders from eOtherPlayer? bool CvDiplomacyAI::IsWantsOpenBordersWithPlayer(PlayerTypes ePlayer) { if (IsVassal(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsVassal(GetID())) return true; if (IsUntrustworthy(ePlayer)) return false; if (IsDenouncedPlayer(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID())) { return false; } if (IsArmyInPlaceForAttack(ePlayer)) { return false; } if (!MOD_BALANCE_FLIPPED_OPEN_BORDERS_TOURISM) { // If going for culture win we always want open borders with civs we need influence on if (IsGoingForCultureVictory()) { InfluenceLevelTypes eInfluenceLevel = m_pPlayer->GetCulture()->GetInfluenceLevel(ePlayer); InfluenceLevelTrend eInfluenceTrend = m_pPlayer->GetCulture()->GetInfluenceTrend(ePlayer); if (eInfluenceLevel < INFLUENCE_LEVEL_INFLUENTIAL || eInfluenceTrend <= INFLUENCE_TREND_STATIC) return true; } } else { // If they need influence over us and they aren't our liberator or DP, we don't want their OB, thanks. if (IsCompetingForVictory() && !IsHasDefensivePact(ePlayer) && GetNumCitiesLiberatedBy(ePlayer) <= 0) { DisputeLevelTypes eVictoryDispute = GetVictoryDisputeLevel(ePlayer); BlockLevelTypes eVictoryBlock = GetVictoryBlockLevel(ePlayer); if (eVictoryDispute > DISPUTE_LEVEL_NONE || eVictoryBlock > BLOCK_LEVEL_NONE) { InfluenceLevelTypes eInfluenceLevel = GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(GetID()); InfluenceLevelTrend eInfluenceTrend = GET_PLAYER(ePlayer).GetCulture()->GetInfluenceTrend(GetID()); if (eInfluenceLevel == INFLUENCE_LEVEL_FAMILIAR && eInfluenceTrend == INFLUENCE_TREND_RISING) // Familiar and rising? Slow them down! { return false; } else if (eInfluenceLevel == INFLUENCE_LEVEL_POPULAR) { if (eInfluenceTrend != INFLUENCE_TREND_FALLING) // Popular and not falling? Slow them down! return false; if (eVictoryDispute == DISPUTE_LEVEL_FIERCE || eVictoryBlock == BLOCK_LEVEL_FIERCE || GetBiggestCompetitor() == ePlayer) // Popular and fiercely competitive? Slow them down! return false; } else if (eInfluenceLevel == INFLUENCE_LEVEL_INFLUENTIAL && eInfluenceTrend != INFLUENCE_TREND_RISING) // Already Influential, but not rising? Let's try to reverse that! { return false; } } } } if (GetCivApproach(ePlayer) == CIV_APPROACH_WAR) { return false; } if (IsHasDefensivePact(ePlayer) || IsDoFAccepted(ePlayer)) { return true; } if (IsGoingForWorldConquest() || MusteringForNeighborAttack(ePlayer)) { return true; } if (GetCivApproach(ePlayer) >= CIV_APPROACH_AFRAID) { return true; } if (m_pPlayer->IsCramped() || (GET_PLAYER(ePlayer).getNumCities() * 3) > (m_pPlayer->getNumCities() * 2)) { return true; } EconomicAIStrategyTypes eNeedRecon = (EconomicAIStrategyTypes) GC.getInfoTypeForString("ECONOMICAISTRATEGY_NEED_RECON"); EconomicAIStrategyTypes eNeedNavalRecon = (EconomicAIStrategyTypes) GC.getInfoTypeForString("ECONOMICAISTRATEGY_NEED_RECON_SEA"); if (m_pPlayer->GetEconomicAI()->IsUsingStrategy(eNeedRecon)) { return true; } if (m_pPlayer->GetEconomicAI()->IsUsingStrategy(eNeedNavalRecon)) { int iCityLoop = 0; for (CvCity* pLoopCity = GET_PLAYER(ePlayer).firstCity(&iCityLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iCityLoop)) { if (pLoopCity->isCoastal()) { return true; } } } if (GetPlayer()->GetProximityToPlayer(ePlayer) == PLAYER_PROXIMITY_NEIGHBORS) { AICityStrategyTypes ePocketCity = (AICityStrategyTypes) GC.getInfoTypeForString("AICITYSTRATEGY_POCKET_CITY"); int iCityLoop = 0; for (CvCity* pLoopCity = GET_PLAYER(ePlayer).firstCity(&iCityLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iCityLoop)) { if (pLoopCity->GetCityStrategyAI()->IsUsingCityStrategy(ePocketCity)) { return true; } } } return false; } /// Are we willing to give Open Borders to eOtherPlayer? bool CvDiplomacyAI::IsWillingToGiveOpenBordersToPlayer(PlayerTypes ePlayer) { if (IsUntrustworthy(ePlayer)) { return false; } // Do not let them in if they think our beautiful fields are their living room! if (GetNumTimesCultureBombed(ePlayer) > 0) return false; // Are they here to steal our PRICELESS ARCHAEOLOGICAL ARTIFACTS??? if (GetNumArtifactsEverDugUp(ePlayer) > 0) { int iHiddenSites = GetPlayer()->GetEconomicAI()->GetVisibleHiddenAntiquitySitesOwnTerritory(); int iNormalSites = GetPlayer()->GetEconomicAI()->GetVisibleAntiquitySitesOwnTerritory() - iHiddenSites; PolicyBranchTypes eHiddenSiteBranch = MOD_BALANCE_VP ? (PolicyBranchTypes)GC.getInfoTypeForString("POLICY_BRANCH_AESTHETICS", true) : (PolicyBranchTypes)GC.getInfoTypeForString("POLICY_BRANCH_EXPLORATION", true); if (iNormalSites > 0) { return false; } // Have they unlocked the branch whose finisher lets them see hidden sites? if (iHiddenSites > 0 && GET_PLAYER(ePlayer).GetPlayerPolicies()->IsPolicyBranchUnlocked(eHiddenSiteBranch)) { return false; } } if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsCloseToWorldConquest()) { return false; } if (IsDenouncedPlayer(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID())) { return false; } if (IsArmyInPlaceForAttack(ePlayer)) { return false; } if (MOD_BALANCE_FLIPPED_OPEN_BORDERS_TOURISM) { // If going for culture win we always want to open our borders to civs we need influence on if (IsGoingForCultureVictory()) { InfluenceLevelTypes eInfluenceLevel = m_pPlayer->GetCulture()->GetInfluenceLevel(ePlayer); InfluenceLevelTrend eInfluenceTrend = m_pPlayer->GetCulture()->GetInfluenceTrend(ePlayer); if (eInfluenceLevel < INFLUENCE_LEVEL_INFLUENTIAL || eInfluenceTrend <= INFLUENCE_TREND_STATIC) return true; } } else { // If they need influence over us and they aren't our liberator or DP, we don't want to give them OB, thanks. if (IsCompetingForVictory() && !IsHasDefensivePact(ePlayer) && GetNumCitiesLiberatedBy(ePlayer) <= 0) { DisputeLevelTypes eVictoryDispute = GetVictoryDisputeLevel(ePlayer); BlockLevelTypes eVictoryBlock = GetVictoryBlockLevel(ePlayer); if (eVictoryDispute > DISPUTE_LEVEL_NONE || eVictoryBlock > BLOCK_LEVEL_NONE) { InfluenceLevelTypes eInfluenceLevel = GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(GetID()); InfluenceLevelTrend eInfluenceTrend = GET_PLAYER(ePlayer).GetCulture()->GetInfluenceTrend(GetID()); if (eInfluenceLevel == INFLUENCE_LEVEL_FAMILIAR && eInfluenceTrend == INFLUENCE_TREND_RISING) // Familiar and rising? Slow them down! { return false; } else if (eInfluenceLevel == INFLUENCE_LEVEL_POPULAR) { if (eInfluenceTrend != INFLUENCE_TREND_FALLING) // Popular and not falling? Slow them down! return false; if (eVictoryDispute == DISPUTE_LEVEL_FIERCE || eVictoryBlock == BLOCK_LEVEL_FIERCE || GetBiggestCompetitor() == ePlayer) // Popular and fiercely competitive? Slow them down! return false; } else if (eInfluenceLevel == INFLUENCE_LEVEL_INFLUENTIAL && eInfluenceTrend != INFLUENCE_TREND_RISING) // Already Influential, but not rising? Let's try to reverse that! { return false; } } } } if (GetCivApproach(ePlayer) == CIV_APPROACH_WAR) { return false; } if (IsHasDefensivePact(ePlayer) || IsDoFAccepted(ePlayer)) { return true; } if (GetCivApproach(ePlayer) >= CIV_APPROACH_AFRAID) { return true; } if (GetMilitaryStrengthComparedToUs(ePlayer) <= STRENGTH_AVERAGE) { return true; } return false; } /// Are we willing to swap Open Borders with ePlayer? bool CvDiplomacyAI::IsOpenBordersExchangeAcceptable(PlayerTypes ePlayer) { return IsWillingToGiveOpenBordersToPlayer(ePlayer) && IsWantsOpenBordersWithPlayer(ePlayer); } ///////////////////////////////////////////////////////// // Planning Exchanges ///////////////////////////////////////////////////////// /// Is this war not worth caring about? bool CvDiplomacyAI::IsPhonyWar(PlayerTypes ePlayer, bool bIgnoreCurrentApproach /* = false */) const { if (!IsAtWar(ePlayer)) return false; // Approach is WAR if (!bIgnoreCurrentApproach && GetCivApproach(ePlayer) == CIV_APPROACH_WAR) return false; // They captured our cities before, don't dismiss them. if (GetNumCitiesCapturedBy(ePlayer) > 0) return false; // War state too high or too low WarStateTypes eWarState = GetWarState(ePlayer); if (eWarState >= WAR_STATE_OFFENSIVE || eWarState <= WAR_STATE_TROUBLED) return false; // War score is too high int iWarScore = GetPlayer()->GetDiplomacyAI()->GetWarScore(ePlayer); if (iWarScore <= GetWarscoreThresholdNegative() || iWarScore >= GetWarscoreThresholdPositive()) return false; // We want to conquer them! if (IsWantsToConquer(ePlayer)) return false; // We have offensive operations ongoing. if (m_pPlayer->HasAnyOffensiveOperationsAgainstPlayer(ePlayer)) return false; // Our cities are threatened by them or vice versa if (GetPlayer()->GetDiplomacyAI()->GetNumberOfThreatenedCities(ePlayer) > 0 || GET_PLAYER(ePlayer).GetDiplomacyAI()->GetNumberOfThreatenedCities(GetID()) > 0) return false; // They're too close or have a lot of soldiers nearby PlayerProximityTypes eProximity = GetPlayer()->GetProximityToPlayer(ePlayer); AggressivePostureTypes ePosture = GetMilitaryAggressivePosture(ePlayer); StrengthTypes eMilitaryStrength = GetMilitaryStrengthComparedToUs(ePlayer); if (eProximity == PLAYER_PROXIMITY_NEIGHBORS) { return false; } else if (eProximity == PLAYER_PROXIMITY_CLOSE) { if (ePosture >= AGGRESSIVE_POSTURE_MEDIUM || eMilitaryStrength >= STRENGTH_POWERFUL) return false; } else { if (ePosture >= AGGRESSIVE_POSTURE_HIGH || eMilitaryStrength == STRENGTH_IMMENSE) return false; } return true; } /// Does the AI even want to conquer another player if they are at war? /// Since there is no "defensive war" flag, this seems to be the best way to differentiate bool CvDiplomacyAI::IsWantsToConquer(PlayerTypes ePlayer) const { if (!IsAtWar(ePlayer)) return false; if (IsAlwaysAtWar(ePlayer)) return true; if (GC.getGame().GetNumMajorCivsAlive() == 2) return true; if (GetPlayer()->IsAITeammateOfHuman()) return true; // Doing well already - let's keep it up. if (GetWarState(ePlayer) >= WAR_STATE_OFFENSIVE) return true; // Captured one of our key cities? if (IsCapitalCapturedBy(ePlayer, true, false) || IsHolyCityCapturedBy(ePlayer, true, false)) return true; // If they're about to win, we have nothing to lose! if (IsEndgameAggressiveTo(ePlayer)) return true; // If we're in bad shape for war, retreat! if (GetPlayer()->IsInTerribleShapeForWar()) return false; TargetValueTypes eTargetValue = GetTargetValue(ePlayer); bool bWeHaveGoodAttackTarget = GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(ePlayer); bool bAggressive = GetPlayer()->GetPlayerTraits()->IsWarmonger() || (IsCompetingForVictory() && IsGoingForWorldConquest()); // They're an easy target, so play offensively! if (IsEasyTarget(ePlayer) && bWeHaveGoodAttackTarget) return true; // We started or wanted this war - if they're not a bad target we should attack at full force if (IsAggressor(ePlayer)) { // Bad target if (eTargetValue == TARGET_VALUE_IMPOSSIBLE || (eTargetValue == TARGET_VALUE_BAD && !bWeHaveGoodAttackTarget)) { // Aggressive and fiercely competitive players don't give up so easily if (IsMajorCompetitor(ePlayer) && bAggressive) { return true; } // We're in a coop war with another player against this guy if (GetGlobalCoopWarAgainstState(ePlayer) >= COOP_WAR_STATE_PREPARING) { return true; } return false; } return true; } // Didn't start or want this war - don't care as much else { // Aggressive and nearby players if (bAggressive && GetPlayer()->GetProximityToPlayer(ePlayer) >= PLAYER_PROXIMITY_CLOSE) { if (eTargetValue == TARGET_VALUE_IMPOSSIBLE) { return false; } else if (eTargetValue == TARGET_VALUE_BAD && !bWeHaveGoodAttackTarget) { return false; } } // Other players else if (eTargetValue <= TARGET_VALUE_BAD || (eTargetValue <= TARGET_VALUE_AVERAGE && !bWeHaveGoodAttackTarget)) { return false; } } return true; //by default we conquer everything } /// Is this major civ a potential military target or threat? bool CvDiplomacyAI::IsPotentialMilitaryTargetOrThreat(PlayerTypes ePlayer, bool bFromApproachSelection) const { if (!GET_PLAYER(ePlayer).isMajorCiv() || !GET_PLAYER(ePlayer).isAlive() || GET_PLAYER(ePlayer).getNumCities() <= 0 || IsTeammate(ePlayer)) return false; if (!IsAtWar(ePlayer) && GC.getGame().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE)) return false; bool bVassal = IsVassal(ePlayer); if (!bVassal) { if (IsNukedBy(ePlayer) || IsCapitalCapturedBy(ePlayer) || IsHolyCityCapturedBy(ePlayer)) return true; } bool bFriends = IsMaster(ePlayer) || IsFriendOrAlly(ePlayer); // Only care if this player is close to us if (GetPlayer()->GetProximityToPlayer(ePlayer) >= PLAYER_PROXIMITY_CLOSE || GET_PLAYER(ePlayer).GetProximityToPlayer(GetID()) >= PLAYER_PROXIMITY_CLOSE || GetMilitaryAggressivePosture(ePlayer) >= AGGRESSIVE_POSTURE_MEDIUM) { bool bStronger = GetRawMilitaryStrengthComparedToUs(ePlayer) > STRENGTH_AVERAGE || GetEconomicStrengthComparedToUs(ePlayer) > STRENGTH_STRONG || GetWarmongerThreat(ePlayer) >= THREAT_SEVERE; if (!bVassal) { if (bStronger && !bFriends) return true; if (GetNumCitiesCapturedBy(ePlayer) > 0) return true; // Going to war? if (AvoidExchangesWithPlayer(ePlayer, true, bFromApproachSelection)) return true; } // If they're stronger than us, our master, or if their military strength is at least POOR (one level below), let's check diplomacy if (bStronger || bVassal || GetRawMilitaryStrengthComparedToUs(ePlayer) >= STRENGTH_POOR) { vector vMyTeam = GET_TEAM(GetTeam()).getPlayers(); vector vPlayersToCheck = GetOffensiveWarAllies(ePlayer, /*bIncludeMinors*/ false, /*bReverseMode*/ true); vPlayersToCheck.push_back(ePlayer); for (std::vector::iterator it = vPlayersToCheck.begin(); it != vPlayersToCheck.end(); ++it) { for (size_t i=0; iGetDiplomacyAI(); if (!pMyTeamPlayer->isMajorCiv() || pMyTeamPlayer->getNumCities() <= 0) continue; // Backstabber? // Annoying to check it like this, but because this function is called a lot, we want to optimize where possible. if (GET_PLAYER(*it).getTeam() == GET_PLAYER(ePlayer).getTeam() && pDiplo->IsUntrustworthyFriend(*it)) return true; if (!pMyTeamPlayer->isAlive()) continue; if (!bVassal) { bool bIgnoreSelfApproach = pMyTeamPlayer->isHuman(ISHUMAN_AI_DIPLOMACY) || (bFromApproachSelection && pMyTeamPlayer->GetID() == GetID()); // Is this a Defensive Pact? if (GET_PLAYER(*it).GetDiplomacyAI()->IsHasDefensivePact(ePlayer)) { // If we're going to war with their DP, they might be a threat or target. // Otherwise, we don't care. if (AvoidExchangesWithPlayer(*it, true, bIgnoreSelfApproach)) return true; continue; } // Bad approach towards them? if (!bIgnoreSelfApproach) { CivApproachTypes eApproach = pDiplo->GetCivApproach(*it); if (eApproach == CIV_APPROACH_AFRAID && !bFriends) return true; if (eApproach <= CIV_APPROACH_GUARDED) return true; } } // Negative approach towards us? CivApproachTypes eApproachTowardsUs = pDiplo->GetVisibleApproachTowardsUs(*it); if (eApproachTowardsUs <= CIV_APPROACH_GUARDED) return true; // Denouncement in either direction? if (pDiplo->IsDenouncedPlayer(*it) || pDiplo->IsDenouncedByPlayer(*it)) return true; // Previous war? if (!bFriends && !bVassal && pDiplo->GetNumWarsDeclaredOnUs(*it) > 0) return true; // Any other reason for them to be mad at us? if (!bVassal && GET_PLAYER(*it).GetDiplomacyAI()->GetNumCitiesCapturedBy(vMyTeam[i]) > 0) return true; if (GET_PLAYER(*it).GetDiplomacyAI()->IsPlayerCapturedCapital(vMyTeam[i]) || GET_PLAYER(*it).GetDiplomacyAI()->IsPlayerCapturedHolyCity(vMyTeam[i])) return true; // Run these checks last because they're more expensive. if (GET_PLAYER(*it).GetDiplomacyAI()->IsUntrustworthy(vMyTeam[i])) return true; if (bVassal && pDiplo->GetVassalTreatmentLevel(*it) <= VASSAL_TREATMENT_MISTREATED) return true; } if (GET_PLAYER(*it).GetNumOurCitiesOwnedBy(GetID()) > 0) return true; } } } return false; } /// Returns if this player has been nuked by ePlayer bool CvDiplomacyAI::IsNukedBy(PlayerTypes ePlayer) const { return (GetNumTimesNuked(ePlayer) > 0); } /// Is this player a friend or ally in any way? Quick heuristic check that only checks for good things. bool CvDiplomacyAI::IsFriendOrAlly(PlayerTypes ePlayer) const { if (IsTeammate(ePlayer)) return true; if (!GET_PLAYER(ePlayer).isAlive() || ePlayer == BARBARIAN_PLAYER) return false; if (IsDoFAccepted(ePlayer)) return true; if (IsHasDefensivePact(ePlayer)) return true; if (GetCivOpinion(ePlayer) == CIV_OPINION_ALLY) return true; if (GetDoFType(ePlayer) >= DOF_TYPE_ALLIES) return true; if (WasResurrectedBy(ePlayer)) return true; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->WasResurrectedBy(GetID())) return true; return false; } /// Is this player causing us problems in the early game? bool CvDiplomacyAI::IsEarlyGameCompetitor(PlayerTypes ePlayer) { // Not the early game if (GetPlayer()->GetCurrentEra() > 2) return false; if (GetCivOpinion(ePlayer) <= CIV_OPINION_ENEMY) return true; if (GetLandDisputeLevel(ePlayer) > DISPUTE_LEVEL_WEAK) return true; if (GetNumWondersBeatenTo(ePlayer) > 0) return true; if (GetNumDemandsMade(ePlayer) > 0) return true; if (GetNegativeReligiousConversionPoints(ePlayer) > 0) return true; if (GetNumTimesTheyPlottedAgainstUs(ePlayer) > 0) return true; if (IsDenouncedPlayer(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID())) return true; // Major early lead in techs/policies? Let's slow that down. if (GetPolicyBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG || GetTechBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG) return true; // Special check for England / Statecraft if (GetNumTimesRobbedBy(ePlayer) > 0 || GetNumTimesPerformedCoupAgainstUs(ePlayer) > 0) return true; if (IgnoredBullyCityStatePromise(ePlayer) || BrokeBullyCityStatePromise(ePlayer) || IsAngryAboutProtectedMinorAttacked(ePlayer) || IsAngryAboutProtectedMinorKilled(ePlayer)) return true; if (IsUntrustworthy(ePlayer)) return true; // Are they a juicy target? if (IsEasyTarget(ePlayer) || GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(ePlayer)) { PlayerProximityTypes eProximity = GetPlayer()->GetProximityToPlayer(ePlayer); bool bRecklessExpander = IsRecklessExpander(ePlayer); bool bWonderSpammer = IsWonderSpammer(ePlayer); if (eProximity == PLAYER_PROXIMITY_NEIGHBORS) { if (bRecklessExpander || bWonderSpammer) return true; } else if (eProximity == PLAYER_PROXIMITY_CLOSE) { if (IsConqueror() || IsSecondaryConqueror()) { if (bRecklessExpander || bWonderSpammer) return true; } else if (IsCultural() || IsSecondaryCultural()) { if (bWonderSpammer) return true; } } } return false; } /// Should we ignore Social Policy differences with ePlayer? bool CvDiplomacyAI::IsIgnorePolicyDifferences(PlayerTypes ePlayer) const { if (!GetPlayer()->isMajorCiv() || !GET_PLAYER(ePlayer).isMajorCiv()) return true; if (IsTeammate(ePlayer) || IsVassal(ePlayer) || IsMaster(ePlayer)) return true; if (IsAtWar(ePlayer) || IsUntrustworthy(ePlayer)) return false; // Capital or Holy City captured? Exception if they resurrected us: only test to see if they captured it. if (WasResurrectedBy(ePlayer)) { vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); for (size_t i=0; i= DOF_TYPE_ALLIES) return true; // Ideological or religious buddies tolerate policy differences. if (IsPlayerSameIdeology(ePlayer) || IsPlayerSameReligion(ePlayer)) return true; if (IsCityRecentlyLiberatedBy(ePlayer)) return true; // The presence of a Diplomat smoothes over cultural (but not religious/ideological) differences. if (GetPlayer()->GetEspionage()->IsMyDiplomatVisitingThem(ePlayer) || GetPlayer()->GetEspionage()->IsOtherDiplomatVisitingMe(ePlayer)) return true; // If they're helping us go to war, we'll set aside our differences for now. if (GetCoopWarAgreementScore(ePlayer) > 0 || GetGlobalCoopWarWithState(ePlayer) >= COOP_WAR_STATE_PREPARING) return true; return false; } /// Should we ignore religious differences with ePlayer? bool CvDiplomacyAI::IsIgnoreReligionDifferences(PlayerTypes ePlayer) const { if (!GetPlayer()->isMajorCiv() || !GET_PLAYER(ePlayer).isMajorCiv()) return true; if (IsTeammate(ePlayer) || IsMaster(ePlayer)) return true; if (GetNegativeReligiousConversionPoints(ePlayer) > 0) return false; if (IsHolyCityCapturedBy(ePlayer)) return false; // Capital captured? Exception if they resurrected us: only test to see if they captured it. if (WasResurrectedBy(ePlayer)) { vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); for (size_t i=0; iHasEverConvertedCity(GetID())) { if (GetPlayer()->GetPlayerTraits()->IsNoNaturalReligionSpread()) return true; if (GET_PLAYER(ePlayer).GetPlayerTraits()->IsNoNaturalReligionSpread()) return true; } if (IsAtWar(ePlayer) || IsUntrustworthy(ePlayer)) return false; // Ideological buddies tolerate religious differences. if (IsPlayerSameIdeology(ePlayer)) return true; if (IsPlayerLiberatedCapital(ePlayer) || IsPlayerLiberatedHolyCity(ePlayer) || IsPlayerReturnedCapital(ePlayer) || IsPlayerReturnedHolyCity(ePlayer) || WasResurrectedBy(ePlayer) || GetDoFType(ePlayer) == DOF_TYPE_BATTLE_BROTHERS) return true; if (IsCityRecentlyLiberatedBy(ePlayer)) return true; // If they're helping us go to war, we'll set aside our differences for now. if (GetCoopWarAgreementScore(ePlayer) > 1 || GetGlobalCoopWarWithState(ePlayer) >= COOP_WAR_STATE_PREPARING) return true; return false; } /// Should we ignore ideological differences with ePlayer? bool CvDiplomacyAI::IsIgnoreIdeologyDifferences(PlayerTypes ePlayer) const { if (!GetPlayer()->isMajorCiv() || !GET_PLAYER(ePlayer).isMajorCiv()) return true; if (IsTeammate(ePlayer) || IsVassal(ePlayer) || IsMaster(ePlayer)) return true; if (IsAtWar(ePlayer) || IsUntrustworthy(ePlayer)) return false; // Capital or Holy City captured? Exception if they resurrected us: only test to see if they captured it. if (WasResurrectedBy(ePlayer)) { vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); for (size_t i=0; i 2 || GetGlobalCoopWarWithState(ePlayer) >= COOP_WAR_STATE_PREPARING) return true; // Cold War resolution active? CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (pLeague != NULL) { // Loop through all (known) Players for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer)) { if (GC.getGame().GetGameLeagues()->IsIdeologyEmbargoed(GetID(), eLoopPlayer)) { return false; } } } } if (IsPlayerLiberatedHolyCity(ePlayer) || IsCityRecentlyLiberatedBy(ePlayer)) return true; return false; } /// Is this player causing trouble around our Minor Civs? bool CvDiplomacyAI::IsMinorCivTroublemaker(PlayerTypes ePlayer, bool bIgnoreBullying /* = false */) const { if (GetMinorCivDisputeLevel(ePlayer) > DISPUTE_LEVEL_WEAK) return true; if (GetNumTimesPerformedCoupAgainstUs(ePlayer) > 0) return true; if (IsAngryAboutProtectedMinorKilled(ePlayer) || IsAngryAboutProtectedMinorAttacked(ePlayer) || BrokeAttackCityStatePromise(ePlayer) || IgnoredAttackCityStatePromise(ePlayer)) return true; if (!bIgnoreBullying) { if (IsAngryAboutProtectedMinorBullied(ePlayer) || BrokeBullyCityStatePromise(ePlayer) || IgnoredBullyCityStatePromise(ePlayer)) return true; } return false; } /// Diplomacy AI Options /// Should we specifically hide dispute-related modifiers towards ePlayer? bool CvDiplomacyAI::ShouldHideDisputeMods(PlayerTypes ePlayer) const { // Game options forbid hiding. if (MOD_DIPLOAI_SHOW_HIDDEN_OPINION_MODIFIERS || GC.getGame().isOption(GAMEOPTION_TRANSPARENT_DIPLOMACY)) return false; // If we're at war, don't bother. if (IsAtWar(ePlayer)) return false; // If we've declared war on them previously, let's be honest about disputes. if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetNumWarsDeclaredOnUs(GetID()) > 0) return false; // If we're their vassal, don't bother. if (IsVassal(ePlayer)) return false; // If we've denounced them, don't bother. if (IsDenouncedPlayer(ePlayer)) return false; // If they've liberated us, let's be honest. if (IsLiberator(ePlayer, false, false)) return false; // If they're untrustworthy, don't bother hiding anything. if (IsUntrustworthy(ePlayer)) return false; CivApproachTypes eSurfaceApproach = GetSurfaceApproach(ePlayer); // Only hide if our surface approach is FRIENDLY or AFRAID return eSurfaceApproach == CIV_APPROACH_FRIENDLY || eSurfaceApproach == CIV_APPROACH_AFRAID; } /// Should we hide certain negative opinion modifiers towards ePlayer? (stuff like competition, warmongering etc) /// NOTE: If both hiding dispute mods and hiding negative mods, dispute mods will show up as if the DisputeLevel was NONE. Otherwise, dispute mods are simply hidden. bool CvDiplomacyAI::ShouldHideNegativeMods(PlayerTypes ePlayer) const { // Game options forbid hiding. if (MOD_DIPLOAI_SHOW_HIDDEN_OPINION_MODIFIERS || GC.getGame().isOption(GAMEOPTION_TRANSPARENT_DIPLOMACY)) return false; // If we're at war, don't bother. if (IsAtWar(ePlayer)) return false; // If we're their vassal, don't bother. if (IsVassal(ePlayer)) return false; // If we've denounced them, don't bother. if (IsDenouncedPlayer(ePlayer)) return false; // If they've liberated us, let's be honest. if (IsLiberator(ePlayer, false, false)) return false; // If they're untrustworthy, don't bother hiding anything. if (IsUntrustworthy(ePlayer)) return false; CivApproachTypes eSurfaceApproach = GetSurfaceApproach(ePlayer); // Always hide if our surface approach is FRIENDLY or AFRAID if (eSurfaceApproach == CIV_APPROACH_FRIENDLY || eSurfaceApproach == CIV_APPROACH_AFRAID) return true; // Never hide if our surface approach is HOSTILE if (eSurfaceApproach == CIV_APPROACH_HOSTILE) return false; // If we're acting hostile, don't hide anything. if (IsActHostileTowardsHuman(ePlayer)) return false; // If they're a favorable target, let's not bother hiding things. if (GetMilitaryStrengthComparedToUs(ePlayer) < STRENGTH_POWERFUL) { if (IsEasyTarget(ePlayer) || GetTargetValue(ePlayer) >= TARGET_VALUE_FAVORABLE) return false; } return true; // Let's conceal our negative thoughts! } // ************************************ // Evaluation of Other Players' Tendencies // ************************************ /// ePlayer made peace with someone, so figure out what that means void CvDiplomacyAI::DoWeMadePeaceWithSomeone(TeamTypes eOtherTeam) { if (eOtherTeam < 0 || eOtherTeam >= MAX_CIV_TEAMS) return; vector vPlayersToReevaluate; vector vOtherTeam = GET_TEAM(eOtherTeam).getPlayers(); for (size_t i=0; igetFirstOffensiveAIOperation(ePeacePlayer) != NULL) { GetPlayer()->StopAllLandOffensiveOperationsAgainstPlayer(ePeacePlayer,AI_ABORT_WAR_STATE_CHANGE); GetPlayer()->StopAllSeaOffensiveOperationsAgainstPlayer(ePeacePlayer,AI_ABORT_WAR_STATE_CHANGE); } // Reset values specific to this war SetAggressor(ePeacePlayer, false); SetWarProgressScore(ePeacePlayer, 0); // Reset number of turns locked into war GET_TEAM(GetTeam()).SetNumTurnsLockedIntoWar(GET_PLAYER(ePeacePlayer).getTeam(), 0); if (GET_PLAYER(ePeacePlayer).isMajorCiv()) { CancelCoopWarsAgainstPlayer(ePeacePlayer, false); // Halve war weariness GetPlayer()->SetWarWeariness(ePeacePlayer, GetPlayer()->GetWarWeariness(ePeacePlayer) / 2); // Clear penalties for stealing territory and refusing to go on coop wars now that we made peace if (!GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { SetNumTimesCultureBombed(ePeacePlayer, 0); if (GetCoopWarAgreementScore(ePeacePlayer) < 0) SetCoopWarAgreementScore(ePeacePlayer, 0); vPlayersToReevaluate.push_back(ePeacePlayer); } } else if (GET_PLAYER(ePeacePlayer).isMinorCiv()) { // Reset flags for taunt messages we sent to other civs about attacking this minor ResetSentAttackProtectedMinorTaunts(ePeacePlayer); } } // Update other diplomacy stuff! DoReevaluatePlayers(vPlayersToReevaluate, false, false); } /// ePlayer declared war on someone, so figure out what that means void CvDiplomacyAI::DoPlayerDeclaredWarOnSomeone(PlayerTypes ePlayer, TeamTypes eOtherTeam, bool bDefensivePact) { if (ePlayer < 0 || ePlayer >= MAX_CIV_PLAYERS || GET_PLAYER(ePlayer).isBarbarian()) return; if (eOtherTeam < 0 || eOtherTeam >= MAX_CIV_TEAMS || GET_TEAM(eOtherTeam).isBarbarian()) return; PlayerTypes eMyPlayer = GetID(); vector vAttackedTeam = GET_TEAM(eOtherTeam).getPlayers(); for (size_t i=0; i 0) { SetCoopWarAgreementScore(ePlayer, 0); } } // Reset DoF values SetDoFAccepted(ePlayer, false); SetDoFType(ePlayer, DOF_TYPE_UNTRUSTWORTHY); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFAccepted(eMyPlayer, false); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFType(eMyPlayer, DOF_TYPE_UNTRUSTWORTHY); if (GetShareApproachResponse(ePlayer) != SHARE_APPROACH_RESPONSE_REFUSED) SetShareApproachResponse(ePlayer, NO_SHARE_APPROACH_RESPONSE); if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetShareApproachResponse(eMyPlayer) != SHARE_APPROACH_RESPONSE_REFUSED) GET_PLAYER(ePlayer).GetDiplomacyAI()->SetShareApproachResponse(eMyPlayer, NO_SHARE_APPROACH_RESPONSE); // End all coop war agreements with this player CancelCoopWarsWithPlayer(ePlayer, !bDefensivePact); // Player broke a promise that he wasn't going to attack us? if (MadeMilitaryPromise(ePlayer)) { if (!bDefensivePact) SetMilitaryPromiseState(ePlayer, PROMISE_STATE_BROKEN); else SetMilitaryPromiseState(ePlayer, NO_PROMISE_STATE); } // Reset various promises for both of us...all is fair in war! SetExpansionPromiseState(ePlayer, NO_PROMISE_STATE); SetBorderPromiseState(ePlayer, NO_PROMISE_STATE); SetSpyPromiseState(ePlayer, NO_PROMISE_STATE); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetExpansionPromiseState(eMyPlayer, NO_PROMISE_STATE); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetBorderPromiseState(eMyPlayer, NO_PROMISE_STATE); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetSpyPromiseState(eMyPlayer, NO_PROMISE_STATE); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetMilitaryPromiseState(eMyPlayer, NO_PROMISE_STATE); // We're no longer trade partners SetRecentTradeValue(ePlayer, 0); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetRecentTradeValue(eMyPlayer, 0); if (!bDefensivePact) { // Forget any of that liberation crud! SetResurrectedBy(ePlayer, false); SetPlayerLiberatedCapital(ePlayer, false); SetPlayerLiberatedHolyCity(ePlayer, false); SetNumCitiesLiberatedBy(ePlayer, 0); SetNumCitiesEverLiberatedBy(ePlayer, 0); SetPlayerReturnedCapital(ePlayer, false); SetPlayerReturnedHolyCity(ePlayer, false); SetMasterLiberatedMeFromVassalage(ePlayer, false); SetVassalagePeacefullyRevokedTurn(ePlayer, -1); // Forget civilians returned, landmarks built, and intrigue shared so they don't affect relations any more SetNumCiviliansReturnedToMe(ePlayer, 0); SetNumLandmarksBuiltForMe(ePlayer, 0); SetNumTimesIntrigueSharedBy(ePlayer, 0); // Clear positive diplomatic values SetCommonFoeValue(ePlayer, 0); SetVassalProtectValue(ePlayer, 0); if (GetRecentAssistValue(ePlayer) < 0) SetRecentAssistValue(ePlayer, 0); } } // If it's us OR we know the attacked player, change appropriate values if (!bDefensivePact && IsPlayerValid(eAttackedPlayer, true) && GET_PLAYER(ePlayer).isMajorCiv()) { if (GET_PLAYER(eAttackedPlayer).isMajorCiv()) { ChangeOtherPlayerNumMajorsAttacked(ePlayer, 1, eOtherTeam); if (GetTeam() != eOtherTeam && !IsAtWar(ePlayer) && !IsUntrustworthy(ePlayer)) { // If we view the target as a backstabber, apply a large diplo bonus. if (IsUntrustworthy(eAttackedPlayer)) { ChangeRecentAssistValue(ePlayer, 300); } // Did they declare war on someone we're at war with? else if (IsAtWar(eAttackedPlayer)) { // If we're doing badly in the war, we appreciate the assistance. switch (GetWarState(eAttackedPlayer)) { // No war state is fine here since they may have just gone to war. case NO_WAR_STATE_TYPE: case WAR_STATE_NEARLY_WON: case WAR_STATE_OFFENSIVE: break; case WAR_STATE_CALM: ChangeRecentAssistValue(ePlayer, 50); break; case WAR_STATE_STALEMATE: ChangeRecentAssistValue(ePlayer, 100); break; case WAR_STATE_TROUBLED: case WAR_STATE_DEFENSIVE: ChangeRecentAssistValue(ePlayer, 200); break; case WAR_STATE_NEARLY_DEFEATED: ChangeRecentAssistValue(ePlayer, 300); break; } } } } else if (GET_PLAYER(eAttackedPlayer).isMinorCiv()) { // Did they attack a Minor we're protecting? bool bProtected = GET_PLAYER(eAttackedPlayer).GetMinorCivAI()->IsProtectedByMajor(eMyPlayer) || GET_PLAYER(eAttackedPlayer).GetMinorCivAI()->IsAllies(eMyPlayer); if (bProtected) { SetOtherPlayerAttackedProtectedMinorTurn(ePlayer, GC.getGame().getGameTurn()); SetOtherPlayerProtectedMinorAttacked(ePlayer, eAttackedPlayer); } // To prevent infinite warmongering exploits, only apply further penalties if they haven't attacked this minor within the last 50 turns. int iTurn = GET_PLAYER(eAttackedPlayer).GetMinorCivAI()->GetTurnLastAttacked(GET_PLAYER(ePlayer).getTeam()); int iTurnDifference = GC.getGame().getGameTurn() - iTurn; if (iTurn > -1 && iTurnDifference < 50) return; if (bProtected) ChangeOtherPlayerNumProtectedMinorsAttacked(ePlayer, 1); ChangeOtherPlayerNumMinorsAttacked(ePlayer, 1, eOtherTeam); } } } } /// ePlayer bullied eOtherPlayer (minor civ), so figure out what that means void CvDiplomacyAI::DoPlayerBulliedSomeone(PlayerTypes ePlayer, PlayerTypes eOtherPlayer) { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return; if (eOtherPlayer < MAX_MAJOR_CIVS || eOtherPlayer >= MAX_CIV_PLAYERS) return; // The bully was someone else if (IsPlayerValid(ePlayer)) { // Did they bully a Minor we're protecting? if (GET_PLAYER(eOtherPlayer).isMinorCiv() && GET_PLAYER(eOtherPlayer).GetMinorCivAI()->IsProtectedByMajor(GetID())) { // Only apply this penalty if they haven't bullied them already if (GET_PLAYER(eOtherPlayer).GetMinorCivAI()->GetTurnLastBulliedByMajor(ePlayer) == -1) { ChangeOtherPlayerNumProtectedMinorsBullied(ePlayer, 1); } SetOtherPlayerProtectedMinorBullied(ePlayer, eOtherPlayer); SetOtherPlayerBulliedProtectedMinorTurn(ePlayer, GC.getGame().getGameTurn()); // You broke the promise you made! if (MadeBullyCityStatePromise(ePlayer)) { SetBullyCityStatePromiseState(ePlayer, PROMISE_STATE_BROKEN); } } } } /// Return the value of the warmonger amount adjusted by how much this player hates warmongers int CvDiplomacyAI::GetOtherPlayerWarmongerScore(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; int iReturnValue = GetOtherPlayerWarmongerAmount(ePlayer); iReturnValue *= GetWarmongerHate(); // ranges from 1 to 10 iReturnValue /= 20; return iReturnValue; } ///////////////////////////////////////////////////////// // Contact ///////////////////////////////////////////////////////// /// First contact between this player and another void CvDiplomacyAI::DoFirstContact(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_CIV_PLAYERS, "DIPLOMACY_AI: Invalid Player Index."); if (ePlayer != GetID()) { DoFirstContactInitRelationship(ePlayer); // Humans don't say hi to one another through the shadow diplo AI and, uh, don't show up in MP please if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { // JdH: notifications do get send in MP + updated to new if (GC.getGame().isFinalInitialized()) { if (!IsAtWar(ePlayer) && CvPreGame::isHuman(ePlayer)) { const char* szText = GetDiploStringForMessage(DIPLO_MESSAGE_INTRO); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DEFAULT_ROOT, szText, LEADERHEAD_ANIM_INTRO); } } } else { if(!GC.getGame().isNetworkMultiPlayer()) // KWG: Candidate for !GC.getGame().IsOption(GAMEOPTION_SIMULTANEOUS_TURNS) { if(!GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { // Should fire off a diplo message when we meet a human if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) { if(!IsAtWar(ePlayer)) { if(GC.getGame().isFinalInitialized()) { if(std::find(m_aGreetPlayers.begin(), m_aGreetPlayers.end(), ePlayer) == m_aGreetPlayers.end()) { // Put in the list of people to greet when their turn comes up. m_aGreetPlayers.push_back(ePlayer); } } } } } else { // Human to Human will just send a notification CvPlayer& kTargetPlayer = GET_PLAYER(ePlayer); if(kTargetPlayer.isHuman(ISHUMAN_AI_DIPLOMACY)) { if(!IsAtWar(ePlayer)) { if(GC.getGame().isFinalInitialized()) { CvNotifications* pNotifications = kTargetPlayer.GetNotifications(); if(pNotifications) { CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_SUMMARY_MET_MINOR_CIV", GetPlayer()->getNameKey()); pNotifications->Add(NOTIFICATION_GENERIC, strBuffer, strBuffer, -1, -1, GetID()); } } } } } } } } } /// Initiate relationship values towards a new player on first contact void CvDiplomacyAI::DoFirstContactInitRelationship(PlayerTypes ePlayer) { if (GC.getGame().isFinalInitialized()) { DoUpdateConquestStats(); DoUpdateMilitaryAggressivePostures(); } // Major Civ if (GET_PLAYER(ePlayer).isMajorCiv() && NotTeam(ePlayer)) { for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eApproach = (CivApproachTypes) iApproachLoop; SetPlayerApproachValue(ePlayer, eApproach, 0); } DoReevaluatePlayer(ePlayer); } // Minor civ else if (GET_PLAYER(ePlayer).isMinorCiv()) { SetCivApproach(ePlayer, CIV_APPROACH_NEUTRAL); } } // ----------------------------------------------------------------------------------------------- /// Player killed us void CvDiplomacyAI::DoKilledByPlayer(PlayerTypes ePlayer) { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { if(ePlayer != NO_PLAYER && CvPreGame::isHuman(ePlayer)) { const char* szText = GetDiploStringForMessage(DIPLO_MESSAGE_DEFEATED); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_DEFEATED); } } else { if(ePlayer == GC.getGame().getActivePlayer() && !GC.getGame().isNetworkMultiPlayer()) { const char* szText = GetDiploStringForMessage(DIPLO_MESSAGE_DEFEATED); gDLL->GameplayDiplomacyAILeaderMessage(GetID(), DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_DEFEATED); } if (MOD_ENABLE_ACHIEVEMENTS && !GC.getGame().isGameMultiPlayer()) { gDLL->UnlockAchievement(ACHIEVEMENT_DESTROY_CIV); CvAchievementUnlocker::AlexanderConquest(ePlayer); } } } // ------------------------------------------------------------------------------------------------------------------- /// Send a statement to another player void CvDiplomacyAI::DoSendStatementToPlayer(PlayerTypes ePlayer, DiploStatementTypes eStatement, int iData1, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); ASSERT(eStatement >= 0, "DIPLOMACY_AI: Invalid DiploStatementType."); PRECONDITION(eStatement < NUM_DIPLO_STATEMENT_TYPES, "DIPLOMACY_AI: Invalid DiploStatementType."); const char* szText = NULL; bool bHuman = GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY); // Aggressive Military warning if(eStatement == DIPLO_STATEMENT_AGGRESSIVE_MILITARY_WARNING) { if(bHuman) { if(IsActHostileTowardsHuman(ePlayer)) szText = GetDiploStringForMessage(DIPLO_MESSAGE_HOSTILE_AGGRESSIVE_MILITARY_WARNING); else szText = GetDiploStringForMessage(DIPLO_MESSAGE_AGGRESSIVE_MILITARY_WARNING); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_AGGRESSIVE_MILITARY_WARNING, szText, LEADERHEAD_ANIM_NEGATIVE); } // AI resolution else { if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam())) { SetMilitaryPromiseState(ePlayer, PROMISE_STATE_MADE); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetMilitaryPromiseState(GetID(), PROMISE_STATE_MADE); } else { MoveTroopsResponseTypes eResponse = GET_PLAYER(ePlayer).GetDiplomacyAI()->GetMoveTroopsRequestResponse(GetID(), /*bJustChecking*/ false); if (eResponse == MOVE_TROOPS_RESPONSE_REFUSE) { if (!GET_PLAYER(ePlayer).GetDiplomacyAI()->DeclareWar(GetTeam())) { SetMilitaryPromiseState(ePlayer, PROMISE_STATE_MADE); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetMilitaryPromiseState(GetID(), PROMISE_STATE_MADE); } else { SetMilitaryPromiseState(ePlayer, PROMISE_STATE_IGNORED); } } else { SetMilitaryPromiseState(ePlayer, PROMISE_STATE_MADE); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetMilitaryPromiseState(GetID(), PROMISE_STATE_MADE); } } } } // Player killed a City-State we were protecting else if (eStatement == DIPLO_STATEMENT_KILLED_PROTECTED_CITY_STATE) { if (bHuman) { PlayerTypes eMinorCiv = (PlayerTypes) iData1; ASSERT(eMinorCiv != NO_PLAYER); if(eMinorCiv != NO_PLAYER) { const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_KILLED_PROTECTED_CITY_STATE, NO_PLAYER, strMinorCivKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } } // Player attacked a City-State we're protecting else if (eStatement == DIPLO_STATEMENT_ATTACKED_PROTECTED_CITY_STATE) { if (bHuman) { PlayerTypes eMinorCiv = (PlayerTypes) iData1; ASSERT(eMinorCiv != NO_PLAYER); if (eMinorCiv != NO_PLAYER) { const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_ATTACKED_PROTECTED_CITY_STATE, NO_PLAYER, strMinorCivKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_ATTACKED_MINOR_CIV, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } else { PlayerTypes eMinorCiv = (PlayerTypes) iData1; ASSERT(eMinorCiv != NO_PLAYER); if (eMinorCiv != NO_PLAYER) { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetCivApproach(eMinorCiv) == CIV_APPROACH_WAR) { SetAttackCityStatePromiseState(ePlayer, PROMISE_STATE_IGNORED); } else { SetAttackCityStatePromiseState(ePlayer, PROMISE_STATE_MADE); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetCivApproach(eMinorCiv, CIV_APPROACH_NEUTRAL); } } } } // Player bullied a City-State we're protecting else if (eStatement == DIPLO_STATEMENT_BULLIED_PROTECTED_CITY_STATE) { if (bHuman) { PlayerTypes eMinorCiv = (PlayerTypes) iData1; ASSERT(eMinorCiv != NO_PLAYER); if (eMinorCiv != NO_PLAYER) { const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_BULLIED_PROTECTED_CITY_STATE, NO_PLAYER, strMinorCivKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_BULLIED_MINOR_CIV, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } else { PlayerTypes eMinorCiv = (PlayerTypes) iData1; ASSERT(eMinorCiv != NO_PLAYER); if (eMinorCiv != NO_PLAYER) { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetCivApproach(eMinorCiv) == CIV_APPROACH_HOSTILE) { SetBullyCityStatePromiseState(ePlayer, PROMISE_STATE_IGNORED); } else { SetBullyCityStatePromiseState(ePlayer, PROMISE_STATE_MADE); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetCivApproach(eMinorCiv, CIV_APPROACH_NEUTRAL); } } } } // Serious Expansion warning else if(eStatement == DIPLO_STATEMENT_EXPANSION_SERIOUS_WARNING) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_EXPANSION_SERIOUS_WARNING); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_EXPANSION_SERIOUS_WARNING, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // Expansion warning else if (eStatement == DIPLO_STATEMENT_EXPANSION_WARNING) { if (bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_EXPANSION_WARNING); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_EXPANSION_WARNING, szText, LEADERHEAD_ANIM_NEGATIVE); } else { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDontSettleAcceptable(GetID())) SetExpansionPromiseState(ePlayer, PROMISE_STATE_MADE); else SetExpansionPromiseState(ePlayer, PROMISE_STATE_IGNORED); } SetAngryAboutExpansion(ePlayer, false); SetEverRequestedExpansionPromise(ePlayer, true); // Flag all of their cities as ignored for future bickering int iCityLoop = 0; for (CvCity* pLoopCity = GET_PLAYER(ePlayer).firstCity(&iCityLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iCityLoop)) { pLoopCity->SetIgnoredForExpansionBickering(GetID(), true); } } // Broken Expansion Promise else if(eStatement == DIPLO_STATEMENT_EXPANSION_BROKEN_PROMISE) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_EXPANSION_BROKEN_PROMISE); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // Serious Plot Buying warning else if(eStatement == DIPLO_STATEMENT_PLOT_BUYING_SERIOUS_WARNING) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_PLOT_BUYING_SERIOUS_WARNING); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_SERIOUS_WARNING, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // Plot Buying warning else if(eStatement == DIPLO_STATEMENT_PLOT_BUYING_WARNING) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_PLOT_BUYING_WARNING); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_WARNING, szText, LEADERHEAD_ANIM_NEGATIVE); } else { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetBoldness() > 8) { SetBorderPromiseState(ePlayer, PROMISE_STATE_IGNORED); } else { SetBorderPromiseState(ePlayer, PROMISE_STATE_MADE); } } } // Broken Plot Buying Promise else if(eStatement == DIPLO_STATEMENT_PLOT_BUYING_BROKEN_PROMISE) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_PLOT_BUYING_BROKEN_PROMISE); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // We attacked a Minor someone has a PtP with else if(eStatement == DIPLO_STATEMENT_WE_ATTACKED_YOUR_MINOR) { if(bHuman) { PlayerTypes eMinorCiv = (PlayerTypes) iData1; ASSERT(eMinorCiv != NO_PLAYER); if(eMinorCiv != NO_PLAYER) { const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); if(IsActHostileTowardsHuman(ePlayer)) szText = GetDiploStringForMessage(DIPLO_MESSAGE_HOSTILE_WE_ATTACKED_YOUR_MINOR, NO_PLAYER, strMinorCivKey); else szText = GetDiploStringForMessage(DIPLO_MESSAGE_WE_ATTACKED_YOUR_MINOR, NO_PLAYER, strMinorCivKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_I_ATTACKED_YOUR_MINOR_CIV, szText, LEADERHEAD_ANIM_POSITIVE, eMinorCiv); // Extra flag, since diplo log does not save which minor civ the message was about SetSentAttackProtectedMinorTaunt(ePlayer, eMinorCiv, true); } } else { PlayerTypes eMinorCiv = (PlayerTypes) iData1; ASSERT(eMinorCiv != NO_PLAYER); if(eMinorCiv != NO_PLAYER) { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetCivApproach(eMinorCiv) == CIV_APPROACH_FRIENDLY || GET_PLAYER(ePlayer).GetDiplomacyAI()->GetMinorCivCompetitiveness() > 6) { SetOtherPlayerSidedWithProtectedMinorTurn(ePlayer, GC.getGame().getGameTurn()); bool bValid = false; if (GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) { if (GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x15c58904).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) < GetWarmongerHate()) { bValid = true; } } if (bValid && (GetBoldness() > 6 || GetMeanness() > 6)) { if (m_pPlayer->GetDiplomacyAI()->DeclareWar(ePlayer)) { pDeal->ClearItems(); bool bCareful = GetPlayer()->CountNumDangerousMajorsAtWarWith(true, true) > 0 && GetGlobalCoopWarAgainstState(ePlayer) < COOP_WAR_STATE_PREPARING; GetPlayer()->GetMilitaryAI()->RequestCityAttack(ePlayer, 3, bCareful); } } } else { GC.getGame().DoMinorPledgeProtection(ePlayer, eMinorCiv, false, true); // Pledge is broken! } } } } // We bullied a Minor someone has a PtP with else if (eStatement == DIPLO_STATEMENT_WE_BULLIED_YOUR_MINOR) { if(bHuman) { PlayerTypes eMinorCiv = (PlayerTypes) iData1; ASSERT(eMinorCiv != NO_PLAYER); if (eMinorCiv != NO_PLAYER) { const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); if(IsActHostileTowardsHuman(ePlayer)) szText = GetDiploStringForMessage(DIPLO_MESSAGE_HOSTILE_WE_BULLIED_YOUR_MINOR, NO_PLAYER, strMinorCivKey); else szText = GetDiploStringForMessage(DIPLO_MESSAGE_WE_BULLIED_YOUR_MINOR, NO_PLAYER, strMinorCivKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_I_BULLIED_YOUR_MINOR_CIV, szText, LEADERHEAD_ANIM_POSITIVE, eMinorCiv); } } else { PlayerTypes eMinorCiv = (PlayerTypes) iData1; ASSERT(eMinorCiv != NO_PLAYER); if(eMinorCiv != NO_PLAYER) { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetCivApproach(eMinorCiv) == CIV_APPROACH_FRIENDLY || GET_PLAYER(ePlayer).GetDiplomacyAI()->GetMinorCivCompetitiveness() > 6) { SetOtherPlayerSidedWithProtectedMinorTurn(ePlayer, GC.getGame().getGameTurn()); bool bValid = false; if (GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) { if (GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x1da72213).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) < GetWarmongerHate()) { bValid = true; } } if (bValid && (GetBoldness() > 7 || GetMeanness() > 7)) { if (m_pPlayer->GetDiplomacyAI()->DeclareWar(ePlayer)) { pDeal->ClearItems(); GetPlayer()->GetMilitaryAI()->RequestCityAttack(ePlayer,3); } } } else { GC.getGame().DoMinorPledgeProtection(ePlayer, eMinorCiv, false, true); // Pledge is broken! } } } } // We'd like a defense pact else if(eStatement == DIPLO_STATEMENT_DEFENSIVE_PACT_REQUEST) { // Active human if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_DEFENSE_PACT_OFFER); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } // Offer to an AI player else { if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // We'd like a 3rd party war else if(eStatement == DIPLO_STATEMENT_THIRDPARTY_WAR_REQUEST) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_THIRDPARTY_WAR_OFFER); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } // Offer to an AI player else { if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // We'd like a peace trade else if(eStatement == DIPLO_STATEMENT_THIRDPARTY_PEACE_REQUEST) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_THIRDPARTY_PEACE_OFFER); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } // Offer to an AI player else { if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // We'd like a vote trade else if(eStatement == DIPLO_STATEMENT_VOTE_REQUEST) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_VOTE_OFFER); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } // Offer to an AI player else { if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // We'd like to trade cities else if(eStatement == DIPLO_STATEMENT_TRADE_CITIES_REQUEST) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_TRADE_CITIES_OFFER); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } // Offer to an AI player else { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // We'd like to exchange cities else if(eStatement == DIPLO_STATEMENT_EXCHANGE_CITIES) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_EXCHANGE_CITIES_OFFER); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } // Offer to an AI player else { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // We'd like to work with a player else if (eStatement == DIPLO_STATEMENT_WORK_WITH_US || eStatement == DIPLO_STATEMENT_DOF_BB || eStatement == DIPLO_STATEMENT_DOF_ALLIES || eStatement == DIPLO_STATEMENT_DOF_FRIENDS || eStatement == DIPLO_STATEMENT_DOF_UNTRUSTWORTHY) { // Send message to human if (bHuman) { switch (eStatement) { case DIPLO_STATEMENT_DOF_BB: szText = GetDiploStringForMessage(DIPLO_MESSAGE_DOF_BATTLE_BROTHERS); break; case DIPLO_STATEMENT_DOF_ALLIES: szText = GetDiploStringForMessage(DIPLO_MESSAGE_DOF_OLD_FRIENDS); break; case DIPLO_STATEMENT_DOF_FRIENDS: szText = GetDiploStringForMessage(DIPLO_MESSAGE_DOF_FRIENDS); break; case DIPLO_STATEMENT_DOF_UNTRUSTWORTHY: szText = GetDiploStringForMessage(DIPLO_MESSAGE_DOF_UNTRUSTWORTHY); break; default: szText = GetDiploStringForMessage(DIPLO_MESSAGE_WORK_WITH_US); break; } CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_WORK_WITH_US, szText, LEADERHEAD_ANIM_REQUEST); } // AI resolution // Accept - reject is assumed from the counter else if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAcceptable(GetID())) { SetDoFAccepted(ePlayer, true); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFAccepted(GetID(), true); // Update diplomacy stuff DoReevaluatePlayer(ePlayer, false, false); GET_PLAYER(ePlayer).GetDiplomacyAI()->DoReevaluatePlayer(GetID(), false, false); LogDoF(ePlayer); } } // We no longer want to work with a player else if(eStatement == DIPLO_STATEMENT_END_WORK_WITH_US) { PlayerTypes eMyPlayer = GetID(); SetDoFAccepted(ePlayer, false); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFAccepted(eMyPlayer, false); // End all coop war agreements with this player GET_PLAYER(ePlayer).GetDiplomacyAI()->CancelCoopWarsWithPlayer(eMyPlayer, true); // End any Defensive Pact GET_TEAM(GET_PLAYER(eMyPlayer).getTeam()).SetHasDefensivePact(GET_PLAYER(ePlayer).getTeam(), false); GET_TEAM(GET_PLAYER(ePlayer).getTeam()).SetHasDefensivePact(GET_PLAYER(eMyPlayer).getTeam(), false); SetDoFType(ePlayer, DOF_TYPE_UNTRUSTWORTHY); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFType(eMyPlayer, DOF_TYPE_UNTRUSTWORTHY); GET_PLAYER(ePlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, -300); // Other players' reactions for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer, true) && !GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && eLoopPlayer != ePlayer && eLoopPlayer != eMyPlayer) { // Our teammates if (IsTeammate(eLoopPlayer)) { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eLoopPlayer) || GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsUntrustworthy(ePlayer)) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, 300); } } else if (GET_PLAYER(eLoopPlayer).getTeam() != GET_PLAYER(ePlayer).getTeam()) { // Player might apply a diplo bonus if they don't hate us if (!GET_TEAM(GetTeam()).isAtWar(GET_PLAYER(eLoopPlayer).getTeam()) && !IsDenouncedPlayer(eLoopPlayer) && !GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsDenouncedPlayer(eMyPlayer) && !GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsUntrustworthy(eMyPlayer) && GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetCivOpinion(eMyPlayer) != CIV_OPINION_UNFORGIVABLE) { // Bonus for ending a DoF with a backstabber if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsUntrustworthy(ePlayer)) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, 75); } // Bonus for ending a DoF with a player they're at war with else if (GET_TEAM(GET_PLAYER(eLoopPlayer).getTeam()).isAtWar(GET_PLAYER(ePlayer).getTeam())) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, 50); // Extra bonus if they're doing badly in the war if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetWarState(ePlayer) <= WAR_STATE_TROUBLED) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, 25); } } // Bonus for ending a DoF with a player who denounced them else if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eLoopPlayer)) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, 50); } // Penalty for ending a DoF with a friend, DP or ally else if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsDoFAccepted(ePlayer) || GET_TEAM(GET_PLAYER(eLoopPlayer).getTeam()).IsHasDefensivePact(GET_PLAYER(ePlayer).getTeam()) || GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetCivOpinion(ePlayer) == CIV_OPINION_ALLY) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, -75); } } // Penalty for ending a DoF with a friend, DP or ally (they hate us) else if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsDoFAccepted(ePlayer) || GET_TEAM(GET_PLAYER(eLoopPlayer).getTeam()).IsHasDefensivePact(GET_PLAYER(ePlayer).getTeam()) || GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetCivOpinion(ePlayer) == CIV_OPINION_ALLY) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, -150); } } // Their teammates else if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsTeammate(ePlayer)) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, -300); } } } GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFBroken(eMyPlayer, true, false); LogBrokenDoF(ePlayer); // Send message to human if (bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_END_WORK_WITH_US, ePlayer); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_NEGATIVE); } } // Denounce else if(eStatement == DIPLO_STATEMENT_DENOUNCE) { DoDenouncePlayer(ePlayer); LogDenounce(ePlayer); // Send message to human if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_WORK_AGAINST_SOMEONE, ePlayer); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // Denounce Friend (backstab) else if(eStatement == DIPLO_STATEMENT_DENOUNCE_FRIEND) { DoDenouncePlayer(ePlayer); LogDenounce(ePlayer, /*bBackstab*/ true); // Send message to human if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_AI_DOF_BACKSTAB, ePlayer); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // Request Friend Denounce Someone else if(eStatement == DIPLO_STATEMENT_REQUEST_FRIEND_DENOUNCE) { PlayerTypes eTarget = (PlayerTypes) iData1; ASSERT(eTarget != NO_PLAYER); if(eTarget != NO_PLAYER) { const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); // Send message to human if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_DOF_AI_DENOUNCE_REQUEST, ePlayer, strTargetCivKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_AI_REQUEST_DENOUNCE, szText, LEADERHEAD_ANIM_POSITIVE, eTarget); } else { bool bAgree = IsDenounceAcceptable(eTarget, /*bBias*/ true); LogFriendRequestDenounce(ePlayer, eTarget, bAgree); if(bAgree) { GET_PLAYER(ePlayer).GetDiplomacyAI()->DoDenouncePlayer(eTarget); GET_PLAYER(ePlayer).GetDiplomacyAI()->LogDenounce(eTarget); // Denounced a human? if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { if(GET_PLAYER(eTarget).isHuman(ISHUMAN_AI_DIPLOMACY)) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_WORK_AGAINST_SOMEONE, eTarget); CvDiplomacyRequests::SendRequest(ePlayer, eTarget, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } else { if(eTarget == GC.getGame().getActivePlayer()) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_WORK_AGAINST_SOMEONE, eTarget); CvDiplomacyRequests::SendRequest(ePlayer, eTarget, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } } else { // Oh, you're gonna say no, are you? if(IsFriendDenounceRefusalUnacceptable(ePlayer, eTarget)) { DoDenouncePlayer(ePlayer); LogDenounce(ePlayer, /*bBackstab*/ false, /*bRefusal*/ true); } } } } } // We'd like to declare war on someone else if (eStatement == DIPLO_STATEMENT_COOP_WAR_REQUEST) { PlayerTypes eAgainstPlayer = (PlayerTypes) iData1; ASSERT(IsPlayerValid(eAgainstPlayer)); if (IsPlayerValid(eAgainstPlayer)) { // Send message to human if (bHuman) { const char* strAgainstPlayerKey = GET_PLAYER(eAgainstPlayer).getNameKey(); szText = GetDiploStringForMessage(DIPLO_MESSAGE_COOP_WAR_REQUEST, ePlayer, strAgainstPlayerKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_COOP_WAR, szText, LEADERHEAD_ANIM_POSITIVE, eAgainstPlayer); } // AI resolution else { CoopWarStates eResponse = GET_PLAYER(ePlayer).GetDiplomacyAI()->RespondToCoopWarRequest(GetID(), eAgainstPlayer); if (eResponse == COOP_WAR_STATE_REJECTED) { int iAssistPenalty = AdjustConditionalModifier(-100, GetNeediness()); ChangeRecentAssistValue(ePlayer, iAssistPenalty); ChangeCoopWarAgreementScore(ePlayer, -1); } } } } /* // We'd like to declare war on someone else if(eStatement == DIPLO_STATEMENT_COOP_WAR_TIME) { PlayerTypes eAgainstPlayer = (PlayerTypes) iData1; ASSERT(eAgainstPlayer != NO_PLAYER); if(eAgainstPlayer != NO_PLAYER) { // Send message to human if(bHuman) { const char* strAgainstPlayerKey = GET_PLAYER(eAgainstPlayer).getNameKey(); szText = GetDiploStringForMessage(DIPLO_MESSAGE_COOP_WAR_TIME, ePlayer, strAgainstPlayerKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_COOP_WAR_TIME, szText, LEADERHEAD_ANIM_POSITIVE, eAgainstPlayer); } } // No AI resolution! This is handled automatically in DoCounters() - no need for diplo exchange } */ // We're making a demand of this player else if (eStatement == DIPLO_STATEMENT_DEMAND) { // Active human if (bHuman) { // Assume they accepted - if they refuse, we'll reset this to 0 in FROM_UI_DIPLO_EVENT_DEMAND_HUMAN_REFUSAL ChangeNumConsecutiveDemandsTheyAccepted(ePlayer, 1); szText = GetDiploStringForMessage(DIPLO_MESSAGE_DEMAND); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_DEMAND, szText, LEADERHEAD_ANIM_DEMAND); } // AI player else { // Process diplomacy consequences from the AI on the receiving end DemandResponseTypes eResponse = GET_PLAYER(ePlayer).GetDealAI()->GetDemandResponse(pDeal); GET_PLAYER(ePlayer).GetDiplomacyAI()->DoDemandMade(GetID(), eResponse); // Did they accept? if (eResponse == DEMAND_RESPONSE_ACCEPT) { if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } // Demand rebuffed else { pDeal->ClearItems(); // Does the AI declare war? bool bDeclareWar = GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID()) && !GetPlayer()->IsNoNewWars(); // Must be a potential war target if (bDeclareWar && !IsPotentialWarTarget(ePlayer)) bDeclareWar = false; // Sanity check - who else would we go to war with? if (bDeclareWar && !DoUpdateOnePlayerSaneDiplomaticTarget(ePlayer, true)) bDeclareWar = false; // Sanity check - avoid going bankrupt int iMinIncome = 2 + (GetPlayer()->GetCurrentEra() * 2); if (bDeclareWar && IsWarWouldBankruptUs(ePlayer, iMinIncome)) bDeclareWar = false; // Sanity check - who else would we go to war with? if (bDeclareWar) { vector vDefensiveWarAllies = GetDefensiveWarAllies(ePlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ true, /*bNewWarsOnly*/ true); for (std::vector::iterator it = vDefensiveWarAllies.begin(); it != vDefensiveWarAllies.end(); ++it) { // Would we be declaring war on a powerful neighbor? if (GET_PLAYER(*it).GetProximityToPlayer(GetID()) >= PLAYER_PROXIMITY_CLOSE) { if (GET_PLAYER(*it).isMajorCiv()) { if (GetCivApproach(*it) == CIV_APPROACH_AFRAID) { bDeclareWar = false; break; } // If we're already planning a war/demand against them, then we don't care. else if (GetCivApproach(*it) != CIV_APPROACH_WAR && GetDemandTargetPlayer() != *it) { if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) { bDeclareWar = false; break; } } } else { if (GetCivApproach(*it) > CIV_APPROACH_HOSTILE) { if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) { bDeclareWar = false; break; } } } } } } if (bDeclareWar) { // If already at war or non-HOSTILE and not decisively stronger, AI has a chance to bluff int iCurrentWars = GetPlayer()->CountNumDangerousMajorsAtWarWith(true, true); bool bCanBluff = iCurrentWars > 0 || (GetCivApproach(ePlayer) > CIV_APPROACH_HOSTILE && GetTargetValue(ePlayer) <= TARGET_VALUE_AVERAGE); if (bCanBluff) { if (GetCivOpinion(ePlayer) == CIV_OPINION_UNFORGIVABLE || IsEndgameAggressiveTo(ePlayer) || IsCapitalCapturedBy(ePlayer, true, true) || IsHolyCityCapturedBy(ePlayer, true, true)) { bCanBluff = false; } } if (bCanBluff) { int iBluffChance = 20 + 20 * min(iCurrentWars, 3); if (GetBiggestCompetitor() == ePlayer) iBluffChance /= 2; if (GC.getGame().randRangeInclusive(1, 100, CvSeeder::fromRaw(0x40f558f0).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) > iBluffChance) bCanBluff = false; } // Not bluffing - declare war! if (!bCanBluff && DeclareWar(ePlayer)) { bool bCareful = iCurrentWars > 0 && GetGlobalCoopWarAgainstState(ePlayer) < COOP_WAR_STATE_PREPARING; GetPlayer()->GetMilitaryAI()->RequestCityAttack(ePlayer, 3, bCareful); } } } } } // We're making a request of this player else if(eStatement == DIPLO_STATEMENT_REQUEST) { // Active human if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_REQUEST); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_REQUEST, szText, LEADERHEAD_ANIM_REQUEST); } // AI player else { // For now the AI will always give in - may eventually write additional logic here if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // Player has a Luxury Resource we'd like to trade for else if(eStatement == DIPLO_STATEMENT_LUXURY_TRADE) { // Active human if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_LUXURY_TRADE); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } // Offer to an AI player else { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // We'd like to exchange embassies with this player else if(eStatement == DIPLO_STATEMENT_EMBASSY_EXCHANGE) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_EMBASSY_EXCHANGE); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } else { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // We want an embassy in this player's capital else if(eStatement == DIPLO_STATEMENT_EMBASSY_OFFER) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_EMBASSY_OFFER); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } else { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // We'd like mutual Open Borders with this player else if(eStatement == DIPLO_STATEMENT_OPEN_BORDERS_EXCHANGE) { // Active human if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_OPEN_BORDERS_EXCHANGE); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } // Offer to an AI player else { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // We'd like this player to open their borders to us else if(eStatement == DIPLO_STATEMENT_OPEN_BORDERS_OFFER) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_OPEN_BORDERS_OFFER); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } // Offer to an AI player else { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // Offer a Research Agreement else if(eStatement == DIPLO_STATEMENT_RESEARCH_AGREEMENT_OFFER) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_RESEARCH_AGREEMENT_OFFER); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } // Offer to an AI player else { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // Offer to renew an existing trade deal else if (eStatement == DIPLO_STATEMENT_RENEW_DEAL) { if(bHuman) { int iDealValueToMe = 0; DiploMessageTypes eMessageType = NUM_DIPLO_MESSAGE_TYPES; bool bCantMatchOffer = false; bool bDealAcceptable = m_pPlayer->GetDealAI()->IsDealWithHumanAcceptable(pDeal, ePlayer, iDealValueToMe, &bCantMatchOffer, false); if(bDealAcceptable) { eMessageType = DIPLO_MESSAGE_RENEW_DEAL; } // We want more from this deal else if(iDealValueToMe < 0) { eMessageType = DIPLO_MESSAGE_WANT_MORE_RENEW_DEAL; } if(eMessageType != NUM_DIPLO_MESSAGE_TYPES) { CvDeal kDeal = *pDeal; CvGameDeals::PrepareRenewDeal(&kDeal); pDeal->m_bCheckedForRenewal = true; szText = GetDiploStringForMessage(eMessageType); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, &kDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST, /* bRenew */ true); } else { CvDeal kDeal = *pDeal; CancelRenewDeal(ePlayer, REASON_CANNOT_COMPROMISE, false, &kDeal); } } // Offer to an AI player else { CvDeal kDeal = *pDeal; CvGameDeals::PrepareRenewDeal(&kDeal); if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(kDeal, true); } else { // Don't need to call DoOffer because we have already checked that the deal is acceptable for both sides GC.getGame().GetGameDeals().AddProposedDeal(kDeal); bool bFinalized = GC.getGame().GetGameDeals().FinalizeDeal(kDeal.GetFromPlayer(), kDeal.GetToPlayer(), true); if (!bFinalized) UNREACHABLE(); // should never happen for a renew deal } } } // Our Opinion of them is now Unforgivable else if(eStatement == DIPLO_STATEMENT_NOW_UNFORGIVABLE) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_NOW_UNFORGIVABLE); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // Our Opinion of them is now Enemy else if(eStatement == DIPLO_STATEMENT_NOW_ENEMY) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_NOW_ENEMY); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // They caught one of our spies else if(eStatement == DIPLO_STATEMENT_CAUGHT_YOUR_SPY) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_CAUGHT_YOUR_SPY); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_CAUGHT_YOUR_SPY, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // They killed one of our spies else if(eStatement == DIPLO_STATEMENT_KILLED_YOUR_SPY) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_KILLED_YOUR_SPY); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_KILLED_YOUR_SPY, szText, LEADERHEAD_ANIM_NEGATIVE); } } // We killed one of their spies else if(eStatement == DIPLO_STATEMENT_KILLED_MY_SPY) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_KILLED_MY_SPY); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_KILLED_MY_SPY, szText, LEADERHEAD_ANIM_DEFEATED); } } // We (the AI) have intrigue information to share with them else if(eStatement == DIPLO_STATEMENT_SHARE_INTRIGUE) { IntrigueNotificationMessage* pNotificationMessage = GetPlayer()->GetEspionage()->GetRecentIntrigueInfo(ePlayer); ASSERT(pNotificationMessage, "pNotificationMessage is null. Whut?"); if (pNotificationMessage) { PRECONDITION(pNotificationMessage->m_eSourcePlayer != NO_PLAYER, "There is no plotter! What's going on"); PlayerTypes ePlotterPlayer = pNotificationMessage->m_eSourcePlayer; CvIntrigueType eIntrigueType = (CvIntrigueType)pNotificationMessage->m_iIntrigueType; // don't share intrigue about two parties if they are already at war, except for the information that someone has been bribed to go to war if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isAtWar(GET_PLAYER(ePlotterPlayer).getTeam()) || eIntrigueType == INTRIGUE_TYPE_BRIBE_WAR) { CvCity* pCity = NULL; if(pNotificationMessage->m_iCityX != -1 && pNotificationMessage->m_iCityY != -1) { CvPlot* pPlot = GC.getMap().plot(pNotificationMessage->m_iCityX, pNotificationMessage->m_iCityY); if(pPlot) { pCity = pPlot->getPlotCity(); } } // add the notification to the player GET_PLAYER(ePlayer).GetEspionage()->AddIntrigueMessage(GetID(), ePlotterPlayer, ePlayer, pNotificationMessage->m_eDiplomacyPlayer, NO_BUILDING, NO_PROJECT, NO_UNIT, eIntrigueType, 0, pCity, false); if(bHuman) { const char* szPlayerName = NULL; if(GC.getGame().isGameMultiPlayer() && GET_PLAYER(ePlotterPlayer).isHuman(ISHUMAN_UI)) { szPlayerName = GET_PLAYER(ePlotterPlayer).getNickName(); } else { szPlayerName = GET_PLAYER(ePlotterPlayer).getNameKey(); } const char* szBribedPlayerName = NULL; szText = ""; switch(eIntrigueType) { case INTRIGUE_TYPE_ARMY_SNEAK_ATTACK: if(pCity) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_ARMY_SNEAK_ATTACK_KNOWN_CITY, NO_PLAYER, szPlayerName, pCity->getNameKey()); } else { szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_ARMY_SNEAK_ATTACK_UNKNOWN_CITY, NO_PLAYER, szPlayerName); } break; case INTRIGUE_TYPE_AMPHIBIOUS_SNEAK_ATTACK: if(pCity) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_AMPHIBIOUS_SNEAK_ATTACK_KNOWN_CITY, NO_PLAYER, szPlayerName, pCity->getNameKey()); } else { szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_AMPHIBIOUS_SNEAK_ATTACK_UNKNOWN_CITY, NO_PLAYER, szPlayerName); } break; case INTRIGUE_TYPE_DECEPTION: szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE, NO_PLAYER, szPlayerName); break; case INTRIGUE_TYPE_BRIBE_WAR: if (GC.getGame().isGameMultiPlayer() && GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).isHuman(ISHUMAN_UI)) { szBribedPlayerName = GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).getNickName(); } else { szBribedPlayerName = GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).getNameKey(); } szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_BRIBE_WAR, NO_PLAYER, szPlayerName, szBribedPlayerName); break; case INTRIGUE_TYPE_COOP_WAR: if (GC.getGame().isGameMultiPlayer() && GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).isHuman(ISHUMAN_UI)) { szBribedPlayerName = GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).getNickName(); } else { szBribedPlayerName = GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).getNameKey(); } szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_COOP_WAR, NO_PLAYER, szPlayerName, szBribedPlayerName); break; default: ASSERT(false, "Unknown intrigue type"); break; } CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } else { GET_PLAYER(ePlayer).GetDiplomacyAI()->ChangeNumTimesIntrigueSharedBy(GetID(), 1); } } // mark the messages as shared so the player isn't told the same thing repeatedly for(uint ui = 0; ui < MAX_MAJOR_CIVS; ui++) { PlayerTypes eLoopPlayer = (PlayerTypes)ui; GET_PLAYER(eLoopPlayer).GetEspionage()->MarkRecentIntrigueAsShared(ePlayer, ePlotterPlayer, pNotificationMessage->m_eDiplomacyPlayer, eIntrigueType); } } } // Stop converting our cities else if(eStatement == DIPLO_STATEMENT_STOP_CONVERSIONS) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_STOP_CONVERSIONS); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_STOP_CONVERSIONS, szText, LEADERHEAD_ANIM_NEGATIVE); } } // Stop digging up our yard else if(eStatement == DIPLO_STATEMENT_STOP_DIGGING) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_STOP_DIGGING); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_STOP_DIGGING, szText, LEADERHEAD_ANIM_NEGATIVE); } } // Insult else if(eStatement == DIPLO_STATEMENT_INSULT) { // Change other players' guess as to our Approach (right now it falls in line exactly with the Approach...) //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuess(GetID(), CIV_APPROACH_HOSTILE); //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuessCounter(GetID(), 0); if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_INSULT_ROOT); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // Compliment else if(eStatement == DIPLO_STATEMENT_COMPLIMENT) { // Change other players' guess as to our Approach (right now it falls in line exactly with the Approach...) //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuess(GetID(), CIV_APPROACH_FRIENDLY); //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuessCounter(GetID(), 0); if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_COMPLIMENT); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } // Boot-kissing of a stronger power else if(eStatement == DIPLO_STATEMENT_BOOT_KISSING) { // Change other players' guess as to our Approach (right now it falls in line exactly with the Approach...) //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuess(GetID(), CIV_APPROACH_AFRAID); //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuessCounter(GetID(), 0); if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_BOOT_KISSING); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } // We're warning a player that his warmongering behavior is attracting attention else if(eStatement == DIPLO_STATEMENT_WARMONGER) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_WARMONGER); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // We're warning a player that his interactions with City-States are not to our liking else if(eStatement == DIPLO_STATEMENT_MINOR_CIV_COMPETITION) { if(bHuman) { PlayerTypes eMinorCiv = (PlayerTypes) iData1; const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); szText = GetDiploStringForMessage(DIPLO_MESSAGE_MINOR_CIV_COMPETITION, NO_PLAYER, strMinorCivKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_NEGATIVE); } } // Human befriended an enemy of this AI! else if(eStatement == DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY) { if(bHuman) { PlayerTypes eTarget = (PlayerTypes) iData1; const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DOFED_ENEMY, ePlayer, strTargetCivKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // Human denounced a friend of this AI! else if(eStatement == DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND) { if(bHuman) { PlayerTypes eTarget = (PlayerTypes) iData1; const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DENOUNCED_FRIEND, ePlayer, strTargetCivKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // Human denounced an enemy of this AI! else if(eStatement == DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY) { if(bHuman) { PlayerTypes eTarget = (PlayerTypes) iData1; const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DENOUNCED_ENEMY, ePlayer, strTargetCivKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } // Human befriended a friend of this AI! else if(eStatement == DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND) { if(bHuman) { PlayerTypes eTarget = (PlayerTypes) iData1; const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DOFED_FRIEND, ePlayer, strTargetCivKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } // AI befriended an enemy of the human! else if(eStatement == DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_ENEMY) { if(bHuman) { PlayerTypes eTarget = (PlayerTypes) iData1; const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DENOUNCE_SO_AI_DOF, ePlayer, strTargetCivKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // AI denounced a friend of the human! else if(eStatement == DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND) { if(bHuman) { PlayerTypes eTarget = (PlayerTypes) iData1; const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DOF_SO_AI_DENOUNCE, ePlayer, strTargetCivKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // AI denounced an enemy of the human! else if(eStatement == DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY) { if(bHuman) { PlayerTypes eTarget = (PlayerTypes) iData1; const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DENOUNCE_SO_AI_DENOUNCE, ePlayer, strTargetCivKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } // AI befriended a friend of the human! else if(eStatement == DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND) { if(bHuman) { PlayerTypes eTarget = (PlayerTypes) iData1; const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DOF_SO_AI_DOF, ePlayer, strTargetCivKey); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } // AI chose same late game policy tree as the human! else if(eStatement == DIPLO_STATEMENT_SAME_POLICIES_FREEDOM) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_SAME_POLICIES_FREEDOM); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } else if(eStatement == DIPLO_STATEMENT_SAME_POLICIES_ORDER) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_SAME_POLICIES_ORDER); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } else if(eStatement == DIPLO_STATEMENT_SAME_POLICIES_AUTOCRACY) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_SAME_POLICIES_AUTOCRACY); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } else if(eStatement == DIPLO_STATEMENT_WE_LIKED_THEIR_PROPOSAL) { if(bHuman) { Localization::String sLeagueName = Localization::Lookup("TXT_KEY_LEAGUE_WORLD_CONGRESS_GENERIC"); CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (pLeague != NULL) { sLeagueName = pLeague->GetName(); } szText = GetDiploStringForMessage(DIPLO_MESSAGE_WE_LIKED_THEIR_PROPOSAL, ePlayer, sLeagueName); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } else if(eStatement == DIPLO_STATEMENT_WE_DISLIKED_THEIR_PROPOSAL) { if(bHuman) { Localization::String sLeagueName = Localization::Lookup("TXT_KEY_LEAGUE_WORLD_CONGRESS_GENERIC"); CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (pLeague != NULL) { sLeagueName = pLeague->GetName(); } szText = GetDiploStringForMessage(DIPLO_MESSAGE_WE_DISLIKED_THEIR_PROPOSAL, ePlayer, sLeagueName); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } else if(eStatement == DIPLO_STATEMENT_THEY_SUPPORTED_OUR_PROPOSAL) { if(bHuman) { Localization::String sLeagueName = Localization::Lookup("TXT_KEY_LEAGUE_WORLD_CONGRESS_GENERIC"); szText = GetDiploStringForMessage(DIPLO_MESSAGE_THEY_SUPPORTED_OUR_PROPOSAL, ePlayer, sLeagueName); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } else if(eStatement == DIPLO_STATEMENT_THEY_FOILED_OUR_PROPOSAL) { if(bHuman) { Localization::String sLeagueName = Localization::Lookup("TXT_KEY_LEAGUE_WORLD_CONGRESS_GENERIC"); szText = GetDiploStringForMessage(DIPLO_MESSAGE_THEY_FOILED_OUR_PROPOSAL, ePlayer, sLeagueName); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } else if(eStatement == DIPLO_STATEMENT_THEY_SUPPORTED_OUR_HOSTING) { if(bHuman) { Localization::String sLeagueName = Localization::Lookup("TXT_KEY_LEAGUE_WORLD_CONGRESS_GENERIC"); CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (pLeague != NULL) { sLeagueName = pLeague->GetName(); } szText = GetDiploStringForMessage(DIPLO_MESSAGE_THEY_SUPPORTED_OUR_HOSTING, ePlayer, sLeagueName); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } // Ideological statements else if(eStatement == DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } else if(eStatement == DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } else if(eStatement == DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } else if(eStatement == DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } else if(eStatement == DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } else if(eStatement == DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } else if(eStatement == DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_FREEDOM) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_SWITCH_OUR_IDEOLOGY_FREEDOM); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } else if(eStatement == DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_ORDER) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_SWITCH_OUR_IDEOLOGY_ORDER); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } else if(eStatement == DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_AUTOCRACY) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_SWITCH_OUR_IDEOLOGY_AUTOCRACY); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } else if(eStatement == DIPLO_STATEMENT_YOUR_CULTURE_INFLUENTIAL) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_YOUR_CULTURE_INFLUENTIAL); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } else if(eStatement == DIPLO_STATEMENT_OUR_CULTURE_INFLUENTIAL) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_OUR_CULTURE_INFLUENTIAL); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } // Player has a Strategic Resource we'd like else if(eStatement == DIPLO_STATEMENT_STRATEGIC_TRADE) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_STRATEGIC_TRADE); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } // Offer to an AI player else { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // Announce to the human that this AI is competing with them for the same victory condition else if(eStatement == DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONQUEST) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONQUEST); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } else if(eStatement == DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONGRESS) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONGRESS); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } else if(eStatement == DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_CULTURE) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_CULTURE); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } else if(eStatement == DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_SPACESHIP) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_SPACESHIP); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } else if(eStatement == DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_CONFUSED) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_CONFUSED); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } // Announce to the human that this AI wants to block them from achieving victory else if(eStatement == DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_WORLD_CONQUEST) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_WORLD_CONQUEST); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } else if(eStatement == DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_WORLD_CONGRESS) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_WORLD_CONGRESS); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } else if(eStatement == DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_CULTURE) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_CULTURE); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } else if(eStatement == DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_SPACESHIP) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_SPACESHIP); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } // We'd like to purchase this player's World Map else if(eStatement == DIPLO_STATEMENT_MAPS_OFFER) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_MAPS_OFFER); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } else { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // We'd like to purchase a technology from this player else if(eStatement == DIPLO_STATEMENT_TECH_OFFER) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_TECH_OFFER); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } else { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // We're making a generous offer to this player else if(eStatement == DIPLO_STATEMENT_GENEROUS_OFFER) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_GENEROUS_OFFER); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_GENEROUS_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } else { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { // For now the AI will always accept - may eventually write additional logic here CvDeal kDeal = *pDeal; GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } //We want to declare independence from our master else if(eStatement == DIPLO_STATEMENT_REVOKE_VASSALAGE) { if (bHuman) { if (IsActHostileTowardsHuman(ePlayer)) szText = GetDiploStringForMessage(DIPLO_MESSAGE_REVOKE_VASSALAGE_HOSTILE); else szText = GetDiploStringForMessage(DIPLO_MESSAGE_REVOKE_VASSALAGE); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_AI_REVOKE_VASSALAGE, szText, LEADERHEAD_ANIM_NEGATIVE); } // AI resolution else { CvPlayer& kVassalPlayer = GET_PLAYER(GetID()); CvPlayer& kMasterPlayer = GET_PLAYER(ePlayer); bool bPeaceful = kMasterPlayer.GetDiplomacyAI()->IsEndVassalageAcceptable(kVassalPlayer.GetID()); GET_TEAM(kVassalPlayer.getTeam()).DoEndVassal(kMasterPlayer.getTeam(), bPeaceful, false); } } //We want this player to liberate their vassals else if(eStatement == DIPLO_STATEMENT_REVOKE_VASSALAGE_THIRD_PARTY) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_REVOKE_VASSAL_THIRD_OFFER); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_POSITIVE); } // Offer to an AI player else { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // AI offers to make ePlayer his voluntary vassal else if(eStatement == DIPLO_STATEMENT_BECOME_MY_VASSAL) { if(bHuman) { ASSERT(false, "Don't send vassalage statement to human!"); } else { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // AI offers to become voluntary vassal of ePlayer else if (eStatement == DIPLO_STATEMENT_ACCEPT_VASSALAGE) { if (bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSALAGE_BECOME_VASSAL); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_POSITIVE); } else { if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } // AI is happy that they were liberated from vassalage else if(eStatement == DIPLO_STATEMENT_LIBERATE_VASSAL) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSALAGE_LIBERATE_VASSAL); CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } // Liberate this vassal GET_TEAM(GetTeam()).DoLiberateVassal(GET_PLAYER(ePlayer).getTeam()); } // AI is upset that their taxes were raised else if(eStatement == DIPLO_STATEMENT_VASSAL_TAXES_RAISED_HUMAN_MASTER) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSAL_TAXES_RAISED_HUMAN_MASTER, ePlayer); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_NEGATIVE); } else { ASSERT(false, "Don't send this message to AI!"); } } // AI is happy that their taxes were lowered else if(eStatement == DIPLO_STATEMENT_VASSAL_TAXES_LOWERED_HUMAN_MASTER) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSAL_TAXES_LOWERED_HUMAN_MASTER, ePlayer); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } else { ASSERT(false, "Don't send this message to AI!"); } } // AI notifies human that their taxes were RAISED else if(eStatement == DIPLO_STATEMENT_VASSAL_TAXES_RAISED_AI_MASTER) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSAL_TAXES_RAISED_AI_MASTER, ePlayer); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_NEGATIVE); } else { ASSERT(false, "Don't send this message to AI!"); } } // AI notifies human that their taxes were LOWERED else if(eStatement == DIPLO_STATEMENT_VASSAL_TAXES_LOWERED_AI_MASTER) { if(bHuman) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSAL_TAXES_LOWERED_AI_MASTER, ePlayer); CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } else { ASSERT(false, "Don't send this message to AI!"); } } // Do we want peace with ePlayer? else if (eStatement == DIPLO_STATEMENT_REQUEST_PEACE) { if (bHuman) { int iOurWarScore = GetWarScore(ePlayer); if (iOurWarScore >= 10) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_WINNER_PEACE_OFFER); } else if (iOurWarScore <= -10) { szText = GetDiploStringForMessage(DIPLO_MESSAGE_PEACE_MADE_BY_HUMAN_GRACIOUS); } else { szText = GetDiploStringForMessage(DIPLO_MESSAGE_PEACE_OFFER); } CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_POSITIVE); } // Offer to an AI player else { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { CvDeal kDeal = *pDeal; // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending GC.getGame().GetGameDeals().AddProposedDeal(kDeal); GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } LogPeaceMade(ePlayer); } } } /// Any Major Civs we want to chat with? void CvDiplomacyAI::DoContactMajorCivs() { DetermineVassalToLiberate(); // NOTE: This function is broken up into two sections: AI contact opportunities, and then human contact opportunities // This is to prevent a nasty bug where the AI will continue making decisions as the diplo screen is firing up. Making humans // handled at the end prevents the Diplo AI from having this problem // Loop through AI Players if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { if (m_eDiploMode == DIPLO_SPECIFIC_PLAYER) { DoContactPlayer(m_eTargetPlayer); } else if (m_eDiploMode == DIPLO_ALL_PLAYERS || m_eDiploMode == DIPLO_AI_PLAYERS) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes)iPlayerLoop; if (!IsPlayerValid(eLoopPlayer)) continue; // No humans if (GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) continue; DoContactPlayer(eLoopPlayer); } } if (m_eDiploMode == DIPLO_ALL_PLAYERS || m_eDiploMode == DIPLO_HUMAN_PLAYERS) { // JdH => contact humans by priority, but use a notification system instead of pop up the diplo screen // every AI can only talk to one human a time (as a human can only talk to one human a time // TODO: the one to one restriction should be removed in favor of a trade resource pool allocation if (!CvDiplomacyRequests::HasActiveDiploRequestWithHuman(GetID())) { vector aeHumansByPriority; vector::const_iterator priorityIter, humanIter; // bring players in priority order for (humanIter = CvDiplomacyRequests::s_aDiploHumans.begin(); humanIter != CvDiplomacyRequests::s_aDiploHumans.end(); ++humanIter) { PlayerTypes eLoopPlayer = *humanIter; ASSERT(CvPreGame::isHuman(eLoopPlayer)); ASSERT(GET_PLAYER(eLoopPlayer).isTurnActive()); if (!IsPlayerValid(eLoopPlayer)) continue; // No AI if (!GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) continue; // Only active Players if (!GET_PLAYER(eLoopPlayer).isTurnActive()) continue; for (priorityIter = aeHumansByPriority.begin(); priorityIter != aeHumansByPriority.end(); ++priorityIter) { if (m_aTradePriority[*priorityIter] < m_aTradePriority[eLoopPlayer]) { aeHumansByPriority.insert(priorityIter, eLoopPlayer); break; } } if (priorityIter == aeHumansByPriority.end()) { aeHumansByPriority.push_back(eLoopPlayer); } } for (humanIter = aeHumansByPriority.begin(); humanIter != aeHumansByPriority.end(); ++humanIter) { DoContactPlayer(*humanIter); if (GET_PLAYER(*humanIter).GetDiplomacyRequests()->HasActiveRequestFrom(GetID())) { // we actually found someone worth talking with, the others must wait... break; } } } } } else { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (!IsPlayerValid(eLoopPlayer)) continue; // No humans if (GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) continue; DoContactPlayer(eLoopPlayer); } // Loop through HUMAN Players - if we're not in MP if (!CvPreGame::isNetworkMultiplayerGame()) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (!IsPlayerValid(eLoopPlayer)) continue; // No AI if (!GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) continue; DoContactPlayer(eLoopPlayer); } } } } /// Individual contact opportunity void CvDiplomacyAI::DoContactPlayer(PlayerTypes ePlayer) { if (!IsValidUIDiplomacyTarget(ePlayer)) return; // Can't contact this player at the moment. // Can't contact this player because of game options if (MOD_DIPLOAI_SHUT_UP && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; DiploStatementTypes eStatement = NO_DIPLO_STATEMENT_TYPE; // We can use this deal pointer to form a trade offer CvDeal* pDeal = GC.getGame().GetGameDeals().GetTempDeal(); // These can be used for info about deal items, e.g. what Minor Civ we're telling the guy to stay away from, etc. int iData1 = 0; // If this is the same turn we've met a player, don't send anything his way quite yet - wait until we've said hello at least if (GET_TEAM(GetTeam()).GetTurnsSinceMeetingTeam(GET_PLAYER(ePlayer).getTeam()) == 0) return; //End the gift exchange at the start of each round. GetPlayer()->GetDiplomacyAI()->SetOfferingGift(ePlayer, false); GetPlayer()->GetDiplomacyAI()->SetOfferedGift(ePlayer, false); pDeal->SetRequestingPlayer(NO_PLAYER); //Clear this data out before any deals are offered. SetCantMatchDeal(ePlayer, false); iData1 = -1; pDeal->ClearItems(); pDeal->SetFromPlayer(GetID()); pDeal->SetToPlayer(ePlayer); pDeal->SetDuration(GC.getGame().getGameSpeedInfo().GetDealDuration()); bool bSanctioned = false; if (MOD_BALANCE_VP) { CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (pLeague && pLeague->IsTradeEmbargoed(m_pPlayer->GetID(), ePlayer)) { bSanctioned = true; } } // JON: Add in some randomization here? // How predictable do we want the AI to be with regards to what state they're in? // Note that the order in which the following functions are called is very important to how the AI behaves - first come, first served CvDeal* pRenewDeal = NULL; // AT PEACE if (!IsAtWar(ePlayer)) { // Avoiding exchanges? if (AvoidExchangesWithPlayer(ePlayer)) { SetAvoidDeals(true); } //DoCoopWarTimeStatement(ePlayer, eStatement, iData1); DoCoopWarStatement(ePlayer, eStatement, iData1); pRenewDeal = DoRenewExpiredDeal(ePlayer, eStatement); // Some things we don't say to teammates if (GetTeam() != GET_PLAYER(ePlayer).getTeam()) { // STATEMENTS - all members but ePlayer passed by address // Some things we only say to our masters if (IsVassal(ePlayer)) { DoEndVassalageStatement(ePlayer, eStatement); } DoAggressiveMilitaryStatement(ePlayer, eStatement); DoKilledCityStateStatement(ePlayer, eStatement, iData1); DoAttackedCityStateStatement(ePlayer, eStatement, iData1); DoBulliedCityStateStatement(ePlayer, eStatement, iData1); //DoSeriousExpansionWarningStatement(ePlayer, eStatement); DoExpansionWarningStatement(ePlayer, eStatement); DoExpansionBrokenPromiseStatement(ePlayer, eStatement); //DoSeriousPlotBuyingWarningStatement(ePlayer, eStatement); DoPlotBuyingWarningStatement(ePlayer, eStatement); DoPlotBuyingBrokenPromiseStatement(ePlayer, eStatement); DoWeAttackedYourMinorStatement(ePlayer, eStatement, iData1); DoWeBulliedYourMinorStatement(ePlayer, eStatement, iData1); DoKilledYourSpyStatement(ePlayer, eStatement); DoKilledMySpyStatement(ePlayer, eStatement); DoCaughtYourSpyStatement(ePlayer, eStatement); DoTheySupportedOurHosting(ePlayer, eStatement); DoWeLikedTheirProposal(ePlayer, eStatement); DoWeDislikedTheirProposal(ePlayer, eStatement); DoTheySupportedOurProposal(ePlayer, eStatement); DoTheyFoiledOurProposal(ePlayer, eStatement); DoConvertedMyCityStatement(ePlayer, eStatement); DoDugUpMyYardStatement(ePlayer, eStatement); DoDoFStatement(ePlayer, eStatement); DoDenounceFriendStatement(ePlayer, eStatement); DoDenounceStatement(ePlayer, eStatement); DoEndDoFStatement(ePlayer, eStatement); //DoRequestFriendDenounceStatement(ePlayer, eStatement, iData1); if (!bSanctioned) { DoMapsOffer(ePlayer, eStatement, pDeal); DoTechOffer(ePlayer, eStatement, pDeal); } DoRevokeVassalageStatement(ePlayer, eStatement, pDeal); DoMakeVassalageStatement(ePlayer, eStatement, pDeal); DoLiberateMyVassalStatement(ePlayer, eStatement); } // OFFERS - all members but ePlayer passed by address if (!bSanctioned) { DoLuxuryTrade(ePlayer, eStatement, pDeal); DoEmbassyExchange(ePlayer, eStatement, pDeal); DoEmbassyOffer(ePlayer, eStatement, pDeal); DoOpenBordersExchange(ePlayer, eStatement, pDeal); DoOpenBordersOffer(ePlayer, eStatement, pDeal); DoResearchAgreementOffer(ePlayer, eStatement, pDeal); DoStrategicTrade(ePlayer, eStatement, pDeal); DoDefensivePactOffer(ePlayer, eStatement, pDeal); DoCityExchange(ePlayer, eStatement, pDeal); DoThirdPartyWarTrade(ePlayer, eStatement, pDeal); DoThirdPartyPeaceTrade(ePlayer, eStatement, pDeal); DoVoteTrade(ePlayer, eStatement, pDeal); } DoBecomeVassalageStatement(ePlayer, eStatement, pDeal); DoShareIntrigueStatement(ePlayer, eStatement); if (!bSanctioned) { DoRequest(ePlayer, eStatement, pDeal); DoGenerousOffer(ePlayer, eStatement, pDeal); } // Second set of things we don't say to teammates if (GetTeam() != GET_PLAYER(ePlayer).getTeam()) { //DoNowUnforgivableStatement(ePlayer, eStatement); //DoNowEnemyStatement(ePlayer, eStatement); //DoFriendlyStatement(ePlayer, eStatement); DoAfraidStatement(ePlayer, eStatement); DoHostileStatement(ePlayer, eStatement); DoWarmongerStatement(ePlayer, eStatement); DoMinorCivCompetitionStatement(ePlayer, eStatement, iData1); // Don't bother with this fluff stuff it's just AI on AI stuff if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) { DoAngryBefriendedEnemy(ePlayer, eStatement, iData1); DoAngryDenouncedFriend(ePlayer, eStatement, iData1); DoHappyDenouncedEnemy(ePlayer, eStatement, iData1); DoHappyBefriendedFriend(ePlayer, eStatement, iData1); DoFYIBefriendedHumanEnemy(ePlayer, eStatement, iData1); DoFYIDenouncedHumanFriend(ePlayer, eStatement, iData1); DoFYIDenouncedHumanEnemy(ePlayer, eStatement, iData1); DoFYIBefriendedHumanFriend(ePlayer, eStatement, iData1); DoHappySamePolicyTree(ePlayer, eStatement); DoIdeologicalStatement(ePlayer, eStatement); DoVictoryCompetitionStatement(ePlayer, eStatement); DoVassalTaxesRaisedStatement(ePlayer, eStatement); DoVassalTaxesLoweredStatement(ePlayer, eStatement); } } } // AT WAR else if (!IsAlwaysAtWar(ePlayer)) { // OFFERS - all members but ePlayer passed by address DoPeaceOffer(ePlayer, eStatement, pDeal); // If not offering peace, can still denounce while at war! DoDenounceStatement(ePlayer, eStatement); } // Reset avoiding deals value SetAvoidDeals(false); #if !defined(FINAL_RELEASE) || defined(VPDEBUG) // Check for an optional message injection from the Tuner if(eStatement == NO_DIPLO_STATEMENT_TYPE && m_eTestStatement != NO_DIPLO_STATEMENT_TYPE && ePlayer == m_eTestToPlayer) { eStatement = m_eTestStatement; iData1 = m_iTestStatementArg1; m_eTestStatement = NO_DIPLO_STATEMENT_TYPE; } #endif // Now send the message if (eStatement != NO_DIPLO_STATEMENT_TYPE) { LogStatementToPlayer(ePlayer, eStatement); SetTurnStatementLastSent(ePlayer, eStatement, GC.getGame().getGameTurn()); DoSendStatementToPlayer(ePlayer, eStatement, iData1, pRenewDeal ? pRenewDeal : pDeal); } } /// Any Minor Civs we want to chat with? void CvDiplomacyAI::DoContactMinorCivs() { if (GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR)) return; int iDiplomacyFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_DIPLOMACY")); int iGoldFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_GOLD")); int iTileImprovementFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_TILE_IMPROVEMENT")); bool bExpandToOtherContinents = GetPlayer()->GetEconomicAI()->IsUsingStrategy((EconomicAIStrategyTypes)GC.getInfoTypeForString("ECONOMICAISTRATEGY_EXPAND_TO_OTHER_CONTINENTS")); bool bNeedHappiness = GetPlayer()->GetEconomicAI()->IsUsingStrategy((EconomicAIStrategyTypes)GC.getInfoTypeForString("ECONOMICAISTRATEGY_NEED_HAPPINESS")); bool bNeedHappinessCritical = GetPlayer()->GetEconomicAI()->IsUsingStrategy((EconomicAIStrategyTypes)GC.getInfoTypeForString("ECONOMICAISTRATEGY_NEED_HAPPINESS_CRITICAL")); bool bLosingMoney = GetPlayer()->GetEconomicAI()->IsUsingStrategy((EconomicAIStrategyTypes)GC.getInfoTypeForString("ECONOMICAISTRATEGY_LOSING_MONEY")); // ************************** // Would we like to buyout a minor this turn? (Austria UA) // ************************** bool bWantsToBuyout = GetPlayer()->IsAbleToAnnexCityStates(); bool bWantsToMarry = GetPlayer()->GetPlayerTraits()->IsDiplomaticMarriage(); // ************************** // Would we like to forcefully annex a minor this turn? // ************************** bool bWantsToBullyAnnex = GetPlayer()->GetPlayerTraits()->IsBullyAnnex(); // ************************** // Would we like to give a gold gift this turn? // ************************** bool bWantsToMakeGoldGift = false; // If we're a highly diplomatic leader, then always look for an opportunity if(iDiplomacyFlavor >= /*4*/ GD_INT_GET(MC_ALWAYS_GIFT_DIPLO_THRESHOLD) || IsGoingForDiploVictory() || IsGoingForCultureVictory() || GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT) || IsHasActiveGoldQuest() || m_pPlayer->calculateGoldRate() > 100) // if we are very wealthy always do this { bWantsToMakeGoldGift = true; } // Otherwise, do a random roll else { int iThreshold = iDiplomacyFlavor; int iRandRoll = GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0xbe42edf0).mix(GetID())); // Threshold will be 15 for a player (3 flavor * 5) // Threshold will be 5 for non-diplomatic player (2 flavor * 5) if(iRandRoll < iThreshold) bWantsToMakeGoldGift = true; } // ************************** // Would we like to get a unit by bullying this turn? // ************************** bool bWantsToBullyUnit = false; // Would we like to get Heavy Tribute by bullying this turn? // Loop through all (known) Minors for (int iMinorLoop = MAX_MAJOR_CIVS; iMinorLoop < MAX_CIV_PLAYERS; iMinorLoop++) { PlayerTypes eMinor = (PlayerTypes) iMinorLoop; if (!GET_PLAYER(eMinor).isMinorCiv()) continue; if (GetCivApproach(eMinor) > CIV_APPROACH_HOSTILE) continue; if (MOD_BALANCE_HEAVY_TRIBUTE) { if (GET_PLAYER(eMinor).GetMinorCivAI()->CalculateBullyScore(GetID(), false) >= 50) { bWantsToBullyUnit = true; } } else { if (GetPlayer()->GetEconomicAI()->GetWorkersToCitiesRatio() < 0.25 && GetPlayer()->GetEconomicAI()->GetImprovedToImprovablePlotsRatio() < 0.50) { bWantsToBullyUnit = true; } // Otherwise, do a random roll else { int iThreshold = iTileImprovementFlavor; //antonjs: todo: XML int iRandRoll = GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0xc87e263f).mix(GetID())); if (iRandRoll < iThreshold) bWantsToBullyUnit = true; } } } // ************************** // Would we like to get some gold by bullying this turn? // ************************** bool bWantsToBullyGold = false; if(iGoldFlavor >= 6 || //antonjs: todo: GD_INT_GET(MC_ALWAYS_BULLY_GOLD_THRESHOLD) IsGoingForWorldConquest() || GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_UNIT) || GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_BUILDING) || bLosingMoney || m_pPlayer->calculateGoldRate() < 0) // if we are losing gold per turn { bWantsToBullyGold = true; } // Otherwise, do a random roll else { int iThreshold = iGoldFlavor; //antonjs: todo: XML int iRandRoll = GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x8ee31b26).mix(GetID())); if(iRandRoll < iThreshold) bWantsToBullyGold = true; } CvWeightedVector veMinorsToBuyout; // BNW Austria UA CvWeightedVector veMinorsToMarry; // VP Austria UA CvWeightedVector veMinorsToBullyAnnex; //Rome UA CvWeightedVector veMinorsToGiveGold; CvWeightedVector veMinorsToBullyGold; CvWeightedVector veMinorsToBullyUnit; int iLargeGift = /*1000*/ GD_INT_GET(MINOR_GOLD_GIFT_LARGE); int iMediumGift = /*500*/ GD_INT_GET(MINOR_GOLD_GIFT_MEDIUM); int iSmallGift = /*250*/ GD_INT_GET(MINOR_GOLD_GIFT_SMALL); PlayerTypes eID = GetID(); int iGrowthFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_GROWTH")); int iScienceFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_SCIENCE")); int iCultureFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_CULTURE")); int iFaithFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_RELIGION")); int iOffenseFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_OFFENSE")); int iHappinessFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_HAPPINESS")); int iProductionFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_PRODUCTION")) / 2; if (GetPlayer()->GetPlayerTraits()->GetBullyMilitaryStrengthModifier() != 0 || GetPlayer()->GetPlayerTraits()->GetBullyValueModifier() != 0) { if (!GetPlayer()->IsEmpireUnhappy()) { bWantsToBullyUnit = true; bWantsToBullyGold = true; bWantsToMakeGoldGift = false; } } if (GetPlayer()->IsCanBullyFriendlyCS()) { bWantsToBullyUnit = true; bWantsToBullyGold = true; bWantsToMakeGoldGift = false; } else if (GetPlayer()->GetBullyGlobalCSReduction()) { bWantsToBullyUnit = true; bWantsToBullyGold = true; bWantsToMakeGoldGift = false; } // Loop through all (known) Minors for (int iMinorLoop = MAX_MAJOR_CIVS; iMinorLoop < MAX_CIV_PLAYERS; iMinorLoop++) { PlayerTypes eMinor = (PlayerTypes) iMinorLoop; CvPlayer* pMinor = &GET_PLAYER(eMinor); CvMinorCivAI* pMinorCivAI = pMinor->GetMinorCivAI(); bool bWantsToConnect = false; bool bWantsToGiveGoldToThisMinor = false; bool bWantsToBullyUnitFromThisMinor = false; bool bWantsToBuyoutThisMinor = false; bool bWantsToMarryThisMinor = false; bool bWantsToBullyAnnexThisMinor = false; if(IsPlayerValid(eMinor)) { // Can't do anything with minors we're at war with, besides make peace (which isn't done here, but in DoMakePeaceWithMinors()) if(IsAtWar(eMinor)) continue; CivApproachTypes eApproach = GetCivApproach(eMinor); // Do we want to change our protection of this minor? DoUpdateMinorCivProtection(eMinor); // Do we want to connect to this player? int iEra = GetPlayer()->GetCurrentEra(); if (iEra <= 0) iEra = 1; if (eApproach == CIV_APPROACH_FRIENDLY && GetPlayer()->getAvgGoldRate() > min(20 * iEra,50)) { if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_NEIGHBORS) { if (pMinorCivAI->IsAllies(eID)) { bWantsToConnect = true; } else if (pMinorCivAI->IsActiveQuestForPlayer(eID, MINOR_CIV_QUEST_ROUTE)) { bWantsToConnect = true; } } } // Calculate desirability to forcefully annex this minor if (bWantsToBullyAnnex) { int iValue = 100; //antonjs: todo: xml // Only bother if we actually can annex CvCity* pMinorCapital = pMinor->getCapitalCity(); if (pMinor->GetMinorCivAI()->CanMajorBullyUnit(eID) && pMinorCapital != NULL) { // Determine presence of player cities on this continent CvArea* pMinorArea = pMinorCapital->plot()->area(); bool bPresenceInArea = false; int iMajorCapitalsInArea = 0; if (pMinorArea) { // Do we have a city here? if (pMinorArea->getCitiesPerPlayer(eID) > 0) bPresenceInArea = true; // Does another major civ have their capital here? (must be visible) for (int iMajorRivalLoop = 0; iMajorRivalLoop < MAX_MAJOR_CIVS; iMajorRivalLoop++) { PlayerTypes eMajorRivalLoop = (PlayerTypes)iMajorRivalLoop; if (eMajorRivalLoop == eID) continue; if (GET_PLAYER(eMajorRivalLoop).isAlive()) { CvCity* pCapital = GET_PLAYER(eMajorRivalLoop).getCapitalCity(); if (pCapital && pCapital->plot()) { CvPlot* pPlot = pCapital->plot(); if (pPlot->isVisible(GetTeam())) iMajorCapitalsInArea++; } } } } else { ASSERT(false, "Could not lookup minor civ's area!"); } // How many units does the city-state have? int iMinorMilitaryUnits = 0; int iMinorUnits = 0; int iLoop = 0; for (CvUnit* pLoopUnit = pMinor->firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = pMinor->nextUnit(&iLoop)) { if (pLoopUnit->IsCanAttack() && pLoopUnit->AI_getUnitAIType() != UNITAI_EXPLORE && pLoopUnit->AI_getUnitAIType() != UNITAI_EXPLORE_SEA) { iMinorMilitaryUnits++; } iMinorUnits++; } // Foreign continent if (!bPresenceInArea) { // Military foothold to attack other majors if (IsGoingForWorldConquest() && iMajorCapitalsInArea > 0) { iValue += 100; //antonjs: todo: xml } // Expansion else if (bExpandToOtherContinents) { iValue += 60; //antonjs: todo: xml } else { iValue += -50; //antonjs: todo: xml } } // Continent we have presence on else { // Proximity plays a large factor, since we don't want a remote, isolated city if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_NEIGHBORS) { iValue += 100; //antonjs: todo: xml // Military units could come to our rescue quickly if (GetStateAllWars() == STATE_ALL_WARS_LOSING) { if (iMinorMilitaryUnits > 0) //antonjs: todo: xml { iValue += (iMinorMilitaryUnits) * 10; //antonjs: todo: xml } else { iValue -= 50; //antonjs: todo: xml } } } else if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_CLOSE) { iValue += 10; //antonjs: todo: xml } else if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_FAR) { iValue += -50; //antonjs: todo: xml } else if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_DISTANT) { iValue += -100; //antonjs: todo: xml } } // Military units - How many, and can we support them? if (GetPlayer()->GetNumUnitsSupplied() >= GetPlayer()->getNumUnits() + iMinorUnits) { iValue += (iMinorMilitaryUnits) * 5; } // Happiness if (bNeedHappiness) iValue += -50; //antonjs: todo: xml if (bNeedHappinessCritical) iValue += -150; //antonjs: todo: xml // Bonuses from Annexed City-States if (GetPlayer()->GetPlayerTraits()->IsAnnexedCityStatesGiveYields()) { MinorCivTraitTypes eTrait = pMinorCivAI->GetTrait(); if (eTrait == MINOR_CIV_TRAIT_MILITARISTIC) { iValue += (iScienceFlavor + iOffenseFlavor); } else if (eTrait == MINOR_CIV_TRAIT_MERCANTILE) { iValue += iHappinessFlavor * 2; } else if (eTrait == MINOR_CIV_TRAIT_CULTURED) { iValue += iCultureFlavor * 2; } else if (eTrait == MINOR_CIV_TRAIT_RELIGIOUS) { iValue += iFaithFlavor * 2; } else if (eTrait == MINOR_CIV_TRAIT_MARITIME) { iValue += iGrowthFlavor * 2; } } // Annexing the Ally of another player? PlayerTypes eAlly = pMinorCivAI->GetAlly(); if (eAlly != NO_PLAYER) { iValue -= (GetPlayer()->GetDiplomacyAI()->GetCivOpinion(eAlly) - CIV_OPINION_NEUTRAL) * 10; } // Time to decide - Do we want it enough? if (iValue > 100) //antonjs: todo: xml { veMinorsToBullyAnnex.push_back(eMinor, iValue); bWantsToBullyAnnexThisMinor = true; } } } // Calculate desirability to buyout this minor if (bWantsToBuyout) { int iValue = 100; //antonjs: todo: xml // Only bother if we actually can buyout CvCity* pMinorCapital = pMinor->getCapitalCity(); if(GetPlayer()->IsAbleToAnnexCityStates() && pMinorCivAI->CanMajorBuyout(eID) && pMinorCapital != NULL) { // Determine presence of player cities on this continent CvArea* pMinorArea = pMinorCapital->plot()->area(); bool bPresenceInArea = false; int iMajorCapitalsInArea = 0; if(pMinorArea) { // Do we have a city here? if(pMinorArea->getCitiesPerPlayer(eID) > 0) bPresenceInArea = true; // Does another major civ have their capital here? (must be visible) for(int iMajorRivalLoop = 0; iMajorRivalLoop < MAX_MAJOR_CIVS; iMajorRivalLoop++) { PlayerTypes eMajorRivalLoop = (PlayerTypes) iMajorRivalLoop; if(eMajorRivalLoop == eID) continue; if(GET_PLAYER(eMajorRivalLoop).isAlive()) { CvCity* pCapital = GET_PLAYER(eMajorRivalLoop).getCapitalCity(); if(pCapital && pCapital->plot()) { CvPlot* pPlot = pCapital->plot(); if(pPlot->isVisible(GetTeam())) iMajorCapitalsInArea++; } } } } else { ASSERT(false, "Could not lookup minor civ's area!"); } // How many units does the city-state have? int iMinorMilitaryUnits = 0; int iMinorUnits = 0; int iLoop = 0; for (CvUnit* pLoopUnit = pMinor->firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = pMinor->nextUnit(&iLoop)) { if(pLoopUnit->IsCanAttack() && pLoopUnit->AI_getUnitAIType() != UNITAI_EXPLORE && pLoopUnit->AI_getUnitAIType() != UNITAI_EXPLORE_SEA) { iMinorMilitaryUnits++; } iMinorUnits++; } // Foreign continent if(!bPresenceInArea) { // Military foothold to attack other majors if(IsGoingForWorldConquest() && iMajorCapitalsInArea > 0) { iValue += 100; //antonjs: todo: xml } // Expansion else if(bExpandToOtherContinents) { iValue += 60; //antonjs: todo: xml } else { iValue += -50; //antonjs: todo: xml } } // Continent we have presence on else { // Proximity plays a large factor, since we don't want a remote, isolated city if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_NEIGHBORS) { iValue += 100; //antonjs: todo: xml // Military units could come to our rescue quickly if(GetStateAllWars() == STATE_ALL_WARS_LOSING) { if(iMinorMilitaryUnits > 0) //antonjs: todo: xml { iValue += (iMinorMilitaryUnits) * 10; //antonjs: todo: xml } else { iValue -= 50; //antonjs: todo: xml } } } else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_CLOSE) { iValue += 10; //antonjs: todo: xml } else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_FAR) { iValue += -50; //antonjs: todo: xml } else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_DISTANT) { iValue += -100; //antonjs: todo: xml } } // Military units - How many, and can we support them? if(GetPlayer()->GetNumUnitsSupplied() >= GetPlayer()->getNumUnits() + iMinorUnits) { iValue += (iMinorMilitaryUnits) * 5; } // Happiness if(bNeedHappiness) iValue += -50; //antonjs: todo: xml if(bNeedHappinessCritical) iValue += -150; //antonjs: todo: xml // Potential bonuses lost MinorCivTraitTypes eTrait = pMinorCivAI->GetTrait(); if(eTrait == MINOR_CIV_TRAIT_CULTURED && IsGoingForCultureVictory()) { iValue += -70; //antonjs: todo: xml } else if(eTrait == MINOR_CIV_TRAIT_MERCANTILE) { if(bNeedHappiness) iValue += -100; //antonjs: todo: xml if(bNeedHappinessCritical) iValue += -150; //antonjs: todo: xml } // Time to decide - Do we want it enough? if(iValue > 100) //antonjs: todo: xml { veMinorsToBuyout.push_back(eMinor, iValue); bWantsToBuyoutThisMinor = true; } } } if (bWantsToMarry) { // No reason not to, as long as we have the gold (checked below) if (pMinorCivAI->CanMajorDiploMarriage(eID)) { // Random weight to shuffle the list veMinorsToMarry.push_back(eMinor, GC.getGame().urandLimitExclusive(1000, CvSeeder::fromRaw(0x977c30aa).mix(GC.getGame().getGameTurn()).mix(eID).mix(eMinor))); bWantsToMarryThisMinor = true; } } // Calculate desirability to give this minor gold if (bWantsToMakeGoldGift && !bWantsToBuyoutThisMinor && !bWantsToBullyAnnexThisMinor) { if (GET_PLAYER(eMinor).GetMinorCivAI()->IsNoAlly()) { continue; } if (GET_PLAYER(eMinor).GetMinorCivAI()->GetPermanentAlly() == GetID()) { continue; } int iValue = /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_THRESHOLD); // If we're not protective, then don't bother with minor diplo if (eApproach == CIV_APPROACH_FRIENDLY) { MinorGoldGiftInfo sGiftInfo; sGiftInfo.eMinor = eMinor; sGiftInfo.eMajorRival = NO_PLAYER; sGiftInfo.bQuickBoost = false; sGiftInfo.iGoldAmount = 0; iValue += /*10*/ GD_INT_GET(MC_GIFT_WEIGHT_PROTECTIVE); // some base value // if we are rich we are more likely to, conversely if we are poor... iValue += min(max(0, m_pPlayer->calculateGoldRate() - 50),100); CvMinorCivInfo* pMinorInfo = GC.getMinorCivInfo(pMinorCivAI->GetMinorCivType()); // Diplo victory makes us more likely to spend gold if (IsGoingForDiploVictory()) iValue += /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_DIPLO_VICTORY); // double up if this is the home stretch if (GC.getGame().IsUnitedNationsActive()) { iValue += /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_DIPLO_VICTORY); } // Going for Culture victory, focus on Cultural city states else if (IsGoingForCultureVictory()) { if (pMinorInfo->GetMinorCivTrait() == MINOR_CIV_TRAIT_CULTURED) iValue += /*200*/ GD_INT_GET(MC_GIFT_WEIGHT_CULTURE_VICTORY); } // Going for Conquest victory, focus on Militaristic city states else if (IsGoingForWorldConquest()) { if (pMinorInfo->GetMinorCivTrait() == MINOR_CIV_TRAIT_MILITARISTIC) iValue += /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_CONQUEST_VICTORY); } //antonjs: todo: work extra gold quest INF potential into the friends/allies/passing logic as well // Gold gift quest is active, so we would get more bang for our bucks if (pMinorCivAI->IsActiveQuestForPlayer(eID, MINOR_CIV_QUEST_GIVE_GOLD)) { iValue += 150; //antonjs: todo: constant/XML } // Invest quest is active, so we would get more bang for our bucks if (pMinorCivAI->IsActiveQuestForPlayer(eID, MINOR_CIV_QUEST_INVEST)) { iValue += 100; //antonjs: todo: constant/XML } // having traits that give us bonuses also make us want to spend gold if (m_pPlayer->GetPlayerTraits()->GetCityStateFriendshipModifier() > 0 || m_pPlayer->GetPlayerTraits()->GetCityStateBonusModifier()) { iValue += /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_DIPLO_VICTORY); } // Nearly everyone likes to grow if (pMinorInfo->GetMinorCivTrait() == MINOR_CIV_TRAIT_MARITIME && !GetPlayer()->IsEmpireUnhappy()) { iValue += iGrowthFlavor * max(1, GetPlayer()->getNumCities() / 3) * /*20*/ GD_INT_GET(MC_GIFT_WEIGHT_MARITIME_GROWTH); } // Slight negative weight towards militaristic if (pMinorInfo->GetMinorCivTrait() == MINOR_CIV_TRAIT_MILITARISTIC && !IsGoingForWorldConquest()) iValue += /*-50*/ GD_INT_GET(MC_GIFT_WEIGHT_MILITARISTIC); // If they have a resource we don't have, add extra weight int iResourcesWeLack = pMinorCivAI->GetNumResourcesMajorLacks(eID); if (iResourcesWeLack > 0) iValue += iResourcesWeLack * /*80*/ GD_INT_GET(MC_GIFT_WEIGHT_RESOURCE_WE_NEED); // If the minor is hostile, then reduce the weighting if (pMinorCivAI->GetPersonality() == MINOR_CIV_PERSONALITY_HOSTILE) iValue += /*-20*/ GD_INT_GET(MC_GIFT_WEIGHT_HOSTILE); // The closer we are the better if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_NEIGHBORS) iValue += /*5*/ GD_INT_GET(MC_GIFT_WEIGHT_NEIGHBORS); else if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_CLOSE) iValue += /*4*/ GD_INT_GET(MC_GIFT_WEIGHT_CLOSE); else if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_FAR) iValue += /*3*/ GD_INT_GET(MC_GIFT_WEIGHT_FAR); int iMediumGiftFriendship = pMinorCivAI->GetFriendshipFromGoldGift(eID, iMediumGift); int iSmallGiftFriendship = pMinorCivAI->GetFriendshipFromGoldGift(eID, iSmallGift); int iFriendshipWithMinor = pMinorCivAI->GetEffectiveFriendshipWithMajor(eID); // Only care if we'll actually be Allies or better bool bMediumGiftAllies = iFriendshipWithMinor + iMediumGiftFriendship >= pMinorCivAI->GetAlliesThreshold(eID); bool bSmallGiftAllies = iFriendshipWithMinor + iSmallGiftFriendship >= pMinorCivAI->GetAlliesThreshold(eID); // Loop through other players to see if we can pass them for (int iOtherMajorLoop = 0; iOtherMajorLoop < MAX_MAJOR_CIVS; iOtherMajorLoop++) { PlayerTypes eOtherMajor = (PlayerTypes) iOtherMajorLoop; // Player must be alive if (!GET_PLAYER(eOtherMajor).isAlive()) continue; int iOtherPlayerFriendshipWithMinor = pMinorCivAI->GetEffectiveFriendshipWithMajor(eOtherMajor); // Player must have friendship with this major if (iOtherPlayerFriendshipWithMinor <= 0) continue; // They must have more friendship with this guy than us if (iFriendshipWithMinor <= iOtherPlayerFriendshipWithMinor) continue; // If we can pass them with a small gift, great if (bSmallGiftAllies && iOtherPlayerFriendshipWithMinor - iFriendshipWithMinor < iSmallGiftFriendship) { iValue += /*30*/ GD_INT_GET(MC_SMALL_GIFT_WEIGHT_PASS_OTHER_PLAYER); sGiftInfo.bQuickBoost = true; sGiftInfo.eMajorRival = eOtherMajor; } // If a medium gift passes them up, that's good too else if (bMediumGiftAllies && iOtherPlayerFriendshipWithMinor - iFriendshipWithMinor < iMediumGiftFriendship) { iValue += /*15*/ GD_INT_GET(MC_GIFT_WEIGHT_PASS_OTHER_PLAYER); sGiftInfo.eMajorRival = eOtherMajor; } // We're behind and we can't catch up right now, so zero-out the value else iValue = 0; } // Are we already allies? if (pMinorCivAI->IsAllies(eID)) { // Are we close to losing our status? if (pMinorCivAI->IsCloseToNotBeingAllies(eID)) { iValue += /*250*/ GD_INT_GET(MC_GIFT_WEIGHT_ALMOST_NOT_ALLIES); sGiftInfo.bQuickBoost = true; } // Not going to lose status, so not worth going after this guy else iValue = 0; } // Are we already Friends? else if (pMinorCivAI->IsFriends(eID)) { // Are we close to losing our status? if (pMinorCivAI->IsCloseToNotBeingFriends(eID)) { iValue += /*150*/ GD_INT_GET(MC_GIFT_WEIGHT_ALMOST_NOT_FRIENDS); sGiftInfo.bQuickBoost = true; } // Not going to lose status, so not worth going after this guy else if (!IsGoingForDiploVictory() || !GC.getGame().IsUnitedNationsActive()) iValue = 0; } // Did we bully you recently? If so, giving you gold now would be very odd. if (pMinorCivAI->IsRecentlyBulliedByMajor(eID)) { iValue -= 100; //antonjs: todo: constant/XML } //antonjs: consider: different behavior to CS that have been bullied by others, bullied by rival, etc. // Do we want it enough? if (iValue > /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_THRESHOLD)) { veMinorsToGiveGold.push_back(sGiftInfo, iValue); bWantsToGiveGoldToThisMinor = true; } } } // Calculate desirability to bully a unit from this minor if (bWantsToBullyUnit && !bWantsToBuyoutThisMinor && !bWantsToMarryThisMinor && !bWantsToBullyAnnexThisMinor && !bWantsToGiveGoldToThisMinor) { int iValue = 100; //antonjs: todo: XML, bully threshold if (MOD_BALANCE_HEAVY_TRIBUTE) iValue = pMinor->GetMinorCivAI()->CalculateBullyScore(eID, true); if (iValue <= 0) continue; if(eApproach == CIV_APPROACH_HOSTILE) { // Only bother if we can successfully bully if (pMinor->GetMinorCivAI()->CanMajorBullyUnit(eID)) { if (MOD_BALANCE_HEAVY_TRIBUTE) { iValue += (GET_PLAYER(eMinor).GetMinorCivAI()->GetBullyGoldAmount(GetID(), false, /*bForUnit*/ true) * iGoldFlavor) / 10; // yields from quests int iNumCities = GetPlayer()->getNumCities(); int iNumUnits = GetPlayer()->getNumMilitaryUnits(); QuestListForPlayer::iterator itr_quest; for (itr_quest = pMinorCivAI->m_QuestsGiven[eID].begin(); itr_quest != pMinorCivAI->m_QuestsGiven[eID].end(); itr_quest++) { if (itr_quest->IsObsolete(true)) // is this quest canceled by demanding heavy tribute? { // half of the quest rewards are given iValue += itr_quest->GetGold() / 2 * iGoldFlavor / 10; iValue += itr_quest->GetScience() / 2 * iScienceFlavor / 10; iValue += itr_quest->GetCulture() / 2 * iCultureFlavor / 10; iValue += itr_quest->GetFaith() / 2 * iFaithFlavor / 10; iValue += itr_quest->GetGoldenAgePoints() / 2 * iCultureFlavor / 10; iValue += itr_quest->GetFood() / 2 * iGrowthFlavor / 10; iValue += itr_quest->GetProduction() / 2 * iProductionFlavor / 10; iValue += itr_quest->GetTourism() / 2 * iCultureFlavor / 10; iValue += itr_quest->GetHappiness() / 2 * iHappinessFlavor / 10; iValue += itr_quest->GetGP() / 2 * (iGoldFlavor + iScienceFlavor + 3 * iCultureFlavor + iProductionFlavor) / 10; // we get GP for each specialist iValue += itr_quest->GetGPGlobal() / 2 * (iGoldFlavor + iScienceFlavor + 3 * iCultureFlavor + iProductionFlavor) * iNumCities / 10; iValue += itr_quest->GetGeneralPoints() / 2 * iOffenseFlavor / 10; iValue += itr_quest->GetAdmiralPoints() / 2 * iOffenseFlavor / 10; iValue += itr_quest->GetExperience() / 2 * iNumUnits * iOffenseFlavor / 10; iValue += itr_quest->GetJuggernauts() * iOffenseFlavor / 10; } } } else { // The closer we are the better, because the unit travels less distance to get home if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_NEIGHBORS) iValue += 25; else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_CLOSE) iValue += 15; else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_FAR) iValue += -15; else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_DISTANT) iValue += -25; //antonjs: consider: knock it down if is there a chance the worker will get captured by a nearby rival } // We like to keep bullying the same minor if (pMinor->GetMinorCivAI()->IsEverBulliedByMajor(eID)) { iValue += 25; } // If this minor has a PtP from someone, bullying it could have big consequences if (!GetPlayer()->GetPlayerTraits()->IgnoreBullyPenalties()) { if (pMinor->GetMinorCivAI()->IsProtectedByAnyMajor()) { iValue += -20; //antonjs: consider: scale based on which major is protecting it } else if (pMinor->GetMinorCivAI()->GetAlly() != NO_PLAYER) { iValue += -20; } else { iValue += 20; } } else { iValue += 25; } //Do we get a bonus from this? if (GetPlayer()->GetPlayerTraits()->GetBullyMilitaryStrengthModifier() != 0 || GetPlayer()->GetPlayerTraits()->GetBullyValueModifier() != 0) { iValue += 25; } for (int iI = 0; iI < NUM_YIELD_TYPES; iI++) { YieldTypes eYield = (YieldTypes)iI; if (eYield != NO_YIELD) { if (GetPlayer()->GetYieldFromMinorDemand(eYield) > 0) { iValue += 10; } } } //antonjs: consider: allies or friends with another major //antonjs: consider: distance to other majors // If we are getting a bonus, don't mess that up! if(pMinor->GetMinorCivAI()->IsAllies(eID) || pMinor->GetMinorCivAI()->IsFriends(eID)) { if (!GetPlayer()->IsCanBullyFriendlyCS()) iValue = 0; } // Do we want it enough? if(iValue > 100) //antonjs: todo: XML for threshold { veMinorsToBullyUnit.push_back(eMinor, iValue); bWantsToBullyUnitFromThisMinor = true; } } } } // Calculate desirability to bully gold from this minor if(bWantsToBullyGold && !bWantsToBuyoutThisMinor && !bWantsToMarryThisMinor && !bWantsToBullyAnnexThisMinor && !bWantsToGiveGoldToThisMinor && !bWantsToBullyUnitFromThisMinor) { int iValue = 100; //antonjs: todo: XML, bully threshold if (MOD_BALANCE_HEAVY_TRIBUTE) iValue = pMinor->GetMinorCivAI()->CalculateBullyScore(eID, false); if (iValue <= 0) continue; if(eApproach == CIV_APPROACH_HOSTILE) { // Only bother if we can successfully bully if (pMinor->GetMinorCivAI()->CanMajorBullyGold(eID, MOD_BALANCE_HEAVY_TRIBUTE ? iValue - 25 : 0)) { iValue += (GET_PLAYER(eMinor).GetMinorCivAI()->GetBullyGoldAmount(GetID()) * iGoldFlavor) / 10; // We like to keep bullying the same minor if(pMinor->GetMinorCivAI()->IsEverBulliedByMajor(eID)) { iValue += 25; } // If this minor has a PtP from someone, bullying it could have big consequences if(pMinor->GetMinorCivAI()->IsProtectedByAnyMajor()) { iValue += -20; //antonjs: consider: scale based on which major is protecting it } else { iValue += 20; } //antonjs: consider: allies or friends another major //antonjs: consider: distance to other majors //Do we get a bonus from this? if (GetPlayer()->GetPlayerTraits()->GetBullyMilitaryStrengthModifier() != 0 || GetPlayer()->GetPlayerTraits()->GetBullyValueModifier() != 0) { iValue += 25; } // If we are getting a bonus, don't mess that up! if(pMinor->GetMinorCivAI()->IsAllies(eID) || pMinor->GetMinorCivAI()->IsFriends(eID)) { if (!GetPlayer()->IsCanBullyFriendlyCS()) iValue = 0; } // Do we want it enough? if(iValue > 100) //antonjs: todo: XML for threshold { veMinorsToBullyGold.push_back(eMinor, iValue); } } } } } SetWantToRouteConnectToMinor(eMinor, bWantsToConnect); } int iGoldReserve = GetPlayer()->GetTreasury()->GetGold(); // Do we want to buyout a minor? veMinorsToBuyout.StableSortItems(); int iGoldLeft = GetPlayer()->GetTreasury()->GetGold(); for (int i = 0; i < veMinorsToBuyout.size(); i++) { PlayerTypes eLoopMinor = veMinorsToBuyout.GetElement(i); int iBuyoutCost = GET_PLAYER(eLoopMinor).GetMinorCivAI()->GetBuyoutCost(eID); if (iGoldLeft >= iBuyoutCost) { if (GET_PLAYER(eLoopMinor).GetMinorCivAI()->CanMajorBuyout(eID)) { GC.getGame().DoMinorBuyout(eID, eLoopMinor); break; // Don't buyout more than once in a single turn } else { ASSERT(false, "Chose a minor to buyout that cannot actually be bought!"); } } else { if (!GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT)) { LogMinorCivBuyout(eLoopMinor, iBuyoutCost, /*bSaving*/ true); GetPlayer()->GetEconomicAI()->StartSaveForPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT, iBuyoutCost, /*350*/ GD_INT_GET(AI_GOLD_PRIORITY_BUYOUT_CITY_STATE)); } } } // Do we want to marry a city-state? veMinorsToMarry.StableSortItems(); iGoldLeft = GetPlayer()->GetTreasury()->GetGold(); for (int i = 0; i < veMinorsToMarry.size(); i++) { PlayerTypes eLoopMinor = veMinorsToMarry.GetElement(i); int iMarriageCost = GET_PLAYER(eLoopMinor).GetMinorCivAI()->GetMarriageCost(eID); if (iGoldLeft >= iMarriageCost) { if (GET_PLAYER(eLoopMinor).GetMinorCivAI()->CanMajorDiploMarriage(eID)) { GET_PLAYER(eLoopMinor).GetMinorCivAI()->DoBuyout(eID); iMarriageCost = GET_PLAYER(eLoopMinor).GetMinorCivAI()->GetMarriageCost(eID); iGoldLeft -= iMarriageCost; } else { ASSERT(false, "Chose a city-state to marry that cannot actually be married!"); } } else { if (!GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT)) { LogMinorCivBuyout(eLoopMinor, iMarriageCost, /*bSaving*/ true); GetPlayer()->GetEconomicAI()->StartSaveForPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT, iMarriageCost, /*350*/ GD_INT_GET(AI_GOLD_PRIORITY_BUYOUT_CITY_STATE)); } } } // Do we want to annex a minor? veMinorsToBullyAnnex.StableSortItems(); for (int i = 0; i < veMinorsToBullyAnnex.size(); i++) { PlayerTypes eLoopMinor = veMinorsToBullyAnnex.GetElement(i); PRECONDITION(eLoopMinor != NO_PLAYER, "Trying to bully-annex NO_PLAYER!"); if (GET_PLAYER(eLoopMinor).GetMinorCivAI()->CanMajorBullyUnit(eID)) { GC.getGame().DoMinorBullyAnnex(eID, eLoopMinor); break; // Don't annex more than one city-state in a single turn } else { ASSERT(false, "Chose a minor to bully-annex that cannot actually be bullied!"); } } // Do we want to give someone Gold enough to actually do it? veMinorsToGiveGold.StableSortItems(); // Sort from highest desirability to lowest for (int i = 0; i < veMinorsToGiveGold.size(); i++) { int iGoldLeft = GetPlayer()->GetTreasury()->GetGold(); MinorGoldGiftInfo sGift = veMinorsToGiveGold.GetElement(i); //Interception! Let's do a tile improvement if we can (and we'll benefit from it) if (sGift.eMinor != NO_PLAYER && GET_PLAYER(sGift.eMinor).GetMinorCivAI()->IsAllies(GetID())) { CvPlot* pImprovementPlot = GET_PLAYER(sGift.eMinor).GetMinorCivAI()->GetMajorGiftTileImprovement(GetID()); if (pImprovementPlot != NULL) { GET_PLAYER(sGift.eMinor).GetMinorCivAI()->DoTileImprovementGiftFromMajor(GetID(), pImprovementPlot->getX(), pImprovementPlot->getY()); LogMinorCivGiftTile(sGift.eMinor); } } //Default is 1 - this will prevent the AI from trying to spam gold gifts of zero gold. if (GD_INT_GET(CSD_GOLD_GIFT_DISABLED) > 0) { continue; } sGift.iGoldAmount = 0; if(iGoldLeft >= iSmallGift && sGift.bQuickBoost) sGift.iGoldAmount = iSmallGift; else if(iGoldLeft >= iLargeGift) sGift.iGoldAmount = iLargeGift; else if(iGoldLeft >= iMediumGift) sGift.iGoldAmount = iMediumGift; int iOldFriendship = GET_PLAYER(sGift.eMinor).GetMinorCivAI()->GetEffectiveFriendshipWithMajor(eID); // Able to give a gift? Don't gift more than half of the gold we have in one turn if(sGift.iGoldAmount > 0 && iGoldLeft >= (iGoldReserve / 2)) { GET_PLAYER(sGift.eMinor).GetMinorCivAI()->DoGoldGiftFromMajor(GetID(), sGift.iGoldAmount); //antonjs: todo: go through CvGame instead? LogMinorCivGiftGold(sGift.eMinor, iOldFriendship, sGift.iGoldAmount, /*bSaving*/ false, sGift.bQuickBoost, sGift.eMajorRival); if(GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT)) GetPlayer()->GetEconomicAI()->CancelSaveForPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT); } // Can't afford gift yet, so start saving else { if(!GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT)) { int iAmountToSaveFor = iMediumGift; if(sGift.bQuickBoost) iAmountToSaveFor = iSmallGift; LogMinorCivGiftGold(sGift.eMinor, iOldFriendship, iAmountToSaveFor, /*bSaving*/ true, sGift.bQuickBoost, sGift.eMajorRival); int iPriority = /*150*/ GD_INT_GET(AI_GOLD_PRIORITY_DIPLOMACY_BASE); iPriority += iDiplomacyFlavor * /*25*/ GD_INT_GET(AI_GOLD_PRIORITY_DIPLOMACY_PER_FLAVOR_POINT); GetPlayer()->GetEconomicAI()->StartSaveForPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT, iAmountToSaveFor, iPriority); } } } // Do we want a unit enough to bully someone? veMinorsToBullyUnit.StableSortItems(); for (int i = 0; i < veMinorsToBullyUnit.size(); i++) { PlayerTypes eLoopMinor = veMinorsToBullyUnit.GetElement(i); PRECONDITION(eLoopMinor != NO_PLAYER, "Trying to bully a unit from NO_PLAYER!"); if (GET_PLAYER(eLoopMinor).GetMinorCivAI()->CanMajorBullyUnit(eID)) { GC.getGame().DoMinorBullyUnit(eID, eLoopMinor); break; // Don't bully a unit more than once in a single turn } else { ASSERT(false, "Chose a minor to bully unit from that cannot actually be bullied!"); } } // Do we want gold enough to bully someone? veMinorsToBullyGold.StableSortItems(); for (int i = 0; i < veMinorsToBullyGold.size(); i++) { PlayerTypes eLoopMinor = veMinorsToBullyGold.GetElement(i); PRECONDITION(eLoopMinor != NO_PLAYER, "Trying to bully gold from NO_PLAYER!"); if (GET_PLAYER(eLoopMinor).GetMinorCivAI()->CanMajorBullyGold(eID)) { GC.getGame().DoMinorBullyGold(eID, eLoopMinor); } else { ASSERT(false, "Chose a minor to bully gold from that cannot actually be bullied!"); } } } void CvDiplomacyAI::DoUpdateMinorCivProtection(PlayerTypes eMinor) { if (GetCivApproach(eMinor) == CIV_APPROACH_FRIENDLY || GET_PLAYER(eMinor).GetMinorCivAI()->GetAlly() == GetID()) { // We are protective, so do a PtP if we are able to and haven't already if (GET_PLAYER(eMinor).GetMinorCivAI()->CanMajorStartProtection(GetID())) { GC.getGame().DoMinorPledgeProtection(GetID(), eMinor, true); } } // Don't cancel a pledge in VP unless the City-State has no capital (we won't get the Influence boost from quests) or they've taken damage (so we'll lose Influence faster) // Pledges will be automatically cancelled if AI decides to bully or war the City-State, so there's no benefit to cancelling now - we might change our mind else if (GD_INT_GET(BALANCE_INFLUENCE_BOOST_PROTECTION_MINOR) <= 0 || GET_PLAYER(eMinor).getCapitalCity() == NULL || (GET_PLAYER(eMinor).getCapitalCity()->getDamage() > 0 && GD_INT_GET(MINOR_FRIENDSHIP_DROP_PER_TURN_DAMAGED_CAPITAL_MULTIPLIER) > 100)) { // We are not protective, so revoke PtP if we can if (GET_PLAYER(eMinor).GetMinorCivAI()->IsProtectedByMajor(GetID()) && GET_PLAYER(eMinor).GetMinorCivAI()->CanMajorWithdrawProtection(GetID())) { GC.getGame().DoMinorPledgeProtection(GetID(), eMinor, false); } } } /* /// Possible Contact Statement - Notify human it's time for a coop war they agreed to void CvDiplomacyAI::DoCoopWarTimeStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if(eStatement == NO_DIPLO_STATEMENT_TYPE) { // Don't send this to AI players - coop war timer is automatically handled in DoCounters() if(!GET_PLAYER(ePlayer).isHuman()) return; CvTeam* pTeam = &GET_TEAM(GET_PLAYER(ePlayer).getTeam()); PlayerTypes eTargetPlayer; TeamTypes eTargetTeam; for(int iTargetLoop = 0; iTargetLoop < MAX_MAJOR_CIVS; iTargetLoop++) { eTargetPlayer = (PlayerTypes) iTargetLoop; bool bInvalid = false; if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GET_PLAYER(eTargetPlayer).getTeam(), ePlayer)) { bInvalid = true; } if (!IsPlayerValid(eTargetPlayer)) { bInvalid = true; } if (bInvalid) { if (GetCoopWarAcceptedState(ePlayer, eTargetPlayer) == COOP_WAR_STATE_SOON) { SetCoopWarAcceptedState(ePlayer, eTargetPlayer, NO_COOP_WAR_STATE); SetCoopWarCounter(ePlayer, eTargetPlayer, -666); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetCoopWarAcceptedState(GetID(), eTargetPlayer, NO_COOP_WAR_STATE); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetCoopWarCounter(GetID(), eTargetPlayer, -666); } continue; } // Agreed to go to war soon... what's the counter at? if(GetCoopWarAcceptedState(ePlayer, eTargetPlayer) == COOP_WAR_STATE_SOON) { if(GetCoopWarCounter(ePlayer, eTargetPlayer) == GD_INT_GET(COOP_WAR_SOON_COUNTER)) { eTargetTeam = GET_PLAYER(eTargetPlayer).getTeam(); // If they're already at war, don't bother if(!pTeam->isAtWar(eTargetTeam) && GET_PLAYER(eTargetPlayer).isAlive()) { eStatement = DIPLO_STATEMENT_COOP_WAR_TIME; iData1 = eTargetPlayer; // Don't evaluate other players break; } // Human is already at war - process what we would have if he'd agreed at this point else { SetCoopWarAcceptedState(ePlayer, eTargetPlayer, COOP_WAR_STATE_ACCEPTED); // AI declaration if(!IsAtWar(eTargetPlayer) && GET_PLAYER(eTargetPlayer).isAlive()) { if (DeclareWar(eTargetPlayer)) { GetPlayer()->GetMilitaryAI()->RequestBasicAttack(eTargetPlayer, 1); } } } } } } } } */ /// Possible Contact Statement - Coop War Request void CvDiplomacyAI::DoCoopWarStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_COOP_WAR_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // Don't start a war if our empire is in bad shape for it if (GetPlayer()->IsNoNewWars()) return; if (IsAvoidDeals()) return; if (eStatement == NO_DIPLO_STATEMENT_TYPE) { PlayerTypes eTargetPlayer; if (DoTestCoopWarDesire(ePlayer, /*passed by address*/ eTargetPlayer)) { if (eTargetPlayer != NO_PLAYER) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_COOP_WAR_REQUEST; int iTurnsBetweenStatements = 10; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { eStatement = eTempStatement; iData1 = eTargetPlayer; } } } } } /// Possible Contact Statement - Demand void CvDiplomacyAI::DoMakeDemand(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); //End the gift exchange at the start of each round. GetPlayer()->GetDiplomacyAI()->SetOfferingGift(ePlayer, false); GetPlayer()->GetDiplomacyAI()->SetOfferedGift(ePlayer, false); // We can use this deal pointer to form a trade offer CvDeal* pDeal = GC.getGame().GetGameDeals().GetTempDeal(); // Clear this data out before any deals are offered. pDeal->ClearItems(); pDeal->SetRequestingPlayer(NO_PLAYER); pDeal->SetDuration(GC.getGame().getGameSpeedInfo().GetDealDuration()); pDeal->SetFromPlayer(GetID()); pDeal->SetToPlayer(ePlayer); SetCantMatchDeal(ePlayer, false); //set up the deal if (GetPlayer()->GetDealAI()->IsMakeDemand(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { int iTurnsBetweenStatements = 10; if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_DEMAND) >= iTurnsBetweenStatements) { DiploStatementTypes eStatement = DIPLO_STATEMENT_DEMAND; DoSendStatementToPlayer(ePlayer, eStatement, -1, pDeal); LogStatementToPlayer(ePlayer, eStatement); SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_DEMAND, GC.getGame().getGameTurn()); } } // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at pDeal->ClearItems(); } /// Possible Contact Statement - guy has his military positioned aggressively near us void CvDiplomacyAI::DoAggressiveMilitaryStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { if (eStatement == NO_DIPLO_STATEMENT_TYPE) { // Don't bother if they've already made or broken a military promise to us if (GetMilitaryPromiseState(ePlayer) > NO_PROMISE_STATE) return; // We must be able to declare war on each other - in both directions, since the promise is mutual TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam(); if (!GET_TEAM(eTeam).canDeclareWar(GetTeam(), ePlayer) || !GET_TEAM(GetTeam()).canDeclareWar(eTeam, GetID())) return; // They're HIGH or INCREDIBLE this turn if (GetMilitaryAggressivePosture(ePlayer) < AGGRESSIVE_POSTURE_HIGH) return; // AI teammates of humans can't send this, otherwise the humans on our team might get a backstabbing penalty for something they weren't aware of if (GetPlayer()->IsAITeammateOfHuman()) return; // Don't send this to AI teammates of humans, otherwise the humans on their team might get a backstabbing penalty for something they weren't aware of if (GET_PLAYER(ePlayer).IsAITeammateOfHuman()) return; // This promise is mutual, so don't send the statement if we (or our teammates) are planning war if (AvoidExchangesWithPlayer(ePlayer, /*bWarOnly*/ true)) return; // Check other player status for (int iThirdPartyLoop = 0; iThirdPartyLoop < MAX_MAJOR_CIVS; iThirdPartyLoop++) { PlayerTypes eThirdParty = (PlayerTypes) iThirdPartyLoop; if (eThirdParty == GetID() || eThirdParty == ePlayer) continue; // Are they at war with anyone we're neighbors with? if (GetPlayer()->GetProximityToPlayer(eThirdParty) == PLAYER_PROXIMITY_NEIGHBORS && GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isAtWar(GET_PLAYER(eThirdParty).getTeam())) return; // Are they an AI preparing for a coop war against us with a human? Don't send this statement, because being dragged into a war early is unfun for humans if (!GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(eThirdParty).isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(eThirdParty).GetDiplomacyAI()->GetCoopWarState(ePlayer, GetID()) == COOP_WAR_STATE_PREPARING) return; } DiploStatementTypes eTempStatement = DIPLO_STATEMENT_AGGRESSIVE_MILITARY_WARNING; int iTurnsBetweenStatements = 20; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) eStatement = eTempStatement; } } /// Possible Contact Statement - Killed a City-State we're protective towards void CvDiplomacyAI::DoKilledCityStateStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if(eStatement == NO_DIPLO_STATEMENT_TYPE) { // They must be able to declare war on us if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam(), ePlayer)) return; if(MadeAttackCityStatePromise(ePlayer)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_KILLED_PROTECTED_CITY_STATE; int iTurnsBetweenStatements = 9999; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { PlayerTypes eMinorCiv = GetOtherPlayerProtectedMinorKilled(ePlayer); if(eMinorCiv != NO_PLAYER) { PRECONDITION(eMinorCiv >= MAX_MAJOR_CIVS); PRECONDITION(eMinorCiv < MAX_CIV_PLAYERS); eStatement = eTempStatement; iData1 = eMinorCiv; } } } } } /// Possible Contact Statement - Attacked a City-State we're protective towards void CvDiplomacyAI::DoAttackedCityStateStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if(eStatement == NO_DIPLO_STATEMENT_TYPE) { // They must be able to declare war on us if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam(), ePlayer)) return; // Don't send this to AI teammates of humans, otherwise the humans on their team might get a backstabbing penalty for something they weren't aware of if (GET_PLAYER(ePlayer).IsAITeammateOfHuman()) return; if (GetOtherPlayerAttackedProtectedMinorTurn(ePlayer) == GC.getGame().getGameTurn()) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_ATTACKED_PROTECTED_CITY_STATE; int iTurnsBetweenStatements = 1; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { PlayerTypes eMinorCiv = GetOtherPlayerProtectedMinorAttacked(ePlayer); if(eMinorCiv != NO_PLAYER) { PRECONDITION(eMinorCiv >= MAX_MAJOR_CIVS); PRECONDITION(eMinorCiv < MAX_CIV_PLAYERS); // Minor civ must still be alive! if(GET_PLAYER(eMinorCiv).isAlive()) { eStatement = eTempStatement; iData1 = eMinorCiv; } } } } } } /// Possible Contact Statement - Bullied a City-State we're protective towards void CvDiplomacyAI::DoBulliedCityStateStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index. "); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index. "); if(eStatement == NO_DIPLO_STATEMENT_TYPE) { // They must be able to declare war on us if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam(), ePlayer)) return; // Bullied a City State we're protective towards if (GetOtherPlayerBulliedProtectedMinorTurn(ePlayer) == GC.getGame().getGameTurn()) { // Have we asked you to make a promise before? if(BrokeBullyCityStatePromise(ePlayer) || IgnoredBullyCityStatePromise(ePlayer)) { // We don't even want to bother with you again, so do nothing if (GetCivApproach(ePlayer) <= CIV_APPROACH_HOSTILE) { const char* strText = NULL; bool bActivePlayer = GC.getGame().getActivePlayer() == ePlayer; if (DeclareWar(ePlayer)) { GetPlayer()->GetMilitaryAI()->RequestCityAttack(ePlayer,1); } if(bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_WARMONGER); gDLL->GameplayDiplomacyAILeaderMessage(GetID(), DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } } // Otherwise, ask you to make a promise else if (GetBullyCityStatePromiseState(ePlayer) == NO_PROMISE_STATE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_BULLIED_PROTECTED_CITY_STATE; int iTurnsBetweenStatements = 1; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { PlayerTypes eMinorCiv = GetOtherPlayerProtectedMinorBullied(ePlayer); if(eMinorCiv != NO_PLAYER) { PRECONDITION(eMinorCiv >= MAX_MAJOR_CIVS); PRECONDITION(eMinorCiv < MAX_CIV_PLAYERS); // Minor civ must still be alive! if(GET_PLAYER(eMinorCiv).isAlive()) { eStatement = eTempStatement; iData1 = eMinorCiv; } } } } } } } /// Possible Contact Statement - Comment on aggressive expansion by this player void CvDiplomacyAI::DoExpansionWarningStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { if (eStatement == NO_DIPLO_STATEMENT_TYPE && IsAngryAboutExpansion(ePlayer)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_EXPANSION_WARNING; int iTurnsBetweenStatements = (/*50*/ GD_INT_GET(EXPANSION_PROMISE_TURNS_EFFECTIVE) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) eStatement = eTempStatement; } } /// Possible Contact Statement - Tell the player he broke his expansion promise void CvDiplomacyAI::DoExpansionBrokenPromiseStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { if (eStatement == NO_DIPLO_STATEMENT_TYPE && BrokeExpansionPromise(ePlayer)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_EXPANSION_BROKEN_PROMISE; int iTurnsBetweenStatements = (/*50*/ GD_INT_GET(EXPANSION_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) eStatement = eTempStatement; } } /// Possible Contact Statement - Comment on aggressive Plot Buying by this player void CvDiplomacyAI::DoPlotBuyingWarningStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { if(eStatement == NO_DIPLO_STATEMENT_TYPE) { bool bSendStatement = false; if (!EverMadeBorderPromise(ePlayer) && GetBorderPromiseState(ePlayer) == NO_PROMISE_STATE) { if(GetLandDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) { // We've spotten them buying up Plots if(GetPlotBuyingAggressivePosture(ePlayer) >= AGGRESSIVE_POSTURE_LOW) bSendStatement = true; } } if(bSendStatement) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_PLOT_BUYING_WARNING; int iTurnsBetweenStatements = (/*50*/ GD_INT_GET(BORDER_PROMISE_TURNS_EFFECTIVE) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) eStatement = eTempStatement; } } } /// Possible Contact Statement - Tell the player he broke his Plot Buying promise void CvDiplomacyAI::DoPlotBuyingBrokenPromiseStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if(eStatement == NO_DIPLO_STATEMENT_TYPE) { if(BrokeBorderPromise(ePlayer)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_PLOT_BUYING_BROKEN_PROMISE; int iTurnsBetweenStatements = 9999; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) eStatement = eTempStatement; } } } /// Possible Contact Statement - We attacked a minor that is protected by someone void CvDiplomacyAI::DoWeAttackedYourMinorStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (eStatement == NO_DIPLO_STATEMENT_TYPE) { // They must be able to declare war on us if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam(), ePlayer)) return; for (int iMinorCivLoop = MAX_MAJOR_CIVS; iMinorCivLoop < MAX_CIV_PLAYERS; iMinorCivLoop++) { PlayerTypes eMinor = (PlayerTypes) iMinorCivLoop; if (IsAtWar(eMinor) && GET_PLAYER(eMinor).isAlive() && GET_PLAYER(eMinor).GetMinorCivAI()->IsProtectedByMajor(ePlayer)) { // Did we declare war on them recently? int iTurn = GET_PLAYER(eMinor).GetMinorCivAI()->GetTurnLastAttacked(GetTeam()); if (iTurn < 0) return; int iTurnDifference = GC.getGame().getGameTurn() - iTurn; if (iTurnDifference < 10) { // Has this message not yet been sent during this war? if (!HasSentAttackProtectedMinorTaunt(ePlayer, eMinor)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_WE_ATTACKED_YOUR_MINOR; int iTurnsBetweenStatements = 1; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { ASSERT(eMinor >= MAX_MAJOR_CIVS); PRECONDITION(eMinor < MAX_CIV_PLAYERS); eStatement = eTempStatement; iData1 = eMinor; } } } } } } } /// Possible Contact Statement - We bullied a minor that is protected by someone void CvDiplomacyAI::DoWeBulliedYourMinorStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index. "); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index. "); if(eStatement == NO_DIPLO_STATEMENT_TYPE) { // They must be able to declare war on us if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam(), ePlayer)) return; PlayerTypes eMinor; for(int iMinorCivLoop = MAX_MAJOR_CIVS; iMinorCivLoop < MAX_CIV_PLAYERS; iMinorCivLoop++) { eMinor = (PlayerTypes) iMinorCivLoop; // Minor must be alive if(!GET_PLAYER(eMinor).isAlive()) continue; // Did we bully this minor last turn? if(GET_PLAYER(eMinor).GetMinorCivAI()->IsEverBulliedByMajor(GetID())) { if(GET_PLAYER(eMinor).GetMinorCivAI()->GetTurnLastBulliedByMajor(GetID()) == (GC.getGame().getGameTurn() - 1)) { // Is this minor protected by this player? if(GET_PLAYER(eMinor).GetMinorCivAI()->IsProtectedByMajor(ePlayer)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_WE_BULLIED_YOUR_MINOR; int iTurnsBetweenStatements = 1; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { ASSERT(eMinor >= MAX_MAJOR_CIVS); PRECONDITION(eMinor < MAX_CIV_PLAYERS); eStatement = eTempStatement; iData1 = eMinor; } } } } } } } /// Possible Contact Statement - We caught this player spying on us void CvDiplomacyAI::DoCaughtYourSpyStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (eStatement == NO_DIPLO_STATEMENT_TYPE) { if (m_pPlayer->GetEspionageAI()->m_aiTurnLastSpyCaught[ePlayer] == GC.getGame().getGameTurn()) { // Ask you to make a promise if you haven't before if (GetSpyPromiseState(ePlayer) == NO_PROMISE_STATE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_CAUGHT_YOUR_SPY; int iTurnsBetweenStatements = 40; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { eStatement = eTempStatement; } } } } } /// Possible Contact Statement - We killed this player's spy void CvDiplomacyAI::DoKilledYourSpyStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if(eStatement == NO_DIPLO_STATEMENT_TYPE) { if(m_pPlayer->GetEspionageAI()->m_aiNumSpiesKilled[ePlayer] > 0) { if(m_pPlayer->GetEspionageAI()->m_aiTurnLastSpyKilled[ePlayer] == GC.getGame().getGameTurn()) { // Ask you to make a promise if you haven't before if (GetSpyPromiseState(ePlayer) == NO_PROMISE_STATE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_KILLED_YOUR_SPY; int iTurnsBetweenStatements = 40; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { eStatement = eTempStatement; } } } } } } /// Possible Contact Statement - This player killed our spy void CvDiplomacyAI::DoKilledMySpyStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { if (eStatement == NO_DIPLO_STATEMENT_TYPE) { if (m_pPlayer->GetEspionageAI()->m_aiTurnLastSpyDied[ePlayer] == GC.getGame().getGameTurn() - 1) { // Don't send the message if they ignored our request before, or we ignored theirs. if (GetSpyPromiseState(ePlayer) < PROMISE_STATE_IGNORED && GET_PLAYER(ePlayer).GetDiplomacyAI()->GetSpyPromiseState(GetID()) < PROMISE_STATE_IGNORED) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_KILLED_MY_SPY; int iTurnsBetweenStatements = 40; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { eStatement = eTempStatement; } } } } } /// Possible Contact Statement - Share intrigue with this player void CvDiplomacyAI::DoShareIntrigueStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if(eStatement == NO_DIPLO_STATEMENT_TYPE) { IntrigueNotificationMessage* pNotificationMessage = m_pPlayer->GetEspionage()->GetRecentIntrigueInfo(ePlayer); if (pNotificationMessage) { // if this player has an untold plot against a player if(pNotificationMessage->m_eSourcePlayer != NO_PLAYER) { bool bIsNewIntrigue = true; // has any other player told the player about this plot? for(uint ui = 0; ui < MAX_MAJOR_CIVS; ui++) { PlayerTypes eOtherPlayer = (PlayerTypes)ui; // don't evaluate the plotting player if(eOtherPlayer == pNotificationMessage->m_eSourcePlayer) { continue; } if(GET_PLAYER(eOtherPlayer).GetEspionage()->HasSharedIntrigue(ePlayer, pNotificationMessage->m_eSourcePlayer, pNotificationMessage->m_eDiplomacyPlayer, (CvIntrigueType)(pNotificationMessage->m_iIntrigueType))) { bIsNewIntrigue = false; break; } } if(bIsNewIntrigue) { CivApproachTypes eApproachTowardsTarget = GetCivApproach(ePlayer); CivOpinionTypes eOpinionOfTarget = GetCivOpinion(ePlayer); CivOpinionTypes eOpinionOfPlotter = GetCivOpinion(pNotificationMessage->m_eSourcePlayer); if (eApproachTowardsTarget > CIV_APPROACH_GUARDED || (eApproachTowardsTarget > CIV_APPROACH_HOSTILE && (eOpinionOfTarget > eOpinionOfPlotter))) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_SHARE_INTRIGUE; int iTurnsBetweenStatements = 1; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { eStatement = eTempStatement; } } } else { // mark this as shared so it doesn't try to interrupt the player m_pPlayer->GetEspionage()->MarkRecentIntrigueAsShared(ePlayer, pNotificationMessage->m_eSourcePlayer, pNotificationMessage->m_eDiplomacyPlayer, (CvIntrigueType)(pNotificationMessage->m_iIntrigueType)); } } } } } /// Possible Contact Statement - They converted one of our cities, and we want them to stop that void CvDiplomacyAI::DoConvertedMyCityStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (eStatement == NO_DIPLO_STATEMENT_TYPE) { if (HasEverConvertedCity(ePlayer) && GetNoConvertPromiseState(ePlayer) == NO_PROMISE_STATE && !GET_PLAYER(ePlayer).GetDiplomacyAI()->IsPlayerAskedNotToConvert(GetID())) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_STOP_CONVERSIONS; int iTurnsBetweenStatements = 50; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { eStatement = eTempStatement; } } } } /// Possible Contact Statement - They dug up one of our artifacts, and we want them to stop that void CvDiplomacyAI::DoDugUpMyYardStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (eStatement == NO_DIPLO_STATEMENT_TYPE) { if (GetNumArtifactsEverDugUp(ePlayer) > 0) // TODO: arch { // Have we asked you to make a promise before? if (GetNoDiggingPromiseState(ePlayer) == NO_PROMISE_STATE && !GET_PLAYER(ePlayer).GetDiplomacyAI()->IsPlayerAskedNotToDig(GetID())) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_STOP_DIGGING; int iTurnsBetweenStatements = 30; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { eStatement = eTempStatement; } } } } } /// Possible Contact Statement - We want to make a Declaration of Friendship with them void CvDiplomacyAI::DoDoFStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_FRIENDSHIP_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if (eStatement == NO_DIPLO_STATEMENT_TYPE) { // Have we already made the agreement? if (!IsDoFAccepted(ePlayer) && !HasEndedFriendshipThisTurn()) { // Do we actually want a DoF with ePlayer? if (IsWantsDoFWithPlayer(ePlayer)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_WORK_WITH_US; if (GetDoFType(ePlayer) == DOF_TYPE_BATTLE_BROTHERS) { eTempStatement = DIPLO_STATEMENT_DOF_BB; } else if (GetDoFType(ePlayer) == DOF_TYPE_ALLIES) { eTempStatement = DIPLO_STATEMENT_DOF_ALLIES; } else if (GetDoFType(ePlayer) == DOF_TYPE_FRIENDS) { eTempStatement = DIPLO_STATEMENT_DOF_FRIENDS; } else if (GetDoFType(ePlayer) == DOF_TYPE_UNTRUSTWORTHY) { eTempStatement = DIPLO_STATEMENT_DOF_UNTRUSTWORTHY; } if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 30) { eStatement = eTempStatement; } } } } } /// Possible Contact Statement - We're denouncing one of our friends (backstabbing) void CvDiplomacyAI::DoDenounceFriendStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (eStatement == NO_DIPLO_STATEMENT_TYPE) { // Must have already made the agreement if (IsDoFAccepted(ePlayer)) { // Done working with this guy, and willing to denounce? if (IsWantsToEndDoFWithPlayer(ePlayer) && IsDenounceFriendAcceptable(ePlayer)) { eStatement = DIPLO_STATEMENT_DENOUNCE_FRIEND; } } } } /// Possible Contact Statement - We're ending our Declaration of Friendship with them void CvDiplomacyAI::DoEndDoFStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) const { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (eStatement == NO_DIPLO_STATEMENT_TYPE) { // Must have already made the agreement if (IsDoFAccepted(ePlayer)) { // Done working with this guy, but not willing to denounce? if (IsWantsToEndDoFWithPlayer(ePlayer)) { eStatement = DIPLO_STATEMENT_END_WORK_WITH_US; } } } } /// Possible Contact Statement - We're denouncing a player void CvDiplomacyAI::DoDenounceStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if(eStatement == NO_DIPLO_STATEMENT_TYPE) { if(IsDenounceAcceptable(ePlayer, /*bBias*/ false)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_DENOUNCE; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= GC.getGame().GetDealDuration() && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_DENOUNCE_RANDFAILED) >= 10) { bool bSendStatement = true; // 1 in 2 chance we don't actually send the message (don't want full predictability) //if (50 < GC.getGame().getJonRandNum(100, "Diplomacy AI: rand roll to see if we ask to work with a player")) // bSendStatement = false; if(bSendStatement) { eStatement = eTempStatement; } // Add this statement to the log so we don't evaluate it again until time has passed else SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_DENOUNCE_RANDFAILED, GC.getGame().getGameTurn()); } } } } /// Possible Contact Statement - We're requesting that a player denounce someone void CvDiplomacyAI::DoRequestFriendDenounceStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if(eStatement == NO_DIPLO_STATEMENT_TYPE) { bool bRandFailed = false; PlayerTypes eTarget = GetRequestFriendToDenounce(ePlayer, bRandFailed); if(eTarget != NO_PLAYER) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_REQUEST_FRIEND_DENOUNCE; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50 && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_REQUEST_FRIEND_DENOUNCE_RANDFAILED) >= 10) { if(!bRandFailed) { eStatement = eTempStatement; iData1 = eTarget; } // Add this statement to the log so we don't evaluate it again until time has passed else { SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_DENOUNCE_RANDFAILED, GC.getGame().getGameTurn()); } } } } } /// Possible Contact Statement - Luxury Trade void CvDiplomacyAI::DoLuxuryTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (IsAvoidDeals()) return; if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_LUXURY_TRADE; int iTurnsBetweenStatements = 20; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { if (GetPlayer()->GetDealAI()->IsMakeOfferForLuxuryResource(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { eStatement = eTempStatement; } else { pDeal->ClearItems(); } } } } /// Possibile Contact Statement - Embassy Exchange void CvDiplomacyAI::DoEmbassyExchange(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (IsAvoidDeals()) return; if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if (GetPlayer()->IsAITeammateOfHuman()) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { // Can both sides open an embassy if(pDeal->IsPossibleToTradeItem(GetID(), ePlayer, TRADE_ITEM_ALLOW_EMBASSY) && pDeal->IsPossibleToTradeItem(ePlayer, GetID(), TRADE_ITEM_ALLOW_EMBASSY)) { // Does this guy want to exchange embassies? if(IsEmbassyExchangeAcceptable(ePlayer)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_EMBASSY_EXCHANGE; int iTurnsBetweenStatements = 20; if ((GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_EMBASSY_OFFER) >= 20) { bool bSendStatement = false; // AI if(!GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) { if(GET_PLAYER(ePlayer).GetDiplomacyAI()->IsEmbassyExchangeAcceptable(GetID())) bSendStatement = true; } // Human else bSendStatement = true; if(bSendStatement) { pDeal->AddAllowEmbassy(GetID()); pDeal->AddAllowEmbassy(ePlayer); bool bUselessReferenceVariable = false; bool bCantMatchOffer = false; bool bDealAcceptable = GetPlayer()->GetDealAI()->DoEqualizeDeal(pDeal, ePlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work if (bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0) { eStatement = eTempStatement; } else { bSendStatement = false; } } if(!bSendStatement) { SetTurnStatementLastSent(ePlayer, eTempStatement, GC.getGame().getGameTurn()); pDeal->ClearItems(); } } } } } } /// Possible Contact Statement - Embassy void CvDiplomacyAI::DoEmbassyOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (IsAvoidDeals()) return; if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if (GetPlayer()->IsAITeammateOfHuman()) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { if(GetPlayer()->GetDealAI()->IsMakeOfferForEmbassy(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_EMBASSY_OFFER; int iTurnsBetweenStatements = 15; if ((GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_EMBASSY_EXCHANGE) >= 10) { eStatement = eTempStatement; } else { pDeal->ClearItems(); } } else { // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at pDeal->ClearItems(); } } } /// Possible Contact Statement - Open Borders Exchange void CvDiplomacyAI::DoOpenBordersExchange(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (IsAvoidDeals()) return; if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if (GetPlayer()->IsAITeammateOfHuman()) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { int iDuration = GC.getGame().GetDealDuration(); // Can both sides trade OB? if(pDeal->IsPossibleToTradeItem(GetID(), ePlayer, TRADE_ITEM_OPEN_BORDERS, iDuration) && pDeal->IsPossibleToTradeItem(ePlayer, GetID(), TRADE_ITEM_OPEN_BORDERS, iDuration)) { // Does this guy want to exchange OB? if(IsOpenBordersExchangeAcceptable(ePlayer)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_OPEN_BORDERS_EXCHANGE; int iTurnsBetweenStatements = 25; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { // OB on each side pDeal->AddOpenBorders(GetID(), iDuration); pDeal->AddOpenBorders(ePlayer, iDuration); bool bUselessReferenceVariable = false; bool bCantMatchOffer = false; bool bDealAcceptable = GetPlayer()->GetDealAI()->DoEqualizeDeal(pDeal, ePlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work if (bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0) { eStatement = eTempStatement; } // Add this statement to the log so we don't evaluate it again until 20 turns has come back around else { SetTurnStatementLastSent(ePlayer, eTempStatement, GC.getGame().getGameTurn()); pDeal->ClearItems(); } } } } } } /// Possible Contact Statement - Open Borders void CvDiplomacyAI::DoOpenBordersOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (IsAvoidDeals()) return; if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if (GetPlayer()->IsAITeammateOfHuman()) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_OPEN_BORDERS_OFFER; int iTurnsBetweenStatements = 25; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { if(GetPlayer()->GetDealAI()->IsMakeOfferForOpenBorders(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { eStatement = eTempStatement; } else { pDeal->ClearItems(); } } else { // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at pDeal->ClearItems(); } } } /// Possible Contact Statement - Research Agreement Offer void CvDiplomacyAI::DoResearchAgreementOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (IsAvoidDeals()) return; if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if (GetPlayer()->IsAITeammateOfHuman()) return; if (eStatement == NO_DIPLO_STATEMENT_TYPE) { if (IsCanMakeResearchAgreementRightNow(ePlayer)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_RESEARCH_AGREEMENT_OFFER; int iTurnsBetweenStatements = 20; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { if (GetPlayer()->GetDealAI()->IsMakeOfferForResearchAgreement(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { eStatement = eTempStatement; } else { pDeal->ClearItems(); } } } } } /// Possible Contact Statement - Strategic Resource Offer void CvDiplomacyAI::DoStrategicTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (IsAvoidDeals()) return; if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_STRATEGIC_TRADE; int iTurnsBetweenStatements = 20; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { if (GetPlayer()->GetDealAI()->IsMakeOfferForStrategicResource(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { eStatement = eTempStatement; } else { pDeal->ClearItems(); } } } } /// Possible Contact Statement - Defensive Pact Offer void CvDiplomacyAI::DoDefensivePactOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (IsAvoidDeals()) return; if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if (GetPlayer()->IsAITeammateOfHuman()) return; if (eStatement == NO_DIPLO_STATEMENT_TYPE) { if (IsWantsDefensivePactWithPlayer(ePlayer)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_DEFENSIVE_PACT_REQUEST; int iTurnsBetweenStatements = 20; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { if (GetPlayer()->GetDealAI()->IsMakeOfferForDefensivePact(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { eStatement = eTempStatement; } else { pDeal->ClearItems(); } } } } } /// Possible Contact Statement - City Exchange void CvDiplomacyAI::DoCityExchange(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_EXCHANGE_CITIES; int iTurnsBetweenStatements = 30; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { if (GetPlayer()->GetDealAI()->IsMakeOfferForCityExchange(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { eStatement = eTempStatement; } else { pDeal->ClearItems(); } } } } /// Possible Contact Statement - Third Party War Trade void CvDiplomacyAI::DoThirdPartyWarTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if (GetPlayer()->IsAITeammateOfHuman()) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_THIRDPARTY_WAR_REQUEST; int iTurnsBetweenStatements = 40; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { if (GetPlayer()->GetDealAI()->IsMakeOfferForThirdPartyWar(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { eStatement = eTempStatement; } else { pDeal->ClearItems(); } } } } /// Possible Contact Statement - Peace Trade void CvDiplomacyAI::DoThirdPartyPeaceTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if (GetPlayer()->IsAITeammateOfHuman()) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_THIRDPARTY_PEACE_REQUEST; int iTurnsBetweenStatements = 30; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { if (GetPlayer()->GetDealAI()->IsMakeOfferForThirdPartyPeace(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { eStatement = eTempStatement; } else { pDeal->ClearItems(); } } } } /// Possible Contact Statement - Vote Trade void CvDiplomacyAI::DoVoteTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (IsAvoidDeals()) return; if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_VOTE_REQUEST; int iTurnsBetweenStatements = 10; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { if (GetPlayer()->GetDealAI()->IsMakeOfferForVote(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { eStatement = eTempStatement; } else { pDeal->ClearItems(); } } } } /// Possible Contact Statement - Renew Recently Expired Deal CvDeal* CvDiplomacyAI::DoRenewExpiredDeal(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); CvGameDeals& kGameDeals = GC.getGame().GetGameDeals(); std::vector renewDeals = kGameDeals.GetRenewableDealsWithPlayer(ePlayer, GetID(), 10); //no valid deals? if (renewDeals.size() <= 0) return NULL; if (eStatement != NO_DIPLO_STATEMENT_TYPE) { CancelRenewDeal(ePlayer, REASON_NO_DEAL); return NULL; } if (IsAvoidDeals()) { CancelRenewDeal(ePlayer, REASON_CANNOT_COMPROMISE); return NULL; } if (MOD_DIPLOAI_SHUT_UP_TRADE_RENEWALS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) { CancelRenewDeal(ePlayer, REASON_HUMAN_REJECTION); return NULL; } CvDeal* pBestDeal = NULL; int iBestDealValue = -INT_MAX; std::vector badDeals; for (uint iDeal = 0; iDeal < renewDeals.size(); iDeal++) { CvDeal* pCurrentDeal = renewDeals[iDeal]; // if this deal is a gift or peace deal, move on. if (pCurrentDeal->m_bIsGift || pCurrentDeal->IsPeaceTreatyTrade(ePlayer) || pCurrentDeal->IsPeaceTreatyTrade(GetID())) { //shouldn't happen but it might. badDeals.push_back(pCurrentDeal); continue; } //Set as considered for renewal. pCurrentDeal->m_iFinalTurn = -1; int iValue = m_pPlayer->GetDealAI()->GetDealValue(pCurrentDeal); if (iValue != INT_MAX) { bool bAbleToEqualize = false; if (!GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) { bool bUselessReferenceVariable = false; bool bCantMatchOffer = false; bAbleToEqualize = m_pPlayer->GetDealAI()->DoEqualizeDeal(pCurrentDeal, ePlayer, bUselessReferenceVariable, bCantMatchOffer); } else bAbleToEqualize = true; if (!bAbleToEqualize) { badDeals.push_back(pCurrentDeal); continue; } else { eStatement = DIPLO_STATEMENT_RENEW_DEAL; if (pCurrentDeal->m_iToPlayerValue > iBestDealValue) { iBestDealValue = pCurrentDeal->m_iToPlayerValue; pBestDeal = pCurrentDeal; } } } else { badDeals.push_back(pCurrentDeal); continue; } } for (uint iDeal = 0; iDeal < badDeals.size(); iDeal++) { CancelRenewDeal(ePlayer, REASON_CANNOT_COMPROMISE, false, badDeals[iDeal]); } // the UI can handle only one renewal offer at a time. out of the remaining deals, we make an offer for the best one and cancel all others for (uint iDeal = 0; iDeal < renewDeals.size(); iDeal++) { if (std::find(badDeals.begin(), badDeals.end(), renewDeals[iDeal]) == badDeals.end()) { if (renewDeals[iDeal]->m_bConsideringForRenewal && renewDeals[iDeal] != pBestDeal) { CancelRenewDeal(ePlayer, REASON_BETTER_RENEWAL_CHOICE, false, renewDeals[iDeal]); } } } return pBestDeal; } /// Possible Contact Statement - Request Help void CvDiplomacyAI::DoRequest(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_HELP_REQUESTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // If we just sent out a generous offer, don't ask for a request until some time has passed if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_GENEROUS_OFFER) < 25) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_REQUEST; // If a request was accepted or rejected, wait 60 turns. If we rolled for rand and failed, wait 15 turns before we try again if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 60 && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_REQUEST_RANDFAILED) >= 15) { bool bRandPassed = false; // This is used to see if we WOULD have made a request, but the rand roll failed (so add an entry to the log) bool bMakeRequest = IsMakeRequest(ePlayer, pDeal, bRandPassed); // Want to make a request of ePlayer? Pass pDeal in to see if there's actually anything we want if(bMakeRequest) { eStatement = eTempStatement; pDeal->SetRequestingPlayer(GetID()); pDeal->m_bIsGift = true; } // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at else pDeal->ClearItems(); // Add this statement to the log so we don't evaluate it again until 15 turns has come back around if(!bRandPassed) { SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_REQUEST_RANDFAILED, GC.getGame().getGameTurn()); pDeal->ClearItems(); } } } } /// Possible Contact Statement - Gift void CvDiplomacyAI::DoGift(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (IsAvoidDeals()) return; if (MOD_DIPLOAI_SHUT_UP_GIFT_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_GIFT; // If a request was accepted or rejected, wait 60 turns. If we rolled for rand and failed, wait 15 turns before we try again if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 60 && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_GIFT_RANDFAILED) >= 15) { bool bRandPassed = false; // This is used to see if we WOULD have made a gift, but the rand roll failed (so add an entry to the log) bool bMakeGift = false;//IsMakeGift(ePlayer, pDeal, bRandPassed); // Want to make a request of ePlayer? Pass pDeal in to see if there's actually anything we want if(bMakeGift) { eStatement = eTempStatement; pDeal->SetRequestingPlayer(GetID()); } // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at else pDeal->ClearItems(); // Add this statement to the log so we don't evaluate it again until 15 turns has come back around if(!bRandPassed) { SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_GIFT_RANDFAILED, GC.getGame().getGameTurn()); pDeal->ClearItems(); } } } } /// Possible Contact Statement //void CvDiplomacyAI::DoNowUnforgivableStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement) //{ // ASSERT(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); // ASSERT(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); // // if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) // return; // // if (eStatement == NO_DIPLO_STATEMENT_TYPE) // { // bool bSendStatement = false; // // // Unforgivable! // if (GetCivOpinion(ePlayer) == CIV_OPINION_UNFORGIVABLE) // { // // Our approach (real or fake) can't be Friendly // if (GetSurfaceApproach(ePlayer) != CIV_APPROACH_FRIENDLY) // { // bSendStatement = true; // } // } // // if (bSendStatement) // { // DiploStatementTypes eTempStatement = DIPLO_STATEMENT_NOW_UNFORGIVABLE; // int iTurnsBetweenStatements = 9999; // // if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) // { // eStatement = eTempStatement; // } // } // } //} /// Possible Contact Statement //void CvDiplomacyAI::DoNowEnemyStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement) //{ // ASSERT(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); // ASSERT(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); // // if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) // return; // // if (eStatement == NO_DIPLO_STATEMENT_TYPE) // { // bool bSendStatement = false; // // // Don't show this message if we've already given a more severe one // if (GetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_NOW_UNFORGIVABLE) > -1) // { // // An enemy // if (GetCivOpinion(ePlayer) == CIV_OPINION_ENEMY) // { // // Our approach (real or fake) can't be Friendly // if (GetSurfaceApproach(ePlayer) != CIV_APPROACH_FRIENDLY) // { // bSendStatement = true; // } // } // // if (bSendStatement) // { // DiploStatementTypes eTempStatement = DIPLO_STATEMENT_NOW_ENEMY; // int iTurnsBetweenStatements = 9999; // // if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) // { // eStatement = eTempStatement; // } // } // } // } //} /// Possible Contact Statement - Approach towards player is now HOSTILE void CvDiplomacyAI::DoHostileStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (WasResurrectedBy(ePlayer)) return; if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // We must be able to declare war on them if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) return; CivApproachTypes eApproach = GetSurfaceApproach(ePlayer); if (eStatement == NO_DIPLO_STATEMENT_TYPE) { if (!IsTooEarlyForDoF(ePlayer) && (eApproach == CIV_APPROACH_HOSTILE) && (GetMeanness() > 6)) { TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam(); // If we've made peace recently, don't go mouthing off right away int iPeaceTreatyTurn = GET_TEAM(GetTeam()).GetTurnMadePeaceTreatyWithTeam(eTeam); if (iPeaceTreatyTurn != -1) { int iTurnsSincePeace = GC.getGame().getElapsedGameTurns() - iPeaceTreatyTurn; if (iTurnsSincePeace < GC.getGame().getHandicapInfo().getPeaceTreatyDampenerTurns()) return; } DiploStatementTypes eTempStatement = DIPLO_STATEMENT_INSULT; int iTurnsBetweenStatements = 75; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) eStatement = eTempStatement; } } } /// Possible Contact Statement //void CvDiplomacyAI::DoFriendlyStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement) //{ // ASSERT(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); // ASSERT(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); // // if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) // return; // // CivApproachTypes eApproach = GetCivApproach(ePlayer); // // if (eStatement == NO_DIPLO_STATEMENT_TYPE) // { // if (eApproach == CIV_APPROACH_FRIENDLY) // { // DiploStatementTypes eTempStatement = DIPLO_STATEMENT_COMPLIMENT; // int iTurnsBetweenStatements = 35; // // if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) // { // eStatement = eTempStatement; // } // } // } //} /// Possible Contact Statement - Approach towards player is now AFRAID void CvDiplomacyAI::DoAfraidStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; CivApproachTypes eApproach = GetSurfaceApproach(ePlayer); if(eStatement == NO_DIPLO_STATEMENT_TYPE) { if(eApproach == CIV_APPROACH_AFRAID) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_BOOT_KISSING; int iTurnsBetweenStatements = 35; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) eStatement = eTempStatement; } } } /// Possible Contact Statement - Warning the player about their warmongering void CvDiplomacyAI::DoWarmongerStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (WasResurrectedBy(ePlayer)) return; if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // We must be able to declare war on them if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) return; if (eStatement == NO_DIPLO_STATEMENT_TYPE) { if (GetWarmongerThreat(ePlayer) >= THREAT_SEVERE) { bool bSendStatement = true; // Don't send statement if we're going for conquest ourselves if (IsGoingForWorldConquest() || IsCloseToWorldConquest()) bSendStatement = false; // 2 in 3 chance we don't actually send the message (don't want to bombard the player from all sides) if (GC.getGame().randRangeInclusive(1, 3, CvSeeder::fromRaw(0x4a229eef).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) < 3) bSendStatement = false; DiploStatementTypes eTempStatement = DIPLO_STATEMENT_WARMONGER; if (bSendStatement) { if (GetTurnStatementLastSent(ePlayer, eTempStatement) == -1) eStatement = eTempStatement; } // Add this statement to the log so we don't evaluate it again next turn else SetTurnStatementLastSent(ePlayer, eTempStatement, GC.getGame().getGameTurn()); } } } /// Possible Contact Statement - Warning the player that we don't like their interactions with "our" City-States void CvDiplomacyAI::DoMinorCivCompetitionStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1, bool bIgnoreTurnsBetweenLimit) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (WasResurrectedBy(ePlayer)) return; if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // We must be able to declare war on them if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) return; if (eStatement == NO_DIPLO_STATEMENT_TYPE) { // They must be able to declare war on us if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam(), ePlayer)) return; if (GetMinorCivDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_MINOR_CIV_COMPETITION; int iTurnsBetweenStatements = 9999; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements || bIgnoreTurnsBetweenLimit) { // Find a city state we're upset over for (int iMinorLoop = MAX_MAJOR_CIVS; iMinorLoop < MAX_CIV_PLAYERS; iMinorLoop++) { PlayerTypes eMinor = (PlayerTypes) iMinorLoop; // Don't evaluate City-States that are unmet/dead if (!IsPlayerValid(eMinor)) continue; // Ignore if League resolutions make it irrelevant if (GET_PLAYER(eMinor).GetMinorCivAI()->IsNoAlly() || GET_PLAYER(eMinor).GetMinorCivAI()->GetPermanentAlly() == GetID()) continue; // Must be a minor we aren't attacking/bullying if (GetCivApproach(eMinor) <= CIV_APPROACH_HOSTILE) continue; // We have a PtP with this minor if (GET_PLAYER(eMinor).GetMinorCivAI()->IsProtectedByMajor(GetID())) { if (GET_PLAYER(eMinor).GetMinorCivAI()->IsAllies(ePlayer)) { iData1 = eMinor; break; } else if (GET_PLAYER(eMinor).GetMinorCivAI()->IsFriends(ePlayer)) { iData1 = eMinor; break; } } } // Don't change the statement unless we found a minor to complain about if (iData1 != NO_PLAYER) { eStatement = eTempStatement; } } } } } /// Possible Contact Statement - We're angry that they befriended a player we denounced void CvDiplomacyAI::DoAngryBefriendedEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (WasResurrectedBy(ePlayer)) return; if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // We must be able to declare war on them if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) return; // We denounced the leader we're talking to - no use whining at this point if(IsDenouncedPlayer(ePlayer) || IsAtWar(ePlayer) || GetSurfaceApproach(ePlayer) == CIV_APPROACH_HOSTILE) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50 && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY_RANDFAILED) >= 20) { CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); // Loop through all players until we find one that is our enemy, that ePlayer befriended for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // Must be alive if(!GET_PLAYER(eLoopPlayer).isAlive()) continue; // Can't be either of us if(eLoopPlayer == GetID() || eLoopPlayer == ePlayer) continue; // We haven't denounced this guy and we're not at war with them if(!IsDenouncedPlayer(eLoopPlayer) && !IsAtWar(eLoopPlayer)) continue; // They haven't befriended this guy if(!pTheirDiploAI->IsDoFAccepted(eLoopPlayer)) continue; // Too much time has passed (or maybe we already sent a message recently) if(pTheirDiploAI->GetTurnsSinceBefriendedPlayer(eLoopPlayer) > 1) continue; // Found a match! int iWeight = GetMeanness(); // Usually ranges from 3 to 7 iWeight += GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x7e74ea9f).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); // We're mean enough to say something if(iWeight >= 10) { eStatement = eTempStatement; iData1 = eLoopPlayer; } // We're going to be nice! else { eStatement = DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY_RANDFAILED; } // We're done in here break; } } } } /// Possible Contact Statement - We're angry that they denounced one of our friends void CvDiplomacyAI::DoAngryDenouncedFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (WasResurrectedBy(ePlayer)) return; if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // We must be able to declare war on them if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) return; // We denounced the leader we're talking to - no use whining at this point if(IsDenouncedPlayer(ePlayer) || IsAtWar(ePlayer) || GetSurfaceApproach(ePlayer) == CIV_APPROACH_HOSTILE) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50 && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND_RANDFAILED) >= 20) { CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); // Loop through all players until we find one that is our friend, that ePlayer denounced for(int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // Must be alive if(!GET_PLAYER(eLoopPlayer).isAlive()) continue; // Can't be either of us if(eLoopPlayer == GetID() || eLoopPlayer == ePlayer) continue; // We haven't befriended this guy if(!IsDoFAccepted(eLoopPlayer)) continue; // They haven't denounced this guy if(!pTheirDiploAI->IsDenouncedPlayer(eLoopPlayer)) continue; // Too much time has passed (or maybe we already sent a message recently) if(pTheirDiploAI->GetTurnsSinceDenouncedPlayer(eLoopPlayer) > 1) continue; // Found a match! int iWeight = GetMeanness(); // Usually ranges from 3 to 7 iWeight += GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x2207e072).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); // We're mean enough to say something if(iWeight >= 10) { eStatement = eTempStatement; iData1 = eLoopPlayer; } // We're going to be nice! else { eStatement = DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND_RANDFAILED; } // We're done in here break; } } } } /// Possible Contact Statement - We're happy that they denounced a player we denounced void CvDiplomacyAI::DoHappyDenouncedEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // We denounced the leader we're talking to - no use talking at this point if(IsDenouncedPlayer(ePlayer) || IsAtWar(ePlayer) || GetSurfaceApproach(ePlayer) == CIV_APPROACH_HOSTILE) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50 && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY_RANDFAILED) >= 20) { CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); // Loop through all players until we find one that is our enemy, that ePlayer denounced for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // Must be alive if(!GET_PLAYER(eLoopPlayer).isAlive()) continue; // Can't be either of us if(eLoopPlayer == GetID() || eLoopPlayer == ePlayer) continue; // We haven't denounced this guy if(!IsDenouncedPlayer(eLoopPlayer)) continue; // They haven't denounced this guy if(!pTheirDiploAI->IsDenouncedPlayer(eLoopPlayer)) continue; // Too much time has passed (or maybe we already sent a message recently) if(pTheirDiploAI->GetTurnsSinceDenouncedPlayer(eLoopPlayer) > 1) continue; // Found a match! int iWeight = GetChattiness(); // Usually ranges from 3 to 7 iWeight += GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0xd55f1785).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); // We're chatty enough to say something if(iWeight >= 10) { eStatement = eTempStatement; iData1 = eLoopPlayer; } // We're going to be nice! else { eStatement = DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY_RANDFAILED; } // We're done in here break; } } } } /// Possible Contact Statement - We're happy they befriended one of our friends void CvDiplomacyAI::DoHappyBefriendedFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // We denounced the leader we're talking to - no use talking at this point if(IsDenouncedPlayer(ePlayer) || IsAtWar(ePlayer) || GetSurfaceApproach(ePlayer) == CIV_APPROACH_HOSTILE) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50 && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND_RANDFAILED) >= 20) { CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); // Loop through all players until we find one that is our friend, that ePlayer DoFed for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // Must be alive if(!GET_PLAYER(eLoopPlayer).isAlive()) continue; // Can't be either of us if(eLoopPlayer == GetID() || eLoopPlayer == ePlayer) continue; // We haven't DoFed this guy if(!IsDoFAccepted(eLoopPlayer)) continue; // They haven't DoFed this guy if(!pTheirDiploAI->IsDoFAccepted(eLoopPlayer)) continue; // Too much time has passed (or maybe we already sent a message recently) if(pTheirDiploAI->GetTurnsSinceBefriendedPlayer(eLoopPlayer) > 1) continue; // Found a match! int iWeight = GetChattiness(); // Usually ranges from 3 to 7 iWeight += GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x2b7c6537).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); // We're chatty enough to say something if(iWeight >= 10) { eStatement = eTempStatement; iData1 = eLoopPlayer; } // We're going to be nice! else { eStatement = DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND_RANDFAILED; } // We're done in here break; } } } } /// Possible Contact Statement - Peace void CvDiplomacyAI::DoPeaceOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_PEACE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if (!IsAtWar(ePlayer)) return; if (eStatement == NO_DIPLO_STATEMENT_TYPE) { // Have to have been at war for at least a little while GetPlayer()->SetCachedValueOfPeaceWithHuman(0); if (IsWantsPeaceWithPlayer(ePlayer)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_REQUEST_PEACE; int iTurnsBetweenStatementsCityTrade = 2; int iTurnsBetweenStatements = 5; // cities can be added to a peace deal if they are in danger of falling, so that check needs to be done more frequently if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatementsCityTrade) { if (GetPlayer()->GetDealAI()->IsOfferPeace(ePlayer, /*pDeal can be modified in this function*/ pDeal, false /*bEqualizingDeals*/) && pDeal->GetNumItems() > 0 && pDeal->ContainsItemType(TRADE_ITEM_CITIES, ePlayer)) { eStatement = eTempStatement; } else { // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at pDeal->ClearItems(); if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { if (GetPlayer()->GetDealAI()->IsOfferPeace(ePlayer, /*pDeal can be modified in this function*/ pDeal, false /*bEqualizingDeals*/) && pDeal->GetNumItems() > 0) { eStatement = eTempStatement; } else { // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at pDeal->ClearItems(); } } } } } } } /// Possible Contact Statement - We befriended one of the human's enemies and we're letting them know void CvDiplomacyAI::DoFYIBefriendedHumanEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (WasResurrectedBy(ePlayer)) return; if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // We must be able to declare war on them if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_ENEMY; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50 && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_ENEMY_RANDFAILED) >= 20) { CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); // Loop through all players until we find one that we just made friends with, that ePlayer denounced for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // Must be alive if(!GET_PLAYER(eLoopPlayer).isAlive()) continue; // Can't be either of us if(eLoopPlayer == GetID() || eLoopPlayer == ePlayer) continue; // We haven't befriended this guy if(!IsDoFAccepted(eLoopPlayer)) continue; // They haven't denounced this guy if(!pTheirDiploAI->IsDenouncedPlayer(eLoopPlayer)) continue; // Too much time has passed (or maybe we already sent a message recently) if(GetTurnsSinceBefriendedPlayer(eLoopPlayer) > 1) continue; CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); CivApproachTypes eApproach = GetCivApproach(ePlayer); // Don't say mean things if we like ePlayer if(eOpinion >= CIV_OPINION_FAVORABLE) continue; if(eApproach == CIV_APPROACH_FRIENDLY) continue; int iWeight = 0; if(eOpinion == CIV_OPINION_COMPETITOR) iWeight += 2; else if(eOpinion == CIV_OPINION_ENEMY) iWeight += 5; else if(eOpinion == CIV_OPINION_UNFORGIVABLE) iWeight += 10; iWeight += GetMeanness(); // Usually ranges from 3 to 7 iWeight += GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x85433256).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); // We're mean enough to say something if(iWeight >= 10) { eStatement = eTempStatement; iData1 = eLoopPlayer; } // We're going to be nice! else { eStatement = DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_ENEMY_RANDFAILED; } // We're done in here break; } } } } /// Possible Contact Statement - We denounced one of the human's friends and we're letting them know void CvDiplomacyAI::DoFYIDenouncedHumanFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (WasResurrectedBy(ePlayer)) return; if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // We must be able to declare war on them if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50 && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND_RANDFAILED) >= 20) { CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); // Loop through all players until we find one that we just denoucned, that ePlayer has befriended for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // Must be alive if (!GET_PLAYER(eLoopPlayer).isAlive()) continue; // Can't be either of us if (eLoopPlayer == GetID() || eLoopPlayer == ePlayer) continue; // We haven't denounced this guy if (!IsDenouncedPlayer(eLoopPlayer)) continue; // They haven't befriended this guy if (!pTheirDiploAI->IsDoFAccepted(eLoopPlayer)) continue; // Too much time has passed (or maybe we already sent a message recently) if (GetTurnsSinceDenouncedPlayer(eLoopPlayer) > 1) continue; CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); CivApproachTypes eApproach = GetCivApproach(ePlayer); // Don't say mean things if we like ePlayer if(eOpinion >= CIV_OPINION_FAVORABLE) continue; if(eApproach == CIV_APPROACH_FRIENDLY) continue; int iWeight = 0; if(eOpinion == CIV_OPINION_COMPETITOR) iWeight += 2; else if(eOpinion == CIV_OPINION_ENEMY) iWeight += 5; else if(eOpinion == CIV_OPINION_UNFORGIVABLE) iWeight += 10; iWeight += GetMeanness(); // Usually ranges from 3 to 7 iWeight += GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x58262340).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); // We're mean enough to say something if(iWeight >= 10) { eStatement = eTempStatement; iData1 = eLoopPlayer; } // We're going to be nice! else { eStatement = DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND_RANDFAILED; } // We're done in here break; } } } } /// Possible Contact Statement - We denounced someone the human has denounced and we're letting them know void CvDiplomacyAI::DoFYIDenouncedHumanEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50 && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY_RANDFAILED) >= 20) { CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); // Loop through all players until we find one that we just denounced, that ePlayer has denounced for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // Must be alive if (!GET_PLAYER(eLoopPlayer).isAlive()) continue; // Can't be either of us if (eLoopPlayer == GetID() || eLoopPlayer == ePlayer) continue; // We haven't denounced this guy if (!IsDenouncedPlayer(eLoopPlayer)) continue; // They haven't denounced this guy if (!pTheirDiploAI->IsDenouncedPlayer(eLoopPlayer)) continue; // Too much time has passed (or maybe we already sent a message recently) if (GetTurnsSinceDenouncedPlayer(eLoopPlayer) > 1) continue; CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); CivApproachTypes eApproach = GetCivApproach(ePlayer); // Don't say nice things if we dislike ePlayer if (eOpinion <= CIV_OPINION_COMPETITOR) continue; if (eApproach == CIV_APPROACH_WAR || eApproach == CIV_APPROACH_HOSTILE) continue; int iWeight = 0; if(eOpinion == CIV_OPINION_FAVORABLE) iWeight += 2; else if(eOpinion == CIV_OPINION_FRIEND) iWeight += 5; else if(eOpinion == CIV_OPINION_ALLY) iWeight += 10; if(eApproach == CIV_APPROACH_FRIENDLY) iWeight += 2; // Add weight if they're strong if(GetMilitaryStrengthComparedToUs(ePlayer) > STRENGTH_AVERAGE) iWeight += 3; iWeight += GetChattiness(); // Usually ranges from 3 to 7 iWeight += GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0xea12329a).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); // We're mean enough to say something if(iWeight >= 10) { eStatement = eTempStatement; iData1 = eLoopPlayer; } // We're going to be nice! else { eStatement = DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY_RANDFAILED; } // We're done in here break; } } } } /// Possible Contact Statement - We befriended one of the human's friends, and we're letting them know void CvDiplomacyAI::DoFYIBefriendedHumanFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if (eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND; int iMessage = 0; int iMessageMax = MAX_INT; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if(eLoopPlayer != NO_PLAYER && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != ePlayer) { iMessage = GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, eTempStatement); if(iMessage < iMessageMax) { iMessageMax = iMessage; } } } if(iMessageMax >= 40 && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND_RANDFAILED) >= 20) { CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); // Loop through all players until we find one that we just befriended, that ePlayer has befriended for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // Must be alive if(!GET_PLAYER(eLoopPlayer).isAlive()) continue; // Can't be either of us if(eLoopPlayer == GetID() || eLoopPlayer == ePlayer) continue; // We haven't befriended this guy if(!IsDoFAccepted(eLoopPlayer)) continue; // They haven't befriended this guy if(!pTheirDiploAI->IsDoFAccepted(eLoopPlayer)) continue; // Too much time has passed (or maybe we already sent a message recently) if(GetTurnsSinceBefriendedPlayer(eLoopPlayer) > 1) continue; CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); CivApproachTypes eApproach = GetCivApproach(ePlayer); // Don't say nice things if we dislike ePlayer if (eOpinion <= CIV_OPINION_COMPETITOR) continue; if (eApproach == CIV_APPROACH_WAR || eApproach == CIV_APPROACH_HOSTILE) continue; int iWeight = 0; if (eOpinion == CIV_OPINION_FAVORABLE) iWeight += 2; else if (eOpinion == CIV_OPINION_FRIEND) iWeight += 5; else if (eOpinion == CIV_OPINION_ALLY) iWeight += 10; if (eApproach == CIV_APPROACH_FRIENDLY) iWeight += 2; iWeight += GetChattiness(); // Usually ranges from 3 to 7 iWeight += GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x5eec20f2).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); // We're mean enough to say something if (iWeight >= 10) { eStatement = eTempStatement; iData1 = eLoopPlayer; } // We're going to be nice! else { eStatement = DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND_RANDFAILED; } // We're done in here break; } } } } /// Possible Contact Statement - We're happy we're following the same ideology as the human void CvDiplomacyAI::DoHappySamePolicyTree(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = NO_DIPLO_STATEMENT_TYPE; PolicyBranchTypes eMyBranch = m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree(); PolicyBranchTypes eTheirBranch = GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree(); if(eMyBranch != NO_POLICY_BRANCH_TYPE && eTheirBranch != NO_POLICY_BRANCH_TYPE && eMyBranch == eTheirBranch && GET_PLAYER(ePlayer).GetCulture()->GetTurnIdeologySwitch() < 0) { // Don't say nice things if we dislike ePlayer bool bSkip = false; CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); CivApproachTypes eApproach = GetCivApproach(ePlayer); if(eOpinion <= CIV_OPINION_COMPETITOR) bSkip = true; if(eApproach == CIV_APPROACH_WAR || eApproach == CIV_APPROACH_HOSTILE) bSkip = true; // Check chattiness to see if we send the message this turn if (!bSkip && GetChattiness() > GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0xf47d6bbc).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID()))) { DiploStatementTypes eOtherStatementToCheck = NO_DIPLO_STATEMENT_TYPE; if(eMyBranch == GD_INT_GET(POLICY_BRANCH_FREEDOM)) { eTempStatement = DIPLO_STATEMENT_SAME_POLICIES_FREEDOM; eOtherStatementToCheck = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_FREEDOM; } else if(eMyBranch == GD_INT_GET(POLICY_BRANCH_ORDER)) { eTempStatement = DIPLO_STATEMENT_SAME_POLICIES_ORDER; eOtherStatementToCheck = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_ORDER; } else if(eMyBranch == GD_INT_GET(POLICY_BRANCH_AUTOCRACY)) { eTempStatement = DIPLO_STATEMENT_SAME_POLICIES_AUTOCRACY; eOtherStatementToCheck = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_AUTOCRACY; } if(eTempStatement != NO_DIPLO_STATEMENT_TYPE) { int iTurnsBetweenStatements = 9999; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { // Also check the statement for joining the ideology. Don't want to send messages like this on back-to-back turns if(GetNumTurnsSinceStatementSent(ePlayer, eOtherStatementToCheck) >= iTurnsBetweenStatements) { eStatement = eTempStatement; } } } } } } } /// Possible Contact Statement - Either AI or human has switched ideologies due to the other's pressure void CvDiplomacyAI::DoIdeologicalStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); PolicyBranchTypes eFreedom = (PolicyBranchTypes)GD_INT_GET(POLICY_BRANCH_FREEDOM); PolicyBranchTypes eOrder = (PolicyBranchTypes)GD_INT_GET(POLICY_BRANCH_ORDER); CvPlayer &kTheirPlayer = GET_PLAYER(ePlayer); if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement; if (m_pPlayer->GetCulture()->GetInfluenceLevel(ePlayer) >= INFLUENCE_LEVEL_INFLUENTIAL) { eTempStatement = DIPLO_STATEMENT_OUR_CULTURE_INFLUENTIAL; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 9999) { eStatement = eTempStatement; return; } } if (kTheirPlayer.GetCulture()->GetInfluenceLevel(GetID()) >= INFLUENCE_LEVEL_INFLUENTIAL) { eTempStatement = DIPLO_STATEMENT_YOUR_CULTURE_INFLUENTIAL; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 9999) { eStatement = eTempStatement; return; } } // Everything below are "insult" type messages if (MOD_DIPLOAI_SHUT_UP_INSULTS && kTheirPlayer.isHuman(ISHUMAN_AI_DIPLOMACY)) return; // We must be able to declare war on them if (!GET_TEAM(GetTeam()).canDeclareWar(kTheirPlayer.getTeam(), GetID())) return; PolicyBranchTypes eMyBranch = m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree(); PolicyBranchTypes eTheirBranch = kTheirPlayer.GetPlayerPolicies()->GetLateGamePolicyTree(); if(eMyBranch != NO_POLICY_BRANCH_TYPE && eTheirBranch != NO_POLICY_BRANCH_TYPE && eMyBranch == eTheirBranch) { PublicOpinionTypes eOpinionInMyCiv = m_pPlayer->GetCulture()->GetPublicOpinionType(); PlayerTypes eMyGreatestInfluence = m_pPlayer->GetCulture()->GetPublicOpinionBiggestInfluence(); PublicOpinionTypes eOpinionInTheirCiv = kTheirPlayer.GetCulture()->GetPublicOpinionType(); PlayerTypes eTheirGreatestInfluence = kTheirPlayer.GetCulture()->GetPublicOpinionBiggestInfluence(); // Did this player recently switch ideology due to our pressure? int iIdeologySwitchTurn = kTheirPlayer.GetCulture()->GetTurnIdeologySwitch(); if (iIdeologySwitchTurn > 0 && iIdeologySwitchTurn + 10 > GC.getGame().getGameTurn()) { DiploStatementTypes eSwitchStatement = NO_DIPLO_STATEMENT_TYPE; if (eTheirBranch == eFreedom) { eSwitchStatement = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_FREEDOM; } else if (eTheirBranch == eOrder) { eSwitchStatement = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_ORDER; } else { eSwitchStatement = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_AUTOCRACY; } //don't spam the message bool bNotRecentlySent = true; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes)iPlayerLoop; if (eLoopPlayer != NO_PLAYER && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != ePlayer) { if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, eSwitchStatement) < 9999) bNotRecentlySent = false; } } if (bNotRecentlySent) { eStatement = eSwitchStatement; return; } } if (eOpinionInMyCiv >= PUBLIC_OPINION_CIVIL_RESISTANCE && eMyGreatestInfluence == ePlayer) { if (eTheirBranch == eFreedom) { eTempStatement = DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM; } else if (eTheirBranch == eOrder) { eTempStatement = DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER; } else { eTempStatement = DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY; } if (eTempStatement != NO_DIPLO_STATEMENT_TYPE) { if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 9999) { eStatement = eTempStatement; return; } } } if (eOpinionInTheirCiv >= PUBLIC_OPINION_CIVIL_RESISTANCE && eTheirGreatestInfluence == GetID()) { if (eMyBranch == eFreedom) { eTempStatement = DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM; } else if (eMyBranch == eOrder) { eTempStatement = DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER; } else { eTempStatement = DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY; } if (eTempStatement != NO_DIPLO_STATEMENT_TYPE) { if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 9999) { eStatement = eTempStatement; return; } } } } } } /// Possible Contact Statement - Message to human if the AI thinks they are getting close to the victory they're also going for. void CvDiplomacyAI::DoVictoryCompetitionStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (!IsCompetingForVictory()) return; if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // We must be able to declare war on them if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) return; int iTurnsBetweenStatements = 50; AIGrandStrategyTypes eMyGrandStrategy = GetPlayer()->GetGrandStrategyAI()->GetActiveGrandStrategy(); if(eMyGrandStrategy == NO_AIGRANDSTRATEGY) { return; } if(eStatement == NO_DIPLO_STATEMENT_TYPE) { EraTypes eModern = (EraTypes) GC.getInfoTypeForString("ERA_MODERN", true); DisputeLevelTypes eDispute = GetVictoryDisputeLevel(ePlayer); if(eDispute < DISPUTE_LEVEL_STRONG) { return; } CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); if(eOpinion >= CIV_OPINION_FAVORABLE) { return; } bool bLeagueCompetitor = false; bool bSpaceRace = false; bool bCulture = false; bool bWar = false; int iVotes = 0; int iNeededVotes = 0; CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if(pLeague != NULL) { iVotes = pLeague->CalculateStartingVotesForMember(ePlayer); iNeededVotes = GC.getGame().GetVotesNeededForDiploVictory(); if(iNeededVotes > 0) { // 33% there? Close! if(iVotes >= (iNeededVotes / 3)) { bLeagueCompetitor = true; } } } int iProjectCount = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetSSProjectCount(); if (iProjectCount > 1) { bSpaceRace = true; } else { int iTheirTechNum = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetTeamTechs()->GetNumTechsKnown(); int iNumOtherPlayers = 0; int iNumPlayersAheadInTech = 0; for(uint ui = 0; ui < MAX_MAJOR_CIVS; ui++) { PlayerTypes eOtherPlayer = (PlayerTypes)ui; if(!GET_PLAYER(eOtherPlayer).isAlive()) { continue; } if (eOtherPlayer == ePlayer) { continue; } iNumOtherPlayers++; int iNumTechs = GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).GetTeamTechs()->GetNumTechsKnown(); if (iTheirTechNum > iNumTechs ) { iNumPlayersAheadInTech++; } } if(iNumPlayersAheadInTech >= iNumOtherPlayers) { bSpaceRace = true; } } if(GetWarmongerThreat(ePlayer) >= THREAT_SEVERE && GET_PLAYER(ePlayer).GetNumCapitalCities() > 1 && GetPlayer()->GetNumCapitalCities() > 1) { bWar = true; } //More than double our influence, and we both have some? if(GET_PLAYER(ePlayer).GetCulture()->GetNumCivsInfluentialOn() > 1 && GetPlayer()->GetCulture()->GetNumCivsInfluentialOn() > 0 && (GET_PLAYER(ePlayer).GetCulture()->GetNumCivsInfluentialOn() > (GetPlayer()->GetCulture()->GetNumCivsInfluentialOn() * 2))) { bCulture = true; } DiploStatementTypes eTempStatement; if(GetPlayer()->GetGrandStrategyAI()->GetGuessOtherPlayerActiveGrandStrategy(ePlayer) == eMyGrandStrategy) { if(GetPlayer()->GetGrandStrategyAI()->GetGuessOtherPlayerActiveGrandStrategyConfidence(ePlayer) >= GUESS_CONFIDENCE_LIKELY) { //Conquered a capital? You are in our way! if(IsGoingForWorldConquest() && bWar) { eTempStatement = DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONQUEST; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { eStatement = eTempStatement; return; } } else if(IsGoingForDiploVictory() && bLeagueCompetitor) { eTempStatement = DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONGRESS; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { eStatement = eTempStatement; return; } } else if(IsGoingForCultureVictory() && bCulture) { //We've both influenced someone? Competitor! eTempStatement = DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_CULTURE; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { eStatement = eTempStatement; return; } } else if(IsGoingForSpaceshipVictory() && bSpaceRace) { eTempStatement = DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_SPACESHIP; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { eStatement = eTempStatement; return; } } //Don't have it figured out, but we're competitive? Grr! else if(GetPlayer()->GetCurrentEra() > eModern) { eTempStatement = DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_CONFUSED; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { eStatement = eTempStatement; return; } } } } } } /// Possible Contact Statement - Message to human if the AI thinks they are getting close to a victory that they're not going for. void CvDiplomacyAI::DoVictoryBlockStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (!IsCompetingForVictory()) return; if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // We must be able to declare war on them if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) return; int iTurnsBetweenStatements = 50; AIGrandStrategyTypes eConquestGrandStrategy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_CONQUEST"); AIGrandStrategyTypes eCultureGrandStrategy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_CULTURE"); AIGrandStrategyTypes eUNGrandStrategy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_UNITED_NATIONS"); AIGrandStrategyTypes eSpaceshipGrandStrategy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_SPACESHIP"); EraTypes eAtomic = (EraTypes) GC.getInfoTypeForString("ERA_POSTMODERN", true); bool bSkip = false; CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); if(eOpinion >= CIV_OPINION_FAVORABLE) { bSkip = true; } //Let's not send this before the Atomic Era, okay? if(GetPlayer()->GetCurrentEra() < eAtomic) { bSkip = true; } if(eStatement == NO_DIPLO_STATEMENT_TYPE && !bSkip) { DiploStatementTypes eTempStatement; if(GetVictoryBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG) { AIGrandStrategyTypes eGrandStrategy = GetPlayer()->GetGrandStrategyAI()->GetGuessOtherPlayerActiveGrandStrategy(ePlayer); if(eGrandStrategy == eConquestGrandStrategy) { eTempStatement = DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_WORLD_CONQUEST; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { eStatement = eTempStatement; return; } } else if(eGrandStrategy == eUNGrandStrategy) { eTempStatement = DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_WORLD_CONGRESS; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { eStatement = eTempStatement; return; } } else if(eGrandStrategy == eCultureGrandStrategy) { eTempStatement = DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_CULTURE; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { eStatement = eTempStatement; return; } } else if(eGrandStrategy == eSpaceshipGrandStrategy) { eTempStatement = DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_SPACESHIP; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { eStatement = eTempStatement; return; } } } } } /// Possible Contact Statement - We liked the human's proposal to the World Congress void CvDiplomacyAI::DoWeLikedTheirProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = NO_DIPLO_STATEMENT_TYPE; if (WeLikedTheirProposal(ePlayer)) { bool bSkip = false; CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); CivApproachTypes eApproach = GetCivApproach(ePlayer); if(eOpinion <= CIV_OPINION_COMPETITOR) bSkip = true; if(eApproach == CIV_APPROACH_WAR || eApproach == CIV_APPROACH_HOSTILE) bSkip = true; if ((GC.getGame().getGameTurn() - GetWeLikedTheirProposalTurn(ePlayer)) > 10) bSkip = true; if (!bSkip && GetChattiness() > GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x4ce615ad).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID()))) { eTempStatement = DIPLO_STATEMENT_WE_LIKED_THEIR_PROPOSAL; int iTurnsBetweenStatements = /*50*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_NUM_TURNS); int iMessage = 0; int iMessageMax = MAX_INT; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if(eLoopPlayer != NO_PLAYER && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != ePlayer) { iMessage = GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, eTempStatement); if(iMessage < iMessageMax) { iMessageMax = iMessage; } } } if(iMessageMax >= iTurnsBetweenStatements && (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements)) eStatement = eTempStatement; } } } } /// Possible Contact Statement - We disliked the human's proposal to the World Congress void CvDiplomacyAI::DoWeDislikedTheirProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // We must be able to declare war on them if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = NO_DIPLO_STATEMENT_TYPE; if (WeDislikedTheirProposal(ePlayer)) { bool bSkip = false; CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); CivApproachTypes eApproach = GetCivApproach(ePlayer); if(eOpinion >= CIV_OPINION_FAVORABLE) bSkip = true; if(eApproach == CIV_APPROACH_FRIENDLY) bSkip = true; if ((GC.getGame().getGameTurn() - GetWeDislikedTheirProposalTurn(ePlayer)) > 10) bSkip = true; if (!bSkip && GetChattiness() > GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x9b0e7357).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID()))) { eTempStatement = DIPLO_STATEMENT_WE_DISLIKED_THEIR_PROPOSAL; int iTurnsBetweenStatements = /*50*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_NUM_TURNS); int iMessage = 0; int iMessageMax = MAX_INT; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if(eLoopPlayer != NO_PLAYER && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != ePlayer) { iMessage = GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, eTempStatement); if(iMessage < iMessageMax) { iMessageMax = iMessage; } } } if(iMessageMax >= iTurnsBetweenStatements && (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements)) eStatement = eTempStatement; } } } } /// Possible Contact Statement - The human helped our proposal pass in the World Congress void CvDiplomacyAI::DoTheySupportedOurProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = NO_DIPLO_STATEMENT_TYPE; bool bEffect = GetTheySupportedOurProposalTurn(ePlayer) > -1; if (bEffect) { bool bSkip = false; CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); CivApproachTypes eApproach = GetCivApproach(ePlayer); if(eOpinion <= CIV_OPINION_COMPETITOR) bSkip = true; if(eApproach == CIV_APPROACH_WAR || eApproach == CIV_APPROACH_HOSTILE) bSkip = true; if ((GC.getGame().getGameTurn() - GetTheySupportedOurProposalTurn(ePlayer)) > 10) bSkip = true; if (!bSkip) { eTempStatement = DIPLO_STATEMENT_THEY_SUPPORTED_OUR_PROPOSAL; int iTurnsBetweenStatements = /*50*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_PROPOSAL_NUM_TURNS); int iMessage = 0; int iMessageMax = MAX_INT; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if(eLoopPlayer != NO_PLAYER && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != ePlayer) { iMessage = GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, eTempStatement); if(iMessage < iMessageMax) { iMessageMax = iMessage; } } } if(iMessageMax >= iTurnsBetweenStatements && (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements)) eStatement = eTempStatement; } } } } /// Possible Contact Statement - The human helped our proposal fail in the World Congress void CvDiplomacyAI::DoTheyFoiledOurProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // We must be able to declare war on them if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = NO_DIPLO_STATEMENT_TYPE; bool bEffect = GetTheyFoiledOurProposalTurn(ePlayer) > -1; if (bEffect) { bool bSkip = false; CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); CivApproachTypes eApproach = GetCivApproach(ePlayer); if(eOpinion >= CIV_OPINION_FAVORABLE) bSkip = true; if(eApproach == CIV_APPROACH_FRIENDLY) bSkip = true; if ((GC.getGame().getGameTurn() - GetTheyFoiledOurProposalTurn(ePlayer)) > 10) bSkip = true; if (!bSkip) { eTempStatement = DIPLO_STATEMENT_THEY_FOILED_OUR_PROPOSAL; int iTurnsBetweenStatements = /*50*/ GD_INT_GET(OPINION_WEIGHT_THEY_FOILED_OUR_PROPOSAL_NUM_TURNS); int iMessage = 0; int iMessageMax = MAX_INT; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if(eLoopPlayer != NO_PLAYER && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != ePlayer) { iMessage = GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, eTempStatement); if(iMessage < iMessageMax) { iMessageMax = iMessage; } } } if(iMessageMax >= iTurnsBetweenStatements && (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements)) eStatement = eTempStatement; } } } } /// Possible Contact Statement - The human helped relocate the World Congress to our lands void CvDiplomacyAI::DoTheySupportedOurHosting(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = NO_DIPLO_STATEMENT_TYPE; if (TheySupportedOurHosting(ePlayer)) { bool bSkip = false; CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); CivApproachTypes eApproach = GetCivApproach(ePlayer); if(eOpinion <= CIV_OPINION_COMPETITOR) bSkip = true; if(eApproach == CIV_APPROACH_WAR || eApproach == CIV_APPROACH_HOSTILE) bSkip = true; if ((GC.getGame().getGameTurn() - GetTheySupportedOurHostingTurn(ePlayer)) > 20) bSkip = true; if (!bSkip) { eTempStatement = DIPLO_STATEMENT_THEY_SUPPORTED_OUR_HOSTING; int iTurnsBetweenStatements = /*50*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_HOSTING_NUM_TURNS); if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) eStatement = eTempStatement; } } } } ///////////////////////////////////////////////////////// // Diplo stuff relating to UI ///////////////////////////////////////////////////////// /// Initiate diplo screen with default state void CvDiplomacyAI::DoBeginDiploWithHuman() { if(!GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR)) { if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { // JdH => go into the deal, if there is anything proposed PlayerTypes eTo = GC.getGame().getActivePlayer(); CvPlayer& kTo = GET_PLAYER(eTo); CvDiplomacyRequests* pRequests = kTo.GetDiplomacyRequests(); pRequests->ActivateAllFrom(GetID()); if (!pRequests->HasActiveRequest()) { LeaderheadAnimationTypes eAnimation = LEADERHEAD_ANIM_NEUTRAL_HELLO; const char* szText = GetGreetHumanMessage(eAnimation); gDLL->GameplayDiplomacyAILeaderMessage(GetID(), DIPLO_UI_STATE_DEFAULT_ROOT, szText, eAnimation); } } else { LeaderheadAnimationTypes eAnimation = LEADERHEAD_ANIM_NEUTRAL_HELLO; const char* szText = GetGreetHumanMessage(eAnimation); gDLL->GameplayDiplomacyAILeaderMessage(GetID(), DIPLO_UI_STATE_DEFAULT_ROOT, szText, eAnimation); } } } /// Initiate diplo screen after the player has clicked on the notification to threaten the thief. void CvDiplomacyAI::DoBeginDiploWithHumanEspionageResult() { if(!GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR) && !IsAtWar(GC.getGame().getActivePlayer())) { LeaderheadAnimationTypes eAnimation = LEADERHEAD_ANIM_NEUTRAL_HELLO; const char* szText = GetDiploStringForMessage(DIPLO_MESSAGE_CONFRONT_YOU_KILLED_MY_SPY, GC.getGame().getActivePlayer()); gDLL->GameplayDiplomacyAILeaderMessage(GetID(), DIPLO_UI_STATE_CONFRONT_YOU_KILLED_MY_SPY, szText, eAnimation); } } /// Initiate diplo screen after the player has clicked on the notification that involves intrigue void CvDiplomacyAI::DoBeginDiploWithHumanInDiscuss() { if(!GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR)) { LeaderheadAnimationTypes eAnimation = LEADERHEAD_ANIM_NEUTRAL_HELLO; const char* szText = GetGreetHumanMessage(eAnimation); gDLL->GameplayDiplomacyAILeaderMessage(GetID(), DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED, szText, eAnimation); } } /// What should an AI leader say for a particular situation? const char* CvDiplomacyAI::GetDiploStringForMessage(DiploMessageTypes eDiploMessage, PlayerTypes eForPlayer) { return GetDiploStringForMessage(eDiploMessage, eForPlayer, Localization::String::Empty, Localization::String::Empty); } /// What should an AI leader say for a particular situation? const char* CvDiplomacyAI::GetDiploStringForMessage(DiploMessageTypes eDiploMessage, PlayerTypes eForPlayer, const Localization::String& strOptionalKey1) { return GetDiploStringForMessage(eDiploMessage, eForPlayer, strOptionalKey1, Localization::String::Empty); } const char* CvDiplomacyAI::GetDiploStringForMessage(DiploMessageTypes eDiploMessage, PlayerTypes eForPlayer, const Localization::String& strOptionalKey1, const Localization::String& strOptionalKey2) { ASSERT(eDiploMessage >= 0, "DIPLOMACY_AI: Invalid DiploMessageType."); PRECONDITION(eDiploMessage < NUM_DIPLO_MESSAGE_TYPES, "DIPLOMACY_AI: Invalid DiploMessageType."); PRECONDITION(eForPlayer >= NO_PLAYER, "DIPLOMACY_AI: Invalid Player Index."); // NO_PLAYER is valid because eForPlayer is used when we need specific data (e.g. for declaring war) PRECONDITION(eForPlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); EraTypes eCurrentEra = GC.getGame().getCurrentEra(); int iMessage = 0; const char* strText = NULL; switch(eDiploMessage) { ////////////////////////////////////////////////////////////// // AI greeting messages ////////////////////////////////////////////////////////////// // Intro case DIPLO_MESSAGE_INTRO: strText = GetDiploTextFromTag("RESPONSE_FIRST_GREETING"); break; // Defeated case DIPLO_MESSAGE_DEFEATED: strText = GetDiploTextFromTag("RESPONSE_DEFEATED"); break; // Repeat Hello (after the player has gone into the screen too many times) case DIPLO_MESSAGE_GREETING_REPEAT_TOO_MUCH: strText = GetDiploTextFromTag("RESPONSE_GREETING_REPEAT_TOO_MUCH"); break; // Repeat Hello case DIPLO_MESSAGE_GREETING_REPEAT: strText = GetDiploTextFromTag("RESPONSE_GREETING_REPEAT"); break; // Repeat Hello Hostile case DIPLO_MESSAGE_GREETING_HOSTILE_REPEAT: strText = GetDiploTextFromTag("RESPONSE_GREETING_HOSTILE_REPEAT"); break; // Polite Hello case DIPLO_MESSAGE_GREETING_FRIENDLY_HELLO: strText = GetDiploTextFromTag("RESPONSE_GREETING_POLITE_HELLO"); break; // Neutral Hello case DIPLO_MESSAGE_GREETING_NEUTRAL_HELLO: strText = GetDiploTextFromTag("RESPONSE_GREETING_NEUTRAL_HELLO"); break; // Hostile Hello case DIPLO_MESSAGE_GREETING_HOSTILE_HELLO: strText = GetDiploTextFromTag("RESPONSE_GREETING_HOSTILE_HELLO"); break; // Greeting: Human is crushing this AI case DIPLO_MESSAGE_GREETING_DESTRUCTION_LOOMS: strText = GetDiploTextFromTag("RESPONSE_GREETING_DESTRUCTION_LOOMS"); break; // Greeting: At War with human but wants peace case DIPLO_MESSAGE_GREETING_AT_WAR_WANTS_PEACE: strText = GetDiploTextFromTag("RESPONSE_GREETING_AT_WAR_WANTS_PEACE"); break; // Greeting: At War with human and is being hostile case DIPLO_MESSAGE_GREETING_AT_WAR_HOSTILE: strText = GetDiploTextFromTag("RESPONSE_GREETING_AT_WAR_HOSTILE"); break; // Greeting: At War with human and now ready to accept surrender case DIPLO_MESSAGE_GREETING_WILL_ACCEPT_SURRENDER: strText = GetDiploTextFromTag("RESPONSE_GREETING_WILL_ACCEPT_SURRENDER"); break; // Greeting: Research Agreement case DIPLO_MESSAGE_GREETING_RESEARCH_AGREEMENT: strText = GetDiploTextFromTag("RESPONSE_GREETING_RESEARCH_AGREEMENT"); break; // Greeting: Human has broken military promises case DIPLO_MESSAGE_GREETING_BROKEN_MILITARY_PROMISE: strText = GetDiploTextFromTag("RESPONSE_GREETING_BROKEN_MILITARY_PROMISE"); break; // Greeting: Players are working together case DIPLO_MESSAGE_GREETING_WORKING_WITH: strText = GetDiploTextFromTag("RESPONSE_GREETING_WORKING_WITH"); break; // Greeting: Players are working together case DIPLO_MESSAGE_GREETING_WORKING_AGAINST: strText = GetDiploTextFromTag("RESPONSE_GREETING_WORKING_AGAINST", strOptionalKey1); break; // Greeting: Players in (or planning) a coop war against a third player case DIPLO_MESSAGE_GREETING_COOP_WAR: strText = GetDiploTextFromTag("RESPONSE_GREETING_COOP_WAR", strOptionalKey1); break; // Greeting: Human at war Hostile case DIPLO_MESSAGE_GREETING_HOSTILE_HUMAN_AT_WAR: strText = GetDiploTextFromTag("RESPONSE_GREETING_HOSTILE_HUMAN_AT_WAR"); break; // Greeting: Human at war case DIPLO_MESSAGE_GREETING_HUMAN_AT_WAR: strText = GetDiploTextFromTag("RESPONSE_GREETING_HUMAN_AT_WAR"); break; // Greeting: Human aggressive military (hostile) case DIPLO_MESSAGE_GREETING_HOSTILE_AGGRESSIVE_MILITARY: strText = GetDiploTextFromTag("RESPONSE_GREETING_HOSTILE_AGGRESSIVE_MILITARY"); break; // Greeting: Human aggressive military case DIPLO_MESSAGE_GREETING_AGGRESSIVE_MILITARY: strText = GetDiploTextFromTag("RESPONSE_GREETING_AGGRESSIVE_MILITARY"); break; // Greeting: Human aggressive expansion (hostile) case DIPLO_MESSAGE_GREETING_HOSTILE_AGGRESSIVE_EXPANSION: strText = GetDiploTextFromTag("RESPONSE_GREETING_HOSTILE_AGGRESSIVE_EXPANSION"); break; // Greeting: Human aggressive expansion case DIPLO_MESSAGE_GREETING_AGGRESSIVE_EXPANSION: strText = GetDiploTextFromTag("RESPONSE_GREETING_AGGRESSIVE_EXPANSION"); break; // Greeting: Human aggressive plot buying (hostile) case DIPLO_MESSAGE_GREETING_HOSTILE_AGGRESSIVE_PLOT_BUYING: strText = GetDiploTextFromTag("RESPONSE_GREETING_HOSTILE_AGGRESSIVE_PLOT_BUYING"); break; // Greeting: Human aggressive plot buying case DIPLO_MESSAGE_GREETING_AGGRESSIVE_PLOT_BUYING: strText = GetDiploTextFromTag("RESPONSE_GREETING_AGGRESSIVE_PLOT_BUYING"); break; // Greeting: Human has a strong military case DIPLO_MESSAGE_GREETING_FRIENDLY_STRONG_MILITARY: strText = GetDiploTextFromTag("RESPONSE_GREETING_FRIENDLY_STRONG_MILITARY"); break; // Greeting: Human has a strong economy case DIPLO_MESSAGE_GREETING_FRIENDLY_STRONG_ECONOMY: strText = GetDiploTextFromTag("RESPONSE_GREETING_FRIENDLY_STRONG_ECONOMY"); break; // Greeting: Human has few Cities case DIPLO_MESSAGE_GREETING_HOSTILE_HUMAN_FEW_CITIES: strText = GetDiploTextFromTag("RESPONSE_GREETING_HOSTILE_HUMAN_FEW_CITIES"); break; // Greeting: Human has a small army case DIPLO_MESSAGE_GREETING_HOSTILE_HUMAN_SMALL_ARMY: strText = GetDiploTextFromTag("RESPONSE_GREETING_HOSTILE_HUMAN_SMALL_ARMY"); break; // Greeting: Human beats up people case DIPLO_MESSAGE_GREETING_HOSTILE_HUMAN_IS_WARMONGER: strText = GetDiploTextFromTag("RESPONSE_GREETING_HOSTILE_HUMAN_IS_WARMONGER"); break; ////////////////////////////////////////////////////////////// // AI has a trade offer for the human ////////////////////////////////////////////////////////////// // AI asks the player what deal he's offering case DIPLO_MESSAGE_DOT_DOT_DOT: strText = GetDiploTextFromTag("RESPONSE_DOT_DOT_DOT"); break; // AI asks the player what deal he's offering case DIPLO_MESSAGE_LETS_HEAR_IT: strText = GetDiploTextFromTag("RESPONSE_LETS_HEAR_IT"); break; // AI asking the Human to make peace case DIPLO_MESSAGE_PEACE_OFFER: strText = GetDiploTextFromTag("RESPONSE_PEACE_OFFER"); break; case DIPLO_MESSAGE_WINNER_PEACE_OFFER: strText = GetDiploTextFromTag("RESPONSE_WINNER_PEACE_OFFER"); break; // AI is making a demand case DIPLO_MESSAGE_DEMAND: strText = GetDiploTextFromTag("RESPONSE_DEMAND"); break; // AI is making a request case DIPLO_MESSAGE_REQUEST: strText = GetDiploTextFromTag("RESPONSE_REQUEST"); break; // AI wants a Luxury someone has case DIPLO_MESSAGE_LUXURY_TRADE: strText = GetDiploTextFromTag("RESPONSE_LUXURY_TRADE"); break; // AI offers equal embassy exchange agreement case DIPLO_MESSAGE_EMBASSY_EXCHANGE: strText = GetDiploTextFromTag("RESPONSE_EMBASSY_EXCHANGE"); break; // AI asks for an embassy case DIPLO_MESSAGE_EMBASSY_OFFER: strText = GetDiploTextFromTag("RESPONSE_EMBASSY_OFFER"); break; // AI offers equal Open Borders agreement case DIPLO_MESSAGE_OPEN_BORDERS_EXCHANGE: strText = GetDiploTextFromTag("RESPONSE_OPEN_BORDERS_EXCHANGE"); break; // AI asks for Open Borders case DIPLO_MESSAGE_OPEN_BORDERS_OFFER: strText = GetDiploTextFromTag("RESPONSE_OPEN_BORDERS_OFFER"); break; // AI wants RA with player case DIPLO_MESSAGE_RESEARCH_AGREEMENT_OFFER: strText = GetDiploTextFromTag("RESPONSE_RESEARCH_AGREEMENT_OFFER"); break; // AI wants to renew an expired deal with player case DIPLO_MESSAGE_RENEW_DEAL: strText = GetDiploTextFromTag("RESPONSE_RENEW_DEAL"); break; // AI wants to renew an expired deal with player, but they need more in exchange case DIPLO_MESSAGE_WANT_MORE_RENEW_DEAL: strText = GetDiploTextFromTag("RESPONSE_WANT_MORE_RENEW_DEAL"); break; ////////////////////////////////////////////////////////////// // Generic AI messages to another player; some friendship, some warnings, etc. ////////////////////////////////////////////////////////////// // AI is warning player about his military placement case DIPLO_MESSAGE_HOSTILE_AGGRESSIVE_MILITARY_WARNING: strText = GetDiploTextFromTag("RESPONSE_HOSTILE_AGGRESSIVE_MILITARY_WARNING"); break; // AI is warning player about his military placement case DIPLO_MESSAGE_AGGRESSIVE_MILITARY_WARNING: strText = GetDiploTextFromTag("RESPONSE_AGGRESSIVE_MILITARY_WARNING"); break; // AI is SERIOUSLY warning player about his expansion case DIPLO_MESSAGE_EXPANSION_SERIOUS_WARNING: strText = GetDiploTextFromTag("RESPONSE_EXPANSION_SERIOUS_WARNING"); break; // AI is warning player about his expansion case DIPLO_MESSAGE_EXPANSION_WARNING: strText = GetDiploTextFromTag("RESPONSE_EXPANSION_WARNING"); break; // AI is telling the player he broke a promise case DIPLO_MESSAGE_EXPANSION_BROKEN_PROMISE: strText = GetDiploTextFromTag("RESPONSE_EXPANSION_BROKEN_PROMISE"); break; // AI is SERIOUSLY warning player about his plot buying case DIPLO_MESSAGE_PLOT_BUYING_SERIOUS_WARNING: strText = GetDiploTextFromTag("RESPONSE_PLOT_BUYING_SERIOUS_WARNING"); break; // AI is warning player about his plot buying case DIPLO_MESSAGE_PLOT_BUYING_WARNING: strText = GetDiploTextFromTag("RESPONSE_PLOT_BUYING_WARNING"); break; // AI is telling the player he broke a promise case DIPLO_MESSAGE_PLOT_BUYING_BROKEN_PROMISE: strText = GetDiploTextFromTag("RESPONSE_PLOT_BUYING_BROKEN_PROMISE"); break; // AI attacked a Minor the human has Friendship with (hostile) case DIPLO_MESSAGE_HOSTILE_WE_ATTACKED_YOUR_MINOR: strText = GetDiploTextFromTag("RESPONSE_HOSTILE_WE_ATTACKED_YOUR_MINOR", strOptionalKey1); break; // AI attacked a Minor the human has Friendship with case DIPLO_MESSAGE_WE_ATTACKED_YOUR_MINOR: strText = GetDiploTextFromTag("RESPONSE_WE_ATTACKED_YOUR_MINOR", strOptionalKey1); break; // AI bullied a Minor the human has a PtP with (hostile) case DIPLO_MESSAGE_HOSTILE_WE_BULLIED_YOUR_MINOR: strText = GetDiploTextFromTag("RESPONSE_HOSTILE_WE_BULLIED_YOUR_MINOR", strOptionalKey1); break; // AI bullied a Minor the human has a PtP with case DIPLO_MESSAGE_WE_BULLIED_YOUR_MINOR: strText = GetDiploTextFromTag("RESPONSE_WE_BULLIED_YOUR_MINOR", strOptionalKey1); break; // AI would like to work with a player case DIPLO_MESSAGE_WORK_WITH_US: strText = GetDiploTextFromTag("RESPONSE_WORK_WITH_US"); break; // AI wants a Strategic Resource that someone has case DIPLO_MESSAGE_STRATEGIC_TRADE: strText = GetDiploTextFromTag("RESPONSE_STRATEGIC_TRADE"); break; // AI would like to work with a player they do not trust case DIPLO_MESSAGE_DOF_UNTRUSTWORTHY: strText = GetDiploTextFromTag("RESPONSE_DOF_UNTRUSTWORTHY"); break; // AI would like to work with a player they've been friends with case DIPLO_MESSAGE_DOF_FRIENDS: strText = GetDiploTextFromTag("RESPONSE_DOF_FRIENDS"); break; // AI would like to work with a player they've been good friends with case DIPLO_MESSAGE_DOF_OLD_FRIENDS: strText = GetDiploTextFromTag("RESPONSE_DOF_ALLIES"); break; // AI would like to work with a player they went on a coop war with case DIPLO_MESSAGE_DOF_BATTLE_BROTHERS: strText = GetDiploTextFromTag("RESPONSE_DOF_BATTLE_BROTHERS"); break; // AI is done working with a player case DIPLO_MESSAGE_END_WORK_WITH_US: strText = GetEndDoFMessage(eForPlayer); break; // AI would like to work against someone with a player case DIPLO_MESSAGE_WORK_AGAINST_SOMEONE: iMessage = GetDenounceMessage(eForPlayer); strText = GetDenounceMessageValue(iMessage); break; // AI is done working with a player against someone case DIPLO_MESSAGE_END_WORK_AGAINST_SOMEONE: strText = GetEndWorkAgainstSomeoneMessage(eForPlayer, strOptionalKey1); break; // AI would like to declare war on someone with a player case DIPLO_MESSAGE_COOP_WAR_REQUEST: strText = GetDiploTextFromTag("RESPONSE_COOP_WAR_REQUEST", strOptionalKey1); break; /* // AI calls up and says it's time to declare war on someone with a player case DIPLO_MESSAGE_COOP_WAR_TIME: strText = GetDiploTextFromTag("RESPONSE_COOP_WAR_TIME", strOptionalKey1); break; */ // AI is telling player he's unforgivable case DIPLO_MESSAGE_NOW_UNFORGIVABLE: strText = GetDiploTextFromTag("RESPONSE_NOW_UNFORGIVABLE"); break; // AI is telling player he's an enemy case DIPLO_MESSAGE_NOW_ENEMY: strText = GetDiploTextFromTag("RESPONSE_NOW_ENEMY"); break; // AI is making fun of the player case DIPLO_MESSAGE_INSULT_ROOT: strText = GetInsultHumanMessage(); break; // AI is making fun of the player case DIPLO_MESSAGE_INSULT_GENERIC: strText = GetDiploTextFromTag("RESPONSE_INSULT_GENERIC"); break; // AI is making fun of the player because of his military case DIPLO_MESSAGE_INSULT_MILITARY: strText = GetDiploTextFromTag("RESPONSE_INSULT_MILITARY"); break; // AI is making fun of the player because of his lack of nukes case DIPLO_MESSAGE_INSULT_NUKE: strText = GetDiploTextFromTag("RESPONSE_INSULT_NUKE"); break; // AI is making fun of the player because he picks on minors case DIPLO_MESSAGE_INSULT_BULLY: strText = GetDiploTextFromTag("RESPONSE_INSULT_BULLY"); break; // AI is making fun of the player because his people are unhappy case DIPLO_MESSAGE_INSULT_UNHAPPINESS: strText = GetDiploTextFromTag("RESPONSE_INSULT_UNHAPPINESS"); break; // AI is making fun of the player because he doesn't have many Cities case DIPLO_MESSAGE_INSULT_CITIES: strText = GetDiploTextFromTag("RESPONSE_INSULT_CITIES"); break; // AI is making fun of the player because of his population case DIPLO_MESSAGE_INSULT_POPULATION: strText = GetDiploTextFromTag("RESPONSE_INSULT_POPULATION"); break; // AI is making fun of the player because of his Culture case DIPLO_MESSAGE_INSULT_CULTURE: strText = GetDiploTextFromTag("RESPONSE_INSULT_CULTURE"); break; // AI is being nice to the player case DIPLO_MESSAGE_COMPLIMENT: strText = GetDiploTextFromTag("RESPONSE_COMPLIMENT"); break; // AI is afraid of the player case DIPLO_MESSAGE_BOOT_KISSING: strText = GetDiploTextFromTag("RESPONSE_BOOT_KISSING"); break; // AI is warning player about being a warmonger case DIPLO_MESSAGE_WARMONGER: strText = GetDiploTextFromTag("RESPONSE_WARMONGER"); break; // AI is warning player about getting too cozy with a minor case DIPLO_MESSAGE_MINOR_CIV_COMPETITION: strText = GetDiploTextFromTag("RESPONSE_MINOR_CIV_COMPETITION", strOptionalKey1); break; // AI is pleased with the human case DIPLO_MESSAGE_PLEASED: strText = GetDiploTextFromTag("RESPONSE_PLEASED"); break; // AI is thankful towards the human case DIPLO_MESSAGE_THANKFUL: strText = GetDiploTextFromTag("RESPONSE_THANKFUL"); break; // AI is disappointed in the human case DIPLO_MESSAGE_DISAPPOINTED: strText = GetDiploTextFromTag("RESPONSE_DISAPPOINTED"); break; // Human has done something bad, and now we're gonna show 'em case DIPLO_MESSAGE_SO_BE_IT: strText = GetDiploTextFromTag("RESPONSE_SO_BE_IT"); break; // Human returned a captured civilian to us! case DIPLO_MESSAGE_RETURNED_CIVILIAN: strText = GetDiploTextFromTag("RESPONSE_RETURNED_CIVILIAN"); break; // Human Culture Bombed us! case DIPLO_MESSAGE_CULTURE_BOMBED: strText = GetDiploTextFromTag("RESPONSE_CULTURE_BOMBED"); break; ////////////////////////////////////////////////////////////// // AI has a public declaration to make to the world ////////////////////////////////////////////////////////////// // AI is protecting a City-State case DIPLO_MESSAGE_DECLARATION_PROTECT_CITY_STATE: strText = GetDiploTextFromTag("RESPONSE_DECLARATION_PROTECT_CITY_STATE", strOptionalKey1, GetPlayer()->getCivilizationShortDescriptionKey()); break; // AI is no longer protecting a City-State case DIPLO_MESSAGE_DECLARATION_ABANDON_CITY_STATE: strText = GetDiploTextFromTag("RESPONSE_DECLARATION_ABANDON_CITY_STATE", strOptionalKey1, GetPlayer()->getCivilizationShortDescriptionKey()); break; ////////////////////////////////////////////////////////////// // Human is asking the AI for something ////////////////////////////////////////////////////////////// // Human has asked us about something recently, and we still say no case DIPLO_MESSAGE_REPEAT_NO: strText = GetDiploTextFromTag("RESPONSE_REPEAT_NO"); break; // Human asks us not to settle near him, and we say yes case DIPLO_MESSAGE_DONT_SETTLE_YES: strText = GetDiploTextFromTag("RESPONSE_DONT_SETTLE_YES"); break; // Human asks us not to settle near him, but we say no case DIPLO_MESSAGE_DONT_SETTLE_NO: strText = GetDiploTextFromTag("RESPONSE_DONT_SETTLE_NO"); break; // Human asks us to work with him, and we say yes case DIPLO_MESSAGE_WORK_WITH_US_YES: strText = GetDiploTextFromTag("RESPONSE_WORK_WITH_US_YES"); break; // Human asks us to work with him, but we say no case DIPLO_MESSAGE_WORK_WITH_US_NO: strText = GetDiploTextFromTag("RESPONSE_WORK_WITH_US_NO"); break; // Human asks us to work against someone, and we say yes case DIPLO_MESSAGE_WORK_AGAINST_SOMEONE_YES: strText = GetDiploTextFromTag("RESPONSE_WORK_AGAINST_SOMEONE_YES"); break; // Human asks us to work against someone, but we say no case DIPLO_MESSAGE_WORK_AGAINST_SOMEONE_NO: strText = GetDiploTextFromTag("RESPONSE_WORK_AGAINST_SOMEONE_NO"); break; // Human asks us to declare war on someone, and we say yes case DIPLO_MESSAGE_COOP_WAR_YES: strText = GetDiploTextFromTag("RESPONSE_COOP_WAR_YES"); break; // Human asks us to declare war on someone, but we say no case DIPLO_MESSAGE_COOP_WAR_NO: strText = GetDiploTextFromTag("RESPONSE_COOP_WAR_NO"); break; // Human asks us to declare war someone, and we say soon case DIPLO_MESSAGE_COOP_WAR_SOON: strText = GetDiploTextFromTag("RESPONSE_COOP_WAR_SOON"); break; // Human makes demand of us, and we say yes case DIPLO_MESSAGE_HUMAN_DEMAND_YES: strText = GetDiploTextFromTag("RESPONSE_HUMAN_DEMAND_YES"); break; // Human makes demand of us, and we say no because they're too weak case DIPLO_MESSAGE_HUMAN_DEMAND_REFUSE_WEAK: strText = GetDiploTextFromTag("RESPONSE_HUMAN_DEMAND_REFUSE_WEAK"); break; // Human makes demand of us, and we say no because we hate them case DIPLO_MESSAGE_HUMAN_DEMAND_REFUSE_HOSTILE: strText = GetDiploTextFromTag("RESPONSE_HUMAN_DEMAND_REFUSE_HOSTILE"); break; // Human makes demand of us, and we tell him he's asking for too much case DIPLO_MESSAGE_HUMAN_DEMAND_REFUSE_TOO_MUCH: strText = GetDiploTextFromTag("RESPONSE_HUMAN_DEMAND_REFUSE_TOO_MUCH"); break; // Human makes demand of us, and we say it's been too soon since last time case DIPLO_MESSAGE_HUMAN_DEMAND_REFUSE_TOO_SOON: strText = GetDiploTextFromTag("RESPONSE_HUMAN_DEMAND_REFUSE_TOO_SOON"); break; // Human makes demand of us, and we say our master will protect us from them case DIPLO_MESSAGE_HUMAN_DEMAND_REFUSE_PROTECTED_BY_MASTER: strText = GetDiploTextFromTag("RESPONSE_HUMAN_DEMAND_REFUSE_PROTECTED_BY_MASTER", strOptionalKey1); break; ////////////////////////////////////////////////////////////// // AI popped up to tell the human something, human responded and now we're responding back ////////////////////////////////////////////////////////////// // We noticed human military buildup and he said he was going to kill us now (hostile) case DIPLO_MESSAGE_HUMAN_HOSTILE_AGGRESSIVE_MILITARY_WARNING_BAD: strText = GetDiploTextFromTag("RESPONSE_HOSTILE_AGGRESSIVE_MILITARY_WARNING_BAD"); break; // We noticed human military buildup and he said betsu ni (hostile) case DIPLO_MESSAGE_HUMAN_HOSTILE_AGGRESSIVE_MILITARY_WARNING_GOOD: strText = GetDiploTextFromTag("RESPONSE_HOSTILE_AGGRESSIVE_MILITARY_WARNING_GOOD"); break; // We noticed human military buildup and he said he was going to kill us now :( case DIPLO_MESSAGE_HUMAN_AGGRESSIVE_MILITARY_WARNING_BAD: strText = GetDiploTextFromTag("RESPONSE_AGGRESSIVE_MILITARY_WARNING_BAD"); break; // We noticed human military buildup and he said betsu ni case DIPLO_MESSAGE_HUMAN_AGGRESSIVE_MILITARY_WARNING_GOOD: strText = GetDiploTextFromTag("RESPONSE_AGGRESSIVE_MILITARY_WARNING_GOOD"); break; // AI attacked human's Minor, human vows revenge, our response (hostile) case DIPLO_MESSAGE_HUMAN_HOSTILE_WE_ATTACKED_MINOR_BAD: strText = GetDiploTextFromTag("RESPONSE_HOSTILE_WE_ATTACKED_MINOR_BAD"); break; // AI attacked human's Minor, human forgives us, our response (hostile) case DIPLO_MESSAGE_HUMAN_HOSTILE_WE_ATTACKED_MINOR_GOOD: strText = GetDiploTextFromTag("RESPONSE_HOSTILE_WE_ATTACKED_MINOR_GOOD"); break; // AI attacked human's Minor, human vows revenge, our response case DIPLO_MESSAGE_HUMAN_WE_ATTACKED_MINOR_BAD: strText = GetDiploTextFromTag("RESPONSE_WE_ATTACKED_MINOR_BAD"); break; // AI attacked human's Minor, human forgives us, our response case DIPLO_MESSAGE_HUMAN_WE_ATTACKED_MINOR_GOOD: strText = GetDiploTextFromTag("RESPONSE_WE_ATTACKED_MINOR_GOOD"); break; // AI bullied human's Minor, human vows revenge, our response (hostile) case DIPLO_MESSAGE_HUMAN_HOSTILE_WE_BULLIED_MINOR_BAD: strText = GetDiploTextFromTag("RESPONSE_HOSTILE_WE_BULLIED_MINOR_BAD"); break; // AI bullied human's Minor, human forgives us, our response (hostile) case DIPLO_MESSAGE_HUMAN_HOSTILE_WE_BULLIED_MINOR_GOOD: strText = GetDiploTextFromTag("RESPONSE_HOSTILE_WE_BULLIED_MINOR_GOOD"); break; // AI bullied human's Minor, human vows revenge, our response case DIPLO_MESSAGE_HUMAN_WE_BULLIED_MINOR_BAD: strText = GetDiploTextFromTag("RESPONSE_WE_BULLIED_MINOR_BAD"); break; // AI bullied human's Minor, human forgives us, our response case DIPLO_MESSAGE_HUMAN_WE_BULLIED_MINOR_GOOD: strText = GetDiploTextFromTag("RESPONSE_WE_BULLIED_MINOR_GOOD"); break; // Other player attacked a City-State this AI is protective of case DIPLO_MESSAGE_HUMAN_ATTACKED_PROTECTED_CITY_STATE: strText = GetDiploTextFromTag("RESPONSE_ATTACKED_PROTECTED_CITY_STATE", strOptionalKey1); break; // AI is upset that human won't stop attacking a Minor case DIPLO_MESSAGE_HUMAN_ATTACKED_MINOR_BAD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_ATTACKED_MINOR_BAD"); break; // AI is happy that human says he'll stop attacking a Minor case DIPLO_MESSAGE_HUMAN_ATTACKED_MINOR_GOOD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_ATTACKED_MINOR_GOOD"); break; // Other player killed a City-State this AI is protective of case DIPLO_MESSAGE_HUMAN_KILLED_PROTECTED_CITY_STATE: strText = GetDiploTextFromTag("RESPONSE_KILLED_PROTECTED_CITY_STATE", strOptionalKey1); break; // AI is upset that human killed a Minor case DIPLO_MESSAGE_HUMAN_KILLED_MINOR_BAD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_KILLED_MINOR_BAD"); break; // AI is willing to receive gift from human to ease relations after killing a protected Minor case DIPLO_MESSAGE_HUMAN_KILLED_MINOR_GOOD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_KILLED_MINOR_GOOD"); break; // Other player bullied a City-State this AI is protective of case DIPLO_MESSAGE_HUMAN_BULLIED_PROTECTED_CITY_STATE: strText = GetDiploTextFromTag("RESPONSE_BULLIED_PROTECTED_CITY_STATE", strOptionalKey1); break; // AI is upset that human won't stop bullying a Minor case DIPLO_MESSAGE_HUMAN_BULLIED_MINOR_BAD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_BULLIED_MINOR_BAD"); break; // AI is happy that human says he'll stop bullying a Minor case DIPLO_MESSAGE_HUMAN_BULLIED_MINOR_GOOD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_BULLIED_MINOR_GOOD"); break; // AI mad at human because he won't heed our serious expansion warning case DIPLO_MESSAGE_HUMAN_SERIOUS_EXPANSION_WARNING_BAD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_SERIOUS_EXPANSION_WARNING_BAD"); break; // AI is willing to receive gift from human to ease relations after serious expansion warning case DIPLO_MESSAGE_HUMAN_SERIOUS_EXPANSION_WARNING_GOOD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_SERIOUS_EXPANSION_WARNING_GOOD"); break; // AI mad at human because he won't heed our expansion warning case DIPLO_MESSAGE_HUMAN_EXPANSION_WARNING_BAD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_EXPANSION_WARNING_BAD"); break; // AI is happy human has said he won't expand near him in the future case DIPLO_MESSAGE_HUMAN_EXPANSION_WARNING_GOOD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_EXPANSION_WARNING_GOOD"); break; // AI mad at human because he won't heed our serious plot buying warning case DIPLO_MESSAGE_HUMAN_SERIOUS_PLOT_BUYING_WARNING_BAD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_SERIOUS_PLOT_BUYING_WARNING_BAD"); break; // AI is willing to receive gift from human to ease relations after serious plot buying warning case DIPLO_MESSAGE_HUMAN_SERIOUS_PLOT_BUYING_WARNING_GOOD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_SERIOUS_PLOT_BUYING_WARNING_GOOD"); break; // AI mad at human because he won't heed our plot buying warning case DIPLO_MESSAGE_HUMAN_PLOT_BUYING_WARNING_BAD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_PLOT_BUYING_WARNING_BAD"); break; // AI is happy human has said he won't buy plots near him in the future case DIPLO_MESSAGE_HUMAN_PLOT_BUYING_WARNING_GOOD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_PLOT_BUYING_WARNING_GOOD"); break; ////////////////////////////////////////////////////////////// // Peace messages - DEPRECATED??? ////////////////////////////////////////////////////////////// // Player requests peace from AI, AI is is curious what the offer will be case DIPLO_MESSAGE_PEACE_WHAT_WILL_HUMAN_OFFER: strText = GetDiploTextFromTag("RESPONSE_PEACE_WHAT_WILL_HUMAN_OFFER"); break; // Player requests peace from AI, AI is happy case DIPLO_MESSAGE_PEACE_MADE_BY_HUMAN_GRACIOUS: strText = GetDiploTextFromTag("RESPONSE_PEACE_MADE_BY_HUMAN_GRACIOUS"); break; // Player requests peace from AI but it says no case DIPLO_MESSAGE_NO_PEACE: strText = GetDiploTextFromTag("RESPONSE_NO_PEACE"); break; // Player requests peace from AI on the same turn it attacked... case DIPLO_MESSAGE_TOO_SOON_NO_PEACE: strText = GetDiploTextFromTag("RESPONSE_TOO_SOON_NO_PEACE"); break; ////////////////////////////////////////////////////////////// // Trade responses ////////////////////////////////////////////////////////////// // Human asks the AI to accept a bad deal too many times case DIPLO_MESSAGE_REPEAT_TRADE_TOO_MUCH: strText = GetDiploTextFromTag("RESPONSE_REPEAT_TRADE_TOO_MUCH"); break; // Human asks the AI to accept a bad deal again case DIPLO_MESSAGE_REPEAT_TRADE: strText = GetDiploTextFromTag("RESPONSE_REPEAT_TRADE"); break; // AI accepts a generous trade offer case DIPLO_MESSAGE_TRADE_ACCEPT_GENEROUS: strText = GetDiploTextFromTag("RESPONSE_TRADE_ACCEPT_GENEROUS"); break; // AI accepts a reasonable trade offer case DIPLO_MESSAGE_TRADE_ACCEPT_ACCEPTABLE: { if (eForPlayer != NO_PLAYER && IsOfferedGift(eForPlayer)) { SetOfferedGift(eForPlayer, false); SetOfferingGift(eForPlayer, false); strText = GetDiploTextFromTag("RESPONSE_PLEASED"); } else { strText = GetDiploTextFromTag("RESPONSE_TRADE_ACCEPT_ACCEPTABLE"); } break; } // Human gave in to AI demand case DIPLO_MESSAGE_TRADE_ACCEPT_AI_DEMAND: strText = GetDiploTextFromTag("RESPONSE_TRADE_ACCEPT_AI_DEMAND"); break; // AI accepts concessions from the human case DIPLO_MESSAGE_TRADE_ACCEPT_HUMAN_CONCESSIONS: strText = GetDiploTextFromTag("RESPONSE_TRADE_ACCEPT_HUMAN_CONCESSIONS"); break; // AI rejects an unreasonable trade offer case DIPLO_MESSAGE_TRADE_REJECT_UNACCEPTABLE: strText = GetDiploTextFromTag("RESPONSE_TRADE_REJECT_UNACCEPTABLE"); break; // AI rejects an insulting trade offer case DIPLO_MESSAGE_TRADE_REJECT_INSULTING: strText = GetDiploTextFromTag("RESPONSE_TRADE_REJECT_INSULTING"); break; // AI sees a good deal it doesn't need to change case DIPLO_MESSAGE_TRADE_DEAL_UNCHANGED: strText = GetDiploTextFromTag("RESPONSE_TRADE_DEAL_UNCHANGED"); break; // AI offers a counter-deal case DIPLO_MESSAGE_TRADE_AI_MAKES_OFFER: strText = GetOfferText(eForPlayer); break; // AI doesn't see a way to make proposed deal work case DIPLO_MESSAGE_TRADE_NO_DEAL_POSSIBLE: strText = GetDiploTextFromTag("RESPONSE_TRADE_NO_DEAL_POSSIBLE"); break; // AI can't fairly match human's trade offer case DIPLO_MESSAGE_TRADE_CANT_MATCH_OFFER: strText = GetDiploTextFromTag("RESPONSE_TRADE_CANT_MATCH_OFFER"); break; ////////////////////////////////////////////////////////////// // Human declared war on AI, what is the AI's response? ////////////////////////////////////////////////////////////// // AI attacked by human player case DIPLO_MESSAGE_ATTACKED_ROOT: strText = GetAttackedByHumanMessage(); break; // Attacked: Hostile response case DIPLO_MESSAGE_ATTACKED_HOSTILE: strText = GetDiploTextFromTag("RESPONSE_ATTACKED_HOSTILE"); break; // Attacked: Hostile response (Weak player) case DIPLO_MESSAGE_ATTACKED_WEAK_HOSTILE: strText = GetDiploTextFromTag("RESPONSE_ATTACKED_WEAK_HOSTILE"); break; // Attacked: Hostile response (Strong player) case DIPLO_MESSAGE_ATTACKED_STRONG_HOSTILE: strText = GetDiploTextFromTag("RESPONSE_ATTACKED_STRONG_HOSTILE"); break; // Attacked: Excited response case DIPLO_MESSAGE_ATTACKED_EXCITED: strText = GetDiploTextFromTag("RESPONSE_ATTACKED_EXCITED"); break; // Attacked: Excited response (Weak Player) case DIPLO_MESSAGE_ATTACKED_WEAK_EXCITED: strText = GetDiploTextFromTag("RESPONSE_ATTACKED_WEAK_EXCITED"); break; // Attacked: Excited response (Strong player) case DIPLO_MESSAGE_ATTACKED_STRONG_EXCITED: strText = GetDiploTextFromTag("RESPONSE_ATTACKED_STRONG_EXCITED"); break; // Attacked: Sad response case DIPLO_MESSAGE_ATTACKED_SAD: strText = GetDiploTextFromTag("RESPONSE_ATTACKED_SAD"); break; // Attacked: Betrayed response case DIPLO_MESSAGE_ATTACKED_BETRAYED: strText = GetDiploTextFromTag("RESPONSE_ATTACKED_BETRAYED"); break; // Attacked: Broken military promise response case DIPLO_MESSAGE_ATTACKED_MILITARY_PROMISE_BROKEN: strText = GetDiploTextFromTag("RESPONSE_ATTACKED_MILITARY_PROMISE_BROKEN"); break; // Attacked: Human is warmonger and AI is stronger case DIPLO_MESSAGE_ATTACKED_STRONG_WARMONGER: strText = GetDiploTextFromTag("RESPONSE_ATTACKED_STRONG_WARMONGER"); break; // Attacked: Human is warmonger and AI is weaker case DIPLO_MESSAGE_ATTACKED_WEAK_WARMONGER: strText = GetDiploTextFromTag("RESPONSE_ATTACKED_WEAK_WARMONGER"); break; // Attacked: Human is warmonger case DIPLO_MESSAGE_ATTACKED_WARMONGER: strText = GetDiploTextFromTag("RESPONSE_ATTACKED_WARMONGER"); break; // Attacked: Human and AI have different ideologies case DIPLO_MESSAGE_ATTACKED_IDEOLOGY_DIFFERENCE: strText = GetDiploTextFromTag("RESPONSE_ATTACKED_IDEOLOGY_DIFFERENCE"); break; // Attacked: Human and AI have the same ideology case DIPLO_MESSAGE_ATTACKED_IDEOLOGY_SAME: strText = GetDiploTextFromTag("RESPONSE_ATTACKED_IDEOLOGY_SAME"); break; // AI DP request case DIPLO_MESSAGE_DEFENSE_PACT_OFFER: strText = GetDiploTextFromTag("RESPONSE_DEFENSIVE_PACT_REQUEST"); break; // Trade Cities request case DIPLO_MESSAGE_TRADE_CITIES_OFFER: strText = GetDiploTextFromTag("RESPONSE_TRADE_CITIES_REQUEST"); break; // Exchange Cities request case DIPLO_MESSAGE_EXCHANGE_CITIES_OFFER: strText = GetDiploTextFromTag("RESPONSE_EXCHANGE_CITIES_REQUEST"); break; // Third party peace case DIPLO_MESSAGE_THIRDPARTY_PEACE_OFFER: strText = GetDiploTextFromTag("RESPONSE_THIRD_PARTY_PEACE_REQUEST"); break; // Third party war case DIPLO_MESSAGE_THIRDPARTY_WAR_OFFER: strText = GetDiploTextFromTag("RESPONSE_THIRD_PARTY_WAR_REQUEST"); break; // World Congress vote case DIPLO_MESSAGE_VOTE_OFFER: strText = GetDiploTextFromTag("RESPONSE_VOTE_COMMITMENT_REQUEST"); break; ////////////////////////////////////////////////////////////// // AI is declaring war on human, what does he say? ////////////////////////////////////////////////////////////// // AI declaring war on human case DIPLO_MESSAGE_DOW_ROOT: strText = GetWarMessage(eForPlayer); break; // AI DoW: Generic declaration case DIPLO_MESSAGE_DOW_GENERIC: strText = GetDiploTextFromTag("RESPONSE_DOW_GENERIC"); break; // AI DoW: Declared war over land dispute case DIPLO_MESSAGE_DOW_LAND: strText = GetDiploTextFromTag("RESPONSE_DOW_LAND"); break; // AI DoW: AI and human have gone to war several times case DIPLO_MESSAGE_DOW_OLD_ENEMIES: strText = GetDiploTextFromTag("RESPONSE_DOW_OLD_ENEMIES"); break; // AI DoW: AI is putting an end to the human's warmongering case DIPLO_MESSAGE_DOW_WARMONGER: strText = GetDiploTextFromTag("RESPONSE_DOW_WARMONGER"); break; // AI DoW: AI and human have different ideologies case DIPLO_MESSAGE_DOW_IDEOLOGY_DIFFERENCE: strText = GetDiploTextFromTag("RESPONSE_DOW_IDEOLOGY_DIFFERENCE"); break; // AI DoW: AI and human have the same ideology case DIPLO_MESSAGE_DOW_IDEOLOGY_SAME: strText = GetDiploTextFromTag("RESPONSE_DOW_IDEOLOGY_SAME"); break; // AI DoW: AI is going for world conquest case DIPLO_MESSAGE_DOW_WORLD_CONQUEST: strText = GetDiploTextFromTag("RESPONSE_DOW_WORLD_CONQUEST"); break; // AI DoW: AI is attacking because the player is weak case DIPLO_MESSAGE_DOW_OPPORTUNITY: strText = GetDiploTextFromTag("RESPONSE_DOW_OPPORTUNITY"); break; // AI DoW: AI is attacking out of desperation case DIPLO_MESSAGE_DOW_DESPERATE: strText = GetDiploTextFromTag("RESPONSE_DOW_DESPERATE"); break; // AI DoW: AI was pretending to be friendly beforehand case DIPLO_MESSAGE_DOW_BETRAYAL: strText = GetDiploTextFromTag("RESPONSE_DOW_BETRAYAL"); break; // AI DoW: AI was pretending to be friendly beforehand AND human is weaker case DIPLO_MESSAGE_DOW_WEAK_BETRAYAL: strText = GetDiploTextFromTag("RESPONSE_DOW_WEAK_BETRAYAL"); break; // AI DoW: AI regrets declaring war case DIPLO_MESSAGE_DOW_REGRET: strText = GetDiploTextFromTag("RESPONSE_DOW_REGRET"); break; // AI declares war because human refused to give in to a demand case DIPLO_MESSAGE_WAR_DEMAND_REFUSED: strText = GetDiploTextFromTag("RESPONSE_WAR_DEMAND_REFUSED"); break; // AI declares war because human insulted them. case DIPLO_MESSAGE_WAR_RUDE_INSULT: strText = GetDiploTextFromTag("RESPONSE_WAR_DEMAND_INSULTED"); break; ///////////////////////////////// // Post Civ 5 Release Mish-Mash of stuff ///////////////////////////////// // AI asks a friend to denounce another player case DIPLO_MESSAGE_DOF_AI_DENOUNCE_REQUEST: strText = GetDiploTextFromTag("RESPONSE_DOF_AI_DENOUNCE_REQUEST", strOptionalKey1); break; // AI asks a friend to declare war on another player case DIPLO_MESSAGE_DOF_AI_WAR_REQUEST: strText = GetDiploTextFromTag("RESPONSE_DOF_AI_WAR_REQUEST", strOptionalKey1); break; // AI asked a friend to do something, but they didn't and now the AI is pissed case DIPLO_MESSAGE_DOF_NOT_HONORED: strText = GetDiploTextFromTag("RESPONSE_DOF_NOT_HONORED"); break; // AI is denouncing the human, who had been a friend case DIPLO_MESSAGE_AI_DOF_BACKSTAB: strText = GetDiploTextFromTag("RESPONSE_AI_DOF_BACKSTAB"); break; // AI response to being denounced by the human case DIPLO_MESSAGE_RESPONSE_TO_BEING_DENOUNCED: strText = GetDiploTextFromTag("RESPONSE_RESPONSE_TO_BEING_DENOUNCED"); break; // AI response to human DoFing a friend case DIPLO_MESSAGE_HUMAN_DOFED_FRIEND: strText = GetDiploTextFromTag("RESPONSE_HUMAN_DOFED_FRIEND", strOptionalKey1); break; // AI response to human DoFing an enemy case DIPLO_MESSAGE_HUMAN_DOFED_ENEMY: strText = GetDiploTextFromTag("RESPONSE_HUMAN_DOFED_ENEMY", strOptionalKey1); break; // AI response to human denouncing a friend case DIPLO_MESSAGE_HUMAN_DENOUNCED_FRIEND: strText = GetDiploTextFromTag("RESPONSE_HUMAN_DENOUNCED_FRIEND", strOptionalKey1); break; // AI response to human denouncing an enemy case DIPLO_MESSAGE_HUMAN_DENOUNCED_ENEMY: strText = GetDiploTextFromTag("RESPONSE_HUMAN_DENOUNCED_ENEMY", strOptionalKey1); break; // AI DoFed someone because the human DoFed them (nice) case DIPLO_MESSAGE_HUMAN_DOF_SO_AI_DOF: strText = GetDiploTextFromTag("RESPONSE_HUMAN_DOF_SO_AI_DOF", strOptionalKey1); break; // AI denounced someone because the human denounced them (nice) case DIPLO_MESSAGE_HUMAN_DENOUNCE_SO_AI_DENOUNCE: strText = GetDiploTextFromTag("RESPONSE_HUMAN_DENOUNCE_SO_AI_DENOUNCE", strOptionalKey1); break; // AI denounced someone because the human DoFed them (mean) case DIPLO_MESSAGE_HUMAN_DOF_SO_AI_DENOUNCE: strText = GetDiploTextFromTag("RESPONSE_HUMAN_DOF_SO_AI_DENOUNCE", strOptionalKey1); break; // AI DoFed someone because the human denounced them (mean) case DIPLO_MESSAGE_HUMAN_DENOUNCE_SO_AI_DOF: strText = GetDiploTextFromTag("RESPONSE_HUMAN_DENOUNCE_SO_AI_DOF", strOptionalKey1); break; // Saying hello - AI denounced the human case DIPLO_MESSAGE_GREETING_DENOUNCED_BY_AI: strText = GetDiploTextFromTag("RESPONSE_GREETING_DENOUNCED_BY_AI"); break; // Saying hello - human denounced the AI case DIPLO_MESSAGE_GREETING_DENOUNCED_AI: strText = GetDiploTextFromTag("RESPONSE_GREETING_DENOUNCED_AI"); break; // Saying hello - Human has a DoF with an enemy of the AI's case DIPLO_MESSAGE_GREETING_OUR_DOF_WITH_AI_ENEMY: strText = GetDiploTextFromTag("RESPONSE_GREETING_OUR_DOF_WITH_ENEMY_OF_AI", strOptionalKey1); break; // Saying hello - Human has a DoF with a friend of the AI's case DIPLO_MESSAGE_GREETING_OUR_DOF_WITH_AI_FRIEND: strText = GetDiploTextFromTag("RESPONSE_GREETING_OUR_DOF_WITH_FRIEND_OF_AI", strOptionalKey1); break; // Saying hello - Human has denounced a friend of the AI's case DIPLO_MESSAGE_GREETING_DENOUNCED_AI_FRIEND: strText = GetDiploTextFromTag("RESPONSE_GREETING_DENOUNCED_FRIEND_OF_AI", strOptionalKey1); break; // Saying hello - Human has denounced an enemy of the AI's case DIPLO_MESSAGE_GREETING_DENOUNCED_AI_ENEMY: strText = GetDiploTextFromTag("RESPONSE_GREETING_DENOUNCED_ENEMY_OF_AI", strOptionalKey1); break; // Human asked for a DoF, but the AI hasn't known him for long enough case DIPLO_MESSAGE_TOO_SOON_FOR_DOF: strText = GetDiploTextFromTag("RESPONSE_TOO_SOON_FOR_DOF"); break; // Same late game policy tree case DIPLO_MESSAGE_SAME_POLICIES_FREEDOM: strText = GetDiploTextFromTag("RESPONSE_SAME_POLICIES_FREEDOM"); break; case DIPLO_MESSAGE_SAME_POLICIES_ORDER: strText = GetDiploTextFromTag("RESPONSE_SAME_POLICIES_ORDER"); break; case DIPLO_MESSAGE_SAME_POLICIES_AUTOCRACY: strText = GetDiploTextFromTag("RESPONSE_SAME_POLICIES_AUTOCRACY"); break; ///////////////////////////////// // Espionage messages ///////////////////////////////// // AI response to catching one of human's spies case DIPLO_MESSAGE_CAUGHT_YOUR_SPY: strText = GetDiploTextFromTag("RESPONSE_CAUGHT_YOUR_SPY"); break; // AI response to killing one of human's spies case DIPLO_MESSAGE_KILLED_YOUR_SPY: strText = GetDiploTextFromTag("RESPONSE_KILLED_YOUR_SPY"); break; // AI response to human killing one of their spies case DIPLO_MESSAGE_KILLED_MY_SPY: strText = GetDiploTextFromTag("RESPONSE_KILLED_MY_SPY"); break; // Human confronts AI about having killed their spy through the notification case DIPLO_MESSAGE_CONFRONT_YOU_KILLED_MY_SPY: strText = GetDiploTextFromTag("RESPONSE_CONFRONT_YOU_KILLED_MY_SPY"); break; // AI asks human to stop sending missionaries and prophets case DIPLO_MESSAGE_STOP_CONVERSIONS: strText = GetDiploTextFromTag("RESPONSE_STOP_CONVERSIONS"); break; // Human agreed to stop spying on AI case DIPLO_MESSAGE_HUMAN_CAUGHT_YOUR_SPY_GOOD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_CAUGHT_YOUR_SPY_GOOD"); break; // Human did not agree to stop spying on AI case DIPLO_MESSAGE_HUMAN_CAUGHT_YOUR_SPY_BAD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_CAUGHT_YOUR_SPY_BAD"); break; // Human asks us not to spy on him, and we say yes case DIPLO_MESSAGE_STOP_SPYING_YES: strText = GetDiploTextFromTag("RESPONSE_STOP_SPYING_YES"); break; // Human asks us not to spy on him, but we say no case DIPLO_MESSAGE_STOP_SPYING_NO: strText = GetDiploTextFromTag("RESPONSE_STOP_SPYING_NO"); break; // Human warns AI about another civ's war plans/deception towards them case DIPLO_MESSAGE_WARNED_ABOUT_INTRIGUE: strText = GetDiploTextFromTag("RESPONSE_WARNED_ABOUT_INTRIGUE", strOptionalKey1); break; // AI warns human that another leader is plotting against them case DIPLO_MESSAGE_SHARE_INTRIGUE: strText = GetDiploTextFromTag("RESPONSE_SHARE_INTRIGUE_DECEPTION", strOptionalKey1); break; // AI warns human that another civ is planning a sneak attack against a specific city of theirs case DIPLO_MESSAGE_SHARE_INTRIGUE_ARMY_SNEAK_ATTACK_KNOWN_CITY: strText = GetDiploTextFromTag("RESPONSE_SHARE_INTRIGUE_ARMY_SNEAK_ATTACK_KNOWN_CITY", strOptionalKey1, strOptionalKey2); break; // AI warns human that another civ is planning a sneak attack against them, but don't know which city case DIPLO_MESSAGE_SHARE_INTRIGUE_ARMY_SNEAK_ATTACK_UNKNOWN_CITY: strText = GetDiploTextFromTag("RESPONSE_SHARE_INTRIGUE_ARMY_SNEAK_ATTACK_UNKNOWN_CITY", strOptionalKey1); break; // AI warns human that another civ is planning a sneak attack against a specific city of theirs (amphibious) case DIPLO_MESSAGE_SHARE_INTRIGUE_AMPHIBIOUS_SNEAK_ATTACK_KNOWN_CITY: strText = GetDiploTextFromTag("RESPONSE_SHARE_INTRIGUE_AMPHIBIOUS_SNEAK_ATTACK_KNOWN_CITY", strOptionalKey1, strOptionalKey2); break; // AI warns human that another civ is planning a sneak attack against them, but don't know which city (amphibious) case DIPLO_MESSAGE_SHARE_INTRIGUE_AMPHIBIOUS_SNEAK_ATTACK_UNKNOWN_CITY: strText = GetDiploTextFromTag("RESPONSE_SHARE_INTRIGUE_AMPHIBIOUS_SNEAK_ATTACK_UNKNOWN_CITY", strOptionalKey1); break; // AI warns human that another civ has bribed a third player to go to war with them case DIPLO_MESSAGE_SHARE_INTRIGUE_BRIBE_WAR: strText = GetDiploTextFromTag("RESPONSE_SHARE_INTRIGUE_BRIBE_WAR", strOptionalKey1, strOptionalKey2); break; // AI warns human that another human has agreed with a third player to go to war with them case DIPLO_MESSAGE_SHARE_INTRIGUE_COOP_WAR: strText = GetDiploTextFromTag("RESPONSE_SHARE_INTRIGUE_COOP_WAR", strOptionalKey1, strOptionalKey2); break; // AI warns human that another AI asked them to start a coop war against human case DIPLO_MESSAGE_COOP_WAR_WARNING: strText = GetDiploTextFromTag("RESPONSE_COOP_WAR_WARNING", strOptionalKey1); break; // Human catches enemy spy and does not forgive the thief case DIPLO_MESSAGE_HUMAN_KILLED_MY_SPY_UNFORGIVEN: strText = GetDiploTextFromTag("RESPONSE_HUMAN_KILLED_MY_SPY_UNFORGIVEN"); break; // Human catches enemy spy and forgives the thief case DIPLO_MESSAGE_HUMAN_KILLED_MY_SPY_FORGIVEN: strText = GetDiploTextFromTag("RESPONSE_HUMAN_KILLED_MY_SPY_FORGIVEN"); break; // AI asks human to stop sending missionaries and prophets. The human agrees. case DIPLO_MESSAGE_HUMAN_STOP_CONVERSIONS_GOOD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_STOP_CONVERSIONS_GOOD"); break; // AI asks human to stop sending missionaries and prophets. The human disagrees. case DIPLO_MESSAGE_HUMAN_STOP_CONVERSIONS_BAD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_STOP_CONVERSIONS_BAD"); break; // Human asks AI to stop sending missionaries and prophets. The AI player agrees. case DIPLO_MESSAGE_STOP_CONVERSIONS_AGREE: strText = GetDiploTextFromTag("RESPONSE_HUMAN_STOP_CONVERSIONS_AGREE"); break; // Human asks AI to stop sending missionaries and prophets. The AI player disagrees. case DIPLO_MESSAGE_STOP_CONVERSIONS_DISAGREE: strText = GetDiploTextFromTag("RESPONSE_HUMAN_STOP_CONVERSIONS_DISAGREE"); break; // AI asks human to stop digging up their artifacts. case DIPLO_MESSAGE_STOP_DIGGING: strText = GetDiploTextFromTag("RESPONSE_STOP_DIGGING"); break; // Human asks AI to stop digging up their artifacts. The AI player agrees. case DIPLO_MESSAGE_STOP_DIGGING_AGREE: strText = GetDiploTextFromTag("RESPONSE_STOP_DIGGING_AGREE"); break; // Human asks AI to stop digging up their artifacts. The AI player disagrees. case DIPLO_MESSAGE_STOP_DIGGING_DISAGREE: strText = GetDiploTextFromTag("RESPONSE_STOP_DIGGING_DISAGREE"); break; // AI asks human to stop digging up their artifacts. The human disagrees. case DIPLO_MESSAGE_HUMAN_STOP_DIGGING_BAD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_STOP_DIGGING_BAD"); break; // AI asks human to stop digging up their artifacts. The human agrees. case DIPLO_MESSAGE_HUMAN_STOP_DIGGING_GOOD: strText = GetDiploTextFromTag("RESPONSE_HUMAN_STOP_DIGGING_GOOD"); break; ///////////////////////////////// // League messages ///////////////////////////////// // AI tells human they liked human's proposal to the World Congress case DIPLO_MESSAGE_WE_LIKED_THEIR_PROPOSAL: strText = GetDiploTextFromTag("RESPONSE_WE_LIKE_HUMAN_PROPOSAL", strOptionalKey1); break; // AI tells human they disliked human's proposal to the World Congress case DIPLO_MESSAGE_WE_DISLIKED_THEIR_PROPOSAL: strText = GetDiploTextFromTag("RESPONSE_WE_DISLIKE_HUMAN_PROPOSAL", strOptionalKey1); break; // AI thanks human for supporting their proposal to the World Congress case DIPLO_MESSAGE_THEY_SUPPORTED_OUR_PROPOSAL: strText = GetDiploTextFromTag("RESPONSE_HUMAN_SUPPORTED_OUR_PROPOSAL", strOptionalKey1); break; // AI is angry at human for helping their proposal fail in the World Congress case DIPLO_MESSAGE_THEY_FOILED_OUR_PROPOSAL: strText = GetDiploTextFromTag("RESPONSE_HUMAN_FOILED_OUR_PROPOSAL", strOptionalKey1); break; // AI thanks human for helping make the AI host of the World Congress case DIPLO_MESSAGE_THEY_SUPPORTED_OUR_HOSTING: strText = GetDiploTextFromTag("RESPONSE_HUMAN_SUPPORTED_OUR_HOSTING", strOptionalKey1); break; // Ideological messages // Human has Freedom, AI does not and AI's people are unhappy with their choice of ideology case DIPLO_MESSAGE_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM: strText = GetDiploTextFromTag("RESPONSE_CIVIL_RESISTANCE_ON_AI_BY_FREEDOM"); break; // Human has Order, AI does not and AI's people are unhappy with their choice of ideology case DIPLO_MESSAGE_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER: strText = GetDiploTextFromTag("RESPONSE_CIVIL_RESISTANCE_ON_AI_BY_ORDER"); break; // Human has Autocracy, AI does not and AI's people are unhappy with their choice of ideology case DIPLO_MESSAGE_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY: strText = GetDiploTextFromTag("RESPONSE_CIVIL_RESISTANCE_ON_AI_BY_AUTOCRACY"); break; // AI has Freedom, human does not and human's people are unhappy with their choice of ideology case DIPLO_MESSAGE_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM: strText = GetDiploTextFromTag("RESPONSE_CIVIL_RESISTANCE_ON_HUMAN_BY_FREEDOM"); break; // AI has Order, human does not and human's people are unhappy with their choice of ideology case DIPLO_MESSAGE_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER: strText = GetDiploTextFromTag("RESPONSE_CIVIL_RESISTANCE_ON_HUMAN_BY_ORDER"); break; // AI has Autocracy, human does not and human's people are unhappy with their choice of ideology case DIPLO_MESSAGE_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY: strText = GetDiploTextFromTag("RESPONSE_CIVIL_RESISTANCE_ON_HUMAN_BY_AUTOCRACY"); break; // Human switched to Freedom and AI has Freedom case DIPLO_MESSAGE_SWITCH_OUR_IDEOLOGY_FREEDOM: strText = GetDiploTextFromTag("RESPONSE_SWITCHED_TO_FREEDOM"); break; // Human switched to Order and AI has Order case DIPLO_MESSAGE_SWITCH_OUR_IDEOLOGY_ORDER: strText = GetDiploTextFromTag("RESPONSE_SWITCHED_TO_ORDER"); break; // Human switched to Autocracy and AI has Autocracy case DIPLO_MESSAGE_SWITCH_OUR_IDEOLOGY_AUTOCRACY: strText = GetDiploTextFromTag("RESPONSE_SWITCHED_TO_AUTOCRACY"); break; // Human is influential over the AI - defeat message based on era case DIPLO_MESSAGE_YOUR_CULTURE_INFLUENTIAL: if (MOD_COREUI_DIPLOMACY_ERA_INFLUENCE) { if (eCurrentEra <= 1) { strText = GetDiploTextFromTag("RESPONSE_INFLUENTIAL_ON_AI_CLASSICAL"); } else if (eCurrentEra >= 2 && eCurrentEra < 4) { strText = GetDiploTextFromTag("RESPONSE_INFLUENTIAL_ON_AI_RENAISSANCE"); } else if (eCurrentEra >= 4 && eCurrentEra < 6) { strText = GetDiploTextFromTag("RESPONSE_INFLUENTIAL_ON_AI_MODERN"); } else { strText = GetDiploTextFromTag("RESPONSE_INFLUENTIAL_ON_AI"); } } else { strText = GetDiploTextFromTag("RESPONSE_INFLUENTIAL_ON_AI"); } break; // AI is influential over human - victory message based on era case DIPLO_MESSAGE_OUR_CULTURE_INFLUENTIAL: if (MOD_COREUI_DIPLOMACY_ERA_INFLUENCE) { if (eCurrentEra <= 1) { strText = GetDiploTextFromTag("RESPONSE_INFLUENTIAL_ON_HUMAN_CLASSICAL"); } else if (eCurrentEra >= 2 && eCurrentEra < 4) { strText = GetDiploTextFromTag("RESPONSE_INFLUENTIAL_ON_HUMAN_RENAISSANCE"); } else if (eCurrentEra >= 4 && eCurrentEra < 6) { strText = GetDiploTextFromTag("RESPONSE_INFLUENTIAL_ON_HUMAN_MODERN"); } else { strText = GetDiploTextFromTag("RESPONSE_INFLUENTIAL_ON_HUMAN"); } } else { strText = GetDiploTextFromTag("RESPONSE_INFLUENTIAL_ON_HUMAN"); } break; // AI announces that they're competing with human over Domination Victory case DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONQUEST: strText = GetDiploTextFromTag("DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONQUEST"); break; // AI announces that they're competing with human over Diplomatic Victory case DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONGRESS: strText = GetDiploTextFromTag("DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONGRESS"); break; // AI announces that they're competing with human over Cultural Victory case DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_CULTURE: strText = GetDiploTextFromTag("DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_CULTURE"); break; // AI announces that they're competing with human over Science Victory case DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_SPACESHIP: strText = GetDiploTextFromTag("DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_SPACESHIP"); break; // AI announces that they're competing with human but doesn't know what victory human is going for case DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_CONFUSED: strText = GetDiploTextFromTag("DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_CONFUSED"); break; // AI announces that they want to stop the human's Domination Victory plans (and aren't also going for that victory) case DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_WORLD_CONQUEST: strText = GetDiploTextFromTag("DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_WORLD_CONQUEST"); break; // AI announces that they want to stop the human's Diplomatic Victory plans (and aren't also going for that victory) case DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_WORLD_CONGRESS: strText = GetDiploTextFromTag("DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_WORLD_CONGRESS"); break; // AI announces that they want to stop the human's Cultural Victory plans (and aren't also going for that victory) case DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_CULTURE: strText = GetDiploTextFromTag("DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_CULTURE"); break; // AI announces that they want to stop the human's Science Victory plans (and aren't also going for that victory) case DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_SPACESHIP: strText = GetDiploTextFromTag("DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_SPACESHIP"); break; // Human repeatedly asks AI for opinion of another player after they said no (hostile) case DIPLO_MESSAGE_HOSTILE_REPEAT_SHARE_APPROACH_NO: strText = GetDiploTextFromTag("RESPONSE_HOSTILE_REPEAT_SHARE_APPROACH_NO"); break; // Human repeatedly asks AI for opinion of another player after they said no case DIPLO_MESSAGE_REPEAT_SHARE_APPROACH_NO: strText = GetDiploTextFromTag("RESPONSE_REPEAT_SHARE_APPROACH_NO"); break; // Human asks AI for their opinion of another player. The AI refuses. (hostile) case DIPLO_MESSAGE_HOSTILE_SHARE_APPROACH_NO: strText = GetDiploTextFromTag("RESPONSE_HOSTILE_SHARE_APPROACH_NO"); break; // Human asks AI for their opinion of another player. The AI refuses. case DIPLO_MESSAGE_SHARE_APPROACH_NO: strText = GetDiploTextFromTag("RESPONSE_SHARE_APPROACH_NO"); break; // AI tells human its approach towards another player (FRIENDLY) case DIPLO_MESSAGE_SHARE_APPROACH_FRIENDLY: strText = GetDiploTextFromTag("RESPONSE_SHARE_APPROACH_FRIENDLY", strOptionalKey1); break; // AI tells human its approach towards another player (NEUTRAL) case DIPLO_MESSAGE_SHARE_APPROACH_NEUTRAL: strText = GetDiploTextFromTag("RESPONSE_SHARE_APPROACH_NEUTRAL", strOptionalKey1); break; // AI tells human its approach towards another player (GUARDED) case DIPLO_MESSAGE_SHARE_APPROACH_GUARDED: strText = GetDiploTextFromTag("RESPONSE_SHARE_APPROACH_GUARDED", strOptionalKey1); break; // AI tells human its approach towards another player (HOSTILE) case DIPLO_MESSAGE_SHARE_APPROACH_HOSTILE: strText = GetDiploTextFromTag("RESPONSE_SHARE_APPROACH_HOSTILE", strOptionalKey1); break; // AI tells human it is at war with another player case DIPLO_MESSAGE_SHARE_APPROACH_WAR: strText = GetDiploTextFromTag("RESPONSE_SHARE_APPROACH_WAR", strOptionalKey1); break; // AI tells human its approach towards another player (AFRAID) case DIPLO_MESSAGE_SHARE_APPROACH_AFRAID: strText = GetDiploTextFromTag("RESPONSE_SHARE_APPROACH_AFRAID", strOptionalKey1); break; // AI tells human it is planning war against another player case DIPLO_MESSAGE_SHARE_APPROACH_PLANNING_WAR: strText = GetDiploTextFromTag("RESPONSE_SHARE_APPROACH_PLANNING_WAR", strOptionalKey1); break; // AI tells human its approach towards another player (DECEPTIVE) case DIPLO_MESSAGE_SHARE_APPROACH_DECEPTIVE: strText = GetDiploTextFromTag("RESPONSE_SHARE_APPROACH_DECEPTIVE", strOptionalKey1); break; // AI tells human it hasn't known them long enough to share its opinion of others case DIPLO_MESSAGE_TOO_SOON_FOR_SHARE_APPROACH: strText = GetDiploTextFromTag("RESPONSE_TOO_SOON_FOR_SHARE_APPROACH"); break; // AI wants to trade for the player's World Map case DIPLO_MESSAGE_MAPS_OFFER: strText = GetDiploTextFromTag("RESPONSE_MAPS_OFFER"); break; // AI wants to trade for one of the player's technologies case DIPLO_MESSAGE_TECH_OFFER: strText = GetDiploTextFromTag("RESPONSE_TECH_OFFER"); break; // AI is making a generous offer towards the player case DIPLO_MESSAGE_GENEROUS_OFFER: strText = GetDiploTextFromTag("RESPONSE_GENEROUS_OFFER"); break; // AI accepts a human's request for help case DIPLO_MESSAGE_HUMAN_REQUEST_YES: strText = GetDiploTextFromTag("RESPONSE_HELP_REQUEST_YES"); break; // AI refuses a human's request for help (too much) case DIPLO_MESSAGE_HUMAN_REQUEST_TOO_MUCH: strText = GetDiploTextFromTag("RESPONSE_HELP_REQUEST_REFUSE_TOO_MUCH"); break; // AI refuses a human's request for help (too soon since last request) case DIPLO_MESSAGE_HUMAN_REQUEST_TOO_SOON: strText = GetDiploTextFromTag("RESPONSE_HELP_REQUEST_REFUSE_TOO_SOON"); break; // Human attacked his AI vassal case DIPLO_MESSAGE_VASSALAGE_ATTACKED_VASSAL: strText = GetDiploTextFromTag("RESPONSE_VASSALAGE_ATTACKED_VASSAL"); break; // AI vassal greets his human master (hostile) case DIPLO_MESSAGE_GREETING_HOSTILE_VASSALAGE_VASSAL: strText = GetDiploTextFromTag("RESPONSE_GREETING_VASSALAGE_VASSAL_HOSTILE"); break; // AI vassal greets his human master case DIPLO_MESSAGE_GREETING_VASSALAGE_VASSAL: strText = GetDiploTextFromTag("RESPONSE_GREETING_VASSALAGE_VASSAL"); break; // AI master greets his human vassal (hostile) case DIPLO_MESSAGE_GREETING_HOSTILE_VASSALAGE_MASTER: strText = GetDiploTextFromTag("RESPONSE_GREETING_HOSTILE_VASSALAGE_MASTER"); break; // AI master greets his human vassal case DIPLO_MESSAGE_GREETING_VASSALAGE_MASTER: strText = GetDiploTextFromTag("RESPONSE_GREETING_VASSALAGE_MASTER"); break; // Ai wants the player to liberate his vassals (trade offer) case DIPLO_MESSAGE_REVOKE_VASSAL_THIRD_OFFER: strText = GetDiploTextFromTag("RESPONSE_VASSALAGE_TRADE_REVOKE"); break; // AI asks human to revoke his vassalage of them (hostile) case DIPLO_MESSAGE_REVOKE_VASSALAGE_HOSTILE: strText = GetDiploTextFromTag("RESPONSE_VASSALAGE_REVOKE_VASSALAGE_HOSTILE"); break; // AI asks human to revoke his vassalage of them case DIPLO_MESSAGE_REVOKE_VASSALAGE: strText = GetDiploTextFromTag("RESPONSE_VASSALAGE_REVOKE_VASSALAGE"); break; // Human revokes AI vassalage peacefully case DIPLO_MESSAGE_VASSALAGE_REVOKED_HUMAN_PEACEFUL: strText = GetDiploTextFromTag("RESPONSE_VASSALAGE_REVOKED_HUMAN_PEACEFUL"); break; // Human tells AI to die case DIPLO_MESSAGE_VASSALAGE_REVOKED_HUMAN_WAR: strText = GetDiploTextFromTag("RESPONSE_VASSALAGE_REVOKED_HUMAN_WAR"); break; // AI grants human independence case DIPLO_MESSAGE_VASSALAGE_REVOKED_PEACEFUL: strText = GetDiploTextFromTag("RESPONSE_VASSALAGE_REVOKED_PEACEFUL"); break; // AI tells human to die case DIPLO_MESSAGE_VASSALAGE_REVOKED_FORCEFUL: strText = GetDiploTextFromTag("RESPONSE_VASSALAGE_REVOKED_FORCEFUL"); break; // AI agrees to remove their troops from human's borders case DIPLO_MESSAGE_MOVE_TROOPS_ACCEPT: strText = GetDiploTextFromTag("RESPONSE_MOVE_TROOPS_ACCEPT"); break; // AI promises their troops are not on human's borders for war case DIPLO_MESSAGE_MOVE_TROOPS_NEUTRAL: strText = GetDiploTextFromTag("RESPONSE_MOVE_TROOPS_NEUTRAL"); break; // AI promises their troops are not on human's borders for war (hostile) case DIPLO_MESSAGE_MOVE_TROOPS_NEUTRAL_HOSTILE: strText = GetDiploTextFromTag("RESPONSE_MOVE_TROOPS_NEUTRAL_HOSTILE"); break; // AI declares war on human after human requested they move their troops case DIPLO_MESSAGE_MOVE_TROOPS_REJECT_CONQUEST: strText = GetDiploTextFromTag("RESPONSE_MOVE_TROOPS_REJECT_CONQUEST"); break; // AI declares war on human after human requested they move their troops case DIPLO_MESSAGE_MOVE_TROOPS_REJECT_DECEPTIVE: strText = GetDiploTextFromTag("RESPONSE_MOVE_TROOPS_REJECT_DECEPTIVE"); break; // AI declares war on human after human requested they move their troops case DIPLO_MESSAGE_MOVE_TROOPS_REJECT_HOSTILE: strText = GetDiploTextFromTag("RESPONSE_MOVE_TROOPS_REJECT_HOSTILE"); break; // AI liberates human vassal without being asked case DIPLO_MESSAGE_VASSALAGE_LIBERATED_HUMAN: strText = GetDiploTextFromTag("RESPONSE_VASSALAGE_LIBERATED_HUMAN"); break; // AI vassal is unhappy that human raised their taxes case DIPLO_MESSAGE_VASSAL_TAXES_RAISED_HUMAN_MASTER: strText = GetDiploTextFromTag("RESPONSE_VASSAL_TAXES_RAISED_HUMAN_MASTER"); break; // AI tells human vassal their taxes are now raised case DIPLO_MESSAGE_VASSAL_TAXES_RAISED_AI_MASTER: strText = GetDiploTextFromTag("RESPONSE_VASSAL_TAXES_RAISED_AI_MASTER"); break; // AI vassal is happy that human lowered their taxes case DIPLO_MESSAGE_VASSAL_TAXES_LOWERED_HUMAN_MASTER: strText = GetDiploTextFromTag("RESPONSE_VASSAL_TAXES_LOWERED_HUMAN_MASTER"); break; // AI tells human vassal their taxes are now lowered case DIPLO_MESSAGE_VASSAL_TAXES_LOWERED_AI_MASTER: strText = GetDiploTextFromTag("RESPONSE_VASSAL_TAXES_LOWERED_AI_MASTER"); break; // Human liberates AI vassal without being asked case DIPLO_MESSAGE_VASSALAGE_LIBERATE_VASSAL: strText = GetDiploTextFromTag("RESPONSE_LIBERATE_VASSAL"); break; // Human liberates AI vassal without being asked case DIPLO_MESSAGE_VASSALAGE_BECOME_VASSAL: strText = GetDiploTextFromTag("RESPONSE_VASSALAGE_BECOME_VASSAL"); break; ////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////// // Should always have a state we're handling default: strText = "NO MESSAGE. Trying to get Diplo string. Something has gone wrong, somehow."; ASSERT(false); break; } return strText; } /// Message from UI to gameplay about something that should happen with regards to diplomacy void CvDiplomacyAI::DoFromUIDiploEvent(PlayerTypes eFromPlayer, FromUIDiploEventTypes eEvent, int iArg1, int iArg2) { ASSERT(eEvent >= 0, "DIPLOMACY_AI: Invalid FromUIDiploEventType."); PRECONDITION(eEvent < NUM_FROM_UI_DIPLO_EVENTS, "DIPLOMACY_AI: Invalid FromUIDiploEventType."); const char* strText = ""; TeamTypes eFromTeam = GET_PLAYER(eFromPlayer).getTeam(); // Are we processing this message on the active player's computer? bool bActivePlayer = GC.getGame().getActivePlayer() == eFromPlayer; PlayerTypes eMyPlayer = GetID(); switch (eEvent) { // ********************************************* // Player declares war on the AI // ********************************************* case FROM_UI_DIPLO_EVENT_HUMAN_DECLARES_WAR: { // Changed some logic around so player can see the special declared war on vassal logic if (IsVassal(eFromPlayer)) { GET_PLAYER(eFromPlayer).GetDiplomacyAI()->DeclareWar(GetTeam()); } else { GET_PLAYER(eFromPlayer).GetDiplomacyAI()->DeclareWar(GetTeam()); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_ROOT); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_WAR_DECLARED_BY_HUMAN, strText, LEADERHEAD_ANIM_ATTACKED, iArg1); } } break; } // ********************************************* // Player requests peace // ********************************************* case FROM_UI_DIPLO_EVENT_HUMAN_NEGOTIATE_PEACE: { if (bActivePlayer) { // Does the AI actually want peace? if (IsWantsPeaceWithPlayer(eFromPlayer)) { // This is essentially the same as the human opening the trade screen GetPlayer()->GetDealAI()->DoTradeScreenOpened(); strText = GetDiploStringForMessage(DIPLO_MESSAGE_PEACE_WHAT_WILL_HUMAN_OFFER); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_TRADE, strText, LEADERHEAD_ANIM_LETS_HEAR_IT); } else { // Player declared war and wants peace right away. Uh huh, right. if (GET_TEAM(GetTeam()).GetNumTurnsAtWar(GET_PLAYER(eFromPlayer).getTeam()) <= 1) strText = GetDiploStringForMessage(DIPLO_MESSAGE_TOO_SOON_NO_PEACE); // Don't want peace for some other reason else strText = GetDiploStringForMessage(DIPLO_MESSAGE_NO_PEACE); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_WAR_DECLARED_BY_HUMAN, strText, LEADERHEAD_ANIM_NO); } } break; } // ********************************************* // Player wants to discuss something with the AI // ********************************************* case FROM_UI_DIPLO_EVENT_HUMAN_WANTS_DISCUSSION: { if(bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_LETS_HEAR_IT); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED, strText, LEADERHEAD_ANIM_LETS_HEAR_IT); } break; } // ********************************************* // Player told the AI to not settle near him // ********************************************* case FROM_UI_DIPLO_EVENT_HUMAN_DISCUSSION_DONT_SETTLE: { bool bAcceptable = IsDontSettleAcceptable(eFromPlayer); if (bAcceptable) GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetExpansionPromiseState(eMyPlayer, PROMISE_STATE_MADE); else GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetExpansionPromiseState(eMyPlayer, PROMISE_STATE_IGNORED); if (bActivePlayer) { if (bAcceptable) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_DONT_SETTLE_YES); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED, strText, LEADERHEAD_ANIM_NEGATIVE); } else { strText = GetDiploStringForMessage(DIPLO_MESSAGE_DONT_SETTLE_NO); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED, strText, LEADERHEAD_ANIM_NO); } } break; } // ********************************************* // Player told the AI to not spy on him // ********************************************* case FROM_UI_DIPLO_EVENT_HUMAN_DISCUSSION_STOP_SPYING: { bool bAcceptable = IsStopSpyingAcceptable(eFromPlayer); if (bAcceptable) { GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetSpyPromiseState(eMyPlayer, PROMISE_STATE_MADE); m_pPlayer->GetEspionageAI()->EvaluateSpiesAssignedToTargetPlayer(eFromPlayer); } if (bActivePlayer) { DiploUIStateTypes eStateType = DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED; if (iArg1 == 1) { eStateType = DIPLO_UI_STATE_BLANK_DISCUSSION_RETURN_TO_ROOT; } LeaderheadAnimationTypes eAnimType; if (bAcceptable) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_STOP_SPYING_YES); eAnimType = LEADERHEAD_ANIM_NEGATIVE; } else { strText = GetDiploStringForMessage(DIPLO_MESSAGE_STOP_SPYING_NO); eAnimType = LEADERHEAD_ANIM_NO; } gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, eStateType, strText, eAnimType); } break; } // ********************************************* // Player told the AI to not spread their religion // ********************************************* case FROM_UI_DIPLO_EVENT_HUMAN_DISCUSSION_STOP_SPREADING_RELIGION: { if (bActivePlayer) { SetPlayerAskedNotToConvert(eFromPlayer, true); DiploUIStateTypes eStateType = DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED; if (iArg1 == 1) { eStateType = DIPLO_UI_STATE_BLANK_DISCUSSION_RETURN_TO_ROOT; } LeaderheadAnimationTypes eAnimType; if (IsStopSpreadingReligionAcceptable(eFromPlayer)) { GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetNoConvertPromiseState(GetID(), PROMISE_STATE_MADE); strText = GetDiploStringForMessage(DIPLO_MESSAGE_STOP_CONVERSIONS_AGREE); eAnimType = LEADERHEAD_ANIM_NEGATIVE; } else { GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetNoConvertPromiseState(GetID(), PROMISE_STATE_IGNORED); strText = GetDiploStringForMessage(DIPLO_MESSAGE_STOP_CONVERSIONS_DISAGREE); eAnimType = LEADERHEAD_ANIM_NO; } gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, eStateType, strText, eAnimType); } break; } // ********************************************* // Player told the AI to not dig up their yard (artifacts) // ********************************************* case FROM_UI_DIPLO_EVENT_HUMAN_DISCUSSION_STOP_DIGGING: { if (bActivePlayer) { SetPlayerAskedNotToDig(eFromPlayer, true); DiploUIStateTypes eStateType = DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED; if (iArg1 == 1) { eStateType = DIPLO_UI_STATE_BLANK_DISCUSSION_RETURN_TO_ROOT; } LeaderheadAnimationTypes eAnimType; if (IsStopDiggingAcceptable(eFromPlayer)) { GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetNoDiggingPromiseState(eMyPlayer, PROMISE_STATE_MADE); strText = GetDiploStringForMessage(DIPLO_MESSAGE_STOP_DIGGING_AGREE); eAnimType = LEADERHEAD_ANIM_NEGATIVE; } else { GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetNoDiggingPromiseState(eMyPlayer, PROMISE_STATE_IGNORED); strText = GetDiploStringForMessage(DIPLO_MESSAGE_STOP_DIGGING_DISAGREE); eAnimType = LEADERHEAD_ANIM_NO; } gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, eStateType, strText, eAnimType); } break; } // ********************************************* // Player asked if the AI will work with him // ********************************************* case FROM_UI_DIPLO_EVENT_HUMAN_DISCUSSION_WORK_WITH_US: { ASSERT(!IsDoFAccepted(eFromPlayer)); // AI hasn't known the human for long enough yet if (IsTooEarlyForDoF(eFromPlayer) && !IsAIMustAcceptHumanDiscussRequests()) { if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_TOO_SOON_FOR_DOF); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED, strText, LEADERHEAD_ANIM_NO); } } // AI gives a new answer else { bool bAcceptable = IsDoFAcceptable(eFromPlayer) || IsAIMustAcceptHumanDiscussRequests(); if (bAcceptable) { SetDoFAccepted(eFromPlayer, true); GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetDoFAccepted(eMyPlayer, true); DoReevaluatePlayer(eFromPlayer, false, false); } if (bActivePlayer) { if (bAcceptable) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_WORK_WITH_US_YES); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED, strText, LEADERHEAD_ANIM_POSITIVE); } else { strText = GetDiploStringForMessage(DIPLO_MESSAGE_WORK_WITH_US_NO); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED, strText, LEADERHEAD_ANIM_NO); } } } break; } // ********************************************* // Player said he's done working with the AI // ********************************************* case FROM_UI_DIPLO_EVENT_HUMAN_DISCUSSION_END_WORK_WITH_US: { SetDoFAccepted(eFromPlayer, false); GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetDoFAccepted(eMyPlayer, false); SetDoFType(eFromPlayer, DOF_TYPE_UNTRUSTWORTHY); GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetDoFType(GetID(), DOF_TYPE_UNTRUSTWORTHY); if(bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_DISAPPOINTED); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEGATIVE); } break; } // ********************************************* // Human refused to give in to demand // ********************************************* case FROM_UI_DIPLO_EVENT_DEMAND_HUMAN_REFUSAL: { SetNumConsecutiveDemandsTheyAccepted(eFromPlayer, 0); // Does the AI declare war? bool bDeclareWar = GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(eFromPlayer).getTeam(), GetID()) && !GetPlayer()->IsNoNewWars(); // Must be a potential war target if (bDeclareWar && !IsPotentialWarTarget(eFromPlayer)) bDeclareWar = false; // Sanity check - who else would we go to war with? if (bDeclareWar && !DoUpdateOnePlayerSaneDiplomaticTarget(eFromPlayer, true)) bDeclareWar = false; // Sanity check - avoid going bankrupt int iMinIncome = 2 + (GetPlayer()->GetCurrentEra() * 2); if (bDeclareWar && IsWarWouldBankruptUs(eFromPlayer, iMinIncome)) bDeclareWar = false; // Sanity check - who else would we go to war with? if (bDeclareWar) { vector vDefensiveWarAllies = GetDefensiveWarAllies(eFromPlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ true, /*bNewWarsOnly*/ true); for (std::vector::iterator it = vDefensiveWarAllies.begin(); it != vDefensiveWarAllies.end(); ++it) { // Would we be declaring war on a powerful neighbor? if (GET_PLAYER(*it).GetProximityToPlayer(GetID()) >= PLAYER_PROXIMITY_CLOSE) { if (GET_PLAYER(*it).isMajorCiv()) { if (GetCivApproach(*it) == CIV_APPROACH_AFRAID) { bDeclareWar = false; break; } // If we're already planning a war/demand against them, then we don't care. else if (GetCivApproach(*it) != CIV_APPROACH_WAR && GetDemandTargetPlayer() != *it) { if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) { bDeclareWar = false; break; } } } else { if (GetCivApproach(*it) > CIV_APPROACH_HOSTILE) { if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) { bDeclareWar = false; break; } } } } } } if (bDeclareWar) { // If already at war or non-HOSTILE and not decisively stronger, AI has a chance to bluff int iCurrentWars = GetPlayer()->CountNumDangerousMajorsAtWarWith(true, true); bool bCanBluff = iCurrentWars > 0 || (GetCivApproach(eFromPlayer) > CIV_APPROACH_HOSTILE && GetTargetValue(eFromPlayer) <= TARGET_VALUE_AVERAGE); if (bCanBluff) { if (GetCivOpinion(eFromPlayer) == CIV_OPINION_UNFORGIVABLE || IsEndgameAggressiveTo(eFromPlayer) || IsCapitalCapturedBy(eFromPlayer, true, true) || IsHolyCityCapturedBy(eFromPlayer, true, true)) { bCanBluff = false; } } if (bCanBluff) { int iBluffChance = 20 + 20 * min(iCurrentWars, 3); if (GetBiggestCompetitor() == eFromPlayer) iBluffChance /= 2; if (GC.getGame().randRangeInclusive(1, 100, CvSeeder::fromRaw(0x240baa80).mix(GetID()).mix(GET_PLAYER(eFromPlayer).GetID())) > iBluffChance) bCanBluff = false; } // Not bluffing - declare war! if (!bCanBluff && DeclareWar(eFromPlayer)) { bool bCareful = iCurrentWars > 0 && GetGlobalCoopWarAgainstState(eFromPlayer) < COOP_WAR_STATE_PREPARING; GetPlayer()->GetMilitaryAI()->RequestCityAttack(eFromPlayer, 3, bCareful); } else bDeclareWar = false; } if (bActivePlayer) { if (bDeclareWar) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_WAR_DEMAND_REFUSED); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); } else { strText = GetDiploStringForMessage(DIPLO_MESSAGE_SO_BE_IT); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } break; } // ********************************************* // Human refused to give in to a request // ********************************************* case FROM_UI_DIPLO_EVENT_REQUEST_HUMAN_REFUSAL: { //If player is offended, AI should take note as penalty to assistance. ChangeRecentAssistValue(eFromPlayer, AdjustConditionalModifier(-90, GetNeediness())); //End the gift exchange after this. GetPlayer()->GetDiplomacyAI()->SetOfferingGift(eFromPlayer, false); GetPlayer()->GetDiplomacyAI()->SetOfferedGift(eFromPlayer, false); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_DISAPPOINTED); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, strText, LEADERHEAD_ANIM_NEGATIVE); } break; } // ********************************************* // AI is telling us he sees our military buildup on his borders // ********************************************* case FROM_UI_DIPLO_EVENT_AGGRESSIVE_MILITARY_WARNING_RESPONSE: { // **** NOTE **** - iArg1 is BUTTON ID from DiscussionDialog.lua // Human says he means no harm if (iArg1 == 1) { SetMilitaryPromiseState(eFromPlayer, PROMISE_STATE_MADE); GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetMilitaryPromiseState(GetID(), PROMISE_STATE_MADE); if (bActivePlayer) { if(IsActHostileTowardsHuman(eFromPlayer)) strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_HOSTILE_AGGRESSIVE_MILITARY_WARNING_GOOD); else strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_AGGRESSIVE_MILITARY_WARNING_GOOD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } // Human told the AI to die else if (iArg1 == 2) { SetMilitaryPromiseState(eFromPlayer, PROMISE_STATE_IGNORED); if (GET_PLAYER(eFromPlayer).GetDiplomacyAI()->DeclareWar(GetTeam())) { if (bActivePlayer) { if (IsActHostileTowardsHuman(eFromPlayer)) strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_HOSTILE_AGGRESSIVE_MILITARY_WARNING_BAD); else strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_AGGRESSIVE_MILITARY_WARNING_BAD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } } break; } // ********************************************* // AI has attacked a Minor the human is friends with, what did the human say in response? // ********************************************* case FROM_UI_DIPLO_EVENT_I_ATTACKED_YOUR_MINOR_CIV_RESPONSE: { // **** NOTE **** - iArg1 is BUTTON ID, iArg2 is MINOR ID from DiscussionDialog.lua PlayerTypes eMinor = (PlayerTypes) iArg2; PRECONDITION(eMinor >= MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index. "); PRECONDITION(eMinor < MAX_CIV_PLAYERS, "DIPLOMACY_AI: Invalid Player Index. "); if (eMinor < MAX_MAJOR_CIVS || eMinor >= MAX_CIV_PLAYERS) { // Fail gracefully, allow UI to continue strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_HOSTILE_WE_ATTACKED_MINOR_BAD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEGATIVE); break; } CvPlayer* pMinor = &GET_PLAYER(eMinor); ASSERT(pMinor, "Error triggering gameplay effects for diplomacy event involving a Protected city-state."); if (!pMinor) { // Fail gracefully, allow UI to continue strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_HOSTILE_WE_ATTACKED_MINOR_BAD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEGATIVE); break; } ASSERT(pMinor->GetMinorCivAI()->IsProtectedByMajor(eFromPlayer), "Diplomacy event involving a Protected city-state when there was no Pledge active."); // Human says he forgives the AI if (iArg1 == 1) { GC.getGame().DoMinorPledgeProtection(eFromPlayer, eMinor, false, true); // Pledge is broken! if (bActivePlayer) { if(IsActHostileTowardsHuman(eFromPlayer)) strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_HOSTILE_WE_ATTACKED_MINOR_GOOD); else strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_WE_ATTACKED_MINOR_GOOD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } // Human said he'd get revenge else if (iArg1 == 2) { SetOtherPlayerSidedWithProtectedMinorTurn(eFromPlayer, GC.getGame().getGameTurn()); // Does the AI declare war? bool bDeclareWar = false; if (GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(eFromPlayer).getTeam(), GetID())) { int iChance = 20; if (GetCivOpinion(eFromPlayer) >= CIV_OPINION_FAVORABLE) { iChance += 5; } else if (GetCivOpinion(eFromPlayer) <= CIV_OPINION_COMPETITOR) { iChance -= 5; } if (GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x4d801c73).mix(GetID()).mix(GET_PLAYER(eFromPlayer).GetID())) > (iChance - GetMeanness() - GetBoldness())) { bDeclareWar = true; } if (GetCivApproach(eFromPlayer) == CIV_APPROACH_AFRAID) { bDeclareWar = false; } if (GetPlayer()->IsVassalOfSomeone() || GET_PLAYER(eFromPlayer).IsVassalOfSomeone()) { bDeclareWar = false; } if (bDeclareWar && GetPlayer()->IsNoNewWars()) { bDeclareWar = false; } if (bDeclareWar && !IsPotentialWarTarget(eFromPlayer)) { bDeclareWar = false; } if (bDeclareWar && !DoUpdateOnePlayerSaneDiplomaticTarget(eFromPlayer, true)) { bDeclareWar = false; } // Sanity check - avoid going bankrupt int iMinIncome = 2 + (GetPlayer()->GetCurrentEra() * 2); if (bDeclareWar && IsWarWouldBankruptUs(eFromPlayer, iMinIncome)) { bDeclareWar = false; } // Sanity check - who else would we go to war with? if (bDeclareWar) { vector vDefensiveWarAllies = GetDefensiveWarAllies(eFromPlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ true, /*bNewWarsOnly*/ true); for (std::vector::iterator it = vDefensiveWarAllies.begin(); it != vDefensiveWarAllies.end(); ++it) { // Would we be declaring war on a powerful neighbor? if (GET_PLAYER(*it).GetProximityToPlayer(GetID()) >= PLAYER_PROXIMITY_CLOSE) { if (GET_PLAYER(*it).isMajorCiv()) { if (GetCivApproach(*it) == CIV_APPROACH_AFRAID) { bDeclareWar = false; break; } // If we're already planning a war/demand against them, then we don't care. else if (GetCivApproach(*it) != CIV_APPROACH_WAR && GetDemandTargetPlayer() != *it) { if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) { bDeclareWar = false; break; } } } else { if (GetCivApproach(*it) > CIV_APPROACH_HOSTILE) { if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) { bDeclareWar = false; break; } } } } } } } if (bDeclareWar) { bDeclareWar = DeclareWar(GET_PLAYER(eFromPlayer).getTeam()); if (bDeclareWar) { GetPlayer()->GetMilitaryAI()->RequestCityAttack(eFromPlayer,2); } } if (bActivePlayer) { if (bDeclareWar) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_DOW_GENERIC); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); } else { if (IsActHostileTowardsHuman(eFromPlayer)) strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_HOSTILE_WE_ATTACKED_MINOR_BAD); else strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_WE_ATTACKED_MINOR_BAD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEGATIVE); } } } break; } // ********************************************* // AI has bullied a Minor the human has a PtP with, what did the human say in response? // ********************************************* case FROM_UI_DIPLO_EVENT_I_BULLIED_YOUR_MINOR_CIV_RESPONSE: { // **** NOTE **** - iArg1 is BUTTON ID, iArg2 is MINOR ID from DiscussionDialog.lua PlayerTypes eMinor = (PlayerTypes) iArg2; PRECONDITION(eMinor >= MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index. "); PRECONDITION(eMinor < MAX_CIV_PLAYERS, "DIPLOMACY_AI: Invalid Player Index. "); if (eMinor < MAX_MAJOR_CIVS || eMinor >= MAX_CIV_PLAYERS) { // Fail gracefully, allow UI to continue strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_HOSTILE_WE_ATTACKED_MINOR_BAD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEGATIVE); break; } CvPlayer* pMinor = &GET_PLAYER(eMinor); ASSERT(pMinor, "Error triggering gameplay effects for breaking a Pledge to Protect a city-state."); if (!pMinor) { // Fail gracefully, allow UI to continue strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_HOSTILE_WE_ATTACKED_MINOR_BAD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEGATIVE); break; } ASSERT(pMinor->GetMinorCivAI()->IsProtectedByMajor(eFromPlayer), "Diplomacy event involving a Protected city-state when there was no Pledge active."); // Human says he forgives the AI if(iArg1 == 1) { GC.getGame().DoMinorPledgeProtection(eFromPlayer, eMinor, false, true); // Pledge is broken! if (bActivePlayer) { if(IsActHostileTowardsHuman(eFromPlayer)) strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_HOSTILE_WE_BULLIED_MINOR_GOOD); else strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_WE_BULLIED_MINOR_GOOD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } // Human said he'd get revenge else if (iArg1 == 2) { SetOtherPlayerSidedWithProtectedMinorTurn(eFromPlayer, GC.getGame().getGameTurn()); // Does the AI declare war? bool bDeclareWar = false; if (GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(eFromPlayer).getTeam(), GetID())) { int iChance = 20; if (GetCivOpinion(eFromPlayer) >= CIV_OPINION_FAVORABLE) { iChance += 7; } else if (GetCivOpinion(eFromPlayer) <= CIV_OPINION_COMPETITOR) { iChance -= 3; } if (GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0xf4bc010c).mix(GetID()).mix(GET_PLAYER(eFromPlayer).GetID())) > (iChance - GetMeanness() - GetBoldness())) { bDeclareWar = true; } if (GetCivApproach(eFromPlayer) == CIV_APPROACH_AFRAID) { bDeclareWar = false; } if (GetPlayer()->IsVassalOfSomeone() || GET_PLAYER(eFromPlayer).IsVassalOfSomeone()) { bDeclareWar = false; } if (bDeclareWar && GetPlayer()->IsNoNewWars()) { bDeclareWar = false; } if (bDeclareWar && !IsPotentialWarTarget(eFromPlayer)) { bDeclareWar = false; } if (bDeclareWar && !DoUpdateOnePlayerSaneDiplomaticTarget(eFromPlayer, true)) { bDeclareWar = false; } // Sanity check - avoid going bankrupt int iMinIncome = 2 + (GetPlayer()->GetCurrentEra() * 2); if (bDeclareWar && IsWarWouldBankruptUs(eFromPlayer, iMinIncome)) { bDeclareWar = false; } // Sanity check - who else would we go to war with? if (bDeclareWar) { vector vDefensiveWarAllies = GetDefensiveWarAllies(eFromPlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ true, /*bNewWarsOnly*/ true); for (std::vector::iterator it = vDefensiveWarAllies.begin(); it != vDefensiveWarAllies.end(); ++it) { // Would we be declaring war on a powerful neighbor? if (GET_PLAYER(*it).GetProximityToPlayer(GetID()) >= PLAYER_PROXIMITY_CLOSE) { if (GET_PLAYER(*it).isMajorCiv()) { if (GetCivApproach(*it) == CIV_APPROACH_AFRAID) { bDeclareWar = false; break; } // If we're already planning a war/demand against them, then we don't care. else if (GetCivApproach(*it) != CIV_APPROACH_WAR && GetDemandTargetPlayer() != *it) { if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) { bDeclareWar = false; break; } } } else { if (GetCivApproach(*it) > CIV_APPROACH_HOSTILE) { if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) { bDeclareWar = false; break; } } } } } } } if (bDeclareWar) { bDeclareWar = DeclareWar(GET_PLAYER(eFromPlayer).getTeam()); if (bDeclareWar) { GetPlayer()->GetMilitaryAI()->RequestCityAttack(eFromPlayer,2); } } if (bActivePlayer) { if (bDeclareWar) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_DOW_GENERIC); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); } else { if (IsActHostileTowardsHuman(eFromPlayer)) strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_HOSTILE_WE_BULLIED_MINOR_BAD); else strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_WE_BULLIED_MINOR_BAD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEGATIVE); } } } break; } // ********************************************* // AI warned human about attacking a protected Minor // ********************************************* case FROM_UI_DIPLO_EVENT_ATTACKED_MINOR_RESPONSE: { // **** NOTE **** - iArg1 is BUTTON ID from DiscussionDialog.lua // Human told the AI it's none of his business if (iArg1 == 1) { SetAttackCityStatePromiseState(eFromPlayer, PROMISE_STATE_IGNORED); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_ATTACKED_MINOR_BAD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // Human said he'd withdraw else if (iArg1 == 2) { SetAttackCityStatePromiseState(eFromPlayer, PROMISE_STATE_MADE); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_ATTACKED_MINOR_GOOD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } break; } // ********************************************* // AI is mad at human for killing a protected Minor // ********************************************* case FROM_UI_DIPLO_EVENT_KILLED_MINOR_RESPONSE: { break; } // ********************************************* // AI warned human about bullying a protected Minor // ********************************************* case FROM_UI_DIPLO_EVENT_BULLIED_MINOR_RESPONSE: { // **** NOTE **** - iArg1 is BUTTON ID from DiscussionDialog.lua // Human told the AI it's none of his business if (iArg1 == 1) { SetBullyCityStatePromiseState(eFromPlayer, PROMISE_STATE_IGNORED); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_BULLIED_MINOR_BAD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // Human said he'd withdraw else if (iArg1 == 2) { SetBullyCityStatePromiseState(eFromPlayer, PROMISE_STATE_MADE); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_BULLIED_MINOR_GOOD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } break; } // ********************************************* // AI is seriously warning the human about his expansion // ********************************************* case FROM_UI_DIPLO_EVENT_EXPANSION_SERIOUS_WARNING_RESPONSE: { break; } // ********************************************* // AI warned human about his expansion // ********************************************* case FROM_UI_DIPLO_EVENT_EXPANSION_WARNING_RESPONSE: { // **** NOTE **** - iArg1 is BUTTON ID from DiscussionDialog.lua // Human told the AI it's none of their business if (iArg1 == 1) { SetExpansionPromiseState(eFromPlayer, PROMISE_STATE_IGNORED); if(bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_EXPANSION_WARNING_BAD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // Human said he wouldn't settle near us again else if (iArg1 == 2) { SetExpansionPromiseState(eFromPlayer, PROMISE_STATE_MADE); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_EXPANSION_WARNING_GOOD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } break; } // ********************************************* // AI is seriously warning the human about his plot buying // ********************************************* case FROM_UI_DIPLO_EVENT_PLOT_BUYING_SERIOUS_WARNING_RESPONSE: { break; } // ********************************************* // AI warned human about his plot buying // ********************************************* case FROM_UI_DIPLO_EVENT_PLOT_BUYING_WARNING_RESPONSE: { // **** NOTE **** - iArg1 is BUTTON ID from DiscussionDialog.lua // Human told the AI it's none of their business if (iArg1 == 1) { SetBorderPromiseState(eFromPlayer, PROMISE_STATE_IGNORED); //Reset city tile numbers so that this can't fire again immediately after the plot warning is up. int iLoop = 0; for (CvCity* pLoopCity = GetPlayer()->firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GetPlayer()->nextCity(&iLoop)) { int iTilesBought = pLoopCity->AI_GetNumPlotsAcquiredByOtherPlayer(eFromPlayer); if (iTilesBought > 0) { pLoopCity->AI_ChangeNumPlotsAcquiredByOtherPlayer(eFromPlayer, -iTilesBought); } } if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_PLOT_BUYING_WARNING_BAD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // Human said he wouldn't buy land near us again else if (iArg1 == 2) { SetBorderPromiseState(eFromPlayer, PROMISE_STATE_MADE); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_PLOT_BUYING_WARNING_GOOD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } break; } // ********************************************* // AI asked human if he'd like to work together // ********************************************* case FROM_UI_DIPLO_EVENT_WORK_WITH_US_RESPONSE: { // **** NOTE **** - iArg1 is BUTTON ID from DiscussionDialog.lua // Human agrees if (iArg1 == 1) { SetDoFAccepted(eFromPlayer, true); GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetDoFAccepted(eMyPlayer, true); // Update diplomacy stuff DoReevaluatePlayer(eFromPlayer, false, false); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_PLEASED); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } // Human says sorry, no else if (iArg1 == 2) { if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_DISAPPOINTED); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEGATIVE); } } break; } // ********************************************* // Human ends AI cooperation // ********************************************* case FROM_UI_DIPLO_EVENT_HUMAN_END_WORK_WITH_US_RESPONSE: { if (IsDoFAccepted(eMyPlayer) || GET_PLAYER(eMyPlayer).GetDiplomacyAI()->IsDoFAccepted(eFromPlayer)) { SetDoFAccepted(eFromPlayer, false); GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetDoFAccepted(eMyPlayer, false); ChangeRecentAssistValue(eFromPlayer, -300); SetDoFType(eFromPlayer, DOF_TYPE_UNTRUSTWORTHY); GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetDoFType(GetID(), DOF_TYPE_UNTRUSTWORTHY); GET_TEAM(GET_PLAYER(eMyPlayer).getTeam()).SetHasDefensivePact(GET_PLAYER(eFromPlayer).getTeam(), false); GET_TEAM(GET_PLAYER(eFromPlayer).getTeam()).SetHasDefensivePact(GET_PLAYER(eMyPlayer).getTeam(), false); // End all coop war agreements with this player CancelCoopWarsWithPlayer(eFromPlayer, true); // Other players' reactions for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer, true) && !GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && eLoopPlayer != eFromPlayer && eLoopPlayer != eMyPlayer) { // Their teammates if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsTeammate(eFromPlayer)) { if (GET_PLAYER(eMyPlayer).GetDiplomacyAI()->IsDenouncedPlayer(eLoopPlayer) || GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsUntrustworthy(eMyPlayer)) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eFromPlayer, 300); } } else if (GET_PLAYER(eLoopPlayer).getTeam() != GET_PLAYER(eMyPlayer).getTeam()) { // Player might apply a diplo bonus if they don't hate them if (!GET_TEAM(GET_PLAYER(eFromPlayer).getTeam()).isAtWar(GET_PLAYER(eLoopPlayer).getTeam()) && !GET_PLAYER(eFromPlayer).GetDiplomacyAI()->IsDenouncedPlayer(eLoopPlayer) && !GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsDenouncedPlayer(eFromPlayer) && !GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsUntrustworthy(eFromPlayer) && GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetCivOpinion(eFromPlayer) != CIV_OPINION_UNFORGIVABLE) { // Bonus for ending a DoF with us, if we're a backstabber if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsUntrustworthy(eMyPlayer)) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eFromPlayer, 75); } // Bonus for ending a DoF with us, if we're at war with them else if (GET_TEAM(GET_PLAYER(eLoopPlayer).getTeam()).isAtWar(GET_PLAYER(eMyPlayer).getTeam())) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eFromPlayer, 50); // Extra bonus if they're doing badly in the war if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetWarState(eMyPlayer) <= WAR_STATE_TROUBLED) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eFromPlayer, 25); } } // Bonus for ending a DoF with us, if we denounced them else if (GET_PLAYER(eMyPlayer).GetDiplomacyAI()->IsDenouncedPlayer(eLoopPlayer)) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eFromPlayer, 50); } // Penalty for ending a DoF with us, for players that consider us a friend, DP or ally else if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsDoFAccepted(eMyPlayer) || GET_TEAM(GET_PLAYER(eLoopPlayer).getTeam()).IsHasDefensivePact(GET_PLAYER(eMyPlayer).getTeam()) || GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetCivOpinion(eMyPlayer) == CIV_OPINION_ALLY) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eFromPlayer, -75); } } // Penalty for ending a DoF with us, for players that consider us a friend, DP or ally (the player hates them) else if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsDoFAccepted(eMyPlayer) || GET_TEAM(GET_PLAYER(eLoopPlayer).getTeam()).IsHasDefensivePact(GET_PLAYER(eMyPlayer).getTeam()) || GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetCivOpinion(eMyPlayer) == CIV_OPINION_ALLY) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eFromPlayer, -150); } } // Our teammates else if (IsTeammate(eLoopPlayer)) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eFromPlayer, -300); } } } SetDoFBroken(eFromPlayer, true, false); // AI message if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_NOW_UNFORGIVABLE); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEGATIVE); } } else { if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_DOT_DOT_DOT); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEUTRAL_IDLE); } } break; } // ********************************************* // Human responds to AI action in a positive or negative way. // ********************************************* case FROM_UI_DIPLO_EVENT_MEAN_RESPONSE: { // If player is offended, AI should take note as penalty to assistance. GetPlayer()->GetDiplomacyAI()->ChangeRecentAssistValue(eFromPlayer, -300); // Does the AI declare war? bool bDeclareWar = false; if (!IsAtWar(eFromPlayer) && GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(eFromPlayer).getTeam(), GetID())) { int iChance = 20; if (GetCivOpinion(eFromPlayer) >= CIV_OPINION_FAVORABLE) { iChance += 5; } if (GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0xd99596fe).mix(GetID()).mix(GET_PLAYER(eFromPlayer).GetID())) > (iChance - GetMeanness() - GetBoldness())) { bDeclareWar = true; } if (GetCivApproach(eFromPlayer) == CIV_APPROACH_AFRAID) { bDeclareWar = false; } if (GetPlayer()->IsVassalOfSomeone() || GET_PLAYER(eFromPlayer).IsVassalOfSomeone()) { bDeclareWar = false; } if (bDeclareWar && GetPlayer()->IsNoNewWars()) { bDeclareWar = false; } if (bDeclareWar && !IsPotentialWarTarget(eFromPlayer)) { bDeclareWar = false; } if (bDeclareWar && !DoUpdateOnePlayerSaneDiplomaticTarget(eFromPlayer, true)) { bDeclareWar = false; } // Sanity check - avoid going bankrupt int iMinIncome = 2 + (GetPlayer()->GetCurrentEra() * 2); if (bDeclareWar && IsWarWouldBankruptUs(eFromPlayer, iMinIncome)) { bDeclareWar = false; } // Sanity check - who else would we go to war with? if (bDeclareWar) { vector vDefensiveWarAllies = GetDefensiveWarAllies(eFromPlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ true, /*bNewWarsOnly*/ true); for (std::vector::iterator it = vDefensiveWarAllies.begin(); it != vDefensiveWarAllies.end(); ++it) { // Would we be declaring war on a powerful neighbor? if (GET_PLAYER(*it).GetProximityToPlayer(GetID()) >= PLAYER_PROXIMITY_CLOSE) { if (GET_PLAYER(*it).isMajorCiv()) { if (GetCivApproach(*it) == CIV_APPROACH_AFRAID) { bDeclareWar = false; break; } // If we're already planning a war/demand against them, then we don't care. else if (GetCivApproach(*it) != CIV_APPROACH_WAR && GetDemandTargetPlayer() != *it) { if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) { bDeclareWar = false; break; } } } else { if (GetCivApproach(*it) > CIV_APPROACH_HOSTILE) { if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) { bDeclareWar = false; break; } } } } } } // Was already planning on declaring war - go to war if it's sane! if ((GetCivApproach(eFromPlayer) == CIV_APPROACH_WAR && IsWarSane(eFromPlayer)) || GetGlobalCoopWarAgainstState(eFromPlayer) >= COOP_WAR_STATE_PREPARING) { bDeclareWar = true; } if (bDeclareWar) { bDeclareWar = DeclareWar(GET_PLAYER(eFromPlayer).getTeam()); if (bDeclareWar) GetPlayer()->GetMilitaryAI()->RequestCityAttack(eFromPlayer,3); } //check again, might have failed. if (!bDeclareWar) { SetCivApproach(eFromPlayer, CIV_APPROACH_NEUTRAL); } if (bActivePlayer) { if (bDeclareWar) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_WAR_RUDE_INSULT); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_DECLARE_WAR); } else { strText = GetDiploStringForMessage(DIPLO_MESSAGE_SO_BE_IT); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEUTRAL_IDLE); } } } else { if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_DOT_DOT_DOT); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEUTRAL_IDLE); } } break; } // ********************************************* // Human denounced us! // ********************************************* case FROM_UI_DIPLO_EVENT_DENOUNCE: { // **** NOTE **** - iArg1 is BUTTON ID from DiscussionDialog.lua GET_PLAYER(eFromPlayer).GetDiplomacyAI()->DoDenouncePlayer(eMyPlayer); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_RESPONSE_TO_BEING_DENOUNCED); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEGATIVE); } break; } // ********************************************* // Human asked AI to go to war against someone // ********************************************* case FROM_UI_DIPLO_EVENT_COOP_WAR_OFFER: { // **** NOTE **** - iArg1 is Player ID from DiscussionDialog.lua PlayerTypes eTargetPlayer = (PlayerTypes) iArg1; CoopWarStates eResponse = RespondToCoopWarRequest(eFromPlayer, eTargetPlayer); switch (eResponse) { case NO_COOP_WAR_STATE: UNREACHABLE(); // Response is never expected to be `NO_COOP_WAR_STATE`. case COOP_WAR_STATE_ONGOING: if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_COOP_WAR_YES); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } break; case COOP_WAR_STATE_PREPARING: if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_COOP_WAR_SOON); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } break; case COOP_WAR_STATE_REJECTED: case COOP_WAR_STATE_WARNED_TARGET: if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_COOP_WAR_NO); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEGATIVE); } break; } break; } // ********************************************* // AI asked human if he'd declare war against someone // ********************************************* case FROM_UI_DIPLO_EVENT_COOP_WAR_RESPONSE: { // **** NOTE **** - iArg1 is BUTTON ID from DiscussionDialog.lua PlayerTypes eTargetPlayer = (PlayerTypes) iArg2; // Human says sorry, no if (iArg1 == 1 || iArg1 == 2) { if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_DISAPPOINTED); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEGATIVE); } // Diplo penalty! // Small penalty for a normal refusal if (iArg1 == 1) { int iAssistPenalty = AdjustConditionalModifier(-100, GetNeediness()); ChangeRecentAssistValue(eFromPlayer, iAssistPenalty); ChangeCoopWarAgreementScore(eFromPlayer, -1); } // Big penalty for warning the target else { GET_PLAYER(eFromPlayer).GetDiplomacyAI()->DoWarnCoopWarTarget(GetID(), eTargetPlayer); } } // Human agrees else if (iArg1 == 3 || iArg1 == 4) { ChangeRecentAssistValue(eFromPlayer, 300); // Human says he needs to prepare if (iArg1 == 3) { SetCoopWarState(eFromPlayer, eTargetPlayer, COOP_WAR_STATE_PREPARING); GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetCoopWarState(GetID(), eTargetPlayer, COOP_WAR_STATE_PREPARING); } // Human agrees to war immediately else { SetCoopWarState(eFromPlayer, eTargetPlayer, COOP_WAR_STATE_PREPARING, true); GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetCoopWarState(GetID(), eTargetPlayer, COOP_WAR_STATE_PREPARING, true); DoStartCoopWar(eFromPlayer, eTargetPlayer); } // Update approach to WAR SetCivApproach(eTargetPlayer, CIV_APPROACH_WAR); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_PLEASED); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } break; } /* // ********************************************* // AI asked human if he'd declare war against someone earlier, and now it's time to put your money where your mouth is // ********************************************* case FROM_UI_DIPLO_EVENT_COOP_WAR_NOW_RESPONSE: { // **** NOTE **** - iArg1 is BUTTON ID from DiscussionDialog.lua PlayerTypes eAgainstPlayer = (PlayerTypes) iArg2; SetCoopWarCounter(eFromPlayer, eAgainstPlayer, 0); // Human agrees if (iArg1 == 1) { if (GET_PLAYER(eFromPlayer).GetDiplomacyAI()->DeclareWar(eAgainstPlayer)) { SetCoopWarAcceptedState(eFromPlayer, eAgainstPlayer, COOP_WAR_STATE_ACCEPTED); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_PLEASED); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } // AI declaration if (DeclareWar(eAgainstPlayer)) { GetPlayer()->GetMilitaryAI()->RequestBasicAttack(eAgainstPlayer, 3); } // Human declaration TeamTypes eAgainstTeam = GET_PLAYER(eAgainstPlayer).getTeam(); int iLockedTurns = GD_INT_GET(COOP_WAR_LOCKED_LENGTH); GET_TEAM(GetTeam()).ChangeNumTurnsLockedIntoWar(eAgainstTeam, iLockedTurns); GET_TEAM(eFromTeam).ChangeNumTurnsLockedIntoWar(eAgainstTeam, iLockedTurns); } } // Human says no if (iArg1 == 2) { SetCoopWarAcceptedState(eFromPlayer, eAgainstPlayer, NO_COOP_WAR_STATE); // No penalty for teammates; otherwise, the AI gets mad if (!IsTeammate(eFromPlayer)) { SetBrokeCoopWarPromise(eFromPlayer, true); ChangeRecentAssistValue(eFromPlayer, -300); ChangeCoopWarAgreementScore(eFromPlayer, -2); } if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_DISAPPOINTED); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEGATIVE); } } break; } */ // ********************************************* // Human made a demand. NOTE: Unlike the other messages in here, this one is actually triggered from inside CvDealAI on a player's local machine // ********************************************* case FROM_UI_DIPLO_EVENT_HUMAN_DEMAND: { // **** NOTE **** - iArg1 is RESPONSE TYPE from CvDealAI::DoHumanDemand() DemandResponseTypes eResponse = (DemandResponseTypes) iArg1; // THIS is the important part of the message - it seeds the demand timer on all players' machines DoDemandMade(eFromPlayer, eResponse); if (bActivePlayer) { // Demand agreed to if (eResponse == DEMAND_RESPONSE_ACCEPT) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DEMAND_YES); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_RETURN_TO_ROOT, strText, LEADERHEAD_ANIM_YES); } // Demand rebuffed else { switch (eResponse) { case DEMAND_RESPONSE_REFUSE_WEAK: strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DEMAND_REFUSE_WEAK); break; case DEMAND_RESPONSE_REFUSE_HOSTILE: strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DEMAND_REFUSE_HOSTILE); break; case DEMAND_RESPONSE_REFUSE_TOO_MUCH: strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DEMAND_REFUSE_TOO_MUCH); break; case DEMAND_RESPONSE_REFUSE_TOO_SOON: case DEMAND_RESPONSE_REFUSE_TOO_SOON_SINCE_PEACE: strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DEMAND_REFUSE_TOO_SOON); break; case DEMAND_RESPONSE_REFUSE_PROTECTED_BY_MASTER: { PlayerTypes eProtector = NO_PLAYER; TeamTypes eOurMaster = GET_TEAM(GetTeam()).GetMaster(); vector vMasterTeam = GET_TEAM(eOurMaster).getPlayers(); for (size_t i=0; i < vMasterTeam.size(); i++) { // Find the first player on the master's team who triggered this response, see CvDealAI::GetDemandResponse() CvPlayer* pMaster = &GET_PLAYER(vMasterTeam[i]); if (!pMaster->isAlive() || pMaster->getNumCities() == 0) continue; if (pMaster->GetDiplomacyAI()->GetRawMilitaryStrengthComparedToUs(eFromPlayer) > STRENGTH_AVERAGE) continue; if (GetVassalProtectValue(vMasterTeam[i]) < 0) continue; // Is our master neighbors with them? if (pMaster->GetProximityToPlayer(eFromPlayer) == PLAYER_PROXIMITY_NEIGHBORS || pMaster->GetProximityToPlayer(eMyPlayer) >= GET_PLAYER(eFromPlayer).GetProximityToPlayer(eMyPlayer)) eProtector = vMasterTeam[i]; } PRECONDITION(eProtector != NO_PLAYER); const char* strProtectorCivKey = GET_PLAYER(eProtector).getCivilizationShortDescriptionKey(); strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DEMAND_REFUSE_PROTECTED_BY_MASTER, NO_PLAYER, strProtectorCivKey); break; } default: UNREACHABLE(); } gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_RETURN_TO_ROOT, strText, LEADERHEAD_ANIM_NO); } } break; } // ********************************************* // AI asked human if he'd like to make a Research Agreement in the future // ********************************************* case FROM_UI_DIPLO_EVENT_PLAN_RA_RESPONSE: { // **** NOTE **** - iArg1 is BUTTON ID from DiscussionDialog.lua // Human agrees if(iArg1 == 1) { if(bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_PLEASED); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } // Human says sorry, no else if(iArg1 == 2) { if(bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_DISAPPOINTED); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEGATIVE); } } break; } // ********************************************* // AI asked human if he'd denounce a third party // ********************************************* case FROM_UI_DIPLO_EVENT_AI_REQUEST_DENOUNCE_RESPONSE: { // **** NOTE **** - iArg1 is BUTTON ID from DiscussionDialog.lua PlayerTypes eAgainstPlayer = (PlayerTypes) iArg2; // Human agrees if(iArg1 == 1) { GET_PLAYER(eFromPlayer).GetDiplomacyAI()->DoDenouncePlayer(eAgainstPlayer); if(bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_PLEASED); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } // Human says sorry, no else if(iArg1 == 2) { // Oh, you're gonna say no, are you? if(IsFriendDenounceRefusalUnacceptable(eFromPlayer, eAgainstPlayer)) { DoDenouncePlayer(eFromPlayer); LogDenounce(eFromPlayer, /*bBackstab*/ false, /*bRefusal*/ true); strText = GetDiploStringForMessage(DIPLO_MESSAGE_DOF_NOT_HONORED); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); } else if(bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_DISAPPOINTED); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEGATIVE); } } break; } // ********************************************* // AI warned human about spying // ********************************************* case FROM_UI_DIPLO_EVENT_CAUGHT_YOUR_SPY_RESPONSE: { // **** NOTE **** - iArg1 is BUTTON ID from DiscussionDialog.lua // Human told the AI it's none of his business if (iArg1 == 1) { SetSpyPromiseState(eFromPlayer, PROMISE_STATE_IGNORED); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_CAUGHT_YOUR_SPY_BAD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // Human said he'd withdraw else if (iArg1 == 2) { SetSpyPromiseState(eFromPlayer, PROMISE_STATE_MADE); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_CAUGHT_YOUR_SPY_GOOD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } break; } // ********************************************* // Human warn AI about spying // ********************************************* case FROM_UI_DIPLO_EVENT_KILLED_MY_SPY_RESPONSE: { // **** NOTE **** - iArg1 is BUTTON ID from DiscussionDialog.lua // Human did not forgive AI player if (iArg1 == 1) { //TODO: AI should either promise or not SetPlayerForgaveForSpying(eFromPlayer, false); m_pPlayer->GetEspionageAI()->m_aiTurnLastSpyCaught[eFromPlayer] = 0; if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_KILLED_MY_SPY_UNFORGIVEN); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_NEGATIVE); } } // Human forgave AI player else if (iArg1 == 2) { SetPlayerForgaveForSpying(eFromPlayer, true); m_pPlayer->GetEspionageAI()->m_aiTurnLastSpyCaught[eFromPlayer] = 0; if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_KILLED_MY_SPY_FORGIVEN); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } break; } case FROM_UI_DIPLO_EVENT_HUMAN_DISCUSSION_SHARE_INTRIGUE: { PlayerTypes ePlottingPlayer = (PlayerTypes)iArg1; CvIntrigueType eIntrigueType = (CvIntrigueType)iArg2; ASSERT(CvPlayerAI::IsValid(ePlottingPlayer)); if (CvPlayerAI::IsValid(ePlottingPlayer)) { ChangeNumTimesIntrigueSharedBy(eFromPlayer, 1); GET_PLAYER(eFromPlayer).GetEspionage()->MarkRecentIntrigueAsShared(eMyPlayer, ePlottingPlayer, NO_PLAYER, eIntrigueType); if (bActivePlayer) { if (MOD_ENABLE_ACHIEVEMENTS) { if (GET_PLAYER(eFromPlayer).GetEspionage()->HasSharedIntrigueAboutMe(eMyPlayer)) gDLL->UnlockAchievement(ACHIEVEMENT_XP1_37); bool bUsingXP1Scenario3 = gDLL->IsModActivated(CIV5_XP1_SCENARIO3_MODID); if (bUsingXP1Scenario3) gDLL->UnlockAchievement(ACHIEVEMENT_XP1_45); } strText = GetDiploStringForMessage(DIPLO_MESSAGE_WARNED_ABOUT_INTRIGUE, NO_PLAYER, GET_PLAYER(ePlottingPlayer).getCivilizationAdjectiveKey()); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED, strText, LEADERHEAD_ANIM_POSITIVE); } } break; } // ********************************************* // AI warned about religious conversions // ********************************************* case FROM_UI_DIPLO_EVENT_STOP_CONVERSIONS: { // **** NOTE **** - iArg1 is BUTTON ID from DiscussionDialog.lua GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetPlayerAskedNotToConvert(eMyPlayer, true); // Human told the AI it's none of his business if (iArg1 == 1) { SetNoConvertPromiseState(eFromPlayer, PROMISE_STATE_IGNORED); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_STOP_CONVERSIONS_BAD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // Human said he'd withdraw else if (iArg1 == 2) { SetNoConvertPromiseState(eFromPlayer, PROMISE_STATE_MADE); // Reduce their conversion points to a maximum since they agreed to withdraw if (GetNegativeReligiousConversionPoints(eFromPlayer) >= /*4*/ GD_INT_GET(RELIGION_DIPLO_HIT_THRESHOLD)) { SetNegativeReligiousConversionPoints(eFromPlayer, GD_INT_GET(RELIGION_DIPLO_HIT_THRESHOLD)); } if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_STOP_CONVERSIONS_GOOD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } break; } // ********************************************* // AI warned about Digging on my turf // ********************************************* case FROM_UI_DIPLO_EVENT_STOP_DIGGING: { // **** NOTE **** - iArg1 is BUTTON ID from DiscussionDialog.lua // Human told the AI it's none of their business if (iArg1 == 1) { SetNoDiggingPromiseState(eFromPlayer, PROMISE_STATE_IGNORED); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_STOP_DIGGING_BAD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } // Human said he'd withdraw else if (iArg1 == 2) { SetNoDiggingPromiseState(eFromPlayer, PROMISE_STATE_MADE); if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_STOP_DIGGING_GOOD); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } break; } // ********************************************* // Player asked the AI about other civilizations // ********************************************* case FROM_UI_DIPLO_EVENT_HUMAN_DISCUSSION_SHARE_APPROACH: { PlayerTypes eTargetPlayer = (PlayerTypes) iArg1; bool bHostile = IsAtWar(eFromPlayer) || IsDenouncedPlayer(eFromPlayer) || IsDenouncedByPlayer(eFromPlayer) || IsUntrustworthy(eFromPlayer) || IsActHostileTowardsHuman(eFromPlayer); bool bAcceptable = !bHostile && !IsTooEarlyForShareApproach(eFromPlayer) && GET_PLAYER(eFromPlayer).isAlive() && !GET_PLAYER(eFromPlayer).isObserver() && (GetShareApproachResponse(eFromPlayer) == SHARE_APPROACH_RESPONSE_ACCEPTED || IsShareApproachAcceptable(eFromPlayer)); bool bDiplomat = false; if (MOD_BALANCE_VP) { // Vassals always share their true feelings with their master. if (IsVassal(eFromPlayer)) { bAcceptable = true; bDiplomat = true; } // Has a Diplomat collected enough network points? else if (GET_PLAYER(eFromPlayer).GetEspionage() && GET_PLAYER(eFromPlayer).GetEspionage()->IsMyDiplomatVisitingThem(m_pPlayer->GetID())) { CvCity* pCapital = GetPlayer()->getCapitalCity(); if (pCapital) { CvCityEspionage* pCapitalEspionage = pCapital->GetCityEspionage(); if (pCapitalEspionage && pCapitalEspionage->IsDiplomatRevealTrueApproaches(eFromPlayer)) { bAcceptable = true; bDiplomat = true; } } } } bool bOverride = IsAtWar(eTargetPlayer) || MOD_DIPLO_DEBUG_MODE || GET_PLAYER(eFromPlayer).isObserver(); // We refuse! Choose a hostile response. if (bHostile && !bOverride) { if (bActivePlayer) { if (GetShareApproachResponse(eFromPlayer) == SHARE_APPROACH_RESPONSE_REFUSED) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HOSTILE_REPEAT_SHARE_APPROACH_NO); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED, strText, LEADERHEAD_ANIM_NO); } else { strText = GetDiploStringForMessage(DIPLO_MESSAGE_HOSTILE_SHARE_APPROACH_NO); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED, strText, LEADERHEAD_ANIM_NO); } } SetShareApproachResponse(eFromPlayer, SHARE_APPROACH_RESPONSE_REFUSED); } // We accepted! Share our approach towards this player with them. else if (bAcceptable || bOverride) { if (bAcceptable) SetShareApproachResponse(eFromPlayer, SHARE_APPROACH_RESPONSE_ACCEPTED); bool bHonest = bDiplomat || bOverride || (GetCivApproach(eFromPlayer) == CIV_APPROACH_FRIENDLY && GetCivOpinion(eFromPlayer) >= CIV_OPINION_FRIEND); CivApproachTypes eTargetApproach = bHonest ? GetCivApproach(eTargetPlayer) : GetSurfaceApproach(eTargetPlayer); if (bActivePlayer) { if (IsAtWar(eTargetPlayer)) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_APPROACH_WAR, NO_PLAYER, GET_PLAYER(eTargetPlayer).getNameKey()); } else { switch (eTargetApproach) { case CIV_APPROACH_FRIENDLY: strText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_APPROACH_FRIENDLY, NO_PLAYER, GET_PLAYER(eTargetPlayer).getNameKey()); break; case CIV_APPROACH_NEUTRAL: strText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_APPROACH_NEUTRAL, NO_PLAYER, GET_PLAYER(eTargetPlayer).getNameKey()); break; case CIV_APPROACH_GUARDED: strText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_APPROACH_GUARDED, NO_PLAYER, GET_PLAYER(eTargetPlayer).getNameKey()); break; case CIV_APPROACH_HOSTILE: strText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_APPROACH_HOSTILE, NO_PLAYER, GET_PLAYER(eTargetPlayer).getNameKey()); break; case CIV_APPROACH_AFRAID: strText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_APPROACH_AFRAID, NO_PLAYER, GET_PLAYER(eTargetPlayer).getNameKey()); break; case CIV_APPROACH_WAR: strText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_APPROACH_PLANNING_WAR, NO_PLAYER, GET_PLAYER(eTargetPlayer).getNameKey()); break; case CIV_APPROACH_DECEPTIVE: strText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_APPROACH_DECEPTIVE, NO_PLAYER, GET_PLAYER(eTargetPlayer).getNameKey()); break; } } gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED, strText, LEADERHEAD_ANIM_POSITIVE); } } // We declined! else { if (IsTooEarlyForShareApproach(eFromPlayer)) { if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_TOO_SOON_FOR_SHARE_APPROACH); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED, strText, LEADERHEAD_ANIM_NO); } } else { if (bActivePlayer) { if (GetShareApproachResponse(eFromPlayer) == SHARE_APPROACH_RESPONSE_REFUSED) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_REPEAT_SHARE_APPROACH_NO); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED, strText, LEADERHEAD_ANIM_NO); } else { strText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_APPROACH_NO); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED, strText, LEADERHEAD_ANIM_NO); } } SetShareApproachResponse(eFromPlayer, SHARE_APPROACH_RESPONSE_REFUSED); } } break; } // ********************************************* // Human asked for or received a generous offer. NOTE: Unlike the other messages in here, this one is actually triggered from inside CvDealAI on a player's local machine // ********************************************* // NOTE: This is going to be called for both AI offering a generous offer and human requesting help // Done this way because AI offering is actually treated as a fake human "demand" case FROM_UI_DIPLO_EVENT_HUMAN_REQUEST: { // **** NOTE **** - iArg1 is RESPONSE TYPE from CvDealAI::DoHumanDemand() DemandResponseTypes eResponse = (DemandResponseTypes) iArg1; // THIS is the important part of the message - it seeds the help request timer on all players' machines DoHelpRequestMade(eFromPlayer, eResponse); if (bActivePlayer) { // Help Request agreed to if (eResponse == DEMAND_RESPONSE_GIFT_ACCEPT) { strText = GetPlayer()->GetDiplomacyAI()->GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_REQUEST_YES); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_RETURN_TO_ROOT, strText, LEADERHEAD_ANIM_POSITIVE); } // Help Request rebuffed else { if (eResponse == DEMAND_RESPONSE_GIFT_REFUSE_TOO_MUCH) strText = GetPlayer()->GetDiplomacyAI()->GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_REQUEST_TOO_MUCH); else if (eResponse == DEMAND_RESPONSE_GIFT_REFUSE_TOO_SOON) strText = GetPlayer()->GetDiplomacyAI()->GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_REQUEST_TOO_SOON); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_RETURN_TO_ROOT, strText, LEADERHEAD_ANIM_NEGATIVE); } } break; } // ********************************************* // Player revokes Vassalage with AI // ********************************************* case FROM_UI_DIPLO_EVENT_HUMAN_ENDS_VASSALAGE: { bool bPeaceful = false; bool bHumanWasMyVassal = false; bool bAcceptable = false; // Human told AI to die if(iArg1 == 1) { bPeaceful = false; } // Human allows AI to leave vassalage else if(iArg1 == 2) { bPeaceful = true; } // Who is revoking? for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).getTeam() == GetTeam()) { // Is this player the vassal of eFromPlayer? if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsVassal(eFromPlayer)) { // Player peacefully revoked our vassalage! Happy AI! :) if (bPeaceful) { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->SetVassalagePeacefullyRevokedTurn(eFromPlayer, GC.getGame().getGameTurn()); GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eFromPlayer, 300); // Friends of the vassal - bonus to recent assistance! for (int iThirdPartyLoop = 0; iThirdPartyLoop < MAX_MAJOR_CIVS; iThirdPartyLoop++) { PlayerTypes eThirdParty = (PlayerTypes) iThirdPartyLoop; if (eThirdParty == eLoopPlayer || eThirdParty == eFromPlayer || GET_PLAYER(eThirdParty).getTeam() == GET_PLAYER(eFromPlayer).getTeam() || GET_PLAYER(eThirdParty).isHuman(ISHUMAN_AI_DIPLOMACY)) continue; if (GET_PLAYER(eFromPlayer).GetDiplomacyAI()->IsPlayerValid(eThirdParty) && GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsPlayerValid(eThirdParty) && GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsDoFAccepted(eThirdParty)) { GET_PLAYER(eThirdParty).GetDiplomacyAI()->ChangeRecentAssistValue(eFromPlayer, 300); } } } else { GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->SetVassalageForcefullyRevokedTurn(eFromPlayer, GC.getGame().getGameTurn()); GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eFromPlayer, -300); GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->SetBackstabbedBy(eFromPlayer, true, true); // Friends of the vassal - penalty to recent assistance! for (int iThirdPartyLoop = 0; iThirdPartyLoop < MAX_MAJOR_CIVS; iThirdPartyLoop++) { PlayerTypes eThirdParty = (PlayerTypes) iThirdPartyLoop; if (eThirdParty == eLoopPlayer || eThirdParty == eFromPlayer || GET_PLAYER(eThirdParty).getTeam() == GET_PLAYER(eFromPlayer).getTeam() || GET_PLAYER(eThirdParty).isHuman(ISHUMAN_AI_DIPLOMACY)) continue; // Only if not a *better* friend/ally of the master (because the AI will often befriend vassals.) if (GET_PLAYER(eThirdParty).GetDiplomacyAI()->IsFriendOrAlly(eFromPlayer) && GET_PLAYER(eThirdParty).GetDiplomacyAI()->GetCachedOpinionWeight(eFromPlayer) < GET_PLAYER(eThirdParty).GetDiplomacyAI()->GetCachedOpinionWeight(eLoopPlayer)) continue; if (GET_PLAYER(eFromPlayer).GetDiplomacyAI()->IsPlayerValid(eThirdParty) && GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsPlayerValid(eThirdParty) && GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsDoFAccepted(eThirdParty)) { GET_PLAYER(eThirdParty).GetDiplomacyAI()->ChangeRecentAssistValue(eFromPlayer, -300); } } } } } } // AI team no longer vassal of human. If it's forcefully, bPeaceful = false if (GET_TEAM(GetTeam()).IsVassal(eFromTeam)) { GET_TEAM(GetTeam()).DoEndVassal(eFromTeam, bPeaceful, false); } // Human was our vassal - what is our response? else if (GET_TEAM(eFromTeam).IsVassal(GetTeam())) { bHumanWasMyVassal = true; bAcceptable = IsEndVassalageAcceptable(eFromPlayer); GET_TEAM(eFromTeam).DoEndVassal(GetTeam(), bAcceptable, false); } // AI message if (bActivePlayer) { // Human revoking AI vassalage if (!bHumanWasMyVassal) { if (bPeaceful) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSALAGE_REVOKED_HUMAN_PEACEFUL); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_RETURN_TO_ROOT, strText, LEADERHEAD_ANIM_YES); } else { strText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSALAGE_REVOKED_HUMAN_WAR); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_RETURN_TO_ROOT, strText, LEADERHEAD_ANIM_ATTACKED); } } // Human requests AI to end vassalage else { if (bAcceptable) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSALAGE_REVOKED_PEACEFUL); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_AI_DECLARED_WAR, strText, LEADERHEAD_ANIM_YES); } else { strText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSALAGE_REVOKED_FORCEFUL); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_AI_DECLARED_WAR, strText, LEADERHEAD_ANIM_ATTACKED); // Anim attacked correct. Not ideal, but otherwise leader won't react to your request } } } break; } // ********************************************************** case FROM_UI_DIPLO_EVENT_HUMAN_MOVE_TROOPS_RESPONSE: { MoveTroopsResponseTypes eResponse = GetMoveTroopsRequestResponse(eFromPlayer, /*bJustChecking*/ false); if (eResponse == MOVE_TROOPS_RESPONSE_REFUSE && !GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(eFromPlayer).getTeam(), GetID())) eResponse = MOVE_TROOPS_RESPONSE_NEUTRAL; // AI says yes, they will move their troops, and even better, they will leave the human alone if (eResponse == MOVE_TROOPS_RESPONSE_ACCEPT) { // AI accepts move troops request // Make sure all players on this team get this check, so that teammates don't screw each other over. for (int iI=0; iI < MAX_MAJOR_CIVS; iI++) { PlayerTypes eTeammate = (PlayerTypes)iI; TeamTypes eLoopTeam = GET_PLAYER(eTeammate).getTeam(); if (eLoopTeam == GetTeam()) { GET_PLAYER(eTeammate).GetDiplomacyAI()->SetPlayerMoveTroopsRequestAccepted(eFromPlayer, true); GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetMilitaryPromiseState(eTeammate, PROMISE_STATE_MADE); GET_PLAYER(eTeammate).GetDiplomacyAI()->SetMilitaryPromiseState(eFromPlayer, PROMISE_STATE_MADE); } } if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_MOVE_TROOPS_ACCEPT); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_YES); } // lower weight for crossing tiles around this player's cities /* int iLoop; for (CvCity* pLoopCity = GET_PLAYER(eFromPlayer).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(eFromPlayer).nextCity(&iLoop)) { //todo: mark cities for pathfinder //open question: when to unmark?? } */ } // AI says they are just moving their troops through else if (eResponse == MOVE_TROOPS_RESPONSE_NEUTRAL) { // AI agrees not to attack // Make sure all players on this team get this check, so that teammates don't screw each other over. for (int iI=0; iI < MAX_MAJOR_CIVS; iI++) { PlayerTypes eTeammate = (PlayerTypes)iI; TeamTypes eLoopTeam = GET_PLAYER(eTeammate).getTeam(); if (eLoopTeam == GetTeam()) { GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetMilitaryPromiseState(eTeammate, PROMISE_STATE_MADE); GET_PLAYER(eTeammate).GetDiplomacyAI()->SetMilitaryPromiseState(eFromPlayer, PROMISE_STATE_MADE); } } if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_MOVE_TROOPS_NEUTRAL); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_YES); } } // AI tells human to die else if (eResponse == MOVE_TROOPS_RESPONSE_REFUSE) { // Declare war on human! bool bWasDeceptive = GetCivApproach(eFromPlayer) == CIV_APPROACH_DECEPTIVE || (GetCivApproach(eFromPlayer) == CIV_APPROACH_WAR && GetSurfaceApproach(eFromPlayer) == CIV_APPROACH_FRIENDLY); if (DeclareWar(eFromTeam)) { GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetMilitaryPromiseState(eMyPlayer, PROMISE_STATE_IGNORED); if (bActivePlayer) { if (IsGoingForWorldConquest()) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_MOVE_TROOPS_REJECT_CONQUEST); } // Was deceptive else if (bWasDeceptive) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_MOVE_TROOPS_REJECT_DECEPTIVE); } // Default case else { strText = GetDiploStringForMessage(DIPLO_MESSAGE_MOVE_TROOPS_REJECT_HOSTILE); } gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_AI_DECLARED_WAR, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } else { // AI agrees not to attack for (int iI = 0; iI < MAX_MAJOR_CIVS; iI++) { PlayerTypes eTeammate = (PlayerTypes)iI; TeamTypes eLoopTeam = GET_PLAYER(eTeammate).getTeam(); if (eLoopTeam == GetTeam()) { GET_PLAYER(eFromPlayer).GetDiplomacyAI()->SetMilitaryPromiseState(eTeammate, PROMISE_STATE_MADE); GET_PLAYER(eTeammate).GetDiplomacyAI()->SetMilitaryPromiseState(eFromPlayer, PROMISE_STATE_MADE); } } if (bActivePlayer) { strText = GetDiploStringForMessage(DIPLO_MESSAGE_MOVE_TROOPS_NEUTRAL); gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_YES); } } } // Something happened! (will generate an error message) else { ASSERT(false); if (bActivePlayer) { gDLL->GameplayDiplomacyAILeaderMessage(eMyPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_YES); } } break; } // Should always have a state we're handling default: ASSERT(false); break; } } /// Is the AI acting mean to the active human player? bool CvDiplomacyAI::IsActHostileTowardsHuman(PlayerTypes eHuman) const { if (IsTeammate(eHuman) || IsDoFAccepted(eHuman) || GET_PLAYER(eHuman).isObserver()) return false; bool bAtWar = IsAtWar(eHuman); bool bAtWarButWantsPeace = bAtWar && (GetTreatyWillingToOffer(eHuman) >= PEACE_TREATY_WHITE_PEACE || GetTreatyWillingToAccept(eHuman) >= PEACE_TREATY_WHITE_PEACE); // Have to be at war, high level AI has to want peace if (bAtWar) { return !bAtWarButWantsPeace; } CivApproachTypes eSurfaceApproach = GetSurfaceApproach(eHuman); CivOpinionTypes eOpinion = GetCivOpinion(eHuman); if (eSurfaceApproach == CIV_APPROACH_HOSTILE) // Hostile approach return true; if (eSurfaceApproach == CIV_APPROACH_AFRAID) // Afraid approach return false; // Denounced them? if (IsDenouncedPlayer(eHuman)) return true; if (eSurfaceApproach == CIV_APPROACH_FRIENDLY) // Friendly approach return false; // NOTE: If we got here, the surface approach must be either NEUTRAL or GUARDED // Backstabber! if (IsUntrustworthy(eHuman)) return true; // Liberator if (IsLiberator(eHuman, false, false)) return false; // Defensive Pact if (IsHasDefensivePact(eHuman)) return false; // Coop war planned or ongoing? if (GetGlobalCoopWarWithState(eHuman) >= COOP_WAR_STATE_PREPARING) return false; // Are we in bad shape for war? Don't act hostile. if (GetPlayer()->IsInTerribleShapeForWar()) return false; // Different ideology if (IsPlayerOpposingIdeology(eHuman) && !IsIgnoreIdeologyDifferences(eHuman)) return true; // High threat ThreatTypes eWarmongerThreat = GetWarmongerThreat(eHuman); if (eWarmongerThreat == THREAT_CRITICAL) return true; if (GetPlayer()->GetProximityToPlayer(eHuman) >= PLAYER_PROXIMITY_CLOSE && eWarmongerThreat == THREAT_SEVERE) return true; if (IsEndgameAggressiveTo(eHuman)) return true; // Same ideology and not denounced/unforgivable if (IsPlayerSameIdeology(eHuman) && !IsDenouncedByPlayer(eHuman) && eOpinion != CIV_OPINION_UNFORGIVABLE) return false; // Poor target value TargetValueTypes eTargetValue = GetTargetValue(eHuman); if (eTargetValue <= TARGET_VALUE_BAD) return false; if (eSurfaceApproach == CIV_APPROACH_NEUTRAL && eTargetValue == TARGET_VALUE_DIFFICULT) return false; // Planning war? Let's not tip them off about our plans. if (IsWantsSneakAttack(eHuman)) { if (!IsEasyTarget(eHuman) && eTargetValue != TARGET_VALUE_SOFT) return false; if (IsEasyTarget(eHuman) && eTargetValue == TARGET_VALUE_AVERAGE) return false; return true; } // They denounced us? if (IsDenouncedByPlayer(eHuman)) return true; // Poor opinion? if (eOpinion <= CIV_OPINION_ENEMY) return true; if (eSurfaceApproach == CIV_APPROACH_GUARDED && eOpinion == CIV_OPINION_COMPETITOR) return true; return false; } /// AI is greeting the human - what does he say? const char* CvDiplomacyAI::GetGreetHumanMessage(LeaderheadAnimationTypes& eAnimation) { PlayerTypes eHuman = GC.getGame().getActivePlayer(); CvPlayer* pHuman = &GET_PLAYER(eHuman); TeamTypes eHumanTeam = pHuman->getTeam(); CvTeam* pHumanTeam = &GET_TEAM(eHumanTeam); GetPlayer()->DoUpdateWarDamageAndWeariness(/*bDamageOnly*/ true); CivApproachTypes eVisibleApproach = GetSurfaceApproach(eHuman); WarStateTypes eWarState = GetWarState(eHuman); DisputeLevelTypes eLandDispute = GetLandDisputeLevel(eHuman); AggressivePostureTypes eMilitaryPosture = GetMilitaryAggressivePosture(eHuman); AggressivePostureTypes eExpansionPosture = GetExpansionAggressivePosture(eHuman); AggressivePostureTypes ePlotBuyingPosture = GetPlotBuyingAggressivePosture(eHuman); StrengthTypes eMilitaryStrength = GetMilitaryStrengthComparedToUs(eHuman); StrengthTypes eEconomicStrength = GetEconomicStrengthComparedToUs(eHuman); int iNumPlayersAttacked = GetOtherPlayerNumMinorsAttacked(eHuman) + GetOtherPlayerNumMajorsAttacked(eHuman); int iNumPlayersKilled = GetPlayerNumMinorsConquered(eHuman) + GetPlayerNumMajorsConquered(eHuman); bool bAtWar = IsAtWar(eHuman); bool bAtWarButWantsPeace = IsWantsPeaceWithPlayer(eHuman); // Most Greetings are added to a vector to be picked from randomly // However, some are returned immediately, as they "fit" well enough that we DEFINITELY want to use that specific greeting vector veValidGreetings; // Determine if the AI is being hostile to the player bool bHostile = IsActHostileTowardsHuman(eHuman); //////////////////////////////////////////// // Pick Greeting Animation //////////////////////////////////////////// if (bHostile) eAnimation = LEADERHEAD_ANIM_HATE_HELLO; else eAnimation = LEADERHEAD_ANIM_NEUTRAL_HELLO; //////////////////////////////////////////// // Repeated Greetings //////////////////////////////////////////// int iTimesScreenOpened = GC.GetEngineUserInterface()->GetStartDiploRepeatCount(); if (iTimesScreenOpened > 4) return GetDiploStringForMessage(DIPLO_MESSAGE_GREETING_REPEAT_TOO_MUCH); else if (iTimesScreenOpened > 2) { if (bHostile) return GetDiploStringForMessage(DIPLO_MESSAGE_GREETING_HOSTILE_REPEAT); else return GetDiploStringForMessage(DIPLO_MESSAGE_GREETING_REPEAT); } //////////////////////////////////////////// // War Greetings //////////////////////////////////////////// // At war but wants peace if (bAtWarButWantsPeace) { if (eWarState == WAR_STATE_NEARLY_DEFEATED) { return GetDiploStringForMessage(DIPLO_MESSAGE_GREETING_DESTRUCTION_LOOMS); } else if (GetTreatyWillingToAccept(eHuman) >= PEACE_TREATY_CAPITULATION) { return GetDiploStringForMessage(DIPLO_MESSAGE_GREETING_WILL_ACCEPT_SURRENDER); } else { return GetDiploStringForMessage(DIPLO_MESSAGE_GREETING_AT_WAR_WANTS_PEACE); } } else if (bAtWar) return GetDiploStringForMessage(DIPLO_MESSAGE_GREETING_AT_WAR_HOSTILE); //////////////////////////////////////////// // Situational Greetings //////////////////////////////////////////// // Working on a Research Agreement - JON: Disabled because this could be invoked if the AI wanted one without the human's knowledge //if (IsWantsResearchAgreementWithPlayer(eHuman)) // return GetDiploStringForMessage(DIPLO_MESSAGE_GREETING_RESEARCH_AGREEMENT); // Player has broken promises about playing nice militarily if (eVisibleApproach != CIV_APPROACH_FRIENDLY) { if (BrokeMilitaryPromise(eHuman) || GetBrokenMilitaryPromiseWithAnybodyScore(eHuman) > 0) { veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_BROKEN_MILITARY_PROMISE); } } // Players are working together if(IsDoFAccepted(eHuman)) veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_WORKING_WITH); // Coop War against anyone? if (GetGlobalCoopWarWithState(eHuman) >= COOP_WAR_STATE_PREPARING) { veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_COOP_WAR); } // Human at war with someone? if(pHumanTeam->getAtWarCount(true) > 0) { if(bHostile) veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_HOSTILE_HUMAN_AT_WAR); else veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_HUMAN_AT_WAR); } // Military Aggressive Posture if(eMilitaryPosture >= AGGRESSIVE_POSTURE_MEDIUM) { if(bHostile) veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_HOSTILE_AGGRESSIVE_MILITARY); else veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_AGGRESSIVE_MILITARY); } // Land Dispute if(eLandDispute >= DISPUTE_LEVEL_STRONG) { // Expansion if(eExpansionPosture >= AGGRESSIVE_POSTURE_MEDIUM) { if(bHostile) veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_HOSTILE_AGGRESSIVE_EXPANSION); else veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_AGGRESSIVE_EXPANSION); } // Plot buying if(ePlotBuyingPosture >= AGGRESSIVE_POSTURE_MEDIUM) { if(bHostile) veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_HOSTILE_AGGRESSIVE_PLOT_BUYING); else veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_AGGRESSIVE_PLOT_BUYING); } } //////////////////////////////////////////// // Friendly Greetings //////////////////////////////////////////// if(!bHostile) { if(eVisibleApproach == CIV_APPROACH_FRIENDLY) { veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_FRIENDLY_HELLO); // Military Strength if(eMilitaryStrength >= STRENGTH_STRONG) veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_FRIENDLY_STRONG_MILITARY); // Economic Strength if(eEconomicStrength >= STRENGTH_STRONG) veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_FRIENDLY_STRONG_ECONOMY); } else veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_NEUTRAL_HELLO); } //////////////////////////////////////////// // Hostile Greetings //////////////////////////////////////////// else { veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_HOSTILE_HELLO); // Human has at least 1 fewer City if(pHuman->getNumCities() < GetPlayer()->getNumCities()) veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_HOSTILE_HUMAN_FEW_CITIES); // Human's military is weak if(eMilitaryStrength <= STRENGTH_POOR) veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_HOSTILE_HUMAN_SMALL_ARMY); // Human has attacked/killed some folks if(iNumPlayersAttacked >= 2 || iNumPlayersKilled >= 1) veValidGreetings.push_back(DIPLO_MESSAGE_GREETING_HOSTILE_HUMAN_IS_WARMONGER); } // Pick a random greeting from the valid ones (if there are any) if (veValidGreetings.size() > 0) { int iIndex = GC.getGame().getAsyncRandNum(veValidGreetings.size(), "Diplomacy AI: Picking random Greeting for AI to give to human."); DiploMessageTypes eGreetingType = veValidGreetings[iIndex]; const char* strOptionalKey = ""; bool bFoundPlayer = false; // Coop War picked? if (eGreetingType == DIPLO_MESSAGE_GREETING_COOP_WAR) { int iThirdPartyLoop = 0; // Loop until we've picked a random guy do { // Reset to beginning of list if (iThirdPartyLoop >= MAX_MAJOR_CIVS) iThirdPartyLoop = 0; if (GetCoopWarState(eHuman, (PlayerTypes) iThirdPartyLoop) >= COOP_WAR_STATE_PREPARING) { // Rand roll if (GC.getGame().getAsyncRandNum(100, "Diplomacy AI: Random coop war greeting.") < 33) { strOptionalKey = GET_PLAYER((PlayerTypes) iThirdPartyLoop).getNameKey(); bFoundPlayer = true; } } iThirdPartyLoop++; } while(!bFoundPlayer); } return GetDiploStringForMessage(eGreetingType, NO_PLAYER, strOptionalKey); } // NOTHING else fits so use generic neutral greeting return GetDiploStringForMessage(DIPLO_MESSAGE_GREETING_NEUTRAL_HELLO); } /// AI cant match human deal. const char* CvDiplomacyAI::GetOfferText(PlayerTypes ePlayer) { if(ePlayer == NO_PLAYER) { ePlayer = GC.getGame().getActivePlayer(); if(ePlayer != NO_PLAYER) { CivApproachTypes eApproach = GetSurfaceApproach(ePlayer); if(IsCantMatchDeal(ePlayer)) { SetCantMatchDeal(ePlayer, false); if(eApproach >= CIV_APPROACH_AFRAID) { return GetDiploTextFromTag("RESPONSE_TRADE_CANT_MATCH_OFFER"); } else { return GetDiploTextFromTag("RESPONSE_TRADE_CANT_MATCH_OFFER_NEGATIVE"); } } else { return GetDiploTextFromTag("RESPONSE_TRADE_AI_MAKES_OFFER"); } } else { return GetDiploTextFromTag("RESPONSE_TRADE_AI_MAKES_OFFER"); } } else { CivApproachTypes eApproach = GetSurfaceApproach(ePlayer); if(IsCantMatchDeal(ePlayer)) { SetCantMatchDeal(ePlayer, false); if(eApproach >= CIV_APPROACH_AFRAID) { return GetDiploTextFromTag("RESPONSE_TRADE_CANT_MATCH_OFFER"); } else { return GetDiploTextFromTag("RESPONSE_TRADE_CANT_MATCH_OFFER_NEGATIVE"); } } else { return GetDiploTextFromTag("RESPONSE_TRADE_AI_MAKES_OFFER"); } } } /// The AI is denouncing the human int CvDiplomacyAI::GetDenounceMessage(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); int iMessage = 0; if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) { return iMessage; } if(ePlayer == NO_PLAYER) { ePlayer = GC.getGame().getActivePlayer(); CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); bool bDogpile = false; PlayerTypes eTargetPlayerLoop; for(int iTargetPlayerLoop = 0; iTargetPlayerLoop < MAX_MAJOR_CIVS; iTargetPlayerLoop++) { eTargetPlayerLoop = (PlayerTypes) iTargetPlayerLoop; if(eTargetPlayerLoop == NO_PLAYER) continue; // Player must be valid if(!IsPlayerValid(eTargetPlayerLoop)) continue; // Don't test player Target himself if(eTargetPlayerLoop == ePlayer) continue; if(GET_PLAYER(eTargetPlayerLoop).GetDiplomacyAI()->IsDenouncedPlayer(ePlayer)) { bDogpile = true; break; } } // Guy is a different ideology if (GetDiploBalance() > 5 && IsPlayerOpposingIdeology(ePlayer) && !IsVassal(ePlayer) && !IsIgnoreIdeologyDifferences(ePlayer)) { if (m_pPlayer->GetCulture()->GetPublicOpinionPreferredIdeology() == GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree()) { if (GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree() == GD_INT_GET(POLICY_BRANCH_AUTOCRACY)) { return 1; } else if (GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree() == GD_INT_GET(POLICY_BRANCH_ORDER)) { return 2; } else if (GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree() == GD_INT_GET(POLICY_BRANCH_FREEDOM)) { return 3; } } else if (GET_PLAYER(ePlayer).GetCulture()->GetPublicOpinionPreferredIdeology() == m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree()) { if (m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree() == GD_INT_GET(POLICY_BRANCH_AUTOCRACY)) { return 4; } else if (m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree() == GD_INT_GET(POLICY_BRANCH_ORDER)) { return 5; } else if (m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree() == GD_INT_GET(POLICY_BRANCH_FREEDOM)) { return 6; } } else { return 7; } } // Guy is a warmonger else if(GetWarmongerThreat(ePlayer) >= THREAT_MAJOR && GetDiploBalance() > 5) { return 8; } // Guy is getting too friendly with our minors else if(GetMinorCivDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG && GetMinorCivCompetitiveness() > 5) { return 9; } // Guy is bullying our minors else if(GetOtherPlayerNumProtectedMinorsBullied(ePlayer) > 0 && GetMinorCivCompetitiveness() > 5) { return 10; } // Guy nuked us! else if(IsNukedBy(ePlayer)) { return 11; } // Guy is a thief! else if(BrokeSpyPromise(ePlayer) && GetLoyalty() > 5) { return 12; } // Guy is settling near us and we don't like it else if(GetLandDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG && eOpinion < CIV_OPINION_NEUTRAL) { return 13; } // Guy has bad friends! else if(IsPlayerDoFWithAnyEnemy(ePlayer) && GetDenounceWillingness() > 5) { return 14; } // Is denounced already else if(bDogpile && GetDenounceWillingness() > 5) { return 15; } // Is untrustworthy else if(IsUntrustworthy(ePlayer)) { return 16; } // Previous wars else if(eOpinion < CIV_OPINION_NEUTRAL && (GetCivilianKillerValue(ePlayer) > 0 || GetNumCitiesCapturedBy(ePlayer) > 0) && GetForgiveness() <= 5) { return 17; } // Guy is a different faith else if(!IsVassal(ePlayer) && !IsIgnoreReligionDifferences(ePlayer) && GetMeanness() > 4 && m_pPlayer->GetReligions()->OwnsReligion(true) && GET_PLAYER(ePlayer).GetReligions()->OwnsReligion(true) && IsPlayerOpposingReligion(ePlayer)) { return 18; } // Artifacts else if(BrokeNoDiggingPromise(ePlayer) && GetForgiveness() <= 5) { return 19; } // Guy built wonders we wanted else if(GetWonderDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG && GetWonderCompetitiveness() > 5) { return 20; } // Guy is pursuing victory too hard else if(GetVictoryDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG && GetVictoryCompetitiveness() > 5) { return 21; } // Guy is pursuing victory too hard else if(GetVictoryBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG && GetVictoryCompetitiveness() > 5) { return 22; } } else { CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); bool bDogpile = false; PlayerTypes eTargetPlayerLoop; for(int iTargetPlayerLoop = 0; iTargetPlayerLoop < MAX_MAJOR_CIVS; iTargetPlayerLoop++) { eTargetPlayerLoop = (PlayerTypes) iTargetPlayerLoop; if(eTargetPlayerLoop == NO_PLAYER) continue; // Player must be valid if(!IsPlayerValid(eTargetPlayerLoop)) continue; // Don't test player Target himself if(eTargetPlayerLoop == ePlayer) continue; if(GET_PLAYER(eTargetPlayerLoop).GetDiplomacyAI()->IsDenouncedPlayer(ePlayer)) { bDogpile = true; } } iMessage = 0; // Guy is a different ideology if (GetDiploBalance() > 5 && IsPlayerOpposingIdeology(ePlayer) && !IsVassal(ePlayer) && !IsIgnoreIdeologyDifferences(ePlayer)) { if (m_pPlayer->GetCulture()->GetPublicOpinionPreferredIdeology() == GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree()) { if (GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree() == GD_INT_GET(POLICY_BRANCH_AUTOCRACY)) { return 1; } else if (GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree() == GD_INT_GET(POLICY_BRANCH_ORDER)) { return 2; } else if (GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree() == GD_INT_GET(POLICY_BRANCH_FREEDOM)) { return 3; } } else if (GET_PLAYER(ePlayer).GetCulture()->GetPublicOpinionPreferredIdeology() == m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree()) { if (m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree() == GD_INT_GET(POLICY_BRANCH_AUTOCRACY)) { return 4; } else if (m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree() == GD_INT_GET(POLICY_BRANCH_ORDER)) { return 5; } else if (m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree() == GD_INT_GET(POLICY_BRANCH_FREEDOM)) { return 6; } } else { return 7; } } // Guy is a warmonger else if(GetWarmongerThreat(ePlayer) >= THREAT_MAJOR && GetDiploBalance() > 5) { return 8; } // Guy is getting too friendly with our minors else if(GetMinorCivDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG && GetMinorCivCompetitiveness() > 5) { return 9; } // Guy is getting too friendly with our minors else if(GetOtherPlayerNumProtectedMinorsBullied(ePlayer) > 0 && GetMinorCivCompetitiveness() > 5) { return 10; } // Guy nuked us! else if(IsNukedBy(ePlayer)) { return 11; } // Guy is a thief! else if(BrokeSpyPromise(ePlayer) && GetLoyalty() > 5) { return 12; } // Guy is setting near us and we don't like it else if(GetLandDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG && eOpinion < CIV_OPINION_NEUTRAL) { return 13; } // Guy has bad friends! else if(IsPlayerDoFWithAnyEnemy(ePlayer) && GetDenounceWillingness() > 5) { return 14; } // Is denounced already else if(bDogpile && GetDenounceWillingness() > 5) { return 15; } //Is untrustworthy else if(IsUntrustworthy(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID())) { return 16; } //Previous wars else if(eOpinion < CIV_OPINION_NEUTRAL && GetNumWarsFought(ePlayer) > 0 && GetForgiveness() <= 5) { return 17; } // Guy is a different faith else if (!IsVassal(ePlayer) && !IsIgnoreReligionDifferences(ePlayer) && GetMeanness() > 4 && IsPlayerOpposingReligion(ePlayer)) { return 18; } //Artifacts else if(BrokeNoDiggingPromise(ePlayer) && GetForgiveness() <= 5) { return 19; } // Guy built wonders we wanted else if(GetWonderDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG && GetWonderCompetitiveness() > 5) { return 20; } // Guy is pursuing victory too hard else if(GetVictoryDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG && GetVictoryCompetitiveness() > 5) { return 21; } // Guy is pursuing victory too hard else if(GetVictoryBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG && GetVictoryCompetitiveness() > 5) { return 22; } } return iMessage; } const char* CvDiplomacyAI::GetDenounceMessageValue(int iValue) { const char* strText = GetDiploTextFromTag("RESPONSE_WORK_AGAINST_SOMEONE"); // Guy is a different ideology if(iValue == 1) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_IDEOLOGY_AUTOCRACY"); } else if(iValue == 2) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_IDEOLOGY_ORDER"); } else if(iValue == 3) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_IDEOLOGY_FREEDOM"); } else if(iValue == 4) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_IDEOLOGY_AUTOCRACY_OTHER"); } else if(iValue == 5) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_IDEOLOGY_ORDER_OTHER"); } else if(iValue == 6) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_IDEOLOGY_FREEDOM_OTHER"); } else if(iValue == 7) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_IDEOLOGY_GENERIC"); } // Guy is a warmonger else if(iValue == 8) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_WARMONGER"); } // Guy is getting too friendly with our minors else if(iValue == 9) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_MINORS"); } // Guy is getting too friendly with our minors else if(iValue == 10) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_MINORS_BULLY"); } // Guy nuked us! else if(iValue == 11) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_NUKED"); } // Guy is a thief! else if(iValue == 12) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_SPIES"); } // Guy is setting near us and we don't like it else if(iValue == 13) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_LAND"); } // Guy has bad friends! else if(iValue == 14) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_DOF_ENEMY"); } // Is denounced already else if(iValue == 15) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_DOF_DOGPILE"); } //Is untrustworthy else if(iValue == 16) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_DOF_UNTRUSTWORTHY"); } //Previous wars else if(iValue == 17) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_UNFORGIVEABLE"); } // Guy is a different faith else if(iValue == 18) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_FAITH"); } //Artifacts else if(iValue == 19) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_DIGGING"); } // Guy built wonders we wanted else if(iValue == 20) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_WONDERS"); } // Guy is pursuing victory too hard else if(iValue == 21) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_VICTORY_DISPUTE"); } // Guy is pursuing victory too hard else if(iValue == 22) { strText = GetDiploTextFromTag("RESPONSE_DENOUNCE_VICTORY_BLOCK"); } else { strText = GetDiploTextFromTag("RESPONSE_WORK_AGAINST_SOMEONE"); } return strText; } /// AI is insulting the human const char* CvDiplomacyAI::GetInsultHumanMessage() { PlayerTypes ePlayer = GC.getGame().getActivePlayer(); CvPlayerAI& kPlayer = GET_PLAYER(ePlayer); vector veValidInsults; // They're weak militarily if (GetTargetValue(ePlayer) >= TARGET_VALUE_SOFT) veValidInsults.push_back(DIPLO_MESSAGE_INSULT_MILITARY); // We have nukes and they don't if (kPlayer.getNumNukeUnits() == 0 && m_pPlayer->getNumNukeUnits() > 0) veValidInsults.push_back(DIPLO_MESSAGE_INSULT_NUKE); // They've attacked a lot of minor civs if (GetOtherPlayerNumMinorsAttacked(ePlayer) > 2) veValidInsults.push_back(DIPLO_MESSAGE_INSULT_BULLY); // Their empire is unhappy if (kPlayer.IsEmpireVeryUnhappy()) veValidInsults.push_back(DIPLO_MESSAGE_INSULT_UNHAPPINESS); // They have fewer Cities than us if (kPlayer.getNumCities() * 2 < m_pPlayer->getNumCities() && m_pPlayer->getNumCities() > 4) veValidInsults.push_back(DIPLO_MESSAGE_INSULT_CITIES); // They have a low population if (kPlayer.getTotalPopulation() * 2 <= m_pPlayer->getTotalPopulation()) veValidInsults.push_back(DIPLO_MESSAGE_INSULT_POPULATION); // They have less Culture than us if (kPlayer.GetJONSCultureEverGeneratedTimes100() * 2 <= m_pPlayer->GetJONSCultureEverGeneratedTimes100()) veValidInsults.push_back(DIPLO_MESSAGE_INSULT_CULTURE); // Pick a random insult from the valid ones if (veValidInsults.size() > 0) { int iIndex = GC.getGame().getAsyncRandNum(veValidInsults.size(), "Picking random insult for AI to give to human."); return GetDiploStringForMessage(veValidInsults[iIndex]); } // Needed to fall back on generic insult return GetDiploStringForMessage(DIPLO_MESSAGE_INSULT_GENERIC); } /// The human declares war on the AI, how does he respond? const char* CvDiplomacyAI::GetAttackedByHumanMessage() { PlayerTypes ePlayer = GC.getGame().getActivePlayer(); CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); StrengthTypes eMilitaryStrengthComparedToUs = GetMilitaryStrengthComparedToUs(ePlayer); // Military Promise Broken // This player said he wasn't going to attack us but did anyways if(BrokeMilitaryPromise(ePlayer)) return GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_MILITARY_PROMISE_BROKEN); // Player broke our vassal agreement (declared war on vassal) if (BrokeVassalAgreement(ePlayer)) return GetDiploStringForMessage(DIPLO_MESSAGE_VASSALAGE_ATTACKED_VASSAL); //Warmonger //If this person is a major warmonger, we should not be surprised if(GetWarmongerThreat(ePlayer) >= THREAT_MAJOR) { // They are WEAKER than us if(eMilitaryStrengthComparedToUs <= STRENGTH_WEAK) return GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_STRONG_WARMONGER); // They are STRONGER than us else if(eMilitaryStrengthComparedToUs >= STRENGTH_POWERFUL) return GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_WEAK_WARMONGER); // Average strength return GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_WARMONGER); } //Ideology PolicyBranchTypes eMyBranch = m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree(); PolicyBranchTypes eTheirBranch = GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree(); if(eMyBranch != NO_POLICY_BRANCH_TYPE && eTheirBranch != NO_POLICY_BRANCH_TYPE && eMyBranch != eTheirBranch) { // Average strength return GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_IDEOLOGY_DIFFERENCE); } if(eMyBranch != NO_POLICY_BRANCH_TYPE && eTheirBranch != NO_POLICY_BRANCH_TYPE && eMyBranch == eTheirBranch) { // Average strength return GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_IDEOLOGY_SAME); } // Sad // If we felt the other player was an Ally or Friend then we're just plain sad that they attacked us if (eOpinion == CIV_OPINION_ALLY || eOpinion == CIV_OPINION_FRIEND) { return GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_SAD); } // Betrayed - TBD // This should be related to active deals, e.g. Open Borders, luxuries, etc. ; // Excited // A player with the Conquest Grand Strategy will be excited // May also look at Boldness or Offense flavor in here eventually { if(IsGoingForWorldConquest()) { // They are WEAKER than us if(eMilitaryStrengthComparedToUs <= STRENGTH_WEAK) return GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_STRONG_EXCITED); // They are STRONGER than us else if(eMilitaryStrengthComparedToUs >= STRENGTH_POWERFUL) return GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_WEAK_EXCITED); // Average strength return GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_EXCITED); } } // Hostile // This is the default response to being attacked // We are STRONGER than the person who attacked us if(eMilitaryStrengthComparedToUs <= STRENGTH_WEAK) return GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_STRONG_HOSTILE); // We are WEAKER than the person who attacked us else if(eMilitaryStrengthComparedToUs >= STRENGTH_POWERFUL) return GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_WEAK_HOSTILE); // Average strength return GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_HOSTILE); } /// The AI is declaring war on a human, what does he say? const char* CvDiplomacyAI::GetWarMessage(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); const char* strText = "OMG I HAVE NO DATA (DECLARING WAR)"; // Only show the message for the active human if(GC.getGame().getActivePlayer() == ePlayer) { CivApproachTypes eApproachHidingTrueFeelings = GetSurfaceApproach(ePlayer); StrengthTypes eMilitaryStrengthComparedToUs = GetMilitaryStrengthComparedToUs(ePlayer); // Betrayal // We were Friendly (either real or fake), so we can presume this AI is betraying if(eApproachHidingTrueFeelings == CIV_APPROACH_FRIENDLY) { // Betrayal, and we're weak if(eMilitaryStrengthComparedToUs >= STRENGTH_STRONG) return GetDiploStringForMessage(DIPLO_MESSAGE_DOW_WEAK_BETRAYAL); // Normal betrayal else return GetDiploStringForMessage(DIPLO_MESSAGE_DOW_BETRAYAL); } // Old Enemies // If we have gone to war in the past more than once, we're old enemies if(GetNumWarsFought(ePlayer) > 1) return GetDiploStringForMessage(DIPLO_MESSAGE_DOW_OLD_ENEMIES); //Warmonger //If this person is a major warmonger, we should tell them about it if(GetWarmongerThreat(ePlayer) >= THREAT_MAJOR) return GetDiploStringForMessage(DIPLO_MESSAGE_DOW_WARMONGER); //Ideology //If we're different ideologies, that's probably a big factor. PolicyBranchTypes eMyBranch = m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree(); PolicyBranchTypes eTheirBranch = GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree(); if(eMyBranch != NO_POLICY_BRANCH_TYPE && eTheirBranch != NO_POLICY_BRANCH_TYPE && eMyBranch != eTheirBranch) { return GetDiploStringForMessage(DIPLO_MESSAGE_DOW_IDEOLOGY_DIFFERENCE); } if(eMyBranch != NO_POLICY_BRANCH_TYPE && eTheirBranch != NO_POLICY_BRANCH_TYPE && eMyBranch == eTheirBranch) { return GetDiploStringForMessage(DIPLO_MESSAGE_DOW_IDEOLOGY_SAME); } // Land Dispute // If Land Dispute is Strong or higher then this is probably a strong contributor to the DoW if(GetLandDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) return GetDiploStringForMessage(DIPLO_MESSAGE_DOW_LAND); // Desperate // The AI is desperate at this point in the game, and views a DoW as one of its only ways to slow an opponent if(eMilitaryStrengthComparedToUs >= STRENGTH_STRONG) return GetDiploStringForMessage(DIPLO_MESSAGE_DOW_DESPERATE); // World Conquest // A player with the Conquest Grand Strategy brags about his goals if(IsGoingForWorldConquest()) return GetDiploStringForMessage(DIPLO_MESSAGE_DOW_WORLD_CONQUEST); // Opportunity // The AI sees an opportunity because the opponent is weak if(eMilitaryStrengthComparedToUs <= STRENGTH_POOR) return GetDiploStringForMessage(DIPLO_MESSAGE_DOW_OPPORTUNITY); // Generic DoW... probably shouldn't ever really use this return GetDiploStringForMessage(DIPLO_MESSAGE_DOW_GENERIC); } return strText; } /// The AI is breaking up with the human const char* CvDiplomacyAI::GetEndDoFMessage(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); const char* strText = "OMG I HAVE NO DATA (DECLARING WAR)"; // Guy is a warmonger if(GetWarmongerThreat(ePlayer) >= THREAT_MAJOR) strText = GetDiploTextFromTag("RESPONSE_END_WORK_WITH_US_WARMONGER"); // Guy is a different ideology else if(!IsVassal(ePlayer) && !IsIgnoreIdeologyDifferences(ePlayer) && IsPlayerOpposingIdeology(ePlayer)) strText = GetDiploTextFromTag("RESPONSE_END_WORK_WITH_US_IDEOLOGY"); // Guy is a different faith else if(!IsVassal(ePlayer) && !IsIgnoreReligionDifferences(ePlayer) && IsPlayerOpposingReligion(ePlayer)) strText = GetDiploTextFromTag("RESPONSE_END_WORK_WITH_US_FAITH"); // Guy is getting too friendly with our minors else if(GetMinorCivDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) strText = GetDiploTextFromTag("RESPONSE_END_WORK_WITH_US_MINORS"); // Guy is setting near us and we don't like it else if(GetLandDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) strText = GetDiploTextFromTag("RESPONSE_END_WORK_WITH_US_LAND"); // Guy built wonders we wanted else if(GetWonderDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) strText = GetDiploTextFromTag("RESPONSE_END_WORK_WITH_US_WONDERS"); // Guy is competing with us for victory else if(GetVictoryDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) strText = GetDiploTextFromTag("RESPONSE_END_WORK_WITH_US_VICTORY"); // Guy is pursuing victory too hard else if(GetVictoryBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG) strText = GetDiploTextFromTag("RESPONSE_END_WORK_WITH_US_BLOCK_VICTORY"); else strText = GetDiploTextFromTag("RESPONSE_END_WORK_WITH_US_DEFAULT"); return strText; } /// The AI is done working against someone const char* CvDiplomacyAI::GetEndWorkAgainstSomeoneMessage(PlayerTypes ePlayer, const Localization::String& strAgainstPlayerKey) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); const char* strText = "OMG I HAVE NO DATA (DECLARING WAR)"; if(ePlayer >=0 && ePlayer < MAX_MAJOR_CIVS) strText = GetDiploTextFromTag("RESPONSE_END_WORK_AGAINST_SOMEONE_DEFAULT", strAgainstPlayerKey); return strText; } /// Wrapper function for getting text from the Diplo Text system const char* CvDiplomacyAI::GetDiploTextFromTag(const char* strTag) { return GetDiploTextFromTag(strTag, Localization::String::Empty, Localization::String::Empty); } const char* CvDiplomacyAI::GetDiploTextFromTag(const char* strTag, const Localization::String& strOptionalKey1) { return GetDiploTextFromTag(strTag, strOptionalKey1, Localization::String::Empty); } const char* CvDiplomacyAI::GetDiploTextFromTag(const char* strTag, const Localization::String& strOptionalKey1, const Localization::String& strOptionalKey2) { // This is the leader text tag from the XML const char* strLeaderTag = GetPlayer()->getLeaderInfo().GetType(); // This has to be a member so that our strings don't go out of scope when we leave this function m_strDiploText = GC.getGame().GetDiploResponse(strLeaderTag, strTag, strOptionalKey1, strOptionalKey2); return m_strDiploText.toUTF8(); } // //////////////////////////////////// // COOP WARS // //////////////////////////////////// /// Are we able to start a coop war against eTargetPlayer? bool CvDiplomacyAI::IsValidCoopWarTarget(PlayerTypes eTargetPlayer, bool bAtWarException) { // Exclude vassals and invalid players from consideration if (GetPlayer()->IsVassalOfSomeone() || !GetPlayer()->isMajorCiv() || !GetPlayer()->isAlive() || GetPlayer()->getNumCities() <= 0) return false; if (GET_PLAYER(eTargetPlayer).IsVassalOfSomeone() || !GET_PLAYER(eTargetPlayer).isMajorCiv() || !GET_PLAYER(eTargetPlayer).isAlive() || GET_PLAYER(eTargetPlayer).getNumCities() <= 0) return false; TeamTypes eMyTeam = GetTeam(); TeamTypes eTargetTeam = GET_PLAYER(eTargetPlayer).getTeam(); // Can't be teammates with the target if (eMyTeam == eTargetTeam) return false; // Can't declare war on an unmet player if (!IsHasMet(eTargetPlayer)) return false; // Can't be a vassal or master of the target if (IsVassal(eTargetPlayer) || IsMaster(eTargetPlayer)) return false; // Can't have a Defensive Pact with the target if (IsHasDefensivePact(eTargetPlayer)) return false; // Can't have a Declaration of Friendship with the target if (IsDoFAccepted(eTargetPlayer)) return false; // Must be able to declare war on the target if (!GET_TEAM(eMyTeam).canDeclareWar(eTargetTeam, GetID())) { if (!bAtWarException || !IsAtWar(eTargetPlayer)) return false; } return true; } /// Are we able to request a coop war with eAllyPlayer against eTargetPlayer? bool CvDiplomacyAI::CanRequestCoopWar(PlayerTypes eAllyPlayer, PlayerTypes eTargetPlayer) { // Must be teammates or have a Declaration of Friendship with eAllyPlayer to start any coop wars if (!IsTeammate(eAllyPlayer) && !IsDoFAccepted(eAllyPlayer)) return false; // Do we already have a coop war planned? if (GetCoopWarState(eAllyPlayer, eTargetPlayer) == COOP_WAR_STATE_PREPARING) return false; return IsValidCoopWarTarget(eTargetPlayer, false) && GET_PLAYER(eAllyPlayer).GetDiplomacyAI()->IsValidCoopWarTarget(eTargetPlayer, false); } /// Do we want to declare war on anyone with ePlayer? bool CvDiplomacyAI::DoTestCoopWarDesire(PlayerTypes eAllyPlayer, PlayerTypes& eChosenTargetPlayer) { // We can only ask our friends. if (!IsDoFAccepted(eAllyPlayer)) return false; // If we hate them for some reason, don't bother. if (GetCivApproach(eAllyPlayer) <= CIV_APPROACH_GUARDED) return false; if (GetCivOpinion(eAllyPlayer) <= CIV_OPINION_ENEMY) return false; if (IsUntrustworthy(eAllyPlayer)) return false; // Find the best target! PlayerTypes eBestTarget = NO_PLAYER; int iBestTargetScore = /*40*/ GD_INT_GET(COOP_WAR_DESIRE_THRESHOLD); for (int iTargetLoop = 0; iTargetLoop < MAX_MAJOR_CIVS; iTargetLoop++) { PlayerTypes eTarget = (PlayerTypes) iTargetLoop; // Don't ask if they've rejected us recently. CoopWarStates eCoopWarState = GET_PLAYER(eAllyPlayer).GetDiplomacyAI()->GetCoopWarState(GetID(), eTarget); if (eCoopWarState == COOP_WAR_STATE_REJECTED || eCoopWarState == COOP_WAR_STATE_WARNED_TARGET) continue; // Must be able to make the request if (!CanRequestCoopWar(eAllyPlayer, eTarget)) continue; // Only ask if we're sufficiently upset with the target if (GetBiggestCompetitor() != eTarget && GetCivApproach(eTarget) > CIV_APPROACH_DECEPTIVE) continue; int iScore = GetCoopWarDesireScore(eAllyPlayer, eTarget, false); if (iScore > iBestTargetScore) { eBestTarget = eTarget; iBestTargetScore = iScore; } } // Found someone? if (eBestTarget != NO_PLAYER) { eChosenTargetPlayer = eBestTarget; return true; } return false; } /// What is this AI's willingness to go to war with eAllyPlayer against eTargetPlayer? int CvDiplomacyAI::GetCoopWarDesireScore(PlayerTypes eAllyPlayer, PlayerTypes eTargetPlayer, bool bAllyRequest) { // Must be a potential war target if (!IsPotentialWarTarget(eTargetPlayer)) return 0; // If we're in bad shape for war, we're not interested. if (GetPlayer()->IsNoNewWars() && !IsEndgameAggressiveTo(eTargetPlayer) && !IsCapitalCapturedBy(eTargetPlayer, true, false)) return 0; // No coop wars with players about to win the game. if (IsEndgameAggressiveTo(eAllyPlayer)) return 0; // Need a valid attack target if (!GetPlayer()->GetMilitaryAI()->HavePossibleAttackTarget(eTargetPlayer)) return 0; if (!GET_PLAYER(eAllyPlayer).isHuman(ISHUMAN_AI_UNITS) && !GET_PLAYER(eAllyPlayer).GetMilitaryAI()->HavePossibleAttackTarget(eTargetPlayer)) return 0; // Sanity checks! if (!IsWarSane(eTargetPlayer)) return 0; // We don't check our ally's war sanity as we can't know that, but let's explicitly check for a DoF, DP, and the backstabbing timer. if (GET_PLAYER(eAllyPlayer).GetDiplomacyAI()->IsDoFAccepted(eTargetPlayer)) return 0; if (GET_PLAYER(eTargetPlayer).GetDiplomacyAI()->IsDoFBroken(eAllyPlayer) && GET_PLAYER(eTargetPlayer).GetDiplomacyAI()->GetTurnsSinceDoFBroken(eAllyPlayer) < /*10*/ GD_INT_GET(DOF_BROKEN_BACKSTAB_TIMER)) return 0; if (!bAllyRequest && GET_PLAYER(eAllyPlayer).GetDiplomacyAI()->IsHasDefensivePact(eTargetPlayer)) return 0; // If we don't trust them, we're not interested. if (IsUntrustworthy(eAllyPlayer)) return 0; // No coop wars if we recently made peace. if (GetNumWarsFought(eTargetPlayer) > 0) { int iPeaceTreatyTurn = GET_TEAM(GetTeam()).GetTurnMadePeaceTreatyWithTeam(GET_PLAYER(eTargetPlayer).getTeam()); if (iPeaceTreatyTurn > -1) { int iTurnsSincePeace = GC.getGame().getGameTurn() - iPeaceTreatyTurn; int iPeaceDampenerTurns = GC.getGame().getHandicapInfo().getPeaceTreatyDampenerTurns(); if (iTurnsSincePeace < iPeaceDampenerTurns) { return 0; } } } int iScore = 0; bool bBadness = false; // How do we feel about the other player? switch (GetCivApproach(eAllyPlayer)) { case CIV_APPROACH_WAR: case CIV_APPROACH_HOSTILE: case CIV_APPROACH_GUARDED: return 0; case CIV_APPROACH_DECEPTIVE: iScore -= 5; break; case CIV_APPROACH_NEUTRAL: iScore -= 2; break; case CIV_APPROACH_AFRAID: iScore += 2; break; case CIV_APPROACH_FRIENDLY: iScore += 5; break; } switch (GetCivOpinion(eAllyPlayer)) { case CIV_OPINION_UNFORGIVABLE: case CIV_OPINION_ENEMY: return 0; case CIV_OPINION_COMPETITOR: iScore -= 5; break; case CIV_OPINION_NEUTRAL: iScore -= 2; break; case CIV_OPINION_FAVORABLE: iScore += 2; break; case CIV_OPINION_FRIEND: iScore += 5; break; case CIV_OPINION_ALLY: iScore += 10; break; } switch (GetDoFType(eAllyPlayer)) { case DOF_TYPE_UNTRUSTWORTHY: case DOF_TYPE_NEW: break; // No change. case DOF_TYPE_FRIENDS: iScore += 2; break; case DOF_TYPE_ALLIES: iScore += 5; break; case DOF_TYPE_BATTLE_BROTHERS: iScore += 10; break; } // Defensive Pact with ally? if (IsHasDefensivePact(eAllyPlayer)) { iScore += 5; } // Resurrected by ally? if (WasResurrectedBy(eAllyPlayer)) { iScore += 5; } // Coop war score with ally? iScore += GetCoopWarAgreementScore(eAllyPlayer) * 3; // How do we feel about the target? switch (GetCivApproach(eTargetPlayer)) { case CIV_APPROACH_WAR: iScore += 1000; // if already planning war, we want allies! break; case CIV_APPROACH_HOSTILE: iScore += 10; break; case CIV_APPROACH_DECEPTIVE: case CIV_APPROACH_GUARDED: iScore += 5; break; case CIV_APPROACH_AFRAID: iScore += 2; break; case CIV_APPROACH_NEUTRAL: iScore -= 5; break; case CIV_APPROACH_FRIENDLY: return 0; } switch (GetCivOpinion(eTargetPlayer)) { case CIV_OPINION_UNFORGIVABLE: iScore += 25; break; case CIV_OPINION_ENEMY: iScore += 10; break; case CIV_OPINION_COMPETITOR: iScore += 5; break; case CIV_OPINION_NEUTRAL: iScore -= 2; break; case CIV_OPINION_FAVORABLE: iScore -= 5; break; case CIV_OPINION_FRIEND: iScore -= 10; break; case CIV_OPINION_ALLY: bBadness = true; break; } switch (GetDoFType(eTargetPlayer)) { case DOF_TYPE_UNTRUSTWORTHY: iScore += 5; break; case DOF_TYPE_NEW: iScore += 2; break; case DOF_TYPE_FRIENDS: iScore -= 5; break; case DOF_TYPE_ALLIES: iScore -= 15; break; case DOF_TYPE_BATTLE_BROTHERS: bBadness = true; break; } // Liberated by target? if (IsLiberator(eTargetPlayer, false, true) || (IsCityRecentlyLiberatedBy(eTargetPlayer) && !IsEndgameAggressiveTo(eTargetPlayer) && GetPlayer()->getCitiesLost() > 0)) bBadness = true; // Coop war score with target? iScore -= GetCoopWarAgreementScore(eTargetPlayer) * 3; // Coop war already planned against this target with somebody else? if (GetGlobalCoopWarAgainstState(eTargetPlayer) >= COOP_WAR_STATE_PREPARING) { iScore += 1000; } // Already in another coop war? if (GetNumCoopWarTargets() > 0) bBadness = true; // Proximity to target? if (GetPlayer()->CanCrossOcean()) { switch (GetPlayer()->GetProximityToPlayer(eTargetPlayer)) { case PLAYER_PROXIMITY_NEIGHBORS: iScore += 5; break; case PLAYER_PROXIMITY_CLOSE: iScore += 2; break; case PLAYER_PROXIMITY_FAR: iScore -= 25; break; case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: iScore -= 50; break; } } else { switch (GetPlayer()->GetProximityToPlayer(eTargetPlayer)) { case PLAYER_PROXIMITY_NEIGHBORS: iScore += 10; break; case PLAYER_PROXIMITY_CLOSE: iScore += 5; break; case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_FAR: case PLAYER_PROXIMITY_DISTANT: iScore -= 500; break; } } // Ally's proximity to target? if (GET_PLAYER(eAllyPlayer).CanCrossOcean()) { switch (GET_PLAYER(eAllyPlayer).GetProximityToPlayer(eTargetPlayer)) { case PLAYER_PROXIMITY_NEIGHBORS: iScore += 10; break; case PLAYER_PROXIMITY_CLOSE: iScore += 5; break; case PLAYER_PROXIMITY_FAR: iScore -= 25; break; case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: iScore -= 50; break; } } else { switch (GET_PLAYER(eAllyPlayer).GetProximityToPlayer(eTargetPlayer)) { case PLAYER_PROXIMITY_NEIGHBORS: iScore += 10; break; case PLAYER_PROXIMITY_CLOSE: iScore += 5; break; case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_FAR: case PLAYER_PROXIMITY_DISTANT: iScore -= 500; break; } } // Competitor? if (GetBiggestCompetitor() == eTargetPlayer) { iScore += 25; } else if (IsMajorCompetitor(eTargetPlayer) || IsEarlyGameCompetitor(eTargetPlayer)) { iScore += 10; } else { iScore -= 10; } // Natural warmonger? if (GetPlayer()->GetPlayerTraits()->IsWarmonger()) { iScore += 10; } // World Conquest? if (IsGoingForWorldConquest() || IsCloseToWorldConquest()) { iScore += GetBoldness() * 2; } // Diplomatic Victory? if (IsGoingForDiploVictory()) { iScore -= GetDoFWillingness(); if (IsCloseToDiploVictory()) { iScore -= 25; } } // Endgame aggressive? if (IsEndgameAggressiveTo(eTargetPlayer)) { iScore += 25; // Doubly so if we're also close to winning if (IsCloseToAnyVictoryCondition()) { iScore += 25; } } // Weight for expanding too fast if (IsRecklessExpander(eTargetPlayer)) { iScore += 10; } // Weight for spamming World Wonders if (IsWonderSpammer(eTargetPlayer)) { iScore += 5; } // Weight for warmonger threat switch (GetWarmongerThreat(eTargetPlayer)) { case THREAT_NONE: case THREAT_MINOR: iScore -= 2; break; case THREAT_MAJOR: iScore += 2; break; case THREAT_SEVERE: iScore += 5; break; case THREAT_CRITICAL: iScore += 10; break; } // Weight for land dispute switch (GetLandDisputeLevel(eTargetPlayer)) { case DISPUTE_LEVEL_NONE: iScore -= 5; break; case DISPUTE_LEVEL_WEAK: iScore += 5; break; case DISPUTE_LEVEL_STRONG: iScore += 10; break; case DISPUTE_LEVEL_FIERCE: iScore += 15; break; } // Weight for victory issues switch (GetVictoryDisputeLevel(eTargetPlayer)) { case DISPUTE_LEVEL_NONE: iScore -= 2; break; case DISPUTE_LEVEL_WEAK: iScore += 2; break; case DISPUTE_LEVEL_STRONG: iScore += 5; break; case DISPUTE_LEVEL_FIERCE: iScore += 10; break; } switch (GetVictoryBlockLevel(eTargetPlayer)) { case BLOCK_LEVEL_NONE: iScore -= 2; break; case BLOCK_LEVEL_WEAK: iScore += 2; break; case BLOCK_LEVEL_STRONG: iScore += 5; break; case BLOCK_LEVEL_FIERCE: iScore += 10; break; } bool bWeHaveGoodAttackTarget = GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(eTargetPlayer); bool bTheyHaveGoodAttackTarget = GET_PLAYER(eAllyPlayer).isHuman(ISHUMAN_AI_UNITS) || GET_PLAYER(eAllyPlayer).GetMilitaryAI()->HavePreferredAttackTarget(eTargetPlayer); // Good city attack target? if (bWeHaveGoodAttackTarget) { iScore += 10; } else { iScore -= 10; } StrengthTypes eLowestStrength = GetRawMilitaryStrengthComparedToUs(eTargetPlayer); bool bStrongerOrEqual = (!IsEasyTarget(eTargetPlayer) && (eLowestStrength >= STRENGTH_AVERAGE)); if (GET_PLAYER(eAllyPlayer).GetDiplomacyAI()->GetRawMilitaryStrengthComparedToUs(eTargetPlayer) < eLowestStrength) { eLowestStrength = GET_PLAYER(eAllyPlayer).GetDiplomacyAI()->GetRawMilitaryStrengthComparedToUs(eTargetPlayer); } if (!bStrongerOrEqual) { switch (eLowestStrength) { case STRENGTH_PATHETIC: iScore -= 30; break; case STRENGTH_WEAK: iScore -= 20; break; case STRENGTH_POOR: iScore -= 10; break; case STRENGTH_AVERAGE: case STRENGTH_STRONG: case STRENGTH_POWERFUL: case STRENGTH_IMMENSE: break; } if (IsEasyTarget(eTargetPlayer) && bWeHaveGoodAttackTarget) { iScore -= 15; } } else { switch (eLowestStrength) { case STRENGTH_PATHETIC: iScore += 20; break; case STRENGTH_WEAK: iScore += 10; break; case STRENGTH_POOR: iScore += 5; break; case STRENGTH_AVERAGE: iScore -= 5; break; case STRENGTH_STRONG: iScore -= 10; break; case STRENGTH_POWERFUL: iScore -= 20; break; case STRENGTH_IMMENSE: iScore -= 30; break; } if (GET_PLAYER(eAllyPlayer).GetDiplomacyAI()->IsEasyTarget(eTargetPlayer) && bTheyHaveGoodAttackTarget) { iScore += 15; } // Have they helped us in war before? iScore += GetCoopWarAgreementScore(eAllyPlayer) * 3; iScore += GetCommonFoeScore(eAllyPlayer) / /*-4*/ max(GD_INT_GET(OPINION_WEIGHT_COMMON_FOE_MAX) / 25, 1); } // Are we in danger of getting conquered by the target? int iDangerMod = GetPlayerNumMajorsConquered(eTargetPlayer) + GET_PLAYER(eTargetPlayer).GetNumCapitalCities() + GET_TEAM(GET_PLAYER(eTargetPlayer).getTeam()).GetNumVassals(); iDangerMod += ((int)GET_PLAYER(eTargetPlayer).GetProximityToPlayer(GetID()) - 3); // -0 for neighbors, -1 for close, -2 for far, -3 for distant if (iDangerMod > 0) { // Avoid direct military action if they're stronger than us ... favor coop wars iDangerMod -= ((int)GetMilitaryStrengthComparedToUs(eTargetPlayer) - 3); if (IsEasyTarget(eTargetPlayer) && bWeHaveGoodAttackTarget) { iDangerMod += 2; } // Factor in our ally's strength as well (prefer stronger allies!) iDangerMod += ((int)GET_PLAYER(eAllyPlayer).GetDiplomacyAI()->GetTargetValue(eTargetPlayer) - 3); if (GET_PLAYER(eAllyPlayer).GetDiplomacyAI()->IsEasyTarget(eTargetPlayer) && bTheyHaveGoodAttackTarget) { iDangerMod += 2; } // Have they helped us in war before? if (iDangerMod > 0) { iDangerMod += GetCoopWarAgreementScore(eAllyPlayer); iDangerMod += GetCommonFoeScore(eAllyPlayer) / /*-20*/ max(GD_INT_GET(OPINION_WEIGHT_COMMON_FOE_MAX) / 5, 1); } // Dangerous player is a neighbor, other player is at least close if (GET_PLAYER(eTargetPlayer).GetProximityToPlayer(GetID()) == PLAYER_PROXIMITY_NEIGHBORS && GET_PLAYER(eAllyPlayer).GetProximityToPlayer(eTargetPlayer) >= PLAYER_PROXIMITY_CLOSE) { iScore += iDangerMod * 5; } // Dangerous player and other player are close, but dangerous player is not a neighbor...yet else if (GET_PLAYER(eTargetPlayer).GetProximityToPlayer(GetID()) >= PLAYER_PROXIMITY_CLOSE && GET_PLAYER(eAllyPlayer).GetProximityToPlayer(eTargetPlayer) >= PLAYER_PROXIMITY_CLOSE) { iScore += iDangerMod * 4; } // Dangerous player OR other player are close else if (GET_PLAYER(eTargetPlayer).GetProximityToPlayer(GetID()) >= PLAYER_PROXIMITY_CLOSE || GET_PLAYER(eAllyPlayer).GetProximityToPlayer(eTargetPlayer) >= PLAYER_PROXIMITY_CLOSE) { if (m_pPlayer->CanCrossOcean() || GET_PLAYER(eAllyPlayer).CanCrossOcean()) iScore += iDangerMod * 2; } // Dangerous player and other player are far else if (GET_PLAYER(eTargetPlayer).GetProximityToPlayer(GetID()) == PLAYER_PROXIMITY_FAR && GET_PLAYER(eAllyPlayer).GetProximityToPlayer(eTargetPlayer) == PLAYER_PROXIMITY_FAR) { if (m_pPlayer->CanCrossOcean()) iScore += iDangerMod; } } if (iScore <= 0) return 0; // Scale weight by target value if (IsMajorCompetitor(eTargetPlayer) || IsEarlyGameCompetitor(eTargetPlayer) || (bWeHaveGoodAttackTarget && IsEasyTarget(eTargetPlayer))) { switch (GetTargetValue(eTargetPlayer)) { case TARGET_VALUE_IMPOSSIBLE: iScore *= 40; break; case TARGET_VALUE_BAD: iScore *= 60; break; case TARGET_VALUE_DIFFICULT: iScore *= 80; break; case TARGET_VALUE_AVERAGE: iScore *= 100; break; case TARGET_VALUE_FAVORABLE: iScore *= 150; break; case TARGET_VALUE_SOFT: iScore *= 200; break; case TARGET_VALUE_CAKEWALK: iScore *= 300; break; } } else { switch (GetTargetValue(eTargetPlayer)) { case TARGET_VALUE_IMPOSSIBLE: case TARGET_VALUE_BAD: case TARGET_VALUE_DIFFICULT: bBadness = true; break; case TARGET_VALUE_AVERAGE: iScore *= 75; break; case TARGET_VALUE_FAVORABLE: iScore *= 125; break; case TARGET_VALUE_SOFT: iScore *= 150; break; case TARGET_VALUE_CAKEWALK: iScore *= 200; break; } } // Scale weight by economic value of closest city to us CvCity* pClosestCityToUs = GetPlayer()->GetClosestCityToUsByPlots(eTargetPlayer); if (pClosestCityToUs != NULL) { int iMedianEconomicPower = GC.getGame().getMedianEconomicValue(); int iLocalEconomicPower = max(pClosestCityToUs->getEconomicValue(eTargetPlayer), pClosestCityToUs->getEconomicValue(GetID())); // Cities are rated on a percentage scale, where 0 = worthless, 100 = equal value with median city, 200 = twice the value of the median city. int iEconomicValue = range(((iLocalEconomicPower * 100) / max(1, iMedianEconomicPower)), 10, 200); if (pClosestCityToUs->IsOriginalMajorCapital()) { if (IsCloseToWorldConquest()) { iEconomicValue *= 4; } else if (IsGoingForWorldConquest()) { iEconomicValue *= 3; } else { iEconomicValue *= 2; } } if (iEconomicValue > 200) iEconomicValue = 200; iScore *= iEconomicValue; iScore /= 10000; } else { return 0; } if (bBadness && !m_pPlayer->HasAnyOffensiveOperationsAgainstPlayer(eTargetPlayer) && !AvoidExchangesWithPlayer(eTargetPlayer, /*bWarOnly*/ true)) return 0; return iScore; } /// Processes this player's response to a coop war request CoopWarStates CvDiplomacyAI::RespondToCoopWarRequest(PlayerTypes eAskingPlayer, PlayerTypes eTargetPlayer) { CoopWarStates eResponse = COOP_WAR_STATE_REJECTED; CoopWarStates eCurrentState = GetCoopWarState(eAskingPlayer, eTargetPlayer); // Failsafe if (!GET_PLAYER(eAskingPlayer).GetDiplomacyAI()->CanRequestCoopWar(GetID(), eTargetPlayer)) { return COOP_WAR_STATE_REJECTED; } bool bBold = GetBoldness() > 7 || GetPlayer()->GetPlayerTraits()->IsWarmonger() || (IsCompetingForVictory() && IsGoingForWorldConquest()); bool bCloseToTarget = GetPlayer()->CanCrossOcean() ? GetPlayer()->GetProximityToPlayer(eTargetPlayer) >= PLAYER_PROXIMITY_CLOSE : GetPlayer()->GetProximityToPlayer(eTargetPlayer) == PLAYER_PROXIMITY_NEIGHBORS; bool bGoodAttackTarget = GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(eTargetPlayer); // Teammates will always agree when a human asks if (IsTeammate(eAskingPlayer) || IsAIMustAcceptHumanDiscussRequests()) { if (bBold && bCloseToTarget && GetTargetValue(eTargetPlayer) >= TARGET_VALUE_FAVORABLE) { eResponse = COOP_WAR_STATE_ONGOING; } else if (bCloseToTarget && IsEasyTarget(eTargetPlayer) && bGoodAttackTarget) { eResponse = COOP_WAR_STATE_ONGOING; } else if (bCloseToTarget && IsEndgameAggressiveTo(eTargetPlayer) && (GetTargetValue(eTargetPlayer) >= TARGET_VALUE_FAVORABLE || IsEasyTarget(eTargetPlayer) || bGoodAttackTarget)) { eResponse = COOP_WAR_STATE_ONGOING; } else { eResponse = COOP_WAR_STATE_PREPARING; } } // Not teammates else { // A coop war is desirable if (GetCoopWarDesireScore(eAskingPlayer, eTargetPlayer, true) >= /*40*/ GD_INT_GET(COOP_WAR_DESIRE_THRESHOLD)) { if (bBold && bCloseToTarget && GetTargetValue(eTargetPlayer) >= TARGET_VALUE_FAVORABLE) { eResponse = COOP_WAR_STATE_ONGOING; } else if (bCloseToTarget && IsEasyTarget(eTargetPlayer) && GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(eTargetPlayer)) { eResponse = COOP_WAR_STATE_ONGOING; } else if (bCloseToTarget && IsEndgameAggressiveTo(eTargetPlayer) && (GetTargetValue(eTargetPlayer) >= TARGET_VALUE_FAVORABLE || IsEasyTarget(eTargetPlayer) || bGoodAttackTarget)) { eResponse = COOP_WAR_STATE_ONGOING; } else { eResponse = COOP_WAR_STATE_PREPARING; } } else if (eCurrentState != COOP_WAR_STATE_REJECTED && eCurrentState != COOP_WAR_STATE_WARNED_TARGET && IsCoopWarRequestUnacceptable(eAskingPlayer, eTargetPlayer)) { eResponse = COOP_WAR_STATE_WARNED_TARGET; } } switch (eResponse) { case COOP_WAR_STATE_ONGOING: ChangeRecentAssistValue(eAskingPlayer, 300); GET_PLAYER(eAskingPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(GetID(), 300); SetCoopWarState(eAskingPlayer, eTargetPlayer, COOP_WAR_STATE_PREPARING, true); GET_PLAYER(eAskingPlayer).GetDiplomacyAI()->SetCoopWarState(GetID(), eTargetPlayer, COOP_WAR_STATE_PREPARING, true); DoStartCoopWar(eAskingPlayer, eTargetPlayer); break; case COOP_WAR_STATE_PREPARING: ChangeRecentAssistValue(eAskingPlayer, 300); GET_PLAYER(eAskingPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(GetID(), 300); SetCoopWarState(eAskingPlayer, eTargetPlayer, eResponse); GET_PLAYER(eAskingPlayer).GetDiplomacyAI()->SetCoopWarState(GetID(), eTargetPlayer, eResponse); break; case COOP_WAR_STATE_WARNED_TARGET: SetCoopWarState(eAskingPlayer, eTargetPlayer, eResponse); DoWarnCoopWarTarget(eAskingPlayer, eTargetPlayer); break; default: if (eCurrentState != COOP_WAR_STATE_WARNED_TARGET) { SetCoopWarState(eAskingPlayer, eTargetPlayer, COOP_WAR_STATE_REJECTED); } break; } if (eResponse == COOP_WAR_STATE_ONGOING || eResponse == COOP_WAR_STATE_PREPARING) { // Update approach to WAR SetCivApproach(eTargetPlayer, CIV_APPROACH_WAR); GET_PLAYER(eAskingPlayer).GetDiplomacyAI()->SetCivApproach(eTargetPlayer, CIV_APPROACH_WAR); } return eResponse; } /// We rejected eAskingPlayer's request to go to war with eTargetPlayer, but should we warn the target? bool CvDiplomacyAI::IsCoopWarRequestUnacceptable(PlayerTypes eAskingPlayer, PlayerTypes eTargetPlayer) const { CivApproachTypes eApproachTowardsAsker = GetCivApproach(eAskingPlayer); CivOpinionTypes eOpinionOfAsker = GetCivOpinion(eAskingPlayer); CivApproachTypes eApproachTowardsTarget = GetCivApproach(eTargetPlayer); CivOpinionTypes eOpinionOfTarget = GetCivOpinion(eTargetPlayer); // Do we want war against the target? if (eApproachTowardsTarget == CIV_APPROACH_WAR || GetDemandTargetPlayer() == eTargetPlayer) { return false; } // Backstabbed by the target? if (IsPlayerCapturedCapital(eTargetPlayer) || IsPlayerCapturedHolyCity(eTargetPlayer) || IsUntrustworthy(eTargetPlayer) || WasEverBackstabbedBy(eTargetPlayer)) { return false; } // Do we want war against the asker? if (eApproachTowardsAsker == CIV_APPROACH_WAR || GetDemandTargetPlayer() == eAskingPlayer) { return true; } // Backstabbed by the asker? if (IsPlayerCapturedCapital(eAskingPlayer) || IsPlayerCapturedHolyCity(eAskingPlayer) || IsUntrustworthy(eAskingPlayer)) { return true; } // We really like the asker? if (WasResurrectedBy(eAskingPlayer) || eOpinionOfAsker == CIV_OPINION_ALLY) { return false; } else if (eApproachTowardsAsker > CIV_APPROACH_AFRAID && eOpinionOfAsker >= CIV_OPINION_FRIEND) { if (GetMostValuableFriend() == eAskingPlayer || GetMostValuableAlly() == eAskingPlayer) { return false; } } // We really like the target? if (WasResurrectedBy(eTargetPlayer) || eOpinionOfTarget == CIV_OPINION_ALLY) { return true; } else if (eApproachTowardsTarget > CIV_APPROACH_AFRAID && eOpinionOfTarget >= CIV_OPINION_FRIEND) { if (GetMostValuableFriend() == eTargetPlayer || GetMostValuableAlly() == eTargetPlayer) { return true; } } // Hate the target or fear the asker? if (eApproachTowardsAsker == CIV_APPROACH_AFRAID || eApproachTowardsTarget <= CIV_APPROACH_GUARDED || eOpinionOfTarget <= CIV_OPINION_ENEMY) { return false; } // Hate the asker? if (eApproachTowardsAsker <= CIV_APPROACH_GUARDED || eOpinionOfAsker <= CIV_OPINION_ENEMY) { return true; } // Competitive with asker? if (IsMajorCompetitor(eAskingPlayer)) { return true; } // Competitive with target? if (IsMajorCompetitor(eTargetPlayer)) { return false; } // Any flavors that should influence the decision? if (GetLoyalty() > 6 && eOpinionOfTarget >= CIV_OPINION_FRIEND) { return true; } if (GetDiploBalance() > 7 && eOpinionOfTarget >= CIV_OPINION_FAVORABLE) { return true; } // Otherwise, warn the target if we like them more than the asker if (eOpinionOfTarget > eOpinionOfAsker) { if (eOpinionOfTarget >= CIV_OPINION_FAVORABLE || eOpinionOfAsker <= CIV_OPINION_COMPETITOR) { return true; } } return false; } /// Warn the target of a coop war requested by eAskingPlayer void CvDiplomacyAI::DoWarnCoopWarTarget(PlayerTypes eAskingPlayer, PlayerTypes eTargetPlayer) { PlayerTypes eMyPlayer = GetID(); TeamTypes eAskingTeam = GET_PLAYER(eAskingPlayer).getTeam(); TeamTypes eTargetTeam = GET_PLAYER(eTargetPlayer).getTeam(); for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // If human was target, send message if (GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) { if (GET_PLAYER(eLoopPlayer).getTeam() == eTargetTeam && !CvPreGame::isNetworkMultiplayerGame() && GC.getGame().getActivePlayer() == eLoopPlayer && !MOD_DIPLOAI_SHUT_UP) { const char* szPlayerName = NULL; if (GC.getGame().isGameMultiPlayer() && GET_PLAYER(eAskingPlayer).isHuman(ISHUMAN_UI)) { szPlayerName = GET_PLAYER(eAskingPlayer).getNickName(); } else { szPlayerName = GET_PLAYER(eAskingPlayer).getNameKey(); } DLLUI->SetForceDiscussionModeQuitOnBack(true); // Set force quit so that when discuss mode pops up the Back button won't go to leader root const char* strText = GetDiploStringForMessage(DIPLO_MESSAGE_COOP_WAR_WARNING, NO_PLAYER, szPlayerName); gDLL->GameplayDiplomacyAILeaderMessage(GetID(), DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } continue; } // Process global reactions for AI players if (IsPlayerValid(eLoopPlayer, true) && eLoopPlayer != eMyPlayer) { CvDiplomacyAI* pDiplo = GET_PLAYER(eLoopPlayer).GetDiplomacyAI(); TeamTypes eLoopTeam = GET_PLAYER(eLoopPlayer).getTeam(); bool bTargetSide = false; bool bAskerSide = false; bool bTargetFriend = false; bool bAskerFriend = false; bool bLikesTarget = !pDiplo->WasEverBackstabbedBy(eTargetPlayer) && pDiplo->GetCivApproach(eTargetPlayer) > CIV_APPROACH_AFRAID && pDiplo->GetBiggestCompetitor() != eTargetPlayer; bool bLikesAsker = !pDiplo->WasEverBackstabbedBy(eAskingPlayer) && pDiplo->GetCivApproach(eAskingPlayer) > CIV_APPROACH_AFRAID && pDiplo->GetBiggestCompetitor() != eAskingPlayer; if (eLoopTeam == eTargetTeam || GET_TEAM(eLoopTeam).IsHasDefensivePact(eTargetTeam)) { bTargetSide = true; } else if (eLoopTeam == eAskingTeam) { bAskerSide = true; } else if (bLikesTarget && pDiplo->IsDoFAccepted(eTargetPlayer) && !pDiplo->IsWantsToEndDoFWithPlayer(eTargetPlayer)) { bTargetFriend = true; } else if (bLikesAsker && ((pDiplo->IsDoFAccepted(eAskingPlayer) && !pDiplo->IsWantsToEndDoFWithPlayer(eAskingPlayer)) || (GET_TEAM(eLoopTeam).IsHasDefensivePact(eAskingTeam) && !pDiplo->IsWantsToEndDefensivePactWithPlayer(eAskingPlayer)))) { bAskerFriend = true; } if (bTargetSide || bTargetFriend) { // Target is mad at asker if (bTargetSide) { pDiplo->ChangeNumTimesTheyPlottedAgainstUs(eAskingPlayer, 1); pDiplo->ChangeRecentAssistValue(eAskingPlayer, -300); } else { pDiplo->ChangeRecentAssistValue(eAskingPlayer, -150); } // Target is happy with reporter pDiplo->ChangeNumTimesIntrigueSharedBy(eMyPlayer, 1); int iAssistBonus = 0; if (eLoopPlayer == eTargetPlayer) { iAssistBonus = 300; } else { switch (GET_PLAYER(eLoopPlayer).GetProximityToPlayer(eAskingPlayer)) { case PLAYER_PROXIMITY_NEIGHBORS: iAssistBonus = 150; break; case PLAYER_PROXIMITY_CLOSE: iAssistBonus = 100; break; case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_FAR: case PLAYER_PROXIMITY_DISTANT: iAssistBonus = 50; break; } if (eLoopTeam == eTargetTeam) { iAssistBonus += 50; } } pDiplo->ChangeRecentAssistValue(eMyPlayer, iAssistBonus); } else if (bAskerSide || bAskerFriend) { // Asker is mad at reporter if (bAskerSide) { pDiplo->ChangeRecentAssistValue(eMyPlayer, -300); pDiplo->ChangeCoopWarAgreementScore(eMyPlayer, -2); //todo: consider denouncing here } else if (!pDiplo->IsTeammate(eMyPlayer) && !pDiplo->IsDoFAccepted(eMyPlayer) && !pDiplo->IsHasDefensivePact(eMyPlayer)) { int iAssistPenalty = AdjustConditionalModifier(-100, GetForgiveness(), true); pDiplo->ChangeRecentAssistValue(eMyPlayer, iAssistPenalty); } } } } } void CvDiplomacyAI::CancelCoopWarsAgainstPlayer(PlayerTypes ePlayer, bool bNotify) { for (int iThirdPartyLoop = 0; iThirdPartyLoop < MAX_MAJOR_CIVS; iThirdPartyLoop++) { PlayerTypes eThirdParty = (PlayerTypes) iThirdPartyLoop; if (eThirdParty == GetID() || GET_PLAYER(eThirdParty).getTeam() == GET_PLAYER(ePlayer).getTeam()) continue; if (!GET_PLAYER(ePlayer).isAlive() || GetCoopWarState(eThirdParty, ePlayer) >= COOP_WAR_STATE_PREPARING) { if (bNotify && GetCoopWarState(eThirdParty, ePlayer) == COOP_WAR_STATE_PREPARING) { CvNotifications* pNotify = GetPlayer()->GetNotifications(); if (pNotify) { Localization::String strSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_S"); Localization::String strText = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_INVALID_TARGET"); strText << GET_PLAYER(ePlayer).getCivilizationShortDescriptionKey(); strText << GET_PLAYER(eThirdParty).getCivilizationShortDescriptionKey(); pNotify->Add(NOTIFICATION_DIPLOMACY_DECLARATION, strText.toUTF8(), strSummary.toUTF8(), -1, -1, -1); } pNotify = GET_PLAYER(eThirdParty).GetNotifications(); if (pNotify) { Localization::String strSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_S"); Localization::String strText = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_INVALID_TARGET"); strText << GET_PLAYER(ePlayer).getCivilizationShortDescriptionKey(); strText << GetPlayer()->getCivilizationShortDescriptionKey(); pNotify->Add(NOTIFICATION_DIPLOMACY_DECLARATION, strText.toUTF8(), strSummary.toUTF8(), -1, -1, -1); } } SetCoopWarState(eThirdParty, ePlayer, NO_COOP_WAR_STATE); GET_PLAYER(eThirdParty).GetDiplomacyAI()->SetCoopWarState(GetID(), ePlayer, NO_COOP_WAR_STATE); } } } void CvDiplomacyAI::CancelCoopWarsWithPlayer(PlayerTypes ePlayer, bool bPenalty) { bool bNotified = GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY); // Don't notify when a coop war is cancelled because a human took the seat of an AI player for (int iThirdPartyLoop = 0; iThirdPartyLoop < MAX_MAJOR_CIVS; iThirdPartyLoop++) { PlayerTypes eThirdParty = (PlayerTypes) iThirdPartyLoop; if (GET_PLAYER(eThirdParty).getTeam() == GetTeam()) continue; if (IsPlayerValid(eThirdParty) && GetCoopWarState(ePlayer, eThirdParty) >= COOP_WAR_STATE_PREPARING) { if (GetCoopWarState(ePlayer, eThirdParty) == COOP_WAR_STATE_PREPARING) { if (bPenalty) { SetBrokeCoopWarPromise(ePlayer, true); ChangeCoopWarAgreementScore(ePlayer, -2); ChangeRecentAssistValue(ePlayer, -300); } if (!bNotified) { CvNotifications* pNotify = GetPlayer()->GetNotifications(); if (pNotify) { Localization::String strSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_S"); Localization::String strText = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_AGGRESSION"); strText << GET_PLAYER(ePlayer).getCivilizationShortDescriptionKey(); pNotify->Add(NOTIFICATION_DIPLOMACY_DECLARATION, strText.toUTF8(), strSummary.toUTF8(), -1, -1, -1); } pNotify = GET_PLAYER(ePlayer).GetNotifications(); if (pNotify) { Localization::String strSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_S"); Localization::String strText = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_AGGRESSION"); strText << GetPlayer()->getCivilizationShortDescriptionKey(); pNotify->Add(NOTIFICATION_DIPLOMACY_DECLARATION, strText.toUTF8(), strSummary.toUTF8(), -1, -1, -1); } bNotified = true; } } SetCoopWarState(ePlayer, eThirdParty, NO_COOP_WAR_STATE); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetCoopWarState(GetID(), eThirdParty, NO_COOP_WAR_STATE); } } } void CvDiplomacyAI::CancelAllCoopWars() { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; TeamTypes eLoopTeam = GET_PLAYER(eLoopPlayer).getTeam(); if (!GET_PLAYER(eLoopPlayer).isAlive() || eLoopPlayer == GetID()) continue; CvNotifications* pNotify = GET_PLAYER(eLoopPlayer).GetNotifications(); bool bNotified = false; for (int iThirdPartyLoop = 0; iThirdPartyLoop < MAX_MAJOR_CIVS; iThirdPartyLoop++) { PlayerTypes eThirdParty = (PlayerTypes) iThirdPartyLoop; TeamTypes eThirdPartyTeam = GET_PLAYER(eThirdParty).getTeam(); if (eThirdPartyTeam == GetTeam() || eThirdPartyTeam == eLoopTeam) continue; if (pNotify && !bNotified && GetCoopWarState(eLoopPlayer, eThirdParty) == COOP_WAR_STATE_PREPARING) { Localization::String strSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_S"); Localization::String strText = Localization::Lookup("TXT_KEY_NOTIFICATION_COOP_WAR_BROKEN_ELIMINATED"); strText << GetPlayer()->getCivilizationShortDescriptionKey(); pNotify->Add(NOTIFICATION_DIPLOMACY_DECLARATION, strText.toUTF8(), strSummary.toUTF8(), -1, -1, -1); bNotified = true; } if (!GetPlayer()->isAlive() || GetCoopWarState(eLoopPlayer, eThirdParty) >= COOP_WAR_STATE_PREPARING) { SetCoopWarState(eLoopPlayer, eThirdParty, NO_COOP_WAR_STATE); GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->SetCoopWarState(GetID(), eThirdParty, NO_COOP_WAR_STATE); } } } } ///////////////////////////////////////////////////////// // Human Demand ///////////////////////////////////////////////////////// /// Human made a demand against this AI, handle everything that means void CvDiplomacyAI::DoDemandMade(PlayerTypes ePlayer, DemandResponseTypes eResponse) { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return; // Don't apply further penalties if it's too soon since the last demand if (eResponse == DEMAND_RESPONSE_REFUSE_TOO_SOON) return; // We accepted the demand if (eResponse == DEMAND_RESPONSE_ACCEPT) { ChangeNumDemandsMade(ePlayer, 1); GET_PLAYER(ePlayer).GetDiplomacyAI()->ChangeNumConsecutiveDemandsTheyAccepted(GetID(), 1); // See how long it'll be before we might agree to another demand int iNumTurns = /*20*/ GD_INT_GET(DEMAND_TURN_LIMIT_MIN); iNumTurns += GC.getGame().randRangeInclusive(0, /*10*/ GD_INT_GET(DEMAND_TURN_LIMIT_RAND), CvSeeder::fromRaw(0x0af32abc).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); SetDemandTooSoonNumTurns(ePlayer, iNumTurns); DoReevaluatePlayer(ePlayer); } // We refused the demand else { GET_PLAYER(ePlayer).GetDiplomacyAI()->SetNumConsecutiveDemandsTheyAccepted(GetID(), 0); // Reset the turn counter for the penalty SetDemandMadeTurn(ePlayer, GC.getGame().getGameTurn()); // Prevent exploit wherein the human spams the demand button to reduce AI Opinion - only allow for one penalty unless the demand is accepted if (GetNumDemandsMade(ePlayer) <= 0) { SetNumDemandsMade(ePlayer, 1); // Did our master protect us from this demand? This counts for protection value! if (eResponse == DEMAND_RESPONSE_REFUSE_PROTECTED_BY_MASTER) { StrengthTypes eDemanderStrength = GetMilitaryStrengthComparedToUs(ePlayer); TeamTypes eOurMaster = GET_TEAM(GetTeam()).GetMaster(); vector vMasterTeam = GET_TEAM(eOurMaster).getPlayers(); for (size_t i=0; i < vMasterTeam.size(); i++) { // Find all players on the master's team who would have triggered this response, see CvDealAI::GetDemandResponse() CvPlayer* pMaster = &GET_PLAYER(vMasterTeam[i]); if (!pMaster->isAlive() || pMaster->getNumCities() == 0) continue; if (pMaster->GetDiplomacyAI()->GetRawMilitaryStrengthComparedToUs(ePlayer) > STRENGTH_AVERAGE) continue; // No bonus if they have a penalty for failing to protect us. if (GetVassalProtectValue(vMasterTeam[i]) < 0) continue; if (pMaster->GetProximityToPlayer(ePlayer) == PLAYER_PROXIMITY_NEIGHBORS || pMaster->GetProximityToPlayer(GetID()) >= GET_PLAYER(ePlayer).GetProximityToPlayer(GetID())) { // Note that the master's strength will be factored into the vassal's strength evaluation of the demander already, so it's OK to be a little generous here int iPercentageOfMaxProtectValue = 0; switch (eDemanderStrength) { case STRENGTH_WEAK: iPercentageOfMaxProtectValue = 5; break; case STRENGTH_POOR: iPercentageOfMaxProtectValue = 10; break; case STRENGTH_AVERAGE: iPercentageOfMaxProtectValue = 15; break; case STRENGTH_STRONG: iPercentageOfMaxProtectValue = 20; break; case STRENGTH_POWERFUL: iPercentageOfMaxProtectValue = 25; break; case STRENGTH_IMMENSE: iPercentageOfMaxProtectValue = 30; break; default: iPercentageOfMaxProtectValue = 3; break; } // Protection from demands can't exceed 50% of max protect value int iMaxValue = GetVassalProtectValue(vMasterTeam[i]); int iBonus = iMaxValue * iPercentageOfMaxProtectValue / 100; int iMaxBonus = iMaxValue * 50 / 100; if (GetVassalProtectValue(vMasterTeam[i]) + iBonus >= iMaxBonus) SetVassalProtectValue(vMasterTeam[i], iMaxBonus); else ChangeVassalProtectValue(vMasterTeam[i], iBonus); } } } } } } ///////////////////////////////////////////////////////// // Don't Settle Request ///////////////////////////////////////////////////////// /// Will this AI agree not to settle near ePlayer? bool CvDiplomacyAI::IsDontSettleAcceptable(PlayerTypes ePlayer) { // Debug mode if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && IsAIMustAcceptHumanDiscussRequests()) return true; // Don't agree if AI has no cities, as AI will break the promise in order to settle their capital // Also prevents a cheesy exploit: trying to get the AI to settle their initial capital in a worse position if (GetPlayer()->getNumCities() == 0) return false; // Always acceptable if they resurrected or liberated us if (IsLiberator(ePlayer, true, true)) return true; CivApproachTypes eApproach = GetCivApproach(ePlayer); CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); // If player is afraid, always say yes if (eApproach == CIV_APPROACH_AFRAID) return true; // If we control 33%+ of other players' original capitals, don't make a promise unless we like them if (GetPlayer()->GetFractionOriginalCapitalsUnderControl() >= 33) { if (eApproach != CIV_APPROACH_FRIENDLY || eOpinion < CIV_OPINION_FRIEND) { if (!IsDoFAccepted(ePlayer) && !IsHasDefensivePact(ePlayer) && GetCoopWarAgreementScore(ePlayer) <= 0) { return false; } } } // Refuse all promises if close to world conquest and they still have original capitals if (IsCloseToWorldConquest() && !IsMaster(ePlayer) && (GET_PLAYER(ePlayer).GetCapitalConqueror() != NO_PLAYER || GET_PLAYER(ePlayer).GetNumCapitalCities() > 0)) return false; // If player is Hostile or planning War, always say no if (eApproach <= CIV_APPROACH_HOSTILE) return false; // If player is an enemy or unforgivable, always say no if (eOpinion <= CIV_OPINION_ENEMY) return false; // If player is a backstabber, always say no if (IsUntrustworthy(ePlayer)) return false; // If player is plotting against us, always say no if (GetNumTimesTheyPlottedAgainstUs(ePlayer) > 0) return false; // If they've broken or ignored an expansion promise, always say no if (IgnoredExpansionPromise(ePlayer) || BrokeExpansionPromise(ePlayer)) return false; // Acceptable if we're friends if ((IsDoFAccepted(ePlayer) && !IsWantsToEndDoFWithPlayer(ePlayer)) || (IsHasDefensivePact(ePlayer) && !IsWantsToEndDefensivePactWithPlayer(ePlayer))) return true; else if (eApproach == CIV_APPROACH_FRIENDLY) { if (GetDoFType(ePlayer) >= DOF_TYPE_ALLIES || GetMostValuableFriend() == ePlayer || GetMostValuableAlly() == ePlayer || GetCoopWarAgreementScore(ePlayer) > 0 || GetCivOpinion(ePlayer) == CIV_OPINION_ALLY) return true; } // If we're fiercely competitive, always say no if (IsCompetingForVictory()) { if (GetVictoryDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG || GetVictoryBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG || (GetPlayer()->GetCurrentEra() < 3 && IsEarlyGameCompetitor(ePlayer)) || (GetPlayer()->GetCurrentEra() >= 3 && GetBiggestCompetitor() == ePlayer) || GetPrimeLeagueCompetitor() == ePlayer || IsEndgameAggressiveTo(ePlayer)) return false; } // If AI is done expanding, always say yes static EconomicAIStrategyTypes eEnoughExpansion = (EconomicAIStrategyTypes)GC.getInfoTypeForString("ECONOMICAISTRATEGY_ENOUGH_EXPANSION"); if (GetPlayer()->GetEconomicAI()->IsUsingStrategy(eEnoughExpansion)) return true; int iExpansionFlavor = m_pPlayer->GetFlavorManager()->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_EXPANSION")); // Default threshold // EX: 8 - 10 Expansion = -2 * 5 = -10% // EX: 8 - 7 Expansion = 1 * 5 = 5% // EX: 8 - 5 Expansion = 3 * 5 = 15% // EX: 8 - 2 Expansion = 6 * 5 = 30% int iThreshold = (/*8*/ GD_INT_GET(DONT_SETTLE_FLAVOR_BASE) - iExpansionFlavor) * /*5*/ GD_INT_GET(DONT_SETTLE_FLAVOR_MULTIPLIER); // If player is Friendly, add weight if (eApproach == CIV_APPROACH_FRIENDLY) iThreshold += /*30*/ GD_INT_GET(DONT_SETTLE_FRIENDLY); int iMilitaryMod = 0; // Military Strength compared to us switch(GetMilitaryStrengthComparedToUs(ePlayer)) { case STRENGTH_PATHETIC: iMilitaryMod += /*-30*/ GD_INT_GET(DONT_SETTLE_STRENGTH_PATHETIC); break; case STRENGTH_WEAK: iMilitaryMod += /*-20*/ GD_INT_GET(DONT_SETTLE_STRENGTH_WEAK); break; case STRENGTH_POOR: iMilitaryMod += /*-10*/ GD_INT_GET(DONT_SETTLE_STRENGTH_POOR); break; case STRENGTH_AVERAGE: iMilitaryMod += /*0*/ GD_INT_GET(DONT_SETTLE_STRENGTH_AVERAGE); break; case STRENGTH_STRONG: iMilitaryMod += /*25*/ GD_INT_GET(DONT_SETTLE_STRENGTH_STRONG); break; case STRENGTH_POWERFUL: iMilitaryMod += /*40*/ GD_INT_GET(DONT_SETTLE_STRENGTH_POWERFUL); break; case STRENGTH_IMMENSE: iMilitaryMod += /*60*/ GD_INT_GET(DONT_SETTLE_STRENGTH_IMMENSE); break; } // If friendly, don't punish for having a weak military if (eApproach == CIV_APPROACH_FRIENDLY && iMilitaryMod < 0) iMilitaryMod = 0; // Military Aggressive Posture - MULTIPLIER if (iMilitaryMod > 0) { switch (GetMilitaryAggressivePosture(ePlayer)) { case AGGRESSIVE_POSTURE_NONE: iMilitaryMod *= /*100*/ GD_INT_GET(DONT_SETTLE_MOD_MILITARY_POSTURE_NONE); break; case AGGRESSIVE_POSTURE_LOW: iMilitaryMod *= /*100*/ GD_INT_GET(DONT_SETTLE_MOD_MILITARY_POSTURE_LOW); break; case AGGRESSIVE_POSTURE_MEDIUM: iMilitaryMod *= /*150*/ GD_INT_GET(DONT_SETTLE_MOD_MILITARY_POSTURE_MEDIUM); break; case AGGRESSIVE_POSTURE_HIGH: iMilitaryMod *= /*200*/ GD_INT_GET(DONT_SETTLE_MOD_MILITARY_POSTURE_HIGH); break; case AGGRESSIVE_POSTURE_INCREDIBLE: iMilitaryMod *= /*300*/ GD_INT_GET(DONT_SETTLE_MOD_MILITARY_POSTURE_INCREDIBLE); break; } } iMilitaryMod /= 100; iThreshold += iMilitaryMod; //Base it on boldness. return GetBoldness() * 10 < iThreshold; } ///////////////////////////////////////////////////////// // Stop Spying Request ///////////////////////////////////////////////////////// /// Will this AI agree to stop spying on ePlayer? bool CvDiplomacyAI::IsStopSpyingAcceptable(PlayerTypes ePlayer) { // Debug mode if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && IsAIMustAcceptHumanDiscussRequests()) return true; // Always acceptable if they resurrected or liberated us if (IsLiberator(ePlayer, true, true)) return true; CivApproachTypes eApproach = GetCivApproach(ePlayer); CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); // If player is afraid, always say yes if (eApproach == CIV_APPROACH_AFRAID) return true; // If we control 33%+ of other players' original capitals, don't make a promise unless we like them if (GetPlayer()->GetFractionOriginalCapitalsUnderControl() >= 33) { if (eApproach != CIV_APPROACH_FRIENDLY || eOpinion < CIV_OPINION_FRIEND) { if (!IsDoFAccepted(ePlayer) && !IsHasDefensivePact(ePlayer) && GetCoopWarAgreementScore(ePlayer) <= 0) { return false; } } } // Refuse all promises if close to world conquest and they still have original capitals if (IsCloseToWorldConquest() && !IsMaster(ePlayer) && (GET_PLAYER(ePlayer).GetCapitalConqueror() != NO_PLAYER || GET_PLAYER(ePlayer).GetNumCapitalCities() > 0)) return false; // If player is Guarded, Hostile or planning War, always say no if (eApproach <= CIV_APPROACH_GUARDED) return false; // If player is an enemy or unforgivable, always say no if (eOpinion <= CIV_OPINION_ENEMY) return false; // If player has used spies offensively against us, always say no if (GetNumTimesRobbedBy(ePlayer) > 0 || GetNumTimesPerformedCoupAgainstUs(ePlayer) > 0) return false; // If player is plotting against us, always say no if (GetNumTimesTheyPlottedAgainstUs(ePlayer) > 0) return false; // If player has broken or ignored a spying promise, always say no if (IgnoredSpyPromise(ePlayer) || BrokeSpyPromise(ePlayer)) return false; // If player is a backstabber, always say no if (IsUntrustworthy(ePlayer)) return false; // Acceptable if we're friends if ((IsDoFAccepted(ePlayer) && !IsWantsToEndDoFWithPlayer(ePlayer)) || (IsHasDefensivePact(ePlayer) && !IsWantsToEndDefensivePactWithPlayer(ePlayer))) return true; else if (eApproach == CIV_APPROACH_FRIENDLY) { if (GetDoFType(ePlayer) >= DOF_TYPE_ALLIES || GetMostValuableFriend() == ePlayer || GetMostValuableAlly() == ePlayer || GetCoopWarAgreementScore(ePlayer) > 0 || GetCivOpinion(ePlayer) == CIV_OPINION_ALLY) return true; } // If we're fiercely competitive, always say no if (IsCompetingForVictory()) { if (GetVictoryDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG || GetVictoryBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG || (GetPlayer()->GetCurrentEra() < 3 && IsEarlyGameCompetitor(ePlayer)) || (GetPlayer()->GetCurrentEra() >= 3 && GetBiggestCompetitor() == ePlayer) || GetPrimeLeagueCompetitor() == ePlayer || IsEndgameAggressiveTo(ePlayer)) return false; } return true; } /////////////////////////////// // Working With Player /////////////////////////////// /// Is this AI willing to work with ePlayer? bool CvDiplomacyAI::IsDoFAcceptable(PlayerTypes ePlayer) { if (IsAtWar(ePlayer)) return false; // Not if we broke off a DoF with them recently if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFBroken(GetID()) && GET_PLAYER(ePlayer).GetDiplomacyAI()->GetTurnsSinceDoFBroken(GetID()) < 30) return false; // We'll agree to a DoF if we're afraid of them, they're not a backstabber and we haven't proposed to sanction each other, but we won't ask for one unless we want one if (GetCivApproach(ePlayer) == CIV_APPROACH_AFRAID && !IsUntrustworthy(ePlayer) && !CvDiplomacyAIHelpers::ProposedSanctionsBlockingDiplomacy(GetID(), ePlayer)) return true; // Not if they broke a DoF this turn... if (GET_PLAYER(ePlayer).GetDiplomacyAI()->HasEndedFriendshipThisTurn()) return false; if (IsWantsDoFWithPlayer(ePlayer)) return true; return false; } /// Do we want to end our friendship with ePlayer early? bool CvDiplomacyAI::IsEndDoFAcceptable(PlayerTypes ePlayer, bool bIgnoreCurrentDoF) { if (!bIgnoreCurrentDoF && !IsDoFAccepted(ePlayer)) return false; if (IsUntrustworthy(ePlayer)) return true; // Is there a sanction proposal in either direction? if (CvDiplomacyAIHelpers::ProposedSanctionsBlockingDiplomacy(GetID(), ePlayer)) return true; // We're planning war and not willing to backstab! if (AvoidExchangesWithPlayer(ePlayer) && (bIgnoreCurrentDoF || !IsWarSane(ePlayer))) return true; // Don't end friendships we just made. if (!bIgnoreCurrentDoF && GetTurnsSinceBefriendedPlayer(ePlayer) <= 15) return false; CivApproachTypes eApproach = GetCivApproach(ePlayer); if (eApproach == CIV_APPROACH_AFRAID || eApproach == CIV_APPROACH_FRIENDLY) return false; CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); if (eOpinion >= CIV_OPINION_FRIEND) return false; if (eOpinion <= CIV_OPINION_ENEMY) return true; // If we got this far and there's reason to end our friendship with a vassal or our master, then do so. if (IsVassal(ePlayer) || GET_PLAYER(ePlayer).IsVassalOfSomeone()) return true; int iChance = 10 + GetLoyalty(); // Liberator? if (WasResurrectedBy(ePlayer)) { iChance += 20; } if (IsPlayerLiberatedCapital(ePlayer)) { iChance += 10; } if (IsPlayerLiberatedHolyCity(ePlayer)) { iChance += 5; } if (IsPlayerReturnedCapital(ePlayer)) { iChance += 5; } if (IsPlayerReturnedHolyCity(ePlayer)) { iChance += 3; } iChance += GetNumCitiesLiberatedBy(ePlayer) * 2; // Ongoing coop war? if (GetGlobalCoopWarWithState(ePlayer) >= COOP_WAR_STATE_PREPARING) { iChance += 5; } // Warmonger? if (GetWarmongerThreat(ePlayer) >= THREAT_SEVERE) { iChance -= GetWarmongerHate(); } // Broken/ignored promises? Each one adds negative weight. bool bBold = GetBoldness() > 7 || IsConqueror() || IsSecondaryConqueror(); bBold |= IsCompetingForVictory() && IsGoingForWorldConquest(); bBold |= GetPlayer()->GetPlayerTraits()->IsWarmonger(); PromiseStates ePromiseState = GetExpansionPromiseState(ePlayer); if (ePromiseState == PROMISE_STATE_BROKEN) { iChance -= bBold ? 10 : 5; } else if (ePromiseState == PROMISE_STATE_IGNORED) { iChance -= bBold ? 10 : 2; } ePromiseState = GetBorderPromiseState(ePlayer); if (ePromiseState == PROMISE_STATE_BROKEN) { iChance -= bBold ? 10 : 5; } else if (ePromiseState == PROMISE_STATE_IGNORED) { iChance -= bBold ? 10 : 2; } bool bDiplomatic = IsDiplomat() || IsSecondaryDiplomat(); bDiplomatic |= IsCompetingForVictory() && IsGoingForDiploVictory(); bDiplomatic |= GetPlayer()->GetPlayerTraits()->IsDiplomat(); ePromiseState = GetBullyCityStatePromiseState(ePlayer); if (ePromiseState == PROMISE_STATE_BROKEN) { iChance -= bDiplomatic ? 10 : 5; } else if (ePromiseState == PROMISE_STATE_IGNORED) { iChance -= bDiplomatic ? 5 : 2; } ePromiseState = GetAttackCityStatePromiseState(ePlayer); if (ePromiseState == PROMISE_STATE_BROKEN) { return true; } else if (ePromiseState == PROMISE_STATE_IGNORED) { iChance -= bDiplomatic ? 10 : 5; } bool bScientific = IsScientist() || IsSecondaryScientist(); bScientific |= IsCompetingForVictory() && IsGoingForSpaceshipVictory(); bScientific |= GetPlayer()->GetPlayerTraits()->IsNerd(); ePromiseState = GetSpyPromiseState(ePlayer); if (ePromiseState == PROMISE_STATE_BROKEN) { iChance -= bScientific ? 20 : 10; } else if (ePromiseState == PROMISE_STATE_IGNORED) { iChance -= bScientific ? 10 : 5; } int iFlavorReligion = m_pPlayer->GetFlavorManager()->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_RELIGION")); bool bReligious = iFlavorReligion > 6; bReligious |= GetPlayer()->GetPlayerTraits()->IsReligious(); ePromiseState = GetNoConvertPromiseState(ePlayer); if (ePromiseState == PROMISE_STATE_BROKEN) { iChance -= bReligious ? 20 : 10; } else if (ePromiseState == PROMISE_STATE_IGNORED) { iChance -= bReligious ? 10 : 5; } int iFlavorCulture = m_pPlayer->GetFlavorManager()->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_CULTURE")); bool bCultural = iFlavorCulture > 6; bCultural |= IsCultural() || IsSecondaryCultural(); bCultural |= IsCompetingForVictory() && IsGoingForCultureVictory(); bCultural = GetPlayer()->GetPlayerTraits()->IsTourism(); ePromiseState = GetNoDiggingPromiseState(ePlayer); if (ePromiseState == PROMISE_STATE_BROKEN) { iChance -= bCultural ? 20 : 10; } else if (ePromiseState == PROMISE_STATE_IGNORED) { iChance -= bCultural ? 10 : 5; } // Disputes? if (IsCompetingForVictory()) { if (IsEndgameAggressiveTo(ePlayer)) { iChance -= GetVictoryCompetitiveness(); } if (GetVictoryDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) { iChance -= GetVictoryCompetitiveness(); } if (GetVictoryBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG) { iChance -= GetVictoryCompetitiveness(); } } if (GetLandDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) { iChance -= GetBoldness(); } if (GetWonderDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) { iChance -= GetWonderCompetitiveness(); } if (GetMinorCivDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) { iChance -= GetMinorCivCompetitiveness(); } if (HasEverSanctionedUs(ePlayer)) { iChance -= 20; } else if (HasTriedToSanctionUs(ePlayer)) { iChance -= 10; } if (WasEverBackstabbedBy(ePlayer)) { iChance -= 10; } // Less likely to break things off if we've been friends for a while. switch (GetDoFType(ePlayer)) { case DOF_TYPE_UNTRUSTWORTHY: case DOF_TYPE_NEW: break; case DOF_TYPE_FRIENDS: iChance += 2; break; case DOF_TYPE_ALLIES: iChance += 5; break; case DOF_TYPE_BATTLE_BROTHERS: iChance += 10; break; } iChance += GetCoopWarAgreementScore(ePlayer); return iChance <= 0; } /// AI won't agree to a DoF until they've known a player for at least a few turns bool CvDiplomacyAI::IsTooEarlyForDoF(PlayerTypes ePlayer) { int iDoFBuffer = /*50*/ GD_INT_GET(DOF_TURN_BUFFER); int iEra = GetPlayer()->GetCurrentEra(); if (iEra > 0) { iDoFBuffer += min(0, (iEra * /*-5*/ GD_INT_GET(DOF_TURN_BUFFER_REDUCTION_PER_ERA))); } if (iDoFBuffer < /*10*/ GD_INT_GET(JUST_MET_TURN_BUFFER) || GetPlayer()->GetDoFToVotes() > 0) { iDoFBuffer = GD_INT_GET(JUST_MET_TURN_BUFFER); } if (GET_TEAM(GetTeam()).GetTurnsSinceMeetingTeam(GET_PLAYER(ePlayer).getTeam()) < iDoFBuffer) return true; return false; } /// How many Research Agreements do we currently have with other players? int CvDiplomacyAI::GetNumRA() const { int iRtnValue = 0; for(int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer) && IsHasResearchAgreement(eLoopPlayer)) { iRtnValue++; } } return iRtnValue; } /// How many Defensive Pacts do we currently have with other players? int CvDiplomacyAI::GetNumDefensePacts() const { int iRtnValue = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv() && IsHasDefensivePact(eLoopPlayer)) { iRtnValue++; } } return iRtnValue; } /// Do we have similar or divergent Social Policies? int CvDiplomacyAI::GetNumSamePolicies(PlayerTypes ePlayer) { int iNumSame = 0; int iNumDifferent = 0; int iNumWeHave = 0; int iNumTheyHave = 0; for (int iPolicyLoop = 0; iPolicyLoop < GC.getNumPolicyBranchInfos(); iPolicyLoop++) { PolicyBranchTypes ePolicyBranch = (PolicyBranchTypes)iPolicyLoop; if (ePolicyBranch != NO_POLICY_BRANCH_TYPE) { CvPolicyBranchEntry* pkPolicyBranchInfo = GC.getPolicyBranchInfo(ePolicyBranch); if (pkPolicyBranchInfo == NULL) { continue; } //No ideologies. if (pkPolicyBranchInfo->IsPurchaseByLevel()) { continue; } //We have it and they don't? if (GetPlayer()->GetPlayerPolicies()->IsPolicyBranchUnlocked(ePolicyBranch) && !GET_PLAYER(ePlayer).GetPlayerPolicies()->IsPolicyBranchUnlocked(ePolicyBranch)) { iNumDifferent++; iNumWeHave++; } //They have it and we don't? else if (!GetPlayer()->GetPlayerPolicies()->IsPolicyBranchUnlocked(ePolicyBranch) && GET_PLAYER(ePlayer).GetPlayerPolicies()->IsPolicyBranchUnlocked(ePolicyBranch)) { iNumDifferent++; iNumTheyHave++; } //We both have it? else if (GetPlayer()->GetPlayerPolicies()->IsPolicyBranchUnlocked(ePolicyBranch) && GET_PLAYER(ePlayer).GetPlayerPolicies()->IsPolicyBranchUnlocked(ePolicyBranch)) { iNumSame++; iNumWeHave++; iNumTheyHave++; } } } // If one of us has only one branch unlocked and the other has none, count it as 0. if (iNumWeHave == 1 && iNumTheyHave == 0) { return 0; } else if (iNumTheyHave == 1 && iNumWeHave == 0) { return 0; } return (iNumSame - iNumDifferent); } /// Are we done with ePlayer, and now want to Denounce him? bool CvDiplomacyAI::IsDenounceFriendAcceptable(PlayerTypes ePlayer) { // We have to be really treacherous to consider doing this, otherwise we'll just break up our friendships. if (!IsBackstabber() && !IsUntrustworthy(ePlayer) && !WasEverBackstabbedBy(ePlayer) && GetDenounceWillingness() < 8 && GetLoyalty() > 3) return false; if (IsMaster(ePlayer) || GetPlayer()->IsVassalOfSomeone()) return false; if (GetDoFType(ePlayer) == DOF_TYPE_BATTLE_BROTHERS) return false; // We've been on more than one coop war together? if (GetCoopWarAgreementScore(ePlayer) > 1) return false; // Liberator? if (WasResurrectedBy(ePlayer) || IsPlayerLiberatedCapital(ePlayer) || IsPlayerLiberatedHolyCity(ePlayer)) return false; // Don't sabotage our teammates' friendships... vector vOurTeam = GET_TEAM(GetTeam()).getPlayers(); for (size_t i=0; iIsDoFAccepted(ePlayer)) { if (GET_PLAYER(vOurTeam[i]).isHuman(ISHUMAN_AI_DIPLOMACY)) return false; if (!GET_PLAYER(vOurTeam[i]).GetDiplomacyAI()->IsWantsToEndDoFWithPlayer(ePlayer)) return false; } } // Regardless of whether we'd like to denounce a friend, it will give us a fairly hefty backstabbing penalty // So only use this option rather than ending the DoF early if we'd benefit from doing so // Look at the geopolitical situation. bool bAnyoneWouldCare = false; int iWeDenouncedFriendCount = GetWeDenouncedFriendCount() + 1; // +1 because we'd be denouncing this friend for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; CvPlayer& kPlayer = GET_PLAYER(eLoopPlayer); if (!kPlayer.isAlive() || !kPlayer.isMajorCiv() || kPlayer.getNumCities() <= 0) continue; // Not this guy or anyone on our team if (eLoopPlayer == ePlayer || kPlayer.getTeam() == GetTeam()) continue; // Does this guy already consider us a backstabber? if (kPlayer.GetDiplomacyAI()->IsUntrustworthy(GetID())) continue; // Relations already terrible? Doesn't really matter, then. if (kPlayer.IsAtWarWith(GetID()) || IsDenouncedPlayer(eLoopPlayer) || IsDenouncedByPlayer(eLoopPlayer)) continue; // Do we care about this person's opinion? Must be friends, wanting to be friends, strategic trade partner, or stronger than us. bool bDeterrent = IsWantsDoFWithPlayer(eLoopPlayer) || IsFriendOrAlly(eLoopPlayer) || GetPrimeLeagueAlly() == eLoopPlayer || IsStrategicTradePartner(eLoopPlayer); if (!bDeterrent) { StrengthTypes eStrength = GetMilitaryStrengthComparedToUs(eLoopPlayer); PlayerProximityTypes eProximity = kPlayer.GetProximityToPlayer(GetID()); // Let's not anger people much stronger than us, that won't end well. if (eStrength >= STRENGTH_POWERFUL) bDeterrent = true; // Let's also not anger people of our strength who are nearby. if (eStrength >= STRENGTH_AVERAGE && eProximity >= PLAYER_PROXIMITY_CLOSE) bDeterrent = true; } // Do they like us more than the friend we're denouncing? if (kPlayer.isHuman(ISHUMAN_AI_DIPLOMACY)) { // If the human is at war with them or has denounced them, assume they'll support us. if (kPlayer.IsAtWarWith(ePlayer) || kPlayer.GetDiplomacyAI()->IsDenouncedPlayer(ePlayer)) { bAnyoneWouldCare = true; } else { // We can't tell what humans think, so just assume they'll care if we backstab their friends, DPs, and vassals, and not otherwise if (kPlayer.GetDiplomacyAI()->IsDoFAccepted(ePlayer) || kPlayer.GetDiplomacyAI()->IsHasDefensivePact(ePlayer) || kPlayer.GetDiplomacyAI()->IsMaster(ePlayer)) { if (bDeterrent) return false; else continue; } // Assume they won't care about what we think if we've previously backstabbed them or vice versa if (CvDiplomacyAIHelpers::IgnoresBackstabbing(eLoopPlayer, GetID())) continue; // Assume that our human friends, and humans who have been denounced or previously backstabbed by the target, would care about what we say. if (IsFriendOrAlly(eLoopPlayer) || kPlayer.GetDiplomacyAI()->IsDenouncedByPlayer(ePlayer) || kPlayer.GetDiplomacyAI()->WasEverBackstabbedBy(ePlayer)) bAnyoneWouldCare = true; } } else { // AI players won't care about people who backstabbed them, or who they backstabbed if (!CvDiplomacyAIHelpers::IgnoresBackstabbing(eLoopPlayer, ePlayer)) { // For AI players - cheat a bit here, use the cached opinion weights if (kPlayer.GetDiplomacyAI()->GetCachedOpinionWeight(ePlayer) <= kPlayer.GetDiplomacyAI()->GetCachedOpinionWeight(GetID()) || kPlayer.GetDiplomacyAI()->IsFriendOrAlly(ePlayer)) { if (bDeterrent) return false; else continue; } // Careful! Other AI players might care if we denounce one friend, but will view us as the problem if we denounce too many friends. // What is their backstabbing tolerance? if (!kPlayer.GetDiplomacyAI()->WasResurrectedBy(GetID())) { int iFriendDenounceTolerance = EstimateLoyalty(eLoopPlayer) < 5 ? 2 : 1; if (EstimateForgiveness(eLoopPlayer) > 8) iFriendDenounceTolerance++; int iNumCivsEver = GC.getGame().GetNumMajorCivsEver(); bool bDangerous = (GC.getGame().GetNumMajorCivsAlive() <= iNumCivsEver / 2) || (GetPlayerNumMajorsConquered(GetID()) >= iNumCivsEver / 3); if (bDangerous) iFriendDenounceTolerance = 1; else if (kPlayer.GetDiplomacyAI()->IsBackstabber()) // If not dangerous and they're a backstabber, they won't care. continue; if (kPlayer.GetDiplomacyAI()->IsLiberator(GetID(), true, true)) iFriendDenounceTolerance++; if (iWeDenouncedFriendCount > iFriendDenounceTolerance) { if (bDeterrent) return false; else continue; } } // They won't care about what we think if we've previously backstabbed them or vice versa if (CvDiplomacyAIHelpers::IgnoresBackstabbing(eLoopPlayer, GetID())) continue; bAnyoneWouldCare = true; } else { // They won't care about what we think if we've previously backstabbed them or vice versa if (CvDiplomacyAIHelpers::IgnoresBackstabbing(eLoopPlayer, GetID())) continue; // They'll only care if they like us more than them // Cheat a bit here, use the cached opinion weights if (kPlayer.GetDiplomacyAI()->GetCachedOpinionWeight(GetID()) < kPlayer.GetDiplomacyAI()->GetCachedOpinionWeight(ePlayer)) bAnyoneWouldCare = true; } } } if (!bAnyoneWouldCare) return false; if (IsUntrustworthy(ePlayer)) return true; // Avoid denouncing friends if we're going for diplo victory! if (!WasEverBackstabbedBy(ePlayer) && IsCompetingForVictory() && IsGoingForDiploVictory()) return false; CivApproachTypes eApproach = GetCivApproach(ePlayer); if (eApproach == CIV_APPROACH_AFRAID || eApproach == CIV_APPROACH_FRIENDLY) return false; if (eApproach == CIV_APPROACH_HOSTILE) return true; // Is there a sanction proposal in either direction? if (CvDiplomacyAIHelpers::ProposedSanctionsBlockingDiplomacy(GetID(), ePlayer)) return true; CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); if (eOpinion <= CIV_OPINION_ENEMY) return true; if (eOpinion >= CIV_OPINION_FAVORABLE) return false; int iChance = 10 + GetLoyalty(); if (eOpinion == CIV_OPINION_COMPETITOR) { iChance -= GetDenounceWillingness(); } if (GetWarmongerThreat(ePlayer) >= THREAT_SEVERE) { iChance -= GetWarmongerHate(); } if (IsCompetingForVictory()) { if (IsEndgameAggressiveTo(ePlayer)) { iChance -= GetVictoryCompetitiveness(); } if (GetVictoryBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG) { iChance -= GetVictoryCompetitiveness(); } if (GetVictoryDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) { iChance -= GetVictoryCompetitiveness(); } // Going for conquest, and they're weak and close if (IsGoingForWorldConquest() && GetPlayer()->GetProximityToPlayer(ePlayer) >= PLAYER_PROXIMITY_CLOSE) { if (GetMilitaryStrengthComparedToUs(ePlayer) <= STRENGTH_POOR && GetTargetValue(ePlayer) >= TARGET_VALUE_AVERAGE) { iChance -= GetMeanness(); } } } if (HasEverSanctionedUs(ePlayer)) { iChance -= 10; } else if (HasTriedToSanctionUs(ePlayer)) { iChance -= 5; } if (WasEverBackstabbedBy(ePlayer)) { iChance -= 10; } if (GetDoFType(ePlayer) == DOF_TYPE_ALLIES) { iChance += 5; } else if (GetDoFType(ePlayer) == DOF_TYPE_FRIENDS) { iChance += 2; } if (GetCoopWarAgreementScore(ePlayer) == 1) { iChance += 2; } else if (GetCoopWarAgreementScore(ePlayer) < 0) { iChance += GetCoopWarAgreementScore(ePlayer); } if (IsPlayerReturnedCapital(ePlayer)) { iChance += 10; } if (IsPlayerReturnedHolyCity(ePlayer)) { iChance += 5; } iChance += GetNumCitiesEverLiberatedBy(ePlayer) * 3; return iChance <= 0; } /// Does ePlayer have a DoF with anyone we have a DoF with? bool CvDiplomacyAI::IsPlayerDoFWithAnyFriend(PlayerTypes ePlayer) const { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) continue; if (!GET_PLAYER(eLoopPlayer).isAlive() || GET_PLAYER(eLoopPlayer).getNumCities() <= 0) continue; if (IsDoFAccepted(eLoopPlayer) && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAccepted(eLoopPlayer)) return true; } return false; } /// Does ePlayer have a DoF with any of our enemies? bool CvDiplomacyAI::IsPlayerDoFWithAnyEnemy(PlayerTypes ePlayer) const { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) continue; if (!GET_PLAYER(eLoopPlayer).isAlive() || GET_PLAYER(eLoopPlayer).getNumCities() <= 0) continue; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAccepted(eLoopPlayer)) { if (IsDenouncedPlayer(eLoopPlayer) || IsAtWar(eLoopPlayer)) return true; } } return false; } /// Does ePlayer have a Defensive Pact with anyone we also have a DP with? bool CvDiplomacyAI::IsPlayerDPWithAnyFriend(PlayerTypes ePlayer) const { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (!GET_PLAYER(eLoopPlayer).isAlive() || GET_PLAYER(eLoopPlayer).getNumCities() <= 0) continue; if (IsHasDefensivePact(eLoopPlayer) && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasDefensivePact(eLoopPlayer)) return true; } return false; } /// Does ePlayer have a Defensive Pact with any of our enemies? bool CvDiplomacyAI::IsPlayerDPWithAnyEnemy(PlayerTypes ePlayer) const { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (!GET_PLAYER(eLoopPlayer).isAlive() || GET_PLAYER(eLoopPlayer).getNumCities() <= 0) continue; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasDefensivePact(eLoopPlayer)) { if (IsDenouncedPlayer(eLoopPlayer) || IsAtWar(eLoopPlayer)) return true; } } return false; } int CvDiplomacyAI::GetNumMutualFriends(PlayerTypes ePlayer) const { int iCount = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) continue; if (!GET_PLAYER(eLoopPlayer).isAlive() || GET_PLAYER(eLoopPlayer).getNumCities() <= 0) continue; if (IsDoFAccepted(eLoopPlayer) && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAccepted(eLoopPlayer)) iCount++; } return iCount; } int CvDiplomacyAI::GetNumEnemyFriends(PlayerTypes ePlayer) const { int iCount = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) continue; if (!GET_PLAYER(eLoopPlayer).isAlive() || GET_PLAYER(eLoopPlayer).getNumCities() <= 0) continue; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAccepted(eLoopPlayer)) { if (IsDenouncedPlayer(eLoopPlayer) || IsAtWar(eLoopPlayer)) iCount++; } } return iCount; } int CvDiplomacyAI::GetNumMutualDefensePacts(PlayerTypes ePlayer) const { int iCount = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (!GET_PLAYER(eLoopPlayer).isAlive() || GET_PLAYER(eLoopPlayer).getNumCities() <= 0) continue; if (IsHasDefensivePact(eLoopPlayer) && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasDefensivePact(eLoopPlayer)) iCount++; } return iCount; } int CvDiplomacyAI::GetNumEnemyDefensePacts(PlayerTypes ePlayer) const { int iCount = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (!GET_PLAYER(eLoopPlayer).isAlive() || GET_PLAYER(eLoopPlayer).getNumCities() <= 0) continue; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasDefensivePact(eLoopPlayer)) { if (IsDenouncedPlayer(eLoopPlayer) || IsAtWar(eLoopPlayer)) iCount++; } } return iCount; } /////////////////////////////// // Religion /////////////////////////////// /// Does ePlayer have similar religious beliefs as we do? bool CvDiplomacyAI::IsPlayerSameReligion(PlayerTypes ePlayer) const { ReligionTypes eOurReligion = GetPlayer()->GetReligions()->GetStateReligion(false); ReligionTypes eTheirReligion = GET_PLAYER(ePlayer).GetReligions()->GetStateReligion(false); if (eOurReligion == NO_RELIGION || eTheirReligion == NO_RELIGION) return false; if (eOurReligion == eTheirReligion) return true; return false; } /// Does ePlayer have a religion that opposes ours? bool CvDiplomacyAI::IsPlayerOpposingReligion(PlayerTypes ePlayer) const { ReligionTypes eOurReligion = GetPlayer()->GetReligions()->GetStateReligion(false); ReligionTypes eTheirReligion = GET_PLAYER(ePlayer).GetReligions()->GetStateReligion(false); if (eOurReligion == NO_RELIGION || eTheirReligion == NO_RELIGION) return false; if (eOurReligion != eTheirReligion) return CvReligionAIHelpers::PassesTeammateReligionCheck(eTheirReligion, GetID(), false); return false; } /////////////////////////////// // Ideology /////////////////////////////// /// Does ePlayer have the same ideology that we do? bool CvDiplomacyAI::IsPlayerSameIdeology(PlayerTypes ePlayer) const { PolicyBranchTypes eMyBranch = m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree(); PolicyBranchTypes eTheirBranch = GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree(); if (eMyBranch == NO_POLICY_BRANCH_TYPE || eTheirBranch == NO_POLICY_BRANCH_TYPE) return false; if (eMyBranch == eTheirBranch) return true; return false; } /// Does ePlayer have an ideology that opposes ours? bool CvDiplomacyAI::IsPlayerOpposingIdeology(PlayerTypes ePlayer) const { PolicyBranchTypes eMyBranch = m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree(); PolicyBranchTypes eTheirBranch = GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree(); if (eMyBranch == NO_POLICY_BRANCH_TYPE || eTheirBranch == NO_POLICY_BRANCH_TYPE) return false; if (eMyBranch != eTheirBranch) return true; return false; } /////////////////////////////// // Denounce Player /////////////////////////////// /// Denounces a player void CvDiplomacyAI::DoDenouncePlayer(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); PlayerTypes eMyPlayer = GetID(); TeamTypes eMyTeam = GetTeam(); TeamTypes eTheirTeam = GET_PLAYER(ePlayer).getTeam(); SetDenouncedPlayer(ePlayer, true); // close both embassies GET_TEAM(eMyTeam).CloseEmbassyAtTeam(eTheirTeam); GET_TEAM(eTheirTeam).CloseEmbassyAtTeam(eMyTeam); // cancel Defensive Pacts GET_TEAM(eMyTeam).SetHasDefensivePact(eTheirTeam, false); GET_TEAM(eTheirTeam).SetHasDefensivePact(eMyTeam, false); // End all coop war agreements with this player GET_PLAYER(ePlayer).GetDiplomacyAI()->CancelCoopWarsWithPlayer(eMyPlayer, true); bool bBackstabTimer = GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFBroken(eMyPlayer) && GET_PLAYER(ePlayer).GetDiplomacyAI()->GetTurnsSinceDoFBroken(eMyPlayer) < /*10*/ GD_INT_GET(DOF_BROKEN_BACKSTAB_TIMER) && !IsDenouncedByPlayer(ePlayer) && !IsAtWar(ePlayer); // WAS working with this player if (IsDoFAccepted(ePlayer) || bBackstabTimer) { SetDoFAccepted(ePlayer, false); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFAccepted(eMyPlayer, false); SetDoFType(ePlayer, DOF_TYPE_UNTRUSTWORTHY); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFType(GetID(), DOF_TYPE_UNTRUSTWORTHY); // They now view us as a traitor! GET_PLAYER(ePlayer).GetDiplomacyAI()->SetFriendDenouncedUs(eMyPlayer, true); GET_PLAYER(ePlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, -300); GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFBroken(eMyPlayer, true, true); } // Update opinions and approaches GET_PLAYER(ePlayer).GetDiplomacyAI()->DoReevaluatePlayer(GetID()); DoReevaluatePlayer(ePlayer); Localization::String someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE"); int iMessage = GetDenounceMessage(ePlayer); if (iMessage > 0 && iMessage <= 7) { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_IDEOLOGY"); } else if(iMessage == 8) { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_WARMONGER"); } else if(iMessage == 9) { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_MINORS"); } else if(iMessage == 10) { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_MINORS"); } else if(iMessage == 11) { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_NUKED"); } else if(iMessage == 12) { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_SPIES"); } else if(iMessage == 13) { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_LAND"); } else if(iMessage == 14) { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_DOF"); } else if(iMessage == 15) { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_DOGPILE"); } else if(iMessage == 16) { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_UNTRUSTWORTHY"); } else if(iMessage == 17) { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_UNFORGIVEABLE"); } else if(iMessage == 18) { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_FAITH"); } else if(iMessage == 19) { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_DIGGING"); } else if(iMessage == 20) { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_WONDERS"); } else if(iMessage == 21) { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_VICTORY"); } else if(iMessage == 22) { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_VICTORY"); } else { someoneDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE"); } Localization::String someoneDenounceSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_S"); Localization::String youDenounceInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_YOU_DENOUNCE"); Localization::String youDenounceSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_YOU_DENOUNCE_S"); Localization::String denounceYouInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCE_YOU"); Localization::String denounceYouSummary = Localization::Lookup("TXT_KEY_NOTIFICATION_DENOUNCED_YOU_S"); for(int iCurPlayer = 0; iCurPlayer < MAX_MAJOR_CIVS; ++iCurPlayer){ PlayerTypes eCurPlayer = (PlayerTypes) iCurPlayer; CvPlayerAI& kCurPlayer = GET_PLAYER(eCurPlayer); CvNotifications* pNotifications = GET_PLAYER(eCurPlayer).GetNotifications(); if(pNotifications) { const char* strThisPlayerName = NULL; const char* strOtherPlayerName = NULL; CvTeam* pNotifyTeam = &GET_TEAM(kCurPlayer.getTeam()); bool bFromMe = GetTeam() == kCurPlayer.getTeam(); bool bAtMe = GET_PLAYER(ePlayer).getTeam() == kCurPlayer.getTeam(); // Have we met these guys yet? if(!bFromMe) { if(pNotifyTeam->isHasMet(GetTeam()) || kCurPlayer.isObserver()) strThisPlayerName = GetPlayer()->getCivilizationShortDescriptionKey(); else strThisPlayerName = "TXT_KEY_UNMET_PLAYER"; } if(!bAtMe) { if(pNotifyTeam->isHasMet(GET_PLAYER(ePlayer).getTeam()) || kCurPlayer.isObserver()) strOtherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescriptionKey(); else strOtherPlayerName = "TXT_KEY_UNMET_PLAYER"; } Localization::String strText; Localization::String strSummary; if(bFromMe) { strText = youDenounceInfo; strText << strOtherPlayerName; strSummary = youDenounceSummary; strSummary << strOtherPlayerName; } else if(bAtMe) { strText = denounceYouInfo; strText << strThisPlayerName; strSummary = denounceYouSummary; strSummary << strThisPlayerName; } else { bool bHasMetThisTeam = pNotifyTeam->isHasMet(GetTeam()) || kCurPlayer.isObserver(); bool bHasMetOtherTeam = pNotifyTeam->isHasMet(GET_PLAYER(ePlayer).getTeam()) || kCurPlayer.isObserver(); //Only display notification if we've met both teams. if (bHasMetThisTeam && bHasMetOtherTeam) { strText = someoneDenounceInfo; strText << strThisPlayerName << strOtherPlayerName; strSummary = someoneDenounceSummary; strSummary << strThisPlayerName << strOtherPlayerName; } else { continue; } } pNotifications->Add(NOTIFICATION_DIPLOMACY_DECLARATION, strText.toUTF8(), strSummary.toUTF8(), -1, -1, GetID(), ePlayer); } } } /// Does this player feel it's time to denounce ePlayer? bool CvDiplomacyAI::IsDenounceAcceptable(PlayerTypes ePlayer, bool bBias) { TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam(); // Can't denounce if always at war if (IsAlwaysAtWar(ePlayer)) return false; if (IsAtWar(ePlayer)) { // Don't denounce if we're at war and want peace if (IsWantsPeaceWithPlayer(ePlayer)) return false; // To circumvent a UI bug, don't declare war and denounce on the same turn if (GET_TEAM(GetTeam()).GetNumTurnsAtWar(eTeam) == 0) return false; } // If we've already denounced, it's no good if (IsDenouncedPlayer(ePlayer)) return false; // If we're friends, return false - this is handled in IsDenounceFriendAcceptable if (IsDoFAccepted(ePlayer)) return false; // Don't denounce players we've just met. if (GET_TEAM(GetTeam()).GetTurnsSinceMeetingTeam(eTeam) < 10) return false; // If we ended a DoF recently and the 10-turn timer is in effect, let's wait it out. if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFBroken(GetID()) && GET_PLAYER(ePlayer).GetDiplomacyAI()->GetTurnsSinceDoFBroken(GetID()) < /*10*/ GD_INT_GET(DOF_BROKEN_BACKSTAB_TIMER) && !IsDenouncedByPlayer(ePlayer) && !IsAtWar(ePlayer)) return false; int iWeight = GetDenounceWeight(ePlayer, bBias); return iWeight > 25; } /// Returns the weight this AI has for denouncing ePlayer int CvDiplomacyAI::GetDenounceWeight(PlayerTypes ePlayer, bool bBias) { // Base Personality value; ranges from 1 to 10 int iWeight = GetDenounceWillingness(); // This guy is our vassal - never denounce! if (IsMaster(ePlayer)) { return 0; } // This guy is our master else if (IsVassal(ePlayer)) { // Voluntary vassals have a huge weight against denouncing if (IsVoluntaryVassalage(ePlayer)) { iWeight -= 50; } // Vassal treatment view switch (GetVassalTreatmentLevel(ePlayer)) { case NO_VASSAL_TREATMENT: UNREACHABLE(); // Content vassals have a huge weight against denouncing case VASSAL_TREATMENT_CONTENT: iWeight -= 50; break; // Disagree? Let's not upset things... case VASSAL_TREATMENT_DISAGREE: iWeight -= 15; break; // Mistreated: Small bonus case VASSAL_TREATMENT_MISTREATED: iWeight += 2; break; // Unhappy: Medium bonus case VASSAL_TREATMENT_UNHAPPY: iWeight += 5; break; // Enslaved: Big bonus case VASSAL_TREATMENT_ENSLAVED: iWeight += 10; break; } } // If this guy is a vassal of someone else, reduce the weight to make us less likely to denounce them else if (GET_PLAYER(ePlayer).IsVassalOfSomeone()) { iWeight -= 8; } switch (GetCivApproach(ePlayer)) { // Do NOT denounce if friendly case CIV_APPROACH_FRIENDLY: return 0; // Hostile: Bonus case CIV_APPROACH_HOSTILE: iWeight += 6; break; // Afraid: Penalty case CIV_APPROACH_AFRAID: iWeight -= 10; break; // Let's not upset things case CIV_APPROACH_NEUTRAL: iWeight -= 8; break; // Slight bump if guarded case CIV_APPROACH_GUARDED: iWeight++; break; // Do NOT reveal if deceptive case CIV_APPROACH_DECEPTIVE: iWeight -= 25; break; // War - depends on surface approach case CIV_APPROACH_WAR: { if (IsAtWar(ePlayer)) { iWeight += 8; } else { switch (GetSurfaceApproach(ePlayer)) { case CIV_APPROACH_DECEPTIVE: case CIV_APPROACH_WAR: UNREACHABLE(); // Higher bump than true case CIV_APPROACH_HOSTILE: iWeight += 8; break; // Slightly higher bump than true GUARDED case CIV_APPROACH_GUARDED: iWeight += 2; break; // Avoid upsetting things, but lower weight than true NEUTRAL case CIV_APPROACH_NEUTRAL: case CIV_APPROACH_AFRAID: iWeight -= 4; break; // Do NOT reveal if pretending to be FRIENDLY case CIV_APPROACH_FRIENDLY: iWeight -= 25; break; } } break; } } switch (GetCivOpinion(ePlayer)) { // Unforgivable: Big Bonus case CIV_OPINION_UNFORGIVABLE: iWeight += 10; break; // Enemy: Bonus case CIV_OPINION_ENEMY: iWeight += 5; break; // Competitor: Small Bonus case CIV_OPINION_COMPETITOR: iWeight += 2; break; // Let's not upset things case CIV_OPINION_NEUTRAL: iWeight -= 5; break; // Good Relations: Don't denounce case CIV_OPINION_FAVORABLE: case CIV_OPINION_FRIEND: case CIV_OPINION_ALLY: return 0; } if (GetVictoryDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) { iWeight += 4; } if (GetVictoryBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG) { iWeight += 4; } if (GetLandDisputeLevel(ePlayer) == DISPUTE_LEVEL_STRONG) { iWeight += 2; } else if (GetLandDisputeLevel(ePlayer) == DISPUTE_LEVEL_FIERCE) { iWeight += 5; } if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetNumDefensePacts() > 0) { iWeight -= (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetNumDefensePacts()*2); } if (GetBiggestCompetitor() == ePlayer) { iWeight += 10; } else if (IsMajorCompetitor(ePlayer)) { iWeight += 2; } // Backstabber: Huge bonus! if (IsUntrustworthy(ePlayer)) { iWeight += 25; } // Sanctions proposed in either direction: Large bonus! if (CvDiplomacyAIHelpers::ProposedSanctionsBlockingDiplomacy(GetID(), ePlayer)) { iWeight += 10; } // Close to victory: Bonus based on difficulty level if (IsEndgameAggressiveTo(ePlayer)) { int iDifficultyMod = 0; if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP)) iDifficultyMod += max(GET_PLAYER(ePlayer).getHandicapInfo().getVictoryDisputePercent(), GET_PLAYER(ePlayer).getHandicapInfo().getVictoryBlockPercent()) / 10; else iDifficultyMod += max(GC.getGame().getHandicapInfo().getVictoryDisputePercent(), GC.getGame().getHandicapInfo().getVictoryBlockPercent()) / 10; iWeight += iDifficultyMod; } // Resurrected us: Huge penalty! if (WasResurrectedBy(ePlayer)) { iWeight -= 25; } // Defensive Pact: Big penalty if (IsHasDefensivePact(ePlayer)) { iWeight -= 10; } // Liberated or returned our key cities: Big penalty if (IsLiberator(ePlayer, false, true)) { iWeight -= 10; } // Liberated cities: Penalty if (GetNumCitiesLiberatedBy(ePlayer) > 0) { iWeight -= 5; } // Resurrected them: Big penalty if (GET_PLAYER(ePlayer).GetDiplomacyAI()->WasResurrectedBy(GetID())) { iWeight -= 15; } // Look for other players we like or are strong, and modify our willingness to denounce based on this for (int iThirdParty = 0; iThirdParty < MAX_MAJOR_CIVS; iThirdParty++) { PlayerTypes eThirdParty = (PlayerTypes) iThirdParty; CvDiplomacyAI* pThirdPartyDiplo = GET_PLAYER(eThirdParty).GetDiplomacyAI(); // Third party has strong positive or negative relations with this player if (pThirdPartyDiplo->IsDoFAccepted(ePlayer) || pThirdPartyDiplo->IsHasDefensivePact(ePlayer) || pThirdPartyDiplo->IsTeammate(ePlayer) || pThirdPartyDiplo->IsDenouncedPlayer(ePlayer) || pThirdPartyDiplo->IsAtWar(ePlayer)) { // Teammate? if (IsTeammate(eThirdParty)) { // Big bonus to denouncing a non-friend if a teammate denounced them if (pThirdPartyDiplo->IsDenouncedPlayer(ePlayer)) { iWeight += 10; } // Never denounce our teammates' friends or DPs else { return 0; } } // Ignore if we hate the third party if (GetCivOpinion(eThirdParty) <= CIV_OPINION_ENEMY) continue; if (GetCivApproach(eThirdParty) <= CIV_APPROACH_DECEPTIVE) continue; if (IsUntrustworthy(eThirdParty)) continue; // Third party is close to victory? Don't be a pawn. if (IsEndgameAggressiveTo(ePlayer) && (GetVictoryDisputeLevel(ePlayer) > DISPUTE_LEVEL_NONE || GetVictoryBlockLevel(ePlayer) > BLOCK_LEVEL_NONE)) continue; int iMod = 0; // We're close to this guy who's at war - want to gain favor if (GetPlayer()->GetProximityToPlayer(eThirdParty) == PLAYER_PROXIMITY_NEIGHBORS) iMod++; // Do we like this guy? iMod += (GetCivOpinion(eThirdParty) - CIV_OPINION_NEUTRAL); // Ex: if opinion is Ally, this will add 3 to the weight; if Competitor, will subtract 1 // Are they strong? if (GetMilitaryStrengthComparedToUs(eThirdParty) > STRENGTH_AVERAGE) iMod += (GetMilitaryStrengthComparedToUs(eThirdParty) - STRENGTH_AVERAGE); // Ex: if they're immense, this will add 3 to the weight // Are we friends with them? if (IsDoFAccepted(eThirdParty)) iMod += 4; // Do we have a DP with them? if (IsHasDefensivePact(eThirdParty)) iMod += 2; if (pThirdPartyDiplo->IsDenouncedPlayer(ePlayer) || pThirdPartyDiplo->IsAtWar(ePlayer)) { iWeight += iMod; } else { iWeight -= iMod; } } } // Are there any quests that should influence our decision? Ignore if we're already close to a non-domination victory, we shouldn't be making additional enemies over City-States. if (!IsCloseToSpaceshipVictory() && !IsCloseToCultureVictory() && !IsCloseToDiploVictory()) { for (int iMinorLoop = MAX_MAJOR_CIVS; iMinorLoop < MAX_CIV_PLAYERS; iMinorLoop++) { PlayerTypes eMinor = (PlayerTypes) iMinorLoop; if (IsPlayerValid(eMinor) && GET_PLAYER(eMinor).isMinorCiv() && !IsAtWar(eMinor) && GetCivApproach(eMinor) > CIV_APPROACH_HOSTILE) { CvPlayer* pMinor = &GET_PLAYER(eMinor); CvMinorCivAI* pMinorCivAI = pMinor->GetMinorCivAI(); if (pMinor && pMinorCivAI) { if (pMinorCivAI->IsActiveQuestForPlayer(GetID(), MINOR_CIV_QUEST_DENOUNCE_MAJOR) && pMinorCivAI->GetQuestData1(GetID(), MINOR_CIV_QUEST_DENOUNCE_MAJOR) == ePlayer) { iWeight += 2; if (IsGoingForDiploVictory() || GetPlayer()->GetPlayerTraits()->IsDiplomat()) iWeight += 3; else if (IsDiplomat() || IsSecondaryDiplomat()) iWeight += 2; } } } } } // Used when friends are asking us to denounce someone if (bBias) iWeight += 3; return iWeight; } /// Has this player denounced someone we have a DoF with? bool CvDiplomacyAI::IsPlayerDenouncedFriend(PlayerTypes ePlayer) const { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) continue; if (!GET_PLAYER(eLoopPlayer).isAlive() || GET_PLAYER(eLoopPlayer).getNumCities() <= 0) continue; if (IsDoFAccepted(eLoopPlayer) && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eLoopPlayer)) return true; } return false; } /// Has this player denounced someone we've also denounced? bool CvDiplomacyAI::IsPlayerDenouncedEnemy(PlayerTypes ePlayer) const { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) continue; if (!GET_PLAYER(eLoopPlayer).isAlive() || GET_PLAYER(eLoopPlayer).getNumCities() <= 0) continue; if ((IsDenouncedPlayer(eLoopPlayer) || IsAtWar(eLoopPlayer)) && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eLoopPlayer)) return true; } return false; } int CvDiplomacyAI::GetNumFriendsDenounced(PlayerTypes ePlayer) const { int iCount = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) continue; if (!GET_PLAYER(eLoopPlayer).isAlive() || GET_PLAYER(eLoopPlayer).getNumCities() <= 0) continue; if (IsDoFAccepted(eLoopPlayer) && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eLoopPlayer)) iCount++; } return iCount; } int CvDiplomacyAI::GetNumEnemiesDenounced(PlayerTypes ePlayer) const { int iCount = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) continue; if (!GET_PLAYER(eLoopPlayer).isAlive() || GET_PLAYER(eLoopPlayer).getNumCities() <= 0) continue; if ((IsDenouncedPlayer(eLoopPlayer) || IsAtWar(eLoopPlayer)) && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eLoopPlayer)) iCount++; } return iCount; } /////////////////////////////// // Requests of Friends /////////////////////////////// /// Does this AI want to request that ePlayer denounce someone? PlayerTypes CvDiplomacyAI::GetRequestFriendToDenounce(PlayerTypes ePlayer, bool& bRandFailed) { bRandFailed = false; // Must be friends with ePlayer if(!IsDoFAccepted(ePlayer)) return NO_PLAYER; PlayerTypes eTarget = NO_PLAYER; int iBestWeight = -1; CvDiplomacyAI* pTheirAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); CvTeam* pTheirTeam = &GET_TEAM(GET_PLAYER(ePlayer).getTeam()); for(int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; CvPlayer* pLoopPlayer = &GET_PLAYER(eLoopPlayer); TeamTypes eLoopTeam = pLoopPlayer->getTeam(); // Must be alive if(!pLoopPlayer->isAlive()) continue; // Can't be either of us if(eLoopPlayer == GetID() || eLoopPlayer == ePlayer) continue; // Don't pick someone they haven't met if(!pTheirTeam->isHasMet(eLoopTeam)) continue; // Don't pick someone they're at war with if(pTheirTeam->isAtWar(eLoopTeam)) continue; // Don't pick someone they've already denounced if(pTheirAI->IsDenouncedPlayer(eLoopPlayer)) continue; // Only look at players we've denounced, or at war with if(!IsAtWar(eLoopPlayer) && !IsDenouncedPlayer(eLoopPlayer)) continue; // Rand roll int iWeight = GetDenounceWeight(ePlayer, /*bBias*/ false); iWeight += GetNeediness(); // Generally ranges from 3 to 7 if(iWeight >= 23 && iWeight > iBestWeight) { iBestWeight = iWeight; eTarget = eLoopPlayer; bRandFailed = false; // Set this here as well, because we could have failed a roll on another player, and then succeeded on a later one } else bRandFailed = true; } return eTarget; } /// Are we upset that our friend ePlayer refused our request to denounce eAgainstPlayer? bool CvDiplomacyAI::IsFriendDenounceRefusalUnacceptable(PlayerTypes ePlayer, PlayerTypes eAgainstPlayer) { UNUSED_VARIABLE(ePlayer); // Rand roll int iDesire = GetDenounceWeight(eAgainstPlayer, /*bBias*/ false); iDesire += GetNeediness(); // Generally ranges from 3 to 7 iDesire -= GetForgiveness(); // Generally ranges from 3 to 7 if(iDesire >= 23) // Note: 23 is required to even ask (see function above this one) return true; return false; } ///////////////////////////////////////////////////////// // A Player's adherence to this AI's statements ///////////////////////////////////////////////////////// /// Will this AI agree to stop sending missionaries and prophets to ePlayer's cities? bool CvDiplomacyAI::IsStopSpreadingReligionAcceptable(PlayerTypes ePlayer) { // Debug mode if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_MECHANICS) && IsAIMustAcceptHumanDiscussRequests()) return true; // Always acceptable for teammates and vassals if (IsTeammate(ePlayer) || IsVassal(ePlayer)) return true; // Always acceptable if they resurrected or liberated us if (IsLiberator(ePlayer, true, true)) return true; CivApproachTypes eApproach = GetCivApproach(ePlayer); CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); // If player is afraid, always say yes if (eApproach == CIV_APPROACH_AFRAID) return true; // If we control 33%+ of other players' original capitals, don't make a promise unless we like them if (GetPlayer()->GetFractionOriginalCapitalsUnderControl() >= 33) { if (eApproach != CIV_APPROACH_FRIENDLY || eOpinion < CIV_OPINION_FRIEND) { if (!IsDoFAccepted(ePlayer) && !IsHasDefensivePact(ePlayer) && GetCoopWarAgreementScore(ePlayer) <= 0) { return false; } } } // Refuse all promises if close to world conquest and they still have original capitals if (IsCloseToWorldConquest() && !IsMaster(ePlayer) && (GET_PLAYER(ePlayer).GetCapitalConqueror() != NO_PLAYER || GET_PLAYER(ePlayer).GetNumCapitalCities() > 0)) return false; // If player is Hostile or planning War, always say no if (eApproach <= CIV_APPROACH_HOSTILE) return false; // If player is an enemy or unforgivable, always say no if (eOpinion <= CIV_OPINION_ENEMY) return false; // If player is plotting against us, always say no if (GetNumTimesTheyPlottedAgainstUs(ePlayer) > 0) return false; // Never acceptable if they've converted our cities (unless a promise was made, and not broken) if (HasEverConvertedCity(ePlayer) && !MadeNoConvertPromise(ePlayer)) return false; if (BrokeNoConvertPromise(ePlayer) || IgnoredNoConvertPromise(ePlayer)) return false; // Never acceptable if they're an Enemy or Unforgivable if (GetCivOpinion(ePlayer) <= CIV_OPINION_ENEMY) return false; // Backstabber? Never acceptable. if (IsUntrustworthy(ePlayer)) return false; // Acceptable if we're friends if ((IsDoFAccepted(ePlayer) && !IsWantsToEndDoFWithPlayer(ePlayer)) || (IsHasDefensivePact(ePlayer) && !IsWantsToEndDefensivePactWithPlayer(ePlayer))) return true; else if (eApproach == CIV_APPROACH_FRIENDLY) { if (GetDoFType(ePlayer) >= DOF_TYPE_ALLIES || GetMostValuableFriend() == ePlayer || GetMostValuableAlly() == ePlayer || GetCoopWarAgreementScore(ePlayer) > 0 || GetCivOpinion(ePlayer) == CIV_OPINION_ALLY) return true; } // If we're fiercely competitive, always say no if (IsCompetingForVictory()) { if (GetVictoryDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG || GetVictoryBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG || (GetPlayer()->GetCurrentEra() < 3 && IsEarlyGameCompetitor(ePlayer)) || (GetPlayer()->GetCurrentEra() >= 3 && GetBiggestCompetitor() == ePlayer) || GetPrimeLeagueCompetitor() == ePlayer || IsEndgameAggressiveTo(ePlayer)) return false; } int iFlavorReligion = m_pPlayer->GetFlavorManager()->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_RELIGION")); if (iFlavorReligion > 4 && GetPlayer()->GetPlayerTraits()->IsReligious()) { return false; } if (iFlavorReligion < 7) { return true; } return false; } /// Will this AI agree to stop digging up ePlayer's artifacts? bool CvDiplomacyAI::IsStopDiggingAcceptable(PlayerTypes ePlayer) { // Debug mode if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_MECHANICS) && IsAIMustAcceptHumanDiscussRequests()) return true; // Always acceptable if they resurrected or liberated us if (IsLiberator(ePlayer, true, true)) return true; CivApproachTypes eApproach = GetCivApproach(ePlayer); CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); // If player is afraid, always say yes if (eApproach == CIV_APPROACH_AFRAID) return true; // If we control 33%+ of other players' original capitals, don't make a promise unless we like them if (GetPlayer()->GetFractionOriginalCapitalsUnderControl() >= 33) { if (eApproach != CIV_APPROACH_FRIENDLY || eOpinion < CIV_OPINION_FRIEND) { if (!IsDoFAccepted(ePlayer) && !IsHasDefensivePact(ePlayer) && GetCoopWarAgreementScore(ePlayer) <= 0) { return false; } } } // Refuse all promises if close to world conquest and they still have original capitals if (IsCloseToWorldConquest() && !IsMaster(ePlayer) && (GET_PLAYER(ePlayer).GetCapitalConqueror() != NO_PLAYER || GET_PLAYER(ePlayer).GetNumCapitalCities() > 0)) return false; // If player is Hostile or planning War, always say no if (eApproach <= CIV_APPROACH_HOSTILE) return false; // If player is an enemy or unforgivable, always say no if (eOpinion <= CIV_OPINION_ENEMY) return false; // Never acceptable if they've dug up our artifacts (unless a promise was made, and not broken) if (GetNumArtifactsEverDugUp(ePlayer) > 0 && !MadeNoDiggingPromise(ePlayer)) return false; if (BrokeNoDiggingPromise(ePlayer) || IgnoredNoDiggingPromise(ePlayer)) return false; // Never acceptable if they're an Enemy or Unforgivable if (GetCivOpinion(ePlayer) <= CIV_OPINION_ENEMY) return false; // If player is plotting against us, always say no if (GetNumTimesTheyPlottedAgainstUs(ePlayer) > 0) return false; // Backstabber? Never acceptable. if (IsUntrustworthy(ePlayer)) return false; // Acceptable if we're friends if ((IsDoFAccepted(ePlayer) && !IsWantsToEndDoFWithPlayer(ePlayer)) || (IsHasDefensivePact(ePlayer) && !IsWantsToEndDefensivePactWithPlayer(ePlayer))) return true; else if (eApproach == CIV_APPROACH_FRIENDLY) { if (GetDoFType(ePlayer) >= DOF_TYPE_ALLIES || GetMostValuableFriend() == ePlayer || GetMostValuableAlly() == ePlayer || GetCoopWarAgreementScore(ePlayer) > 0 || GetCivOpinion(ePlayer) == CIV_OPINION_ALLY) return true; } // If we're fiercely competitive, always say no if (IsCompetingForVictory()) { if (GetVictoryDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG || GetVictoryBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG || (GetPlayer()->GetCurrentEra() < 3 && IsEarlyGameCompetitor(ePlayer)) || (GetPlayer()->GetCurrentEra() >= 3 && GetBiggestCompetitor() == ePlayer) || GetPrimeLeagueCompetitor() == ePlayer || IsEndgameAggressiveTo(ePlayer)) return false; } // Going for or close to culture victory? if (IsGoingForCultureVictory() || IsCloseToCultureVictory()) return false; int iFlavorCulture = m_pPlayer->GetFlavorManager()->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_CULTURE")); if (iFlavorCulture > 4 && GetPlayer()->GetPlayerTraits()->IsTourism()) { return false; } if (iFlavorCulture < 7) { return true; } return false; } ///////////////////////////////////////////////////////// // Opinion modifiers ///////////////////////////////////////////////////////// /// Adjusts the duration of a temporary opinion modifier based on a flavor and game speed /// 1 = -20%, 2 = -15%, 3 = -10%, 4 = -5%, 5 = 0%, 6 = +4%, 7 = +8%, 8 = +12%, 9 = +16%, 10 = +20% /// bInvertModifier: 1 = +20%, 2 = +15%, 3 = +10%, 4 = +5%, 5 = 0%, 6 = -4%, 7 = -8%, 8 = -12%, 9 = -16%, 10 = -20% int CvDiplomacyAI::AdjustModifierDuration(int iDuration, int iFlavorValue, bool bInvertModifier, bool bGamespeed) const { if (iDuration <= 0) return 1; if (!bInvertModifier) { switch (iFlavorValue) { case 1: { iDuration *= 80; iDuration /= 100; break; } case 2: { iDuration *= 85; iDuration /= 100; break; } case 3: { iDuration *= 90; iDuration /= 100; break; } case 4: { iDuration *= 95; iDuration /= 100; break; } case 6: { iDuration *= 104; iDuration /= 100; break; } case 7: { iDuration *= 108; iDuration /= 100; break; } case 8: { iDuration *= 112; iDuration /= 100; break; } case 9: { iDuration *= 116; iDuration /= 100; break; } case 10: { iDuration *= 120; iDuration /= 100; break; } default: break; } } else { switch (iFlavorValue) { case 1: { iDuration *= 120; iDuration /= 100; break; } case 2: { iDuration *= 115; iDuration /= 100; break; } case 3: { iDuration *= 110; iDuration /= 100; break; } case 4: { iDuration *= 105; iDuration /= 100; break; } case 6: { iDuration *= 96; iDuration /= 100; break; } case 7: { iDuration *= 92; iDuration /= 100; break; } case 8: { iDuration *= 88; iDuration /= 100; break; } case 9: { iDuration *= 84; iDuration /= 100; break; } case 10: { iDuration *= 80; iDuration /= 100; break; } default: break; } } if (bGamespeed) { iDuration *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDuration /= 100; } return max(iDuration, 1); } /// Adjusts the value of a temporary opinion modifier based on modifier type and how many turns have passed int CvDiplomacyAI::AdjustTimedModifier(int iValue, int iDuration, int iTurn, TimedModifierTypes eModifierType, int iStacks, int iFirstStackValue) { // Some error checking in case a bad value gets in // Don't crash here if the value is invalid, because the UI could call this mid-TestOpinionModifiers() if (iValue == 0 || iTurn < 0 || iStacks < 0) return iValue; if (iDuration <= 0) iDuration = 1; if (eModifierType == TIMED_MODIFIER_DIMINISHING) { if ((iValue > 0 && (iFirstStackValue < 0 || iFirstStackValue > iValue)) || (iValue < 0 && (iFirstStackValue > 0 || iFirstStackValue < iValue))) { UNREACHABLE(); // crash because we have configured defines incorrectly! } } // Modifier has not yet begun to expire. Note: Some WC modifiers can have -1 turns passed int iTurnsPassed = GC.getGame().getGameTurn() - iTurn; if (iTurnsPassed <= 0) return iValue; int iDurationPercent = (iTurnsPassed * 100) / iDuration; int iPercentageLeft = 100 - iDurationPercent; // Modifier has fully expired, TestOpinionModifiers() will clean this up to the proper value, for now return 0. if (iTurnsPassed >= iDuration || iPercentageLeft <= 0) return 0; // One stack = identical behavior to a standard modifier // Diminishing modifiers with a first stack of 0 = assume the user meant to make it stacked if (iStacks == 1) eModifierType = TIMED_MODIFIER_STANDARD; else if (eModifierType == TIMED_MODIFIER_DIMINISHING && iFirstStackValue == 0) eModifierType = TIMED_MODIFIER_STACKED; switch (eModifierType) { // Standard modifier - reduce based on % of timer left case TIMED_MODIFIER_STANDARD: { return iValue < 0 ? std::min(-1, iValue * iPercentageLeft / 100) : std::max(1, iValue * iPercentageLeft / 100); } // Stacked modifier (modifier value * # of occurrences) - reduces to 1/2 of current stacks (rounded down) as timer ticks down, and then resets timer if stacks are remaining case TIMED_MODIFIER_STACKED: { int iValuePerStack = iValue / iStacks; // # of stacks int iStacksToReduceTo = iStacks / 2; // # of stacks after the next reduction int iValueToReduceTo = iStacksToReduceTo * iValuePerStack; // after the next reduction int iAmountToReduceBy = (iValue - iValueToReduceTo) * iDurationPercent / 100; return iValue < 0 ? std::min(-1, iValue - iAmountToReduceBy) : std::max(1, iValue - iAmountToReduceBy); } // Diminishing modifiers - same as stacked modifier, but the first stack is worth more than all subsequent ones case TIMED_MODIFIER_DIMINISHING: { int iRemainingValue = iValue - iFirstStackValue; int iRemainingStacks = iStacks - 1; int iStacksToReduceTo = iStacks / 2; if (iRemainingStacks == 1 || iStacksToReduceTo == 1) { int iAmountToReduceBy = iRemainingValue * iDurationPercent / 100; return iValue < 0 ? std::min(-1, iValue - iAmountToReduceBy) : std::max(1, iValue - iAmountToReduceBy); } // If we got here, iRemainingStacks > 1 and iStacksToReduceTo > 1 int iValuePerStack = iRemainingValue / iRemainingStacks; int iValueToReduceTo = iFirstStackValue + ((iStacksToReduceTo - 1) * iValuePerStack); int iAmountToReduceBy = (iValue - iValueToReduceTo) * iDurationPercent / 100; return iValue < 0 ? std::min(-1, iValue - iAmountToReduceBy) : std::max(1, iValue - iAmountToReduceBy); } default: UNREACHABLE(); } } /// Adjusts the value of a conditional opinion modifier based on a leader flavor /// 1 = -50%, 2 = -37.5%, 3 = -25%, 4 = -12.5%, 5 = 0%, 6 = +10%, 7 = +20%, 8 = +30%, 9 = +40%, 10 = +50% /// bInvertModifier: 1 = +50%, 2 = +37.5%, 3 = +25%, 4 = +12.5%, 5 = 0%, 6 = -10%, 7 = -20%, 8 = -30%, 9 = -40%, 10 = -50% int CvDiplomacyAI::AdjustConditionalModifier(int iValue, int iFlavorValue, bool bInvertModifier) const { if (iValue == 0 || iFlavorValue == 0) return iValue; if (!bInvertModifier) { switch (iFlavorValue) { case 1: { iValue *= 50; iValue /= 100; break; } case 2: { iValue *= 625; iValue /= 1000; break; } case 3: { iValue *= 75; iValue /= 100; break; } case 4: { iValue *= 875; iValue /= 1000; break; } case 6: { iValue *= 110; iValue /= 100; break; } case 7: { iValue *= 120; iValue /= 100; break; } case 8: { iValue *= 130; iValue /= 100; break; } case 9: { iValue *= 140; iValue /= 100; break; } case 10: { iValue *= 150; iValue /= 100; break; } default: break; } } else { switch (iFlavorValue) { case 1: { iValue *= 150; iValue /= 100; break; } case 2: { iValue *= 1375; iValue /= 1000; break; } case 3: { iValue *= 125; iValue /= 100; break; } case 4: { iValue *= 1125; iValue /= 1000; break; } case 6: { iValue *= 90; iValue /= 100; break; } case 7: { iValue *= 80; iValue /= 100; break; } case 8: { iValue *= 70; iValue /= 100; break; } case 9: { iValue *= 60; iValue /= 100; break; } case 10: { iValue *= 50; iValue /= 100; break; } default: break; } } return iValue; } static void checkAndUpdateStackingOpinionModifier( CvDiplomacyAI* pThis, PlayerTypes ePlayer, int iTurn, int (CvDiplomacyAI::*getStacks)(PlayerTypes) const, void (CvDiplomacyAI::*setStacks)(PlayerTypes, int), int (CvDiplomacyAI::*getTurn)(PlayerTypes) const, int baseTurns, int modifier, bool bInvert = false) { int iStacks = (pThis->*getStacks)(ePlayer); if (iStacks > 0) { int iTurnDifference = iTurn - (pThis->*getTurn)(ePlayer); int iDuration = pThis->AdjustModifierDuration(baseTurns, modifier, bInvert, true); while (iTurnDifference >= iDuration && iStacks > 0) { iStacks /= 2; iTurnDifference -= iDuration; } (pThis->*setStacks)(ePlayer, iStacks); } } static void checkAndUpdateNonStackingOpinionModifier( CvDiplomacyAI* pThis, PlayerTypes ePlayer, int iTurn, bool (CvDiplomacyAI::*isModifierActive)(PlayerTypes) const, void (CvDiplomacyAI::*setTurn)(PlayerTypes, int), int (CvDiplomacyAI::*getTurn)(PlayerTypes) const, int baseTurns, int modifier, bool bInvert = false) { if ((pThis->*isModifierActive)(ePlayer)) { int iTurnDifference = iTurn - (pThis->*getTurn)(ePlayer); int iDuration = pThis->AdjustModifierDuration(baseTurns, modifier, bInvert, true); if (iTurnDifference >= iDuration) (pThis->*setTurn)(ePlayer, -1); } } /// Clear any expired promises and opinion modifiers prior to updating Opinion for the turn void CvDiplomacyAI::TestOpinionModifiers() { int iTurn = GC.getGame().getGameTurn(); int iTurnDifference = 0; int iDuration = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = static_cast(iPlayerLoop); if (!IsHasMet(ePlayer, true) || ePlayer == GetID()) continue; // Some promises generate notifications on their turn of expiry, handle that first if (GetNumTurnsExpansionPromise(ePlayer) == 0) { CvNotifications* pNotifications = GET_PLAYER(ePlayer).GetNotifications(); if (pNotifications) { CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_EXPANSION_PROMISE_EXPIRED", GET_PLAYER(GetID()).getCivilizationShortDescriptionKey()); CvString strSummary = GetLocalizedText("TXT_KEY_NOTIFICATION_EXPANSION_PROMISE_EXPIRED_S"); pNotifications->Add(NOTIFICATION_EXPANSION_PROMISE_EXPIRED, strBuffer, strSummary, -1, -1, GetID(), ePlayer); } pNotifications = GetPlayer()->GetNotifications(); if (pNotifications) { CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_EXPANSION_PROMISE_EXPIRED_THEM", GET_PLAYER(ePlayer).getCivilizationShortDescriptionKey()); CvString strSummary = GetLocalizedText("TXT_KEY_NOTIFICATION_EXPANSION_PROMISE_EXPIRED_THEM_S", GET_PLAYER(ePlayer).getCivilizationShortDescriptionKey()); pNotifications->Add(NOTIFICATION_EXPANSION_PROMISE_EXPIRED, strBuffer, strSummary, -1, -1, ePlayer, GetID()); } } if (GetNumTurnsBorderPromise(ePlayer) == 0) { CvNotifications* pNotifications = GET_PLAYER(ePlayer).GetNotifications(); if (pNotifications) { CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_BORDER_PROMISE_EXPIRED", GET_PLAYER(GetID()).getCivilizationShortDescriptionKey()); CvString strSummary = GetLocalizedText("TXT_KEY_NOTIFICATION_BORDER_PROMISE_EXPIRED_S"); pNotifications->Add(NOTIFICATION_BORDER_PROMISE_EXPIRED, strBuffer, strSummary, -1, -1, GetID(), ePlayer); } } // Clear expired promises & expired promise penalties // Broken Military & Attack CS promises are handled in TestBackstabbingPenalties() instead // Military Promise checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::MadeMilitaryPromise, &CvDiplomacyAI::GetMilitaryPromiseTurn, &CvDiplomacyAI::SetMilitaryPromiseState, /*20*/ GD_INT_GET(MOVE_TROOPS_MEMORY_TURN_EXPIRATION), false); checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::IgnoredMilitaryPromise, &CvDiplomacyAI::GetMilitaryPromiseTurn, &CvDiplomacyAI::SetMilitaryPromiseState, /*40*/ GD_INT_GET(MILITARY_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN), false); // Expansion Promise checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::MadeExpansionPromise, &CvDiplomacyAI::GetExpansionPromiseTurn, &CvDiplomacyAI::SetExpansionPromiseState, /*50*/ GD_INT_GET(EXPANSION_PROMISE_TURNS_EFFECTIVE)); checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::IgnoredExpansionPromise, &CvDiplomacyAI::GetExpansionPromiseTurn, &CvDiplomacyAI::SetExpansionPromiseState, /*30*/ GD_INT_GET(EXPANSION_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN)); checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::BrokeExpansionPromise, &CvDiplomacyAI::GetExpansionPromiseTurn, &CvDiplomacyAI::SetExpansionPromiseState, /*50*/ GD_INT_GET(EXPANSION_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN)); // Border Promise checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::MadeBorderPromise, &CvDiplomacyAI::GetBorderPromiseTurn, &CvDiplomacyAI::SetBorderPromiseState, /*50*/ GD_INT_GET(BORDER_PROMISE_TURNS_EFFECTIVE)); checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::IgnoredBorderPromise, &CvDiplomacyAI::GetBorderPromiseTurn, &CvDiplomacyAI::SetBorderPromiseState, /*30*/ GD_INT_GET(BORDER_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN)); checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::BrokeBorderPromise, &CvDiplomacyAI::GetBorderPromiseTurn, &CvDiplomacyAI::SetBorderPromiseState, /*50*/ GD_INT_GET(BORDER_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN)); // Bully City-State Promise checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::IgnoredBullyCityStatePromise, &CvDiplomacyAI::GetBullyCityStatePromiseTurn, &CvDiplomacyAI::SetBullyCityStatePromiseState, /*30*/ GD_INT_GET(BULLY_CS_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN)); checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::BrokeBullyCityStatePromise, &CvDiplomacyAI::GetBullyCityStatePromiseTurn, &CvDiplomacyAI::SetBullyCityStatePromiseState, /*50*/ GD_INT_GET(BULLY_CS_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN)); // Attack City-State Promise checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::IgnoredAttackCityStatePromise, &CvDiplomacyAI::GetAttackCityStatePromiseTurn, &CvDiplomacyAI::SetAttackCityStatePromiseState, /*40*/ GD_INT_GET(ATTACK_CS_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN)); // Spy Promise checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::IgnoredSpyPromise, &CvDiplomacyAI::GetSpyPromiseTurn, &CvDiplomacyAI::SetSpyPromiseState, /*30*/ GD_INT_GET(SPY_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN)); checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::BrokeSpyPromise, &CvDiplomacyAI::GetSpyPromiseTurn, &CvDiplomacyAI::SetSpyPromiseState, /*50*/ GD_INT_GET(SPY_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN)); // No Convert Promise checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::IgnoredNoConvertPromise, &CvDiplomacyAI::GetNoConvertPromiseTurn, &CvDiplomacyAI::SetNoConvertPromiseState, /*30*/ GD_INT_GET(CONVERT_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN)); checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::BrokeNoConvertPromise, &CvDiplomacyAI::GetNoConvertPromiseTurn, &CvDiplomacyAI::SetNoConvertPromiseState, /*50*/ GD_INT_GET(CONVERT_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN)); // No Digging Promise checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::IgnoredNoDiggingPromise, &CvDiplomacyAI::GetNoDiggingPromiseTurn, &CvDiplomacyAI::SetNoConvertPromiseState, /*30*/ GD_INT_GET(DIGGING_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN)); checkAndUpdatePromise(this, ePlayer, iTurn, &CvDiplomacyAI::BrokeNoDiggingPromise, &CvDiplomacyAI::GetNoDiggingPromiseTurn, &CvDiplomacyAI::SetNoConvertPromiseState, /*50*/ GD_INT_GET(DIGGING_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN)); // Coop War Promise // This one does not scale with game speed, and needs special handling. if (BrokeCoopWarPromise(ePlayer)) { iTurnDifference = iTurn - GetBrokeCoopWarPromiseTurn(ePlayer); if (iTurnDifference >= /*60*/ max(GD_INT_GET(COOP_WAR_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN), 1)) SetBrokeCoopWarPromise(ePlayer, false); } // Reduce value counters ChangeRecentTradeValue(ePlayer, /*-2*/ -GD_INT_GET(DEAL_VALUE_PER_TURN_DECAY)); ChangeCommonFoeValue(ePlayer, /*-25*/ -GD_INT_GET(COMMON_FOE_VALUE_PER_TURN_DECAY)); ChangeCivilianKillerValue(ePlayer, /*-100*/ -GD_INT_GET(CIVILIAN_KILLER_VALUE_PER_TURN_DECAY)); ChangeVassalProtectValue(ePlayer, GetVassalProtectValue(ePlayer) >= 0 ? /*-25*/ -GD_INT_GET(VASSALAGE_PROTECTED_PER_TURN_DECAY) : /*25*/ GD_INT_GET(VASSALAGE_PROTECTED_PER_TURN_DECAY), true); if (GetRecentAssistValue(ePlayer) > 0) { // Bonus does not decay when a coop war is "soon". if (GetGlobalCoopWarWithState(ePlayer, /*bExcludeOngoing*/ true) != COOP_WAR_STATE_PREPARING) { ChangeRecentAssistValue(ePlayer, /*-3*/ -GD_INT_GET(ASSIST_VALUE_PER_TURN_DECAY), true); } } else if (GetRecentAssistValue(ePlayer) < 0) { ChangeRecentAssistValue(ePlayer, /*3*/ GD_INT_GET(ASSIST_VALUE_PER_TURN_DECAY), true); } // Clear any expired opinion modifiers // Civilians returned? checkAndUpdateStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::GetNumCiviliansReturnedToMe, &CvDiplomacyAI::SetNumCiviliansReturnedToMe, &CvDiplomacyAI::GetCiviliansReturnedToMeTurn, /*50*/ GD_INT_GET(RETURNED_CIVILIAN_TURNS_UNTIL_FORGOTTEN), GetLoyalty()); // Landmarks built? checkAndUpdateStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::GetNumLandmarksBuiltForMe, &CvDiplomacyAI::SetNumLandmarksBuiltForMe, &CvDiplomacyAI::GetLandmarksBuiltForMeTurn, /*50*/ GD_INT_GET(BUILT_LANDMARK_TURNS_UNTIL_FORGOTTEN), GetLoyalty()); // Cities liberated? checkAndUpdateStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::GetNumCitiesLiberatedBy, &CvDiplomacyAI::SetNumCitiesLiberatedBy, &CvDiplomacyAI::GetLiberatedCitiesTurn, /*75*/ GD_INT_GET(LIBERATED_CITY_TURNS_UNTIL_FORGOTTEN), GetLoyalty()); // Forgave for spying? checkAndUpdateNonStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::IsPlayerForgaveForSpying, &CvDiplomacyAI::SetForgaveForSpyingTurn, &CvDiplomacyAI::GetForgaveForSpyingTurn, /*30*/ GD_INT_GET(FORGAVE_FOR_SPYING_TURNS_UNTIL_FORGOTTEN), GetLoyalty()); // Trade demands? checkAndUpdateStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::GetNumDemandsMade, &CvDiplomacyAI::SetNumDemandsMade, &CvDiplomacyAI::GetDemandMadeTurn, /*50*/ GD_INT_GET(MADE_DEMAND_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true); // Converted our cities? checkAndUpdateStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::GetNegativeReligiousConversionPoints, &CvDiplomacyAI::SetNegativeReligiousConversionPoints, &CvDiplomacyAI::GetReligiousConversionTurn, /*25*/ GD_INT_GET(RELIGIOUS_CONVERSION_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true); // Robbed us? checkAndUpdateStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::GetNumTimesRobbedBy, &CvDiplomacyAI::SetNumTimesRobbedBy, &CvDiplomacyAI::GetRobbedTurn, /*50*/ GD_INT_GET(ROBBED_US_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true); // Plundered our trade routes? checkAndUpdateStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::GetNumTradeRoutesPlundered, &CvDiplomacyAI::SetNumTradeRoutesPlundered, &CvDiplomacyAI::GetPlunderedTradeRouteTurn, /*15*/ GD_INT_GET(PLUNDERED_TRADE_ROUTE_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true); // Plotted against us? Special case - scale modifier duration dynamically based on military strength and proximity. int iStacks = GetNumTimesTheyPlottedAgainstUs(ePlayer); if (iStacks > 0) { int iDurationMod = (int)GetMilitaryStrengthComparedToUs(ePlayer); // between 0 and 7, inclusive iDurationMod += ((int)GET_PLAYER(ePlayer).GetProximityToPlayer(GetID()) * 2); // between 0 and 6, inclusive iDurationMod *= 2; iDuration = /*14*/ GD_INT_GET(PLOTTED_AGAINST_US_TURNS_UNTIL_FORGIVEN) + iDurationMod; // between 14 and 40 turns, inclusive, not scaling with gamespeed iTurnDifference = iTurn - GetPlottedAgainstUsTurn(ePlayer); if (iTurnDifference >= max(iDuration, 1)) { iStacks /= 2; SetNumTimesTheyPlottedAgainstUs(ePlayer, iStacks); } } // Beat us to World Wonders? checkAndUpdateStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::GetNumWondersBeatenTo, &CvDiplomacyAI::SetNumWondersBeatenTo, &CvDiplomacyAI::GetBeatenToWonderTurn, /*60*/ GD_INT_GET(BEATEN_TO_WONDER_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true); // Lowered our City-State Influence? checkAndUpdateStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::GetNumTimesTheyLoweredOurInfluence, &CvDiplomacyAI::SetNumTimesTheyLoweredOurInfluence, &CvDiplomacyAI::GetLoweredOurInfluenceTurn, /*40*/ GD_INT_GET(LOWERED_OUR_INFLUENCE_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true); // Stole our City-State allies? checkAndUpdateStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::GetNumTimesPerformedCoupAgainstUs, &CvDiplomacyAI::SetNumTimesPerformedCoupAgainstUs, &CvDiplomacyAI::GetPerformedCoupTurn, /*50*/ GD_INT_GET(PERFORMED_COUP_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true); // Stole our artifacts? checkAndUpdateStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::GetNegativeArchaeologyPoints, &CvDiplomacyAI::SetNegativeArchaeologyPoints, &CvDiplomacyAI::GetStoleArtifactTurn, /*50*/ GD_INT_GET(EXCAVATED_ARTIFACT_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true); // Shared intrigue with us? checkAndUpdateStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::GetNumTimesIntrigueSharedBy, &CvDiplomacyAI::SetNumTimesIntrigueSharedBy, &CvDiplomacyAI::GetIntrigueSharedTurn, /*50*/ GD_INT_GET(SHARED_INTRIGUE_TURNS_UNTIL_FORGOTTEN), GetLoyalty()); // Sided with their protected minor? checkAndUpdateNonStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::IsAngryAboutSidedWithProtectedMinor, &CvDiplomacyAI::SetOtherPlayerSidedWithProtectedMinorTurn, &CvDiplomacyAI::GetOtherPlayerSidedWithProtectedMinorTurn, /*10*/ GD_INT_GET(OPINION_WEIGHT_SIDED_WITH_THEIR_MINOR_NUM_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true); // NOTE: We intentionally omit bullying here because of the Lua method GetTurnsSincePlayerBulliedProtectedMinor(). // The modifier will still be cancelled out in IsAngryAboutProtectedMinorBullied(), but the last City-State bullied and the turn it was bullied on remain stored. // Attacked our protected minor? checkAndUpdateNonStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::IsAngryAboutProtectedMinorAttacked, &CvDiplomacyAI::SetOtherPlayerAttackedProtectedMinorTurn, &CvDiplomacyAI::GetOtherPlayerAttackedProtectedMinorTurn, /*30*/ GD_INT_GET(OPINION_WEIGHT_ATTACKED_PROTECTED_MINOR_NUM_TURNS_UNTIL_FORGIVEN), GetMinorCivApproachBias(CIV_APPROACH_FRIENDLY)); // Killed our protected minor? checkAndUpdateNonStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::IsAngryAboutProtectedMinorKilled, &CvDiplomacyAI::SetOtherPlayerKilledProtectedMinorTurn, &CvDiplomacyAI::GetOtherPlayerKilledProtectedMinorTurn, /*50*/ GD_INT_GET(OPINION_WEIGHT_KILLED_PROTECTED_MINOR_NUM_TURNS_UNTIL_FORGIVEN), GetMinorCivApproachBias(CIV_APPROACH_FRIENDLY)); // Liked their WC proposal? checkAndUpdateNonStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::WeLikedTheirProposal, &CvDiplomacyAI::SetWeLikedTheirProposalTurn, &CvDiplomacyAI::GetWeLikedTheirProposalTurn, /*50*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_NUM_TURNS), GetWorkWithWillingness()); // Disliked their WC proposal? checkAndUpdateNonStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::WeDislikedTheirProposal, &CvDiplomacyAI::SetWeDislikedTheirProposalTurn, &CvDiplomacyAI::GetWeDislikedTheirProposalTurn, /*50*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_NUM_TURNS), GetWorkAgainstWillingness()); int iSupportValue = GetSupportedOurProposalValue(ePlayer); // They foiled our proposals! if (iSupportValue > 0) { int iProposalTurn = 0; iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(OPINION_WEIGHT_THEY_FOILED_OUR_PROPOSAL_NUM_TURNS), GetWorkAgainstWillingness()); if (IsFoiledOurProposalAndThenSupportedUs(ePlayer)) { // The more recent action should carry higher value if Forgiveness is higher iProposalTurn = GetTheySupportedOurProposalTurn(ePlayer); int iDurationMod = (GetForgiveness() - 5) * 2; iDurationMod *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDurationMod /= 100; if (iDurationMod > 0) { iDuration -= iDurationMod; } } else { iProposalTurn = GetTheyFoiledOurProposalTurn(ePlayer); } iTurnDifference = iTurn - iProposalTurn; if (iTurnDifference >= max(iDuration, 1)) { SetTheySupportedOurProposalTurn(ePlayer, -1); SetTheyFoiledOurProposalTurn(ePlayer, -1); SetSupportedOurProposalValue(ePlayer, 0); } } // They supported our proposals! else if (iSupportValue < 0) { int iProposalTurn = 0; iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_PROPOSAL_NUM_TURNS), GetWorkWithWillingness()); if (IsSupportedOurProposalAndThenFoiledUs(ePlayer)) { // The more recent action should carry higher value if Neediness is higher iProposalTurn = GetTheyFoiledOurProposalTurn(ePlayer); int iDurationMod = (GetNeediness() - 5) * 2; iDurationMod *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDurationMod /= 100; if (iDurationMod > 0) { iDuration -= iDurationMod; } } else { iProposalTurn = GetTheySupportedOurProposalTurn(ePlayer); } iTurnDifference = iTurn - iProposalTurn; if (iTurnDifference >= max(iDuration, 1)) { SetTheySupportedOurProposalTurn(ePlayer, -1); SetTheyFoiledOurProposalTurn(ePlayer, -1); SetSupportedOurProposalValue(ePlayer, 0); } } // Supported our hosting? checkAndUpdateNonStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::TheySupportedOurHosting, &CvDiplomacyAI::SetTheySupportedOurHostingTurn, &CvDiplomacyAI::GetTheySupportedOurHostingTurn, /*50*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_HOSTING_NUM_TURNS), GetWorkWithWillingness()); // Sanctioned us? checkAndUpdateNonStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::HasTriedToSanctionUs, &CvDiplomacyAI::SetTheySanctionedUsTurn, &CvDiplomacyAI::GetTheySanctionedUsTurn, /*50*/ GD_INT_GET(SANCTIONED_US_TURNS_UNTIL_FORGIVEN), HasEverSanctionedUs(ePlayer) ? GetMeanness() : GetWorkAgainstWillingness()); // Un-sanctioned us? checkAndUpdateNonStackingOpinionModifier(this, ePlayer, iTurn, &CvDiplomacyAI::HasTriedToUnsanctionUs, &CvDiplomacyAI::SetTheyUnsanctionedUsTurn, &CvDiplomacyAI::GetTheyUnsanctionedUsTurn, /*50*/ GD_INT_GET(UNSANCTIONED_US_TURNS_UNTIL_FORGOTTEN), HasEverUnsanctionedUs(ePlayer) ? GetLoyalty() : GetWorkWithWillingness()); } } ////////////////////////////////////// // BASE OPINION SCORE ////////////////////////////////////// int CvDiplomacyAI::GetBaseOpinionScore(PlayerTypes ePlayer) { return GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getHumanOpinionChange() : GC.getGame().getHandicapInfo().getAIOpinionChange(); } ////////////////////////////////////// // DISPUTE MODIFIERS ////////////////////////////////////// int CvDiplomacyAI::GetLandDisputeLevelScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; int iEra = (int)GetPlayer()->GetCurrentEra(); if (iEra <= 0) iEra = 1; switch (GetLandDisputeLevel(ePlayer)) { case DISPUTE_LEVEL_FIERCE: iOpinionWeight += /*40*/ GD_INT_GET(OPINION_WEIGHT_LAND_FIERCE); break; case DISPUTE_LEVEL_STRONG: iOpinionWeight += /*25*/ GD_INT_GET(OPINION_WEIGHT_LAND_STRONG); break; case DISPUTE_LEVEL_WEAK: iOpinionWeight += /*15*/ GD_INT_GET(OPINION_WEIGHT_LAND_WEAK); break; case DISPUTE_LEVEL_NONE: iOpinionWeight += /*-10*/ GD_INT_GET(OPINION_WEIGHT_LAND_NONE); break; } // AI will care more about land if they're a warmonger. if (iOpinionWeight > 0) { if (IsConqueror()) { iOpinionWeight += /*10*/ GD_INT_GET(OPINION_WEIGHT_LAND_WARMONGER); } } else if (iOpinionWeight < 0) { if (IsConqueror()) { iOpinionWeight += (/*-5*/ GD_INT_GET(OPINION_WEIGHT_LAND_NONE_WARMONGER) - GetNeediness()) * iEra / 2; } } // Scale based on flavors and difficulty level if (iOpinionWeight != 0) { iOpinionWeight *= GetBoldness(); iOpinionWeight *= GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getLandDisputePercent() : GC.getGame().getHandicapInfo().getLandDisputePercent(); if (iOpinionWeight > 0) { iOpinionWeight /= 750; } else { iOpinionWeight /= 500; } } return iOpinionWeight; } int CvDiplomacyAI::GetWonderDisputeLevelScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; int iEra = (int)GetPlayer()->GetCurrentEra(); if (iEra <= 0) iEra = 1; switch (GetWonderDisputeLevel(ePlayer)) { case DISPUTE_LEVEL_FIERCE: iOpinionWeight += /*50*/ GD_INT_GET(OPINION_WEIGHT_WONDER_FIERCE); break; case DISPUTE_LEVEL_STRONG: iOpinionWeight += /*35*/ GD_INT_GET(OPINION_WEIGHT_WONDER_STRONG); break; case DISPUTE_LEVEL_WEAK: iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_WONDER_WEAK); break; case DISPUTE_LEVEL_NONE: iOpinionWeight += /*0*/ GD_INT_GET(OPINION_WEIGHT_WONDER_NONE); break; } if (IsCultural()) { if (iOpinionWeight > 0) { iOpinionWeight += /*10*/ GD_INT_GET(OPINION_WEIGHT_WONDER_CULTURAL); } else if (!IsWonderSpammer(ePlayer)) { iOpinionWeight += (/*-5*/ GD_INT_GET(OPINION_WEIGHT_WONDER_NONE_CULTURAL) - GetNeediness()) * iEra / 2; } } // Scale based on flavors and difficulty level if (iOpinionWeight != 0) { iOpinionWeight *= GetWonderCompetitiveness(); iOpinionWeight *= GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getWonderDisputePercent() : GC.getGame().getHandicapInfo().getWonderDisputePercent(); if (iOpinionWeight > 0) { iOpinionWeight /= 750; } else { iOpinionWeight /= 500; } } return iOpinionWeight; } int CvDiplomacyAI::GetMinorCivDisputeLevelScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; int iEra = (int)GetPlayer()->GetCurrentEra(); if (iEra <= 0) iEra = 1; if (GetPlayer()->HasMetValidMinorCiv()) { switch (GetMinorCivDisputeLevel(ePlayer)) { case DISPUTE_LEVEL_FIERCE: iOpinionWeight += /*30*/ GD_INT_GET(OPINION_WEIGHT_MINOR_CIV_FIERCE); break; case DISPUTE_LEVEL_STRONG: iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_MINOR_CIV_STRONG); break; case DISPUTE_LEVEL_WEAK: iOpinionWeight += /*10*/ GD_INT_GET(OPINION_WEIGHT_MINOR_CIV_WEAK); break; case DISPUTE_LEVEL_NONE: iOpinionWeight += /*0*/ GD_INT_GET(OPINION_WEIGHT_MINOR_CIV_NONE); break; } if (IsDiplomat()) { if (iOpinionWeight > 0) { iOpinionWeight += /*10*/ GD_INT_GET(OPINION_WEIGHT_MINOR_CIV_DIPLOMAT); } else if (!IsMinorCivTroublemaker(ePlayer, true)) { iOpinionWeight += (/*-5*/ GD_INT_GET(OPINION_WEIGHT_MINOR_CIV_NONE_DIPLOMAT) - GetNeediness()) * iEra / 2; } } // Scale based on flavors and difficulty level if (iOpinionWeight != 0) { iOpinionWeight *= GetMinorCivCompetitiveness(); iOpinionWeight *= GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getMinorCivDisputePercent() : GC.getGame().getHandicapInfo().getMinorCivDisputePercent(); if (iOpinionWeight > 0) { iOpinionWeight /= 750; } else { iOpinionWeight /= 500; } } } return iOpinionWeight; } int CvDiplomacyAI::GetTechBlockLevelScore(PlayerTypes ePlayer) { if (!IsScientist()) return 0; int iOpinionWeight = 0; int iEra = (int)GetPlayer()->GetCurrentEra(); if (iEra < 0) iEra = 0; int iTheirEra = (int)GET_PLAYER(ePlayer).GetCurrentEra(); if (iTheirEra < 0) iTheirEra = 0; int iBlockEra = max(0, max(iEra, iTheirEra) - 1); switch (GetTechBlockLevel(ePlayer)) { case BLOCK_LEVEL_FIERCE: iOpinionWeight += /*30*/ GD_INT_GET(OPINION_WEIGHT_TECH_FIERCE); break; case BLOCK_LEVEL_STRONG: iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_TECH_STRONG); break; case BLOCK_LEVEL_WEAK: iOpinionWeight += /*10*/ GD_INT_GET(OPINION_WEIGHT_TECH_WEAK); break; case BLOCK_LEVEL_NONE: iOpinionWeight += (/*-5*/ GD_INT_GET(OPINION_WEIGHT_TECH_NONE) - GetNeediness()) * max(iEra, 1) / 2; break; } // Scale based on flavors and difficulty level if (iOpinionWeight != 0) { iOpinionWeight *= GetDiploBalance(); iOpinionWeight *= GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getTechBlockPercent() : GC.getGame().getHandicapInfo().getTechBlockPercent(); if (iOpinionWeight > 0) { iOpinionWeight /= max(500, 1000 - (iBlockEra * 125)); } else { iOpinionWeight /= 500; } } return iOpinionWeight; } int CvDiplomacyAI::GetPolicyBlockLevelScore(PlayerTypes ePlayer) { if (!IsCultural()) return 0; int iOpinionWeight = 0; int iEra = (int)GetPlayer()->GetCurrentEra(); if (iEra < 0) iEra = 0; int iTheirEra = (int)GET_PLAYER(ePlayer).GetCurrentEra(); if (iTheirEra < 0) iTheirEra = 0; int iBlockEra = max(0, max(iEra, iTheirEra) - 1); switch (GetPolicyBlockLevel(ePlayer)) { case BLOCK_LEVEL_FIERCE: iOpinionWeight += /*30*/ GD_INT_GET(OPINION_WEIGHT_POLICY_FIERCE); break; case BLOCK_LEVEL_STRONG: iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_POLICY_STRONG); break; case BLOCK_LEVEL_WEAK: iOpinionWeight += /*10*/ GD_INT_GET(OPINION_WEIGHT_POLICY_WEAK); break; case BLOCK_LEVEL_NONE: iOpinionWeight += (/*-5*/ GD_INT_GET(OPINION_WEIGHT_POLICY_NONE) - GetNeediness()) * max(iEra, 1) / 2; break; } // Scale based on flavors and difficulty level if (iOpinionWeight != 0) { iOpinionWeight *= GetDiploBalance(); iOpinionWeight *= GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getPolicyBlockPercent() : GC.getGame().getHandicapInfo().getPolicyBlockPercent(); if (iOpinionWeight > 0) { iOpinionWeight /= max(500, 1000 - (iBlockEra * 125)); } else { iOpinionWeight /= 500; } } return iOpinionWeight; } int CvDiplomacyAI::GetVictoryDisputeLevelScore(PlayerTypes ePlayer) const { if (!IsCompetingForVictory()) return 0; // Don't stack! if ((int)GetVictoryBlockLevel(ePlayer) > (int)GetVictoryDisputeLevel(ePlayer)) return 0; int iOpinionWeight = 0; switch (GetVictoryDisputeLevel(ePlayer)) { case DISPUTE_LEVEL_FIERCE: iOpinionWeight = /*40*/ GD_INT_GET(OPINION_WEIGHT_VICTORY_FIERCE); break; case DISPUTE_LEVEL_STRONG: iOpinionWeight = /*30*/ GD_INT_GET(OPINION_WEIGHT_VICTORY_STRONG); break; case DISPUTE_LEVEL_WEAK: iOpinionWeight = /*20*/ GD_INT_GET(OPINION_WEIGHT_VICTORY_WEAK); break; case DISPUTE_LEVEL_NONE: iOpinionWeight = IsMajorCompetitor(ePlayer) ? 0 : /*0*/ GD_INT_GET(OPINION_WEIGHT_VICTORY_NONE); break; } if (iOpinionWeight > 0) { iOpinionWeight += GET_PLAYER(ePlayer).GetCurrentEra() * /*4*/ GD_INT_GET(OPINION_WEIGHT_VICTORY_PER_ERA); iOpinionWeight *= GetVictoryCompetitiveness(); iOpinionWeight *= GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getVictoryDisputePercent() : GC.getGame().getHandicapInfo().getVictoryDisputePercent(); iOpinionWeight /= 500; } else if (!IsMajorCompetitor(ePlayer)) { iOpinionWeight += GET_PLAYER(ePlayer).GetCurrentEra() * /*0*/ GD_INT_GET(OPINION_WEIGHT_VICTORY_NONE_PER_ERA); iOpinionWeight *= GetVictoryCompetitiveness(); iOpinionWeight *= GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getVictoryDisputePercent() : GC.getGame().getHandicapInfo().getVictoryDisputePercent(); iOpinionWeight /= 500; } return iOpinionWeight; } int CvDiplomacyAI::GetVictoryBlockLevelScore(PlayerTypes ePlayer) const { if (!IsCompetingForVictory()) return 0; // Don't stack! if ((int)GetVictoryDisputeLevel(ePlayer) >= (int)GetVictoryBlockLevel(ePlayer)) return 0; int iOpinionWeight = 0; switch (GetVictoryBlockLevel(ePlayer)) { case BLOCK_LEVEL_FIERCE: iOpinionWeight = /*40*/ GD_INT_GET(OPINION_WEIGHT_VICTORY_BLOCK_FIERCE); break; case BLOCK_LEVEL_STRONG: iOpinionWeight = /*30*/ GD_INT_GET(OPINION_WEIGHT_VICTORY_BLOCK_STRONG); break; case BLOCK_LEVEL_WEAK: iOpinionWeight = /*20*/ GD_INT_GET(OPINION_WEIGHT_VICTORY_BLOCK_WEAK); break; case BLOCK_LEVEL_NONE: UNREACHABLE(); } if (iOpinionWeight > 0) { iOpinionWeight += GET_PLAYER(ePlayer).GetCurrentEra() * /*4*/ GD_INT_GET(OPINION_WEIGHT_VICTORY_BLOCK_PER_ERA); iOpinionWeight *= GetVictoryCompetitiveness(); iOpinionWeight *= GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getVictoryBlockPercent() : GC.getGame().getHandicapInfo().getVictoryBlockPercent(); iOpinionWeight /= 500; } return iOpinionWeight; } int CvDiplomacyAI::GetRecklessExpanderScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsRecklessExpander(ePlayer)) { iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_RECKLESS_EXPANDER); int iMedianCities = GC.getGame().CalculateMedianNumCities(); int iMedianPlots = GC.getGame().CalculateMedianNumPlots(); int iNumCities = GET_PLAYER(ePlayer).getNumCities(); int iNumPlots = GET_PLAYER(ePlayer).getTotalLand(); int iCivCityDifference = iNumCities - GetPlayer()->getNumCities(); int iCivPlotDifference = iNumPlots - GetPlayer()->getTotalLand(); int iMedianCityDifference = (((iNumCities*100) - (iMedianCities * /*200*/ GD_INT_GET(RECKLESS_EXPANDER_CITIES_THRESHOLD))) / 100); int iMedianPlotDifference = (((iNumPlots*100) - (iMedianPlots * /*250*/ GD_INT_GET(RECKLESS_EXPANDER_LAND_THRESHOLD))) / 100); // For scaling, go with whichever value is smaller int iCityWeight = 0; int iCityDifference = min(iCivCityDifference, iMedianCityDifference); if (iCityDifference > 1) { iCityWeight += ((iCityDifference-1) * /*10*/ GD_INT_GET(OPINION_WEIGHT_RECKLESS_EXPANDER_PER_CITY)); } // Since land can be a factor, also factor in tile count (scale by whichever value is smaller) int iPlotWeight = 0; int iPlotDifference = min(iCivPlotDifference, iMedianPlotDifference); if (iPlotDifference > 1) { iPlotWeight += ((iPlotDifference-1) * /*1*/ GD_INT_GET(OPINION_WEIGHT_RECKLESS_EXPANDER_PER_TILE)); } // Apply the highest of the two scaling weights to Opinion iOpinionWeight += max(iCityWeight, iPlotWeight); if (IsConqueror() || IsSecondaryConqueror() || GetPlayer()->GetPlayerTraits()->IsWarmonger()) { iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_RECKLESS_EXPANDER_STRATEGIC_MOD); } if (iOpinionWeight > 0) { iOpinionWeight *= GetBoldness(); iOpinionWeight *= GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getLandDisputePercent() : GC.getGame().getHandicapInfo().getLandDisputePercent(); iOpinionWeight /= 500; } } return iOpinionWeight; } int CvDiplomacyAI::GetWonderSpammerScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsWonderSpammer(ePlayer)) { iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_WONDER_SPAMMER); int iCivDifference = GET_PLAYER(ePlayer).GetWondersConstructed() - GetPlayer()->GetWondersConstructed(); int iMedianDifference = GET_PLAYER(ePlayer).GetWondersConstructed() - GC.getGame().CalculateMedianNumWondersConstructed(); // For scaling, go with whichever value is smaller int iWonderDifference = min(iCivDifference, iMedianDifference); int iDifficultyMod = GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getWonderBlockMod() : GC.getGame().getHandicapInfo().getWonderBlockMod(); if (iWonderDifference > /*4*/ (GD_INT_GET(WONDER_SPAMMER_THRESHOLD) + 1 - iDifficultyMod)) { iOpinionWeight += ((iWonderDifference - (GD_INT_GET(WONDER_SPAMMER_THRESHOLD) + 1 - iDifficultyMod)) * /*5*/ GD_INT_GET(OPINION_WEIGHT_WONDER_SPAMMER_PER_WONDER)); if (iOpinionWeight > /*60*/ GD_INT_GET(OPINION_WEIGHT_WONDER_SPAMMER_CAP)) { iOpinionWeight = GD_INT_GET(OPINION_WEIGHT_WONDER_SPAMMER_CAP); } } if (IsConqueror() || IsSecondaryConqueror() || GetPlayer()->GetPlayerTraits()->IsWarmonger()) { iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_WONDER_SPAMMER_STRATEGIC_MOD); } if (iOpinionWeight > 0) { iOpinionWeight *= GetWonderCompetitiveness(); iOpinionWeight *= GET_PLAYER(ePlayer).isHuman(ISHUMAN_HANDICAP) ? GET_PLAYER(ePlayer).getHandicapInfo().getWonderBlockPercent() : GC.getGame().getHandicapInfo().getWonderBlockPercent(); iOpinionWeight /= 750; } } return iOpinionWeight; } ////////////////////////////////////// // WAR STUFF ////////////////////////////////////// int CvDiplomacyAI::GetMilitaryAggressivePostureScore(PlayerTypes ePlayer) { // War, vassal, or Peace Treaty? Ignore this. if (IsAtWar(ePlayer) || IsVassal(ePlayer) || GET_TEAM(GetTeam()).isForcePeace(GET_PLAYER(ePlayer).getTeam())) return 0; int iOpinionWeight = 0; switch (GetMilitaryAggressivePosture(ePlayer)) { case AGGRESSIVE_POSTURE_NONE: break; case AGGRESSIVE_POSTURE_LOW: iOpinionWeight = /*10*/ GD_INT_GET(OPINION_WEIGHT_MILITARY_AGGRESSIVE_POSTURE_LOW); break; case AGGRESSIVE_POSTURE_MEDIUM: iOpinionWeight = /*20*/ GD_INT_GET(OPINION_WEIGHT_MILITARY_AGGRESSIVE_POSTURE_MEDIUM); break; case AGGRESSIVE_POSTURE_HIGH: iOpinionWeight = /*40*/ GD_INT_GET(OPINION_WEIGHT_MILITARY_AGGRESSIVE_POSTURE_HIGH); break; case AGGRESSIVE_POSTURE_INCREDIBLE: iOpinionWeight = /*60*/ GD_INT_GET(OPINION_WEIGHT_MILITARY_AGGRESSIVE_POSTURE_INCREDIBLE); break; } return iOpinionWeight; } int CvDiplomacyAI::GetWarmongerThreatScore(PlayerTypes ePlayer) { if (IsAlwaysAtWar(ePlayer)) return 0; return GetOtherPlayerWarmongerScore(ePlayer); } int CvDiplomacyAI::GetTradeRoutesPlunderedScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; int iRoutesPlundered = GetNumTradeRoutesPlundered(ePlayer); if (iRoutesPlundered > 0) { iOpinionWeight = (iRoutesPlundered * /*5*/ GD_INT_GET(OPINION_WEIGHT_PLUNDERED_TRADE_ROUTE)); int iDuration = AdjustModifierDuration(/*15*/ GD_INT_GET(PLUNDERED_TRADE_ROUTE_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true); return AdjustTimedModifier(iOpinionWeight, iDuration, GetPlunderedTradeRouteTurn(ePlayer), TIMED_MODIFIER_STACKED, iRoutesPlundered); } return iOpinionWeight; } int CvDiplomacyAI::GetCivilianKillerScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; if (GetCivilianKillerValue(ePlayer) > 0) { int iCurrentValuePercent = (GetCivilianKillerValue(ePlayer) * 100) / max(GetMaxCivilianKillerValue(), 1); if (iCurrentValuePercent > 0) { iOpinionWeight = /*50*/ GD_INT_GET(OPINION_WEIGHT_CIVILIAN_KILLER_MAX) * iCurrentValuePercent / 100; } } return iOpinionWeight; } int CvDiplomacyAI::GetCivilianKillerGlobalScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (GetCivOpinion(ePlayer) <= CIV_OPINION_NEUTRAL && !IsDoFAccepted(ePlayer) && !IsHasDefensivePact(ePlayer)) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer, true) && eLoopPlayer != GetID() && GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetCivilianKillerScore(ePlayer) >= /*25*/ GD_INT_GET(OPINION_WEIGHT_CIVILIAN_KILLER_WORLD_THRESHOLD)) { CivApproachTypes eApproach = GetCivApproach(eLoopPlayer); bool bDontCare = (WasEverBackstabbedBy(eLoopPlayer) || IsAtWar(eLoopPlayer) || IsDenouncedPlayer(eLoopPlayer) || IsDenouncedByPlayer(eLoopPlayer) || IsUntrustworthy(eLoopPlayer) || eApproach == CIV_APPROACH_AFRAID || eApproach <= CIV_APPROACH_DECEPTIVE || GetCivOpinion(eLoopPlayer) <= CIV_OPINION_ENEMY); if (IsBackstabber() && !IsFriendOrAlly(eLoopPlayer)) bDontCare = true; if (!bDontCare) { iOpinionWeight = /*20*/ GD_INT_GET(OPINION_WEIGHT_CIVILIAN_KILLER_WORLD); break; } } } } return iOpinionWeight; } int CvDiplomacyAI::GetNukedByScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; if (IsNukedBy(ePlayer)) { iOpinionWeight = /*100*/ GD_INT_GET(OPINION_WEIGHT_NUKED_MAX); } return iOpinionWeight; } int CvDiplomacyAI::GetHolyCityCapturedByScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsHolyCityCapturedBy(ePlayer)) { iOpinionWeight = /*80*/ GD_INT_GET(OPINION_WEIGHT_CAPTURED_HOLY_CITY); // Halve the weight if they willingly returned our Holy City. if (IsPlayerReturnedHolyCity(ePlayer, true)) { iOpinionWeight *= 100; iOpinionWeight /= max(100, /*200*/ GD_INT_GET(OPINION_WEIGHT_CAPTURED_KEY_CITY_RETURNED_DIVISOR)); } // If we're a well-treated capitulated vassal, halve the weight. if (IsVassal(ePlayer) && !IsVoluntaryVassalage(ePlayer) && GetVassalTreatmentLevel(ePlayer) <= VASSAL_TREATMENT_DISAGREE) { iOpinionWeight *= 100; iOpinionWeight /= max(100, /*200*/ GD_INT_GET(OPINION_WEIGHT_CAPTURED_KEY_CITY_CAPITULATION_DIVISOR)); } } return iOpinionWeight; } int CvDiplomacyAI::GetCapitalCapturedByScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsCapitalCapturedBy(ePlayer)) { iOpinionWeight = /*160*/ GD_INT_GET(OPINION_WEIGHT_CAPTURED_CAPITAL); // Halve the weight if they willingly returned our capital. if (IsPlayerReturnedCapital(ePlayer, true)) { iOpinionWeight *= 100; iOpinionWeight /= max(100, /*200*/ GD_INT_GET(OPINION_WEIGHT_CAPTURED_KEY_CITY_RETURNED_DIVISOR)); } // If we're a well-treated capitulated vassal, halve the weight. if (IsVassal(ePlayer) && !IsVoluntaryVassalage(ePlayer) && GetVassalTreatmentLevel(ePlayer) <= VASSAL_TREATMENT_DISAGREE) { iOpinionWeight *= 100; iOpinionWeight /= max(100, /*200*/ GD_INT_GET(OPINION_WEIGHT_CAPTURED_KEY_CITY_CAPITULATION_DIVISOR)); } } return iOpinionWeight; } ////////////////////////////////////// // Player has done nice stuff ////////////////////////////////////// int CvDiplomacyAI::GetRecentTradeScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; if (GetRecentTradeValue(ePlayer) > 0) { int iCurrentValuePercent = (GetRecentTradeValue(ePlayer) * 100) / max(GetMaxRecentTradeValue(), 1); if (iCurrentValuePercent > 0) { iOpinionWeight = /*-40*/ GD_INT_GET(OPINION_WEIGHT_TRADE_MAX) * iCurrentValuePercent / 100; } if (IsStrategicTradePartner(ePlayer)) { iOpinionWeight *= /*150*/ GD_INT_GET(OPINION_WEIGHT_STRATEGIC_TRADE_PARTNER_MULTIPLIER); iOpinionWeight /= 100; } } return iOpinionWeight; } int CvDiplomacyAI::GetRecentAssistScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; int iRecentAssistValue = GetRecentAssistValue(ePlayer) * 100; if (iRecentAssistValue != 0) { int iCurrentValuePercent = iRecentAssistValue > 0 ? iRecentAssistValue / max(GetMaxRecentAssistValue(), 1) : iRecentAssistValue / min(GetMaxRecentFailedAssistValue(), -1); if (iCurrentValuePercent > 0) { if (iRecentAssistValue > 0) { iOpinionWeight = /*-30*/ GD_INT_GET(OPINION_WEIGHT_ASSIST_MAX) * iCurrentValuePercent / 100; } else { iOpinionWeight = /*30*/ GD_INT_GET(OPINION_WEIGHT_FAILED_ASSIST_MAX) * iCurrentValuePercent / 100; } } } return iOpinionWeight; } int CvDiplomacyAI::GetCommonFoeScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; if (GetCommonFoeValue(ePlayer) > 0) { int iCurrentValuePercent = (GetCommonFoeValue(ePlayer) * 100) / max(GetMaxCommonFoeValue(), 1); if (iCurrentValuePercent > 0) { iOpinionWeight = /*-100*/ GD_INT_GET(OPINION_WEIGHT_COMMON_FOE_MAX) * iCurrentValuePercent / 100; } } return iOpinionWeight; } int CvDiplomacyAI::GetCiviliansReturnedToMeScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; int iNumCivs = GetNumCiviliansReturnedToMe(ePlayer); if (iNumCivs > 0) { // Full credit for first one iOpinionWeight = /*-10*/ GD_INT_GET(OPINION_WEIGHT_RETURNED_CIVILIAN); // Partial credit for any after first if (iNumCivs > 1) { iOpinionWeight += /*-5*/ GD_INT_GET(OPINION_WEIGHT_RETURNED_CIVILIAN_SUBSEQUENT) * (iNumCivs - 1); } int iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(RETURNED_CIVILIAN_TURNS_UNTIL_FORGOTTEN), GetLoyalty()); return AdjustTimedModifier(iOpinionWeight, iDuration, GetCiviliansReturnedToMeTurn(ePlayer), TIMED_MODIFIER_DIMINISHING, iNumCivs, GD_INT_GET(OPINION_WEIGHT_RETURNED_CIVILIAN)); } return iOpinionWeight; } int CvDiplomacyAI::GetLandmarksBuiltForMeScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; int iNumLandmarks = GetNumLandmarksBuiltForMe(ePlayer); if (iNumLandmarks > 0) { // Full credit for first one iOpinionWeight = /*-20*/ GD_INT_GET(OPINION_WEIGHT_BUILT_LANDMARK); // Partial credit for any after first if (iNumLandmarks > 1) { iOpinionWeight += /*-10*/ GD_INT_GET(OPINION_WEIGHT_BUILT_LANDMARK_SUBSEQUENT) * (iNumLandmarks - 1); } int iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(BUILT_LANDMARK_TURNS_UNTIL_FORGOTTEN), GetLoyalty()); return AdjustTimedModifier(iOpinionWeight, iDuration, GetLandmarksBuiltForMeTurn(ePlayer), TIMED_MODIFIER_DIMINISHING, iNumLandmarks, GD_INT_GET(OPINION_WEIGHT_BUILT_LANDMARK)); } return iOpinionWeight; } int CvDiplomacyAI::GetResurrectedScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; if (WasResurrectedBy(ePlayer)) { iOpinionWeight = /*-200*/ GD_INT_GET(OPINION_WEIGHT_RESURRECTED); // Halve the weight if they captured our capital if (IsCapitalCapturedBy(ePlayer, false, true, true)) { iOpinionWeight *= 100; iOpinionWeight /= max(100, /*200*/ GD_INT_GET(OPINION_WEIGHT_LIBERATOR_CAPTURED_CAPITAL_DIVISOR)); } // Halve the weight if they captured our Holy City. if (IsHolyCityCapturedBy(ePlayer, false, true, true)) { iOpinionWeight *= 100; iOpinionWeight /= max(100, /*200*/ GD_INT_GET(OPINION_WEIGHT_LIBERATOR_CAPTURED_HOLY_CITY_DIVISOR)); } } return iOpinionWeight; } int CvDiplomacyAI::GetLiberatedCapitalScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsPlayerLiberatedCapital(ePlayer)) { iOpinionWeight = /*-120*/ GD_INT_GET(OPINION_WEIGHT_LIBERATED_CAPITAL); // Increase the weight if we're someone's vassal if (GetPlayer()->IsVassalOfSomeone()) { iOpinionWeight *= /*150*/ GD_INT_GET(OPINION_WEIGHT_LIBERATED_CAPITAL_VASSAL_MULTIPLIER); iOpinionWeight /= 100; } // Halve the weight if they captured our Holy City. if (IsHolyCityCapturedBy(ePlayer, false, true, true)) { iOpinionWeight *= 100; iOpinionWeight /= max(100, /*200*/ GD_INT_GET(OPINION_WEIGHT_LIBERATOR_CAPTURED_HOLY_CITY_DIVISOR)); } } return iOpinionWeight; } int CvDiplomacyAI::GetLiberatedHolyCityScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsPlayerLiberatedHolyCity(ePlayer)) { iOpinionWeight = /*-80*/ GD_INT_GET(OPINION_WEIGHT_LIBERATED_HOLY_CITY); // Increase the weight if we're someone's vassal if (GetPlayer()->IsVassalOfSomeone()) { iOpinionWeight *= /*150*/ GD_INT_GET(OPINION_WEIGHT_LIBERATED_HOLY_CITY_VASSAL_MULTIPLIER); iOpinionWeight /= 100; } } return iOpinionWeight; } int CvDiplomacyAI::GetLiberatedCitiesScore(PlayerTypes ePlayer) { // No bonus if they captured our capital if (IsCapitalCapturedBy(ePlayer, false, true, true)) return 0; int iOpinionWeight = 0; int iNumCitiesLiberated = GetNumCitiesLiberatedBy(ePlayer); if (iNumCitiesLiberated > 0) { int iLiberationValue = /*-30*/ GD_INT_GET(OPINION_WEIGHT_LIBERATED_CITY) * iNumCitiesLiberated; int iDuration = AdjustModifierDuration(/*75*/ GD_INT_GET(LIBERATED_CITY_TURNS_UNTIL_FORGOTTEN), GetLoyalty()); iOpinionWeight = AdjustTimedModifier(iLiberationValue, iDuration, GetLiberatedCitiesTurn(ePlayer), TIMED_MODIFIER_STACKED, iNumCitiesLiberated); // Double the weight if we're someone's vassal if (GetPlayer()->IsVassalOfSomeone()) { iOpinionWeight *= /*200*/ GD_INT_GET(OPINION_WEIGHT_LIBERATED_CITY_VASSAL_MULTIPLIER); iOpinionWeight /= 100; } // Halve the weight if they captured our Holy City. if (IsHolyCityCapturedBy(ePlayer, false, true, true)) { iOpinionWeight *= 100; iOpinionWeight /= max(100, /*200*/ GD_INT_GET(OPINION_WEIGHT_LIBERATOR_CAPTURED_HOLY_CITY_DIVISOR)); } // Reduce the weight if they've been capturing more of our cities than they've been liberating int iDivisor = max(1, (GetNumCitiesCapturedBy(ePlayer) - GetNumCitiesEverLiberatedBy(ePlayer) + 1)); iOpinionWeight /= iDivisor; } return iOpinionWeight; } int CvDiplomacyAI::GetReturnedCapitalScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsPlayerReturnedCapital(ePlayer)) { iOpinionWeight = /*-60*/ GD_INT_GET(OPINION_WEIGHT_RETURNED_CAPITAL); } // Increase the weight if we're someone's vassal if (GetPlayer()->IsVassalOfSomeone()) { iOpinionWeight *= /*150*/ GD_INT_GET(OPINION_WEIGHT_RETURNED_CAPITAL_VASSAL_MULTIPLIER); iOpinionWeight /= 100; } // Halve the weight if they captured our Holy City. if (IsHolyCityCapturedBy(ePlayer, false, true, true)) { iOpinionWeight *= 100; iOpinionWeight /= max(100, /*200*/ GD_INT_GET(OPINION_WEIGHT_LIBERATOR_CAPTURED_HOLY_CITY_DIVISOR)); } return iOpinionWeight; } int CvDiplomacyAI::GetReturnedHolyCityScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsPlayerLiberatedHolyCity(ePlayer)) { iOpinionWeight = /*-40*/ GD_INT_GET(OPINION_WEIGHT_RETURNED_HOLY_CITY); } // Increase the weight if we're someone's vassal if (GetPlayer()->IsVassalOfSomeone()) { iOpinionWeight *= /*150*/ GD_INT_GET(OPINION_WEIGHT_RETURNED_HOLY_CITY_VASSAL_MULTIPLIER); iOpinionWeight /= 100; } return iOpinionWeight; } int CvDiplomacyAI::GetEmbassyScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsHasEmbassy(ePlayer) && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasEmbassy(GetID())) { iOpinionWeight = /*-3*/ GD_INT_GET(OPINION_WEIGHT_EMBASSY_MUTUAL); } else if (IsHasEmbassy(ePlayer)) { iOpinionWeight = /*-2*/ GD_INT_GET(OPINION_WEIGHT_EMBASSY); } else if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasEmbassy(GetID())) { iOpinionWeight = /*-1*/ GD_INT_GET(OPINION_WEIGHT_EMBASSY_THEM); } return iOpinionWeight; } int CvDiplomacyAI::GetDiplomatScore(PlayerTypes ePlayer) { // Masters gets a free Diplomat in their vassals' capitals // Therefore this bonus doesn't apply for a vassal unless the vassal is being treated kindly if (IsVassal(ePlayer) && !WasResurrectedBy(ePlayer)) { if (IsVoluntaryVassalage(ePlayer)) { if (GetVassalTreatmentLevel(ePlayer) > VASSAL_TREATMENT_DISAGREE) return 0; } else { if (GetVassalTreatmentLevel(ePlayer) != VASSAL_TREATMENT_CONTENT) return 0; } } // No bonus if the player has recent spying penalties if (GetNumTimesRobbedBy(ePlayer) > 0 || BrokeSpyPromise(ePlayer) || IgnoredSpyPromise(ePlayer)) return 0; int iOpinionWeight = 0; // They have a Spy as a Diplomat in our Capital if (GetPlayer()->GetEspionage()->IsOtherDiplomatVisitingMe(ePlayer)) { iOpinionWeight = /*-20*/ GD_INT_GET(OPINION_WEIGHT_DIPLOMAT); if (IsScientist() || GetPlayer()->GetPlayerTraits()->IsNerd()) { iOpinionWeight += /*-10*/ GD_INT_GET(OPINION_WEIGHT_DIPLOMAT_MOD); } } return iOpinionWeight; } int CvDiplomacyAI::GetForgaveForSpyingScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsPlayerForgaveForSpying(ePlayer)) { iOpinionWeight = /*-10*/ GD_INT_GET(OPINION_WEIGHT_FORGAVE_FOR_SPYING); int iDuration = AdjustModifierDuration(/*30*/ GD_INT_GET(FORGAVE_FOR_SPYING_TURNS_UNTIL_FORGOTTEN), GetLoyalty()); return AdjustTimedModifier(iOpinionWeight, iDuration, GetForgaveForSpyingTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetTimesIntrigueSharedScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; int iNumIntrigue = GetNumTimesIntrigueSharedBy(ePlayer); if (iNumIntrigue > 0) { // Full credit for first one iOpinionWeight = /*-10*/ GD_INT_GET(OPINION_WEIGHT_INTRIGUE_SHARED_BY); // Partial credit for any after first if (iNumIntrigue > 1) { iOpinionWeight += /*-5*/ GD_INT_GET(OPINION_WEIGHT_INTRIGUE_SHARED_BY_SUBSEQUENT) * (iNumIntrigue - 1); } int iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(SHARED_INTRIGUE_TURNS_UNTIL_FORGOTTEN), GetLoyalty()); return AdjustTimedModifier(iOpinionWeight, iDuration, GetIntrigueSharedTurn(ePlayer), TIMED_MODIFIER_DIMINISHING, iNumIntrigue, GD_INT_GET(OPINION_WEIGHT_INTRIGUE_SHARED_BY)); } return iOpinionWeight; } ////////////////////////////////////// // Player has done mean stuff ////////////////////////////////////// int CvDiplomacyAI::GetTimesCultureBombedScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; if (GetNumTimesCultureBombed(ePlayer) > 0) iOpinionWeight += GetNumTimesCultureBombed(ePlayer) * /*10*/ GD_INT_GET(OPINION_WEIGHT_CULTURE_BOMBED); return iOpinionWeight; } int CvDiplomacyAI::GetTimesRobbedScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; int iTimesRobbed = GetNumTimesRobbedBy(ePlayer); if (iTimesRobbed > 0) { iOpinionWeight = iTimesRobbed * /*20*/ GD_INT_GET(OPINION_WEIGHT_ROBBED_BY); int iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(ROBBED_US_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true); return AdjustTimedModifier(iOpinionWeight, iDuration, GetRobbedTurn(ePlayer), TIMED_MODIFIER_STACKED, iTimesRobbed); } return iOpinionWeight; } int CvDiplomacyAI::GetTimesPlottedAgainstUsScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; int iNumPlots = GetNumTimesTheyPlottedAgainstUs(ePlayer); if (iNumPlots > 0) { // Full penalty for first time iOpinionWeight = /*20*/ GD_INT_GET(OPINION_WEIGHT_PLOTTED_AGAINST_US); // Half penalty for subsequent times if (iNumPlots > 1) { iOpinionWeight += /*10*/ GD_INT_GET(OPINION_WEIGHT_PLOTTED_AGAINST_US_SUBSEQUENT) * (iNumPlots - 1); } // Scale modifier duration based on military strength int iDurationMod = (int)GetMilitaryStrengthComparedToUs(ePlayer); // between 0 and 7, inclusive iDurationMod += ((int)GET_PLAYER(ePlayer).GetProximityToPlayer(GetID()) * 2); // between 0 and 6, inclusive iDurationMod *= 2; int iDuration = /*14*/ GD_INT_GET(PLOTTED_AGAINST_US_TURNS_UNTIL_FORGIVEN) + iDurationMod; // returns between 14 and 40 turns, inclusive, not scaling with gamespeed return AdjustTimedModifier(iOpinionWeight, iDuration, GetPlottedAgainstUsTurn(ePlayer), TIMED_MODIFIER_DIMINISHING, iNumPlots, GD_INT_GET(OPINION_WEIGHT_PLOTTED_AGAINST_US)); } return iOpinionWeight; } int CvDiplomacyAI::GetTimesPerformedCoupScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; int iNumCoups = GetNumTimesPerformedCoupAgainstUs(ePlayer); if (iNumCoups > 0) { iOpinionWeight = iNumCoups * /*30*/ GD_INT_GET(OPINION_WEIGHT_PERFORMED_COUP); int iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(PERFORMED_COUP_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true); return AdjustTimedModifier(iOpinionWeight, iDuration, GetPerformedCoupTurn(ePlayer), TIMED_MODIFIER_STACKED, iNumCoups); } return iOpinionWeight; } int CvDiplomacyAI::GetStoleArtifactsScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; int iNumArtifacts = GetNegativeArchaeologyPoints(ePlayer); if (iNumArtifacts > 0) { iOpinionWeight = iNumArtifacts * /*30*/ GD_INT_GET(OPINION_WEIGHT_EXCAVATED_ARTIFACT); int iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(EXCAVATED_ARTIFACT_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true); return AdjustTimedModifier(iOpinionWeight, iDuration, GetStoleArtifactTurn(ePlayer), TIMED_MODIFIER_STACKED, iNumArtifacts); } return iOpinionWeight; } ////////////////////////////////////// // Player has asked us to do things we don't like ////////////////////////////////////// int CvDiplomacyAI::GetNoSettleRequestScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetExpansionPromiseState(GetID()) >= PROMISE_STATE_IGNORED) { iOpinionWeight = /*20*/ GD_INT_GET(OPINION_WEIGHT_ASKED_NO_SETTLE); int iDuration = GET_PLAYER(ePlayer).GetDiplomacyAI()->GetExpansionPromiseState(GetID()) == PROMISE_STATE_IGNORED ? /*30*/ GD_INT_GET(EXPANSION_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN) : /*50*/ GD_INT_GET(EXPANSION_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN); iDuration *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDuration /= 100; return AdjustTimedModifier(iOpinionWeight, iDuration, GET_PLAYER(ePlayer).GetDiplomacyAI()->GetExpansionPromiseTurn(GetID()), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetStopSpyingRequestScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetSpyPromiseState(GetID()) >= PROMISE_STATE_IGNORED) { iOpinionWeight = /*10*/ GD_INT_GET(OPINION_WEIGHT_ASKED_STOP_SPYING); int iDuration = GET_PLAYER(ePlayer).GetDiplomacyAI()->GetSpyPromiseState(GetID()) == PROMISE_STATE_IGNORED ? /*30*/ GD_INT_GET(SPY_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN) : /*50*/ GD_INT_GET(SPY_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN); iDuration *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDuration /= 100; return AdjustTimedModifier(iOpinionWeight, iDuration, GET_PLAYER(ePlayer).GetDiplomacyAI()->GetSpyPromiseTurn(GetID()), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetDemandMadeScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; int iNumDemands = GetNumDemandsMade(ePlayer); if (iNumDemands > 0) { // Full penalty for first one iOpinionWeight = /*20*/ GD_INT_GET(OPINION_WEIGHT_MADE_DEMAND_OF_US); // Half penalty for every subsequent demand if (iNumDemands > 1) { iOpinionWeight += /*10*/ GD_INT_GET(OPINION_WEIGHT_MADE_DEMAND_OF_US_SUBSEQUENT) * (iNumDemands - 1); } int iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(MADE_DEMAND_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true); iOpinionWeight = AdjustTimedModifier(iOpinionWeight, iDuration, GetDemandMadeTurn(ePlayer), TIMED_MODIFIER_DIMINISHING, iNumDemands, GD_INT_GET(OPINION_WEIGHT_MADE_DEMAND_OF_US)); // Reduce the penalty if we're not currently giving them anything if (!GET_PLAYER(ePlayer).GetDiplomacyAI()->IsRecentDemandAccepted(GetID())) { iOpinionWeight *= 100; iOpinionWeight /= /*200*/ max(GD_INT_GET(OPINION_WEIGHT_MADE_DEMAND_YOU_NO_TAKE_DIVISOR), 1); } // Increase the penalty if we are giving them something and we're also bankrupt else if (GetPlayer()->GetTreasury()->GetGold() == 0 && GetPlayer()->getAvgGoldRate() <= 0) { iOpinionWeight *= /*300*/ GD_INT_GET(OPINION_WEIGHT_MADE_DEMAND_BANKRUPT_MULTIPLIER); iOpinionWeight /= 100; } // If we're giving them something and going bankrupt soon, increase the penalty gradually else { int iTurnsToBankruptcy = GetPlayer()->getTurnsToBankruptcy(0); if (iTurnsToBankruptcy == 0) { iOpinionWeight *= /*300*/ GD_INT_GET(OPINION_WEIGHT_MADE_DEMAND_BANKRUPT_MULTIPLIER); iOpinionWeight /= 100; } else if (iTurnsToBankruptcy < /*20*/ GD_INT_GET(OPINION_WEIGHT_MADE_DEMAND_BANKRUPT_MULTIPLIER_TURNS)) { int iIncreasedPenalty = iOpinionWeight * GD_INT_GET(OPINION_WEIGHT_MADE_DEMAND_BANKRUPT_MULTIPLIER) / 100; int iExtraPenalty = (iIncreasedPenalty - iOpinionWeight) * (GD_INT_GET(OPINION_WEIGHT_MADE_DEMAND_BANKRUPT_MULTIPLIER_TURNS) - iTurnsToBankruptcy) / GD_INT_GET(OPINION_WEIGHT_MADE_DEMAND_BANKRUPT_MULTIPLIER_TURNS); iOpinionWeight += iExtraPenalty; } } } return iOpinionWeight; } ////////////////////////////////////// // DENOUNCING ////////////////////////////////////// int CvDiplomacyAI::GetDenouncedScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; // We denounced each other! if (IsDenouncedPlayer(ePlayer) && IsDenouncedByPlayer(ePlayer)) { iOpinionWeight = /*50*/ GD_INT_GET(OPINION_WEIGHT_MUTUAL_DENOUNCEMENT); } else if (IsDenouncedByPlayer(ePlayer)) { iOpinionWeight = /*50*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_ME); } else if (IsDenouncedPlayer(ePlayer)) { iOpinionWeight = /*0*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_THEM); } return iOpinionWeight; } int CvDiplomacyAI::GetDenouncedFriendScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; int iNumFriendsDenounced = GetNumFriendsDenounced(ePlayer); // They've denounced someone we have a DoF with if (iNumFriendsDenounced > 0) { iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_FRIEND); if (iNumFriendsDenounced > 1) { iOpinionWeight += /*8*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_FRIEND_SUBSEQUENT) * (iNumFriendsDenounced - 1); } PlayerTypes eBestFriend = GetMostValuableFriend(); PlayerTypes eBestAlly = GetMostValuableAlly(); if (eBestFriend == NO_PLAYER || !IsDoFAccepted(eBestFriend) || !GET_PLAYER(eBestFriend).isAlive() || GET_PLAYER(eBestFriend).getNumCities() <= 0) eBestFriend = NO_PLAYER; if (eBestAlly == NO_PLAYER || !IsDoFAccepted(eBestAlly) || !IsHasDefensivePact(eBestAlly) || !GET_PLAYER(eBestAlly).isAlive() || GET_PLAYER(eBestAlly).getNumCities() <= 0) eBestAlly = NO_PLAYER; if (eBestFriend != NO_PLAYER && eBestAlly != NO_PLAYER) { if (/*20*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_MOST_VALUED_FRIEND) >= /*10*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_MOST_VALUED_ALLY)) { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eBestFriend)) iOpinionWeight += GD_INT_GET(OPINION_WEIGHT_DENOUNCED_MOST_VALUED_FRIEND); else if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eBestAlly)) iOpinionWeight += GD_INT_GET(OPINION_WEIGHT_DENOUNCED_MOST_VALUED_ALLY); } else { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eBestAlly)) iOpinionWeight += GD_INT_GET(OPINION_WEIGHT_DENOUNCED_MOST_VALUED_ALLY); else if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eBestFriend)) iOpinionWeight += GD_INT_GET(OPINION_WEIGHT_DENOUNCED_MOST_VALUED_FRIEND); } } else if (GET_TEAM(GetTeam()).isDefensivePactTradingAllowed() && /*2*/ min(GD_INT_GET(DEFENSIVE_PACT_LIMIT_BASE), GD_INT_GET(AI_DEFENSIVE_PACT_LIMIT_BASE)) > 0) { if ((eBestFriend != NO_PLAYER && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eBestFriend)) || (eBestAlly != NO_PLAYER && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eBestAlly))) { iOpinionWeight += /*20*/ max(GD_INT_GET(OPINION_WEIGHT_DENOUNCED_MOST_VALUED_FRIEND), GD_INT_GET(OPINION_WEIGHT_DENOUNCED_MOST_VALUED_ALLY)); } } else if (eBestFriend != NO_PLAYER && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eBestFriend)) { iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_MOST_VALUED_FRIEND); } } // Scale with Loyalty return AdjustConditionalModifier(iOpinionWeight, GetLoyalty()); } int CvDiplomacyAI::GetDenouncedEnemyScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; int iNumEnemiesDenounced = GetNumEnemiesDenounced(ePlayer); // They've denounced someone we've denounced if (iNumEnemiesDenounced > 0) { iOpinionWeight += /*-20*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_ENEMY); if (iNumEnemiesDenounced > 1) { iOpinionWeight += /*-8*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_ENEMY_SUBSEQUENT) * (iNumEnemiesDenounced - 1); } PlayerTypes eWorstCompetitor = GetBiggestCompetitor(); PlayerTypes ePrimeLeagueCompetitor = GetPrimeLeagueCompetitor(); if (eWorstCompetitor == NO_PLAYER || !GET_PLAYER(eWorstCompetitor).isAlive() || GET_PLAYER(eWorstCompetitor).getNumCities() <= 0 || !(IsDenouncedPlayer(eWorstCompetitor) || IsAtWar(eWorstCompetitor))) eWorstCompetitor = NO_PLAYER; if (ePrimeLeagueCompetitor == NO_PLAYER || !GET_PLAYER(ePrimeLeagueCompetitor).isAlive() || GET_PLAYER(ePrimeLeagueCompetitor).getNumCities() <= 0 || !(IsDenouncedPlayer(ePrimeLeagueCompetitor) || IsAtWar(ePrimeLeagueCompetitor))) ePrimeLeagueCompetitor = NO_PLAYER; if (eWorstCompetitor != NO_PLAYER && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eWorstCompetitor)) iOpinionWeight += /*-20*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_BIGGEST_COMPETITOR); if (ePrimeLeagueCompetitor != NO_PLAYER && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(ePrimeLeagueCompetitor)) iOpinionWeight += /*-20*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_BIGGEST_LEAGUE_RIVAL); } // Scale with WorkAgainstWillingness return AdjustConditionalModifier(iOpinionWeight, GetWorkAgainstWillingness()); } int CvDiplomacyAI::GetDenouncedByOurFriendScore(PlayerTypes ePlayer) const { // This penalty only applies if we're not friends or allies. if (IsDoFAccepted(ePlayer) || IsHasDefensivePact(ePlayer)) return 0; // Ignore for liberators. if (IsLiberator(ePlayer, false, false)) return 0; int iOpinionWeight = 0; int iNumDenouncements = 0; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (!GET_PLAYER(eLoopPlayer).isAlive() || GET_PLAYER(eLoopPlayer).getNumCities() <= 0) continue; if (!GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedByPlayer(eLoopPlayer)) continue; if (!IsTeammate(eLoopPlayer)) { if (GetDoFType(ePlayer) > GetDoFType(eLoopPlayer)) continue; if (GetCachedOpinionWeight(ePlayer) <= GetCachedOpinionWeight(eLoopPlayer)) continue; } if (IsTeammate(eLoopPlayer) || IsDoFAccepted(eLoopPlayer) || IsHasDefensivePact(eLoopPlayer)) { iNumDenouncements++; } if (IsTeammate(eLoopPlayer) || (GetMostValuableFriend() == eLoopPlayer && IsDoFAccepted(eLoopPlayer)) || (GetMostValuableAlly() == eLoopPlayer && IsHasDefensivePact(eLoopPlayer))) { iOpinionWeight += /*15*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_BY_THEIR_KEY_FRIEND); } } if (iNumDenouncements > 0) { iOpinionWeight += /*15*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_BY_THEIR_FRIEND); if (iNumDenouncements > 1) { iOpinionWeight += /*5*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_BY_THEIR_FRIEND_SUBSEQUENT) * (iNumDenouncements - 1); } } // Scale with WorkAgainstWillingness return AdjustConditionalModifier(iOpinionWeight, GetWorkAgainstWillingness()); } ////////////////////////////////////// // PROMISES ////////////////////////////////////// int CvDiplomacyAI::GetBrokenMilitaryPromiseScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; // No scaling for backstabbing penalties! if (BrokeMilitaryPromise(ePlayer)) { iOpinionWeight = /*80*/ GD_INT_GET(OPINION_WEIGHT_BROKEN_MILITARY_PROMISE); } return iOpinionWeight; } int CvDiplomacyAI::GetBrokenMilitaryPromiseWithAnybodyScore(PlayerTypes ePlayer) { if (IsFriendOrAlly(ePlayer) && !CvDiplomacyAIHelpers::IgnoresBackstabbing(GetID(), ePlayer, true)) return 0; int iOpinionWeight = 0; // Don't add this if they broke a military promise with US if (!BrokeMilitaryPromise(ePlayer)) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer, true) && eLoopPlayer != ePlayer && eLoopPlayer != GetID()) { // If this guy is untrustworthy or we hate him, he doesn't count. if (!CvDiplomacyAIHelpers::IgnoresBackstabbing(GetID(), eLoopPlayer, true) && !IsAtWar(eLoopPlayer)) { if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->BrokeMilitaryPromise(ePlayer)) { iOpinionWeight += /*40*/ GD_INT_GET(OPINION_WEIGHT_BROKEN_MILITARY_PROMISE_WORLD); break; } } } } } return iOpinionWeight; } int CvDiplomacyAI::GetIgnoredMilitaryPromiseScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IgnoredMilitaryPromise(ePlayer)) { iOpinionWeight = /*0*/ GD_INT_GET(OPINION_WEIGHT_IGNORED_MILITARY_PROMISE); int iDuration = /*40*/ GD_INT_GET(MILITARY_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN); return AdjustTimedModifier(iOpinionWeight, iDuration, GetMilitaryPromiseTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetBrokenExpansionPromiseScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (BrokeExpansionPromise(ePlayer)) { iOpinionWeight = /*40*/ GD_INT_GET(OPINION_WEIGHT_BROKEN_EXPANSION_PROMISE); int iDuration = /*50*/ GD_INT_GET(EXPANSION_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN); iDuration *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDuration /= 100; return AdjustTimedModifier(iOpinionWeight, iDuration, GetExpansionPromiseTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetIgnoredExpansionPromiseScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IgnoredExpansionPromise(ePlayer)) { iOpinionWeight = /*30*/ GD_INT_GET(OPINION_WEIGHT_IGNORED_EXPANSION_PROMISE); int iDuration = /*30*/ GD_INT_GET(EXPANSION_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN); iDuration *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDuration /= 100; return AdjustTimedModifier(iOpinionWeight, iDuration, GetExpansionPromiseTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetBrokenBorderPromiseScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (BrokeBorderPromise(ePlayer)) { iOpinionWeight = /*40*/ GD_INT_GET(OPINION_WEIGHT_BROKEN_BORDER_PROMISE); int iDuration = /*50*/ GD_INT_GET(BORDER_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN); iDuration *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDuration /= 100; return AdjustTimedModifier(iOpinionWeight, iDuration, GetBorderPromiseTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetIgnoredBorderPromiseScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IgnoredBorderPromise(ePlayer)) { iOpinionWeight = /*30*/ GD_INT_GET(OPINION_WEIGHT_IGNORED_BORDER_PROMISE); int iDuration = /*30*/ GD_INT_GET(BORDER_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN); iDuration *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDuration /= 100; return AdjustTimedModifier(iOpinionWeight, iDuration, GetBorderPromiseTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetBrokenAttackCityStatePromiseScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; // No scaling for backstabbing penalties! if (BrokeAttackCityStatePromise(ePlayer)) { iOpinionWeight = /*80*/ GD_INT_GET(OPINION_WEIGHT_BROKEN_CITY_STATE_PROMISE); } return iOpinionWeight; } int CvDiplomacyAI::GetBrokenAttackCityStatePromiseWithAnybodyScore(PlayerTypes ePlayer) { if (IsFriendOrAlly(ePlayer) && !CvDiplomacyAIHelpers::IgnoresBackstabbing(GetID(), ePlayer, true)) return 0; int iOpinionWeight = 0; // Don't add this if they broke a City-State attack promise with US if (!BrokeAttackCityStatePromise(ePlayer)) { for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer, true) && eLoopPlayer != ePlayer && eLoopPlayer != GetID()) { // If this guy is untrustworthy or we hate him, he doesn't count if (!CvDiplomacyAIHelpers::IgnoresBackstabbing(GetID(), eLoopPlayer, true) && !IsAtWar(eLoopPlayer)) { if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->BrokeAttackCityStatePromise(ePlayer)) { iOpinionWeight = /*40*/ GD_INT_GET(OPINION_WEIGHT_BROKEN_CITY_STATE_PROMISE_WORLD); break; } } } } } return iOpinionWeight; } int CvDiplomacyAI::GetIgnoredAttackCityStatePromiseScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IgnoredAttackCityStatePromise(ePlayer)) { iOpinionWeight = /*30*/ GD_INT_GET(OPINION_WEIGHT_IGNORED_CITY_STATE_PROMISE); int iDuration = /*40*/ GD_INT_GET(ATTACK_CS_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN); iDuration *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDuration /= 100; return AdjustTimedModifier(iOpinionWeight, iDuration, GetAttackCityStatePromiseTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetBrokenBullyCityStatePromiseScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (BrokeBullyCityStatePromise(ePlayer)) { iOpinionWeight = /*40*/ GD_INT_GET(OPINION_WEIGHT_BROKEN_BULLY_CITY_STATE_PROMISE); int iDuration = /*50*/ GD_INT_GET(BULLY_CS_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN); iDuration *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDuration /= 100; return AdjustTimedModifier(iOpinionWeight, iDuration, GetBullyCityStatePromiseTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetIgnoredBullyCityStatePromiseScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IgnoredBullyCityStatePromise(ePlayer)) { iOpinionWeight = /*20*/ GD_INT_GET(OPINION_WEIGHT_IGNORED_BULLY_CITY_STATE_PROMISE); int iDuration = /*50*/ GD_INT_GET(BULLY_CS_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN); iDuration *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDuration /= 100; return AdjustTimedModifier(iOpinionWeight, iDuration, GetBullyCityStatePromiseTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetBrokenNoConvertPromiseScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (BrokeNoConvertPromise(ePlayer)) { iOpinionWeight = /*10*/ GD_INT_GET(OPINION_WEIGHT_BROKEN_NO_CONVERT_PROMISE) * GC.getEraInfo(GC.getGame().getCurrentEra())->getDiploEmphasisReligion(); int iDuration = /*50*/ GD_INT_GET(CONVERT_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN); iDuration *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDuration /= 100; return AdjustTimedModifier(iOpinionWeight, iDuration, GetNoConvertPromiseTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetIgnoredNoConvertPromiseScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IgnoredNoConvertPromise(ePlayer)) { iOpinionWeight = /*5*/ GD_INT_GET(OPINION_WEIGHT_IGNORED_NO_CONVERT_PROMISE) * GC.getEraInfo(GC.getGame().getCurrentEra())->getDiploEmphasisReligion(); int iDuration = /*30*/ GD_INT_GET(CONVERT_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN); iDuration *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDuration /= 100; return AdjustTimedModifier(iOpinionWeight, iDuration, GetNoConvertPromiseTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetBrokenNoDiggingPromiseScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (BrokeNoDiggingPromise(ePlayer)) { iOpinionWeight = /*40*/ GD_INT_GET(OPINION_WEIGHT_BROKEN_NO_DIG_PROMISE); int iDuration = /*50*/ GD_INT_GET(DIGGING_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN); iDuration *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDuration /= 100; return AdjustTimedModifier(iOpinionWeight, iDuration, GetNoDiggingPromiseTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetIgnoredNoDiggingPromiseScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IgnoredNoDiggingPromise(ePlayer)) { iOpinionWeight = /*30*/ GD_INT_GET(OPINION_WEIGHT_IGNORED_NO_DIG_PROMISE); int iDuration = /*30*/ GD_INT_GET(DIGGING_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN); iDuration *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDuration /= 100; return AdjustTimedModifier(iOpinionWeight, iDuration, GetNoDiggingPromiseTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetBrokenSpyPromiseScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (BrokeSpyPromise(ePlayer)) { iOpinionWeight = /*40*/ GD_INT_GET(OPINION_WEIGHT_BROKEN_SPY_PROMISE); int iDuration = /*50*/ GD_INT_GET(SPY_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN); iDuration *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDuration /= 100; return AdjustTimedModifier(iOpinionWeight, iDuration, GetSpyPromiseTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetIgnoredSpyPromiseScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IgnoredSpyPromise(ePlayer)) { iOpinionWeight = /*30*/ GD_INT_GET(OPINION_WEIGHT_IGNORED_SPY_PROMISE); int iDuration = /*30*/ GD_INT_GET(SPY_PROMISE_IGNORED_TURNS_UNTIL_FORGIVEN); iDuration *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDuration /= 100; return AdjustTimedModifier(iOpinionWeight, iDuration, GetSpyPromiseTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetBrokenCoopWarPromiseScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (BrokeCoopWarPromise(ePlayer)) { iOpinionWeight = /*40*/ GD_INT_GET(OPINION_WEIGHT_BROKEN_COOP_WAR_PROMISE); int iDuration = /*60*/ GD_INT_GET(COOP_WAR_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN); return AdjustTimedModifier(iOpinionWeight, iDuration, GetBrokeCoopWarPromiseTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } ////////////////////////////////////// // RELIGION/IDEOLOGY ////////////////////////////////////// int CvDiplomacyAI::GetPolicyScore(PlayerTypes ePlayer) { if (GC.getGame().isOption(GAMEOPTION_NO_POLICIES)) return 0; int iOpinionWeight = 0; int iNumPolicies = GetNumSamePolicies(ePlayer); if (iNumPolicies < 0) { iOpinionWeight = max(/*10*/ GD_INT_GET(OPINION_WEIGHT_DIVERGENT_POLICIES), iNumPolicies * /*5*/ GD_INT_GET(OPINION_WEIGHT_PER_DIVERGENT_POLICY)); } else if (iNumPolicies > 0) { iOpinionWeight = min(/*-10*/ GD_INT_GET(OPINION_WEIGHT_SIMILAR_POLICIES), iNumPolicies * /*-5*/ GD_INT_GET(OPINION_WEIGHT_PER_SIMILAR_POLICY)); } if (GetNeediness() >= /*8*/ GD_INT_GET(POLICY_SCORE_NEEDY_THRESHOLD)) { if (iOpinionWeight > 0) { iOpinionWeight += /*5*/ GD_INT_GET(POLICY_SCORE_NEEDY_BONUS); } else if (iOpinionWeight < 0) { iOpinionWeight += /*-5*/ -GD_INT_GET(POLICY_SCORE_NEEDY_BONUS); } } if (iOpinionWeight > 0 && IsIgnorePolicyDifferences(ePlayer)) return 0; return iOpinionWeight; } int CvDiplomacyAI::GetReligionScore(PlayerTypes ePlayer) { if (GC.getGame().isOption(GAMEOPTION_NO_RELIGION)) return 0; if (IsVassal(ePlayer)) return 0; // Vassals have their own function for this int iOpinionWeight = 0; ReligionTypes eOurOwnedReligion = GetPlayer()->GetReligions()->GetOwnedReligion(); ReligionTypes eOurStateReligion = GetPlayer()->GetReligions()->GetStateReligion(false); ReligionTypes eTheirOwnedReligion = GET_PLAYER(ePlayer).GetReligions()->GetOwnedReligion(); ReligionTypes eTheirStateReligion = GET_PLAYER(ePlayer).GetReligions()->GetStateReligion(false); int iFlavorReligion = m_pPlayer->GetFlavorManager()->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_RELIGION")); int iEraMod = GC.getEraInfo(GC.getGame().getCurrentEra())->getDiploEmphasisReligion(); // Weight increases or decreases based on flavors if (iFlavorReligion < 5) { iEraMod = max(0, iEraMod - 1); } else if (iFlavorReligion > 7) { iEraMod++; } iEraMod += GetPlayer()->GetPlayerTraits()->IsReligious() ? 1 : 0; // We didn't found or conquer, but have a state religion if (eOurOwnedReligion == NO_RELIGION && eOurStateReligion != NO_RELIGION) { if (GetNegativeReligiousConversionPoints(ePlayer) <= 0 && !IsHolyCityCapturedBy(ePlayer)) { if (eOurStateReligion == eTheirOwnedReligion) { iOpinionWeight = /*-4*/ GD_INT_GET(OPINION_WEIGHT_ADOPTING_HIS_RELIGION) * iEraMod; // If it's the World Religion and they're its controller, support them since we get extra League votes from it if (GC.getGame().GetGameLeagues()->GetReligionSpreadStrengthModifier(ePlayer, eTheirOwnedReligion) > 0) { iOpinionWeight *= /*150*/ GD_INT_GET(OPINION_WEIGHT_WORLD_RELIGION_MODIFIER); iOpinionWeight /= 100; } } // Same state religions? else if (eOurStateReligion == eTheirStateReligion) { iOpinionWeight = /*-2*/ GD_INT_GET(OPINION_WEIGHT_SAME_STATE_RELIGIONS) * iEraMod; } } // Different majority religions? if (eOurStateReligion != eTheirStateReligion && eTheirStateReligion != NO_RELIGION) { iOpinionWeight = /*2*/ GD_INT_GET(OPINION_WEIGHT_DIFFERENT_STATE_RELIGIONS) * iEraMod; } } // We founded or conquered else if (eOurOwnedReligion != NO_RELIGION) { // Did they also found or conquer? We don't like that! if (eTheirOwnedReligion != NO_RELIGION) { iOpinionWeight = /*5*/ GD_INT_GET(OPINION_WEIGHT_DIFFERENT_OWNED_RELIGIONS) * iEraMod; // If it's the World Religion and they control its Holy City, we should work against them if (GC.getGame().GetGameLeagues()->GetReligionSpreadStrengthModifier(ePlayer, eTheirOwnedReligion) > 0) { iOpinionWeight *= /*150*/ GD_INT_GET(OPINION_WEIGHT_WORLD_RELIGION_MODIFIER); iOpinionWeight /= 100; } } // No? Well, do they have a state religion? else if (eTheirStateReligion != NO_RELIGION) { // Ours? if (eTheirStateReligion == eOurOwnedReligion) { iOpinionWeight = /*-8*/ GD_INT_GET(OPINION_WEIGHT_ADOPTING_MY_RELIGION) * iEraMod; // If it's the World Religion and we control its Holy City, we should work together if (GC.getGame().GetGameLeagues()->GetReligionSpreadStrengthModifier(GetID(), eOurOwnedReligion) > 0) { iOpinionWeight *= /*150*/ GD_INT_GET(OPINION_WEIGHT_WORLD_RELIGION_MODIFIER); iOpinionWeight /= 100; } } // Someone else's? else { // Who controls the Holy City? PlayerTypes eController = NO_PLAYER; const CvReligion* pReligion = GC.getGame().GetGameReligions()->GetReligion(eTheirStateReligion, NO_PLAYER); if (MOD_BALANCE_VP) { if (pReligion) { CvCity* pHolyCity = pReligion->GetHolyCity(); if (pHolyCity != NULL) { eController = pHolyCity->getOwner(); } } } else if (pReligion) { eController = pReligion->m_eFounder; } // If the religion's founder is our teammate, don't apply a penalty. if (eController != NO_PLAYER && IsTeammate(eController)) return 0; // If the religion's founder is THEIR teammate, treat it like an opposing state religion. if (eController != NO_PLAYER && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsTeammate(eController)) { iOpinionWeight = /*5*/ GD_INT_GET(OPINION_WEIGHT_DIFFERENT_OWNED_RELIGIONS) * iEraMod; // If it's the World Religion and their teammate controls its Holy City, we should work against them if (GC.getGame().GetGameLeagues()->GetReligionSpreadStrengthModifier((PlayerTypes)pReligion->m_eFounder, eTheirStateReligion) > 0) { iOpinionWeight *= /*150*/ GD_INT_GET(OPINION_WEIGHT_WORLD_RELIGION_MODIFIER); iOpinionWeight /= 100; } } // Otherwise, apply a penalty for different majority religions. else { iOpinionWeight = /*2*/ GD_INT_GET(OPINION_WEIGHT_DIFFERENT_STATE_RELIGIONS) * iEraMod; } } } } if (iOpinionWeight > 0 && IsIgnoreReligionDifferences(ePlayer)) return 0; return iOpinionWeight; } int CvDiplomacyAI::GetReligiousConversionPointsScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; int iPoints = GetNegativeReligiousConversionPoints(ePlayer); if (iPoints > 0) { iOpinionWeight = (GetNegativeReligiousConversionPoints(ePlayer) * GC.getEraInfo(GC.getGame().getCurrentEra())->getDiploEmphasisReligion() * /*1*/ GD_INT_GET(OPINION_WEIGHT_PER_NEGATIVE_CONVERSION)); int iDuration = AdjustModifierDuration(/*25*/ GD_INT_GET(RELIGIOUS_CONVERSION_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true); return AdjustTimedModifier(iOpinionWeight, iDuration, GetReligiousConversionTurn(ePlayer), TIMED_MODIFIER_STACKED, iPoints); } return iOpinionWeight; } int CvDiplomacyAI::GetIdeologyScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; PolicyBranchTypes eMyBranch = m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree(); PolicyBranchTypes eTheirBranch = GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree(); CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (eMyBranch != NO_POLICY_BRANCH_TYPE && eTheirBranch != NO_POLICY_BRANCH_TYPE) { int iFlavorCulture = m_pPlayer->GetFlavorManager()->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_CULTURE")); int iEraMod = GC.getEraInfo(GC.getGame().getCurrentEra())->getDiploEmphasisLatePolicies(); // Weight increases or decreases based on flavors if (iFlavorCulture < 5) { iEraMod = max(0, iEraMod - 1); } else if (iFlavorCulture > 7) { iEraMod++; } iEraMod += (GetPlayer()->GetPlayerTraits()->IsTourism() || IsCultural() || IsSecondaryCultural()) ? 1 : 0; iEraMod += IsCloseToCultureVictory() ? 1 : 0; if (eMyBranch == eTheirBranch) { iOpinionWeight += iEraMod * /*-10*/ GD_INT_GET(OPINION_WEIGHT_SAME_LATE_POLICIES); // World Ideology modifier? if (pLeague != NULL && pLeague->GetPressureForIdeology(eMyBranch) > 0) { iOpinionWeight *= /*150*/ GD_INT_GET(OPINION_WEIGHT_WORLD_IDEOLOGY_MODIFIER); iOpinionWeight /= 100; } } else if (eMyBranch != eTheirBranch) { iOpinionWeight += iEraMod * /*10*/ GD_INT_GET(OPINION_WEIGHT_DIFFERENT_LATE_POLICIES); // World Ideology modifier? if (pLeague != NULL && (pLeague->GetPressureForIdeology(eMyBranch) > 0 || pLeague->GetPressureForIdeology(eTheirBranch) > 0)) { iOpinionWeight *= /*150*/ GD_INT_GET(OPINION_WEIGHT_WORLD_IDEOLOGY_MODIFIER); iOpinionWeight /= 100; } } } if (iOpinionWeight > 0 && IsIgnoreIdeologyDifferences(ePlayer)) return 0; // Vassals care less, since they're forced to adopt the master's ideology if (IsVassal(ePlayer) && !IsVoluntaryVassalage(ePlayer)) { iOpinionWeight *= 100; iOpinionWeight /= max(100, /*200*/ GD_INT_GET(OPINION_WEIGHT_IDEOLOGY_VASSAL_DIVISOR)); } else if (!IsVassal(ePlayer) && GetPlayer()->IsVassalOfSomeone()) { iOpinionWeight *= 100; iOpinionWeight /= max(100, /*200*/ GD_INT_GET(OPINION_WEIGHT_IDEOLOGY_VASSAL_DIVISOR)); } // Care less about vassals' ideologies, for the same reason else if (GET_PLAYER(ePlayer).IsVassalOfSomeone()) { iOpinionWeight *= 100; iOpinionWeight /= max(100, /*200*/ GD_INT_GET(OPINION_WEIGHT_IDEOLOGY_VASSAL_DIVISOR)); } return iOpinionWeight; } ////////////////////////////////////// // PROTECTED MINORS ////////////////////////////////////// int CvDiplomacyAI::GetPtPSameCSScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; int iSamePtP = 0; for (int iPlayerLoop = MAX_MAJOR_CIVS; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eMinorLoopPlayer = (PlayerTypes) iPlayerLoop; if (eMinorLoopPlayer == NO_PLAYER) continue; if (GET_PLAYER(eMinorLoopPlayer).isMinorCiv() && GET_PLAYER(eMinorLoopPlayer).getNumCities() > 0) { if (GET_PLAYER(eMinorLoopPlayer).GetMinorCivAI()->IsProtectedByMajor(ePlayer) && GET_PLAYER(eMinorLoopPlayer).GetMinorCivAI()->IsProtectedByMajor(GetID())) { iSamePtP++; } } } // Diplomatic civs apply a larger bonus for this if they're not being competed with. if ((IsDiplomat() || IsSecondaryDiplomat()) && !IsMinorCivTroublemaker(ePlayer)) { iSamePtP *= /*200*/ GD_INT_GET(OPINION_WEIGHT_PTP_SAME_MINOR_DIPLOMAT_MULTIPLIER); iSamePtP /= 100; } if (iSamePtP > 0) { iOpinionWeight += min(/*-12*/ GD_INT_GET(OPINION_WEIGHT_PTP_SAME_MINOR_MIN), (iSamePtP * /*-3*/ GD_INT_GET(OPINION_WEIGHT_PTP_SAME_MINOR_EACH))); } return iOpinionWeight; } int CvDiplomacyAI::GetAngryAboutProtectedMinorKilledScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsAngryAboutProtectedMinorKilled(ePlayer)) { iOpinionWeight += /*40*/ GD_INT_GET(OPINION_WEIGHT_KILLED_PROTECTED_MINOR); if (GetOtherPlayerNumProtectedMinorsKilled(ePlayer) > 1) iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_KILLED_MANY_PROTECTED_MINORS); int iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(OPINION_WEIGHT_KILLED_PROTECTED_MINOR_NUM_TURNS_UNTIL_FORGIVEN), GetMinorCivApproachBias(CIV_APPROACH_FRIENDLY)); return AdjustTimedModifier(iOpinionWeight, iDuration, GetOtherPlayerKilledProtectedMinorTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetAngryAboutProtectedMinorAttackedScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsAngryAboutProtectedMinorAttacked(ePlayer)) { iOpinionWeight = /*20*/ GD_INT_GET(OPINION_WEIGHT_ATTACKED_PROTECTED_MINOR_RECENTLY); if (GetOtherPlayerNumProtectedMinorsAttacked(ePlayer) > 1) iOpinionWeight += /*15*/ GD_INT_GET(OPINION_WEIGHT_ATTACKED_MANY_PROTECTED_MINORS); int iDuration = AdjustModifierDuration(/*30*/ GD_INT_GET(OPINION_WEIGHT_ATTACKED_PROTECTED_MINOR_NUM_TURNS_UNTIL_FORGIVEN), GetMinorCivApproachBias(CIV_APPROACH_FRIENDLY)); return AdjustTimedModifier(iOpinionWeight, iDuration, GetOtherPlayerAttackedProtectedMinorTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetAngryAboutProtectedMinorBulliedScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsAngryAboutProtectedMinorBullied(ePlayer)) { iOpinionWeight = /*15*/ GD_INT_GET(OPINION_WEIGHT_BULLIED_PROTECTED_MINOR_RECENTLY); if (GetOtherPlayerNumProtectedMinorsBullied(ePlayer) > 1) iOpinionWeight += /*10*/ GD_INT_GET(OPINION_WEIGHT_BULLIED_MANY_PROTECTED_MINORS); int iDuration = AdjustModifierDuration(/*30*/ GD_INT_GET(OPINION_WEIGHT_BULLIED_PROTECTED_MINOR_NUM_TURNS_UNTIL_FORGIVEN), true); return AdjustTimedModifier(iOpinionWeight, iDuration, GetOtherPlayerBulliedProtectedMinorTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetAngryAboutSidedWithProtectedMinorScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsAngryAboutSidedWithProtectedMinor(ePlayer)) { iOpinionWeight = /*10*/ GD_INT_GET(OPINION_WEIGHT_SIDED_WITH_THEIR_MINOR); if (!IsDoFAccepted(ePlayer)) { if (GetBoldness() > 7 || GetMeanness() > 7) iOpinionWeight += /*10*/ GD_INT_GET(OPINION_WEIGHT_SIDED_WITH_THEIR_MINOR_AGGRESSIVE_MOD); else if (GetPlayer()->GetPlayerTraits()->GetBullyMilitaryStrengthModifier() != 0 || GetPlayer()->GetPlayerTraits()->GetBullyValueModifier() != 0 || GetPlayer()->GetPlayerTraits()->GetCityStateCombatModifier() != 0 || GetPlayer()->GetPlayerTraits()->IsBullyAnnex() || GetPlayer()->GetPlayerTraits()->IgnoreBullyPenalties() || GetPlayer()->IsCanBullyFriendlyCS()) { iOpinionWeight += /*10*/ GD_INT_GET(OPINION_WEIGHT_SIDED_WITH_THEIR_MINOR_AGGRESSIVE_MOD); } } int iDuration = AdjustModifierDuration(/*10*/ GD_INT_GET(OPINION_WEIGHT_SIDED_WITH_THEIR_MINOR_NUM_TURNS_UNTIL_FORGIVEN), GetForgiveness(), true); return AdjustTimedModifier(iOpinionWeight, iDuration, GetOtherPlayerSidedWithProtectedMinorTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } ////////////////////////////////////// // DECLARATION OF FRIENDSHIP ////////////////////////////////////// int CvDiplomacyAI::GetDOFAcceptedScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; if (IsDoFAccepted(ePlayer)) { iOpinionWeight += /*-30*/ GD_INT_GET(OPINION_WEIGHT_DOF); if (GetMostValuableFriend() == ePlayer) { iOpinionWeight += /*-20*/ GD_INT_GET(OPINION_WEIGHT_DOF_MOST_VALUED_FRIEND); } } else { // Were we previously friends? switch (GetDoFType(ePlayer)) { case DOF_TYPE_UNTRUSTWORTHY: case DOF_TYPE_NEW: break; case DOF_TYPE_FRIENDS: iOpinionWeight += /*-10*/ GD_INT_GET(OPINION_WEIGHT_DOF_TYPE_FRIENDS); break; case DOF_TYPE_ALLIES: iOpinionWeight += /*-20*/ GD_INT_GET(OPINION_WEIGHT_DOF_TYPE_ALLIES); break; case DOF_TYPE_BATTLE_BROTHERS: iOpinionWeight += /*-30*/ GD_INT_GET(OPINION_WEIGHT_DOF_TYPE_BATTLE_BROTHERS); break; } } // Scale inversely with DoFWillingness - AIs more willing to be friends place less value on individual friends; vice versa for AIs who want fewer but closer friends return AdjustConditionalModifier(iOpinionWeight, GetDoFWillingness(), true); } int CvDiplomacyAI::GetDOFWithAnyFriendScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; int iMutualFriends = GetNumMutualFriends(ePlayer); // They have a DoF with at least one other player we have a DoF with if (iMutualFriends > 0) { iOpinionWeight += /*-20*/ GD_INT_GET(OPINION_WEIGHT_DOF_WITH_FRIEND); if (iMutualFriends > 1) { iOpinionWeight += /*-8*/ GD_INT_GET(OPINION_WEIGHT_DOF_WITH_FRIEND_SUBSEQUENT) * (iMutualFriends - 1); } PlayerTypes eBestFriend = GetMostValuableFriend(); PlayerTypes eBestAlly = GetMostValuableAlly(); if (eBestFriend == NO_PLAYER || !IsDoFAccepted(eBestFriend) || !GET_PLAYER(eBestFriend).isAlive() || GET_PLAYER(eBestFriend).getNumCities() <= 0) eBestFriend = NO_PLAYER; if (eBestAlly == NO_PLAYER || !IsDoFAccepted(eBestAlly) || !IsHasDefensivePact(eBestAlly) || !GET_PLAYER(eBestAlly).isAlive() || GET_PLAYER(eBestAlly).getNumCities() <= 0) eBestAlly = NO_PLAYER; if (eBestFriend != NO_PLAYER && eBestAlly != NO_PLAYER) { if (/*-20*/ GD_INT_GET(OPINION_WEIGHT_DOF_WITH_MOST_VALUED_FRIEND) <= /*-10*/ GD_INT_GET(OPINION_WEIGHT_DOF_WITH_MOST_VALUED_ALLY)) { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAccepted(eBestFriend)) iOpinionWeight += GD_INT_GET(OPINION_WEIGHT_DOF_WITH_MOST_VALUED_FRIEND); else if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAccepted(eBestAlly)) iOpinionWeight += GD_INT_GET(OPINION_WEIGHT_DOF_WITH_MOST_VALUED_ALLY); } else { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAccepted(eBestAlly)) iOpinionWeight += GD_INT_GET(OPINION_WEIGHT_DOF_WITH_MOST_VALUED_ALLY); else if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAccepted(eBestFriend)) iOpinionWeight += GD_INT_GET(OPINION_WEIGHT_DOF_WITH_MOST_VALUED_FRIEND); } } else if (GET_TEAM(GetTeam()).isDefensivePactTradingAllowed() && /*2*/ min(GD_INT_GET(DEFENSIVE_PACT_LIMIT_BASE), GD_INT_GET(AI_DEFENSIVE_PACT_LIMIT_BASE)) > 0) { if ((eBestFriend != NO_PLAYER && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAccepted(eBestFriend)) || (eBestAlly != NO_PLAYER && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAccepted(eBestAlly))) { iOpinionWeight += /*-20*/ min(GD_INT_GET(OPINION_WEIGHT_DOF_WITH_MOST_VALUED_FRIEND), GD_INT_GET(OPINION_WEIGHT_DOF_WITH_MOST_VALUED_ALLY)); } } else if (eBestFriend != NO_PLAYER && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAccepted(eBestFriend)) { iOpinionWeight += /*-20*/ GD_INT_GET(OPINION_WEIGHT_DOF_WITH_MOST_VALUED_FRIEND); } } // Scale with WorkWithWillingness return AdjustConditionalModifier(iOpinionWeight, GetWorkWithWillingness()); } int CvDiplomacyAI::GetDOFWithAnyEnemyScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; int iEnemyFriends = GetNumEnemyFriends(ePlayer); // They have a DoF with at least one other player we have DENOUNCED or are AT WAR with if (iEnemyFriends > 0) { iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_DOF_WITH_ENEMY); if (iEnemyFriends > 1) { iOpinionWeight += /*8*/ GD_INT_GET(OPINION_WEIGHT_DOF_WITH_ENEMY_SUBSEQUENT) * (iEnemyFriends - 1); } PlayerTypes eWorstCompetitor = GetBiggestCompetitor(); PlayerTypes ePrimeLeagueCompetitor = GetPrimeLeagueCompetitor(); if (eWorstCompetitor == NO_PLAYER || !GET_PLAYER(eWorstCompetitor).isAlive() || GET_PLAYER(eWorstCompetitor).getNumCities() <= 0 || !(IsDenouncedPlayer(eWorstCompetitor) || IsAtWar(eWorstCompetitor))) eWorstCompetitor = NO_PLAYER; if (ePrimeLeagueCompetitor == NO_PLAYER || !GET_PLAYER(ePrimeLeagueCompetitor).isAlive() || GET_PLAYER(ePrimeLeagueCompetitor).getNumCities() <= 0 || !(IsDenouncedPlayer(ePrimeLeagueCompetitor) || IsAtWar(ePrimeLeagueCompetitor))) ePrimeLeagueCompetitor = NO_PLAYER; if (eWorstCompetitor != NO_PLAYER && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAccepted(eWorstCompetitor)) iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_DOF_WITH_BIGGEST_COMPETITOR); if (ePrimeLeagueCompetitor != NO_PLAYER && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAccepted(ePrimeLeagueCompetitor)) iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_DOF_WITH_BIGGEST_LEAGUE_RIVAL); } // Scale with Neediness return AdjustConditionalModifier(iOpinionWeight, GetNeediness()); } ////////////////////////////////////// // TRADE AGREEMENTS ////////////////////////////////////// int CvDiplomacyAI::GetDPAcceptedScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; // We have made a Defensive Pact if (IsHasDefensivePact(ePlayer)) { iOpinionWeight += /*-20*/ GD_INT_GET(OPINION_WEIGHT_DP); if (GetMostValuableAlly() == ePlayer) { iOpinionWeight += /*-20*/ GD_INT_GET(OPINION_WEIGHT_DP_MOST_VALUED_ALLY); } } // Maximum scaling if we have conquered someone else if (GetPlayerNumMajorsConquered(GetID()) > 0) { iOpinionWeight *= /*150*/ GD_INT_GET(OPINION_WEIGHT_DP_CONQUEROR_MULTIPLIER); iOpinionWeight /= 100; return iOpinionWeight; } // Otherwise, scale with WarmongerHate return AdjustConditionalModifier(iOpinionWeight, GetWarmongerHate()); } int CvDiplomacyAI::GetDPWithAnyFriendScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; int iMutualDefensePacts = GetNumMutualDefensePacts(ePlayer); // They have a DP with at least one other player we have a DP with if (iMutualDefensePacts > 0) { iOpinionWeight += /*-15*/ GD_INT_GET(OPINION_WEIGHT_DP_WITH_FRIEND); if (iMutualDefensePacts > 1) { iOpinionWeight += /*-10*/ GD_INT_GET(OPINION_WEIGHT_DP_WITH_FRIEND_SUBSEQUENT) * (iMutualDefensePacts - 1); } PlayerTypes eBestAlly = GetMostValuableAlly(); PlayerTypes eBestFriend = GetMostValuableFriend(); if (eBestAlly == NO_PLAYER || !IsHasDefensivePact(eBestAlly) || !GET_PLAYER(eBestAlly).isAlive() || GET_PLAYER(eBestAlly).getNumCities() <= 0) eBestAlly = NO_PLAYER; if (eBestFriend == NO_PLAYER || !IsHasDefensivePact(eBestFriend) || !IsDoFAccepted(eBestFriend) || !GET_PLAYER(eBestFriend).isAlive() || GET_PLAYER(eBestFriend).getNumCities() <= 0) eBestFriend = NO_PLAYER; if (eBestAlly != NO_PLAYER && eBestFriend != NO_PLAYER) { if (/*-10*/ GD_INT_GET(OPINION_WEIGHT_DP_WITH_MOST_VALUED_ALLY) <= /*-10*/ GD_INT_GET(OPINION_WEIGHT_DP_WITH_MOST_VALUED_FRIEND)) { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasDefensivePact(eBestAlly)) iOpinionWeight += GD_INT_GET(OPINION_WEIGHT_DP_WITH_MOST_VALUED_ALLY); else if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasDefensivePact(eBestFriend)) iOpinionWeight += GD_INT_GET(OPINION_WEIGHT_DP_WITH_MOST_VALUED_FRIEND); } else { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasDefensivePact(eBestFriend)) iOpinionWeight += GD_INT_GET(OPINION_WEIGHT_DP_WITH_MOST_VALUED_FRIEND); else if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasDefensivePact(eBestAlly)) iOpinionWeight += GD_INT_GET(OPINION_WEIGHT_DP_WITH_MOST_VALUED_ALLY); } } else if ((eBestFriend != NO_PLAYER && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasDefensivePact(eBestFriend)) || (eBestAlly != NO_PLAYER && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasDefensivePact(eBestAlly))) { iOpinionWeight += /*-10*/ min(GD_INT_GET(OPINION_WEIGHT_DP_WITH_MOST_VALUED_FRIEND), GD_INT_GET(OPINION_WEIGHT_DP_WITH_MOST_VALUED_ALLY)); } } // Scale with WarmongerHate return AdjustConditionalModifier(iOpinionWeight, GetWarmongerHate()); } int CvDiplomacyAI::GetDPWithAnyEnemyScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; int iEnemyDefensePacts = GetNumEnemyDefensePacts(ePlayer); // They have a DP with at least one enemy we have denounced if (iEnemyDefensePacts > 0) { iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_DP_WITH_ENEMY); if (iEnemyDefensePacts > 1) { iOpinionWeight += /*10*/ GD_INT_GET(OPINION_WEIGHT_DP_WITH_ENEMY_SUBSEQUENT) * (iEnemyDefensePacts - 1); } PlayerTypes eWorstCompetitor = GetBiggestCompetitor(); PlayerTypes ePrimeLeagueCompetitor = GetPrimeLeagueCompetitor(); if (eWorstCompetitor == NO_PLAYER || !GET_PLAYER(eWorstCompetitor).isAlive() || GET_PLAYER(eWorstCompetitor).getNumCities() <= 0 || !(IsDenouncedPlayer(eWorstCompetitor) || IsAtWar(eWorstCompetitor))) eWorstCompetitor = NO_PLAYER; if (ePrimeLeagueCompetitor == NO_PLAYER || !GET_PLAYER(ePrimeLeagueCompetitor).isAlive() || GET_PLAYER(ePrimeLeagueCompetitor).getNumCities() <= 0 || !(IsDenouncedPlayer(ePrimeLeagueCompetitor) || IsAtWar(ePrimeLeagueCompetitor))) ePrimeLeagueCompetitor = NO_PLAYER; if (eWorstCompetitor != NO_PLAYER && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasDefensivePact(eWorstCompetitor)) iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_DP_WITH_BIGGEST_COMPETITOR); if (ePrimeLeagueCompetitor != NO_PLAYER && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasDefensivePact(ePrimeLeagueCompetitor)) iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_DP_WITH_BIGGEST_LEAGUE_RIVAL); } // Scale with Neediness return AdjustConditionalModifier(iOpinionWeight, GetNeediness()); } int CvDiplomacyAI::GetOpenBordersScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; bool bTheyAllow = IsHasOpenBorders(ePlayer); bool bWeAllow = GET_PLAYER(ePlayer).GetDiplomacyAI()->IsHasOpenBorders(GetID()); bool bVassalPenalty = false; if (IsVassal(ePlayer) && !WasResurrectedBy(ePlayer)) { if (IsVoluntaryVassalage(ePlayer)) { if (GetVassalTreatmentLevel(ePlayer) > VASSAL_TREATMENT_DISAGREE) bVassalPenalty = true; } else { if (GetVassalTreatmentLevel(ePlayer) != VASSAL_TREATMENT_CONTENT) bVassalPenalty = true; } } if (bWeAllow && bTheyAllow) { iOpinionWeight = bVassalPenalty ? /*-8*/ GD_INT_GET(OPINION_WEIGHT_OPEN_BORDERS_US) : /*-12*/ GD_INT_GET(OPINION_WEIGHT_OPEN_BORDERS_MUTUAL); } else if (bTheyAllow) { iOpinionWeight = /*-8*/ GD_INT_GET(OPINION_WEIGHT_OPEN_BORDERS_US); } else if (bWeAllow) { iOpinionWeight = bVassalPenalty ? 0 : /*-4*/ GD_INT_GET(OPINION_WEIGHT_OPEN_BORDERS_THEM); } return iOpinionWeight; } int CvDiplomacyAI::GetResearchAgreementScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsHasResearchAgreement(ePlayer)) { iOpinionWeight += /*-10*/ GD_INT_GET(OPINION_WEIGHT_RA); if (IsScientist() || IsSecondaryScientist() || GetPlayer()->GetPlayerTraits()->IsNerd()) { iOpinionWeight += /*-10*/ GD_INT_GET(OPINION_WEIGHT_RA_SCIENTIFIC_MOD); } } return iOpinionWeight; } ////////////////////////////////////// // TRAITOR OPINION ////////////////////////////////////// int CvDiplomacyAI::GetFriendDenouncementScore(PlayerTypes ePlayer) { int iTraitorOpinion = 0; // How many of their friends have denounced them? for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != GetID() && eLoopPlayer != ePlayer && IsHasMet(eLoopPlayer, true) && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsFriendDenouncedUs(eLoopPlayer)) { // If this guy is untrustworthy or we hate him, he doesn't count if (!CvDiplomacyAIHelpers::IgnoresBackstabbing(GetID(), eLoopPlayer, true) && !IsAtWar(eLoopPlayer) && !IsDenouncedPlayer(eLoopPlayer) && !IsDenouncedByPlayer(eLoopPlayer)) { // Do we like this player who denounced them more than the player we're evaluating? if (GetCachedOpinionWeight(eLoopPlayer) < GetCachedOpinionWeight(ePlayer)) { int iPenalty = /*20*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_BY_FRIEND_EACH); if (IsFriendOrAlly(eLoopPlayer)) { iPenalty *= /*200*/ GD_INT_GET(OPINION_WEIGHT_BETRAYED_OUR_FRIEND_MULTIPLIER); iPenalty /= 100; } iTraitorOpinion += iPenalty; } else { iTraitorOpinion += /*10*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_BY_FRIEND_DONT_LIKE); } } } } // Scale with WorkAgainstWillingness return AdjustConditionalModifier(iTraitorOpinion, GetWorkAgainstWillingness()); } int CvDiplomacyAI::GetPlayerDenouncedFriendScore(PlayerTypes ePlayer) { int iTraitorOpinion = 0; // If we don't view them as untrustworthy, disregard this if (!IsUntrustworthy(ePlayer)) return 0; int iWeight = /*40*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_FRIEND_EACH); // How many of their friends have they denounced? for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != GetID() && eLoopPlayer != ePlayer && IsHasMet(eLoopPlayer, true) && GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsFriendDenouncedUs(ePlayer)) { // If this guy is untrustworthy, he doesn't count if (!CvDiplomacyAIHelpers::IgnoresBackstabbing(GetID(), eLoopPlayer, true)) { // Someone important to us? if (IsFriendOrAlly(eLoopPlayer)) { iTraitorOpinion += (iWeight * /*200*/ GD_INT_GET(OPINION_WEIGHT_BETRAYED_OUR_FRIEND_MULTIPLIER) / 100); } else { iTraitorOpinion += iWeight; } } } } return iTraitorOpinion; } int CvDiplomacyAI::GetFriendDenouncedUsScore(PlayerTypes ePlayer) const { int iTraitorOpinion = 0; // Denounced us while we were his friend // No scaling for backstabbing penalties! if (IsFriendDenouncedUs(ePlayer)) { iTraitorOpinion = /*80*/ GD_INT_GET(OPINION_WEIGHT_DENOUNCED_ME_FRIENDS); } return iTraitorOpinion; } int CvDiplomacyAI::GetPlayerAttackedVassalScore(PlayerTypes ePlayer) { int iTraitorOpinion = 0; // If we don't view them as untrustworthy, disregard this if (!IsUntrustworthy(ePlayer)) return 0; int iWeight = /*50*/ GD_INT_GET(OPINION_WEIGHT_ATTACKED_OWN_VASSAL); // How many of their vassals have they declared war on? for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != GetID() && eLoopPlayer != ePlayer && IsHasMet(eLoopPlayer, true) && GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->BrokeVassalAgreement(ePlayer)) { // If this guy is untrustworthy, he doesn't count if (!CvDiplomacyAIHelpers::IgnoresBackstabbing(GetID(), eLoopPlayer, true)) { // Someone important to us? if (IsFriendOrAlly(eLoopPlayer)) { iTraitorOpinion += (iWeight * /*200*/ GD_INT_GET(OPINION_WEIGHT_BETRAYED_OUR_FRIEND_MULTIPLIER) / 100); } else { iTraitorOpinion += iWeight; } } } } return iTraitorOpinion; } int CvDiplomacyAI::GetMasterAttackedUsScore(PlayerTypes ePlayer) const { int iTraitorOpinion = 0; // Declared war on us while we were his vassal // No scaling for backstabbing penalties! if (BrokeVassalAgreement(ePlayer)) { iTraitorOpinion = /*100*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_BROKEN_VASSAL_AGREEMENT); } return iTraitorOpinion; } int CvDiplomacyAI::GetPlayerAttackedFriendScore(PlayerTypes ePlayer) { int iTraitorOpinion = 0; // If we don't view them as untrustworthy, disregard this if (!IsUntrustworthy(ePlayer)) return 0; int iWeight = /*75*/ GD_INT_GET(OPINION_WEIGHT_WAR_FRIEND_EACH); // How many of their friends have they declared war on? for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != GetID() && eLoopPlayer != ePlayer && IsHasMet(eLoopPlayer, true) && GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsFriendDeclaredWarOnUs(ePlayer)) { // If this guy is untrustworthy, he doesn't count if (!CvDiplomacyAIHelpers::IgnoresBackstabbing(GetID(), eLoopPlayer, true)) { // Someone important to us? if (IsFriendOrAlly(eLoopPlayer)) { iTraitorOpinion += (iWeight * /*200*/ GD_INT_GET(OPINION_WEIGHT_BETRAYED_OUR_FRIEND_MULTIPLIER) / 100); } else { iTraitorOpinion += iWeight; } } } } return iTraitorOpinion; } int CvDiplomacyAI::GetFriendAttackedUsScore(PlayerTypes ePlayer) const { int iTraitorOpinion = 0; // Declared war on us while we were his friend // No scaling for backstabbing penalties! if (IsFriendDeclaredWarOnUs(ePlayer)) { iTraitorOpinion = /*150*/ GD_INT_GET(OPINION_WEIGHT_WAR_ME_FRIENDS); } return iTraitorOpinion; } int CvDiplomacyAI::GetPlayerAttackedResurrectedCivScore(PlayerTypes ePlayer) { int iTraitorOpinion = 0; // If we don't view them as untrustworthy, disregard this if (!IsUntrustworthy(ePlayer)) return 0; int iWeight = /*75*/ GD_INT_GET(OPINION_WEIGHT_ATTACKED_RESURRECTED_PLAYER); // How many resurrected players have they declared war on? for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != GetID() && eLoopPlayer != ePlayer && IsHasMet(eLoopPlayer, true) && GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsResurrectorAttackedUs(ePlayer)) { // If this guy is untrustworthy, he doesn't count if (!CvDiplomacyAIHelpers::IgnoresBackstabbing(GetID(), eLoopPlayer, true)) { // Someone important to us? if (IsFriendOrAlly(eLoopPlayer)) { iTraitorOpinion += (iWeight * /*200*/ GD_INT_GET(OPINION_WEIGHT_BETRAYED_OUR_FRIEND_MULTIPLIER) / 100); } else { iTraitorOpinion += iWeight; } } } } return iTraitorOpinion; } int CvDiplomacyAI::GetResurrectorAttackedUsScore(PlayerTypes ePlayer) const { int iTraitorOpinion = 0; if (IsResurrectorAttackedUs(ePlayer)) { iTraitorOpinion = /*200*/ GD_INT_GET(OPINION_WEIGHT_RESURRECTOR_ATTACKED_US); } return iTraitorOpinion; } ////////////////////////////////////// // WORLD CONGRESS ////////////////////////////////////// int CvDiplomacyAI::GetVotingHistoryOpinionScore(PlayerTypes ePlayer) { if (!GC.getGame().GetGameLeagues()->GetActiveLeague()) return 0; int iOpinionWeight = 0; int iVotingScore = GetVotingHistoryScore(ePlayer); if (iVotingScore > 0) { int iMaxVotingBonus = /*2400*/ GD_INT_GET(VOTING_HISTORY_SCORE_MAX); int iPercentOfMaximum = (iVotingScore * 100) / max(iMaxVotingBonus, 1); iOpinionWeight = (/*-60*/ -GD_INT_GET(OPINION_WEIGHT_VOTING_HISTORY_MAX)) * iPercentOfMaximum / 100; } else if (iVotingScore < 0) { int iMaxVotingPenalty = /*-2400*/ -GD_INT_GET(VOTING_HISTORY_SCORE_MAX); int iPercentOfMaximum = (iVotingScore * 100) / min(iMaxVotingPenalty, -1); iOpinionWeight = (/*60*/ GD_INT_GET(OPINION_WEIGHT_VOTING_HISTORY_MAX)) * iPercentOfMaximum / 100; } if (IsDiplomat() || IsSecondaryDiplomat() || GetPlayer()->GetPlayerTraits()->IsDiplomat()) { iOpinionWeight *= /*200*/ GD_INT_GET(OPINION_WEIGHT_VOTING_HISTORY_DIPLOMAT_MULTIPLIER); iOpinionWeight /= 100; } return iOpinionWeight; } int CvDiplomacyAI::GetLikedTheirProposalScore(PlayerTypes ePlayer) { if (!GC.getGame().GetGameLeagues()->GetActiveLeague()) return 0; int iOpinionWeight = 0; if (WeLikedTheirProposal(ePlayer)) { iOpinionWeight = GetLikedTheirProposalValue(ePlayer); if (IsDiplomat() || IsSecondaryDiplomat() || GetPlayer()->GetPlayerTraits()->IsDiplomat()) { iOpinionWeight *= /*134*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_DIPLOMAT_MULTIPLIER); iOpinionWeight /= 100; } int iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_NUM_TURNS), GetWorkWithWillingness()); return AdjustTimedModifier(iOpinionWeight, iDuration, GetWeLikedTheirProposalTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetDislikedTheirProposalScore(PlayerTypes ePlayer) { if (!GC.getGame().GetGameLeagues()->GetActiveLeague()) return 0; int iOpinionWeight = 0; if (WeDislikedTheirProposal(ePlayer)) { iOpinionWeight = GetLikedTheirProposalValue(ePlayer); if (IsDiplomat() || IsSecondaryDiplomat() || GetPlayer()->GetPlayerTraits()->IsDiplomat()) { iOpinionWeight *= /*134*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_DIPLOMAT_MULTIPLIER); iOpinionWeight /= 100; } int iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_NUM_TURNS), GetWorkAgainstWillingness()); return AdjustTimedModifier(iOpinionWeight, iDuration, GetWeDislikedTheirProposalTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetSupportedOurProposalScore(PlayerTypes ePlayer) { if (!GC.getGame().GetGameLeagues()->GetActiveLeague()) return 0; int iOpinionWeight = 0; int iSupportValue = GetSupportedOurProposalValue(ePlayer); // Foiled our proposals! if (iSupportValue > 0) { int iTurn = 0; int iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(OPINION_WEIGHT_THEY_FOILED_OUR_PROPOSAL_NUM_TURNS), GetWorkAgainstWillingness()); if (IsFoiledOurProposalAndThenSupportedUs(ePlayer)) { iTurn = GetTheySupportedOurProposalTurn(ePlayer); // The more recent action should carry higher value if Forgiveness is higher int iDurationMod = (GetForgiveness() - 5) * 2; iDurationMod *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDurationMod /= 100; if (iDurationMod > 0) { iDuration -= iDurationMod; } } else { iTurn = GetTheyFoiledOurProposalTurn(ePlayer); } int iMinOpinionWeight = max(0, /*10*/ GD_INT_GET(OPINION_WEIGHT_THEY_FOILED_OUR_PROPOSAL)); int iMaxOpinionWeight = max(0, /*60*/ GD_INT_GET(OPINION_WEIGHT_THEY_FOILED_OUR_PROPOSAL_MAX)); int iDifference = iMaxOpinionWeight - iMinOpinionWeight; int iAwardPercentage = min(100, (iSupportValue * max(1, /*2*/ GD_INT_GET(OPINION_WEIGHT_PER_VOTE_PERCENT)))); // 2% of the penalty for each 1% of the vote they contributed, up to 100% iOpinionWeight = iMinOpinionWeight; iOpinionWeight += (iDifference * iAwardPercentage / 100); if (IsDiplomat() || IsSecondaryDiplomat() || GetPlayer()->GetPlayerTraits()->IsDiplomat()) { iOpinionWeight *= /*167*/ GD_INT_GET(OPINION_WEIGHT_THEY_FOILED_OUR_PROPOSAL_DIPLOMAT_MULTIPLIER); iOpinionWeight /= 100; } return AdjustTimedModifier(iOpinionWeight, iDuration, iTurn, TIMED_MODIFIER_STANDARD); } // Supported our proposals! else if (iSupportValue < 0) { int iTurn = 0; int iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_PROPOSAL_NUM_TURNS), GetWorkWithWillingness()); if (IsSupportedOurProposalAndThenFoiledUs(ePlayer)) { iTurn = GetTheyFoiledOurProposalTurn(ePlayer); // The more recent action should carry higher value if Neediness is higher int iDurationMod = (GetNeediness() - 5) * 2; iDurationMod *= GC.getGame().getGameSpeedInfo().getOpinionDurationPercent(); iDurationMod /= 100; if (iDurationMod > 0) { iDuration -= iDurationMod; } } else { iTurn = GetTheySupportedOurProposalTurn(ePlayer); } int iMinOpinionWeight = min(0, /*-10*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_PROPOSAL)); int iMaxOpinionWeight = min(0, /*-60*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_PROPOSAL_MAX)); int iDifference = iMaxOpinionWeight - iMinOpinionWeight; int iAwardPercentage = min(100, (-iSupportValue * max(1, /*2*/ GD_INT_GET(OPINION_WEIGHT_PER_VOTE_PERCENT)))); // 2% of the bonus for each 1% of the vote they contributed, up to 100% iOpinionWeight = iMinOpinionWeight; iOpinionWeight += (iDifference * iAwardPercentage / 100); if (IsDiplomat() || IsSecondaryDiplomat() || GetPlayer()->GetPlayerTraits()->IsDiplomat()) { iOpinionWeight *= /*167*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_PROPOSAL_DIPLOMAT_MULTIPLIER); iOpinionWeight /= 100; } return AdjustTimedModifier(iOpinionWeight, iDuration, iTurn, TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetSupportedMyHostingScore(PlayerTypes ePlayer) { if (!GC.getGame().GetGameLeagues()->GetActiveLeague()) return 0; int iOpinionWeight = 0; if (TheySupportedOurHosting(ePlayer)) { int iMinOpinionWeight = min(0, /*-20*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_HOSTING)); int iMaxOpinionWeight = min(0, /*-70*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_HOSTING_MAX)); int iDifference = iMaxOpinionWeight - iMinOpinionWeight; int iAwardPercentage = min(100, (GetSupportedOurHostingValue(ePlayer) * max(1, /*2*/ GD_INT_GET(OPINION_WEIGHT_PER_VOTE_PERCENT)))); // 2% of the bonus for each 1% of the vote they contributed, up to 100% iOpinionWeight = iMinOpinionWeight; iOpinionWeight += (iDifference * iAwardPercentage / 100); if (IsDiplomat() || IsSecondaryDiplomat() || GetPlayer()->GetPlayerTraits()->IsDiplomat()) { iOpinionWeight *= /*150*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_HOSTING_DIPLOMAT_MULTIPLIER); iOpinionWeight /= 100; } int iDuration = AdjustModifierDuration(/*50*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_HOSTING_NUM_TURNS), GetWorkWithWillingness()); return AdjustTimedModifier(iOpinionWeight, iDuration, GetTheySupportedOurHostingTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetSanctionedUsScore(PlayerTypes ePlayer) { if (!GC.getGame().GetGameLeagues()->GetActiveLeague()) return 0; int iOpinionWeight = 0; if (HasTriedToSanctionUs(ePlayer)) { iOpinionWeight = /*50*/ GD_INT_GET(OPINION_WEIGHT_THEY_SANCTIONED_US); if (IsDiplomat() || IsSecondaryDiplomat() || GetPlayer()->GetPlayerTraits()->IsDiplomat()) { iOpinionWeight *= /*200*/ GD_INT_GET(OPINION_WEIGHT_THEY_SANCTIONED_US_DIPLOMAT_MULTIPLIER); iOpinionWeight /= 100; } if (!HasEverSanctionedUs(ePlayer)) { iOpinionWeight *= 100; iOpinionWeight /= /*200*/ max(GD_INT_GET(OPINION_WEIGHT_THEY_SANCTIONED_US_FAILURE_DIVISOR), 1); } int iDuration = HasEverSanctionedUs(ePlayer) ? AdjustModifierDuration(/*50*/ GD_INT_GET(SANCTIONED_US_TURNS_UNTIL_FORGIVEN), GetMeanness()) : AdjustModifierDuration(GD_INT_GET(SANCTIONED_US_TURNS_UNTIL_FORGIVEN), GetWorkAgainstWillingness()); return AdjustTimedModifier(iOpinionWeight, iDuration, GetTheySanctionedUsTurn(ePlayer), TIMED_MODIFIER_STANDARD); } else if (HasTriedToUnsanctionUs(ePlayer)) { iOpinionWeight = /*-50*/ GD_INT_GET(OPINION_WEIGHT_THEY_UNSANCTIONED_US); if (IsDiplomat() || IsSecondaryDiplomat() || GetPlayer()->GetPlayerTraits()->IsDiplomat()) { iOpinionWeight *= /*200*/ GD_INT_GET(OPINION_WEIGHT_THEY_UNSANCTIONED_US_DIPLOMAT_MULTIPLIER); iOpinionWeight /= 100; } if (!HasEverUnsanctionedUs(ePlayer)) { iOpinionWeight *= 100; iOpinionWeight /= /*200*/ max(GD_INT_GET(OPINION_WEIGHT_THEY_UNSANCTIONED_US_FAILURE_DIVISOR), 1); } int iDuration = HasEverUnsanctionedUs(ePlayer) ? AdjustModifierDuration(/*50*/ GD_INT_GET(UNSANCTIONED_US_TURNS_UNTIL_FORGOTTEN), GetLoyalty()) : AdjustModifierDuration(GD_INT_GET(UNSANCTIONED_US_TURNS_UNTIL_FORGOTTEN), GetWorkWithWillingness()); return AdjustTimedModifier(iOpinionWeight, iDuration, GetTheyUnsanctionedUsTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } ////////////////////////////////////// // VASSALAGE ////////////////////////////////////// /// Opinion weight change based on being vassal int CvDiplomacyAI::GetVassalScore(PlayerTypes ePlayer) const { if (!IsVassal(ePlayer)) return 0; int iOpinionWeight = IsVoluntaryVassalage(ePlayer) ? /*-20*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_WE_ARE_VOLUNTARY_VASSAL) : /*0*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_WE_ARE_VASSAL); return iOpinionWeight; } /// Opinion weight change based on how well we've been treated by our master int CvDiplomacyAI::GetVassalTreatedScore(PlayerTypes ePlayer) { if (!IsVassal(ePlayer)) return 0; int iOpinionWeight = 0; iOpinionWeight += GetVassalDemandScore(ePlayer); iOpinionWeight += GetVassalTheftScore(ePlayer); iOpinionWeight += GetVassalDenouncementScore(ePlayer); iOpinionWeight += GetVassalTaxScore(ePlayer); iOpinionWeight += GetVassalProtectScore(ePlayer); iOpinionWeight += GetVassalTradeRouteScore(ePlayer); iOpinionWeight += GetVassalOpenBordersScore(ePlayer); iOpinionWeight += GetVassalReligionScore(ePlayer); return iOpinionWeight; } int CvDiplomacyAI::GetVassalDemandScore(PlayerTypes ePlayer) { if (!IsVassal(ePlayer)) return 0; int iOpinionWeight = (GetDemandMadeScore(ePlayer) * 100) / /*100*/ max(GD_INT_GET(OPINION_WEIGHT_DEMANDED_WHILE_VASSAL), 1); if (iOpinionWeight > 0 && IsVoluntaryVassalage(ePlayer)) { iOpinionWeight *= /*120*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_VOLUNTARY_VASSAL_MOD); iOpinionWeight /= 100; } return iOpinionWeight; } int CvDiplomacyAI::GetVassalTheftScore(PlayerTypes ePlayer) { if (!IsVassal(ePlayer)) return 0; int iOpinionWeight = 0; if (!IsVoluntaryVassalage(ePlayer)) { int iPlunderScore = (GetTradeRoutesPlunderedScore(ePlayer) * 100) / /*100*/ max(GD_INT_GET(OPINION_WEIGHT_CAPITULATED_VASSAL_PLUNDERED_DIVISOR), 1); int iSpyingScore = (GetTimesRobbedScore(ePlayer) * 100) / /*100*/ max(GD_INT_GET(OPINION_WEIGHT_CAPITULATED_VASSAL_SPYING_DIVISOR), 1); int iCultureBombScore = (GetTimesCultureBombedScore(ePlayer) * 100) / /*100*/ max(GD_INT_GET(OPINION_WEIGHT_CAPITULATED_VASSAL_CULTURE_BOMB_DIVISOR), 1); iOpinionWeight = iPlunderScore + iSpyingScore + iCultureBombScore; } else { iOpinionWeight = (GetTradeRoutesPlunderedScore(ePlayer) + GetTimesRobbedScore(ePlayer) + GetTimesCultureBombedScore(ePlayer)) * /*120*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_VOLUNTARY_VASSAL_MOD); iOpinionWeight /= 100; } return iOpinionWeight; } int CvDiplomacyAI::GetVassalDenouncementScore(PlayerTypes ePlayer) const { if (!IsVassal(ePlayer)) return 0; int iOpinionWeight = 0; if (IsDenouncedByPlayer(ePlayer)) { iOpinionWeight = /*25*/ GD_INT_GET(OPINION_WEIGHT_VASSAL_DENOUNCED_BY_MASTER); if (IsVoluntaryVassalage(ePlayer)) { iOpinionWeight *= /*120*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_VOLUNTARY_VASSAL_MOD); iOpinionWeight /= 100; } } return iOpinionWeight; } int CvDiplomacyAI::GetVassalTaxScore(PlayerTypes ePlayer) { if (!IsVassal(ePlayer)) return 0; int iOpinionWeight = 0; int iGoldTaxedSinceVassalStarted = GetVassalGoldPerTurnTaxedSinceVassalStarted(ePlayer); int iPercentTaxed = iGoldTaxedSinceVassalStarted * 100 / max(GetVassalGoldPerTurnCollectedSinceVassalStarted(ePlayer), 1); // Opinion weight from how much % we've taxed them iOpinionWeight = ((int) pow((float)iPercentTaxed, /*1.5f*/ GD_FLOAT_GET(OPINION_WEIGHT_VASSAL_TAX_EXPONENT))) / max(/*4*/ GD_INT_GET(OPINION_WEIGHT_VASSAL_TAX_DIVISOR), 1); // Opinion weight from current tax % iOpinionWeight += GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetVassalTax(GetID()) * /*50*/ GD_INT_GET(OPINION_WEIGHT_VASSAL_CURRENT_TAX_MODIFIER) / 100; if (iOpinionWeight > 0 && IsVoluntaryVassalage(ePlayer)) { iOpinionWeight *= /*120*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_VOLUNTARY_VASSAL_MOD); iOpinionWeight /= 100; } return iOpinionWeight; } int CvDiplomacyAI::GetVassalProtectScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; int iVassalProtectValue = GetVassalProtectValue(ePlayer) * 100; if (iVassalProtectValue != 0) { int iCurrentValuePercent = iVassalProtectValue > 0 ? iVassalProtectValue / max(GetMaxVassalProtectValue(), 1) : iVassalProtectValue / min(GetMaxVassalFailedProtectValue(), -1); if (iCurrentValuePercent > 0) { if (iVassalProtectValue > 0) { iOpinionWeight = /*-50*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_PROTECT_MAX) * iCurrentValuePercent / 100; } else { iOpinionWeight = /*50*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_FAILED_PROTECT_MAX) * iCurrentValuePercent / 100; if (IsVassal(ePlayer) && IsVoluntaryVassalage(ePlayer)) { iOpinionWeight *= /*120*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_VOLUNTARY_VASSAL_MOD); iOpinionWeight /= 100; } } } } return iOpinionWeight; } int CvDiplomacyAI::GetVassalTradeRouteScore(PlayerTypes ePlayer) { if (!IsVassal(ePlayer)) return 0; int iOpinionWeight = 0; int iMasterTradeRoutesToVassal = GC.getGame().GetGameTrade()->CountNumPlayerConnectionsToPlayer(ePlayer, GetID(), true); if (iMasterTradeRoutesToVassal > 0) { iOpinionWeight = /*-15*/ GD_INT_GET(OPINION_WEIGHT_VASSAL_TRADE_ROUTE); if (iMasterTradeRoutesToVassal > 1) { iOpinionWeight += /*-10*/ GD_INT_GET(OPINION_WEIGHT_VASSAL_TRADE_ROUTE_SUBSEQUENT) * (iMasterTradeRoutesToVassal - 1); } } return iOpinionWeight; } int CvDiplomacyAI::GetVassalOpenBordersScore(PlayerTypes ePlayer) const { if (!IsVassal(ePlayer)) return 0; int iOpinionWeight = 0; if (IsHasOpenBorders(ePlayer)) { iOpinionWeight = /*-5*/ GD_INT_GET(OPINION_WEIGHT_VASSAL_OPEN_BORDERS); } return iOpinionWeight; } int CvDiplomacyAI::GetVassalReligionScore(PlayerTypes ePlayer) { if (GC.getGame().isOption(GAMEOPTION_NO_RELIGION)) return 0; if (!IsVassal(ePlayer)) return 0; int iOpinionWeight = 0; ReligionTypes eVassalOwnedReligion = GetPlayer()->GetReligions()->GetOwnedReligion(); ReligionTypes eVassalStateReligion = GetPlayer()->GetReligions()->GetStateReligion(false); ReligionTypes eMasterOwnedReligion = GET_PLAYER(ePlayer).GetReligions()->GetOwnedReligion(); ReligionTypes eMasterStateReligion = GET_PLAYER(ePlayer).GetReligions()->GetStateReligion(false); // No vassal religion - don't care if (eVassalOwnedReligion == NO_RELIGION && eVassalStateReligion == NO_RELIGION) return 0; // Vassal did not found or conquer if (eVassalOwnedReligion == NO_RELIGION) { // Master founded, and we have their religion if (eMasterOwnedReligion != NO_RELIGION && eMasterOwnedReligion == eVassalStateReligion) { iOpinionWeight += /*-20*/ GD_INT_GET(OPINION_WEIGHT_VASSAL_HAPPILY_ADOPTED_RELIGION); } // Same majority religions else if (eMasterStateReligion != NO_RELIGION && eMasterStateReligion == eVassalStateReligion) { iOpinionWeight += /*-10*/ GD_INT_GET(OPINION_WEIGHT_VASSAL_SAME_STATE_RELIGION); } } // Vassal did found or conquer // Master did not found or conquer, but does have a majority religion - ours else if (eMasterOwnedReligion == NO_RELIGION && eMasterStateReligion != NO_RELIGION && eMasterStateReligion == eVassalOwnedReligion) { iOpinionWeight += /*-40*/ GD_INT_GET(OPINION_WEIGHT_VASSAL_FOUNDER_MASTER_ADOPTED_RELIGION); } // No opinion bonuses if the Holy City was captured if (iOpinionWeight < 0 && IsHolyCityCapturedBy(ePlayer)) return 0; return iOpinionWeight; } int CvDiplomacyAI::GetMasterScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; if (IsMaster(ePlayer)) { iOpinionWeight = /*-40*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_WE_ARE_MASTER); } return iOpinionWeight; } int CvDiplomacyAI::GetTooManyVassalsScore(PlayerTypes ePlayer) const { // Vassals, friends and Defensive Pacts aren't too concerned if (IsVassal(ePlayer) || IsDoFAccepted(ePlayer) || IsHasDefensivePact(ePlayer)) return 0; int iOpinionWeight = 0; int iNumVassals = 0; // Each vassal that we have met contributes +20 to score (each player on a team counts as 1 vassal) for (int iI = 0; iI < MAX_MAJOR_CIVS; iI++) { PlayerTypes eLoopPlayer = (PlayerTypes) iI; if (IsHasMet(eLoopPlayer) && GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsVassal(ePlayer)) { if (GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).IsVassal(GET_PLAYER(ePlayer).getTeam())) { iOpinionWeight += /*20*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_TOO_MANY_VASSALS); iNumVassals++; } } } // First vassal worth nothing if (iNumVassals == 1) return 0; return iOpinionWeight; } int CvDiplomacyAI::GetSameMasterScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (GetPlayer()->IsVassalOfSomeone()) { TeamTypes eMaster = GET_TEAM(GetTeam()).GetMaster(); PlayerTypes eMasterLeader = GET_TEAM(eMaster).getLeaderID(); if (eMasterLeader != NO_PLAYER && eMaster == GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetMaster()) { iOpinionWeight += /*-60*/ GD_INT_GET(OPINION_WEIGHT_SAME_MASTER); bool bWeAreVoluntary = IsVoluntaryVassalage(eMasterLeader); bool bTheyAreVoluntary = GET_PLAYER(ePlayer).GetDiplomacyAI()->IsVoluntaryVassalage(eMasterLeader); if ((!bWeAreVoluntary && bTheyAreVoluntary) || (bWeAreVoluntary && !bTheyAreVoluntary)) { iOpinionWeight *= 100; iOpinionWeight /= /*200*/ max(1, GD_INT_GET(OPINION_WEIGHT_SAME_MASTER_DIFFERENT_SURRENDER_DIVISOR)); } } } return iOpinionWeight; } int CvDiplomacyAI::GetMasterLiberatedMeFromVassalageScore(PlayerTypes ePlayer) const { int iOpinionWeight = 0; if (IsMasterLiberatedMeFromVassalage(ePlayer)) { iOpinionWeight += /*-50*/ GD_INT_GET(OPINION_WEIGHT_MASTER_LIBERATED_ME_FROM_VASSALAGE); } return iOpinionWeight; } int CvDiplomacyAI::GetHappyAboutVassalagePeacefullyRevokedScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsHappyAboutPlayerVassalagePeacefullyRevoked(ePlayer)) { iOpinionWeight = /*-40*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_THEY_PEACEFULLY_REVOKED); int iDuration = AdjustModifierDuration(/*100*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_PEACEFULLY_REVOKED_NUM_TURNS_UNTIL_FORGOTTEN), GetLoyalty()); return AdjustTimedModifier(iOpinionWeight, iDuration, GetVassalagePeacefullyRevokedTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } int CvDiplomacyAI::GetAngryAboutVassalageForcefullyRevokedScore(PlayerTypes ePlayer) { int iOpinionWeight = 0; if (IsAngryAboutPlayerVassalageForcefullyRevoked(ePlayer)) { iOpinionWeight += /*50*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_THEY_FORCIBLY_REVOKED); int iDuration = AdjustModifierDuration(/*100*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_FORCIBLY_REVOKED_NUM_TURNS_UNTIL_FORGIVEN), GetBoldness()); return AdjustTimedModifier(iOpinionWeight, iDuration, GetVassalageForcefullyRevokedTurn(ePlayer), TIMED_MODIFIER_STANDARD); } return iOpinionWeight; } ////////////////////////////////////// // SCENARIO-SPECIFIC ////////////////////////////////////// int CvDiplomacyAI::GetDiploModifiers(PlayerTypes eToPlayer, std::vector& aOpinions) { int iValue = 0; int iModifier = 0; iModifier = GetScenarioModifier1(eToPlayer); if (iModifier != 0) { iValue += iModifier; Opinion kOpinion; kOpinion.m_iValue = iModifier; Localization::String strOpinion = Localization::Lookup("TXT_KEY_SPECIFIC_DIPLO_STRING_1"); kOpinion.m_str = strOpinion.toUTF8(); aOpinions.push_back(kOpinion); } iModifier = GetScenarioModifier2(eToPlayer); if (iModifier != 0) { iValue += iModifier; Opinion kOpinion; kOpinion.m_iValue = iModifier; Localization::String strOpinion = Localization::Lookup("TXT_KEY_SPECIFIC_DIPLO_STRING_2"); kOpinion.m_str = strOpinion.toUTF8(); aOpinions.push_back(kOpinion); } iModifier = GetScenarioModifier3(eToPlayer); if (iModifier != 0) { iValue += iModifier; Opinion kOpinion; kOpinion.m_iValue = iModifier; Localization::String strOpinion = Localization::Lookup("TXT_KEY_SPECIFIC_DIPLO_STRING_3"); kOpinion.m_str = strOpinion.toUTF8(); aOpinions.push_back(kOpinion); } if (MOD_EVENTS_DIPLO_MODIFIERS) { PlayerTypes eFromPlayer = GetID(); CivilizationTypes eFromCiv = m_pPlayer->getCivilizationType(); CvPlayer* pToPlayer = &GET_PLAYER(eToPlayer); CivilizationTypes eToCiv = pToPlayer->getCivilizationType(); for (int iI = 0; iI < GC.getNumDiploModifierInfos(); iI++) { CvDiploModifierInfo* pDiploModifierInfo = GC.getDiploModifierInfo((DiploModifierTypes) iI); if (pDiploModifierInfo && pDiploModifierInfo->isForFromCiv(eFromCiv) && pDiploModifierInfo->isForToCiv(eToCiv)) { iModifier = 0; if (GAMEEVENTINVOKE_VALUE(iModifier, GAMEEVENT_GetDiploModifier, pDiploModifierInfo->GetID(), eFromPlayer, eToPlayer) == GAMEEVENTRETURN_VALUE) { if (iModifier != 0) { iValue += iModifier; Opinion kOpinion; kOpinion.m_iValue = iModifier; Localization::String strOpinion = Localization::Lookup(pDiploModifierInfo->GetDescriptionKey()); strOpinion << iModifier; strOpinion << m_pPlayer->getName(); strOpinion << m_pPlayer->getCivilizationDescription(); strOpinion << pToPlayer->getName(); strOpinion << pToPlayer->getCivilizationDescription(); kOpinion.m_str = strOpinion.toUTF8(); aOpinions.push_back(kOpinion); } } } } } return iValue; } int CvDiplomacyAI::GetScenarioModifier1(PlayerTypes ePlayer) { ICvEngineScriptSystem1* pkScriptSystem = gDLL->GetScriptSystem(); if(pkScriptSystem) { CvLuaArgsHandle args; args->Push(GetID()); args->Push(ePlayer); int iValue = 0; if (LuaSupport::CallAccumulator(pkScriptSystem, "GetScenarioDiploModifier1", args.get(), iValue)) { return iValue; } } return 0; } int CvDiplomacyAI::GetScenarioModifier2(PlayerTypes ePlayer) { ICvEngineScriptSystem1* pkScriptSystem = gDLL->GetScriptSystem(); if(pkScriptSystem) { CvLuaArgsHandle args; args->Push(GetID()); args->Push(ePlayer); int iValue = 0; if (LuaSupport::CallAccumulator(pkScriptSystem, "GetScenarioDiploModifier2", args.get(), iValue)) { return iValue; } } return 0; } int CvDiplomacyAI::GetScenarioModifier3(PlayerTypes ePlayer) { ICvEngineScriptSystem1* pkScriptSystem = gDLL->GetScriptSystem(); if(pkScriptSystem) { CvLuaArgsHandle args; args->Push(GetID()); args->Push(ePlayer); int iValue = 0; if (LuaSupport::CallAccumulator(pkScriptSystem, "GetScenarioDiploModifier3", args.get(), iValue)) { return iValue; } } return 0; } ///////////////////////////////////////////////////////// // Miscellaneous ///////////////////////////////////////////////////////// /// Are we *actively* trying and wanting to liberate a city? bool CvDiplomacyAI::IsTryingToLiberate(CvCity* pCity) { if (!pCity) return false; PlayerTypes ePlayerToLiberate = GetPlayer()->GetPlayerToLiberate(pCity); if (ePlayerToLiberate == NO_PLAYER) return false; // Always try to liberate a city originally owned by a teammate if (IsTeammate(ePlayerToLiberate)) return true; // Backstabbers won't go out of their way to liberate cities if (IsBackstabber()) return false; // If we're going for world conquest, never liberate an original major capital! if (IsGoingForWorldConquest() || IsCloseToWorldConquest()) { if (pCity->IsOriginalMajorCapital()) return false; } // Never liberate a rival Holy City... if (GetPlayer()->GetReligions()->GetStateReligion(false) != NO_RELIGION) { if (pCity->GetCityReligions()->IsHolyCityAnyReligion() && !pCity->GetCityReligions()->IsHolyCityForReligion(GetPlayer()->GetReligions()->GetStateReligion(false))) return false; } bool bIsDiplo = IsGoingForDiploVictory() || GetPlayer()->GetPlayerTraits()->IsDiplomat() || IsDiplomat(); CivOpinionTypes eOpinion = GetCivOpinion(ePlayerToLiberate); CivApproachTypes eApproach = GetCivApproach(ePlayerToLiberate); if (GET_PLAYER(ePlayerToLiberate).isMajorCiv()) { if (GET_PLAYER(ePlayerToLiberate).isAlive()) { // Hate them? Don't consider liberating! if (eOpinion <= CIV_OPINION_ENEMY || eApproach <= CIV_APPROACH_GUARDED || IsUntrustworthy(ePlayerToLiberate)) return false; } //diplo civs will try to liberate friendly capitals/holy cities and dead players to get a big bonus if (bIsDiplo) { if (!GET_PLAYER(ePlayerToLiberate).isAlive()) { return true; } else if (eOpinion >= CIV_OPINION_FRIEND && (pCity->GetCityReligions()->IsHolyCityAnyReligion() || pCity->IsOriginalMajorCapital())) { return true; } } } else if (GET_PLAYER(ePlayerToLiberate).isMinorCiv()) { bool bHasLiberateQuest = false; bool bWantsLiberateQuest = false; // Are there any liberation quests for (int iMinorLoop = MAX_MAJOR_CIVS; iMinorLoop < MAX_CIV_PLAYERS; iMinorLoop++) { PlayerTypes eMinor = (PlayerTypes)iMinorLoop; if (IsPlayerValid(eMinor) && GET_PLAYER(eMinor).isMinorCiv() && !IsAtWar(eMinor) && GetCivApproach(eMinor) > CIV_APPROACH_HOSTILE) { CvPlayer* pMinor = &GET_PLAYER(eMinor); CvMinorCivAI* pMinorCivAI = pMinor->GetMinorCivAI(); if (pMinorCivAI->IsActiveQuestForPlayer(GetID(), MINOR_CIV_QUEST_LIBERATION)) { PlayerTypes eQuestTarget = (PlayerTypes)pMinorCivAI->GetQuestData3(GetID(), MINOR_CIV_QUEST_LIBERATION); int iX = pMinorCivAI->GetQuestData1(GetID(), MINOR_CIV_QUEST_LIBERATION); int iY = pMinorCivAI->GetQuestData2(GetID(), MINOR_CIV_QUEST_LIBERATION); if (eQuestTarget == ePlayerToLiberate && pCity->getX() == iX && pCity->getY() == iY) { bHasLiberateQuest = true; if (eApproach == CIV_APPROACH_FRIENDLY || pMinorCivAI->GetAlly() == GetID()) bWantsLiberateQuest = true; break; } } } } //if there is a quest to liberate, then diplo civs or civs that care about the CS with the quest will liberate if (bHasLiberateQuest && (bIsDiplo || bWantsLiberateQuest)) { return true; } } //policy boosts liberation? Will try to go after cities to liberate, if friendly/diplomatic, or if it'd be a resurrection if (GetPlayer()->GetPlayerPolicies()->GetNumericModifier(POLICYMOD_LIBERATION_BONUS) > 0) { if ((bIsDiplo && eOpinion > CIV_OPINION_NEUTRAL) || eOpinion >= CIV_OPINION_FRIEND || !GET_PLAYER(ePlayerToLiberate).isAlive()) { return true; } } return false; } /// Will this player liberate a Major's City that it now owns? /// Only used for CvPlayerAI::AI_conquerCity() bool CvDiplomacyAI::DoPossibleMajorLiberation(CvCity* pCity) { if (!pCity) return false; PlayerTypes ePlayerToLiberate = GetPlayer()->GetPlayerToLiberate(pCity); if (ePlayerToLiberate == NO_PLAYER) return false; bool bLiberate = false; CivOpinionTypes eOpinion = GetCivOpinion(ePlayerToLiberate); CivApproachTypes eApproach = GetCivApproach(ePlayerToLiberate); PlayerTypes eOldOwner = pCity->getPreviousOwner(); // If we're going for world conquest, never liberate an original major capital! if (IsGoingForWorldConquest() || IsCloseToWorldConquest()) { if (pCity->IsOriginalMajorCapital()) return false; } // Never liberate a rival Holy City... if (GetPlayer()->GetReligions()->GetStateReligion(false) != NO_RELIGION) { if (pCity->GetCityReligions()->IsHolyCityAnyReligion() && !pCity->GetCityReligions()->IsHolyCityForReligion(GetPlayer()->GetReligions()->GetStateReligion(false))) return false; } // Hate them? Don't consider liberating! if (GET_PLAYER(ePlayerToLiberate).isAlive()) { if (eOpinion <= CIV_OPINION_ENEMY || eApproach <= CIV_APPROACH_GUARDED || IsUntrustworthy(ePlayerToLiberate)) return false; } // Empire unhappy? if (GetPlayer()->IsEmpireUnhappy()) { if (eOpinion >= CIV_OPINION_FRIEND) { bLiberate = true; } else if (eOpinion >= CIV_OPINION_NEUTRAL) { // Very unhappy and war weary? Liberating a city will help. if (GetPlayer()->IsEmpireVeryUnhappy() && GetPlayer()->GetUnhappinessFromWarWeariness() > 0) { bLiberate = true; } } } if (!bLiberate) { bool bBadWarDecision = eOldOwner != NO_PLAYER && eOldOwner != BARBARIAN_PLAYER && IsAtWar(eOldOwner) && (GetWarState(eOldOwner) <= WAR_STATE_TROUBLED || GetWarScore(eOldOwner) <= -10) && (!GET_PLAYER(ePlayerToLiberate).isAlive() || GET_PLAYER(ePlayerToLiberate).IsAtWarWith(eOldOwner)); // Player we'd be liberating is alive if (GET_PLAYER(ePlayerToLiberate).isAlive()) { // DP, and we're both at war with the old owner? if (IsHasDefensivePact(ePlayerToLiberate) && IsAtWar(eOldOwner) && GET_PLAYER(ePlayerToLiberate).IsAtWarWith(eOldOwner)) { bLiberate = true; } // Coop war against the old owner? else if (GET_PLAYER(eOldOwner).isMajorCiv() && GetCoopWarState(ePlayerToLiberate, eOldOwner) >= COOP_WAR_STATE_PREPARING) { bLiberate = true; } // They majorly liberated us previously? else if (IsLiberator(ePlayerToLiberate, false, true)) { bLiberate = true; } // We majorly liberated them previously? else if (GET_PLAYER(ePlayerToLiberate).GetDiplomacyAI()->IsLiberator(GetID(), true, true)) { bLiberate = true; } // Do we really like them? else if (eOpinion == CIV_OPINION_ALLY || GetDoFType(ePlayerToLiberate) >= DOF_TYPE_ALLIES || GetCoopWarAgreementScore(ePlayerToLiberate) > 1) { bLiberate = true; } // They liberated some of our cities before or vice versa, and at least Favorable? else if ((GetNumCitiesLiberatedBy(ePlayerToLiberate) > 0 || GET_PLAYER(ePlayerToLiberate).GetDiplomacyAI()->GetNumCitiesLiberatedBy(GetID()) > 0) && eOpinion >= CIV_OPINION_FAVORABLE) { bLiberate = !bBadWarDecision; } // Have policies that boost liberation? else if (GetPlayer()->GetPlayerPolicies()->GetNumericModifier(POLICYMOD_LIBERATION_BONUS) > 0) { bLiberate = !bBadWarDecision; } if (!bLiberate) { // Friends? if ((IsDoFAccepted(ePlayerToLiberate) && !IsWantsToEndDoFWithPlayer(ePlayerToLiberate)) || IsWantsDoFWithPlayer(ePlayerToLiberate) || IsHasDefensivePact(ePlayerToLiberate) || eOpinion >= CIV_OPINION_FRIEND || eApproach == CIV_APPROACH_FRIENDLY) { // Going for diplo victory? if (IsGoingForDiploVictory() || IsCloseToDiploVictory()) { bLiberate = true; } // Do we really like them? else if (eOpinion >= CIV_OPINION_FRIEND && eApproach == CIV_APPROACH_FRIENDLY) { bLiberate = !bBadWarDecision; } // Going for culture victory, and we're okay for influence/victory against this guy? else if (IsGoingForCultureVictory()) { bLiberate = !bBadWarDecision && GetVictoryDisputeLevel(ePlayerToLiberate) < DISPUTE_LEVEL_STRONG && GetVictoryBlockLevel(ePlayerToLiberate) < BLOCK_LEVEL_FIERCE && !IsEndgameAggressiveTo(ePlayerToLiberate); if (bLiberate && IsCloseToCultureVictory()) { if (GetPlayer()->GetCulture()->GetInfluenceLevel(ePlayerToLiberate) < INFLUENCE_LEVEL_INFLUENTIAL) { if (GetPlayer()->GetCulture()->GetInfluenceLevel(ePlayerToLiberate) < INFLUENCE_LEVEL_POPULAR) { bLiberate = false; } else if (GetPlayer()->GetCulture()->GetInfluenceTrend(ePlayerToLiberate) != INFLUENCE_TREND_RISING) { bLiberate = false; } } } } } } } // Player we'd be liberating is dead (resurrection!) else if (!GET_PLAYER(ePlayerToLiberate).isHuman(ISHUMAN_AI_DIPLOMACY)) { // Going for diplo victory? Resurrect for super diplo bonuses! if (IsGoingForDiploVictory() || IsCloseToDiploVictory()) { bLiberate = true; } // They majorly liberated us previously? else if (IsLiberator(ePlayerToLiberate, false, true)) { bLiberate = true; } // We majorly liberated them previously? else if (GET_PLAYER(ePlayerToLiberate).GetDiplomacyAI()->IsLiberator(GetID(), true, true)) { bLiberate = true; } // We had good relations? else if (GetDoFType(ePlayerToLiberate) >= DOF_TYPE_ALLIES || GetCoopWarAgreementScore(ePlayerToLiberate) > 0) { bLiberate = true; } else { bLiberate = !bBadWarDecision; } } } if (bLiberate) { GetPlayer()->DoLiberatePlayer(ePlayerToLiberate, pCity->GetID(), false, false); } return bLiberate; } /// Is this a bad target to steal from? bool CvDiplomacyAI::IsBadTheftTarget(PlayerTypes ePlayer, TheftTypes eTheftType, const CvPlot* pPlot /* = NULL */) { // Failsafe if (!pPlot && (eTheftType == THEFT_TYPE_TRADE_ROUTE || eTheftType == THEFT_TYPE_PLOT)) return true; if (ePlayer == NO_PLAYER || ePlayer == BARBARIAN_PLAYER || ePlayer == GetID() || !GET_PLAYER(ePlayer).isAlive()) return false; if (IsAtWar(ePlayer)) return false; // Handle extra logic for humans if this function is called by an automated worker/recommender bool bHuman = GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY); // Handle minors here (only citadels and plots are applicable) if (GET_PLAYER(ePlayer).isMinorCiv()) { switch (eTheftType) { case THEFT_TYPE_CULTURE_BOMB: if ((!bHuman && GetCivApproach(ePlayer) == CIV_APPROACH_FRIENDLY) || GET_PLAYER(ePlayer).GetMinorCivAI()->GetAlly() == GetID()) { return true; } if (bHuman && GET_PLAYER(ePlayer).GetMinorCivAI()->IsFriends(GetID())) { return true; } break; case THEFT_TYPE_PLOT: // America UA if (bHuman && GET_PLAYER(ePlayer).GetMinorCivAI()->IsFriends(GetID())) { return true; } // Steal Natural Wonders and other teams' embassies, the City-State's feelings be damned! if (pPlot->IsNaturalWonder()) { return false; } if (pPlot->IsImprovementEmbassy() && GET_PLAYER(pPlot->GetPlayerThatBuiltImprovement()).getTeam() != GetTeam()) { return false; } if ((!bHuman && GetCivApproach(ePlayer) == CIV_APPROACH_FRIENDLY) || GET_PLAYER(ePlayer).GetMinorCivAI()->GetAlly() == GetID()) { return true; } break; case THEFT_TYPE_CONVERSION: case THEFT_TYPE_ARTIFACT: case THEFT_TYPE_SPY: case THEFT_TYPE_TRADE_ROUTE: case THEFT_TYPE_EMBASSY: break; } return false; } // Exception for religious conversion: Not a bad target if they haven't created a religion and haven't asked us to stop. if (eTheftType == THEFT_TYPE_CONVERSION && !GET_PLAYER(ePlayer).GetReligions()->OwnsReligion() && !m_abAskedNotToConvert[ePlayer]) return false; // If any of the below conditions are true, never steal from this player if (IsTeammate(ePlayer)) return true; if (IsDoFAccepted(ePlayer)) return true; if (IsLiberator(ePlayer, false, false)) return true; if (IsHasDefensivePact(ePlayer)) return true; CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); CivApproachTypes eTrueApproach = GetCivApproach(ePlayer); CivApproachTypes eSurfaceApproach = GetSurfaceApproach(ePlayer); if (!bHuman) { if (eOpinion == CIV_OPINION_ALLY) return true; if (eTrueApproach == CIV_APPROACH_AFRAID) return true; if (eOpinion >= CIV_OPINION_FRIEND && eTrueApproach >= CIV_APPROACH_FRIENDLY) return true; if (eOpinion >= CIV_OPINION_COMPETITOR && eTrueApproach >= CIV_APPROACH_NEUTRAL && GetDoFType(ePlayer) >= DOF_TYPE_ALLIES) return true; } // Morocco can plunder trade routes with no diplo penalty if the plot is not visible to the other team, so use this // We want to know whether they can still see the plot *after* we plunder the caravan, so check for > 1 bool bPlotIsVisibleToOtherTeam = false; if (eTheftType == THEFT_TYPE_TRADE_ROUTE) { bPlotIsVisibleToOtherTeam = (pPlot->GetKnownVisibilityCount(GET_PLAYER(ePlayer).getTeam()) > 1); } // Additional conditions depend on the type of theft we'd be doing switch (eTheftType) { case THEFT_TYPE_CULTURE_BOMB: { if (IsVassal(ePlayer)) return true; if (bHuman) return false; if (GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeExpansionPromise(GetID())) return true; if (IsPlayerMoveTroopsRequestAccepted(ePlayer)) return true; // Only steal if we're hostile or covet their lands if (eSurfaceApproach <= CIV_APPROACH_GUARDED || GetLandDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) return false; return true; } case THEFT_TYPE_CONVERSION: { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeNoConvertPromise(GetID())) return true; if (!bHuman && IsVassal(ePlayer)) return true; return false; } case THEFT_TYPE_ARTIFACT: { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeNoDiggingPromise(GetID())) return true; if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).IsAllowsOpenBordersToTeam(GetTeam())) return true; if (!bHuman && IsVassal(ePlayer)) return true; return false; } case THEFT_TYPE_SPY: { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeSpyPromise(GetID())) return true; if (!bHuman && IsVassal(ePlayer) && GetVassalTreatmentLevel(ePlayer) == VASSAL_TREATMENT_CONTENT) return true; return false; } case THEFT_TYPE_TRADE_ROUTE: // Morocco UA { if (bHuman) return false; if (eTrueApproach == CIV_APPROACH_FRIENDLY) return true; if (eTrueApproach == CIV_APPROACH_NEUTRAL && eOpinion >= CIV_OPINION_FAVORABLE) return true; if (IsVassal(ePlayer) && GetVassalTreatmentLevel(ePlayer) == VASSAL_TREATMENT_CONTENT) return true; if (IsPlayerMoveTroopsRequestAccepted(ePlayer)) return true; if (eSurfaceApproach == CIV_APPROACH_FRIENDLY && bPlotIsVisibleToOtherTeam) return true; if (eSurfaceApproach == CIV_APPROACH_NEUTRAL && eOpinion >= CIV_OPINION_FAVORABLE && bPlotIsVisibleToOtherTeam) return true; return false; } case THEFT_TYPE_PLOT: // America UA { if (GET_PLAYER(ePlayer).GetDiplomacyAI()->MadeExpansionPromise(GetID())) return true; if (IsPlayerMoveTroopsRequestAccepted(ePlayer)) return true; if (IsVassal(ePlayer)) return true; return false; } case THEFT_TYPE_EMBASSY: // America UA or Culture Bomb { if (!bHuman && IsVassal(ePlayer)) { // Stealing an embassy from your master is purely spiteful, so require significant mistreatment to allow it VassalTreatmentTypes eVassalTreatment = GetVassalTreatmentLevel(ePlayer); if (eVassalTreatment <= VASSAL_TREATMENT_DISAGREE) return true; if (eVassalTreatment == VASSAL_TREATMENT_MISTREATED && (IsLiberator(ePlayer, false, false) || IsVoluntaryVassalage(ePlayer))) return true; } } } return false; } /// How many players that we're Competitive or more with is ePlayer at war with? int CvDiplomacyAI::GetNumOurEnemiesPlayerAtWarWith(PlayerTypes ePlayer) const { int iAtWarCount = 0; TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam(); for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; TeamTypes eLoopTeam = GET_PLAYER(eLoopPlayer).getTeam(); if (IsPlayerValid(eLoopPlayer)) { if (IsAtWar(eLoopPlayer)) { if (GET_TEAM(eTeam).isAtWar(eLoopTeam)) { iAtWarCount++; } } } } return iAtWarCount; } /// AI is doing something with a Minor civ void CvDiplomacyAI::LogMinorCivGiftTile(PlayerTypes eMinor) { if (GC.getLogging() && GC.getAILogging()) { CvString strLogName = GC.getDiploMinorLogFileName(GetPlayer()); CvString playerName = GetPlayer()->getCivilizationShortDescription(); CvString strOutBuf; CvString strBaseString; CvString otherPlayerName; CvString strTemp; FILogFile* pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Turn number strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); // Our Name strBaseString += playerName; // Their Name otherPlayerName = GET_PLAYER(eMinor).getCivilizationShortDescription(); strBaseString += ", " + otherPlayerName; strOutBuf = strBaseString; // Gift amount strTemp.Format("Tile Improvement"); strOutBuf += ", " + strTemp; LogGrandStrategy(strOutBuf); // City State type strOutBuf += ", "; strOutBuf += GC.getMinorCivInfo(GET_PLAYER(eMinor).GetMinorCivAI()->GetMinorCivType())->GetType(); pLog->Msg(strOutBuf); } } void CvDiplomacyAI::LogMinorCivGiftGold(PlayerTypes eMinor, int iOldFriendship, int iGold, bool bSaving, bool bWantQuickBoost, PlayerTypes ePlayerTryingToPass) { if(GC.getLogging() && GC.getAILogging()) { CvString strLogName = GC.getDiploMinorLogFileName(GetPlayer()); CvString playerName = GetPlayer()->getCivilizationShortDescription(); CvString strOutBuf; CvString strBaseString; CvString otherPlayerName; CvString strTemp; FILogFile* pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Turn number strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); // Our Name strBaseString += playerName; // Their Name otherPlayerName = GET_PLAYER(eMinor).getCivilizationShortDescription(); strBaseString += ", " + otherPlayerName; strOutBuf = strBaseString; // Gift amount strTemp.Format("Gold Gift: %d", iGold); strOutBuf += ", " + strTemp; strTemp.Format("Friendship: %d to %d", iOldFriendship, GET_PLAYER(eMinor).GetMinorCivAI()->GetEffectiveFriendshipWithMajor(GetID())); strOutBuf += ", " + strTemp; LogGrandStrategy(strOutBuf); // City State type strOutBuf += ", "; strOutBuf += GC.getMinorCivInfo(GET_PLAYER(eMinor).GetMinorCivAI()->GetMinorCivType())->GetType(); // Saving Gold for Gift if(bSaving) strOutBuf += ", (SAVING) "; // Wants a quick boost if(bWantQuickBoost) strOutBuf += ", Boost! "; // Trying to pass up a player! if(ePlayerTryingToPass != NO_PLAYER) { strOutBuf += ", Passing "; strOutBuf += GET_PLAYER(ePlayerTryingToPass).getCivilizationShortDescription(); } pLog->Msg(strOutBuf); } } /// AI is doing something with a Minor civ void CvDiplomacyAI::LogMinorCivBullyGold(PlayerTypes eMinor, int iOldFriendshipTimes100, int iNewFriendshipTimes100, int iGold, bool bSuccess, int iBullyMetricScore) { if(GC.getLogging() && GC.getAILogging()) { CvString strLogName = GC.getDiploMinorLogFileName(GetPlayer()); CvString playerName = GetPlayer()->getCivilizationShortDescription(); CvString strOutBuf; CvString strBaseString; CvString otherPlayerName; CvString strTemp; FILogFile* pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Turn number strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); // Our Name strBaseString += playerName; // Their Name otherPlayerName = GET_PLAYER(eMinor).getCivilizationShortDescription(); strBaseString += ", " + otherPlayerName; strOutBuf = strBaseString; // Gold tribute strTemp.Format("Gold Bully: %d", iGold); strOutBuf += ", " + strTemp; strTemp.Format("Friendship: %d to %d", iOldFriendshipTimes100 / 100, iNewFriendshipTimes100 / 100); strOutBuf += ", " + strTemp; LogGrandStrategy(strOutBuf); // City State type strOutBuf += ", "; strOutBuf += GC.getMinorCivInfo(GET_PLAYER(eMinor).GetMinorCivAI()->GetMinorCivType())->GetType(); // Did it work? if(bSuccess) { strOutBuf += ", "; strOutBuf += "Success!"; } else { strOutBuf += ", "; strOutBuf += "Failure"; } // Append the bully metric score strTemp.Format("Bully Score: %d", iBullyMetricScore); strOutBuf += ", " + strTemp; pLog->Msg(strOutBuf); } } void CvDiplomacyAI::LogMinorCivBullyHeavy(PlayerTypes eMinor, int iOldFriendshipTimes100, int iNewFriendshipTimes100, YieldTypes eYield, int iValue, bool bSuccess, int iBullyMetricScore) { if (GC.getLogging() && GC.getAILogging()) { CvString strLogName = GC.getDiploMinorLogFileName(GetPlayer()); CvString playerName = GetPlayer()->getCivilizationShortDescription(); CvString strOutBuf; CvString strBaseString; CvString otherPlayerName; CvString strTemp; FILogFile* pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Turn number strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); // Our Name strBaseString += playerName; // Their Name otherPlayerName = GET_PLAYER(eMinor).getCivilizationShortDescription(); strBaseString += ", " + otherPlayerName; strOutBuf = strBaseString; // Heavy tribute strTemp.Format("Heavy Tribute Bully: %d %s", iValue, GC.getYieldInfo(eYield)->GetDescription()); strOutBuf += ", " + strTemp; strTemp.Format("Friendship: %d to %d", iOldFriendshipTimes100 / 100, iNewFriendshipTimes100 / 100); strOutBuf += ", " + strTemp; LogGrandStrategy(strOutBuf); // City State type strOutBuf += ", "; strOutBuf += GC.getMinorCivInfo(GET_PLAYER(eMinor).GetMinorCivAI()->GetMinorCivType())->GetType(); // Did it work? if (bSuccess) { strOutBuf += ", "; strOutBuf += "Success!"; } else { strOutBuf += ", "; strOutBuf += "Failure"; } // Append the bully metric score strTemp.Format("Bully Score: %d", iBullyMetricScore); strOutBuf += ", " + strTemp; pLog->Msg(strOutBuf); } } void CvDiplomacyAI::LogMinorCivBullyUnit(PlayerTypes eMinor, int iOldFriendshipTimes100, int iNewFriendshipTimes100, UnitTypes eUnit, bool bSuccess, int iBullyMetricScore) { if(GC.getLogging() && GC.getAILogging()) { CvString strLogName = GC.getDiploMinorLogFileName(GetPlayer()); CvString playerName = GetPlayer()->getCivilizationShortDescription(); CvString strOutBuf; CvString strBaseString; CvString otherPlayerName; CvString strTemp; FILogFile* pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Turn number strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); // Our Name strBaseString += playerName; // Their Name otherPlayerName = GET_PLAYER(eMinor).getCivilizationShortDescription(); strBaseString += ", " + otherPlayerName; strOutBuf = strBaseString; // Unit tribute strTemp.Format("Unit Bully: %s", eUnit == (UnitTypes) GC.getInfoTypeForString("UNIT_WORKER") ? "Worker" : "Other unit"); strOutBuf += ", " + strTemp; strTemp.Format("Friendship: %d to %d", iOldFriendshipTimes100 / 100, iNewFriendshipTimes100 / 100); strOutBuf += ", " + strTemp; LogGrandStrategy(strOutBuf); // City State type strOutBuf += ", "; strOutBuf += GC.getMinorCivInfo(GET_PLAYER(eMinor).GetMinorCivAI()->GetMinorCivType())->GetType(); // Did it work? if(bSuccess) { strOutBuf += ", "; strOutBuf += "Success!"; } else { strOutBuf += ", "; strOutBuf += "Failure"; } // Append the bully metric score strTemp.Format("Bully Score: %d", iBullyMetricScore); strOutBuf += ", " + strTemp; pLog->Msg(strOutBuf); } } /// A quest was given to us, so log it void CvDiplomacyAI::LogMinorCivQuestReceived(PlayerTypes eMinor, int iOldFriendshipTimes100, int iNewFriendshipTimes100, MinorCivQuestTypes eType) { if(GC.getLogging() && GC.getAILogging()) { CvString strLogName = GC.getDiploMinorLogFileName(GetPlayer()); CvString playerName = GetPlayer()->getCivilizationShortDescription(); CvString strOutBuf; CvString strBaseString; CvString otherPlayerName; CvString strTemp; FILogFile* pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Turn number strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); // Our Name strBaseString += playerName; // Their Name otherPlayerName = GET_PLAYER(eMinor).getCivilizationShortDescription(); strBaseString += ", " + otherPlayerName; strOutBuf = strBaseString; // Quest Info strTemp = "RECEIVED QUEST"; strOutBuf += ", " + strTemp; LogMinorCivQuestType(strOutBuf, eType); // Friendship Change strTemp.Format("Friendship: %d to %d", iOldFriendshipTimes100 / 100, iNewFriendshipTimes100 / 100); strOutBuf += ", " + strTemp; // City State type strOutBuf += ", "; strOutBuf += GC.getMinorCivInfo(GET_PLAYER(eMinor).GetMinorCivAI()->GetMinorCivType())->GetType(); pLog->Msg(strOutBuf); } } /// We finished a quest! Now log it void CvDiplomacyAI::LogMinorCivQuestFinished(PlayerTypes eMinor, int iOldFriendshipTimes100, int iNewFriendshipTimes100, MinorCivQuestTypes eType) { if(GC.getLogging() && GC.getAILogging()) { CvString strLogName = GC.getDiploMinorLogFileName(GetPlayer()); CvString playerName = GetPlayer()->getCivilizationShortDescription(); CvString strOutBuf; CvString strBaseString; CvString otherPlayerName; CvString strTemp; FILogFile* pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Turn number strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); // Our Name strBaseString += playerName; // Their Name otherPlayerName = GET_PLAYER(eMinor).getCivilizationShortDescription(); strBaseString += ", " + otherPlayerName; strOutBuf = strBaseString; // Quest Info strTemp = "FINISHED QUEST"; strOutBuf += ", " + strTemp; LogMinorCivQuestType(strOutBuf, eType); // Friendship Change strTemp.Format("Friendship: %d to %d", iOldFriendshipTimes100 / 100, iNewFriendshipTimes100 / 100); strOutBuf += ", " + strTemp; // City State type strOutBuf += ", "; strOutBuf += GC.getMinorCivInfo(GET_PLAYER(eMinor).GetMinorCivAI()->GetMinorCivType())->GetType(); pLog->Msg(strOutBuf); } } /// A quest became obsolete and was cancelled, so log it void CvDiplomacyAI::LogMinorCivQuestCancelled(PlayerTypes eMinor, int iOldFriendshipTimes100, int iNewFriendshipTimes100, MinorCivQuestTypes eType) { if(GC.getLogging() && GC.getAILogging()) { CvString strLogName = GC.getDiploMinorLogFileName(GetPlayer()); CvString playerName = GetPlayer()->getCivilizationShortDescription(); CvString strOutBuf; CvString strBaseString; CvString otherPlayerName; CvString strTemp; FILogFile* pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Turn number strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); // Our Name strBaseString += playerName; // Their Name otherPlayerName = GET_PLAYER(eMinor).getCivilizationShortDescription(); strBaseString += ", " + otherPlayerName; strOutBuf = strBaseString; // Quest Info strTemp = "cancelled"; strOutBuf += ", " + strTemp; LogMinorCivQuestType(strOutBuf, eType); // Friendship Change strTemp.Format("Friendship: %d to %d", iOldFriendshipTimes100 / 100, iNewFriendshipTimes100 / 100); strOutBuf += ", " + strTemp; // City State type strOutBuf += ", "; strOutBuf += GC.getMinorCivInfo(GET_PLAYER(eMinor).GetMinorCivAI()->GetMinorCivType())->GetType(); pLog->Msg(strOutBuf); } } /// A minor civ was bought, and possession changed void CvDiplomacyAI::LogMinorCivBuyout(PlayerTypes eMinor, int iGoldPaid, bool bSaving) { if(GC.getLogging() && GC.getAILogging()) { CvString strLogName = GC.getDiploMinorLogFileName(GetPlayer()); CvString playerName = GetPlayer()->getCivilizationShortDescription(); CvString strOutBuf; CvString strBaseString; CvString otherPlayerName; CvString strTemp; FILogFile* pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Turn number strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); // Our Name strBaseString += playerName; // Their Name otherPlayerName = GET_PLAYER(eMinor).getCivilizationShortDescription(); strBaseString += ", " + otherPlayerName; strOutBuf = strBaseString; strTemp.Format("BUYOUT: %d", iGoldPaid); strOutBuf += ", " + strTemp; // Spacing strOutBuf += ", , "; // City State type strOutBuf += ", "; strOutBuf += GC.getMinorCivInfo(GET_PLAYER(eMinor).GetMinorCivAI()->GetMinorCivType())->GetType(); // Saving Gold for Gift if(bSaving) strOutBuf += ", (SAVING) "; pLog->Msg(strOutBuf); } } /// Deal to renew std::vector CvDiplomacyAI::GetDealsToRenew(PlayerTypes eOtherPlayer, bool bOnlyCheckedDeals) { std::vector renewDeals; if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return renewDeals; CvGameDeals& kGameDeals = GC.getGame().GetGameDeals(); return kGameDeals.GetRenewableDealsWithPlayer(eOtherPlayer, GetID(), 50, bOnlyCheckedDeals); } /// Deal to renew void CvDiplomacyAI::CancelRenewDeal(PlayerTypes eOtherPlayer, RenewalReason eReason, bool bJustLogging, CvDeal* pPassDeal, bool bOnlyCheckedDeals, bool bSendNetworkMessage) { if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) return; std::vector pRenewalDeals; if (pPassDeal != NULL) { pRenewalDeals.push_back(pPassDeal); } else { pRenewalDeals = GetDealsToRenew(eOtherPlayer, bOnlyCheckedDeals); } if (pRenewalDeals.size() <= 0) return; for (uint i = 0; i < pRenewalDeals.size(); i++) { CvDeal* pRenewalDeal = pRenewalDeals[i]; if (!bJustLogging) { // network message for multiplayer. we have to use sendNetDealAccepted because we don't have a specific function to send the information that a deal has not been renewed. see CvDllDealAI::DoAcceptedDeal if (bSendNetworkMessage && GET_PLAYER(eOtherPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) { CvInterfacePtr pDllDeal = GC.WrapDealPointer(pRenewalDeal); gDLL->sendNetDealAccepted(GetPlayer()->GetID(), eOtherPlayer, pDllDeal.get(), INT_MAX, INT_MAX, INT_MAX); // processing of the deal will be done via the network message continue; } TradedItemList::iterator itemIter; for (itemIter = pRenewalDeal->m_TradedItems.begin(); itemIter != pRenewalDeal->m_TradedItems.end(); ++itemIter) { //OutputDebugString("Cleared item from expired renewal deal \n"); //If we checked for renewal, we don't need to remove items, as we already did it. GC.getGame().GetGameDeals().DoEndTradedItem(&*itemIter, pRenewalDeal->GetOtherPlayer(itemIter->m_eFromPlayer), false, pRenewalDeal->m_bCheckedForRenewal); } } pRenewalDeal->m_bConsideringForRenewal = false; //log it for me bby if (GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString playerName; CvString otherPlayerName; CvString strDesc; CvString strLogName; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if (GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_TradeAgreements_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_TradeAgreements_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName + ", "; otherPlayerName = GET_PLAYER(eOtherPlayer).getCivilizationShortDescription(); strOutBuf = strBaseString + ", * TRADE RENEWAL CANCELED*, " + otherPlayerName; TradedItemList::iterator itemIter; for (itemIter = pRenewalDeal->m_TradedItems.begin(); itemIter != pRenewalDeal->m_TradedItems.end(); ++itemIter) { CvString strItems; strItems.Format(",ItemType: %d, ", (int)itemIter->m_eItemType); strOutBuf += strItems; } CvString strReason; switch (eReason) { case NO_REASON: strReason.Format(",REASON: No Reason Given"); strOutBuf += strReason; break; case REASON_NO_GPT: strReason.Format(",REASON: Invalid Items"); strOutBuf += strReason; break; case REASON_NO_DEAL: strReason.Format(",REASON: No Deal Found"); strOutBuf += strReason; break; case REASON_CANNOT_COMPROMISE: strReason.Format(",REASON: Cannot Re-negotiate with AI"); strOutBuf += strReason; break; case REASON_HUMAN_REJECTION: strReason.Format(",REASON: Human Rejection"); strOutBuf += strReason; break; case REASON_BETTER_RENEWAL_CHOICE: strReason.Format(",REASON: Better Renewal Choice"); strOutBuf += strReason; break; } pLog->Msg(strOutBuf); } //pRenewalDeal->ClearItems(); } } // ************************************ // LOGGING (PRIVATE FUNCTIONS) // ************************************ // ----------------------------------------------------------------------------------------------- // //////////////////////////////////// // Estimations of other players' tendencies // //////////////////////////////////// // Recursive: Because of how Firaxis programmed the diplomacy flavors, a separate estimation function is necessary for each of them, plus one for other flavors // For Default Victory Focus, just grab the other leader's personality instead of estimating ... // ... humans can learn this by studying opinion modifiers whenever the AI's approach isn't FRIENDLY (or with Transparent Diplomacy enabled) /// How much do we estimate this other leader gets angry when another player is competing for Victory? int CvDiplomacyAI::EstimateVictoryCompetitiveness(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetVictoryCompetitiveness(); } // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return 5; } // AI? Use the other player's base XML value. return range(GET_PLAYER(ePlayer).getLeaderInfo().GetVictoryCompetitiveness(), 1, 10); } /// How much do we estimate this other leader gets angry when they're beaten to a World Wonder? int CvDiplomacyAI::EstimateWonderCompetitiveness(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetWonderCompetitiveness(); } // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return 5; } // AI? Use the other player's base XML value. // WonderCompetitiveness can be set to -12 to guarantee that the AI will pursue Culture Victory if possible. This is treated as a 10. int iWonderCompetitiveness = GET_PLAYER(ePlayer).getLeaderInfo().GetWonderCompetitiveness(); if (iWonderCompetitiveness == -12) iWonderCompetitiveness = 10; return range(iWonderCompetitiveness, 1, 10); } /// How much do we estimate this other leader gets angry when another player is befriending "their" minor civs? int CvDiplomacyAI::EstimateMinorCivCompetitiveness(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetMinorCivCompetitiveness(); } // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return 5; } // AI? Use the other player's base XML value. // MinorCivCompetitiveness can be set to -12 to guarantee that the AI will pursue Diplomatic Victory if possible. This is treated as a 10. int iMinorCivCompetitiveness = GET_PLAYER(ePlayer).getLeaderInfo().GetMinorCivCompetitiveness(); if (iMinorCivCompetitiveness == -12) iMinorCivCompetitiveness = 10; return range(iMinorCivCompetitiveness, 1, 10); } /// What is this other leader's estimated likelihood to take risks / go for World Conquest? int CvDiplomacyAI::EstimateBoldness(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetBoldness(); } // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return 5; } // AI? Use the other player's base XML value. // Boldness can be set to -12 to guarantee that the AI will pursue Domination Victory if possible. This is treated as a 10. int iBoldness = GET_PLAYER(ePlayer).getLeaderInfo().GetBoldness(); if (iBoldness == -12) iBoldness = 10; return range(iBoldness, 1, 10); } /// How much do we estimate this other leader wants to maintain a balance of power in the world? int CvDiplomacyAI::EstimateDiploBalance(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetDiploBalance(); } // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return 5; } // AI? Use the other player's base XML value. return range(GET_PLAYER(ePlayer).getLeaderInfo().GetDiploBalance(), 1, 10); } /// How much does this other leader gets angry when someone's being a warmonger? int CvDiplomacyAI::EstimateWarmongerHate(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetWarmongerHate(); } int iRtnValue = 5; if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return iRtnValue; int iMax = 10; int iMin = 1; // AI and not Random Personalities? Use the other player's base XML value. if (!GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { iRtnValue = GET_PLAYER(ePlayer).getLeaderInfo().GetWarmongerHate(); if (iRtnValue >= 12 || iRtnValue == -12) // WarmongerHate can be set to -12 to guarantee that the AI will pursue Science Victory if possible. This is treated as a 10. return iMax; if (iRtnValue <= -1) return iMin; iRtnValue = range(iRtnValue, iMin, iMax); } // Special: If we currently have warmongering penalties with this player, we have a better idea of their flavor... // This uses the warmonger hate thresholds from the opinion table in LuaPlayer bool bHideNegatives = IsAtWar(ePlayer) || !GET_PLAYER(ePlayer).GetDiplomacyAI()->ShouldHideNegativeMods(GetID()); if (!bHideNegatives && GET_PLAYER(ePlayer).GetDiplomacyAI()->GetWarmongerThreatScore(GetID()) > 0) { int iActualValue = GET_PLAYER(ePlayer).GetDiplomacyAI()->GetWarmongerHate(); // Raise if too low if (iActualValue >= 9 && iRtnValue < 9) { iRtnValue = 9; } else if (iActualValue >= 7 && iRtnValue < 7) { iRtnValue = 7; } else if (iActualValue >= 5 && iRtnValue < 5) { iRtnValue = 5; } else if (iActualValue >= 3 && iRtnValue < 3) { iRtnValue = 3; } // Lower if too high if (iActualValue <= 2 && iRtnValue > 2) { iRtnValue = 2; } else if (iActualValue <= 4 && iRtnValue > 4) { iRtnValue = 4; } else if (iActualValue <= 6 && iRtnValue > 6) { iRtnValue = 6; } else if (iActualValue <= 8 && iRtnValue > 8) { iRtnValue = 8; } } return iRtnValue; } /// What is this other leader's estimated likelihood to befriend other players? int CvDiplomacyAI::EstimateDoFWillingness(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetDoFWillingness(); } // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return 5; } // AI? Use the other player's base XML value. return range(GET_PLAYER(ePlayer).getLeaderInfo().GetDoFWillingness(), 1, 10); } /// What is this other leader's estimated likelihood to badmouth other players? int CvDiplomacyAI::EstimateDenounceWillingness(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetDenounceWillingness(); } // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return 5; } // AI? Use the other player's base XML value. return range(GET_PLAYER(ePlayer).getLeaderInfo().GetDenounceWillingness(), 1, 10); } /// What is this other leader's estimated likelihood to form strategic alliances WITH others? int CvDiplomacyAI::EstimateWorkWithWillingness(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetWorkWithWillingness(); } // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return 5; } // AI? Use the other player's base XML value. return range(GET_PLAYER(ePlayer).getLeaderInfo().GetWorkWithWillingness(), 1, 10); } /// What is this other leader's estimated likelihood to form strategic alliances AGAINST others? int CvDiplomacyAI::EstimateWorkAgainstWillingness(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetWorkAgainstWillingness(); } // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return 5; } // AI? Use the other player's base XML value. return range(GET_PLAYER(ePlayer).getLeaderInfo().GetWorkAgainstWillingness(), 1, 10); } /// What is this other leader's estimated likelihood to refrain from backstabbing their friends? int CvDiplomacyAI::EstimateLoyalty(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetLoyalty(); } // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return 5; } // AI? Use the other player's base XML value. return range(GET_PLAYER(ePlayer).getLeaderInfo().GetLoyalty(), 1, 10); } /// How much do we estimate this other leader is willing to forgive transgressions against them? int CvDiplomacyAI::EstimateForgiveness(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetForgiveness(); } // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return 5; } // AI? Use the other player's base XML value. return range(GET_PLAYER(ePlayer).getLeaderInfo().GetForgiveness(), 1, 10); } /// How much do we estimate this other leader wants the support of its friends in rough times? int CvDiplomacyAI::EstimateNeediness(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetNeediness(); } // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return 5; } // AI? Use the other player's base XML value. return range(GET_PLAYER(ePlayer).getLeaderInfo().GetNeediness(), 1, 10); } /// How much do we estimate this other leader likes to talk smack / bully others? int CvDiplomacyAI::EstimateMeanness(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetMeanness(); } // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return 5; } // AI? Use the other player's base XML value. return range(GET_PLAYER(ePlayer).getLeaderInfo().GetMeanness(), 1, 10); } /// How much do we estimate this other leader likes to pop up and talk? int CvDiplomacyAI::EstimateChattiness(PlayerTypes ePlayer) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetChattiness(); } // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return 5; } // AI? Use the other player's base XML value. return range(GET_PLAYER(ePlayer).getLeaderInfo().GetChattiness(), 1, 10); } /// What is our estimate of another leader's bias for a particular Major Civ Approach? int CvDiplomacyAI::EstimateMajorCivApproachBias(PlayerTypes ePlayer, CivApproachTypes eApproach) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; if (eApproach < 0 || eApproach >= NUM_CIV_APPROACHES) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetMajorCivApproachBias(eApproach); } // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return 5; } // AI? Use the other player's base XML value. int iRtnValue = 0; switch (eApproach) { case CIV_APPROACH_WAR: iRtnValue = GET_PLAYER(ePlayer).getLeaderInfo().GetWarBias(false); break; case CIV_APPROACH_HOSTILE: iRtnValue = GET_PLAYER(ePlayer).getLeaderInfo().GetHostileBias(false); break; case CIV_APPROACH_DECEPTIVE: iRtnValue = GET_PLAYER(ePlayer).getLeaderInfo().GetDeceptiveBias(); break; case CIV_APPROACH_GUARDED: iRtnValue = GET_PLAYER(ePlayer).getLeaderInfo().GetGuardedBias(); break; case CIV_APPROACH_AFRAID: iRtnValue = GET_PLAYER(ePlayer).getLeaderInfo().GetAfraidBias(); break; case CIV_APPROACH_NEUTRAL: iRtnValue = GET_PLAYER(ePlayer).getLeaderInfo().GetNeutralBias(false); break; case CIV_APPROACH_FRIENDLY: iRtnValue = GET_PLAYER(ePlayer).getLeaderInfo().GetFriendlyBias(false); break; default: return 0; break; } return range(iRtnValue, 1, 10); } /// What is our estimate of another leader's bias for a particular Minor Civ Approach? int CvDiplomacyAI::EstimateMinorCivApproachBias(PlayerTypes ePlayer, CivApproachTypes eApproach) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; // Only some of the approaches are possible for City-States if (eApproach != CIV_APPROACH_WAR && eApproach != CIV_APPROACH_HOSTILE && eApproach != CIV_APPROACH_NEUTRAL && eApproach != CIV_APPROACH_FRIENDLY) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetDiplomacyAI()->GetMinorCivApproachBias(eApproach); } // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return 5; } // AI? Use the other player's base XML value. int iRtnValue = 0; switch (eApproach) { case CIV_APPROACH_WAR: iRtnValue = GET_PLAYER(ePlayer).getLeaderInfo().GetWarBias(true); break; case CIV_APPROACH_HOSTILE: iRtnValue = GET_PLAYER(ePlayer).getLeaderInfo().GetHostileBias(true); break; case CIV_APPROACH_NEUTRAL: iRtnValue = GET_PLAYER(ePlayer).getLeaderInfo().GetNeutralBias(true); break; case CIV_APPROACH_FRIENDLY: iRtnValue = GET_PLAYER(ePlayer).getLeaderInfo().GetFriendlyBias(true); break; default: return 0; break; } return range(iRtnValue, 1, 10); } /// What is our estimate of another leader's value for a personality flavor? int CvDiplomacyAI::EstimateFlavorValue(PlayerTypes ePlayer, FlavorTypes eFlavor) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; if (eFlavor < 0 || eFlavor >= GC.getNumFlavorTypes()) return 0; // We always know our team's flavors if (GetTeam() == GET_PLAYER(ePlayer).getTeam()) { return GET_PLAYER(ePlayer).GetFlavorManager()->GetPersonalityIndividualFlavor(eFlavor); } int iRtnValue = 5; // Human or Random Personalities? Assume the default flavor value. if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) || GC.getGame().isOption(GAMEOPTION_RANDOM_PERSONALITIES)) { return iRtnValue; } // AI? Use the other player's base XML value. LeaderHeadTypes leader = GET_PLAYER(ePlayer).getPersonalityType(); if (leader != NO_LEADER) { CvLeaderHeadInfo* pkLeaderHeadInfo = GC.getLeaderHeadInfo(leader); if (pkLeaderHeadInfo) { iRtnValue = pkLeaderHeadInfo->getFlavorValue((int)eFlavor); } } return range(iRtnValue, 1, 10); } // ----------------------------------------------------------------------------------------------- /// Log war declaration void CvDiplomacyAI::LogWarDeclaration(PlayerTypes ePlayer, int iTotalWarWeight) { if(GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString playerName; CvString otherPlayerName; CvString strDesc; CvString strLogName; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if(GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName + ", "; otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); strOutBuf = strBaseString + ", ***** WAR DECLARATION! *****, " + otherPlayerName; if(iTotalWarWeight >= 0) { CvString strWarWeight; strWarWeight.Format(", %d, ", iTotalWarWeight); strOutBuf += strWarWeight; } pLog->Msg(strOutBuf); // Want this in DiploMessage Log // if (!GET_PLAYER(ePlayer).isMinorCiv()) { if(GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Messages_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Messages_Log.csv"; } pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); pLog->Msg(strOutBuf); } // Log it to military AI too m_pPlayer->GetMilitaryAI()->LogDeclarationOfWar(ePlayer); } } /// Log war declaration void CvDiplomacyAI::LogPeaceMade(PlayerTypes ePlayer) { if(GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString playerName; CvString otherPlayerName; CvString strDesc; CvString strLogName; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if(GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName + ", "; otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); strOutBuf = strBaseString + ", ***** PEACE MADE! *****, " + otherPlayerName; pLog->Msg(strOutBuf); // Want this in DiploMessage Log // if (!GET_PLAYER(ePlayer).isMinorCiv()) { if(GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Messages_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Messages_Log.csv"; } pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); pLog->Msg(strOutBuf); } // Log it to military AI too m_pPlayer->GetMilitaryAI()->LogPeace(ePlayer); } } /// Log working with a player void CvDiplomacyAI::LogDoF(PlayerTypes ePlayer) { if(GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString playerName; CvString otherPlayerName; CvString strDesc; CvString strLogName; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if(GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Messages_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Messages_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName + ", "; otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); CvString strRank; strRank.Format("Rank: %03d, ", (int)GetDoFType(ePlayer)); strOutBuf = strBaseString + ",***** NOW FRIENDS " + otherPlayerName + strRank + "! *****"; pLog->Msg(strOutBuf); } } /// Log DoF Broken with a player void CvDiplomacyAI::LogBrokenDoF(PlayerTypes ePlayer) { if(GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString playerName; CvString otherPlayerName; CvString strDesc; CvString strLogName; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if(GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Messages_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Messages_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName + ", "; otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); strOutBuf = strBaseString + ",***** NO LONGER FRIENDS " + otherPlayerName + "! *****"; pLog->Msg(strOutBuf); } } /// Log working with a player void CvDiplomacyAI::LogDenounce(PlayerTypes ePlayer, bool bBackstab, bool bRefusal) { if(GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString playerName; CvString strDesc; CvString strLogName; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if(GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Messages_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Messages_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName + ", "; CvString otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); //CvString againstPlayerName = GET_PLAYER(eAgainstPlayer).getCivilizationShortDescription(); if(bBackstab) strOutBuf = strBaseString + ",***** BACKSTABBED " + otherPlayerName + "! *****"; else if(bRefusal) strOutBuf = strBaseString + ",***** REFUSED TO FORGIVE " + otherPlayerName + "! *****"; else { strOutBuf = strBaseString + ",***** DENOUNCED GENERIC " + otherPlayerName + "! *****"; int iMessage = GetDenounceMessage(ePlayer); if(iMessage != 0 && iMessage <= 7) { strOutBuf = strBaseString + ",***** DENOUNCED BECAUSE OF IDEOLOGY " + otherPlayerName + "! *****"; } else if(iMessage == 8) { strOutBuf = strBaseString + ",***** DENOUNCED BECAUSE OF WARMONGER " + otherPlayerName + "! *****"; } else if(iMessage == 9) { strOutBuf = strBaseString + ",***** DENOUNCED BECAUSE OF MINORS " + otherPlayerName + "! *****"; } else if(iMessage == 10) { strOutBuf = strBaseString + ",***** DENOUNCED BECAUSE OF BULLYING" + otherPlayerName + "! *****"; } else if(iMessage == 11) { strOutBuf = strBaseString + ",***** DENOUNCED BECAUSE OF NUKES " + otherPlayerName + "! *****"; } else if(iMessage == 12) { strOutBuf = strBaseString + ",***** DENOUNCED BECAUSE OF SPYING " + otherPlayerName + "! *****"; } else if(iMessage == 13) { strOutBuf = strBaseString + ",***** DENOUNCED BECAUSE OF LAND " + otherPlayerName + "! *****"; } else if(iMessage == 14) { strOutBuf = strBaseString + ",***** DENOUNCED BECAUSE IS ENEMY OF FRIEND " + otherPlayerName + "! *****"; } else if(iMessage == 15) { strOutBuf = strBaseString + ",***** DENOUNCED BECAUSE DOGPILE " + otherPlayerName + "! *****"; } else if(iMessage == 16) { strOutBuf = strBaseString + ",***** DENOUNCED BECAUSE UNTRUSTWORTHY " + otherPlayerName + "! *****"; } else if(iMessage == 17) { strOutBuf = strBaseString + ",***** DENOUNCED BECAUSE UNFORGIVEABLE " + otherPlayerName + "! *****"; } else if(iMessage == 18) { strOutBuf = strBaseString + ",***** DENOUNCED BECAUSE OF RELIGION " + otherPlayerName + "! *****"; } else if(iMessage == 19) { strOutBuf = strBaseString + ",***** DENOUNCED BECAUSE OF DIGGING " + otherPlayerName + "! *****"; } else if(iMessage == 20) { strOutBuf = strBaseString + ",***** DENOUNCED BECAUSE OF WONDERS " + otherPlayerName + "! *****"; } else if(iMessage == 21) { strOutBuf = strBaseString + ",***** DENOUNCED BECAUSE OF VICTORY DISPUTE " + otherPlayerName + "! *****"; } else if(iMessage == 22) { strOutBuf = strBaseString + ",***** DENOUNCED BECAUSE OF VICTORY BLOCK " + otherPlayerName + "! *****"; } } pLog->Msg(strOutBuf); } } /// Log friend asks another to denounce someone void CvDiplomacyAI::LogFriendRequestDenounce(PlayerTypes ePlayer, PlayerTypes eAgainstPlayer, bool bAgreed) { if(GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString playerName; CvString strDesc; CvString strLogName; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if(GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Messages_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Messages_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName + ", "; CvString otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); CvString againstPlayerName = GET_PLAYER(eAgainstPlayer).getCivilizationShortDescription(); // Did they actually agree? if(bAgreed) strOutBuf = strBaseString + ",***** CONVINCED " + otherPlayerName + " TO DENOUNCE " + againstPlayerName + "! *****"; else strOutBuf = strBaseString + ", ASKED " + otherPlayerName + " TO DENOUNCE " + againstPlayerName + "!"; pLog->Msg(strOutBuf); } } /// Log coop war state change update void CvDiplomacyAI::LogCoopWar(PlayerTypes ePlayer, PlayerTypes eAgainstPlayer, CoopWarStates eState) { if (GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString playerName; CvString strDesc; CvString strLogName; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if (GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Messages_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Messages_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName + ", "; CvString withPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); CvString againstPlayerName = GET_PLAYER(eAgainstPlayer).getCivilizationShortDescription(); switch (eState) { case NO_COOP_WAR_STATE: strOutBuf = strBaseString + ",***** COOP WAR STATE CHANGE: Coop war state with player " + withPlayerName + " against " + againstPlayerName + " has been reset (NO COOP WAR STATE)."; break; case COOP_WAR_STATE_WARNED_TARGET: strOutBuf = strBaseString + ",***** COOP WAR STATE CHANGE: We warned " + againstPlayerName + " about " + withPlayerName + "'s coop war plans (WARNED TARGET)."; break; case COOP_WAR_STATE_REJECTED: strOutBuf = strBaseString + ",***** COOP WAR STATE CHANGE: We rejected " + withPlayerName + "'s request to go to war against " + againstPlayerName + " (REJECTED)."; break; case COOP_WAR_STATE_PREPARING: strOutBuf = strBaseString + ",***** COOP WAR STATE CHANGE: We are preparing to go to war with " + withPlayerName + " against " + againstPlayerName + " (PREPARING)."; break; case COOP_WAR_STATE_ONGOING: strOutBuf = strBaseString + ",***** COOP WAR STATE CHANGE: We are now in a coop war with " + withPlayerName + " against " + againstPlayerName + " (ONGOING)."; break; default: strOutBuf = strBaseString + ", ERROR! Invalid coop war state for " + withPlayerName + " against " + againstPlayerName + "."; } pLog->Msg(strOutBuf); } } /// Log player wanting a RA void CvDiplomacyAI::LogWantRA(PlayerTypes ePlayer) { if(GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString playerName; CvString otherPlayerName; CvString strDesc; CvString strLogName; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if(GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Messages_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Messages_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName + ", "; otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); strOutBuf = strBaseString + otherPlayerName + ", Wants Research Agreement!"; pLog->Msg(strOutBuf); } } /// Log player wanting a DP void CvDiplomacyAI::LogWantDP(PlayerTypes ePlayer) { if(GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString playerName; CvString otherPlayerName; CvString strDesc; CvString strLogName; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if(GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Messages_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Messages_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName + ", "; otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); strOutBuf = strBaseString + otherPlayerName + ", Wants Defensive Pact!"; pLog->Msg(strOutBuf); } } /// Log Major Civ Approach Update void CvDiplomacyAI::LogApproachValueDeltas(PlayerTypes ePlayer, const int* aiApproachValues, const int* aiScratchValues) { if(GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString strTemp; CvString playerName; CvString otherPlayerName; CvString strDesc; CvString strLogName; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if(GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Approach_Deltas_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Approach_Deltas_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName; otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); strBaseString += ", " + otherPlayerName; CivApproachTypes eMajorCivApproach; // Major Civs if(!GET_PLAYER(ePlayer).isMinorCiv()) { strTemp.Format("Opinion: %d", GetCachedOpinionWeight(ePlayer)); strOutBuf += ", " + strTemp; // Weights for each possible Approach for(int iMajorCivApproachLoop = 0; iMajorCivApproachLoop < NUM_CIV_APPROACHES; iMajorCivApproachLoop++) { eMajorCivApproach = (CivApproachTypes) iMajorCivApproachLoop; switch(eMajorCivApproach) { case CIV_APPROACH_WAR: strTemp.Format("War"); break; case CIV_APPROACH_HOSTILE: strTemp.Format("Hostile"); break; case CIV_APPROACH_DECEPTIVE: strTemp.Format("Deceptive"); break; case CIV_APPROACH_GUARDED: strTemp.Format("Guarded"); break; case CIV_APPROACH_AFRAID: strTemp.Format("Afraid"); break; case CIV_APPROACH_FRIENDLY: strTemp.Format("Friendly"); break; case CIV_APPROACH_NEUTRAL: strTemp.Format("Neutral"); break; default: strTemp.Format("XXX"); break; } strOutBuf += ", " + strTemp; strTemp.Format("Value: %d", aiApproachValues[eMajorCivApproach]); strOutBuf += ", " + strTemp; strTemp.Format("Scratch: %d", aiScratchValues[eMajorCivApproach]); strOutBuf += ", " + strTemp; int iDelta = 0; if(aiApproachValues[eMajorCivApproach] >= aiScratchValues[eMajorCivApproach]) { iDelta = (aiApproachValues[eMajorCivApproach] - aiScratchValues[eMajorCivApproach]); strTemp.Format("Delta: +%d", iDelta); strOutBuf += ", " + strTemp; } else { iDelta = (aiScratchValues[eMajorCivApproach] - aiApproachValues[eMajorCivApproach]); strTemp.Format("Delta: -%d", iDelta); strOutBuf += ", " + strTemp; } } strOutBuf = strBaseString + strOutBuf; pLog->Msg(strOutBuf); } } } /// Log Major Civ Warmonger Threat update void CvDiplomacyAI::LogMajorCivWarmongerUpdate(PlayerTypes ePlayer, int iValue, bool bUpdateLogsSpecial) { if (GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString strTemp; CvString playerName; CvString otherPlayerName; CvString strLogName; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if (GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_WarmongerStatus_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_WarmongerStatus_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName; otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); strBaseString += ", " + otherPlayerName; // Don't fill out this field for Minors, as it just makes the log harder to read if (GET_PLAYER(ePlayer).isMinorCiv()) { strTemp.Format(","); } else { int iResistance = m_pPlayer->GetDominationResistance(ePlayer); if (bUpdateLogsSpecial) { switch (GetWarmongerThreat(ePlayer)) { case THREAT_CRITICAL: strTemp.Format(" -- NEW STATUS -- W_THT CRIT, %d/%d, Decay: %d, Resistance: %d", GetOtherPlayerWarmongerAmount(ePlayer), GetOtherPlayerWarmongerScore(ePlayer), iValue, iResistance); break; case THREAT_SEVERE: strTemp.Format(" -- NEW STATUS -- W_THT SEVR, %d/%d, Decay: %d, Resistance: %d", GetOtherPlayerWarmongerAmount(ePlayer), GetOtherPlayerWarmongerScore(ePlayer), iValue, iResistance); break; case THREAT_MAJOR: strTemp.Format(" -- NEW STATUS -- W_THT MAJR, %d/%d, Decay: %d, Resistance: %d", GetOtherPlayerWarmongerAmount(ePlayer), GetOtherPlayerWarmongerScore(ePlayer), iValue, iResistance); break; case THREAT_MINOR: strTemp.Format(" -- NEW STATUS -- W_THT MINR, %d/%d, Decay: %d, Resistance: %d", GetOtherPlayerWarmongerAmount(ePlayer), GetOtherPlayerWarmongerScore(ePlayer), iValue, iResistance); break; case THREAT_NONE: strTemp.Format(" -- NEW STATUS -- W_THT NONE, %d/%d, Decay: %d, Resistance: %d", GetOtherPlayerWarmongerAmount(ePlayer), GetOtherPlayerWarmongerScore(ePlayer), iValue, iResistance); break; default: strTemp.Format(" -- NEW STATUS -- W_THT XXXX, %d/%d, Decay: %d, Resistance: %d", GetOtherPlayerWarmongerAmount(ePlayer), GetOtherPlayerWarmongerScore(ePlayer), iValue, iResistance); break; } } else { strTemp.Format(" -- %d/%d, Decay: %d, Resistance : %d", GetOtherPlayerWarmongerAmount(ePlayer), GetOtherPlayerWarmongerScore(ePlayer), iValue, iResistance); } } strOutBuf = strBaseString + strTemp; pLog->Msg(strOutBuf); } } /// Log Major Civ Approach Update void CvDiplomacyAI::LogMajorCivApproachUpdate(PlayerTypes ePlayer, const int* aiApproachValues, CivApproachTypes eNewMajorCivApproach, CivApproachTypes eOldApproach, CivApproachTypes eSurfaceApproach) { if (!GET_PLAYER(ePlayer).isMajorCiv()) return; if (GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString strTemp; CvString playerName; CvString otherPlayerName; CvString strDesc; CvString strLogName; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if (GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Approach_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Approach_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName; otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); strBaseString += ", " + otherPlayerName; // Major Civs LogMajorCivApproach(strOutBuf, eNewMajorCivApproach, eSurfaceApproach); if (eNewMajorCivApproach != eOldApproach) { LogMajorCivApproach(strOutBuf, eOldApproach, eSurfaceApproach); } else { strTemp.Format("---"); strOutBuf += ", " + strTemp; } LogOpinion(strOutBuf, ePlayer); LogWarmongerThreat(strOutBuf, ePlayer); LogTargetValue(strOutBuf, ePlayer); LogMilitaryAggressivePosture(strOutBuf, ePlayer); LogProximity(strOutBuf, ePlayer); LogLandDispute(strOutBuf, ePlayer); LogVictoryDispute(strOutBuf, ePlayer); LogVictoryBlock(strOutBuf, ePlayer); LogWonderDispute(strOutBuf, ePlayer); LogMinorCivDispute(strOutBuf, ePlayer); strTemp.Format("---"); strOutBuf += ", " + strTemp; // Weights for each possible Approach for (int iMajorCivApproachLoop = 0; iMajorCivApproachLoop < NUM_CIV_APPROACHES; iMajorCivApproachLoop++) { CivApproachTypes eMajorCivApproach = (CivApproachTypes) iMajorCivApproachLoop; switch (eMajorCivApproach) { case CIV_APPROACH_WAR: strTemp.Format("War"); break; case CIV_APPROACH_HOSTILE: strTemp.Format("Hostile"); break; case CIV_APPROACH_DECEPTIVE: strTemp.Format("Deceptive"); break; case CIV_APPROACH_GUARDED: strTemp.Format("Guarded"); break; case CIV_APPROACH_AFRAID: strTemp.Format("Afraid"); break; case CIV_APPROACH_NEUTRAL: strTemp.Format("Neutral"); break; case CIV_APPROACH_FRIENDLY: strTemp.Format("Friendly"); break; } strOutBuf += ", " + strTemp; strTemp.Format("%d", aiApproachValues[(int)eMajorCivApproach]); strOutBuf += ", " + strTemp; } strOutBuf = strBaseString + strOutBuf; pLog->Msg(strOutBuf); } } /// Log Minor Civ Approach Update void CvDiplomacyAI::LogMinorCivApproachUpdate(PlayerTypes ePlayer, const int* aiApproachValues, CivApproachTypes eNewMinorCivApproach, CivApproachTypes eOldApproach) { if (!GET_PLAYER(ePlayer).isMinorCiv()) return; if (GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString strTemp; CvString playerName; CvString otherPlayerName; CvString strDesc; CvString strLogName; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if (GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Approach_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Approach_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); // Name strBaseString += playerName; otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); strBaseString += ", --- " + otherPlayerName; LogMinorCivApproach(strOutBuf, eNewMinorCivApproach); if (eNewMinorCivApproach != eOldApproach) { LogMinorCivApproach(strOutBuf, eOldApproach); } else { strTemp.Format("---"); strOutBuf += ", " + strTemp; } LogTargetValue(strOutBuf, ePlayer); LogMilitaryAggressivePosture(strOutBuf, ePlayer); LogProximity(strOutBuf, ePlayer); LogLandDispute(strOutBuf, ePlayer); strOutBuf += ", ---"; // Weights for each possible Approach for (int iMinorCivApproachLoop = 0; iMinorCivApproachLoop < NUM_CIV_APPROACHES; iMinorCivApproachLoop++) { CivApproachTypes eMinorCivApproach = (CivApproachTypes) iMinorCivApproachLoop; switch (eMinorCivApproach) { case CIV_APPROACH_DECEPTIVE: case CIV_APPROACH_GUARDED: case CIV_APPROACH_AFRAID: break; // Not applicable to minors. case CIV_APPROACH_WAR: strTemp.Format("Conquest"); break; case CIV_APPROACH_HOSTILE: strTemp.Format("Bully"); break; case CIV_APPROACH_NEUTRAL: strTemp.Format("Ignore"); break; case CIV_APPROACH_FRIENDLY: strTemp.Format("Protective"); break; } strOutBuf += ", " + strTemp; strTemp.Format("%d", aiApproachValues[(int)eMinorCivApproach]); strOutBuf += ", " + strTemp; } strOutBuf = strBaseString + strOutBuf; pLog->Msg(strOutBuf); } } /// Log the Personality of this player (Flavors & Personality Traits) void CvDiplomacyAI::LogPersonality() { if (GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString playerName; CvString strDesc; CvString strLogName; CvString strTemp; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if (GC.getPlayerAndCityAILogSplit()) { strLogName = "AI_Personality_Log_" + playerName + ".csv"; } else { strLogName = "AI_Personality_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, PERSONALITY, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName; // Loop through all Flavors for (int iFlavorLoop = 0; iFlavorLoop < GC.getNumFlavorTypes(); iFlavorLoop++) { strTemp.Format("%s, %d", GC.getFlavorTypes((FlavorTypes) iFlavorLoop).GetCString(), GetPlayer()->GetFlavorManager()->GetPersonalityIndividualFlavor((FlavorTypes) iFlavorLoop)); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); } // Loop through all Approaches to see what this player's bias is for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eMajorCivApproach = (CivApproachTypes) iApproachLoop; switch (eMajorCivApproach) { case CIV_APPROACH_WAR: strTemp.Format("War"); break; case CIV_APPROACH_HOSTILE: strTemp.Format("Hostile"); break; case CIV_APPROACH_DECEPTIVE: strTemp.Format("Deceptive"); break; case CIV_APPROACH_GUARDED: strTemp.Format("Guarded"); break; case CIV_APPROACH_AFRAID: strTemp.Format("Afraid"); break; case CIV_APPROACH_FRIENDLY: strTemp.Format("Friendly"); break; case CIV_APPROACH_NEUTRAL: strTemp.Format("Neutral"); break; } strOutBuf = strBaseString + ", " + strTemp; strTemp.Format("%d", GetMajorCivApproachBias(eMajorCivApproach)); strOutBuf += ", " + strTemp; pLog->Msg(strOutBuf); } // Loop through all Approaches to see what this player's bias is for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) { CivApproachTypes eMinorCivApproach = (CivApproachTypes) iApproachLoop; bool bValid = false; // Convert approach names back to the XML table's names to avoid confusion switch (eMinorCivApproach) { case CIV_APPROACH_WAR: bValid = true; strTemp.Format("Conquest"); break; case CIV_APPROACH_HOSTILE: bValid = true; strTemp.Format("Bully"); break; case CIV_APPROACH_NEUTRAL: bValid = true; strTemp.Format("Ignore"); break; case CIV_APPROACH_FRIENDLY: bValid = true; strTemp.Format("Protective"); break; default: break; // Not applicable to minors. } if (bValid) { strOutBuf = strBaseString + ", " + strTemp; strTemp.Format("%d", GetMinorCivApproachBias(eMinorCivApproach)); strOutBuf += ", " + strTemp; pLog->Msg(strOutBuf); } } strTemp.Format("VICTORY COMPETITIVENESS, %d", GetVictoryCompetitiveness()); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); strTemp.Format("WONDER COMPETITIVENESS, %d", GetWonderCompetitiveness()); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); strTemp.Format("MINOR COMPETITIVENESS, %d", GetMinorCivCompetitiveness()); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); strTemp.Format("BOLDNESS, %d", GetBoldness()); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); strTemp.Format("DIPLO BALANCE, %d", GetDiploBalance()); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); strTemp.Format("WARMONGER HATE, %d", GetWarmongerHate()); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); strTemp.Format("DOF, %d", GetDoFWillingness()); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); strTemp.Format("DENOUNCE, %d", GetDenounceWillingness()); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); strTemp.Format("LOYALTY, %d", GetLoyalty()); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); strTemp.Format("FORGIVENESS, %d", GetForgiveness()); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); strTemp.Format("NEEDINESS, %d", GetNeediness()); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); strTemp.Format("MEANNESS, %d", GetMeanness()); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); strTemp.Format("CHATTINESS, %d", GetChattiness()); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); switch (GetPrimaryVictoryPursuit()) { case VICTORY_PURSUIT_DOMINATION: strTemp.Format("DEFAULT PRIMARY PURSUIT, DOMINATION"); break; case VICTORY_PURSUIT_DIPLOMACY: strTemp.Format("DEFAULT PRIMARY PURSUIT, DIPLOMACY"); break; case VICTORY_PURSUIT_CULTURE: strTemp.Format("DEFAULT PRIMARY PURSUIT, CULTURE"); break; case VICTORY_PURSUIT_SCIENCE: strTemp.Format("DEFAULT PRIMARY PURSUIT, SCIENCE"); break; default: strTemp.Format("DEFAULT PRIMARY PURSUIT, NONE"); break; } strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); switch (GetSecondaryVictoryPursuit()) { case VICTORY_PURSUIT_DOMINATION: strTemp.Format("DEFAULT SECONDARY PURSUIT, DOMINATION"); break; case VICTORY_PURSUIT_DIPLOMACY: strTemp.Format("DEFAULT SECONDARY PURSUIT, DIPLOMACY"); break; case VICTORY_PURSUIT_CULTURE: strTemp.Format("DEFAULT SECONDARY PURSUIT, CULTURE"); break; case VICTORY_PURSUIT_SCIENCE: strTemp.Format("DEFAULT SECONDARY PURSUIT, SCIENCE"); break; default: strTemp.Format("DEFAULT SECONDARY PURSUIT, NONE"); break; } strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); CvPlayerTraits* pTraits = GetPlayer()->GetPlayerTraits(); if (pTraits->IsWarmonger()) { strTemp.Format("UA TYPE, WARMONGERING"); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); } if (pTraits->IsDiplomat()) { strTemp.Format("UA TYPE, DIPLOMACY"); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); } if (pTraits->IsTourism()) { strTemp.Format("UA TYPE, CULTURE"); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); } if (pTraits->IsNerd()) { strTemp.Format("UA TYPE, SCIENCE"); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); } if (pTraits->IsReligious()) { strTemp.Format("UA TYPE, RELIGIOUS"); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); } if (pTraits->IsExpansionist()) { strTemp.Format("UA TYPE, WIDE"); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); } if (pTraits->IsSmaller()) { strTemp.Format("UA TYPE, TALL"); strOutBuf = strBaseString + ", " + strTemp; pLog->Msg(strOutBuf); } } } /// Log diplomatic status with other Players void CvDiplomacyAI::LogStatus() { if(GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString playerName; CvString otherPlayerName; CvString strMinorString; CvString strDesc; CvString strLogName; CvString strTemp; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if(GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName; // Loop through all (known) Players for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if(IsPlayerValid(eLoopPlayer)) { strOutBuf = strBaseString; LogGrandStrategy(strOutBuf); // Name if(GET_PLAYER(eLoopPlayer).isMinorCiv()) { strMinorString = "--- "; } otherPlayerName = GET_PLAYER(eLoopPlayer).getCivilizationShortDescription(); strOutBuf += ", " + strMinorString + otherPlayerName; // Major Civ if (!GET_PLAYER(eLoopPlayer).isMinorCiv()) { if(IsDoFAccepted(eLoopPlayer)) strOutBuf += ", WW"; else strOutBuf += ", "; if(IsDenouncedPlayer(eLoopPlayer)) strOutBuf += ", DEN"; else strOutBuf += ", "; if(IsAtWar(eLoopPlayer)) strOutBuf += ", AT WAR"; else strOutBuf += ", "; CoopWarStates eCoopWarState = GetGlobalCoopWarAgainstState(eLoopPlayer); if (eCoopWarState == COOP_WAR_STATE_ONGOING) strOutBuf += ", CW"; else if (eCoopWarState == COOP_WAR_STATE_PREPARING) strOutBuf += ", CWS"; else strOutBuf += ", "; LogMajorCivApproach(strOutBuf, GetCivApproach(eLoopPlayer), GetSurfaceApproach(eLoopPlayer)); LogOpinion(strOutBuf, eLoopPlayer); } // Minor Civ else { strOutBuf += ", , , , "; LogMinorCivApproach(strOutBuf, GetCivApproach(eLoopPlayer)); strOutBuf += ", "; // Opinion } LogProximity(strOutBuf, eLoopPlayer); LogWarState(strOutBuf, eLoopPlayer); LogEconomicStrength(strOutBuf, eLoopPlayer); LogMilitaryStrength(strOutBuf, eLoopPlayer); LogTargetValue(strOutBuf, eLoopPlayer); LogMilitaryAggressivePosture(strOutBuf, eLoopPlayer); LogLandDispute(strOutBuf, eLoopPlayer); if (!GET_PLAYER(eLoopPlayer).isMinorCiv()) { LogWarmongerThreat(strOutBuf, eLoopPlayer); LogPlotBuyingAggressivePosture(strOutBuf, eLoopPlayer); LogVictoryDispute(strOutBuf, eLoopPlayer); LogWonderDispute(strOutBuf, eLoopPlayer); LogMinorCivDispute(strOutBuf, eLoopPlayer); // Other Player's Estimated Grand Strategy AIGrandStrategyTypes eGrandStrategy = GetPlayer()->GetGrandStrategyAI()->GetGuessOtherPlayerActiveGrandStrategy(eLoopPlayer); CvAIGrandStrategyXMLEntry* pEntry = eGrandStrategy != NO_AIGRANDSTRATEGY ? GC.getAIGrandStrategyInfo(eGrandStrategy) : NULL; if(pEntry != NULL) { strTemp.Format("GSS %s", pEntry->GetType()); strOutBuf += ", " + strTemp; // Confidence in our guess switch(GetPlayer()->GetGrandStrategyAI()->GetGuessOtherPlayerActiveGrandStrategyConfidence(eLoopPlayer)) { case GUESS_CONFIDENCE_POSITIVE: strTemp.Format("POSITIVE"); break; case GUESS_CONFIDENCE_LIKELY: strTemp.Format("Likely"); break; case GUESS_CONFIDENCE_UNSURE: strTemp.Format("Unsure"); break; } } else { strTemp.Format("No GS Guess, "); } } else { strTemp.Format(", "); } strOutBuf += ", " + strTemp; pLog->Msg(strOutBuf); } } } } /// Log Diplomatic War status with other Players void CvDiplomacyAI::LogWarStatus() { if(GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString playerName; CvString otherPlayerName; CvString strMinorString; CvString strDesc; CvString strLogName; CvString strTemp; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if(GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_War_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_War_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName; bool bLogPlayer = false; // Loop through all (known) Players for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if(IsPlayerValid(eLoopPlayer)) { if(IsAtWar(eLoopPlayer)) { bLogPlayer = true; } else if (GetWarState(eLoopPlayer) != NO_WAR_STATE_TYPE) { bLogPlayer = true; } else if (IsWantsSneakAttack(eLoopPlayer)) { bLogPlayer = true; } else { bLogPlayer = false; } // Should we actually record a log entry for this player? if(bLogPlayer) { strOutBuf = strBaseString; // Name if(GET_PLAYER(eLoopPlayer).isMinorCiv()) { strMinorString = "--- "; } otherPlayerName = GET_PLAYER(eLoopPlayer).getCivilizationShortDescription(); strOutBuf += ", " + strMinorString + otherPlayerName; // Approach if(GET_PLAYER(eLoopPlayer).isMinorCiv()) { if(GetCivApproach(eLoopPlayer) == CIV_APPROACH_WAR) { strOutBuf += ", APP: CONQUEST"; } else { strOutBuf += ", APP: o"; } } else { if(GetCivApproach(eLoopPlayer) == CIV_APPROACH_WAR) { strOutBuf += ", APP: WAR"; } else { strOutBuf += ", APP: o"; } } // Wants to conquer the world? if(IsGoingForWorldConquest()) { strOutBuf += ", WC"; } else { strOutBuf += ", "; } // Wants peace with eLoopPlayer? if(!GET_PLAYER(eLoopPlayer).isMinorCiv() && IsWantsPeaceWithPlayer(eLoopPlayer)) { strOutBuf += ", Wants Peace!!!"; } else { strOutBuf += ", "; } LogWarState(strOutBuf, eLoopPlayer); LogWarPeaceWillingToOffer(strOutBuf, eLoopPlayer); LogWarPeaceWillingToAccept(strOutBuf, eLoopPlayer); // # of turns at War if (IsAtWar(eLoopPlayer) && GET_TEAM(GetTeam()).GetNumTurnsAtWar(GET_PLAYER(eLoopPlayer).getTeam()) > 0) { strTemp.Format("%d", GET_TEAM(GetTeam()).GetNumTurnsAtWar(GET_PLAYER(eLoopPlayer).getTeam())); strOutBuf += ", " + strTemp; } else { strOutBuf += ", "; } LogProximity(strOutBuf, eLoopPlayer); LogTargetValue(strOutBuf, eLoopPlayer); LogMilitaryStrength(strOutBuf, eLoopPlayer); LogEconomicStrength(strOutBuf, eLoopPlayer); if(!GET_PLAYER(eLoopPlayer).isMinorCiv() && IsAtWar(eLoopPlayer)) { strTemp.Format(" !!!!WAR SCORE: %d !!!! ", GetWarScore(eLoopPlayer)); strOutBuf += ", " + strTemp; } strTemp.Format(" --- War Weariness: %d, Supply: %d", m_pPlayer->GetWarWearinessPercent(eLoopPlayer), m_pPlayer->GetNumUnitsSupplied()); strOutBuf += ", " + strTemp; pLog->Msg(strOutBuf); } } } } } /// Log Grand Strategy void CvDiplomacyAI::LogGrandStrategy(CvString& strString) { CvString strTemp; AIGrandStrategyTypes eGrandStrategy =GetPlayer()->GetGrandStrategyAI()->GetActiveGrandStrategy(); if(eGrandStrategy == GC.getInfoTypeForString("AIGRANDSTRATEGY_CONQUEST")) { strTemp.Format("Conquest"); } else if(eGrandStrategy == GC.getInfoTypeForString("AIGRANDSTRATEGY_SPACESHIP")) { strTemp.Format("Spaceship"); } else if(eGrandStrategy == GC.getInfoTypeForString("AIGRANDSTRATEGY_UNITED_NATIONS")) { strTemp.Format("Diplomacy"); } else if(eGrandStrategy == GC.getInfoTypeForString("AIGRANDSTRATEGY_CULTURE")) { strTemp.Format("Culture"); } strString += ", " + strTemp; } /// Log Current Approach towards Major void CvDiplomacyAI::LogMajorCivApproach(CvString& strString, CivApproachTypes eNewMajorCivApproach, CivApproachTypes eSurfaceApproach) { CvString strTemp; switch (eNewMajorCivApproach) { case CIV_APPROACH_WAR: switch (eSurfaceApproach) { case CIV_APPROACH_HOSTILE: strTemp.Format("**WAR_HOSTILE**"); break; case CIV_APPROACH_NEUTRAL: strTemp.Format("**WAR_NEUTRAL**"); break; case CIV_APPROACH_FRIENDLY: strTemp.Format("**WAR_FRIENDLY**"); break; case CIV_APPROACH_GUARDED: strTemp.Format("**WAR_GUARDED**"); break; default: strTemp.Format("**WAR**"); break; } break; case CIV_APPROACH_HOSTILE: strTemp.Format("HOSTILE"); break; case CIV_APPROACH_DECEPTIVE: strTemp.Format("DECEPTIVE"); break; case CIV_APPROACH_GUARDED: strTemp.Format("GUARDED"); break; case CIV_APPROACH_AFRAID: strTemp.Format("AFRAID"); break; case CIV_APPROACH_FRIENDLY: strTemp.Format("FRIENDLY"); break; case CIV_APPROACH_NEUTRAL: strTemp.Format("N"); break; default: strTemp.Format("ApproachUnknown"); break; } strString += ", " + strTemp; } /// Log Current Approach towards Minor void CvDiplomacyAI::LogMinorCivApproach(CvString& strString, CivApproachTypes eNewMinorCivApproach) { CvString strTemp; switch (eNewMinorCivApproach) { case CIV_APPROACH_WAR: strTemp.Format("**CONQUEST**"); break; case CIV_APPROACH_HOSTILE: strTemp.Format("BULLY"); break; case CIV_APPROACH_NEUTRAL: strTemp.Format("IGNORE"); break; case CIV_APPROACH_FRIENDLY: strTemp.Format("PROTECTIVE"); break; default: strTemp.Format("ApproachUnknown"); break; } strString += ", " + strTemp; } /// Log a quest from a Minor void CvDiplomacyAI::LogMinorCivQuestType(CvString& strString, MinorCivQuestTypes eQuestType) { CvString strTemp; switch (eQuestType) { case MINOR_CIV_QUEST_ROUTE: strTemp.Format("Route"); break; case MINOR_CIV_QUEST_KILL_CAMP: strTemp.Format("Kill Camp"); break; case MINOR_CIV_QUEST_CONNECT_RESOURCE: strTemp.Format("Connect Resource"); break; case MINOR_CIV_QUEST_CONSTRUCT_WONDER: strTemp.Format("Construct Wonder"); break; case MINOR_CIV_QUEST_GREAT_PERSON: strTemp.Format("Great Person"); break; case MINOR_CIV_QUEST_KILL_CITY_STATE: strTemp.Format("Kill City-State"); break; case MINOR_CIV_QUEST_FIND_PLAYER: strTemp.Format("Find Player"); break; case MINOR_CIV_QUEST_FIND_NATURAL_WONDER: strTemp.Format("Find Natural Wonder"); break; case MINOR_CIV_QUEST_GIVE_GOLD: strTemp.Format("Give Gold"); break; case MINOR_CIV_QUEST_PLEDGE_TO_PROTECT: strTemp.Format("Pledge to Protect"); break; case MINOR_CIV_QUEST_CONTEST_CULTURE: strTemp.Format("Contest Culture"); break; case MINOR_CIV_QUEST_CONTEST_FAITH: strTemp.Format("Contest Faith"); break; case MINOR_CIV_QUEST_CONTEST_TECHS: strTemp.Format("Contest Techs"); break; case MINOR_CIV_QUEST_INVEST: strTemp.Format("Invest"); break; case MINOR_CIV_QUEST_BULLY_CITY_STATE: strTemp.Format("Bully City-State"); break; case MINOR_CIV_QUEST_DENOUNCE_MAJOR: strTemp.Format("Denounce Major"); break; case MINOR_CIV_QUEST_SPREAD_RELIGION: strTemp.Format("Spread Religion"); break; case MINOR_CIV_QUEST_TRADE_ROUTE: strTemp.Format("Trade Route"); break; case MINOR_CIV_QUEST_FIND_CITY: strTemp.Format("Find City"); break; case MINOR_CIV_QUEST_WAR: strTemp.Format("War"); break; case MINOR_CIV_QUEST_CONSTRUCT_NATIONAL_WONDER: strTemp.Format("Construct National Wonder"); break; case MINOR_CIV_QUEST_GIFT_SPECIFIC_UNIT: strTemp.Format("Gift Specific Unit"); break; case MINOR_CIV_QUEST_FIND_CITY_STATE: strTemp.Format("Find City-State"); break; case MINOR_CIV_QUEST_INFLUENCE: strTemp.Format("Influence"); break; case MINOR_CIV_QUEST_CONTEST_TOURISM: strTemp.Format("Contest Tourism"); break; case MINOR_CIV_QUEST_ARCHAEOLOGY: strTemp.Format("Archaeology"); break; case MINOR_CIV_QUEST_CIRCUMNAVIGATION: strTemp.Format("Circumnavigation"); break; case MINOR_CIV_QUEST_LIBERATION: strTemp.Format("Liberation"); break; case MINOR_CIV_QUEST_HORDE: strTemp.Format("Horde"); break; case MINOR_CIV_QUEST_REBELLION: strTemp.Format("Rebellion"); break; case MINOR_CIV_QUEST_EXPLORE_AREA: strTemp.Format("Explore Area"); break; case MINOR_CIV_QUEST_BUILD_X_BUILDINGS: strTemp.Format("Build X Buildings"); break; case MINOR_CIV_QUEST_SPY_ON_MAJOR: strTemp.Format("Spy On Major"); break; case MINOR_CIV_QUEST_COUP: strTemp.Format("Coup"); break; case MINOR_CIV_QUEST_ACQUIRE_CITY: strTemp.Format("Acquire City"); break; default: strTemp.Format("Unknown Quest"); break; } strString += ", " + strTemp; } /// Log Current Opinion of Major void CvDiplomacyAI::LogOpinion(CvString& strString, PlayerTypes ePlayer) const { CvString strTemp; // Opinion switch (GetCivOpinion(ePlayer)) { case CIV_OPINION_ALLY: strTemp.Format("OPN **ALLY**"); break; case CIV_OPINION_FRIEND: strTemp.Format("OPN FRIEND"); break; case CIV_OPINION_FAVORABLE: strTemp.Format("OPN Favorable"); break; case CIV_OPINION_NEUTRAL: strTemp.Format("OPN N"); break; case CIV_OPINION_COMPETITOR: strTemp.Format("OPN Competitor"); break; case CIV_OPINION_ENEMY: strTemp.Format("OPN ENEMY"); break; case CIV_OPINION_UNFORGIVABLE: strTemp.Format("OPN **UNFORGIVABLE**"); break; default: strTemp.Format("OPN Unknown"); break; } strString += ", " + strTemp; } /// Log Warmonger Threat void CvDiplomacyAI::LogWarmongerThreat(CvString& strString, PlayerTypes ePlayer) { CvString strTemp; // Don't fill out this field for Minors, as it just makes the log harder to read if(GET_PLAYER(ePlayer).isMinorCiv()) { strTemp.Format(","); } else { switch(GetWarmongerThreat(ePlayer)) { case THREAT_CRITICAL: strTemp.Format("W_THT CRIT, %d/%d", GetOtherPlayerWarmongerAmount(ePlayer), GetOtherPlayerWarmongerScore(ePlayer)); break; case THREAT_SEVERE: strTemp.Format("W_THT SEVR, %d/%d", GetOtherPlayerWarmongerAmount(ePlayer), GetOtherPlayerWarmongerScore(ePlayer)); break; case THREAT_MAJOR: strTemp.Format("W_THT MAJR, %d/%d", GetOtherPlayerWarmongerAmount(ePlayer), GetOtherPlayerWarmongerScore(ePlayer)); break; case THREAT_MINOR: strTemp.Format("W_THT MINR, %d/%d", GetOtherPlayerWarmongerAmount(ePlayer), GetOtherPlayerWarmongerScore(ePlayer)); break; case THREAT_NONE: strTemp.Format("W_THT NONE, %d/%d", GetOtherPlayerWarmongerAmount(ePlayer), GetOtherPlayerWarmongerScore(ePlayer)); break; default: strTemp.Format("W_THT XXXX, %d/%d", GetOtherPlayerWarmongerAmount(ePlayer), GetOtherPlayerWarmongerScore(ePlayer)); break; } } strString += ", " + strTemp; } /// Log Military Strength void CvDiplomacyAI::LogMilitaryStrength(CvString& strString, PlayerTypes ePlayer) const { CvString strTemp; // Military Strength switch(GetMilitaryStrengthComparedToUs(ePlayer)) { case STRENGTH_IMMENSE: strTemp.Format("MSTR **IMMENSE**"); break; case STRENGTH_POWERFUL: strTemp.Format("MSTR POWERFUL"); break; case STRENGTH_STRONG: strTemp.Format("MSTR Strong"); break; case STRENGTH_AVERAGE: strTemp.Format("MSTR Avg"); break; case STRENGTH_POOR: strTemp.Format("MSTR Poor"); break; case STRENGTH_WEAK: strTemp.Format("MSTR WEAK"); break; case STRENGTH_PATHETIC: strTemp.Format("MSTR **PATHETIC**"); break; } strString += ", " + strTemp; } /// Log Economic Strength void CvDiplomacyAI::LogEconomicStrength(CvString& strString, PlayerTypes ePlayer) const { CvString strTemp; // Economic Strength switch(GetEconomicStrengthComparedToUs(ePlayer)) { case STRENGTH_IMMENSE: strTemp.Format("ESTR **IMMENSE**"); break; case STRENGTH_POWERFUL: strTemp.Format("ESTR POWERFUL"); break; case STRENGTH_STRONG: strTemp.Format("ESTR Strong"); break; case STRENGTH_AVERAGE: strTemp.Format("ESTR Avg"); break; case STRENGTH_POOR: strTemp.Format("ESTR Poor"); break; case STRENGTH_WEAK: strTemp.Format("ESTR WEAK"); break; case STRENGTH_PATHETIC: strTemp.Format("ESTR **PATHETIC**"); break; } strString += ", " + strTemp; } /// Log Target Value void CvDiplomacyAI::LogTargetValue(CvString& strString, PlayerTypes ePlayer) const { CvString strTemp; // Target Value switch (GetTargetValue(ePlayer)) { case TARGET_VALUE_IMPOSSIBLE: strTemp.Format("TGT Impossible"); break; case TARGET_VALUE_BAD: strTemp.Format("TGT Bad"); break; case TARGET_VALUE_DIFFICULT: strTemp.Format("TGT Difficult"); break; case TARGET_VALUE_AVERAGE: strTemp.Format("TGT Avg"); break; case TARGET_VALUE_FAVORABLE: strTemp.Format("TGT Favorable"); break; case TARGET_VALUE_SOFT: strTemp.Format("TGT Soft"); break; case TARGET_VALUE_CAKEWALK: strTemp.Format("TGT Cakewalk"); break; default: strTemp.Format("TGT Unknown"); break; } strString += ", " + strTemp; } /// Log Peace Treaty Willing to Offer void CvDiplomacyAI::LogWarPeaceWillingToOffer(CvString& strString, PlayerTypes ePlayer) const { CvString strTemp; // No Minor Civs if(GET_PLAYER(ePlayer).isMinorCiv()) { strTemp.Format(""); } else { // War Goal switch(GetTreatyWillingToOffer(ePlayer)) { case NO_PEACE_TREATY_TYPE: strTemp.Format(""); break; case PEACE_TREATY_WHITE_PEACE: strTemp.Format("PeaceOff WhitePeace(0)"); break; case PEACE_TREATY_ARMISTICE: strTemp.Format("PeaceOff Armistice(1)"); break; case PEACE_TREATY_SETTLEMENT: strTemp.Format("PeaceOff Settlement(2)"); break; case PEACE_TREATY_BACKDOWN: strTemp.Format("PeaceOff Backdown(3)"); break; case PEACE_TREATY_SUBMISSION: strTemp.Format("PeaceOff Submission(4)"); break; case PEACE_TREATY_SURRENDER: strTemp.Format("PeaceOff Surrender(5)"); break; case PEACE_TREATY_CESSION: strTemp.Format("PeaceOff Cession(6)"); break; case PEACE_TREATY_CAPITULATION: strTemp.Format("PeaceOff CAPITULATION(7)"); break; case PEACE_TREATY_UNCONDITIONAL_SURRENDER: strTemp.Format("PeaceOff **SURRENDER(8)**"); break; default: strTemp.Format("PeaceOff Unknown"); break; } } strString += ", " + strTemp; } /// Log Peace Treaty Willing to Accept void CvDiplomacyAI::LogWarPeaceWillingToAccept(CvString& strString, PlayerTypes ePlayer) const { CvString strTemp; // No Minor Civs if(GET_PLAYER(ePlayer).isMinorCiv()) { strTemp.Format(""); } else { // War Goal switch(GetTreatyWillingToAccept(ePlayer)) { case NO_PEACE_TREATY_TYPE: strTemp.Format(""); break; case PEACE_TREATY_WHITE_PEACE: strTemp.Format("PeaceAcc WhitePeace(0)"); break; case PEACE_TREATY_ARMISTICE: strTemp.Format("PeaceAcc Armistice(1)"); break; case PEACE_TREATY_SETTLEMENT: strTemp.Format("PeaceAcc Settlement(2)"); break; case PEACE_TREATY_BACKDOWN: strTemp.Format("PeaceAcc Backdown(3)"); break; case PEACE_TREATY_SUBMISSION: strTemp.Format("PeaceAcc Submission(4)"); break; case PEACE_TREATY_SURRENDER: strTemp.Format("PeaceAcc Surrender(5)"); break; case PEACE_TREATY_CESSION: strTemp.Format("PeaceAcc Cession(6)"); break; case PEACE_TREATY_CAPITULATION: strTemp.Format("PeaceAcc CAPITULATION(7)"); break; case PEACE_TREATY_UNCONDITIONAL_SURRENDER: strTemp.Format("PeaceAcc **SURRENDER(8)**"); break; default: strTemp.Format("PeaceAcc Unknown"); break; } } strString += ", " + strTemp; } /// Log War State void CvDiplomacyAI::LogWarState(CvString& strString, PlayerTypes ePlayer) { CvString strTemp; bool bShowOperationProgress = false; // Log progress towards Sneak Attack Operation launch if we're not yet at war if (IsWantsSneakAttack(ePlayer)) { CvAIOperation* pOperation = GetPlayer()->getFirstOffensiveAIOperation(ePlayer); if(pOperation) { bShowOperationProgress = true; int iOperationPercentMustered = pOperation->PercentFromMusterPointToTarget(); if(iOperationPercentMustered > 0) { strTemp.Format("PREP PRGS %2d", iOperationPercentMustered); } else { strTemp.Format("PREP Gathering"); } } } // Preparing a demand? if (GetDemandTargetPlayer() == ePlayer) { CvAIOperation* pOperation = GetPlayer()->getFirstOffensiveAIOperation(ePlayer); if(pOperation) { bShowOperationProgress = true; int iOperationPercentMustered = pOperation->PercentFromMusterPointToTarget(); if(iOperationPercentMustered > 0) { strTemp.Format("DMND PRGS %2d", iOperationPercentMustered); } else { strTemp.Format("DMND Gathering"); } } } if(!bShowOperationProgress) { // War State switch(GetWarState(ePlayer)) { case WAR_STATE_CALM: strTemp.Format("WST Calm"); break; case WAR_STATE_NEARLY_WON: strTemp.Format("WST Nearly Won"); break; case WAR_STATE_OFFENSIVE: strTemp.Format("WST Offensive"); break; case WAR_STATE_STALEMATE: strTemp.Format("WST Stalemate"); break; case WAR_STATE_TROUBLED: strTemp.Format("WST Troubled"); break; case WAR_STATE_DEFENSIVE: strTemp.Format("WST DEFENSIVE"); break; case WAR_STATE_NEARLY_DEFEATED: strTemp.Format("WST **NEARLY DEFEATED**"); break; default: if(IsAtWar(ePlayer)) { strTemp.Format("WST Unknown"); } else { strTemp.Format("WST None"); } break; } } strString += ", " + strTemp; } /// Log Military Aggressive Posture void CvDiplomacyAI::LogMilitaryAggressivePosture(CvString& strString, PlayerTypes ePlayer) const { CvString strTemp; // Military Aggressive Posture switch(GetMilitaryAggressivePosture(ePlayer)) { case AGGRESSIVE_POSTURE_NONE: strTemp.Format("MAGG None"); break; case AGGRESSIVE_POSTURE_LOW: strTemp.Format("MAGG Low"); break; case AGGRESSIVE_POSTURE_MEDIUM: strTemp.Format("MAGG Medium"); break; case AGGRESSIVE_POSTURE_HIGH: strTemp.Format("MAGG HIGH"); break; case AGGRESSIVE_POSTURE_INCREDIBLE: strTemp.Format("MAGG **INCREDIBLE**"); break; default: strTemp.Format("MAGG Unknown"); break; } strString += ", " + strTemp; } /// Log Plot Buying Aggressive Posture void CvDiplomacyAI::LogPlotBuyingAggressivePosture(CvString& strString, PlayerTypes ePlayer) const { CvString strTemp; // Plot Buying Aggressive Posture switch(GetPlotBuyingAggressivePosture(ePlayer)) { case AGGRESSIVE_POSTURE_NONE: strTemp.Format("BAGG None"); break; case AGGRESSIVE_POSTURE_LOW: strTemp.Format("BAGG Low"); break; case AGGRESSIVE_POSTURE_MEDIUM: strTemp.Format("BAGG Medium"); break; case AGGRESSIVE_POSTURE_HIGH: strTemp.Format("BAGG HIGH"); break; case AGGRESSIVE_POSTURE_INCREDIBLE: strTemp.Format("BAGG **INCREDIBLE**"); break; default: strTemp.Format("BAGG Unknown"); break; } strString += ", " + strTemp; } /// Log Land Dispute void CvDiplomacyAI::LogLandDispute(CvString& strString, PlayerTypes ePlayer) const { CvString strTemp; // Don't fill out this field for Minors, as it just makes the log harder to read if(GET_PLAYER(ePlayer).isMinorCiv()) { strTemp.Format(""); } else { switch(GetLandDisputeLevel(ePlayer)) { case DISPUTE_LEVEL_FIERCE: strTemp.Format("LND **FIERCE**"); break; case DISPUTE_LEVEL_STRONG: strTemp.Format("LND STRONG"); break; case DISPUTE_LEVEL_WEAK: strTemp.Format("LND Weak"); break; case DISPUTE_LEVEL_NONE: strTemp.Format("LND N"); break; default: strTemp.Format("LND Unknown"); break; } } strString += ", " + strTemp; } /// Log Victory Dispute void CvDiplomacyAI::LogVictoryDispute(CvString& strString, PlayerTypes ePlayer) const { CvString strTemp; // Don't fill out this field for Minors, as it just makes the log harder to read if(GET_PLAYER(ePlayer).isMinorCiv()) { strTemp.Format(""); } else { // Victory Dispute switch(GetVictoryDisputeLevel(ePlayer)) { case DISPUTE_LEVEL_FIERCE: strTemp.Format("VCT **FIERCE**"); break; case DISPUTE_LEVEL_STRONG: strTemp.Format("VCT STRONG"); break; case DISPUTE_LEVEL_WEAK: strTemp.Format("VCT Weak"); break; case DISPUTE_LEVEL_NONE: strTemp.Format("VCT N"); break; default: strTemp.Format("VCT Unknown"); break; } } strString += ", " + strTemp; } /// Log Victory Block void CvDiplomacyAI::LogVictoryBlock(CvString& strString, PlayerTypes ePlayer) const { CvString strTemp; // Don't fill out this field for Minors, as it just makes the log harder to read if(GET_PLAYER(ePlayer).isMinorCiv()) { strTemp.Format(""); } else { // Victory Dispute switch(GetVictoryBlockLevel(ePlayer)) { case BLOCK_LEVEL_FIERCE: strTemp.Format("VBlock **FIERCE**"); break; case BLOCK_LEVEL_STRONG: strTemp.Format("VBlock STRONG"); break; case BLOCK_LEVEL_WEAK: strTemp.Format("VBlock Weak"); break; case BLOCK_LEVEL_NONE: strTemp.Format("VBlock N"); break; default: strTemp.Format("VBlock Unknown"); break; } } strString += ", " + strTemp; } /// Log Wonder Dispute void CvDiplomacyAI::LogWonderDispute(CvString& strString, PlayerTypes ePlayer) const { CvString strTemp; // Don't fill out this field for Minors, as it just makes the log harder to read if(GET_PLAYER(ePlayer).isMinorCiv()) { strTemp.Format(""); } else { // Victory Dispute switch(GetWonderDisputeLevel(ePlayer)) { case DISPUTE_LEVEL_FIERCE: strTemp.Format("WND **FIERCE**"); break; case DISPUTE_LEVEL_STRONG: strTemp.Format("WND STRONG"); break; case DISPUTE_LEVEL_WEAK: strTemp.Format("WND Weak"); break; case DISPUTE_LEVEL_NONE: strTemp.Format("WND N"); break; default: strTemp.Format("WND Unknown"); break; } } strString += ", " + strTemp; } /// Log Minor Civ Dispute void CvDiplomacyAI::LogMinorCivDispute(CvString& strString, PlayerTypes ePlayer) const { CvString strTemp; // Don't fill out this field for Minors, as it just makes the log harder to read if(GET_PLAYER(ePlayer).isMinorCiv()) { strTemp.Format(""); } else { // Victory Dispute switch(GetMinorCivDisputeLevel(ePlayer)) { case DISPUTE_LEVEL_FIERCE: strTemp.Format("MCF **FIERCE**"); break; case DISPUTE_LEVEL_STRONG: strTemp.Format("MCF STRONG"); break; case DISPUTE_LEVEL_WEAK: strTemp.Format("MCF Weak"); break; case DISPUTE_LEVEL_NONE: strTemp.Format("MCF N"); break; default: strTemp.Format("MCF Unknown"); break; } } strString += ", " + strTemp; } /// Log Proximity void CvDiplomacyAI::LogProximity(CvString& strString, PlayerTypes ePlayer) { CvString strTemp; // Proximity switch(GetPlayer()->GetProximityToPlayer(ePlayer)) { case PLAYER_PROXIMITY_NEIGHBORS: strTemp.Format("PRX Neighbors"); break; case PLAYER_PROXIMITY_CLOSE: strTemp.Format("PRX Close"); break; case PLAYER_PROXIMITY_FAR: strTemp.Format("PRX Far"); break; case PLAYER_PROXIMITY_DISTANT: strTemp.Format("PRX Distant"); break; default: strTemp.Format("PRX Unknown"); break; } strString += ", " + strTemp; } /// AI sent a message to someone... what is it?!?!? :o void CvDiplomacyAI::LogStatementToPlayer(PlayerTypes ePlayer, DiploStatementTypes eMessage) { if(GC.getLogging() && GC.getAILogging()) { CvString strLogName; CvString strOutBuf; CvString strBaseString; CvString playerName; CvString otherPlayerName; CvString strTemp; playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if(GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Messages_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Messages_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Turn number strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); // Our Name strBaseString += playerName; // Their Name otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); strBaseString += ", " + otherPlayerName; strOutBuf = strBaseString; CivApproachTypes eApproach = GetCivApproach(ePlayer); bool bMinorMessage = false; switch(eMessage) { case DIPLO_STATEMENT_REQUEST_PEACE: strTemp.Format("We request peace!!!"); break; case DIPLO_STATEMENT_AGGRESSIVE_MILITARY_WARNING: strTemp.Format("Your Military is stationed aggressively!"); break; case DIPLO_STATEMENT_KILLED_PROTECTED_CITY_STATE: strTemp.Format("You Killed a Protected City State!"); bMinorMessage = true; break; case DIPLO_STATEMENT_ATTACKED_PROTECTED_CITY_STATE: strTemp.Format("You Attacked a Protected City State!"); bMinorMessage = true; break; case DIPLO_STATEMENT_BULLIED_PROTECTED_CITY_STATE: strTemp.Format("You Bullied a Protected City State!"); bMinorMessage = true; break; case DIPLO_STATEMENT_EXPANSION_SERIOUS_WARNING: strTemp.Format("Expansion SERIOUS Warning!"); break; case DIPLO_STATEMENT_EXPANSION_WARNING: strTemp.Format("Expansion Warning!"); break; case DIPLO_STATEMENT_EXPANSION_BROKEN_PROMISE: strTemp.Format("Expansion Broken Promise!"); break; case DIPLO_STATEMENT_PLOT_BUYING_SERIOUS_WARNING: strTemp.Format("Plot Buying SERIOUS Warning!"); break; case DIPLO_STATEMENT_PLOT_BUYING_WARNING: strTemp.Format("Plot Buying Warning!"); break; case DIPLO_STATEMENT_PLOT_BUYING_BROKEN_PROMISE: strTemp.Format("Plot Buying Broken Promise!"); break; case DIPLO_STATEMENT_WE_ATTACKED_YOUR_MINOR: strTemp.Format("Haha! We attacked your Minor!"); bMinorMessage = true; break; case DIPLO_STATEMENT_WE_BULLIED_YOUR_MINOR: strTemp.Format("Haha! We bullied your Minor!"); bMinorMessage = true; break; case DIPLO_STATEMENT_WORK_WITH_US: strTemp.Format("Wanna team up?"); break; case DIPLO_STATEMENT_END_WORK_WITH_US: strTemp.Format("***** We're done working with you. *****"); break; case DIPLO_STATEMENT_DENOUNCE: strTemp.Format("Denounce!"); break; case DIPLO_STATEMENT_DENOUNCE_RANDFAILED: strTemp.Format("Denounce RANDFAILED"); break; case DIPLO_STATEMENT_END_WORK_AGAINST_SOMEONE: strTemp.Format("***** We're done working against someone with you. *****"); break; case DIPLO_STATEMENT_COOP_WAR_REQUEST: strTemp.Format("Wanna coop war against someone?"); break; case DIPLO_STATEMENT_COOP_WAR_TIME: strTemp.Format("It's time to coop war against someone!"); break; case DIPLO_STATEMENT_NOW_UNFORGIVABLE: strTemp.Format("***** You are Unforgivable! *****"); break; case DIPLO_STATEMENT_NOW_ENEMY: strTemp.Format("***** You are an Enemy! *****"); break; case DIPLO_STATEMENT_DEMAND: strTemp.Format("***** Give in to my demands or else! *****"); break; case DIPLO_STATEMENT_REQUEST: strTemp.Format("***** Can you spare something for a friend? *****"); break; case DIPLO_STATEMENT_REQUEST_RANDFAILED: strTemp.Format("***** Request RANDFAILED *****"); break; case DIPLO_STATEMENT_LUXURY_TRADE: strTemp.Format("***** You have a Luxury I would like *****"); break; case DIPLO_STATEMENT_OPEN_BORDERS_EXCHANGE: strTemp.Format("Open Borders Exchange"); break; case DIPLO_STATEMENT_OPEN_BORDERS_OFFER: strTemp.Format("Open Borders Offer"); break; case DIPLO_STATEMENT_RESEARCH_AGREEMENT_OFFER: strTemp.Format("Research Agreement Offer"); break; case DIPLO_STATEMENT_RENEW_DEAL: strTemp.Format("Renew Deal"); break; case DIPLO_STATEMENT_INSULT: strTemp.Format("Insult"); break; case DIPLO_STATEMENT_COMPLIMENT: if(eApproach == CIV_APPROACH_DECEPTIVE) { strTemp.Format("DECEPTIVE Compliment"); } else { strTemp.Format("Compliment"); } break; case DIPLO_STATEMENT_BOOT_KISSING: strTemp.Format("Boot Kissing"); break; case DIPLO_STATEMENT_WARMONGER: strTemp.Format("Warmonger!"); break; case DIPLO_STATEMENT_DENOUNCE_FRIEND: strTemp.Format("***** DENOUNCING A FRIEND *****"); break; case DIPLO_STATEMENT_REQUEST_FRIEND_DENOUNCE: strTemp.Format("***** DENOUNCE SOMEONE FOR ME? *****"); break; case DIPLO_STATEMENT_REQUEST_FRIEND_DENOUNCE_RANDFAILED: strTemp.Format("***** Denounce someone for me? RANDFAILED *****"); break; case DIPLO_STATEMENT_REQUEST_FRIEND_WAR: strTemp.Format("***** DECLARE WAR ON SOMEONE FOR ME? *****"); break; case DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY: strTemp.Format("***** YOU BEFRIENDED AN ENEMY OF MINE! *****"); break; case DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY_RANDFAILED: strTemp.Format("***** You befriended an enemy of mine! RANDFAILED *****"); break; case DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND: strTemp.Format("***** YOU DENOUNCED A FRIEND OF MINE! *****"); break; case DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND_RANDFAILED: strTemp.Format("***** You denounced a friend of mine! RANDFAILED *****"); break; case DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY: strTemp.Format("***** YAY - YOU DENOUNCED AN ENEMY! *****"); break; case DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY_RANDFAILED: strTemp.Format("***** Yay - you denounced an enemy! RANDFAILED *****"); break; case DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND: strTemp.Format("***** YAY - YOU BEFRIENDED A FRIEND! *****"); break; case DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND_RANDFAILED: strTemp.Format("***** Yay - you befriended a friend! RANDFAILED *****"); break; case DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_ENEMY: strTemp.Format("***** JUST FYI - I BEFRIENDED YOUR ENEMY! *****"); break; case DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_ENEMY_RANDFAILED: strTemp.Format("***** Just FYI - I befriended your enemy! RANDFAILED *****"); break; case DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND: strTemp.Format("***** JUST FYI - I DENOUNCED YOUR FRIEND! *****"); break; case DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND_RANDFAILED: strTemp.Format("***** Just FYI - I denounced your friend! RANDFAILED *****"); break; case DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY: strTemp.Format("***** JUST FYI - I DENOUNCED YOUR ENEMY! *****"); break; case DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY_RANDFAILED: strTemp.Format("***** Just FYI - I denounced your enemy! RANDFAILED *****"); break; case DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND: strTemp.Format("***** JUST FYI - I BEFRIENDED YOUR FRIEND! *****"); break; case DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND_RANDFAILED: strTemp.Format("***** Just FYI - I befriended your friend! RANDFAILED *****"); break; case DIPLO_STATEMENT_SAME_POLICIES_AUTOCRACY: case DIPLO_STATEMENT_SAME_POLICIES_FREEDOM: case DIPLO_STATEMENT_SAME_POLICIES_ORDER: strTemp.Format("***** Yay! We like the same late game social policy tree *****"); break; case DIPLO_STATEMENT_WE_LIKED_THEIR_PROPOSAL: strTemp.Format("***** We liked their World Congress proposal *****"); break; case DIPLO_STATEMENT_WE_DISLIKED_THEIR_PROPOSAL: strTemp.Format("***** We disliked their World Congress proposal *****"); break; case DIPLO_STATEMENT_THEY_SUPPORTED_OUR_PROPOSAL: strTemp.Format("***** They supported our World Congress proposal *****"); break; case DIPLO_STATEMENT_THEY_FOILED_OUR_PROPOSAL: strTemp.Format("***** They foiled our World Congress proposal *****"); break; case DIPLO_STATEMENT_THEY_SUPPORTED_OUR_HOSTING: strTemp.Format("***** They supported our World Congress hosting *****"); break; case DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM: case DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER: case DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY: strTemp.Format("***** Your ideology is causing civil unrest in our civ *****"); break; case DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM: case DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER: case DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY: strTemp.Format("***** Taunt - our ideology is causing unrest in your civ *****"); break; case DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_FREEDOM: case DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_ORDER: case DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_AUTOCRACY: strTemp.Format("***** Yay! They switched to our ideology *****"); break; case DIPLO_STATEMENT_YOUR_CULTURE_INFLUENTIAL: strTemp.Format("***** Your culture is now influential over us *****"); break; case DIPLO_STATEMENT_OUR_CULTURE_INFLUENTIAL: strTemp.Format("***** Taunt - Our culture is now influential over you *****"); break; case DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONQUEST: strTemp.Format("***** Taunt - We are coming for you with guns! *****"); break; case DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONGRESS: strTemp.Format("***** Taunt - We will rule the world through politics! *****"); break; case DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_CULTURE: strTemp.Format("***** Taunt - Our culture is becoming unstoppable! *****"); break; case DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_SPACESHIP: strTemp.Format("***** Taunt - Our spaceship will be the first to launch! *****"); break; case DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_CONFUSED: strTemp.Format("***** Taunt - We don't know what you're doing, but we are grumpy! *****"); break; case DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_WORLD_CONQUEST: strTemp.Format("***** Taunt - We don't want you to win war! *****"); break; case DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_WORLD_CONGRESS: strTemp.Format("***** Taunt - We don't want you to win WC! *****"); break; case DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_CULTURE: strTemp.Format("***** Taunt - We don't want you to win culture! *****"); break; case DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_SPACESHIP: strTemp.Format("***** Taunt - We don't want you to win spaceship! *****"); break; case DIPLO_STATEMENT_DOF_BB: strTemp.Format("DOF offer - Battle Brothers"); break; case DIPLO_STATEMENT_DOF_ALLIES: strTemp.Format("DOF offer - Allies"); break; case DIPLO_STATEMENT_DOF_FRIENDS: strTemp.Format("DOF offer - Friends"); break; case DIPLO_STATEMENT_DOF_UNTRUSTWORTHY: strTemp.Format("DOF offer - Untrustworthy"); break; case DIPLO_STATEMENT_STRATEGIC_TRADE: strTemp.Format("Strategic Resource Trade"); break; case DIPLO_STATEMENT_DEFENSIVE_PACT_REQUEST: strTemp.Format("Defensive Pact Offer"); break; case DIPLO_STATEMENT_TRADE_CITIES_REQUEST: strTemp.Format("Trade Cities Offer"); break; case DIPLO_STATEMENT_EXCHANGE_CITIES: strTemp.Format("Exchange Cities Offer"); break; case DIPLO_STATEMENT_THIRDPARTY_PEACE_REQUEST: strTemp.Format("3rd Party PEACE Offer"); break; case DIPLO_STATEMENT_THIRDPARTY_WAR_REQUEST: strTemp.Format("3rd Party WAR Offer"); break; case DIPLO_STATEMENT_VOTE_REQUEST: strTemp.Format("Vote Buy Offer"); break; case DIPLO_STATEMENT_EMBASSY_EXCHANGE: strTemp.Format("***** Embassy Exchange *****"); break; case DIPLO_STATEMENT_EMBASSY_OFFER: strTemp.Format("***** Embassy Offer *****"); break; case DIPLO_STATEMENT_MINOR_CIV_COMPETITION: strTemp.Format("***** MINOR CIV COMPETITION *****"); break; case DIPLO_STATEMENT_SHARE_INTRIGUE: strTemp.Format("***** Share Intrigue *****"); break; case DIPLO_STATEMENT_CAUGHT_YOUR_SPY: strTemp.Format("***** SPY CAUGHT *****"); break; case DIPLO_STATEMENT_KILLED_YOUR_SPY: strTemp.Format("***** KILLED YOUR SPY *****"); break; case DIPLO_STATEMENT_KILLED_MY_SPY: strTemp.Format("***** KILLED MY SPY *****"); break; case DIPLO_STATEMENT_STOP_DIGGING: strTemp.Format("***** STOP DIGGING WARNING *****"); break; case DIPLO_STATEMENT_STOP_CONVERSIONS: strTemp.Format("***** STOP CONVERSIONS! *****"); break; case DIPLO_STATEMENT_GENEROUS_OFFER: strTemp.Format("***** We would like to offer you a gift. *****"); break; case DIPLO_STATEMENT_GENEROUS_OFFER_RANDFAILED: strTemp.Format("***** Generous Offer RANDFAILED *****"); break; case DIPLO_STATEMENT_MAPS_OFFER: strTemp.Format("*****Maps Offer*****"); break; case DIPLO_STATEMENT_TECH_EXCHANGE: strTemp.Format("*****Technology Exchange*****"); break; case DIPLO_STATEMENT_TECH_OFFER: strTemp.Format("*****You have a technology I would like*****"); break; case DIPLO_STATEMENT_BECOME_MY_VASSAL: strTemp.Format("***** BECOME MY VASSAL *****"); break; case DIPLO_STATEMENT_ACCEPT_VASSALAGE: strTemp.Format("***** BECOME YOUR VASSAL *****"); break; case DIPLO_STATEMENT_REVOKE_VASSALAGE: strTemp.Format("***** REVOKING VASSALAGE *****"); break; case DIPLO_STATEMENT_REVOKE_VASSALAGE_THIRD_PARTY: strTemp.Format("***** REVOKING VASSALAGE VIA TRADE *****"); break; case DIPLO_STATEMENT_VASSAL_TAXES_RAISED_HUMAN_MASTER: strTemp.Format("***** HUMAN RAISED MY TAXES! *****"); break; case DIPLO_STATEMENT_VASSAL_TAXES_RAISED_AI_MASTER: strTemp.Format("***** I RAISED HUMAN'S TAXES! *****"); break; case DIPLO_STATEMENT_VASSAL_TAXES_LOWERED_HUMAN_MASTER: strTemp.Format("***** HUMAN RAISED MY TAXES! *****"); break; case DIPLO_STATEMENT_VASSAL_TAXES_LOWERED_AI_MASTER: strTemp.Format("***** I LOWERED HUMAN'S TAXES! *****"); break; case DIPLO_STATEMENT_LIBERATE_VASSAL: strTemp.Format("***** LIBERATED VASSAL! *****"); break; default: strTemp.Format("Unknown message!!! %d", eMessage); break; } strOutBuf += ", " + strTemp; // Don't log these messages for now - they have their own function if(eMessage != DIPLO_STATEMENT_DENOUNCE && eMessage != DIPLO_STATEMENT_COOP_WAR_REQUEST) pLog->Msg(strOutBuf); // Also send message to Minor Civ log if applicable if(bMinorMessage) { // Open the log file if(GC.getPlayerAndCityAILogSplit()) strLogName = "DiplomacyAI_MinorCiv_Log_" + playerName + ".csv"; else strLogName = "DiplomacyAI_MinorCiv_Log.csv"; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); pLog->Msg(strOutBuf); } } } // ------------------------------------------------------------------------------------- void CvDiplomacyAI::TestUIDiploStatement(PlayerTypes eToPlayer, DiploStatementTypes eStatement, int iArg1) { m_eTestToPlayer = eToPlayer; m_eTestStatement = eStatement; m_iTestStatementArg1 = iArg1; } void CvDiplomacyAI::LogMinorStatusChange(PlayerTypes eMinor, const char* msg) { if (GC.getLogging() && GC.getAILogging()) { CvString strLogName = GC.getDiploMinorLogFileName(GetPlayer()); CvString playerName = GetPlayer()->getCivilizationShortDescription(); CvString strTemp; FILogFile* pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Turn number CvString strBaseString; strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); // Our Name strBaseString += playerName; // Their Name CvString otherPlayerName = GET_PLAYER(eMinor).getCivilizationShortDescription(); strBaseString += ", " + otherPlayerName; CvString strOutBuf = strBaseString; // Actual Info (friends/allies gained/lost) strOutBuf += ", "; strOutBuf += msg; pLog->Msg(strOutBuf); } } // ------------------------------------------------------------------------------------- void CvDiplomacyAI::LogOpenEmbassy(PlayerTypes ePlayer) { if(GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString playerName; CvString otherPlayerName; CvString strDesc; CvString strLogName; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if(GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Messages_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Messages_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName + ", "; otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); strOutBuf = strBaseString + ",***** OPENED EMBASSY @ " + otherPlayerName + "! *****"; pLog->Msg(strOutBuf); } } // ------------------------------------------------------------------------------------- void CvDiplomacyAI::LogCloseEmbassy(PlayerTypes ePlayer) { if(GC.getLogging() && GC.getAILogging()) { CvString strOutBuf; CvString strBaseString; CvString playerName; CvString otherPlayerName; CvString strDesc; CvString strLogName; // Find the name of this civ and city playerName = GetPlayer()->getCivilizationShortDescription(); // Open the log file if(GC.getPlayerAndCityAILogSplit()) { strLogName = "DiplomacyAI_Messages_Log_" + playerName + ".csv"; } else { strLogName = "DiplomacyAI_Messages_Log.csv"; } FILogFile* pLog = NULL; pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp); // Get the leading info for this line strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns()); strBaseString += playerName + ", "; otherPlayerName = GET_PLAYER(ePlayer).getCivilizationShortDescription(); strOutBuf = strBaseString + ",***** CLOSED EMBASSY @ " + otherPlayerName + "! *****"; pLog->Msg(strOutBuf); } } // ------------------------------------------------------------------------------------- // Returns true if the target is valid to show a UI to immediately. // This will return true if the source and destination are both AI. bool CvDiplomacyAI::IsValidUIDiplomacyTarget(PlayerTypes eTargetPlayer) { if(eTargetPlayer != NO_PLAYER) { CvPlayer& kTarget = GET_PLAYER(eTargetPlayer); if (m_eDiploMode == DIPLO_ALL_PLAYERS || (m_eDiploMode == DIPLO_SPECIFIC_PLAYER && m_eTargetPlayer == eTargetPlayer) || (m_eDiploMode == DIPLO_AI_PLAYERS && !kTarget.isHuman(ISHUMAN_AI_DIPLOMACY)) || (m_eDiploMode == DIPLO_HUMAN_PLAYERS && kTarget.isHuman(ISHUMAN_AI_DIPLOMACY))) return true; } return false; } // AI HELPER ROUTINES int CvDiplomacyAIHelpers::GetWarmongerTriggerPenalty(PlayerTypes eWarmonger, TeamTypes eDefendingTeam, PlayerTypes eObserver, WarmongerTriggerTypes eWarmongerTrigger) { if (eObserver < 0 || eObserver >= MAX_MAJOR_CIVS || !GET_PLAYER(eObserver).isAlive() || !GET_PLAYER(eObserver).isMajorCiv()) return 0; int iWarmongerValue = 0; switch (eWarmongerTrigger) { case WARMONGER_MAJOR_ATTACKED: { iWarmongerValue = /*10*/ GD_INT_GET(WARMONGER_THREAT_MAJOR_ATTACKED_WEIGHT); iWarmongerValue += GET_PLAYER(eObserver).GetDiplomacyAI()->GetOtherPlayerNumMajorsAttacked(eWarmonger) * /*10*/ GD_INT_GET(WARMONGER_THREAT_MAJOR_ATTACKED_WEIGHT) / 2; break; } case WARMONGER_MINOR_ATTACKED: { iWarmongerValue = /*5*/ GD_INT_GET(WARMONGER_THREAT_MINOR_ATTACKED_WEIGHT); iWarmongerValue += GET_PLAYER(eObserver).GetDiplomacyAI()->GetOtherPlayerNumMinorsAttacked(eWarmonger) * /*5*/ GD_INT_GET(WARMONGER_THREAT_MINOR_ATTACKED_WEIGHT) / 2; break; } case WARMONGER_NUKED_PLAYER: { iWarmongerValue = /*20*/ GD_INT_GET(WARMONGER_THREAT_USED_NUKE_WEIGHT); iWarmongerValue += GET_PLAYER(eObserver).GetDiplomacyAI()->GetNumTimesNuked(eWarmonger) * /*20*/ GD_INT_GET(WARMONGER_THREAT_USED_NUKE_WEIGHT); break; } default: { return 0; } } if (iWarmongerValue <= 0) return 0; int iWarmongerStrengthModifier = 0; // INCREASE if he's big and nasty, DECREASE if he's not. switch (GET_PLAYER(eObserver).GetDiplomacyAI()->GetMilitaryStrengthComparedToUs(eWarmonger)) { case STRENGTH_IMMENSE: iWarmongerStrengthModifier = /*100*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_IMMENSE); break; case STRENGTH_POWERFUL: iWarmongerStrengthModifier = /*75*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_POWERFUL); break; case STRENGTH_STRONG: iWarmongerStrengthModifier = /*50*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_STRONG); break; case STRENGTH_AVERAGE: iWarmongerStrengthModifier = /*33*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_AVERAGE); break; case STRENGTH_POOR: iWarmongerStrengthModifier = /*0*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_POOR); break; case STRENGTH_WEAK: iWarmongerStrengthModifier = /*-25*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_WEAK); break; case STRENGTH_PATHETIC: iWarmongerStrengthModifier = /*-50*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_PATHETIC); break; } // Do any additional factors affect this amount? bool bSanctioned = false; TeamTypes eTeam = GET_PLAYER(eObserver).getTeam(); CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (eWarmongerTrigger != WARMONGER_MINOR_ATTACKED) { if (eTeam != eDefendingTeam && !GET_TEAM(eDefendingTeam).IsVassal(eTeam) && !GET_TEAM(eTeam).IsVassal(eDefendingTeam) && !GET_TEAM(eTeam).IsHasDefensivePact(eDefendingTeam)) { StrengthTypes eHighestStrength = STRENGTH_PATHETIC; vector vDefendingTeam = GET_TEAM(eDefendingTeam).getPlayers(); for (size_t i=0; iGetMilitaryStrengthComparedToUs(vDefendingTeam[i]); if (eStrength > eHighestStrength) { eHighestStrength = eStrength; } if (pLeague && pLeague->IsTradeEmbargoed(eObserver, vDefendingTeam[i])) { bSanctioned = true; } } // DECREASE if opponent is big and nasty. switch (eHighestStrength) { case STRENGTH_IMMENSE: iWarmongerStrengthModifier += /*-75*/ GD_INT_GET(WARMONGER_THREAT_DEFENDER_STRENGTH_IMMENSE); break; case STRENGTH_POWERFUL: iWarmongerStrengthModifier += /*-50*/ GD_INT_GET(WARMONGER_THREAT_DEFENDER_STRENGTH_POWERFUL); break; case STRENGTH_STRONG: iWarmongerStrengthModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_DEFENDER_STRENGTH_STRONG); break; case STRENGTH_AVERAGE: iWarmongerStrengthModifier += /*0*/ GD_INT_GET(WARMONGER_THREAT_DEFENDER_STRENGTH_AVERAGE); break; case STRENGTH_POOR: case STRENGTH_WEAK: case STRENGTH_PATHETIC: break; // Not applicable. } } } else if (pLeague && GC.getGame().GetGameLeagues()->IsCityStateEmbargo(eObserver)) { bSanctioned = true; } iWarmongerValue *= max(0, 100 + iWarmongerStrengthModifier); iWarmongerValue /= 100; if (pLeague) { // DECREASE if opponent is sanctioned. if (bSanctioned && MOD_BALANCE_VP) { iWarmongerValue *= /*50*/ GD_INT_GET(WARMONGER_THREAT_ATTACKED_SANCTIONED_PLAYER); iWarmongerValue /= 100; } // Casus Belli? if (GC.getGame().GetGameLeagues()->IsWorldWar(eObserver) > 0) { iWarmongerValue *= /*50*/ GD_INT_GET(WARMONGER_THREAT_ATTACKED_WEIGHT_WORLD_WAR); iWarmongerValue /= 100; } // Global Peace Accords? else if (GC.getGame().GetGameLeagues()->GetUnitMaintenanceMod(eObserver) > 0) { iWarmongerValue *= /*200*/ GD_INT_GET(WARMONGER_THREAT_ATTACKED_WEIGHT_WORLD_PEACE); iWarmongerValue /= 100; } } iWarmongerValue *= GC.getEraInfo(GC.getGame().getCurrentEra())->getWarmongerPercent(); iWarmongerValue /= 100; return iWarmongerValue; } void CvDiplomacyAIHelpers::ApplyWarmongerPenalties(CvCity* pCity, PlayerTypes eConqueror, PlayerTypes eCityOwner) { if (!pCity || eConqueror == NO_PLAYER || !GET_PLAYER(eConqueror).isMajorCiv() || eCityOwner == NO_PLAYER || GET_PLAYER(eCityOwner).isBarbarian()) return; for (int iObservingMajorLoop = 0; iObservingMajorLoop < MAX_MAJOR_CIVS; iObservingMajorLoop++) { PlayerTypes eObserver = (PlayerTypes) iObservingMajorLoop; if (GET_PLAYER(eObserver).getTeam() != GET_PLAYER(eConqueror).getTeam() && GET_PLAYER(eObserver).isAlive() && GET_PLAYER(eObserver).isMajorCiv()) { int iWarmonger = GetCityWarmongerValue(pCity, eConqueror, eCityOwner, eObserver); if (iWarmonger > 0) { //CUSTOMLOG("WarmongerTimes100: Total=%i", iWarmonger); GET_PLAYER(eObserver).GetDiplomacyAI()->ChangeOtherPlayerWarmongerAmountTimes100(eConqueror, iWarmonger * 100); GET_PLAYER(eObserver).GetDiplomacyAI()->DoUpdateWarmongerThreats(true); } } } } void CvDiplomacyAIHelpers::ApplyLiberationBonuses(CvCity* pCity, PlayerTypes eLiberator, PlayerTypes eNewOwner) { if (!pCity || eLiberator == NO_PLAYER || !GET_PLAYER(eLiberator).isMajorCiv() || eNewOwner == NO_PLAYER || GET_PLAYER(eNewOwner).isBarbarian()) return; for (int iObservingMajorLoop = 0; iObservingMajorLoop < MAX_MAJOR_CIVS; iObservingMajorLoop++) { PlayerTypes eObserver = (PlayerTypes) iObservingMajorLoop; if (GET_PLAYER(eObserver).getTeam() != GET_PLAYER(eLiberator).getTeam() && GET_PLAYER(eObserver).isAlive() && GET_PLAYER(eObserver).isMajorCiv()) { int iLiberation = GetCityLiberationValue(pCity, eLiberator, eNewOwner, eObserver); if (iLiberation < 0) { //CUSTOMLOG("LiberationTimes100: Total=%i", iLiberation); GET_PLAYER(eObserver).GetDiplomacyAI()->ChangeOtherPlayerWarmongerAmountTimes100(eLiberator, iLiberation * 100); GET_PLAYER(eObserver).GetDiplomacyAI()->DoUpdateWarmongerThreats(true); } } } } int CvDiplomacyAIHelpers::GetCityWarmongerValue(CvCity* pCity, PlayerTypes eConqueror, PlayerTypes eCityOwner, PlayerTypes eObserver) { CvDiplomacyAI* pDiplo = GET_PLAYER(eObserver).GetDiplomacyAI(); // Must have met the conqueror and conqueree - no cheating! if (!pDiplo->IsHasMet(eConqueror) || !pDiplo->IsHasMet(eCityOwner)) return 0; // Masters and vassals ignore each other's warmongering. if (pDiplo->IsMaster(eConqueror) || pDiplo->IsVassal(eConqueror)) return 0; // UI Only! // Captured a city from barbs? Everyone likes that! if (eCityOwner == BARBARIAN_PLAYER) return 0; // Don't award warmongering for reconquering a player's own city if (pCity->getOriginalOwner() == eConqueror) return 0; // Don't award warmongering for reconquering a city this player owned last if (pCity->getPreviousOwner() == eConqueror) return 0; // END UI Only! // Are we at war with the conquered player? if (pDiplo->IsAtWar(eCityOwner)) { // If our warscore is negative, ignore warmongering. They've done us a favor! vector vMyTeam = GET_TEAM(GET_PLAYER(eObserver).getTeam()).getPlayers(); for (size_t i=0; i 0 && GET_PLAYER(vMyTeam[i]).GetDiplomacyAI()->GetWarScore(eCityOwner) < 0) return 0; } } // Cities are rated on a percentage scale, where 0 = worthless, 50 = equal value with median city, 100 = twice the value of the median city. // Value is capped between 5 and 100. int iMedianEconomicPower = GC.getGame().getMedianEconomicValue(); int iLocalEconomicPower = pCity->getEconomicValue(eCityOwner); int iWarmongerValue = range(((iLocalEconomicPower * 100) / max(1, iMedianEconomicPower) / 2), 5, 100); // Modders can change this value to apply a multiplier to the worth of all cities iWarmongerValue *= /*100*/ GD_INT_GET(WARMONGER_THREAT_CITY_VALUE_MULTIPLIER); iWarmongerValue /= 100; // Original major capitals are worth more. if (pCity->IsOriginalCapitalForPlayer(eCityOwner) && pCity->IsOriginalMajorCapital()) { iWarmongerValue *= /*150*/ GD_INT_GET(WARMONGER_THREAT_CAPITAL_CITY_PERCENT); iWarmongerValue /= 100; } // Reduce value significantly if conqueror has owned the city before. int iNumTimesOwned = pCity->GetNumTimesOwned(eConqueror); if (pCity->IsNoWarmongerYet()) iNumTimesOwned--; if (iNumTimesOwned > 0) iWarmongerValue /= iNumTimesOwned + 2; if (iWarmongerValue <= 0) iWarmongerValue = 1; // //////////////////////////////////// // STATUS MODIFIER : this one mainly deals with defensive pacts and vassalage // //////////////////////////////////// int iWarmongerStatusModifier = 100; bool bHisLossIsOurOwn = false; // Vassalage? if (GET_PLAYER(eCityOwner).isMajorCiv() && (pDiplo->IsVassal(eCityOwner) || pDiplo->IsMaster(eCityOwner))) { iWarmongerStatusModifier = /*200*/ GD_INT_GET(WARMONGER_THREAT_SHARED_FATE_PERCENT); bHisLossIsOurOwn = true; } // Alliances? else if (GET_PLAYER(eCityOwner).isMajorCiv()) { // The observing player is in a co-op war with the attacker against the defender - this trumps everything! if (pDiplo->GetCoopWarState(eConqueror, eCityOwner) == COOP_WAR_STATE_ONGOING) { iWarmongerStatusModifier = /*10*/ GD_INT_GET(WARMONGER_THREAT_COOP_WAR_PERCENT); } // The observing player is in a co-op war with the defender against the attacker, or has a Defensive Pact/is a teammate - his loss is our own! else if (pDiplo->GetCoopWarState(eCityOwner, eConqueror) == COOP_WAR_STATE_ONGOING || pDiplo->IsHasDefensivePact(eCityOwner) || GET_PLAYER(eObserver).getTeam() == GET_PLAYER(eCityOwner).getTeam()) { iWarmongerStatusModifier = max(100, /*200*/ GD_INT_GET(WARMONGER_THREAT_SHARED_FATE_PERCENT)); bHisLossIsOurOwn = true; } // Care less if we're also at war with the city owner else if (pDiplo->IsAtWar(eCityOwner)) { iWarmongerStatusModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); } } else if (pDiplo->IsAtWar(eCityOwner)) { iWarmongerStatusModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); } iWarmongerValue *= max(0, iWarmongerStatusModifier); iWarmongerValue /= 100; if (iWarmongerValue <= 0) return 0; // //////////////////////////////////// // STRENGTH MODIFIER : this one mainly deals with relative power // //////////////////////////////////// int iWarmongerStrengthModifier = 0; // INCREASE if he's big and nasty, DECREASE if he's not. switch (pDiplo->GetMilitaryStrengthComparedToUs(eConqueror)) { case STRENGTH_IMMENSE: iWarmongerStrengthModifier = /*100*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_IMMENSE); break; case STRENGTH_POWERFUL: iWarmongerStrengthModifier = /*75*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_POWERFUL); break; case STRENGTH_STRONG: iWarmongerStrengthModifier = /*50*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_STRONG); break; case STRENGTH_AVERAGE: iWarmongerStrengthModifier = /*33*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_AVERAGE); break; case STRENGTH_POOR: iWarmongerStrengthModifier = /*0*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_POOR); break; case STRENGTH_WEAK: iWarmongerStrengthModifier = /*-25*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_WEAK); break; case STRENGTH_PATHETIC: iWarmongerStrengthModifier = /*-50*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_PATHETIC); break; } // No strength-based reductions if it's our loss... if (bHisLossIsOurOwn && iWarmongerStrengthModifier < 0) iWarmongerStrengthModifier = 0; if (!bHisLossIsOurOwn) { // DECREASE if opponent is big and nasty. switch (pDiplo->GetMilitaryStrengthComparedToUs(eCityOwner)) { case STRENGTH_IMMENSE: iWarmongerStrengthModifier += /*-75*/ GD_INT_GET(WARMONGER_THREAT_DEFENDER_STRENGTH_IMMENSE); break; case STRENGTH_POWERFUL: iWarmongerStrengthModifier += /*-50*/ GD_INT_GET(WARMONGER_THREAT_DEFENDER_STRENGTH_POWERFUL); break; case STRENGTH_STRONG: iWarmongerStrengthModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_DEFENDER_STRENGTH_STRONG); break; case STRENGTH_AVERAGE: iWarmongerStrengthModifier += /*0*/ GD_INT_GET(WARMONGER_THREAT_DEFENDER_STRENGTH_AVERAGE); break; case STRENGTH_POOR: case STRENGTH_WEAK: case STRENGTH_PATHETIC: break; // Not applicable. } } iWarmongerValue *= max(25, 100 + iWarmongerStrengthModifier); iWarmongerValue /= 100; if (iWarmongerValue <= 0) return 0; // //////////////////////////////////// // POLITICAL MODIFIER : this is the big one - geopolitics etc. // //////////////////////////////////// int iWarmongerPoliticalModifier = 0; if (!bHisLossIsOurOwn) { // City-State if (GET_PLAYER(eCityOwner).isMinorCiv()) { CvMinorCivAI* pMinorAI = GET_PLAYER(eCityOwner).GetMinorCivAI(); // Allies? if (pMinorAI->GetEffectiveFriendshipWithMajor(eObserver) >= /*60*/ GD_INT_GET(FRIENDSHIP_THRESHOLD_ALLIES) || pMinorAI->GetPermanentAlly() == eObserver) { iWarmongerPoliticalModifier += /*75*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_LARGE); } else if (pMinorAI->IsFriends(eObserver)) { iWarmongerPoliticalModifier += /*50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_MEDIUM); } else { iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); } if (pMinorAI->IsProtectedByMajor(eObserver)) { iWarmongerPoliticalModifier += /*50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_MEDIUM); } else if (pMinorAI->GetEffectiveFriendshipWithMajor(eObserver) < /*60*/ GD_INT_GET(FRIENDSHIP_THRESHOLD_ALLIES)) { iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); } } // Major Civ else { // Are we friends with the city owner? if (pDiplo->IsDoFAccepted(eCityOwner) || pDiplo->IsHasDefensivePact(eCityOwner)) { switch (pDiplo->GetDoFType(eCityOwner)) { case DOF_TYPE_UNTRUSTWORTHY: case DOF_TYPE_NEW: break; case DOF_TYPE_FRIENDS: iWarmongerPoliticalModifier += /*25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_SMALL); break; case DOF_TYPE_ALLIES: iWarmongerPoliticalModifier += /*50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_MEDIUM); break; case DOF_TYPE_BATTLE_BROTHERS: iWarmongerPoliticalModifier += /*75*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_LARGE); break; } } else { // Have we denounced the city owner? if (pDiplo->IsDenouncedPlayer(eCityOwner)) { iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); } // Have we BEEN denounced by the city owner? if (pDiplo->IsDenouncedByPlayer(eCityOwner)) { iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); } // Is this a backstabber? if (pDiplo->IsUntrustworthy(eCityOwner)) { iWarmongerPoliticalModifier += /*-50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_MEDIUM); } } // Are we friends with the city taker? if (pDiplo->IsDoFAccepted(eConqueror) || pDiplo->IsHasDefensivePact(eConqueror)) { switch (pDiplo->GetDoFType(eConqueror)) { case DOF_TYPE_UNTRUSTWORTHY: case DOF_TYPE_NEW: break; case DOF_TYPE_FRIENDS: iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); break; case DOF_TYPE_ALLIES: case DOF_TYPE_BATTLE_BROTHERS: iWarmongerPoliticalModifier += /*-50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_MEDIUM); break; } } else { // Have we denounced the city taker? if (pDiplo->IsDenouncedPlayer(eConqueror)) { iWarmongerPoliticalModifier += /*25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_SMALL); } // Have we BEEN denounced by the city taker? if (pDiplo->IsDenouncedByPlayer(eConqueror)) { iWarmongerPoliticalModifier += /*25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_SMALL); } // Is this a backstabber? if (pDiplo->IsUntrustworthy(eConqueror)) { iWarmongerPoliticalModifier += /*75*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_LARGE); } } // Religious brothers/sisters should turn a blind eye to war conducted on different faiths. if (pDiplo->IsPlayerSameReligion(eConqueror)) { // Reduced penalties for religious friends. iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); // We don't mind when you war on enemies of the faith. if (pDiplo->IsPlayerOpposingReligion(eCityOwner) && !pDiplo->IsIgnoreReligionDifferences(eCityOwner)) { iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); } } // Religious enemies will not be allowed to expand! else if (pDiplo->IsPlayerOpposingReligion(eConqueror) && !pDiplo->IsIgnoreReligionDifferences(eConqueror)) { // Increased penalties for religious enemies. iWarmongerPoliticalModifier += /*25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_SMALL); // We don't like it when you war on brothers of the faith. if (pDiplo->IsPlayerSameReligion(eCityOwner)) { iWarmongerPoliticalModifier += /*25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_SMALL); } } // Are the city taker and I of the same ideology? if (pDiplo->IsPlayerSameIdeology(eConqueror)) { // Reduced penalties for ideological companions. iWarmongerPoliticalModifier += /*-50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_MEDIUM); // We overlook war on our ideological opponents! if (pDiplo->IsPlayerOpposingIdeology(eCityOwner) && !pDiplo->IsIgnoreIdeologyDifferences(eCityOwner)) { iWarmongerPoliticalModifier += /*-50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_MEDIUM); } } // Are the city taker and I of different ideologies? We shall not overlook this! else if (pDiplo->IsPlayerOpposingIdeology(eConqueror) && !pDiplo->IsIgnoreIdeologyDifferences(eConqueror)) { // Increased penalties for ideological enemies. iWarmongerPoliticalModifier += /*50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_MEDIUM); // If they're warring our ideological companions, this will really irritate us. if (pDiplo->IsPlayerSameIdeology(eCityOwner)) { iWarmongerPoliticalModifier += /*50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_MEDIUM); } } // Proximity should matter. switch (GET_PLAYER(eConqueror).GetProximityToPlayer(eObserver)) { case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); break; case PLAYER_PROXIMITY_FAR: iWarmongerPoliticalModifier += /*25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_SMALL); break; case PLAYER_PROXIMITY_CLOSE: iWarmongerPoliticalModifier += /*50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_MEDIUM); break; case PLAYER_PROXIMITY_NEIGHBORS: iWarmongerPoliticalModifier += /*75*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_LARGE); break; } } } // Capped at 25% and SHARED_FATE_PERCENT iWarmongerValue *= range(100 + iWarmongerPoliticalModifier, 25, max(100, /*200*/ GD_INT_GET(WARMONGER_THREAT_SHARED_FATE_PERCENT))); iWarmongerValue /= 100; if (iWarmongerValue <= 0) return 0; // //////////////////////////////////// // RESURRECTION MODIFIER // //////////////////////////////////// if (pDiplo->WasResurrectedBy(eConqueror) || GET_PLAYER(eConqueror).GetDiplomacyAI()->WasResurrectedBy(eObserver)) { iWarmongerValue /= 2; } if (GET_PLAYER(eCityOwner).isMajorCiv() && (pDiplo->WasResurrectedBy(eCityOwner) || GET_PLAYER(eCityOwner).GetDiplomacyAI()->WasResurrectedBy(eObserver))) { iWarmongerValue *= 2; } // //////////////////////////////////// // GLOBAL WARMONGERING MODIFIERS // //////////////////////////////////// CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (pLeague) { // Sanctioned player? Worth half. if (MOD_BALANCE_VP && pLeague->IsTradeEmbargoed(eObserver, eCityOwner) && !bHisLossIsOurOwn) { iWarmongerValue *= /*50*/ GD_INT_GET(WARMONGER_THREAT_ATTACKED_SANCTIONED_PLAYER); iWarmongerValue /= 100; } // Casus Belli? Warmongering and liberation are worth half. if (GC.getGame().GetGameLeagues()->IsWorldWar(eObserver) > 0) { iWarmongerValue *= /*50*/ GD_INT_GET(WARMONGER_THREAT_ATTACKED_WEIGHT_WORLD_WAR); iWarmongerValue /= 100; } // Global Peace Accords? Warmongering and liberation are worth double. else if (GC.getGame().GetGameLeagues()->GetUnitMaintenanceMod(eObserver) > 0) { iWarmongerValue *= /*200*/ GD_INT_GET(WARMONGER_THREAT_ATTACKED_WEIGHT_WORLD_PEACE); iWarmongerValue /= 100; } } // Scale the final amount by era iWarmongerValue *= GC.getEraInfo(GC.getGame().getCurrentEra())->getWarmongerPercent(); iWarmongerValue /= 100; return max(iWarmongerValue, 0); } int CvDiplomacyAIHelpers::GetCityLiberationValue(CvCity* pCity, PlayerTypes eLiberator, PlayerTypes eNewOwner, PlayerTypes eObserver) { CvDiplomacyAI* pDiplo = GET_PLAYER(eObserver).GetDiplomacyAI(); // Must have met the liberator and liberatee - no cheating! if (!pDiplo->IsHasMet(eLiberator) || !pDiplo->IsHasMet(eNewOwner)) return 0; // Masters and vassals ignore each other's warmongering. if (pDiplo->IsMaster(eLiberator) || pDiplo->IsVassal(eLiberator)) return 0; // No bonuses for liberating your own team's city. if (GET_PLAYER(eNewOwner).getTeam() == GET_PLAYER(eLiberator).getTeam()) return 0; // Are we at war with the liberated player? No liberation bonuses! if (pDiplo->IsAtWar(eNewOwner)) return 0; // Is this a city we've previously owned, and they're giving it to someone else? No liberation bonuses! if (GET_PLAYER(eNewOwner).getTeam() != GET_PLAYER(eObserver).getTeam()) { if (pCity->getOriginalOwner() != NO_PLAYER && GET_PLAYER(pCity->getOriginalOwner()).getTeam() == GET_PLAYER(eObserver).getTeam()) return 0; vector vMyTeam = GET_TEAM(GET_PLAYER(eObserver).getTeam()).getPlayers(); for (size_t i=0; iGetNumTimesOwned(vMyTeam[i]) > 0) return 0; } } // Cities are rated on a percentage scale, where 0 = worthless, 50 = equal value with median city, 100 = twice the value of the median city. // Value is capped between 5 and 100. int iMedianEconomicPower = GC.getGame().getMedianEconomicValue(); int iLocalEconomicPower = pCity->getEconomicValue(eLiberator); // rationale for using the liberator: how much is the liberator giving up instead of conquering? int iLiberationValue = range(((iLocalEconomicPower * 100) / max(1, iMedianEconomicPower) / 2), 5, 100); // Modders can change this value to apply a multiplier to the worth of all cities iLiberationValue *= /*100*/ GD_INT_GET(WARMONGER_THREAT_CITY_VALUE_MULTIPLIER); iLiberationValue /= 100; // Original major capitals are worth more. if (pCity->IsOriginalCapitalForPlayer(eNewOwner) && pCity->IsOriginalMajorCapital()) { iLiberationValue *= /*150*/ GD_INT_GET(WARMONGER_THREAT_CAPITAL_CITY_PERCENT); iLiberationValue /= 100; } // Reduce value significantly if liberator has owned the city before. int iNumTimesOwned = pCity->GetNumTimesOwned(eLiberator); if (pCity->IsNoWarmongerYet()) iNumTimesOwned--; if (iNumTimesOwned > 0) iLiberationValue /= iNumTimesOwned + 2; if (iLiberationValue <= 0) iLiberationValue = 1; // Flip it! iLiberationValue *= -1; // //////////////////////////////////// // STATUS MODIFIER : this one mainly deals with defensive pacts and vassalage // //////////////////////////////////// int iWarmongerStatusModifier = 100; bool bHisGainIsOurOwn = false; if (GET_PLAYER(eNewOwner).isMajorCiv()) { // Vassalage? if (pDiplo->IsVassal(eNewOwner) || pDiplo->IsMaster(eNewOwner)) { iWarmongerStatusModifier = /*200*/ GD_INT_GET(WARMONGER_THREAT_SHARED_FATE_PERCENT); bHisGainIsOurOwn = true; } // Alliances? // The observing player has a Defensive Pact/is a teammate - his gain is our own! else if (pDiplo->IsHasDefensivePact(eNewOwner) || GET_PLAYER(eObserver).getTeam() == GET_PLAYER(eNewOwner).getTeam()) { iWarmongerStatusModifier = max(100, /*200*/ GD_INT_GET(WARMONGER_THREAT_SHARED_FATE_PERCENT)); bHisGainIsOurOwn = true; // A city? For me? Thanks!! if (GET_PLAYER(eObserver).getTeam() == GET_PLAYER(eNewOwner).getTeam()) { iWarmongerStatusModifier += max(0, /*200*/ GD_INT_GET(WARMONGER_THREAT_LIBERATED_TEAM_BONUS_PERCENT)); } } } iLiberationValue *= max(0, iWarmongerStatusModifier); iLiberationValue /= 100; if (iLiberationValue >= 0) return 0; // //////////////////////////////////// // STRENGTH MODIFIER : this one mainly deals with relative power // //////////////////////////////////// int iWarmongerStrengthModifier = 0; // DECREASE if he's big and nasty, INCREASE if he's not. // Flip the values used for warmongering switch (pDiplo->GetMilitaryStrengthComparedToUs(eLiberator)) { case STRENGTH_IMMENSE: iWarmongerStrengthModifier = /*-50*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_PATHETIC); break; case STRENGTH_POWERFUL: iWarmongerStrengthModifier = /*-25*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_WEAK); break; case STRENGTH_STRONG: iWarmongerStrengthModifier = /*0*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_POOR); break; case STRENGTH_AVERAGE: iWarmongerStrengthModifier = /*33*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_AVERAGE); break; case STRENGTH_POOR: iWarmongerStrengthModifier = /*50*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_STRONG); break; case STRENGTH_WEAK: iWarmongerStrengthModifier = /*75*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_POWERFUL); break; case STRENGTH_PATHETIC: iWarmongerStrengthModifier = /*100*/ GD_INT_GET(WARMONGER_THREAT_ATTACKER_STRENGTH_IMMENSE); break; } // No strength-based reductions if it's our gain... if (bHisGainIsOurOwn && iWarmongerStrengthModifier < 0) iWarmongerStrengthModifier = 0; if (!bHisGainIsOurOwn) { StrengthTypes eLiberatedPlayerStrength = pDiplo->GetMilitaryStrengthComparedToUs(eNewOwner); // DECREASE if liberated player is big and nasty. switch (eLiberatedPlayerStrength) { case STRENGTH_IMMENSE: iWarmongerStrengthModifier += /*-75*/ GD_INT_GET(WARMONGER_THREAT_DEFENDER_STRENGTH_IMMENSE); break; case STRENGTH_POWERFUL: iWarmongerStrengthModifier += /*-50*/ GD_INT_GET(WARMONGER_THREAT_DEFENDER_STRENGTH_POWERFUL); break; case STRENGTH_STRONG: iWarmongerStrengthModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_DEFENDER_STRENGTH_STRONG); break; case STRENGTH_AVERAGE: iWarmongerStrengthModifier += /*0*/ GD_INT_GET(WARMONGER_THREAT_DEFENDER_STRENGTH_AVERAGE); break; case STRENGTH_POOR: case STRENGTH_WEAK: case STRENGTH_PATHETIC: break; // Not applicable. } } iLiberationValue *= max(25, 100 + iWarmongerStrengthModifier); iLiberationValue /= 100; if (iLiberationValue >= 0) return 0; // //////////////////////////////////// // POLITICAL MODIFIER : this is the big one - geopolitics etc. // //////////////////////////////////// int iWarmongerPoliticalModifier = 0; if (!bHisGainIsOurOwn) { // City-State if (GET_PLAYER(eNewOwner).isMinorCiv()) { CvMinorCivAI* pMinorAI = GET_PLAYER(eNewOwner).GetMinorCivAI(); // Allies? if (pMinorAI->GetEffectiveFriendshipWithMajor(eObserver) >= /*60*/ GD_INT_GET(FRIENDSHIP_THRESHOLD_ALLIES) || pMinorAI->GetPermanentAlly() == eObserver) { iWarmongerPoliticalModifier += /*75*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_LARGE); } else if (pMinorAI->IsFriends(eObserver)) { iWarmongerPoliticalModifier += /*50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_MEDIUM); } else { iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); } if (pMinorAI->IsProtectedByMajor(eObserver)) { iWarmongerPoliticalModifier += /*50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_MEDIUM); } else if (pMinorAI->GetEffectiveFriendshipWithMajor(eObserver) < /*60*/ GD_INT_GET(FRIENDSHIP_THRESHOLD_ALLIES)) { iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); } } // Major Civ else { // Are we friends with the city owner? if (pDiplo->IsDoFAccepted(eNewOwner) || pDiplo->IsHasDefensivePact(eNewOwner)) { switch (pDiplo->GetDoFType(eNewOwner)) { case DOF_TYPE_UNTRUSTWORTHY: case DOF_TYPE_NEW: break; case DOF_TYPE_FRIENDS: iWarmongerPoliticalModifier += /*25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_SMALL); break; case DOF_TYPE_ALLIES: iWarmongerPoliticalModifier += /*50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_MEDIUM); break; case DOF_TYPE_BATTLE_BROTHERS: iWarmongerPoliticalModifier += /*75*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_LARGE); break; } } else { // Have we denounced the city owner? if (pDiplo->IsDenouncedPlayer(eNewOwner)) { iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); } // Have we BEEN denounced by the city owner? if (pDiplo->IsDenouncedByPlayer(eNewOwner)) { iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); } // Is this a backstabber? if (pDiplo->IsUntrustworthy(eNewOwner)) { iWarmongerPoliticalModifier += /*-50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_MEDIUM); } } // Are we friends with the liberator? if (pDiplo->IsDoFAccepted(eLiberator) || pDiplo->IsHasDefensivePact(eLiberator)) { switch (pDiplo->GetDoFType(eLiberator)) { case DOF_TYPE_UNTRUSTWORTHY: case DOF_TYPE_NEW: break; case DOF_TYPE_FRIENDS: iWarmongerPoliticalModifier += /*25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_SMALL); break; case DOF_TYPE_ALLIES: case DOF_TYPE_BATTLE_BROTHERS: iWarmongerPoliticalModifier += /*50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_MEDIUM); break; } } else { // Have we denounced the liberator? if (pDiplo->IsDenouncedPlayer(eLiberator)) { iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); } // Have we BEEN denounced by the liberator? if (pDiplo->IsDenouncedByPlayer(eLiberator)) { iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); } // Is this a backstabber? if (pDiplo->IsUntrustworthy(eLiberator)) { iWarmongerPoliticalModifier += /*-50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_MEDIUM); } } // We're happy when you liberate our religious brothers/sisters. if (pDiplo->IsPlayerSameReligion(eNewOwner)) { iWarmongerPoliticalModifier += /*25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_SMALL); // Even moreso when you're also of our faith! if (pDiplo->IsPlayerSameReligion(eLiberator)) { iWarmongerPoliticalModifier += /*25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_SMALL); } } // We don't like when you liberate enemies of the faith. else if (pDiplo->IsPlayerOpposingReligion(eNewOwner) && !pDiplo->IsIgnoreReligionDifferences(eNewOwner)) { iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); // Even moreso if you're also of another faith. if (pDiplo->IsPlayerOpposingReligion(eLiberator) && !pDiplo->IsIgnoreReligionDifferences(eLiberator)) { iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); } } // If they're liberating our ideological companions, we like this! if (pDiplo->IsPlayerSameIdeology(eNewOwner)) { iWarmongerPoliticalModifier += /*50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_MEDIUM); // Are the liberator and I also of the same ideology? if (pDiplo->IsPlayerSameIdeology(eLiberator)) { iWarmongerPoliticalModifier += /*50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_MEDIUM); } } // We dislike the liberation of our ideological opponents! else if (pDiplo->IsPlayerOpposingIdeology(eNewOwner) && !pDiplo->IsIgnoreIdeologyDifferences(eNewOwner)) { iWarmongerPoliticalModifier += /*-50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_MEDIUM); // Are the liberator and I also of different ideologies? if (pDiplo->IsPlayerOpposingIdeology(eLiberator) && !pDiplo->IsIgnoreIdeologyDifferences(eLiberator)) { iWarmongerPoliticalModifier += /*-50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_MEDIUM); } } // Proximity should matter. switch (GET_PLAYER(eLiberator).GetProximityToPlayer(eObserver)) { case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: iWarmongerPoliticalModifier += /*-25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_NEGATIVE_SMALL); break; case PLAYER_PROXIMITY_FAR: iWarmongerPoliticalModifier += /*25*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_SMALL); break; case PLAYER_PROXIMITY_CLOSE: iWarmongerPoliticalModifier += /*50*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_MEDIUM); break; case PLAYER_PROXIMITY_NEIGHBORS: iWarmongerPoliticalModifier += /*75*/ GD_INT_GET(WARMONGER_THREAT_MODIFIER_LARGE); break; } } } // Capped at 25% and SHARED_FATE_PERCENT iLiberationValue *= range(100 + iWarmongerPoliticalModifier, 25, max(100, /*200*/ GD_INT_GET(WARMONGER_THREAT_SHARED_FATE_PERCENT))); iLiberationValue /= 100; if (iLiberationValue >= 0) return 0; // //////////////////////////////////// // RESURRECTION MODIFIER // //////////////////////////////////// if (pDiplo->WasResurrectedBy(eLiberator) || GET_PLAYER(eLiberator).GetDiplomacyAI()->WasResurrectedBy(eObserver)) { iLiberationValue *= 2; } if (GET_PLAYER(eNewOwner).isMajorCiv() && (pDiplo->WasResurrectedBy(eNewOwner) || GET_PLAYER(eNewOwner).GetDiplomacyAI()->WasResurrectedBy(eObserver))) { iLiberationValue *= 2; } // //////////////////////////////////// // GLOBAL WARMONGERING MODIFIERS // //////////////////////////////////// CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (pLeague) { // Sanctioned player? Worth half. if (MOD_BALANCE_VP && pLeague->IsTradeEmbargoed(eObserver, eNewOwner) && !bHisGainIsOurOwn) { iLiberationValue *= /*50*/ GD_INT_GET(WARMONGER_THREAT_ATTACKED_SANCTIONED_PLAYER); iLiberationValue /= 100; } // Casus Belli? Warmongering and liberation are worth half. if (GC.getGame().GetGameLeagues()->IsWorldWar(eObserver) > 0) { iLiberationValue *= /*50*/ GD_INT_GET(WARMONGER_THREAT_ATTACKED_WEIGHT_WORLD_WAR); iLiberationValue /= 100; } // Global Peace Accords? Warmongering and liberation are worth double. else if (GC.getGame().GetGameLeagues()->GetUnitMaintenanceMod(eObserver) > 0) { iLiberationValue *= /*200*/ GD_INT_GET(WARMONGER_THREAT_ATTACKED_WEIGHT_WORLD_PEACE); iLiberationValue /= 100; } } // Scale the final amount by era iLiberationValue *= GC.getEraInfo(GC.getGame().getCurrentEra())->getWarmongerPercent(); iLiberationValue /= 100; return min(iLiberationValue, 0); } CvString CvDiplomacyAIHelpers::GetWarmongerPreviewString(PlayerTypes eCurrentOwner, CvCity* pCity, PlayerTypes eActivePlayer) { CvString szRtnValue = ""; if (eActivePlayer == NO_PLAYER) eActivePlayer = GC.getGame().getActivePlayer(); if (pCity != NULL && eActivePlayer != NO_PLAYER && eCurrentOwner != NO_PLAYER) { szRtnValue = Localization::Lookup("TXT_KEY_WARMONGER_PREVIEW_HEADER").toUTF8(); CvWeightedVector veWarmongerWeights; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { // Ignore minors if (!GET_PLAYER((PlayerTypes)iPlayerLoop).isMinorCiv() && GET_PLAYER((PlayerTypes)iPlayerLoop).isAlive() && (PlayerTypes)iPlayerLoop != eActivePlayer) { // Ignore unmet players if (!GET_TEAM(GET_PLAYER(eActivePlayer).getTeam()).isHasMet(GET_PLAYER((PlayerTypes)iPlayerLoop).getTeam())) continue; int iWarmongerCare = CvDiplomacyAIHelpers::GetCityWarmongerValue(pCity, eActivePlayer, eCurrentOwner, (PlayerTypes)iPlayerLoop); if (iWarmongerCare > 0) { veWarmongerWeights.push_back((PlayerTypes)iPlayerLoop, iWarmongerCare); } } } if (veWarmongerWeights.size() > 0) { veWarmongerWeights.StableSortItems(); int iHighestVal = veWarmongerWeights.GetWeight(0); int iCap = 0; int iMax = 8; for (int iWarmongerCivs = 0; iWarmongerCivs < (int) veWarmongerWeights.size(); iWarmongerCivs++) { if (iCap >= iMax) { break; } PlayerTypes eLoopPlayer = (PlayerTypes) veWarmongerWeights.GetElement(iWarmongerCivs); if (eLoopPlayer == NO_PLAYER) continue; int iWarmongerCare = veWarmongerWeights.GetWeight(iWarmongerCivs); int iPercentOfMax = (iWarmongerCare * 100) / iHighestVal; if (eLoopPlayer != NO_PLAYER) { if (iPercentOfMax >= 75) { szRtnValue += "[NEWLINE]"; szRtnValue += GetLocalizedText("TXT_KEY_WARMONGER_PREVIEW_CARE_1000", GET_PLAYER(eLoopPlayer).getCivilizationShortDescriptionKey()); } else if (iPercentOfMax >= 60) { szRtnValue += "[NEWLINE]"; szRtnValue += GetLocalizedText("TXT_KEY_WARMONGER_PREVIEW_CARE_800", GET_PLAYER(eLoopPlayer).getCivilizationShortDescriptionKey()); } else if (iPercentOfMax >= 45) { szRtnValue += "[NEWLINE]"; szRtnValue += GetLocalizedText("TXT_KEY_WARMONGER_PREVIEW_CARE_600", GET_PLAYER(eLoopPlayer).getCivilizationShortDescriptionKey()); } else if (iPercentOfMax >= 30) { szRtnValue += "[NEWLINE]"; szRtnValue += GetLocalizedText("TXT_KEY_WARMONGER_PREVIEW_CARE_400", GET_PLAYER(eLoopPlayer).getCivilizationShortDescriptionKey()); } else if (iPercentOfMax >= 15) { szRtnValue += "[NEWLINE]"; szRtnValue += GetLocalizedText("TXT_KEY_WARMONGER_PREVIEW_CARE_200", GET_PLAYER(eLoopPlayer).getCivilizationShortDescriptionKey()); } else { szRtnValue += "[NEWLINE]"; szRtnValue += GetLocalizedText("TXT_KEY_WARMONGER_PREVIEW_CARE_NIL", GET_PLAYER(eLoopPlayer).getCivilizationShortDescriptionKey()); } } } } else { szRtnValue = ""; } return szRtnValue; } else { szRtnValue = ""; } return szRtnValue; } CvString CvDiplomacyAIHelpers::GetLiberationPreviewString(PlayerTypes eOriginalOwner, CvCity* pCity, PlayerTypes eActivePlayer) { CvString szRtnValue = ""; if (eActivePlayer == NO_PLAYER) eActivePlayer = GC.getGame().getActivePlayer(); if (pCity != NULL && eActivePlayer != NO_PLAYER && eOriginalOwner != NO_PLAYER) { szRtnValue = Localization::Lookup("TXT_KEY_LIBERATOR_PREVIEW_HEADER").toUTF8(); CvWeightedVector veWarmongerWeights; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { // Ignore minors if (!GET_PLAYER((PlayerTypes)iPlayerLoop).isMinorCiv() && GET_PLAYER((PlayerTypes)iPlayerLoop).isAlive() && (PlayerTypes)iPlayerLoop != eActivePlayer) { if (!GET_TEAM(GET_PLAYER(eActivePlayer).getTeam()).isHasMet(GET_PLAYER((PlayerTypes)iPlayerLoop).getTeam())) continue; int iWarmongerCare = CvDiplomacyAIHelpers::GetCityLiberationValue(pCity, eActivePlayer, eOriginalOwner, (PlayerTypes)iPlayerLoop); // Flip this for liberation! iWarmongerCare *= -1; if (iWarmongerCare > 0) { veWarmongerWeights.push_back((PlayerTypes)iPlayerLoop, iWarmongerCare); } } } if (veWarmongerWeights.size() > 0) { veWarmongerWeights.StableSortItems(); int iHighestVal = veWarmongerWeights.GetWeight(0); int iCap = 0; int iMax = 8; for (int iWarmongerCivs = 0; iWarmongerCivs < (int) veWarmongerWeights.size(); iWarmongerCivs++) { if (iCap >= iMax) { break; } PlayerTypes eLoopPlayer = (PlayerTypes)veWarmongerWeights.GetElement(iWarmongerCivs); if (eLoopPlayer == NO_PLAYER) continue; int iWarmongerCare = veWarmongerWeights.GetWeight(iWarmongerCivs); int iPercentOfMax = (iWarmongerCare * 100) / iHighestVal; if (iPercentOfMax >= 75) { szRtnValue += "[NEWLINE]"; szRtnValue += GetLocalizedText("TXT_KEY_LIBERATOR_PREVIEW_CARE_1000", GET_PLAYER(eLoopPlayer).getCivilizationShortDescriptionKey()); } else if (iPercentOfMax >= 60) { szRtnValue += "[NEWLINE]"; szRtnValue += GetLocalizedText("TXT_KEY_LIBERATOR_PREVIEW_CARE_800", GET_PLAYER(eLoopPlayer).getCivilizationShortDescriptionKey()); } else if (iPercentOfMax >= 45) { szRtnValue += "[NEWLINE]"; szRtnValue += GetLocalizedText("TXT_KEY_LIBERATOR_PREVIEW_CARE_600", GET_PLAYER(eLoopPlayer).getCivilizationShortDescriptionKey()); } else if (iPercentOfMax >= 30) { szRtnValue += "[NEWLINE]"; szRtnValue += GetLocalizedText("TXT_KEY_LIBERATOR_PREVIEW_CARE_400", GET_PLAYER(eLoopPlayer).getCivilizationShortDescriptionKey()); } else if (iPercentOfMax >= 15) { szRtnValue += "[NEWLINE]"; szRtnValue += GetLocalizedText("TXT_KEY_LIBERATOR_PREVIEW_CARE_200", GET_PLAYER(eLoopPlayer).getCivilizationShortDescriptionKey()); } else { szRtnValue += "[NEWLINE]"; szRtnValue += GetLocalizedText("TXT_KEY_LIBERATOR_PREVIEW_CARE_NIL", GET_PLAYER(eLoopPlayer).getCivilizationShortDescriptionKey()); } iCap++; } } else { szRtnValue = ""; } return szRtnValue; } else { szRtnValue = ""; } return szRtnValue; } bool CvDiplomacyAIHelpers::BackstabbedPlayer(PlayerTypes eBackstabber, PlayerTypes eVictim, bool bIncludeDoFEnd) { CvDiplomacyAI* pDiplo = GET_PLAYER(eVictim).GetDiplomacyAI(); return pDiplo->IsFriendDenouncedUs(eBackstabber) || pDiplo->IsFriendDeclaredWarOnUs(eBackstabber) || pDiplo->IsResurrectorAttackedUs(eBackstabber) || (bIncludeDoFEnd && pDiplo->IsDoFBroken(eBackstabber)) || pDiplo->BrokeMilitaryPromise(eBackstabber) || pDiplo->BrokeAttackCityStatePromise(eBackstabber) || pDiplo->BrokeVassalAgreement(eBackstabber); } bool CvDiplomacyAIHelpers::IgnoresBackstabbing(PlayerTypes eObserver, PlayerTypes eVictim, bool bCheckCurrent /*= false*/) { if (GET_PLAYER(eObserver).GetDiplomacyAI()->WasEverBackstabbedBy(eVictim) || GET_PLAYER(eVictim).GetDiplomacyAI()->WasEverBackstabbedBy(eObserver)) return true; if (!bCheckCurrent) return false; return GET_PLAYER(eObserver).GetDiplomacyAI()->IsUntrustworthy(eVictim); } bool CvDiplomacyAIHelpers::ProposedSanctionsBlockingDiplomacy(PlayerTypes ePlayer, PlayerTypes eOtherPlayer) { CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (!pLeague) return false; CvDiplomacyAI* pDiplo = GET_PLAYER(ePlayer).GetDiplomacyAI(); bool bRecentLiberation = pDiplo->IsCityRecentlyLiberatedBy(eOtherPlayer) && GET_PLAYER(ePlayer).getCitiesLost() > 0 && !GET_PLAYER(eOtherPlayer).GetDiplomacyAI()->IsCloseToWorldConquest() && !pDiplo->IsEndgameAggressiveTo(eOtherPlayer); // Is there a current sanction proposal in either direction? for (EnactProposalList::iterator it = pLeague->m_vEnactProposals.begin(); it != pLeague->m_vEnactProposals.end(); ++it) { PlayerTypes eProposer = it->GetProposerDecision()->GetProposer(); if (eProposer != NO_PLAYER && GET_PLAYER(ePlayer).GetLeagueAI()->IsSanctionProposal(&(*it), NO_PLAYER)) { PlayerTypes eTarget = (PlayerTypes)it->GetProposerDecision()->GetDecision(); TeamTypes eProposerTeam = GET_PLAYER(eProposer).getTeam(); TeamTypes eTargetTeam = GET_PLAYER(eTarget).getTeam(); if (eTargetTeam == GET_PLAYER(ePlayer).getTeam() && eProposerTeam == GET_PLAYER(eOtherPlayer).getTeam()) { return true; } else if (eProposerTeam == GET_PLAYER(ePlayer).getTeam() && eTargetTeam == GET_PLAYER(eOtherPlayer).getTeam()) { // Add some one-way exceptions if the player has proposed sanctions, but has been liberated by the other player, or the other player is a vassal of someone. return !pDiplo->IsLiberator(eOtherPlayer, false, true) && !bRecentLiberation && !GET_PLAYER(eOtherPlayer).IsVassalOfSomeone(); } } } return false; } /// Possible Contact Statement - AI only void CvDiplomacyAI::DoMakeVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // note: we check to see if it's possible in IsMakeOfferForVassalage() if(eStatement == NO_DIPLO_STATEMENT_TYPE) { // Can we make an offer for vassalage? if(GetPlayer()->GetDealAI()->IsMakeOfferForVassalage(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_BECOME_MY_VASSAL; int iTurnsBetweenStatement = 50; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatement) { // Send the statement if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatement) eStatement = eTempStatement; } } else { pDeal->ClearItems(); } } } /// Possible Contact Statement - AI only void CvDiplomacyAI::DoBecomeVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (IsAvoidDeals()) return; if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; // note: we check to see if it's possible in IsMakeOfferForVassalage() if (eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_ACCEPT_VASSALAGE; int iTurnsBetweenStatement = 50; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatement) { // Can we make an offer for vassalage? if (GetPlayer()->GetDealAI()->IsMakeOfferToBecomeVassal(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { eStatement = eTempStatement; } else { pDeal->ClearItems(); } } } } /// Possible Contact Statement - Vassal taxes have been raised void CvDiplomacyAI::DoVassalTaxesRaisedStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if(eStatement == NO_DIPLO_STATEMENT_TYPE) { // Which player is actually the vassal? PlayerTypes eVassal = NO_PLAYER; if(IsVassal(ePlayer)) eVassal = GetID(); else if(GET_PLAYER(ePlayer).GetDiplomacyAI()->IsVassal(GetID())) eVassal = ePlayer; if(eVassal != NO_PLAYER) { // We are ePlayer's vassal if(eVassal == GetID()) { if(IsVassalTaxRaised(ePlayer)) { eStatement = DIPLO_STATEMENT_VASSAL_TAXES_RAISED_HUMAN_MASTER; SetVassalTaxRaised(ePlayer, false); } } // ePlayer is our vassal else if(eVassal == ePlayer) { if(GET_PLAYER(ePlayer).GetDiplomacyAI()->IsVassalTaxRaised(GetID())) { eStatement = DIPLO_STATEMENT_VASSAL_TAXES_RAISED_AI_MASTER; for(int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // Modify player view to all AI teammates if (GET_PLAYER(eLoopPlayer).getTeam() == GetTeam()) { eLoopPlayer = (PlayerTypes) iPlayerLoop; GET_PLAYER(ePlayer).GetDiplomacyAI()->SetVassalTaxRaised(eLoopPlayer, false); } } } } } } } /// Possible Contact Statement - Vassal taxes have been lowered void CvDiplomacyAI::DoVassalTaxesLoweredStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if(eStatement == NO_DIPLO_STATEMENT_TYPE) { // Which player is actually the vassal? PlayerTypes eVassal = NO_PLAYER; if(IsVassal(ePlayer)) eVassal = GetID(); else if(GET_PLAYER(ePlayer).GetDiplomacyAI()->IsVassal(GetID())) eVassal = ePlayer; if(eVassal != NO_PLAYER) { // We are ePlayer's vassal if(eVassal == GetID()) { if(IsVassalTaxLowered(ePlayer)) { eStatement = DIPLO_STATEMENT_VASSAL_TAXES_LOWERED_HUMAN_MASTER; SetVassalTaxLowered(ePlayer, false); } } // ePlayer is our vassal else if(eVassal == ePlayer) { if(GET_PLAYER(ePlayer).GetDiplomacyAI()->IsVassalTaxLowered(GetID())) { eStatement = DIPLO_STATEMENT_VASSAL_TAXES_LOWERED_AI_MASTER; for(int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; // Modify player view to all AI teammates if (GET_PLAYER(eLoopPlayer).getTeam() == GetTeam()) { eLoopPlayer = (PlayerTypes) iPlayerLoop; GET_PLAYER(ePlayer).GetDiplomacyAI()->SetVassalTaxLowered(eLoopPlayer, false); } } } } } } } /// Possible Contact Statement - Vassal has been liberated void CvDiplomacyAI::DoLiberateMyVassalStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (eStatement == NO_DIPLO_STATEMENT_TYPE) { if (GetVassalPlayerToLiberate() == ePlayer) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_LIBERATE_VASSAL; int iTurnsBetweenStatement = 1; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatement) { eStatement = eTempStatement; } } } } void CvDiplomacyAI::DetermineVassalToLiberate() { if (GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR)) return; // Humans make all the decisions! if (m_pPlayer->IsAITeammateOfHuman()) { SetVassalPlayerToLiberate(NO_PLAYER); return; } // Only run this check if we're the team leader PlayerTypes eMasterTeamLeader = GET_TEAM(GetTeam()).getLeaderID(); if (eMasterTeamLeader != GetID()) { SetVassalPlayerToLiberate(NO_PLAYER); return; } int iBestScoreForLiberate = INT_MIN; PlayerTypes eBestCandidate = NO_PLAYER; for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (!GET_PLAYER(eLoopPlayer).isAlive() || GET_PLAYER(eLoopPlayer).getNumCities() == 0) continue; if (GET_PLAYER(eLoopPlayer).IsAITeammateOfHuman()) continue; // Must be either human or the team leader TeamTypes eTeam = GET_PLAYER(eLoopPlayer).getTeam(); if (GET_TEAM(eTeam).getLeaderID() == eLoopPlayer || GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) { // If human, must be contactable if (GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) { if (MOD_DIPLOAI_SHUT_UP) continue; if (GC.getGame().isReallyNetworkMultiPlayer() && !MOD_ACTIVE_DIPLOMACY) continue; } int iScoreForLiberate = 0; if (IsWantToLiberateVassal(eLoopPlayer, iScoreForLiberate)) { if (iScoreForLiberate > iBestScoreForLiberate) { eBestCandidate = eLoopPlayer; iBestScoreForLiberate = iScoreForLiberate; } } } } SetVassalPlayerToLiberate(eBestCandidate); } /// Do we want to liberate ePlayer's team? bool CvDiplomacyAI::IsWantToLiberateVassal(PlayerTypes ePlayer, int& iScoreForLiberate) const { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) UNREACHABLE(); // Can't liberate? Abort! TeamTypes eVassalTeam = GET_PLAYER(ePlayer).getTeam(); if (!GET_TEAM(GetTeam()).CanLiberateVassal(eVassalTeam)) return false; // Do not liberate if they have at least 90% of our population int iNumPop = GET_TEAM(GetTeam()).getTotalPopulation(); int iNumVassalPop = GET_TEAM(eVassalTeam).getTotalPopulation(); if ((iNumVassalPop * 100) >= (iNumPop * 90)) return false; // Do not liberate if they own other players' capitals if (GET_PLAYER(ePlayer).GetNumCapitalCities() > 0) return false; // Do not liberate if they own any of our cities if (m_pPlayer->GetNumOurCitiesOwnedBy(ePlayer) > 0) return false; // If someone else previously resurrected this vassal and that team is alive, don't liberate // They will probably fly into the arms of their former protector and come after us for revenge! TeamTypes eLiberatedByTeam = GET_TEAM(eVassalTeam).GetLiberatedByTeam(); if (eLiberatedByTeam != NO_TEAM && eLiberatedByTeam != GetTeam() && GET_TEAM(eLiberatedByTeam).isAlive()) return false; // Refuse if they aren't following the one true ideology if (GetPlayer()->GetPlayerPolicies()->GetLateGamePolicyTree() != NO_POLICY_BRANCH_TYPE && !IsPlayerSameIdeology(ePlayer)) return false; vector vMasterTeam = GET_TEAM(GetTeam()).getPlayers(); vector vVassalTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); CivApproachTypes eWorstMasterApproach = CIV_APPROACH_FRIENDLY; int iWorstMasterOpinion = SHRT_MIN; StrengthTypes eVassalStrength = STRENGTH_PATHETIC; StrengthTypes eVassalEcoStrength = STRENGTH_PATHETIC; InfluenceLevelTypes eMasterInfluence = NO_INFLUENCE_LEVEL; InfluenceLevelTypes eVassalInfluence = NO_INFLUENCE_LEVEL; ReligionTypes eMasterReligion = NO_RELIGION; int iMostFollowers = 0; for (size_t i=0; iGetOwnedReligion(); if (eReligion != NO_RELIGION) { // Performance optimization - only consider followers if there's more than one person on the master team if (iMostFollowers == 0 && i + 1 >= vMasterTeam.size()) { eMasterReligion = eReligion; continue; } int iFollowers = GC.getGame().GetGameReligions()->GetNumFollowers(eMasterReligion); if (iFollowers > iMostFollowers) { eMasterReligion = eReligion; iMostFollowers = iFollowers; } } } for (size_t i=0; iIsGoingForWorldConquest() || GET_PLAYER(eMaster).GetNumCapitalCities() > 0) return false; // If we're close to winning, now is not the time to be liberating vassals! if (pDiplo->IsCloseToAnyVictoryCondition()) return false; // Separate check for World Conquest if the game is already won, since IsCloseToAnyVictoryCondition() will return false if (GC.getGame().getWinner() != NO_TEAM && pDiplo->IsCloseToWorldConquest()) return false; for (size_t j=0; jGetCachedOpinionWeight(eVassal) - pDiplo->GetMasterScore(eVassal); // exclude the diplo bonus they have for just being our vassal CivApproachTypes eApproach = pDiplo->GetCivApproach(eVassal); CivApproachTypes eVisibleApproach = GET_PLAYER(eVassal).isHuman(ISHUMAN_AI_DIPLOMACY) ? CIV_APPROACH_NEUTRAL : pDiplo->GetVisibleApproachTowardsUs(eVassal); if (eApproach <= CIV_APPROACH_AFRAID || eVisibleApproach <= CIV_APPROACH_GUARDED || iCachedOpinionWeight >= /*30*/ GD_INT_GET(OPINION_THRESHOLD_COMPETITOR)) return false; if (eApproach < eWorstMasterApproach) eWorstMasterApproach = eApproach; if (iCachedOpinionWeight > iWorstMasterOpinion) iWorstMasterOpinion = iCachedOpinionWeight; // Check strength compared to us - care only about the highest // Do not liberate if stronger than us - could be a threat StrengthTypes eStrength = pDiplo->GetRawMilitaryStrengthComparedToUs(eVassal); if (eStrength > STRENGTH_AVERAGE) return false; if (eStrength > eVassalStrength) eVassalStrength = eStrength; eStrength = pDiplo->GetEconomicStrengthComparedToUs(eVassal); if (eStrength > STRENGTH_AVERAGE) return false; if (eStrength > eVassalEcoStrength) eVassalEcoStrength = eStrength; // Do not liberate if endgame aggressive if (pDiplo->IsEndgameAggressiveTo(eVassal)) return false; // Denouncement in either direction? if (pDiplo->IsDenouncedPlayer(eVassal) || pDiplo->IsDenouncedByPlayer(eVassal)) return false; // Refuse if it says they're a HERETIC! if (eMasterReligion != NO_RELIGION && GET_PLAYER(eVassal).GetReligions()->GetStateReligion(false) != eMasterReligion) return false; // Check cultural influence - only care about the highest / lowest InfluenceLevelTypes eVassalInfluenceOverMaster = GET_PLAYER(eVassal).GetCulture()->GetInfluenceLevel(eMaster); // Don't liberate if influential or rising towards us - could be a threat if (eVassalInfluenceOverMaster >= INFLUENCE_LEVEL_INFLUENTIAL || GET_PLAYER(eVassal).GetCulture()->GetInfluenceTrend(eMaster) == INFLUENCE_TREND_RISING) return false; if (eVassalInfluenceOverMaster > eVassalInfluence) eVassalInfluence = eVassalInfluenceOverMaster; InfluenceLevelTypes eMasterInfluenceOverVassal = GET_PLAYER(eMaster).GetCulture()->GetInfluenceLevel(eVassal); // Don't liberate if not influential yet and going for Cultural Victory - we could use the Tourism boost if (eMasterInfluenceOverVassal < INFLUENCE_LEVEL_INFLUENTIAL && (pDiplo->IsGoingForCultureVictory() || pDiplo->IsCloseToCultureVictory())) return false; if (eMasterInfluenceOverVassal < eMasterInfluence) eMasterInfluence = eMasterInfluenceOverVassal; } } // Do not liberate if it would cause unhappiness or financial problems! int iTotalHappinessBoost = 0; bool bGoodRevenueSource = false; bool bNuclearGandhiException = false; int iNecessaryRevenueSource = 0; int iFinancialLiability = 0; for (size_t i=0; i 0) return false; if (GET_PLAYER(eVassal).GetDiplomacyAI()->IsNuclearGandhi(true)) bNuclearGandhiException = true; iTotalHappinessBoost += GetPlayer()->GetHappinessFromVassal(eVassal); } for (size_t i=0; i 0 && GET_PLAYER(eMaster).IsEmpireUnhappy()) return false; int iUnhappy = GET_PLAYER(eMaster).GetUnhappinessFromCitizenNeeds(); int iHappy = GET_PLAYER(eMaster).GetHappinessFromCitizenNeeds() - iTotalHappinessBoost; if (((iHappy * 100) / max(1, iUnhappy) / 2) < /*50*/ GD_INT_GET(UNHAPPY_THRESHOLD)) return false; bool bSkip = false; int iRevenue = GET_PLAYER(eMaster).GetTreasury()->GetMyShareOfVassalTaxes(eVassalTeam); int iExpense = GET_PLAYER(eMaster).GetTreasury()->GetVassalGoldMaintenance(eVassalTeam); if (iRevenue < iExpense) { // We're losing more than we're gaining from this vassal, and we're going bankrupt. Not good! if (GET_PLAYER(eMaster).getTurnsToBankruptcy(0) != INT_MAX) { // Hmm...can we rectify the situation by raising taxes on them? if (GET_TEAM(GetTeam()).GetVassalTax(ePlayer) < /*25*/ GD_INT_GET(VASSALAGE_VASSAL_TAX_PERCENT_MAXIMUM)) { int iNewRevenue = GET_PLAYER(eMaster).GetTreasury()->GetMyShareOfVassalTaxes(eVassalTeam, /*25*/ GD_INT_GET(VASSALAGE_VASSAL_TAX_PERCENT_MAXIMUM)); int iDifference = iNewRevenue - iRevenue; if (iNewRevenue >= iExpense || GET_PLAYER(eMaster).getTurnsToBankruptcy(-iDifference) != INT_MAX) { // If raising taxes would bring us out of bankruptcy or equalize the expenses, don't liberate return false; } // No? Then this vassal's bad to keep. else { iFinancialLiability++; bSkip = true; } } else { iFinancialLiability++; bSkip = true; } } } if (!bSkip) { // Good revenue source? int iGoldRate = GET_PLAYER(eMaster).calculateGoldRateTimes100(); if (iRevenue * 100 > iGoldRate * 20) bGoodRevenueSource = true; // Necessary revenue source? if (iGoldRate - iRevenue <= 0) iNecessaryRevenueSource++; } } if (iNecessaryRevenueSource > iFinancialLiability) return false; // Base liberation score is determined by approach - either FRIENDLY or NEUTRAL iScoreForLiberate = eWorstMasterApproach == CIV_APPROACH_FRIENDLY ? 10 : 0; // Mod based on opinion - usually the most important factor CivOpinionTypes eWorstMasterOpinion = CIV_OPINION_ALLY; if (iWorstMasterOpinion > /*-30*/ GD_INT_GET(OPINION_THRESHOLD_FAVORABLE)) eWorstMasterOpinion = CIV_OPINION_NEUTRAL; else if (iWorstMasterOpinion > /*-80*/ GD_INT_GET(OPINION_THRESHOLD_FRIEND)) eWorstMasterOpinion = CIV_OPINION_FAVORABLE; else if (iWorstMasterOpinion > /*-160*/ GD_INT_GET(OPINION_THRESHOLD_ALLY)) eWorstMasterOpinion = CIV_OPINION_FRIEND; switch (eWorstMasterOpinion) { case CIV_OPINION_NEUTRAL: iScoreForLiberate += 0; break; case CIV_OPINION_FAVORABLE: iScoreForLiberate += 5; break; case CIV_OPINION_FRIEND: iScoreForLiberate += 20; break; case CIV_OPINION_ALLY: iScoreForLiberate += 40; break; default: UNREACHABLE(); } // Financial liability - go away if (iFinancialLiability > 0 && iNecessaryRevenueSource == 0) { iScoreForLiberate += 1000; } // But also a necessary revenue source? More likely, but not guaranteed. else if (iFinancialLiability > 0 && iFinancialLiability >= iNecessaryRevenueSource) { iScoreForLiberate *= 200; iScoreForLiberate /= 100; } // Don't liberate voluntary vassals unless they're a financial liability - they can choose to leave on their own if they don't like us // Also don't liberate Gandhi unless we need to or he can't go nuclear on our ass else if (bNuclearGandhiException || IsVoluntaryVassalage(ePlayer)) { iScoreForLiberate = 0; return false; } // Good source of revenue for us - not so likely to break off else if (bGoodRevenueSource) { iScoreForLiberate *= 50; iScoreForLiberate /= 100; } switch (eVassalStrength) { case STRENGTH_PATHETIC: iScoreForLiberate *= 125; iScoreForLiberate /= 100; break; case STRENGTH_WEAK: iScoreForLiberate *= 115; iScoreForLiberate /= 100; break; case STRENGTH_POOR: iScoreForLiberate *= 100; iScoreForLiberate /= 100; break; case STRENGTH_AVERAGE: iScoreForLiberate *= 90; iScoreForLiberate /= 100; break; default: UNREACHABLE(); } switch (eVassalEcoStrength) { case STRENGTH_PATHETIC: iScoreForLiberate *= 125; iScoreForLiberate /= 100; break; case STRENGTH_WEAK: iScoreForLiberate *= 115; iScoreForLiberate /= 100; break; case STRENGTH_POOR: iScoreForLiberate *= 100; iScoreForLiberate /= 100; break; case STRENGTH_AVERAGE: iScoreForLiberate *= 90; iScoreForLiberate /= 100; break; default: UNREACHABLE(); } // Mod based on proximity switch (m_pPlayer->GetProximityToPlayer(ePlayer)) { case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: iScoreForLiberate *= 125; iScoreForLiberate /= 100; break; case PLAYER_PROXIMITY_FAR: iScoreForLiberate *= 110; iScoreForLiberate /= 100; break; case PLAYER_PROXIMITY_CLOSE: iScoreForLiberate *= 100; iScoreForLiberate /= 100; break; case PLAYER_PROXIMITY_NEIGHBORS: iScoreForLiberate *= 75; iScoreForLiberate /= 100; break; } // City comparison modifier if (iNumVassalPop * 100 > iNumPop * 75) { iScoreForLiberate *= 75; iScoreForLiberate /= 100; } else if (iNumVassalPop * 100 > iNumPop * 50) { iScoreForLiberate *= 100; iScoreForLiberate /= 100; } else if (iNumVassalPop * 100 > iNumPop * 33) { iScoreForLiberate *= 125; iScoreForLiberate /= 100; } else { iScoreForLiberate *= 150; // not a threat at all to us iScoreForLiberate /= 100; } int iDominanceOverVassal = eMasterInfluence - eVassalInfluence; // someone is pretty dominant over vassal (not too much of a modifier, but helps our chances of liberation) if (iDominanceOverVassal > 0) { iScoreForLiberate *= 120; iScoreForLiberate /= 100; } else if (iDominanceOverVassal < 0) { iScoreForLiberate *= 50; iScoreForLiberate /= 100; } // We're influential over them if (eMasterInfluence >= INFLUENCE_LEVEL_INFLUENTIAL) { iScoreForLiberate *= 150; iScoreForLiberate /= 100; } // The longer they've been our vassal, give a very small boost toward liberation iScoreForLiberate *= 100 + (GET_TEAM(eVassalTeam).GetNumTurnsIsVassal() * GC.getGame().getGameSpeedInfo().getTrainPercent() / 1000); // 1% per 10 turns (standard speed) iScoreForLiberate /= 100; return iScoreForLiberate > /*100*/ GD_INT_GET(VASSALAGE_LIBERATE_BASE_THRESHOLD); } /// Possible Contact Statement - Third-party offer for ePlayer to liberate their vassals void CvDiplomacyAI::DoRevokeVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); // note: we check to see if it's possible in IsMakeOfferForVassalage() if(eStatement == NO_DIPLO_STATEMENT_TYPE) { // Can we make an offer for vassalage? if(GetPlayer()->GetDealAI()->IsMakeOfferForRevokeVassalage(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_REVOKE_VASSALAGE_THIRD_PARTY; int iTurnsBetweenStatement = 50; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatement) { // Send the statement if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatement) eStatement = eTempStatement; } } else { pDeal->ClearItems(); } } } /// Do we want to become the vassal of ePlayer? bool CvDiplomacyAI::IsVassalageAcceptable(PlayerTypes ePlayer, bool bMasterEvaluation) { if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return false; // Shadow AI does not make decisions for human! if (GetPlayer()->IsAITeammateOfHuman()) return false; vector vOurTeam = GET_TEAM(GetTeam()).getPlayers(); vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); int iNumTeamMembersInFavor = 0; int iNumTeamMembersAgainst = 0; // We are the MASTER receiving the request if (bMasterEvaluation) { // Always willing to accept capitulation if we're willing to make peace if (IsAtWar(ePlayer)) return true; for (size_t i=0; iIsVoluntaryVassalageRequestAcceptable(vTheirTeam[j])) { bYes = true; iNumTeamMembersInFavor++; break; } } if (!bYes) iNumTeamMembersAgainst++; } // More team members must agree than refuse return iNumTeamMembersInFavor > 0 && iNumTeamMembersInFavor > iNumTeamMembersAgainst; } // We are already ePlayer's vassal if (IsVassal(ePlayer)) return false; // We can't become ePlayer's vassal if (!GET_TEAM(GetTeam()).canBecomeVassal(GET_PLAYER(ePlayer).getTeam())) return false; // Can't voluntarily capitulate if we have vassals if (!IsAtWar(ePlayer) && GetPlayer()->GetNumVassals() > 0) return false; // If a player on this team was resurrected by one team, can't voluntarily capitulate to another team if (!IsAtWar(ePlayer) && GET_TEAM(GetTeam()).GetLiberatedByTeam() != NO_TEAM && GET_TEAM(GetTeam()).GetLiberatedByTeam() != GET_PLAYER(ePlayer).getTeam()) return false; for (size_t i=0; iIsCapitulationAcceptable(vTheirTeam[j])) { bYes = true; iNumTeamMembersInFavor++; break; } } else { if (GET_PLAYER(vOurTeam[i]).GetDiplomacyAI()->IsVoluntaryVassalageAcceptable(vTheirTeam[j])) { bYes = true; iNumTeamMembersInFavor++; break; } } } if (!bYes) iNumTeamMembersAgainst++; } // More team members must agree than refuse return iNumTeamMembersInFavor > 0 && iNumTeamMembersInFavor > iNumTeamMembersAgainst; } /// Do we want to capitulate to ePlayer due to war? bool CvDiplomacyAI::IsCapitulationAcceptable(PlayerTypes ePlayer) { int iWarScore = GetWarScore(ePlayer); // We've totally lost... if (iWarScore <= -95) return true; // War score must be at least -75 to even consider it if (iWarScore > -75) return false; // Not defensive if (GetWarState(ePlayer) > WAR_STATE_DEFENSIVE) return false; int iCapitulationScore = iWarScore * -1; // How strong are they militarily? (must be stronger!) switch (GetMilitaryStrengthComparedToUs(ePlayer)) { case STRENGTH_IMMENSE: iCapitulationScore += 20; break; case STRENGTH_POWERFUL: iCapitulationScore += 10; break; case STRENGTH_STRONG: iCapitulationScore += 5; break; default: return false; break; } // Are they threatening us with military? switch (GetMilitaryAggressivePosture(ePlayer)) { case AGGRESSIVE_POSTURE_INCREDIBLE: iCapitulationScore += 10; break; case AGGRESSIVE_POSTURE_HIGH: iCapitulationScore += 5; break; case AGGRESSIVE_POSTURE_MEDIUM: iCapitulationScore += 0; break; default: return false; break; } // How close are they to us? switch (GET_PLAYER(ePlayer).GetProximityToPlayer(GetID())) { case PLAYER_PROXIMITY_NEIGHBORS: iCapitulationScore += 10; break; case PLAYER_PROXIMITY_CLOSE: iCapitulationScore += 0; break; case PLAYER_PROXIMITY_FAR: iCapitulationScore -= 10; break; case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: iCapitulationScore -= 20; break; } // We're going for conquest if (IsGoingForWorldConquest()) iCapitulationScore -= 20; // We're close to victory? Resist! if (IsCloseToAnyVictoryCondition()) iCapitulationScore -= 20; // Are we very unhappy? if (GetPlayer()->IsEmpireSuperUnhappy()) { iCapitulationScore += 20; } else if (GetPlayer()->IsEmpireVeryUnhappy()) { iCapitulationScore += 10; } // No? How about them? else { iCapitulationScore -= GetPlayer()->IsEmpireUnhappy() ? 5 : 10; // Their cities might revolt! Resist! if (GET_PLAYER(ePlayer).IsEmpireSuperUnhappy()) return false; // If they're very unhappy, hold out for longer... else if (GET_PLAYER(ePlayer).IsEmpireVeryUnhappy()) iCapitulationScore -= 10; } // Are any of our cities about to be lost? Try to maintain as many of our cities as we can if we're going to lose anyway. int iCityLoop = 0; int iNumCitiesInDanger = 0; vector vTheirWarAllies = GetOffensiveWarAllies(ePlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ true); vTheirWarAllies.push_back(ePlayer); for (CvCity* pLoopCity = GetPlayer()->firstCity(&iCityLoop); pLoopCity != NULL; pLoopCity = GetPlayer()->nextCity(&iCityLoop)) { if (!pLoopCity->IsInDangerFromPlayers(vTheirWarAllies)) continue; iNumCitiesInDanger++; if (pLoopCity->isInDangerOfFalling(true) || pLoopCity->getDamage() >= (pLoopCity->GetMaxHitPoints()/3)) { // Our last city about to fall? if (GetPlayer()->getNumCities() == 1) return true; iCapitulationScore += 20; } else if ((pLoopCity->IsBlockadedWaterAndLand() && pLoopCity->getDamage() >= (pLoopCity->GetMaxHitPoints()/4)) || pLoopCity->getDamage() >= (pLoopCity->GetMaxHitPoints()/2)) { iCapitulationScore += 10; } } // Are any of THEIR cities in danger? Try to grab any cities we can from them. int iNumTheirCitiesInDanger = 0; vector vOurWarAllies = GetOffensiveWarAllies(ePlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ false); vOurWarAllies.push_back(GetID()); for (CvCity* pLoopCity = GET_PLAYER(ePlayer).firstCity(&iCityLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iCityLoop)) { if (!pLoopCity->IsInDangerFromPlayers(vOurWarAllies)) continue; iNumTheirCitiesInDanger++; if (pLoopCity->isInDangerOfFalling(true) || pLoopCity->getDamage() >= (pLoopCity->GetMaxHitPoints()/3)) { return false; } else if (pLoopCity->IsBlockadedWaterAndLand() && pLoopCity->getDamage() >= (pLoopCity->GetMaxHitPoints()/4)) { // Don't surrender if we could get our capital or Holy City back! if (pLoopCity->IsOriginalCapitalForPlayer(GetID()) || pLoopCity->GetCityReligions()->IsHolyCityForReligion(GetPlayer()->GetReligions()->GetOriginalReligionCreatedByPlayer())) return false; iCapitulationScore -= 15; } else if (pLoopCity->isUnderSiege() && pLoopCity->getDamage() >= (pLoopCity->GetMaxHitPoints()/2)) { // Don't surrender if we could get our capital or Holy City back! if (pLoopCity->IsOriginalCapitalForPlayer(GetID()) || pLoopCity->GetCityReligions()->IsHolyCityForReligion(GetPlayer()->GetReligions()->GetOriginalReligionCreatedByPlayer())) return false; iCapitulationScore -= 10; } } // Have we lost our capital? if (GetPlayer()->IsHasLostCapital()) { iCapitulationScore += 20; // To them? if (IsCapitalCapturedBy(ePlayer, true, true)) { iCapitulationScore += 20; } } // No cities in danger and we still have our capital? Resist! else if (iNumCitiesInDanger == 0) { return false; } // At least as many of their cities are endangered as ours? Resist! else if (iNumTheirCitiesInDanger >= iNumCitiesInDanger) { return false; } int iOurCivs = GET_TEAM(GetTeam()).getAliveCount(); int iTheirCivs = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getAliveCount(); // Harder if we have more civs on our team if (iOurCivs > iTheirCivs) { iCapitulationScore -= min(30, 10 * (iOurCivs - iTheirCivs)); } // Easier if they have more civs on their team else if (iTheirCivs > iOurCivs) { iCapitulationScore += min(30, 10 * (iTheirCivs - iOurCivs)); } return iCapitulationScore > /*100*/ GD_INT_GET(VASSALAGE_CAPITULATE_BASE_THRESHOLD); } /// Do we want to voluntarily become ePlayer's vassal? bool CvDiplomacyAI::IsVoluntaryVassalageAcceptable(PlayerTypes ePlayer) { // No voluntary capitulation if there's less than 50% of the players ever alive if (GC.getGame().GetNumMajorCivsAlive() < (GC.getGame().GetNumMajorCivsEver() / 2)) return false; // Never acceptable if they've refused to give us independence! if (IsAngryAboutPlayerVassalageForcefullyRevoked(ePlayer)) return false; // They failed to protect us before! if (GetVassalProtectValue(ePlayer) < 0) return false; // They stole from us recently - no if (GetNumTimesRobbedBy(ePlayer) > 0 || GetNumTimesCultureBombed(ePlayer) > 0 || GetNumTimesPerformedCoupAgainstUs(ePlayer) > 0) return false; // Plotting against us? No thanks! if (GetNumTimesTheyPlottedAgainstUs(ePlayer) > 0) return false; int iOurCivs = GET_TEAM(GetTeam()).getAliveCount(); int iTheirCivs = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getAliveCount(); // We have more members - no if (iOurCivs > iTheirCivs) return false; // They're too far away - no if (GET_PLAYER(ePlayer).GetProximityToPlayer(GetID()) < PLAYER_PROXIMITY_CLOSE) return false; // Refuse if we have different ideologies; we'll lose our ideology! if (GetPlayer()->GetPlayerPolicies()->GetLateGamePolicyTree() != NO_POLICY_BRANCH_TYPE && !IsPlayerSameIdeology(ePlayer)) return false; // if we're hostile with this player, don't want to be his voluntary vassal if (GetCivApproach(ePlayer) <= CIV_APPROACH_GUARDED) return false; if (GetCivOpinion(ePlayer) <= CIV_OPINION_ENEMY) return false; if (GetPlayer()->HasAnyOffensiveOperationsAgainstPlayer(ePlayer) || AvoidExchangesWithPlayer(ePlayer, false)) return false; // Unacceptable if they're untrustworthy (this includes if they captured our capital/Holy City; that's what capitulation is for...) if (IsUntrustworthy(ePlayer)) return false; // Don't become a vassal if we're close to winning! if (IsCloseToAnyVictoryCondition()) return false; // The point of vassalage is protection, so they must be stronger than us if (GetMilitaryStrengthComparedToUs(ePlayer) <= STRENGTH_AVERAGE) return false; if (GetEconomicStrengthComparedToUs(ePlayer) <= STRENGTH_AVERAGE) return false; if (GetTargetValue(ePlayer) >= TARGET_VALUE_FAVORABLE) return false; if (IsEasyTarget(ePlayer)) return false; // If we're going for world conquest, they must be at least 2x stronger than us for us to consider this if (IsGoingForWorldConquest() && GetMilitaryStrengthComparedToUs(ePlayer) < STRENGTH_POWERFUL) return false; // Are we dominating them culturally? if (GetPlayer()->GetCulture()->GetInfluenceLevel(ePlayer) >= INFLUENCE_LEVEL_POPULAR) { if (GetPlayer()->GetCulture()->GetInfluenceLevel(ePlayer) >= INFLUENCE_LEVEL_INFLUENTIAL || GetPlayer()->GetCulture()->GetInfluenceTrend(ePlayer) != INFLUENCE_TREND_FALLING) return false; } // If they aren't significantly bigger than us, they probably can't protect us if (GetPlayer()->getNumCities() >= (GET_PLAYER(ePlayer).getNumCities() * 0.75f)) { return false; } if (GetPlayer()->getTotalPopulation() >= (GET_PLAYER(ePlayer).getTotalPopulation() * 0.75f)) { return false; } // Do we own more capitals than they do? int iOurCapitals = 0; int iTheirCapitals = 0; vector vOurTeam = GET_TEAM(GetTeam()).getPlayers(); vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); for (size_t i=0; iIsCloseToAnyVictoryCondition()) return false; if (!GET_PLAYER(vOurTeam[i]).IsHasLostCapital()) iOurCapitals++; iOurCapitals += GET_PLAYER(vOurTeam[i]).GetNumCapitalCities(); // while we're at it, let's check to see if any of our teammates hate any of their teammates for (size_t j=0; jGetCivApproach(vTheirTeam[j]) <= CIV_APPROACH_GUARDED) return false; if (GET_PLAYER(vOurTeam[i]).GetDiplomacyAI()->GetCivOpinion(vTheirTeam[j]) <= CIV_OPINION_ENEMY) return false; } } for (size_t i=0; i iTheirCapitals) return false; // ...or if we have more capitals than team members if (iOurCapitals > iOurCivs) return false; // If we got down here, then vassalage is possible - let's evaluate int iWantVassalageScore = 0; // What do we think about them? switch (GetCivOpinion(ePlayer)) { case CIV_OPINION_ALLY: iWantVassalageScore += 15; break; case CIV_OPINION_FRIEND: iWantVassalageScore += 5; break; case CIV_OPINION_FAVORABLE: iWantVassalageScore -= 5; break; case CIV_OPINION_NEUTRAL: iWantVassalageScore -= 25; break; case CIV_OPINION_COMPETITOR: iWantVassalageScore -= 50; break; case CIV_OPINION_UNFORGIVABLE: case CIV_OPINION_ENEMY: break; } switch (GetCivApproach(ePlayer)) { case CIV_APPROACH_FRIENDLY: iWantVassalageScore += 5; break; case CIV_APPROACH_NEUTRAL: iWantVassalageScore -= 10; break; case CIV_APPROACH_AFRAID: iWantVassalageScore += 20; break; case CIV_APPROACH_WAR: case CIV_APPROACH_HOSTILE: case CIV_APPROACH_DECEPTIVE: case CIV_APPROACH_GUARDED: break; } // How strong are they compared to us? switch (GetMilitaryStrengthComparedToUs(ePlayer)) { case STRENGTH_IMMENSE: iWantVassalageScore += 40; break; case STRENGTH_POWERFUL: iWantVassalageScore += 20; break; case STRENGTH_STRONG: case STRENGTH_AVERAGE: case STRENGTH_POOR: case STRENGTH_WEAK: case STRENGTH_PATHETIC: break; } switch (GetEconomicStrengthComparedToUs(ePlayer)) { case STRENGTH_IMMENSE: iWantVassalageScore += 40; break; case STRENGTH_POWERFUL: iWantVassalageScore += 20; break; case STRENGTH_STRONG: case STRENGTH_AVERAGE: case STRENGTH_POOR: case STRENGTH_WEAK: case STRENGTH_PATHETIC: break; } // Are they a warmonger? switch (GetWarmongerThreat(ePlayer)) { case THREAT_CRITICAL: iWantVassalageScore -= 150; break; case THREAT_SEVERE: iWantVassalageScore -= 75; break; case THREAT_MAJOR: iWantVassalageScore -= 30; break; case THREAT_MINOR: iWantVassalageScore -= 10; break; case THREAT_NONE: iWantVassalageScore += 10; break; } int iTechPercent = (GET_TEAM(GetTeam()).GetTeamTechs()->GetNumTechsKnown() * 100) / max(1, GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetTeamTechs()->GetNumTechsKnown()); if (iTechPercent > 100) // We are ahead of them in technology! iWantVassalageScore -= 50; else if (iTechPercent >= 95) // We are at a similar tech level! iWantVassalageScore -= 25; else if (iTechPercent >= 85) // Lagging behind! iWantVassalageScore += 10; else if (iTechPercent >= 75) iWantVassalageScore += 20; else if (iTechPercent >= 65) // Really far behind! iWantVassalageScore += 30; else iWantVassalageScore += 40; // Unhappy? if (GetPlayer()->IsEmpireUnhappy()) iWantVassalageScore += 10; // Unique abilities influence this... if (GetPlayer()->GetPlayerTraits()->IsWarmonger() || GetPlayer()->GetPlayerTraits()->IsExpansionist() || GetPlayer()->GetPlayerTraits()->IsDiplomat()) { iWantVassalageScore -= 25; } else if (GetPlayer()->GetPlayerTraits()->IsNerd() || GetPlayer()->GetPlayerTraits()->IsTourism() || GetPlayer()->GetPlayerTraits()->IsSmaller()) { iWantVassalageScore += 20; } // Adjust score based on civ flavors iWantVassalageScore += (GetDoFWillingness() + GetLoyalty()) * 2; iWantVassalageScore -= (GetBoldness() + GetDiploBalance()) * 2; // Evaluate current wars for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (IsPlayerValid(eLoopPlayer) && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsPlayerValid(eLoopPlayer)) { // At war with a common foe? if (IsAtWar(eLoopPlayer) && GET_PLAYER(ePlayer).IsAtWarWith(eLoopPlayer)) { WarStateTypes eOurWarState = GetWarState(eLoopPlayer); WarStateTypes eTheirWarState = GET_PLAYER(ePlayer).GetDiplomacyAI()->GetWarState(eLoopPlayer); // Are they doing better than us? if (eOurWarState < WAR_STATE_OFFENSIVE && eTheirWarState > WAR_STATE_STALEMATE && eTheirWarState > eOurWarState) { iWantVassalageScore += ((int)eTheirWarState - (int)eOurWarState) * 30; } // Are they doing worse than us? else if (eTheirWarState < WAR_STATE_OFFENSIVE && eOurWarState > eTheirWarState) { iWantVassalageScore -= ((int)eOurWarState - (int)eTheirWarState) * 20; } } // We're at war and they're not? else if (IsAtWar(eLoopPlayer) && !GET_PLAYER(ePlayer).IsAtWarWith(eLoopPlayer)) { int iStrengthFactor = (int)GET_PLAYER(ePlayer).GetDiplomacyAI()->GetMilitaryStrengthComparedToUs(eLoopPlayer) - 3; switch (GetWarState(eLoopPlayer)) { case NO_WAR_STATE_TYPE: UNREACHABLE(); case WAR_STATE_NEARLY_WON: iWantVassalageScore -= 80; break; case WAR_STATE_OFFENSIVE: iWantVassalageScore -= 30; break; case WAR_STATE_CALM: iWantVassalageScore -= 10; break; case WAR_STATE_STALEMATE: iWantVassalageScore += 10 * iStrengthFactor; break; case WAR_STATE_TROUBLED: case WAR_STATE_DEFENSIVE: iWantVassalageScore += 30 * iStrengthFactor; break; case WAR_STATE_NEARLY_DEFEATED: iWantVassalageScore += 80 * iStrengthFactor; break; } } // They're at war and we're not? else if (!IsAtWar(eLoopPlayer) && GET_PLAYER(ePlayer).IsAtWarWith(eLoopPlayer)) { // At war with our friends or trade partners? Grr... if (IsFriendOrAlly(eLoopPlayer) || GetCivOpinion(eLoopPlayer) >= CIV_OPINION_FRIEND || (IsStrategicTradePartner(eLoopPlayer) && GetCivOpinion(eLoopPlayer) >= CIV_OPINION_FAVORABLE)) { iWantVassalageScore -= 100; } // With someone close to us? else if (GET_PLAYER(eLoopPlayer).GetProximityToPlayer(GetID()) >= PLAYER_PROXIMITY_CLOSE) { int iStrengthFactor = (int)GetMilitaryStrengthComparedToUs(eLoopPlayer) - 3; int iOpinionFactor = GetCivOpinion(ePlayer) <= CIV_OPINION_ENEMY ? 1 : 2; // How's the war going? switch (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetWarState(eLoopPlayer)) { case NO_WAR_STATE_TYPE: UNREACHABLE(); case WAR_STATE_NEARLY_DEFEATED: iWantVassalageScore -= 100 * iStrengthFactor * iOpinionFactor; // eep! No thanks! break; case WAR_STATE_DEFENSIVE: case WAR_STATE_TROUBLED: iWantVassalageScore -= 30 * iStrengthFactor * iOpinionFactor; // should probably bail! break; case WAR_STATE_STALEMATE: iWantVassalageScore -= 10 * iStrengthFactor * iOpinionFactor; // bail if they're too strong or we don't dislike them enough break; case WAR_STATE_CALM: iWantVassalageScore -= 5 * iStrengthFactor * iOpinionFactor; break; case WAR_STATE_OFFENSIVE: iWantVassalageScore += 5 * iStrengthFactor * iOpinionFactor; // may want to become a voluntary vassal if they're going to conquer our neighbors, especially if we don't like them... break; case WAR_STATE_NEARLY_WON: iWantVassalageScore += 10 * iStrengthFactor * iOpinionFactor; // ditto, but moreso! break; } } } } } // Modify score based on global politics - are they likely to support our interests? iWantVassalageScore += GetNumMutualFriends(ePlayer) * 5; iWantVassalageScore += GetNumEnemiesDenounced(ePlayer) * 5; iWantVassalageScore += GetNumMutualDefensePacts(ePlayer) * 10; iWantVassalageScore -= GetNumEnemyFriends(ePlayer) * 20; iWantVassalageScore -= GetNumEnemyDefensePacts(ePlayer) * 20; iWantVassalageScore -= GetNumFriendsDenounced(ePlayer) * 20; // Increase score if they've liberated us if (WasResurrectedBy(ePlayer)) iWantVassalageScore += 100; if (IsPlayerLiberatedCapital(ePlayer)) iWantVassalageScore += 50; else if (IsPlayerReturnedCapital(ePlayer)) iWantVassalageScore += 20; if (IsPlayerLiberatedHolyCity(ePlayer)) iWantVassalageScore += 30; else if (IsPlayerReturnedHolyCity(ePlayer)) iWantVassalageScore += 10; if (GetNumCitiesEverLiberatedBy(ePlayer) > 0 && GetNumCitiesEverLiberatedBy(ePlayer) > GetNumCitiesCapturedBy(ePlayer)) iWantVassalageScore += 10 * (GetNumCitiesEverLiberatedBy(ePlayer) - GetNumCitiesCapturedBy(ePlayer)); // Reduce score based on past aggression towards us iWantVassalageScore -= GetNumWarsDeclaredOnUs(ePlayer) * 10; iWantVassalageScore -= GetNumCitiesCapturedBy(ePlayer) * 30; // Reduce score based on number of his vassals iWantVassalageScore -= 20 * GET_PLAYER(ePlayer).GetNumVassals(); if (iWantVassalageScore <= 0) return false; // Modifier based on culture int iCulturalDominanceOverUs = GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(GetID()) - GetPlayer()->GetCulture()->GetInfluenceLevel(ePlayer); iWantVassalageScore *= (100 + 10 * iCulturalDominanceOverUs); iWantVassalageScore /= 100; // Modifier based on proximity switch (GET_PLAYER(ePlayer).GetProximityToPlayer(GetID())) { case PLAYER_PROXIMITY_NEIGHBORS: iWantVassalageScore *= 125; iWantVassalageScore /= 100; break; case PLAYER_PROXIMITY_CLOSE: iWantVassalageScore *= 100; iWantVassalageScore /= 100; break; case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_FAR: case PLAYER_PROXIMITY_DISTANT: break; } return iWantVassalageScore >= /*100*/ GD_INT_GET(VASSALAGE_CAPITULATE_BASE_THRESHOLD); } /// Do we want to accept ePlayer as our voluntary vassal? bool CvDiplomacyAI::IsVoluntaryVassalageRequestAcceptable(PlayerTypes ePlayer) { // We will only accept forced capitulation from backstabbers and people who have stolen cities from us. if (IsUntrustworthy(ePlayer) || (GetNumCitiesCapturedBy(ePlayer) > 0 && GetPlayer()->OwnsOurCity(ePlayer))) return false; vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); bool bWeLikeOneOfThem = false; for (size_t i=0; i= CIV_OPINION_FRIEND && GetCivApproach(vTheirTeam[i]) == CIV_APPROACH_FRIENDLY); // We must be stronger. if (bWeLikeThem) { if (GetMilitaryStrengthComparedToUs(vTheirTeam[i]) > STRENGTH_STRONG) return false; if (GetEconomicStrengthComparedToUs(vTheirTeam[i]) > STRENGTH_STRONG) return false; // If we like them and we can't honestly protect them, then don't agree. if (GetPlayer()->GetProximityToPlayer(vTheirTeam[i]) < PLAYER_PROXIMITY_CLOSE) return false; bWeLikeOneOfThem = true; } else { if (GetMilitaryStrengthComparedToUs(vTheirTeam[i]) > STRENGTH_AVERAGE) return false; if (GetEconomicStrengthComparedToUs(vTheirTeam[i]) > STRENGTH_AVERAGE) return false; } } // We must be willing to go to war with everyone they're currently at war with. vector vNewWarPlayers; for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; if (!IsPlayerValid(eLoopPlayer)) continue; // We're already at war - ignore if (IsAtWar(eLoopPlayer)) continue; if (GET_PLAYER(ePlayer).IsAtWarWith(eLoopPlayer)) { if (std::find(vNewWarPlayers.begin(), vNewWarPlayers.end(), eLoopPlayer) == vNewWarPlayers.end()) vNewWarPlayers.push_back(eLoopPlayer); vector vDefensiveWarAllies = GetDefensiveWarAllies(eLoopPlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ true, /*bNewWarsOnly*/ true); for (std::vector::iterator it = vDefensiveWarAllies.begin(); it != vDefensiveWarAllies.end(); ++it) { if (std::find(vNewWarPlayers.begin(), vNewWarPlayers.end(), *it) == vNewWarPlayers.end()) vNewWarPlayers.push_back(*it); } } } for (std::vector::iterator it = vNewWarPlayers.begin(); it != vNewWarPlayers.end(); ++it) { // Don't attack friends or people keeping us financially afloat. if (!IsWarSane(*it)) return false; // Don't attack if they're not a potential war target. if (GET_PLAYER(*it).isMajorCiv() && !IsPotentialWarTarget(*it)) return false; // Ignore City-States unless peace blocked, since we can just make peace as their new master if we want. if (GET_PLAYER(*it).isMinorCiv()) { PeaceBlockReasons eReason = GET_PLAYER(ePlayer).GetDiplomacyAI()->GetPeaceBlockReason(*it); if (eReason == PEACE_BLOCK_REASON_ALWAYS_WAR || eReason == PEACE_BLOCK_REASON_AT_WAR_WITH_ALLY || eReason == PEACE_BLOCK_REASON_SCENARIO) { if (GetCivApproach(*it) > CIV_APPROACH_HOSTILE && GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) return false; } } // Avoid DoWing a powerful neighbor. else if (GET_PLAYER(*it).GetProximityToPlayer(GetID()) >= PLAYER_PROXIMITY_CLOSE) { if (GetCivApproach(*it) == CIV_APPROACH_AFRAID) return false; else if (GetCivApproach(*it) != CIV_APPROACH_WAR && GetDemandTargetPlayer() != *it) { if (bWeLikeOneOfThem && GetMilitaryStrengthComparedToUs(*it) > STRENGTH_STRONG) return false; else if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) return false; } } } // If everything is clear, then accept. return true; } /// Is it acceptable for us to terminate the vassalage agreement with ePlayer? bool CvDiplomacyAI::IsEndVassalageAcceptable(PlayerTypes ePlayer) { // Shadow AI does not make decisions for human! if (GetPlayer()->IsAITeammateOfHuman()) return false; // We are the MASTER receiving the request if (IsMaster(ePlayer)) { int iPeaceful = 0; int iForceful = 0; vector vOurTeam = GET_TEAM(GetTeam()).getPlayers(); vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); for (size_t i=0; iIsEndVassalageRequestAcceptable(vTheirTeam[j])) iPeaceful++; else iForceful++; } } return iPeaceful > 0 && iPeaceful > iForceful; } if (!IsVassal(ePlayer)) return false; // We are the VASSAL sending the request // If we actually can't end Vassalage with ePlayer (conditions not satisfied) then abort if (!GET_TEAM(GetTeam()).canEndVassal(GET_PLAYER(ePlayer).getTeam())) return false; // don't do this in anarchy if (m_pPlayer->IsAnarchy()) return false; // if we're close to winning, we want out if (IsCloseToAnyVictoryCondition()) return true; int iOurCivs = 0; int iOurCapitals = 0; int iTheirCapitals = 0; int iMastersWantIndependenceFrom = 0; int iMastersWantToRemainWith = 0; vector vOurTeam = GET_TEAM(GetTeam()).getPlayers(); vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); for (size_t i=0; iIsCloseToAnyVictoryCondition()) return true; if (!GET_PLAYER(vOurTeam[i]).IsHasLostCapital()) iOurCapitals++; iOurCivs++; iOurCapitals += GET_PLAYER(vOurTeam[i]).GetNumCapitalCities(); } for (size_t i=0; i iTheirCapitals) return true; if (iOurCapitals > iOurCivs) return true; // Don't want independence from anyone? if (iMastersWantIndependenceFrom == 0) return false; // Want to remain with more masters than we want to abandon? if (iMastersWantToRemainWith > 0 && iMastersWantToRemainWith > iMastersWantIndependenceFrom) return false; // If a single member on the team wants independence, all other members must accept it to give their teammate a shot at winning. return true; } bool CvDiplomacyAI::IsEndVassalageWithPlayerAcceptable(PlayerTypes ePlayer) { int iIndependenceScore = GetBoldness(); bool bVoluntary = IsVoluntaryVassalage(ePlayer); // Do not declare independence if we're bankrupt if (GetPlayer()->GetTreasury()->GetGold() <= 0 && GetPlayer()->getAvgGoldRate() <= 0) return false; // If voluntary vassal and we're losing wars, don't break the agreement if (bVoluntary && GetStateAllWars() == STATE_ALL_WARS_LOSING) return false; // How are we being treated? This is a major factor! switch (GetVassalTreatmentLevel(ePlayer)) { case NO_VASSAL_TREATMENT: UNREACHABLE(); // This shouldn't have been called if they aren't a vassal. case VASSAL_TREATMENT_CONTENT: iIndependenceScore -= 20; break; case VASSAL_TREATMENT_DISAGREE: iIndependenceScore += 20; break; case VASSAL_TREATMENT_MISTREATED: iIndependenceScore += 50; break; case VASSAL_TREATMENT_UNHAPPY: iIndependenceScore += 70; break; case VASSAL_TREATMENT_ENSLAVED: iIndependenceScore += 100; break; } // Double this for voluntary vassalage. if (bVoluntary) iIndependenceScore *= 2; // How strong are they compared to us? Military strength is a huge factor. switch (GetMilitaryStrengthComparedToUs(ePlayer)) { case STRENGTH_IMMENSE: iIndependenceScore -= 200; break; case STRENGTH_POWERFUL: iIndependenceScore -= 100; break; case STRENGTH_STRONG: iIndependenceScore -= 50; break; case STRENGTH_AVERAGE: iIndependenceScore += 30; break; case STRENGTH_POOR: iIndependenceScore += 70; break; case STRENGTH_WEAK: iIndependenceScore += 150; break; case STRENGTH_PATHETIC: iIndependenceScore += 300; break; } // How far are they from us? More likely to request independence from distant players than neighbors switch (GET_PLAYER(ePlayer).GetProximityToPlayer(GetID())) { case PLAYER_PROXIMITY_NEIGHBORS: iIndependenceScore -= 30; break; case PLAYER_PROXIMITY_CLOSE: iIndependenceScore -= 15; break; case PLAYER_PROXIMITY_FAR: iIndependenceScore += 15; break; case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: iIndependenceScore += 30; break; } // What do we think of them? switch (GetCivOpinion(ePlayer)) { case CIV_OPINION_UNFORGIVABLE: iIndependenceScore += 50; break; case CIV_OPINION_ENEMY: iIndependenceScore += 30; break; case CIV_OPINION_COMPETITOR: iIndependenceScore += 15; break; case CIV_OPINION_NEUTRAL: iIndependenceScore -= 10; break; case CIV_OPINION_FAVORABLE: iIndependenceScore -= 20; break; case CIV_OPINION_FRIEND: iIndependenceScore -= 30; break; case CIV_OPINION_ALLY: iIndependenceScore -= 50; break; } if (GetCivApproach(ePlayer) <= CIV_APPROACH_GUARDED) { iIndependenceScore += 25; } else if (GetCivApproach(ePlayer) == CIV_APPROACH_AFRAID) { iIndependenceScore -= 50; } // Are we unhappy? if (GetPlayer()->IsEmpireSuperUnhappy()) { iIndependenceScore -= 200; } else if (GetPlayer()->IsEmpireVeryUnhappy()) { iIndependenceScore -= 80; } else if (GetPlayer()->IsEmpireUnhappy()) { iIndependenceScore -= 20; } else { iIndependenceScore += 10; } // Did they resurrect us? if (WasResurrectedBy(ePlayer)) iIndependenceScore -= 50; if (iIndependenceScore <= 0) return false; // Modifier based on number of wars currently fighting - don't like lots of conflict if (!bVoluntary) { iIndependenceScore *= (100 + GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getAtWarCount(true) * 20); iIndependenceScore /= 100; } // Are we culturally dominant over them? int iCulturalDominanceOverUs = (int)GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(GetID()) - (int)GetPlayer()->GetCulture()->GetInfluenceLevel(ePlayer); if (iCulturalDominanceOverUs <= 0) { iIndependenceScore *= 150; iIndependenceScore /= 100; } // Master is failing to protect us if (GetVassalProtectValue(ePlayer) < 0) { iIndependenceScore *= 200; iIndependenceScore /= 100; } // Master is protecting us else if (GetVassalProtectValue(ePlayer) > 0) { iIndependenceScore *= bVoluntary ? 50 : 75; iIndependenceScore /= 100; } // We're bigger than him? Bail! if (GetPlayer()->getNumCities() > GET_PLAYER(ePlayer).getNumCities()) { iIndependenceScore *= 200; iIndependenceScore /= 100; } if (GetPlayer()->getTotalPopulation() > GET_PLAYER(ePlayer).getTotalPopulation()) { iIndependenceScore *= 200; iIndependenceScore /= 100; } // if we're a warmonger if (IsGoingForWorldConquest() || GetPlayer()->GetPlayerTraits()->IsWarmonger()) { iIndependenceScore *= 200; iIndependenceScore /= 100; } // Master is threatening us with military near our borders switch (GetMilitaryAggressivePosture(ePlayer)) { case AGGRESSIVE_POSTURE_NONE: break; case AGGRESSIVE_POSTURE_INCREDIBLE: iIndependenceScore *= 40; iIndependenceScore /= 100; break; case AGGRESSIVE_POSTURE_HIGH: iIndependenceScore *= 60; iIndependenceScore /= 100; break; case AGGRESSIVE_POSTURE_MEDIUM: iIndependenceScore *= 80; iIndependenceScore /= 100; break; case AGGRESSIVE_POSTURE_LOW: iIndependenceScore *= 90; iIndependenceScore /= 100; break; } return iIndependenceScore >= 100; // todo: global define } /// Player ended vassalage with us, is that acceptable? bool CvDiplomacyAI::IsEndVassalageRequestAcceptable(PlayerTypes ePlayer) { if (!IsMaster(ePlayer)) return false; CivOpinionTypes const eOpinion = GetCivOpinion(ePlayer); CivApproachTypes const eApproach = GetCivApproach(ePlayer); // We hate him - war! if (eOpinion <= CIV_OPINION_ENEMY) return false; if (eApproach <= CIV_APPROACH_GUARDED) return false; // We're afraid - give in if (eApproach == CIV_APPROACH_AFRAID) return true; // We're a backstabber - too bad! if (IsBackstabber()) return false; // Player has original capitals and we're going for world conquest - not a chance! if (IsGoingForWorldConquest() || IsCloseToWorldConquest()) { if (!GET_PLAYER(ePlayer).IsHasLostCapital() || GET_PLAYER(ePlayer).GetNumCapitalCities() > 0) return false; } // We're close to other victory conditions and they're not a threat - let them go else if (IsCloseToAnyVictoryCondition() && !GET_PLAYER(ePlayer).GetDiplomacyAI()->IsCloseToAnyVictoryCondition() && GetMilitaryStrengthComparedToUs(ePlayer) < STRENGTH_AVERAGE) { return true; } int iChanceToGiveIn = 0; // Do we like him? switch (eOpinion) { case CIV_OPINION_ENEMY: case CIV_OPINION_UNFORGIVABLE: UNREACHABLE(); case CIV_OPINION_ALLY: iChanceToGiveIn += 60; break; case CIV_OPINION_FRIEND: iChanceToGiveIn += 40; break; case CIV_OPINION_FAVORABLE: iChanceToGiveIn += 20; break; case CIV_OPINION_COMPETITOR: iChanceToGiveIn -= 20; break; case CIV_OPINION_NEUTRAL: break; } switch (GetMilitaryStrengthComparedToUs(ePlayer)) { case STRENGTH_IMMENSE: iChanceToGiveIn += 90; break; case STRENGTH_POWERFUL: iChanceToGiveIn += 60; break; case STRENGTH_STRONG: iChanceToGiveIn += 30; break; case STRENGTH_AVERAGE: iChanceToGiveIn += 0; break; case STRENGTH_POOR: iChanceToGiveIn -= 30; break; case STRENGTH_WEAK: iChanceToGiveIn -= 60; break; case STRENGTH_PATHETIC: iChanceToGiveIn -= 90; break; } // How much money are we making off this vassal and his team? int iTaxIncome = GET_PLAYER(ePlayer).GetTreasury()->GetExpensePerTurnFromVassalTaxesTimes100(); int iOurGPT = m_pPlayer->calculateGoldRateTimes100(); if (iTaxIncome * 50 >= iOurGPT) // Tax is 50% or more of our income, heavily reliant on it iChanceToGiveIn += -25; else if (iTaxIncome * 20 >= iOurGPT) // Tax is 20% or more of our income iChanceToGiveIn += -10; else iChanceToGiveIn += 0; // Resurrection in either direction? if (WasResurrectedBy(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->WasResurrectedBy(GetID())) iChanceToGiveIn += 150; // Liberator? if (IsPlayerLiberatedCapital(ePlayer) || IsPlayerLiberatedHolyCity(ePlayer)) iChanceToGiveIn += 50; else if (IsPlayerReturnedCapital(ePlayer) || IsPlayerReturnedHolyCity(ePlayer)) iChanceToGiveIn += 25; if (GetNumCitiesEverLiberatedBy(ePlayer) > 0 && GetNumCitiesEverLiberatedBy(ePlayer) > GetNumCitiesCapturedBy(ePlayer)) iChanceToGiveIn += 10 * (GetNumCitiesEverLiberatedBy(ePlayer) - GetNumCitiesCapturedBy(ePlayer)); // We're close to or going for Diplomatic Victory - we want their league votes if (IsGoingForDiploVictory() || IsCloseToDiploVictory()) { if (!GET_PLAYER(ePlayer).GetDiplomacyAI()->WasResurrectedBy(GetID())) { iChanceToGiveIn -= 25; } } if (iChanceToGiveIn <= 0) return false; switch (GetPlayer()->GetProximityToPlayer(ePlayer)) { case PLAYER_PROXIMITY_NEIGHBORS: iChanceToGiveIn *= 80; break; case PLAYER_PROXIMITY_CLOSE: iChanceToGiveIn *= 100; break; case PLAYER_PROXIMITY_FAR: iChanceToGiveIn *= 110; break; case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: iChanceToGiveIn *= 120; break; } iChanceToGiveIn /= 1000; if (iChanceToGiveIn <= 3) iChanceToGiveIn = 3; return GC.getGame().randRangeInclusive(1, 10, CvSeeder::fromRaw(0xda4c95ed).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) <= iChanceToGiveIn; } /// Possible Contact Statement - We're done being ePlayer's vassal void CvDiplomacyAI::DoEndVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); // Don't send this to an AI teammate of a human if (GET_PLAYER(ePlayer).IsAITeammateOfHuman()) return; if (MOD_DIPLOAI_SHUT_UP_INDEPENDENCE_REQUESTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { // Must be vassal of ePlayer if(IsVassal(ePlayer)) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_REVOKE_VASSALAGE; int iTurnsBetweenStatement = 10; // Done being this player's vassal? if (IsEndVassalageAcceptable(ePlayer)) { if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatement) eStatement = eTempStatement; } } } } /// Possible Contact Statement - World Map void CvDiplomacyAI::DoMapsOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (GetPlayer()->IsAITeammateOfHuman()) return; //problem: on larger maps evaluating the map value every turn for every player is significant performance overhead //solution: we could cache the value for the active player, saving half the effort, but it's simpler to just not even contemplate the offer every turn if ((GC.getGame().getGameTurn() + GetID()) % 5 != 0) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_MAPS_OFFER; int iTurnsBetweenStatements = 30; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { if (GetPlayer()->GetDealAI()->IsMakeOfferForMaps(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { eStatement = eTempStatement; } else { pDeal->ClearItems(); } } } } /// Possible Contact Statement - Purchase technology void CvDiplomacyAI::DoTechOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (GetPlayer()->IsAITeammateOfHuman()) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_TECH_OFFER; int iTurnsBetweenStatements = 30; if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { if (GetPlayer()->GetDealAI()->IsMakeOfferForTech(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { eStatement = eTempStatement; } else { pDeal->ClearItems(); } } } } /// Possible Contact Statement - Generous Offer void CvDiplomacyAI::DoGenerousOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if(eStatement == NO_DIPLO_STATEMENT_TYPE) { DiploStatementTypes eTempStatement = DIPLO_STATEMENT_GENEROUS_OFFER; if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 60 && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_GENEROUS_OFFER_RANDFAILED) >= 15 && // Don't send a generous offer request if we recently sent a Request to this player GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_REQUEST) >= 25) { bool bRandPassed = false; bool bMakeGenerousOffer = IsMakeGenerousOffer(ePlayer, pDeal, bRandPassed); if(bMakeGenerousOffer && pDeal->GetNumItems() > 0) { eStatement = eTempStatement; SetOfferingGift(ePlayer, true); bRandPassed = true; pDeal->m_bIsGift = true; } else { pDeal->ClearItems(); } // Add this statement to the log so we don't evaluate it again until 15 turns has come back around if (!bRandPassed) SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_GENEROUS_OFFER_RANDFAILED, GC.getGame().getGameTurn()); } } } /// Does this AI want to purchase a technology? bool CvDiplomacyAI::IsTechRequest(PlayerTypes ePlayer, CvDeal* pDeal, int& iWeightBias) { if (GetPlayer()->IsAITeammateOfHuman()) return false; iWeightBias = 0; TechTypes eTechToAskFor = NO_TECH; int iTechLoop = 0; int iNumTechsWeHave = GET_TEAM(GetTeam()).GetTeamTechs()->GetNumTechsKnown(); int iNumTechsTheyHave = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetTeamTechs()->GetNumTechsKnown(); if(iNumTechsTheyHave == 0) return false; int iNumTechsPercent = iNumTechsWeHave * 100 / iNumTechsTheyHave; if(iNumTechsPercent < 90) return false; // See if the other player has a Tech to trade for(iTechLoop = 0; iTechLoop < GC.getNumTechInfos(); iTechLoop++) { const TechTypes eTech = static_cast(iTechLoop); // Can they actually give us this item if(!pDeal->IsPossibleToTradeItem(ePlayer, GetID(), TRADE_ITEM_TECHS, eTech)) continue; eTechToAskFor = eTech; break; } // Didn't find something they could give us? if(eTechToAskFor == NO_TECH) return false; // Add a little something extra since we're in dire straits if(iNumTechsPercent < 90) iWeightBias += 25; // Now seed the deal pDeal->AddTechTrade(ePlayer, eTechToAskFor); return true; } /// Do we want to trade world maps with eOtherPlayer? - this is only used for when to trigger an AI request, not whether or not the AI will accept a deal period bool CvDiplomacyAI::WantsMapsFromPlayer(PlayerTypes ePlayer) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); if (GetPlayer()->IsAITeammateOfHuman()) return false; CivApproachTypes eApproach = GetSurfaceApproach(ePlayer); if (eApproach == CIV_APPROACH_HOSTILE) { return false; } // Physically see how much the deal will cost us. Only send request if it's in an acceptable range int iMapValue = GetPlayer()->GetDealAI()->GetMapValue(false, ePlayer); return iMapValue > 750; } /// Do we want to make a generous offer to ePlayer? bool CvDiplomacyAI::IsMakeGenerousOffer(PlayerTypes ePlayer, CvDeal* pDeal, bool& bRandPassed) { if (GetCivApproach(ePlayer) == CIV_APPROACH_FRIENDLY && IsDoFAccepted(ePlayer)) { // We don't want to offer help if they've been given help from us recently if (IsHelpRequestTooSoon(ePlayer)) return false; int iBrokenTurns = (40 * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100; int iIgnoredTurns = (15 * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100; // If they've broken promises or ignored our requests lately, no if (BrokeAnyPromise(ePlayer, iBrokenTurns) || IgnoredAnyPromise(ePlayer, iIgnoredTurns)) return false; // Something we want to offer? bool bWantsToOfferSomething = false; // Gold Request if(!bWantsToOfferSomething) bWantsToOfferSomething = IsGoldGenerousOffer(ePlayer, pDeal); // Luxury Request if(!bWantsToOfferSomething) bWantsToOfferSomething = IsLuxuryGenerousOffer(ePlayer, pDeal); // Tech Request //if(!bWantsToOfferSomething) // bWantsToOfferSomething = IsTechGenerousOffer(ePlayer, pDeal); if(bWantsToOfferSomething) { // Random element int iRand = GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x36cfc7ec).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); // modifier based on AI loyalty int iModifier = (GetLoyalty() - 5); // +20 for 7 Loyalty, +0 for 5 Loyalty, -30 for 2 Loyalty, +50 for 10 Loyalty iRand += iModifier; if(iRand >= 6) { bRandPassed = true; return true; } else { bRandPassed = false; return false; } } } return false; } /// Do we want to make a gift of gold to ePlayer? bool CvDiplomacyAI::IsGoldGenerousOffer(PlayerTypes ePlayer, CvDeal* pDeal) { int iOurGold = GetPlayer()->GetTreasury()->GetGold(); int iOurGPT = GetPlayer()->calculateGoldRate(); int iOurExpenses = GetPlayer()->GetTreasury()->CalculateTotalCosts(); int iOurGrossIncome = iOurGPT + iOurExpenses; // If we have no expenses, don't offer (and also don't crash) if(iOurExpenses == 0) return false; // If we don't have gold saved up, don't bother if(iOurGold < 100) return false; // If we're not making 35% more than we're spending then don't offer, we're not doing alright if(iOurGrossIncome * 100 / iOurExpenses < 135) return false; int iTheirGold = GET_PLAYER(ePlayer).GetTreasury()->GetGold(); int iTheirGPT = GET_PLAYER(ePlayer).calculateGoldRate(); int iTheirExpenses = GET_PLAYER(ePlayer).GetTreasury()->CalculateTotalCosts(); int iTheirGrossIncome = iTheirGPT + iTheirExpenses; // Don't divide by zero please if(iTheirExpenses != 0) { // If they're making more than 35% more than they're spending then don't offer, they're in good shape if(iTheirGrossIncome * 100 / iTheirExpenses > 135) return false; } else if(iTheirGPT >= iOurGPT) { return false; } // Let's not offer any gold if they have more than us if(iTheirGold > iOurGold) { return false; } // If we've made it this far we'd like to offer, so figure out how much we want to offer int iGoldToOffer = iOurGPT * GC.getGame().GetDealDuration() / 20; int iGPTToOffer = 0; // Let's not offer the player more than double the amount of gold they have /*if(iTheirGold != 0) { if(iGoldToOffer > iTheirGold * 2) iGoldToOffer = iTheirGold * 2; }*/ if(iGoldToOffer > iOurGold) { iGoldToOffer = 0; iGPTToOffer = max(1, iOurGPT / 6); } // Now seed the deal if(iGoldToOffer > 0) pDeal->AddGoldTrade(GetID(), iGoldToOffer, true); else if(iGPTToOffer > 0) pDeal->AddGoldPerTurnTrade(GetID(), iGPTToOffer, GC.getGame().GetDealDuration()); return true; } /// Do we want to gift a luxury to ePlayer? bool CvDiplomacyAI::IsLuxuryGenerousOffer(PlayerTypes ePlayer, CvDeal* pDeal) { ResourceTypes eLuxuryToOffer = NO_RESOURCE; int iResourceLoop = 0; // See if there's any Luxuries WE can trade for(iResourceLoop = 0; iResourceLoop < GC.getNumResourceInfos(); iResourceLoop++) { const ResourceTypes eResource = static_cast(iResourceLoop); CvResourceInfo* pkResource = GC.getResourceInfo(eResource); if(pkResource) { // Only look at Luxuries if(pkResource->getResourceUsage() != RESOURCEUSAGE_LUXURY) continue; // Any extras? if(GetPlayer()->getNumResourceAvailable(eResource, /*bIncludeImport*/ false) < 2) continue; // Can they actually give us this item if(!pDeal->IsPossibleToTradeItem(GetID(), ePlayer, TRADE_ITEM_RESOURCES, eResource, 1)) continue; eLuxuryToOffer = eResource; break; } } // Didn't find something we could give them? if(eLuxuryToOffer == NO_RESOURCE) return false; // See if the other player has a Resource to trade (because if there are then we shouldn't be offering hand outs) for(iResourceLoop = 0; iResourceLoop < GC.getNumResourceInfos(); iResourceLoop++) { const ResourceTypes eResource = static_cast(iResourceLoop); CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource); if(pkResourceInfo) { // Only look at Luxuries if(pkResourceInfo->getResourceUsage() != RESOURCEUSAGE_LUXURY) continue; // Any extras? if(GET_PLAYER(ePlayer).getNumResourceAvailable(eResource, /*bIncludeImport*/ false) < 2) continue; // Can they actually give us this item if(!pDeal->IsPossibleToTradeItem(ePlayer, GetID(), TRADE_ITEM_RESOURCES, eResource, 1)) continue; // Found something we can trade to them, so abort return false; } } // Now seed the deal pDeal->AddResourceTrade(GetID(), eLuxuryToOffer, 1, GC.getGame().GetDealDuration()); return true; } /// Do we want to gift a technology to ePlayer? bool CvDiplomacyAI::IsTechGenerousOffer(PlayerTypes ePlayer, CvDeal* pDeal) { if (GetPlayer()->IsAITeammateOfHuman()) return false; TechTypes eTechToOffer = NO_TECH; int iTechLoop = 0; // See if the other player is lagging in science int iOurScience = GetPlayer()->GetScience(); int iTheirScience = GET_PLAYER(ePlayer).GetScience(); if (iOurScience < iTheirScience) return false; // See if there's any Technologies WE can trade for(iTechLoop = 0; iTechLoop < GC.getNumTechInfos(); iTechLoop++) { const TechTypes eTech = static_cast(iTechLoop); // Can we actually give us this item if(!pDeal->IsPossibleToTradeItem(GetID(), ePlayer, TRADE_ITEM_TECHS, eTech, 1)) continue; eTechToOffer = eTech; break; } // Didn't find something we could give them? if(eTechToOffer == NO_TECH) return false; // We made it this far, let's give them a tech. // Now seed the deal pDeal->AddTechTrade(GetID(), eTechToOffer); return true; } /// Are we willing to share our approach towards other players with ePlayer? bool CvDiplomacyAI::IsShareApproachAcceptable(PlayerTypes ePlayer) { // Debug setting if (MOD_DIPLO_DEBUG_MODE && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return true; CivApproachTypes eApproach = GetCivApproach(ePlayer); CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); // Have to share approach to master if (IsVassal(ePlayer)) return true; // Teammates if (IsTeammate(ePlayer)) return true; // Resurrected us if (WasResurrectedBy(ePlayer)) return true; // Backstabber? if (IsUntrustworthy(ePlayer)) return false; // If we act hostile, it's not acceptable if(IsActHostileTowardsHuman(ePlayer)) return false; // If we have a declaration of friendship, always yes if (IsDoFAccepted(ePlayer)) return true; // Just in case we're not acting hostile (for some reason), but our approach is still something bad... if (eApproach <= CIV_APPROACH_GUARDED) { return false; } if (eApproach == CIV_APPROACH_AFRAID) { return true; } // Haven't known this guy for long enough if(IsTooEarlyForShareApproach(ePlayer)) return false; // If player is unforgivable or an enemy, always say no if (eOpinion <= CIV_OPINION_ENEMY) return false; // Has there been a denouncement in either direction? if(IsDenouncedPlayer(ePlayer)) return false; if(GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID())) return false; // If they're Friends or better, say yes if (eOpinion >= CIV_OPINION_FRIEND) return true; // If they're Favorable and our approach is Friendly, say yes if (eApproach == CIV_APPROACH_FRIENDLY && eOpinion >= CIV_OPINION_FAVORABLE) return true; return false; } /// AI won't agree to share Approaches until they've known a player for at least a few turns. bool CvDiplomacyAI::IsTooEarlyForShareApproach(PlayerTypes ePlayer) { // Never too early for teammates to share approach if (IsTeammate(ePlayer)) { return false; } // Never too early for vassals to share approach if (IsVassal(ePlayer)) { return false; } // Never too early for friends to share approach if (IsDoFAccepted(ePlayer)) { return false; } if (GET_TEAM(GetTeam()).GetTurnsSinceMeetingTeam(GET_PLAYER(ePlayer).getTeam()) < max(/*20*/ GD_INT_GET(SHARE_APPROACH_TURN_BUFFER), /*10*/ GD_INT_GET(JUST_MET_TURN_BUFFER))) return true; return false; } void CvDiplomacyAI::DoHelpRequestMade(PlayerTypes ePlayer, DemandResponseTypes eResponse) { if (eResponse == DEMAND_RESPONSE_GIFT_ACCEPT) { SetHelpRequestAcceptedTurn(ePlayer, GC.getGame().getGameTurn()); // Decide how long it'll be before we might agree to another help request int iNumTurns = /*20*/ GD_INT_GET(HELP_REQUEST_TURN_LIMIT_MIN); iNumTurns += GC.getGame().randRangeInclusive(0, /*10*/ GD_INT_GET(HELP_REQUEST_TURN_LIMIT_RAND), CvSeeder::fromRaw(0xd1c5ae34).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); SetHelpRequestTooSoonNumTurns(ePlayer, iNumTurns); } } /// Get how we think ePlayer has treated us as our master VassalTreatmentTypes CvDiplomacyAI::GetVassalTreatmentLevel(PlayerTypes ePlayer) { if (!IsVassal(ePlayer)) return NO_VASSAL_TREATMENT; int iScore = GetVassalTreatedScore(ePlayer); if (iScore >= /*80*/ GD_INT_GET(VASSALAGE_TREATMENT_THRESHOLD_ENSLAVED)) return VASSAL_TREATMENT_ENSLAVED; else if (iScore >= /*50*/ GD_INT_GET(VASSALAGE_TREATMENT_THRESHOLD_UNHAPPY)) return VASSAL_TREATMENT_UNHAPPY; else if (iScore >= /*25*/ GD_INT_GET(VASSALAGE_TREATMENT_THRESHOLD_MISTREATED)) return VASSAL_TREATMENT_MISTREATED; else if (iScore >= /*1*/ GD_INT_GET(VASSALAGE_TREATMENT_THRESHOLD_DISAGREE)) return VASSAL_TREATMENT_DISAGREE; return VASSAL_TREATMENT_CONTENT; } /// Tooltip for GetVassalTreatmentLevel() CvString CvDiplomacyAI::GetVassalTreatmentToolTip(PlayerTypes ePlayer) { CvString szRtnValue = ""; CvString szColor; int iScore = 0; int iTotalScore = GetVassalTreatedScore(ePlayer); if (iTotalScore == 0) { szRtnValue += GetLocalizedText("TXT_KEY_VO_TREATMENT_NOTHING", -iScore); } else { szRtnValue += GetLocalizedText("TXT_KEY_VO_TREATMENT_TT") + "[NEWLINE]"; // Demands made of them iScore = GetVassalDemandScore(ePlayer); if (iScore != 0) { szColor = (iScore < 0) ? "[COLOR_POSITIVE_TEXT]" : "[COLOR_NEGATIVE_TEXT]"; szRtnValue += "[NEWLINE][TAB][ICON_BULLET]" + szColor + GetLocalizedText("TXT_KEY_VO_TREATMENT_DEMAND", -iScore) + "[ENDCOLOR]"; } // Stealing from them iScore = GetVassalTheftScore(ePlayer); if (iScore != 0) { szColor = (iScore < 0) ? "[COLOR_POSITIVE_TEXT]" : "[COLOR_NEGATIVE_TEXT]"; szRtnValue += "[NEWLINE][TAB][ICON_BULLET]" + szColor + GetLocalizedText("TXT_KEY_VO_TREATMENT_STEALING", -iScore) + "[ENDCOLOR]"; } // Denouncing them iScore = GetVassalDenouncementScore(ePlayer); if (iScore != 0) { szColor = (iScore < 0) ? "[COLOR_POSITIVE_TEXT]" : "[COLOR_NEGATIVE_TEXT]"; szRtnValue += "[NEWLINE][TAB][ICON_BULLET]" + szColor + GetLocalizedText("TXT_KEY_VO_TREATMENT_DENOUNCING", -iScore) + "[ENDCOLOR]"; } // Taxation policies iScore = GetVassalTaxScore(ePlayer); szColor = ((iScore == 0) ? "[COLOR_GREY]" : ((iScore < 0) ? "[COLOR_POSITIVE_TEXT]" : "[COLOR_NEGATIVE_TEXT]")); szRtnValue += "[NEWLINE][TAB][ICON_BULLET]" + szColor + GetLocalizedText("TXT_KEY_VO_TREATMENT_TAX", -iScore) + "[ENDCOLOR]"; // Protection of their lands iScore = GetVassalProtectScore(ePlayer); szColor = ((iScore == 0) ? "[COLOR_GREY]" : ((iScore < 0) ? "[COLOR_POSITIVE_TEXT]" : "[COLOR_NEGATIVE_TEXT]")); szRtnValue += "[NEWLINE][TAB][ICON_BULLET]" + szColor + GetLocalizedText("TXT_KEY_VO_TREATMENT_PROTECT", -iScore) + "[ENDCOLOR]"; // Trade Routes with them iScore = GetVassalTradeRouteScore(ePlayer); szColor = ((iScore == 0) ? "[COLOR_GREY]" : ((iScore < 0) ? "[COLOR_POSITIVE_TEXT]" : "[COLOR_NEGATIVE_TEXT]")); szRtnValue += "[NEWLINE][TAB][ICON_BULLET]" + szColor + GetLocalizedText("TXT_KEY_VO_TREATMENT_TRADE_ROUTE", -iScore) + "[ENDCOLOR]"; // Opening our borders iScore = GetVassalOpenBordersScore(ePlayer); szColor = ((iScore == 0) ? "[COLOR_GREY]" : ((iScore < 0) ? "[COLOR_POSITIVE_TEXT]" : "[COLOR_NEGATIVE_TEXT]")); szRtnValue += "[NEWLINE][TAB][ICON_BULLET]" + szColor + GetLocalizedText("TXT_KEY_VO_TREATMENT_OPEN_BORDERS", -iScore) + "[ENDCOLOR]"; // Shared religion interests if (!GC.getGame().isOption(GAMEOPTION_NO_RELIGION)) { iScore = GetVassalReligionScore(ePlayer); szColor = ((iScore == 0) ? "[COLOR_GREY]" : ((iScore < 0) ? "[COLOR_POSITIVE_TEXT]" : "[COLOR_NEGATIVE_TEXT]")); szRtnValue += "[NEWLINE][TAB][ICON_BULLET]" + szColor + GetLocalizedText("TXT_KEY_VO_TREATMENT_RELIGION", -iScore) + "[ENDCOLOR]"; } // Total score szColor = ((iTotalScore == 0) ? "[COLOR_GREY]" : ((iTotalScore < 0) ? "[COLOR_POSITIVE_TEXT]" : "[COLOR_NEGATIVE_TEXT]")); szRtnValue += "[NEWLINE][NEWLINE][TAB]" + szColor + GetLocalizedText("TXT_KEY_VO_TREATMENT_TOTAL", -iTotalScore) + "[ENDCOLOR]"; } return szRtnValue; } /// Returns if we've peacefully revoked vassalage (and they agreed) bool CvDiplomacyAI::IsHappyAboutPlayerVassalagePeacefullyRevoked(PlayerTypes ePlayer) { int iTurn = GetVassalagePeacefullyRevokedTurn(ePlayer); if (iTurn < 0) return false; int iTurnDifference = GC.getGame().getGameTurn() - iTurn; int iDuration = AdjustModifierDuration(/*100*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_PEACEFULLY_REVOKED_NUM_TURNS_UNTIL_FORGOTTEN), GetLoyalty()); return iTurnDifference < iDuration; } /// Returns if we've forcefully revoked vassalage bool CvDiplomacyAI::IsAngryAboutPlayerVassalageForcefullyRevoked(PlayerTypes ePlayer) { int iTurn = GetVassalageForcefullyRevokedTurn(ePlayer); if (iTurn < 0) return false; int iTurnDifference = GC.getGame().getGameTurn() - iTurn; int iDuration = AdjustModifierDuration(/*100*/ GD_INT_GET(OPINION_WEIGHT_VASSALAGE_FORCIBLY_REVOKED_NUM_TURNS_UNTIL_FORGIVEN), GetBoldness()); return iTurnDifference < iDuration; } /// eMasterTeam became our master void CvDiplomacyAI::DoWeMadeVassalageWithSomeone(TeamTypes eMasterTeam, bool bVoluntary) { const PlayerTypes ID = (PlayerTypes)GetID(); // Cancel all war plans CancelAllCoopWars(); SetBackstabber(false); SetDemandTargetPlayer(NO_PLAYER); SetCSWarTargetPlayer(NO_PLAYER); SetCSBullyTargetPlayer(NO_PLAYER); for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; TeamTypes eLoopTeam = GET_PLAYER(eLoopPlayer).getTeam(); if (eLoopTeam != GetTeam()) { CvDiplomacyAI* pOther = GET_PLAYER(eLoopPlayer).GetDiplomacyAI(); // Reset locked war turns GET_TEAM(GetTeam()).SetNumTurnsLockedIntoWar(eLoopTeam, 0); GET_TEAM(eLoopTeam).SetNumTurnsLockedIntoWar(GetTeam(), 0); // Cancel coop wars targeting us pOther->CancelCoopWarsAgainstPlayer(ID, true); if (!bVoluntary) { // Kill any attack operations SetArmyInPlaceForAttack(eLoopPlayer, false); pOther->SetArmyInPlaceForAttack(ID, false); if (GetPlayer()->getFirstOffensiveAIOperation(eLoopPlayer) != NULL) { GetPlayer()->StopAllLandOffensiveOperationsAgainstPlayer(eLoopPlayer,AI_ABORT_TARGET_NOT_VALID); GetPlayer()->StopAllSeaOffensiveOperationsAgainstPlayer(eLoopPlayer,AI_ABORT_TARGET_NOT_VALID); } if (GET_PLAYER(eLoopPlayer).getFirstOffensiveAIOperation(ID) != NULL) { GET_PLAYER(eLoopPlayer).StopAllLandOffensiveOperationsAgainstPlayer(ID,AI_ABORT_TARGET_NOT_VALID); GET_PLAYER(eLoopPlayer).StopAllSeaOffensiveOperationsAgainstPlayer(ID,AI_ABORT_TARGET_NOT_VALID); } // Forget any denouncing SetDenouncedPlayer(eLoopPlayer, false); pOther->SetDenouncedPlayer(ID, false); // Clear this player's warmongering penalties pOther->SetWarmongerThreat(ID, THREAT_NONE); pOther->SetOtherPlayerWarmongerAmountTimes100(ID, 0); // Clear this player's backstabbing penalties pOther->SetDoFBroken(ID, false, true); pOther->SetFriendDenouncedUs(ID, false); pOther->SetFriendDeclaredWarOnUs(ID, false); pOther->SetVassalageForcefullyRevokedTurn(ID, -1); pOther->SetBrokeVassalAgreement(ID, false); pOther->SetMilitaryPromiseState(ID, NO_PROMISE_STATE); pOther->SetAttackCityStatePromiseState(ID, NO_PROMISE_STATE); pOther->SetBrokeCoopWarPromise(ID, false); // Reset war-related penalties pOther->SetCivilianKillerValue(ID, 0); pOther->SetNumTradeRoutesPlundered(ID, 0); pOther->SetNumTimesNuked(ID, 0); if (GetCoopWarAgreementScore(eLoopPlayer) < 0) SetCoopWarAgreementScore(eLoopPlayer, 0); if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetCoopWarAgreementScore(ID) < 0) GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->SetCoopWarAgreementScore(ID, 0); } } } vector vMasterTeam = GET_TEAM(eMasterTeam).getPlayers(); for (size_t i=0; iSetWarmongerThreat(ID, THREAT_NONE); pOther->SetOtherPlayerWarmongerAmountTimes100(ID, 0); // Reset land dispute penalty SetLandDisputeLevel(eOtherTeamPlayer, DISPUTE_LEVEL_NONE); if (!GET_PLAYER(eOtherTeamPlayer).OwnsOurCity(ID)) pOther->SetLandDisputeLevel(ID, DISPUTE_LEVEL_NONE); // Vassal thought they were a liberator, but Master had other plans... SetMasterLiberatedMeFromVassalage(eOtherTeamPlayer, false); SetVassalagePeacefullyRevokedTurn(eOtherTeamPlayer, -1); // Master forgets any backstabbing pOther->SetDoFBroken(ID, false, true); pOther->SetFriendDenouncedUs(ID, false); pOther->SetFriendDeclaredWarOnUs(ID, false); pOther->SetVassalageForcefullyRevokedTurn(ID, -1); pOther->SetBrokeVassalAgreement(ID, false); pOther->SetMilitaryPromiseState(ID, NO_PROMISE_STATE); pOther->SetAttackCityStatePromiseState(ID, NO_PROMISE_STATE); pOther->SetBrokeCoopWarPromise(ID, false); // Reset memory of demands made and some other "minor" grievances if (!BrokeVassalAgreement(eOtherTeamPlayer) && !IsResurrectorAttackedUs(eOtherTeamPlayer)) { SetNumDemandsMade(eOtherTeamPlayer, 0); SetNumTradeRoutesPlundered(eOtherTeamPlayer, 0); SetNegativeReligiousConversionPoints(eOtherTeamPlayer, 0); SetEverConvertedCity(eOtherTeamPlayer, false); SetNumTimesTheyLoweredOurInfluence(eOtherTeamPlayer, 0); SetNumWondersBeatenTo(eOtherTeamPlayer, 0); SetWonderDisputeLevel(eOtherTeamPlayer, DISPUTE_LEVEL_NONE); } // During capitulation, reset almost all (negative) diplomatic scores. Rationale: When capitulating, AI tends to be very hostile. if (!bVoluntary && !BrokeVassalAgreement(eOtherTeamPlayer) && !IsResurrectorAttackedUs(eOtherTeamPlayer)) { SetMinorCivDisputeLevel(eOtherTeamPlayer, DISPUTE_LEVEL_NONE); if (GetRecentAssistValue(eOtherTeamPlayer) > 0) { SetRecentAssistValue(eOtherTeamPlayer, 0); } SetCivilianKillerValue(eOtherTeamPlayer, 0); SetNumTimesTheyPlottedAgainstUs(eOtherTeamPlayer, 0); SetNumTimesPerformedCoupAgainstUs(eOtherTeamPlayer, 0); SetNumTimesCultureBombed(eOtherTeamPlayer, 0); SetNegativeArchaeologyPoints(eOtherTeamPlayer, 0); SetNumTimesRobbedBy(eOtherTeamPlayer, 0); // Reset most promises if (!BrokeMilitaryPromise(eOtherTeamPlayer)) { SetMilitaryPromiseState(eOtherTeamPlayer, NO_PROMISE_STATE); } if (!BrokeAttackCityStatePromise(eOtherTeamPlayer)) { SetAttackCityStatePromiseState(eOtherTeamPlayer, NO_PROMISE_STATE); } SetExpansionPromiseState(eOtherTeamPlayer, NO_PROMISE_STATE); SetBorderPromiseState(eOtherTeamPlayer, NO_PROMISE_STATE); SetBullyCityStatePromiseState(eOtherTeamPlayer, NO_PROMISE_STATE); SetSpyPromiseState(eOtherTeamPlayer, NO_PROMISE_STATE); SetNoConvertPromiseState(eOtherTeamPlayer, NO_PROMISE_STATE); SetNoDiggingPromiseState(eOtherTeamPlayer, NO_PROMISE_STATE); SetBrokeCoopWarPromise(eOtherTeamPlayer, false); SetOtherPlayerNumProtectedMinorsKilled(eOtherTeamPlayer, 0); SetOtherPlayerNumProtectedMinorsAttacked(eOtherTeamPlayer, 0); SetOtherPlayerNumProtectedMinorsBullied(eOtherTeamPlayer, 0); SetOtherPlayerSidedWithProtectedMinorTurn(eOtherTeamPlayer, -1); // Vassal forgets broken DoF if master hasn't backstabbed them if (IsDoFBroken(eOtherTeamPlayer) && !IsFriendDenouncedUs(eOtherTeamPlayer) && !IsFriendDeclaredWarOnUs(eOtherTeamPlayer)) { SetDoFBroken(eOtherTeamPlayer, false, true); } SetNumTimesNuked(eOtherTeamPlayer, 0); SetWeDislikedTheirProposalTurn(eOtherTeamPlayer, -1); SetTheyFoiledOurProposalTurn(eOtherTeamPlayer, -1); if (GetLikedTheirProposalValue(eOtherTeamPlayer) > 0) { SetLikedTheirProposalValue(eOtherTeamPlayer, 0); } if (GetSupportedOurProposalValue(eOtherTeamPlayer) > 0) { SetSupportedOurProposalValue(eOtherTeamPlayer, 0); } if (GetVotingHistoryScore(eOtherTeamPlayer) < 0) { SetVotingHistoryScore(eOtherTeamPlayer, 0); } SetTheySanctionedUsTurn(eOtherTeamPlayer, -1); SetEverSanctionedUs(eOtherTeamPlayer, false); // Reset common foe bonus SetCommonFoeValue(eOtherTeamPlayer, 0); SetCoopWarAgreementScore(eOtherTeamPlayer, 0); GET_PLAYER(eOtherTeamPlayer).GetDiplomacyAI()->SetCoopWarAgreementScore(ID, 0); } else if (IsResurrectorAttackedUs(eOtherTeamPlayer)) { SetCommonFoeValue(eOtherTeamPlayer, 0); SetCoopWarAgreementScore(eOtherTeamPlayer, 0); GET_PLAYER(eOtherTeamPlayer).GetDiplomacyAI()->SetCoopWarAgreementScore(ID, 0); } } } } /// ePlayer ended vassalage with someone, so figure out what that means void CvDiplomacyAI::DoWeEndedVassalageWithSomeone(TeamTypes eTeam) { // Loop through players on the former master's team for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop; if (GET_PLAYER(ePlayer).getTeam() == eTeam) { // Reset our memory of GPT that was taxed from us SetVassalGoldPerTurnCollectedSinceVassalStarted(ePlayer, 0); SetVassalGoldPerTurnTaxedSinceVassalStarted(ePlayer, 0); SetVassalTaxRaised(ePlayer, false); SetVassalTaxLowered(ePlayer, false); } } } /// We are liberated by a master void CvDiplomacyAI::DoLiberatedFromVassalage(TeamTypes eTeam, bool bSkipPopup) { if (eTeam < 0 || eTeam >= MAX_CIV_TEAMS) return; // Get players from Master's team vector vMasterTeam = GET_TEAM(eTeam).getPlayers(); for (size_t i=0; i(vMasterTeam[i]); SetMasterLiberatedMeFromVassalage(eMasterPlayer, true); // Remove any previous penalty for refusing to give independence SetVassalageForcefullyRevokedTurn(eMasterPlayer, -1); if (!GET_PLAYER(eMasterPlayer).isAlive()) continue; ChangeRecentAssistValue(eMasterPlayer, 300); // Friends of the vassal - bonus to recent assistance! for (int iThirdPartyLoop = 0; iThirdPartyLoop < MAX_MAJOR_CIVS; iThirdPartyLoop++) { PlayerTypes eThirdParty = (PlayerTypes) iThirdPartyLoop; if (GET_PLAYER(eThirdParty).getTeam() == GET_PLAYER(eMasterPlayer).getTeam()) continue; if (IsPlayerValid(eThirdParty) && GET_PLAYER(eMasterPlayer).GetDiplomacyAI()->IsPlayerValid(eThirdParty) && IsDoFAccepted(eThirdParty)) { GET_PLAYER(eThirdParty).GetDiplomacyAI()->ChangeRecentAssistValue(eMasterPlayer, 300); } } if (bSkipPopup) continue; if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { if (CvPreGame::isHuman(eMasterPlayer)) { const char* strText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSALAGE_LIBERATED_HUMAN, eMasterPlayer); CvDiplomacyRequests::SendRequest(GetID(), eMasterPlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } else if (!CvPreGame::isNetworkMultiplayerGame() && GC.getGame().getActivePlayer() == eMasterPlayer) { const char* strText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSALAGE_LIBERATED_HUMAN, eMasterPlayer); gDLL->GameplayDiplomacyAILeaderMessage(GetID(), DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_POSITIVE); } } } // Version 9 /// Is moving our troops from ePlayer's lands acceptable? MoveTroopsResponseTypes CvDiplomacyAI::GetMoveTroopsRequestResponse(PlayerTypes ePlayer, bool bJustChecking) { if (IsVassal(ePlayer)) return MOVE_TROOPS_RESPONSE_ACCEPT; // We have an operation en route to opponent, or we're planning war if (GetPlayer()->HasAnyOffensiveOperationsAgainstPlayer(ePlayer) || AvoidExchangesWithPlayer(ePlayer, /*bWarOnly*/ true)) { return MOVE_TROOPS_RESPONSE_REFUSE; // War! } CivApproachTypes eTrueApproach = GetCivApproach(ePlayer); CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); vector viMoveTroopsWeights(NUM_MOVE_TROOPS_RESPONSES, 0); // Initialize our parallel arrays based on various approaches // i.e. more inclined to agree to leave if they like to be friendly toward civs viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] = GetMajorCivApproachBias(CIV_APPROACH_FRIENDLY); viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] = GetMajorCivApproachBias(CIV_APPROACH_NEUTRAL); viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] = GetMajorCivApproachBias(CIV_APPROACH_WAR); if (IsGoingForWorldConquest()) { viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += -3; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += -3; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += 5; } if (IsCloseToWorldConquest()) { viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += -10; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += -10; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += 10; } // If we hate the guy then add weight for attacking switch (eOpinion) { case CIV_OPINION_UNFORGIVABLE: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += -4; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += -2; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += 6; break; case CIV_OPINION_ENEMY: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += -1; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += 0; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += 4; break; case CIV_OPINION_COMPETITOR: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += 0; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += 0; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += 2; break; case CIV_OPINION_NEUTRAL: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += 0; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += 2; // Slight weight for neutral as to not piss off neighbors viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += 0; break; case CIV_OPINION_FAVORABLE: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += 2; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += 4; // Just favorable? Weight being neutral higher. viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += 0; break; case CIV_OPINION_FRIEND: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += 4; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += 2; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += -8; case CIV_OPINION_ALLY: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += 6; // Allies want to leave their allies alone more viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += 3; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += -10; break; } // Add weight based on approach switch(eTrueApproach) { case CIV_APPROACH_WAR: case CIV_APPROACH_HOSTILE: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += -2; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += -1; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += 4; break; case CIV_APPROACH_GUARDED: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += -2; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += -1; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += 3; break; case CIV_APPROACH_NEUTRAL: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += 0; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += 0; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += 0; break; case CIV_APPROACH_DECEPTIVE: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += 0; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += 0; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += 3; break; case CIV_APPROACH_FRIENDLY: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += 5; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += 5; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += 0; break; case CIV_APPROACH_AFRAID: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += 8; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += 0; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += -2; break; } // The REAL meat of the strategy: military strength switch(GetMilitaryStrengthComparedToUs(ePlayer)) { case STRENGTH_IMMENSE: // if he's really strong let's pull out! viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += 10; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += 3; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += -10; break; case STRENGTH_POWERFUL: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += 7; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += 5; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += -6; break; case STRENGTH_STRONG: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += 3; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += 4; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += -3; break; case STRENGTH_AVERAGE: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += 0; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += 3; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += 0; break; case STRENGTH_WEAK: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += -2; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += 0; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += 3; break; case STRENGTH_POOR: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += -6; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += -4; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += 6; break; case STRENGTH_PATHETIC: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += -10; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += -10; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += 10; break; } // If he's allowing open borders then maybe we want to move through them if(GET_TEAM(GET_PLAYER(ePlayer).getTeam()).IsAllowsOpenBordersToTeam(GetTeam())) { viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] *= 70; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] /= 100; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] *= 125; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] /= 100; } // Reduce weight if we're trading with them int iTradeRouteValue = GetPlayer()->GetTrade()->GetAllTradeValueFromPlayerTimes100(YIELD_GOLD, ePlayer); if (iTradeRouteValue > 0 || GC.getGame().GetGameDeals().IsReceivingItemsFromPlayer(GetID(), ePlayer, false)) { viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] += 2; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] += 5; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] += -3; } // Modifier for proximity switch(GetPlayer()->GetProximityToPlayer(ePlayer)) { case NO_PLAYER_PROXIMITY: case PLAYER_PROXIMITY_DISTANT: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] *= 120; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] /= 100; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] *= 100; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] /= 100; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] *= 40; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] /= 100; break; case PLAYER_PROXIMITY_FAR: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] *= 100; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] /= 100; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] *= 100; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] /= 100; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] *= 80; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] /= 100; break; case PLAYER_PROXIMITY_CLOSE: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] *= 85; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] /= 100; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] *= 100; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] /= 100; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] *= 100; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] /= 100; break; case PLAYER_PROXIMITY_NEIGHBORS: viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] *= 70; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_ACCEPT] /= 100; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] *= 120; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_NEUTRAL] /= 100; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] *= 120; viMoveTroopsWeights[MOVE_TROOPS_RESPONSE_REFUSE] /= 100; break; } // And a random weight from 1-5 to each value int iRand = 0; for(int i=0; i < NUM_MOVE_TROOPS_RESPONSES; i++) { iRand = GC.getGame().randRangeExclusive(0, 5, CvSeeder::fromRaw(0xc96d1a82).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); viMoveTroopsWeights[i] += iRand; } // This vector is what we'll use to sort CvWeightedVector< int> vMoveTroopsWeightsForSorting; vMoveTroopsWeightsForSorting.clear(); // Transfer values over to the sorting vector for(int i = 0; i < NUM_MOVE_TROOPS_RESPONSES; i++) { vMoveTroopsWeightsForSorting.push_back(i, max(viMoveTroopsWeights[i], 0)); } vMoveTroopsWeightsForSorting.StableSortItems(); MoveTroopsResponseTypes eResponse = (MoveTroopsResponseTypes) vMoveTroopsWeightsForSorting.GetElement(0); // If we're planning on war or going for world conquest and guarded or worse then refuse if (eTrueApproach == CIV_APPROACH_WAR || (IsGoingForWorldConquest() && eTrueApproach <= CIV_APPROACH_GUARDED)) { eResponse = MOVE_TROOPS_RESPONSE_REFUSE; } // Sanity check: What do my teammates think? // Prevent human from abusing AI to declare war on a human if(!bJustChecking) { int iYes = 0; int iNeutral = 0; int iNo = 0; for (int iMajorLoop = 0; iMajorLoop < MAX_MAJOR_CIVS; iMajorLoop++) { PlayerTypes eLoopPlayer = (PlayerTypes)iMajorLoop; // Has to be on our team if(IsTeammate(eLoopPlayer)) { // bJustChecking = true to prevent infinite loop MoveTroopsResponseTypes eTeammateResponse = GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetMoveTroopsRequestResponse(ePlayer, /*bJustChecking*/ true); switch (eTeammateResponse) { case MOVE_TROOPS_RESPONSE_ACCEPT: iYes++; break; case MOVE_TROOPS_RESPONSE_NEUTRAL: iNeutral++; break; case MOVE_TROOPS_RESPONSE_REFUSE: iNo++; break; } } } // Add in our vote switch(eResponse) { case MOVE_TROOPS_RESPONSE_ACCEPT: iYes++; break; case MOVE_TROOPS_RESPONSE_NEUTRAL: iNeutral++; break; case MOVE_TROOPS_RESPONSE_REFUSE: iNo++; break; } // Change eResponse to whatever is highest if (iYes >= iNeutral && iNeutral >= iNo) { eResponse = MOVE_TROOPS_RESPONSE_ACCEPT; } else if (iNeutral >= iYes && iYes >= iNo) { eResponse = MOVE_TROOPS_RESPONSE_NEUTRAL; } else { eResponse = MOVE_TROOPS_RESPONSE_REFUSE; } } // If we hate the guy then only neutral if(eTrueApproach == CIV_APPROACH_HOSTILE) { if (eResponse == MOVE_TROOPS_RESPONSE_ACCEPT) eResponse = MOVE_TROOPS_RESPONSE_NEUTRAL; } // Final sanity check, if we're not planning war then don't go to war! if (!IsWantsSneakAttack(ePlayer) && GetDemandTargetPlayer() != ePlayer) { if (eResponse == MOVE_TROOPS_RESPONSE_REFUSE) eResponse = MOVE_TROOPS_RESPONSE_NEUTRAL; } return eResponse; } //--------------------------------------------------