Rise of Mankind: A New Dawn Code
Brought to you by:
afforess,
n4538-e1347
// playerAI.cpp
#include "CvGameCoreDLL.h"
#include "CvPlayerAI.h"
#include "CvRandom.h"
#include "CvGlobals.h"
#include "CvGameCoreUtils.h"
#include "CvMap.h"
#include "CvArea.h"
#include "CvPlot.h"
#include "CvGameAI.h"
#include "CvTeamAI.h"
#include "CvGameCoreUtils.h"
#include "CvDiploParameters.h"
#include "CvInitCore.h"
#include "CyArgsList.h"
#include "CvDLLInterfaceIFaceBase.h"
#include "CvDLLEntityIFaceBase.h"
#include "CvDLLPythonIFaceBase.h"
#include "CvDLLEngineIFaceBase.h"
#include "CvInfos.h"
#include "CvPopupInfo.h"
#include "FProfiler.h"
#include "CvDLLFAStarIFaceBase.h"
#include "FAStarNode.h"
#include "CvEventReporter.h"
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 10/02/09 jdog5000 */
/* */
/* AI logging */
/************************************************************************************************/
#include "BetterBTSAI.h"
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 08/21/09 jdog5000 */
/* */
/* Efficiency */
/************************************************************************************************/
// Plot danger cache
//#define DANGER_RANGE (4)
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
#define GREATER_FOUND_RANGE (5)
#define CIVIC_CHANGE_DELAY (25)
#define RELIGION_CHANGE_DELAY (15)
// Koshling - save flag indicating this player has no data in the save as they have never
// been alive
#define PLAYER_UI_FLAG_OMITTED 4
// Koshling - to try to normalize the new tech building evaluation to the same magnitude as the old
// (so that it doesn't chnage it's value as a component relative to other factors) a multiplier is needed
#define BUILDING_VALUE_TO_TECH_BUILDING_VALUE_MULTIPLIER 30
// statics
CvPlayerAI* CvPlayerAI::m_aPlayers = NULL;
static CRITICAL_SECTION g_cAveragesCalculationSection;
void CvPlayerAI::initStatics()
{
m_aPlayers = new CvPlayerAI[MAX_PLAYERS];
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
m_aPlayers[iI].m_eID = ((PlayerTypes)iI);
}
InitializeCriticalSection(&g_cAveragesCalculationSection);
}
void CvPlayerAI::freeStatics()
{
SAFE_DELETE_ARRAY(m_aPlayers);
}
bool CvPlayerAI::areStaticsInitialized()
{
if(m_aPlayers == NULL)
{
return false;
}
return true;
}
DllExport CvPlayerAI& CvPlayerAI::getPlayerNonInl(PlayerTypes ePlayer)
{
return getPlayer(ePlayer);
}
// Public Functions...
CvPlayerAI::CvPlayerAI()
{
InitializeCriticalSectionAndSpinCount(&m_csBonusValSection,1000);
m_aiNumTrainAIUnits = new int[NUM_UNITAI_TYPES];
m_aiNumAIUnits = new int[NUM_UNITAI_TYPES];
m_aiSameReligionCounter = new int[MAX_PLAYERS];
m_aiDifferentReligionCounter = new int[MAX_PLAYERS];
m_aiFavoriteCivicCounter = new int[MAX_PLAYERS];
m_aiBonusTradeCounter = new int[MAX_PLAYERS];
m_aiPeacetimeTradeValue = new int[MAX_PLAYERS];
m_aiPeacetimeGrantValue = new int[MAX_PLAYERS];
m_aiGoldTradedTo = new int[MAX_PLAYERS];
m_aiAttitudeExtra = new int[MAX_PLAYERS];
m_abFirstContact = new bool[MAX_PLAYERS];
m_aaiContactTimer = new int*[MAX_PLAYERS];
for (int i = 0; i < MAX_PLAYERS; i++)
{
m_aaiContactTimer[i] = new int[NUM_CONTACT_TYPES];
}
m_aaiMemoryCount = new int*[MAX_PLAYERS];
for (int i = 0; i < MAX_PLAYERS; i++)
{
m_aaiMemoryCount[i] = new int[NUM_MEMORY_TYPES];
}
m_aiAverageYieldMultiplier = new int[NUM_YIELD_TYPES];
m_aiAverageCommerceMultiplier = new int[NUM_COMMERCE_TYPES];
m_aiAverageCommerceExchange = new int[NUM_COMMERCE_TYPES];
m_aiBonusValue = NULL;
m_aiTradeBonusValue = NULL;
m_abNonTradeBonusCalculated = NULL;
m_aiUnitClassWeights = NULL;
m_aiUnitCombatWeights = NULL;
m_aiCloseBordersAttitudeCache = new int[MAX_PLAYERS];
//Afforess
m_aiCivicValueCache = NULL;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 09/03/09 poyuzhe & jdog5000 */
/* */
/* Efficiency */
/************************************************************************************************/
// From Sanguo Mod Performance, ie the CAR Mod
// Attitude cache
m_aiAttitudeCache = new int[MAX_PLAYERS];
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
AI_reset(true);
}
CvPlayerAI::~CvPlayerAI()
{
AI_uninit();
SAFE_DELETE_ARRAY(m_aiNumTrainAIUnits);
SAFE_DELETE_ARRAY(m_aiNumAIUnits);
SAFE_DELETE_ARRAY(m_aiSameReligionCounter);
SAFE_DELETE_ARRAY(m_aiDifferentReligionCounter);
SAFE_DELETE_ARRAY(m_aiFavoriteCivicCounter);
SAFE_DELETE_ARRAY(m_aiBonusTradeCounter);
SAFE_DELETE_ARRAY(m_aiPeacetimeTradeValue);
SAFE_DELETE_ARRAY(m_aiPeacetimeGrantValue);
SAFE_DELETE_ARRAY(m_aiGoldTradedTo);
SAFE_DELETE_ARRAY(m_aiAttitudeExtra);
SAFE_DELETE_ARRAY(m_abFirstContact);
for (int i = 0; i < MAX_PLAYERS; i++)
{
SAFE_DELETE_ARRAY(m_aaiContactTimer[i]);
}
SAFE_DELETE_ARRAY(m_aaiContactTimer);
for (int i = 0; i < MAX_PLAYERS; i++)
{
SAFE_DELETE_ARRAY(m_aaiMemoryCount[i]);
}
SAFE_DELETE_ARRAY(m_aaiMemoryCount);
SAFE_DELETE_ARRAY(m_aiAverageYieldMultiplier);
SAFE_DELETE_ARRAY(m_aiAverageCommerceMultiplier);
SAFE_DELETE_ARRAY(m_aiAverageCommerceExchange);
SAFE_DELETE_ARRAY(m_aiCloseBordersAttitudeCache);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 09/03/09 poyuzhe & jdog5000 */
/* */
/* Efficiency */
/************************************************************************************************/
// From Sanguo Mod Performance, ie the CAR Mod
// Attitude cache
SAFE_DELETE_ARRAY(m_aiAttitudeCache);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
DeleteCriticalSection(&m_csBonusValSection);
}
void CvPlayerAI::AI_init()
{
AI_reset(false);
//--------------------------------
// Init other game data
if ((GC.getInitCore().getSlotStatus(getID()) == SS_TAKEN) || (GC.getInitCore().getSlotStatus(getID()) == SS_COMPUTER))
{
FAssert(getPersonalityType() != NO_LEADER);
AI_setPeaceWeight(GC.getLeaderHeadInfo(getPersonalityType()).getBasePeaceWeight() + GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getPeaceWeightRand(), "AI Peace Weight"));
AI_setEspionageWeight(GC.getLeaderHeadInfo(getPersonalityType()).getEspionageWeight());
//AI_setCivicTimer(((getMaxAnarchyTurns() == 0) ? (GC.getDefineINT("MIN_REVOLUTION_TURNS") * 2) : CIVIC_CHANGE_DELAY) / 2);
AI_setReligionTimer(1);
AI_setCivicTimer(1);
}
}
void CvPlayerAI::AI_uninit()
{
SAFE_DELETE_ARRAY(m_aiBonusValue);
SAFE_DELETE_ARRAY(m_aiTradeBonusValue);
SAFE_DELETE_ARRAY(m_abNonTradeBonusCalculated);
SAFE_DELETE_ARRAY(m_aiUnitClassWeights);
SAFE_DELETE_ARRAY(m_aiUnitCombatWeights);
//Afforess
SAFE_DELETE_ARRAY(m_aiCivicValueCache);
}
void CvPlayerAI::AI_reset(bool bConstructor)
{
int iI;
AI_uninit();
m_iPeaceWeight = 0;
m_iEspionageWeight = 0;
m_iAttackOddsChange = 0;
m_iCivicTimer = 0;
m_iReligionTimer = 0;
m_iExtraGoldTarget = 0;
bUnitRecalcNeeded = false;
m_bCitySitesNotCalculated = true;
m_iCityGrowthValueBase = -1;
m_turnsSinceLastRevolution = 50; // Start off at the functional max (larger makes no diff)
m_iCivicSwitchMinDeltaThreshold = 0;
m_eBestResearchTarget = NO_TECH;
m_cachedTechValues.clear();
m_eEspionagePlan = NO_ESPIONAGE_PLAN;
/************************************************************************************************/
/* CHANGE_PLAYER 06/08/09 jdog5000 */
/* */
/* */
/************************************************************************************************/
if( bConstructor || getNumUnits() == 0 )
{
for (iI = 0; iI < NUM_UNITAI_TYPES; iI++)
{
m_aiNumTrainAIUnits[iI] = 0;
m_aiNumAIUnits[iI] = 0;
}
}
/************************************************************************************************/
/* CHANGE_PLAYER END */
/************************************************************************************************/
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
m_aiSameReligionCounter[iI] = 0;
m_aiDifferentReligionCounter[iI] = 0;
m_aiFavoriteCivicCounter[iI] = 0;
m_aiBonusTradeCounter[iI] = 0;
m_aiPeacetimeTradeValue[iI] = 0;
m_aiPeacetimeGrantValue[iI] = 0;
m_aiGoldTradedTo[iI] = 0;
m_aiAttitudeExtra[iI] = 0;
m_abFirstContact[iI] = false;
for (int iJ = 0; iJ < NUM_CONTACT_TYPES; iJ++)
{
m_aaiContactTimer[iI][iJ] = 0;
}
for (int iJ = 0; iJ < NUM_MEMORY_TYPES; iJ++)
{
m_aaiMemoryCount[iI][iJ] = 0;
}
if (!bConstructor && getID() != NO_PLAYER)
{
PlayerTypes eLoopPlayer = (PlayerTypes) iI;
CvPlayerAI& kLoopPlayer = GET_PLAYER(eLoopPlayer);
kLoopPlayer.m_aiSameReligionCounter[getID()] = 0;
kLoopPlayer.m_aiDifferentReligionCounter[getID()] = 0;
kLoopPlayer.m_aiFavoriteCivicCounter[getID()] = 0;
kLoopPlayer.m_aiBonusTradeCounter[getID()] = 0;
kLoopPlayer.m_aiPeacetimeTradeValue[getID()] = 0;
kLoopPlayer.m_aiPeacetimeGrantValue[getID()] = 0;
kLoopPlayer.m_aiGoldTradedTo[getID()] = 0;
kLoopPlayer.m_aiAttitudeExtra[getID()] = 0;
kLoopPlayer.m_abFirstContact[getID()] = false;
for (int iJ = 0; iJ < NUM_CONTACT_TYPES; iJ++)
{
kLoopPlayer.m_aaiContactTimer[getID()][iJ] = 0;
}
for (int iJ = 0; iJ < NUM_MEMORY_TYPES; iJ++)
{
kLoopPlayer.m_aaiMemoryCount[getID()][iJ] = 0;
}
}
}
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
m_aiAverageYieldMultiplier[iI] = 0;
}
for (iI = 0; iI< NUM_COMMERCE_TYPES; iI++)
{
m_aiAverageCommerceMultiplier[iI] = 0;
m_aiAverageCommerceExchange[iI] = 0;
}
m_iAverageGreatPeopleMultiplier = 0;
m_iAveragesCacheTurn = -1;
m_iStrategyHash = 0;
m_iStrategyHashCacheTurn = -1;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/18/10 jdog5000 */
/* */
/* Victory Strategy AI */
/************************************************************************************************/
m_iStrategyRand = 0;
m_iVictoryStrategyHash = 0;
m_iVictoryStrategyHashCacheTurn = -1;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
m_bWasFinancialTrouble = false;
m_iTurnLastProductionDirty = -1;
m_iUpgradeUnitsCacheTurn = -1;
m_iUpgradeUnitsCachedExpThreshold = 0;
m_iUpgradeUnitsCachedGold = 0;
/************************************************************************************************/
/* Afforess Start 10/31/10 */
/* */
/* */
/************************************************************************************************/
m_iMilitaryProductionCityCount = -1;
FAssert(m_aiCivicValueCache == NULL);
m_aiCivicValueCache = new int[GC.getNumCivicInfos()*2];
for (iI = 0; iI < GC.getNumCivicInfos()*2; iI++)
{
m_aiCivicValueCache[iI] = MAX_INT;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
m_aiAICitySites.clear();
FAssert(m_aiBonusValue == NULL);
FAssert(m_aiTradeBonusValue == NULL);
m_aiBonusValue = new int[GC.getNumBonusInfos()];
m_aiTradeBonusValue = new int[GC.getNumBonusInfos()];
m_abNonTradeBonusCalculated = new bool[GC.getNumBonusInfos()];
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
m_aiBonusValue[iI] = -1;
m_aiTradeBonusValue[iI] = -1;
m_abNonTradeBonusCalculated[iI] = false;
}
FAssert(m_aiUnitClassWeights == NULL);
m_aiUnitClassWeights = new int[GC.getNumUnitClassInfos()];
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
m_aiUnitClassWeights[iI] = 0;
}
FAssert(m_aiUnitCombatWeights == NULL);
m_aiUnitCombatWeights = new int[GC.getNumUnitCombatInfos()];
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
m_aiUnitCombatWeights[iI] = 0;
}
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
m_aiCloseBordersAttitudeCache[iI] = 0;
if (!bConstructor && getID() != NO_PLAYER)
{
GET_PLAYER((PlayerTypes) iI).m_aiCloseBordersAttitudeCache[getID()] = 0;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 09/03/09 poyuzhe & jdog5000 */
/* */
/* Efficiency */
/************************************************************************************************/
// From Sanguo Mod Performance, ie the CAR Mod
// Attitude cache
m_aiAttitudeCache[iI] = MAX_INT;
if (!bConstructor && getID() != NO_PLAYER)
{
GET_PLAYER((PlayerTypes) iI).m_aiAttitudeCache[getID()] = MAX_INT;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
m_missionTargetCache.clear();
}
int CvPlayerAI::AI_getFlavorValue(FlavorTypes eFlavor) const
{
FAssertMsg((getPersonalityType() >= 0), "getPersonalityType() is less than zero");
FAssertMsg((eFlavor >= 0), "eFlavor is less than zero");
return GC.getLeaderHeadInfo(getPersonalityType()).getFlavorValue(eFlavor);
}
void CvPlayerAI::AI_doTurnPre()
{
PROFILE_FUNC();
m_cachedTechValues.clear();
FAssertMsg(getPersonalityType() != NO_LEADER, "getPersonalityType() is not expected to be equal with NO_LEADER");
FAssertMsg(getLeaderType() != NO_LEADER, "getLeaderType() is not expected to be equal with NO_LEADER");
FAssertMsg(getCivilizationType() != NO_CIVILIZATION, "getCivilizationType() is not expected to be equal with NO_CIVILIZATION");
// AIAndy: Calculate the strategy rand if needed
AI_calculateStrategyRand();
if ( bUnitRecalcNeeded )
{
AI_recalculateUnitCounts();
}
// Force recalculation of the mission target cache each turn for
// reliabilty reasons (more robust to bugs)
m_missionTargetCache.clear();
#ifdef _DEBUG
// Validate AI unit counts
{
CvUnit* pLoopUnit;
CvArea* pLoopArea;
int iLoop;
int iLoop2;
int iMilitary = 0;
for(pLoopArea = GC.getMapINLINE().firstArea(&iLoop); pLoopArea != NULL; pLoopArea = GC.getMapINLINE().nextArea(&iLoop))
{
for(int iI = 0; iI < NUM_UNITAI_TYPES; iI++)
{
int iCount = 0;
for(pLoopUnit = firstUnit(&iLoop2); pLoopUnit != NULL; pLoopUnit = nextUnit(&iLoop2))
{
UnitAITypes eAIType = pLoopUnit->AI_getUnitAIType();
if ( (UnitAITypes)iI == eAIType && pLoopUnit->area() == pLoopArea)
{
iCount++;
if (GC.getUnitInfo(pLoopUnit->getUnitType()).isMilitarySupport())
{
iMilitary++;
}
}
}
if ( iCount != 0 )
{
OutputDebugString(CvString::format("Player %d, Area %d, unitAI %s count=%d\n", getID(), pLoopArea->getID(), GC.getUnitAIInfo((UnitAITypes)iI).getType(), iCount).c_str());
}
if ( iCount != pLoopArea->getNumAIUnits(getID(), (UnitAITypes)iI) )
{
FAssertMsg(false,"UnitAI miscount");
}
}
}
FAssert(iMilitary == getNumMilitaryUnits());
}
#endif
AI_invalidateCloseBordersAttitudeCache();
AI_doCounter();
AI_updateEspionagePlan();
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 09/03/09 poyuzhe & jdog5000 */
/* */
/* Efficiency */
/************************************************************************************************/
// From Sanguo Mod Performance, ie the CAR Mod
// Attitude cache
AI_invalidateAttitudeCache();
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
m_numBuildingsNeeded.clear();
/************************************************************************************************/
/* Afforess Start 01/16/10 */
/* */
/* Cache Civic Values */
/************************************************************************************************/
for (int iI = 0; iI < GC.getNumCivicInfos()*2; iI++) {
m_aiCivicValueCache[iI] = MAX_INT;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
AI_updateBonusValue();
AI_doEnemyUnitData();
if (isHuman())
{
return;
}
// Mark previous yield data as stale
#ifdef YIELD_VALUE_CACHING
CvCity* pLoopCity;
int iLoop;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
pLoopCity->ClearYieldValueCache();
}
#endif
AI_doResearch();
AI_doCommerce();
AI_doMilitary();
AI_doCivics();
AI_doReligion();
/************************************************************************************************/
/* RevDCM Start 12/9/09 */
/* */
/* Inquisitions */
/************************************************************************************************/
AI_setPushReligiousVictory();
AI_setConsiderReligiousVictory();
AI_setHasInquisitionTarget();
/************************************************************************************************/
/* RevDCM END */
/************************************************************************************************/
AI_doCheckFinancialTrouble();
/************************************************************************************************/
/* Afforess Start 10/29/10 */
/* */
/* */
/************************************************************************************************/
AI_doMilitaryProductionCity();
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (isBarbarian())
{
return;
}
if (isMinorCiv())
{
return;
}
}
void CvPlayerAI::AI_doTurnPost()
{
PROFILE_FUNC();
if (isHuman())
{
return;
}
if (isBarbarian())
{
return;
}
if (isMinorCiv())
{
return;
}
AI_doDiplo();
/************************************************************************************************/
/* UNOFFICIAL_PATCH 06/16/09 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
// Moved per alexman's suggestion
//AI_doSplit();
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
for (int i = 0; i < GC.getNumVictoryInfos(); ++i)
{
AI_launch((VictoryTypes)i);
}
}
void CvPlayerAI::AI_doTurnUnitsPre()
{
PROFILE_FUNC();
// Clear cached defensive status info on each city
CvCity* pLoopCity;
int iLoop;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
pLoopCity->AI_preUnitTurn();
}
#ifdef PLOT_DANGER_CACHING
// Clear plot danger cache
plotDangerCache.clear();
#endif
AI_updateFoundValues(true);
if (GC.getGameINLINE().getSorenRandNum(8, "AI Update Area Targets") == 0) // XXX personality???
{
AI_updateAreaTargets();
}
if (isHuman())
{
return;
}
if (isBarbarian())
{
return;
}
//k-mod uncommented
if (AI_isDoStrategy(AI_STRATEGY_CRUSH))
{
AI_convertUnitAITypesForCrush();
}
}
void CvPlayerAI::AI_doTurnUnitsPost()
{
PROFILE_FUNC();
CvUnit* pLoopUnit;
CvPlot* pUnitPlot;
bool bValid;
int iPass;
int iLoop;
if (!isHuman() || isOption(PLAYEROPTION_AUTO_PROMOTION))
{
for(pLoopUnit = firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = nextUnit(&iLoop))
{
/************************************************************************************************/
/* Afforess Start 06/24/10 */
/* */
/* Afforess Speed Tweak */
/************************************************************************************************/
if (pLoopUnit->isPromotionReady())
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
pLoopUnit->AI_promote();
}
}
/************************************************************************************************/
/* Afforess/RevDCM Start 07/12/10 */
/* */
/* Advanced Automations */
/************************************************************************************************/
if (isHuman())
{
CvCivilizationInfo& kCivilization = GC.getCivilizationInfo(getCivilizationType());
int iMinGoldToUpgrade = getModderOption(MODDEROPTION_UPGRADE_MIN_GOLD);
CvUnit* pLoopUnit;
int iLoop;
if (isModderOption(MODDEROPTION_UPGRADE_MOST_EXPENSIVE) && getGold() > iMinGoldToUpgrade)
{
for (int iUnitCount = 0; iUnitCount < getNumUnits(); iUnitCount++)
{
CvUnit* pBestUnit = NULL;
int iCost = 0;
UnitTypes eLoopUnit = NO_UNIT;
UnitTypes eBestUnit = NO_UNIT;
for (pLoopUnit = firstUnit(&iLoop); getGold() > iMinGoldToUpgrade && pLoopUnit != NULL; pLoopUnit = nextUnit(&iLoop))
{
if (pLoopUnit->isAutoUpgrading() && pLoopUnit->isReadyForUpgrade())
{
std::vector<int> aPotentialUnitClassTypes = GC.getUnitInfo(pLoopUnit->getUnitType()).getUpgradeUnitClassTypes();
for (int iI = 0; iI < (int)aPotentialUnitClassTypes.size(); iI++)
{
eLoopUnit = (UnitTypes)kCivilization.getCivilizationUnits((UnitClassTypes)aPotentialUnitClassTypes[iI]);
if (eLoopUnit != NO_UNIT)
{
if (pLoopUnit->canUpgrade(eLoopUnit, false))
{
int iUpgradeCost = pLoopUnit->upgradePrice(eLoopUnit);
if (iCost < iUpgradeCost)
{
iCost = iUpgradeCost;
eBestUnit = eLoopUnit;
pBestUnit = pLoopUnit;
}
}
}
}
}
}
if (pBestUnit == NULL)
{
break;
}
else
{
pBestUnit->upgrade(eBestUnit);
pBestUnit->doDelayedDeath();
}
}
}
else if (isModderOption(MODDEROPTION_UPGRADE_MOST_EXPERIENCED) && getGold() > iMinGoldToUpgrade)
{
for (int iUnitCount = 0; iUnitCount < getNumUnits(); iUnitCount++)
{
CvUnit* pBestUnit = NULL;
int iExperience = -1;
UnitTypes eLoopUnit = NO_UNIT;
for (pLoopUnit = firstUnit(&iLoop); getGold() > iMinGoldToUpgrade && pLoopUnit != NULL; pLoopUnit = nextUnit(&iLoop))
{
if (pLoopUnit->isAutoUpgrading() && pLoopUnit->isReadyForUpgrade())
{
std::vector<int> aPotentialUnitClassTypes = GC.getUnitInfo(pLoopUnit->getUnitType()).getUpgradeUnitClassTypes();
for (int iI = 0; iI < (int)aPotentialUnitClassTypes.size(); iI++)
{
eLoopUnit = (UnitTypes)kCivilization.getCivilizationUnits((UnitClassTypes)aPotentialUnitClassTypes[iI]);
if (eLoopUnit != NO_UNIT)
{
if (pLoopUnit->canUpgrade(eLoopUnit, false))
{
int iCurExperience = pLoopUnit->getExperience100();
if (iExperience < iCurExperience)
{
iExperience = iCurExperience;
pBestUnit = pLoopUnit;
break;
}
}
}
}
}
}
if (pBestUnit == NULL)
{
break;
}
else
{
pBestUnit->AI_upgrade();
}
}
}
else if (getGold() > iMinGoldToUpgrade)
{
for (pLoopUnit = firstUnit(&iLoop); getGold() > iMinGoldToUpgrade && pLoopUnit != NULL; pLoopUnit = nextUnit(&iLoop))
{
if (pLoopUnit->isAutoUpgrading() && pLoopUnit->isReadyForUpgrade())
{
pLoopUnit->AI_upgrade();
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (isHuman())
{
return;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 02/24/10 jdog5000 */
/* */
/* Gold AI */
/************************************************************************************************/
bool bAnyWar = (GET_TEAM(getTeam()).getAnyWarPlanCount(true) > 0);
int iStartingGold = getGold();
int iTargetGold = AI_goldTarget();
int iUpgradeBudget = (AI_goldToUpgradeAllUnits() / (bAnyWar ? 1 : 2));
iUpgradeBudget = std::min(iUpgradeBudget, (iStartingGold - iTargetGold < iUpgradeBudget) ? (iStartingGold - iTargetGold) : iStartingGold/2);
if ( iUpgradeBudget < 0 )
{
iUpgradeBudget = 0;
}
if( gPlayerLogLevel > 2 )
{
logBBAI(" %S calculates upgrade budget of %d from %d current gold, %d target", getCivilizationDescription(0), iUpgradeBudget, iStartingGold, iTargetGold);
}
if( AI_isFinancialTrouble() )
{
iUpgradeBudget /= 3;
}
// Always willing to upgrade 1 unit if we have the money
iUpgradeBudget = std::max(iUpgradeBudget,1);
bool bUnderBudget = true;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
CvPlot* pLastUpgradePlot = NULL;
for (iPass = 0; iPass < 4; iPass++)
{
for(pLoopUnit = firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = nextUnit(&iLoop))
{
bool bNoDisband = false;
bValid = false;
if ( pLoopUnit->isDead() || pLoopUnit->isDelayedDeath() )
{
continue;
}
// Koshling - never upgrade workers or subdued animals here as they typically have outcome
// missions and construction capabilities that must be evaluated comparatively. The UnitAI
// processing for these AI types handles upgrade explicitly
switch( pLoopUnit->AI_getUnitAIType() )
{
case UNITAI_SUBDUED_ANIMAL:
case UNITAI_WORKER:
continue;
default:
break;
}
switch (iPass)
{
case 0:
// BBAI note: Effectively only for galleys, triremes, and ironclads ... unit types which are limited in
// what terrain they can operate in
if (AI_unitImpassableCount(pLoopUnit->getUnitType()) > 0)
{
bValid = true;
}
break;
case 1:
pUnitPlot = pLoopUnit->plot();
FAssert(pUnitPlot != NULL);
if (pUnitPlot->isCity())
{
if (pUnitPlot->getBestDefender(getID()) == pLoopUnit)
{
bNoDisband = true;
bValid = true;
pLastUpgradePlot = pUnitPlot;
}
// try to upgrade units which are in danger... but don't get obsessed
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 08/20/09 jdog5000 */
/* */
/* Unit AI, Efficiency */
/************************************************************************************************/
//if (!bValid && (pLastUpgradePlot != pUnitPlot) && ((AI_getPlotDanger(pUnitPlot, 1, false)) > 0))
if (!bValid && (pLastUpgradePlot != pUnitPlot) && ((AI_getAnyPlotDanger(pUnitPlot, 1, false))))
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
bNoDisband = true;
bValid = true;
pLastUpgradePlot = pUnitPlot;
}
}
break;
case 2:
/********************************************************************************/
/* BETTER_BTS_AI_MOD 9/15/08 jdog5000 */
/* */
/* Gold AI */
/********************************************************************************/
/* original BTS code
if (pLoopUnit->cargoSpace() > 0)
{
bValid = true;
}
*/
bUnderBudget = (iStartingGold - getGold()) < iUpgradeBudget;
// Thomas SG - AC: Advanced Cargo START
// Only normal transports
{
if ( (pLoopUnit->totalCargoSpace() - pLoopUnit->getTotalSpecialCargoSpace()) > 0 )
{
bValid = (bAnyWar || bUnderBudget);
}
}
// Thomas SG - AC: Advanced Cargo END
// Also upgrade escort ships
if ( pLoopUnit->AI_getUnitAIType() == UNITAI_ESCORT_SEA )
{
bValid = (bAnyWar || bUnderBudget);
}
/********************************************************************************/
/* BETTER_BTS_AI_MOD END */
/********************************************************************************/
break;
case 3:
/********************************************************************************/
/* BETTER_BTS_AI_MOD 9/15/08 jdog5000 */
/* */
/* Gold AI */
/********************************************************************************/
/* original BTS code
bValid = true;
*/
bUnderBudget = (iStartingGold - getGold()) < iUpgradeBudget;
bValid = (bAnyWar || bUnderBudget);
/********************************************************************************/
/* BETTER_BTS_AI_MOD END */
/********************************************************************************/
break;
default:
FAssert(false);
break;
}
if (bValid)
{
bool bKilled = false;
if (!bNoDisband)
{
// RevolutionDCM start
if (pLoopUnit->canFight() && !pLoopUnit->isAnimal() && pLoopUnit->AI_getUnitAIType() != UNITAI_SUBDUED_ANIMAL)
// RevolutionDCM end
{
int iExp = pLoopUnit->getExperience();
CvCity* pPlotCity = pLoopUnit->plot()->getPlotCity();
if (pPlotCity != NULL && pPlotCity->getOwnerINLINE() == getID())
{
int iCityExp = 0;
iCityExp += pPlotCity->getFreeExperience();
iCityExp += pPlotCity->getDomainFreeExperience(pLoopUnit->getDomainType());
iCityExp += pPlotCity->getUnitCombatFreeExperience(pLoopUnit->getUnitCombatType());
// Afforess - also include wonder, religion & civic experience
if (getStateReligion() != NO_RELIGION && pPlotCity->isHasReligion(getStateReligion()))
{
iCityExp += getStateReligionFreeExperience(); //religions
}
iCityExp += pPlotCity->getSpecialistFreeExperience(); //great generals
iCityExp += getFreeExperience(); //civics & wonders
// Afforess - end
//TB SubCombat Mod Begin
UnitCombatTypes eSubCombatType;
int iI;
for (iI = 0; iI < pLoopUnit->getUnitInfo().getNumSubCombatTypes(); iI++)
{
eSubCombatType = ((UnitCombatTypes)pLoopUnit->getUnitInfo().getSubCombatType(iI));
iCityExp += pPlotCity->getUnitCombatFreeExperience(eSubCombatType);
}
//TB SubCombat Mod End
// Afforess - don't check this, iExp == 0 should be good enough!
//if (iCityExp > 0)
if (iExp == 0 || iExp < iCityExp)
{
//if ((iExp == 0) || (iExp < (iCityExp + 1) / 2))
if (pPlotCity->plot()->getNumDefenders(getID()) > pPlotCity->AI_neededDefenders())
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 08/20/09 jdog5000 */
/* */
/* Unit AI, Efficiency */
/************************************************************************************************/
if ((pLoopUnit->getDomainType() != DOMAIN_LAND) || pLoopUnit->plot()->plotCount(PUF_isMilitaryHappiness, -1, -1, getID()) > 1)
{
if ((calculateUnitCost() > 0) &&
(!AI_getAnyPlotDanger( pLoopUnit->plot(), 2, false) || !pLoopUnit->canDefend()) &&
pPlotCity->canTrain(pLoopUnit->getUnitType()))
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
pLoopUnit->getGroup()->AI_setMissionAI(MISSIONAI_DELIBERATE_KILL, NULL, NULL);
pLoopUnit->kill(false);
bKilled = true;
pLastUpgradePlot = NULL;
}
}
}
}
}
}
}
if (!bKilled)
{
pLoopUnit->AI_upgrade(); // CAN DELETE UNIT!!!
}
}
}
}
if (isBarbarian())
{
return;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 02/24/10 jdog5000 */
/* */
/* AI Logging */
/************************************************************************************************/
if( gPlayerLogLevel > 2 )
{
if( iStartingGold - getGold() > 0 )
{
logBBAI(" %S spends %d on unit upgrades out of budget of %d, %d gold remaining", getCivilizationDescription(0), iStartingGold - getGold(), iUpgradeBudget, getGold());
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
/************************************************************************************************/
/* UNOFFICIAL_PATCH 06/16/09 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
// Moved here per alexman's suggestion
AI_doSplit();
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
}
void CvPlayerAI::AI_doPeace()
{
PROFILE_FUNC();
CvDiploParameters* pDiplo;
CvCity* pBestReceiveCity;
CvCity* pBestGiveCity;
CvCity* pLoopCity;
CLinkList<TradeData> ourList;
CLinkList<TradeData> theirList;
bool abContacted[MAX_TEAMS];
TradeData item;
TechTypes eBestReceiveTech;
TechTypes eBestGiveTech;
int iReceiveGold;
int iGiveGold;
int iGold;
int iValue;
int iBestValue;
int iOurValue;
int iTheirValue;
int iLoop;
int iI, iJ;
FAssert(!isHuman());
FAssert(!isMinorCiv());
FAssert(!isBarbarian());
for (iI = 0; iI < MAX_TEAMS; iI++)
{
abContacted[iI] = false;
}
for (iI = 0; iI < MAX_CIV_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (iI != getID())
{
if (canContact((PlayerTypes)iI) && AI_isWillingToTalk((PlayerTypes)iI))
{
if (!(GET_TEAM(getTeam()).isHuman()) && (GET_PLAYER((PlayerTypes)iI).isHuman() || !(GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isHuman())))
{
if (GET_TEAM(getTeam()).isAtWar(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
if (!(GET_PLAYER((PlayerTypes)iI).isHuman()) || (GET_TEAM(getTeam()).getLeaderID() == getID()))
{
FAssertMsg(!(GET_PLAYER((PlayerTypes)iI).isBarbarian()), "(GET_PLAYER((PlayerTypes)iI).isBarbarian()) did not return false as expected");
FAssertMsg(iI != getID(), "iI is not expected to be equal with getID()");
FAssert(GET_PLAYER((PlayerTypes)iI).getTeam() != getTeam());
/************************************************************************************************/
/* Afforess Start 04/06/10 */
/* */
/* */
/************************************************************************************************/
bool bConsiderPeace;
if (GC.getGameINLINE().isOption(GAMEOPTION_ADVANCED_DIPLOMACY))
{
bConsiderPeace = ((GET_TEAM(getTeam()).AI_getAtWarCounter(GET_PLAYER((PlayerTypes)iI).getTeam()) > 10) || (GET_TEAM(getTeam()).getAtWarCount(false, true) > 1) ||
(GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_getWarSuccess(getTeam()) > (GET_TEAM(getTeam()).AI_getWarSuccess(GET_PLAYER((PlayerTypes)iI).getTeam()) * 2)));
}
else
{
bConsiderPeace = (GET_TEAM(getTeam()).AI_getAtWarCounter(GET_PLAYER((PlayerTypes)iI).getTeam()) > 10);
}
if (bConsiderPeace)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
if (AI_getContactTimer(((PlayerTypes)iI), CONTACT_PEACE_TREATY) == 0)
{
bool bOffered = false;
setTradeItem(&item, TRADE_SURRENDER);
if (canTradeItem((PlayerTypes)iI, item, true))
{
ourList.clear();
theirList.clear();
ourList.insertAtEnd(item);
bOffered = true;
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_PEACE_TREATY, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_PEACE_TREATY));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_PEACE"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 01/22/09 jdog5000 */
/* */
/* War Strategy AI */
/************************************************************************************************/
if( GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_acceptSurrender(getTeam()) )
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
}
if (!bOffered)
{
if (GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_PEACE_TREATY), "AI Diplo Peace Treaty") == 0)
{
setTradeItem(&item, TRADE_PEACE_TREATY);
if (canTradeItem(((PlayerTypes)iI), item, true) && GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
iOurValue = GET_TEAM(getTeam()).AI_endWarVal(GET_PLAYER((PlayerTypes)iI).getTeam());
iTheirValue = GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_endWarVal(getTeam());
eBestReceiveTech = NO_TECH;
eBestGiveTech = NO_TECH;
iReceiveGold = 0;
iGiveGold = 0;
pBestReceiveCity = NULL;
pBestGiveCity = NULL;
if (iTheirValue > iOurValue)
{
if (iTheirValue > iOurValue)
{
iBestValue = 0;
for (iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, iJ);
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(10000, "AI Peace Trading (Tech #1)"));
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestReceiveTech = ((TechTypes)iJ);
}
}
}
if (eBestReceiveTech != NO_TECH)
{
iOurValue += GET_TEAM(getTeam()).AI_techTradeVal(eBestReceiveTech, GET_PLAYER((PlayerTypes)iI).getTeam());
}
}
iGold = std::min((iTheirValue - iOurValue), GET_PLAYER((PlayerTypes)iI).AI_maxGoldTrade(getID()));
if (iGold > 0)
{
setTradeItem(&item, TRADE_GOLD, iGold);
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
iReceiveGold = iGold;
iOurValue += iGold;
}
}
if (iTheirValue > iOurValue)
{
iBestValue = 0;
for (pLoopCity = GET_PLAYER((PlayerTypes)iI).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iI).nextCity(&iLoop))
{
setTradeItem(&item, TRADE_CITIES, pLoopCity->getID());
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
iValue = pLoopCity->plot()->calculateCulturePercent(getID());
if (iValue > iBestValue)
{
iBestValue = iValue;
pBestReceiveCity = pLoopCity;
}
}
}
if (pBestReceiveCity != NULL)
{
iOurValue += AI_cityTradeVal(pBestReceiveCity);
}
}
}
else if (iOurValue > iTheirValue)
{
iBestValue = 0;
for (iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, iJ);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
if (GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_techTradeVal((TechTypes)iJ, getTeam()) <= (iOurValue - iTheirValue))
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(10000, "AI Peace Trading (Tech #2)"));
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestGiveTech = ((TechTypes)iJ);
}
}
}
}
if (eBestGiveTech != NO_TECH)
{
iTheirValue += GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_techTradeVal(eBestGiveTech, getTeam());
}
iGold = std::min((iOurValue - iTheirValue), AI_maxGoldTrade((PlayerTypes)iI));
if (iGold > 0)
{
setTradeItem(&item, TRADE_GOLD, iGold);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
iGiveGold = iGold;
iTheirValue += iGold;
}
}
iBestValue = 0;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
setTradeItem(&item, TRADE_CITIES, pLoopCity->getID());
if (canTradeItem(((PlayerTypes)iI), item, true))
{
if (GET_PLAYER((PlayerTypes)iI).AI_cityTradeVal(pLoopCity) <= (iOurValue - iTheirValue))
{
iValue = pLoopCity->plot()->calculateCulturePercent((PlayerTypes)iI);
if (iValue > iBestValue)
{
iBestValue = iValue;
pBestGiveCity = pLoopCity;
}
}
}
}
if (pBestGiveCity != NULL)
{
iTheirValue += GET_PLAYER((PlayerTypes)iI).AI_cityTradeVal(pBestGiveCity);
}
}
if ((GET_PLAYER((PlayerTypes)iI).isHuman()) ? (iOurValue >= iTheirValue) : ((iOurValue > ((iTheirValue * 3) / 5)) && (iTheirValue > ((iOurValue * 3) / 5))))
{
ourList.clear();
theirList.clear();
setTradeItem(&item, TRADE_PEACE_TREATY);
ourList.insertAtEnd(item);
theirList.insertAtEnd(item);
if (eBestGiveTech != NO_TECH)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, eBestGiveTech);
ourList.insertAtEnd(item);
}
if (eBestReceiveTech != NO_TECH)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, eBestReceiveTech);
theirList.insertAtEnd(item);
}
if (iGiveGold != 0)
{
setTradeItem(&item, TRADE_GOLD, iGiveGold);
ourList.insertAtEnd(item);
}
if (iReceiveGold != 0)
{
setTradeItem(&item, TRADE_GOLD, iReceiveGold);
theirList.insertAtEnd(item);
}
if (pBestGiveCity != NULL)
{
setTradeItem(&item, TRADE_CITIES, pBestGiveCity->getID());
ourList.insertAtEnd(item);
}
if (pBestReceiveCity != NULL)
{
setTradeItem(&item, TRADE_CITIES, pBestReceiveCity->getID());
theirList.insertAtEnd(item);
}
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_PEACE_TREATY, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_PEACE_TREATY));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_PEACE"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
void CvPlayerAI::AI_updateFoundValues(bool bClear, CvArea* area) const
{
PROFILE_FUNC();
CvPlot* pLoopPlot;
CvArea* pLoopArea;
int iLoop;
int iValue;
int iI;
bool bSetup = (getNumCities() == 0);
if ( bClear )
{
m_bCitySitesNotCalculated = true;
for (iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
{
pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
if ( bSetup || pLoopPlot->isRevealed(getTeam(), false) )
{
GC.getMapINLINE().plotByIndexINLINE(iI)->clearFoundValue(getID());
}
}
for(pLoopArea = GC.getMapINLINE().firstArea(&iLoop); pLoopArea != NULL; pLoopArea = GC.getMapINLINE().nextArea(&iLoop))
{
pLoopArea->setBestFoundValue(getID(), -1);
}
}
else
{
std::vector<CvArea*> aUncalculatedAreas;
for (iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
{
pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
if ((area == NULL || pLoopPlot->area() == area) && (bSetup || pLoopPlot->isRevealed(getTeam(), false)))
{
bool bNeedsCalculating = false;
if (!pLoopPlot->area()->hasBestFoundValue(getID()))
{
bNeedsCalculating = true;
pLoopPlot->area()->setBestFoundValue(getID(),0);
aUncalculatedAreas.push_back(pLoopPlot->area());
}
else
{
for(unsigned int iJ = 0; iJ < aUncalculatedAreas.size(); iJ++)
{
if ( aUncalculatedAreas[iJ] == pLoopPlot->area() )
{
bNeedsCalculating = true;
break;
}
}
}
if ( bNeedsCalculating )
{
long lResult=-1;
if(GC.getUSE_GET_CITY_FOUND_VALUE_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyArgsList argsList;
argsList.add((int)getID());
argsList.add(pLoopPlot->getX());
argsList.add(pLoopPlot->getY());
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "getCityFoundValue", argsList.makeFunctionArgs(), &lResult);
}
if (lResult == -1)
{
iValue = AI_foundValue(pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE());
}
else
{
iValue = lResult;
}
pLoopPlot->setFoundValue(getID(), iValue);
if (iValue > pLoopPlot->area()->getBestFoundValue(getID()))
{
pLoopPlot->area()->setBestFoundValue(getID(), iValue);
}
}
}
}
}
}
void CvPlayerAI::AI_updateAreaTargets()
{
CvArea* pLoopArea;
int iLoop;
for(pLoopArea = GC.getMapINLINE().firstArea(&iLoop); pLoopArea != NULL; pLoopArea = GC.getMapINLINE().nextArea(&iLoop))
{
if (!(pLoopArea->isWater()))
{
if (GC.getGameINLINE().getSorenRandNum(3, "AI Target City") == 0)
{
pLoopArea->setTargetCity(getID(), NULL);
}
else
{
pLoopArea->setTargetCity(getID(), AI_findTargetCity(pLoopArea));
}
}
}
}
// Returns priority for unit movement (lower values move first...)
int CvPlayerAI::AI_movementPriority(CvSelectionGroup* pGroup) const
{
CvUnit* pHeadUnit;
int iCurrCombat;
int iBestCombat;
pHeadUnit = pGroup->getHeadUnit();
if (pHeadUnit != NULL)
{
/********************************************************************************/
/* BETTER_BTS_AI_MOD 10/08/09 jdog5000 */
/* */
/* Air AI, Espionage AI */
/********************************************************************************/
/* original BTS code
if (pHeadUnit->hasCargo())
{
if (pHeadUnit->specialCargo() == NO_SPECIALUNIT)
{
return 0;
}
else
{
return 1;
}
}
if (pHeadUnit->getDomainType() == DOMAIN_AIR)
{
return 2;
}
if ((pHeadUnit->AI_getUnitAIType() == UNITAI_WORKER) || (pHeadUnit->AI_getUnitAIType() == UNITAI_WORKER_SEA))
{
return 3;
}
if ((pHeadUnit->AI_getUnitAIType() == UNITAI_EXPLORE) || (pHeadUnit->AI_getUnitAIType() == UNITAI_EXPLORE_SEA))
{
return 4;
}
if (pHeadUnit->bombardRate() > 0)
{
return 5;
}
if (pHeadUnit->collateralDamage() > 0)
{
return 6;
}
if (pHeadUnit->canFight())
{
if (pHeadUnit->withdrawalProbability() > 20)
{
return 7;
}
if (pHeadUnit->withdrawalProbability() > 0)
{
return 8;
}
iCurrCombat = pHeadUnit->currCombatStr(NULL, NULL);
iBestCombat = (GC.getGameINLINE().getBestLandUnitCombat() * 100);
if (pHeadUnit->noDefensiveBonus())
{
iCurrCombat *= 3;
iCurrCombat /= 2;
}
if (pHeadUnit->AI_isCityAIType())
{
iCurrCombat /= 2;
}
if (iCurrCombat > iBestCombat)
{
return 9;
}
else if (iCurrCombat > ((iBestCombat * 4) / 5))
{
return 10;
}
else if (iCurrCombat > ((iBestCombat * 3) / 5))
{
return 11;
}
else if (iCurrCombat > ((iBestCombat * 2) / 5))
{
return 12;
}
else if (iCurrCombat > ((iBestCombat * 1) / 5))
{
return 13;
}
else
{
return 14;
}
}
return 15;
}
return 16;
*/
if( pHeadUnit->isSpy() )
{
return 0;
}
if (pHeadUnit->hasCargo())
{
// Thomas SG - AC: Advanced Cargo START
if (!(pHeadUnit->getNumSpecialCargos() > 0))
{
return 1;
}
else
{
return 2;
}
}
// Thomas SG - AC: Advanced Cargo END
// Make fighters move before bombers, they are better at clearing out air defenses
if (pHeadUnit->getDomainType() == DOMAIN_AIR)
{
if( pHeadUnit->canAirDefend() )
{
return 3;
}
else
{
return 4;
}
}
if ((pHeadUnit->AI_getUnitAIType() == UNITAI_WORKER) || (pHeadUnit->AI_getUnitAIType() == UNITAI_WORKER_SEA))
{
return 5;
}
if ((pHeadUnit->AI_getUnitAIType() == UNITAI_EXPLORE) || (pHeadUnit->AI_getUnitAIType() == UNITAI_EXPLORE_SEA))
{
return 6;
}
if (pHeadUnit->bombardRate() > 0)
{
return 7;
}
if (pHeadUnit->collateralDamage() > 0)
{
return 8;
}
if (pHeadUnit->canFight())
{
if (pHeadUnit->withdrawalProbability() > 20)
{
return 9;
}
if (pHeadUnit->withdrawalProbability() > 0)
{
return 10;
}
iCurrCombat = pHeadUnit->currCombatStr(NULL, NULL);
iBestCombat = (GC.getGameINLINE().getBestLandUnitCombat() * 100);
if (pHeadUnit->noDefensiveBonus())
{
iCurrCombat *= 3;
iCurrCombat /= 2;
}
if (pHeadUnit->AI_isCityAIType())
{
iCurrCombat /= 2;
}
if (iCurrCombat > iBestCombat)
{
return 11;
}
else if (iCurrCombat > ((iBestCombat * 4) / 5))
{
return 12;
}
else if (iCurrCombat > ((iBestCombat * 3) / 5))
{
return 13;
}
else if (iCurrCombat > ((iBestCombat * 2) / 5))
{
return 14;
}
else if (iCurrCombat > ((iBestCombat * 1) / 5))
{
return 15;
}
else
{
return 16;
}
}
return 17;
}
return 18;
/********************************************************************************/
/* BETTER_BTS_AI_MOD END */
/********************************************************************************/
}
void CvPlayerAI::AI_unitUpdate()
{
PROFILE_FUNC();
CLLNode<int>* pCurrUnitNode;
CLLNode<int>* pNextUnitNode;
int iLoop;
// Do delayed death here so that empty groups are removed now
for(CvSelectionGroup* pLoopSelectionGroup = firstSelectionGroup(&iLoop); pLoopSelectionGroup; pLoopSelectionGroup = nextSelectionGroup(&iLoop))
{
pLoopSelectionGroup->doDelayedDeath(); // could destroy the selection group...
}
// Should have the same set of groups as is represented by m_selectionGroups
// as (indirectly via their head units) by m_groupCycle. These have been seen to
// get out of step, which results in a WFoC so if they contain differing member
// counts go through and fix it!
// Note - this is fixing a symtpom rather than a cause which is distasteful, but as
// yet the cause remains ellusive
if ( m_groupCycle.getLength() != m_selectionGroupsList.size() - (m_pTempUnit == NULL ? 0 : 1) )
{
if ( m_pTempUnit != NULL )
{
FAssert(m_pTempUnit->getGroup() != NULL);
OutputDebugString(CvString::format("temp group id is %d\n", m_pTempUnit->getGroup()->getID()).c_str());
}
OutputDebugString("Group cycle:\n");
pCurrUnitNode = headGroupCycleNode();
while (pCurrUnitNode != NULL)
{
CvSelectionGroup* pLoopSelectionGroup = getSelectionGroup(pCurrUnitNode->m_data);
pCurrUnitNode = nextGroupCycleNode(pCurrUnitNode);
OutputDebugString(CvString::format(" %d with %d units at (%d,%d)\n",
pLoopSelectionGroup->getID(),
pLoopSelectionGroup->getNumUnits(),
pLoopSelectionGroup->plot() == NULL ? -1 : pLoopSelectionGroup->plot()->getX_INLINE(),
pLoopSelectionGroup->plot() == NULL ? -1 : pLoopSelectionGroup->plot()->getY_INLINE()).c_str());
}
FAssert((int)m_selectionGroupsList.size() > m_groupCycle.getLength()); // Other way round not seen - not handled currently
OutputDebugString("Selection groups:\n");
for (CvSelectionGroup* pLoopSelectionGroup = firstSelectionGroup(&iLoop); pLoopSelectionGroup != NULL; pLoopSelectionGroup = nextSelectionGroup(&iLoop))
{
OutputDebugString(CvString::format(" %d with %d units at (%d,%d)\n",
pLoopSelectionGroup->getID(),
pLoopSelectionGroup->getNumUnits(),
pLoopSelectionGroup->plot() == NULL ? -1 : pLoopSelectionGroup->plot()->getX_INLINE(),
pLoopSelectionGroup->plot() == NULL ? -1 : pLoopSelectionGroup->plot()->getY_INLINE()).c_str());
if ( pLoopSelectionGroup->getHeadUnit() != m_pTempUnit )
{
updateGroupCycle(pLoopSelectionGroup->getHeadUnit(), true);
}
}
}
if (!hasBusyUnit())
{
pCurrUnitNode = headGroupCycleNode();
CvPathGenerator::EnableMaxPerformance(true);
while (pCurrUnitNode != NULL)
{
CvSelectionGroup* pLoopSelectionGroup = getSelectionGroup(pCurrUnitNode->m_data);
pNextUnitNode = nextGroupCycleNode(pCurrUnitNode);
// Since we know the set of selection groups can (somehow - reason is unknown) get out of step
// with the set in the update cycle (hence the code in the section above this one that copes with
// selection gropups that don't have group cycle entries), it follows that the converse may also
// occur. Thus we must be prepared to handle cycle entries with no corresponding selection group
// still extant
if ( pLoopSelectionGroup == NULL )
{
deleteGroupCycleNode(pCurrUnitNode);
}
else if (pLoopSelectionGroup->AI_isForceSeparate())
{
// do not split groups that are in the midst of attacking
if (pLoopSelectionGroup->isForceUpdate() || !pLoopSelectionGroup->AI_isGroupAttack())
{
pLoopSelectionGroup->AI_separate(); // pointers could become invalid...
}
}
pCurrUnitNode = pNextUnitNode;
}
if (isHuman())
{
pCurrUnitNode = headGroupCycleNode();
while (pCurrUnitNode != NULL)
{
CvSelectionGroup* pLoopSelectionGroup = getSelectionGroup(pCurrUnitNode->m_data);
pCurrUnitNode = nextGroupCycleNode(pCurrUnitNode);
if (pLoopSelectionGroup->AI_update())
{
break; // pointers could become invalid...
}
}
}
else
{
std::vector<std::pair<int, int> > groupList;
pCurrUnitNode = headGroupCycleNode();
while (pCurrUnitNode != NULL)
{
CvSelectionGroup* pLoopSelectionGroup = getSelectionGroup(pCurrUnitNode->m_data);
FAssert(pLoopSelectionGroup != NULL);
int iPriority = AI_movementPriority(pLoopSelectionGroup);
groupList.push_back(std::make_pair(iPriority, pCurrUnitNode->m_data));
pCurrUnitNode = nextGroupCycleNode(pCurrUnitNode);
}
std::sort(groupList.begin(), groupList.end());
for (size_t i = 0; i < groupList.size(); i++)
{
CvSelectionGroup* pLoopSelectionGroup = getSelectionGroup(groupList[i].second);
if (pLoopSelectionGroup && pLoopSelectionGroup->AI_update())
{
FAssert(pLoopSelectionGroup && pLoopSelectionGroup->getNumUnits() > 0);
break;
}
}
}
CvPathGenerator::EnableMaxPerformance(false);
}
}
void CvPlayerAI::AI_makeAssignWorkDirty()
{
CvCity* pLoopCity;
int iLoop;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
pLoopCity->AI_setAssignWorkDirty(true);
}
}
void CvPlayerAI::AI_assignWorkingPlots()
{
CvCity* pLoopCity;
int iLoop;
/************************************************************************************************/
/* Afforess Start 6/22/11 */
/* */
/* */
/************************************************************************************************/
//Who cares, plots return no yields anyway?
//Also fixes assert in cities, when they shuffle specialists with angry citizens from the anarchy
if (isAnarchy())
{
return;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
pLoopCity->AI_assignWorkingPlots();
}
}
void CvPlayerAI::AI_updateAssignWork()
{
CvCity* pLoopCity;
int iLoop;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
pLoopCity->AI_updateAssignWork();
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 05/08/09 jdog5000 */
/* */
/* City AI */
/************************************************************************************************/
void CvPlayerAI::AI_doCentralizedProduction()
{
PROFILE_FUNC();
CvCity* pLoopCity;
int iLoop;
int iI;
if( isHuman() )
{
return;
}
if( isBarbarian() )
{
return;
}
// BBAI TODO: Temp testing
//if( getID() % 2 == 1 )
//{
return;
//}
// Determine number of cities player can use building wonders currently
int iMaxNumWonderCities = 1 + getNumCities()/5;
bool bIndustrious = (getMaxPlayerBuildingProductionModifier() > 0);
bool bAtWar = (GET_TEAM(getTeam()).getAtWarCount(true) > 0);
if( bIndustrious )
{
iMaxNumWonderCities += 1;
}
// Dagger?
// Power?
// Research?
if( bAtWar )
{
int iWarCapRatio = GET_TEAM(getTeam()).AI_getWarSuccessCapitulationRatio();
if( iWarCapRatio < -90 )
{
iMaxNumWonderCities = 0;
}
else
{
if( iWarCapRatio < 30 )
{
iMaxNumWonderCities -= 1;
}
if( iWarCapRatio < -50 )
{
iMaxNumWonderCities /= 2;
}
}
}
if( isMinorCiv() && (GET_TEAM(getTeam()).getHasMetCivCount(false) > 1) )
{
iMaxNumWonderCities /= 2;
}
iMaxNumWonderCities = std::min(iMaxNumWonderCities, getNumCities());
// Gather city statistics
// Could rank cities based on gold, production here, could be O(n) instead of O(n^2)
int iWorldWonderCities = 0;
int iLimitedWonderCities = 0;
int iNumDangerCities = 0;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if( pLoopCity->isProductionBuilding() )
{
if( isLimitedWonderClass((BuildingClassTypes)(GC.getBuildingInfo(pLoopCity->getProductionBuilding()).getBuildingClassType())))
{
iLimitedWonderCities++;
if (isWorldWonderClass((BuildingClassTypes)(GC.getBuildingInfo(pLoopCity->getProductionBuilding()).getBuildingClassType())))
{
iWorldWonderCities++;
}
}
}
if( pLoopCity->isProductionProject() )
{
if( isLimitedProject(pLoopCity->getProductionProject()))
{
iLimitedWonderCities++;
if( isWorldProject(pLoopCity->getProductionProject()))
{
iWorldWonderCities++;
}
}
}
}
// Check for any global wonders to build
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
if (isWorldWonderClass((BuildingClassTypes)iI))
{
//canConstruct(
}
}
// Check for any projects to build
for (iI = 0; iI < GC.getNumProjectInfos(); iI++)
{
}
// Check for any national/team wonders to build
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
void CvPlayerAI::AI_makeProductionDirty()
{
CvCity* pLoopCity;
int iLoop;
FAssertMsg(!isHuman(), "isHuman did not return false as expected");
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
pLoopCity->AI_setChooseProductionDirty(true);
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 07/05/10 jdog5000 */
/* */
/* War tactics AI */
/************************************************************************************************/
void CvPlayerAI::AI_conquerCity(CvCity* pCity)
{
bool bRaze = false;
int iRazeValue;
int iI;
if (canRaze(pCity))
{
iRazeValue = 0;
int iCloseness = pCity->AI_playerCloseness(getID());
// Reasons to always raze
if( GC.getGameINLINE().culturalVictoryValid() && pCity->getCulture(pCity->getPreviousOwner()) > pCity->getCultureThreshold(GC.getGameINLINE().culturalVictoryCultureLevel())/2 )
{
CvCity* pLoopCity;
int iLoop;
int iHighCultureCount = 1;
if( GET_TEAM(getTeam()).AI_getEnemyPowerPercent(false) > 75 )
{
for( pLoopCity = GET_PLAYER(pCity->getPreviousOwner()).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(pCity->getPreviousOwner()).nextCity(&iLoop) )
{
if( pLoopCity->getCulture(pCity->getPreviousOwner()) > pLoopCity->getCultureThreshold(GC.getGameINLINE().culturalVictoryCultureLevel())/2 )
{
iHighCultureCount++;
if( iHighCultureCount >= GC.getGameINLINE().culturalVictoryNumCultureCities() )
{
//Raze city enemy needs for cultural victory unless we greatly over power them
logBBAI( " Razing enemy cultural victory city" );
bRaze = true;
}
}
}
}
}
if( !bRaze )
{
// Reasons to not raze
if( (getNumCities() <= 1) || (getNumCities() < 5 && iCloseness > 0) )
{
if( gPlayerLogLevel >= 1 )
{
logBBAI(" Player %d (%S) decides not to raze %S because they have few cities", getID(), getCivilizationDescription(0), pCity->getName().GetCString() );
}
}
else if( AI_isDoVictoryStrategy(AI_VICTORY_DOMINATION3) && GET_TEAM(getTeam()).AI_isPrimaryArea(pCity->area()) )
{
// Do not raze, going for domination
if( gPlayerLogLevel >= 1 )
{
logBBAI(" Player %d (%S) decides not to raze %S because they're going for domination", getID(), getCivilizationDescription(0), pCity->getName().GetCString() );
}
}
else if( isBarbarian() )
{
if ( !(pCity->isHolyCity()) && !(pCity->hasActiveWorldWonder()))
{
if( (pCity->getPreviousOwner() != BARBARIAN_PLAYER) && (pCity->getOriginalOwner() != BARBARIAN_PLAYER) )
{
iRazeValue += GC.getLeaderHeadInfo(getPersonalityType()).getRazeCityProb();
iRazeValue -= iCloseness;
}
}
}
else
{
bool bFinancialTrouble = AI_isFinancialTrouble();
bool bBarbCity = (pCity->getPreviousOwner() == BARBARIAN_PLAYER) && (pCity->getOriginalOwner() == BARBARIAN_PLAYER);
bool bPrevOwnerBarb = (pCity->getPreviousOwner() == BARBARIAN_PLAYER);
if (GET_TEAM(getTeam()).countNumCitiesByArea(pCity->area()) == 0)
{
// Conquered city in new continent/island
int iBestValue;
if( pCity->area()->getNumCities() == 1 && AI_getNumAreaCitySites(pCity->area()->getID(), iBestValue) == 0 )
{
// Probably small island
if( iCloseness == 0 )
{
// Safe to raze these now that AI can do pick up ...
iRazeValue += GC.getLeaderHeadInfo(getPersonalityType()).getRazeCityProb();
}
}
else
{
// At least medium sized island
if( iCloseness < 10 )
{
if( bFinancialTrouble )
{
// Raze if we might start incuring colony maintenance
iRazeValue = 100;
}
else
{
if (pCity->getPreviousOwner() != NO_PLAYER && !bPrevOwnerBarb)
{
if (GET_TEAM(GET_PLAYER(pCity->getPreviousOwner()).getTeam()).countNumCitiesByArea(pCity->area()) > 3)
{
if (GC.getGameINLINE().isOption(GAMEOPTION_NO_REVOLUTION))
{
//Fuyu: not so much
iRazeValue += std::max(0, (GC.getLeaderHeadInfo(getPersonalityType()).getRazeCityProb() - iCloseness));
}
else
{
iRazeValue += GC.getLeaderHeadInfo(getPersonalityType()).getRazeCityProb();
}
}
}
}
}
}
}
else
{
// Distance related aspects
if (iCloseness > 0)
{
iRazeValue -= iCloseness;
}
else
{
iRazeValue += 40;
CvCity* pNearestTeamAreaCity = GC.getMapINLINE().findCity(pCity->getX_INLINE(), pCity->getY_INLINE(), NO_PLAYER, getTeam(), true, false, NO_TEAM, NO_DIRECTION, pCity);
if( pNearestTeamAreaCity == NULL )
{
// Shouldn't happen
iRazeValue += 30;
}
else
{
int iDistance = plotDistance(pCity->getX_INLINE(), pCity->getY_INLINE(), pNearestTeamAreaCity->getX_INLINE(), pNearestTeamAreaCity->getY_INLINE());
iDistance -= DEFAULT_PLAYER_CLOSENESS + 2;
if ( iDistance > 0 )
{
iRazeValue += iDistance * (bBarbCity ? 8 : 5);
}
}
}
if (bFinancialTrouble)
{
iRazeValue += std::max(0, (70 - 15 * pCity->getPopulation()));
}
// Scale down distance/maintenance effects for organized
if( iRazeValue > 0 )
{
for (iI = 0; iI < GC.getNumTraitInfos(); iI++)
{
if (hasTrait((TraitTypes)iI))
{
iRazeValue *= (100 - (GC.getTraitInfo((TraitTypes)iI).getUpkeepModifier()));
iRazeValue /= 100;
if( (GC.getTraitInfo((TraitTypes)iI).getUpkeepModifier() > 0) && gPlayerLogLevel >= 1 )
{
logBBAI(" Reduction for upkeep modifier %d", (GC.getTraitInfo((TraitTypes)iI).getUpkeepModifier()) );
}
}
}
}
// Non-distance related aspects
if (GC.getGameINLINE().isOption(GAMEOPTION_NO_REVOLUTION))
{
//Fuyu: not so much
iRazeValue += std::max(0, ((GC.getLeaderHeadInfo(getPersonalityType()).getRazeCityProb() / 2) - iCloseness));
}
else
{
iRazeValue += GC.getLeaderHeadInfo(getPersonalityType()).getRazeCityProb();
}
if (getStateReligion() != NO_RELIGION)
{
if (pCity->isHasReligion(getStateReligion()))
{
if (GET_TEAM(getTeam()).hasShrine(getStateReligion()))
{
iRazeValue -= 50;
if( gPlayerLogLevel >= 1 )
{
logBBAI(" Reduction for state religion with shrine" );
}
}
else
{
iRazeValue -= 10;
if( gPlayerLogLevel >= 1 )
{
logBBAI(" Reduction for state religion" );
}
}
}
}
}
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (pCity->isHolyCity((ReligionTypes)iI))
{
logBBAI(" Reduction for holy city" );
if( getStateReligion() == iI )
{
iRazeValue -= 150;
}
else
{
iRazeValue -= 5 + GC.getGameINLINE().calculateReligionPercent((ReligionTypes)iI);
}
}
}
iRazeValue -= 25 * pCity->getNumActiveWorldWonders();
iRazeValue -= pCity->calculateTeamCulturePercent(getTeam());
CvPlot* pLoopPlot = NULL;
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
{
pLoopPlot = plotCity(pCity->getX_INLINE(), pCity->getY_INLINE(), iI);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getBonusType(getTeam()) != NO_BONUS)
{
iRazeValue -= std::max(2, AI_bonusVal(pLoopPlot->getBonusType(getTeam()))/2);
}
}
}
// More inclined to raze if we're unlikely to hold it
if( GET_TEAM(getTeam()).getPower(false)*10 < GET_TEAM(GET_PLAYER(pCity->getPreviousOwner()).getTeam()).getPower(true)*8 )
{
int iTempValue = 20;
iTempValue *= (GET_TEAM(GET_PLAYER(pCity->getPreviousOwner()).getTeam()).getPower(true) - GET_TEAM(getTeam()).getPower(false));
iTempValue /= std::max( 100, GET_TEAM(getTeam()).getPower(false) );
logBBAI(" Low power, so boost raze odds by %d", std::min( 75, iTempValue ) );
iRazeValue += std::min( 75, iTempValue );
}
if( gPlayerLogLevel >= 1 )
{
if( bBarbCity ) logBBAI(" %S is a barb city", pCity->getName().GetCString() );
if( bPrevOwnerBarb ) logBBAI(" %S was last owned by barbs", pCity->getName().GetCString() );
logBBAI(" %S has area cities %d, closeness %d, bFinTrouble %d", pCity->getName().GetCString(), GET_TEAM(getTeam()).countNumCitiesByArea(pCity->area()), iCloseness, bFinancialTrouble );
}
}
if( gPlayerLogLevel >= 1 )
{
logBBAI(" Player %d (%S) has odds %d to raze city %S", getID(), getCivilizationDescription(0), iRazeValue, pCity->getName().GetCString() );
}
if (iRazeValue > 0)
{
if (GC.getGameINLINE().getSorenRandNum(100, "AI Raze City") < iRazeValue)
{
bRaze = true;
}
}
}
}
if( bRaze )
{
//Fuyu fix
logBBAI(" Player %d (%S) decides to to raze city %S!!!", getID(), getCivilizationDescription(0), pCity->getName().GetCString() );
pCity->doTask(TASK_RAZE);
//logBBAI(" Player %d (%S) decides to to raze city %S!!!", getID(), getCivilizationDescription(0), pCity->getName().GetCString() );
}
else
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH 06/14/09 Maniac & jdog5000 */
/* */
/* */
/************************************************************************************************/
/* original bts code
CvEventReporter::getInstance().cityAcquiredAndKept(GC.getGameINLINE().getActivePlayer(), pCity);
*/
CvEventReporter::getInstance().cityAcquiredAndKept(getID(), pCity);
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
bool CvPlayerAI::AI_acceptUnit(CvUnit* pUnit) const
{
if (isHuman())
{
return true;
}
if (AI_isFinancialTrouble())
{
if (pUnit->AI_getUnitAIType() == UNITAI_WORKER)
{
if (AI_neededWorkers(pUnit->area()) > 0)
{
return true;
}
}
if (pUnit->AI_getUnitAIType() == UNITAI_WORKER_SEA)
{
return true;
}
if (pUnit->AI_getUnitAIType() == UNITAI_MISSIONARY)
{
return true; //XXX
}
return false;
}
return true;
}
bool CvPlayerAI::AI_captureUnit(UnitTypes eUnit, CvPlot* pPlot) const
{
CvCity* pNearestCity;
FAssert(!isHuman());
/************************************************************************************************/
/* Afforess Start 07/15/10 */
/* */
/* AI Always capture too */
/************************************************************************************************/
return true;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (pPlot->getTeam() == getTeam())
{
return true;
}
pNearestCity = GC.getMapINLINE().findCity(pPlot->getX_INLINE(), pPlot->getY_INLINE(), NO_PLAYER, getTeam());
if (pNearestCity != NULL)
{
if (plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pNearestCity->getX_INLINE(), pNearestCity->getY_INLINE()) <= 4)
{
return true;
}
}
return false;
}
DomainTypes CvPlayerAI::AI_unitAIDomainType(UnitAITypes eUnitAI) const
{
switch (eUnitAI)
{
case UNITAI_UNKNOWN:
return NO_DOMAIN;
break;
case UNITAI_ANIMAL:
case UNITAI_SETTLE:
case UNITAI_WORKER:
case UNITAI_ATTACK:
case UNITAI_ATTACK_CITY:
case UNITAI_COLLATERAL:
case UNITAI_PILLAGE:
case UNITAI_RESERVE:
case UNITAI_COUNTER:
case UNITAI_PARADROP:
case UNITAI_CITY_DEFENSE:
case UNITAI_CITY_COUNTER:
case UNITAI_CITY_SPECIAL:
case UNITAI_EXPLORE:
case UNITAI_MISSIONARY:
case UNITAI_PROPHET:
case UNITAI_ARTIST:
case UNITAI_SCIENTIST:
case UNITAI_GENERAL:
case UNITAI_MERCHANT:
case UNITAI_ENGINEER:
case UNITAI_SPY:
case UNITAI_ATTACK_CITY_LEMMING:
case UNITAI_PILLAGE_COUNTER:
case UNITAI_SUBDUED_ANIMAL:
case UNITAI_HUNTER:
/************************************************************************************************/
/* Great Diplomat MOD START */
/************************************************************************************************/
case UNITAI_DIPLOMAT:
/************************************************************************************************/
/* Great Diplomat MOD END */
/************************************************************************************************/
return DOMAIN_LAND;
break;
case UNITAI_ICBM:
return DOMAIN_IMMOBILE;
break;
case UNITAI_WORKER_SEA:
case UNITAI_ATTACK_SEA:
case UNITAI_RESERVE_SEA:
case UNITAI_ESCORT_SEA:
case UNITAI_EXPLORE_SEA:
case UNITAI_ASSAULT_SEA:
case UNITAI_SETTLER_SEA:
case UNITAI_MISSIONARY_SEA:
case UNITAI_SPY_SEA:
case UNITAI_CARRIER_SEA:
case UNITAI_MISSILE_CARRIER_SEA:
case UNITAI_PIRATE_SEA:
return DOMAIN_SEA;
break;
case UNITAI_ATTACK_AIR:
case UNITAI_DEFENSE_AIR:
case UNITAI_CARRIER_AIR:
case UNITAI_MISSILE_AIR:
return DOMAIN_AIR;
break;
default:
FAssert(false);
break;
}
return NO_DOMAIN;
}
int CvPlayerAI::AI_yieldWeight(YieldTypes eYield) const
{
if (eYield == YIELD_PRODUCTION)
{
int iProductionModifier = 100 + (30 * std::max(0, GC.getGame().getCurrentEra() - 1) / std::max(1, (GC.getNumEraInfos() - 2)));
return (GC.getYieldInfo(eYield).getAIWeightPercent() * iProductionModifier) / 100;
}
return GC.getYieldInfo(eYield).getAIWeightPercent();
}
int CvPlayerAI::AI_commerceWeight(CommerceTypes eCommerce, CvCity* pCity) const
{
int iWeight;
iWeight = GC.getCommerceInfo(eCommerce).getAIWeightPercent();
//sorry but the merchant descrimination must stop.
iWeight = std::min(110, iWeight);
//XXX Add something for 100%/0% type situations
switch (eCommerce)
{
case COMMERCE_RESEARCH:
if (AI_avoidScience())
{
if (isNoResearchAvailable())
{
iWeight = 0;
}
else
{
iWeight /= 8;
}
}
break;
case COMMERCE_GOLD:
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 06/12/09 jdog5000 */
/* */
/* Gold AI */
/************************************************************************************************/
if (getCommercePercent(COMMERCE_GOLD) > 70)
{
//avoid strikes
if (getGoldPerTurn() < -getGold()/100)
{
iWeight += 15;
}
}
else if (getCommercePercent(COMMERCE_GOLD) < 25)
{
//put more money towards other commerce types
if (getGoldPerTurn() > -getGold()/40)
{
iWeight -= 25 - getCommercePercent(COMMERCE_GOLD);
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
break;
case COMMERCE_CULTURE:
// COMMERCE_CULTURE AIWeightPercent is 25% in default xml
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/08/10 jdog5000 */
/* */
/* Bugfix, Cultural Victory AI */
/************************************************************************************************/
// Adjustments for human player going for cultural victory (who won't have AI strategy set)
// so that governors do smart things
if (pCity != NULL)
{
if (pCity->getCultureTimes100(getID()) >= 100 * GC.getGameINLINE().getCultureThreshold((CultureLevelTypes)(GC.getNumCultureLevelInfos() - 1)))
{
iWeight /= 50;
}
// Slider check works for detection of whether human player is going for cultural victory
else if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE3) || getCommercePercent(COMMERCE_CULTURE) >= 90 )
{
int iCultureRateRank = pCity->findCommerceRateRank(COMMERCE_CULTURE);
int iCulturalVictoryNumCultureCities = GC.getGameINLINE().culturalVictoryNumCultureCities();
// if one of the currently best cities, then focus hard, *4 or more
if (iCultureRateRank <= iCulturalVictoryNumCultureCities)
{
iWeight *= (3 + iCultureRateRank);
}
// if one of the 3 close to the top, then still emphasize culture some, *2
else if (iCultureRateRank <= iCulturalVictoryNumCultureCities + 3)
{
iWeight *= 2;
}
else if (isHuman())
{
iWeight *= 2;
}
}
else if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE2) || getCommercePercent(COMMERCE_CULTURE) >= 70)
{
iWeight *= 3;
}
else if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE1) || getCommercePercent(COMMERCE_CULTURE) >= 50)
{
iWeight *= 2;
}
iWeight += (100 - pCity->plot()->calculateCulturePercent(getID()));
if (pCity->getCultureLevel() <= (CultureLevelTypes) 1)
{
iWeight = std::max(iWeight, 800);
}
}
// pCity == NULL
else
{
if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE3) || getCommercePercent(COMMERCE_CULTURE) >=90 )
{
iWeight *= 3;
iWeight /= 4;
}
else if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE2) || getCommercePercent(COMMERCE_CULTURE) >= 70 )
{
iWeight *= 2;
iWeight /= 3;
}
else if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE1) || getCommercePercent(COMMERCE_CULTURE) >= 50 )
{
iWeight /= 2;
}
else
{
iWeight /= 3;
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
break;
case COMMERCE_ESPIONAGE:
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 04/29/09 jdog5000 */
/* */
/* Espionage AI, Bugfix */
/************************************************************************************************/
// Fixed bug where espionage weight set to 0 if winning all esp point races
// Smoothed out emphasis
int iEspBehindWeight = 0;
for (int iTeam = 0; iTeam < MAX_CIV_TEAMS; ++iTeam)
{
CvTeam& kLoopTeam = GET_TEAM((TeamTypes)iTeam);
if (kLoopTeam.isAlive() && iTeam != getTeam() && !kLoopTeam.isVassal(getTeam()) && !GET_TEAM(getTeam()).isVassal((TeamTypes)iTeam))
{
int iPointDiff = kLoopTeam.getEspionagePointsAgainstTeam(getTeam()) - GET_TEAM(getTeam()).getEspionagePointsAgainstTeam((TeamTypes)iTeam);
if (iPointDiff > 0)
{
iEspBehindWeight += 1;
if( GET_TEAM(getTeam()).AI_getAttitude((TeamTypes)iTeam) < ATTITUDE_CAUTIOUS )
{
iEspBehindWeight += 1;
}
}
}
}
iWeight *= 2*iEspBehindWeight + (3*GET_TEAM(getTeam()).getHasMetCivCount(true))/4 + 1;
iWeight *= AI_getEspionageWeight();
iWeight /= GET_TEAM(getTeam()).getHasMetCivCount(true) + 1;
iWeight /= 100;
// K-Mod
if (AI_isDoStrategy(AI_STRATEGY_BIG_ESPIONAGE))
{
iWeight *= 2;
}
if( getCommercePercent(COMMERCE_ESPIONAGE) == 0 )
{
iWeight *= 2;
iWeight /= 3;
}
else if( isHuman() )
{
// UNOFFICIAL_PATCH todo: should this tweak come over in some form?
// There's still an issue with upping espionage slider for human player.
if( getCommercePercent(COMMERCE_ESPIONAGE) > 50 )
{
iWeight *= getCommercePercent(COMMERCE_ESPIONAGE);
iWeight /= 50;
}
}
else
{
// AI Espionage slider use maxed out at 20 percent
if( getCommercePercent(COMMERCE_ESPIONAGE) >= 20 )
{
iWeight *= 3;
iWeight /= 2;
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
break;
default:
break;
}
return iWeight;
}
// Improved as per Blake - thanks!
int CvPlayerAI::AI_foundValue(int iX, int iY, int iMinRivalRange, bool bStartingLoc) const
{
CvCity* pNearestCity;
CvArea* pArea;
CvPlot* pPlot;
CvPlot* pLoopPlot;
FeatureTypes eFeature;
BonusTypes eBonus;
ImprovementTypes eBonusImprovement;
bool bHasGoodBonus;
int iOwnedTiles;
int iBadTile;
int iTakenTiles;
int iTeammateTakenTiles;
int iDifferentAreaTile;
int iTeamAreaCities;
int iHealth;
int iValue;
int iTempValue;
int iRange;
int iDX, iDY;
int iI;
bool bIsCoastal;
int iResourceValue = 0;
int iSpecialFood = 0;
int iSpecialFoodPlus = 0;
int iSpecialFoodMinus = 0;
int iSpecialProduction = 0;
int iSpecialCommerce = 0;
bool bNeutralTerritory = true;
int iGreed;
int iNumAreaCities;
pPlot = GC.getMapINLINE().plotINLINE(iX, iY);
if (!canFound(iX, iY))
{
return 0;
}
bIsCoastal = pPlot->isCoastalLand(GC.getMIN_WATER_SIZE_FOR_OCEAN());
pArea = pPlot->area();
iNumAreaCities = pArea->getCitiesPerPlayer(getID());
bool bAdvancedStart = (getAdvancedStartPoints() >= 0);
if (!bStartingLoc && !bAdvancedStart)
{
if (!bIsCoastal && iNumAreaCities == 0)
{
return 0;
}
}
if (bAdvancedStart)
{
//FAssert(!bStartingLoc);
FAssert(GC.getGameINLINE().isOption(GAMEOPTION_ADVANCED_START));
if (bStartingLoc)
{
bAdvancedStart = false;
}
}
//Explaination of city site adjustment:
//Any plot which is otherwise within the radius of a city site
//is basically treated as if it's within an existing city radius
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
std::vector<bool> abCitySiteRadius(NUM_CITY_PLOTS, false);
*/
std::vector<bool> abCitySiteRadius(NUM_CITY_PLOTS_2, false);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (!bStartingLoc && !m_bCitySitesNotCalculated)
{
if (!AI_isPlotCitySite(pPlot))
{
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
*/
for (iI = 0; iI < NUM_CITY_PLOTS_2; iI++)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
pLoopPlot = plotCity(iX, iY, iI);
if (pLoopPlot != NULL)
{
for (int iJ = 0; iJ < AI_getNumCitySites(); iJ++)
{
CvPlot* pCitySitePlot = AI_getCitySite(iJ);
if (pCitySitePlot != pPlot)
{
if (plotDistance(pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE(), pCitySitePlot->getX_INLINE(), pCitySitePlot->getY_INLINE()) <= CITY_PLOTS_RADIUS)
{
//Plot is inside the radius of a city site
abCitySiteRadius[iI] = true;
break;
}
}
}
}
}
}
}
std::vector<int> paiBonusCount;
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
paiBonusCount.push_back(0);
}
if (iMinRivalRange != -1)
{
for (iDX = -(iMinRivalRange); iDX <= iMinRivalRange; iDX++)
{
for (iDY = -(iMinRivalRange); iDY <= iMinRivalRange; iDY++)
{
pLoopPlot = plotXY(iX, iY, iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->plotCheck(PUF_isOtherTeam, getID()) != NULL)
{
return 0;
}
}
}
}
}
if (bStartingLoc)
{
if (pPlot->isGoody())
{
return 0;
}
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
*/
for (iI = 0; iI < NUM_CITY_PLOTS_2; iI++)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
pLoopPlot = plotCity(iX, iY, iI);
if (pLoopPlot == NULL)
{
return 0;
}
}
}
iOwnedTiles = 0;
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
*/
for (iI = 0; iI < NUM_CITY_PLOTS_2; iI++)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
pLoopPlot = plotCity(iX, iY, iI);
if (pLoopPlot == NULL)
{
iOwnedTiles++;
}
else if (pLoopPlot->isOwned())
{
if (pLoopPlot->getTeam() != getTeam())
{
iOwnedTiles++;
}
}
}
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
if (iOwnedTiles > (NUM_CITY_PLOTS / 3))
*/
if (iOwnedTiles > (NUM_CITY_PLOTS_2 / 3))
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
return 0;
}
iBadTile = 0;
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
*/
for (iI = 0; iI < NUM_CITY_PLOTS_2; iI++)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
pLoopPlot = plotCity(iX, iY, iI);
if (iI != CITY_HOME_PLOT)
{
/************************************************************************************************/
/* Afforess Mountains Start 09/18/09 */
/* */
/* */
/************************************************************************************************/
if ((pLoopPlot == NULL) || pLoopPlot->isImpassable(getTeam())) //added getTeam()
/************************************************************************************************/
/* Afforess Mountains End END */
/************************************************************************************************/
{
iBadTile += 2;
}
else if (!(pLoopPlot->isFreshWater()) && !(pLoopPlot->isHills()))
{
if ((pLoopPlot->calculateBestNatureYield(YIELD_FOOD, getTeam()) == 0) || (pLoopPlot->calculateTotalBestNatureYield(getTeam()) <= 1))
{
iBadTile += 2;
}
else if (pLoopPlot->isWater() && !bIsCoastal && (pLoopPlot->calculateBestNatureYield(YIELD_FOOD, getTeam()) <= 1))
{
iBadTile++;
}
}
else if (pLoopPlot->isOwned())
{
if (pLoopPlot->getTeam() == getTeam())
{
if (pLoopPlot->isCityRadius() || abCitySiteRadius[iI])
{
iBadTile += bAdvancedStart ? 2 : 1;
}
}
}
}
}
iBadTile /= 2;
if (!bStartingLoc)
{
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
if ((iBadTile > (NUM_CITY_PLOTS / 2)) || (pArea->getNumTiles() <= 2))
*/
if ((iBadTile > (NUM_CITY_PLOTS_2 / 2)) || (pArea->getNumTiles() <= 2))
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
bHasGoodBonus = false;
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
*/
for (iI = 0; iI < NUM_CITY_PLOTS_2; iI++)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
pLoopPlot = plotCity(iX, iY, iI);
if (pLoopPlot != NULL)
{
if (!(pLoopPlot->isOwned()))
{
if (pLoopPlot->isWater() || (pLoopPlot->area() == pArea) || (pLoopPlot->area()->getCitiesPerPlayer(getID()) > 0))
{
eBonus = pLoopPlot->getBonusType(getTeam());
if (eBonus != NO_BONUS)
{
if ((getNumTradeableBonuses(eBonus) == 0) || (AI_bonusVal(eBonus) > 10)
|| (GC.getBonusInfo(eBonus).getYieldChange(YIELD_FOOD) > 0))
{
bHasGoodBonus = true;
break;
}
}
}
}
}
}
if (!bHasGoodBonus)
{
return 0;
}
}
}
iTakenTiles = 0;
iTeammateTakenTiles = 0;
iHealth = 0;
iValue = 1000;
iGreed = 100;
// K-Mod - EasyCulture means that it will be easy for us to pop the culture to the 2nd border
bool bEasyCulture = false;
if (bAdvancedStart)
{
iGreed = 150;
}
else if (!bStartingLoc)
{
for (iI = 0; iI < GC.getNumTraitInfos(); iI++)
{
if (hasTrait((TraitTypes)iI))
{
//Greedy founding means getting the best possible sites - fitting maximum
//resources into the fat cross.
iGreed += (GC.getTraitInfo((TraitTypes)iI).getUpkeepModifier() / 2);
iGreed += 20 * (GC.getTraitInfo((TraitTypes)iI).getCommerceChange(COMMERCE_CULTURE));
// K-Mod note: I don't think this is the right way to calculate greed.
// For example, if greed is high, the civ will end up having fewer, more spread out cities.
// That's the opposite of what makes an upkeep reduction most useful.
if (GC.getTraitInfo((TraitTypes)iI).getCommerceChange(COMMERCE_CULTURE) > 0)
bEasyCulture = true;
}
}
/********************************************************************************/
/* Better City Placement? 24.07.2010 Fuyu */
/********************************************************************************/
//Fuyu: be greedier
if (!isFoundedFirstCity())
{
//here I'm assuming that the capital gets a palace that has significant culture output, thus popping the final level 2 really quickly
iGreed = std::max(iGreed, 145);
}
else
{
for (iI = 0; (iGreed < 140 && iI < GC.getNumProcessInfos()); iI++)
{
if (canMaintain((ProcessTypes)iI))
{
iGreed = std::max(iGreed, 100 + 20 * std::min(2, (GC.getProcessInfo((ProcessTypes)iI).getProductionToCommerceModifier(COMMERCE_CULTURE)/100)) );
}
}
for (iI = 0; (iGreed < 140 && iI < GC.getNumSpecialistInfos()); iI++)
{
if (isSpecialistValid((SpecialistTypes)iI))
{
iGreed = std::max(iGreed, 100 + 10 * std::min(4, (GC.getSpecialistInfo((SpecialistTypes)iI).getCommerceChange(COMMERCE_CULTURE) + getSpecialistExtraCommerce(COMMERCE_CULTURE))) );
}
}
if (iGreed < 140 && AI_isAreaAlone(pArea))
{
//assuming that the player actually has access to something that can make a city produce culture
iGreed = std::max(iGreed, 125 + (5 * std::min(3, pArea->getCitiesPerPlayer(getID()) )) );
}
}
iGreed = range(iGreed, 100, 150);
/********************************************************************************/
/* Better City Placement? END */
/********************************************************************************/
}
// K-Mod, easy culture
// culture building process
if (!bEasyCulture)
{
for (int iJ = 0; iJ < GC.getNumProcessInfos(); iJ++)
{
if (GET_TEAM(getTeam()).isHasTech((TechTypes)GC.getProcessInfo((ProcessTypes)iJ).getTechPrereq()) &&
GC.getProcessInfo((ProcessTypes)iJ).getProductionToCommerceModifier(COMMERCE_CULTURE) > 0)
{
bEasyCulture = true;
break;
}
}
}
// free culture building
if (!bEasyCulture)
{
for (int iJ = 0; iJ < GC.getNumBuildingInfos(); iJ++)
{
if (isBuildingFree((BuildingTypes)iJ) && GC.getBuildingInfo((BuildingTypes)iJ).getObsoleteSafeCommerceChange(COMMERCE_CULTURE) > 0)
{
bEasyCulture = true;
break;
}
}
}
// easy artists
if (!bEasyCulture)
{
for (int iJ = 0; iJ < GC.getNumSpecialistInfos(); iJ++)
{
if (isSpecialistValid((SpecialistTypes)iJ) && specialistCommerce((SpecialistTypes)iJ, COMMERCE_CULTURE) > 0)
{
bEasyCulture = true;
break;
}
}
}
// K-Mod end
//iClaimThreshold is the culture required to pop the 2nd borders.
int iClaimThreshold = GC.getGameINLINE().getCultureThreshold((CultureLevelTypes)(std::min(2, (GC.getNumCultureLevelInfos() - 1))));
iClaimThreshold = std::max(1, iClaimThreshold);
//iClaimThreshold *= (std::max(100, iGreed));
iClaimThreshold *= (bEasyCulture ? 140 : 100); // K-Mod
/************************************************************************************************/
/* UNOFFICIAL_PATCH 04/25/10 denev & jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
// Was missing this
/* Fuyu: we can do this when we are actually using this variable
iClaimThreshold /= 100;
*/
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
int iYieldLostHere = 0;
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
*/
for (iI = 0; iI < NUM_CITY_PLOTS_2; iI++)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
pLoopPlot = plotCity(iX, iY, iI);
if (pLoopPlot == NULL)
{
iTakenTiles++;
}
else if (pLoopPlot->isCityRadius() || abCitySiteRadius[iI])
{
iTakenTiles++;
if (abCitySiteRadius[iI])
{
iTeammateTakenTiles++;
}
}
else
{
iTempValue = 0;
eFeature = pLoopPlot->getFeatureType();
eBonus = pLoopPlot->getBonusType((bStartingLoc) ? NO_TEAM : getTeam());
eBonusImprovement = NO_IMPROVEMENT;
int iCultureMultiplier;
if (!pLoopPlot->isOwned() || (pLoopPlot->getOwnerINLINE() == getID()))
{
iCultureMultiplier = 100;
}
else
{
bNeutralTerritory = false;
int iOurCulture = pLoopPlot->getCulture(getID());
int iOtherCulture = std::max(1, pLoopPlot->getCulture(pLoopPlot->getOwnerINLINE()));
/************************************************************************************************/
/* UNOFFICIAL_PATCH 04/25/10 denev & Fuyu */
/* */
/* Bugfix */
/************************************************************************************************/
/*original code
iCultureMultiplier = 100 * (iOurCulture + iClaimThreshold);
iCultureMultiplier /= (iOtherCulture + iClaimThreshold);
*/
iCultureMultiplier = (100 * iOurCulture) + iClaimThreshold;
iCultureMultiplier /= (((100 * iOtherCulture) + iClaimThreshold) / 100);
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
iCultureMultiplier = std::min(100, iCultureMultiplier);
//The multiplier is basically normalized...
//100% means we own (or rightfully own) the tile.
//50% means the hostile culture is fairly firmly entrenched.
}
if (iCultureMultiplier < ((iNumAreaCities > 0) ? 25 : 50))
{
//discourage hopeless cases, especially on other continents.
iTakenTiles += (iNumAreaCities > 0) ? 1 : 2;
}
if (eBonus != NO_BONUS)
{
for (int iImprovement = 0; iImprovement < GC.getNumImprovementInfos(); ++iImprovement)
{
CvImprovementInfo& kImprovement = GC.getImprovementInfo((ImprovementTypes)iImprovement);
if (kImprovement.isImprovementBonusMakesValid(eBonus))
{
eBonusImprovement = (ImprovementTypes)iImprovement;
break;
}
}
}
int aiYield[NUM_YIELD_TYPES];
for (int iYieldType = 0; iYieldType < NUM_YIELD_TYPES; ++iYieldType)
{
YieldTypes eYield = (YieldTypes)iYieldType;
aiYield[eYield] = pLoopPlot->getYield(eYield);
if (iI == CITY_HOME_PLOT)
{
int iBasePlotYield = aiYield[eYield];
aiYield[eYield] += GC.getYieldInfo(eYield).getCityChange();
if (eFeature != NO_FEATURE)
{
aiYield[eYield] -= GC.getFeatureInfo(eFeature).getYieldChange(eYield);
iBasePlotYield = std::max(iBasePlotYield, aiYield[eYield]);
}
if (eBonus == NO_BONUS)
{
aiYield[eYield] = std::max(aiYield[eYield], GC.getYieldInfo(eYield).getMinCity());
}
else
{
int iBonusYieldChange = GC.getBonusInfo(eBonus).getYieldChange(eYield);
aiYield[eYield] += iBonusYieldChange;
iBasePlotYield += iBonusYieldChange;
aiYield[eYield] = std::max(aiYield[eYield], GC.getYieldInfo(eYield).getMinCity());
}
if (eBonusImprovement != NO_IMPROVEMENT)
{
iBasePlotYield += GC.getImprovementInfo(eBonusImprovement).getImprovementBonusYield(eBonus, eYield);
if (iBasePlotYield > aiYield[eYield])
{
aiYield[eYield] -= 2 * (iBasePlotYield - aiYield[eYield]);
}
else
{
aiYield[eYield] += aiYield[eYield] - iBasePlotYield;
}
}
}
}
if (iI == CITY_HOME_PLOT)
{
iTempValue += aiYield[YIELD_FOOD] * 60;
iTempValue += aiYield[YIELD_PRODUCTION] * 60;
iTempValue += aiYield[YIELD_COMMERCE] * 40;
}
else if (aiYield[YIELD_FOOD] >= GC.getFOOD_CONSUMPTION_PER_POPULATION())
{
iTempValue += aiYield[YIELD_FOOD] * 40;
iTempValue += aiYield[YIELD_PRODUCTION] * 40;
iTempValue += aiYield[YIELD_COMMERCE] * 30;
if (bStartingLoc)
{
iTempValue *= 2;
}
}
else if (aiYield[YIELD_FOOD] == GC.getFOOD_CONSUMPTION_PER_POPULATION() - 1)
{
iTempValue += aiYield[YIELD_FOOD] * 25;
iTempValue += aiYield[YIELD_PRODUCTION] * 25;
iTempValue += aiYield[YIELD_COMMERCE] * 20;
}
else
{
iTempValue += aiYield[YIELD_FOOD] * 15;
iTempValue += aiYield[YIELD_PRODUCTION] * 15;
iTempValue += aiYield[YIELD_COMMERCE] * 10;
}
if (pLoopPlot->isWater())
{
if (aiYield[YIELD_COMMERCE] >= 1)
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 08/05/09 jdog5000 */
/* */
/* Settler AI */
/************************************************************************************************/
/* orginal bts code
iTempValue += bIsCoastal ? 30 : -20;
*/
// Upside is much higher based on multipliers above, with lighthouse a standard coast
// plot moves up into the higher multiplier category.
iTempValue += 10*aiYield[YIELD_COMMERCE];
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
if (bIsCoastal && (aiYield[YIELD_FOOD] >= GC.getFOOD_CONSUMPTION_PER_POPULATION()))
{
iSpecialFoodPlus += 1;
}
if (bStartingLoc && !pPlot->isStartingPlot())
{
// I'm pretty much forbidding starting 1 tile inland non-coastal.
// with more than a few coast tiles.
iTempValue += bIsCoastal ? 0 : -400;
}
}
}
if (pLoopPlot->isRiver())
{
/************************************************************************************************/
/* Afforess Start 07/21/10 */
/* */
/* Favor extra river tiles for eventual building yields, if we are on a river */
/************************************************************************************************/
if (pPlot->isRiver())
{
iTempValue += 10 * GC.getGameINLINE().getRiverBuildings();
}
else
{
iTempValue += 10;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
/************************************************************************************************/
/* Afforess Start 03/18/10 */
/* */
/* */
/************************************************************************************************/
if (pLoopPlot->isPeak())
{
if (GC.getGameINLINE().isOption(GAMEOPTION_MOUNTAINS))
{
iTempValue += 10;
}
else
{
iTempValue -= 10;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
/* original bts code
if (iI == CITY_HOME_PLOT)
{
iTempValue *= 2;
}
else if ((pLoopPlot->getOwnerINLINE() == getID()) || (stepDistance(iX, iY, pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE()) == 1))
{
// BBAI Notes: Extra weight on tiles which will be available immediately
iTempValue *= 3;
iTempValue /= 2;
}
else
{
iTempValue *= iGreed;
iTempValue /= 100;
}*/
// K-Mod version
if (bEasyCulture)
{
// 5/4 * 21 ~= 9 * 1.5 + 12 * 1;
iTempValue *= 5;
iTempValue /= 4;
}
else
{
if ((pLoopPlot->getOwnerINLINE() == getID()) || (stepDistance(iX, iY, pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE()) <= 1))
{
iTempValue *= 3;
iTempValue /= 2;
}
}
iTempValue *= iGreed; // (note: see comments about iGreed higher in the code)
iTempValue /= 100;
// K-Mod end
iTempValue *= iCultureMultiplier;
iTempValue /= 100;
iValue += iTempValue;
if (iCultureMultiplier > 33) //ignore hopelessly entrenched tiles.
{
if (eFeature != NO_FEATURE)
{
if (iI != CITY_HOME_PLOT)
{
iHealth += GC.getFeatureInfo(eFeature).getHealthPercent();
iSpecialFoodPlus += std::max(0, aiYield[YIELD_FOOD] - GC.getFOOD_CONSUMPTION_PER_POPULATION());
}
}
if ((eBonus != NO_BONUS) && ((pLoopPlot->area() == pPlot->area()) ||
(pLoopPlot->area()->getCitiesPerPlayer(getID()) > 0) ||
pLoopPlot->isWater()))
{
paiBonusCount[eBonus]++;
FAssert(paiBonusCount[eBonus] > 0);
iTempValue = (AI_bonusVal(eBonus) * ((!bStartingLoc && (getNumTradeableBonuses(eBonus) == 0) && (paiBonusCount[eBonus] == 1)) ? 80 : 20));
iTempValue *= ((bStartingLoc) ? 100 : iGreed);
iTempValue /= 100;
if (iI != CITY_HOME_PLOT && !bStartingLoc)
{
if ((pLoopPlot->getOwnerINLINE() != getID()) && stepDistance(pPlot->getX_INLINE(),pPlot->getY_INLINE(), pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE()) > 1)
{
iTempValue *= 2;
iTempValue /= 3;
iTempValue *= std::min(150, iGreed);
iTempValue /= 100;
}
}
if ( pLoopPlot->isWater() && !pLoopPlot->isAdjacentToLand() )
{
iTempValue /= 2;
}
iValue += (iTempValue + 10);
if (iI != CITY_HOME_PLOT)
{
if (eBonusImprovement != NO_IMPROVEMENT)
{
int iSpecialFoodTemp;
iSpecialFoodTemp = pLoopPlot->calculateBestNatureYield(YIELD_FOOD, getTeam()) + GC.getImprovementInfo(eBonusImprovement).getImprovementBonusYield(eBonus, YIELD_FOOD);
iSpecialFood += iSpecialFoodTemp;
iSpecialFoodTemp -= GC.getFOOD_CONSUMPTION_PER_POPULATION();
iSpecialFoodPlus += std::max(0,iSpecialFoodTemp);
iSpecialFoodMinus -= std::min(0,iSpecialFoodTemp);
iSpecialProduction += pLoopPlot->calculateBestNatureYield(YIELD_PRODUCTION, getTeam()) + GC.getImprovementInfo(eBonusImprovement).getImprovementBonusYield(eBonus, YIELD_PRODUCTION);
iSpecialCommerce += pLoopPlot->calculateBestNatureYield(YIELD_COMMERCE, getTeam()) + GC.getImprovementInfo(eBonusImprovement).getImprovementBonusYield(eBonus, YIELD_COMMERCE);
}
if (eFeature != NO_FEATURE)
{
if (GC.getFeatureInfo(eFeature).getYieldChange(YIELD_FOOD) < 0)
{
iResourceValue -= 30;
}
}
}
}
}
}
}
iResourceValue += iSpecialFood * 50;
iResourceValue += iSpecialProduction * 50;
iResourceValue += iSpecialCommerce * 50;
if (bStartingLoc)
{
iResourceValue /= 2;
}
iValue += std::max(0, iResourceValue);
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
if (iTakenTiles > (NUM_CITY_PLOTS / 3) && iResourceValue < 250)
*/
if (iTakenTiles > (NUM_CITY_PLOTS_2 / 3) && iResourceValue < 250)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
return 0;
}
if (iTeammateTakenTiles > 1)
{
return 0;
}
iValue += (iHealth / 5);
if (bIsCoastal)
{
if (!bStartingLoc)
{
if (pArea->getCitiesPerPlayer(getID()) == 0)
{
if (bNeutralTerritory)
{
iValue += (iResourceValue > 0) ? 800 : 100;
}
}
else
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 02/03/09 jdog5000 */
/* */
/* Settler AI */
/************************************************************************************************/
iValue += 200;
// Push players to get more coastal cities so they can build navies
CvArea* pWaterArea = pPlot->waterArea(true);
if( pWaterArea != NULL )
{
iValue += 200;
if( GET_TEAM(getTeam()).AI_isWaterAreaRelevant(pWaterArea) )
{
iValue += 200;
if( (countNumCoastalCities() < (getNumCities()/2)) || (countNumCoastalCitiesByArea(pPlot->area()) == 0) )
{
iValue *= (100*(getNumCities() - countNumCoastalCities()))/getNumCities() + 250;
iValue /= 200;
}
// If this location bridges two water areas that's worth a boost
CvArea* pSecondWaterArea = pPlot->secondWaterArea();
if ( pSecondWaterArea != NULL )
{
if ( GET_TEAM(getTeam()).AI_isWaterAreaRelevant(pSecondWaterArea) )
{
iValue *= 4;
iValue /= 3;
}
else
{
// Just bridges a lake or something
iValue += 50;
}
}
// If this will be our first city on this water area give another boost
if ( countNumCoastalCitiesByArea(pWaterArea) == 0 )
{
iValue *= 5;
iValue /= 4;
}
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
}
else
{
//let other penalties bring this down.
iValue += 600;
if (!pPlot->isStartingPlot())
{
if (pArea->getNumStartingPlots() == 0)
{
iValue += 1000;
}
}
}
}
if (pPlot->isHills())
{
iValue += 200;
}
if (pPlot->isRiver())
{
/************************************************************************************************/
/* Afforess Start 07/21/10 */
/* */
/* */
/************************************************************************************************/
iValue += 100 * GC.getGameINLINE().getRiverBuildings();
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
if (bIsCoastal)
{
// Koshling - analogous for coastal buildings. Coefficient is lower just based on
// sampling the numbers in C2C at the time I added this
iValue += 20 * GC.getGameINLINE().getCoastalBuildings();
}
/************************************************************************************************/
/* Afforess Start 04/01/10 */
/* */
/* */
/************************************************************************************************/
if (pPlot->isFreshWater(true))
{
iValue += 40;
iValue += (GC.getDefineINT("FRESH_WATER_HEALTH_CHANGE") * 30);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (bStartingLoc)
{
iRange = GREATER_FOUND_RANGE;
int iGreaterBadTile = 0;
for (iDX = -(iRange); iDX <= iRange; iDX++)
{
for (iDY = -(iRange); iDY <= iRange; iDY++)
{
pLoopPlot = plotXY(iX, iY, iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->isWater() || (pLoopPlot->area() == pArea))
{
if (plotDistance(iX, iY, pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE()) <= iRange)
{
iTempValue = 0;
iTempValue += (pLoopPlot->getYield(YIELD_FOOD) * 15);
iTempValue += (pLoopPlot->getYield(YIELD_PRODUCTION) * 11);
iTempValue += (pLoopPlot->getYield(YIELD_COMMERCE) * 5);
iValue += iTempValue;
if (iTempValue < 21)
{
iGreaterBadTile += 2;
if (pLoopPlot->getFeatureType() != NO_FEATURE)
{
if (pLoopPlot->calculateBestNatureYield(YIELD_FOOD,getTeam()) > 1)
{
iGreaterBadTile--;
}
}
}
}
}
}
}
}
if (!pPlot->isStartingPlot())
{
iGreaterBadTile /= 2;
if (iGreaterBadTile > 12)
{
iValue *= 11;
iValue /= iGreaterBadTile;
}
}
int iWaterCount = 0;
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
*/
for (iI = 0; iI < NUM_CITY_PLOTS_2; iI++)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
pLoopPlot = plotCity(iX, iY, iI);
if (pLoopPlot != NULL)
{
if (pLoopPlot->isWater())
{
iWaterCount ++;
if (pLoopPlot->getYield(YIELD_FOOD) <= 1)
{
iWaterCount++;
}
}
}
}
iWaterCount /= 2;
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
int iLandCount = (NUM_CITY_PLOTS - iWaterCount);
*/
int iLandCount = (NUM_CITY_PLOTS_2 - iWaterCount);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
if (iLandCount < (NUM_CITY_PLOTS / 2))
*/
if (iLandCount < (NUM_CITY_PLOTS_2 / 2))
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
//discourage very water-heavy starts.
iValue *= 1 + iLandCount;
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
iValue /= (1 + (NUM_CITY_PLOTS / 2));
*/
iValue /= (1 + (NUM_CITY_PLOTS_2 / 2));
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
}
if (bStartingLoc)
{
if (pPlot->getMinOriginalStartDist() == -1)
{
iValue += (GC.getMapINLINE().maxStepDistance() * 100);
}
else
{
iValue *= (1 + 4 * pPlot->getMinOriginalStartDist());
iValue /= (1 + 2 * GC.getMapINLINE().maxStepDistance());
}
//nice hacky way to avoid this messing with normalizer, use elsewhere?
if (!pPlot->isStartingPlot())
{
int iMinDistanceFactor = MAX_INT;
int iMinRange = startingPlotRange();
iValue *= 100;
for (int iJ = 0; iJ < MAX_CIV_PLAYERS; iJ++)
{
if (GET_PLAYER((PlayerTypes)iJ).isAlive())
{
if (iJ != getID())
{
int iClosenessFactor = GET_PLAYER((PlayerTypes)iJ).startingPlotDistanceFactor(pPlot, getID(), iMinRange);
iMinDistanceFactor = std::min(iClosenessFactor, iMinDistanceFactor);
if (iClosenessFactor < 1000)
{
iValue *= 2000 + iClosenessFactor;
iValue /= 3000;
}
}
}
}
if (iMinDistanceFactor > 1000)
{
//give a maximum boost of 25% for somewhat distant locations, don't go overboard.
iMinDistanceFactor = std::min(1500, iMinDistanceFactor);
iValue *= (1000 + iMinDistanceFactor);
iValue /= 2000;
}
else if (iMinDistanceFactor < 1000)
{
//this is too close so penalize again.
iValue *= iMinDistanceFactor;
iValue /= 1000;
iValue *= iMinDistanceFactor;
iValue /= 1000;
}
iValue /= 10;
if (pPlot->getBonusType() != NO_BONUS)
{
iValue /= 2;
}
}
}
if (bAdvancedStart)
{
if (pPlot->getBonusType() != NO_BONUS)
{
iValue *= 70;
iValue /= 100;
}
}
pNearestCity = GC.getMapINLINE().findCity(iX, iY, ((isBarbarian()) ? NO_PLAYER : getID()));
if (pNearestCity != NULL)
{
if (isBarbarian())
{
iValue -= (std::max(0, (8 - plotDistance(iX, iY, pNearestCity->getX_INLINE(), pNearestCity->getY_INLINE()))) * 200);
}
else
{
int iDistance = plotDistance(iX, iY, pNearestCity->getX_INLINE(), pNearestCity->getY_INLINE());
int iNumCities = getNumCities();
if (iDistance > 5)
{
iValue -= (iDistance - 5) * 500;
}
else if (iDistance < 4)
{
iValue -= (4 - iDistance) * 2000;
}
iValue *= (8 + iNumCities * 4);
iValue /= (2 + (iNumCities * 4) + iDistance);
if (pNearestCity->isCapital())
{
iValue *= 150;
iValue /= 100;
}
else if (getCapitalCity() != NULL)
{
//Provide up to a 50% boost to value (80% for adv.start)
//for city sites which are relatively close to the core
//compared with the most distance city from the core
//(having a boost rather than distance penalty avoids some distortion)
//This is not primarly about maitenance but more about empire
//shape as such forbidden palace/state property are not big deal.
CvCity* pLoopCity;
int iLoop;
int iMaxDistanceFromCapital = 0;
int iCapitalX = getCapitalCity()->getX();
int iCapitalY = getCapitalCity()->getY();
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
iMaxDistanceFromCapital = std::max(iMaxDistanceFromCapital, plotDistance(iCapitalX, iCapitalY, pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE()));
}
int iDistanceToCapital = plotDistance(iCapitalX, iCapitalY, iX, iY);
FAssert(iMaxDistanceFromCapital > 0);
iValue *= 100 + (((bAdvancedStart ? 80 : 50) * std::max(0, (iMaxDistanceFromCapital - iDistance))) / iMaxDistanceFromCapital);
iValue /= 100;
}
}
}
else
{
pNearestCity = GC.getMapINLINE().findCity(iX, iY, ((isBarbarian()) ? NO_PLAYER : getID()), ((isBarbarian()) ? NO_TEAM : getTeam()), false);
if (pNearestCity != NULL)
{
int iDistance = plotDistance(iX, iY, pNearestCity->getX_INLINE(), pNearestCity->getY_INLINE());
iValue -= std::min(500 * iDistance, (8000 * iDistance) / GC.getMapINLINE().maxPlotDistance());
}
}
if (iValue <= 0)
{
return 1;
}
if (pArea->getNumCities() == 0)
{
iValue *= 2;
}
else
{
iTeamAreaCities = GET_TEAM(getTeam()).countNumCitiesByArea(pArea);
if (pArea->getNumCities() == iTeamAreaCities)
{
iValue *= 3;
iValue /= 2;
}
else if (pArea->getNumCities() == (iTeamAreaCities + GET_TEAM(BARBARIAN_TEAM).countNumCitiesByArea(pArea)))
{
iValue *= 4;
iValue /= 3;
}
else if (iTeamAreaCities > 0)
{
iValue *= 5;
iValue /= 4;
}
}
if (!bStartingLoc)
{
int iFoodSurplus = std::max(0, iSpecialFoodPlus - iSpecialFoodMinus);
int iFoodDeficit = std::max(0, iSpecialFoodMinus - iSpecialFoodPlus);
iValue *= 100 + 20 * std::max(0, std::min(iFoodSurplus, 2 * GC.getFOOD_CONSUMPTION_PER_POPULATION()));
iValue /= 100 + 20 * std::max(0, iFoodDeficit);
}
if ((!bStartingLoc) && (getNumCities() > 0))
{
int iBonusCount = 0;
int iUniqueBonusCount = 0;
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
iBonusCount += paiBonusCount[iI];
iUniqueBonusCount += (paiBonusCount[iI] > 0) ? 1 : 0;
}
if (iBonusCount > 4)
{
iValue *= 5;
iValue /= (1 + iBonusCount);
}
else if (iUniqueBonusCount > 2)
{
iValue *= 5;
iValue /= (3 + iUniqueBonusCount);
}
}
if (!bStartingLoc)
{
int iDeadLockCount = AI_countDeadlockedBonuses(pPlot);
if (bAdvancedStart && (iDeadLockCount > 0))
{
iDeadLockCount += 2;
}
iValue /= (1 + iDeadLockCount);
}
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
iValue /= (std::max(0, (iBadTile - (NUM_CITY_PLOTS / 4))) + 3);
*/
iValue /= (std::max(0, (iBadTile - (NUM_CITY_PLOTS_2 / 4))) + 3);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (bStartingLoc)
{
iDifferentAreaTile = 0;
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
*/
for (iI = 0; iI < NUM_CITY_PLOTS_2; iI++)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
pLoopPlot = plotCity(iX, iY, iI);
if ((pLoopPlot == NULL) || !(pLoopPlot->isWater() || pLoopPlot->area() == pArea))
{
iDifferentAreaTile++;
}
}
/************************************************************************************************/
/* Afforess Start 06/03/10 */
/* */
/* */
/************************************************************************************************/
/*
iValue /= (std::max(0, (iDifferentAreaTile - ((NUM_CITY_PLOTS * 2) / 3))) + 2);
*/
iValue /= (std::max(0, (iDifferentAreaTile - ((NUM_CITY_PLOTS_2 * 2) / 3))) + 2);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
return std::max(1, iValue);
}
bool CvPlayerAI::AI_isAreaAlone(CvArea* pArea) const
{
return ((pArea->getNumCities() - GET_TEAM(BARBARIAN_TEAM).countNumCitiesByArea(pArea)) == GET_TEAM(getTeam()).countNumCitiesByArea(pArea));
}
bool CvPlayerAI::AI_isCapitalAreaAlone() const
{
CvCity* pCapitalCity;
pCapitalCity = getCapitalCity();
if (pCapitalCity != NULL)
{
return AI_isAreaAlone(pCapitalCity->area());
}
return false;
}
bool CvPlayerAI::AI_isPrimaryArea(CvArea* pArea) const
{
CvCity* pCapitalCity;
if (pArea->isWater())
{
return false;
}
if (pArea->getCitiesPerPlayer(getID()) > 2)
{
return true;
}
pCapitalCity = getCapitalCity();
if (pCapitalCity != NULL)
{
if (pCapitalCity->area() == pArea)
{
return true;
}
}
return false;
}
int CvPlayerAI::AI_militaryWeight(CvArea* pArea) const
{
return (pArea->getPopulationPerPlayer(getID()) + pArea->getCitiesPerPlayer(getID()) + 1);
}
int CvPlayerAI::AI_targetCityValue(CvCity* pCity, bool bRandomize, bool bIgnoreAttackers) const
{
PROFILE_FUNC();
CvCity* pNearestCity;
CvPlot* pLoopPlot;
int iValue;
int iI;
FAssertMsg(pCity != NULL, "City is not assigned a valid value");
iValue = 1;
iValue += ((pCity->getPopulation() * (50 + pCity->calculateCulturePercent(getID()))) / 100);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 06/30/10 Mongoose & jdog5000 */
/* */
/* War strategy AI */
/************************************************************************************************/
// Prefer lower defense
iValue += std::max( 0, (100 - pCity->getDefenseModifier(false))/30 );
if (pCity->getDefenseDamage() > 0)
{
iValue += ((pCity->getDefenseDamage() / 30) + 1);
}
// Significant amounting of borrowing/adapting from Mongoose AITargetCityValueFix
if (pCity->isCoastal(GC.getMIN_WATER_SIZE_FOR_OCEAN()))
{
iValue += 2;
}
iValue += 4*pCity->getNumActiveWorldWonders();
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (pCity->isHolyCity((ReligionTypes)iI))
{
iValue += std::max(2,((GC.getGameINLINE().calculateReligionPercent((ReligionTypes)iI)) / 5));
iValue += ((AI_getFlavorValue(/* AI_FLAVOR_RELIGION */ (FlavorTypes)1) +2) / 6);
if (GET_PLAYER(pCity->getOwnerINLINE()).getStateReligion() == iI)
{
iValue += 2;
}
if (getStateReligion() == iI)
{
iValue += 8;
}
if (GC.getLeaderHeadInfo(getLeaderType()).getFavoriteReligion() == iI)
{
iValue += 1;
}
}
}
if (pCity->isEverOwned(getID()))
{
iValue += 3;
if( pCity->getOriginalOwner() == getID() )
{
iValue += 3;
}
}
if (!bIgnoreAttackers)
{
iValue += std::min( 8, (AI_adjacentPotentialAttackers(pCity->plot()) + 2)/3 );
}
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
{
pLoopPlot = plotCity(pCity->getX_INLINE(), pCity->getY_INLINE(), iI);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getBonusType(getTeam()) != NO_BONUS)
{
iValue += std::min(8,std::max(1, AI_bonusVal(pLoopPlot->getBonusType(getTeam()))/10));
}
if (pLoopPlot->getOwnerINLINE() == getID())
{
iValue++;
}
if (pLoopPlot->isAdjacentPlayer(getID(), true))
{
iValue++;
}
}
}
if( GET_PLAYER(pCity->getOwnerINLINE()).AI_isDoVictoryStrategy(AI_VICTORY_CULTURE3) )
{
if( pCity->getCultureLevel() >= (GC.getGameINLINE().culturalVictoryCultureLevel() - 1) )
{
iValue += 15;
if( GET_PLAYER(pCity->getOwnerINLINE()).AI_isDoVictoryStrategy(AI_VICTORY_CULTURE4) )
{
iValue += 25;
if( pCity->getCultureLevel() >= (GC.getGameINLINE().culturalVictoryCultureLevel()) )
{
iValue += 10;
}
}
}
}
if( GET_PLAYER(pCity->getOwnerINLINE()).AI_isDoVictoryStrategy(AI_VICTORY_SPACE3) )
{
if( pCity->isCapital() )
{
iValue += 10;
if( GET_PLAYER(pCity->getOwnerINLINE()).AI_isDoVictoryStrategy(AI_VICTORY_SPACE4) )
{
iValue += 20;
if( GET_TEAM(pCity->getTeam()).getVictoryCountdown(GC.getGameINLINE().getSpaceVictory()) >= 0 )
{
iValue += 30;
}
}
}
}
pNearestCity = GC.getMapINLINE().findCity(pCity->getX_INLINE(), pCity->getY_INLINE(), getID());
if (pNearestCity != NULL)
{
// Now scales sensibly with map size, on large maps this term was incredibly dominant in magnitude
int iTempValue = 30;
iTempValue *= std::max(1, ((GC.getMapINLINE().maxStepDistance() * 2) - GC.getMapINLINE().calculatePathDistance(pNearestCity->plot(), pCity->plot())));
iTempValue /= std::max(1, (GC.getMapINLINE().maxStepDistance() * 2));
iValue += iTempValue;
}
if (bRandomize)
{
iValue += GC.getGameINLINE().getSorenRandNum(((pCity->getPopulation() / 2) + 1), "AI Target City Value");
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
// Prefer less defended
int iDefense = (static_cast<CvCityAI*>(pCity))->getGarrisonStrength();
iValue = (iValue*100)/(5 + iDefense);
return iValue;
}
CvCity* CvPlayerAI::AI_findTargetCity(CvArea* pArea) const
{
CvCity* pLoopCity;
CvCity* pBestCity;
int iValue;
int iBestValue;
int iLoop;
int iI;
iBestValue = 0;
pBestCity = NULL;
for (iI = 0; iI < MAX_CIV_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (isPotentialEnemy(getTeam(), GET_PLAYER((PlayerTypes)iI).getTeam()))
{
for (pLoopCity = GET_PLAYER((PlayerTypes)iI).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iI).nextCity(&iLoop))
{
if (pLoopCity->area() == pArea)
{
iValue = AI_targetCityValue(pLoopCity, true);
if (iValue > iBestValue)
{
iBestValue = iValue;
pBestCity = pLoopCity;
}
}
}
}
}
}
return pBestCity;
}
bool CvPlayerAI::AI_isCommercePlot(CvPlot* pPlot) const
{
return (pPlot->getYield(YIELD_FOOD) >= GC.getFOOD_CONSUMPTION_PER_POPULATION());
}
bool CvPlayerAI::AI_getVisiblePlotDanger(CvPlot* pPlot, int iRange, bool bAnimalOnly, CvSelectionGroup* group, int acceptableOdds) const
{
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
CvPlot* pLoopPlot;
int iDX, iDY;
CvArea *pPlotArea = pPlot->area();
for (iDX = -(iRange); iDX <= iRange; iDX++)
{
for (iDY = -(iRange); iDY <= iRange; iDY++)
{
pLoopPlot = plotXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->area() == pPlotArea)
{
pUnitNode = pLoopPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
// No need to loop over tiles full of our own units
if( pLoopUnit->getTeam() == getTeam() )
{
if( !(pLoopUnit->alwaysInvisible()) && (pLoopUnit->getInvisibleType() == NO_INVISIBLE) )
{
break;
}
}
if (pLoopUnit->isEnemy(getTeam()) && (!bAnimalOnly || pLoopUnit->isAnimal()))
{
if (pLoopUnit->canAttack())
{
if (!(pLoopUnit->isInvisible(getTeam(), false)))
{
if (pLoopUnit->canMoveOrAttackInto(pPlot))
{
if ( group == NULL || pLoopUnit->getGroup()->AI_attackOdds(pPlot,true,true) > 100 - acceptableOdds )
{
return true;
}
}
}
}
}
}
}
}
}
}
return false;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 08/20/09 jdog5000 */
/* */
/* General AI, Efficiency */
/************************************************************************************************/
// Plot danger cache
// The vast majority of checks for plot danger are boolean checks during path planning for non-combat
// units like workers, settlers, and GP. Since these are simple checks for any danger they can be
// cutoff early if danger is found. To this end, the two caches tracked are for whether a given plot
// is either known to be safe for the player who is currently moving, or for whether the plot is
// known to be a plot bordering an enemy of this team and therefore unsafe.
//
// The safe plot cache is only for the active moving player and is only set if this is not a
// multiplayer game with simultaneous turns. The safety cache for all plots is reset when the active
// player changes or a new game is loaded.
//
// The border cache is done by team and works for all game types. The border cache is reset for all
// plots when war or peace are declared, and reset over a limited range whenever a ownership over a plot
// changes.
bool CvPlayerAI::AI_getAnyPlotDanger(CvPlot* pPlot, int iRange, bool bTestMoves) const
{
bool bResult = false;
PROFILE_FUNC();
if (iRange == -1)
{
iRange = DANGER_RANGE;
}
if( bTestMoves && isTurnActive() )
{
PROFILE("CvPlayerAI::AI_getAnyPlotDanger.ActiveTurn");
if( (iRange <= DANGER_RANGE) && pPlot->isActivePlayerNoDangerCache() )
{
PROFILE("CvPlayerAI::AI_getAnyPlotDanger.NoDangerHit");
return false;
}
else if (iRange >= DANGER_RANGE && pPlot->isActivePlayerHasDangerCache() )
{
PROFILE("CvPlayerAI::AI_getAnyPlotDanger.HasDangerHit");
return true;
}
}
TeamTypes eTeam = getTeam();
bool bCheckBorder = (!isHuman() && !pPlot->isCity());
if( bCheckBorder )
{
if( (iRange >= DANGER_RANGE) && pPlot->isTeamBorderCache(eTeam) )
{
bResult = true;
}
}
// If we have plot danger count here over the same threhold that workers use
// to require escorts then consider that dangerous for the AI
if ( !bResult && !isHuman() && pPlot->getDangerCount(m_eID) > 20 )
{
bResult = true;
}
if ( !bResult )
{
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
CvPlot* pLoopPlot;
int iDistance;
int iDX, iDY;
CvArea *pPlotArea = pPlot->area();
int iDangerRange;
for (iDX = -(iRange); iDX <= iRange && !bResult; iDX++)
{
for (iDY = -(iRange); iDY <= iRange && !bResult; iDY++)
{
pLoopPlot = plotXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->area() == pPlotArea)
{
iDistance = stepDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE());
if( bCheckBorder )
{
if (atWar(pLoopPlot->getTeam(), eTeam))
{
// Border cache is reversible, set for both team and enemy
if (iDistance == 1)
{
pPlot->setIsTeamBorderCache(eTeam, true);
pPlot->setIsTeamBorderCache(pLoopPlot->getTeam(), true);
pLoopPlot->setIsTeamBorderCache(eTeam, true);
pLoopPlot->setIsTeamBorderCache(pLoopPlot->getTeam(), true);
bResult = true;
break;
}
else if ((iDistance == 2) && (pLoopPlot->isRoute()))
{
pPlot->setIsTeamBorderCache(eTeam, true);
pPlot->setIsTeamBorderCache(pLoopPlot->getTeam(), true);
pLoopPlot->setIsTeamBorderCache(eTeam, true);
pLoopPlot->setIsTeamBorderCache(pLoopPlot->getTeam(), true);
bResult = true;
break;
}
}
}
pUnitNode = pLoopPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
// No need to loop over tiles full of our own units
if( pLoopUnit->getTeam() == eTeam )
{
if( !(pLoopUnit->alwaysInvisible()) && (pLoopUnit->getInvisibleType() == NO_INVISIBLE) )
{
break;
}
}
if (pLoopUnit->isEnemy(eTeam))
{
if (pLoopUnit->canAttack())
{
if (!(pLoopUnit->isInvisible(eTeam, false)))
{
if (pLoopUnit->canMoveOrAttackInto(pPlot))
{
if (!bTestMoves)
{
bResult = true;
break;
}
else
{
iDangerRange = pLoopUnit->baseMoves();
iDangerRange += ((pLoopPlot->isValidRoute(pLoopUnit)) ? 1 : 0);
if (iDangerRange >= iDistance)
{
bResult = true;
break;
}
}
}
}
}
}
}
}
}
}
}
}
// The test moves case is a strict subset of the more general case,
// either is appropriate for setting the cache. However, since the test moves
// case is called far more frequently, it is more important and the cache
// value being true is only assumed to mean that the plot is safe in the
// test moves case.
//if( bTestMoves )
{
if( isTurnActive() )
{
if( !(GC.getGameINLINE().isMPOption(MPOPTION_SIMULTANEOUS_TURNS)) && (GC.getGameINLINE().getNumGameTurnActive() == 1) )
{
if ( bResult )
{
if (iRange <= DANGER_RANGE)
{
pPlot->setIsActivePlayerHasDangerCache(true);
}
}
else if (iRange >= DANGER_RANGE)
{
pPlot->setIsActivePlayerNoDangerCache(true);
}
}
}
}
return bResult;
}
#ifdef PLOT_DANGER_CACHING
plotDangerCache CvPlayerAI::plotDangerCache;
int CvPlayerAI::plotDangerCacheHits = 0;
int CvPlayerAI::plotDangerCacheReads = 0;
#endif
int CvPlayerAI::AI_getPlotDanger(CvPlot* pPlot, int iRange, bool bTestMoves) const
{
PROFILE_FUNC();
if (iRange == -1)
{
iRange = DANGER_RANGE;
}
if( bTestMoves && isTurnActive() )
{
if( (iRange <= DANGER_RANGE) && pPlot->isActivePlayerNoDangerCache() )
{
return 0;
}
}
#ifdef PLOT_DANGER_CACHING
#ifdef _DEBUG
// Uncomment this to perform functional verification
//#define VERIFY_PLOT_DANGER_CACHE_RESULTS
#endif
// Check cache first
int worstLRU = 0x7FFFFFFF;
struct plotDangerCacheEntry* worstLRUEntry = NULL;
plotDangerCacheReads++;
//OutputDebugString(CvString::format("AI_yieldValue (%d,%d,%d) at seq %d\n", piYields[0], piYields[1], piYields[2], yieldValueCacheReads).c_str());
//PROFILE_STACK_DUMP
for(int i = 0; i < PLOT_DANGER_CACHE_SIZE; i++)
{
struct plotDangerCacheEntry* entry = &plotDangerCache.entries[i];
if ( entry->iLastUseCount == 0 )
{
worstLRUEntry = entry;
break;
}
if ( pPlot->getX_INLINE() == entry->plotX &&
pPlot->getY_INLINE() == entry->plotY &&
iRange == entry->iRange &&
bTestMoves == entry->bTestMoves )
{
entry->iLastUseCount = ++plotDangerCache.currentUseCounter;
plotDangerCacheHits++;
#ifdef VERIFY_PLOT_DANGER_CACHE_RESULTS
int realValue = AI_getPlotDangerInternal(pPlot, iRange, bTestMoves);
if ( realValue != entry->iResult )
{
FAssertMsg(false, "Plot danger cache verification failure");
}
#endif
return entry->iResult;
}
else if ( entry->iLastUseCount < worstLRU )
{
worstLRU = entry->iLastUseCount;
worstLRUEntry = entry;
}
}
worstLRUEntry->plotX = pPlot->getX_INLINE();
worstLRUEntry->plotY = pPlot->getY_INLINE();
worstLRUEntry->iRange = iRange;
worstLRUEntry->bTestMoves = bTestMoves;
worstLRUEntry->iLastUseCount = ++plotDangerCache.currentUseCounter;
worstLRUEntry->iResult = AI_getPlotDangerInternal(pPlot, iRange, bTestMoves);
return worstLRUEntry->iResult;
#else
return AI_getPlotDangerInternal(pPlot, iRange, bTestMoves);
#endif
}
int CvPlayerAI::AI_getPlotDangerInternal(CvPlot* pPlot, int iRange, bool bTestMoves) const
{
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
CvPlot* pLoopPlot;
int iCount;
int iDistance;
int iBorderDanger;
int iDX, iDY;
CvArea *pPlotArea = pPlot->area();
int iDangerRange;
TeamTypes eTeam = getTeam();
iCount = 0;
iBorderDanger = 0;
OutputDebugString(CvString::format("AI_getPlotDanger for (%d,%d) at range %d (bTestMoves=%d)\n",
pPlot->getX_INLINE(), pPlot->getY_INLINE(),
iRange,
bTestMoves).c_str());
for (iDX = -(iRange); iDX <= iRange; iDX++)
{
for (iDY = -(iRange); iDY <= iRange; iDY++)
{
pLoopPlot = plotXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->area() == pPlotArea)
{
iDistance = stepDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE());
if (atWar(pLoopPlot->getTeam(), eTeam))
{
if (iDistance == 1)
{
iBorderDanger++;
}
else if ((iDistance == 2) && (pLoopPlot->isRoute()))
{
iBorderDanger++;
}
}
pUnitNode = pLoopPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
// No need to loop over tiles full of our own units
if( pLoopUnit->getTeam() == eTeam )
{
if( !(pLoopUnit->alwaysInvisible()) && (pLoopUnit->getInvisibleType() == NO_INVISIBLE) )
{
break;
}
}
if (pLoopUnit->isEnemy(eTeam))
{
if (pLoopUnit->canAttack() || pLoopUnit->plot() == pPlot)
{
if (!(pLoopUnit->isInvisible(eTeam, false)))
{
if (pLoopUnit->canMoveOrAttackInto(pPlot) || pLoopUnit->plot() == pPlot)
{
if (!bTestMoves)
{
iCount++;
}
else
{
iDangerRange = pLoopUnit->baseMoves();
iDangerRange += ((pLoopPlot->isValidRoute(pLoopUnit)) ? 1 : 0);
if (iDangerRange >= iDistance)
{
iCount++;
}
}
}
}
}
}
}
}
}
}
}
if (iBorderDanger > 0)
{
if (!isHuman() && !pPlot->isCity())
{
iCount += iBorderDanger;
}
}
return iCount;
}
// Never used ...
/*
int CvPlayerAI::AI_getUnitDanger(CvUnit* pUnit, int iRange, bool bTestMoves, bool bAnyDanger) const
{
PROFILE_FUNC();
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
CvPlot* pLoopPlot;
int iCount;
int iDistance;
int iBorderDanger;
int iDX, iDY;
CvPlot* pPlot = pUnit->plot();
iCount = 0;
iBorderDanger = 0;
if (iRange == -1)
{
iRange = DANGER_RANGE;
}
for (iDX = -(iRange); iDX <= iRange; iDX++)
{
for (iDY = -(iRange); iDY <= iRange; iDY++)
{
pLoopPlot = plotXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->area() == pPlot->area())
{
iDistance = stepDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE());
if (atWar(pLoopPlot->getTeam(), getTeam()))
{
if (iDistance == 1)
{
iBorderDanger++;
}
else if ((iDistance == 2) && (pLoopPlot->isRoute()))
{
iBorderDanger++;
}
}
pUnitNode = pLoopPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
if (atWar(pLoopUnit->getTeam(), getTeam()))
{
if (pLoopUnit->canAttack())
{
if (!(pLoopUnit->isInvisible(getTeam(), false)))
{
if (pLoopUnit->canMoveOrAttackInto(pPlot))
{
if (!bTestMoves)
{
iCount++;
}
else
{
int iDangerRange = pLoopUnit->baseMoves();
iDangerRange += ((pLoopPlot->isValidRoute(pLoopUnit)) ? 1 : 0);
if (iDangerRange >= iDistance)
{
iCount++;
}
}
}
}
}
}
}
}
}
}
}
if (iBorderDanger > 0)
{
if (!isHuman() || pUnit->isAutomated())
{
iCount += iBorderDanger;
}
}
return iCount;
}
*/
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
int CvPlayerAI::AI_countNumLocalNavy(CvPlot* pPlot, int iRange) const
{
PROFILE_FUNC();
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
CvPlot* pLoopPlot;
int iCount;
int iDX, iDY;
iCount = 0;
if (iRange == -1)
{
iRange = DANGER_RANGE;
}
for (iDX = -(iRange); iDX <= iRange; iDX++)
{
for (iDY = -(iRange); iDY <= iRange; iDY++)
{
pLoopPlot = plotXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->isWater() || pLoopPlot->getPlotCity() != NULL)
{
if (pPlot->area() == pLoopPlot->area() || pPlot->isAdjacentToArea(pLoopPlot->getArea()))
{
pUnitNode = pLoopPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
UnitAITypes aiType = pLoopUnit->AI_getUnitAIType();
if ( aiType == UNITAI_ATTACK_SEA || aiType == UNITAI_PIRATE_SEA || aiType == UNITAI_RESERVE_SEA )
{
if (pLoopUnit->getTeam() == getTeam())
{
iCount++;
}
}
}
}
}
}
}
}
return iCount;
}
int CvPlayerAI::AI_getWaterDanger(CvPlot* pPlot, int iRange, bool bTestMoves) const
{
PROFILE_FUNC();
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
CvPlot* pLoopPlot;
int iCount;
int iDX, iDY;
iCount = 0;
if (iRange == -1)
{
iRange = DANGER_RANGE;
}
CvArea* pWaterArea = pPlot->waterArea();
for (iDX = -(iRange); iDX <= iRange; iDX++)
{
for (iDY = -(iRange); iDY <= iRange; iDY++)
{
pLoopPlot = plotXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->isWater())
{
if (pPlot->isAdjacentToArea(pLoopPlot->getArea()))
{
pUnitNode = pLoopPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->isEnemy(getTeam()))
{
if (pLoopUnit->canAttack())
{
if (!(pLoopUnit->isInvisible(getTeam(), false)))
{
iCount++;
}
}
}
}
}
}
}
}
}
return iCount;
}
bool CvPlayerAI::AI_avoidScience() const
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/08/10 jdog5000 */
/* */
/* Victory Strategy AI */
/************************************************************************************************/
if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE4))
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
return true;
}
if (isCurrentResearchRepeat())
{
return true;
}
if (isNoResearchAvailable())
{
return true;
}
return false;
}
int CvPlayerAI::AI_safeCostAsPercentIncome() const
{
int iSafePercent = 50;
if (!GC.getGameINLINE().isOption(GAMEOPTION_NO_REVOLUTION))
{
// Afforess - these calculations mimic the Revolution.py assessment for the revolutions mod (check Revolution.py ~ line 1870)
// Higher safe percents mean AI has to earn more to be considered "safe"
int iRank = GC.getGameINLINE().getPlayerRank(getID());
if (iRank < 3)
{
iSafePercent += ((4 - iRank) * 5);
}
int iAtWarCount = GET_TEAM(getTeam()).getAtWarCount(true);
if (iAtWarCount > 0)
{
iSafePercent -= (10 + 2 * std::min(iAtWarCount, 5));
}
if (isCurrentResearchRepeat())
{
iSafePercent *= 2;
iSafePercent /= 3;
}
if (getCommercePercent(COMMERCE_CULTURE) > 70)
{
iSafePercent *= 2;
iSafePercent /= 3;
}
}
else
{
int iWarSuccessRatio = GET_TEAM(getTeam()).AI_getWarSuccessCapitulationRatio();
if (iWarSuccessRatio < -30)
{
iSafePercent -= std::max(20, iWarSuccessRatio / 3);
}
if (AI_avoidScience())
{
iSafePercent -= 8;
}
// Afforess - AI has been made aware of financials in war planning, this is not needed here
// if (GET_TEAM(getTeam()).getAnyWarPlanCount(true))
// {
// iSafePercent -= 12;
// }
if (isCurrentResearchRepeat())
{
iSafePercent -= 10;
}
}
return iSafePercent;
}
int CvPlayerAI::AI_costAsPercentIncome(int iExtraCost) const
{
PROFILE_FUNC();
int iTotalCommerce = calculateTotalYield(YIELD_COMMERCE);
int iBaseNetCommerce = 1 + getCommerceRate(COMMERCE_GOLD) + std::max(0, getGoldPerTurn());
int iNetCommerce = iBaseNetCommerce + ((100-getCommercePercent(COMMERCE_GOLD))*iTotalCommerce)/100;
/************************************************************************************************/
/* UNOFFICIAL_PATCH 06/11/09 jdog5000 & DanF5771 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original BTS code
int iNetExpenses = calculateInflatedCosts() + std::min(0, getGoldPerTurn());
*/
int iNetExpenses = calculateInflatedCosts() + std::max(0, -getGoldPerTurn());
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
// Afforess - iExtraCost lets us "extrapolate" our cost percents if we have extra future expenses
// iExtraCost should be between 0 (default) and some positive extra gold per turn cost to us
iNetExpenses += std::max(0, iExtraCost);
// Koshling - if we can fund our ongoing expenses with no tax we never consider ourselves to be
// in financial difficulties
if ( iBaseNetCommerce - (getCommercePercent(COMMERCE_GOLD)*iTotalCommerce)/100 > iNetExpenses )
{
return 100;
}
// Koshling - we're never in financial trouble if we can run at current deficits for more than
// 50 turns and stay in healthy territory (100 + 100*era as per REV calculation), so claim full
// funding or even excess funding in such a case!
int iEraGoldThreshold = 100 + 100*GC.getGameINLINE().getCurrentEra();
if ( getGold() > iEraGoldThreshold &&
(AI_avoidScience() || getCommercePercent(COMMERCE_RESEARCH) > 50) && // If we're forcing science below 50 to achieve this don't exempt it
((iNetCommerce - iNetExpenses) >= 0 || getGold() + 50*(iNetCommerce - iNetExpenses) > iEraGoldThreshold) )
{
int iValue = 100 + (getGold()/iEraGoldThreshold);
if ( iNetCommerce < iNetExpenses )
{
// Each 10 turns we can fund entirely out of treasury, even with the deficit, add 1%
int iFundableTurns = (getGold()-iEraGoldThreshold)/(iNetExpenses - iNetCommerce);
// Turns under 100 fundable slightly reduce over funding reported
if ( iFundableTurns < 100 )
{
iValue -= (100 - iFundableTurns);
if ( iValue < 100 )
{
// We know we are still at least fully funded - its only over-funding we're reducing
iValue = 100;
}
}
}
return iValue;
}
else
{
return (100 * (iNetCommerce - iNetExpenses)) / std::max(1, iNetCommerce);
}
}
// Calculate a (percentage) modifier the AI can apply to gold to determine
// how to value it
int CvPlayerAI::AI_goldValueAssessmentModifier() const
{
int iFundedPercent = AI_costAsPercentIncome();
int iSafePercent = AI_safeCostAsPercentIncome();
// Normalize funded percent by safe percent
int iModifier = (100*iSafePercent)/std::max(1,iFundedPercent);
// If we're only just funding at the safety level that's not good - rate that as 150% valuation for gold
return (iModifier*3)/2;
}
// XXX
bool CvPlayerAI::AI_isFinancialTrouble() const
{
PROFILE_FUNC();
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 06/12/09 jdog5000 */
/* */
/* Barbarian AI */
/************************************************************************************************/
if( isBarbarian() )
{
return false;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
//if (getCommercePercent(COMMERCE_GOLD) > 50)
{
int iFundedPercent = AI_costAsPercentIncome();
int iSafePercent = AI_safeCostAsPercentIncome();
if (iFundedPercent < iSafePercent)
{
return true;
}
}
return false;
}
int CvPlayerAI::AI_goldTarget() const
{
int iGold = 0;
/************************************************************************************************/
/* UNOFFICIAL_PATCH 02/24/10 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original bts code
if (GC.getGameINLINE().getElapsedGameTurns() >= 40)
*/
if (GC.getGameINLINE().getElapsedGameTurns() >= 40 || getNumCities() > 3)
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
{
int iMultiplier = 0;
iMultiplier += GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getResearchPercent();
iMultiplier += GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getTrainPercent();
iMultiplier += GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getConstructPercent();
iMultiplier /= 3;
iGold += ((getNumCities() * 3) + (getTotalPopulation() / 3));
/************************************************************************************************/
/* Fuyu 16.07.2010 */
/************************************************************************************************/
/* original bts code
iGold += (GC.getGameINLINE().getElapsedGameTurns() / 2);
iGold *= iMultiplier;
iGold /= 100;
*/
iGold *= iMultiplier;
iGold /= 100;
iGold += ( (iMultiplier * GC.getGameINLINE().getElapsedGameTurns()) / (((GC.getGameINLINE().isOption(GAMEOPTION_NO_EVENTS))? 10 : 6) * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getVictoryDelayPercent()) );
/************************************************************************************************/
/* Fuyu END */
/************************************************************************************************/
/************************************************************************************************/
/* Afforess Start 02/01/10 */
/************************************************************************************************/
iGold *= (100 + calculateInflationRate());
iGold /= 100;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
bool bAnyWar = GET_TEAM(getTeam()).getAnyWarPlanCount(true) > 0;
if (bAnyWar)
{
iGold *= 3;
iGold /= 2;
}
/************************************************************************************************/
/* Afforess Start 02/01/10 */
/* */
/* Don't bother saving gold if we can't trade it for anything */
/************************************************************************************************/
if (!GET_TEAM(getTeam()).isGoldTrading() || !(GET_TEAM(getTeam()).isTechTrading()) || (GC.getGameINLINE().isOption(GAMEOPTION_NO_TECH_TRADING)))
{
iGold /= 3;
}
//Fuyu: Afforess says gold is also less useful without tech brokering, so why not add it
else if (GC.getGameINLINE().isOption(GAMEOPTION_NO_TECH_BROKERING))
{
iGold *= 3;
iGold /= 4;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (AI_avoidScience())
{
iGold *= 10;
}
iGold += (AI_goldToUpgradeAllUnits() / (bAnyWar ? 1 : 2));
CorporationTypes eActiveCorporation = NO_CORPORATION;
for (int iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
if (getHasCorporationCount((CorporationTypes)iI) > 0)
{
eActiveCorporation = (CorporationTypes)iI;
break;
}
}
if (eActiveCorporation != NO_CORPORATION)
{
int iSpreadCost = std::max(0, GC.getCorporationInfo(eActiveCorporation).getSpreadCost() * (100 + calculateInflationRate()));
iSpreadCost /= 50;
iGold += iSpreadCost;
}
}
return iGold + AI_getExtraGoldTarget();
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 05/14/10 jdog5000 */
/* */
/* Tech AI */
/************************************************************************************************/
TechTypes CvPlayerAI::AI_bestTech(int iMaxPathLength, bool bIgnoreCost, bool bAsync, TechTypes eIgnoreTech, AdvisorTypes eIgnoreAdvisor) const
{
MEMORY_TRACK()
PROFILE("CvPlayerAI::AI_bestTech");
// Don't bother with expensive evaluations if we cannot do any research anyway (barbarians
// befoer the first barb city for example)
if (!isResearch() && getAdvancedStartPoints() < 0)
{
return NO_TECH;
}
logBBAI("Begin best tech evaluation..." );
int iValue;
int iBestValue = 0;
TechTypes eBestTech = NO_TECH;
TechTypes eFirstTech = NO_TECH;
int iPathLength;
CvTeam& kTeam = GET_TEAM(getTeam());
/************************************************************************************************/
/* Afforess Start 08/09/10 */
/* */
/* Forces AI to Beeline for Religious Techs if they have no religions */
/************************************************************************************************/
bool bValid = GC.getGameINLINE().isOption(GAMEOPTION_RUTHLESS_AI);
if (!bValid)
{
for (int iI = 0; iI < GC.getNumTraitInfos(); iI++)
{
if (hasTrait((TraitTypes)iI))
{
if (GC.getTraitInfo((TraitTypes)iI).getMaxAnarchy() >= 0)
{
bValid = true;
break;
}
}
}
}
if (bValid)
{
if (getCommercePercent(COMMERCE_RESEARCH) < 90)
{
bValid = false;
}
if (countHolyCities() > 0 && (!GC.getGameINLINE().isOption(GAMEOPTION_NO_REVOLUTION) || GC.getGameINLINE().isOption(GAMEOPTION_LIMITED_RELIGIONS)))
{
bValid = false;
}
if (GET_TEAM(getTeam()).getAnyWarPlanCount(true) == 0)
{
bValid = false;
}
if (getNumCities() == 1)
{
bValid = false;
}
}
if (bValid)
{
eBestTech = AI_bestReligiousTech(iMaxPathLength * 3, eIgnoreTech, eIgnoreAdvisor);
if (eBestTech != NO_TECH)
{
// Don't retain the beeline persistently since we need to re-evaluate
// each turn in case someone has beaten us to it for religions
return eBestTech;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
int* paiBonusClassRevealed;
int* paiBonusClassUnrevealed;
int* paiBonusClassHave;
paiBonusClassRevealed = new int[GC.getNumBonusClassInfos()];
paiBonusClassUnrevealed = new int[GC.getNumBonusClassInfos()];
paiBonusClassHave = new int[GC.getNumBonusClassInfos()];
for (int iI = 0; iI < GC.getNumBonusClassInfos(); iI++)
{
paiBonusClassRevealed[iI] = 0;
paiBonusClassUnrevealed[iI] = 0;
paiBonusClassHave[iI] = 0;
}
for (int iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
TechTypes eRevealTech = (TechTypes)GC.getBonusInfo((BonusTypes)iI).getTechReveal();
BonusClassTypes eBonusClass = (BonusClassTypes)GC.getBonusInfo((BonusTypes)iI).getBonusClassType();
if (eRevealTech != NO_TECH)
{
if ((kTeam.isHasTech(eRevealTech)))
{
paiBonusClassRevealed[eBonusClass]++;
}
else
{
paiBonusClassUnrevealed[eBonusClass]++;
}
if (getNumAvailableBonuses((BonusTypes)iI) > 0)
{
paiBonusClassHave[eBonusClass]++;
}
else if (countOwnedBonuses((BonusTypes)iI) > 0)
{
paiBonusClassHave[eBonusClass]++;
}
}
}
// If we had already decided to beeline previously, stick with it
if ( m_eBestResearchTarget != NO_TECH && iMaxPathLength > 1 )
{
if (canEverResearch(m_eBestResearchTarget) && !kTeam.isHasTech(m_eBestResearchTarget))
{
techPath* path = findBestPath(m_eBestResearchTarget, iValue, bIgnoreCost, bAsync, paiBonusClassRevealed, paiBonusClassUnrevealed, paiBonusClassHave);
eFirstTech = findStartTech(path);
if( gPlayerLogLevel >= 1)
{
if (eFirstTech != NO_TECH)
{
logBBAI(" Player %d (%S) continues toward tech %S, start tech %S", getID(), getCivilizationDescription(0), GC.getTechInfo(m_eBestResearchTarget).getDescription(), GC.getTechInfo(eFirstTech).getDescription() );
}
else
{
logBBAI(" Player %d (%S) continues toward tech %S, start tech NONE", getID(), getCivilizationDescription(0), GC.getTechInfo(m_eBestResearchTarget).getDescription());
}
}
return eFirstTech;
}
}
#ifdef DEBUG_TECH_CHOICES
CvWString szPlayerName = getName();
DEBUGLOG("AI_bestTech:%S\n", szPlayerName.GetCString());
#endif
bool beeLine = false;
int beeLineThreshold;
do
{
for (int iI = 0; iI < GC.getNumTechInfos(); iI++)
{
if ((eIgnoreTech == NO_TECH) || (iI != eIgnoreTech))
{
if ((eIgnoreAdvisor == NO_ADVISOR) || (GC.getTechInfo((TechTypes)iI).getAdvisorType() != eIgnoreAdvisor))
{
if (canEverResearch((TechTypes)iI))
{
if (!(kTeam.isHasTech((TechTypes)iI)))
{
if (GC.getTechInfo((TechTypes)iI).getEra() <= (getCurrentEra() + 1))
{
iPathLength = findPathLength(((TechTypes)iI), false);
bool bValid;
if ( beeLine )
{
if ( iPathLength > iMaxPathLength && iPathLength <= iMaxPathLength*7 )
{
int iTempValue = AI_TechValueCached((TechTypes)iI, bAsync, paiBonusClassRevealed, paiBonusClassUnrevealed, paiBonusClassHave);
bValid = ((iTempValue*100)/GC.getTechInfo((TechTypes)iI).getResearchCost() > iBestValue);
if ( bValid )
{
logBBAI(" Beelining worth examining tech %S (val %d)", GC.getTechInfo((TechTypes)iI).getDescription(), (iTempValue*100)/GC.getTechInfo((TechTypes)iI).getResearchCost() );
}
else
{
logBBAI(" Beelining rejects examination of tech %S (val %d)", GC.getTechInfo((TechTypes)iI).getDescription(),(iTempValue*100)/GC.getTechInfo((TechTypes)iI).getResearchCost() );
}
}
else
{
bValid = false;
}
}
else
{
bValid = (iPathLength <= iMaxPathLength);
}
if ( bValid )
{
MEMORY_TRACK_NAME("CvPlayerAI::AI_bestTech.bestPath");
techPath* path = findBestPath((TechTypes)iI, iValue, bIgnoreCost, bAsync, paiBonusClassRevealed, paiBonusClassUnrevealed, paiBonusClassHave);
//iValue = AI_techValue( (TechTypes)iI, iPathLength, bIgnoreCost, bAsync, paiBonusClassRevealed, paiBonusClassUnrevealed, paiBonusClassHave );
/*if( gPlayerLogLevel >= 3 )
{
logBBAI(" Player %d (%S) consider tech %S with value %d", getID(), getCivilizationDescription(0), GC.getTechInfo((TechTypes)iI).getDescription(), iValue );
}*/
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestTech = ((TechTypes)iI);
eFirstTech = findStartTech(path);
}
delete path;
}
}
}
}
}
}
}
// Don't beeline for async human advisor calls to this method and especially don't get a synced rand to decide if you want to beeline
if (bAsync)
break;
// Most of the time check for good bee-lines. The probability is so high because the AI will
// re-evaluate every time it earns a tech so it will re-decide to beeline (or not) each time
// Barbarians are stupid and never beeline
if ( isBarbarian() || iMaxPathLength == 1 || GC.getGame().getSorenRandNum(8, "AI tech beeline") == 0 )
{
break;
}
if ( !beeLine && eBestTech != NO_TECH) //Afforess check against NO_TECH (or else CTD...)
{
logBBAI(" Non-beeline tech choice is %S with value/cost %d - checking beeline possibilities...", GC.getTechInfo(eBestTech).getDescription(), iBestValue );
}
beeLineThreshold = iBestValue;
beeLine = !beeLine;
} while( beeLine );
if( gPlayerLogLevel >= 1 && eBestTech != NO_TECH && eFirstTech != NO_TECH)
{
logBBAI(" Player %d (%S) selects tech %S with value/cost %d, start tech %S", getID(), getCivilizationDescription(0), GC.getTechInfo(eBestTech).getDescription(), iBestValue, GC.getTechInfo(eFirstTech).getDescription() );
}
if ( iMaxPathLength > 1 )
{
// Only cache tragets generated from non-immediate best-tech searches
m_eBestResearchTarget = eBestTech;
}
SAFE_DELETE_ARRAY(paiBonusClassRevealed);
SAFE_DELETE_ARRAY(paiBonusClassUnrevealed);
SAFE_DELETE_ARRAY(paiBonusClassHave);
return eFirstTech;
}
// Calculate an estimate of the value of the average tech amongst those we could currently reserach
// for perofamnce reasons we just sample rather than measuring all possibilities
int CvPlayerAI::AI_averageCurrentTechValue(TechTypes eRelativeTo, bool bAsync, int* paiBonusClassRevealed, int* paiBonusClassUnrevealed, int* paiBonusClassHave) const
{
int iTotal = 0;
int iCount = 0;
int iDivisor = 1;
#define MAX_SAMPLE_SIZE 4
TechTypes sampleTech[MAX_SAMPLE_SIZE];
int iCost = GC.getTechInfo(eRelativeTo).getResearchCost();
// Determine the sample to use - we use the reserachable techs closest in base cost to the one
// we are seeking to compare with the average
for(int iI = 0; iCount < MAX_SAMPLE_SIZE && iI < GC.getNumTechInfos(); iI++)
{
if ( iI != eRelativeTo && canResearch((TechTypes)iI) )
{
int iThisCost = GC.getTechInfo((TechTypes)iI).getResearchCost();
if ( iCount < MAX_SAMPLE_SIZE )
{
sampleTech[iCount] = (TechTypes)iI;
iCount++;
}
else
{
int iWorstDistance = 0;
int iWorstIndex = -1;
for(int iJ = 0; iJ < iCount; iJ++)
{
int iCurrentCost = GC.getTechInfo(sampleTech[iJ]).getResearchCost();
int iDistance = std::abs(iCurrentCost - iCost);
if ( iDistance > iWorstDistance )
{
iWorstDistance = iDistance;
iWorstIndex = iJ;
}
}
if ( iWorstIndex != -1 && std::abs(iThisCost - iCost) < iWorstDistance )
{
sampleTech[iWorstIndex] = (TechTypes)iI;
}
}
}
}
for(int iI = 0; iI < iCount; iI++)
{
int iValue = AI_TechValueCached(sampleTech[iI], bAsync, paiBonusClassRevealed, paiBonusClassUnrevealed, paiBonusClassHave);
while ( MAX_INT - iTotal < iValue/iDivisor )
{
iTotal /= 2;
iDivisor *= 2;
}
iTotal += iValue/iDivisor;
}
return (iCount == 0 ? AI_TechValueCached(eRelativeTo, bAsync, paiBonusClassRevealed, paiBonusClassUnrevealed, paiBonusClassHave) : (iTotal/iCount)*iDivisor);
}
int CvPlayerAI::AI_TechValueCached(TechTypes eTech, bool bAsync, int* paiBonusClassRevealed, int* paiBonusClassUnrevealed, int* paiBonusClassHave, bool considerFollowOns) const
{
int iValue;
PROFILE_FUNC();
MEMORY_TRACK()
std::map<TechTypes,int>::const_iterator techValueItr = m_cachedTechValues.find(eTech);
if ( techValueItr == m_cachedTechValues.end() )
{
PROFILE("CvPlayerAI::AI_TechValueCached.CacheMiss");
if ( gPlayerLogLevel > 2 )
{
logBBAI("Player %d (%S) calculate value for tech %S (cache miss)",
getID(),
getCivilizationDescription(0),
GC.getTechInfo(eTech).getDescription());
}
iValue = AI_techValue(eTech, findPathLength(eTech, false), true, bAsync, paiBonusClassRevealed, paiBonusClassUnrevealed, paiBonusClassHave);
if (!bAsync)
{
MEMORY_TRACK_EXEMPT()
m_cachedTechValues[eTech] = iValue;
}
}
else
{
iValue = m_cachedTechValues[eTech];
}
if ( considerFollowOns )
{
int iTotalWeight = 100;
// What does it (immediately) lead to?
for(int iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
{
bool bIsORPreReq = false;
bool bIsANDPreReq = false;
int iANDPrereqs = 0;
int iK;
for (iK = 0; iK < GC.getNUM_OR_TECH_PREREQS(); iK++)
{
TechTypes ePrereq = (TechTypes)GC.getTechInfo((TechTypes)iJ).getPrereqOrTechs(iK);
if (ePrereq != NO_TECH)
{
// If we've already got an OR pre-req another makes no difference
if ( GET_TEAM(getTeam()).isHasTech(ePrereq) )
{
bIsORPreReq = false;
break;
}
else if (ePrereq == eTech)
{
bIsORPreReq = true;
}
}
}
for (iK = 0; iK < GC.getNUM_AND_TECH_PREREQS(); iK++)
{
TechTypes ePrereq = (TechTypes)GC.getTechInfo((TechTypes)iJ).getPrereqAndTechs(iK);
if (ePrereq != NO_TECH && !GET_TEAM(getTeam()).isHasTech(ePrereq))
{
iANDPrereqs++;
if (ePrereq == eTech)
{
bIsANDPreReq = true;
}
}
}
if ( bIsORPreReq || bIsANDPreReq )
{
// Consider all the AND pre-reqs as worth 33% of the follow on,
// and any OR one as 25%
int iANDPercentage = (bIsANDPreReq ? 33/iANDPrereqs : 0);
int iORPercentage = (bIsORPreReq ? 25 : 0);
iTotalWeight += iANDPercentage + iORPercentage;
iValue += ((iANDPercentage + iORPercentage)*AI_TechValueCached((TechTypes)iJ, bAsync, paiBonusClassRevealed, paiBonusClassUnrevealed, paiBonusClassHave))/100;
}
}
// Normalize to an average to make it comparable with a tech evaluated without follow-ons
while(iValue > MAX_INT/100)
{
iValue /= 2;
iTotalWeight /= 2;
if ( iTotalWeight == 0 )
{
iTotalWeight = 1;
}
}
iValue = (iValue*100)/iTotalWeight;
}
return iValue;
}
int CvPlayerAI::techPathValuePerUnitCost(techPath* path, TechTypes eTech, bool bIgnoreCost, bool bAsync, int* paiBonusClassRevealed, int* paiBonusClassUnrevealed, int* paiBonusClassHave) const
{
int iCost = 0;
int iValue = 0;
int iScaleFactor = 1;
logBBAI(" Evaluate tech path value:" );
for(std::vector<TechTypes>::const_iterator itr = path->begin(); itr != path->end(); ++itr)
{
CvTechInfo& techInfo = GC.getTechInfo(*itr);
int iTempCost = std::max(1,GET_TEAM(getTeam()).getResearchCost(eTech) - GET_TEAM(getTeam()).getResearchProgress(eTech));
int iTempValue = AI_TechValueCached(*itr, bAsync, paiBonusClassRevealed, paiBonusClassUnrevealed, paiBonusClassHave);
logBBAI(" tech %S: cost %d, value %d", GC.getTechInfo(*itr).getDescription(), iTempCost, iTempValue );
iCost += iTempCost;
iValue += iTempValue/iScaleFactor;
while( iValue > MAX_INT/100 )
{
iScaleFactor *= 2;
iValue /= 2;
}
}
iValue = (100*iValue)/(bIgnoreCost ? 1 : iCost);
if ( iValue > MAX_INT/iScaleFactor )
{
return MAX_INT;
}
else
{
return iValue*iScaleFactor;
}
}
techPath* CvPlayerAI::findBestPath(TechTypes eTech, int& valuePerUnitCost, bool bIgnoreCost, bool bAsync, int* paiBonusClassRevealed, int* paiBonusClassUnrevealed, int* paiBonusClassHave) const
{
PROFILE_FUNC();
std::vector<techPath*> possiblePaths;
techPath* initialSeed = new techPath();
possiblePaths.push_back(initialSeed);
constructTechPathSet(eTech, possiblePaths, *initialSeed);
// Find the lowest cost of the possible paths
int iValue;
int iBestValue = 0;
techPath* bestPath = NULL;
for(std::vector<techPath*>::const_iterator itr = possiblePaths.begin(); itr != possiblePaths.end(); ++itr)
{
iValue = techPathValuePerUnitCost(*itr, eTech, bIgnoreCost, bAsync, paiBonusClassRevealed, paiBonusClassUnrevealed, paiBonusClassHave);
logBBAI(" Evaluated tech path value leading to %S as %d", GC.getTechInfo(eTech).getDescription(), iValue );
if ( iValue >= iBestValue )
{
iBestValue = iValue;
bestPath = *itr;
}
}
for(std::vector<techPath*>::const_iterator itr = possiblePaths.begin(); itr != possiblePaths.end(); ++itr)
{
if ( *itr != bestPath )
{
delete *itr;
}
}
valuePerUnitCost = iBestValue;
return bestPath;
}
TechTypes CvPlayerAI::findStartTech(techPath* path) const
{
for(std::vector<TechTypes>::const_iterator itr = path->begin(); itr != path->end(); ++itr)
{
if ( canResearch(*itr) )
{
return *itr;
}
}
return NO_TECH;
}
int CvPlayerAI::AI_techValue( TechTypes eTech, int iPathLength, bool bIgnoreCost, bool bAsync, int* paiBonusClassRevealed, int* paiBonusClassUnrevealed, int* paiBonusClassHave) const
{
MEMORY_TRACK()
PROFILE_FUNC();
CvCity* pCapitalCity;
ImprovementTypes eImprovement;
RouteTypes eRoute;
int iNumBonuses;
int iValue;
int iTempValue;
int iBuildValue;
int iBonusValue;
int iI, iJ;
if ( gPlayerLogLevel > 2 )
{
logBBAI("Player %d (%S) calculate value for tech %S",
getID(),
getCivilizationDescription(0),
GC.getTechInfo(eTech).getDescription());
}
pCapitalCity = getCapitalCity();
CvTeam& kTeam = GET_TEAM(getTeam());
//bool bWarPlan = (kTeam.getAnyWarPlanCount(true) > 0);
bool bCapitalAlone = (GC.getGameINLINE().getElapsedGameTurns() > 0) ? AI_isCapitalAreaAlone() : false;
bool bFinancialTrouble = AI_isFinancialTrouble();
bool bAdvancedStart = getAdvancedStartPoints() >= 0;
int iHasMetCount = kTeam.getHasMetCivCount(true);
int iCoastalCities = countNumCoastalCities();
int iConnectedForeignCities = countPotentialForeignTradeCitiesConnected();
int iCityCount = getNumCities();
//int iTeamCityCount = kTeam.getNumCities();
iValue = 0;
int iRandomFactor = ((bAsync) ? GC.getASyncRand().get(2000, "AI Research ASYNC") : GC.getGameINLINE().getSorenRandNum(2000, "AI Research"));
int iRandomMax = 2000;
//iValue += iRandomFactor;
// Map stuff
if (GC.getTechInfo(eTech).isExtraWaterSeeFrom())
{
if (iCoastalCities > 0)
{
iValue += 100;
if (bCapitalAlone)
{
iValue += 400;
}
}
}
if (GC.getTechInfo(eTech).isMapCentering())
{
iValue += 100;
}
if (GC.getTechInfo(eTech).isMapVisible())
{
iValue += 100;
if (bCapitalAlone)
{
iValue += 400;
}
}
// Expand trading options
if (GC.getTechInfo(eTech).isMapTrading())
{
iValue += 100;
if (bCapitalAlone)
{
iValue += 400;
}
}
if (GC.getTechInfo(eTech).isTechTrading() && !GC.getGameINLINE().isOption(GAMEOPTION_NO_TECH_TRADING))
{
iValue += 500;
iValue += 500 * iHasMetCount;
}
if (GC.getTechInfo(eTech).isGoldTrading())
{
iValue += 200;
if (iHasMetCount > 0)
{
iValue += 400;
}
}
if (GC.getTechInfo(eTech).isOpenBordersTrading())
{
if (iHasMetCount > 0)
{
iValue += 500;
if (iCoastalCities > 0)
{
iValue += 400;
}
/************************************************************************************************/
/* REVOLUTION_MOD 05/30/08 jdog5000 */
/* */
/* Revolution AI */
/************************************************************************************************/
if( isMinorCiv() && GC.getGame().isOption(GAMEOPTION_START_AS_MINORS) )
{
iValue += 250 + 120*iHasMetCount;
}
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
}
}
if (GC.getTechInfo(eTech).isDefensivePactTrading())
{
iValue += 400;
}
if (GC.getTechInfo(eTech).isPermanentAllianceTrading() && (GC.getGameINLINE().isOption(GAMEOPTION_PERMANENT_ALLIANCES)))
{
iValue += 200;
}
if (GC.getTechInfo(eTech).isVassalStateTrading() && !(GC.getGameINLINE().isOption(GAMEOPTION_NO_VASSAL_STATES)))
{
iValue += 200;
}
// Tile improvement abilities
if (GC.getTechInfo(eTech).isBridgeBuilding())
{
iValue += 200;
}
if (GC.getTechInfo(eTech).isIrrigation())
{
iValue += 400;
}
if (GC.getTechInfo(eTech).isIgnoreIrrigation())
{
iValue += 500;
}
if (GC.getTechInfo(eTech).isWaterWork())
{
iValue += (600 * iCoastalCities);
}
/************************************************************************************************/
/* Afforess Start 03/7/10 */
/* */
/* */
/************************************************************************************************/
CvTechInfo& kTech = GC.getTechInfo(eTech);
int iLoop;
iTempValue = 0;
if (kTech.isCanPassPeaks())
{
if (!GC.getGameINLINE().isOption(GAMEOPTION_MOUNTAINS))
{
CvPlot* pPlot;
for (int iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
{
pPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
if (pPlot->isPeak())
{
if (pPlot->getOwnerINLINE() != NO_PLAYER)
{
if (GET_PLAYER(pPlot->getOwnerINLINE()).getID() == getID())
{
iTempValue += 35;
}
}
}
}
}
iValue += iTempValue;
iTempValue = 0;
}
if (kTech.isMoveFastPeaks())
{
iValue += 150;
}
if (kTech.isCanFoundOnPeaks())
{
iValue += 100;
}
if ( gPlayerLogLevel > 2 )
{
logBBAI("\tMisc value: %d",
iValue);
}
iTempValue = 0;
for (iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
if (GC.getCorporationInfo((CorporationTypes)iI).getObsoleteTech() != NO_TECH)
{
if (GC.getCorporationInfo((CorporationTypes)iI).getObsoleteTech() == eTech)
{
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (pLoopCity->isHasCorporation((CorporationTypes)iI))
{
iTempValue -= AI_corporationValue((CorporationTypes)iI, pLoopCity);
}
}
}
}
}
iValue += iTempValue / 1000;
if ( gPlayerLogLevel > 2 )
{
logBBAI("\tCorporation value: %d",
iTempValue / 1000);
}
iTempValue = 0;
for (iI = 0; iI < GC.getNumPromotionInfos(); iI++)
{
if (GC.getPromotionInfo((PromotionTypes)iI).getObsoleteTech() != NO_TECH)
{
if (GC.getPromotionInfo((PromotionTypes)iI).getObsoleteTech() == eTech)
{
for (CvUnit* pLoopUnit = firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = nextUnit(&iLoop))
{
if (pLoopUnit->isHasPromotion((PromotionTypes)iI))
{
iTempValue -= AI_promotionValue((PromotionTypes)iI, pLoopUnit->getUnitType(), pLoopUnit);
}
}
}
}
}
iValue += iTempValue / 100;
if ( gPlayerLogLevel > 2 )
{
logBBAI("\tPromotion value: %d",
iTempValue / 100);
}
iTempValue = 0;
if (kTech.isRebaseAnywhere() && GC.getDefineINT("MAX_AIRLIFT_RANGE") > 0)
{
iValue += 300;
}
for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
{
if (kTech.getFreeSpecialistCount(iI) != 0)
{
iValue += 50 * getNumCities() * kTech.getFreeSpecialistCount(iI);
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iValue += (GC.getTechInfo(eTech).getFeatureProductionModifier() * 2);
iValue += (GC.getTechInfo(eTech).getWorkerSpeedModifier() * 4);
iValue += (GC.getTechInfo(eTech).getTradeRoutes() * (std::max((getNumCities() + 2), iConnectedForeignCities) + 1) * ((bFinancialTrouble) ? 200 : 100));
if ( AI_isDoVictoryStrategy(AI_VICTORY_DOMINATION4) )
{
iValue += (GC.getTechInfo(eTech).getHealth() * 350);
}
else
{
iValue += (GC.getTechInfo(eTech).getHealth() * 200);
}
for (iJ = 0; iJ < GC.getNumRouteInfos(); iJ++)
{
iValue += -(GC.getRouteInfo((RouteTypes)iJ).getTechMovementChange(eTech) * 100);
}
for (iJ = 0; iJ < NUM_DOMAIN_TYPES; iJ++)
{
iValue += (GC.getTechInfo(eTech).getDomainExtraMoves(iJ) * 200);
}
for (iJ = 0; iJ < NUM_COMMERCE_TYPES; iJ++)
{
if (GC.getTechInfo(eTech).isCommerceFlexible(iJ))
{
iValue += 100;
if ((iJ == COMMERCE_CULTURE) && (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE2)))
{
iValue += 1000;
}
}
}
for (iJ = 0; iJ < GC.getNumTerrainInfos(); iJ++)
{
if (GC.getTechInfo(eTech).isTerrainTrade(iJ))
{
if (GC.getTerrainInfo((TerrainTypes)iJ).isWater())
{
if (pCapitalCity != NULL)
{
iValue += (countPotentialForeignTradeCities(pCapitalCity->area()) * 100);
}
if (iCoastalCities > 0)
{
iValue += ((bCapitalAlone) ? 950 : 350);
}
iValue += 50;
}
else
{
iValue += 1000;
}
}
}
if (GC.getTechInfo(eTech).isRiverTrade())
{
iValue += 1000;
}
/* ------------------ Tile Improvement Value ------------------ */
int iTileImprovementValue = 0;
for (iJ = 0; iJ < GC.getNumImprovementInfos(); iJ++)
{
for (int iK = 0; iK < NUM_YIELD_TYPES; iK++)
{
iTempValue = 0;
/* original code
iTempValue += (GC.getImprovementInfo((ImprovementTypes)iJ).getTechYieldChanges(eTech, iK) * getImprovementCount((ImprovementTypes)iJ) * 50); */
// Often, an improvment only becomes viable after it gets the tech bonus.
// So it's silly to score the bonus proportionally to how many of the improvements we already have.
iTempValue += (GC.getImprovementInfo((ImprovementTypes)iJ).getTechYieldChanges(eTech, iK) * (getImprovementCount((ImprovementTypes)iJ) + 2 * getNumCities()) * 35);
// This new version is still bork, but at least it won't be worthless.
iTempValue *= AI_yieldWeight((YieldTypes)iK);
iTempValue /= 100;
iTileImprovementValue += iTempValue;
}
}
iValue += iTileImprovementValue;
if ( gPlayerLogLevel > 2 )
{
logBBAI("\tTile improvement value: %d",
iTileImprovementValue);
}
//ls612: Tech Commerce Modifiers
for (int iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
if (GC.getTechInfo(eTech).getCommerceModifier(iI) != 0)
{
iValue += (GC.getTechInfo(eTech).getCommerceModifier(iI) * 100);
}
//Extra check for financially challenged AIs
if (iI == 0 && GC.getTechInfo(eTech).getCommerceModifier(iI) < 0)
{
if (AI_isFinancialTrouble())
{
iValue -= 100;
}
}
}
iBuildValue = 0;
for (iJ = 0; iJ < GC.getNumBuildInfos(); iJ++)
{
if (GC.getBuildInfo((BuildTypes)iJ).getTechPrereq() == eTech)
{
eImprovement = (ImprovementTypes)(GC.getBuildInfo((BuildTypes)iJ).getImprovement());
if (eImprovement != NO_IMPROVEMENT)
{
// If it's an upgradable improvement
eImprovement = GET_TEAM(getTeam()).finalImprovementUpgrade(eImprovement);
}
else
{
// only increment build value if it is not an improvement, otherwise handle it there
iBuildValue += 200;
}
if (eImprovement != NO_IMPROVEMENT)
{
CvImprovementInfo& kImprovement = GC.getImprovementInfo(eImprovement);
int iImprovementValue = 300;
iImprovementValue += ((kImprovement.isActsAsCity()) ? 100 : 0);
iImprovementValue += ((kImprovement.isHillsMakesValid()) ? 100 : 0);
iImprovementValue += ((kImprovement.isFreshWaterMakesValid()) ? 200 : 0);
iImprovementValue += ((kImprovement.isRiverSideMakesValid()) ? 100 : 0);
iImprovementValue += ((kImprovement.isCarriesIrrigation()) ? 300 : 0);
for (int iK = 0; iK < GC.getNumTerrainInfos(); iK++)
{
iImprovementValue += (kImprovement.getTerrainMakesValid(iK) ? 50 : 0);
/************************************************************************************************/
/* Afforess Start 05/22/10 */
/* */
/* */
/************************************************************************************************/
//Desert has negative defense
if (GC.getTerrainInfo((TerrainTypes)iK).getDefenseModifier() < 0)
{
if (GET_TEAM(getTeam()).isCanFarmDesert())
{
iImprovementValue += 50;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
for (int iK = 0; iK < GC.getNumFeatureInfos(); iK++)
{
iImprovementValue += (kImprovement.getFeatureMakesValid(iK) ? 50 : 0);
}
for (int iK = 0; iK < NUM_YIELD_TYPES; iK++)
{
iTempValue = 0;
iTempValue += (kImprovement.getYieldChange(iK) * 200);
iTempValue += (kImprovement.getRiverSideYieldChange(iK) * 100);
iTempValue += (kImprovement.getHillsYieldChange(iK) * 100);
iTempValue += (kImprovement.getIrrigatedYieldChange(iK) * 150);
// land food yield is more valueble
if (iK == YIELD_FOOD && !kImprovement.isWater())
{
iTempValue *= 3;
iTempValue /= 2;
}
if (bFinancialTrouble && iK == YIELD_COMMERCE)
{
iTempValue *= 2;
}
iTempValue *= AI_yieldWeight((YieldTypes)iK);
iTempValue /= 100;
iImprovementValue += iTempValue;
}
for (int iK = 0; iK < GC.getNumBonusInfos(); iK++)
{
iBonusValue = 0;
iBonusValue += ((kImprovement.isImprovementBonusMakesValid(iK)) ? 450 : 0);
iBonusValue += ((kImprovement.isImprovementBonusTrade(iK)) ? (45 * AI_bonusVal((BonusTypes) iK)) : 0);
if (iBonusValue > 0)
{
for (int iL = 0; iL < NUM_YIELD_TYPES; iL++)
{
iTempValue = 0;
iTempValue += (kImprovement.getImprovementBonusYield(iK, iL) * 300);
iTempValue += (kImprovement.getIrrigatedYieldChange(iL) * 200);
// food bonuses are more valuable
if (iL == YIELD_FOOD)
{
iTempValue *= 2;
}
// otherwise, devalue the bonus slightly
else if (iL == YIELD_COMMERCE && bFinancialTrouble)
{
iTempValue *= 4;
iTempValue /= 3;
}
else
{
iTempValue *= 3;
iTempValue /= 4;
}
if (bAdvancedStart && getCurrentEra() < 2)
{
iValue *= (iL == YIELD_FOOD) ? 3 : 2;
}
iTempValue *= AI_yieldWeight((YieldTypes)iL);
iTempValue /= 100;
iBonusValue += iTempValue;
}
iNumBonuses = countOwnedBonuses((BonusTypes)iK);
if (iNumBonuses > 0)
{
iBonusValue *= (iNumBonuses + 2);
/********************************************************************************/
/* Tech Value for Bonus Yields 04.08.2010 Fuyu */
/********************************************************************************/
//Fuyu: massive bonus for early worker logic
int iCityRadiusBonusCount = 0;
if (getNumCities() <= 3 && (GC.getGame().getElapsedGameTurns() < ((30 * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getTrainPercent()) / 100)))
{
CvCity* pLoopCity;
int iLoop;
//count bonuses inside city radius or easily claimed
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
iCityRadiusBonusCount += pLoopCity->AI_countNumBonuses((BonusTypes)iK, true, pLoopCity->getCommerceRate(COMMERCE_CULTURE) > 0, -1);
}
}
if (iCityRadiusBonusCount > 1)
{
iTempValue *= 3 + iCityRadiusBonusCount - getNumCities();
}
/********************************************************************************/
/* Tech Value for Bonus Yields END */
/********************************************************************************/
iBonusValue /= kImprovement.isWater() ? 4 : 3; // water resources are worth less
iImprovementValue += iBonusValue;
}
}
}
// if water improvement, weight by coastal cities (weight the whole build)
if (kImprovement.isWater())
{
iImprovementValue *= iCoastalCities;
iImprovementValue /= std::max(1, iCityCount/2);
}
iBuildValue += iImprovementValue;
}
eRoute = ((RouteTypes)(GC.getBuildInfo((BuildTypes)iJ).getRoute()));
if (eRoute != NO_ROUTE)
{
iBuildValue += ((getBestRoute() == NO_ROUTE) ? 700 : 200) * (getNumCities() + (bAdvancedStart ? 4 : 0));
for (int iK = 0; iK < NUM_YIELD_TYPES; iK++)
{
iTempValue = 0;
iTempValue += (GC.getRouteInfo(eRoute).getYieldChange(iK) * 100);
for (int iL = 0; iL < GC.getNumImprovementInfos(); iL++)
{
iTempValue += (GC.getImprovementInfo((ImprovementTypes)iL).getRouteYieldChanges(eRoute, iK) * 50);
}
iTempValue *= AI_yieldWeight((YieldTypes)iK);
iTempValue /= 100;
iBuildValue += iTempValue;
}
}
}
}
//the way feature-remove is done in XML is pretty weird
//I believe this code needs to be outside the general BuildTypes loop
//to ensure the feature-remove is only counted once rather than once per build
//which could be a lot since nearly every build clears jungle...
for (iJ = 0; iJ < GC.getNumFeatureInfos(); iJ++)
{
bool bIsFeatureRemove = false;
for (int iK = 0; iK < GC.getNumBuildInfos(); iK++)
{
if (GC.getBuildInfo((BuildTypes)iK).getFeatureTech(iJ) == eTech)
{
bIsFeatureRemove = true;
break;
}
}
if (bIsFeatureRemove)
{
iBuildValue += 100;
/********************************************************************************/
/* Tech Value for Feature Remove 05.08.2010 Fuyu */
/********************************************************************************/
//Fuyu: bonus for early worker logic
if ((GC.getFeatureInfo(FeatureTypes(iJ)).getHealthPercent() < 0) ||
((GC.getFeatureInfo(FeatureTypes(iJ)).getYieldChange(YIELD_FOOD) + GC.getFeatureInfo(FeatureTypes(iJ)).getYieldChange(YIELD_PRODUCTION) + GC.getFeatureInfo(FeatureTypes(iJ)).getYieldChange(YIELD_COMMERCE)) < 0))
{
if (getNumCities() <= 3)
{
iBuildValue += 25 * countCityFeatures((FeatureTypes)iJ) * (3 - getNumCities()/2);
}
else
{
iBuildValue += 25 * countCityFeatures((FeatureTypes)iJ);
}
}
else
{
if (getNumCities() <= 3)
{
iBuildValue += 5 * countCityFeatures((FeatureTypes)iJ) * (3 - getNumCities()/2);
}
else
{
iBuildValue += 5 * countCityFeatures((FeatureTypes)iJ);
}
}
/********************************************************************************/
/* Tech Value for Feature Remove END */
/********************************************************************************/
}
}
/*if (AI_totalUnitAIs(UNITAI_WORKER) == 0)
{
iBuildValue /= 2;
}*/
iValue += iBuildValue;
if ( gPlayerLogLevel > 2 )
{
logBBAI("\tBuild value: %d",
iBuildValue);
}
// does tech reveal bonus resources
int iBonusRevealValue = 0;
for (iJ = 0; iJ < GC.getNumBonusInfos(); iJ++)
{
if (GC.getBonusInfo((BonusTypes)iJ).getTechReveal() == eTech)
{
int iRevealValue = 150;
iRevealValue += (AI_bonusVal((BonusTypes)iJ) * 50);
BonusClassTypes eBonusClass = (BonusClassTypes)GC.getBonusInfo((BonusTypes)iJ).getBonusClassType();
int iBonusClassTotal = (paiBonusClassRevealed[eBonusClass] + paiBonusClassUnrevealed[eBonusClass]);
//iMultiplier is basically a desperation value
//it gets larger as the AI runs out of options
//Copper after failing to get horses is +66%
//Iron after failing to get horse or copper is +200%
//but with either copper or horse, Iron is only +25%
int iMultiplier = 0;
if (iBonusClassTotal > 0)
{
iMultiplier = (paiBonusClassRevealed[eBonusClass] - paiBonusClassHave[eBonusClass]);
iMultiplier *= 100;
iMultiplier /= iBonusClassTotal;
iMultiplier *= (paiBonusClassRevealed[eBonusClass] + 1);
iMultiplier /= ((paiBonusClassHave[eBonusClass] * iBonusClassTotal) + 1);
}
iMultiplier *= std::min(3, getNumCities());
iMultiplier /= 3;
iRevealValue *= 100 + iMultiplier;
iRevealValue /= 100;
// K-Mod
// If we don't yet have the 'enable' tech, reduce the value of the reveal.
if (GC.getBonusInfo((BonusTypes)iJ).getTechCityTrade() != eTech && !GET_TEAM(getTeam()).isHasTech((TechTypes)(GC.getBonusInfo((BonusTypes)iJ).getTechCityTrade())))
iRevealValue /= 3;
// K-Mod end
iBonusRevealValue += iRevealValue;
}
// K-Mod: Value for enabling resources that are already revealed
else if (GC.getBonusInfo((BonusTypes)iJ).getTechCityTrade() == eTech && GET_TEAM(getTeam()).isHasTech((TechTypes)(GC.getBonusInfo((BonusTypes)iJ).getTechReveal())))
{
int iOwned = countOwnedBonuses((BonusTypes)iJ);
if (iOwned > 0)
{
int iEnableValue = 150;
iEnableValue += (AI_bonusVal((BonusTypes)iJ) * 50);
iEnableValue *= (iOwned > 1) ? 150 : 100;
iEnableValue /= 100;
iValue += iEnableValue;
}
}
// K-Mod end
}
if ( gPlayerLogLevel > 2 )
{
logBBAI("\tBonus reveal value: %d",
iBonusRevealValue);
}
iValue += iBonusRevealValue;
/* ------------------ Unit Value ------------------ */
bool bEnablesUnitWonder;
int iUnitValue = AI_techUnitValue( eTech, iPathLength, bEnablesUnitWonder );
if ( gPlayerLogLevel > 2 )
{
logBBAI("\tUnit value: %d",
iUnitValue);
}
iValue += iUnitValue;
if (bEnablesUnitWonder)
{
int iWonderRandom = ((bAsync) ? GC.getASyncRand().get(400, "AI Research Wonder Unit ASYNC") : GC.getGameINLINE().getSorenRandNum(400, "AI Research Wonder Unit"));
iValue += iWonderRandom + (bCapitalAlone ? 200 : 0);
iRandomMax += 400;
iRandomFactor += iWonderRandom;
}
/* ------------------ Building Value ------------------ */
bool bEnablesWonder;
int iBuildingValue = AI_techBuildingValue( eTech, iPathLength, bEnablesWonder );
if ( gPlayerLogLevel > 2 )
{
logBBAI("\tBuilding value: %d",
iBuildingValue);
}
iValue += iBuildingValue;
// if it gives at least one wonder
if (bEnablesWonder)
{
int iWonderRandom = ((bAsync) ? GC.getASyncRand().get(800, "AI Research Wonder Building ASYNC") : GC.getGameINLINE().getSorenRandNum(800, "AI Research Wonder Building"));
iValue += (500 + iWonderRandom) / (bAdvancedStart ? 5 : 1);
iRandomMax += 800;
iRandomFactor += iWonderRandom;
}
/* ------------------ Project Value ------------------ */
bool bEnablesProjectWonder = false;
for (iJ = 0; iJ < GC.getNumProjectInfos(); iJ++)
{
if (GC.getProjectInfo((ProjectTypes)iJ).getTechPrereq() == eTech)
{
iValue += 1000;
if( (VictoryTypes)GC.getProjectInfo((ProjectTypes)iJ).getVictoryPrereq() != NO_VICTORY )
{
if( !(GC.getProjectInfo((ProjectTypes)iJ).isSpaceship()) )
{
// Apollo
iValue += (AI_isDoVictoryStrategy(AI_VICTORY_SPACE2) ? 2000 : 100);
}
else
{
// Space ship parts
if( AI_isDoVictoryStrategy(AI_VICTORY_SPACE3) )
{
iValue += 1000;
}
}
}
if (iPathLength <= 1)
{
if (getTotalPopulation() > 5)
{
if (isWorldProject((ProjectTypes)iJ))
{
if (!(GC.getGameINLINE().isProjectMaxedOut((ProjectTypes)iJ)))
{
bEnablesProjectWonder = true;
if (bCapitalAlone)
{
iValue += 100;
}
}
}
}
}
}
}
if (bEnablesProjectWonder)
{
int iWonderRandom = ((bAsync) ? GC.getASyncRand().get(200, "AI Research Wonder Project ASYNC") : GC.getGameINLINE().getSorenRandNum(200, "AI Research Wonder Project"));
iValue += iWonderRandom;
iRandomMax += 200;
iRandomFactor += iWonderRandom;
}
/* ------------------ Process Value ------------------ */
bool bIsGoodProcess = false;
for (iJ = 0; iJ < GC.getNumProcessInfos(); iJ++)
{
if (GC.getProcessInfo((ProcessTypes)iJ).getTechPrereq() == eTech)
{
iValue += 100;
for (int iK = 0; iK < NUM_COMMERCE_TYPES; iK++)
{
iTempValue = (GC.getProcessInfo((ProcessTypes)iJ).getProductionToCommerceModifier(iK) * 4);
iTempValue *= AI_commerceWeight((CommerceTypes)iK);
iTempValue /= 100;
if (iK == COMMERCE_GOLD || iK == COMMERCE_RESEARCH)
{
bIsGoodProcess = true;
}
else if ((iK == COMMERCE_CULTURE) && AI_isDoVictoryStrategy(AI_VICTORY_CULTURE1))
{
iTempValue *= 3;
}
iValue += iTempValue;
}
}
}
if (bIsGoodProcess && bFinancialTrouble)
{
int iJ;
bool bHaveGoodProcess = false;
for (iJ = 0; iJ < GC.getNumProcessInfos(); iJ++)
{
if (kTeam.isHasTech((TechTypes)GC.getProcessInfo((ProcessTypes)iJ).getTechPrereq()))
{
bHaveGoodProcess = (GC.getProcessInfo((ProcessTypes)iJ).getProductionToCommerceModifier(COMMERCE_GOLD) + GC.getProcessInfo((ProcessTypes)iJ).getProductionToCommerceModifier(COMMERCE_RESEARCH)) > 0;
if (bHaveGoodProcess)
{
break;
}
}
}
if (!bHaveGoodProcess)
{
iValue += 1500;
}
}
/* ------------------ Civic Value ------------------ */
for (iJ = 0; iJ < GC.getNumCivicInfos(); iJ++)
{
if (GC.getCivicInfo((CivicTypes)iJ).getTechPrereq() == eTech)
{
iValue += 200;
CivicTypes eCivic = getCivics((CivicOptionTypes)(GC.getCivicInfo((CivicTypes)iJ).getCivicOptionType()));
if (NO_CIVIC != eCivic)
{
int iCurrentCivicValue = AI_civicValue(eCivic);
int iNewCivicValue = AI_civicValue((CivicTypes)iJ);
int iTechCivicValue = 0;
if (iNewCivicValue > iCurrentCivicValue)
{
// Because civic values can be negative theer is no absolute scale so we cannot meaningfully scale
// this relative to the current value. Aslo 2400 is not enough to matter for some critical changes
//iValue += std::min(2400, (2400 * (iNewCivicValue - iCurrentCivicValue)) / std::max(1, iCurrentCivicValue));
iTechCivicValue = std::min(50000, 50 * (iNewCivicValue - iCurrentCivicValue));
iValue += iTechCivicValue;
}
if (eCivic == GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivic())
{
iValue += 600;
}
if ( gPlayerLogLevel > 2 )
{
logBBAI("Player %d - tech %S enabled civic %S with value %d vs current %d - additional tech value %d",
getID(),
GC.getTechInfo(eTech).getDescription(),
GC.getCivicInfo((CivicTypes)iJ).getDescription(),
iNewCivicValue,
iCurrentCivicValue,
iTechCivicValue);
}
}
}
}
if (iPathLength <= 10)
{
if (GC.getGameINLINE().countKnownTechNumTeams(eTech) == 0)
{
int iReligionValue = 0;
int iPotentialReligions = 0;
for (int iJ = 0; iJ < GC.getNumReligionInfos(); iJ++)
{
TechTypes eReligionTech = (TechTypes)GC.getReligionInfo((ReligionTypes)iJ).getTechPrereq();
if (kTeam.isHasTech(eReligionTech))
{
if (!(GC.getGameINLINE().isReligionSlotTaken((ReligionTypes)iJ)))
{
/************************************************************************************************/
/* RevDCM Start 09/08/10 */
/* */
/* OC_LIMITED_RELIGIONS */
/************************************************************************************************/
if(canFoundReligion())
{
iPotentialReligions++;
}
/************************************************************************************************/
/* REVDCM END */
/************************************************************************************************/
}
}
if (eReligionTech == eTech)
{
if (!(GC.getGameINLINE().isReligionSlotTaken((ReligionTypes)iJ)))
{
int iRoll = 10000;
if (!GC.getGame().isOption(GAMEOPTION_PICK_RELIGION))
{
/************************************************************************************************/
/* RevDCM Start 09/08/10 */
/* */
/* OC_LIMITED_RELIGIONS */
/************************************************************************************************/
if(canFoundReligion())
{
ReligionTypes eFavorite = (ReligionTypes)GC.getLeaderHeadInfo(getLeaderType()).getFavoriteReligion();
if (eFavorite != NO_RELIGION)
{
if (iJ == eFavorite)
{
iReligionValue += 1 + ((bAsync) ? GC.getASyncRand().get(1200, "AI Research Religion (Favorite) ASYNC") : GC.getGameINLINE().getSorenRandNum(1200, "AI Research Religion (Favorite)"));
}
else
{
iRoll *= 2;
iRoll /= 3;
}
}
}
/************************************************************************************************/
/* REVDCM END */
/************************************************************************************************/
}
iReligionValue += 1 + ((bAsync) ? GC.getASyncRand().get(iRoll, "AI Research Religion ASYNC") : GC.getGameINLINE().getSorenRandNum(iRoll, "AI Research Religion"));
if (iPathLength < 2)
{
iReligionValue *= 3;
iReligionValue /= 2;
}
}
}
}
if (iReligionValue > 0)
{
if (countHolyCities() < 1)
{
iReligionValue *= 2;
}
if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE1))
{
iReligionValue += 1000;
}
else
{
iReligionValue /= (1 + countHolyCities() + ((iPotentialReligions > 0) ? 1 : 0));
}
if ((countTotalHasReligion() == 0) && (iPotentialReligions == 0))
{
iReligionValue *= 2;
iReligionValue += 500;
}
if (AI_isDoStrategy(AI_STRATEGY_DAGGER))
{
iReligionValue /= 2;
}
iReligionValue = (5*iReligionValue/(iPathLength+4));
if ( gPlayerLogLevel > 2 )
{
logBBAI("\tReligion value: %d",
iReligionValue);
}
iValue += iReligionValue;
}
int iCorporationValue = 0;
for (int iJ = 0; iJ < GC.getNumCorporationInfos(); iJ++)
{
if (GC.getCorporationInfo((CorporationTypes)iJ).getTechPrereq() == eTech)
{
if (!(GC.getGameINLINE().isCorporationFounded((CorporationTypes)iJ)))
{
iCorporationValue += 100 + ((bAsync) ? GC.getASyncRand().get(2400, "AI Research Corporation ASYNC") : GC.getGameINLINE().getSorenRandNum(2400, "AI Research Corporation"));
}
}
}
if ( iCorporationValue > 0 )
{
iCorporationValue = (5*iCorporationValue/(iPathLength+4));
if ( gPlayerLogLevel > 2 )
{
logBBAI("\tCorporation found value: %d",
iCorporationValue);
}
iValue += iCorporationValue;
}
int iFreeStuffValue = 0;
if (getTechFreeUnit(eTech) != NO_UNIT)
{
int iGreatPeopleRandom = ((bAsync) ? GC.getASyncRand().get(3200, "AI Research Great People ASYNC") : GC.getGameINLINE().getSorenRandNum(3200, "AI Research Great People"));
iFreeStuffValue += iGreatPeopleRandom;
iRandomMax += 3200;
iRandomFactor += iGreatPeopleRandom;
if (bCapitalAlone)
{
iFreeStuffValue += 400;
}
iFreeStuffValue += 200;
}
// Free techs are REALLY valuable - as an estimate we assume they are at least as valuable as the enabling tech
// since we'll be able to choose anything accessible once it is researched and up to twice as much as that on a
// random scale
if ( GC.getTechInfo(eTech).getFirstFreeTechs() > 0 )
{
int iPercentageMultiplier = GC.getTechInfo(eTech).getFirstFreeTechs()*((bCapitalAlone ? 150 : 100) + (bAsync ? GC.getASyncRand().get(100, "AI Research Free Tech ASYNC") : GC.getGameINLINE().getSorenRandNum(100, "AI Research Free Tech")));
iFreeStuffValue += (iValue*iPercentageMultiplier)/100;
}
if ( iFreeStuffValue > 0 )
{
iFreeStuffValue = (5*iFreeStuffValue/(iPathLength+4));
if ( gPlayerLogLevel > 2 )
{
logBBAI("\tFree on first discovery value: %d",
iFreeStuffValue);
}
iValue += iFreeStuffValue;
}
}
}
iValue += GC.getTechInfo(eTech).getAIWeight();
if (!isHuman() && iValue > 0)
{
for (int iJ = 0; iJ < GC.getNumFlavorTypes(); iJ++)
{
iValue += (AI_getFlavorValue((FlavorTypes)iJ) * GC.getTechInfo(eTech).getFlavorValue(iJ) * 20);
}
}
if (GC.getTechInfo(eTech).isRepeat())
{
iValue /= 10;
}
if ( gPlayerLogLevel > 2 )
{
logBBAI("Player %d (%S) raw value for tech %S is %d",
getID(),
getCivilizationDescription(0),
GC.getTechInfo(eTech).getDescription(),
iValue);
}
if (!bIgnoreCost)
{
iValue *= (1 + (getResearchTurnsLeft((eTech), false)));
iValue /= 10;
}
//Tech Whore
if (!GC.getGameINLINE().isOption(GAMEOPTION_NO_TECH_TRADING))
{
if (GC.getTechInfo(eTech).isTechTrading() || kTeam.isTechTrading())
{
if (((bAsync) ? GC.getASyncRand().get(100, "AI Tech Whore ASYNC") : GC.getGameINLINE().getSorenRandNum(100, "AI Tech Whore")) < (GC.getGameINLINE().isOption(GAMEOPTION_NO_TECH_BROKERING) ? 20 : 10))
{
int iKnownCount = 0;
int iPossibleKnownCount = 0;
for (int iTeam = 0; iTeam < MAX_CIV_TEAMS; iTeam++)
{
if (GET_TEAM((TeamTypes)iTeam).isAlive())
{
if (GET_TEAM(getTeam()).isHasMet((TeamTypes)iTeam))
{
if (GET_TEAM((TeamTypes)iTeam).isHasTech(eTech))
{
iKnownCount++;
}
}
iPossibleKnownCount++;
}
}
if (iKnownCount == 0)
{
if (iPossibleKnownCount > 2)
{
int iTradeModifier = std::min(150, 25 * (iPossibleKnownCount - 2));
iValue *= 100 + iTradeModifier;
iValue /= 100;
}
}
}
}
}
if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE3))
{
int iCVValue = AI_cultureVictoryTechValue(eTech);
iValue *= (iCVValue + 10);
iValue /= ((iCVValue < 100) ? 400 : 100);
}
iRandomFactor = ((bAsync) ? GC.getASyncRand().get(20, "AI Research ASYNC") : GC.getGameINLINE().getSorenRandNum(20, "AI Research"));
iValue += (iValue*(iRandomFactor-10))/100;
iValue = std::max(1, iValue);
return iValue;
}
int CvPlayerAI::AI_techBuildingValue( TechTypes eTech, int iPathLength, bool &bEnablesWonder ) const
{
MEMORY_TRACK()
PROFILE_FUNC();
if ( gPlayerLogLevel > 2 )
{
logBBAI("Player %d (%S) evaluating buildings for tech %S",
getID(),
getCivilizationDescription(0),
GC.getTechInfo(eTech).getDescription());
}
#ifdef USE_BOTH_TECHBUILDING_EVALUATIONS
bool bCapitalAlone = (GC.getGameINLINE().getElapsedGameTurns() > 0) ? AI_isCapitalAreaAlone() : false;
bool bFinancialTrouble = AI_isFinancialTrouble();
int iTeamCityCount = GET_TEAM(getTeam()).getNumCities();
int iCoastalCities = countNumCoastalCities();
int iCityCount = getNumCities();
int iTempValue = 0;
int iBestLandBuildingValue = 0;
bool bIsCultureBuilding = false;
#endif
BuildingTypes eLoopBuilding;
int iValue = 0;
int iExistingCultureBuildingCount = 0;
bEnablesWonder = false;
for (int iJ = 0; iJ < GC.getNumBuildingClassInfos(); iJ++)
{
eLoopBuilding = ((BuildingTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iJ)));
/************************************************************************************************/
/* Afforess Start 07/27/10 */
/* */
/* */
/************************************************************************************************/
/*
if (eLoopBuilding != NO_BUILDING)
*/
if (GC.getGameINLINE().canEverConstruct(eLoopBuilding))
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
CvBuildingInfo& kLoopBuilding = GC.getBuildingInfo(eLoopBuilding);
if (isTechRequiredForBuilding((eTech), eLoopBuilding))
{
if (isWorldWonderClass((BuildingClassTypes)iJ))
{
if (!(GC.getGameINLINE().isBuildingClassMaxedOut((BuildingClassTypes)iJ)))
{
bEnablesWonder = true;
}
}
#ifdef USE_BOTH_TECHBUILDING_EVALUATIONS
int iBuildingValue = 0;
if (kLoopBuilding.getSpecialBuildingType() != NO_BUILDING)
{
iBuildingValue += ((bCapitalAlone) ? 100 : 25);
}
else
{
iBuildingValue += ((bCapitalAlone) ? 200 : 50);
}
//the granary effect is SO powerful it deserves special code
if (kLoopBuilding.getFoodKept() > 0)
{
iBuildingValue += (15 * kLoopBuilding.getFoodKept());
}
/************************************************************************************************/
/* Afforess & Fuyu Start 16.07.2010 */
/************************************************************************************************/
if (kLoopBuilding.getAirlift() > 0)
{
iValue += 300;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (kLoopBuilding.getMaintenanceModifier() < 0)
{
int iLoop;
int iCount = 0;
CvCity* pLoopCity;
iTempValue = 0;
for (pLoopCity = firstCity(&iLoop, true); pLoopCity != NULL; pLoopCity = nextCity(&iLoop, true))
{
iTempValue += pLoopCity->getMaintenanceTimes100();
iCount++;
if (iCount > 4)
{
break;
}
}
iTempValue /= std::max(1, iCount);
iTempValue *= -kLoopBuilding.getMaintenanceModifier();
iTempValue /= 10 * 100;
iValue += iTempValue;
}
iBuildingValue += 100;
// If the building has an AI weight assume that's for a good reason and factor it in here, discounting
// it somwhat if its a special build
iBuildingValue += kLoopBuilding.getAIWeight()/(kLoopBuilding.getProductionCost() == -1 ? 5 : 1);
if ((GC.getBuildingClassInfo((BuildingClassTypes)iJ).getDefaultBuildingIndex()) != (GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iJ)))
{
//UB
iBuildingValue += 600;
}
if( !isLimitedWonderClass((BuildingClassTypes)iJ) )
{
if (kLoopBuilding.getCommerceChange(COMMERCE_CULTURE) > 0 || kLoopBuilding.getObsoleteSafeCommerceChange(COMMERCE_CULTURE) > 0)
{
bIsCultureBuilding = true;
}
}
if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE2))
{
int iMultiplier = (isLimitedWonderClass((BuildingClassTypes)iJ) ? 1 : 3);
iBuildingValue += (150 * (kLoopBuilding.getCommerceChange(COMMERCE_CULTURE) + kLoopBuilding.getObsoleteSafeCommerceChange(COMMERCE_CULTURE))) * iMultiplier;
iBuildingValue += kLoopBuilding.getCommerceModifier(COMMERCE_CULTURE) * 4 * iMultiplier ;
}
if (bFinancialTrouble)
{
iBuildingValue += (-kLoopBuilding.getMaintenanceModifier()) * 15;
iBuildingValue += kLoopBuilding.getYieldModifier(YIELD_COMMERCE) * 8;
iBuildingValue += kLoopBuilding.getCommerceModifier(COMMERCE_GOLD) * 15;
}
// if this is a religious building, its not as useful
ReligionTypes eReligion = (ReligionTypes) kLoopBuilding.getReligionType();
if (eReligion != NO_RELIGION)
{
// reduce by a factor based on how many cities we have with that relgion
if (iTeamCityCount > 0)
{
int iCitiesWithReligion = GET_TEAM(getTeam()).getHasReligionCount(eReligion);
iBuildingValue *= (4 + iCitiesWithReligion);
iBuildingValue /= (4 + iTeamCityCount);
}
// if this building requires a religion, then only count it as 1/7th as much
// or in other words, only count things like temples once, not 7 times
// doing it this way in case some mods give buildings to only one religion
iBuildingValue /= std::max(1, GC.getNumReligionInfos());
}
// if we're close to pop domination, we love medicine!
// don't adjust for negative modifiers to prevent ignoring assembly line, etc.
if ( AI_isDoVictoryStrategy(AI_VICTORY_DOMINATION3) && kLoopBuilding.getHealth() > 0)
{
iBuildingValue += kLoopBuilding.getHealth() * 150;
}
if (kLoopBuilding.getPrereqAndTech() == eTech)
{
if (iPathLength <= 1)
{
if (getTotalPopulation() > 5)
{
if (isWorldWonderClass((BuildingClassTypes)iJ))
{
if (!(GC.getGameINLINE().isBuildingClassMaxedOut((BuildingClassTypes)iJ)))
{
bEnablesWonder = true;
if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE1))
{
if (kLoopBuilding.getCommerceChange(COMMERCE_CULTURE) >= 3 ||
kLoopBuilding.getObsoleteSafeCommerceChange(COMMERCE_CULTURE) >= 3 ||
kLoopBuilding.getCommerceModifier(COMMERCE_CULTURE) >= 10)
{
iValue += 400;
}
}
if (bCapitalAlone)
{
iBuildingValue += 400;
}
}
}
}
}
}
if( AI_isDoVictoryStrategy(AI_VICTORY_DIPLOMACY1) )
{
if( kLoopBuilding.getVoteSourceType() >= 0 )
{
iValue += 400;
}
}
if (iBuildingValue > iBestLandBuildingValue)
{
iBestLandBuildingValue = iBuildingValue;
}
// if water building, weight by coastal cities
if (kLoopBuilding.isWater())
{
iBuildingValue *= iCoastalCities;
iBuildingValue /= std::max(1, iCityCount/2);
}
// if land building, is it the best?
else if (iBuildingValue > iBestLandBuildingValue)
{
iBestLandBuildingValue = iBuildingValue;
}
if ( gPlayerLogLevel > 2 )
{
logBBAI("\tBuilding %S old mechanism value: %d",
kLoopBuilding.getDescription(),
iBuildingValue);
}
#endif
// Now recalculate the new way
int iBuildingValue = 0;
int iEconomyFlags = (BUILDINGFOCUS_PRODUCTION |
BUILDINGFOCUS_FOOD |
BUILDINGFOCUS_GOLD |
BUILDINGFOCUS_RESEARCH |
BUILDINGFOCUS_MAINTENANCE |
BUILDINGFOCUS_HAPPY |
BUILDINGFOCUS_HEALTHY |
BUILDINGFOCUS_CULTURE |
BUILDINGFOCUS_SPECIALIST |
BUILDINGFOCUS_DEFENSE |
BUILDINGFOCUS_EXPERIENCE |
BUILDINGFOCUS_MAINTENANCE |
BUILDINGFOCUS_WORLDWONDER |
BUILDINGFOCUS_DOMAINSEA);
if (!GC.getGameINLINE().isOption(GAMEOPTION_NO_ESPIONAGE))
{
iEconomyFlags |= BUILDINGFOCUS_ESPIONAGE;
}
int iLoop;
CvCity* pLoopCity;
CvCity* pRepresentativeCity = NULL;
int iBestValue = 0;
int iTotalValue = 0;
int iTotalWeight = 0;
int iRepresentativeBuildingValueInCity = -1;
bool bCanConstructCityFound = false;
for (pLoopCity = firstCity(&iLoop, true); pLoopCity != NULL; pLoopCity = nextCity(&iLoop, true))
{
int iWillGetProbability;
if ( pRepresentativeCity == NULL )
{
pRepresentativeCity = pLoopCity;
}
if ( pLoopCity->canConstruct(eLoopBuilding, false, false, true, false, false, eTech, &iWillGetProbability) )
{
iWillGetProbability = 100;
if ( !bCanConstructCityFound )
{
bCanConstructCityFound = true;
pRepresentativeCity = pLoopCity;
}
}
iTotalWeight += iWillGetProbability;
}
if ( iTotalWeight > 0)
{
// Chances are we'll be doing evaluations for many buildings and tending to use the same representative
// cities, so get the canTrain cache fully populated so that cached values can be used in the evaluation
pRepresentativeCity->populateCanTrainCache(false);
// 2/3rds factor applied here since the representative city we are using is likely to be large (early id)
// and so over-represent a bit
iRepresentativeBuildingValueInCity = (2*BUILDING_VALUE_TO_TECH_BUILDING_VALUE_MULTIPLIER*pRepresentativeCity->AI_buildingValue(eLoopBuilding, iEconomyFlags, true))/3;
}
iBestValue = iRepresentativeBuildingValueInCity;
iTotalValue = (iRepresentativeBuildingValueInCity*iTotalWeight)/100;
if (isWorldWonderClass((BuildingClassTypes)(kLoopBuilding.getBuildingClassType())))
{
if (!(GC.getGameINLINE().isBuildingClassMaxedOut((BuildingClassTypes)iJ)))
{
iBuildingValue += 3*iBestValue; // Opportunity cost for denying others
}
}
else if (isTeamWonderClass((BuildingClassTypes)(kLoopBuilding.getBuildingClassType())) ||
isNationalWonderClass((BuildingClassTypes)(kLoopBuilding.getBuildingClassType())))
{
iBuildingValue += iBestValue;
}
else
{
iBuildingValue += iTotalValue;
}
// Average value per city
iBuildingValue /= std::max(1,getNumCities());
if ( gPlayerLogLevel > 2 )
{
logBBAI("\tBuilding %S new mechanism value: %d",
kLoopBuilding.getDescription(),
iBuildingValue);
}
iValue += iBuildingValue;
}
else
{
if (canConstruct(eLoopBuilding))
{
if (!isLimitedWonderClass((BuildingClassTypes)iJ))
{
if (kLoopBuilding.getCommerceChange(COMMERCE_CULTURE) > 0 || kLoopBuilding.getObsoleteSafeCommerceChange(COMMERCE_CULTURE) > 0)
{
iExistingCultureBuildingCount++;
}
}
int iNumExisting = 0;
int iLoop;
CvCity* pLoopCity;
for (pLoopCity = firstCity(&iLoop, true); pLoopCity != NULL; pLoopCity = nextCity(&iLoop, true))
{
if ( pLoopCity->getNumBuilding(eLoopBuilding) )
{
iNumExisting++;
}
}
if ( iNumExisting > 0 )
{
int iTempValue = 0;
int iJ;
for (iJ = 0; iJ < NUM_COMMERCE_TYPES; iJ++)
{
int iCommerceChange = kLoopBuilding.getTechCommerceChange(eTech, iJ);
int iCommerceModifier = kLoopBuilding.getTechCommerceModifier(eTech, iJ);
if ( iCommerceChange != 0 )
{
iTempValue += 400*iCommerceChange;
}
if ( iCommerceModifier != 0 )
{
iTempValue += (4*getCommerceRate((CommerceTypes)iJ)*iCommerceModifier)/getNumCities();
}
}
for (iJ = 0; iJ < NUM_YIELD_TYPES; iJ++)
{
int iYieldChange = kLoopBuilding.getTechYieldChange(eTech, iJ);
int iYieldModifier = kLoopBuilding.getTechYieldModifier(eTech, iJ);
if ( iYieldChange != 0 )
{
iTempValue += 400*iYieldChange;
}
if ( iYieldModifier != 0 )
{
iTempValue += (4*calculateTotalYield((YieldTypes)iJ)*iYieldModifier)/getNumCities();
}
}
for (iJ = 0; iJ < GC.getNumSpecialistInfos(); iJ++)
{
int iSpecialistChange = kLoopBuilding.getTechSpecialistChange(eTech, iJ);
if ( iSpecialistChange != 0 )
{
iTempValue += 800*iSpecialistChange;
}
}
if ( iTempValue != 0 )
{
iTempValue = (iTempValue * BUILDING_VALUE_TO_TECH_BUILDING_VALUE_MULTIPLIER)/100;
if ( gPlayerLogLevel > 2 )
{
logBBAI("\tBuilding %S is modified. We have %d of them, total value: %d",
kLoopBuilding.getDescription(),
iNumExisting,
iTempValue);
}
iValue += iTempValue;
}
}
}
}
}
}
#if 0
if (bIsCultureBuilding && iExistingCultureBuildingCount < 2)
{
if (getFreeCityCommerce(COMMERCE_CULTURE) == 0)
{
if (getNumCities() > 1)
{
iValue += 150 * std::max(1, (3 - 2 * iExistingCultureBuildingCount)) * (getNumCities() - 1);
}
}
}
// if tech gives at least one building (so we can count each individual building less)
if (iBestLandBuildingValue > 0)
{
iValue += iBestLandBuildingValue;
}
#endif
return iValue;
}
int CvPlayerAI::AI_techUnitValue( TechTypes eTech, int iPathLength, bool &bEnablesUnitWonder ) const
{
MEMORY_TRACK()
bool bWarPlan = (GET_TEAM(getTeam()).getAnyWarPlanCount(true) > 0);
if( !bWarPlan )
{
// Aggressive players will stick with war civics
if( GET_TEAM(getTeam()).AI_getTotalWarOddsTimes100() > 400 )
{
bWarPlan = true;
}
}
bool bCapitalAlone = (GC.getGameINLINE().getElapsedGameTurns() > 0) ? AI_isCapitalAreaAlone() : false;
int iHasMetCount = GET_TEAM(getTeam()).getHasMetCivCount(true);
int iCoastalCities = countNumCoastalCities();
CvCity* pCapitalCity = getCapitalCity();
UnitTypes eLoopUnit;
int iMilitaryValue = 0;
int iValue = 0;
bEnablesUnitWonder = false;
for (int iJ = 0; iJ < GC.getNumUnitClassInfos(); iJ++)
{
eLoopUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iJ)));
/************************************************************************************************/
/* Afforess Start 07/27/10 */
/* */
/* */
/************************************************************************************************/
/*
if (eLoopUnit != NO_UNIT)
*/
if (GC.getGameINLINE().canEverTrain(eLoopUnit))
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
if (isTechRequiredForUnit((eTech), eLoopUnit))
{
CvUnitInfo& kLoopUnit = GC.getUnitInfo(eLoopUnit);
iValue += 200;
int iUnitValue = 0;
int iNavalValue = 0;
if ((GC.getUnitClassInfo((UnitClassTypes)iJ).getDefaultUnitIndex()) != (GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iJ)))
{
//UU
iUnitValue += 600;
}
if (kLoopUnit.getPrereqAndTech() == eTech)
{
iMilitaryValue = 0;
// BBAI TODO: Change this to evaluating all unitai types defined in XML for unit?
// Without this change many unit types are hard to evaluate, like offensive value of rifles
// or defensive value of collateral seige
switch (kLoopUnit.getDefaultUnitAIType())
{
case UNITAI_UNKNOWN:
case UNITAI_ANIMAL:
case UNITAI_SUBDUED_ANIMAL:
case UNITAI_HUNTER:
break;
case UNITAI_SETTLE:
{
// Special case - if this would be our FIRST settler unit it has
// a much higher weighting
bool bHasSettler = false;
for (int iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
UnitTypes eLoopUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI)));
if (eLoopUnit != NO_UNIT)
{
if (GC.getUnitInfo(eLoopUnit).getDefaultUnitAIType() == UNITAI_SETTLE)
{
if ( canTrain(eLoopUnit) )
{
bHasSettler = true;
break;
}
}
}
}
if ( bHasSettler )
{
iUnitValue += 1200;
}
else
{
iUnitValue += 10000;
}
}
break;
case UNITAI_WORKER:
iUnitValue += 800;
break;
case UNITAI_ATTACK:
iMilitaryValue += ((bWarPlan) ? 600 : 300);
iMilitaryValue += (AI_isDoStrategy(AI_STRATEGY_DAGGER ) ? 800 : 0);
iUnitValue += 100;
break;
case UNITAI_ATTACK_CITY:
iMilitaryValue += ((bWarPlan) ? 800 : 400);
iMilitaryValue += (AI_isDoStrategy(AI_STRATEGY_DAGGER ) ? 800 : 0);
if (kLoopUnit.getBombardRate() > 0)
{
iMilitaryValue += 200;
if (AI_calculateTotalBombard(DOMAIN_LAND) == 0)
{
iMilitaryValue += 800;
if (AI_isDoStrategy(AI_STRATEGY_DAGGER))
{
iMilitaryValue += 1000;
}
}
}
iUnitValue += 100;
break;
case UNITAI_COLLATERAL:
iMilitaryValue += ((bWarPlan) ? 600 : 300);
break;
case UNITAI_PILLAGE:
iMilitaryValue += ((bWarPlan) ? 200 : 100);
break;
case UNITAI_PILLAGE_COUNTER:
iMilitaryValue += ((bWarPlan) ? 300 : 200);
break;
case UNITAI_RESERVE:
iMilitaryValue += ((bWarPlan) ? 200 : 100);
break;
case UNITAI_COUNTER:
iMilitaryValue += ((bWarPlan) ? 600 : 300);
iMilitaryValue += (AI_isDoStrategy(AI_STRATEGY_DAGGER ) ? 600 : 0);
break;
case UNITAI_PARADROP:
iMilitaryValue += ((bWarPlan) ? 600 : 300);
break;
case UNITAI_CITY_DEFENSE:
iMilitaryValue += ((bWarPlan) ? 800 : 400);
iMilitaryValue += ((!bCapitalAlone) ? 400 : 200);
iUnitValue += ((iHasMetCount > 0) ? 800 : 200);
break;
case UNITAI_CITY_COUNTER:
iMilitaryValue += ((bWarPlan) ? 800 : 400);
break;
case UNITAI_CITY_SPECIAL:
iMilitaryValue += ((bWarPlan) ? 800 : 400);
break;
case UNITAI_EXPLORE:
iUnitValue += ((bCapitalAlone) ? 100 : 200);
break;
case UNITAI_MISSIONARY:
iUnitValue += ((getStateReligion() != NO_RELIGION) ? 600 : 300);
break;
case UNITAI_PROPHET:
case UNITAI_ARTIST:
case UNITAI_SCIENTIST:
case UNITAI_GENERAL:
case UNITAI_MERCHANT:
case UNITAI_ENGINEER:
break;
case UNITAI_SPY:
iMilitaryValue += ((bWarPlan) ? 100 : 50);
break;
case UNITAI_ICBM:
iMilitaryValue += ((bWarPlan) ? 200 : 100);
break;
case UNITAI_WORKER_SEA:
if (iCoastalCities > 0)
{
// note, workboat improvements are already counted in the improvement section
}
break;
case UNITAI_ATTACK_SEA:
// BBAI TODO: Boost value for maps where Barb ships are pestering us
if (iCoastalCities > 0)
{
iMilitaryValue += ((bWarPlan) ? 200 : 100);
}
iNavalValue += 100;
break;
case UNITAI_RESERVE_SEA:
if (iCoastalCities > 0)
{
iMilitaryValue += ((bWarPlan) ? 100 : 50);
}
iNavalValue += 100;
break;
case UNITAI_ESCORT_SEA:
if (iCoastalCities > 0)
{
iMilitaryValue += ((bWarPlan) ? 100 : 50);
}
iNavalValue += 100;
break;
case UNITAI_EXPLORE_SEA:
if (iCoastalCities > 0)
{
iUnitValue += ((bCapitalAlone) ? 1800 : 600);
}
break;
case UNITAI_ASSAULT_SEA:
if (iCoastalCities > 0)
{
iMilitaryValue += ((bWarPlan || bCapitalAlone) ? 400 : 200);
}
iNavalValue += 200;
break;
case UNITAI_SETTLER_SEA:
if (iCoastalCities > 0)
{
iUnitValue += ((bWarPlan || bCapitalAlone) ? 100 : 200);
}
iNavalValue += 200;
break;
case UNITAI_MISSIONARY_SEA:
if (iCoastalCities > 0)
{
iUnitValue += 100;
}
break;
case UNITAI_SPY_SEA:
if (iCoastalCities > 0)
{
iMilitaryValue += 100;
}
break;
case UNITAI_CARRIER_SEA:
if (iCoastalCities > 0)
{
iMilitaryValue += ((bWarPlan) ? 100 : 50);
}
break;
case UNITAI_MISSILE_CARRIER_SEA:
if (iCoastalCities > 0)
{
iMilitaryValue += ((bWarPlan) ? 100 : 50);
}
break;
case UNITAI_PIRATE_SEA:
if (iCoastalCities > 0)
{
iMilitaryValue += 100;
}
iNavalValue += 100;
break;
case UNITAI_ATTACK_AIR:
iMilitaryValue += ((bWarPlan) ? 1200 : 800);
break;
case UNITAI_DEFENSE_AIR:
iMilitaryValue += ((bWarPlan) ? 1200 : 800);
break;
case UNITAI_CARRIER_AIR:
if (iCoastalCities > 0)
{
iMilitaryValue += ((bWarPlan) ? 200 : 100);
}
iNavalValue += 400;
break;
case UNITAI_MISSILE_AIR:
iMilitaryValue += ((bWarPlan) ? 200 : 100);
break;
default:
FAssert(false);
break;
}
if( AI_isDoStrategy(AI_STRATEGY_ALERT1) )
{
if( kLoopUnit.getUnitAIType(UNITAI_COLLATERAL) )
{
iUnitValue += 500;
}
if( kLoopUnit.getUnitAIType(UNITAI_CITY_DEFENSE) )
{
iUnitValue += (1000 * GC.getGameINLINE().AI_combatValue(eLoopUnit))/100;
}
}
if( AI_isDoStrategy(AI_STRATEGY_TURTLE) && iPathLength <= 1)
{
if( kLoopUnit.getUnitAIType(UNITAI_COLLATERAL) )
{
iUnitValue += 1000;
}
if( kLoopUnit.getUnitAIType(UNITAI_CITY_DEFENSE) )
{
iUnitValue += (2000 * GC.getGameINLINE().AI_combatValue(eLoopUnit))/100;
}
}
if( AI_isDoVictoryStrategy(AI_VICTORY_CONQUEST3) )
{
if( kLoopUnit.getUnitAIType(UNITAI_ATTACK_CITY) )
{
iUnitValue += (1500 * GC.getGameINLINE().AI_combatValue(eLoopUnit))/100;
}
}
else if( AI_isDoVictoryStrategy(AI_VICTORY_CONQUEST1) )
{
if( kLoopUnit.getUnitAIType(UNITAI_ATTACK_CITY) )
{
iUnitValue += (500 * GC.getGameINLINE().AI_combatValue(eLoopUnit))/100;
}
}
if (kLoopUnit.getUnitAIType(UNITAI_ASSAULT_SEA) && iCoastalCities > 0)
{
int iAssaultValue = 0;
UnitTypes eExistingUnit = NO_UNIT;
if (AI_bestAreaUnitAIValue(UNITAI_ASSAULT_SEA, NULL, &eExistingUnit) == 0)
{
iAssaultValue += 250;
}
else if( eExistingUnit != NO_UNIT )
{
iAssaultValue += 1000 * std::max(0, AI_unitImpassableCount(eLoopUnit) - AI_unitImpassableCount(eExistingUnit));
// Thomas SG - AC: Advanced Cargo START
int iNewCapacity = 0;
int iOldCapacity = 0;
iNewCapacity = kLoopUnit.getMoves() * kLoopUnit.getTotalCargoSpace();
iOldCapacity = GC.getUnitInfo(eExistingUnit).getMoves() * GC.getUnitInfo(eExistingUnit).getTotalCargoSpace();
iAssaultValue += (800 * (iNewCapacity - iOldCapacity)) / std::max(1, iOldCapacity);
}
// Thomas SG - AC: Advanced Cargo END
if (iAssaultValue > 0)
{
int iLoop;
CvArea* pLoopArea;
bool bIsAnyAssault = false;
for(pLoopArea = GC.getMapINLINE().firstArea(&iLoop); pLoopArea != NULL; pLoopArea = GC.getMapINLINE().nextArea(&iLoop))
{
if (AI_isPrimaryArea(pLoopArea))
{
if (pLoopArea->getAreaAIType(getTeam()) == AREAAI_ASSAULT)
{
bIsAnyAssault = true;
break;
}
}
}
if (bIsAnyAssault)
{
iUnitValue += iAssaultValue * 4;
}
else
{
iUnitValue += iAssaultValue;
}
}
}
if (iNavalValue > 0)
{
if (getCapitalCity() != NULL)
{
// BBAI TODO: A little odd ... naval value is 0 if have no colonies.
iNavalValue *= 2 * (getNumCities() - getCapitalCity()->area()->getCitiesPerPlayer(getID()));
iNavalValue /= getNumCities();
iUnitValue += iNavalValue;
}
}
if (AI_totalUnitAIs((UnitAITypes)(kLoopUnit.getDefaultUnitAIType())) == 0)
{
// do not give bonus to seagoing units if they are worthless
if (iUnitValue > 0)
{
iUnitValue *= 2;
}
if (kLoopUnit.getDefaultUnitAIType() == UNITAI_EXPLORE)
{
if (pCapitalCity != NULL)
{
iUnitValue += (AI_neededExplorers(pCapitalCity->area()) * 400);
}
}
if (kLoopUnit.getDefaultUnitAIType() == UNITAI_EXPLORE_SEA)
{
iUnitValue += 400;
iUnitValue += ((GC.getGameINLINE().countCivTeamsAlive() - iHasMetCount) * 200);
}
}
if (kLoopUnit.getUnitAIType(UNITAI_SETTLER_SEA))
{
if (getCapitalCity() != NULL)
{
UnitTypes eExistingUnit = NO_UNIT;
int iBestAreaValue = 0;
AI_getNumAreaCitySites(getCapitalCity()->getArea(), iBestAreaValue);
//Early Expansion by sea
if (AI_bestAreaUnitAIValue(UNITAI_SETTLER_SEA, NULL, &eExistingUnit) == 0)
{
CvArea* pWaterArea = getCapitalCity()->waterArea();
if (pWaterArea != NULL)
{
int iBestOtherValue = 0;
AI_getNumAdjacentAreaCitySites(pWaterArea->getID(), getCapitalCity()->getArea(), iBestOtherValue);
if (iBestAreaValue == 0)
{
iUnitValue += 2000;
}
else if (iBestAreaValue < iBestOtherValue)
{
iUnitValue += 1000;
}
else if (iBestOtherValue > 0)
{
iUnitValue += 500;
}
}
}
// Landlocked expansion over ocean
else if( eExistingUnit != NO_UNIT )
{
if( AI_unitImpassableCount(eLoopUnit) < AI_unitImpassableCount(eExistingUnit) )
{
if( iBestAreaValue < AI_getMinFoundValue() )
{
iUnitValue += (AI_isDoVictoryStrategy(AI_VICTORY_DOMINATION2) ? 2000 : 500);
}
}
}
}
}
if( iMilitaryValue > 0 )
{
if (iHasMetCount == 0)
{
iMilitaryValue /= 2;
}
if (bCapitalAlone)
{
iMilitaryValue *= 2;
iMilitaryValue /= 3;
}
// K-Mod
if (AI_isDoStrategy(AI_STRATEGY_GET_BETTER_UNITS))
{
iMilitaryValue *= 3;
iMilitaryValue /= 2;
}
iUnitValue += iMilitaryValue;
}
if (iPathLength <= 1)
{
if (getTotalPopulation() > 5)
{
if (isWorldUnitClass((UnitClassTypes)iJ))
{
if (!(GC.getGameINLINE().isUnitClassMaxedOut((UnitClassTypes)iJ)))
{
bEnablesUnitWonder = true;
}
}
}
}
iValue += iUnitValue;
}
}
}
/************************************************************************************************/
/* Afforess Start 6/11/11 */
/* */
/* */
/************************************************************************************************/
if (GC.getTechInfo(eTech).getUnitClassStrengthChange(iJ) != 0)
{
int iUnitValue = GC.getTechInfo(eTech).getUnitClassStrengthChange(iJ) * getUnitClassCountPlusMaking((UnitClassTypes)iJ) * 20;
iUnitValue += 50;
if (bWarPlan)
{
iUnitValue *= 3;
iUnitValue /= 2;
}
iValue += iUnitValue;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
return iValue;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
void CvPlayerAI::AI_chooseFreeTech()
{
TechTypes eBestTech;
clearResearchQueue();
/************************************************************************************************/
/* Afforess Start 04/29/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getUSE_AI_BESTTECH_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyArgsList argsList;
long lResult;
argsList.add(getID());
argsList.add(true);
lResult = -1;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "AI_chooseTech", argsList.makeFunctionArgs(), &lResult);
eBestTech = ((TechTypes)lResult);
}
else
{
eBestTech = NO_TECH;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (eBestTech == NO_TECH)
{
eBestTech = AI_bestTech(1, true);
}
if (eBestTech != NO_TECH)
{
GET_TEAM(getTeam()).setHasTech(eBestTech, true, getID(), true, true);
}
}
void CvPlayerAI::AI_startGoldenAge()
{
// Golden age start - reconsider civics at the first opportunity
AI_setCivicTimer( 0 );
}
void CvPlayerAI::AI_chooseResearch()
{
TechTypes eBestTech;
int iI;
clearResearchQueue();
if (getCurrentResearch() == NO_TECH)
{
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if ((iI != getID()) && (GET_PLAYER((PlayerTypes)iI).getTeam() == getTeam()))
{
if (GET_PLAYER((PlayerTypes)iI).getCurrentResearch() != NO_TECH)
{
if (canResearch(GET_PLAYER((PlayerTypes)iI).getCurrentResearch()))
{
pushResearch(GET_PLAYER((PlayerTypes)iI).getCurrentResearch());
}
}
}
}
}
}
if (getCurrentResearch() == NO_TECH)
{
/************************************************************************************************/
/* Afforess Start 04/29/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getUSE_AI_BESTTECH_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyArgsList argsList;
long lResult;
argsList.add(getID());
argsList.add(false);
lResult = -1;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "AI_chooseTech", argsList.makeFunctionArgs(), &lResult);
eBestTech = ((TechTypes)lResult);
}
else
{
eBestTech = NO_TECH;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (eBestTech == NO_TECH)
{
int iAIResearchDepth;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/08/10 jdog5000 */
/* */
/* Victory Strategy AI */
/************************************************************************************************/
iAIResearchDepth = AI_isDoVictoryStrategy(AI_VICTORY_CULTURE3) ? 1 : 3;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
eBestTech = AI_bestTech((isHuman()) ? 1 : iAIResearchDepth);
}
if (eBestTech != NO_TECH)
{
CvTechInfo& tech = GC.getTechInfo(eBestTech);
OutputDebugString(CvString::format("Game turn %d, AI chooses tech %S\n", GC.getGame().getGameTurn(), tech.getDescription()).c_str());
pushResearch(eBestTech);
}
}
}
DiploCommentTypes CvPlayerAI::AI_getGreeting(PlayerTypes ePlayer) const
{
TeamTypes eWorstEnemy;
if (GET_PLAYER(ePlayer).getTeam() != getTeam())
{
eWorstEnemy = GET_TEAM(getTeam()).AI_getWorstEnemy();
if ((eWorstEnemy != NO_TEAM) && (eWorstEnemy != GET_PLAYER(ePlayer).getTeam()) && GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isHasMet(eWorstEnemy) && (GC.getASyncRand().get(4) == 0))
{
if (GET_PLAYER(ePlayer).AI_hasTradedWithTeam(eWorstEnemy) && !atWar(GET_PLAYER(ePlayer).getTeam(), eWorstEnemy))
{
return (DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_WORST_ENEMY_TRADING");
}
else
{
return (DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_WORST_ENEMY");
}
}
else if ((getNumNukeUnits() > 0) && (GC.getASyncRand().get(4) == 0))
{
return (DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_NUKES");
}
else if ((GET_PLAYER(ePlayer).getPower() < getPower()) && AI_getAttitude(ePlayer) < ATTITUDE_PLEASED && (GC.getASyncRand().get(4) == 0))
{
return (DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_UNIT_BRAG");
}
}
return (DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_GREETINGS");
}
bool CvPlayerAI::AI_isWillingToTalk(PlayerTypes ePlayer) const
{
PROFILE_FUNC();
FAssertMsg(getPersonalityType() != NO_LEADER, "getPersonalityType() is not expected to be equal with NO_LEADER");
FAssertMsg(ePlayer != getID(), "shouldn't call this function on ourselves");
if (GET_PLAYER(ePlayer).getTeam() == getTeam()
|| GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isVassal(getTeam())
|| GET_TEAM(getTeam()).isVassal(GET_PLAYER(ePlayer).getTeam()))
{
return true;
}
if (GET_TEAM(getTeam()).isHuman())
{
return false;
}
/************************************************************************************************/
/* Afforess Start 07/12/10 */
/* */
/* */
/************************************************************************************************/
for (int iCivicOption = 0; iCivicOption < GC.getNumCivicOptionInfos(); iCivicOption++)
{
CivicTypes eCivic = GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)iCivicOption);
if (eCivic != NO_CIVIC && GC.getCivicInfo(eCivic).isCanNotCeaseRelations())
{
return true;
}
}
bool bRuthlessAI = GC.getGameINLINE().isOption(GAMEOPTION_RUTHLESS_AI);
if (bRuthlessAI)
{
if (AI_getMemoryCount(ePlayer, MEMORY_BACKSTAB) > 0)
{
return false;
}
}
// Afforess - cease contact counter
if (getCeaseContactCounter(ePlayer) > 0)
{
return false;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (atWar(getTeam(), GET_PLAYER(ePlayer).getTeam()))
{
int iRefuseDuration = (GC.getLeaderHeadInfo(getPersonalityType()).getRefuseToTalkWarThreshold() * ((GET_TEAM(getTeam()).AI_isChosenWar(GET_PLAYER(ePlayer).getTeam())) ? 2 : 1));
int iOurSuccess = 1 + GET_TEAM(getTeam()).AI_getWarSuccess(GET_PLAYER(ePlayer).getTeam());
int iTheirSuccess = 1 + GET_TEAM(GET_PLAYER(ePlayer).getTeam()).AI_getWarSuccess(getTeam());
if (iTheirSuccess > iOurSuccess * 2)
{
iRefuseDuration *= 20 + ((80 * iOurSuccess * 2) / iTheirSuccess);
iRefuseDuration /= 100;
}
if (GET_TEAM(getTeam()).AI_getAtWarCounter(GET_PLAYER(ePlayer).getTeam()) < iRefuseDuration)
{
return false;
}
if (GET_TEAM(getTeam()).isAVassal())
{
return false;
}
/************************************************************************************************/
/* Afforess Start 07/12/10 */
/* */
/* */
/************************************************************************************************/
if (GET_PLAYER(ePlayer).getNumCities() == 0)
{
return false;
}
if (bRuthlessAI)
{
if (!AI_isFinancialTrouble())
{
if (iOurSuccess * 2 > iTheirSuccess * 3)
{
return false;
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
else
{
if (AI_getMemoryCount(ePlayer, MEMORY_STOPPED_TRADING_RECENT) > 0)
{
return false;
}
}
return true;
}
// XXX what if already at war???
// Returns true if the AI wants to sneak attack...
bool CvPlayerAI::AI_demandRebukedSneak(PlayerTypes ePlayer) const
{
FAssertMsg(!isHuman(), "isHuman did not return false as expected");
FAssertMsg(ePlayer != getID(), "shouldn't call this function on ourselves");
FAssert(!(GET_TEAM(getTeam()).isAVassal()));
FAssert(!(GET_TEAM(getTeam()).isHuman()));
if (GC.getGameINLINE().getSorenRandNum(100, "AI Demand Rebuked") < GC.getLeaderHeadInfo(getPersonalityType()).getDemandRebukedSneakProb())
{
if (GET_TEAM(getTeam()).getPower(true) > GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getDefensivePower(getTeam()))
{
return true;
}
}
return false;
}
// XXX what if already at war???
// Returns true if the AI wants to declare war...
bool CvPlayerAI::AI_demandRebukedWar(PlayerTypes ePlayer) const
{
FAssertMsg(!isHuman(), "isHuman did not return false as expected");
FAssertMsg(ePlayer != getID(), "shouldn't call this function on ourselves");
FAssert(!(GET_TEAM(getTeam()).isAVassal()));
FAssert(!(GET_TEAM(getTeam()).isHuman()));
// needs to be async because it only happens on the computer of the player who is in diplomacy...
if (GC.getASyncRand().get(100, "AI Demand Rebuked ASYNC") < GC.getLeaderHeadInfo(getPersonalityType()).getDemandRebukedWarProb())
{
if (GET_TEAM(getTeam()).getPower(true) > GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getDefensivePower())
{
if (GET_TEAM(getTeam()).AI_isAllyLandTarget(GET_PLAYER(ePlayer).getTeam()))
{
return true;
}
}
}
return false;
}
// XXX maybe make this a little looser (by time...)
bool CvPlayerAI::AI_hasTradedWithTeam(TeamTypes eTeam) const
{
int iI;
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (GET_PLAYER((PlayerTypes)iI).getTeam() == eTeam)
{
if ((AI_getPeacetimeGrantValue((PlayerTypes)iI) + AI_getPeacetimeTradeValue((PlayerTypes)iI)) > 0)
{
return true;
}
}
}
}
return false;
}
// static
AttitudeTypes CvPlayerAI::AI_getAttitudeFromValue(int iAttitudeVal)
{
if (iAttitudeVal >= 10)
{
return ATTITUDE_FRIENDLY;
}
else if (iAttitudeVal >= 3)
{
return ATTITUDE_PLEASED;
}
else if (iAttitudeVal <= -10)
{
return ATTITUDE_FURIOUS;
}
else if (iAttitudeVal <= -3)
{
return ATTITUDE_ANNOYED;
}
else
{
return ATTITUDE_CAUTIOUS;
}
}
AttitudeTypes CvPlayerAI::AI_getAttitude(PlayerTypes ePlayer, bool bForced) const
{
PROFILE_FUNC();
if (ePlayer < 0 || ePlayer > MAX_PLAYERS)
{
FAssert(false);
return NO_ATTITUDE;
}
if ( GET_PLAYER(ePlayer).isAlive() )
{
// FAssertMsg(ePlayer != getID(), "shouldn't call this function on ourselves");
//AI Autoplay calls this
return (AI_getAttitudeFromValue(AI_getAttitudeVal(ePlayer, bForced)));
}
else
{
throw new std::exception();
return NO_ATTITUDE;
}
}
int CvPlayerAI::AI_getAttitudeVal(PlayerTypes ePlayer, bool bForced) const
{
PROFILE_FUNC();
int iRankDifference;
int iAttitude;
int iI;
// FAssertMsg(ePlayer != getID(), "shouldn't call this function on ourselves");
//AI Autoplay calls this
if (bForced)
{
if (getTeam() == GET_PLAYER(ePlayer).getTeam() || (GET_TEAM(getTeam()).isVassal(GET_PLAYER(ePlayer).getTeam()) && !GET_TEAM(getTeam()).isCapitulated()))
{
return 100;
}
if (isBarbarian() || GET_PLAYER(ePlayer).isBarbarian())
{
return -100;
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 09/03/09 poyuzhe & jdog5000 */
/* */
/* Efficiency */
/************************************************************************************************/
// From Sanguo Mod Performance, ie the CAR Mod
// Attitude cache
if (m_aiAttitudeCache[ePlayer] != MAX_INT)
{
return m_aiAttitudeCache[ePlayer];
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
iAttitude = GC.getLeaderHeadInfo(getPersonalityType()).getBaseAttitude();
iAttitude += GC.getHandicapInfo(GET_PLAYER(ePlayer).getHandicapType()).getAttitudeChange();
// if (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI))
// {
// if (GET_PLAYER(ePlayer).isHuman())
// {
// iAttitude -= 2;
// }
// }
if (!(GET_PLAYER(ePlayer).isHuman()))
{
iAttitude += (4 - abs(AI_getPeaceWeight() - GET_PLAYER(ePlayer).AI_getPeaceWeight()));
iAttitude += std::min(GC.getLeaderHeadInfo(getPersonalityType()).getWarmongerRespect(), GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPersonalityType()).getWarmongerRespect());
}
iAttitude -= std::max(0, (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getNumMembers() - GET_TEAM(getTeam()).getNumMembers()));
iRankDifference = (GC.getGameINLINE().getPlayerRank(getID()) - GC.getGameINLINE().getPlayerRank(ePlayer));
if (iRankDifference > 0)
{
iAttitude += ((GC.getLeaderHeadInfo(getPersonalityType()).getWorseRankDifferenceAttitudeChange() * iRankDifference) / (GC.getGameINLINE().countCivPlayersEverAlive() + 1));
}
else
{
iAttitude += ((GC.getLeaderHeadInfo(getPersonalityType()).getBetterRankDifferenceAttitudeChange() * -(iRankDifference)) / (GC.getGameINLINE().countCivPlayersEverAlive() + 1));
}
if ((GC.getGameINLINE().getPlayerRank(getID()) >= (GC.getGameINLINE().countCivPlayersEverAlive() / 2)) &&
(GC.getGameINLINE().getPlayerRank(ePlayer) >= (GC.getGameINLINE().countCivPlayersEverAlive() / 2)))
{
iAttitude++;
}
if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).AI_getWarSuccess(getTeam()) > GET_TEAM(getTeam()).AI_getWarSuccess(GET_PLAYER(ePlayer).getTeam()))
{
iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getLostWarAttitudeChange();
}
iAttitude += AI_getCloseBordersAttitude(ePlayer);
iAttitude += AI_getWarAttitude(ePlayer);
iAttitude += AI_getPeaceAttitude(ePlayer);
iAttitude += AI_getSameReligionAttitude(ePlayer);
iAttitude += AI_getDifferentReligionAttitude(ePlayer);
iAttitude += AI_getBonusTradeAttitude(ePlayer);
iAttitude += AI_getOpenBordersAttitude(ePlayer);
iAttitude += AI_getDefensivePactAttitude(ePlayer);
iAttitude += AI_getRivalDefensivePactAttitude(ePlayer);
iAttitude += AI_getRivalVassalAttitude(ePlayer);
iAttitude += AI_getShareWarAttitude(ePlayer);
iAttitude += AI_getFavoriteCivicAttitude(ePlayer);
iAttitude += AI_getTradeAttitude(ePlayer);
iAttitude += AI_getRivalTradeAttitude(ePlayer);
/************************************************************************************************/
/* Afforess Start 12/14/09 */
/* */
/* */
/************************************************************************************************/
iAttitude += AI_getCivicShareAttitude(ePlayer);
iAttitude += AI_getEmbassyAttitude(ePlayer);
iAttitude += AI_getCivicAttitudeChange(ePlayer);
iAttitude += AI_getWeakestRelationsAttitudeChange(ePlayer);
iAttitude += AI_getPowerfulRelationsAttitudeChange(ePlayer);
iAttitude += AI_getLessPowerfulRelationsAttitudeChange(ePlayer);
iAttitude += GET_PLAYER(ePlayer).AI_getCivicRelationsAttitudeChange();
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
for (iI = 0; iI < NUM_MEMORY_TYPES; iI++)
{
iAttitude += AI_getMemoryAttitude(ePlayer, ((MemoryTypes)iI));
}
iAttitude += AI_getColonyAttitude(ePlayer);
iAttitude += AI_getAttitudeExtra(ePlayer);
/************************************************************************************************/
/* REVOLUTION_MOD 05/18/08 jdog5000 */
/* */
/* Revolution AI */
/************************************************************************************************/
if( GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isRebelAgainst(getTeam()) )
{
iAttitude -= 5;
}
else if( GET_TEAM(getTeam()).isRebelAgainst(GET_PLAYER(ePlayer).getTeam()) )
{
iAttitude -= 3;
}
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
/************************************************************************************************/
/* Afforess Start 06/01/10 */
/* */
/* Ruthless AI: The Enemy of Our Enemy is our Friend! */
/************************************************************************************************/
if (GC.getGameINLINE().isOption(GAMEOPTION_RUTHLESS_AI))
{
if (GET_TEAM(getTeam()).AI_getWorstEnemy() != NO_TEAM && GET_TEAM(GET_PLAYER(ePlayer).getTeam()).AI_getWorstEnemy() == GET_TEAM(getTeam()).AI_getWorstEnemy())
{
iAttitude += 2;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
// Afforess: Warmonger index hurts attitude
int iWarMongererDivisor = GC.getDefineINT("WARMONGERER_INDEX_ATTITUDE_DIVISOR", 500);
if (iWarMongererDivisor > 0)
{
//Cap the attitude hit to 5
iAttitude -= std::min(5, GET_PLAYER(ePlayer).getWarMongererIndex() / iWarMongererDivisor);
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 09/03/09 poyuzhe & jdog5000 */
/* */
/* Efficiency */
/************************************************************************************************/
// From Sanguo Mod Performance, ie the CAR Mod
// Attitude cache
m_aiAttitudeCache[ePlayer] = range(iAttitude, -100, 100);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
return range(iAttitude, -100, 100);
}
// BEGIN: Show Hidden Attitude Mod 01/22/2009
bool isShowPersonalityModifiers()
{
#ifdef _MOD_SHAM_SPOILER
return true;
#else
return !GC.getGameINLINE().isOption(GAMEOPTION_RANDOM_PERSONALITIES) || GC.getGameINLINE().isDebugMode();
#endif
}
bool isShowSpoilerModifiers()
{
#ifdef _MOD_SHAM_SPOILER
return true;
#else
return GC.getGameINLINE().isDebugMode();
#endif
}
int CvPlayerAI::AI_getFirstImpressionAttitude(PlayerTypes ePlayer) const
{
bool bShowPersonalityAttitude = isShowPersonalityModifiers();
CvPlayerAI& kPlayer = GET_PLAYER(ePlayer);
int iAttitude = GC.getHandicapInfo(kPlayer.getHandicapType()).getAttitudeChange();
//ls612: If you Start as Minors the first impression is not important
if (GC.getGameINLINE().isOption(GAMEOPTION_START_AS_MINORS))
{
return 0;
}
if (bShowPersonalityAttitude)
{
iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getBaseAttitude();
if (!kPlayer.isHuman())
{
if (isShowSpoilerModifiers())
{
// iBasePeaceWeight + iPeaceWeightRand
iAttitude += (4 - abs(AI_getPeaceWeight() - kPlayer.AI_getPeaceWeight()));
}
else
{
// iBasePeaceWeight
iAttitude += (4 - abs(GC.getLeaderHeadInfo(getPersonalityType()).getBasePeaceWeight() - GC.getLeaderHeadInfo(kPlayer.getPersonalityType()).getBasePeaceWeight()));
}
iAttitude += std::min(GC.getLeaderHeadInfo(getPersonalityType()).getWarmongerRespect(), GC.getLeaderHeadInfo(kPlayer.getPersonalityType()).getWarmongerRespect());
}
}
return iAttitude;
}
int CvPlayerAI::AI_getTeamSizeAttitude(PlayerTypes ePlayer) const
{
return -std::max(0, (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getNumMembers() - GET_TEAM(getTeam()).getNumMembers()));
}
// Count only players visible on the active player's scoreboard
int CvPlayerAI::AI_getKnownPlayerRank(PlayerTypes ePlayer) const
{
PlayerTypes eActivePlayer = GC.getGameINLINE().getActivePlayer();
if (NO_PLAYER == eActivePlayer || GC.getGameINLINE().isDebugMode()) {
// Use the full scoreboard
return GC.getGameINLINE().getPlayerRank(ePlayer);
}
TeamTypes eActiveTeam = GC.getGameINLINE().getActiveTeam();
int iRank = 0;
for (int iI = 0; iI < MAX_CIV_PLAYERS; iI++)
{
PlayerTypes eRankPlayer = GC.getGameINLINE().getRankPlayer(iI);
if (eRankPlayer != NO_PLAYER)
{
CvTeam& kRankTeam = GET_TEAM(GET_PLAYER(eRankPlayer).getTeam());
if (kRankTeam.isAlive() && (kRankTeam.isHasMet(eActiveTeam) || kRankTeam.isHuman()))
{
if (eRankPlayer == ePlayer) {
return iRank;
}
iRank++;
}
}
}
// Should only get here if we tried to find the rank of an unknown player
return iRank + 1;
}
int CvPlayerAI::AI_getBetterRankDifferenceAttitude(PlayerTypes ePlayer) const
{
if (!isShowPersonalityModifiers())
{
return 0;
}
int iRankDifference;
if (isShowSpoilerModifiers())
{
iRankDifference = GC.getGameINLINE().getPlayerRank(ePlayer) - GC.getGameINLINE().getPlayerRank(getID());
}
else
{
iRankDifference = AI_getKnownPlayerRank(ePlayer) - AI_getKnownPlayerRank(getID());
}
if (iRankDifference > 0)
{
return GC.getLeaderHeadInfo(getPersonalityType()).getBetterRankDifferenceAttitudeChange() * iRankDifference / (GC.getGameINLINE().countCivPlayersEverAlive() + 1);
}
return 0;
}
int CvPlayerAI::AI_getWorseRankDifferenceAttitude(PlayerTypes ePlayer) const
{
if (!isShowPersonalityModifiers())
{
return 0;
}
int iRankDifference;
if (isShowSpoilerModifiers())
{
iRankDifference = GC.getGameINLINE().getPlayerRank(getID()) - GC.getGameINLINE().getPlayerRank(ePlayer);
}
else
{
iRankDifference = AI_getKnownPlayerRank(getID()) - AI_getKnownPlayerRank(ePlayer);
}
if (iRankDifference > 0)
{
return GC.getLeaderHeadInfo(getPersonalityType()).getWorseRankDifferenceAttitudeChange() * iRankDifference / (GC.getGameINLINE().countCivPlayersEverAlive() + 1);
}
return 0;
}
int CvPlayerAI::AI_getLowRankAttitude(PlayerTypes ePlayer) const
{
int iThisPlayerRank;
int iPlayerRank;
if (isShowSpoilerModifiers())
{
iThisPlayerRank = GC.getGameINLINE().getPlayerRank(getID());
iPlayerRank = GC.getGameINLINE().getPlayerRank(ePlayer);
}
else
{
iThisPlayerRank = AI_getKnownPlayerRank(getID());
iPlayerRank = AI_getKnownPlayerRank(ePlayer);
}
int iMedianRank = GC.getGameINLINE().countCivPlayersEverAlive() / 2;
return (iThisPlayerRank >= iMedianRank && iPlayerRank >= iMedianRank) ? 1 : 0;
}
int CvPlayerAI::AI_getLostWarAttitude(PlayerTypes ePlayer) const
{
if (!isShowPersonalityModifiers())
{
return 0;
}
TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam();
if (!isShowSpoilerModifiers() && NO_PLAYER != GC.getGameINLINE().getActivePlayer())
{
// Hide war success for wars you are not involved in
if (GC.getGameINLINE().getActiveTeam() != getTeam() && GC.getGameINLINE().getActiveTeam() != eTeam)
{
return 0;
}
}
if (GET_TEAM(eTeam).AI_getWarSuccess(getTeam()) > GET_TEAM(getTeam()).AI_getWarSuccess(eTeam))
{
return GC.getLeaderHeadInfo(getPersonalityType()).getLostWarAttitudeChange();
}
return 0;
}
// END: Show Hidden Attitude Mod
int CvPlayerAI::AI_calculateStolenCityRadiusPlots(PlayerTypes ePlayer) const
{
PROFILE_FUNC();
CvPlot* pLoopPlot;
int iCount;
int iI;
FAssert(ePlayer != getID());
iCount = 0;
for (iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
{
pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
if (pLoopPlot->getOwnerINLINE() == ePlayer)
{
if (pLoopPlot->isPlayerCityRadius(getID()))
{
iCount++;
}
}
}
return iCount;
}
int CvPlayerAI::AI_getCloseBordersAttitude(PlayerTypes ePlayer) const
{
if (m_aiCloseBordersAttitudeCache[ePlayer] == MAX_INT)
{
PROFILE_FUNC();
int iPercent;
if (getTeam() == GET_PLAYER(ePlayer).getTeam() || GET_TEAM(getTeam()).isVassal(GET_PLAYER(ePlayer).getTeam()) || GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isVassal(getTeam()))
{
return 0;
}
iPercent = std::min(60, (AI_calculateStolenCityRadiusPlots(ePlayer) * 3));
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 06/12/10 jdog5000 */
/* */
/* Bugfix, Victory Strategy AI */
/************************************************************************************************/
if (GET_TEAM(getTeam()).AI_isLandTarget(GET_PLAYER(ePlayer).getTeam(), true))
{
iPercent += 40;
}
if( AI_isDoStrategy(AI_VICTORY_CONQUEST3) )
{
iPercent = std::min( 120, (3 * iPercent)/2 );
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
m_aiCloseBordersAttitudeCache[ePlayer] = ((GC.getLeaderHeadInfo(getPersonalityType()).getCloseBordersAttitudeChange() * iPercent) / 100);
}
return m_aiCloseBordersAttitudeCache[ePlayer];
}
int CvPlayerAI::AI_getWarAttitude(PlayerTypes ePlayer) const
{
int iAttitudeChange;
int iAttitude;
iAttitude = 0;
if (atWar(getTeam(), GET_PLAYER(ePlayer).getTeam()))
{
iAttitude -= 3;
}
if (GC.getLeaderHeadInfo(getPersonalityType()).getAtWarAttitudeDivisor() != 0)
{
iAttitudeChange = (GET_TEAM(getTeam()).AI_getAtWarCounter(GET_PLAYER(ePlayer).getTeam()) / GC.getLeaderHeadInfo(getPersonalityType()).getAtWarAttitudeDivisor());
iAttitude += range(iAttitudeChange, -(abs(GC.getLeaderHeadInfo(getPersonalityType()).getAtWarAttitudeChangeLimit())), abs(GC.getLeaderHeadInfo(getPersonalityType()).getAtWarAttitudeChangeLimit()));
}
return iAttitude;
}
int CvPlayerAI::AI_getPeaceAttitude(PlayerTypes ePlayer) const
{
int iAttitudeChange;
if (GC.getLeaderHeadInfo(getPersonalityType()).getAtPeaceAttitudeDivisor() != 0)
{
iAttitudeChange = (GET_TEAM(getTeam()).AI_getAtPeaceCounter(GET_PLAYER(ePlayer).getTeam()) / GC.getLeaderHeadInfo(getPersonalityType()).getAtPeaceAttitudeDivisor());
return range(iAttitudeChange, -(abs(GC.getLeaderHeadInfo(getPersonalityType()).getAtPeaceAttitudeChangeLimit())), abs(GC.getLeaderHeadInfo(getPersonalityType()).getAtPeaceAttitudeChangeLimit()));
}
return 0;
}
int CvPlayerAI::AI_getSameReligionAttitude(PlayerTypes ePlayer) const
{
int iAttitudeChange;
int iAttitude;
iAttitude = 0;
if ((getStateReligion() != NO_RELIGION) && (getStateReligion() == GET_PLAYER(ePlayer).getStateReligion()))
{
iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getSameReligionAttitudeChange();
if (hasHolyCity(getStateReligion()))
{
iAttitude++;
}
if (GC.getLeaderHeadInfo(getPersonalityType()).getSameReligionAttitudeDivisor() != 0)
{
iAttitudeChange = (AI_getSameReligionCounter(ePlayer) / GC.getLeaderHeadInfo(getPersonalityType()).getSameReligionAttitudeDivisor());
iAttitude += range(iAttitudeChange, -(abs(GC.getLeaderHeadInfo(getPersonalityType()).getSameReligionAttitudeChangeLimit())), abs(GC.getLeaderHeadInfo(getPersonalityType()).getSameReligionAttitudeChangeLimit()));
}
}
return iAttitude;
}
int CvPlayerAI::AI_getDifferentReligionAttitude(PlayerTypes ePlayer) const
{
int iAttitudeChange;
int iAttitude;
iAttitude = 0;
if ((getStateReligion() != NO_RELIGION) && (GET_PLAYER(ePlayer).getStateReligion() != NO_RELIGION) && (getStateReligion() != GET_PLAYER(ePlayer).getStateReligion()))
{
iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getDifferentReligionAttitudeChange();
if (hasHolyCity(getStateReligion()))
{
iAttitude--;
}
if (GC.getLeaderHeadInfo(getPersonalityType()).getDifferentReligionAttitudeDivisor() != 0)
{
iAttitudeChange = (AI_getDifferentReligionCounter(ePlayer) / GC.getLeaderHeadInfo(getPersonalityType()).getDifferentReligionAttitudeDivisor());
iAttitude += range(iAttitudeChange, -(abs(GC.getLeaderHeadInfo(getPersonalityType()).getDifferentReligionAttitudeChangeLimit())), abs(GC.getLeaderHeadInfo(getPersonalityType()).getDifferentReligionAttitudeChangeLimit()));
}
}
return iAttitude;
}
int CvPlayerAI::AI_getBonusTradeAttitude(PlayerTypes ePlayer) const
{
int iAttitudeChange;
if (!atWar(getTeam(), GET_PLAYER(ePlayer).getTeam()))
{
if (GC.getLeaderHeadInfo(getPersonalityType()).getBonusTradeAttitudeDivisor() != 0)
{
iAttitudeChange = (AI_getBonusTradeCounter(ePlayer) / GC.getLeaderHeadInfo(getPersonalityType()).getBonusTradeAttitudeDivisor());
return range(iAttitudeChange, -(abs(GC.getLeaderHeadInfo(getPersonalityType()).getBonusTradeAttitudeChangeLimit())), abs(GC.getLeaderHeadInfo(getPersonalityType()).getBonusTradeAttitudeChangeLimit()));
}
}
return 0;
}
int CvPlayerAI::AI_getOpenBordersAttitude(PlayerTypes ePlayer) const
{
int iAttitudeChange;
if (!atWar(getTeam(), GET_PLAYER(ePlayer).getTeam()))
{
if (GC.getLeaderHeadInfo(getPersonalityType()).getOpenBordersAttitudeDivisor() != 0)
{
iAttitudeChange = (GET_TEAM(getTeam()).AI_getOpenBordersCounter(GET_PLAYER(ePlayer).getTeam()) / GC.getLeaderHeadInfo(getPersonalityType()).getOpenBordersAttitudeDivisor());
return range(iAttitudeChange, -(abs(GC.getLeaderHeadInfo(getPersonalityType()).getOpenBordersAttitudeChangeLimit())), abs(GC.getLeaderHeadInfo(getPersonalityType()).getOpenBordersAttitudeChangeLimit()));
}
}
return 0;
}
int CvPlayerAI::AI_getDefensivePactAttitude(PlayerTypes ePlayer) const
{
int iAttitudeChange;
if (getTeam() != GET_PLAYER(ePlayer).getTeam() && (GET_TEAM(getTeam()).isVassal(GET_PLAYER(ePlayer).getTeam()) || GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isVassal(getTeam())))
{
return GC.getLeaderHeadInfo(getPersonalityType()).getDefensivePactAttitudeChangeLimit();
}
if (!atWar(getTeam(), GET_PLAYER(ePlayer).getTeam()))
{
if (GC.getLeaderHeadInfo(getPersonalityType()).getDefensivePactAttitudeDivisor() != 0)
{
iAttitudeChange = (GET_TEAM(getTeam()).AI_getDefensivePactCounter(GET_PLAYER(ePlayer).getTeam()) / GC.getLeaderHeadInfo(getPersonalityType()).getDefensivePactAttitudeDivisor());
return range(iAttitudeChange, -(abs(GC.getLeaderHeadInfo(getPersonalityType()).getDefensivePactAttitudeChangeLimit())), abs(GC.getLeaderHeadInfo(getPersonalityType()).getDefensivePactAttitudeChangeLimit()));
}
}
return 0;
}
int CvPlayerAI::AI_getRivalDefensivePactAttitude(PlayerTypes ePlayer) const
{
int iAttitude = 0;
if (getTeam() == GET_PLAYER(ePlayer).getTeam() || GET_TEAM(getTeam()).isVassal(GET_PLAYER(ePlayer).getTeam()) || GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isVassal(getTeam()))
{
return iAttitude;
}
if (!(GET_TEAM(getTeam()).isDefensivePact(GET_PLAYER(ePlayer).getTeam())))
{
iAttitude -= ((4 * GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getDefensivePactCount(GET_PLAYER(ePlayer).getTeam())) / std::max(1, (GC.getGameINLINE().countCivTeamsAlive() - 2)));
}
return iAttitude;
}
int CvPlayerAI::AI_getRivalVassalAttitude(PlayerTypes ePlayer) const
{
int iAttitude = 0;
if (getTeam() == GET_PLAYER(ePlayer).getTeam() || GET_TEAM(getTeam()).isVassal(GET_PLAYER(ePlayer).getTeam()) || GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isVassal(getTeam()))
{
return iAttitude;
}
if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getVassalCount(getTeam()) > 0)
{
iAttitude -= (6 * GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPower(true)) / std::max(1, GC.getGameINLINE().countTotalCivPower());
}
return iAttitude;
}
int CvPlayerAI::AI_getShareWarAttitude(PlayerTypes ePlayer) const
{
int iAttitudeChange;
int iAttitude;
iAttitude = 0;
if (!atWar(getTeam(), GET_PLAYER(ePlayer).getTeam()))
{
if (GET_TEAM(getTeam()).AI_shareWar(GET_PLAYER(ePlayer).getTeam()))
{
iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getShareWarAttitudeChange();
}
if (GC.getLeaderHeadInfo(getPersonalityType()).getShareWarAttitudeDivisor() != 0)
{
iAttitudeChange = (GET_TEAM(getTeam()).AI_getShareWarCounter(GET_PLAYER(ePlayer).getTeam()) / GC.getLeaderHeadInfo(getPersonalityType()).getShareWarAttitudeDivisor());
iAttitude += range(iAttitudeChange, -(abs(GC.getLeaderHeadInfo(getPersonalityType()).getShareWarAttitudeChangeLimit())), abs(GC.getLeaderHeadInfo(getPersonalityType()).getShareWarAttitudeChangeLimit()));
}
}
return iAttitude;
}
int CvPlayerAI::AI_getFavoriteCivicAttitude(PlayerTypes ePlayer) const
{
int iAttitudeChange;
int iAttitude;
iAttitude = 0;
if (GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivic() != NO_CIVIC)
{
if (isCivic((CivicTypes)(GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivic())) && GET_PLAYER(ePlayer).isCivic((CivicTypes)(GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivic())))
{
iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivicAttitudeChange();
if (GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivicAttitudeDivisor() != 0)
{
iAttitudeChange = (AI_getFavoriteCivicCounter(ePlayer) / GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivicAttitudeDivisor());
iAttitude += range(iAttitudeChange, -(abs(GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivicAttitudeChangeLimit())), abs(GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivicAttitudeChangeLimit()));
}
}
}
return iAttitude;
}
int CvPlayerAI::AI_getTradeAttitude(PlayerTypes ePlayer) const
{
// XXX human only?
return range(((AI_getPeacetimeGrantValue(ePlayer) + std::max(0, (AI_getPeacetimeTradeValue(ePlayer) - GET_PLAYER(ePlayer).AI_getPeacetimeTradeValue(getID())))) / ((GET_TEAM(getTeam()).AI_getHasMetCounter(GET_PLAYER(ePlayer).getTeam()) + 1) * 5)), 0, 4);
}
int CvPlayerAI::AI_getRivalTradeAttitude(PlayerTypes ePlayer) const
{
// XXX human only?
return -(range(((GET_TEAM(getTeam()).AI_getEnemyPeacetimeGrantValue(GET_PLAYER(ePlayer).getTeam()) + (GET_TEAM(getTeam()).AI_getEnemyPeacetimeTradeValue(GET_PLAYER(ePlayer).getTeam()) / 3)) / ((GET_TEAM(getTeam()).AI_getHasMetCounter(GET_PLAYER(ePlayer).getTeam()) + 1) * 10)), 0, 4));
}
int CvPlayerAI::AI_getMemoryAttitude(PlayerTypes ePlayer, MemoryTypes eMemory) const
{
return ((AI_getMemoryCount(ePlayer, eMemory) * GC.getLeaderHeadInfo(getPersonalityType()).getMemoryAttitudePercent(eMemory)) / 100);
}
int CvPlayerAI::AI_getColonyAttitude(PlayerTypes ePlayer) const
{
int iAttitude = 0;
if (getParent() == ePlayer)
{
iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getFreedomAppreciation();
}
return iAttitude;
}
PlayerVoteTypes CvPlayerAI::AI_diploVote(const VoteSelectionSubData& kVoteData, VoteSourceTypes eVoteSource, bool bPropose)
{
PROFILE_FUNC();
CivicTypes eBestCivic;
int iOpenCount;
int iClosedCount;
int iValue;
int iBestValue;
int iI;
VoteTypes eVote = kVoteData.eVote;
if (GC.getGameINLINE().isTeamVote(eVote))
{
if (GC.getGameINLINE().isTeamVoteEligible(getTeam(), eVoteSource))
{
return (PlayerVoteTypes)getTeam();
}
if (GC.getVoteInfo(eVote).isVictory())
{
iBestValue = 7;
}
else
{
iBestValue = 0;
}
PlayerVoteTypes eBestTeam = PLAYER_VOTE_ABSTAIN;
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (GET_TEAM((TeamTypes)iI).isAlive())
{
if (GC.getGameINLINE().isTeamVoteEligible((TeamTypes)iI, eVoteSource))
{
if (GET_TEAM(getTeam()).isVassal((TeamTypes)iI))
{
return (PlayerVoteTypes)iI;
}
iValue = GET_TEAM(getTeam()).AI_getAttitudeVal((TeamTypes)iI);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestTeam = (PlayerVoteTypes)iI;
}
}
}
}
return eBestTeam;
}
else
{
TeamTypes eSecretaryGeneral = GC.getGameINLINE().getSecretaryGeneral(eVoteSource);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 12/30/08 jdog5000 */
/* */
/* Diplomacy AI */
/************************************************************************************************/
/* original BTS code
if (!bPropose)
{
if (eSecretaryGeneral != NO_TEAM)
{
if (eSecretaryGeneral == getTeam() ||(GET_TEAM(getTeam()).AI_getAttitude(eSecretaryGeneral) == ATTITUDE_FRIENDLY))
{
return PLAYER_VOTE_YES;
}
}
}
*/
// Remove blanket auto approval for friendly secretary
bool bFriendlyToSecretary = false;
if (!bPropose)
{
if (eSecretaryGeneral != NO_TEAM)
{
if (eSecretaryGeneral == getTeam())
{
return PLAYER_VOTE_YES;
}
else
{
bFriendlyToSecretary = (GET_TEAM(getTeam()).AI_getAttitude(eSecretaryGeneral) == ATTITUDE_FRIENDLY);
}
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
bool bDefy = false;
bool bValid = true;
if (bValid)
{
for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
{
if (GC.getVoteInfo(eVote).isForceCivic(iI))
{
if (!isCivic((CivicTypes)iI))
{
eBestCivic = AI_bestCivic((CivicOptionTypes)(GC.getCivicInfo((CivicTypes)iI).getCivicOptionType()));
if (eBestCivic != NO_CIVIC)
{
if (eBestCivic != ((CivicTypes)iI))
{
int iBestCivicValue = AI_civicValue(eBestCivic);
int iNewCivicValue = AI_civicValue((CivicTypes)iI);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 12/30/08 jdog5000 */
/* */
/* Diplomacy AI */
/************************************************************************************************/
/* original BTS code
if (iBestCivicValue > ((iNewCivicValue * 120) / 100))
{
bValid = false;
if (iBestCivicValue > ((iNewCivicValue * (140 + (GC.getGame().getSorenRandNum(120, "AI Erratic Defiance (Force Civic)"))) / 100)))
*/
// Increase threshold of voting for friend's proposal
if( bFriendlyToSecretary )
{
iNewCivicValue *= 6;
iNewCivicValue /= 5;
}
if (iBestCivicValue > ((iNewCivicValue * 120) / 100))
{
bValid = false;
// Increase odds of defiance, particularly on AggressiveAI
if (iBestCivicValue > ((iNewCivicValue * (140 + (GC.getGame().getSorenRandNum((GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) ? 60 : 80), "AI Erratic Defiance (Force Civic)"))) / 100)))
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
bDefy = true;
}
break;
}
}
}
}
}
}
}
if (bValid)
{
if (GC.getVoteInfo(eVote).getTradeRoutes() > 0)
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 12/30/08 jdog5000 */
/* */
/* Diplomacy AI */
/************************************************************************************************/
if( bFriendlyToSecretary )
{
return PLAYER_VOTE_YES;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
if (getNumCities() > ((GC.getGameINLINE().getNumCities() * 2) / (GC.getGameINLINE().countCivPlayersAlive() + 1)))
{
bValid = false;
}
}
}
if (bValid)
{
if (GC.getVoteInfo(eVote).isNoNukes())
{
int iVoteBanThreshold = 0;
iVoteBanThreshold += GET_TEAM(getTeam()).getNukeInterception() / 3;
iVoteBanThreshold += GC.getLeaderHeadInfo(getPersonalityType()).getBuildUnitProb();
iVoteBanThreshold *= std::max(1, GC.getLeaderHeadInfo(getPersonalityType()).getWarmongerRespect());
if (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI))
{
iVoteBanThreshold *= 2;
}
bool bAnyHasSdi = false;
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (GET_TEAM((TeamTypes)iI).isAlive() && iI != getTeam())
{
if (GET_TEAM((TeamTypes)iI).getNukeInterception() > 0)
{
bAnyHasSdi = true;
break;
}
}
}
if (!bAnyHasSdi && GET_TEAM(getTeam()).getNukeInterception() > 0 && GET_TEAM(getTeam()).getNumNukeUnits() > 0)
{
iVoteBanThreshold *= 2;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 12/30/08 jdog5000 */
/* */
/* Diplomacy AI */
/************************************************************************************************/
if( bFriendlyToSecretary )
{
iVoteBanThreshold *= 2;
iVoteBanThreshold /= 3;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
// 45°: Enhanced NPT evaluation based on total nukes available on the map and total nukes already exploded, scaled by mapsize (from here...)
// 45°: preventing nuke ban if only 1 civ has nukes
int iNukeTeams = 0;
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (GET_TEAM((TeamTypes)iI).isAlive() && iI != getTeam())
{
if (GET_TEAM((TeamTypes)iI).getNumNukeUnits() > 0)
{
iNukeTeams++;
}
}
}
if (iNukeTeams <= 1)
{
iVoteBanThreshold *= 3;
}
// 45°: nukes ban is more probable if many nukes have been used or if there are many nukes on the map, scaled by mapsize
iVoteBanThreshold -= (int)((GC.getGameINLINE().getNukesExploded()*1000)/(GC.getMapINLINE().getLandPlots()));
iVoteBanThreshold -= (int)(((GC.getGameINLINE().countTotalNukeUnits())*500)/(GC.getMapINLINE().getLandPlots()));
iVoteBanThreshold = std::max(3, iVoteBanThreshold);
bValid = (GC.getGameINLINE().getSorenRandNum(100, "AI nuke ban vote") > iVoteBanThreshold);
if (AI_isDoStrategy(AI_STRATEGY_OWABWNW) && iVoteBanThreshold > 30)
{
bValid = false;
}
else if (((GET_TEAM(getTeam()).getNumNukeUnits() / std::max(1, GET_TEAM(getTeam()).getNumMembers())) < (GC.getGameINLINE().countTotalNukeUnits() / std::max(1, GC.getGameINLINE().countCivPlayersAlive()))) && iVoteBanThreshold > 15 ) //(...to here)
{
bValid = false;
}
if (!bValid && AI_getNumTrainAIUnits(UNITAI_ICBM) > 0)
{
if (GC.getGame().getSorenRandNum(AI_isDoStrategy(AI_STRATEGY_OWABWNW) ? 2 : 3, "AI Erratic Defiance (No Nukes)") == 0)
{
bDefy = true;
}
}
}
}
if (bValid)
{
if (GC.getVoteInfo(eVote).isFreeTrade())
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 12/30/08 jdog5000 */
/* */
/* Diplomacy AI */
/************************************************************************************************/
if( bFriendlyToSecretary )
{
return PLAYER_VOTE_YES;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
iOpenCount = 0;
iClosedCount = 0;
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (GET_TEAM((TeamTypes)iI).isAlive())
{
if (iI != getTeam())
{
if (GET_TEAM(getTeam()).isOpenBorders((TeamTypes)iI))
{
iOpenCount += GET_TEAM((TeamTypes)iI).getNumCities();
}
else
{
iClosedCount += GET_TEAM((TeamTypes)iI).getNumCities();
}
}
}
}
if (iOpenCount >= (getNumCities() * getTradeRoutes()))
{
bValid = false;
}
if (iClosedCount == 0)
{
bValid = false;
}
}
}
if (bValid)
{
if (GC.getVoteInfo(eVote).isOpenBorders())
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 12/30/08 jdog5000 */
/* */
/* Diplomacy AI */
/************************************************************************************************/
if( bFriendlyToSecretary )
{
return PLAYER_VOTE_YES;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
bValid = true;
for (iI = 0; iI < MAX_CIV_TEAMS; iI++)
{
if (iI != getTeam())
{
if (GET_TEAM((TeamTypes)iI).isVotingMember(eVoteSource))
{
if (NO_DENIAL != GET_TEAM(getTeam()).AI_openBordersTrade((TeamTypes)iI))
{
bValid = false;
break;
}
}
}
}
}
else if (GC.getVoteInfo(eVote).isDefensivePact())
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 12/30/08 jdog5000 */
/* */
/* Diplomacy AI */
/************************************************************************************************/
if( bFriendlyToSecretary )
{
return PLAYER_VOTE_YES;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
bValid = true;
for (iI = 0; iI < MAX_CIV_TEAMS; iI++)
{
if (iI != getTeam())
{
if (GET_TEAM((TeamTypes)iI).isVotingMember(eVoteSource))
{
if (NO_DENIAL != GET_TEAM(getTeam()).AI_defensivePactTrade((TeamTypes)iI))
{
bValid = false;
break;
}
}
}
}
}
else if (GC.getVoteInfo(eVote).isForcePeace())
{
FAssert(kVoteData.ePlayer != NO_PLAYER);
TeamTypes ePeaceTeam = GET_PLAYER(kVoteData.ePlayer).getTeam();
int iWarsWinning = 0;
int iWarsLosing = 0;
int iChosenWar = 0;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 01/03/09 jdog5000 */
/* */
/* Diplomacy AI */
/************************************************************************************************/
bool bLosingBig = false;
bool bWinningBig = false;
bool bThisPlayerWinning = false;
int iWinDeltaThreshold = 3*GC.getDefineINT("WAR_SUCCESS_ATTACKING");
int iLossAbsThreshold = std::max(3, getNumMilitaryUnits()/40)*GC.getDefineINT("WAR_SUCCESS_ATTACKING");
bool bAggressiveAI = GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI);
if( bAggressiveAI )
{
iWinDeltaThreshold *= 2;
iWinDeltaThreshold /= 3;
iLossAbsThreshold *= 4;
iLossAbsThreshold /= 3;
}
// Is ePeaceTeam winning wars?
for (iI = 0; iI < MAX_CIV_TEAMS; iI++)
{
if (GET_TEAM((TeamTypes)iI).isAlive())
{
if (iI != ePeaceTeam)
{
if (GET_TEAM((TeamTypes)iI).isAtWar(ePeaceTeam))
{
int iPeaceTeamSuccess = GET_TEAM(ePeaceTeam).AI_getWarSuccess((TeamTypes)iI);
int iOtherTeamSuccess = GET_TEAM((TeamTypes)iI).AI_getWarSuccess(ePeaceTeam);
if ( (iPeaceTeamSuccess - iOtherTeamSuccess) > iWinDeltaThreshold )
{
// Have to be ahead by at least a few victories to count as win
++iWarsWinning;
if ( (iPeaceTeamSuccess - iOtherTeamSuccess) > (3*iWinDeltaThreshold) )
{
bWinningBig = true;
}
}
else if( (iOtherTeamSuccess >= iPeaceTeamSuccess) )
{
if( iI == getTeam() )
{
if( (iOtherTeamSuccess - iPeaceTeamSuccess) > iWinDeltaThreshold )
{
bThisPlayerWinning = true;
}
}
if( (iOtherTeamSuccess > iLossAbsThreshold) )
{
// Have to have non-trivial loses
++iWarsLosing;
if( (iOtherTeamSuccess - iPeaceTeamSuccess) > (3*iLossAbsThreshold) )
{
bLosingBig = true;
}
}
else if( GET_TEAM(ePeaceTeam).AI_getAtWarCounter((TeamTypes)iI) < 10 )
{
// Not winning, just recently attacked, and in multiple wars, be pessimistic
// Counts ties from no actual battles
if( (GET_TEAM(ePeaceTeam).getAtWarCount(true) > 1) && !(GET_TEAM(ePeaceTeam).AI_isChosenWar((TeamTypes)iI)) )
{
++iWarsLosing;
}
}
}
if (GET_TEAM(ePeaceTeam).AI_isChosenWar((TeamTypes)iI))
{
++iChosenWar;
}
}
}
}
}
if (ePeaceTeam == getTeam())
{
int iPeaceRand = GC.getLeaderHeadInfo(getPersonalityType()).getBasePeaceWeight();
iPeaceRand /= (bAggressiveAI ? 2 : 1);
// K-Mod
iPeaceRand /= (AI_isDoVictoryStrategy(AI_VICTORY_CONQUEST2) ? 2 : 1);
// Always true for real war-mongers, rarely true for less aggressive types
bool bWarmongerRoll = (GC.getGame().getSorenRandNum(iPeaceRand, "AI Erratic Defiance (Force Peace)") == 0);
if( bLosingBig && (!bWarmongerRoll || bPropose) )
{
// Non-warmongers want peace to escape loss
bValid = true;
}
//else if ( !bLosingBig && (iChosenWar > iWarsLosing) )
else if (!bLosingBig && (iChosenWar > iWarsLosing || AI_isDoVictoryStrategy(AI_VICTORY_CONQUEST3))) // K-Mod
{
// If chosen to be in most wars, keep it going
bValid = false;
}
else
{
// If losing most wars, vote for peace
bValid = (iWarsLosing > iWarsWinning);
}
if (!bValid && !bLosingBig && bWinningBig)
{
// Can we continue this war with defiance penalties?
if( !AI_isFinancialTrouble() )
{
if (bWarmongerRoll)
{
bDefy = true;
}
}
}
}
else if (eSecretaryGeneral == getTeam() && !bPropose)
{
bValid = true;
}
else if (GET_TEAM(ePeaceTeam).isAtWar(getTeam()))
{
bool bWantsToEndWar = (GET_TEAM(getTeam()).AI_endWarVal(ePeaceTeam) > (3*GET_TEAM(ePeaceTeam).AI_endWarVal(getTeam()))/2);
bValid = bWantsToEndWar;
if( bValid )
{
bValid = bWinningBig || (iWarsWinning > iWarsLosing) || (GET_TEAM(getTeam()).getAtWarCount(true, true) > 1);
}
if (!bValid && bThisPlayerWinning && (iWarsLosing >= iWarsWinning) && !bPropose )
{
if( !GET_TEAM(getTeam()).isAVassal() )
{
if( (GET_TEAM(getTeam()).getAtWarCount(true) == 1) || bLosingBig )
{
// Can we continue this war with defiance penalties?
if( !AI_isFinancialTrouble() )
{
int iDefyRand = GC.getLeaderHeadInfo(getPersonalityType()).getBasePeaceWeight();
iDefyRand /= (bAggressiveAI ? 2 : 1);
if (GC.getGame().getSorenRandNum(iDefyRand, "AI Erratic Defiance (Force Peace)") == 0)
{
bDefy = true;
}
}
}
}
}
if( !bValid && !bDefy && !bPropose )
{
if((GET_TEAM(getTeam()).AI_getAttitude(eSecretaryGeneral) > GC.getLeaderHeadInfo(getPersonalityType()).getVassalRefuseAttitudeThreshold()) )
{
// Influence by secretary
if( NO_DENIAL == GET_TEAM(getTeam()).AI_makePeaceTrade(ePeaceTeam, eSecretaryGeneral) )
{
bValid = true;
}
else if( eSecretaryGeneral != NO_TEAM && GET_TEAM(getTeam()).isVassal(eSecretaryGeneral) )
{
bValid = true;
}
}
}
}
else
{
if( GET_TEAM(getTeam()).AI_getWarPlan(ePeaceTeam) != NO_WARPLAN )
{
// Keep planned enemy occupied
bValid = false;
}
else if( GET_TEAM(getTeam()).AI_shareWar(ePeaceTeam) && !(GET_TEAM(getTeam()).isVassal(ePeaceTeam)) )
{
// Keep ePeaceTeam at war with our common enemies
bValid = false;
}
else if(iWarsLosing > iWarsWinning)
{
// Feel pity for team that is losing (if like them enough to not declare war on them)
bValid = (GET_TEAM(getTeam()).AI_getAttitude(ePeaceTeam) >= GC.getLeaderHeadInfo(getPersonalityType()).getDeclareWarThemRefuseAttitudeThreshold());
}
else
{
// Stop a team that is winning (if don't like them enough to join them in war)
bValid = (GET_TEAM(getTeam()).AI_getAttitude(ePeaceTeam) < GC.getLeaderHeadInfo(getPersonalityType()).getDeclareWarRefuseAttitudeThreshold());
}
if( !bValid )
{
if( bFriendlyToSecretary && !GET_TEAM(getTeam()).isVassal(ePeaceTeam) )
{
// Influence by secretary
bValid = true;
}
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
else if (GC.getVoteInfo(eVote).isForceNoTrade())
{
FAssert(kVoteData.ePlayer != NO_PLAYER);
TeamTypes eEmbargoTeam = GET_PLAYER(kVoteData.ePlayer).getTeam();
if (eSecretaryGeneral == getTeam() && !bPropose)
{
bValid = true;
}
else if (eEmbargoTeam == getTeam())
{
bValid = false;
if (!isNoForeignTrade())
{
bDefy = true;
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 12/30/08 jdog5000 */
/* */
/* Diplomacy AI */
/************************************************************************************************/
else
{
if( bFriendlyToSecretary )
{
return PLAYER_VOTE_YES;
}
else if( canStopTradingWithTeam(eEmbargoTeam) )
{
bValid = (NO_DENIAL == AI_stopTradingTrade(eEmbargoTeam, kVoteData.ePlayer));
if (bValid)
{
bValid = (GET_TEAM(getTeam()).AI_getAttitude(eEmbargoTeam) <= ATTITUDE_CAUTIOUS);
}
}
else
{
bValid = (GET_TEAM(getTeam()).AI_getAttitude(eEmbargoTeam) < ATTITUDE_CAUTIOUS);
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
else if (GC.getVoteInfo(eVote).isForceWar())
{
FAssert(kVoteData.ePlayer != NO_PLAYER);
TeamTypes eWarTeam = GET_PLAYER(kVoteData.ePlayer).getTeam();
if (eSecretaryGeneral == getTeam() && !bPropose)
{
bValid = true;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 12/30/08 jdog5000 */
/* */
/* Diplomacy AI */
/************************************************************************************************/
/* original BTS code
else if (eWarTeam == getTeam())
{
bValid = false;
}
else if (GET_TEAM(eWarTeam).isAtWar(getTeam()))
*/
else if (eWarTeam == getTeam() || GET_TEAM(getTeam()).isVassal(eWarTeam))
{
// Explicit rejection by all who will definitely be attacked
bValid = false;
}
else if ( GET_TEAM(getTeam()).AI_getWarPlan(eWarTeam) != NO_WARPLAN )
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
bValid = true;
}
else
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 07/20/09 jdog5000 */
/* */
/* Diplomacy AI */
/************************************************************************************************/
/* original BTS code
bValid = (bPropose || NO_DENIAL == GET_TEAM(getTeam()).AI_declareWarTrade(eWarTeam, eSecretaryGeneral));
if (bValid)
{
bValid = (GET_TEAM(getTeam()).AI_getAttitude(eWarTeam) < ATTITUDE_CAUTIOUS);
}
*/
if( !bPropose && GET_TEAM(getTeam()).isAVassal() )
{
// Vassals always deny war trade requests and thus previously always voted no
bValid = false;
if( GET_TEAM(getTeam()).getAnyWarPlanCount(true) == 0 )
{
if( eSecretaryGeneral == NO_TEAM || (GET_TEAM(getTeam()).AI_getAttitude(eSecretaryGeneral) > GC.getLeaderHeadInfo(getPersonalityType()).getDeclareWarRefuseAttitudeThreshold()) )
{
if( eSecretaryGeneral != NO_TEAM && GET_TEAM(getTeam()).isVassal(eSecretaryGeneral) )
{
bValid = true;
}
else if( (GET_TEAM(getTeam()).isAVassal() ? GET_TEAM(getTeam()).getCurrentMasterPower(true) : GET_TEAM(getTeam()).getPower(true)) > GET_TEAM(eWarTeam).getDefensivePower() )
{
bValid = true;
}
}
}
}
else
{
bValid = (bPropose || NO_DENIAL == GET_TEAM(getTeam()).AI_declareWarTrade(eWarTeam, eSecretaryGeneral));
}
if (bValid)
{
int iNoWarOdds = GC.getLeaderHeadInfo(getPersonalityType()).getNoWarAttitudeProb((GET_TEAM(getTeam()).AI_getAttitude(eWarTeam)));
bValid = ((iNoWarOdds < 30) || (GC.getGame().getSorenRandNum(100, "AI War Vote Attitude Check (Force War)") > iNoWarOdds));
}
/*
else
{
// Consider defying resolution
if( !GET_TEAM(getTeam()).isAVassal() )
{
if( eSecretaryGeneral == NO_TEAM || GET_TEAM(getTeam()).AI_getAttitude(eWarTeam) > GET_TEAM(getTeam()).AI_getAttitude(eSecretaryGeneral) )
{
if( GET_TEAM(getTeam()).AI_getAttitude(eWarTeam) > GC.getLeaderHeadInfo(getPersonalityType()).getDefensivePactRefuseAttitudeThreshold() )
{
int iDefyRand = GC.getLeaderHeadInfo(getPersonalityType()).getBasePeaceWeight();
iDefyRand /= (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) ? 2 : 1);
if (GC.getGame().getSorenRandNum(iDefyRand, "AI Erratic Defiance (Force War)") > 0)
{
bDefy = true;
}
}
}
}
}
*/
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
}
else if (GC.getVoteInfo(eVote).isAssignCity())
{
bValid = false;
FAssert(kVoteData.ePlayer != NO_PLAYER);
CvPlayer& kPlayer = GET_PLAYER(kVoteData.ePlayer);
CvCity* pCity = kPlayer.getCity(kVoteData.iCityId);
if (NULL != pCity)
{
if (NO_PLAYER != kVoteData.eOtherPlayer && kVoteData.eOtherPlayer != pCity->getOwnerINLINE())
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 10/03/09 jdog5000 */
/* */
/* Diplomacy AI */
/************************************************************************************************/
if ((!bPropose && eSecretaryGeneral == getTeam()) || GET_PLAYER(kVoteData.eOtherPlayer).getTeam() == getTeam())
{
bValid = true;
}
else if (kPlayer.getTeam() == getTeam())
{
bValid = false;
// BBAI TODO: Wonders, holy city, aggressive AI?
if (GC.getGame().getSorenRandNum(3, "AI Erratic Defiance (Assign City)") == 0)
{
bDefy = true;
}
}
else
{
bValid = (AI_getAttitude(kVoteData.ePlayer) < AI_getAttitude(kVoteData.eOtherPlayer));
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
}
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 12/30/08 jdog5000 */
/* */
/* Diplomacy AI */
/************************************************************************************************/
/* original BTS code
if (bDefy && canDefyResolution(eVoteSource, kVoteData))
*/
// Don't defy resolutions from friends
if( bDefy && !bFriendlyToSecretary && canDefyResolution(eVoteSource, kVoteData))
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
return PLAYER_VOTE_NEVER;
}
return (bValid ? PLAYER_VOTE_YES : PLAYER_VOTE_NO);
}
}
int CvPlayerAI::AI_dealVal(PlayerTypes ePlayer, const CLinkList<TradeData>* pList, bool bIgnoreAnnual, int iChange) const
{
CLLNode<TradeData>* pNode;
CvCity* pCity;
int iValue;
FAssertMsg(ePlayer != getID(), "shouldn't call this function on ourselves");
iValue = 0;
if (atWar(getTeam(), GET_PLAYER(ePlayer).getTeam()))
{
iValue += GET_TEAM(getTeam()).AI_endWarVal(GET_PLAYER(ePlayer).getTeam());
}
for (pNode = pList->head(); pNode; pNode = pList->next(pNode))
{
FAssertMsg(!(pNode->m_data.m_bHidden), "(pNode->m_data.m_bHidden) did not return false as expected");
switch (pNode->m_data.m_eItemType)
{
case TRADE_TECHNOLOGIES:
iValue += GET_TEAM(getTeam()).AI_techTradeVal((TechTypes)(pNode->m_data.m_iData), GET_PLAYER(ePlayer).getTeam());
break;
case TRADE_RESOURCES:
if (!bIgnoreAnnual)
{
iValue += AI_bonusTradeVal(((BonusTypes)(pNode->m_data.m_iData)), ePlayer, iChange);
// The partner player is also loosing value for it, which is good for us
iValue += GET_PLAYER(ePlayer).AI_bonusTradeVal(((BonusTypes)(pNode->m_data.m_iData)), getID(), -iChange);
}
break;
case TRADE_CITIES:
pCity = GET_PLAYER(ePlayer).getCity(pNode->m_data.m_iData);
if (pCity != NULL)
{
iValue += AI_cityTradeVal(pCity);
// The partner player is also loosing value for it, which is good for us
iValue += GET_PLAYER(ePlayer).AI_ourCityValue(pCity);
}
break;
case TRADE_GOLD:
iValue += (pNode->m_data.m_iData * AI_goldTradeValuePercent()) / 100;
break;
case TRADE_GOLD_PER_TURN:
if (!bIgnoreAnnual)
{
iValue += AI_goldPerTurnTradeVal(pNode->m_data.m_iData);
}
break;
case TRADE_MAPS:
iValue += GET_TEAM(getTeam()).AI_mapTradeVal(GET_PLAYER(ePlayer).getTeam());
break;
case TRADE_SURRENDER:
if (!bIgnoreAnnual)
{
iValue += GET_TEAM(getTeam()).AI_surrenderTradeVal(GET_PLAYER(ePlayer).getTeam());
}
break;
case TRADE_VASSAL:
if (!bIgnoreAnnual)
{
iValue += GET_TEAM(getTeam()).AI_vassalTradeVal(GET_PLAYER(ePlayer).getTeam());
}
break;
case TRADE_OPEN_BORDERS:
iValue += GET_TEAM(getTeam()).AI_openBordersTradeVal(GET_PLAYER(ePlayer).getTeam());
break;
case TRADE_DEFENSIVE_PACT:
iValue += GET_TEAM(getTeam()).AI_defensivePactTradeVal(GET_PLAYER(ePlayer).getTeam());
break;
case TRADE_PEACE:
iValue += GET_TEAM(getTeam()).AI_makePeaceTradeVal(((TeamTypes)(pNode->m_data.m_iData)), GET_PLAYER(ePlayer).getTeam());
break;
case TRADE_WAR:
iValue += GET_TEAM(getTeam()).AI_declareWarTradeVal(((TeamTypes)(pNode->m_data.m_iData)), GET_PLAYER(ePlayer).getTeam());
break;
case TRADE_EMBARGO:
iValue += AI_stopTradingTradeVal(((TeamTypes)(pNode->m_data.m_iData)), ePlayer);
break;
case TRADE_CIVIC:
iValue += AI_civicTradeVal(((CivicTypes)(pNode->m_data.m_iData)), ePlayer);
break;
case TRADE_RELIGION:
iValue += AI_religionTradeVal(((ReligionTypes)(pNode->m_data.m_iData)), ePlayer);
break;
/************************************************************************************************/
/* Afforess Start 06/16/10 */
/* */
/* Advanced Diplomacy */
/************************************************************************************************/
case TRADE_EMBASSY:
iValue += GET_TEAM(getTeam()).AI_embassyTradeVal(GET_PLAYER(ePlayer).getTeam());
break;
case TRADE_CONTACT:
iValue += GET_TEAM(getTeam()).AI_contactTradeVal((TeamTypes)(pNode->m_data.m_iData), GET_PLAYER(ePlayer).getTeam());
break;
case TRADE_WAR_REPARATIONS:
iValue -= AI_tradeWarReparationsVal((PlayerTypes)(pNode->m_data.m_iData));
break;
case TRADE_CEASE_RELATIONS:
iValue += AI_tradeCeaseRelationsVal((PlayerTypes)(pNode->m_data.m_iData), ePlayer);
break;
case TRADE_CORPORATION:
iValue += AI_corporationTradeVal((CorporationTypes)(pNode->m_data.m_iData), ePlayer);
// Partner is losing it also
iValue += GET_PLAYER(ePlayer).AI_corporationTradeVal((CorporationTypes)(pNode->m_data.m_iData), getID());
break;
case TRADE_PLEDGE_VOTE:
iValue += AI_pledgeVoteTradeVal(GC.getGameINLINE().getVoteTriggered(GC.getGameINLINE().getCurrentVoteID()), ((PlayerVoteTypes)(pNode->m_data.m_iData)), ePlayer);
break;
case TRADE_SECRETARY_GENERAL_VOTE:
iValue += AI_secretaryGeneralTradeVal((VoteSourceTypes)(pNode->m_data.m_iData), ePlayer);
break;
case TRADE_RITE_OF_PASSAGE:
iValue += GET_TEAM(getTeam()).AI_LimitedBordersTradeVal(GET_PLAYER(ePlayer).getTeam());
break;
case TRADE_FREE_TRADE_ZONE:
iValue += GET_TEAM(getTeam()).AI_FreeTradeAgreementVal(GET_PLAYER(ePlayer).getTeam());
break;
case TRADE_WORKER:
{
CvUnit* pUnit = GET_PLAYER(ePlayer).getUnit(pNode->m_data.m_iData);
if (pUnit != NULL)
{
iValue += AI_workerTradeVal(pUnit);
}
}
break;
case TRADE_MILITARY_UNIT:
{
CvUnit* pUnit = GET_PLAYER(ePlayer).getUnit(pNode->m_data.m_iData);
if (pUnit != NULL)
{
iValue += AI_militaryUnitTradeVal(pUnit);
}
}
break;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
}
return iValue;
}
bool CvPlayerAI::AI_goldDeal(const CLinkList<TradeData>* pList) const
{
CLLNode<TradeData>* pNode;
for (pNode = pList->head(); pNode; pNode = pList->next(pNode))
{
FAssert(!(pNode->m_data.m_bHidden));
switch (pNode->m_data.m_eItemType)
{
case TRADE_GOLD:
case TRADE_GOLD_PER_TURN:
return true;
break;
}
}
return false;
}
/// \brief AI decision making on a proposal it is given
///
/// In this function the AI considers whether or not to accept another player's proposal. This is used when
/// considering proposals from the human player made in the diplomacy window as well as a couple other places.
bool CvPlayerAI::AI_considerOffer(PlayerTypes ePlayer, const CLinkList<TradeData>* pTheirList, const CLinkList<TradeData>* pOurList, int iChange) const
{
CLLNode<TradeData>* pNode;
int iThreshold;
FAssertMsg(ePlayer != getID(), "shouldn't call this function on ourselves");
if (AI_goldDeal(pTheirList) && AI_goldDeal(pOurList))
{
return false;
}
if (iChange > -1)
{
for (pNode = pOurList->head(); pNode; pNode = pOurList->next(pNode))
{
if (getTradeDenial(ePlayer, pNode->m_data) != NO_DENIAL)
{
return false;
}
}
}
if (GET_PLAYER(ePlayer).getTeam() == getTeam())
{
return true;
}
/************************************************************************************************/
/* Afforess Start 04/02/10 */
/* */
/* */
/************************************************************************************************/
for (pNode = pOurList->head(); pNode; pNode = pOurList->next(pNode))
{
if( pNode->m_data.m_eItemType == TRADE_CORPORATION )
{
if (pTheirList->getLength() == 0)
{
return false;
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 10/23/09 jdog5000 */
/* */
/* Diplomacy AI */
/************************************************************************************************/
// Don't always accept giving deals, TRADE_VASSAL and TRADE_SURRENDER come with strings attached
bool bVassalTrade = false;
for (pNode = pTheirList->head(); pNode; pNode = pTheirList->next(pNode))
{
if( pNode->m_data.m_eItemType == TRADE_VASSAL )
{
bVassalTrade = true;
for (int iTeam = 0; iTeam < MAX_CIV_TEAMS; iTeam++)
{
if (GET_TEAM((TeamTypes)iTeam).isAlive())
{
if (iTeam != getTeam() && iTeam != GET_PLAYER(ePlayer).getTeam() && atWar(GET_PLAYER(ePlayer).getTeam(), (TeamTypes)iTeam) && !atWar(getTeam(), (TeamTypes)iTeam))
{
if (GET_TEAM(getTeam()).AI_declareWarTrade((TeamTypes)iTeam, GET_PLAYER(ePlayer).getTeam(), false) != NO_DENIAL)
{
return false;
}
}
}
}
}
else if( pNode->m_data.m_eItemType == TRADE_SURRENDER )
{
bVassalTrade = true;
if( !(GET_TEAM(getTeam()).AI_acceptSurrender(GET_PLAYER(ePlayer).getTeam())) )
{
return false;
}
}
}
if( !bVassalTrade )
{
if ((pOurList->getLength() == 0) && (pTheirList->getLength() > 0))
{
return true;
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
int iOurValue = GET_PLAYER(ePlayer).AI_dealVal(getID(), pOurList, false, iChange);
int iTheirValue = AI_dealVal(ePlayer, pTheirList, false, iChange);
/************************************************************************************************/
/* Afforess Start 5/30/11 */
/* */
/* */
/************************************************************************************************/
for (pNode = pOurList->head(); pNode; pNode = pOurList->next(pNode))
{
if( pNode->m_data.m_eItemType == TRADE_CITIES )
{
if (pTheirList->getLength() == 0)
{
return false;
}
else
{
//only accept 1 time lump sums, continuing gold per turn or resource per turn could be backstabbed
for (CLLNode<TradeData>* pTheirNode = pTheirList->head(); pTheirNode; pTheirNode = pTheirList->next(pTheirNode))
{
if( pNode->m_data.m_eItemType == TRADE_GOLD_PER_TURN || pNode->m_data.m_eItemType == TRADE_DEFENSIVE_PACT || pNode->m_data.m_eItemType == TRADE_RESOURCES)
{
return false;
}
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (iOurValue > 0 && 0 == pTheirList->getLength() && 0 == iTheirValue)
{
if (GET_TEAM(getTeam()).isVassal(GET_PLAYER(ePlayer).getTeam()) && CvDeal::isVassalTributeDeal(pOurList))
{
if (AI_getAttitude(ePlayer, false) <= GC.getLeaderHeadInfo(getPersonalityType()).getVassalRefuseAttitudeThreshold()
&& GET_TEAM(getTeam()).getAtWarCount(true) == 0
&& GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getDefensivePactCount() == 0)
{
iOurValue *= (GET_TEAM(getTeam()).getPower(false) + 10);
iOurValue /= (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPower(false) + 10);
}
else
{
return true;
}
}
else
{
if (AI_getAttitude(ePlayer) < ATTITUDE_PLEASED)
{
if (GET_TEAM(getTeam()).getPower(false) > ((GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPower(false) * 4) / 3))
{
return false;
}
}
if (AI_getMemoryCount(ePlayer, MEMORY_MADE_DEMAND_RECENT) > 0)
{
return false;
}
}
iThreshold = (GET_TEAM(getTeam()).AI_getHasMetCounter(GET_PLAYER(ePlayer).getTeam()) + 50);
iThreshold *= 2;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 06/12/10 jdog5000 */
/* */
/* Diplomacy AI */
/************************************************************************************************/
if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).AI_isLandTarget(getTeam()))
{
iThreshold *= 3;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
iThreshold *= (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPower(false) + 100);
iThreshold /= (GET_TEAM(getTeam()).getPower(false) + 100);
iThreshold -= GET_PLAYER(ePlayer).AI_getPeacetimeGrantValue(getID());
return (iOurValue < iThreshold);
}
if (iChange < 0)
{
return (iTheirValue * 110 >= iOurValue * 100);
}
return (iTheirValue >= iOurValue);
}
bool CvPlayerAI::AI_counterPropose(PlayerTypes ePlayer, const CLinkList<TradeData>* pTheirList, const CLinkList<TradeData>* pOurList, CLinkList<TradeData>* pTheirInventory, CLinkList<TradeData>* pOurInventory, CLinkList<TradeData>* pTheirCounter, CLinkList<TradeData>* pOurCounter) const
{
CLLNode<TradeData>* pNode;
CLLNode<TradeData>* pBestNode;
CLLNode<TradeData>* pGoldPerTurnNode;
CLLNode<TradeData>* pGoldNode;
bool* pabBonusDeal;
CvCity* pCity;
bool bTheirGoldDeal;
bool bOurGoldDeal;
int iHumanDealWeight;
int iAIDealWeight;
int iGoldData;
int iGoldWeight;
int iWeight;
int iBestWeight;
int iValue;
int iBestValue;
int iI;
bTheirGoldDeal = AI_goldDeal(pTheirList);
bOurGoldDeal = AI_goldDeal(pOurList);
if (bOurGoldDeal && bTheirGoldDeal)
{
return false;
}
pabBonusDeal = new bool[GC.getNumBonusInfos()];
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
pabBonusDeal[iI] = false;
}
pGoldPerTurnNode = NULL;
pGoldNode = NULL;
iHumanDealWeight = AI_dealVal(ePlayer, pTheirList);
iAIDealWeight = GET_PLAYER(ePlayer).AI_dealVal(getID(), pOurList);
int iGoldValuePercent = AI_goldTradeValuePercent();
pTheirCounter->clear();
pOurCounter->clear();
/************************************************************************************************/
/* Afforess Start 5/30/11 */
/* */
/* */
/************************************************************************************************/
bool bOfferingCity = false;
bool bReceivingCity = false;
for (pNode = pTheirList->head(); pNode; pNode = pTheirList->next(pNode))
{
if (pNode->m_data.m_eItemType == TRADE_CITIES)
{
bReceivingCity = true;
break;
}
}
for (pNode = pOurList->head(); pNode; pNode = pOurList->next(pNode))
{
if (pNode->m_data.m_eItemType == TRADE_CITIES)
{
bOfferingCity = true;
break;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (iAIDealWeight > iHumanDealWeight)
{
if (atWar(getTeam(), GET_PLAYER(ePlayer).getTeam()))
{
iBestValue = 0;
iBestWeight = 0;
pBestNode = NULL;
for (pNode = pTheirInventory->head(); pNode && iAIDealWeight > iHumanDealWeight; pNode = pTheirInventory->next(pNode))
{
if (!pNode->m_data.m_bOffering && !pNode->m_data.m_bHidden)
{
if (pNode->m_data.m_eItemType == TRADE_CITIES)
{
FAssert(GET_PLAYER(ePlayer).canTradeItem(getID(), pNode->m_data));
if (GET_PLAYER(ePlayer).getTradeDenial(getID(), pNode->m_data) == NO_DENIAL)
{
pCity = GET_PLAYER(ePlayer).getCity(pNode->m_data.m_iData);
if (pCity != NULL)
{
iWeight = AI_cityTradeVal(pCity);
if (iWeight > 0)
{
iValue = AI_targetCityValue(pCity, false);
if (iValue > iBestValue)
{
iBestValue = iValue;
iBestWeight = iWeight;
pBestNode = pNode;
}
}
}
}
}
}
}
if (pBestNode != NULL)
{
iHumanDealWeight += iBestWeight;
pTheirCounter->insertAtEnd(pBestNode->m_data);
}
}
for (pNode = pTheirInventory->head(); pNode && iAIDealWeight > iHumanDealWeight; pNode = pTheirInventory->next(pNode))
{
if (!pNode->m_data.m_bOffering && !pNode->m_data.m_bHidden)
{
FAssert(GET_PLAYER(ePlayer).canTradeItem(getID(), pNode->m_data));
if (GET_PLAYER(ePlayer).getTradeDenial(getID(), pNode->m_data) == NO_DENIAL)
{
switch (pNode->m_data.m_eItemType)
{
case TRADE_GOLD:
if (!bOurGoldDeal)
{
pGoldNode = pNode;
}
break;
case TRADE_GOLD_PER_TURN:
if (!bOurGoldDeal)
{
pGoldPerTurnNode = pNode;
}
break;
}
}
}
}
int iGoldWeight = iAIDealWeight - iHumanDealWeight;
if (iGoldWeight > 0)
{
if (pGoldNode)
{
iGoldData = iGoldWeight * 100;
iGoldData /= iGoldValuePercent;
/************************************************************************************************/
/* UNOFFICIAL_PATCH 09/17/09 dilandau & jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original bts code
if ((iGoldData * iGoldValuePercent) < iGoldWeight)
*/
if ((iGoldData * iGoldValuePercent) < iGoldWeight * 100)
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
{
iGoldData++;
}
if (GET_PLAYER(ePlayer).AI_maxGoldTrade(getID()) >= iGoldData)
{
pGoldNode->m_data.m_iData = iGoldData;
iHumanDealWeight += (iGoldData * iGoldValuePercent) / 100;
pTheirCounter->insertAtEnd(pGoldNode->m_data);
pGoldNode = NULL;
}
}
}
for (pNode = pOurList->head(); pNode; pNode = pOurList->next(pNode))
{
FAssert(!(pNode->m_data.m_bHidden));
switch (pNode->m_data.m_eItemType)
{
case TRADE_RESOURCES:
pabBonusDeal[pNode->m_data.m_iData] = true;
break;
}
}
for (pNode = pTheirInventory->head(); pNode && iAIDealWeight > iHumanDealWeight; pNode = pTheirInventory->next(pNode))
{
if (!pNode->m_data.m_bOffering && !pNode->m_data.m_bHidden)
{
FAssert(GET_PLAYER(ePlayer).canTradeItem(getID(), pNode->m_data));
if (GET_PLAYER(ePlayer).getTradeDenial(getID(), pNode->m_data) == NO_DENIAL)
{
iWeight = 0;
switch (pNode->m_data.m_eItemType)
{
case TRADE_TECHNOLOGIES:
iWeight += GET_TEAM(getTeam()).AI_techTradeVal((TechTypes)(pNode->m_data.m_iData), GET_PLAYER(ePlayer).getTeam());
break;
case TRADE_RESOURCES:
/************************************************************************************************/
/* Afforess Start 5/30/11 */
/* */
/* */
/************************************************************************************************/
/*
if (!pabBonusDeal[pNode->m_data.m_iData])
*/
if (!bOfferingCity && !pabBonusDeal[pNode->m_data.m_iData])
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
if (GET_PLAYER(ePlayer).getNumTradeableBonuses((BonusTypes)(pNode->m_data.m_iData)) > 1)
{
if (GET_PLAYER(ePlayer).AI_corporationBonusVal((BonusTypes)(pNode->m_data.m_iData)) == 0)
{
iWeight += AI_bonusTradeVal(((BonusTypes)(pNode->m_data.m_iData)), ePlayer, 1);
pabBonusDeal[pNode->m_data.m_iData] = true;
}
}
}
break;
}
if (iWeight > 0)
{
iHumanDealWeight += iWeight;
pTheirCounter->insertAtEnd(pNode->m_data);
}
}
}
}
for (pNode = pTheirInventory->head(); pNode && iAIDealWeight > iHumanDealWeight; pNode = pTheirInventory->next(pNode))
{
if (!pNode->m_data.m_bOffering && !pNode->m_data.m_bHidden)
{
if (pNode->m_data.m_eItemType == TRADE_MAPS)
{
FAssert(GET_PLAYER(ePlayer).canTradeItem(getID(), pNode->m_data));
if (GET_PLAYER(ePlayer).getTradeDenial(getID(), pNode->m_data) == NO_DENIAL)
{
iWeight = GET_TEAM(getTeam()).AI_mapTradeVal(GET_PLAYER(ePlayer).getTeam());
if (iWeight > 0)
{
iHumanDealWeight += iWeight;
pTheirCounter->insertAtEnd(pNode->m_data);
}
}
}
}
}
/************************************************************************************************/
/* Afforess Start 08/24/10 */
/* */
/* */
/************************************************************************************************/
for (pNode = pTheirInventory->head(); pNode && iAIDealWeight > iHumanDealWeight; pNode = pTheirInventory->next(pNode))
{
if (!pNode->m_data.m_bOffering && !pNode->m_data.m_bHidden)
{
if (pNode->m_data.m_eItemType == TRADE_CONTACT)
{
FAssert(canTradeItem(ePlayer, pNode->m_data));
if (getTradeDenial(ePlayer, pNode->m_data) == NO_DENIAL)
{
if ((TeamTypes)pNode->m_data.m_iData != NO_TEAM)
{
iWeight = GET_TEAM(getTeam()).AI_contactTradeVal((TeamTypes)pNode->m_data.m_iData, GET_PLAYER(ePlayer).getTeam());
if (iWeight > 0)
{
iHumanDealWeight += iWeight;
pTheirCounter->insertAtEnd(pNode->m_data);
}
}
}
}
}
}
for (pNode = pTheirInventory->head(); pNode && iAIDealWeight > iHumanDealWeight; pNode = pTheirInventory->next(pNode))
{
if (!pNode->m_data.m_bOffering && !pNode->m_data.m_bHidden)
{
if (pNode->m_data.m_eItemType == TRADE_MILITARY_UNIT || pNode->m_data.m_eItemType == TRADE_WORKER)
{
FAssert(canTradeItem(ePlayer, pNode->m_data));
if (getTradeDenial(ePlayer, pNode->m_data) == NO_DENIAL)
{
CvUnit* pUnit = getUnit(pNode->m_data.m_iData);
if (pUnit != NULL)
{
iWeight = std::max(GET_PLAYER(ePlayer).AI_militaryUnitTradeVal(pUnit), GET_PLAYER(ePlayer).AI_workerTradeVal(pUnit));
if (iWeight > 0)
{
iHumanDealWeight += iWeight;
pTheirCounter->insertAtEnd(pNode->m_data);
}
}
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iGoldWeight = iAIDealWeight - iHumanDealWeight;
if (iGoldWeight > 0)
{
if (pGoldNode)
{
iGoldData = iGoldWeight * 100;
iGoldData /= iGoldValuePercent;
if ((iGoldWeight * 100) > (iGoldData * iGoldValuePercent))
{
iGoldData++;
}
iGoldData = std::min(iGoldData, GET_PLAYER(ePlayer).AI_maxGoldTrade(getID()));
if (iGoldData > 0)
{
pGoldNode->m_data.m_iData = iGoldData;
iHumanDealWeight += (iGoldData * iGoldValuePercent) / 100;
pTheirCounter->insertAtEnd(pGoldNode->m_data);
pGoldNode = NULL;
}
}
}
/************************************************************************************************/
/* Afforess Start 5/30/11 */
/* */
/* */
/************************************************************************************************/
/*
if (iAIDealWeight > iHumanDealWeight)
*/
if (!bOfferingCity && iAIDealWeight > iHumanDealWeight)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
if (pGoldPerTurnNode)
{
iGoldData = 0;
while (AI_goldPerTurnTradeVal(iGoldData) < (iAIDealWeight - iHumanDealWeight))
{
iGoldData++;
}
iGoldData = std::min(iGoldData, GET_PLAYER(ePlayer).AI_maxGoldPerTurnTrade(getID()));
if (iGoldData > 0)
{
pGoldPerTurnNode->m_data.m_iData = iGoldData;
iHumanDealWeight += AI_goldPerTurnTradeVal(pGoldPerTurnNode->m_data.m_iData);
pTheirCounter->insertAtEnd(pGoldPerTurnNode->m_data);
pGoldPerTurnNode = NULL;
}
}
}
/************************************************************************************************/
/* Afforess Start 5/30/11 */
/* */
/* */
/************************************************************************************************/
if (!bOfferingCity)
{
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
for (pNode = pTheirInventory->head(); pNode && iAIDealWeight > iHumanDealWeight; pNode = pTheirInventory->next(pNode))
{
if (!pNode->m_data.m_bOffering && !pNode->m_data.m_bHidden)
{
if (pNode->m_data.m_eItemType == TRADE_RESOURCES)
{
FAssert(GET_PLAYER(ePlayer).canTradeItem(getID(), pNode->m_data));
if (GET_PLAYER(ePlayer).getTradeDenial(getID(), pNode->m_data) == NO_DENIAL)
{
iWeight = 0;
if (!pabBonusDeal[pNode->m_data.m_iData])
{
if (GET_PLAYER(ePlayer).getNumTradeableBonuses((BonusTypes)(pNode->m_data.m_iData)) > 0)
{
iWeight += AI_bonusTradeVal(((BonusTypes)(pNode->m_data.m_iData)), ePlayer, 1);
pabBonusDeal[pNode->m_data.m_iData] = true;
}
}
if (iWeight > 0)
{
iHumanDealWeight += iWeight;
pTheirCounter->insertAtEnd(pNode->m_data);
}
}
}
}
}
/************************************************************************************************/
/* Afforess Start 5/30/11 */
/* */
/* */
/************************************************************************************************/
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
else if (iHumanDealWeight > iAIDealWeight)
{
if (atWar(getTeam(), GET_PLAYER(ePlayer).getTeam()))
{
bool bSurrender = false;
for (pNode = pOurInventory->head(); pNode; pNode = pOurInventory->next(pNode))
{
if (!pNode->m_data.m_bOffering && !pNode->m_data.m_bHidden)
{
if (pNode->m_data.m_eItemType == TRADE_SURRENDER)
{
if (getTradeDenial(ePlayer, pNode->m_data) == NO_DENIAL)
{
iAIDealWeight += GET_TEAM(GET_PLAYER(ePlayer).getTeam()).AI_surrenderTradeVal(getTeam());
pOurCounter->insertAtEnd(pNode->m_data);
bSurrender = true;
}
break;
}
}
}
if (!bSurrender)
{
for (pNode = pOurInventory->head(); pNode; pNode = pOurInventory->next(pNode))
{
if (!pNode->m_data.m_bOffering && !pNode->m_data.m_bHidden)
{
if (pNode->m_data.m_eItemType == TRADE_PEACE_TREATY)
{
pOurCounter->insertAtEnd(pNode->m_data);
break;
}
}
}
}
iBestValue = 0;
iBestWeight = 0;
pBestNode = NULL;
for (pNode = pOurInventory->head(); pNode && iHumanDealWeight > iAIDealWeight; pNode = pOurInventory->next(pNode))
{
if (!pNode->m_data.m_bOffering && !pNode->m_data.m_bHidden)
{
if (pNode->m_data.m_eItemType == TRADE_CITIES)
{
FAssert(canTradeItem(ePlayer, pNode->m_data));
if (getTradeDenial(ePlayer, pNode->m_data) == NO_DENIAL)
{
pCity = getCity(pNode->m_data.m_iData);
if (pCity != NULL)
{
iWeight = GET_PLAYER(ePlayer).AI_cityTradeVal(pCity);
if (iWeight > 0)
{
iValue = GET_PLAYER(ePlayer).AI_targetCityValue(pCity, false);
if (iValue > iBestValue)
{
if (iHumanDealWeight >= (iAIDealWeight + iWeight))
{
iBestValue = iValue;
iBestWeight = iWeight;
pBestNode = pNode;
}
}
}
}
}
}
}
}
if (pBestNode != NULL)
{
iAIDealWeight += iBestWeight;
pOurCounter->insertAtEnd(pBestNode->m_data);
}
}
for (pNode = pOurInventory->head(); pNode && iHumanDealWeight > iAIDealWeight; pNode = pOurInventory->next(pNode))
{
if (!pNode->m_data.m_bOffering && !pNode->m_data.m_bHidden)
{
FAssert(canTradeItem(ePlayer, pNode->m_data));
if (getTradeDenial(ePlayer, pNode->m_data) == NO_DENIAL)
{
switch (pNode->m_data.m_eItemType)
{
case TRADE_GOLD:
if (!bTheirGoldDeal)
{
pGoldNode = pNode;
}
break;
case TRADE_GOLD_PER_TURN:
if (!bTheirGoldDeal)
{
pGoldPerTurnNode = pNode;
}
break;
}
}
}
}
iGoldWeight = iHumanDealWeight - iAIDealWeight;
if (iGoldWeight > 0)
{
if (pGoldNode)
{
int iGoldData = iGoldWeight * 100;
iGoldData /= iGoldValuePercent;
if (AI_maxGoldTrade(ePlayer) >= iGoldData)
{
pGoldNode->m_data.m_iData = iGoldData;
iAIDealWeight += ((iGoldData * iGoldValuePercent) / 100);
pOurCounter->insertAtEnd(pGoldNode->m_data);
pGoldNode = NULL;
}
}
}
for (pNode = pTheirList->head(); pNode; pNode = pTheirList->next(pNode))
{
FAssert(!(pNode->m_data.m_bHidden));
switch (pNode->m_data.m_eItemType)
{
case TRADE_RESOURCES:
pabBonusDeal[pNode->m_data.m_iData] = true;
break;
}
}
for (pNode = pOurInventory->head(); pNode && iHumanDealWeight > iAIDealWeight; pNode = pOurInventory->next(pNode))
{
if (!pNode->m_data.m_bOffering && !pNode->m_data.m_bHidden)
{
FAssert(canTradeItem(ePlayer, pNode->m_data));
if (getTradeDenial(ePlayer, pNode->m_data) == NO_DENIAL)
{
iWeight = 0;
switch (pNode->m_data.m_eItemType)
{
case TRADE_TECHNOLOGIES:
iWeight += GET_TEAM(GET_PLAYER(ePlayer).getTeam()).AI_techTradeVal((TechTypes)(pNode->m_data.m_iData), getTeam());
break;
case TRADE_RESOURCES:
if (!pabBonusDeal[pNode->m_data.m_iData])
{
if (getNumTradeableBonuses((BonusTypes)(pNode->m_data.m_iData)) > 1)
{
iWeight += GET_PLAYER(ePlayer).AI_bonusTradeVal(((BonusTypes)(pNode->m_data.m_iData)), getID(), 1);
pabBonusDeal[pNode->m_data.m_iData] = true;
}
}
break;
}
if (iWeight > 0)
{
if (iHumanDealWeight >= (iAIDealWeight + iWeight))
{
iAIDealWeight += iWeight;
pOurCounter->insertAtEnd(pNode->m_data);
}
}
}
}
}
for (pNode = pOurInventory->head(); pNode && iHumanDealWeight > iAIDealWeight; pNode = pOurInventory->next(pNode))
{
if (!pNode->m_data.m_bOffering && !pNode->m_data.m_bHidden)
{
if (pNode->m_data.m_eItemType == TRADE_MAPS)
{
FAssert(canTradeItem(ePlayer, pNode->m_data));
if (getTradeDenial(ePlayer, pNode->m_data) == NO_DENIAL)
{
iWeight = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).AI_mapTradeVal(getTeam());
if (iWeight > 0)
{
if (iHumanDealWeight >= (iAIDealWeight + iWeight))
{
iAIDealWeight += iWeight;
pOurCounter->insertAtEnd(pNode->m_data);
}
}
}
}
}
}
/************************************************************************************************/
/* Afforess Start 04/06/10 */
/* */
/* */
/************************************************************************************************/
for (pNode = pOurInventory->head(); pNode && iHumanDealWeight > iAIDealWeight; pNode = pOurInventory->next(pNode))
{
if (!pNode->m_data.m_bOffering && !pNode->m_data.m_bHidden)
{
if (pNode->m_data.m_eItemType == TRADE_CONTACT)
{
FAssert(canTradeItem(ePlayer, pNode->m_data));
if (getTradeDenial(ePlayer, pNode->m_data) == NO_DENIAL)
{
if ((TeamTypes)pNode->m_data.m_iData != NO_TEAM)
{
iWeight = GET_TEAM(getTeam()).AI_contactTradeVal((TeamTypes)pNode->m_data.m_iData, GET_PLAYER(ePlayer).getTeam());
if (iWeight > 0)
{
if (iHumanDealWeight >= (iAIDealWeight + iWeight))
{
iAIDealWeight += iWeight;
pOurCounter->insertAtEnd(pNode->m_data);
}
}
}
}
}
}
}
for (pNode = pOurInventory->head(); pNode && iHumanDealWeight > iAIDealWeight; pNode = pOurInventory->next(pNode))
{
if (!pNode->m_data.m_bOffering && !pNode->m_data.m_bHidden)
{
if (pNode->m_data.m_eItemType == TRADE_MILITARY_UNIT)
{
FAssert(canTradeItem(ePlayer, pNode->m_data));
if (getTradeDenial(ePlayer, pNode->m_data) == NO_DENIAL)
{
CvUnit* pUnit = getUnit(pNode->m_data.m_iData);
if (pUnit != NULL)
{
iWeight = AI_militaryUnitTradeVal(pUnit);
if (iWeight > 0)
{
if (iHumanDealWeight >= (iAIDealWeight + iWeight))
{
iAIDealWeight += iWeight;
pOurCounter->insertAtEnd(pNode->m_data);
}
}
}
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iGoldWeight = iHumanDealWeight - iAIDealWeight;
if (iGoldWeight > 0)
{
if (pGoldNode)
{
iGoldData = iGoldWeight * 100;
iGoldData /= AI_goldTradeValuePercent();
iGoldData = std::min(iGoldData, AI_maxGoldTrade(ePlayer));
if (iGoldData > 0)
{
pGoldNode->m_data.m_iData = iGoldData;
iAIDealWeight += (iGoldData * AI_goldTradeValuePercent()) / 100;
pOurCounter->insertAtEnd(pGoldNode->m_data);
pGoldNode = NULL;
}
}
}
if (iHumanDealWeight > iAIDealWeight)
{
if (pGoldPerTurnNode)
{
iGoldData = 0;
while (GET_PLAYER(ePlayer).AI_goldPerTurnTradeVal(iGoldData + 1) <= (iHumanDealWeight - iAIDealWeight))
{
iGoldData++;
}
iGoldData = std::min(iGoldData, AI_maxGoldPerTurnTrade(ePlayer));
if (iGoldData > 0)
{
pGoldPerTurnNode->m_data.m_iData = iGoldData;
iAIDealWeight += GET_PLAYER(ePlayer).AI_goldPerTurnTradeVal(pGoldPerTurnNode->m_data.m_iData);
pOurCounter->insertAtEnd(pGoldPerTurnNode->m_data);
pGoldPerTurnNode = NULL;
}
}
}
}
SAFE_DELETE_ARRAY(pabBonusDeal);
return ((iAIDealWeight <= iHumanDealWeight) && ((pOurList->getLength() > 0) || (pOurCounter->getLength() > 0) || (pTheirCounter->getLength() > 0)));
}
int CvPlayerAI::AI_maxGoldTrade(PlayerTypes ePlayer) const
{
int iMaxGold;
int iResearchBuffer;
FAssert(ePlayer != getID());
if (isHuman() || (GET_PLAYER(ePlayer).getTeam() == getTeam()))
{
iMaxGold = getGold();
}
else
{
// Afforess: Realistic Diplomacy
if (GC.getGameINLINE().isOption(GAMEOPTION_ADVANCED_DIPLOMACY))
{
iMaxGold = getGold();
}
else
{
iMaxGold = getTotalPopulation();
iMaxGold *= (GET_TEAM(getTeam()).AI_getHasMetCounter(GET_PLAYER(ePlayer).getTeam()) + 10);
}
iMaxGold *= GC.getLeaderHeadInfo(getPersonalityType()).getMaxGoldTradePercent();
iMaxGold /= 100;
iMaxGold -= AI_getGoldTradedTo(ePlayer);
iResearchBuffer = -calculateGoldRate() * 12;
iResearchBuffer *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getResearchPercent();
iResearchBuffer /= 100;
/************************************************************************************************/
/* Afforess Start 04/06/10 */
/* */
/* */
/************************************************************************************************/
iMaxGold *= (100 + calculateInflationRate());
iMaxGold /= 100;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iMaxGold = std::min(iMaxGold, getGold() - iResearchBuffer);
iMaxGold = std::min(iMaxGold, getGold());
if (!GC.getGameINLINE().isOption(GAMEOPTION_ADVANCED_DIPLOMACY))
iMaxGold -= (iMaxGold % GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
}
return std::max(0, iMaxGold);
}
int CvPlayerAI::AI_maxGoldPerTurnTrade(PlayerTypes ePlayer) const
{
int iMaxGoldPerTurn;
FAssert(ePlayer != getID());
if (isHuman() || (GET_PLAYER(ePlayer).getTeam() == getTeam()))
{
iMaxGoldPerTurn = (calculateGoldRate() + (getGold() / getTreatyLength()));
}
else
{
iMaxGoldPerTurn = getTotalPopulation();
iMaxGoldPerTurn *= GC.getLeaderHeadInfo(getPersonalityType()).getMaxGoldPerTurnTradePercent();
iMaxGoldPerTurn /= 100;
iMaxGoldPerTurn += std::min(0, getGoldPerTurnByPlayer(ePlayer));
}
return std::max(0, std::min(iMaxGoldPerTurn, calculateGoldRate()));
}
int CvPlayerAI::AI_goldPerTurnTradeVal(int iGoldPerTurn) const
{
int iValue = iGoldPerTurn * getTreatyLength();
iValue *= AI_goldTradeValuePercent();
iValue /= 100;
return iValue;
}
int CvPlayerAI::AI_bonusVal(BonusTypes eBonus, int iChange, bool bForTrade) const
{
PROFILE_FUNC();
int iBonusCount = getNumAvailableBonuses(eBonus);
bool bAssumeHasBonusChanges = ((iChange == 0) || ((iChange == 1) && (iBonusCount == 0)) || ((iChange == -1) && (iBonusCount == 1)));
int iValue = 0;
if (bAssumeHasBonusChanges)
{
//This is assuming the none-to-one or one-to-none case.
iValue += AI_baseBonusVal(eBonus, bForTrade);
iValue += AI_corporationBonusVal(eBonus);
}
else
{
//This is basically the marginal value of an additional instance of a bonus.
iValue += AI_baseBonusVal(eBonus, bForTrade) / 5;
iValue += AI_corporationBonusVal(eBonus);
}
return iValue;
}
//Value sans corporation
int CvPlayerAI::AI_baseBonusVal(BonusTypes eBonus, bool bForTrade) const
{
PROFILE_FUNC();
EnterCriticalSection(&m_csBonusValSection);
bool bRecalcNeeded = (m_aiBonusValue[eBonus] == -1 || (!bForTrade && !m_abNonTradeBonusCalculated[eBonus]));
LeaveCriticalSection(&m_csBonusValSection);
//recalculate if not defined
if(bRecalcNeeded)
{
PROFILE("CvPlayerAI::AI_baseBonusVal::recalculate");
UnitTypes eLoopUnit;
BuildingTypes eLoopBuilding;
int iDiff;
int iValue = 0;
int iTradeValue = 0;
int iTempValue;
int iTempTradeValue;
int iTempNonTradeValue;
int iI, iJ;
// If we've already calculated everythign except the not-currently-constructable
// buildings (which only appky to the non-trade value) and now we need the full
// non-trade value thn we just need to add the contriobution from those buildings
bool bJustNonTradeBuildings = (m_aiBonusValue[eBonus] != -1 );
CvTeam& kTeam = GET_TEAM(getTeam());
if (!kTeam.isBonusObsolete(eBonus))
{
CvCity* pCapital = getCapitalCity();
int iCityCount = getNumCities();
int iCoastalCityCount = countNumCoastalCities();
// find the first coastal city
CvCity* pCoastalCity = NULL;
CvCity* pUnconnectedCoastalCity = NULL;
if (iCoastalCityCount > 0)
{
pCoastalCity = findBestCoastalCity();
}
if ( !bJustNonTradeBuildings )
{
iValue += (GC.getBonusInfo(eBonus).getHappiness() * 100);
iValue += (GC.getBonusInfo(eBonus).getHealth() * 100);
iTradeValue = iValue;
{
PROFILE("CvPlayerAI::AI_baseBonusVal::recalculate Unit Value");
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
eLoopUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI)));
if (eLoopUnit != NO_UNIT)
{
CvUnitInfo& kLoopUnit = GC.getUnitInfo(eLoopUnit);
iTempValue = 0;
iTempTradeValue = 0;
// Don't consider units more than one era ahead of us
if ( kLoopUnit.getPrereqAndTech() != NO_TECH )
{
CvTechInfo& prereqTech = GC.getTechInfo((TechTypes)kLoopUnit.getPrereqAndTech());
if ( prereqTech.getEra() > (int)getCurrentEra() + 1 )
{
continue;
}
}
if (kLoopUnit.getPrereqAndBonus() == eBonus)
{
iTempValue += 50;
}
int iBonusORVal = 0;
int iHasOther = 0;
for (iJ = 0; iJ < GC.getNUM_UNIT_PREREQ_OR_BONUSES(); iJ++)
{
if ( kLoopUnit.getPrereqOrBonuses(iJ) != NO_BONUS )
{
if (kLoopUnit.getPrereqOrBonuses(iJ) == eBonus)
{
iBonusORVal = 40;
}
else if ( getNumAvailableBonuses((BonusTypes)kLoopUnit.getPrereqOrBonuses(iJ)) > 0 )
{
iHasOther += getNumAvailableBonuses((BonusTypes)kLoopUnit.getPrereqOrBonuses(iJ));
}
}
}
while(iHasOther-- > 0)
{
iBonusORVal /= 4;
}
iTempValue += iBonusORVal;
iTempValue += kLoopUnit.getBonusProductionModifier(eBonus) / 10;
if (iTempValue > 0)
{
bool bIsWater = (kLoopUnit.getDomainType() == DOMAIN_SEA);
// if non-limited water unit, weight by coastal cities
if (bIsWater && !isLimitedUnitClass((UnitClassTypes)(kLoopUnit.getUnitClassType())))
{
iTempValue *= std::min(iCoastalCityCount * 2, iCityCount); // double coastal cities, cap at total cities
iTempValue /= std::max(1, iCityCount);
}
// is it a water unit and no coastal cities
if (bIsWater && pCoastalCity == NULL )
{
// worthless
iTempValue = 2;
iTempTradeValue = iTempValue;
}
else if (canTrain(eLoopUnit))
{
// is it a water unit and no coastal cities or our coastal city cannot build because its obsolete
if ((bIsWater && (pCoastalCity->allUpgradesAvailable(eLoopUnit) != NO_UNIT)) ||
// or our capital cannot build because its obsolete (we can already build all its upgrades)
(pCapital != NULL && pCapital->allUpgradesAvailable(eLoopUnit) != NO_UNIT))
{
// its worthless
iTempValue = 2;
}
// otherwise, value units we could build if we had this bonus double
else
{
iTempValue *= 2;
}
iTempTradeValue = iTempValue;
}
else
{
// Trades are short-term - if we can't train the unit now assume we
// won't be able to do so for the duration of the trade
iTempTradeValue = 0;
}
if ( iTempValue > 0 )
{
int iTechDistance = 0;
// If building this is dependent (directly or otherwise) on a tech, assess how
// distant that tech is
if ( kLoopUnit.getPrereqAndTech() != NO_TECH )
{
iTechDistance = std::max(iTechDistance,findPathLength((TechTypes)kLoopUnit.getPrereqAndTech(), false));
}
// Without some more checks we are over-assessing religious buildings a lot
// so if there is a religion pre-req make some basic checks on the availability of
// the religion
if ( kLoopUnit.getPrereqReligion() != NO_RELIGION )
{
CvReligionInfo& kReligion = GC.getReligionInfo((ReligionTypes)kLoopUnit.getPrereqReligion());
iTechDistance = std::max(iTechDistance,findPathLength((TechTypes)kReligion.getTechPrereq(), false));
}
// Similarly corporations
if ( kLoopUnit.getPrereqCorporation() != NO_RELIGION )
{
CvCorporationInfo& kCorporation = GC.getCorporationInfo((CorporationTypes)kLoopUnit.getPrereqCorporation());
iTechDistance = std::max(iTechDistance,findPathLength((TechTypes)kCorporation.getTechPrereq(), false));
}
iTempValue = (iTempValue*15)/(10+iTechDistance);
iTempTradeValue = (iTempTradeValue*15)/(10+iTechDistance);
}
iValue += iTempValue;
iTradeValue += iTempTradeValue;
}
}
}
}
}
{
PROFILE("CvPlayerAI::AI_baseBonusVal::recalculate Building Value");
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
eLoopBuilding = ((BuildingTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iI)));
if (eLoopBuilding != NO_BUILDING && !GET_TEAM(getTeam()).isObsoleteBuilding(eLoopBuilding))
{
CvBuildingInfo& kLoopBuilding = GC.getBuildingInfo(eLoopBuilding);
int iBuildingTechDistance;
bool bCanConstruct;
if ( bJustNonTradeBuildings || bForTrade )
{
bCanConstruct = canConstruct(eLoopBuilding, false, /*bTestVisible*/ true, /*bIgnoreCost*/ true);
if ( bCanConstruct == bJustNonTradeBuildings )
{
continue;
}
}
iTempValue = 0;
iTempTradeValue = 0;
iTempNonTradeValue = 0;
TechTypes eBuildingTech = (TechTypes)kLoopBuilding.getPrereqAndTech();
if ( eBuildingTech != NO_TECH )
{
if ( GC.getTechInfo(eBuildingTech).getEra() <= getCurrentEra() + 1 )
{
iBuildingTechDistance = findPathLength((TechTypes)kLoopBuilding.getPrereqAndTech(), false);
}
else
{
iBuildingTechDistance = MAX_INT;
}
}
else
{
iBuildingTechDistance = 0;
}
if ( iBuildingTechDistance < 15 )
{
if ( !bJustNonTradeBuildings && !bForTrade )
{
bCanConstruct = canConstruct(eLoopBuilding, false, /*bTestVisible*/ true, /*bIgnoreCost*/ true);
}
bool bCouldConstruct = false;
bool bCanConstructAnyway = true;
if (kLoopBuilding.getPrereqAndBonus() != NO_BONUS)
{
if ( kLoopBuilding.getPrereqAndBonus() == eBonus )
{
bCouldConstruct = true;
bCanConstructAnyway = false;
}
else
{
if ( !hasBonus((BonusTypes)kLoopBuilding.getPrereqAndBonus()) )
{
bCanConstructAnyway = false;
}
}
}
if ( bCouldConstruct || bCanConstructAnyway )
{
bool bHasOR = false;
bool bGetsOR = false;
bool bRequiresOR = false;
for (iJ = 0; iJ < GC.getNUM_BUILDING_PREREQ_OR_BONUSES(); iJ++)
{
if (kLoopBuilding.getPrereqOrBonuses(iJ) != NO_BONUS)
{
bRequiresOR = true;
if ( kLoopBuilding.getPrereqOrBonuses(iJ) == eBonus )
{
bGetsOR = true;
}
else if ( hasBonus((BonusTypes)kLoopBuilding.getPrereqOrBonuses(iJ)) )
{
bHasOR = true;
break;
}
}
}
if ( bRequiresOR )
{
if ( bHasOR )
{
if ( bGetsOR )
{
bCouldConstruct |= bCanConstructAnyway;
}
}
else
{
bCanConstructAnyway = false;
}
}
}
if ( bCouldConstruct && !bCanConstructAnyway )
{
iTempNonTradeValue += 100;
}
if ( bCouldConstruct || bCanConstructAnyway )
{
iTempValue += kLoopBuilding.getBonusProductionModifier(eBonus) / 10;
if (kLoopBuilding.getPowerBonus() == eBonus)
{
iTempValue += 60;
}
int iBuildingClassCount = std::max(1, getBuildingClassCount((BuildingClassTypes)iI));
for (iJ = 0; iJ < NUM_YIELD_TYPES; iJ++)
{
iTempValue += kLoopBuilding.getBonusYieldModifier(eBonus, iJ) / 2;
if (kLoopBuilding.getPowerBonus() == eBonus)
{
iTempValue += kLoopBuilding.getPowerYieldModifier(iJ);
}
/************************************************************************************************/
/* Afforess Start 03/2/10 */
/* */
/* Better AI Resources Evalution */
/************************************************************************************************/
//Remember, these are all divided by 10 at the end...
// This is supposed to be a per-city value so since we're multiplying by the number we
// have of this building class, divide by the total number of cities
iTempValue += (kLoopBuilding.getBonusYieldChanges(eBonus, iJ) * iBuildingClassCount * 60)/std::max(1,getNumCities());
iTempValue += (kLoopBuilding.getBonusYieldModifier(eBonus, iJ) * iBuildingClassCount * 10)/std::max(1,getNumCities());
}
for (iJ = 0; iJ < NUM_COMMERCE_TYPES; iJ++)
{
// Percent modifier on commerce estimate per city as that modifer applied to the player's total commerce divided by num cities
iTempValue += (kLoopBuilding.getBonusCommercePercentChanges(eBonus, iJ) * iBuildingClassCount * getCommerceRate((CommerceTypes)iJ))/(10*std::max(1,getNumCities()*getNumCities()));
iTempValue += (kLoopBuilding.getBonusCommerceModifier(eBonus, iJ) * iBuildingClassCount * 10)/std::max(1,getNumCities());
}
iTempValue += (iBuildingClassCount * kLoopBuilding.getBonusHappinessChanges(eBonus) * 120)/std::max(1,getNumCities());
iTempValue += (iBuildingClassCount * kLoopBuilding.getBonusHealthChanges(eBonus) * 80)/std::max(1,getNumCities());
iTempValue += (iBuildingClassCount * kLoopBuilding.getBonusDefenseChanges(eBonus) * 10)/std::max(1,getNumCities());
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
// determine whether we have the tech for this building
bool bHasTechForBuilding = true;
if (!(kTeam.isHasTech((TechTypes)(kLoopBuilding.getPrereqAndTech()))))
{
bHasTechForBuilding = false;
}
for (int iPrereqIndex = 0; bHasTechForBuilding && iPrereqIndex < GC.getNUM_BUILDING_AND_TECH_PREREQS(); iPrereqIndex++)
{
if (kLoopBuilding.getPrereqAndTechs(iPrereqIndex) != NO_TECH)
{
if (!(kTeam.isHasTech((TechTypes)(kLoopBuilding.getPrereqAndTechs(iPrereqIndex)))))
{
bHasTechForBuilding = false;
}
}
}
bool bIsStateReligion = (((ReligionTypes) kLoopBuilding.getStateReligion()) != NO_RELIGION);
// bCanNeverBuild when true is accurate, it may be false in some cases where we will never be able to build
bool bCanNeverBuild = (bHasTechForBuilding && !bCanConstruct && !bIsStateReligion);
// if we can never build this, it is worthless
if (bCanNeverBuild)
{
iTempNonTradeValue = 0;
iTempTradeValue = 0;
}
// double value if we can build it right now
else if (bCanConstruct)
{
iTempNonTradeValue += iTempValue;
iTempTradeValue += iTempValue;
iTempNonTradeValue *= 2;
iTempTradeValue *= 2;
}
iTempValue = 0;
// if non-limited water building, weight by coastal cities
if (kLoopBuilding.isWater() && !isLimitedWonderClass((BuildingClassTypes)(kLoopBuilding.getBuildingClassType())))
{
iTempNonTradeValue *= iCoastalCityCount;
iTempNonTradeValue /= std::max(1, iCityCount/2);
iTempTradeValue *= iCoastalCityCount;
iTempTradeValue /= std::max(1, iCityCount/2);
}
if (iTempNonTradeValue > 0 || iTempTradeValue > 0)
{
int iTechDistance = iBuildingTechDistance;
// Without some more checks we are over-assessing religious buildings a lot
// so if there is a religion pre-req make some basic checks on the availability of
// the religion
ReligionTypes eReligion = (ReligionTypes)kLoopBuilding.getPrereqReligion();
if ( eReligion != NO_RELIGION )
{
CvReligionInfo& kReligion = GC.getReligionInfo(eReligion);
// Trade is short term - don't assume useful religion spread - just weight by
// the cities that have the religion already
iTempTradeValue = (iTempTradeValue*getHasReligionCount(eReligion))/std::max(1, iCityCount);
iTechDistance = std::max(iTechDistance,findPathLength((TechTypes)kReligion.getTechPrereq(), false));
}
// Similarly corporations
CorporationTypes eCorporation = (CorporationTypes)kLoopBuilding.getPrereqCorporation();
if ( iTempValue > 0 && eCorporation != NO_CORPORATION )
{
CvCorporationInfo& kCorporation = GC.getCorporationInfo(eCorporation);
// Trade is short term - don't assume useful corporation spread - just weight by
// the cities that have the religion already
iTempTradeValue = (iTempTradeValue*getHasCorporationCount(eCorporation))/std::max(1, iCityCount);
iTechDistance = std::max(iTechDistance,findPathLength((TechTypes)kCorporation.getTechPrereq(), false));
}
if ( iTempNonTradeValue > 0 && !bCanConstruct )
{
// If building this is dependent (directly or otherwise) on a tech, assess how
// distant that tech is
iTempNonTradeValue = (iTempNonTradeValue*15)/(10+iTechDistance);
}
iTempTradeValue = (iTempTradeValue*15)/(10+iTechDistance);
}
}
//Special Wonder Considerations...
if (isLimitedWonderClass((BuildingClassTypes)iI) && bCanConstruct)
{
// World wonders we are competing for so boost them higher
int iWonderModifier = (isWorldWonderClass((BuildingClassTypes)iI) ? 3 : 1)*kLoopBuilding.getBonusProductionModifier(eBonus);
iTempTradeValue = (iTempTradeValue*iWonderModifier)/100;
iTempNonTradeValue = (iTempNonTradeValue*iWonderModifier)/100;
}
// Trades are short-term - if we can't construct the building now assume we
// won't be able to do so for the duration of the trade
if ( !bCanConstruct )
{
iTempTradeValue = 0;
}
// Buildings will beconme diabled once their pre-reqs are no longer present
// so give them less weight for temporary trades
iValue += iTempNonTradeValue;
iTradeValue += iTempTradeValue/3;
}
}
}
}
}
if ( !bJustNonTradeBuildings )
{
PROFILE("CvPlayerAI::AI_baseBonusVal::recalculate Project Value");
for (iI = 0; iI < GC.getNumProjectInfos(); iI++)
{
ProjectTypes eProject = (ProjectTypes) iI;
CvProjectInfo& kLoopProject = GC.getProjectInfo(eProject);
iTempValue = 0;
iTempTradeValue = 0;
iTempNonTradeValue = 0;
iTempValue += kLoopProject.getBonusProductionModifier(eBonus) / 10;
if (iTempValue > 0)
{
bool bMaxedOut = (GC.getGameINLINE().isProjectMaxedOut(eProject) || kTeam.isProjectMaxedOut(eProject));
if (!bMaxedOut)
{
if (canCreate(eProject))
{
iTempValue *= 2;
iTempTradeValue += iTempValue;
iTempNonTradeValue += iTempValue;
}
else
{
// Trades are short-term - if we can't construct the building now assume we
// won't be able to do so for the duration of the trade
iTempNonTradeValue += iTempValue;
}
}
if (kLoopProject.getTechPrereq() != NO_TECH)
{
iDiff = abs(GC.getTechInfo((TechTypes)(kLoopProject.getTechPrereq())).getEra() - getCurrentEra());
if (iDiff == 0)
{
iTempTradeValue *= 3;
iTempTradeValue /= 2;
iTempNonTradeValue *= 3;
iTempNonTradeValue /= 2;
}
else
{
iTempTradeValue /= iDiff;
iTempNonTradeValue /= iDiff;
}
}
iValue += iTempNonTradeValue;
iTradeValue += iTempTradeValue;
}
}
}
if ( !bJustNonTradeBuildings )
{
PROFILE("CvPlayerAI::AI_baseBonusVal::recalculate Route Value");
RouteTypes eBestRoute = getBestRoute();
for (iI = 0; iI < GC.getNumBuildInfos(); iI++)
{
RouteTypes eRoute = (RouteTypes)(GC.getBuildInfo((BuildTypes)iI).getRoute());
if (eRoute != NO_ROUTE)
{
iTempValue = 0;
if (GC.getRouteInfo(eRoute).getPrereqBonus() == eBonus)
{
iTempValue += 80;
}
for (iJ = 0; iJ < GC.getNUM_ROUTE_PREREQ_OR_BONUSES(); iJ++)
{
if (GC.getRouteInfo(eRoute).getPrereqOrBonus(iJ) == eBonus)
{
iTempValue += 40;
}
}
if ((eBestRoute != NO_ROUTE) && (GC.getRouteInfo(getBestRoute()).getValue() > GC.getRouteInfo(eRoute).getValue()))
{
iTempValue /= 2;
}
iValue += iTempValue;
iTradeValue += iTempValue;
}
}
}
/************************************************************************************************/
/* Afforess Start 01/18/10 */
/* */
/* */
/************************************************************************************************/
//Resource scarcity. If there are only limited quantities of this resource, treasure it.
if ( !bJustNonTradeBuildings )
{
PROFILE("CvPlayerAI::AI_baseBonusVal::recalculate Bonus Scarcity");
int iTotalBonusCount = 0;
int iKnownPlayers = 0;
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (iI == getID() || GET_TEAM(getTeam()).isHasMet((GET_PLAYER((PlayerTypes)iI).getTeam())))
{
iTotalBonusCount += GET_PLAYER((PlayerTypes)iI).getNumAvailableBonuses(eBonus);
iKnownPlayers++;
}
}
}
if (iKnownPlayers > 0 && iTotalBonusCount > 0)
{
int iModifier = iKnownPlayers * 100;
iModifier /= iTotalBonusCount;
if (getNumAvailableBonuses(eBonus) == 0)
{
iModifier = std::max(75, iModifier);
}
iTempValue = (iModifier * iValue) / 100;
iTempValue += GC.getBonusInfo(eBonus).getAIObjective() * 10;
iValue = iTempValue;
iTempValue = (iModifier * iTradeValue) / 100;
iTempValue += GC.getBonusInfo(eBonus).getAIObjective() * 10;
iTradeValue = iTempValue;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
// int iCorporationValue = AI_corporationBonusVal(eBonus);
// iValue += iCorporationValue;
//
// if (iCorporationValue <= 0 && getNumAvailableBonuses(eBonus) > 0)
// {
// iValue /= 3;
// }
iValue /= 10;
iTradeValue /= 10;
}
// All these effects are only going to be with us for a short period so devalue
iTradeValue /= 3;
EnterCriticalSection(&m_csBonusValSection);
// Check there wasn't a race copndition that meant some other thread already did this
if (m_aiBonusValue[eBonus] == -1 || (!bForTrade && !m_abNonTradeBonusCalculated[eBonus]))
{
if ( !bJustNonTradeBuildings )
{
m_aiBonusValue[eBonus] = std::max(0, iValue);
m_aiTradeBonusValue[eBonus] = std::max(0, iTradeValue);
}
else
{
m_aiBonusValue[eBonus] += std::max(0, iValue);
}
m_abNonTradeBonusCalculated[eBonus] |= !bForTrade;
}
LeaveCriticalSection(&m_csBonusValSection);
}
return (bForTrade ? m_aiTradeBonusValue[eBonus] : m_aiBonusValue[eBonus]);
}
int CvPlayerAI::AI_corporationBonusVal(BonusTypes eBonus) const
{
int iValue = 0;
int iCityCount = getNumCities();
iCityCount += iCityCount / 6 + 1;
for (int iCorporation = 0; iCorporation < GC.getNumCorporationInfos(); ++iCorporation)
{
int iCorpCount = getHasCorporationCount((CorporationTypes)iCorporation);
if (iCorpCount > 0)
{
int iNumCorpBonuses = 0;
iCorpCount += getNumCities() / 6 + 1;
CvCorporationInfo& kCorp = GC.getCorporationInfo((CorporationTypes)iCorporation);
for (int i = 0; i < GC.getNUM_CORPORATION_PREREQ_BONUSES(); ++i)
{
if (eBonus == kCorp.getPrereqBonus(i))
{
iValue += (50 * kCorp.getYieldProduced(YIELD_FOOD) * iCorpCount) / iCityCount;
iValue += (50 * kCorp.getYieldProduced(YIELD_PRODUCTION) * iCorpCount) / iCityCount;
iValue += (30 * kCorp.getYieldProduced(YIELD_COMMERCE) * iCorpCount) / iCityCount;
iValue += (30 * kCorp.getCommerceProduced(COMMERCE_GOLD) * iCorpCount) / iCityCount;
iValue += (30 * kCorp.getCommerceProduced(COMMERCE_RESEARCH) * iCorpCount) / iCityCount;
iValue += (12 * kCorp.getCommerceProduced(COMMERCE_CULTURE) * iCorpCount) / iCityCount;
iValue += (20 * kCorp.getCommerceProduced(COMMERCE_ESPIONAGE) * iCorpCount) / iCityCount;
//Disabled since you can't found/spread a corp unless there is already a bonus,
//and that bonus will provide the entirity of the bonusProduced benefit.
/*if (NO_BONUS != kCorp.getBonusProduced())
{
if (getNumAvailableBonuses((BonusTypes)kCorp.getBonusProduced()) == 0)
{
iBonusValue += (1000 * iCorpCount * AI_baseBonusVal((BonusTypes)kCorp.getBonusProduced())) / (10 * iCityCount);
}
}*/
}
}
}
}
iValue /= 100; //percent
iValue /= 10; //match AI_baseBonusVal
return iValue;
}
int CvPlayerAI::AI_bonusTradeVal(BonusTypes eBonus, PlayerTypes ePlayer, int iChange) const
{
PROFILE_FUNC();
int iValue;
FAssertMsg(ePlayer != getID(), "shouldn't call this function on ourselves");
iValue = AI_bonusVal(eBonus, iChange, true);
iValue *= ((std::min(getNumCities(), GET_PLAYER(ePlayer).getNumCities()) + 3) * 30);
iValue /= 100;
iValue *= std::max(0, (GC.getBonusInfo(eBonus).getAITradeModifier() + 100));
iValue /= 100;
if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isVassal(getTeam()) && !GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isCapitulated())
{
iValue /= 2;
}
/************************************************************************************************/
/* Afforess Start 6/22/11 */
/* */
/* */
/************************************************************************************************/
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return (iValue * getTreatyLength());
}
DenialTypes CvPlayerAI::AI_bonusTrade(BonusTypes eBonus, PlayerTypes ePlayer) const
{
PROFILE_FUNC();
AttitudeTypes eAttitude;
bool bStrategic;
int iI, iJ;
FAssertMsg(ePlayer != getID(), "shouldn't call this function on ourselves");
if (isHuman() && GET_PLAYER(ePlayer).isHuman())
{
return NO_DENIAL;
}
if (GET_TEAM(getTeam()).isVassal(GET_PLAYER(ePlayer).getTeam()))
{
return NO_DENIAL;
}
if (atWar(getTeam(), GET_PLAYER(ePlayer).getTeam()))
{
return NO_DENIAL;
}
if (GET_PLAYER(ePlayer).getTeam() == getTeam())
{
return NO_DENIAL;
}
if (GET_PLAYER(ePlayer).getNumAvailableBonuses(eBonus) > 0 && GET_PLAYER(ePlayer).AI_corporationBonusVal(eBonus) <= 0)
{
return (GET_PLAYER(ePlayer).isHuman() ? DENIAL_JOKING : DENIAL_NO_GAIN);
}
if (isHuman())
{
return NO_DENIAL;
}
if (GET_TEAM(getTeam()).AI_getWorstEnemy() == GET_PLAYER(ePlayer).getTeam())
{
return DENIAL_WORST_ENEMY;
}
if (AI_corporationBonusVal(eBonus) > 0)
{
return DENIAL_JOKING;
}
bStrategic = false;
/************************************************************************************************/
/* Fuyu & Afforess Start 6/22/10 */
/* */
/* Better AI: Strategic For Current Era */
/************************************************************************************************/
//disregard obsolete units
CvCity* pCapitalCity = getCapitalCity();
for (iI = 0; iI < GC.getNumUnitInfos(); iI++)
{
if (!GC.getGameINLINE().canEverTrain((UnitTypes)iI))
{
continue;
}
if (pCapitalCity->allUpgradesAvailable((UnitTypes)iI) != NO_UNIT)
{
continue;
}
/************************************************************************************************/
/* Fuyu END */
/************************************************************************************************/
if (GC.getUnitInfo((UnitTypes) iI).getPrereqAndBonus() == eBonus)
{
bStrategic = true;
}
for (iJ = 0; iJ < GC.getNUM_UNIT_PREREQ_OR_BONUSES(); iJ++)
{
if (GC.getUnitInfo((UnitTypes) iI).getPrereqOrBonuses(iJ) == eBonus)
{
bStrategic = true;
}
}
}
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
/************************************************************************************************/
/* Fuyu & Afforess Start 6/22/10 */
/* */
/* Better AI: Strategic For Current Era */
/************************************************************************************************/
//disregard obsolete buildings
if ((TechTypes)GC.getBuildingInfo((BuildingTypes)iI).getObsoleteTech() != NO_TECH)
{
if (GET_TEAM(getTeam()).isHasTech((TechTypes)(GC.getBuildingInfo((BuildingTypes)iI).getObsoleteTech())))
{
continue;
}
if (!GC.getGameINLINE().canEverConstruct((BuildingTypes)iI))
{
continue;
}
}
/************************************************************************************************/
/* Fuyu END */
/************************************************************************************************/
if (GC.getBuildingInfo((BuildingTypes) iI).getPrereqAndBonus() == eBonus)
{
bStrategic = true;
}
for (iJ = 0; iJ < GC.getNUM_BUILDING_PREREQ_OR_BONUSES(); iJ++)
{
if (GC.getBuildingInfo((BuildingTypes) iI).getPrereqOrBonuses(iJ) == eBonus)
{
bStrategic = true;
}
}
}
// XXX marble and stone???
eAttitude = AI_getAttitude(ePlayer);
if (bStrategic)
{
/************************************************************************************************/
/* Afforess Start 03/19/10 */
/* */
/* Ruthless AI */
/************************************************************************************************/
//If we are planning war, don't sell our resources!
if (GC.getGameINLINE().isOption(GAMEOPTION_RUTHLESS_AI))
{
if (GET_TEAM(getTeam()).getAnyWarPlanCount(true) > 0)
{
return DENIAL_MYSTERY;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (eAttitude <= GC.getLeaderHeadInfo(getPersonalityType()).getStrategicBonusRefuseAttitudeThreshold())
{
return DENIAL_ATTITUDE;
}
}
if (GC.getBonusInfo(eBonus).getHappiness() > 0)
{
if (eAttitude <= GC.getLeaderHeadInfo(getPersonalityType()).getHappinessBonusRefuseAttitudeThreshold())
{
return DENIAL_ATTITUDE;
}
}
if (GC.getBonusInfo(eBonus).getHealth() > 0)
{
if (eAttitude <= GC.getLeaderHeadInfo(getPersonalityType()).getHealthBonusRefuseAttitudeThreshold())
{
return DENIAL_ATTITUDE;
}
}
return NO_DENIAL;
}
int CvPlayerAI::AI_cityTradeVal(CvCity* pCity) const
{
CvPlot* pLoopPlot;
int iValue;
int iI;
FAssert(pCity->getOwnerINLINE() != getID());
/************************************************************************************************/
/* Afforess 5/29/11 */
/* */
/* */
/************************************************************************************************/
/*
iValue = 300;
*/
iValue = 500;
//consider infrastructure
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (pCity->getNumBuilding((BuildingTypes)iI) > 0)
{
if (isWorldWonderClass((BuildingClassTypes)GC.getBuildingInfo((BuildingTypes)iI).getBuildingClassType()))
{
iValue += pCity->getNumBuilding((BuildingTypes)iI) * GC.getBuildingInfo((BuildingTypes)iI).getProductionCost() / 3;
}
else if (isLimitedWonderClass((BuildingClassTypes)GC.getBuildingInfo((BuildingTypes)iI).getBuildingClassType()))
{
iValue += pCity->getNumBuilding((BuildingTypes)iI) * GC.getBuildingInfo((BuildingTypes)iI).getProductionCost() / 5;
}
else
{
iValue += pCity->getNumBuilding((BuildingTypes)iI) * GC.getBuildingInfo((BuildingTypes)iI).getProductionCost() / 10;
}
}
}
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (pCity->isHasReligion((ReligionTypes)iI))
{
if (getStateReligion() == iI)
{
iValue += 100;
if (pCity->isHolyCity((ReligionTypes)iI))
{
iValue += 500;
}
break;
}
}
}
for (iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
if (pCity->isHasCorporation((CorporationTypes)iI))
{
iValue += AI_corporationValue((CorporationTypes)iI, pCity) / 25;
if (pCity->isHeadquarters((CorporationTypes)iI))
{
iValue += AI_corporationTradeVal((CorporationTypes)iI, pCity->getOwnerINLINE());
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iValue += (pCity->getPopulation() * 50);
iValue += (pCity->getCultureLevel() * 200);
/************************************************************************************************/
/* Afforess 5/29/11 */
/* */
/* Improved AI */
/************************************************************************************************/
/*
iValue += (((((pCity->getPopulation() * 50) + GC.getGameINLINE().getElapsedGameTurns() + 100) * 4) * pCity->plot()->calculateCulturePercent(pCity->getOwnerINLINE())) / 100);
*/
iValue += (((GC.getGameINLINE().getElapsedGameTurns() + 100) * 4) * pCity->plot()->calculateCulturePercent(pCity->getOwnerINLINE())) / 400;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
for (iI = 0; iI < pCity->getNumCityPlots(); iI++)
{
pLoopPlot = plotCity(pCity->getX_INLINE(), pCity->getY_INLINE(), iI);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getBonusType(getTeam()) != NO_BONUS)
{
iValue += (AI_bonusVal(pLoopPlot->getBonusType(getTeam())) * 10);
}
}
}
// Add in a multiple of what it produces each turn
int iCommercePerTurn = pCity->getCommerceRate(COMMERCE_GOLD) + pCity->getCommerceRate(COMMERCE_RESEARCH) + pCity->getCommerceRate(COMMERCE_CULTURE) + pCity->getCommerceRate(COMMERCE_ESPIONAGE);
iValue += 6*iCommercePerTurn;
// Don't count food - it doesn't contribute globally to the civ so population is a proxy
int iYieldPerTurn = pCity->getYieldRate(YIELD_PRODUCTION);
iValue += 12*iYieldPerTurn;
if (!(pCity->isEverOwned(getID())))
{
iValue *= 3;
iValue /= 2;
}
/************************************************************************************************/
/* Afforess 5/29/11 */
/* */
/* */
/************************************************************************************************/
//in danger
if (pCity->AI_isDanger() && !pCity->AI_isDefended(strengthOfBestUnitAI(DOMAIN_LAND, UNITAI_CITY_DEFENSE)))
{
iValue *= 2;
iValue /= 3;
}
//unstable
if (pCity->getRevolutionIndex() > 1000)
{
iValue *= 2;
iValue /= 3;
}
//colony
if (!GC.getMap().getArea(pCity->getArea())->isHomeArea(GET_PLAYER(pCity->getOwnerINLINE()).getID()))
{
iValue *= 4;
iValue /= 5;
}
//This city costs money, and we can't afford it
if (AI_isFinancialTrouble() && (pCity->getCommerceRateTimes100(COMMERCE_GOLD) - pCity->getMaintenanceTimes100() < 0))
{
iValue /= 2;
}
/*
iValue -= (iValue % GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
if (isHuman())
{
return std::max(iValue, GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
}
else
{
return iValue;
}
*/
return iValue * 15;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
/************************************************************************************************/
/* Afforess 5/29/11 */
/* */
/* */
/************************************************************************************************/
int CvPlayerAI::AI_ourCityValue(CvCity* pCity) const
{
CvPlot* pLoopPlot;
int iValue;
int iI;
iValue = 150;
//consider infrastructure
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (pCity->getNumBuilding((BuildingTypes)iI) > 0)
{
if (isWorldWonderClass((BuildingClassTypes)GC.getBuildingInfo((BuildingTypes)iI).getBuildingClassType()))
{
iValue += pCity->getNumBuilding((BuildingTypes)iI) * GC.getBuildingInfo((BuildingTypes)iI).getProductionCost() / 3;
}
else if (isLimitedWonderClass((BuildingClassTypes)GC.getBuildingInfo((BuildingTypes)iI).getBuildingClassType()))
{
iValue += pCity->getNumBuilding((BuildingTypes)iI) * GC.getBuildingInfo((BuildingTypes)iI).getProductionCost() / 5;
}
else
{
iValue += pCity->getNumBuilding((BuildingTypes)iI) * GC.getBuildingInfo((BuildingTypes)iI).getProductionCost() / 8;
}
}
}
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (pCity->isHasReligion((ReligionTypes)iI))
{
if (getStateReligion() == iI)
{
iValue += 100;
if (pCity->isHolyCity((ReligionTypes)iI))
{
iValue += 500;
}
break;
}
}
}
for (iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
if (pCity->isHasCorporation((CorporationTypes)iI))
{
iValue += AI_corporationValue((CorporationTypes)iI, pCity) / 25;
if (pCity->isHeadquarters((CorporationTypes)iI))
{
iValue += AI_corporationTradeVal((CorporationTypes)iI, pCity->getOwnerINLINE());
}
}
}
iValue += (pCity->getPopulation() * 50);
iValue += (pCity->getCultureLevel() * 200);
iValue += (((GC.getGameINLINE().getElapsedGameTurns() + 100) * 4) * pCity->plot()->calculateCulturePercent(pCity->getOwnerINLINE())) / 400;
for (iI = 0; iI < pCity->getNumCityPlots(); iI++)
{
pLoopPlot = plotCity(pCity->getX_INLINE(), pCity->getY_INLINE(), iI);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getBonusType(getTeam()) != NO_BONUS)
{
iValue += (AI_bonusVal(pLoopPlot->getBonusType(getTeam())) * 10);
}
}
}
//in danger
if (pCity->AI_isDanger() && !pCity->AI_isDefended(strengthOfBestUnitAI(DOMAIN_LAND, UNITAI_CITY_DEFENSE))) {
iValue *= 2;
iValue /= 3;
}
//unstable
if (pCity->getRevolutionIndex() > 1000) {
iValue *= 2;
iValue /= 3;
}
//colony
if (!GC.getMap().getArea(pCity->getArea())->isHomeArea(getID())) {
iValue *= 4;
iValue /= 5;
}
//This city is costing us money, and we can't afford it
if (AI_isFinancialTrouble() && (pCity->getCommerceRateTimes100(COMMERCE_GOLD) - pCity->getMaintenanceTimes100() < 0)) {
iValue /= 2;
}
return iValue;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
DenialTypes CvPlayerAI::AI_cityTrade(CvCity* pCity, PlayerTypes ePlayer) const
{
CvCity* pNearestCity;
FAssert(pCity->getOwnerINLINE() == getID());
if (pCity->getLiberationPlayer(false) == ePlayer)
{
return NO_DENIAL;
}
/************************************************************************************************/
/* Afforess Start 5/29/11 */
/* */
/* Improved AI */
/************************************************************************************************/
if (pCity->getID() == getCapitalCity()->getID())
{
return DENIAL_JOKING;
}
if (atWar(getTeam(), GET_PLAYER(ePlayer).getTeam()))
{
return NO_DENIAL;
}
if (!(GET_PLAYER(ePlayer).isHuman()) && GET_PLAYER(ePlayer).AI_isFinancialTrouble())
{
return DENIAL_MYSTERY;
}
if (!(GET_PLAYER(ePlayer).isHuman()))
{
pNearestCity = GC.getMapINLINE().findCity(pCity->getX_INLINE(), pCity->getY_INLINE(), ePlayer, NO_TEAM, true, false, NO_TEAM, NO_DIRECTION, pCity);
if ((pNearestCity == NULL) || (plotDistance(pCity->getX_INLINE(), pCity->getY_INLINE(), pNearestCity->getX_INLINE(), pNearestCity->getY_INLINE()) > 18))
{
return DENIAL_NO_GAIN;
}
}
if (isHuman())
{
return NO_DENIAL;
}
if (AI_ourCityValue(pCity) < (600 * getCurrentEra()))
{
if (getNumCities() > 1)
{
return NO_DENIAL;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (GET_PLAYER(ePlayer).getTeam() != getTeam())
{
return DENIAL_NEVER;
}
if (pCity->calculateCulturePercent(getID()) > 50)
{
return DENIAL_TOO_MUCH;
}
return NO_DENIAL;
}
int CvPlayerAI::AI_stopTradingTradeVal(TeamTypes eTradeTeam, PlayerTypes ePlayer) const
{
CvDeal* pLoopDeal;
int iModifier;
int iValue;
int iLoop;
FAssertMsg(ePlayer != getID(), "shouldn't call this function on ourselves");
FAssertMsg(GET_PLAYER(ePlayer).getTeam() != getTeam(), "shouldn't call this function on ourselves");
FAssertMsg(eTradeTeam != getTeam(), "shouldn't call this function on ourselves");
FAssertMsg(GET_TEAM(eTradeTeam).isAlive(), "GET_TEAM(eWarTeam).isAlive is expected to be true");
FAssertMsg(!atWar(eTradeTeam, GET_PLAYER(ePlayer).getTeam()), "eTeam should be at peace with eWarTeam");
iValue = (50 + (GC.getGameINLINE().getGameTurn() / 2));
iValue += (GET_TEAM(eTradeTeam).getNumCities() * 5);
iModifier = 0;
switch (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).AI_getAttitude(eTradeTeam))
{
case ATTITUDE_FURIOUS:
break;
case ATTITUDE_ANNOYED:
iModifier += 25;
break;
case ATTITUDE_CAUTIOUS:
iModifier += 50;
break;
case ATTITUDE_PLEASED:
iModifier += 100;
break;
case ATTITUDE_FRIENDLY:
iModifier += 200;
break;
default:
FAssert(false);
break;
}
iValue *= std::max(0, (iModifier + 100));
iValue /= 100;
if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isOpenBorders(eTradeTeam))
{
iValue *= 2;
}
if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isDefensivePact(eTradeTeam))
{
iValue *= 3;
}
for(pLoopDeal = GC.getGameINLINE().firstDeal(&iLoop); pLoopDeal != NULL; pLoopDeal = GC.getGameINLINE().nextDeal(&iLoop))
{
if (pLoopDeal->isCancelable(getID()) && !(pLoopDeal->isPeaceDeal()))
{
if (GET_PLAYER(pLoopDeal->getFirstPlayer()).getTeam() == GET_PLAYER(ePlayer).getTeam())
{
if (pLoopDeal->getLengthSecondTrades() > 0)
{
iValue += (GET_PLAYER(pLoopDeal->getFirstPlayer()).AI_dealVal(pLoopDeal->getSecondPlayer(), pLoopDeal->getSecondTrades()) * ((pLoopDeal->getLengthFirstTrades() == 0) ? 2 : 1));
}
}
if (GET_PLAYER(pLoopDeal->getSecondPlayer()).getTeam() == GET_PLAYER(ePlayer).getTeam())
{
if (pLoopDeal->getLengthFirstTrades() > 0)
{
iValue += (GET_PLAYER(pLoopDeal->getSecondPlayer()).AI_dealVal(pLoopDeal->getFirstPlayer(), pLoopDeal->getFirstTrades()) * ((pLoopDeal->getLengthSecondTrades() == 0) ? 2 : 1));
}
}
}
}
if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isVassal(getTeam()))
{
iValue /= 2;
}
iValue -= (iValue % GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
if (isHuman())
{
return std::max(iValue, GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
}
else
{
return iValue;
}
}
DenialTypes CvPlayerAI::AI_stopTradingTrade(TeamTypes eTradeTeam, PlayerTypes ePlayer) const
{
AttitudeTypes eAttitude;
AttitudeTypes eAttitudeThem;
int iI;
FAssertMsg(ePlayer != getID(), "shouldn't call this function on ourselves");
FAssertMsg(GET_PLAYER(ePlayer).getTeam() != getTeam(), "shouldn't call this function on ourselves");
FAssertMsg(eTradeTeam != getTeam(), "shouldn't call this function on ourselves");
FAssertMsg(GET_TEAM(eTradeTeam).isAlive(), "GET_TEAM(eTradeTeam).isAlive is expected to be true");
FAssertMsg(!atWar(getTeam(), eTradeTeam), "should be at peace with eTradeTeam");
if (isHuman())
{
return NO_DENIAL;
}
if (GET_TEAM(getTeam()).isVassal(GET_PLAYER(ePlayer).getTeam()))
{
return NO_DENIAL;
}
if (GET_TEAM(getTeam()).isVassal(eTradeTeam))
{
return DENIAL_POWER_THEM;
}
eAttitude = GET_TEAM(getTeam()).AI_getAttitude(GET_PLAYER(ePlayer).getTeam());
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (GET_PLAYER((PlayerTypes)iI).getTeam() == getTeam())
{
if (eAttitude <= GC.getLeaderHeadInfo(GET_PLAYER((PlayerTypes)iI).getPersonalityType()).getStopTradingRefuseAttitudeThreshold())
{
return DENIAL_ATTITUDE;
}
}
}
}
eAttitudeThem = GET_TEAM(getTeam()).AI_getAttitude(eTradeTeam);
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (GET_PLAYER((PlayerTypes)iI).getTeam() == getTeam())
{
if (eAttitudeThem > GC.getLeaderHeadInfo(GET_PLAYER((PlayerTypes)iI).getPersonalityType()).getStopTradingThemRefuseAttitudeThreshold())
{
return DENIAL_ATTITUDE_THEM;
}
}
}
}
/************************************************************************************************/
/* Afforess Start 03/19/10 */
/* */
/* Ruthless AI: Don't cancel open borders, we may need those in war */
/************************************************************************************************/
//Fuyu: looks like a good idea, not just for ruthless AI
/*
if (GC.getGameINLINE().isOption(GAMEOPTION_RUTHLESS_AI))
*/
{
if (GET_TEAM(getTeam()).isOpenBorders(eTradeTeam))
{
if (GET_TEAM(getTeam()).getAnyWarPlanCount(true) > 0 && GET_TEAM(getTeam()).AI_getWarPlan(eTradeTeam) == NO_WARPLAN)
{
return DENIAL_MYSTERY;
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return NO_DENIAL;
}
int CvPlayerAI::AI_civicTradeVal(CivicTypes eCivic, PlayerTypes ePlayer) const
{
CivicTypes eBestCivic;
int iValue;
iValue = (2 * (getTotalPopulation() + GET_PLAYER(ePlayer).getTotalPopulation())); // XXX
eBestCivic = GET_PLAYER(ePlayer).AI_bestCivic((CivicOptionTypes)(GC.getCivicInfo(eCivic).getCivicOptionType()));
if (eBestCivic != NO_CIVIC)
{
if (eBestCivic != eCivic)
{
iValue += std::max(0, ((GET_PLAYER(ePlayer).AI_civicValue(eBestCivic) - GET_PLAYER(ePlayer).AI_civicValue(eCivic)) * 2));
}
}
if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isVassal(getTeam()))
{
iValue /= 2;
}
iValue -= (iValue % GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
if (isHuman())
{
return std::max(iValue, GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
}
else
{
return iValue;
}
}
DenialTypes CvPlayerAI::AI_civicTrade(CivicTypes eCivic, PlayerTypes ePlayer) const
{
/************************************************************************************************/
/* Afforess Start 04/05/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getGameINLINE().isOption(GAMEOPTION_ADVANCED_DIPLOMACY))
{
if (GET_TEAM(getTeam()).isAtWar(GET_PLAYER(ePlayer).getTeam()))
{
return NO_DENIAL;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (isHuman())
{
return NO_DENIAL;
}
if (GET_TEAM(getTeam()).isVassal(GET_PLAYER(ePlayer).getTeam()))
{
return NO_DENIAL;
}
if (atWar(getTeam(), GET_PLAYER(ePlayer).getTeam()))
{
return NO_DENIAL;
}
if (GET_PLAYER(ePlayer).getTeam() == getTeam())
{
return NO_DENIAL;
}
if (getCivicPercentAnger(getCivics((CivicOptionTypes)(GC.getCivicInfo(eCivic).getCivicOptionType())),true) > getCivicPercentAnger(eCivic))
{
return DENIAL_ANGER_CIVIC;
}
CivicTypes eFavoriteCivic = (CivicTypes)GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivic();
if (eFavoriteCivic != NO_CIVIC)
{
if (isCivic(eFavoriteCivic) && (GC.getCivicInfo(eCivic).getCivicOptionType() == GC.getCivicInfo(eFavoriteCivic).getCivicOptionType()))
{
return DENIAL_FAVORITE_CIVIC;
}
}
if (GC.getCivilizationInfo(getCivilizationType()).getCivilizationInitialCivics(GC.getCivicInfo(eCivic).getCivicOptionType()) == eCivic)
{
return DENIAL_JOKING;
}
if (AI_getAttitude(ePlayer) <= GC.getLeaderHeadInfo(getPersonalityType()).getAdoptCivicRefuseAttitudeThreshold())
{
return DENIAL_ATTITUDE;
}
/************************************************************************************************/
/* Afforess Start 03/19/10 */
/* */
/* Ruthless AI: Don't change civics when planning war */
/************************************************************************************************/
if (GC.getGameINLINE().isOption(GAMEOPTION_RUTHLESS_AI))
{
if (GET_TEAM(getTeam()).getAnyWarPlanCount(true) > 0)
{
return DENIAL_JOKING;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return NO_DENIAL;
}
int CvPlayerAI::AI_religionTradeVal(ReligionTypes eReligion, PlayerTypes ePlayer) const
{
ReligionTypes eBestReligion;
int iValue;
iValue = (3 * (getTotalPopulation() + GET_PLAYER(ePlayer).getTotalPopulation())); // XXX
eBestReligion = GET_PLAYER(ePlayer).AI_bestReligion();
if (eBestReligion != NO_RELIGION)
{
if (eBestReligion != eReligion)
{
iValue += std::max(0, (GET_PLAYER(ePlayer).AI_religionValue(eBestReligion) - GET_PLAYER(ePlayer).AI_religionValue(eReligion)));
}
}
if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isVassal(getTeam()))
{
iValue /= 2;
}
iValue -= (iValue % GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
if (isHuman())
{
return std::max(iValue, GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
}
else
{
return iValue;
}
}
DenialTypes CvPlayerAI::AI_religionTrade(ReligionTypes eReligion, PlayerTypes ePlayer) const
{
if (isHuman())
{
return NO_DENIAL;
}
if (GET_TEAM(getTeam()).isVassal(GET_PLAYER(ePlayer).getTeam()))
{
return NO_DENIAL;
}
if (atWar(getTeam(), GET_PLAYER(ePlayer).getTeam()))
{
return NO_DENIAL;
}
if (GET_PLAYER(ePlayer).getTeam() == getTeam())
{
return NO_DENIAL;
}
if (getStateReligion() != NO_RELIGION)
{
if (getHasReligionCount(eReligion) < std::min((getHasReligionCount(getStateReligion()) - 1), (getNumCities() / 2)))
{
return DENIAL_MINORITY_RELIGION;
}
}
if (AI_getAttitude(ePlayer) <= GC.getLeaderHeadInfo(getPersonalityType()).getConvertReligionRefuseAttitudeThreshold())
{
return DENIAL_ATTITUDE;
}
/************************************************************************************************/
/* Afforess Start 03/19/10 */
/* */
/* Ruthless AI: Don't Change Religions When we are planning war (Anarchy is bad) */
/************************************************************************************************/
if (GC.getGameINLINE().isOption(GAMEOPTION_RUTHLESS_AI))
{
if (GET_TEAM(getTeam()).getAnyWarPlanCount(true) > 0)
{
return DENIAL_NO_GAIN;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return NO_DENIAL;
}
int CvPlayerAI::AI_unitImpassableCount(UnitTypes eUnit) const
{
int iCount = 0;
for (int iI = 0; iI < GC.getNumTerrainInfos(); iI++)
{
if (GC.getUnitInfo(eUnit).getTerrainImpassable(iI))
{
TechTypes eTech = (TechTypes)GC.getUnitInfo(eUnit).getTerrainPassableTech(iI);
if (NO_TECH == eTech || !GET_TEAM(getTeam()).isHasTech(eTech))
{
iCount++;
}
}
}
for (int iI = 0; iI < GC.getNumFeatureInfos(); iI++)
{
if (GC.getUnitInfo(eUnit).getFeatureImpassable(iI))
{
TechTypes eTech = (TechTypes)GC.getUnitInfo(eUnit).getFeaturePassableTech(iI);
if (NO_TECH == eTech || !GET_TEAM(getTeam()).isHasTech(eTech))
{
iCount++;
}
}
}
return iCount;
}
int CvPlayerAI::AI_unitPropertyValue(UnitTypes eUnit, PropertyTypes eProperty) const
{
CvPropertyManipulators* propertyManipulators = GC.getUnitInfo(eUnit).getPropertyManipulators();
int iValue = 0;
if ( propertyManipulators != NULL )
{
for(int iI = 0; iI < propertyManipulators->getNumSources(); iI++)
{
CvPropertySource* pSource = propertyManipulators->getSource(iI);
if ( pSource->getType() == PROPERTYSOURCE_CONSTANT &&
(eProperty == NO_PROPERTY || pSource->getProperty() == eProperty))
{
// We have a source for a property - value is crudely just the AIweight of that property times the source size (which is expected to only depend on the player)
PropertyTypes eProperty = pSource->getProperty();
iValue += GC.getPropertyInfo(eProperty).getAIWeight()*((CvPropertySourceConstant*)pSource)->getAmountPerTurn(getGameObjectConst());
}
}
}
return iValue;
}
/********************************************************************************/
/* City Defenders 24.07.2010 Fuyu */
/********************************************************************************/
//Fuyu bIgnoreNotUnitAIs
int CvPlayerAI::AI_unitValue(UnitTypes eUnit, UnitAITypes eUnitAI, CvArea* pArea, CvUnitSelectionCriteria* criteria) const
/********************************************************************************/
/* City Defenders END */
/********************************************************************************/
{
PROFILE_FUNC();
bool bValid;
int iNeededMissionaries;
int iCombatValue;
int iValue;
int iTempValue;
int iI;
FAssertMsg(eUnit != NO_UNIT, "Unit is not assigned a valid value");
FAssertMsg(eUnitAI != NO_UNITAI, "UnitAI is not assigned a valid value");
if (GC.getUnitInfo(eUnit).getDomainType() != AI_unitAIDomainType(eUnitAI))
{
if (eUnitAI != UNITAI_ICBM)//XXX
{
return 0;
}
}
/********************************************************************************/
/* City Defenders 24.07.2010 Fuyu */
/********************************************************************************/
//Fuyu bIgnoreNotUnitAIs
if (GC.getUnitInfo(eUnit).getNotUnitAIType(eUnitAI) && (criteria == NULL || !criteria->m_bIgnoreNotUnitAIs))
{
return 0;
}
/********************************************************************************/
/* City Defenders END */
/********************************************************************************/
bValid = GC.getUnitInfo(eUnit).getUnitAIType(eUnitAI);
if (!bValid)
{
switch (eUnitAI)
{
case UNITAI_UNKNOWN:
break;
case UNITAI_SUBDUED_ANIMAL:
case UNITAI_HUNTER:
break;
//TB Animal Search Note: will need adjusting when multiple animal ais are implemented.
case UNITAI_ANIMAL:
if (GC.getUnitInfo(eUnit).isAnimal())
{
bValid = true;
}
break;
case UNITAI_SETTLE:
if (GC.getUnitInfo(eUnit).isFound())
{
bValid = true;
}
break;
case UNITAI_WORKER:
for (iI = 0; iI < GC.getNumBuildInfos(); iI++)
{
if (GC.getUnitInfo(eUnit).getBuilds(iI))
{
bValid = true;
break;
}
}
break;
case UNITAI_ATTACK:
if (GC.getUnitInfo(eUnit).getCombat() > 0)
{
if (!(GC.getUnitInfo(eUnit).isOnlyDefensive()))
{
bValid = true;
}
}
break;
case UNITAI_ATTACK_CITY:
if (GC.getUnitInfo(eUnit).getCombat() > 0)
{
if (!(GC.getUnitInfo(eUnit).isOnlyDefensive()))
{
if (!(GC.getUnitInfo(eUnit).isNoCapture()))
{
bValid = true;
}
}
}
break;
case UNITAI_COLLATERAL:
if (GC.getUnitInfo(eUnit).getCombat() > 0)
{
if (!(GC.getUnitInfo(eUnit).isOnlyDefensive()))
{
if (GC.getUnitInfo(eUnit).getCollateralDamage() > 0)
{
bValid = true;
}
}
}
break;
case UNITAI_PILLAGE:
if (GC.getUnitInfo(eUnit).getCombat() > 0)
{
if (!(GC.getUnitInfo(eUnit).isOnlyDefensive()))
{
bValid = true;
}
}
break;
case UNITAI_RESERVE:
if (GC.getUnitInfo(eUnit).getCombat() > 0)
{
if (!(GC.getUnitInfo(eUnit).isOnlyDefensive()))
{
bValid = true;
}
}
break;
case UNITAI_PILLAGE_COUNTER:
if (GC.getUnitInfo(eUnit).getCombat() > 0)
{
if (!(GC.getUnitInfo(eUnit).isOnlyDefensive()))
{
if (eUnitAI == UNITAI_PILLAGE_COUNTER && GC.getUnitInfo(eUnit).getNumSeeInvisibleTypes() > 0)
{
// Slightly crude - should really do a domain check but this will suffice unless
// the XML is weird
bValid = true;
}
}
}
break;
case UNITAI_COUNTER:
if (GC.getUnitInfo(eUnit).getCombat() > 0)
{
if (!(GC.getUnitInfo(eUnit).isOnlyDefensive()))
{
if (GC.getUnitInfo(eUnit).getInterceptionProbability() > 0)
{
bValid = true;
}
if (!bValid)
{
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
if (GC.getUnitInfo(eUnit).getUnitClassAttackModifier(iI) > 0)
{
bValid = true;
break;
}
if (GC.getUnitInfo(eUnit).getTargetUnitClass(iI))
{
bValid = true;
break;
}
}
}
if (!bValid)
{
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
if (GC.getUnitInfo(eUnit).getUnitCombatModifier(iI) > 0)
{
bValid = true;
break;
}
if (GC.getUnitInfo(eUnit).getTargetUnitCombat(iI))
{
bValid = true;
break;
}
}
}
if (!bValid)
{
for (iI = 0; iI < GC.getNumUnitInfos(); iI++)
{
int iUnitClass = GC.getUnitInfo(eUnit).getUnitClassType();
if (NO_UNITCLASS != iUnitClass && GC.getUnitInfo((UnitTypes)iI).getDefenderUnitClass(iUnitClass))
{
bValid = true;
break;
}
int iUnitCombat = GC.getUnitInfo(eUnit).getUnitCombatType();
if (NO_UNITCOMBAT != iUnitCombat && GC.getUnitInfo((UnitTypes)iI).getDefenderUnitCombat(iUnitCombat))
{
bValid = true;
break;
}
}
}
}
}
break;
case UNITAI_CITY_DEFENSE:
if (GC.getUnitInfo(eUnit).getCombat() > 0)
{
if (!(GC.getUnitInfo(eUnit).isNoDefensiveBonus()))
{
if (GC.getUnitInfo(eUnit).getCityDefenseModifier() > 0)
{
bValid = true;
}
}
}
break;
case UNITAI_CITY_COUNTER:
if (GC.getUnitInfo(eUnit).getCombat() > 0)
{
if (!(GC.getUnitInfo(eUnit).isNoDefensiveBonus()))
{
if (GC.getUnitInfo(eUnit).getInterceptionProbability() > 0)
{
bValid = true;
}
if (!bValid)
{
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
if (GC.getUnitInfo(eUnit).getUnitClassDefenseModifier(iI) > 0)
{
bValid = true;
break;
}
}
}
if (!bValid)
{
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
if (GC.getUnitInfo(eUnit).getUnitCombatModifier(iI) > 0)
{
bValid = true;
break;
}
}
}
}
}
break;
case UNITAI_CITY_SPECIAL:
break;
case UNITAI_PARADROP:
if (GC.getUnitInfo(eUnit).getDropRange() > 0)
{
bValid = true;
}
break;
case UNITAI_EXPLORE:
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 12/09/09 jdog5000 */
/* */
/* Unit AI */
/************************************************************************************************/
if (GC.getUnitInfo(eUnit).getCombat() > 0 && !(GC.getUnitInfo(eUnit).isNoRevealMap()))
{
if (0 == AI_unitImpassableCount(eUnit))
{
bValid = true;
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
break;
case UNITAI_MISSIONARY:
if (pArea != NULL)
{
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (GC.getUnitInfo(eUnit).getReligionSpreads((ReligionTypes)iI) > 0)
{
iNeededMissionaries = AI_neededMissionaries(pArea, ((ReligionTypes)iI));
if (iNeededMissionaries > 0)
{
if (iNeededMissionaries > countReligionSpreadUnits(pArea, ((ReligionTypes)iI)))
{
bValid = true;
break;
}
}
}
}
for (iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
if (GC.getUnitInfo(eUnit).getCorporationSpreads((CorporationTypes)iI) > 0)
{
iNeededMissionaries = AI_neededExecutives(pArea, ((CorporationTypes)iI));
if (iNeededMissionaries > 0)
{
if (iNeededMissionaries > countCorporationSpreadUnits(pArea, ((CorporationTypes)iI)))
{
bValid = true;
break;
}
}
}
}
}
break;
case UNITAI_PROPHET:
case UNITAI_ARTIST:
case UNITAI_SCIENTIST:
case UNITAI_GENERAL:
case UNITAI_MERCHANT:
case UNITAI_ENGINEER:
case UNITAI_SPY:
/************************************************************************************************/
/* Great Diplomat MOD START */
/************************************************************************************************/
case UNITAI_DIPLOMAT:
/************************************************************************************************/
/* Great Diplomat MOD END */
/************************************************************************************************/
break;
case UNITAI_ICBM:
if (GC.getUnitInfo(eUnit).getNukeRange() != -1)
{
bValid = true;
}
break;
case UNITAI_WORKER_SEA:
for (iI = 0; iI < GC.getNumBuildInfos(); iI++)
{
if (GC.getUnitInfo(eUnit).getBuilds(iI))
{
bValid = true;
break;
}
}
break;
case UNITAI_ATTACK_SEA:
// Thomas SG - AC: Advanced Cargo START
{
if (GC.getUnitInfo(eUnit).getCombat() > 0)
{
if ((GC.getUnitInfo(eUnit).getTotalCargoSpace() - GC.getUnitInfo(eUnit).getTotalSpecialCargoSpace()) == 0)
{
if (!(GC.getUnitInfo(eUnit).isInvisible()) && (GC.getUnitInfo(eUnit).getInvisibleType() == NO_INVISIBLE))
{
bValid = true;
}
}
}
}
// Thomas SG - AC: Advanced Cargo END
break;
case UNITAI_RESERVE_SEA:
// Thomas SG - AC: Advanced Cargo START
{
if (GC.getUnitInfo(eUnit).getCombat() > 0)
{
if ((GC.getUnitInfo(eUnit).getTotalCargoSpace() - GC.getUnitInfo(eUnit).getTotalSpecialCargoSpace()) == 0)
{
bValid = true;
}
}
}
// Thomas SG - AC: Advanced Cargo END
break;
case UNITAI_ESCORT_SEA:
// Thomas SG - AC: Advanced Cargo START
{
if (GC.getUnitInfo(eUnit).getCombat() > 0)
{
if ((GC.getUnitInfo(eUnit).getTotalCargoSpace() - GC.getUnitInfo(eUnit).getTotalSpecialCargoSpace()) == 0)
{
if (0 == AI_unitImpassableCount(eUnit))
{
bValid = true;
}
}
}
}
// Thomas SG - AC: Advanced Cargo END
break;
case UNITAI_EXPLORE_SEA:
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 12/09/09 jdog5000 */
/* */
/* Unit AI */
/************************************************************************************************/
// Thomas SG - AC: Advanced Cargo START
{
if ((GC.getUnitInfo(eUnit).getTotalCargoSpace() - GC.getUnitInfo(eUnit).getTotalSpecialCargoSpace()) <= 2 && !(GC.getUnitInfo(eUnit).isNoRevealMap()))
{
bValid = true;
}
}
// Thomas SG - AC: Advanced Cargo END
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
break;
case UNITAI_ASSAULT_SEA:
case UNITAI_SETTLER_SEA:
// Thomas SG - AC: Advanced Cargo START
{
if ((GC.getUnitInfo(eUnit).getTotalCargoSpace() - GC.getUnitInfo(eUnit).getTotalSpecialCargoSpace()) > 0)
{
bValid = true;
}
break;
}
// Thomas SG - AC: Advanced Cargo END
break;
case UNITAI_MISSIONARY_SEA:
case UNITAI_SPY_SEA:
case UNITAI_CARRIER_SEA:
case UNITAI_MISSILE_CARRIER_SEA:
// Thomas SG - AC: Advanced Cargo START
{
if (GC.getUnitInfo(eUnit).getTotalSpecialCargoSpace() > 0)
{
if (GC.getUnitInfo(eUnit).getNumSpecialCargos() > 0)
{
for (int j = 0; j < GC.getUnitInfo(eUnit).getNumSpecialCargos(); j++)
{
for (int i = 0; i < NUM_UNITAI_TYPES; ++i)
{
if (GC.getSpecialUnitInfo((SpecialUnitTypes)GC.getUnitInfo(eUnit).getSpecialCargo(j)).isCarrierUnitAIType(eUnitAI))
{
bValid = true;
break;
}
}
}
}
}
break;
}
// Thomas SG - AC: Advanced Cargo END
case UNITAI_PIRATE_SEA:
if (GC.getUnitInfo(eUnit).isAlwaysHostile() && GC.getUnitInfo(eUnit).isHiddenNationality())
{
bValid = true;
}
break;
case UNITAI_ATTACK_AIR:
if (GC.getUnitInfo(eUnit).getAirCombat() > 0)
{
if (!GC.getUnitInfo(eUnit).isSuicide())
{
bValid = true;
}
}
break;
case UNITAI_DEFENSE_AIR:
if (GC.getUnitInfo(eUnit).getInterceptionProbability() > 0)
{
bValid = true;
}
break;
case UNITAI_CARRIER_AIR:
if (GC.getUnitInfo(eUnit).getAirCombat() > 0)
{
if (GC.getUnitInfo(eUnit).getInterceptionProbability() > 0)
{
bValid = true;
}
}
break;
case UNITAI_MISSILE_AIR:
if (GC.getUnitInfo(eUnit).getAirCombat() > 0)
{
if (GC.getUnitInfo(eUnit).isSuicide())
{
bValid = true;
}
}
break;
case UNITAI_ATTACK_CITY_LEMMING:
bValid = false;
break;
default:
FAssert(false);
break;
}
}
if (!bValid)
{
return 0;
}
iCombatValue = GC.getGameINLINE().AI_combatValue(eUnit);
PropertyTypes ePropertyRequested = (criteria == NULL ? NO_PROPERTY : criteria->m_eProperty);
int iPropertyValue = AI_unitPropertyValue(eUnit, ePropertyRequested);
if ( ePropertyRequested != NO_PROPERTY && iPropertyValue <= 0 )
{
iValue = 0;
}
else
{
iValue = 1;
iValue += GC.getUnitInfo(eUnit).getAIWeight();
int iFastMoverMultiplier;
switch (eUnitAI)
{
case UNITAI_UNKNOWN:
case UNITAI_ANIMAL:
case UNITAI_SUBDUED_ANIMAL:
break;
case UNITAI_SETTLE:
iValue += (GC.getUnitInfo(eUnit).getMoves() * 100);
break;
case UNITAI_WORKER:
for (iI = 0; iI < GC.getNumBuildInfos(); iI++)
{
if (GC.getUnitInfo(eUnit).getBuilds(iI))
{
iValue++;
}
}
iValue += (GC.getUnitInfo(eUnit).getMoves() * iValue)/2;
// Scale by how fast a worker works - the extra '4' is a fudge factor
// to make worker values (somewhat) comparable to military unit values
// now that we have workers that can upgrade to military and we need to
// compare (at least very roughly)
iValue = (iValue * GC.getUnitInfo(eUnit).getWorkRate())/400;
break;
case UNITAI_ATTACK:
// For now the AI cannot cope with bad prroperty values on anything but hunter or pillage units
if ( iPropertyValue < 0 )
{
iValue = 0;
break;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 06/12/09 jdog5000 */
/* */
/* Unit AI */
/************************************************************************************************/
iFastMoverMultiplier = AI_isDoStrategy(AI_STRATEGY_FASTMOVERS) ? 3 : 1;
iValue += iCombatValue;
iValue += ((iCombatValue * (GC.getUnitInfo(eUnit).getMoves() - 1) * iFastMoverMultiplier) / 3); // K-Mod put in -1 !
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getWithdrawalProbability()) / 100);
if (GC.getUnitInfo(eUnit).getCombatLimit() < 100)
{
iValue -= (iCombatValue * (125 - GC.getUnitInfo(eUnit).getCombatLimit())) / 100;
}
// Also useful if attack stacks can make use of defensive terrain, though
// its not a huge factor since we can assume some defensive units will be
// along for the ride
if ( GC.getUnitInfo(eUnit).isNoDefensiveBonus() )
{
iValue *= 4;
iValue /= 5;
}
// Combat modifiers matter for attack units
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getUnitCombatModifier(iI) * AI_getUnitCombatWeight((UnitCombatTypes)iI)) / 10000);
}
break;
case UNITAI_ATTACK_CITY:
// For now the AI cannot cope with bad prroperty values on anything but hunter or pillage units
if ( iPropertyValue < 0 )
{
iValue = 0;
break;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 02/24/10 jdog5000 */
/* */
/* War strategy AI */
/* Rewritten by Fuyu */
/************************************************************************************************/
// Effect army composition to have more collateral/bombard units
iFastMoverMultiplier = AI_isDoStrategy(AI_STRATEGY_FASTMOVERS) ? 4 : 1;
iTempValue = ((iCombatValue * iCombatValue) / 75) + (iCombatValue / 2);
iValue += iTempValue;
if (GC.getUnitInfo(eUnit).isNoDefensiveBonus())
{
//iValue -= iTempValue / 2;
iValue -= iTempValue / 4; // K-Mod. (I'd say knights, tanks, etc. are very good for city attack...)
}
if (GC.getUnitInfo(eUnit).getDropRange() > 0)
{
//iValue -= iTempValue / 2;
// K-Mod (how is drop range a disadvantage?)
}
if (GC.getUnitInfo(eUnit).isFirstStrikeImmune())
{
iValue += (iTempValue * 8) / 100;
}
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getCityAttackModifier()) / 75);
/* Collateral Damage valuation moved to bombard part
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getCollateralDamage()) / 400);
*/
iValue += ((iCombatValue * (GC.getUnitInfo(eUnit).getMoves() - 1) * iFastMoverMultiplier) / 4); // K-Mod put in -1 !
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getWithdrawalProbability()) / 100);
/*
if (!AI_isDoStrategy(AI_STRATEGY_AIR_BLITZ))
{
*/
if (GC.getUnitInfo(eUnit).getBombardRate() > 0 || (GC.getUnitInfo(eUnit).getCollateralDamageMaxUnits() > 0 && GC.getUnitInfo(eUnit).getCollateralDamage() > 0))
{
// Army composition needs to scale with army size, bombard unit potency
//modified AI_calculateTotalBombard(DOMAIN_LAND) code
int iII;
int iTotalBombard = 0;
int iThisBombard = GC.getUnitInfo(eUnit).getBombardRate();
int iSiegeUnits = 0;
int iSiegeImmune = 0;
int iTotalSiegeMaxUnits = 0;
bool bNoBombardValue = false;
for (iII = 0; iII < GC.getNumUnitClassInfos(); iII++)
{
UnitTypes eLoopUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iII)));
if (eLoopUnit != NO_UNIT)
{
if (GC.getUnitInfo(eLoopUnit).getDomainType() == DOMAIN_LAND)
{
int iClassCount = getUnitClassCount((UnitClassTypes)iII);
int iBombardRate = GC.getUnitInfo(eLoopUnit).getBombardRate();
if (iBombardRate > 0)
{
iTotalBombard += ((iBombardRate * iClassCount * ((GC.getUnitInfo(eLoopUnit).isIgnoreBuildingDefense()) ? 3 : 2)) / 2);
}
int iBombRate = GC.getUnitInfo(eLoopUnit).getBombRate();
if (iBombRate > 0)
{
iTotalBombard += iBombRate * iClassCount;
}
int iCollateralDamageMaxUnits = GC.getUnitInfo(eLoopUnit).getCollateralDamageMaxUnits();
if (iCollateralDamageMaxUnits > 0 && GC.getUnitInfo(eLoopUnit).getCollateralDamage() > 0)
{
iTotalSiegeMaxUnits += iCollateralDamageMaxUnits * iClassCount;
iSiegeUnits += iClassCount;
}
else if (GC.getUnitInfo(eLoopUnit).getUnitCombatCollateralImmune((UnitCombatTypes)GC.getUnitInfo(eUnit).getUnitCombatType()))
{
iSiegeImmune+= iClassCount;
}
}
}
}
if (iThisBombard == 0)
{
bNoBombardValue = true;
}
else if ((100*iTotalBombard)/(std::max(1, (iThisBombard*AI_totalUnitAIs(UNITAI_ATTACK_CITY)))) >= GC.getDefineINT("BBAI_BOMBARD_ATTACK_CITY_MAX_STACK_FRACTION"))
{
//too many bombard units already
bNoBombardValue = true;
}
int iNumOffensiveUnits = AI_totalUnitAIs(UNITAI_ATTACK_CITY) + AI_totalUnitAIs(UNITAI_ATTACK) + AI_totalUnitAIs(UNITAI_COUNTER)/2;
int iNumDefensiveUnits = AI_totalUnitAIs(UNITAI_CITY_DEFENSE) + AI_totalUnitAIs(UNITAI_RESERVE) + AI_totalUnitAIs(UNITAI_CITY_COUNTER)/2 + AI_totalUnitAIs(UNITAI_COLLATERAL)/2;
iSiegeUnits += (iSiegeImmune*iNumOffensiveUnits)/std::max(1,iNumOffensiveUnits+iNumDefensiveUnits);
int iMAX_HIT_POINTS = GC.getDefineINT("MAX_HIT_POINTS");
int iCollateralDamageMaxUnitsWeight = (100 * (iNumOffensiveUnits - iSiegeUnits)) / std::max(1,iTotalSiegeMaxUnits);
iCollateralDamageMaxUnitsWeight = std::min(100, iCollateralDamageMaxUnitsWeight);
//to decrease value further for units with low damage limits:
int iCollateralDamageLimitWeight = 100*iMAX_HIT_POINTS - std::max(0, ((iMAX_HIT_POINTS - GC.getUnitInfo(eUnit).getCollateralDamageLimit()) * (100 - iCollateralDamageMaxUnitsWeight)));
iCollateralDamageLimitWeight /= iMAX_HIT_POINTS;
int iCollateralValue = iCombatValue * GC.getUnitInfo(eUnit).getCollateralDamage() * GC.getDefineINT("COLLATERAL_COMBAT_DAMAGE");
iCollateralValue /= 100;
iCollateralValue *= std::max(100, (GC.getUnitInfo(eUnit).getCollateralDamageMaxUnits() * iCollateralDamageMaxUnitsWeight));
iCollateralValue /= 100;
iCollateralValue *= iCollateralDamageLimitWeight;
iCollateralValue /= 100;
iCollateralValue /= iMAX_HIT_POINTS;
iValue += iCollateralValue;
if (!bNoBombardValue && !AI_isDoStrategy(AI_STRATEGY_AIR_BLITZ))
{
/* original code
int iBombardValue = GC.getUnitInfo(eUnit).getBombardRate() * 4;
*/
int iBombardValue = GC.getUnitInfo(eUnit).getBombardRate() * ((GC.getUnitInfo(eUnit).isIgnoreBuildingDefense()) ? 3 : 2);
//int iTotalBombardValue = 4 * iTotalBombard;
//int iNumBombardUnits = 2 * iTotalBombard / iBombardValue;
int iAIDesiredBombardFraction = std::max( 5, GC.getDefineINT("BBAI_BOMBARD_ATTACK_STACK_FRACTION")); /*default: 15*/
int iActualBombardFraction = (100 * 2 * iTotalBombard)/(iBombardValue * std::max(1, iNumOffensiveUnits));
iActualBombardFraction = std::min(100, iActualBombardFraction);
// K - Mod note : This goal has no dependancy on civ size, map size, era, strategy, or anything else that matters
// a flat goal of 200... This needs to be fixed. For now, I'll just replace it with something rough.
// But this is a future "todo".
// int iGoalTotalBombard = 200;
int iGoalTotalBombard = (getNumCities() + 3) * (getCurrentEra() + 2) * (AI_isDoStrategy(AI_STRATEGY_CRUSH) ? 10 : 5);
int iTempBombardValue = 0;
if (iTotalBombard < iGoalTotalBombard) //still less than 200 bombard points
{
iTempBombardValue = iBombardValue * (iGoalTotalBombard + 7 * (iGoalTotalBombard - iTotalBombard));
iTempBombardValue /= iGoalTotalBombard;
//iTempBombardValue is at most (8 * iBombardValue)
}
else
{
iTempBombardValue *= iGoalTotalBombard;
iTempBombardValue /= std::min(2*iGoalTotalBombard, 2*iTotalBombard - iGoalTotalBombard);
}
if (iActualBombardFraction < iAIDesiredBombardFraction)
{
iBombardValue *= (iAIDesiredBombardFraction + 5 * (iAIDesiredBombardFraction - iActualBombardFraction));
iBombardValue /= iAIDesiredBombardFraction;
//new iBombardValue is at most (6 * old iBombardValue)
}
else
{
iBombardValue *= iAIDesiredBombardFraction;
iBombardValue /= std::max(1, iActualBombardFraction);
}
if (iTempBombardValue > iBombardValue)
{
iBombardValue = iTempBombardValue;
}
iValue += iBombardValue;
}
}
/*
}
*/
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
break;
case UNITAI_COLLATERAL:
iValue += iCombatValue;
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getCollateralDamage()) / 50);
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getMoves()) / 4);
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getWithdrawalProbability()) / 25);
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getCityAttackModifier()) / 100);// was -= ???
break;
case UNITAI_PILLAGE:
iValue += iCombatValue;
iValue += (iCombatValue * GC.getUnitInfo(eUnit).getMoves());
break;
case UNITAI_RESERVE:
iValue += iCombatValue;
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getCollateralDamage()) / 200);
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
// int iCombatModifier = GC.getUnitInfo(eUnit).getUnitCombatModifier(iI);
// iCombatModifier = (iCombatModifier < 40) ? iCombatModifier : (40 + (iCombatModifier - 40) / 2);
// iValue += ((iCombatValue * iCombatModifier) / 100);
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getUnitCombatModifier(iI) * AI_getUnitCombatWeight((UnitCombatTypes)iI)) / 12000);
}
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getMoves()) / 2);
break;
case UNITAI_PILLAGE_COUNTER:
// For now the AI cannot cope with bad prroperty values on anything but hunter or pillage units
if ( iPropertyValue < 0 )
{
iValue = 0;
break;
}
break;
case UNITAI_COUNTER:
iValue += (iCombatValue / 2);
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getUnitClassAttackModifier(iI) * AI_getUnitClassWeight((UnitClassTypes)iI)) / 7500);
iValue += ((iCombatValue * (GC.getUnitInfo(eUnit).getTargetUnitClass(iI) ? 50 : 0)) / 100);
}
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
// int iCombatModifier = GC.getUnitInfo(eUnit).getUnitCombatModifier(iI);
// iCombatModifier = (iCombatModifier < 40) ? iCombatModifier : (40 + (iCombatModifier - 40) / 2);
// iValue += ((iCombatValue * iCombatModifier) / 100);
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getUnitCombatModifier(iI) * AI_getUnitCombatWeight((UnitCombatTypes)iI)) / 10000);
iValue += ((iCombatValue * (GC.getUnitInfo(eUnit).getTargetUnitCombat(iI) ? 50 : 0)) / 100);
}
for (iI = 0; iI < GC.getNumUnitInfos(); iI++)
{
int eUnitClass = GC.getUnitInfo(eUnit).getUnitClassType();
if (NO_UNITCLASS != eUnitClass && GC.getUnitInfo((UnitTypes)iI).getDefenderUnitClass(eUnitClass))
{
iValue += (50 * iCombatValue) / 100;
}
int eUnitCombat = GC.getUnitInfo(eUnit).getUnitCombatType();
if (NO_UNITCOMBAT != eUnitCombat && GC.getUnitInfo((UnitTypes)iI).getDefenderUnitCombat(eUnitCombat))
{
iValue += (50 * iCombatValue) / 100;
}
}
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getMoves()) / 2);
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getWithdrawalProbability()) / 100);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/20/10 jdog5000 */
/* */
/* War strategy AI */
/************************************************************************************************/
//iValue += (GC.getUnitInfo(eUnit).getInterceptionProbability() * 2);
if( GC.getUnitInfo(eUnit).getInterceptionProbability() > 0 )
{
int iTempValue = GC.getUnitInfo(eUnit).getInterceptionProbability();
iTempValue *= (25 + std::min(175, GET_TEAM(getTeam()).AI_getRivalAirPower()));
iTempValue /= 100;
iValue += iTempValue;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
break;
case UNITAI_CITY_DEFENSE:
iValue += ((iCombatValue * 2) / 3);
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getCityDefenseModifier()) / 75);
// The '30' scaling is empirical based on what seems reasonable for crime fighting units
iValue += AI_unitPropertyValue(eUnit)/(ePropertyRequested != NO_PROPERTY ? 30 : 60);
// Combat modifiers matter for defensive units
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getUnitCombatModifier(iI) * AI_getUnitCombatWeight((UnitCombatTypes)iI)) / 12000);
}
// ls612: consider that a unit with OnlyDefensive is less useful
if (GC.getUnitInfo(eUnit).isOnlyDefensive())
{
iValue *= 4;
iValue /= 5;
}
// ls612: consider that a unit with ExtraCost is less useful
if ( GC.getUnitInfo(eUnit).getExtraCost() > 0 )
{
int iNetCommerce = 1 + getCommerceRate(COMMERCE_GOLD) + std::max(0, getGoldPerTurn());
int iNetExpenses = calculateInflatedCosts() + std::max(0, -getGoldPerTurn());
if (iNetCommerce > iNetExpenses + GC.getUnitInfo(eUnit).getExtraCost())
{
iValue = (iValue*(iNetCommerce - iNetExpenses - GC.getUnitInfo(eUnit).getExtraCost())) / (iNetCommerce - iNetExpenses);
}
else
{
iValue = 1; // Don't set the value to 0, just make this the least useful option
}
}
break;
case UNITAI_CITY_COUNTER:
case UNITAI_CITY_SPECIAL:
// For now the AI cannot cope with bad prroperty values on anything but hunter or pillage units
if ( iPropertyValue < 0 )
{
iValue = 0;
break;
}
// The '30' scaling is empirical based on what seems reasonable for crime fighting units
iValue += AI_unitPropertyValue(eUnit)/(ePropertyRequested != NO_PROPERTY ? 30 : 60);
// Drop through
case UNITAI_PARADROP:
// For now the AI cannot cope with bad property values on anything but hunter or pillage units
if ( iPropertyValue < 0 )
{
iValue = 0;
break;
}
iValue += (iCombatValue / 2);
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getCityDefenseModifier()) / 100);
iValue /= (GC.getUnitInfo(eUnit).isOnlyDefensive() ? 2 : 1);
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getUnitClassAttackModifier(iI) * AI_getUnitClassWeight((UnitClassTypes)iI)) / 10000);
iValue += ((iCombatValue * (GC.getUnitInfo(eUnit).getDefenderUnitClass(iI) ? 50 : 0)) / 100);
}
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getUnitCombatModifier(iI) * AI_getUnitCombatWeight((UnitCombatTypes)iI)) / 10000);
iValue += ((iCombatValue * (GC.getUnitInfo(eUnit).getDefenderUnitCombat(iI) ? 50 : 0)) / 100);
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/20/10 jdog5000 */
/* */
/* War strategy AI */
/************************************************************************************************/
//iValue += (GC.getUnitInfo(eUnit).getInterceptionProbability() * 3);
if( GC.getUnitInfo(eUnit).getInterceptionProbability() > 0 )
{
int iTempValue = GC.getUnitInfo(eUnit).getInterceptionProbability();
iTempValue *= (25 + std::min(125, GET_TEAM(getTeam()).AI_getRivalAirPower()));
iTempValue /= 50;
iValue += iTempValue;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
break;
case UNITAI_EXPLORE:
iValue += (iCombatValue*2/(GC.getUnitInfo(eUnit).isOnlyDefensive() ? 4 : 3));
iValue += (GC.getUnitInfo(eUnit).getMoves() * 200);
if (GC.getUnitInfo(eUnit).isNoBadGoodies())
{
iValue += 100;
}
break;
case UNITAI_HUNTER:
iValue += ((iCombatValue*2)/(GC.getUnitInfo(eUnit).isOnlyDefensive() ? 2 : 1));
iValue *= (100 + GC.getUnitInfo(eUnit).getMoves() * 30);
iValue *= (100 + GC.getUnitInfo(eUnit).getAnimalCombatModifier());
iValue /= 10000;
break;
case UNITAI_MISSIONARY:
iValue += (GC.getUnitInfo(eUnit).getMoves() * 100);
if (getStateReligion() != NO_RELIGION)
{
if (GC.getUnitInfo(eUnit).getReligionSpreads(getStateReligion()) > 0)
{
iValue += (5 * GC.getUnitInfo(eUnit).getReligionSpreads(getStateReligion())) / 2;
}
}
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (GC.getUnitInfo(eUnit).getReligionSpreads((ReligionTypes)iI) && hasHolyCity((ReligionTypes)iI))
{
iValue += 80;
break;
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/08/10 jdog5000 */
/* */
/* Victory Strategy AI */
/************************************************************************************************/
if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE2))
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
int iTempValue = 0;
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (GC.getUnitInfo(eUnit).getReligionSpreads((ReligionTypes)iI))
{
iTempValue += (50 * getNumCities()) / (1 + getHasReligionCount((ReligionTypes)iI));
}
}
iValue += iTempValue;
}
for (iI = 0; iI < GC.getNumCorporationInfos(); ++iI)
{
if (hasHeadquarters((CorporationTypes)iI))
{
if (GC.getUnitInfo(eUnit).getCorporationSpreads(iI) > 0)
{
iValue += (5 * GC.getUnitInfo(eUnit).getCorporationSpreads(iI)) / 2;
/************************************************************************************************/
/* UNOFFICIAL_PATCH 06/03/09 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
// Fix potential crash, probably would only happen in mods
if( pArea != NULL )
{
iValue += 300 / std::max(1, pArea->countHasCorporation((CorporationTypes)iI, getID()));
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
}
}
}
break;
case UNITAI_PROPHET:
case UNITAI_ARTIST:
case UNITAI_SCIENTIST:
case UNITAI_GENERAL:
case UNITAI_MERCHANT:
case UNITAI_ENGINEER:
/************************************************************************************************/
/* Great Diplomat MOD START */
/************************************************************************************************/
case UNITAI_DIPLOMAT:
/************************************************************************************************/
/* Great Diplomat MOD END */
/************************************************************************************************/
break;
case UNITAI_SPY:
iValue += (GC.getUnitInfo(eUnit).getMoves() * 100);
if (GC.getUnitInfo(eUnit).isSabotage())
{
iValue += 50;
}
if (GC.getUnitInfo(eUnit).isDestroy())
{
iValue += 50;
}
if (GC.getUnitInfo(eUnit).isCounterSpy())
{
iValue += 100;
}
break;
case UNITAI_ICBM:
if (GC.getUnitInfo(eUnit).getNukeRange() != -1)
{
iTempValue = 40 + (GC.getUnitInfo(eUnit).getNukeRange() * 40);
if (GC.getUnitInfo(eUnit).getAirRange() == 0)
{
iValue += iTempValue;
}
else
{
iValue += (iTempValue * std::min(10, GC.getUnitInfo(eUnit).getAirRange())) / 10;
}
iValue += (iTempValue * (60 + GC.getUnitInfo(eUnit).getEvasionProbability())) / 100;
}
break;
case UNITAI_WORKER_SEA:
for (iI = 0; iI < GC.getNumBuildInfos(); iI++)
{
if (GC.getUnitInfo(eUnit).getBuilds(iI))
{
iValue += 50;
}
}
iValue += (GC.getUnitInfo(eUnit).getMoves() * 100);
break;
case UNITAI_ATTACK_SEA:
iValue += iCombatValue;
iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getMoves()) / 2);
iValue += (GC.getUnitInfo(eUnit).getBombardRate() * 4);
break;
case UNITAI_RESERVE_SEA:
iValue += iCombatValue;
iValue += (iCombatValue * GC.getUnitInfo(eUnit).getMoves());
break;
case UNITAI_ESCORT_SEA:
iValue += iCombatValue;
iValue += (iCombatValue * GC.getUnitInfo(eUnit).getMoves());
iValue += (GC.getUnitInfo(eUnit).getInterceptionProbability() * 3);
if (GC.getUnitInfo(eUnit).getNumSeeInvisibleTypes() > 0)
{
iValue += 200;
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 06/03/09 jdog5000 */
/* */
/* General AI */
/************************************************************************************************/
// Boats which can't be seen don't play defense, don't make good escorts
if (GC.getUnitInfo(eUnit).getInvisibleType() != NO_INVISIBLE)
{
iValue /= 2;
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
break;
case UNITAI_EXPLORE_SEA:
{
int iExploreValue = 100;
if (pArea != NULL)
{
if (pArea->isWater())
{
if (pArea->getUnitsPerPlayer(BARBARIAN_PLAYER) > 0)
{
iExploreValue += (2 * iCombatValue);
}
}
}
iValue += (GC.getUnitInfo(eUnit).getMoves() * iExploreValue);
if (GC.getUnitInfo(eUnit).isAlwaysHostile())
{
iValue /= 2;
}
iValue /= (1 + AI_unitImpassableCount(eUnit));
}
break;
case UNITAI_ASSAULT_SEA:
case UNITAI_SETTLER_SEA:
case UNITAI_MISSIONARY_SEA:
case UNITAI_SPY_SEA:
iValue += (iCombatValue / 2);
iValue += (GC.getUnitInfo(eUnit).getMoves() * 200);
// Thomas SG - AC: Advanced Cargo START
{
iValue += (std::min(2,GC.getUnitInfo(eUnit).getTotalCargoSpace()) * 300);
}
// Thomas SG - AC: Advanced Cargo END
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 05/18/09 jdog5000 */
/* */
/* City AI */
/************************************************************************************************/
// Never build galley transports when ocean faring ones exist (issue mainly for Carracks)
iValue /= (1 + AI_unitImpassableCount(eUnit));
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
break;
case UNITAI_CARRIER_SEA:
iValue += iCombatValue;
iValue += (GC.getUnitInfo(eUnit).getMoves() * 50);
// Thomas SG - AC: Advanced Cargo START
{
iValue += (GC.getUnitInfo(eUnit).getTotalCargoSpace() * 400);
}
// Thomas SG - AC: Advanced Cargo END
break;
case UNITAI_MISSILE_CARRIER_SEA:
iValue += iCombatValue * GC.getUnitInfo(eUnit).getMoves();
// Thomas SG - AC: Advanced Cargo START
{
iValue += (25 + iCombatValue) * (3 + (GC.getUnitInfo(eUnit).getTotalSpecialCargoSpace()));
}
// Thomas SG - AC: Advanced Cargo END
break;
case UNITAI_PIRATE_SEA:
iValue += iCombatValue;
iValue += (iCombatValue * GC.getUnitInfo(eUnit).getMoves());
break;
case UNITAI_ATTACK_AIR:
iValue += iCombatValue;
iValue += (GC.getUnitInfo(eUnit).getCollateralDamage() * iCombatValue) / 100;
iValue += 4 * GC.getUnitInfo(eUnit).getBombRate();
iValue += (iCombatValue * (100 + 2 * GC.getUnitInfo(eUnit).getCollateralDamage()) * GC.getUnitInfo(eUnit).getAirRange()) / 100;
break;
case UNITAI_DEFENSE_AIR:
iValue += iCombatValue;
iValue += (GC.getUnitInfo(eUnit).getInterceptionProbability() * 3);
iValue += (GC.getUnitInfo(eUnit).getAirRange() * iCombatValue);
break;
case UNITAI_CARRIER_AIR:
iValue += (iCombatValue);
iValue += (GC.getUnitInfo(eUnit).getInterceptionProbability() * 2);
iValue += (GC.getUnitInfo(eUnit).getAirRange() * iCombatValue);
break;
case UNITAI_MISSILE_AIR:
iValue += iCombatValue;
iValue += 4 * GC.getUnitInfo(eUnit).getBombRate();
iValue += GC.getUnitInfo(eUnit).getAirRange() * iCombatValue;
break;
case UNITAI_ATTACK_CITY_LEMMING:
iValue += iCombatValue;
break;
default:
FAssert(false);
break;
}
}
if ((iCombatValue > 0) && ((eUnitAI == UNITAI_ATTACK) || (eUnitAI == UNITAI_ATTACK_CITY)))
{
if (pArea != NULL)
{
AreaAITypes eAreaAI = pArea->getAreaAIType(getTeam());
if (eAreaAI == AREAAI_ASSAULT || eAreaAI == AREAAI_ASSAULT_MASSING)
{
for (int iI = 0; iI < GC.getNumPromotionInfos(); iI++)
{
if (GC.getUnitInfo(eUnit).getFreePromotions(iI))
{
if (GC.getPromotionInfo((PromotionTypes)iI).isAmphib())
{
iValue *= 133;
iValue /= 100;
break;
}
}
}
}
}
}
return std::max(0, iValue);
}
int CvPlayerAI::AI_totalUnitAIs(UnitAITypes eUnitAI) const
{
return (AI_getNumTrainAIUnits(eUnitAI) + AI_getNumAIUnits(eUnitAI));
}
int CvPlayerAI::AI_totalAreaUnitAIs(CvArea* pArea, UnitAITypes eUnitAI) const
{
return (pArea->getNumTrainAIUnits(getID(), eUnitAI) + pArea->getNumAIUnits(getID(), eUnitAI));
}
int CvPlayerAI::AI_totalWaterAreaUnitAIs(CvArea* pArea, UnitAITypes eUnitAI) const
{
CvCity* pLoopCity;
int iCount;
int iLoop;
int iI;
iCount = AI_totalAreaUnitAIs(pArea, eUnitAI);
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
for (pLoopCity = GET_PLAYER((PlayerTypes)iI).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iI).nextCity(&iLoop))
{
if (pLoopCity->waterArea() == pArea)
{
iCount += pLoopCity->plot()->plotCount(PUF_isUnitAIType, eUnitAI, -1, getID());
if (pLoopCity->getOwnerINLINE() == getID())
{
iCount += pLoopCity->getNumTrainUnitAI(eUnitAI);
}
}
}
}
}
return iCount;
}
int CvPlayerAI::AI_countCargoSpace(UnitAITypes eUnitAI) const
{
CvUnit* pLoopUnit;
int iCount;
int iLoop;
iCount = 0;
// Thomas SG - AC: Advanced Cargo START
{
for(pLoopUnit = firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = nextUnit(&iLoop))
{
if (pLoopUnit->AI_getUnitAIType() == eUnitAI)
{
iCount += pLoopUnit->totalCargoSpace();
}
}
}
// Thomas SG - AC: Advanced Cargo END
return iCount;
}
int CvPlayerAI::AI_neededExplorers(CvArea* pArea, bool bIdeal) const
{
FAssert(pArea != NULL);
int iNeeded = 0;
if (pArea->isWater())
{
iNeeded = std::min(iNeeded + (pArea->getNumUnrevealedTiles(getTeam()) / 400), bIdeal ? 5 : std::max(2, ((getNumCities() / 2) + 1)));
}
else
{
// Koshling - modified explorer AI to keep 'exploring' already revealed neutral territory as lower priority than
// revealing new tiles so as to keep up neutral area patrols for animal hunting and general intelligence gathering
// Note - we cap the number of explorers we dieally need at 5, so returns diminish fast
iNeeded = std::min((pArea->getNumUnownedTiles() + 4*pArea->getNumUnrevealedTiles(getTeam())) / 500, bIdeal ? 5 : std::max(3, ((getNumCities() / 3) + 2)));
}
if (0 == iNeeded)
{
if ((GC.getGameINLINE().countCivTeamsAlive() - 1) > GET_TEAM(getTeam()).getHasMetCivCount(true))
{
if (pArea->isWater())
{
if (GC.getMap().findBiggestArea(true) == pArea)
{
iNeeded++;
}
}
else
{
if (getCapitalCity() != NULL && pArea->getID() == getCapitalCity()->getArea())
{
for (int iPlayer = 0; iPlayer < MAX_CIV_PLAYERS; iPlayer++)
{
CvPlayerAI& kPlayer = GET_PLAYER((PlayerTypes)iPlayer);
if (kPlayer.isAlive() && kPlayer.getTeam() != getTeam())
{
if (!GET_TEAM(getTeam()).isHasMet(kPlayer.getTeam()))
{
if (pArea->getCitiesPerPlayer(kPlayer.getID()) > 0)
{
iNeeded++;
break;
}
}
}
}
}
}
}
}
return iNeeded;
}
int CvPlayerAI::AI_neededHunters(CvArea* pArea, bool bIdeal) const
{
FAssert(pArea != NULL);
int iNeeded = 0;
if (pArea->isWater())
{
iNeeded = 0; // Hunter AI currently only operates on land
}
else
{
// Note - we do not cap the number of ideally needed hunters at all, but we
// only consider (an appoximation of) the part of the landmass that is revealed to us
int iCityCountCap = std::max(3, (getNumCities() / 3) + 2);
iNeeded = std::min((pArea->getNumUnownedTiles()+150)/200, bIdeal ? MAX_INT : iCityCountCap);
if ( bIdeal )
{
if ( iNeeded > iCityCountCap )
{
// Normalise for percentage revealed
iNeeded *= pArea->getNumRevealedTiles(getTeam());
iNeeded /= pArea->getNumTiles();
if ( iNeeded < iCityCountCap )
{
iNeeded = iCityCountCap;
}
}
}
}
return iNeeded;
}
int CvPlayerAI::AI_neededWorkers(CvArea* pArea) const
{
CvCity* pLoopCity;
int iCount;
int iLoop;
iCount = countUnimprovedBonuses(pArea) * 2;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (pLoopCity->getArea() == pArea->getID())
{
iCount += pLoopCity->AI_getWorkersNeeded() * 3;
}
}
if (iCount == 0)
{
return 0;
}
if (getBestRoute() != NO_ROUTE)
{
iCount += pArea->getCitiesPerPlayer(getID()) / 2;
}
iCount += 1;
iCount /= 3;
iCount = std::min(iCount, 3 * pArea->getCitiesPerPlayer(getID()));
iCount = std::min(iCount, (1 + getTotalPopulation()) / 2);
return std::max(1, iCount);
}
int CvPlayerAI::AI_neededMissionaries(CvArea* pArea, ReligionTypes eReligion) const
{
PROFILE_FUNC();
int iCount;
bool bHoly, bState, bHolyState;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/08/10 jdog5000 */
/* */
/* Victory Strategy AI */
/************************************************************************************************/
bool bCultureVictory = AI_isDoVictoryStrategy(AI_VICTORY_CULTURE2);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
bHoly = hasHolyCity(eReligion);
bState = (getStateReligion() == eReligion);
bHolyState = ((getStateReligion() != NO_RELIGION) && hasHolyCity(getStateReligion()));
/************************************************************************************************/
/* RevDCM Start 5/1/09 */
/* */
/* Inquisitions */
/************************************************************************************************/
int iInternalCount = 0;
int iExternalCount = 0;
iCount = 0;
bool bReligiousVictory = false;
if(isPushReligiousVictory() || isConsiderReligiousVictory())
{
bReligiousVictory = true;
}
//internal spread.
if ( (bCultureVictory || bState || bHoly)
&& !(!bState && bReligiousVictory) )
{
iInternalCount = std::max(iInternalCount, (pArea->getCitiesPerPlayer(getID()) - pArea->countHasReligion(eReligion, getID())));
if (iInternalCount > 0)
{
if (!bCultureVictory && !bReligiousVictory)
{
iInternalCount = std::max(1, iInternalCount / (bHoly ? 2 : 4));
}
}
}
//external spread.
if ( ((bHoly && bState) || (bHoly && !bHolyState && (getStateReligion() != NO_RELIGION)))
&& !(!bState && bReligiousVictory) )
{
if(bState && bReligiousVictory)
{
iExternalCount += (pArea->getNumCities() - pArea->countHasReligion(eReligion));
} else
{
iExternalCount += ((pArea->getNumCities() * 2) - (pArea->countHasReligion(eReligion) * 3));
}
if(bState && bReligiousVictory)
{
if(isConsiderReligiousVictory())
{
iExternalCount /= 3;
}
} else
{
iExternalCount /= 8;
}
iExternalCount = std::max(0, iExternalCount);
if (AI_isPrimaryArea(pArea))
{
iExternalCount++;
}
}
iCount = iExternalCount + iInternalCount;
/************************************************************************************************/
/* Inquisitions END */
/************************************************************************************************/
/************************************************************************************************/
/* Afforess Start 6/19/11 */
/* */
/* Count existing missionaries */
/************************************************************************************************/
for (int iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
if (GC.getGame().canEverTrain((UnitTypes)GC.getUnitClassInfo((UnitClassTypes)iI).getDefaultUnitIndex()))
{
if (GC.getUnitInfo((UnitTypes)GC.getUnitClassInfo((UnitClassTypes)iI).getDefaultUnitIndex()).getDefaultUnitAIType() == UNITAI_MISSIONARY &&
GC.getUnitInfo((UnitTypes)GC.getUnitClassInfo((UnitClassTypes)iI).getDefaultUnitIndex()).getAdvisorType() == 1 /*Advisor Religion*/)
{
iCount -= getUnitClassCountPlusMaking((UnitClassTypes)iI);
}
}
}
iCount = std::max(0, iCount);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iCount;
}
int CvPlayerAI::AI_neededExecutives(CvArea* pArea, CorporationTypes eCorporation) const
{
if (!hasHeadquarters(eCorporation))
{
return 0;
}
int iCount = ((pArea->getCitiesPerPlayer(getID()) - pArea->countHasCorporation(eCorporation, getID())) * 2);
iCount += (pArea->getNumCities() - pArea->countHasCorporation(eCorporation));
iCount /= 3;
if (AI_isPrimaryArea(pArea))
{
++iCount;
}
return iCount;
}
int CvPlayerAI::AI_adjacentPotentialAttackers(CvPlot* pPlot, bool bTestCanMove) const
{
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
CvPlot* pLoopPlot;
int iCount;
int iI;
iCount = 0;
for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
pLoopPlot = plotDirection(pPlot->getX_INLINE(), pPlot->getY_INLINE(), ((DirectionTypes)iI));
if (pLoopPlot != NULL)
{
if (pLoopPlot->area() == pPlot->area())
{
pUnitNode = pLoopPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getOwnerINLINE() == getID())
{
if (pLoopUnit->getDomainType() == ((pPlot->isWater()) ? DOMAIN_SEA : DOMAIN_LAND))
{
if (pLoopUnit->canAttack())
{
if (!bTestCanMove || pLoopUnit->canMove())
{
if (!(pLoopUnit->AI_isCityAIType()))
{
iCount++;
}
}
}
}
}
}
}
}
}
return iCount;
}
int CvPlayerAI::AI_totalMissionAIs(MissionAITypes eMissionAI, CvSelectionGroup* pSkipSelectionGroup) const
{
PROFILE_FUNC();
CvSelectionGroup* pLoopSelectionGroup;
int iCount;
int iLoop;
iCount = 0;
for(pLoopSelectionGroup = firstSelectionGroup(&iLoop); pLoopSelectionGroup; pLoopSelectionGroup = nextSelectionGroup(&iLoop))
{
if (pLoopSelectionGroup != pSkipSelectionGroup)
{
if (pLoopSelectionGroup->AI_getMissionAIType() == eMissionAI)
{
iCount += pLoopSelectionGroup->getNumUnits();
}
}
}
return iCount;
}
int CvPlayerAI::AI_missionaryValue(CvArea* pArea, ReligionTypes eReligion, PlayerTypes* peBestPlayer) const
{
CvTeam& kTeam = GET_TEAM(getTeam());
CvGame& kGame = GC.getGame();
int iSpreadInternalValue = 100;
int iSpreadExternalValue = 0;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/08/10 jdog5000 */
/* */
/* Victory Strategy AI */
/************************************************************************************************/
// Obvious copy & paste bug
if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE1))
{
iSpreadInternalValue += 500;
if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE2))
{
iSpreadInternalValue += 1500;
if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE3))
{
iSpreadInternalValue += 3000;
}
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
/************************************************************************************************/
/* RevDCM Start 5/1/09 */
/* */
/* Inquisitions */
/************************************************************************************************/
bool bStateReligion = (getStateReligion() == eReligion);
bool bReligiousVictory = false;
if(isPushReligiousVictory() || isConsiderReligiousVictory())
{
bReligiousVictory = true;
}
if(!bStateReligion && bReligiousVictory)
{
return 0;
}
if (bStateReligion && bReligiousVictory)
{
if (isPushReligiousVictory())
{
iSpreadInternalValue += 2500;
} else
{
iSpreadInternalValue += 700;
}
}
/************************************************************************************************/
/* Inquisitions END */
/************************************************************************************************/
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 10/03/09 jdog5000 */
/* */
/* Missionary AI */
/************************************************************************************************/
// In free religion, treat all religions like state religions
if (!isStateReligion())
{
// Free religion
iSpreadInternalValue += 500;
bStateReligion = true;
}
else if(bStateReligion)
{
iSpreadInternalValue += 1000;
}
else
{
iSpreadInternalValue += (500 * getHasReligionCount(eReligion)) / std::max(1, getNumCities());
}
int iGoldValue = 0;
if (kTeam.hasHolyCity(eReligion))
{
iSpreadInternalValue += bStateReligion ? 1000 : 300;
iSpreadExternalValue += bStateReligion ? 1000 : 150;
if (kTeam.hasShrine(eReligion))
{
iSpreadInternalValue += bStateReligion ? 500 : 300;
iSpreadExternalValue += bStateReligion ? 300 : 200;
int iGoldMultiplier = kGame.getHolyCity(eReligion)->getTotalCommerceRateModifier(COMMERCE_GOLD);
iGoldValue = 6 * iGoldMultiplier;
}
}
int iOurCitiesHave = 0;
int iOurCitiesCount = 0;
if (NULL == pArea)
{
iOurCitiesHave = kTeam.getHasReligionCount(eReligion);
iOurCitiesCount = kTeam.getNumCities();
}
else
{
iOurCitiesHave = pArea->countHasReligion(eReligion, getID()) + countReligionSpreadUnits(pArea, eReligion,true);
iOurCitiesCount = pArea->getCitiesPerPlayer(getID());
}
if (iOurCitiesHave < iOurCitiesCount)
{
iSpreadInternalValue *= 30 + ((100 * (iOurCitiesCount - iOurCitiesHave))/ iOurCitiesCount);
iSpreadInternalValue /= 100;
iSpreadInternalValue += iGoldValue;
}
else
{
iSpreadInternalValue = 0;
}
if (iSpreadExternalValue > 0)
{
int iBestPlayer = NO_PLAYER;
int iBestValue = 0;
for (int iPlayer = 0; iPlayer < MAX_PLAYERS; iPlayer++)
{
if (iPlayer != getID())
{
CvPlayer& kLoopPlayer = GET_PLAYER((PlayerTypes)iPlayer);
if (kLoopPlayer.isAlive() && kLoopPlayer.getTeam() != getTeam() && kLoopPlayer.getNumCities() > 0)
{
/************************************************************************************************/
/* Afforess Start 12/9/09 */
/* */
/* */
/************************************************************************************************/
if (GET_TEAM(kLoopPlayer.getTeam()).isOpenBorders(getTeam())|| GET_TEAM(kLoopPlayer.getTeam()).isLimitedBorders(getTeam()))
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
int iCitiesCount = 0;
int iCitiesHave = 0;
int iMultiplier = AI_isDoStrategy(AI_STRATEGY_MISSIONARY) ? 60 : 25;
if (!kLoopPlayer.isNoNonStateReligionSpread() || (kLoopPlayer.getStateReligion() == eReligion))
{
if (NULL == pArea)
{
iCitiesCount += 1 + (kLoopPlayer.getNumCities() * 75) / 100;
iCitiesHave += std::min(iCitiesCount, kLoopPlayer.getHasReligionCount(eReligion));
}
else
{
int iPlayerSpreadPercent = (100 * kLoopPlayer.getHasReligionCount(eReligion)) / kLoopPlayer.getNumCities();
iCitiesCount += pArea->getCitiesPerPlayer((PlayerTypes)iPlayer);
iCitiesHave += std::min(iCitiesCount, (iCitiesCount * iPlayerSpreadPercent) / 75);
}
}
if (kLoopPlayer.getStateReligion() == NO_RELIGION)
{
// Paganism counts as a state religion civic, that's what's caught below
if (kLoopPlayer.getStateReligionCount() > 0)
{
int iTotalReligions = kLoopPlayer.countTotalHasReligion();
iMultiplier += 100 * std::max(0, kLoopPlayer.getNumCities() - iTotalReligions);
iMultiplier += (iTotalReligions == 0) ? 100 : 0;
}
}
int iValue = (iMultiplier * iSpreadExternalValue * (iCitiesCount - iCitiesHave)) / std::max(1, iCitiesCount);
iValue /= 100;
iValue += iGoldValue;
if (iValue > iBestValue)
{
iBestValue = iValue;
iBestPlayer = iPlayer;
}
}
}
}
}
if (iBestValue > iSpreadInternalValue)
{
if (NULL != peBestPlayer)
{
*peBestPlayer = (PlayerTypes)iBestPlayer;
}
return iBestValue;
}
}
if (NULL != peBestPlayer)
{
*peBestPlayer = getID();
}
return iSpreadInternalValue;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
int CvPlayerAI::AI_executiveValue(CvArea* pArea, CorporationTypes eCorporation, PlayerTypes* peBestPlayer) const
{
CvTeam& kTeam = GET_TEAM(getTeam());
CvGame& kGame = GC.getGame();
CvCorporationInfo& kCorp = GC.getCorporationInfo(eCorporation);
int iSpreadInternalValue = 100;
int iSpreadExternalValue = 0;
if (kTeam.hasHeadquarters(eCorporation))
{
int iGoldMultiplier = kGame.getHeadquarters(eCorporation)->getTotalCommerceRateModifier(COMMERCE_GOLD);
iSpreadInternalValue += 10 * std::max(0, (iGoldMultiplier - 100));
iSpreadExternalValue += 15 * std::max(0, (iGoldMultiplier - 150));
}
int iOurCitiesHave = 0;
int iOurCitiesCount = 0;
if (NULL == pArea)
{
iOurCitiesHave = kTeam.getHasCorporationCount(eCorporation);
iOurCitiesCount = kTeam.getNumCities();
}
else
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 11/14/09 jdog5000 */
/* */
/* City AI */
/************************************************************************************************/
iOurCitiesHave = pArea->countHasCorporation(eCorporation, getID()) + countCorporationSpreadUnits(pArea,eCorporation,true);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
iOurCitiesCount = pArea->getCitiesPerPlayer(getID());
}
for (int iCorp = 0; iCorp < GC.getNumCorporationInfos(); iCorp++)
{
if (kGame.isCompetingCorporation(eCorporation, (CorporationTypes)iCorp))
{
if (NULL == pArea)
{
iOurCitiesHave += kTeam.getHasCorporationCount(eCorporation);
}
else
{
iOurCitiesHave += pArea->countHasCorporation(eCorporation, getID());
}
}
}
if (iOurCitiesHave >= iOurCitiesCount)
{
iSpreadInternalValue = 0;
/************************************************************************************************/
/* UNOFFICIAL_PATCH 06/23/10 denev */
/* */
/* Bugfix */
/************************************************************************************************/
/* original bts code
if (iSpreadExternalValue = 0)
*/
if (iSpreadExternalValue == 0)
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
{
return 0;
}
}
int iBonusValue = 0;
CvCity* pCity = getCapitalCity();
if (pCity != NULL)
{
iBonusValue = AI_corporationValue(eCorporation, pCity);
iBonusValue /= 25;
}
for (int iPlayer = 0; iPlayer < MAX_PLAYERS; iPlayer++)
{
CvPlayer& kLoopPlayer = GET_PLAYER((PlayerTypes)iPlayer);
if (kLoopPlayer.isAlive() && (kLoopPlayer.getNumCities() > 0))
{
if ((kLoopPlayer.getTeam() == getTeam()) || GET_TEAM(kLoopPlayer.getTeam()).isVassal(getTeam()))
{
if (kLoopPlayer.getHasCorporationCount(eCorporation) == 0)
{
iBonusValue += 1000;
}
}
}
}
if (iBonusValue == 0)
{
return 0;
}
iSpreadInternalValue += iBonusValue;
if (iSpreadExternalValue > 0)
{
int iBestPlayer = NO_PLAYER;
int iBestValue = 0;
for (int iPlayer = 0; iPlayer < MAX_PLAYERS; iPlayer++)
{
if (iPlayer != getID())
{
CvPlayer& kLoopPlayer = GET_PLAYER((PlayerTypes)iPlayer);
if (kLoopPlayer.isAlive() && (kLoopPlayer.getTeam() != getTeam()) && (kLoopPlayer.getNumCities() > 0))
{
/************************************************************************************************/
/* Afforess Start 12/9/09 */
/* */
/* */
/************************************************************************************************/
if (GET_TEAM(kLoopPlayer.getTeam()).isOpenBorders(getTeam()) || GET_TEAM(kLoopPlayer.getTeam()).isLimitedBorders(getTeam()))
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
if (!kLoopPlayer.isNoCorporations() && !kLoopPlayer.isNoForeignCorporations())
{
int iCitiesCount = 0;
int iCitiesHave = 0;
int iMultiplier = AI_getAttitudeWeight((PlayerTypes)iPlayer);
if (NULL == pArea)
{
iCitiesCount += 1 + (kLoopPlayer.getNumCities() * 50) / 100;
iCitiesHave += std::min(iCitiesCount, kLoopPlayer.getHasCorporationCount(eCorporation));
}
else
{
int iPlayerSpreadPercent = (100 * kLoopPlayer.getHasCorporationCount(eCorporation)) / kLoopPlayer.getNumCities();
iCitiesCount += pArea->getCitiesPerPlayer((PlayerTypes)iPlayer);
iCitiesHave += std::min(iCitiesCount, (iCitiesCount * iPlayerSpreadPercent) / 50);
}
if (iCitiesHave < iCitiesCount)
{
int iValue = (iMultiplier * iSpreadExternalValue);
iValue += ((iMultiplier - 55) * iBonusValue) / 4;
iValue /= 100;
if (iValue > iBestValue)
{
iBestValue = iValue;
iBestPlayer = iPlayer;
}
}
}
}
}
}
}
if (iBestValue > iSpreadInternalValue)
{
if (NULL != peBestPlayer)
{
*peBestPlayer = (PlayerTypes)iBestPlayer;
}
return iBestValue;
}
}
if (NULL != peBestPlayer)
{
*peBestPlayer = getID();
}
return iSpreadInternalValue;
}
//Returns approximately 100 x gpt value of the corporation.
int CvPlayerAI::AI_corporationValue(CorporationTypes eCorporation, CvCity* pCity) const
{
MEMORY_TRACK()
if (pCity == NULL)
{
if (getCapitalCity() != NULL)
{
pCity = getCapitalCity();
}
}
if (NULL == pCity)
{
return 0;
}
CvCorporationInfo& kCorp = GC.getCorporationInfo(eCorporation);
int iBonusValue = 0;
for (int iBonus = 0; iBonus < GC.getNumBonusInfos(); iBonus++)
{
BonusTypes eBonus = (BonusTypes)iBonus;
int iBonusCount = pCity->getNumBonuses(eBonus);
if (iBonusCount > 0)
{
for (int i = 0; i < GC.getNUM_CORPORATION_PREREQ_BONUSES(); ++i)
{
if (eBonus == kCorp.getPrereqBonus(i))
{
// These are all in hundredths, so the multipliers here accoutn for the division
// by 100 at the very end and are 100 times smaller than the multipliers for the
// absolute commerces/yields later
// Production is considerd wiorth 4 X gold, food 3 X
iBonusValue += (300 * kCorp.getYieldProduced(YIELD_FOOD) * iBonusCount);
iBonusValue += (400 * kCorp.getYieldProduced(YIELD_PRODUCTION) * iBonusCount);
iBonusValue += (100 * kCorp.getYieldProduced(YIELD_COMMERCE) * iBonusCount);
iBonusValue += (100 * kCorp.getCommerceProduced(COMMERCE_GOLD) * iBonusCount);
iBonusValue += (100 * kCorp.getCommerceProduced(COMMERCE_RESEARCH) * iBonusCount);
iBonusValue += (50 * kCorp.getCommerceProduced(COMMERCE_CULTURE) * iBonusCount);
iBonusValue += (40 * kCorp.getCommerceProduced(COMMERCE_ESPIONAGE) * iBonusCount);
if (NO_BONUS != kCorp.getBonusProduced())
{
int iBonuses = getNumAvailableBonuses((BonusTypes)kCorp.getBonusProduced());
iBonusValue += (AI_baseBonusVal((BonusTypes)kCorp.getBonusProduced()) * 1000) / (1 + 3 * iBonuses * iBonuses);
}
}
}
}
}
iBonusValue *= 3;
/************************************************************************************************/
/* Afforess Start 02/09/10 */
/* */
/* */
/************************************************************************************************/
//TODO: Move this to CityAI?
iBonusValue += kCorp.getHealth() * 15000;
iBonusValue += kCorp.getHappiness() * 25000;
iBonusValue += kCorp.getMilitaryProductionModifier() * 3500;
iBonusValue += kCorp.getFreeXP() * 15000;
//these are whole numbers, not like the percents above.
iBonusValue += (30000 * kCorp.getYieldChange(YIELD_FOOD));
iBonusValue += (40000 * kCorp.getYieldChange(YIELD_PRODUCTION));
iBonusValue += (10000 * kCorp.getYieldChange(YIELD_COMMERCE));
iBonusValue += (10000 * kCorp.getCommerceChange(COMMERCE_GOLD));
iBonusValue += (10000 * kCorp.getCommerceChange(COMMERCE_RESEARCH));
iBonusValue += (5000 * kCorp.getCommerceChange(COMMERCE_CULTURE));
iBonusValue += (4000 * kCorp.getCommerceChange(COMMERCE_ESPIONAGE));
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
// Koshling - this result was 2-orders of magnitude out (relative to what the comment at
// the top of the routine claims). The net result was that pretty much all corporation
// headquarters were in a totally different league from oher Wonders and actually triggered
// an integer overflow assertion failure in building assessment (for the headquarters)!
// Dividing by 100 to bring it back into line with what the header comment claims
return iBonusValue/100;
}
int CvPlayerAI::AI_areaMissionAIs(CvArea* pArea, MissionAITypes eMissionAI, CvSelectionGroup* pSkipSelectionGroup) const
{
PROFILE_FUNC();
CvSelectionGroup* pLoopSelectionGroup;
CvPlot* pMissionPlot;
int iCount;
int iLoop;
iCount = 0;
for(pLoopSelectionGroup = firstSelectionGroup(&iLoop); pLoopSelectionGroup; pLoopSelectionGroup = nextSelectionGroup(&iLoop))
{
if (pLoopSelectionGroup != pSkipSelectionGroup)
{
if (pLoopSelectionGroup->AI_getMissionAIType() == eMissionAI)
{
pMissionPlot = pLoopSelectionGroup->AI_getMissionAIPlot();
if (pMissionPlot != NULL)
{
if (pMissionPlot->area() == pArea)
{
iCount += pLoopSelectionGroup->getNumUnits();
}
}
}
}
}
return iCount;
}
int CvPlayerAI::AI_plotTargetMissionAIsInternal(CvPlot* pPlot, MissionAITypes eMissionAI, int iRange, int* piClosest, CvSelectionGroup* pSkipSelectionGroup) const
{
PROFILE_FUNC();
int iCount = 0;
if ( piClosest != NULL )
{
*piClosest = MAX_INT;
}
int iLoop;
for(CvSelectionGroup* pLoopSelectionGroup = firstSelectionGroup(&iLoop); pLoopSelectionGroup; pLoopSelectionGroup = nextSelectionGroup(&iLoop))
{
MissionAITypes eGroupMissionAI = pLoopSelectionGroup->AI_getMissionAIType();
//Afforess: start pSkipSelectionGroup
if (pLoopSelectionGroup == pSkipSelectionGroup)
{
continue;
}
if ( eMissionAI == NO_MISSIONAI || eGroupMissionAI == eMissionAI )
{
CvPlot* pMissionPlot = pLoopSelectionGroup->AI_getMissionAIPlot();
if (pMissionPlot != NULL)
{
int iDistance = stepDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pMissionPlot->getX_INLINE(), pMissionPlot->getY_INLINE());
if (iDistance <= iRange)
{
iCount += pLoopSelectionGroup->getNumUnits();
if ( piClosest != NULL )
{
int iGroupDistance = stepDistance(pLoopSelectionGroup->getX(), pLoopSelectionGroup->getY(), pMissionPlot->getX_INLINE(), pMissionPlot->getY_INLINE());
if ( iGroupDistance < *piClosest )
{
*piClosest = iGroupDistance;
}
}
}
}
}
}
return iCount;
}
int CvPlayerAI::AI_plotTargetMissionAIs(CvPlot* pPlot, MissionAITypes eMissionAI, CvSelectionGroup* pSkipSelectionGroup, int iRange, int* piClosest) const
{
PROFILE_FUNC();
int iCount = 0;
if ( piClosest != NULL )
{
*piClosest = MAX_INT;
}
// Only cache 0-range, specific mission AI results
std::map<MissionAITypes,boost::shared_ptr<std::map<CvPlot*,MissionTargetInfo> > >::const_iterator itr = ((iRange == 0 && eMissionAI != NO_MISSIONAI) ? m_missionTargetCache.find(eMissionAI) : m_missionTargetCache.end());
if ( itr == m_missionTargetCache.end() )
{
if ( iRange == 0 && eMissionAI != NO_MISSIONAI )
{
std::map<CvPlot*,MissionTargetInfo>* pMap = new std::map<CvPlot*,MissionTargetInfo>();
if ( eMissionAI == MISSIONAI_SPREAD )
{
OutputDebugString(CvString::format("Build missionAI target cache for player %d\n", getID()).c_str());
}
// Since we have to walk all the groups now anyway populate the full cache map for this missionAI
int iLoop;
for(CvSelectionGroup* pLoopSelectionGroup = firstSelectionGroup(&iLoop); pLoopSelectionGroup; pLoopSelectionGroup = nextSelectionGroup(&iLoop))
{
MissionAITypes eGroupMissionAI = pLoopSelectionGroup->AI_getMissionAIType();
if ( eGroupMissionAI == eMissionAI )
{
CvPlot* pMissionPlot = pLoopSelectionGroup->AI_getMissionAIPlot();
if (pMissionPlot != NULL)
{
int iDistance = stepDistance(pLoopSelectionGroup->getX(), pLoopSelectionGroup->getY(), pMissionPlot->getX_INLINE(), pMissionPlot->getY_INLINE());
std::map<CvPlot*,MissionTargetInfo>::iterator itr2 = pMap->find(pMissionPlot);
if ( eMissionAI == MISSIONAI_SPREAD )
{
OutputDebugString(CvString::format("(%d,%d), group %d (%d)\n", pMissionPlot->getX_INLINE(), pMissionPlot->getY_INLINE(), pLoopSelectionGroup->getID(), pLoopSelectionGroup->getNumUnits()).c_str());
}
if ( itr2 != pMap->end() )
{
itr2->second.iCount += pLoopSelectionGroup->getNumUnits();
if ( iDistance < itr2->second.iClosest )
{
itr2->second.iClosest = iDistance;
}
}
else
{
MissionTargetInfo info;
info.iClosest = iDistance;
info.iCount = pLoopSelectionGroup->getNumUnits();
pMap->insert(std::make_pair(pMissionPlot, info));
}
if ( pMissionPlot == pPlot )
{
iCount += pLoopSelectionGroup->getNumUnits();
if ( piClosest != NULL && iDistance < *piClosest )
{
*piClosest = iDistance;
}
}
}
}
}
m_missionTargetCache.insert(std::make_pair(eMissionAI, boost::shared_ptr<std::map<CvPlot*,MissionTargetInfo> >(pMap)));
}
else
{
iCount = AI_plotTargetMissionAIsInternal(pPlot, eMissionAI, iRange, piClosest);
}
}
else
{
if ( eMissionAI == MISSIONAI_SPREAD )
{
OutputDebugString(CvString::format("Query missionAI target cache for player %d (%d,%d)\n", getID(),pPlot->getX_INLINE(), pPlot->getY_INLINE()).c_str());
}
std::map<CvPlot*,MissionTargetInfo>::const_iterator itr2 = itr->second.get()->find(pPlot);
if ( itr2 != itr->second.get()->end() )
{
iCount = itr2->second.iCount;
if ( piClosest != NULL )
{
*piClosest = itr2->second.iClosest;
}
if ( eMissionAI == MISSIONAI_SPREAD )
{
OutputDebugString(CvString::format("Found count of %d\n", iCount).c_str());
}
}
else
{
iCount = 0;
if ( eMissionAI == MISSIONAI_SPREAD )
{
OutputDebugString(CvString::format("No entry so return 0\n").c_str());
}
}
}
if ( pSkipSelectionGroup != NULL &&
(pSkipSelectionGroup->AI_getMissionAIType() == eMissionAI || eMissionAI == NO_MISSIONAI) &&
pPlot == pSkipSelectionGroup->AI_getMissionAIPlot())
{
iCount -= pSkipSelectionGroup->getNumUnits();
if ( eMissionAI == MISSIONAI_SPREAD )
{
OutputDebugString(CvString::format("Skip group matches plot - final count %d\n", iCount).c_str());
}
}
FAssert(iCount >= 0);
return iCount;
}
void CvPlayerAI::AI_noteMissionAITargetCountChange(MissionAITypes eMissionAI, CvPlot* pPlot, int iChange, CvPlot* pUnitPlot)
{
std::map<MissionAITypes,boost::shared_ptr<std::map<CvPlot*,MissionTargetInfo> > >::const_iterator itr = m_missionTargetCache.find(eMissionAI);
if ( eMissionAI == MISSIONAI_SPREAD )
{
OutputDebugString(CvString::format("Chnage plot target count (%d,%d), change=%d\n", pPlot->getX_INLINE(), pPlot->getY_INLINE(),iChange).c_str());
}
if ( itr != m_missionTargetCache.end() )
{
std::map<CvPlot*,MissionTargetInfo>::iterator itr2 = itr->second.get()->find(pPlot);
int iDistance = (pUnitPlot == NULL ? MAX_INT : stepDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pUnitPlot->getX_INLINE(), pUnitPlot->getY_INLINE()));
if ( itr2 != itr->second.get()->end() )
{
itr2->second.iCount += iChange;
if ( itr2->second.iCount == 0 )
{
itr2->second.iClosest = MAX_INT;
}
else if ( iDistance < itr2->second.iClosest )
{
itr2->second.iClosest = iDistance;
}
}
else
{
FAssert(iChange > 0);
MissionTargetInfo info;
info.iCount = iChange;
info.iClosest = iDistance;
itr->second.get()->insert(std::make_pair(pPlot, info));
}
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 04/03/10 jdog5000 */
/* */
/* General AI */
/************************************************************************************************/
int CvPlayerAI::AI_cityTargetUnitsByPath(CvCity* pCity, CvSelectionGroup* pSkipSelectionGroup, int iMaxPathTurns) const
{
PROFILE_FUNC();
int iCount = 0;
int iLoop;
int iPathTurns;
for(CvSelectionGroup* pLoopSelectionGroup = firstSelectionGroup(&iLoop); pLoopSelectionGroup; pLoopSelectionGroup = nextSelectionGroup(&iLoop))
{
if (pLoopSelectionGroup != pSkipSelectionGroup && pLoopSelectionGroup->plot() != NULL && pLoopSelectionGroup->getNumUnits() > 0)
{
CvPlot* pMissionPlot = pLoopSelectionGroup->AI_getMissionAIPlot();
if (pMissionPlot != NULL )
{
int iDistance = stepDistance(pCity->getX_INLINE(), pCity->getY_INLINE(), pMissionPlot->getX_INLINE(), pMissionPlot->getY_INLINE());
if (iDistance <= 1)
{
if( pLoopSelectionGroup->generatePath(pLoopSelectionGroup->plot(), pMissionPlot, 0, true, &iPathTurns) )
{
if( !(pLoopSelectionGroup->canAllMove()) )
{
iPathTurns++;
}
if( iPathTurns <= iMaxPathTurns )
{
iCount += pLoopSelectionGroup->getNumUnits();
}
}
}
}
}
}
return iCount;
}
int CvPlayerAI::AI_unitTargetMissionAIs(CvUnit* pUnit, MissionAITypes eMissionAI, CvSelectionGroup* pSkipSelectionGroup) const
{
return AI_unitTargetMissionAIs(pUnit, &eMissionAI, 1, pSkipSelectionGroup, -1);
}
int CvPlayerAI::AI_unitTargetMissionAIs(CvUnit* pUnit, MissionAITypes* aeMissionAI, int iMissionAICount, CvSelectionGroup* pSkipSelectionGroup) const
{
return AI_unitTargetMissionAIs(pUnit, aeMissionAI, iMissionAICount, pSkipSelectionGroup, -1);
}
int CvPlayerAI::AI_unitTargetMissionAIs(CvUnit* pUnit, MissionAITypes* aeMissionAI, int iMissionAICount, CvSelectionGroup* pSkipSelectionGroup, int iMaxPathTurns) const
{
PROFILE_FUNC();
CvSelectionGroup* pLoopSelectionGroup;
int iCount;
int iLoop;
iCount = 0;
for(pLoopSelectionGroup = firstSelectionGroup(&iLoop); pLoopSelectionGroup; pLoopSelectionGroup = nextSelectionGroup(&iLoop))
{
if (pLoopSelectionGroup != pSkipSelectionGroup)
{
if (pLoopSelectionGroup->AI_getMissionAIUnit() == pUnit)
{
MissionAITypes eGroupMissionAI = pLoopSelectionGroup->AI_getMissionAIType();
int iPathTurns = MAX_INT;
if( iMaxPathTurns >= 0 && (pUnit->plot() != NULL) && (pLoopSelectionGroup->plot() != NULL))
{
pLoopSelectionGroup->generatePath(pLoopSelectionGroup->plot(), pUnit->plot(), 0, false, &iPathTurns);
if( !(pLoopSelectionGroup->canAllMove()) )
{
iPathTurns++;
}
}
if ((iMaxPathTurns == -1) || (iPathTurns <= iMaxPathTurns))
{
for (int iMissionAIIndex = 0; iMissionAIIndex < iMissionAICount; iMissionAIIndex++)
{
if (eGroupMissionAI == aeMissionAI[iMissionAIIndex] || NO_MISSIONAI == aeMissionAI[iMissionAIIndex])
{
iCount += pLoopSelectionGroup->getNumUnits();
}
}
}
}
}
}
return iCount;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
int CvPlayerAI::AI_enemyTargetMissionAIs(MissionAITypes eMissionAI, CvSelectionGroup* pSkipSelectionGroup) const
{
return AI_enemyTargetMissionAIs(&eMissionAI, 1, pSkipSelectionGroup);
}
int CvPlayerAI::AI_enemyTargetMissionAIs(MissionAITypes* aeMissionAI, int iMissionAICount, CvSelectionGroup* pSkipSelectionGroup) const
{
PROFILE_FUNC();
int iCount = 0;
int iLoop;
for(CvSelectionGroup* pLoopSelectionGroup = firstSelectionGroup(&iLoop); pLoopSelectionGroup; pLoopSelectionGroup = nextSelectionGroup(&iLoop))
{
if (pLoopSelectionGroup != pSkipSelectionGroup)
{
CvPlot* pMissionPlot = pLoopSelectionGroup->AI_getMissionAIPlot();
if (NULL != pMissionPlot && pMissionPlot->isOwned())
{
MissionAITypes eGroupMissionAI = pLoopSelectionGroup->AI_getMissionAIType();
for (int iMissionAIIndex = 0; iMissionAIIndex < iMissionAICount; iMissionAIIndex++)
{
if (eGroupMissionAI == aeMissionAI[iMissionAIIndex] || NO_MISSIONAI == aeMissionAI[iMissionAIIndex])
{
if (GET_TEAM(getTeam()).AI_isChosenWar(pMissionPlot->getTeam()))
{
iCount += pLoopSelectionGroup->getNumUnits();
iCount += pLoopSelectionGroup->getCargo();
}
}
}
}
}
}
return iCount;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 05/19/10 jdog5000 */
/* */
/* General AI */
/************************************************************************************************/
int CvPlayerAI::AI_enemyTargetMissions(TeamTypes eTargetTeam, CvSelectionGroup* pSkipSelectionGroup) const
{
PROFILE_FUNC();
int iCount = 0;
int iLoop;
for(CvSelectionGroup* pLoopSelectionGroup = firstSelectionGroup(&iLoop); pLoopSelectionGroup; pLoopSelectionGroup = nextSelectionGroup(&iLoop))
{
if (pLoopSelectionGroup != pSkipSelectionGroup)
{
CvPlot* pMissionPlot = pLoopSelectionGroup->AI_getMissionAIPlot();
if( pMissionPlot == NULL )
{
pMissionPlot = pLoopSelectionGroup->plot();
}
if (NULL != pMissionPlot )
{
if( pMissionPlot->isOwned() && pMissionPlot->getTeam() == eTargetTeam )
{
if (atWar(getTeam(),pMissionPlot->getTeam()) || pLoopSelectionGroup->AI_isDeclareWar(pMissionPlot))
{
iCount += pLoopSelectionGroup->getNumUnits();
iCount += pLoopSelectionGroup->getCargo();
}
}
}
}
}
return iCount;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
int CvPlayerAI::AI_wakePlotTargetMissionAIs(CvPlot* pPlot, MissionAITypes eMissionAI, CvSelectionGroup* pSkipSelectionGroup) const
{
PROFILE_FUNC();
FAssert(pPlot != NULL);
int iCount = 0;
int iLoop;
for(CvSelectionGroup* pLoopSelectionGroup = firstSelectionGroup(&iLoop); pLoopSelectionGroup; pLoopSelectionGroup = nextSelectionGroup(&iLoop))
{
if (pLoopSelectionGroup != pSkipSelectionGroup)
{
MissionAITypes eGroupMissionAI = pLoopSelectionGroup->AI_getMissionAIType();
if (eMissionAI == NO_MISSIONAI || eMissionAI == eGroupMissionAI)
{
CvPlot* pMissionPlot = pLoopSelectionGroup->AI_getMissionAIPlot();
if (pMissionPlot != NULL && pMissionPlot == pPlot)
{
iCount += pLoopSelectionGroup->getNumUnits();
pLoopSelectionGroup->setActivityType(ACTIVITY_AWAKE);
}
}
}
}
return iCount;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 07/19/09 jdog5000 */
/* */
/* Civic AI */
/************************************************************************************************/
CivicTypes CvPlayerAI::AI_bestCivic(CivicOptionTypes eCivicOption) const
{
int iBestValue;
return AI_bestCivic( eCivicOption, &iBestValue, false );
}
CivicTypes CvPlayerAI::AI_bestCivic(CivicOptionTypes eCivicOption, int* iBestValue, bool bCivicOptionVacuum, CivicTypes* paeSelectedCivics) const
{
CivicTypes eBestCivic;
int iValue;
int iI;
(*iBestValue) = MIN_INT;
eBestCivic = NO_CIVIC;
for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
{
if (GC.getCivicInfo((CivicTypes)iI).getCivicOptionType() == eCivicOption)
{
if (canDoCivics((CivicTypes)iI))
{
iValue = AI_civicValue((CivicTypes)iI, bCivicOptionVacuum, paeSelectedCivics);
if (iValue > (*iBestValue))
{
(*iBestValue) = iValue;
eBestCivic = ((CivicTypes)iI);
}
}
}
}
return eBestCivic;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
// Provide a measure of overall happyness (weighted appropriately by city)
int CvPlayerAI::AI_getOverallHappyness(int iExtraUnhappy) const
{
CvCity* pLoopCity;
int iLoop = 0;
int iHappyness = 0;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if ( !pLoopCity->isNoUnhappiness() )
{
iHappyness += (pLoopCity->happyLevel() - pLoopCity->unhappyLevel() - iExtraUnhappy)*50;
}
}
return iHappyness;
}
// Helper function to compute a trnuncated quadratic to asign a value to (net) happyness
static int happynessValue(int iNetHappyness)
{
if ( iNetHappyness > 5 )
{
// Cap useful gains 0n the postive side at 5
iNetHappyness = 5;
}
if ( iNetHappyness > 0 )
{
return 100 * iNetHappyness - 10 * iNetHappyness * iNetHappyness;
}
else
{
return 100 * iNetHappyness; // Just linear on the negative side since each is one less working pop
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
/********************************************************************************/
/* New Civic AI 19.08.2010 Fuyu */
/********************************************************************************/
int CvPlayerAI::AI_civicValue(CivicTypes eCivic, bool bCivicOptionVacuum, CivicTypes* paeSelectedCivics) const
{
PROFILE_FUNC();
bool bWarPlan;
int iConnectedForeignCities;
int iTotalReligonCount;
int iHighestReligionCount;
int iWarmongerPercent;
//int iHappiness;
int iValue;
int iTempValue;
int iI, iJ;
bool bCultureVictory3 = AI_isDoVictoryStrategy(AI_VICTORY_CULTURE3);
bool bCultureVictory2 = AI_isDoVictoryStrategy(AI_VICTORY_CULTURE2);
/************************************************************************************************/
/* UNOFFICIAL_PATCH 10/05/09 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
// Circumvents crash bug in simultaneous turns MP games
if( eCivic == NO_CIVIC )
{
return 1;
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
FAssertMsg(eCivic < GC.getNumCivicInfos(), "eCivic is expected to be within maximum bounds (invalid Index)");
FAssertMsg(eCivic >= 0, "eCivic is expected to be non-negative (invalid Index)");
if( isBarbarian() )
{
return 1;
}
/************************************************************************************************/
/* Afforess Start 01/16/10 */
/* */
/* Cache Civic Values */
/************************************************************************************************/
if (m_aiCivicValueCache[eCivic + (bCivicOptionVacuum ? 0 : GC.getNumCivicInfos())] != MAX_INT) {
return m_aiCivicValueCache[eCivic + (bCivicOptionVacuum ? 0 : GC.getNumCivicInfos())];
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
CvCivicInfo& kCivic = GC.getCivicInfo(eCivic);
bWarPlan = (GET_TEAM(getTeam()).getAnyWarPlanCount(true) > 0);
if( bWarPlan )
{
bWarPlan = false;
int iEnemyWarSuccess = 0;
for( int iTeam = 0; iTeam < MAX_CIV_TEAMS; iTeam++ )
{
if( GET_TEAM((TeamTypes)iTeam).isAlive() && !GET_TEAM((TeamTypes)iTeam).isMinorCiv() )
{
if( GET_TEAM(getTeam()).AI_getWarPlan((TeamTypes)iTeam) != NO_WARPLAN )
{
if( GET_TEAM(getTeam()).AI_getWarPlan((TeamTypes)iTeam) == WARPLAN_TOTAL || GET_TEAM(getTeam()).AI_getWarPlan((TeamTypes)iTeam) == WARPLAN_PREPARING_TOTAL )
{
bWarPlan = true;
break;
}
if( GET_TEAM(getTeam()).AI_isLandTarget((TeamTypes)iTeam) )
{
bWarPlan = true;
break;
}
iEnemyWarSuccess += GET_TEAM((TeamTypes)iTeam).AI_getWarSuccess(getTeam());
}
}
}
if( !bWarPlan )
{
if( iEnemyWarSuccess > std::min(getNumCities(), 4) * GC.getWAR_SUCCESS_CITY_CAPTURING() )
{
// Lots of fighting, so war is real
bWarPlan = true;
}
else if( iEnemyWarSuccess > std::min(getNumCities(), 2) * GC.getWAR_SUCCESS_CITY_CAPTURING() )
{
if( GET_TEAM(getTeam()).AI_getEnemyPowerPercent() > 120 )
{
bWarPlan = true;
}
}
}
}
/*RevDCM - Fuyu commented out
if( !bWarPlan )
{
// Aggressive players will stick with war civics
if( GET_TEAM(getTeam()).AI_getTotalWarOddsTimes100() > 200 )
{
bWarPlan = true;
}
}
*/
iConnectedForeignCities = countPotentialForeignTradeCitiesConnected();
iTotalReligonCount = countTotalHasReligion();
ReligionTypes eBestReligion = AI_bestReligion();
if (!kCivic.isStateReligion() && !isStateReligion())
{
eBestReligion = NO_RELIGION;
}
else if (eBestReligion == NO_RELIGION)
{
eBestReligion = getStateReligion();
}
iHighestReligionCount = ((eBestReligion == NO_RELIGION) ? 0 : getHasReligionCount(eBestReligion));
iWarmongerPercent = 25000 / std::max(100, (100 + GC.getLeaderHeadInfo(getPersonalityType()).getMaxWarRand()));
//Fuyu Civic AI: restructuring
//#0: constant values
iValue = (getNumCities() * 6);
iValue += (GC.getCivicInfo(eCivic).getAIWeight() * getNumCities());
if ( gPlayerLogLevel > 2 )
{
logBBAI("Player %d (%S) Civic %S base value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iValue);
}
// Koshling - Anarchy length is not part of the civi's value - its part of the cost of an overall switch
// and is now evaluated in that process
//iValue += -(GC.getCivicInfo(eCivic).getAnarchyLength() * getNumCities());
//Afforess: weight civic upkeep costs more seriously
int iUpkeepCosts = getSingleCivicUpkeep(eCivic, true) * 2;
bool bFinancialTrouble = AI_isFinancialTrouble();
//Assume upkeep costs will get worse over time
int iCivicFundedPercent = AI_costAsPercentIncome((iUpkeepCosts * 3) / 2);
int iSafePercent = AI_safeCostAsPercentIncome();
//We are in financial trouble already
if (bFinancialTrouble)
{
iUpkeepCosts *= 2;
}
//Else if this civic will push us into financial trouble
else if (iCivicFundedPercent < iSafePercent)
{
iUpkeepCosts *= 3;
iUpkeepCosts /= 2;
}
iTempValue = -iUpkeepCosts;
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S upkeep value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
CvCity* pCapital = getCapitalCity();
iValue += ((kCivic.getGreatPeopleRateModifier() * getNumCities()) / 10);
// Koshling - made the GG calculations non-linear as they were not scaling well with large armies
int iGGMultiplier = 100 - 1000/(10 + range(getNumMilitaryUnits(), 1, 100));
iTempValue = ((kCivic.getGreatGeneralRateModifier() * iGGMultiplier) / 10 );
iTempValue += ((kCivic.getDomesticGreatGeneralRateModifier() * iGGMultiplier) / 20 );
//Fuyu: Only if wars ongoing, as suggested by Munch - modified by Koshling to just be an increase then
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S GG modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue/((bWarPlan || isMinorCiv()) ? 3 : 1);
//Distance Maintenance
int iLoop;
int iSaved = 0;
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
iSaved += pLoopCity->getDistanceMaintenanceSavedTimes100ByCivic(eCivic);
}
iTempValue = iSaved / (iSaved < 0 && bFinancialTrouble ? 50 : 80);
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S distance maintenance modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
//Num Cities Maintenance
iSaved = 0;
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
iSaved += pLoopCity->getNumCitiesMaintenanceSavedTimes100ByCivic(eCivic);
}
iTempValue = iSaved / (iSaved < 0 && bFinancialTrouble ? 50 : 80);
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S num cities maintenance modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
//Home Area Maintenance
iSaved = 0;
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
iSaved += pLoopCity->getHomeAreaMaintenanceSavedTimes100ByCivic(eCivic);
}
iTempValue = iSaved / (iSaved < 0 && bFinancialTrouble ? 50 : 80);
if (gPlayerLogLevel > 2 && iTempValue != 0)
{
logBBAI("Player %d (%S) Civic %S home area maintenance modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
//Other Area Maintenance
iSaved = 0;
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
iSaved += pLoopCity->getOtherAreaMaintenanceSavedTimes100ByCivic(eCivic);
}
iTempValue = iSaved / (iSaved < 0 && bFinancialTrouble ? 50 : 80);
if (gPlayerLogLevel > 2 && iTempValue != 0)
{
logBBAI("Player %d (%S) Civic %S other area maintenance modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
if( kCivic.getFreeExperience() > 0 )
{
// Free experience increases value of hammers spent on units, population is an okay measure of base hammer production
iTempValue = (kCivic.getFreeExperience() * getTotalPopulation() * (bWarPlan ? 30 : 12))/100;
iTempValue *= AI_averageYieldMultiplier(YIELD_PRODUCTION);
iTempValue /= 100;
iTempValue *= iWarmongerPercent;
iTempValue /= 100;
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S free experience value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
iTempValue = ((kCivic.getWorkerSpeedModifier() * AI_getNumAIUnits(UNITAI_WORKER)) / 15);
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S worker speed value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = ((kCivic.getImprovementUpgradeRateModifier() * getNumCities()) / 50);
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S improvement upgrade modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = (kCivic.getMilitaryProductionModifier() * getNumCities() * iWarmongerPercent) / (bWarPlan ? 300 : 500 );
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S military production modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = (kCivic.getBaseFreeUnits() / 2);
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S free units value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = (kCivic.getBaseFreeMilitaryUnits() / 2);
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S free military units value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = ((kCivic.getFreeUnitsPopulationPercent() * getTotalPopulation()) / 200);
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S free units/pop value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = ((kCivic.getFreeMilitaryUnitsPopulationPercent() * getTotalPopulation()) / 300);
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S free military units/pop value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = -(kCivic.getGoldPerUnit() * getNumUnits());
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S unit cost value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
// Afforess - GPM is much more severe than just for warmongerers
// iTempValue = -(kCivic.getGoldPerMilitaryUnit() * getNumMilitaryUnits() * iWarmongerPercent) / 200
iTempValue = -kCivic.getGoldPerMilitaryUnit() * (bWarPlan ? ((getNumMilitaryUnits() * 3) / 2) : getNumMilitaryUnits());
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S military unit cost value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
if (kCivic.getInflationModifier() != 0)
{
// Koshling - Use 100 turns of first order costs to judge inflation modifiers
iTempValue = -getCurrentInflationPerTurnTimes10000()*calculatePreInflatedCosts()*kCivic.getInflationModifier()/10000;
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S inflation modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
/************************************************************************************************/
/* Afforess Start 01/16/10 */
/* */
/* Better AI Civic Calculation */
/************************************************************************************************/
iTempValue = 0;
//Dumb to pay ourselves tribute
bool bStrongCiv = GC.getGameINLINE().isInStrongestPlayerThreshold(getID());
bool bWeakCiv = GC.getGameINLINE().isInWeakestPlayerThreshold(getID());
if (kCivic.getTributePercent() > 0 && bStrongCiv)
{
iTempValue -= 250;
}
else if (kCivic.getTributePercent() < 0 && bWeakCiv)
{
iTempValue -= 250;
}
//Treat negative values the same as positives
int iTributePercent = std::max(kCivic.getTributePercent(), -kCivic.getTributePercent());
if (iTributePercent != 0)
{
int iTotalCommerce = 0;
for (int iCommerce = 0; iCommerce < NUM_COMMERCE_TYPES; iCommerce++)
{
iTotalCommerce += getCommerceRate((CommerceTypes)iCommerce);
}
iTempValue -= (iTributePercent * iTotalCommerce) / (bFinancialTrouble ? 25 : 75);
}
if (gPlayerLogLevel > 2 && iTempValue != 0)
{
logBBAI("Player %d (%S) Civic %S tribute payment value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
if (kCivic.getPowerfulRelationsChange() != 0)
{
iTempValue = kCivic.getPowerfulRelationsChange() * GC.getGameINLINE().getNumStrongestPlayers(getID()) * 8;
if (bStrongCiv)
{
iTempValue /= 3;
}
else if (bWeakCiv)
{
iTempValue *= 3;
iTempValue /= 2;
}
}
if (gPlayerLogLevel > 2 && iTempValue != 0)
{
logBBAI("Player %d (%S) Civic %S powerful civ relations change value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
if (kCivic.getWeakestRelationsChange() != 0)
{
iTempValue = kCivic.getWeakestRelationsChange() * GC.getGameINLINE().getNumWeakestPlayers(getID()) * 4;
}
if (gPlayerLogLevel > 2 && iTempValue != 0)
{
logBBAI("Player %d (%S) Civic %S weakest civ relations change value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
if (kCivic.getLessPowerfulRelationsChange() != 0)
{
int iLessPowerful = 0;
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (iI != getID() && GET_PLAYER((PlayerTypes)iI).isAlive() && GET_TEAM(getTeam()).isHasMet(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
if (getPower() > GET_PLAYER((PlayerTypes)iI).getPower())
{
iLessPowerful++;
}
}
}
iTempValue = kCivic.getLessPowerfulRelationsChange() * iLessPowerful * 5;
if (bStrongCiv)
{
iTempValue /= 3;
}
}
if (gPlayerLogLevel > 2 && iTempValue != 0)
{
logBBAI("Player %d (%S) Civic %S less powerful civ relations change value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
if (kCivic.getGlobalRelationsChange() != 0)
{
int iMetCivs = 0;
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (iI != getID() && GET_PLAYER((PlayerTypes)iI).isAlive() && GET_TEAM(getTeam()).isHasMet(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
iMetCivs++;
}
}
iTempValue = kCivic.getGlobalRelationsChange() * iMetCivs * 5;
if (bStrongCiv)
{
iTempValue *= 2;
iTempValue /= 3;
}
}
if (gPlayerLogLevel > 2 && iTempValue != 0)
{
logBBAI("Player %d (%S) Civic %S global civ relations change value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
if (kCivic.getDomesticConnectednessModifier() != 0)
{
int iDomesticConnectedness = 0;
int iLoop;
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
iDomesticConnectedness += pLoopCity->getDomesticConnectednessCommerce();
}
iTempValue = (iDomesticConnectedness * kCivic.getDomesticConnectednessModifier()) / 50;
iTempValue -= iDomesticConnectedness;
}
if (gPlayerLogLevel > 2 && iTempValue != 0)
{
logBBAI("Player %d (%S) Civic %S domestic connectedness modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
if (kCivic.getForeignConnectednessModifier() != 0)
{
int iForeignConnectedness = 0;
int iLoop;
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
iForeignConnectedness += pLoopCity->getForeignConnectednessCommerce();
}
iTempValue = (iForeignConnectedness * kCivic.getForeignConnectednessModifier()) / 50;
iTempValue -= iForeignConnectedness;
}
if (gPlayerLogLevel > 2 && iTempValue != 0)
{
logBBAI("Player %d (%S) Civic %S foreign connectedness modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
if (kCivic.getSeizedForeignConnectednessPercent() != 0)
{
int iSeizedGold = 0;
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isCanSeizeForeignConnectednessFromUs(getID(), true))
{
int iLoop;
for (CvCity* pLoopCity = GET_PLAYER((PlayerTypes)iI).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iI).nextCity(&iLoop))
{
iSeizedGold += (pLoopCity->getForeignConnectednessCommerce() + pLoopCity->getDomesticConnectednessCommerce()) * kCivic.getSeizedForeignConnectednessPercent();
}
}
}
iSeizedGold /= 100;
iTempValue += iSeizedGold;
}
if (gPlayerLogLevel > 2 && iTempValue != 0)
{
logBBAI("Player %d (%S) Civic %S seized foreign connectedness value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
if (kCivic.isCanNotCeaseRelations())
{
iTempValue += 50;
}
if (gPlayerLogLevel > 2 && iTempValue != 0)
{
logBBAI("Player %d (%S) Civic %S can not cease relations value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
if (kCivic.isForcesOpenTradeWithWeakerPlayers())
{
int iLessPowerful = 0;
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (iI != getID() && GET_PLAYER((PlayerTypes)iI).isAlive() && GET_TEAM(getTeam()).isHasMet(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
if (getPower() > GET_PLAYER((PlayerTypes)iI).getPower())
{
iLessPowerful++;
}
}
}
iTempValue = iLessPowerful * 5;
}
if (gPlayerLogLevel > 2 && iTempValue != 0)
{
logBBAI("Player %d (%S) Civic %S forces open trade with weaker players value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
if (kCivic.isMilitaryFoodProduction())
{
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
iTempValue += pLoopCity->foodDifference(false, true, true);
}
//If not at war Food is generally more valuable then hammers
if (!bWarPlan)
{
iTempValue /= -4;
}
//If we are at war hammers are more valuable
else
{
iTempValue *= 3;
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S military food production value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
// Consider REV effects
if (!GC.getGameINLINE().isOption(GAMEOPTION_NO_REVOLUTION))
{
// If there is no civoption vacuum we need to subtract out the current civic
CvCivicInfo* kCurrentCivic = NULL;
if ( !bCivicOptionVacuum )
{
CivicTypes eCurrentCivic = getCivics((CivicOptionTypes)kCivic.getCivicOptionType());
if ( eCurrentCivic != NO_CIVIC )
{
kCurrentCivic = &GC.getCivicInfo(eCurrentCivic);
}
}
if (kCivic.getRevIdxLocal() != 0)
{
// What's our current situation?
int localRevIdx = AI_calculateAverageLocalInstability();
// Use the more serious of the before and after values if this civic were to be chosen
if ( kCivic.getRevIdxLocal() > 0 )
{
localRevIdx += kCivic.getRevIdxLocal();
}
// If there is no civoption vacuum we need to subtract out the current civic
if ( kCurrentCivic != NULL )
{
localRevIdx -= kCurrentCivic->getRevIdxLocal();
}
// Treat instability seriously as it goes up - not just linear
int localRevScaling = (localRevIdx < 0 ? 0 : std::min(localRevIdx*localRevIdx/50 + localRevIdx/2, 100));
iTempValue = -(kCivic.getRevIdxLocal() * localRevScaling * getNumCities())/4;
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S local stability value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
if (kCivic.getRevIdxNational() != 0)
{
iTempValue = -(8 * getNumCities()) * kCivic.getRevIdxNational();
// If there is no civoption vacuum we need to subtract out the current civic
if ( kCurrentCivic != NULL )
{
iTempValue += (8 * getNumCities()) * kCurrentCivic->getRevIdxNational();
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S national stability value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
if (kCivic.getRevIdxDistanceModifier() != 0)
{
int iCapitalDistance = AI_calculateAverageCityDistance();
int iOldCapitalDistance = iCapitalDistance;
iCapitalDistance *= 100 + kCivic.getRevIdxDistanceModifier() - (kCurrentCivic == NULL ? 0 : kCurrentCivic->getRevIdxDistanceModifier());
iCapitalDistance /= 100;
iTempValue = (getNumCities() * (iOldCapitalDistance - iCapitalDistance) * (10+std::max(0,AI_calculateAverageLocalInstability())));
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S REV distance modifier %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
}
if (kCivic.getCityLimit(getID()) > 0)
{
if ( kCivic.getCityOverLimitUnhappy() == 0 )
{
// Treat numCities == limit as a want-to-expand case even if we have not actually (yet) decided
// to produce the settler
if (getNumCities() + AI_totalUnitAIs(UNITAI_SETTLE) >= kCivic.getCityLimit(getID()))
{
iValue -= (getNumCities() + AI_totalUnitAIs(UNITAI_SETTLE) + 1 - kCivic.getCityLimit(getID())) * 100; //if we are planning to expand, city limitations suck
}
else
{
// Smaller limits suck more but since we are not trying to expand it can't be
// worse than the best case where we ARE trying and can't
iValue -= 100 / kCivic.getCityLimit(getID());
}
}
else
{
// Happiness effect calculation
CvCity* pLoopCity;
int iLoop = 0;
int iCost = 0;
int iCount = 0;
int iWantToBuild = getNumCities() + AI_totalUnitAIs(UNITAI_SETTLE) + 1;
if (iWantToBuild > kCivic.getCityLimit(getID()))
{
int iExtraCities = iWantToBuild - kCivic.getCityLimit(getID());
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if ( !pLoopCity->isNoUnhappiness() )
{
int iHappy = pLoopCity->happyLevel() - pLoopCity->unhappyLevel(3); // Allow for pop growth of 3
iCount++;
if ( iHappy < iExtraCities*kCivic.getCityOverLimitUnhappy() )
{
// Weight by city size as the happiness calculation does
// [TBD - is this really right though - unhappy in smaller cities is arguably worse]
iCost += 50*(iExtraCities*kCivic.getCityOverLimitUnhappy() - iHappy);
}
}
}
// Same normalization as the happiness calculations later use
if ( iCount != 0 )
{
int iTempValue = (getNumCities() * 3 * iCost) / (25 * iCount);
iValue -= iTempValue;
if ( gPlayerLogLevel > 2 )
{
logBBAI("Player %d (%S) Civic %S city limit bad value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
}
}
}
}
//Upgrade Anywhere
iTempValue = 0;
if (kCivic.isUpgradeAnywhere())
{
iTempValue += getNumMilitaryUnits() * iWarmongerPercent / 100;
bool bRich = false;
//the current gold we have plus the gold we will have in 10 turns is a decent
//estimate of whether we are rich (if we can afford to upgrade units, anyway)
if (getGold() + (calculateBaseNetGold() * 10) > (50 + 100 * getCurrentEra()))
bRich = true;
if (bWarPlan)
{
iTempValue *= 2;
if (bRich)
{
iTempValue *= 2;
}
}
else
{
iTempValue /= 2;
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S upgrade anywhere value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
bool bValid = true;
iTempValue = 0;
int iNonstateReligionCount = 0;
//Inquisition Civic Values
if (kCivic.isAllowInquisitions())
{
if (getStateReligion() != NO_RELIGION)
{
//check that we don't have a civic that already blocks this inquisitions...
for (iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
//we are considering changing this civic, so ignore it
if (GC.getCivicInfo(eCivic).getCivicOptionType() != (CivicOptionTypes)iI)
{
if (GC.getCivicInfo(getCivics((CivicOptionTypes)iI)).isDisallowInquisitions())
{
bValid = false;
}
}
}
if (bValid)
{
if(hasInquisitionTarget())
{
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if ((ReligionTypes)iI != getStateReligion())
{
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (pLoopCity->isHasReligion((ReligionTypes)iI))
{
iNonstateReligionCount++;
}
}
}
}
}
}
}
if(isPushReligiousVictory())
{
iTempValue += iNonstateReligionCount * 20;
} else if(isConsiderReligiousVictory())
{
iTempValue /= 5;
}
iTempValue += countCityReligionRevolts() * 5;
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S inquisitions value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
iTempValue = 0;
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
iTempValue += (kCivic.getUnitCombatProductionModifier(iI) * 2) / 3;
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S unitcombat production modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
int iLoop;
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
BuildingTypes eBuilding = (BuildingTypes)GC.getCivilizationInfo(pLoopCity->getCivilizationType()).getCivilizationBuildings(iI);
if (pLoopCity->canConstruct(eBuilding, true, false))
{
iTempValue += pLoopCity->getBaseYieldRate(YIELD_PRODUCTION) * kCivic.getBuildingClassProductionModifier(iI);
}
}
}
iTempValue /= 100;
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S building class production modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
iTempValue += kCivic.getBonusMintedPercent(iI) * getNumAvailableBonuses((BonusTypes)iI) * getNumCities() * 2;
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S bonus minting value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
iValue += (kCivic.getUnitClassProductionModifier(iI) * 2) / 5;
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S unitclass production modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = kCivic.isEnablesMAD() ? 5 * getNumNukeUnits() : 0;
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S MAD value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
if (kCivic.getPopulationgrowthratepercentage() != 0)
{
if ( m_iCityGrowthValueBase == -1 )
{
int iLoop;
int iCityCount = 0;
iTempValue = 0;
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
int iCityHappy = pLoopCity->happyLevel() - pLoopCity->unhappyLevel();
int iCurrentFoodToGrow = pLoopCity->growthThreshold();
int iFoodPerTurn = pLoopCity->foodDifference(false, true, true);
int iCityValue = 0;
if ( iFoodPerTurn > 0 )
{
if ( iCityHappy >= 0 )
{
iCityValue += (std::min(3,iCityHappy+1)*iCurrentFoodToGrow)/iFoodPerTurn;
}
}
iTempValue += iCityValue;
}
}
else
{
iTempValue = m_iCityGrowthValueBase;
}
if ( getNumCities() > 0 )
{
// iTempValue is now essentially the average number of turns to grow * iCityCount
// We want to normalize the value to be somewhere near gold-per-turn units, so that's
// roughly the amount of extra 'value' an extra population has multiplied by the
// reduction in turns to grow. The value of an extra pop is more or less the yield
// value it produces which can be estimated as a constant that varies with era, reflecting
// increased producitivity of farms, mines, specialists, etc. (era-index * 2 + 3) is a reasonable
// estimate.
// So the value is:
int onePopBaseValue = (int)getCurrentEra()*2 + 3;
iTempValue = -(kCivic.getPopulationgrowthratepercentage()*iTempValue*onePopBaseValue)/(getNumCities()*100);
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S growth rate modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
}
if (kCivic.getAttitudeShareMod() != 0)
{
int iTempValue = 0;
// The AI will disfavor bad attitude modifiers more than good ones
if (kCivic.getAttitudeShareMod() < 0)
{
iTempValue += (kCivic.getAttitudeShareMod() * 4);
}
else if (kCivic.getAttitudeShareMod() > 0)
{
iTempValue += (kCivic.getAttitudeShareMod() * 3);
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S attitude share value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
iValue += kCivic.getFractionalXPEarnedInCity() * 100;
if (bWarPlan)
{
int iTempValue = 0;
//negative values are good, positive ones, bad
if (kCivic.getDistantUnitSupportCostModifier() < 0)
{
iTempValue = (-kCivic.getDistantUnitSupportCostModifier() * 2);
}
//if we are going to war soon, we can't afford high costs
else if (kCivic.getDistantUnitSupportCostModifier() > 0)
{
iTempValue = (-kCivic.getDistantUnitSupportCostModifier() * 3);
}
//City defense is good, especially during wars
if (kCivic.getExtraCityDefense() > 0)
{
iTempValue += (kCivic.getExtraCityDefense() * 2);
}
//Negative city defense would be really bad in a war, avoid at all costs
else if (kCivic.getExtraCityDefense() < 0)
{
iTempValue -= (kCivic.getExtraCityDefense() * 4);
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S war-time modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
else
{
iTempValue = (-kCivic.getDistantUnitSupportCostModifier() / 2);
iTempValue += kCivic.getExtraCityDefense();
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S non-war-time modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
if (kCivic.getTaxRateUnhappiness() != 0)
{
int iNewAnger = (getCommercePercent(COMMERCE_GOLD) * getTaxRateUnhappiness() / 100);
iTempValue = (12 * getNumCities() * AI_getHappinessWeight(-iNewAnger, 0)) / 100;
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S tax-rate unhappiness value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
//bool bFinancialTrouble = AI_isFinancialTrouble();
iTempValue = 0;
if (kCivic.isTaxationAnger())
{
int iNetIncome = 1 + getCommerceRate(COMMERCE_GOLD) + std::max(0, getGoldPerTurn());
int iNetExpenses = 1 + calculateInflatedCosts() + std::min(0, getGoldPerTurn());
int iNetProfit = iNetIncome - iNetExpenses;
iTempValue += iNetProfit;
iTempValue -= getNumCities() * 5;
if (bFinancialTrouble || (!GC.getGameINLINE().isOption(GAMEOPTION_NO_REVOLUTION)))
{
iTempValue *= 2;
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S taxation anger value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
iTempValue = 0;
for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
{
if (kCivic.getFreeSpecialistCount(iI) > 0)
{
iTempValue += getNumCities() * kCivic.getFreeSpecialistCount(iI) * 12;
}
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S free specialist count value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
if (pCapital != NULL)
{
// Warm up the can train cache for the capital
pCapital->populateCanTrainCache(false);
CivicTypes eCurrentCivic = getCivics((CivicOptionTypes)kCivic.getCivicOptionType());
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
BuildingTypes eLoopBuilding = ((BuildingTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iI)));
bool bValidCivicsWith = true;
bool bValidCivicsWithout = true;
bool bCivicIsEnabler = false;
if (eLoopBuilding != NO_BUILDING)
{
if (GC.getBuildingInfo(eLoopBuilding).getPrereqAndTech() == NO_TECH || GC.getTechInfo((TechTypes)GC.getBuildingInfo(eLoopBuilding).getPrereqAndTech()).getEra() <= getCurrentEra())
{
if ((GC.getBuildingInfo(eLoopBuilding).isPrereqAndCivics(eCivic) || GC.getBuildingInfo(eLoopBuilding).isPrereqOrCivics(eCivic)) ||
(eCurrentCivic != NO_CIVIC && (GC.getBuildingInfo(eLoopBuilding).isPrereqAndCivics(eCurrentCivic) || GC.getBuildingInfo(eLoopBuilding).isPrereqOrCivics(eCurrentCivic))))
{
if ( !GC.getGameINLINE().isBuildingClassMaxedOut((BuildingClassTypes)iI) || getBuildingClassCount((BuildingClassTypes)iI) > 0 )
{
bool bValidWith = false;
bool bValidWithout = false;
bool bHasMultipleEnablingCivicCategories = false;
CivicOptionTypes eEnablingCategory = NO_CIVICOPTION;
for (int iJ = 0; iJ < GC.getNumCivicInfos(); iJ++)
{
if (GC.getBuildingInfo(eLoopBuilding).isPrereqAndCivics(iJ))
{
if ( eEnablingCategory == NO_CIVICOPTION )
{
eEnablingCategory = (CivicOptionTypes)GC.getCivicInfo((CivicTypes)iJ).getCivicOptionType();
}
else
{
bHasMultipleEnablingCivicCategories = true;
}
if (eCivic != iJ)
{
if (kCivic.getCivicOptionType() == GC.getCivicInfo((CivicTypes)iJ).getCivicOptionType())
{
bValidCivicsWith = false;
bValidCivicsWithout = (!bCivicOptionVacuum && eCurrentCivic == iJ);
}
else
{
if ( !isCivic((CivicTypes)iJ) )
{
bValidCivicsWith = false;
bValidCivicsWithout = false;
}
}
}
else
{
bCivicIsEnabler = true;
bValidCivicsWithout = false;
}
}
}
//Make sure we have the correct prereq Or Civics. We need just one
if (bValidCivicsWith || bValidCivicsWithout)
{
bool bHasOrCivicReq = false;
for (int iJ = 0; iJ < GC.getNumCivicInfos(); iJ++)
{
if (GC.getBuildingInfo(eLoopBuilding).isPrereqOrCivics(iJ))
{
if ( eEnablingCategory == NO_CIVICOPTION )
{
eEnablingCategory = (CivicOptionTypes)GC.getCivicInfo((CivicTypes)iJ).getCivicOptionType();
}
else
{
bHasMultipleEnablingCivicCategories = true;
}
bHasOrCivicReq = true;
if (kCivic.getCivicOptionType() != GC.getCivicInfo((CivicTypes)iJ).getCivicOptionType())
{
if (isCivic((CivicTypes)iJ))
{
bValidWithout = true;
bValidWith = true;
}
}
else if ( eCivic == (CivicTypes)iJ )
{
bValidWith = true;
bCivicIsEnabler = true;
}
else
{
bValidWithout |= (!bCivicOptionVacuum && eCurrentCivic == iJ);
}
}
}
if ( bHasOrCivicReq)
{
bValidCivicsWith &= bValidWith;
bValidCivicsWithout &= bValidWithout;
}
}
iTempValue = 0;
int iValueDivisor = 1;
if ( GC.getBuildingInfo(eLoopBuilding).getPrereqAndTech() != NO_TECH &&
!GET_TEAM(getTeam()).isHasTech((TechTypes)GC.getBuildingInfo(eLoopBuilding).getPrereqAndTech()) )
{
iValueDivisor = 2;
}
bool bIsWonder = isNationalWonderClass((BuildingClassTypes)iI) || isWorldWonderClass((BuildingClassTypes)iI) || isTeamWonderClass((BuildingClassTypes)iI);
int iNumInstancesToScore = (bIsWonder ? 1 : std::max(getNumCities(), getBuildingClassCount((BuildingClassTypes)iI) + getNumCities() / 4));
// If the building is enabled by multiple categories just count it at half value always
// This isn't strictly accurate, but because civic evaluation works by linarly combining
// evaluations in different categories, cross-category couplings like this have to give
// stable result or else civic choices will oscillate
if (bHasMultipleEnablingCivicCategories && bValidCivicsWith && bCivicIsEnabler)
{
//Estimate value from capital city
iTempValue = (pCapital->AI_buildingValue(eLoopBuilding, 0) * iNumInstancesToScore) / (12*iValueDivisor);
if ( gPlayerLogLevel > 2 )
{
logBBAI("Player %d (%S) Civic %S jointly enables building %S with value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
GC.getBuildingInfo(eLoopBuilding).getDescription(),
iTempValue);
}
}
else if (bValidCivicsWith && !bValidCivicsWithout)
{
//Estimate value from capital city
iTempValue = (pCapital->AI_buildingValue(eLoopBuilding, 0) * iNumInstancesToScore) / (6*iValueDivisor);
if ( gPlayerLogLevel > 2 )
{
logBBAI("Player %d (%S) Civic %S enables building %S with value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
GC.getBuildingInfo(eLoopBuilding).getDescription(),
iTempValue);
}
}
else if (!bValidCivicsWith && bValidCivicsWithout)
{
//Loses us the ability to construct the building
iTempValue = -(pCapital->AI_buildingValue(eLoopBuilding, 0) * iNumInstancesToScore) / (6*iValueDivisor);
if ( gPlayerLogLevel > 2 )
{
logBBAI("Player %d (%S) Civic %S disables building %S with value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
GC.getBuildingInfo(eLoopBuilding).getDescription(),
iTempValue);
}
}
iValue += iTempValue;
}
}
}
}
}
}
iTempValue = 0;
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
BuildingTypes eLoopBuilding = ((BuildingTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iI)));
if (eLoopBuilding != NO_BUILDING)
{
for (iJ = 0; iJ < NUM_COMMERCE_TYPES; iJ++)
{
if (kCivic.getBuildingCommerceModifier(eLoopBuilding, iJ) != 0)
{
int iMulitplier = (iJ == COMMERCE_GOLD && bFinancialTrouble) ? 6 : 4;
iTempValue += getBuildingClassCount((BuildingClassTypes)iI) * kCivic.getBuildingCommerceModifier(eLoopBuilding, iJ) * iMulitplier;
}
if (kCivic.getBuildingClassCommerceChange(iI, iJ) != 0)
{
int iMulitplier = (iJ == COMMERCE_GOLD && bFinancialTrouble) ? 12 : 8;
iTempValue += getBuildingClassCount((BuildingClassTypes)iI) * kCivic.getBuildingClassCommerceChange(iI, iJ) * iMulitplier;
}
}
}
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S building commerce value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
for (iJ = 0; iJ < GC.getNumImprovementInfos(); iJ++)
{
iTempValue += (8 * (kCivic.getImprovementHappinessChanges(iJ) * (getImprovementCount((ImprovementTypes)iJ) + getNumCities())));
iTempValue += ((8 * (kCivic.getImprovementHealthPercentChanges(iJ) * (getImprovementCount((ImprovementTypes)iJ) + getNumCities()))) / 100);
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S improvement change value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
{
for (iJ = 0; iJ < NUM_COMMERCE_TYPES; iJ++)
{
iTempValue += ((kCivic.getSpecialistCommercePercentChanges(iI, iJ) * getTotalPopulation()) / 500);
}
for (iJ = 0; iJ < NUM_YIELD_TYPES; iJ++)
{
iTempValue += ((kCivic.getSpecialistYieldPercentChanges(iI, iJ) * getTotalPopulation()) / 500);
}
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S specialist change value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
for (iJ = 0; iJ < GC.getNumTerrainInfos(); iJ++)
{
iTempValue += (AI_averageYieldMultiplier((YieldTypes)iI) * (kCivic.getTerrainYieldChanges(iJ, iI) * (NUM_CITY_PLOTS + getNumCities()/2))) / 100;
}
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S yield change value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = 0;
if (!GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI))
{
for (iI = 0; iI < GC.getNumFlavorTypes(); iI++)
{
iValue += AI_getFlavorValue((FlavorTypes)iI) * GC.getCivicInfo(eCivic).getFlavorValue((FlavorTypes)iI);
}
if (gPlayerLogLevel > 2 && iTempValue != 0)
{
logBBAI("Player %d (%S) Civic %S flavor value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
iTempValue = 0;
CivicTypes eTargetCivic;
CivicTypes eCurrentCivic = getCivics((CivicOptionTypes)GC.getCivicInfo(eCivic).getCivicOptionType());
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
int iOurPower = std::max(1, GET_TEAM(getTeam()).getPower(true));
int iTheirPower = std::max(1, GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).getDefensivePower());
if (!GET_PLAYER((PlayerTypes)iI).isHuman() && GET_PLAYER((PlayerTypes)iI).isAlive() && (GET_PLAYER((PlayerTypes)iI).getTeam() != getTeam()) && (!GET_PLAYER((PlayerTypes)iI).isBarbarian()))
{
int iPlayerValue = 0;
for (int iJ = 0; iJ < GC.getNumCivicOptionInfos(); iJ++)
{
eTargetCivic = GET_PLAYER((PlayerTypes)iI).getCivics((CivicOptionTypes)iJ);
int iAttitudeChange = (eTargetCivic != NO_CIVIC ? (GC.getCivicInfo(eCivic).getCivicAttitudeChange(eTargetCivic) - (eCurrentCivic != NO_CIVIC ? GC.getCivicInfo(eCurrentCivic).getCivicAttitudeChange(eTargetCivic) : 0)) : 0);
//New Civic Attitude minus old civic attitude
int iCurrentAttitude = AI_getAttitudeVal((PlayerTypes)iI);
//We are close friends
if (iCurrentAttitude > 5)
{//Positive Changes are welcome, negative ones, not so much
iPlayerValue += iAttitudeChange * 3;
}
//we aren't friends
else
{//if we aren't gearing up for a war yet...
if (GET_TEAM(getTeam()).AI_getWarPlan((TeamTypes)iI) != NO_WARPLAN)
{//Then we would welcome some diplomatic improvements
iPlayerValue += iAttitudeChange * 3;
iPlayerValue /= 2;
}
else
{
//We are going to war, screw diplomacy
}
}
}
if (GET_TEAM(getTeam()).isVassal(GET_PLAYER((PlayerTypes)iI).getTeam()))
{//Who cares about vassals?
iPlayerValue /= 5;
}
float fPowerRatio = ((float)iTheirPower)/((float)iOurPower);
iTempValue += (int)((float)iPlayerValue * fPowerRatio);
}
}
if (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI))
{
iTempValue /= 10;
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S attitude value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
int iCorpMaintenanceMod;
if (GC.getGameINLINE().isOption(GAMEOPTION_REALISTIC_CORPORATIONS))
{
iCorpMaintenanceMod = kCivic.getRealCorporationMaintenanceModifier() + 100;
}
else
{
iCorpMaintenanceMod = kCivic.getCorporationMaintenanceModifier();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
/************************************************************************************************/
/* REVOLUTION_MOD 05/22/08 jdog5000 */
/* */
/* Revolution AI */
/************************************************************************************************/
iTempValue = ( AI_RevCalcCivicRelEffect(eCivic) );
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S REV effect value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
if( getWorldSizeMaxConscript(eCivic) > 0 && (pCapital != NULL) )
{
UnitTypes eConscript = pCapital->getConscriptUnit();
if( eConscript != NO_UNIT )
{
// Nationhood
int iCombatValue = GC.getGameINLINE().AI_combatValue(eConscript);
if( iCombatValue > 33 )
{
iTempValue = getNumCities() + ((bWarPlan) ? 30 : 10);
iTempValue *= range(GET_TEAM(getTeam()).AI_getEnemyPowerPercent(), 50, 300);
iTempValue /= 100;
iTempValue *= iCombatValue;
iTempValue /= 75;
int iWarSuccessRatio = GET_TEAM(getTeam()).AI_getWarSuccessCapitulationRatio();
if( iWarSuccessRatio < -25 )
{
iTempValue *= 75 + range(-iWarSuccessRatio, 25, 100);
iTempValue /= 100;
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S conscription value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
}
}
if (bWarPlan)
{
iTempValue = ((kCivic.getExpInBorderModifier() * getNumMilitaryUnits()) / 200);
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S exp in borders value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
iTempValue = -((kCivic.getWarWearinessModifier() * getNumCities()) / ((bWarPlan) ? 10 : 50));
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S war weariness value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
iTempValue = (kCivic.getFreeSpecialist() * getNumCities() * 12);
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S free specialist value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
int iYieldValue = 0;
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
iTempValue = 0;
//Afforess: better civic yield valuation
if (kCivic.getYieldModifier(iI) != 0)
{
int iYieldMultiplier = AI_averageYieldMultiplier((YieldTypes)iI);
int iExtraYield = 0;
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
iExtraYield += kCivic.getYieldModifier(iI) * pLoopCity->getBaseYieldRate((YieldTypes)iI) * iYieldMultiplier;
}
if (getNumCities() < GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities())
{
iExtraYield *= ((1 + GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities()) * (1 + GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities()));
iExtraYield /= ((1 + getNumCities()) * (1 + getNumCities()));
}
iTempValue += iExtraYield / 8000;
}
if (pCapital)
{
// Bureaucracy
// Benefit of having a supercity is higher than just increases in yield since will also win more
// wonder races, build things that much faster
//iTempValue += ((kCivic.getCapitalYieldModifier(iI)) / 2);
//iTemp *= pCapital->AI_yieldMultiplier((YieldTypes)iI);
//iTemp /= 100;
iTempValue += (kCivic.getCapitalYieldModifier(iI) * pCapital->getBaseYieldRate((YieldTypes)iI))/80;
}
iTempValue += ((kCivic.getTradeYieldModifier(iI) * getNumCities()) / 11);
for (iJ = 0; iJ < GC.getNumImprovementInfos(); iJ++)
{
// Free Speech
iTempValue += (AI_averageYieldMultiplier((YieldTypes)iI) * (kCivic.getImprovementYieldChanges(iJ, iI) * (getImprovementCount((ImprovementTypes)iJ) + getNumCities()/2))) / 100;
}
if (iI == YIELD_FOOD)
{
iTempValue *= 3;
}
else if (iI == YIELD_PRODUCTION)
{
iTempValue *= ((AI_avoidScience()) ? 6 : 2);
}
else if (iI == YIELD_COMMERCE)
{
iTempValue *= ((AI_avoidScience()) ? 2 : 4);
iTempValue /= 3;
}
iYieldValue += iTempValue;
}
if ( gPlayerLogLevel > 2 && iYieldValue != 0 )
{
logBBAI("Player %d (%S) Civic %S yield value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iYieldValue);
}
iValue += iYieldValue;
int iCommerceValue = 0;
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
iTempValue = 0;
//Afforess: better civic commerce valuation
if (kCivic.getCommerceModifier(iI) != 0)
{
int iExtraCommerce = 0;
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
iExtraCommerce += kCivic.getCommerceModifier(iI) * pLoopCity->getBaseCommerceRateTimes100((CommerceTypes)iI);
}
if (getNumCities() < GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities())
{
iExtraCommerce *= ((1 + GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities()) * (1 + GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities()));
iExtraCommerce /= ((1 + getNumCities()) * (1 + getNumCities()));
}
iTempValue += iExtraCommerce / (bFinancialTrouble && iI == COMMERCE_GOLD ? 4000 : 8000); // 100 * 100 to compensate for commerce rate times 100 above.
}
// Nationhood
iTempValue += (kCivic.getCapitalCommerceModifier(iI) / 2);
if (iI == COMMERCE_ESPIONAGE)
{
iTempValue *= AI_getEspionageWeight();
iTempValue /= 500;
}
// Representation
iTempValue += ((kCivic.getSpecialistExtraCommerce(iI) * getTotalPopulation()) / 15);
iTempValue *= AI_commerceWeight((CommerceTypes)iI);
if ((iI == COMMERCE_CULTURE) && bCultureVictory2)
{
iTempValue *= 2;
if (bCultureVictory3)
{
iTempValue *= 2;
}
}
iTempValue /= 100;
iCommerceValue += iTempValue;
}
if ( gPlayerLogLevel > 2 && iCommerceValue != 0 )
{
logBBAI("Player %d (%S) Civic %S commerce value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iCommerceValue);
}
iValue += iCommerceValue;
//makes the AI switch to Emancipation even if there is no unhappiness yet from not doing it
if (kCivic.getCivicPercentAnger() != 0)
{
int iNumOtherCities = GC.getGameINLINE().getNumCities() - getNumCities();
iValue += (30 * getNumCities() * getCivicPercentAnger(eCivic, true)) / kCivic.getCivicPercentAnger();
int iTargetGameTurn = 2 * getNumCities() * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getGrowthPercent();
iTargetGameTurn /= GC.getGame().countCivPlayersEverAlive();
iTargetGameTurn += GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getGrowthPercent() * 30;
iTargetGameTurn /= 100;
iTargetGameTurn = std::max(10, iTargetGameTurn);
int iElapsedTurns = GC.getGame().getElapsedGameTurns();
if (iElapsedTurns > iTargetGameTurn)
{
iTempValue = (std::min(iTargetGameTurn, iElapsedTurns - iTargetGameTurn) * (iNumOtherCities * kCivic.getCivicPercentAnger())) / (15 * iTargetGameTurn);
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S anger modifier value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
}
//Everything hereafter requires at least civic option vacuum
CivicTypes eCivicOptionCivic = getCivics((CivicOptionTypes)(kCivic.getCivicOptionType()));
//#1: Happiness
if ( (getNumCities() > 0) &&
( (kCivic.getCivicPercentAnger() != 0 && getCivicPercentAnger(eCivic, true) != 0)
|| kCivic.getCivicHappiness() != 0 || kCivic.getHappyPerMilitaryUnit() != 0 || kCivic.getLargestCityHappiness() != 0
|| (kCivic.getWarWearinessModifier() != 0 && getWarWearinessPercentAnger() != 0)
|| kCivic.isAnyBuildingHappinessChange() || kCivic.isAnyFeatureHappinessChange()
|| kCivic.getNonStateReligionHappiness() != 0
|| (kCivic.getCityLimit(getID()) > 0 && kCivic.getCityOverLimitUnhappy() > 0)
|| (kCivic.getStateReligionHappiness() != 0 && (kCivic.isStateReligion() || isStateReligion())) ) )
{
//int CvPlayerAI::AI_getHappinessWeight(int iHappy, int iExtraPop) const
//int iWorstHappy = 0;
//int iBestHappy = 0;
//int iTotalUnhappy = 0;
//int iTotalHappy = 0;
int iExtraPop = 1;
int iLoop;
CvCity* pLoopCity;
int iCount = 0;
//if (0 == iHappy)
//{
// iHappy = 1;
//}
int iHappyValue = 0;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
int iCityHappy = pLoopCity->happyLevel() - pLoopCity->unhappyLevel(iExtraPop);
iCityHappy -= std::max(0, pLoopCity->getCommerceHappiness());
int iMilitaryHappinessDefenders = 0;
if (getHappyPerMilitaryUnit() != 0 || kCivic.getHappyPerMilitaryUnit() != 0)
{
//only count happiness from units that are expected to stay inside the city. Maximum 3
iMilitaryHappinessDefenders = std::max(0, (pLoopCity->plot()->plotCount(PUF_isMilitaryHappiness, -1, -1, getID(), NO_TEAM, PUF_isCityAIType)
- pLoopCity->plot()->plotCount(PUF_isUnitAIType, UNITAI_SETTLE, -1, getID()) - ((pLoopCity->getProductionUnitAI() == UNITAI_SETTLE)? 1 : 0)));
if (iMilitaryHappinessDefenders >= 4)
iMilitaryHappinessDefenders = 3;
//else
// iMilitaryHappinessDefenders = std::min(2, iMilitaryHappinessDefenders);
if (getHappyPerMilitaryUnit() != 0)
{
iCityHappy -= pLoopCity->getMilitaryHappiness();
//iCityHappy += getHappyPerMilitaryUnit() * iMilitaryHappinessDefenders;
}
}
//eBestReligion may not be state religion but is treated as if it was
if (eBestReligion != NO_RELIGION && isStateReligion() && getStateReligion() != eBestReligion)
{
if (getStateReligion() != NO_RELIGION && pLoopCity->isHasReligion(getStateReligion()))
{
iCityHappy -= getStateReligionHappiness();
iCityHappy += getNonStateReligionHappiness();
}
if (pLoopCity->isHasReligion(eBestReligion))
{
iCityHappy -= getNonStateReligionHappiness();
iCityHappy += getStateReligionHappiness();
}
}
if (!bCivicOptionVacuum)
{
//int iCivicOptionHappy;
iCityHappy -= pLoopCity->getAdditionalHappinessByCivic(eCivicOptionCivic, false, bCivicOptionVacuum, eBestReligion, iExtraPop, 0);
}
//Happy calculation
int iHappy = pLoopCity->getAdditionalHappinessByCivic(eCivic, false, bCivicOptionVacuum, eBestReligion, iExtraPop, std::max(0, iMilitaryHappinessDefenders));
int iHappyNow = iCityHappy;
int iHappyThen = iCityHappy + iHappy;
// Factored original manipulation of the values into a sub-routine, and
// modified (in that sub-routine) to better handle negative values
iTempValue = happynessValue(iHappyThen) - happynessValue(iHappyNow);
iHappyValue += iTempValue * /* weighting */ (pLoopCity->getPopulation() + iExtraPop + 2);
//iCount++;
iCount += (pLoopCity->getPopulation() + iExtraPop + 2);
//if (iCount > 6)
//{
// break;
//}
}
//return (0 == iCount) ? 50 * iHappy : iHappyValue / iCount;
if (iCount <= 0)
{
//iValue += (getNumCities() * 12 * 50*iHappy) / 100; //always 0 because getNumCities() is 0
}
else
{
//iValue += (getNumCities() * 12 * iHappyValue) / (100 * iCount);
// line below is equal to line above
// Bracketed this way to prevent possible interger overflow issues with large negative
// values that arise when anarchism and similar are evaluated in advanced civilizations
iTempValue = getNumCities() * ((3 * iHappyValue) / (25 * iCount));
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S extra happiness value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
}
//happiness is handled in CvCity::getAdditionalHappinessByCivic
/*
iValue += (getCivicPercentAnger(eCivic, true) / 10);
iTempValue = kCivic.getHappyPerMilitaryUnit() * 3;
if (iTempValue != 0)
{
iValue += (getNumCities() * 9 * ((isCivic(eCivic)) ? -AI_getHappinessWeight(-iTempValue, 1) : AI_getHappinessWeight(iTempValue, 1) )) / 100;
}
iTempValue = kCivic.getLargestCityHappiness();
if (iTempValue != 0)
{
iValue += (12 * std::min(getNumCities(), GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities()) * ((isCivic(eCivic)) ? -AI_getHappinessWeight(-iTempValue, 1) : AI_getHappinessWeight(iTempValue, 1) )) / 100;
}
if (kCivic.getWarWearinessModifier() != 0)
{
int iAngerPercent = getWarWearinessPercentAnger();
int iPopulation = 3 + (getTotalPopulation() / std::max(1, getNumCities()));
int iTempValue = (-kCivic.getWarWearinessModifier() * iAngerPercent * iPopulation) / (GC.getPERCENT_ANGER_DIVISOR() * 100);
if (iTempValue != 0)
{
iValue += (11 * getNumCities() * ((isCivic(eCivic)) ? -AI_getHappinessWeight(-iTempValue, 1) : AI_getHappinessWeight(iTempValue, 1) )) / 100;
}
}
if (!kCivic.isStateReligion() && !isStateReligion())
{
iHighestReligionCount = 0;
}
iValue += (kCivic.getNonStateReligionHappiness() * (iTotalReligonCount - iHighestReligionCount) * 5);
iValue += (kCivic.getStateReligionHappiness() * iHighestReligionCount * 4);
*/
if (kCivic.isAnyBuildingHappinessChange())
{
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
iTempValue = kCivic.getBuildingHappinessChanges(iI);
if (iTempValue != 0)
{
// Nationalism
if( !isLimitedWonderClass((BuildingClassTypes)iI))
{
BuildingTypes eBuilding = ((BuildingTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iI)));
if ( eBuilding != NO_BUILDING &&
(GC.getBuildingInfo(eBuilding).getPrereqAndTech() == NO_TECH ||
GC.getTechInfo((TechTypes)GC.getBuildingInfo(eBuilding).getPrereqAndTech()).getEra() <= getCurrentEra()) &&
(GC.getBuildingInfo(eBuilding).getObsoleteTech() == NO_TECH ||
GET_TEAM(getTeam()).isHasTech((TechTypes)GC.getBuildingInfo(eBuilding).getObsoleteTech())))
{
//+0.5 per city that does not yet have that building
iTempValue = (iTempValue * std::min(getNumCities(), (getNumCities()*GC.getCITY_MAX_NUM_BUILDINGS() - getBuildingClassCount((BuildingClassTypes)iI))))/2;
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S nat wonder happiness change value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
}
//happiness is handled in CvCity::getAdditionalHappinessByCivic
//iValue += (iTempValue * getBuildingClassCountPlusMaking((BuildingClassTypes)iI) * 2);
}
}
}
//happiness is handled in CvCity::getAdditionalHappinessByCivic
/*
for (iI = 0; iI < GC.getNumFeatureInfos(); iI++)
{
// Environmentalism
iHappiness = kCivic.getFeatureHappinessChanges(iI);
if (iHappiness != 0)
{
iValue += (iHappiness * countCityFeatures((FeatureTypes)iI) * 5);
}
}
*/
//#1: Happinesss - end
//#2: Health
if ( (getNumCities() > 0) &&
( kCivic.isNoUnhealthyPopulation() || kCivic.isBuildingOnlyHealthy()
|| kCivic.getExtraHealth() != 0 || kCivic.isAnyBuildingHealthChange() ) )
{
//int CvPlayerAI::AI_getHealthWeight(int iHealth, int iExtraPop) const
//int iWorstHealth = 0;
//int iBestHealth = 0;
//int iTotalUnhappy = 0;
//int iTotalHealth = 0;
int iExtraPop = 1;
int iLoop;
CvCity* pLoopCity;
int iCount = 0;
//if (0 == iHealth)
//{
// iHealth = 1;
//}
int iCivicsNoUnhealthyPopulationCountNow = 0;
int iCivicsNoUnhealthyPopulationCountThen = 0;
int iCivicsBuildingOnlyHealthyCountNow = 0;
int iCivicsBuildingOnlyHealthyCountThen = 0;
if (kCivic.isNoUnhealthyPopulation())
{
iCivicsNoUnhealthyPopulationCountThen++;
}
if (kCivic.isBuildingOnlyHealthy())
{
iCivicsBuildingOnlyHealthyCountThen++;
}
for (iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
CivicTypes eTempCivic = ((paeSelectedCivics == NULL)? getCivics((CivicOptionTypes)iI) : paeSelectedCivics[iI]);
if ( eTempCivic != NO_CIVIC )
{
CvCivicInfo& kTempCivic = GC.getCivicInfo(eTempCivic);
if (kTempCivic.getCivicOptionType() == iI)
{
if (bCivicOptionVacuum)
continue;
else
{
if (kTempCivic.isNoUnhealthyPopulation())
{
iCivicsNoUnhealthyPopulationCountNow++;
}
if (kTempCivic.isBuildingOnlyHealthy())
{
iCivicsBuildingOnlyHealthyCountNow++;
}
}
}
else
{
if (kTempCivic.isNoUnhealthyPopulation())
{
iCivicsNoUnhealthyPopulationCountNow++;
iCivicsNoUnhealthyPopulationCountThen++;
}
if (kTempCivic.isBuildingOnlyHealthy())
{
iCivicsBuildingOnlyHealthyCountNow++;
iCivicsBuildingOnlyHealthyCountThen++;
}
}
}
}
int iHealthValue = 0;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
int iCityHealth = pLoopCity->goodHealth() - pLoopCity->badHealth(false, iExtraPop);
int iGoodHealthFromOtherCivics = 0;
int iBadHealthFromOtherCivics = 0;
int iGood; int iBad; int iBadBuilding;
int iGoodFromNoUnhealthyPopulation = 0;
int iGoodFromBuildingOnlyHealthy = 0;
//Health calculation (iGood encludes effects from NoUnhealthyPopulation and BuildingOnlyHealthy)
iGood = 0; iBad = 0; iBadBuilding = 0;
/*int iHealth =*/ pLoopCity->getAdditionalHealthByCivic(eCivic, iGood, iBad, iBadBuilding, false, iExtraPop, /* bCivicOptionVacuum */ true, iCivicsNoUnhealthyPopulationCountNow, iCivicsBuildingOnlyHealthyCountNow);
if (iGood == 0 && iBad == 0)
continue;
int iTempAdditionalHealthByPlayerBuildingOnlyHealthy = 0;
if (!bCivicOptionVacuum)
{
int iTempGood = 0; int iTempBad = 0; int iTempBadBuilding = 0;
iCityHealth -= pLoopCity->getAdditionalHealthByCivic(eCivicOptionCivic, iTempGood, iTempBad, iTempBadBuilding, false, iExtraPop, /* bCivicOptionVacuum */ true, 0, 0);
iTempAdditionalHealthByPlayerBuildingOnlyHealthy -= iTempBadBuilding;
}
for (iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
if (kCivic.getCivicOptionType() == iI)
continue;
CivicTypes eOtherCivic = ((paeSelectedCivics == NULL)? getCivics((CivicOptionTypes)iI) : paeSelectedCivics[iI]);
if (eOtherCivic != NULL && eOtherCivic != NO_CIVIC)
{
//iGood = 0; iBad = 0; iBadBuilding = 0;
//int iTempHealth = pLoopCity->getAdditionalHealthByCivic(eOtherCivic, iGood, iBad, iBadBuilding, false, iExtraPop, /* bCivicOptionVacuum */ true, iIgnoreNoUnhealthyPopulationCount, iIgnoreBuildingOnlyHealthyCount);
//if (iGood > 0)
//{
// iGoodHealthFromOtherCivics += iGood;
//}
//if (iBad > 0)
//{
// iBadHealthFromOtherCivics -= iBad; //negative values
//}
int iTempBadBuilding = 0;
pLoopCity->getAdditionalHealthByCivic(eOtherCivic, iGoodHealthFromOtherCivics, iBadHealthFromOtherCivics, iTempBadBuilding, false, iExtraPop, /* bCivicOptionVacuum */ true, 0, 0);
}
iBadHealthFromOtherCivics = -iBadHealthFromOtherCivics; //negative values
}
//free iCityHealth from all current civic health effects
iCityHealth -= (iGoodHealthFromOtherCivics + iBadHealthFromOtherCivics); //does not include effects from NoUnhealthyPopulation or BuildingOnlyHealthy
if (iCivicsNoUnhealthyPopulationCountNow > 0)
{
iGoodFromNoUnhealthyPopulation = pLoopCity->getAdditionalHealthByPlayerNoUnhealthyPopulation(iExtraPop, iCivicsNoUnhealthyPopulationCountNow);
iCityHealth -= iGoodFromNoUnhealthyPopulation;
}
if (iCivicsBuildingOnlyHealthyCountNow > 0)
{
iGoodFromBuildingOnlyHealthy = pLoopCity->getAdditionalHealthByPlayerBuildingOnlyHealthy(iCivicsBuildingOnlyHealthyCountNow);
iCityHealth -= iGoodFromBuildingOnlyHealthy;
}
//Health calculation
//iGood = 0; iBad = 0; iBadBuilding = 0;
//int iHealth = pLoopCity->getAdditionalHealthByCivic(eCivic, iGood, iBad, iBadBuilding, false, iExtraPop, /* bCivicOptionVacuum */ true, iIgnoreNoUnhealthyPopulationCount, iIgnoreBuildingOnlyHealthyCount);
if (kCivic.isBuildingOnlyHealthy())
{
//iHealth += iTempAdditionalHealthByPlayerBuildingOnlyHealthy;
iGood += iTempAdditionalHealthByPlayerBuildingOnlyHealthy;
}
int iBadTotal = -iBad;
int iGoodTotal = iGood;
if (kCivic.isNoUnhealthyPopulation())
{
iGoodFromNoUnhealthyPopulation = pLoopCity->getAdditionalHealthByPlayerNoUnhealthyPopulation(iExtraPop, iCivicsNoUnhealthyPopulationCountNow);
}
if (kCivic.isBuildingOnlyHealthy())
{
iGoodFromBuildingOnlyHealthy = pLoopCity->getAdditionalHealthByPlayerBuildingOnlyHealthy(iCivicsBuildingOnlyHealthyCountNow);
}
iTempAdditionalHealthByPlayerBuildingOnlyHealthy += iBadBuilding;
iGoodFromBuildingOnlyHealthy += iTempAdditionalHealthByPlayerBuildingOnlyHealthy;
iBadTotal += iBadHealthFromOtherCivics;
iGoodTotal += iGoodHealthFromOtherCivics;
if (iCivicsNoUnhealthyPopulationCountThen > 0 && !kCivic.isNoUnhealthyPopulation())
iGoodTotal += iGoodFromNoUnhealthyPopulation;
if (iCivicsBuildingOnlyHealthyCountThen > 0 && !kCivic.isBuildingOnlyHealthy())
iGoodTotal += iGoodFromBuildingOnlyHealthy;
if (iGood > 0)
{
//add new civic health effects to iHealthNow/Then
int iHealthNow = iCityHealth + iBadTotal;
int iHealthThen = iCityHealth + iGoodTotal + iBadTotal;
//Fuyu: max health 8
iHealthNow = std::min(8, iHealthNow);
iHealthThen = std::min(8, iHealthThen);
//Integration
iTempValue = (((100 * iHealthThen - 6 * iHealthThen * iHealthThen)) - (100 * iHealthNow - 6 * iHealthNow * iHealthNow));
if (iBadTotal > 0)
{
iHealthNow -= iBadTotal;
iHealthThen -= iBadTotal;
//Fuyu: max health 8
iHealthNow = std::min(8, iHealthNow);
iHealthThen = std::min(8, iHealthThen);
iTempValue = (((100 * iHealthThen - 6 * iHealthThen * iHealthThen)) - (100 * iHealthNow - 6 * iHealthNow * iHealthNow));
iTempValue /= 2;
}
//iTempValue = (iTempValue * iGood) / (iGood + iGoodHealthFromOtherCivics);
int iTempValueFromNoUnhealthyPopulation = 0;
int iTempValueFromBuildingOnlyHealthy = 0;
int iTempValueFromRest;
int iGoodFromRest = iGood;
if (kCivic.isNoUnhealthyPopulation() && iCivicsNoUnhealthyPopulationCountThen > 1)
{
iTempValueFromNoUnhealthyPopulation = (iTempValue * iGoodFromNoUnhealthyPopulation) / (iCivicsNoUnhealthyPopulationCountThen * iGoodTotal);
iGoodFromRest -= iGoodFromNoUnhealthyPopulation;
}
if (kCivic.isBuildingOnlyHealthy() && iCivicsBuildingOnlyHealthyCountThen > 1)
{
iTempValueFromBuildingOnlyHealthy = (iTempValue * iGoodFromBuildingOnlyHealthy) / (iCivicsBuildingOnlyHealthyCountThen * iGoodTotal);
iGoodFromRest -= iGoodFromBuildingOnlyHealthy;
}
iTempValueFromRest = (iTempValue * iGood) / iGoodTotal;
iTempValue = iTempValueFromNoUnhealthyPopulation + iTempValueFromBuildingOnlyHealthy + iTempValueFromRest;
iHealthValue += std::max(0, iTempValue) * /* weighting */ (pLoopCity->getPopulation() + iExtraPop + 2);
}
if (iBad > 0)
{
//add new civic health effects to iHealthNow/Then
int iHealthNow = iCityHealth;
int iHealthThen = iCityHealth + iBadTotal;
//Fuyu: max health 8
iHealthNow = std::min(8, iHealthNow);
iHealthThen = std::min(8, iHealthThen);
//Integration
int iTempValue = (((100 * iHealthThen - 6 * iHealthThen * iHealthThen)) - (100 * iHealthNow - 6 * iHealthNow * iHealthNow));
if (iGoodTotal > 0)
{
iHealthNow += iGoodTotal;
iHealthThen += iGoodTotal;
//Fuyu: max health 8
iHealthNow = std::min(8, iHealthNow);
iHealthThen = std::min(8, iHealthThen);
iTempValue = (((100 * iHealthThen - 6 * iHealthThen * iHealthThen)) - (100 * iHealthNow - 6 * iHealthNow * iHealthNow));
iTempValue /= 2;
}
iTempValue = (iTempValue * iBad) / std::max(1, (-iBadTotal)); //-iBadTotal == iBad - iBadHealthFromOtherCivics
iHealthValue += std::min(0, iTempValue) * /* weighting */ (pLoopCity->getPopulation() + iExtraPop + 2);
}
//iCount++;
iCount += (pLoopCity->getPopulation() + iExtraPop + 2);
//if (iCount > 6)
//{
// break;
//}
}
//return (0 == iCount) ? 50*iHealth : iHealthValue / iCount;
if (iCount <= 0)
{
//iValue += (getNumCities() * 6 * 50*iHealth) / 100; //always 0 because getNumCities() is 0
}
else
{
//iValue += (getNumCities() * 6 * iHealthValue) / (100 * iCount);
// line below is equal to line above
iTempValue = (getNumCities() * 3 * iHealthValue) / (50 * iCount);
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S health value value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
}
//health is handled in CvCity::getAdditionalHealthByCivic
/*
iValue += ((kCivic.isNoUnhealthyPopulation()) ? (getTotalPopulation() / 3) : 0);
iValue += ((kCivic.isBuildingOnlyHealthy()) ? (getNumCities() * 3) : 0);
if (kCivic.getExtraHealth() != 0)
{
iValue += (getNumCities() * 6 * ((isCivic(eCivic)) ? -AI_getHealthWeight(-kCivic.getExtraHealth(), 1) : AI_getHealthWeight(kCivic.getExtraHealth(), 1) )) / 100;
}
*/
iTempValue = 0;
if (kCivic.isAnyBuildingHealthChange())
{
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
iTempValue = kCivic.getBuildingHealthChanges(iI);
if (iTempValue != 0)
{
// Nationalism
if( !isLimitedWonderClass((BuildingClassTypes)iI) )
{
//+0.5 per city that does not yet have that building
iTempValue += (iTempValue * std::min(getNumCities(), (getNumCities()*GC.getCITY_MAX_NUM_BUILDINGS() - getBuildingClassCount((BuildingClassTypes)iI))))/2;
}
//health is handled in CvCity::getAdditionalHealthByCivic
//iValue += (iTempValue * getBuildingClassCountPlusMaking((BuildingClassTypes)iI) * 2);
}
}
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S building health value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
//#2: Health - end
//#3: Trade
int iTempNoForeignTradeCount = getNoForeignTradeCount();
if (!bCivicOptionVacuum)
{
iTempNoForeignTradeCount -= ((eCivicOptionCivic != NO_CIVIC && GC.getCivicInfo(eCivicOptionCivic).isNoForeignTrade()) ? 1 : 0);
}
iTempValue = 0;
//if (isNoForeignTrade())
if (iTempNoForeignTradeCount > 0)
{
if (kCivic.isNoForeignTrade())
{
iTempValue -= (iConnectedForeignCities * 3)/(1 + iTempNoForeignTradeCount);
//no additional negative value from NoForeignTrade
iTempValue += (kCivic.getTradeRoutes() * (/*std::max(0, iConnectedForeignCities - getNumCities() * 3) * 6 + */ (getNumCities() * 2)));
}
else
{
//kCivic.getTradeRoutes() should be 0
//FAssertMsg(kCivic.getTradeRoutes() == 0, "kCivic.getTradeRoutes() is supposed to be 0 if kPlayer.isNoForeignTrade() is true");
iTempValue += (kCivic.getTradeRoutes() * (std::max(0, iConnectedForeignCities - getNumCities() * 3) * 6 + (getNumCities() * 2)));
}
}
else
{
if (kCivic.isNoForeignTrade())
{
iTempValue -= iConnectedForeignCities * 3;
iTempValue += (kCivic.getTradeRoutes() * (/*std::max(0, iConnectedForeignCities - getNumCities() * 3) * 6 + */ (getNumCities() * 2)));
}
else
{
iTempValue += (kCivic.getTradeRoutes() * (std::max(0, iConnectedForeignCities - getNumCities() * 3) * 6 + (getNumCities() * 2)));
}
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S foreign trade value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
/*
iValue += (kCivic.getTradeRoutes() * (std::max(0, iConnectedForeignCities - getNumCities() * 3) * 6 + (getNumCities() * 2)));
iValue += -((kCivic.isNoForeignTrade()) ? (iConnectedForeignCities * 3) : 0);
*/
//#3: Trade - end
//#4: Corporations
iTempValue = 0;
if (kCivic.isNoCorporations() || kCivic.isNoForeignCorporations() || iCorpMaintenanceMod != 0)
{
int iHQCount = 0;
int iOwnCorpCount = 0;
int iForeignCorpCount = 0;
for (int iCorp = 0; iCorp < GC.getNumCorporationInfos(); ++iCorp)
{
if (GET_TEAM(getTeam()).hasHeadquarters((CorporationTypes)iCorp))
{
iHQCount++;
iOwnCorpCount += countCorporations((CorporationTypes)iCorp);
}
else
{
iForeignCorpCount += countCorporations((CorporationTypes)iCorp);
}
}
int iTempNoForeignCorporationsCount = 0;
iTempNoForeignCorporationsCount += getNoForeignCorporationsCount();
if (!bCivicOptionVacuum)
{
if (eCivicOptionCivic != NO_CIVIC ? GC.getCivicInfo(eCivicOptionCivic).isNoForeignCorporations() : false)
{
iTempNoForeignCorporationsCount--;
}
}
iTempNoForeignCorporationsCount = std::max(0, iTempNoForeignCorporationsCount);
int iTempNoCorporationsCount = 0;
iTempNoCorporationsCount += getNoCorporationsCount();
if (!bCivicOptionVacuum)
{
if (eCivicOptionCivic != NO_CIVIC ? GC.getCivicInfo(eCivicOptionCivic).isNoCorporations() : false)
{
iTempNoCorporationsCount--;
}
}
iTempNoCorporationsCount = std::max(0, iTempNoCorporationsCount);
int iTempCorporationValue = 0;
if (kCivic.isNoCorporations())
{
iTempCorporationValue = 0;
iTempCorporationValue -= iHQCount * (40 + 3 * getNumCities());
iTempValue += iTempCorporationValue/(1 + iTempNoCorporationsCount);
if (kCivic.isNoForeignCorporations()) // ROM Planned denies all corporations
{
iTempCorporationValue = iForeignCorpCount * 3;
iTempValue += iTempCorporationValue/(2 + iTempNoForeignCorporationsCount + iTempNoCorporationsCount);
}
else if (iTempNoForeignCorporationsCount > 0)
{
iTempCorporationValue = -iForeignCorpCount * 3;
iTempValue += iTempCorporationValue/(1 + iTempNoForeignCorporationsCount + iTempNoCorporationsCount);
}
//FAssertMsg((iCorpMaintenanceMod== 0), "NoCorporation civics are not supposed to be have a maintenace modifier");
//subtracting value from already applied kPlayer.getCorporationMaintenanceModifier()
if ((getCorporationMaintenanceModifier() + iCorpMaintenanceMod) != 0)
{
iTempCorporationValue = 0;
iTempCorporationValue -= (-(getCorporationMaintenanceModifier() + iCorpMaintenanceMod) * (iHQCount * (25 + getNumCities() * 2) + iOwnCorpCount * 7)) / 25;
iTempValue += iTempCorporationValue/(2*(1 + iTempNoCorporationsCount));
iTempCorporationValue = 0;
iTempCorporationValue -= (-(getCorporationMaintenanceModifier() + iCorpMaintenanceMod) * (iForeignCorpCount * 7)) / 25;
iTempValue += iTempCorporationValue/(2*(1 + ((kCivic.isNoForeignCorporations())? 1 : 0) + iTempNoForeignCorporationsCount + iTempNoCorporationsCount));
}
}
else if (kCivic.isNoForeignCorporations())
{
iTempCorporationValue = iForeignCorpCount * 3;
iTempValue += iTempCorporationValue/(1 + iTempNoForeignCorporationsCount + iTempNoCorporationsCount);
if ((getCorporationMaintenanceModifier() + iCorpMaintenanceMod) != 0)
{
iTempCorporationValue = 0;
iTempCorporationValue -= (-(getCorporationMaintenanceModifier() + iCorpMaintenanceMod) * (iForeignCorpCount * 7)) / 25;
iTempValue += iTempCorporationValue/(2*(1 + iTempNoForeignCorporationsCount + iTempNoCorporationsCount));
}
}
if (iCorpMaintenanceMod != 0)
{
iTempCorporationValue = 0;
iTempCorporationValue += (-iCorpMaintenanceMod * (iForeignCorpCount * 7)) / 25;
if (kCivic.isNoForeignCorporations() || kCivic.isNoCorporations() || iTempNoForeignCorporationsCount > 0 || iTempNoCorporationsCount > 0)
iTempCorporationValue /= 2;
iTempValue += iTempCorporationValue;
iTempCorporationValue = 0;
iTempCorporationValue += (-iCorpMaintenanceMod * (iHQCount * (25 + getNumCities() * 2) + iOwnCorpCount * 7)) / 25;
if (kCivic.isNoCorporations() || iTempNoCorporationsCount > 0)
iTempCorporationValue /= 2;
iTempValue += iTempCorporationValue;
}
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S corporation value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
/*
if (kCivic.isNoCorporations())
{
iValue -= countHeadquarters() * (40 + 3 * getNumCities());
}
if (kCivic.isNoForeignCorporations())
{
for (int iCorp = 0; iCorp < GC.getNumCorporationInfos(); ++iCorp)
{
if (!GET_TEAM(getTeam()).hasHeadquarters((CorporationTypes)iCorp))
{
iValue += countCorporations((CorporationTypes)iCorp) * 3;
}
}
}
if (iCorpMaintenanceMod != 0)
{
int iCorpCount = 0;
int iHQCount = 0;
for (int iCorp = 0; iCorp < GC.getNumCorporationInfos(); ++iCorp)
{
if (GET_TEAM(getTeam()).hasHeadquarters((CorporationTypes)iCorp))
{
iHQCount++;
}
iCorpCount += countCorporations((CorporationTypes)iCorp);
}
iValue += (-iCorpMaintenanceMod * (iHQCount * (25 + getNumCities() * 2) + iCorpCount * 7)) / 25;
}
*/
//#4: Corporations - end
//#5: state religion
int iStateReligionValue = 0;
int iTempStateReligionCount = 0;
iTempStateReligionCount += getStateReligionCount();
if (!bCivicOptionVacuum)
{
iTempStateReligionCount -= ((eCivicOptionCivic != NO_CIVIC && GC.getCivicInfo(eCivicOptionCivic).isStateReligion()) ? 1 : 0);
}
if (kCivic.isStateReligion() || iTempStateReligionCount > 0)
{
if (iHighestReligionCount > 0)
{
//iValue += ((kCivic.isNoNonStateReligionSpread() && !isNoNonStateReligionSpread()) ? ((getNumCities() - iHighestReligionCount) * 2) : 0);
if (kCivic.isNoNonStateReligionSpread())
{
int iTempNoNonStateReligionSpreadCount = 0;
iTempNoNonStateReligionSpreadCount += getNoNonStateReligionSpreadCount();
if (!bCivicOptionVacuum)
{
iTempNoNonStateReligionSpreadCount -= ((eCivicOptionCivic != NO_CIVIC && GC.getCivicInfo(eCivicOptionCivic).isNoNonStateReligionSpread())? 1 : 0);
}
iTempNoNonStateReligionSpreadCount = std::max(0, iTempNoNonStateReligionSpreadCount);
iStateReligionValue += ((getNumCities() - iHighestReligionCount) * 2)/std::max(1,iTempNoNonStateReligionSpreadCount);
}
//iValue += (kCivic.getStateReligionHappiness() * iHighestReligionCount * 4);
iStateReligionValue += ((kCivic.getStateReligionGreatPeopleRateModifier() * iHighestReligionCount) / 20);
iStateReligionValue += (kCivic.getStateReligionGreatPeopleRateModifier() / 4);
iStateReligionValue += ((kCivic.getStateReligionUnitProductionModifier() * iHighestReligionCount) / 4);
iStateReligionValue += ((kCivic.getStateReligionBuildingProductionModifier() * iHighestReligionCount) / 3);
iStateReligionValue += (kCivic.getStateReligionFreeExperience() * iHighestReligionCount * ((bWarPlan) ? 6 : 2));
int iTempReligionValue = 0;
if (kCivic.isStateReligion())
{
iTempReligionValue += iHighestReligionCount;
// Value civic based on current gains from having a state religion
for (int iI = 0; iI < GC.getNumVoteSourceInfos(); ++iI)
{
if (GC.getGameINLINE().isDiploVote((VoteSourceTypes)iI))
{
ReligionTypes eReligion = GC.getGameINLINE().getVoteSourceReligion((VoteSourceTypes)iI);
if( NO_RELIGION != eReligion && eReligion == eBestReligion )
{
// Are we leader of AP?
if( getTeam() == GC.getGameINLINE().getSecretaryGeneral((VoteSourceTypes)iI) )
{
iTempReligionValue += 100;
}
// Any benefits we get from AP tied to state religion?
/*
for (int iYield = 0; iYield < NUM_YIELD_TYPES; ++iYield)
{
iTempValue = iHighestReligionCount*GC.getVoteSourceInfo((VoteSourceTypes)iI).getReligionYield(iYield);
iTempValue *= AI_yieldWeight((YieldTypes)iYield);
iTempValue /= 100;
iTempReligionValue += iTempValue;
}
for (int iCommerce = 0; iCommerce < NUM_COMMERCE_TYPES; ++iCommerce)
{
iTempValue = (iHighestReligionCount*GC.getVoteSourceInfo((VoteSourceTypes)iI).getReligionCommerce(iCommerce))/2;
iTempValue *= AI_commerceWeight((CommerceTypes)iCommerce);
iTempValue = 100;
iTempReligionValue += iTempValue;
}
*/
}
}
}
// Value civic based on wonders granting state religion boosts
for (int iCommerce = 0; iCommerce < NUM_COMMERCE_TYPES; ++iCommerce)
{
iTempValue = (iHighestReligionCount * getStateReligionBuildingCommerce((CommerceTypes)iCommerce))/2;
iTempValue *= AI_commerceWeight((CommerceTypes)iCommerce);
iTempValue /= 100;
iTempReligionValue += iTempValue;
}
}
iStateReligionValue += iTempReligionValue/std::max(1,iTempStateReligionCount);
}
}
if ( gPlayerLogLevel > 2 && iStateReligionValue != 0 )
{
logBBAI("Player %d (%S) Civic %S state religion value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iStateReligionValue;
//#5: state religion - end
//#6: other possibly non-constant factors
//iValue += ((kCivic.isMilitaryFoodProduction()) ? 0 : 0);
if (kCivic.isMilitaryFoodProduction())
{
int iTempMilitaryFoodProductionCount = 0;
iTempMilitaryFoodProductionCount += getMilitaryFoodProductionCount();
if (!bCivicOptionVacuum)
{
if (eCivicOptionCivic != NO_CIVIC && GC.getCivicInfo(eCivicOptionCivic).isMilitaryFoodProduction())
{
iTempMilitaryFoodProductionCount--;
}
}
iTempMilitaryFoodProductionCount = std::max(0, iTempMilitaryFoodProductionCount);
if (AI_isDoStrategy(AI_STRATEGY_LAST_STAND)) //when is it wanted, and how much is it worth in those cases?
{
iTempValue = getNumCities()/(1 + iTempMilitaryFoodProductionCount);
}
else
{
//We want to grow so we don't want this
iTempValue = -10*getNumCities()/(1 + std::max(0,getMilitaryFoodProductionCount()));
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S military food production value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
for (iI = 0; iI < GC.getNumHurryInfos(); iI++)
{
if (kCivic.isHurry(iI))
{
iTempValue = 0;
if (GC.getHurryInfo((HurryTypes)iI).getGoldPerProduction() > 0)
{
iTempValue += ((((AI_avoidScience()) ? 50 : 25) * getNumCities()) / GC.getHurryInfo((HurryTypes)iI).getGoldPerProduction());
}
iTempValue += (GC.getHurryInfo((HurryTypes)iI).getProductionPerPopulation() * getNumCities() * (bWarPlan ? 2 : 1)) / 5;
int iTempHurryCount = 0;
iTempHurryCount += getHurryCount((HurryTypes)iI);
if (!bCivicOptionVacuum)
{
if (eCivicOptionCivic != NO_CIVIC && GC.getCivicInfo(eCivicOptionCivic).isHurry(iI))
{
iTempHurryCount--;
}
}
iTempHurryCount = std::max(0, iTempHurryCount);
iTempValue = iTempValue/(1 + iTempHurryCount);
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S hurry value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
}
iTempValue = 0;
for (iI = 0; iI < GC.getNumSpecialBuildingInfos(); iI++)
{
if (kCivic.isSpecialBuildingNotRequired(iI))
{
int iTempSpecialBuildingNotRequiredCount = 0;
iTempSpecialBuildingNotRequiredCount += getSpecialBuildingNotRequiredCount((SpecialBuildingTypes)iI);
if (!bCivicOptionVacuum)
{
if (eCivicOptionCivic != NO_CIVIC && GC.getCivicInfo(eCivicOptionCivic).isSpecialBuildingNotRequired(iI))
{
iTempSpecialBuildingNotRequiredCount--;
}
}
iTempSpecialBuildingNotRequiredCount = std::max(0, iTempSpecialBuildingNotRequiredCount);
iTempValue += ((getNumCities() / 2) + 1)/(1 + iTempSpecialBuildingNotRequiredCount); // XXX
}
}
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S special buildings value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
int iTempSpecialistValue = 0;
for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
{
iTempValue = 0;
if (kCivic.isSpecialistValid(iI))
{
iTempValue += ((getNumCities() * (bCultureVictory3 ? 10 : 1)) + 6);
}
int iTempSpecialistValidCount = 0;
iTempSpecialistValidCount += getSpecialistValidCount((SpecialistTypes)iI);
if (!bCivicOptionVacuum)
{
if (eCivicOptionCivic != NO_CIVIC && GC.getCivicInfo(eCivicOptionCivic).isSpecialistValid(iI))
{
iTempSpecialistValidCount--;
}
}
iTempSpecialistValidCount = std::max(0, iTempSpecialistValidCount);
iValue += (iTempValue / 2)/(1 + iTempSpecialistValidCount);
}
if ( gPlayerLogLevel > 2 && iTempSpecialistValue != 0 )
{
logBBAI("Player %d (%S) Civic %S specialist value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempSpecialistValue;
//#7: final modifiers
//Afforess: disabled favorite civic selection, logic is insane, makes AI choose non-optimal civics.
/*
if (!GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) && GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivic() == eCivic)
{
iTempValue = iValue;
if (!kCivic.isStateReligion() || iHighestReligionCount > 0)
{
iTempValue *= 5;
iTempValue /= 4;
iTempValue += 6 * getNumCities();
iTempValue += 20;
}
iTempValue -= iValue;
if ( gPlayerLogLevel > 2 && iTempValue != 0 )
{
logBBAI("Player %d (%S) Civic %S favorite civic value %d",
getID(),
getCivilizationDescription(0),
kCivic.getDescription(),
iTempValue);
}
iValue += iTempValue;
}
*/
if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE2) && (GC.getCivicInfo(eCivic).isNoNonStateReligionSpread()))
{
//is this really necessary, even if already running culture3/4 ?
iValue /= 10;
}
/************************************************************************************************/
/* Afforess Start 01/16/10 */
/* */
/* Cache Civic Values */
/************************************************************************************************/
m_aiCivicValueCache[eCivic + (bCivicOptionVacuum ? 0 : GC.getNumCivicInfos())] = iValue;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iValue;
}
/********************************************************************************/
/* New Civic AI END */
/********************************************************************************/
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
/************************************************************************************************/
/* REVOLUTION_MOD 05/30/08 jdog5000 */
/* */
/* Revolution AI */
/************************************************************************************************/
int CvPlayerAI::AI_RevCalcCivicRelEffect(CivicTypes eCivic) const
{
if (isBarbarian())
return 0;
if(!isAlive())
return 0;
if(getNumCities() == 0)
return 0;
int iTotalScore = 0;
if ( GC.getCivicInfo(eCivic).isStateReligion() )
{
int iRelScore = 0;
float fRelGoodMod = GC.getCivicInfo(eCivic).getRevIdxGoodReligionMod();
float fRelBadMod = GC.getCivicInfo(eCivic).getRevIdxBadReligionMod();
int iHolyCityGood = GC.getCivicInfo(eCivic).getRevIdxHolyCityGood();
int iHolyCityBad = GC.getCivicInfo(eCivic).getRevIdxHolyCityBad();
ReligionTypes eStateReligion = getStateReligion();
if( eStateReligion == NO_RELIGION )
{
eStateReligion = getLastStateReligion();
}
if( eStateReligion == NO_RELIGION )
{
eStateReligion = GET_PLAYER(getID()).AI_findHighestHasReligion();
}
if( eStateReligion == NO_RELIGION )
{
return 0;
}
CvCity * pHolyCity = GC.getGame().getHolyCity(eStateReligion);
int iLoop;
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
float fCityStateReligion = 0;
float fCityNonStateReligion = 0;
if (pLoopCity == NULL)
{
//logMsg("error pLoopCity is NULL");
}
if (pLoopCity->isHasReligion(eStateReligion))
{
fCityStateReligion += 4;
}
for ( int iI = 0; iI < GC.getNumReligionInfos(); iI++ )
{
if ((pLoopCity->isHasReligion((ReligionTypes)iI )) && !(eStateReligion == iI) )
{
if (fCityNonStateReligion <= 4 )
{
fCityNonStateReligion += 2.5;
}
else
{
fCityNonStateReligion += 1;
}
}
}
if (pLoopCity->isHolyCity())
{
if (pLoopCity->isHolyCity(eStateReligion))
{
fCityStateReligion += 5;
}
else
{
fCityNonStateReligion += 4;
}
}
int iLiberalism = GC.getInfoTypeForString("TECH_LIBERALISM");
int iSciMethod = GC.getInfoTypeForString("TECH_SCIENTIFIC_METHOD");
bool bHeathens = false;
if (!(GET_TEAM(getTeam()).isHasTech((TechTypes)iLiberalism)) && (pLoopCity->isHasReligion(eStateReligion)))
{
if (pHolyCity != NULL)
{
PlayerTypes eHolyCityOwnerID = pHolyCity->getOwner();
if (getID() == eHolyCityOwnerID)
{
fCityStateReligion += iHolyCityGood;
}
else
{
if (GET_PLAYER(eHolyCityOwnerID).getStateReligion() != eStateReligion)//heathens!
{
bHeathens = true;
}
}
}
}
int iRelBadEffect = (int)floor((fCityNonStateReligion * (1+fRelBadMod)) + .5);
int iRelGoodEffect = (int)floor((fCityStateReligion * (1+fRelGoodMod)) + .5);
if (GET_TEAM(getTeam()).getAtWarCount(true) > 0 )
{
iRelGoodEffect = (int)floor((iRelGoodEffect * 1.5) + .5);
}
int iNetCivicRelEffect = iRelBadEffect - iRelGoodEffect;
if (bHeathens)
{
iNetCivicRelEffect += iHolyCityBad;
}
if (GET_TEAM(getTeam()).isHasTech((TechTypes)iSciMethod))
{
iNetCivicRelEffect /= 3;
}
else if (GET_TEAM(getTeam()).isHasTech((TechTypes)iLiberalism))
{
iNetCivicRelEffect /= 2;
}
int iRevIdx = pLoopCity->getRevolutionIndex();
iRevIdx = std::max(iRevIdx-300,100);
float fCityReligionScore = iNetCivicRelEffect*(((float)iRevIdx)/ 600);
iRelScore += (int)(floor(fCityReligionScore));
}//end of each city loop
iRelScore *= 3;
iTotalScore -= iRelScore;
}//end of if eCivic isStateRel
if( GC.getCivicInfo(eCivic).getNonStateReligionHappiness() > 0 )
{
int iCivicScore = 0;
int iLoop;
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
int iCityScore = GC.getCivicInfo(eCivic).getNonStateReligionHappiness()*pLoopCity->getReligionCount();
int iRevIdx = pLoopCity->getRevolutionIndex();
iRevIdx = std::max(iRevIdx-300,100);
iCityScore *= iRevIdx;
iCityScore /= (pLoopCity->angryPopulation() > 0) ? 500 : 700;
iCivicScore += iCityScore;
}
iTotalScore += iCivicScore;
}
return iTotalScore;
}
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
ReligionTypes CvPlayerAI::AI_bestReligion() const
{
ReligionTypes eBestReligion;
int iValue;
int iBestValue;
int iI;
iBestValue = 0;
eBestReligion = NO_RELIGION;
ReligionTypes eFavorite = (ReligionTypes)GC.getLeaderHeadInfo(getLeaderType()).getFavoriteReligion();
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (canDoReligion((ReligionTypes)iI))
{
iValue = AI_religionValue((ReligionTypes)iI);
if (getStateReligion() == ((ReligionTypes)iI))
{
iValue *= 4;
iValue /= 3;
}
if (eFavorite == iI)
{
iValue *= 5;
iValue /= 4;
}
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestReligion = ((ReligionTypes)iI);
}
}
}
if ((NO_RELIGION == eBestReligion) || AI_isDoStrategy(AI_STRATEGY_MISSIONARY))
{
return eBestReligion;
}
int iBestCount = getHasReligionCount(eBestReligion);
int iSpreadPercent = (iBestCount * 100) / std::max(1, getNumCities());
int iPurityPercent = (iBestCount * 100) / std::max(1, countTotalHasReligion());
if (iPurityPercent < 49)
{
if (iSpreadPercent > ((eBestReligion == eFavorite) ? 65 : 75))
{
if (iPurityPercent > ((eBestReligion == eFavorite) ? 25 : 32))
{
return eBestReligion;
}
}
return NO_RELIGION;
}
return eBestReligion;
}
int CvPlayerAI::AI_religionValue(ReligionTypes eReligion) const
{
if (getHasReligionCount(eReligion) == 0)
{
return 0;
}
int iValue = GC.getGameINLINE().countReligionLevels(eReligion);
int iLoop;
CvCity* pLoopCity;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (pLoopCity->isHasReligion(eReligion))
{
iValue += pLoopCity->getPopulation();
}
}
CvCity* pHolyCity = GC.getGameINLINE().getHolyCity(eReligion);
if (pHolyCity != NULL)
{
bool bOurHolyCity = pHolyCity->getOwnerINLINE() == getID();
bool bOurTeamHolyCity = pHolyCity->getTeam() == getTeam();
if (bOurHolyCity || bOurTeamHolyCity)
{
int iCommerceCount = 0;
for (int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (pHolyCity->getNumActiveBuilding((BuildingTypes)iI) > 0)
{
for (int iJ = 0; iJ < NUM_COMMERCE_TYPES; iJ++)
{
if (GC.getBuildingInfo((BuildingTypes)iI).getGlobalReligionCommerce() == eReligion)
{
iCommerceCount += GC.getReligionInfo(eReligion).getGlobalReligionCommerce((CommerceTypes)iJ) * pHolyCity->getNumActiveBuilding((BuildingTypes)iI);
}
}
}
}
if (bOurHolyCity)
{
iValue *= (3 + iCommerceCount);
iValue /= 2;
}
else if (bOurTeamHolyCity)
{
iValue *= (4 + iCommerceCount);
iValue /= 3;
}
}
}
return iValue;
}
/************************************************************************************************/
/* REVOLUTION_MOD 05/22/08 jdog5000 */
/* */
/* Revolution AI */
/************************************************************************************************/
ReligionTypes CvPlayerAI::AI_findHighestHasReligion()
{
int iValue;
int iBestValue;
int iI;
ReligionTypes eMostReligion = NO_RELIGION;
iBestValue = 0;
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
iValue = getHasReligionCount((ReligionTypes)iI);
if (iValue > iBestValue)
{
iBestValue = iValue;
eMostReligion = (ReligionTypes)iI;
}
}
return eMostReligion;
}
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 01/07/10 jdog5000 */
/* */
/* Espionage AI */
/************************************************************************************************/
EspionageMissionTypes CvPlayerAI::AI_bestPlotEspionage(CvPlot* pSpyPlot, PlayerTypes& eTargetPlayer, CvPlot*& pPlot, int& iData) const
{
//ooookay what missions are possible
FAssert(pSpyPlot != NULL);
pPlot = NULL;
iData = -1;
EspionageMissionTypes eBestMission = NO_ESPIONAGEMISSION;
//int iBestValue = 0;
int iBestValue = 20;
/************************************************************************************************/
/* Afforess Start 06/29/10 */
/* */
/* */
/************************************************************************************************/
if (pSpyPlot->isBarbarian())
{
return eBestMission;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (pSpyPlot->isOwned())
{
if (pSpyPlot->getTeam() != getTeam())
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH 09/05/08 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original BTS code
if (!AI_isDoStrategy(AI_STRATEGY_BIG_ESPIONAGE) && (GET_TEAM(getTeam()).AI_getWarPlan(pSpyPlot->getTeam()) != NO_WARPLAN || AI_getAttitudeWeight(pSpyPlot->getOwner()) < (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) ? 50 : 1)))
*/
// Attitude weight < 50 is equivalent to < 1, < 51 is clearly what was intended
if (!AI_isDoStrategy(AI_STRATEGY_BIG_ESPIONAGE) && (GET_TEAM(getTeam()).AI_getWarPlan(pSpyPlot->getTeam()) != NO_WARPLAN || AI_getAttitudeWeight(pSpyPlot->getOwner()) < (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) ? 51 : 1)))
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
{
//Destroy Improvement.
if (pSpyPlot->getImprovementType() != NO_IMPROVEMENT)
{
for (int iMission = 0; iMission < GC.getNumEspionageMissionInfos(); ++iMission)
{
CvEspionageMissionInfo& kMissionInfo = GC.getEspionageMissionInfo((EspionageMissionTypes)iMission);
if (kMissionInfo.isDestroyImprovement())
{
int iValue = AI_espionageVal(pSpyPlot->getOwnerINLINE(), (EspionageMissionTypes)iMission, pSpyPlot, -1);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestMission = (EspionageMissionTypes)iMission;
eTargetPlayer = pSpyPlot->getOwnerINLINE();
pPlot = pSpyPlot;
iData = -1;
}
}
}
}
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
//RevolutionDCM start
//Bribe
if (pSpyPlot->plotCount(PUF_isOtherTeam, getID(), -1, NO_PLAYER, NO_TEAM, PUF_isVisible, getID()) >= 1)
{
if (pSpyPlot->plotCount(PUF_isUnitAIType, UNITAI_WORKER, -1) >= 1)
{
for (int iMission = 0; iMission < GC.getNumEspionageMissionInfos(); ++iMission)
{
CvEspionageMissionInfo& kMissionInfo = GC.getEspionageMissionInfo((EspionageMissionTypes)iMission);
if (kMissionInfo.getBuyUnitCostFactor() > 0 && GC.getDefineINT("SS_BRIBE"))
{
int iValue = AI_espionageVal(pSpyPlot->getOwnerINLINE(), (EspionageMissionTypes)iMission, pSpyPlot, -1);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestMission = (EspionageMissionTypes)iMission;
eTargetPlayer = pSpyPlot->getOwnerINLINE();
pPlot = pSpyPlot;
iData = -1;
}
}
}
}
}
}
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
/************************************************************************************************/
/* Afforess Start 06/29/10 */
/* */
/* */
/************************************************************************************************/
CvCity* pCity = pSpyPlot->getPlotCity();
if (pCity != NULL)
{
for (int iMission = 0; iMission < GC.getNumEspionageMissionInfos(); ++iMission)
{
CvEspionageMissionInfo& kMissionInfo = GC.getEspionageMissionInfo((EspionageMissionTypes)iMission);
if (kMissionInfo.isRevolt() || kMissionInfo.isDisablePower() || kMissionInfo.getWarWearinessCounter() > 0 || kMissionInfo.getBuyCityCostFactor() > 0)
{
int iValue = AI_espionageVal(pSpyPlot->getOwnerINLINE(), (EspionageMissionTypes)iMission, pSpyPlot, -1);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestMission = (EspionageMissionTypes)iMission;
eTargetPlayer = pSpyPlot->getOwnerINLINE();
pPlot = pSpyPlot;
iData = -1;
}
}
}
}
if (pCity != NULL)
{
if (GET_TEAM(getTeam()).AI_getWarPlan(pCity->getTeam()) != NO_WARPLAN)
{
for (int iMission = 0; iMission < GC.getNumEspionageMissionInfos(); ++iMission)
{
CvEspionageMissionInfo& kMissionInfo = GC.getEspionageMissionInfo((EspionageMissionTypes)iMission);
if (kMissionInfo.isNuke())
{
int iValue = AI_espionageVal(pSpyPlot->getOwnerINLINE(), (EspionageMissionTypes)iMission, pSpyPlot, -1);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestMission = (EspionageMissionTypes)iMission;
eTargetPlayer = pSpyPlot->getOwnerINLINE();
pPlot = pSpyPlot;
iData = -1;
}
}
}
}
}
if (pCity != NULL)
{
if ((pCity->plot()->countTotalCulture() / std::max(1, pCity->plot()->getCulture(getID()))) > 25)
{
for (int iMission = 0; iMission < GC.getNumEspionageMissionInfos(); ++iMission)
{
CvEspionageMissionInfo& kMissionInfo = GC.getEspionageMissionInfo((EspionageMissionTypes)iMission);
if (kMissionInfo.getCityInsertCultureAmountFactor() > 0)
{
int iValue = AI_espionageVal(pSpyPlot->getOwnerINLINE(), (EspionageMissionTypes)iMission, pSpyPlot, -1);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestMission = (EspionageMissionTypes)iMission;
eTargetPlayer = pSpyPlot->getOwnerINLINE();
pPlot = pSpyPlot;
iData = -1;
}
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (pCity != NULL)
{
//Something malicious
/************************************************************************************************/
/* UNOFFICIAL_PATCH 09/05/08 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original BTS code
if (AI_getAttitudeWeight(pSpyPlot->getOwner()) < (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) ? 50 : 1))
*/
// Attitude weight < 50 is equivalent to < 1, < 51 is clearly what was intended
if (AI_getAttitudeWeight(pSpyPlot->getOwner()) < (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) ? 51 : 1))
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
{
//Destroy Building.
if (!AI_isDoStrategy(AI_STRATEGY_BIG_ESPIONAGE))
{
for (int iMission = 0; iMission < GC.getNumEspionageMissionInfos(); ++iMission)
{
CvEspionageMissionInfo& kMissionInfo = GC.getEspionageMissionInfo((EspionageMissionTypes)iMission);
if (kMissionInfo.getDestroyBuildingCostFactor() > 0)
{
for (int iBuilding = 0; iBuilding < GC.getNumBuildingInfos(); iBuilding++)
{
BuildingTypes eBuilding = (BuildingTypes)iBuilding;
if (pCity->getNumBuilding(eBuilding) > 0)
{
int iValue = AI_espionageVal(pSpyPlot->getOwnerINLINE(), (EspionageMissionTypes)iMission, pSpyPlot, iBuilding);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestMission = (EspionageMissionTypes)iMission;
eTargetPlayer = pSpyPlot->getOwnerINLINE();
pPlot = pSpyPlot;
iData = iBuilding;
}
}
}
}
}
}
//Destroy Project
for (int iMission = 0; iMission < GC.getNumEspionageMissionInfos(); ++iMission)
{
CvEspionageMissionInfo& kMissionInfo = GC.getEspionageMissionInfo((EspionageMissionTypes)iMission);
if (kMissionInfo.getDestroyProjectCostFactor() > 0)
{
for (int iProject = 0; iProject < GC.getNumProjectInfos(); iProject++)
{
ProjectTypes eProject = (ProjectTypes)iProject;
int iValue = AI_espionageVal(pSpyPlot->getOwnerINLINE(), (EspionageMissionTypes)iMission, pSpyPlot, iProject);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestMission = (EspionageMissionTypes)iMission;
eTargetPlayer = pSpyPlot->getOwnerINLINE();
pPlot = pSpyPlot;
iData = iProject;
}
}
}
}
//General dataless city mission.
if (!AI_isDoStrategy(AI_STRATEGY_BIG_ESPIONAGE))
{
for (int iMission = 0; iMission < GC.getNumEspionageMissionInfos(); ++iMission)
{
CvEspionageMissionInfo& kMissionInfo = GC.getEspionageMissionInfo((EspionageMissionTypes)iMission);
{
if ((kMissionInfo.getCityPoisonWaterCounter() > 0) || (kMissionInfo.getDestroyProductionCostFactor() > 0)
|| (kMissionInfo.getStealTreasuryTypes() > 0))
{
int iValue = AI_espionageVal(pSpyPlot->getOwnerINLINE(), (EspionageMissionTypes)iMission, pSpyPlot, -1);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestMission = (EspionageMissionTypes)iMission;
eTargetPlayer = pSpyPlot->getOwnerINLINE();
pPlot = pSpyPlot;
iData = -1;
}
}
}
}
}
//Disruption suitable for war.
if (GET_TEAM(getTeam()).isAtWar(pSpyPlot->getTeam()))
{
for (int iMission = 0; iMission < GC.getNumEspionageMissionInfos(); ++iMission)
{
CvEspionageMissionInfo& kMissionInfo = GC.getEspionageMissionInfo((EspionageMissionTypes)iMission);
if ((kMissionInfo.getCityRevoltCounter() > 0) || (kMissionInfo.getPlayerAnarchyCounter() > 0))
{
int iValue = AI_espionageVal(pSpyPlot->getOwnerINLINE(), (EspionageMissionTypes)iMission, pSpyPlot, -1);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestMission = (EspionageMissionTypes)iMission;
eTargetPlayer = pSpyPlot->getOwnerINLINE();
pPlot = pSpyPlot;
iData = -1;
}
}
}
}
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
//RevolutionDCM start
//Assassinate
if (true)
{
for (int iMission = 0; iMission < GC.getNumEspionageMissionInfos(); ++iMission)
{
CvEspionageMissionInfo& kMissionInfo = GC.getEspionageMissionInfo((EspionageMissionTypes)iMission);
if (kMissionInfo.getDestroyUnitCostFactor() > 0 && GC.getDefineINT("SS_ASSASSINATE"))
{
SpecialistTypes theGreatSpecialistTarget = (SpecialistTypes)0;
CvCity* pCity = pSpyPlot->getPlotCity();
if (NULL != pCity)
{
//loop through all great specialist types
for (int iSpecialist = 7; iSpecialist < GC.getNumSpecialistInfos(); iSpecialist++)
{
SpecialistTypes tempSpecialist = (SpecialistTypes)0;
//does this city contain this great specialist type?
if (pCity->getFreeSpecialistCount((SpecialistTypes)iSpecialist) > 0)
{
//sort who is the most significant great specialist in the city
//prefer any custom specialist (SpecialistTypes)>13
//then great spies (SpecialistTypes)13
//then great generals (SpecialistTypes)12
//then great engineers (SpecialistTypes)11
//then great merchants (SpecialistTypes)10
//then great scientists (SpecialistTypes)9
//then great artists (SpecialistTypes)8
//then great priests (SpecialistTypes)7
tempSpecialist = (SpecialistTypes)iSpecialist;
if (tempSpecialist > theGreatSpecialistTarget)
{
theGreatSpecialistTarget = tempSpecialist;
}
}
}
}
int iValue = AI_espionageVal(pSpyPlot->getOwnerINLINE(), (EspionageMissionTypes)iMission, pSpyPlot, -1);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestMission = (EspionageMissionTypes)iMission;
eTargetPlayer = pSpyPlot->getOwnerINLINE();
pPlot = pSpyPlot;
iData = theGreatSpecialistTarget;
}
}
}
}
}
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
//TSHEEP - Counter Espionage (Why the heck don't AIs use this in vanilla?) -
//Requires either annoyance or memory of past Spy transgression
if ((AI_getAttitudeWeight(pSpyPlot->getOwner()) < (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) ? 50 : 0) ||
AI_getMemoryCount(pSpyPlot->getOwner(), MEMORY_SPY_CAUGHT) > 0) &&
GET_TEAM(getTeam()).getCounterespionageTurnsLeftAgainstTeam(GET_PLAYER(pSpyPlot->getOwner()).getTeam()) <= 0)
{
for (int iMission = 0; iMission < GC.getNumEspionageMissionInfos(); ++iMission)
{
CvEspionageMissionInfo& kMissionInfo = GC.getEspionageMissionInfo((EspionageMissionTypes)iMission);
if (kMissionInfo.getCounterespionageNumTurns() > 0)
{
int iValue = AI_espionageVal(pSpyPlot->getOwnerINLINE(), (EspionageMissionTypes)iMission, pSpyPlot, -1);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestMission = (EspionageMissionTypes)iMission;
eTargetPlayer = pSpyPlot->getOwnerINLINE();
pPlot = pSpyPlot;
iData = -1;
}
}
}
}
//TSHEEP End of Counter Espionage
//Steal Technology
for (int iMission = 0; iMission < GC.getNumEspionageMissionInfos(); ++iMission)
{
CvEspionageMissionInfo& kMissionInfo = GC.getEspionageMissionInfo((EspionageMissionTypes)iMission);
if (kMissionInfo.getBuyTechCostFactor() > 0)
{
for (int iTech = 0; iTech < GC.getNumTechInfos(); iTech++)
{
TechTypes eTech = (TechTypes)iTech;
int iValue = AI_espionageVal(pSpyPlot->getOwnerINLINE(), (EspionageMissionTypes)iMission, pSpyPlot, eTech);
iValue *= 2; //Increase AI weight of techvalues TSHEEP
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestMission = (EspionageMissionTypes)iMission;
eTargetPlayer = pSpyPlot->getOwnerINLINE();
pPlot = pSpyPlot;
iData = eTech;
}
}
}
}
}
}
}
return eBestMission;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 10/23/09 jdog5000 */
/* */
/* Espionage AI */
/************************************************************************************************/
/// \brief Value of espionage mission at this plot.
///
/// Assigns value to espionage mission against ePlayer at pPlot, where iData can provide additional information about mission.
int CvPlayerAI::AI_espionageVal(PlayerTypes eTargetPlayer, EspionageMissionTypes eMission, CvPlot* pPlot, int iData) const
{
PROFILE_FUNC();
TeamTypes eTargetTeam = GET_PLAYER(eTargetPlayer).getTeam();
if (eTargetPlayer == NO_PLAYER)
{
return 0;
}
int iCost = getEspionageMissionCost(eMission, eTargetPlayer, pPlot, iData);
if (!canDoEspionageMission(eMission, eTargetPlayer, pPlot, iData, NULL))
{
return 0;
}
bool bMalicious = (AI_getAttitudeWeight(pPlot->getOwner()) < (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) ? 51 : 1) || GET_TEAM(getTeam()).AI_getWarPlan(eTargetTeam) != NO_WARPLAN);
int iValue = 0;
if (bMalicious && GC.getEspionageMissionInfo(eMission).isDestroyImprovement())
{
if (NULL != pPlot)
{
if (pPlot->getOwnerINLINE() == eTargetPlayer)
{
ImprovementTypes eImprovement = pPlot->getImprovementType();
if (eImprovement != NO_IMPROVEMENT)
{
BonusTypes eBonus = pPlot->getNonObsoleteBonusType(GET_PLAYER(eTargetPlayer).getTeam());
if (NO_BONUS != eBonus)
{
iValue += GET_PLAYER(eTargetPlayer).AI_bonusVal(eBonus, -1);
int iTempValue = 0;
if (NULL != pPlot->getWorkingCity())
{
iTempValue += (pPlot->calculateImprovementYieldChange(eImprovement, YIELD_FOOD, pPlot->getOwnerINLINE()) * 2);
iTempValue += (pPlot->calculateImprovementYieldChange(eImprovement, YIELD_PRODUCTION, pPlot->getOwnerINLINE()) * 1);
iTempValue += (pPlot->calculateImprovementYieldChange(eImprovement, YIELD_COMMERCE, pPlot->getOwnerINLINE()) * 2);
iTempValue += GC.getImprovementInfo(eImprovement).getUpgradeTime() / 2;
iValue += iTempValue;
}
}
}
}
}
}
if (bMalicious && GC.getEspionageMissionInfo(eMission).getDestroyBuildingCostFactor() > 0)
{
if (canSpyDestroyBuilding(eTargetPlayer, (BuildingTypes)iData))
{
if (NULL != pPlot)
{
CvCity* pCity = pPlot->getPlotCity();
if (NULL != pCity)
{
if (pCity->getNumRealBuilding((BuildingTypes)iData) > 0)
{
CvBuildingInfo& kBuilding = GC.getBuildingInfo((BuildingTypes)iData);
if ((kBuilding.getProductionCost() > 1) && !isWorldWonderClass((BuildingClassTypes)kBuilding.getBuildingClassType()))
{
// BBAI TODO: Should this be based on production cost of building? Others are
/*int iEspionageFlags = 0;
iEspionageFlags |= BUILDINGFOCUS_FOOD;
iEspionageFlags |= BUILDINGFOCUS_PRODUCTION;
iEspionageFlags |= BUILDINGFOCUS_DEFENSE;
iEspionageFlags |= BUILDINGFOCUS_HAPPY;
iEspionageFlags |= BUILDINGFOCUS_HEALTHY;
iEspionageFlags |= BUILDINGFOCUS_GOLD;
iEspionageFlags |= BUILDINGFOCUS_RESEARCH;*/
iValue += pCity->AI_buildingValue((BuildingTypes)iData);
// K-Mod
iValue *= 60 + kBuilding.getProductionCost();
iValue /= 100;
// K-Mod end
}
}
}
}
}
}
if (bMalicious && GC.getEspionageMissionInfo(eMission).getDestroyProjectCostFactor() > 0)
{
if (canSpyDestroyProject(eTargetPlayer, (ProjectTypes)iData))
{
CvProjectInfo& kProject = GC.getProjectInfo((ProjectTypes)iData);
iValue += getProductionNeeded((ProjectTypes)iData) * ((kProject.getMaxTeamInstances() == 1) ? 3 : 2);
}
}
if (bMalicious && GC.getEspionageMissionInfo(eMission).getDestroyProductionCostFactor() > 0)
{
if (NULL != pPlot)
{
CvCity* pCity = pPlot->getPlotCity();
FAssert(pCity != NULL);
if (pCity != NULL)
{
int iTempValue = pCity->getProduction();
if (iTempValue > 0)
{
if (pCity->getProductionProject() != NO_PROJECT)
{
CvProjectInfo& kProject = GC.getProjectInfo(pCity->getProductionProject());
iValue += iTempValue * ((kProject.getMaxTeamInstances() == 1) ? 4 : 2);
}
else if (pCity->getProductionBuilding() != NO_BUILDING)
{
CvBuildingInfo& kBuilding = GC.getBuildingInfo(pCity->getProductionBuilding());
if (isWorldWonderClass((BuildingClassTypes)kBuilding.getBuildingClassType()))
{
iValue += 3 * iTempValue;
}
iValue += iTempValue;
}
else
{
iValue += iTempValue;
}
}
}
}
}
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start
if (bMalicious && GC.getEspionageMissionInfo(eMission).getDestroyUnitCostFactor() > 0)
{
/*
Assassination iValues competes with:
poisoning (64-768)
destroy building (2-4439)
destroy production (8-137)
revolt (45-150?)
counter espionage (104-112)
steal tech (180-17080)
*/
SpecialistTypes theGreatSpecialistTarget = (SpecialistTypes)0;
CvCity* pCity = pPlot->getPlotCity();
if (NULL != pCity)
{
for (int iSpecialist = 7; iSpecialist < GC.getNumSpecialistInfos(); iSpecialist++)
{
SpecialistTypes tempSpecialist = (SpecialistTypes)0;
if (pCity->getFreeSpecialistCount((SpecialistTypes)iSpecialist) > 0)
{
tempSpecialist = (SpecialistTypes)iSpecialist;
if (tempSpecialist > theGreatSpecialistTarget)
{
theGreatSpecialistTarget = tempSpecialist;
}
}
}
}
if (theGreatSpecialistTarget >= 7)
{
iValue += 1000;
}
}
if (bMalicious && GC.getEspionageMissionInfo(eMission).getBuyUnitCostFactor() > 0)
{
/*
Bribe iValues compete with:
destroy improvement (1-60)
*/
if (pPlot->plotCount(PUF_isOtherTeam, getID(), -1, NO_PLAYER, NO_TEAM, PUF_isVisible, getID()) >= 1)
{
if (pPlot->plotCount(PUF_isUnitAIType, UNITAI_WORKER, -1) >= 1)
{
iValue += 100;
}
}
}
//RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
if (bMalicious && GC.getEspionageMissionInfo(eMission).getStealTreasuryTypes() > 0)
{
if( pPlot != NULL && pPlot->getPlotCity() != NULL )
{
int iGoldStolen = (GET_PLAYER(eTargetPlayer).getGold() * GC.getEspionageMissionInfo(eMission).getStealTreasuryTypes()) / 100;
iGoldStolen *= pPlot->getPlotCity()->getPopulation();
iGoldStolen /= std::max(1, GET_PLAYER(eTargetPlayer).getTotalPopulation());
iValue += ((GET_PLAYER(eTargetPlayer).AI_isFinancialTrouble() || AI_isFinancialTrouble()) ? 4 : 2) * (2 * std::max(0, iGoldStolen - iCost));
}
}
if (GC.getEspionageMissionInfo(eMission).getCounterespionageNumTurns() > 0)
{
//iValue += 100 * GET_TEAM(getTeam()).AI_getAttitudeVal(GET_PLAYER(eTargetPlayer).getTeam());
// K-Mod (I didn't comment that line out, btw.)
const TeamTypes eTeam = GET_PLAYER(eTargetPlayer).getTeam();
const int iEra = getCurrentEra();
int iCounterValue = 5;
iCounterValue *= 50 * iEra*iEra + GET_TEAM(eTeam).getEspionagePointsAgainstTeam(getTeam());
iCounterValue /= std::max(1, 50 * iEra*iEra + GET_TEAM(getTeam()).getEspionagePointsAgainstTeam(eTeam));
iCounterValue *= AI_getMemoryCount(eTargetPlayer, MEMORY_SPY_CAUGHT) + 1;
iValue += iCounterValue;
//TSHEEP - Make Counterespionage matter
if (NULL != pPlot)
{
CvCity* pCity = pPlot->getPlotCity();
if (NULL != pCity)
{
iValue += std::max((100 - GET_TEAM(getTeam()).AI_getAttitudeVal(GET_PLAYER(eTargetPlayer).getTeam())) * (1 + std::max(AI_getMemoryCount(eTargetPlayer, MEMORY_SPY_CAUGHT), 0)),0);
}
}
//TSHEEP End
}
if (bMalicious && GC.getEspionageMissionInfo(eMission).getBuyCityCostFactor() > 0)
{
if (NULL != pPlot)
{
CvCity* pCity = pPlot->getPlotCity();
if (NULL != pCity)
{
iValue += AI_cityTradeVal(pCity);
/************************************************************************************************/
/* Afforess Start 06/29/10 */
/* */
/* */
/************************************************************************************************/
if (GET_PLAYER(pCity->getOwnerINLINE()).getNumCities() == 1)
{
iValue *= 3;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
}
}
if (bMalicious && GC.getEspionageMissionInfo(eMission).getCityInsertCultureAmountFactor() > 0)
{
if (NULL != pPlot)
{
CvCity* pCity = pPlot->getPlotCity();
if (NULL != pCity)
{
if (pCity->getOwner() != getID())
{
int iPlotCulture = pPlot->getCulture(getID());
int iScale = 1;
if ( iPlotCulture > MAX_INT/1000 )
{
iPlotCulture /= 1000;
iScale = 1000;
}
int iCultureAmount = GC.getEspionageMissionInfo(eMission).getCityInsertCultureAmountFactor() * pPlot->getCulture(getID());
iCultureAmount /= 100;
iCultureAmount *= iScale;
if (pCity->calculateCulturePercent(getID()) > 40)
{
iValue += iCultureAmount * 3;
}
}
}
}
}
if (bMalicious && GC.getEspionageMissionInfo(eMission).getCityPoisonWaterCounter() > 0)
{
if (NULL != pPlot)
{
CvCity* pCity = pPlot->getPlotCity();
if (NULL != pCity)
{
int iCityHealth = pCity->goodHealth() - pCity->badHealth(false, 0);
int iBaseUnhealth = GC.getEspionageMissionInfo(eMission).getCityPoisonWaterCounter();
// K-Mod: fixing some "wtf".
/*
int iAvgFoodShortage = std::max(0, iBaseUnhealth - iCityHealth) - pCity->foodDifference();
iAvgFoodShortage += std::max(0, iBaseUnhealth/2 - iCityHealth) - pCity->foodDifference();
iAvgFoodShortage /= 2;
if( iAvgFoodShortage > 0 )
{
iValue += 8 * iAvgFoodShortage * iAvgFoodShortage;
}*/
int iAvgFoodShortage = std::max(0, iBaseUnhealth - iCityHealth) - pCity->foodDifference();
iAvgFoodShortage += std::max(0, -iCityHealth) - pCity->foodDifference();
iAvgFoodShortage /= 2;
if (iAvgFoodShortage > 0)
{
iValue += 4 * iAvgFoodShortage * iBaseUnhealth;
}
}
}
}
if (bMalicious && GC.getEspionageMissionInfo(eMission).getCityUnhappinessCounter() > 0)
{
if (NULL != pPlot)
{
CvCity* pCity = pPlot->getPlotCity();
if (NULL != pCity)
{
int iCityCurAngerLevel = pCity->happyLevel() - pCity->unhappyLevel(0);
int iBaseAnger = GC.getEspionageMissionInfo(eMission).getCityUnhappinessCounter();
int iAvgUnhappy = iCityCurAngerLevel - iBaseAnger/2;
if (iAvgUnhappy < 0)
{
iValue += 14 * abs(iAvgUnhappy) * iBaseAnger;
}
}
}
}
if (bMalicious && GC.getEspionageMissionInfo(eMission).getCityRevoltCounter() > 0)
{
// Handled else where
}
if (GC.getEspionageMissionInfo(eMission).getBuyTechCostFactor() > 0)
{
if (iCost < GET_TEAM(getTeam()).getResearchLeft((TechTypes)iData) * 4 / 3)
{
int iTempValue = GET_TEAM(getTeam()).AI_techTradeVal((TechTypes)iData, GET_PLAYER(eTargetPlayer).getTeam());
if( GET_TEAM(getTeam()).getBestKnownTechScorePercent() < 85 )
{
iTempValue *= 2;
}
iValue += iTempValue;
}
}
if (bMalicious && GC.getEspionageMissionInfo(eMission).getSwitchCivicCostFactor() > 0)
{
iValue += AI_civicTradeVal((CivicTypes)iData, eTargetPlayer);
}
if (bMalicious && GC.getEspionageMissionInfo(eMission).getSwitchReligionCostFactor() > 0)
{
iValue += AI_religionTradeVal((ReligionTypes)iData, eTargetPlayer);
}
if (bMalicious && GC.getEspionageMissionInfo(eMission).getPlayerAnarchyCounter() > 0)
{
/************************************************************************************************/
/* Afforess Start 06/29/10 */
/* */
/* */
/************************************************************************************************/
iValue += GC.getEspionageMissionInfo(eMission).getPlayerAnarchyCounter() * 40;
}
if (bMalicious && GC.getEspionageMissionInfo(eMission).isRevolt())
{
if (NULL != pPlot)
{
CvCity* pCity = pPlot->getPlotCity();
if (NULL != pCity)
{
int iCurRevStatus = pCity->getRevolutionIndex();
iValue += std::max(300, 300+ (iCurRevStatus / 5));
}
}
}
if (bMalicious && GC.getEspionageMissionInfo(eMission).isNuke())
{
if (NULL != pPlot)
{
CvCity* pCity = pPlot->getPlotCity();
if (NULL != pCity)
{
int iTempValue = 1;
int iRange = 1;
iTempValue += GC.getGameINLINE().getSorenRandNum((pCity->getPopulation() + 1), "AI Nuke City Value");
iTempValue += std::max(0, pCity->getPopulation() - 10);
iTempValue += ((pCity->getPopulation() * (100 + pCity->calculateCulturePercent(pCity->getOwnerINLINE()))) / 100);
iTempValue += AI_getAttitudeVal(pCity->getOwnerINLINE()) / 3;
for (int iDX = -(iRange); iDX <= iRange; iDX++)
{
for (int iDY = -(iRange); iDY <= iRange; iDY++)
{
CvPlot* pLoopPlot = plotXY(pCity->getX_INLINE(), pCity->getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getImprovementType() != NO_IMPROVEMENT)
{
iTempValue++;
}
if (pLoopPlot->getNonObsoleteBonusType(getTeam()) != NO_BONUS)
{
iTempValue++;
}
}
}
}
if (!(pCity->isEverOwned(getID())))
{
iTempValue *= 3;
iTempValue /= 2;
}
if (!GET_TEAM(pCity->getTeam()).isAVassal())
{
iTempValue *= 2;
}
if (pCity->plot()->isVisible(getTeam(), false))
{
iTempValue += 2 * pCity->plot()->getNumVisibleUnits(getID());
}
else
{
iTempValue += 6;
}
iValue += iTempValue * 10;
}
}
}
if (bMalicious && GC.getEspionageMissionInfo(eMission).isDisablePower())
{
if (NULL != pPlot)
{
CvCity* pCity = pPlot->getPlotCity();
if (NULL != pCity)
{
int iTempValue = 0;
for (int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (pCity->getNumRealBuilding((BuildingTypes)iI) > 0)
{
if (GC.getBuildingInfo((BuildingTypes)iI).isPrereqPower())
{
iTempValue += 20;
}
for (int iJ = 0; iJ < NUM_YIELD_TYPES; iJ++)
{
iTempValue += GC.getBuildingInfo((BuildingTypes)iI).getPowerYieldModifier(iJ);
}
}
}
iValue += iTempValue;
}
}
}
if (bMalicious && GC.getEspionageMissionInfo(eMission).getWarWearinessCounter() > 0)
{
if (NULL != pPlot)
{
CvCity* pCity = pPlot->getPlotCity();
if (NULL != pCity)
{
int iCityCurAngerLevel = pCity->happyLevel() - pCity->unhappyLevel(0);
int iBaseAnger = pCity->getWarWearinessPercentAnger();
iBaseAnger *= (100 + GC.getEspionageMissionInfo(eMission).getWarWearinessCounter());
iBaseAnger /= 100;
iBaseAnger -= pCity->getWarWearinessPercentAnger();
int iAvgUnhappy = iCityCurAngerLevel - iBaseAnger/2;
if (iAvgUnhappy < 0)
{
iValue += 14 * abs(iAvgUnhappy) * iBaseAnger;
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iValue;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
int CvPlayerAI::AI_getPeaceWeight() const
{
return m_iPeaceWeight;
}
void CvPlayerAI::AI_setPeaceWeight(int iNewValue)
{
m_iPeaceWeight = iNewValue;
}
int CvPlayerAI::AI_getEspionageWeight() const
{
if (GC.getGameINLINE().isOption(GAMEOPTION_NO_ESPIONAGE))
{
return 0;
}
return m_iEspionageWeight;
}
void CvPlayerAI::AI_setEspionageWeight(int iNewValue)
{
m_iEspionageWeight = iNewValue;
}
int CvPlayerAI::AI_getAttackOddsChange() const
{
return m_iAttackOddsChange;
}
void CvPlayerAI::AI_setAttackOddsChange(int iNewValue)
{
m_iAttackOddsChange = iNewValue;
}
int CvPlayerAI::AI_getCivicTimer() const
{
return m_iCivicTimer;
}
void CvPlayerAI::AI_setCivicTimer(int iNewValue)
{
m_iCivicTimer = iNewValue;
FAssert(AI_getCivicTimer() >= 0);
}
void CvPlayerAI::AI_changeCivicTimer(int iChange)
{
AI_setCivicTimer(AI_getCivicTimer() + iChange);
}
int CvPlayerAI::AI_getReligionTimer() const
{
return m_iReligionTimer;
}
void CvPlayerAI::AI_setReligionTimer(int iNewValue)
{
m_iReligionTimer = iNewValue;
FAssert(AI_getReligionTimer() >= 0);
}
void CvPlayerAI::AI_changeReligionTimer(int iChange)
{
AI_setReligionTimer(AI_getReligionTimer() + iChange);
}
int CvPlayerAI::AI_getExtraGoldTarget() const
{
return m_iExtraGoldTarget;
}
void CvPlayerAI::AI_setExtraGoldTarget(int iNewValue)
{
m_iExtraGoldTarget = iNewValue;
}
int CvPlayerAI::AI_getNumTrainAIUnits(UnitAITypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < NUM_UNITAI_TYPES, "eIndex is expected to be within maximum bounds (invalid Index)");
return m_aiNumTrainAIUnits[eIndex];
}
void CvPlayerAI::AI_changeNumTrainAIUnits(UnitAITypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < NUM_UNITAI_TYPES, "eIndex is expected to be within maximum bounds (invalid Index)");
int iOldValue;
// Do this in a thread-safe manner
do
{
iOldValue = m_aiNumTrainAIUnits[eIndex];
} while( InterlockedCompareExchange((volatile LONG*)&m_aiNumTrainAIUnits[eIndex], iOldValue + iChange, iOldValue) != iOldValue );
//m_aiNumTrainAIUnits[eIndex] = (m_aiNumTrainAIUnits[eIndex] + iChange);
FAssert(AI_getNumTrainAIUnits(eIndex) >= 0);
}
int CvPlayerAI::AI_getNumAIUnits(UnitAITypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < NUM_UNITAI_TYPES, "eIndex is expected to be within maximum bounds (invalid Index)");
return m_aiNumAIUnits[eIndex];
}
void CvPlayerAI::AI_changeNumAIUnits(UnitAITypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < NUM_UNITAI_TYPES, "eIndex is expected to be within maximum bounds (invalid Index)");
m_aiNumAIUnits[eIndex] = (m_aiNumAIUnits[eIndex] + iChange);
FAssert(AI_getNumAIUnits(eIndex) >= 0);
}
int CvPlayerAI::AI_getSameReligionCounter(PlayerTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
return m_aiSameReligionCounter[eIndex];
}
void CvPlayerAI::AI_changeSameReligionCounter(PlayerTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
m_aiSameReligionCounter[eIndex] = (m_aiSameReligionCounter[eIndex] + iChange);
FAssert(AI_getSameReligionCounter(eIndex) >= 0);
}
int CvPlayerAI::AI_getDifferentReligionCounter(PlayerTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
return m_aiDifferentReligionCounter[eIndex];
}
void CvPlayerAI::AI_changeDifferentReligionCounter(PlayerTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
m_aiDifferentReligionCounter[eIndex] = (m_aiDifferentReligionCounter[eIndex] + iChange);
FAssert(AI_getDifferentReligionCounter(eIndex) >= 0);
}
int CvPlayerAI::AI_getFavoriteCivicCounter(PlayerTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
return m_aiFavoriteCivicCounter[eIndex];
}
void CvPlayerAI::AI_changeFavoriteCivicCounter(PlayerTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
m_aiFavoriteCivicCounter[eIndex] = (m_aiFavoriteCivicCounter[eIndex] + iChange);
FAssert(AI_getFavoriteCivicCounter(eIndex) >= 0);
}
int CvPlayerAI::AI_getBonusTradeCounter(PlayerTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
return m_aiBonusTradeCounter[eIndex];
}
void CvPlayerAI::AI_changeBonusTradeCounter(PlayerTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
m_aiBonusTradeCounter[eIndex] = (m_aiBonusTradeCounter[eIndex] + iChange);
FAssert(AI_getBonusTradeCounter(eIndex) >= 0);
}
int CvPlayerAI::AI_getPeacetimeTradeValue(PlayerTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
return m_aiPeacetimeTradeValue[eIndex];
}
void CvPlayerAI::AI_changePeacetimeTradeValue(PlayerTypes eIndex, int iChange)
{
PROFILE_FUNC();
int iI;
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
if (iChange != 0)
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 09/03/09 poyuzhe & jdog5000 */
/* */
/* Efficiency */
/************************************************************************************************/
// From Sanguo Mod Performance, ie the CAR Mod
// Attitude cache
AI_invalidateAttitudeCache(eIndex);
GET_PLAYER(eIndex).AI_invalidateAttitudeCache(getID());
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
m_aiPeacetimeTradeValue[eIndex] = (m_aiPeacetimeTradeValue[eIndex] + iChange);
FAssert(AI_getPeacetimeTradeValue(eIndex) >= 0);
FAssert(iChange > 0);
if (iChange > 0)
{
if (GET_PLAYER(eIndex).getTeam() != getTeam())
{
for (iI = 0; iI < MAX_CIV_TEAMS; iI++)
{
if (GET_TEAM((TeamTypes)iI).isAlive())
{
if (GET_TEAM((TeamTypes)iI).AI_getWorstEnemy() == getTeam())
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH 03/02/10 Sephi */
/* */
/* Bug fix */
/************************************************************************************************/
/* orig bts code
GET_TEAM((TeamTypes)iI).AI_changeEnemyPeacetimeTradeValue(GET_PLAYER(eIndex).getTeam(), iChange);
*/
//make sure that if A trades with B and A is C's worst enemy, C is only mad at B if C has met B before
//A = this
//B = eIndex
//C = (TeamTypes)iI
if (GET_TEAM((TeamTypes)iI).isHasMet(GET_PLAYER(eIndex).getTeam()))
{
GET_TEAM((TeamTypes)iI).AI_changeEnemyPeacetimeTradeValue(GET_PLAYER(eIndex).getTeam(), iChange);
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
}
}
}
}
}
}
}
int CvPlayerAI::AI_getPeacetimeGrantValue(PlayerTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
return m_aiPeacetimeGrantValue[eIndex];
}
void CvPlayerAI::AI_changePeacetimeGrantValue(PlayerTypes eIndex, int iChange)
{
PROFILE_FUNC();
int iI;
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
if (iChange != 0)
{
m_aiPeacetimeGrantValue[eIndex] = (m_aiPeacetimeGrantValue[eIndex] + iChange);
FAssert(AI_getPeacetimeGrantValue(eIndex) >= 0);
FAssert(iChange > 0);
if (iChange > 0)
{
if (GET_PLAYER(eIndex).getTeam() != getTeam())
{
for (iI = 0; iI < MAX_CIV_TEAMS; iI++)
{
if (GET_TEAM((TeamTypes)iI).isAlive())
{
if (GET_TEAM((TeamTypes)iI).AI_getWorstEnemy() == getTeam())
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH 03/02/10 Sephi */
/* */
/* Bug fix */
/************************************************************************************************/
/* orig bts code
GET_TEAM((TeamTypes)iI).AI_changeEnemyPeacetimeGrantValue(GET_PLAYER(eIndex).getTeam(), iChange);
*/
//make sure that if A trades with B and A is C's worst enemy, C is only mad at B if C has met B before
//A = this
//B = eIndex
//C = (TeamTypes)iI
if (GET_TEAM((TeamTypes)iI).isHasMet(GET_PLAYER(eIndex).getTeam()))
{
GET_TEAM((TeamTypes)iI).AI_changeEnemyPeacetimeGrantValue(GET_PLAYER(eIndex).getTeam(), iChange);
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
}
}
}
}
}
}
}
int CvPlayerAI::AI_getGoldTradedTo(PlayerTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
return m_aiGoldTradedTo[eIndex];
}
void CvPlayerAI::AI_changeGoldTradedTo(PlayerTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
m_aiGoldTradedTo[eIndex] = (m_aiGoldTradedTo[eIndex] + iChange);
FAssert(AI_getGoldTradedTo(eIndex) >= 0);
}
int CvPlayerAI::AI_getAttitudeExtra(PlayerTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
return m_aiAttitudeExtra[eIndex];
}
void CvPlayerAI::AI_setAttitudeExtra(PlayerTypes eIndex, int iNewValue)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 09/03/09 poyuzhe & jdog5000 */
/* */
/* Efficiency */
/************************************************************************************************/
// From Sanguo Mod Performance, ie the CAR Mod
// Attitude cache
if (m_aiAttitudeExtra[eIndex] != iNewValue)
{
GET_PLAYER(getID()).AI_invalidateAttitudeCache(eIndex);
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
m_aiAttitudeExtra[eIndex] = iNewValue;
}
void CvPlayerAI::AI_changeAttitudeExtra(PlayerTypes eIndex, int iChange)
{
AI_setAttitudeExtra(eIndex, (AI_getAttitudeExtra(eIndex) + iChange));
}
bool CvPlayerAI::AI_isFirstContact(PlayerTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
return m_abFirstContact[eIndex];
}
void CvPlayerAI::AI_setFirstContact(PlayerTypes eIndex, bool bNewValue)
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex is expected to be within maximum bounds (invalid Index)");
m_abFirstContact[eIndex] = bNewValue;
}
int CvPlayerAI::AI_getContactTimer(PlayerTypes eIndex1, ContactTypes eIndex2) const
{
FAssertMsg(eIndex1 >= 0, "eIndex1 is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex1 < MAX_PLAYERS, "eIndex1 is expected to be within maximum bounds (invalid Index)");
FAssertMsg(eIndex2 >= 0, "eIndex2 is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex2 < NUM_CONTACT_TYPES, "eIndex2 is expected to be within maximum bounds (invalid Index)");
return m_aaiContactTimer[eIndex1][eIndex2];
}
void CvPlayerAI::AI_changeContactTimer(PlayerTypes eIndex1, ContactTypes eIndex2, int iChange)
{
FAssertMsg(eIndex1 >= 0, "eIndex1 is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex1 < MAX_PLAYERS, "eIndex1 is expected to be within maximum bounds (invalid Index)");
FAssertMsg(eIndex2 >= 0, "eIndex2 is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex2 < NUM_CONTACT_TYPES, "eIndex2 is expected to be within maximum bounds (invalid Index)");
/************************************************************************************************/
/* Afforess Start 09/15/10 */
/* */
/* AI's trade with AI's much more often */
/************************************************************************************************/
if (GC.getGameINLINE().isOption(GAMEOPTION_RUTHLESS_AI) && eIndex1 != NO_PLAYER && !GET_PLAYER(eIndex1).isHuman() && iChange > 0)
{
m_aaiContactTimer[eIndex1][eIndex2] = (AI_getContactTimer(eIndex1, eIndex2) + (iChange / 3));
}
else
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
m_aaiContactTimer[eIndex1][eIndex2] = (AI_getContactTimer(eIndex1, eIndex2) + iChange);
FAssert(AI_getContactTimer(eIndex1, eIndex2) >= 0);
}
int CvPlayerAI::AI_getMemoryCount(PlayerTypes eIndex1, MemoryTypes eIndex2) const
{
FAssertMsg(eIndex1 >= 0, "eIndex1 is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex1 < MAX_PLAYERS, "eIndex1 is expected to be within maximum bounds (invalid Index)");
FAssertMsg(eIndex2 >= 0, "eIndex2 is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex2 < NUM_MEMORY_TYPES, "eIndex2 is expected to be within maximum bounds (invalid Index)");
return m_aaiMemoryCount[eIndex1][eIndex2];
}
void CvPlayerAI::AI_changeMemoryCount(PlayerTypes eIndex1, MemoryTypes eIndex2, int iChange)
{
FAssertMsg(eIndex1 >= 0, "eIndex1 is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex1 < MAX_PLAYERS, "eIndex1 is expected to be within maximum bounds (invalid Index)");
FAssertMsg(eIndex2 >= 0, "eIndex2 is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex2 < NUM_MEMORY_TYPES, "eIndex2 is expected to be within maximum bounds (invalid Index)");
m_aaiMemoryCount[eIndex1][eIndex2] += iChange;
// BUG - Update Attitude Icons - start
if (eIndex1 == GC.getGameINLINE().getActivePlayer())
{
gDLL->getInterfaceIFace()->setDirty(Score_DIRTY_BIT, true);
}
// BUG - Update Attitude Icons - end
FAssert(AI_getMemoryCount(eIndex1, eIndex2) >= 0);
}
int CvPlayerAI::AI_calculateGoldenAgeValue() const
{
int iValue;
int iTempValue;
int iI;
iValue = 0;
for (iI = 0; iI < NUM_YIELD_TYPES; ++iI)
{
iTempValue = (GC.getYieldInfo((YieldTypes)iI).getGoldenAgeYield() * AI_yieldWeight((YieldTypes)iI));
iTempValue /= std::max(1, (1 + GC.getYieldInfo((YieldTypes)iI).getGoldenAgeYieldThreshold()));
iValue += iTempValue;
}
iValue *= getTotalPopulation();
iValue *= GC.getGameINLINE().goldenAgeLength();
iValue /= 100;
/************************************************************************************************/
/* Afforess Start 02/033/10 */
/* */
/* */
/************************************************************************************************/
//Golden Ages Reduce Revolutions
if (!GC.getGameINLINE().isOption(GAMEOPTION_NO_REVOLUTION))
{
int iLoop;
CvCity* pLoopCity;
int iNationalRevIndex = 0;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
iNationalRevIndex += pLoopCity->getRevolutionIndex();
}
iNationalRevIndex /= std::max(1, getNumCities());
iValue *= std::max(1, iNationalRevIndex / 500);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iValue;
}
// Protected Functions...
void CvPlayerAI::AI_doCounter()
{
PROFILE_FUNC();
int iBonusImports;
int iI, iJ;
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (GET_PLAYER((PlayerTypes)iI).getTeam() != getTeam())
{
if (GET_TEAM(getTeam()).isHasMet(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
if ((getStateReligion() != NO_RELIGION) &&
(getStateReligion() == GET_PLAYER((PlayerTypes)iI).getStateReligion()))
{
AI_changeSameReligionCounter(((PlayerTypes)iI), 1);
}
else
{
if (AI_getSameReligionCounter((PlayerTypes)iI) > 0)
{
AI_changeSameReligionCounter(((PlayerTypes)iI), -1);
}
}
if ((getStateReligion() != NO_RELIGION) &&
(GET_PLAYER((PlayerTypes)iI).getStateReligion() != NO_RELIGION) &&
(getStateReligion() != GET_PLAYER((PlayerTypes)iI).getStateReligion()))
{
AI_changeDifferentReligionCounter(((PlayerTypes)iI), 1);
}
else
{
if (AI_getDifferentReligionCounter((PlayerTypes)iI) > 0)
{
AI_changeDifferentReligionCounter(((PlayerTypes)iI), -1);
}
}
if (GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivic() != NO_CIVIC)
{
if (isCivic((CivicTypes)(GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivic())) &&
GET_PLAYER((PlayerTypes)iI).isCivic((CivicTypes)(GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivic())))
{
AI_changeFavoriteCivicCounter(((PlayerTypes)iI), 1);
}
else
{
if (AI_getFavoriteCivicCounter((PlayerTypes)iI) > 0)
{
AI_changeFavoriteCivicCounter(((PlayerTypes)iI), -1);
}
}
}
iBonusImports = getNumTradeBonusImports((PlayerTypes)iI);
if (iBonusImports > 0)
{
AI_changeBonusTradeCounter(((PlayerTypes)iI), iBonusImports);
}
else
{
AI_changeBonusTradeCounter(((PlayerTypes)iI), -(std::min(AI_getBonusTradeCounter((PlayerTypes)iI), ((GET_PLAYER((PlayerTypes)iI).getNumCities() / 4) + 1))));
}
}
}
}
}
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
for (iJ = 0; iJ < NUM_CONTACT_TYPES; iJ++)
{
if (AI_getContactTimer(((PlayerTypes)iI), ((ContactTypes)iJ)) > 0)
{
AI_changeContactTimer(((PlayerTypes)iI), ((ContactTypes)iJ), -1);
}
}
}
}
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
for (iJ = 0; iJ < NUM_MEMORY_TYPES; iJ++)
{
if (AI_getMemoryCount(((PlayerTypes)iI), ((MemoryTypes)iJ)) > 0)
{
if (GC.getLeaderHeadInfo(getPersonalityType()).getMemoryDecayRand(iJ) > 0)
{
/************************************************************************************************/
/* Afforess Start 04/26/14 */
/* */
/* Ruthless AI: Easier for the AI to forget past wrongs */
/************************************************************************************************/
//AI attitude is designed to make AI players feel human, but it makes them weak
//A Perfect AI treats enemies and friends alike, both are obstacles to victory
int iRand = GC.getLeaderHeadInfo(getPersonalityType()).getMemoryDecayRand(iJ);
iRand *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getGrowthPercent(); //scaling memory decay for gamespeed
iRand /= 100;
if (GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_REALISTIC_DIPLOMACY))
{
iRand /= 3;
//iRand /= AI_getMemoryCount(((PlayerTypes)iI), ((MemoryTypes)iJ)); 45° removed because iRand was getting too small
}
if (GC.getGameINLINE().isOption(GAMEOPTION_RUTHLESS_AI))
{
iRand /= 2;
}
iRand = std::max(4, iRand);
if (GC.getGameINLINE().getSorenRandNum(iRand, "Memory Decay") == 0)
{
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
AI_changeMemoryCount(((PlayerTypes)iI), ((MemoryTypes)iJ), -1);
}
}
}
}
}
}
}
void CvPlayerAI::AI_doMilitary()
{
PROFILE_FUNC();
// Afforess - add multiple passes
if (AI_isFinancialTrouble())
{
if (GET_TEAM(getTeam()).getAnyWarPlanCount(true) == 0)
{
for (int iPass = 0; iPass < 4; iPass++)
{
int iFundedPercent = AI_costAsPercentIncome();
int iSafePercent = AI_safeCostAsPercentIncome();
int iSafeBuffer = (1 + iPass) * 5; // this prevents the AI from disbanding their elite units unless the financial trouble is very severe
while ((iFundedPercent < (iSafePercent - iSafeBuffer)) && (calculateUnitCost() > 0))
{
int iExperienceThreshold;
switch (iPass)
{
case 0: iExperienceThreshold = 1; break;
case 1: iExperienceThreshold = 3; break;
case 2: iExperienceThreshold = 5; break;
case 3: iExperienceThreshold = -1; break;
}
if (!AI_disbandUnit(iExperienceThreshold, false))
{
break;
}
//Recalculate funding
iFundedPercent = AI_costAsPercentIncome();
}
}
}
}
AI_setAttackOddsChange(GC.getLeaderHeadInfo(getPersonalityType()).getBaseAttackOddsChange() +
GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getAttackOddsChangeRand(), "AI Attack Odds Change #1") +
GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getAttackOddsChangeRand(), "AI Attack Odds Change #2"));
}
void CvPlayerAI::AI_doResearch()
{
PROFILE_FUNC();
FAssertMsg(!isHuman(), "isHuman did not return false as expected");
if (getCurrentResearch() == NO_TECH)
{
AI_chooseResearch();
AI_forceUpdateStrategies(); //to account for current research.
}
}
void CvPlayerAI::AI_doCommerce()
{
PROFILE_FUNC();
CvCity* pLoopCity;
int iIdealPercent;
int iGoldTarget;
int iLoop;
FAssertMsg(!isHuman(), "isHuman did not return false as expected");
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 07/20/09 jdog5000 */
/* */
/* Barbarian AI, efficiency */
/************************************************************************************************/
if( isBarbarian() || getAnarchyTurns() > 0)
{
return;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
iGoldTarget = AI_goldTarget();
int iTargetTurns = 4 * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getResearchPercent();
iTargetTurns /= 100;
iTargetTurns = std::max(3, iTargetTurns);
if (isCommerceFlexible(COMMERCE_RESEARCH) && !AI_avoidScience())
{
// set research rate to 100%
setCommercePercent(COMMERCE_RESEARCH, 100);
// if the gold rate is under 0 at 90% research
int iGoldRate = calculateGoldRate();
if (iGoldRate < 0)
{
TechTypes eCurrentResearch = getCurrentResearch();
if (eCurrentResearch != NO_TECH)
{
int iResearchTurnsLeft = getResearchTurnsLeft(eCurrentResearch, true);
// if we can finish the current research without running out of gold, let us spend 2/3rds of our gold
if (getGold() >= iResearchTurnsLeft * iGoldRate)
{
iGoldTarget /= 3;
}
}
}
}
bool bReset = false;
if (isCommerceFlexible(COMMERCE_CULTURE))
{
if (getCommercePercent(COMMERCE_CULTURE) > 0)
{
setCommercePercent(COMMERCE_CULTURE, 0);
bReset = true;
}
}
if (isCommerceFlexible(COMMERCE_ESPIONAGE))
{
/********************************************************************************/
/* BETTER_BTS_AI_MOD 9/6/08 jdog5000 */
/* */
/* Espionage AI */
/********************************************************************************/
/* original BTS code
if (getCommercePercent(COMMERCE_ESPIONAGE) > 0)
{
setCommercePercent(COMMERCE_ESPIONAGE, 0);
for (int iTeam = 0; iTeam < MAX_CIV_TEAMS; ++iTeam)
{
setEspionageSpendingWeightAgainstTeam((TeamTypes)iTeam, 0);
}
bReset = true;
}
*/
// Reset espionage spending always
for (int iTeam = 0; iTeam < MAX_CIV_TEAMS; ++iTeam)
{
setEspionageSpendingWeightAgainstTeam((TeamTypes)iTeam, 0);
}
if (getCommercePercent(COMMERCE_ESPIONAGE) > 0)
{
setCommercePercent(COMMERCE_ESPIONAGE, 0);
bReset = true;
}
/********************************************************************************/
/* BETTER_BTS_AI_MOD END */
/********************************************************************************/
}
if (bReset)
{
AI_assignWorkingPlots();
}
bool bFirstTech = AI_isFirstTech(getCurrentResearch());
if (isCommerceFlexible(COMMERCE_CULTURE))
{
if (getNumCities() > 0)
{
iIdealPercent = 0;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (pLoopCity->getCommerceHappinessPer(COMMERCE_CULTURE) > 0)
{
iIdealPercent += ((pLoopCity->angryPopulation() * 100) / pLoopCity->getCommerceHappinessPer(COMMERCE_CULTURE));
}
}
iIdealPercent /= getNumCities();
iIdealPercent -= (iIdealPercent % GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS"));
iIdealPercent = std::min(iIdealPercent, 20);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/08/10 jdog5000 */
/* */
/* Victory Strategy AI */
/************************************************************************************************/
if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE4))
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
iIdealPercent = 100;
}
setCommercePercent(COMMERCE_CULTURE, iIdealPercent);
}
}
if (isCommerceFlexible(COMMERCE_RESEARCH))
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/08/10 jdog5000 */
/* */
/* Victory Strategy AI */
/************************************************************************************************/
if ((isNoResearchAvailable() || AI_isDoVictoryStrategy(AI_VICTORY_CULTURE4)) && !bFirstTech)
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
setCommercePercent(COMMERCE_RESEARCH, 0);
}
else
{
while (calculateGoldRate() > 0)
{
changeCommercePercent(COMMERCE_RESEARCH, GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS"));
if (getCommercePercent(COMMERCE_RESEARCH) == 100)
{
break;
}
}
if (getGold() + iTargetTurns * calculateGoldRate() < iGoldTarget)
{
while (getGold() + iTargetTurns * calculateGoldRate() <= iGoldTarget)
{
changeCommercePercent(COMMERCE_RESEARCH, -(GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS")));
if ((getCommercePercent(COMMERCE_RESEARCH) == 0))
{
break;
}
}
}
else
{
if (AI_avoidScience())
{
changeCommercePercent(COMMERCE_RESEARCH, -(GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS")));
}
}
if ((GET_TEAM(getTeam()).getChosenWarCount(true) > 0) || (GET_TEAM(getTeam()).getWarPlanCount(WARPLAN_ATTACKED_RECENT, true) > 0))
{
changeCommercePercent(COMMERCE_RESEARCH, -(GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS")));
}
if ((getCommercePercent(COMMERCE_RESEARCH) == 0) && (calculateGoldRate() > 0))
{
setCommercePercent(COMMERCE_RESEARCH, GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS"));
}
}
}
if (isCommerceFlexible(COMMERCE_ESPIONAGE) && !bFirstTech)
{
/********************************************************************************/
/* BETTER_BTS_AI_MOD 9/7/08 jdog5000 */
/* */
/* Espionage AI */
/********************************************************************************/
// original BTS code
/*
int iEspionageTargetRate = 0;
//TSHEEP Additional variables for targetting tech leader
int iTechTeam = MAX_CIV_TEAMS;
int iTechScore = 0;
int iTechHighScore = 0;
//TSHEEP End
for (int iTeam = 0; iTeam < MAX_CIV_TEAMS; ++iTeam)
{
CvTeam& kLoopTeam = GET_TEAM((TeamTypes)iTeam);
if (kLoopTeam.isAlive() && iTeam != getTeam() && !kLoopTeam.isVassal(getTeam()) && !GET_TEAM(getTeam()).isVassal((TeamTypes)iTeam))
{
int iTarget = (kLoopTeam.getEspionagePointsAgainstTeam(getTeam()) - GET_TEAM(getTeam()).getEspionagePointsAgainstTeam((TeamTypes)iTeam)) / 8;
iTarget -= GET_TEAM(getTeam()).AI_getAttitudeVal((TeamTypes)iTeam);
if (iTarget > 0)
{
iEspionageTargetRate += iTarget;
changeEspionageSpendingWeightAgainstTeam((TeamTypes)iTeam, iTarget);
}
}
}
*/
int iEspionageTargetRate = 0;
int* piTarget = new int[MAX_CIV_TEAMS];
int* piWeight = new int[MAX_CIV_TEAMS];
for (int iTeam = 0; iTeam < MAX_CIV_TEAMS; ++iTeam)
{
piTarget[iTeam] = 0;
piWeight[iTeam] = 0;
CvTeam& kLoopTeam = GET_TEAM((TeamTypes)iTeam);
if (kLoopTeam.isAlive() && iTeam != getTeam() && !kLoopTeam.isVassal(getTeam()) && !GET_TEAM(getTeam()).isVassal((TeamTypes)iTeam))
{
if( GET_TEAM(getTeam()).isHasMet((TeamTypes)iTeam) )
{
int iTheirEspPoints = kLoopTeam.getEspionagePointsAgainstTeam(getTeam());
int iOurEspPoints = GET_TEAM(getTeam()).getEspionagePointsAgainstTeam((TeamTypes)iTeam);
int iDesiredMissionPoints = 0;
int iDesiredEspPoints = 0;
piWeight[iTeam] = 10;
int iRateDivisor = 12;
if( GET_TEAM(getTeam()).AI_getWarPlan((TeamTypes)iTeam) != NO_WARPLAN )
{
iTheirEspPoints *= 3;
iTheirEspPoints /= 2;
for (int iMission = 0; iMission < GC.getNumEspionageMissionInfos(); ++iMission)
{
CvEspionageMissionInfo& kMissionInfo = GC.getEspionageMissionInfo((EspionageMissionTypes)iMission);
if( kMissionInfo.isPassive() )
{
if( kMissionInfo.isSeeDemographics() || kMissionInfo.isSeeResearch() )
{
int iMissionCost = (11*getEspionageMissionCost((EspionageMissionTypes)iMission, GET_TEAM((TeamTypes)iTeam).getLeaderID(), NULL, -1, NULL))/10;
if( iDesiredMissionPoints < iMissionCost )
{
iDesiredMissionPoints = iMissionCost;
}
}
}
}
iRateDivisor = 10;
piWeight[iTeam] = 20;
if( GET_TEAM(getTeam()).AI_hasCitiesInPrimaryArea((TeamTypes)iTeam) )
{
piWeight[iTeam] = 30;
iRateDivisor = 8;
}
}
else
{
int iAttitude = range(GET_TEAM(getTeam()).AI_getAttitudeVal((TeamTypes)iTeam), -12, 12);
iTheirEspPoints -= (iTheirEspPoints*iAttitude)/(2*12);
if( iAttitude <= -3 )
{
for (int iMission = 0; iMission < GC.getNumEspionageMissionInfos(); ++iMission)
{
CvEspionageMissionInfo& kMissionInfo = GC.getEspionageMissionInfo((EspionageMissionTypes)iMission);
if( kMissionInfo.isPassive() )
{
if( kMissionInfo.isSeeDemographics() || kMissionInfo.isSeeResearch() )
{
int iMissionCost = (11*getEspionageMissionCost((EspionageMissionTypes)iMission, GET_TEAM((TeamTypes)iTeam).getLeaderID(), NULL, -1, NULL))/10;
if( iDesiredMissionPoints < iMissionCost )
{
iDesiredMissionPoints = iMissionCost;
}
}
}
}
}
else if( iAttitude < 3 )
{
for (int iMission = 0; iMission < GC.getNumEspionageMissionInfos(); ++iMission)
{
CvEspionageMissionInfo& kMissionInfo = GC.getEspionageMissionInfo((EspionageMissionTypes)iMission);
if( kMissionInfo.isPassive() )
{
if( kMissionInfo.isSeeDemographics() )
{
int iMissionCost = (11*getEspionageMissionCost((EspionageMissionTypes)iMission, GET_TEAM((TeamTypes)iTeam).getLeaderID(), NULL, -1, NULL))/10;
if( iDesiredMissionPoints < iMissionCost )
{
iDesiredMissionPoints = iMissionCost;
}
}
}
}
}
iRateDivisor += (iAttitude/5);
piWeight[iTeam] -= (iAttitude/2);
}
iDesiredEspPoints = std::max(iTheirEspPoints,iDesiredMissionPoints);
piTarget[iTeam] = (iDesiredEspPoints - iOurEspPoints)/std::max(6,iRateDivisor);
if( piTarget[iTeam] > 0 )
{
iEspionageTargetRate += piTarget[iTeam];
}
}
}
}
for (int iTeam = 0; iTeam < MAX_CIV_TEAMS; ++iTeam)
{
if( piTarget[iTeam] > 0 )
{
piWeight[iTeam] += (150*piTarget[iTeam])/std::max(4,iEspionageTargetRate);
}
else if( piTarget[iTeam] < 0 )
{
piWeight[iTeam] += 2*piTarget[iTeam];
}
setEspionageSpendingWeightAgainstTeam((TeamTypes)iTeam, std::max(0,piWeight[iTeam]));
}
SAFE_DELETE_ARRAY(piTarget);
SAFE_DELETE_ARRAY(piWeight);
/********************************************************************************/
/* BETTER_BTS_AI_MOD END */
/********************************************************************************/
//if economy is weak, neglect espionage spending.
//instead invest hammers into espionage via spies/builds
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/08/10 jdog5000 */
/* */
/* Victory Strategy AI */
/************************************************************************************************/
if (AI_isFinancialTrouble() || AI_isDoVictoryStrategy(AI_VICTORY_CULTURE3))
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
//can still get trickle espionage income
iEspionageTargetRate = 0;
}
else
{
iEspionageTargetRate *= (110 - getCommercePercent(COMMERCE_GOLD) * 2);
iEspionageTargetRate /= 110;
iEspionageTargetRate *= GC.getLeaderHeadInfo(getLeaderType()).getEspionageWeight();
iEspionageTargetRate /= 100;
int iInitialResearchPercent = getCommercePercent(COMMERCE_RESEARCH);
/************************************************************************************************/
/* Afforess Start 06/28/10 */
/* */
/* */
/************************************************************************************************/
/*
while (getCommerceRate(COMMERCE_ESPIONAGE) < iEspionageTargetRate && getCommercePercent(COMMERCE_ESPIONAGE) < 20)
*/
int iMaxEspionage = AI_isFinancialTrouble() ? 0 : std::max(5, (5 - GC.getGameINLINE().getPlayerRank(getID())) * 5);
while (getCommerceRate(COMMERCE_ESPIONAGE) < iEspionageTargetRate && getCommercePercent(COMMERCE_ESPIONAGE) < iMaxEspionage)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
changeCommercePercent(COMMERCE_RESEARCH, -GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS"));
changeCommercePercent(COMMERCE_ESPIONAGE, GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS"));
if (getGold() + iTargetTurns * calculateGoldRate() < iGoldTarget)
{
break;
}
if (!AI_avoidScience() && !isNoResearchAvailable())
{
// if (2 * getCommercePercent(COMMERCE_RESEARCH) < iInitialResearchPercent)
// {
// break;
// }
if (getCommercePercent(COMMERCE_RESEARCH) * 2 <= (getCommercePercent(COMMERCE_ESPIONAGE) + GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS")) * 3)
{
break;
}
}
}
}
}
if (!bFirstTech && (getGold() < iGoldTarget) && (getCommercePercent(COMMERCE_RESEARCH) > 40))
{
bool bHurryGold = false;
for (int iHurry = 0; iHurry < GC.getNumHurryInfos(); iHurry++)
{
if ((GC.getHurryInfo((HurryTypes)iHurry).getGoldPerProduction() > 0) && canHurry((HurryTypes)iHurry))
{
bHurryGold = true;
break;
}
}
if (bHurryGold)
{
if (getCommercePercent(COMMERCE_ESPIONAGE) > 0)
{
changeCommercePercent(COMMERCE_ESPIONAGE, -GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS"));
}
else
{
changeCommercePercent(COMMERCE_RESEARCH, -GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS"));
}
//changeCommercePercent(COMMERCE_GOLD, GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS"));
}
}
/************************************************************************************************/
/* Afforess Start 01/14/10 */
/* */
/* Taxation Anger Check */
/************************************************************************************************/
int iMaxTaxRate = 100;
if (AI_avoidIncreasingTaxes())
{
iMaxTaxRate = getLastTurnTaxRate() + 10;
//if we round down instead, abuse our extra knowledge...
if (GC.getDefineINT("TAXATION_ANGER_ROUND_DOWN"))
iMaxTaxRate += 10;
}
//This allows the AI still to adjust their taxes up by at least 10% each turn, so they have some leeway.
while ((iMaxTaxRate <= getCommercePercent(COMMERCE_GOLD)) && iMaxTaxRate != 100)
{
if (isCommerceFlexible(COMMERCE_ESPIONAGE) && (getCommercePercent(COMMERCE_ESPIONAGE) > GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS")))
{
changeCommercePercent(COMMERCE_ESPIONAGE, -GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS"));
}
//Check to see if we meet the target now
if (iMaxTaxRate < getCommercePercent(COMMERCE_GOLD))
break;
if (isCommerceFlexible(COMMERCE_RESEARCH) && (getCommercePercent(COMMERCE_RESEARCH) > GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS")))
{
changeCommercePercent(COMMERCE_RESEARCH, -GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS"));
}
//Check to see if we meet the target now
if (iMaxTaxRate < getCommercePercent(COMMERCE_GOLD))
break;
if (isCommerceFlexible(COMMERCE_CULTURE) && (getCommercePercent(COMMERCE_CULTURE) > GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS")))
{
changeCommercePercent(COMMERCE_CULTURE, -GC.getDefineINT("COMMERCE_PERCENT_CHANGE_INCREMENTS"));
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
// this is called on doTurn, so make sure our gold is high enough keep us above zero gold.
verifyGoldCommercePercent();
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 07/19/09 jdog5000 */
/* */
/* Civic AI */
/************************************************************************************************/
void CvPlayerAI::AI_doCivics()
{
PROFILE_FUNC();
CivicTypes* paeBestCivic = NULL;
int* paeBestCivicValue = NULL;
int* paeBestNearFutureCivicValue = NULL;
int* paeCurCivicValue = NULL;
int* paiAvailableChoices = NULL;
int iCurCivicsValue = 0;
int iBestCivicsValue = 0;
int iI;
FAssertMsg(!isHuman(), "isHuman did not return false as expected");
m_turnsSinceLastRevolution++;
m_iCivicSwitchMinDeltaThreshold = (m_iCivicSwitchMinDeltaThreshold*95)/100;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 07/20/09 jdog5000 */
/* */
/* Barbarian AI, efficiency */
/************************************************************************************************/
if( isBarbarian() )
{
return;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
if (AI_getCivicTimer() > 0)
{
AI_changeCivicTimer(-1);
return;
}
if (!canRevolution(NULL))
{
return;
}
int iLoop;
m_iCityGrowthValueBase = 0;
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
int iCityHappy = pLoopCity->happyLevel() - pLoopCity->unhappyLevel();
int iCurrentFoodToGrow = pLoopCity->growthThreshold();
int iFoodPerTurn = pLoopCity->foodDifference(false, true, true);
int iCityValue = 0;
if ( gPlayerLogLevel > 1 )
{
logBBAI("Player %d (%S) city %S has food/turn %d (%d from trade), happy %d",
getID(),
getCivilizationDescription(0),
pLoopCity->getName().c_str(),
iFoodPerTurn,
pLoopCity->getTradeYield(YIELD_FOOD),
iCityHappy);
}
iCityValue = iCurrentFoodToGrow;
int iFoodDiffDivisor = std::abs(iFoodPerTurn - 1) + 5;
int iHappyDivisor = std::max(1, -iCityHappy + 1) + 4;
#if 0
// We Always count at least 3 food per turn on any city we evaluate at all, and want to
// evaluate any that are near suplus. This is to promote civic stability, since small
// health changes are likely in any civic switch and we don't want them to move a city
// from not counting at all to counting a lot
if ( iFoodPerTurn > -2 )
{
if ( iCityHappy >= 0 )
{
// We look at the food difference without trade yields because otherwise civic switches that change the trade
// yield can wildly distort the value of growth.
iCityValue = (std::min(3,iCityHappy+1)*iCurrentFoodToGrow)/std::max(3, iFoodPerTurn-pLoopCity->getTradeYield(YIELD_FOOD));
if ( gPlayerLogLevel > 1 )
{
logBBAI("Player %d (%S) city %S growth value %d",
getID(),
getCivilizationDescription(0),
pLoopCity->getName().c_str(),
iCityValue);
}
}
}
#else
iCityValue = (iCityValue*10)/(iFoodDiffDivisor + iHappyDivisor);
if ( gPlayerLogLevel > 1 )
{
logBBAI("Player %d (%S) city %S growth value %d",
getID(),
getCivilizationDescription(0),
pLoopCity->getName().c_str(),
iCityValue);
}
#endif
m_iCityGrowthValueBase += iCityValue;
}
FAssertMsg(AI_getCivicTimer() == 0, "AI Civic timer is expected to be 0");
paeBestCivic = new CivicTypes[GC.getNumCivicOptionInfos()];
paeBestCivicValue = new int[GC.getNumCivicOptionInfos()];
paeCurCivicValue = new int[GC.getNumCivicOptionInfos()];
paeBestNearFutureCivicValue = new int[GC.getNumCivicOptionInfos()];
paiAvailableChoices = new int[GC.getNumCivicOptionInfos()];
/********************************************************************************/
/* New Civic AI 19.08.2010 Fuyu */
/********************************************************************************/
bool bDoRevolution = false;
int iCurValue;
int iBestValue;
for (iI = 0; iI < GC.getNumCivicInfos()*2; iI++)
{
m_aiCivicValueCache[iI] = MAX_INT;
}
for(iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
paiAvailableChoices[iI] = 0;
}
for(iI = 0; iI < GC.getNumCivicInfos(); iI++)
{
if ( canDoCivics((CivicTypes)iI) )
{
int iCivicOption = GC.getCivicInfo((CivicTypes)iI).getCivicOptionType();
FAssert(iCivicOption >= 0);
FAssert(iCivicOption < GC.getNumCivicOptionInfos());
paiAvailableChoices[iCivicOption]++;
}
}
/*
//Might be good to have if many civics from different cathegories affect each other much and become available at the same time
// otherwise this is not needed and therefore commented out
//To use this, simply uncomment and replace any "iI" or "(CivicOptionTypes)iI" in the rest of this this function
// with "(paeShuffledCivicOptions[iI])"
CivicOptionTypes* paeShuffledCivicOptions = new Array[GC.getNumCivicOptionInfos()];
for (iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
paeShuffledCivicOptions[iI] = (CivicOptionTypes)iI;
}
int iNumPermutations = 1;
for (iI = GC.getNumCivicOptionInfos(); iI > 1; iI--)
{
iNumPermutations *= iI;
}
int iPermutation = GC.getGame().getSorenRandNum(iNumPermutations, "AI Civic Option Shuffling");
//mapping each possible iPermutation to one possible permutation
int iPermutationWidth;
CivicOptionTypes eTempShuffleCivicOption;
for (iI = GC.getNumCivicOptionInfos(); (iI > 0 && iPermutation > 0); iI--)
{
iPermutationWidth = iPermutation % iI;
iNumPermutations /= iI;
iPermutation %= iNumPermutations;
if (iPermutationWidth > 0)
{
eTempShuffleCivicOption = paeShuffledCivicOptions[iI];
paeShuffledCivicOptions[iI] = paeShuffledCivicOptions[(iI+iPermutationWidth)];
paeShuffledCivicOptions[(iI+iPermutationWidth)] = eTempShuffleCivicOption;
}
}
SAFE_DELETE_ARRAY(paeShuffledCivicOptions); //<- not to be forgotten at the end of this function
*/
//initializing
for (iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
paeBestCivic[iI] = getCivics((CivicOptionTypes)iI);
}
for (iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
if ( paiAvailableChoices[iI] > 1 )
{
//civic option vacuum
if (getCivics((CivicOptionTypes)iI) != NO_CIVIC)
processCivics(getCivics((CivicOptionTypes)iI), -1, /* bLimited */ true);
paeBestCivic[iI] = AI_bestCivic((CivicOptionTypes)iI, &iBestValue, /* bCivicOptionVacuum */ true, paeBestCivic);
paeCurCivicValue[iI] = AI_civicValue( getCivics((CivicOptionTypes)iI), /* bCivicOptionVacuum */ true, paeBestCivic );
if ( paeBestCivic[iI] == NO_CIVIC || iBestValue <= paeCurCivicValue[iI] )
{
paeBestCivic[iI] = getCivics((CivicOptionTypes)iI);
paeBestCivicValue[iI] = paeCurCivicValue[iI];
}
else
{
FAssert(paeBestCivic[iI] != 0);
paeBestCivicValue[iI] = iBestValue;
}
if (paeBestCivic[iI] != NO_CIVIC && paeBestCivic[iI] != getCivics((CivicOptionTypes)iI))
{
logBBAI("Player %d (%S) suggests civic switch from %S->%S on initial pass (values %d and %d)",
getID(),
getCivilizationDescription(0),
GC.getCivicInfo(getCivics((CivicOptionTypes)iI)).getDescription(),
GC.getCivicInfo(paeBestCivic[iI]).getDescription(),
paeCurCivicValue[iI],
paeBestCivicValue[iI]);
bDoRevolution = true;
}
if (paeBestCivic[iI] != NO_CIVIC)
processCivics(paeBestCivic[iI], 1, /* bLimited */ true);
}
else
{
paeBestCivicValue[iI] = -1; // Not set
}
}
//repeat? just to be sure we aren't doing anything stupid
bool bChange = (bDoRevolution);
int iPass = 0;
while (bChange && iPass < GC.getNumCivicOptionInfos())
{
for (iI = 0; iI < GC.getNumCivicInfos()*2; iI++)
{
m_aiCivicValueCache[iI] = MAX_INT;
}
iPass++;
FAssertMsg(iPass <= 2, "Civic decision takes too long.");
bChange = false;
iBestCivicsValue = 0;
iCurCivicsValue = 0;
CivicTypes eNewBestCivic;
for (iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
if ( paiAvailableChoices[iI] > 1 )
{
if (paeBestCivic[iI] != NO_CIVIC)
processCivics(paeBestCivic[iI], -1, /* bLimited */ true);
eNewBestCivic = AI_bestCivic((CivicOptionTypes)iI, &iBestValue, /* bCivicOptionVacuum */ true, paeBestCivic);
iCurValue = AI_civicValue( getCivics((CivicOptionTypes)iI), /* bCivicOptionVacuum */ true, paeBestCivic );
if ( eNewBestCivic == NO_CIVIC || iBestValue < iCurValue )
{
if (paeBestCivic[iI] != getCivics((CivicOptionTypes)iI))
{
bChange = true;
paeBestCivic[iI] = getCivics((CivicOptionTypes)iI);
if (eNewBestCivic == NO_CIVIC) //when does this happen?
{
paeBestCivicValue[iI] = iCurValue;
iBestValue = iCurValue;
}
logBBAI("Player %d (%S) prefers original civic %S on pass %d (values %d and %d)",
getID(),
getCivilizationDescription(0),
GC.getCivicInfo(paeBestCivic[iI]).getDescription(),
iPass,
paeCurCivicValue[iI],
paeBestCivicValue[iI]);
}
}
else
{
if (paeBestCivic[iI] != eNewBestCivic)
{
bChange = true;
paeBestCivic[iI] = eNewBestCivic;
paeBestCivicValue[iI] = iBestValue;
logBBAI("Player %d (%S) prefers civic switch from %S->%S on pass %d (values %d and %d)",
getID(),
getCivilizationDescription(0),
GC.getCivicInfo(getCivics((CivicOptionTypes)iI)).getDescription(),
GC.getCivicInfo(paeBestCivic[iI]).getDescription(),
iPass,
paeCurCivicValue[iI],
paeBestCivicValue[iI]);
}
}
iBestCivicsValue += iBestValue;
iCurCivicsValue += iCurValue;
if (paeBestCivic[iI] != NO_CIVIC)
processCivics(paeBestCivic[iI], 1, /* bLimited */ true);
}
}
}
// Put back current civics
int iCivicChanges = 0;
for (iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
if ( paiAvailableChoices[iI] > 1 )
{
if (paeBestCivic[iI] != getCivics((CivicOptionTypes)iI))
{
if (paeBestCivic[iI] != NO_CIVIC)
{
iCivicChanges++;
processCivics(paeBestCivic[iI], -1, /* bLimited */ true);
if ( gPlayerLogLevel > 1 )
{
logBBAI("Player %d (%S) considering civic switch from %S->%S (values %d and %d)",
getID(),
getCivilizationDescription(0),
GC.getCivicInfo(getCivics((CivicOptionTypes)iI)).getDescription(),
GC.getCivicInfo(paeBestCivic[iI]).getDescription(),
paeCurCivicValue[iI],
paeBestCivicValue[iI]);
}
}
if (getCivics((CivicOptionTypes)iI) != NO_CIVIC)
processCivics(getCivics((CivicOptionTypes)iI), 1, /* bLimited */ true);
}
}
}
if ( bDoRevolution )
{
FAssert(iBestCivicsValue >= iCurCivicsValue);
// If we have no anarchy or if we're in a golden age that will still be going
// next time we get an opportunity to change civs just do it (it's costless)
if ( getMaxAnarchyTurns() != 0 &&
(!isGoldenAge() || GC.getDefineINT("MIN_REVOLUTION_TURNS") >= getGoldenAgeTurns()) )
{
if ( iBestCivicsValue - iCurCivicsValue < m_iCivicSwitchMinDeltaThreshold )
{
if ( gPlayerLogLevel > 1 )
{
logBBAI("Civic switch deferred since minimum value threshold not reached (%d vs %d)",
iBestCivicsValue - iCurCivicsValue,
m_iCivicSwitchMinDeltaThreshold);
}
bDoRevolution = false;
}
else
{
// Are we close to discovering new civic enablers? If so should we wait for them?
int researchRate = calculateResearchRate();
bDoRevolution = false;
while(iCivicChanges > 0 && !bDoRevolution)
{
int iNearFutureCivicsBestValue = iBestCivicsValue;
int iMaxHorizon = 20*getCivicAnarchyLength(paeBestCivic);
for (iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
paeBestNearFutureCivicValue[iI] = 0;
}
for(int iOptionType = 0; iOptionType < GC.getNumCivicOptionInfos(); iOptionType++ )
{
bool bTestSwitched = false;
for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
{
if ( GC.getCivicInfo((CivicTypes)iI).getCivicOptionType() == iOptionType && !canDoCivics((CivicTypes)iI) )
{
TechTypes eTech = (TechTypes)GC.getCivicInfo((CivicTypes)iI).getTechPrereq();
if ( !GET_TEAM(getTeam()).isHasTech(eTech) )
{
CvTechInfo& kTech = GC.getTechInfo(eTech);
if ( kTech.getEra() <= GC.getGameINLINE().getCurrentEra() + 1 )
{
//civic option vacuum
if (getCivics((CivicOptionTypes)iOptionType) != NO_CIVIC && !bTestSwitched)
{
processCivics(getCivics((CivicOptionTypes)iOptionType), -1, /* bLimited */ true);
bTestSwitched = true;
}
int iNearFutureValue = AI_civicValue( (CivicTypes)iI, /* bCivicOptionVacuum */ true, paeBestCivic );
if ( paeBestCivicValue[iOptionType] == -1 )
{
// Not calculated yet - do so now
paeBestCivicValue[iOptionType] = AI_civicValue( paeBestCivic[iOptionType], /* bCivicOptionVacuum */ true, paeBestCivic );
}
if ( gPlayerLogLevel > 1 )
{
logBBAI("Near future civic %S has value %d, vs current %d for that civic option",
GC.getCivicInfo((CivicTypes)iI).getDescription(),
iNearFutureValue,
paeBestCivicValue[iOptionType]);
}
if ( iNearFutureValue > paeBestCivicValue[iOptionType] )
{
int iTechPathLen = findPathLength(eTech, true);
// Arkatakor - The handicap type will be based on the average handicap of all human players
iTechPathLen *= GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAIResearchPercent();
iTechPathLen /= 100;
iTechPathLen *= GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getResearchPercent();
iTechPathLen /= 100;
iTechPathLen *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getResearchPercent();
iTechPathLen /= 100;
iTechPathLen *= GC.getEraInfo(GC.getGameINLINE().getStartEra()).getResearchPercent();
iTechPathLen /= 100;
iTechPathLen /= std::max(1,researchRate);
if ( gPlayerLogLevel > 1 )
{
logBBAI("Tech path len is %d vs horizon %d",
iTechPathLen,
iMaxHorizon);
}
if ( iTechPathLen <= iMaxHorizon )
{
int weightedDelta = (iNearFutureValue - paeBestCivicValue[iOptionType])*20/(20+iTechPathLen);
if ( gPlayerLogLevel > 1 )
{
logBBAI("weightedDelta is %d vs best %d",
weightedDelta,
paeBestNearFutureCivicValue[iOptionType]);
}
if ( weightedDelta > paeBestNearFutureCivicValue[iOptionType] )
{
iNearFutureCivicsBestValue -= paeBestNearFutureCivicValue[iOptionType];
iNearFutureCivicsBestValue += weightedDelta;
paeBestNearFutureCivicValue[iOptionType] = weightedDelta;
if ( gPlayerLogLevel > 0 )
{
logBBAI("Found near-future civic %S with value %d",
GC.getCivicInfo((CivicTypes)iI).getDescription(),
iNearFutureValue);
}
}
}
}
}
}
}
}
if (bTestSwitched)
{
processCivics(getCivics((CivicOptionTypes)iOptionType), 1, /* bLimited */ true);
}
}
FAssert(iNearFutureCivicsBestValue >= iBestCivicsValue);
// So if the best we can do now is an improvement, and the degree of improvement is greater than
// the time-discounted degree of additional improvement we'd get from near future civic just do it now
bDoRevolution = ( iBestCivicsValue > iCurCivicsValue && (iBestCivicsValue - iCurCivicsValue) > (iNearFutureCivicsBestValue - iBestCivicsValue));
if ( gPlayerLogLevel > 1 )
{
logBBAI("Gain from immediate switch is %d, additional gain from near-future switch %d",
iBestCivicsValue - iCurCivicsValue,
iNearFutureCivicsBestValue - iBestCivicsValue);
}
if ( bDoRevolution )
{
// Factor in lost production/GNP due to anarchy
int iTotalEconomyTurnValue = (getCommerceRate(COMMERCE_GOLD) +
getCommerceRate(COMMERCE_RESEARCH) +
getCommerceRate(COMMERCE_CULTURE) +
getCommerceRate(COMMERCE_ESPIONAGE) +
2*calculateTotalYield(YIELD_PRODUCTION));
int iPerTurnEstimatedIncrease = (iBestCivicsValue - iCurCivicsValue);
int iAnarchyCost = getCivicAnarchyLength(paeBestCivic)*iTotalEconomyTurnValue;
int iBenfitInTurns = std::min(50, m_turnsSinceLastRevolution)*GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getBuildPercent()/100;;
int iBenefit = iPerTurnEstimatedIncrease*iBenfitInTurns;
if ( gPlayerLogLevel > 0 )
{
logBBAI("Estimated number of turns to justify %d (threshold for switch, with %d turns since last revolution, at this game speed: %d)",
iAnarchyCost/iPerTurnEstimatedIncrease,
m_turnsSinceLastRevolution,
iBenfitInTurns);
}
// If we won't make it up in (arbitrary number) 50 turns (at standard speed) don't bother
if ( iAnarchyCost > iBenefit )
{
bDoRevolution = false;
}
}
else
{
if ( gPlayerLogLevel > 0 )
{
logBBAI("Waiting for near future civics before switching");
}
}
if ( !bDoRevolution )
{
// Check that a cheaper change is not worthwhile - remove the least-efficient civic option
// switch that is in the proposed set and check again
int iLowestEfficiency = MAX_INT;
CivicOptionTypes eWorstOptionSwitch = NO_CIVICOPTION;
for (iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
if (paeBestCivic[iI] != getCivics((CivicOptionTypes)iI))
{
if (paeBestCivic[iI] != NO_CIVIC)
{
int anarchyLen = GC.getCivicInfo(paeBestCivic[iI]).getAnarchyLength();
if ( anarchyLen > 0 )
{
int efficiency = (paeBestCivicValue[iI] - paeCurCivicValue[iI])/anarchyLen;
if ( efficiency < iLowestEfficiency )
{
iLowestEfficiency = efficiency;
eWorstOptionSwitch = (CivicOptionTypes)iI;
}
}
else if ( iLowestEfficiency == MAX_INT )
{
eWorstOptionSwitch = (CivicOptionTypes)iI;
}
}
}
}
FAssert(eWorstOptionSwitch != NO_CIVICOPTION);
paeBestCivic[eWorstOptionSwitch] = getCivics(eWorstOptionSwitch);
iCivicChanges--;
iBestCivicsValue -= (paeBestCivicValue[eWorstOptionSwitch] - paeCurCivicValue[eWorstOptionSwitch]);
}
}
}
}
else
{
if ( gPlayerLogLevel > 0 )
{
logBBAI("No anarchy so switching");
}
}
}
// XXX AI skips revolution???
if (bDoRevolution && canRevolution(paeBestCivic))
/********************************************************************************/
/* New Civic AI END */
/********************************************************************************/
{
m_iCivicSwitchMinDeltaThreshold = (iBestCivicsValue - iCurCivicsValue)*2;
revolution(paeBestCivic);
AI_setCivicTimer((getMaxAnarchyTurns() == 0 || isGoldenAge()) ? GC.getDefineINT("MIN_REVOLUTION_TURNS") : CIVIC_CHANGE_DELAY);
}
else
{
// AI will re-evaluate whenever it gets a tech anyway, but if it's in a long stagant
// period have it do so periodically anyway (but not so often as to create a peformance overhead)
AI_setCivicTimer(CIVIC_CHANGE_DELAY);
}
m_iCityGrowthValueBase = -1;
SAFE_DELETE_ARRAY(paeBestCivic);
SAFE_DELETE_ARRAY(paeBestCivicValue);
SAFE_DELETE_ARRAY(paeBestNearFutureCivicValue);
SAFE_DELETE_ARRAY(paeCurCivicValue);
SAFE_DELETE_ARRAY(paiAvailableChoices);
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
void CvPlayerAI::AI_doReligion()
{
PROFILE_FUNC();
ReligionTypes eBestReligion;
FAssertMsg(!isHuman(), "isHuman did not return false as expected");
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 07/20/09 jdog5000 */
/* */
/* Barbarian AI, efficiency */
/************************************************************************************************/
if( isBarbarian() )
{
return;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
if (AI_getReligionTimer() > 0)
{
AI_changeReligionTimer(-1);
return;
}
if (!canChangeReligion())
{
return;
}
FAssertMsg(AI_getReligionTimer() == 0, "AI Religion timer is expected to be 0");
eBestReligion = AI_bestReligion();
if (eBestReligion == NO_RELIGION)
{
eBestReligion = getStateReligion();
}
if (canConvert(eBestReligion))
{
convert(eBestReligion);
AI_setReligionTimer((getMaxAnarchyTurns() == 0) ? (GC.getDefineINT("MIN_CONVERSION_TURNS") * 2) : RELIGION_CHANGE_DELAY);
}
}
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
void CvPlayerAI::AI_beginDiplomacy(CvDiploParameters* pDiploParams, PlayerTypes ePlayer)
{
if (isDoNotBotherStatus(ePlayer))
{
// Divert AI diplomacy away from the diplomacy screen and induce the appropriate reaction
// in the AI equivalent to a human rejecting the AI's requests in the interface. There are
// a number of AI requests that do not need handling and that simply time out. There are
// also AI requests that occur in CvTeam that induce the diplomacy screen in any case.
// This diplomacy modification does not alter the AI's characteristics at all and is actually
// just an interface modification for a player to shut down talks with an AI automatically.
int ai_request;
ai_request = (DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_RELIGION_PRESSURE");
if (ai_request == pDiploParams->getDiploComment())
{
this->handleDiploEvent(DIPLOEVENT_NO_CONVERT, ePlayer, -1, -1);
}
ai_request = (DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_CIVIC_PRESSURE");
if (ai_request == pDiploParams->getDiploComment())
{
this->handleDiploEvent(DIPLOEVENT_NO_REVOLUTION, ePlayer, -1, -1);
}
ai_request = (DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_JOIN_WAR");
if (ai_request == pDiploParams->getDiploComment())
{
this->handleDiploEvent(DIPLOEVENT_NO_JOIN_WAR, ePlayer, -1, -1);
}
ai_request = (DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_STOP_TRADING");
if (ai_request == pDiploParams->getDiploComment())
{
this->handleDiploEvent(DIPLOEVENT_NO_STOP_TRADING, ePlayer, -1, -1);
}
ai_request = (DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_ASK_FOR_HELP");
if (ai_request == pDiploParams->getDiploComment())
{
this->handleDiploEvent(DIPLOEVENT_REFUSED_HELP, ePlayer, -1, -1);
}
ai_request = (DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_DEMAND_TRIBUTE");
if (ai_request == pDiploParams->getDiploComment())
{
this->handleDiploEvent(DIPLOEVENT_REJECTED_DEMAND, ePlayer, -1, -1);
if (AI_demandRebukedWar(ePlayer))
{
this->handleDiploEvent(DIPLOEVENT_DEMAND_WAR, ePlayer, -1, -1);
}
}
}
else
{
//OutputDebugString("UI interaction - diplomacy\n");
GET_PLAYER(ePlayer).setTurnHadUIInteraction(true);
gDLL->beginDiplomacy(pDiploParams, (PlayerTypes)ePlayer);
}
}
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
void CvPlayerAI::AI_doDiplo()
{
PROFILE_FUNC();
CLLNode<TradeData>* pNode;
CvDiploParameters* pDiplo;
CvDeal* pLoopDeal;
CvCity* pLoopCity;
CvPlot* pLoopPlot;
CLinkList<TradeData> ourList;
CLinkList<TradeData> theirList;
bool abContacted[MAX_TEAMS];
TradeData item;
CivicTypes eFavoriteCivic;
BonusTypes eBestReceiveBonus;
BonusTypes eBestGiveBonus;
TechTypes eBestReceiveTech;
TechTypes eBestGiveTech;
TeamTypes eBestTeam;
bool bCancelDeal;
int iReceiveGold;
int iGiveGold;
int iGold;
int iGoldData;
int iGoldWeight;
int iGoldValuePercent;
int iCount;
int iPossibleCount;
int iValue;
int iBestValue;
int iOurValue;
int iTheirValue;
int iPass;
int iLoop;
int iI, iJ;
FAssert(!isHuman());
FAssert(!isMinorCiv());
FAssert(!isBarbarian());
/************************************************************************************************/
/* Afforess Start 04/29/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getUSE_AI_DO_DIPLO_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
PROFILE("CvPlayerAI::AI_doDiplo.Python");
CyArgsList argsList;
argsList.add(getID());
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "AI_doDiplo", argsList.makeFunctionArgs(), &lResult);
if (lResult == 1)
{
return;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iGoldValuePercent = AI_goldTradeValuePercent();
for (iI = 0; iI < MAX_TEAMS; iI++)
{
abContacted[iI] = false;
}
int iRandomTechChoiceSeed = GC.getGameINLINE().getSorenRandNum(GC.getNumTechInfos(), "AI trade random tech choice seed");
stdext::hash_map<int,int> receivableTechs;
{
PROFILE("CvPlayerAI::AI_doDiplo.preCalcTechSources");
for (iI = 0; iI < MAX_CIV_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (iI != getID())
{
if (canContact((PlayerTypes)iI) && AI_isWillingToTalk((PlayerTypes)iI))
{
for (iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, iJ);
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
int iCount = 0;
stdext::hash_map<int,int>::const_iterator itr = receivableTechs.find(iJ);
if ( itr != receivableTechs.end() )
{
iCount = itr->second;
}
receivableTechs[iJ] = iCount + 1;
}
}
}
}
}
}
for(stdext::hash_map<int,int>::const_iterator itr = receivableTechs.begin(); itr != receivableTechs.end(); ++itr)
{
logBBAI("Receivable tech %S has %d sources", GC.getTechInfo((TechTypes)itr->first).getDescription(), itr->second);
}
}
for (iPass = 0; iPass < 2; iPass++)
{
for (iI = 0; iI < MAX_CIV_PLAYERS; iI++)
{
logBBAI("Player %d trade calc to/from player %d", getID(), iI);
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (GET_PLAYER((PlayerTypes)iI).isHuman() == (iPass == 1))
{
//Afforess Ruthless AI
bool bNoContactDelay = GC.getGameINLINE().isOption(GAMEOPTION_RUTHLESS_AI) && !GET_PLAYER((PlayerTypes)iI).isHuman();
if (iI != getID())
{
if (GET_PLAYER((PlayerTypes)iI).getTeam() != getTeam())
{
PROFILE("CvPlayerAI::AI_doDiplo.Existing");
for(pLoopDeal = GC.getGameINLINE().firstDeal(&iLoop); pLoopDeal != NULL; pLoopDeal = GC.getGameINLINE().nextDeal(&iLoop))
{
if (pLoopDeal->isCancelable(getID()))
{
if ((GC.getGameINLINE().getGameTurn() - pLoopDeal->getInitialGameTurn()) >= (getTreatyLength() * 2))
{
bCancelDeal = false;
if ((pLoopDeal->getFirstPlayer() == getID()) && (pLoopDeal->getSecondPlayer() == ((PlayerTypes)iI)))
{
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!AI_considerOffer(((PlayerTypes)iI), pLoopDeal->getSecondTrades(), pLoopDeal->getFirstTrades(), -1))
{
bCancelDeal = true;
}
}
else
{
for (pNode = pLoopDeal->getFirstTrades()->head(); pNode; pNode = pLoopDeal->getFirstTrades()->next(pNode))
{
if (getTradeDenial(((PlayerTypes)iI), pNode->m_data) != NO_DENIAL)
{
bCancelDeal = true;
break;
}
}
}
}
else if ((pLoopDeal->getFirstPlayer() == ((PlayerTypes)iI)) && (pLoopDeal->getSecondPlayer() == getID()))
{
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!AI_considerOffer(((PlayerTypes)iI), pLoopDeal->getFirstTrades(), pLoopDeal->getSecondTrades(), -1))
{
bCancelDeal = true;
}
}
else
{
for (pNode = pLoopDeal->getSecondTrades()->head(); pNode; pNode = pLoopDeal->getSecondTrades()->next(pNode))
{
if (getTradeDenial(((PlayerTypes)iI), pNode->m_data) != NO_DENIAL)
{
bCancelDeal = true;
break;
}
}
}
}
if (bCancelDeal)
{
if (canContact((PlayerTypes)iI) && AI_isWillingToTalk((PlayerTypes)iI))
{
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
ourList.clear();
theirList.clear();
for (pNode = pLoopDeal->headFirstTradesNode(); (pNode != NULL); pNode = pLoopDeal->nextFirstTradesNode(pNode))
{
if (pLoopDeal->getFirstPlayer() == getID())
{
ourList.insertAtEnd(pNode->m_data);
}
else
{
theirList.insertAtEnd(pNode->m_data);
}
}
for (pNode = pLoopDeal->headSecondTradesNode(); (pNode != NULL); pNode = pLoopDeal->nextSecondTradesNode(pNode))
{
if (pLoopDeal->getSecondPlayer() == getID())
{
ourList.insertAtEnd(pNode->m_data);
}
else
{
theirList.insertAtEnd(pNode->m_data);
}
}
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
if (pLoopDeal->isVassalDeal())
{
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_NO_VASSAL"));
pDiplo->setAIContact(true);
/***************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
}
else
{
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_CANCEL_DEAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/ }
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
/************************************************************************************************/
/* Afforess Start 07/29/10 */
/* */
/* Advanced Diplomacy */
/************************************************************************************************/
bool bEmbassyCanceled = pLoopDeal->isEmbassy();
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
pLoopDeal->kill(); // XXX test this for AI...
/************************************************************************************************/
/* Afforess Start 07/29/10 */
/* */
/* Advanced Diplomacy */
/************************************************************************************************/
if (bEmbassyCanceled)
{
for (int iPlayer = 0; iPlayer < MAX_PLAYERS; iPlayer++)
{
if (GET_PLAYER((PlayerTypes)iPlayer).isAlive())
{
if (getTeam() == GET_PLAYER((PlayerTypes)iPlayer).getTeam())
{
GET_PLAYER((PlayerTypes)iPlayer).AI_changeMemoryCount(((PlayerTypes)iI), MEMORY_RECALLED_AMBASSADOR, -AI_getMemoryCount(((PlayerTypes)iI), MEMORY_RECALLED_AMBASSADOR));
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
}
}
}
}
if (canContact((PlayerTypes)iI) && AI_isWillingToTalk((PlayerTypes)iI))
{
PROFILE("CvPlayerAI::AI_doDiplo.CanContact");
if (GET_PLAYER((PlayerTypes)iI).getTeam() == getTeam() || GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isVassal(getTeam()))
{
PROFILE("CvPlayerAI::AI_doDiplo.BonusTrade");
// XXX will it cancel this deal if it loses it's first resource???
iBestValue = 0;
eBestGiveBonus = NO_BONUS;
for (iJ = 0; iJ < GC.getNumBonusInfos(); iJ++)
{
if (getNumTradeableBonuses((BonusTypes)iJ) > 1)
{
if ((GET_PLAYER((PlayerTypes)iI).AI_bonusTradeVal(((BonusTypes)iJ), getID(), 1) > 0)
&& (GET_PLAYER((PlayerTypes)iI).AI_bonusVal((BonusTypes)iJ, 1) > AI_bonusVal((BonusTypes)iJ, -1)))
{
setTradeItem(&item, TRADE_RESOURCES, iJ);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(10000, "AI Bonus Trading #1"));
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestGiveBonus = ((BonusTypes)iJ);
}
}
}
}
}
if (eBestGiveBonus != NO_BONUS)
{
ourList.clear();
theirList.clear();
setTradeItem(&item, TRADE_RESOURCES, eBestGiveBonus);
ourList.insertAtEnd(item);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_GIVE_HELP"));
pDiplo->setAIContact(true);
pDiplo->setTheirOfferList(ourList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
if (GET_PLAYER((PlayerTypes)iI).getTeam() != getTeam() && GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isVassal(getTeam()))
{
PROFILE("CvPlayerAI::AI_doDiplo.TechTrade");
iBestValue = 0;
eBestGiveTech = NO_TECH;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 12/06/09 jdog5000 */
/* */
/* Diplomacy */
/************************************************************************************************/
// Don't give techs for free to advanced vassals ...
if( GET_PLAYER((PlayerTypes)iI).getTechScore()*10 < getTechScore()*9 )
{
for (iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
{
if (GET_TEAM(getTeam()).AI_techTrade((TechTypes)iJ, GET_PLAYER((PlayerTypes)iI).getTeam()) == NO_DENIAL)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, iJ);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(10000, "AI Vassal Tech gift"));
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestGiveTech = ((TechTypes)iJ);
}
}
}
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
if (eBestGiveTech != NO_TECH)
{
ourList.clear();
theirList.clear();
setTradeItem(&item, TRADE_TECHNOLOGIES, eBestGiveTech);
ourList.insertAtEnd(item);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_GIVE_HELP"));
pDiplo->setAIContact(true);
pDiplo->setTheirOfferList(ourList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
if (GET_PLAYER((PlayerTypes)iI).getTeam() != getTeam() && !(GET_TEAM(getTeam()).isHuman()) && (GET_PLAYER((PlayerTypes)iI).isHuman() || !(GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isHuman())))
{
FAssertMsg(!(GET_PLAYER((PlayerTypes)iI).isBarbarian()), "(GET_PLAYER((PlayerTypes)iI).isBarbarian()) did not return false as expected");
FAssertMsg(iI != getID(), "iI is not expected to be equal with getID()");
if (GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isVassal(getTeam()))
{
PROFILE("CvPlayerAI::AI_doDiplo.Vasal.BonusTrade");
iBestValue = 0;
eBestGiveBonus = NO_BONUS;
for (iJ = 0; iJ < GC.getNumBonusInfos(); iJ++)
{
if (GET_PLAYER((PlayerTypes)iI).getNumTradeableBonuses((BonusTypes)iJ) > 0 && getNumAvailableBonuses((BonusTypes)iJ) == 0)
{
iValue = AI_bonusTradeVal((BonusTypes)iJ, (PlayerTypes)iI, 1);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestGiveBonus = ((BonusTypes)iJ);
}
}
}
if (eBestGiveBonus != NO_BONUS)
{
theirList.clear();
ourList.clear();
setTradeItem(&item, TRADE_RESOURCES, eBestGiveBonus);
theirList.insertAtEnd(item);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
CvPopupInfo* pInfo = new CvPopupInfo(BUTTONPOPUP_VASSAL_GRANT_TRIBUTE, getID(), eBestGiveBonus);
if (pInfo)
{
gDLL->getInterfaceIFace()->addPopup(pInfo, (PlayerTypes)iI);
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
if (!(GET_TEAM(getTeam()).isAtWar(GET_PLAYER((PlayerTypes)iI).getTeam())))
{
if (AI_getAttitude((PlayerTypes)iI) >= ATTITUDE_CAUTIOUS)
{
PROFILE("CvPlayerAI::AI_doDiplo.Cities");
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (pLoopCity->getPreviousOwner() != ((PlayerTypes)iI))
{
if (((pLoopCity->getGameTurnAcquired() + 4) % 20) == (GC.getGameINLINE().getGameTurn() % 20))
{
iCount = 0;
iPossibleCount = 0;
for (iJ = 0; iJ < NUM_CITY_PLOTS; iJ++)
{
pLoopPlot = plotCity(pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE(), iJ);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getOwnerINLINE() == iI)
{
iCount++;
}
iPossibleCount++;
}
}
if (iCount >= (iPossibleCount / 2))
{
setTradeItem(&item, TRADE_CITIES, pLoopCity->getID());
if (canTradeItem(((PlayerTypes)iI), item, true))
{
ourList.clear();
ourList.insertAtEnd(item);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
//if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_CITY"));
pDiplo->setAIContact(true);
pDiplo->setTheirOfferList(ourList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, NULL);
}
}
}
}
}
}
}
if (GET_TEAM(getTeam()).getLeaderID() == getID())
{
PROFILE("CvPlayerAI::AI_doDiplo.PermAlliance");
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_PERMANENT_ALLIANCE) == 0)
{
bool bOffered = false;
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_PERMANENT_ALLIANCE), "AI Diplo Alliance") == 0)
{
setTradeItem(&item, TRADE_PERMANENT_ALLIANCE);
if (canTradeItem(((PlayerTypes)iI), item, true) && GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
ourList.clear();
theirList.clear();
ourList.insertAtEnd(item);
theirList.insertAtEnd(item);
bOffered = true;
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_PERMANENT_ALLIANCE, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_PERMANENT_ALLIANCE));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_DEAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
break; // move on to next player since we are on the same team now
}
}
}
if (!bOffered)
{
setTradeItem(&item, TRADE_VASSAL);
if (canTradeItem((PlayerTypes)iI, item, true))
{
ourList.clear();
theirList.clear();
ourList.insertAtEnd(item);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_PERMANENT_ALLIANCE, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_PERMANENT_ALLIANCE));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_VASSAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
bool bAccepted = true;
TeamTypes eMasterTeam = GET_PLAYER((PlayerTypes)iI).getTeam();
for (int iTeam = 0; iTeam < MAX_CIV_TEAMS; iTeam++)
{
if (GET_TEAM((TeamTypes)iTeam).isAlive())
{
if (iTeam != getTeam() && iTeam != eMasterTeam && atWar(getTeam(), (TeamTypes)iTeam) && !atWar(eMasterTeam, (TeamTypes)iTeam))
{
if (GET_TEAM(eMasterTeam).AI_declareWarTrade((TeamTypes)iTeam, getTeam(), false) != NO_DENIAL)
{
bAccepted = false;
break;
}
}
}
}
if (bAccepted)
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
}
}
}
if (GET_PLAYER((PlayerTypes)iI).isHuman() && (GET_TEAM(getTeam()).getLeaderID() == getID()) && !GET_TEAM(getTeam()).isVassal(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
PROFILE("CvPlayerAI::AI_doDiplo.Religion");
if (getStateReligion() != NO_RELIGION)
{
if (GET_PLAYER((PlayerTypes)iI).canConvert(getStateReligion()))
{
if (AI_getContactTimer(((PlayerTypes)iI), CONTACT_RELIGION_PRESSURE) == 0)
{
if (GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_RELIGION_PRESSURE), "AI Diplo Religion Pressure") == 0)
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_RELIGION_PRESSURE, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_RELIGION_PRESSURE));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_RELIGION_PRESSURE"));
pDiplo->setAIContact(true);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
}
}
}
}
if (GET_PLAYER((PlayerTypes)iI).isHuman() && (GET_TEAM(getTeam()).getLeaderID() == getID()) && !GET_TEAM(getTeam()).isVassal(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
PROFILE("CvPlayerAI::AI_doDiplo.Civic");
eFavoriteCivic = ((CivicTypes)(GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivic()));
if (eFavoriteCivic != NO_CIVIC)
{
if (isCivic(eFavoriteCivic))
{
if (GET_PLAYER((PlayerTypes)iI).canDoCivics(eFavoriteCivic) && !(GET_PLAYER((PlayerTypes)iI).isCivic(eFavoriteCivic)))
{
if (GET_PLAYER((PlayerTypes)iI).canRevolution(NULL))
{
if (AI_getContactTimer(((PlayerTypes)iI), CONTACT_CIVIC_PRESSURE) == 0)
{
if (GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_CIVIC_PRESSURE), "AI Diplo Civic Pressure") == 0)
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_CIVIC_PRESSURE, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_CIVIC_PRESSURE));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_CIVIC_PRESSURE"), GC.getCivicInfo(eFavoriteCivic).getTextKeyWide());
pDiplo->setAIContact(true);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
}
}
}
}
}
}
/************************************************************************************************/
/* Afforess Start 05/16/10 */
/* */
/* */
/************************************************************************************************/
if (GET_PLAYER((PlayerTypes)iI).isHuman() && (GET_TEAM(getTeam()).getLeaderID() == getID()) && GC.getDefineINT("CAN_TRADE_WAR") > 0)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
PROFILE("CvPlayerAI::AI_doDiplo.WarWith");
if ((AI_getMemoryCount(((PlayerTypes)iI), MEMORY_DECLARED_WAR) == 0) && (AI_getMemoryCount(((PlayerTypes)iI), MEMORY_HIRED_WAR_ALLY) == 0))
{
if (AI_getContactTimer(((PlayerTypes)iI), CONTACT_JOIN_WAR) == 0)
{
/************************************************************************************************/
/* Afforess Start 04/22/14 */
/* */
/* Advanced Diplomacy - make unfriendly AI's less likely to ask for help */
/************************************************************************************************/
int iRand = GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_JOIN_WAR);
AttitudeTypes eAttitude = AI_getAttitude((PlayerTypes)iI);
if (eAttitude != ATTITUDE_FRIENDLY)
{
iRand *= (eAttitude == ATTITUDE_PLEASED ? 10 : 100);
}
if (GC.getGameINLINE().getSorenRandNum(iRand, "AI Diplo Join War") == 0)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
iBestValue = 0;
eBestTeam = NO_TEAM;
for (iJ = 0; iJ < MAX_CIV_TEAMS; iJ++)
{
if (GET_TEAM((TeamTypes)iJ).isAlive())
{
if (atWar(((TeamTypes)iJ), getTeam()) && !atWar(((TeamTypes)iJ), GET_PLAYER((PlayerTypes)iI).getTeam()))
{
if (GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isHasMet((TeamTypes)iJ))
{
if (GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).canDeclareWar((TeamTypes)iJ))
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(10000, "AI Joining War"));
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestTeam = ((TeamTypes)iJ);
}
}
}
}
}
}
if (eBestTeam != NO_TEAM)
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
/************************************************************************************************/
/* Afforess Start 05/23/10 */
/* */
/* */
/************************************************************************************************/
m_eDemandWarAgainstTeam = eBestTeam;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_JOIN_WAR, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_JOIN_WAR));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_JOIN_WAR"), GET_PLAYER(GET_TEAM(eBestTeam).getLeaderID()).getCivilizationAdjectiveKey());
pDiplo->setAIContact(true);
pDiplo->setData(eBestTeam);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
}
}
}
}
if (GET_PLAYER((PlayerTypes)iI).isHuman() && (GET_TEAM(getTeam()).getLeaderID() == getID()) && !GET_TEAM(getTeam()).isVassal(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
PROFILE("CvPlayerAI::AI_doDiplo.StopTradingWith");
if (AI_getContactTimer(((PlayerTypes)iI), CONTACT_STOP_TRADING) == 0)
{
/************************************************************************************************/
/* Afforess Start 04/22/14 */
/* */
/* Advanced Diplomacy - make unfriendly AI's less likely to ask for help */
/************************************************************************************************/
int iRand = GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_STOP_TRADING);
AttitudeTypes eAttitude = AI_getAttitude((PlayerTypes)iI);
if (eAttitude != ATTITUDE_FRIENDLY)
{
iRand *= (eAttitude == ATTITUDE_PLEASED ? 10 : 100);
}
if (GC.getGameINLINE().getSorenRandNum(iRand, "AI Diplo Stop Trading") == 0)
{
if (GC.getGameINLINE().isOption(GAMEOPTION_ADVANCED_DIPLOMACY))
{
eBestTeam = AI_bestStopTradeTeam((PlayerTypes)iI);
}
else
{
eBestTeam = GET_TEAM(getTeam()).AI_getWorstEnemy();
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if ((eBestTeam != NO_TEAM) && GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isHasMet(eBestTeam) && !GET_TEAM(eBestTeam).isVassal(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
if (GET_PLAYER((PlayerTypes)iI).canStopTradingWithTeam(eBestTeam))
{
FAssert(!atWar(GET_PLAYER((PlayerTypes)iI).getTeam(), eBestTeam));
FAssert(GET_PLAYER((PlayerTypes)iI).getTeam() != eBestTeam);
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_STOP_TRADING, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_STOP_TRADING));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_STOP_TRADING"), GET_PLAYER(GET_TEAM(eBestTeam).getLeaderID()).getCivilizationAdjectiveKey());
pDiplo->setAIContact(true);
pDiplo->setData(eBestTeam);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
}
}
}
}
if (GET_PLAYER((PlayerTypes)iI).isHuman() && (GET_TEAM(getTeam()).getLeaderID() == getID()))
{
PROFILE("CvPlayerAI::AI_doDiplo.Help");
if (GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).getAssets() < (GET_TEAM(getTeam()).getAssets() / 2))
{
if (AI_getAttitude((PlayerTypes)iI) > GC.getLeaderHeadInfo(GET_PLAYER((PlayerTypes)iI).getPersonalityType()).getNoGiveHelpAttitudeThreshold())
{
if (AI_getContactTimer(((PlayerTypes)iI), CONTACT_GIVE_HELP) == 0)
{
if (GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_GIVE_HELP), "AI Diplo Give Help") == 0)
{
// XXX maybe do gold instead???
iBestValue = 0;
eBestGiveTech = NO_TECH;
for (iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, iJ);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(10000, "AI Giving Help"));
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestGiveTech = ((TechTypes)iJ);
}
}
}
if (eBestGiveTech != NO_TECH)
{
ourList.clear();
setTradeItem(&item, TRADE_TECHNOLOGIES, eBestGiveTech);
ourList.insertAtEnd(item);
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_GIVE_HELP, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_GIVE_HELP));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_GIVE_HELP"));
pDiplo->setAIContact(true);
pDiplo->setTheirOfferList(ourList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
}
}
}
}
}
if (GET_PLAYER((PlayerTypes)iI).isHuman() && (GET_TEAM(getTeam()).getLeaderID() == getID()))
{
PROFILE("CvPlayerAI::AI_doDiplo.AskHelp");
if (GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).getAssets() > (GET_TEAM(getTeam()).getAssets() / 2))
{
if (AI_getContactTimer(((PlayerTypes)iI), CONTACT_ASK_FOR_HELP) == 0)
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 02/12/10 jdog5000 */
/* */
/* Diplomacy */
/************************************************************************************************/
int iRand = GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_ASK_FOR_HELP);
int iTechPerc = GET_TEAM(getTeam()).getBestKnownTechScorePercent();
if( iTechPerc < 90 )
{
iRand *= std::max(1, iTechPerc - 60);
iRand /= 30;
}
//Afforess make unfriendly AI's less likely to ask for help
AttitudeTypes eAttitude = AI_getAttitude((PlayerTypes)iI);
if (eAttitude != ATTITUDE_FRIENDLY)
{
iRand *= (eAttitude == ATTITUDE_PLEASED ? 10 : 100);
}
if (GC.getGameINLINE().getSorenRandNum(iRand, "AI Diplo Ask For Help") == 0)
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
iBestValue = 0;
eBestReceiveTech = NO_TECH;
for (iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
{
TechTypes eCandidateTech = (TechTypes)((iRandomTechChoiceSeed + iJ)%GC.getNumTechInfos());
setTradeItem(&item, TRADE_TECHNOLOGIES, eCandidateTech);
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
eBestReceiveTech = eCandidateTech;
break;
}
}
if (eBestReceiveTech != NO_TECH)
{
theirList.clear();
setTradeItem(&item, TRADE_TECHNOLOGIES, eBestReceiveTech);
theirList.insertAtEnd(item);
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_ASK_FOR_HELP, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_ASK_FOR_HELP));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_ASK_FOR_HELP"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
}
}
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 05/06/09 jdog5000 */
/* */
/* Bugfix, Diplomacy AI */
/************************************************************************************************/
// Reduced duplication so easier to maintain
if (GET_PLAYER((PlayerTypes)iI).isHuman() && (GET_TEAM(getTeam()).getLeaderID() == getID()))
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH 05/06/09 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original bts code
if (GET_TEAM(getTeam()).canDeclareWar(GET_PLAYER((PlayerTypes)iI).getTeam()) && !GET_TEAM(getTeam()).AI_isSneakAttackPreparing(GET_PLAYER((PlayerTypes)iI).getTeam()))
*/
// Bug fix: when team was sneak attack ready but hadn't declared, could demand tribute
// If other team accepted, it blocked war declaration for 10 turns but AI didn't react.
if (GET_TEAM(getTeam()).canDeclareWar(GET_PLAYER((PlayerTypes)iI).getTeam()) && !GET_TEAM(getTeam()).AI_isChosenWar(GET_PLAYER((PlayerTypes)iI).getTeam()))
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
{
PROFILE("CvPlayerAI::AI_doDiplo.Tribute");
//Afforess changed to check if we are at least 1.5x as powerful
if ((GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).getPower(true) * 3) < (GET_TEAM(getTeam()).getPower(true) * 2))
{
if (AI_getAttitude((PlayerTypes)iI) <= GC.getLeaderHeadInfo(GET_PLAYER((PlayerTypes)iI).getPersonalityType()).getDemandTributeAttitudeThreshold())
{
if (AI_getContactTimer(((PlayerTypes)iI), CONTACT_DEMAND_TRIBUTE) == 0)
{
if (GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_DEMAND_TRIBUTE), "AI Diplo Demand Tribute") == 0)
{
iReceiveGold = std::min(std::max(0, (GET_PLAYER((PlayerTypes)iI).getGold() - 50)), GET_PLAYER((PlayerTypes)iI).AI_goldTarget());
iReceiveGold -= (iReceiveGold % GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
if (iReceiveGold > 50)
{
theirList.clear();
setTradeItem(&item, TRADE_GOLD, iReceiveGold);
theirList.insertAtEnd(item);
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_DEMAND_TRIBUTE, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_DEMAND_TRIBUTE));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_DEMAND_TRIBUTE"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
}
if (GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_DEMAND_TRIBUTE), "AI Diplo Demand Tribute") == 0)
{
if (GET_TEAM(getTeam()).AI_mapTradeVal(GET_PLAYER((PlayerTypes)iI).getTeam()) > 100)
{
theirList.clear();
setTradeItem(&item, TRADE_MAPS);
theirList.insertAtEnd(item);
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_DEMAND_TRIBUTE, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_DEMAND_TRIBUTE));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_DEMAND_TRIBUTE"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
}
if (GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_DEMAND_TRIBUTE), "AI Diplo Demand Tribute") == 0)
{
iBestValue = 0;
eBestReceiveTech = NO_TECH;
for (iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, iJ);
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
if (GC.getGameINLINE().countKnownTechNumTeams((TechTypes)iJ) > 1)
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(10000, "AI Demanding Tribute (Tech)"));
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestReceiveTech = ((TechTypes)iJ);
}
}
}
}
if (eBestReceiveTech != NO_TECH)
{
theirList.clear();
setTradeItem(&item, TRADE_TECHNOLOGIES, eBestReceiveTech);
theirList.insertAtEnd(item);
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_DEMAND_TRIBUTE, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_DEMAND_TRIBUTE));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_DEMAND_TRIBUTE"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
}
if (GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_DEMAND_TRIBUTE), "AI Diplo Demand Tribute") == 0)
{
iBestValue = 0;
eBestReceiveBonus = NO_BONUS;
for (iJ = 0; iJ < GC.getNumBonusInfos(); iJ++)
{
if (GET_PLAYER((PlayerTypes)iI).getNumTradeableBonuses((BonusTypes)iJ) > 1)
{
if (AI_bonusTradeVal(((BonusTypes)iJ), ((PlayerTypes)iI), 1) > 0)
{
setTradeItem(&item, TRADE_RESOURCES, iJ);
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(10000, "AI Demanding Tribute (Bonus)"));
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestReceiveBonus = ((BonusTypes)iJ);
}
}
}
}
}
if (eBestReceiveBonus != NO_BONUS)
{
theirList.clear();
setTradeItem(&item, TRADE_RESOURCES, eBestReceiveBonus);
theirList.insertAtEnd(item);
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_DEMAND_TRIBUTE, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_DEMAND_TRIBUTE));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_DEMAND_TRIBUTE"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
}
}
}
}
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
if (GET_TEAM(getTeam()).getLeaderID() == getID())
{
PROFILE("CvPlayerAI::AI_doDiplo.OpenBorders");
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_OPEN_BORDERS) == 0)
{
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_OPEN_BORDERS), "AI Diplo Open Borders") == 0)
{
setTradeItem(&item, TRADE_OPEN_BORDERS);
if (canTradeItem(((PlayerTypes)iI), item, true) && GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
ourList.clear();
theirList.clear();
ourList.insertAtEnd(item);
theirList.insertAtEnd(item);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_OPEN_BORDERS, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_OPEN_BORDERS));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_DEAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
}
}
if (GET_TEAM(getTeam()).getLeaderID() == getID())
{
PROFILE("CvPlayerAI::AI_doDiplo.DefensivePact");
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_DEFENSIVE_PACT) == 0)
{
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_DEFENSIVE_PACT), "AI Diplo Defensive Pact") == 0)
{
setTradeItem(&item, TRADE_DEFENSIVE_PACT);
if (canTradeItem(((PlayerTypes)iI), item, true) && GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
ourList.clear();
theirList.clear();
ourList.insertAtEnd(item);
theirList.insertAtEnd(item);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_DEFENSIVE_PACT, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_DEFENSIVE_PACT));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_DEAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/ abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
}
}
if (!(GET_PLAYER((PlayerTypes)iI).isHuman()) || (GET_TEAM(getTeam()).getLeaderID() == getID()))
{
PROFILE("CvPlayerAI::AI_doDiplo.TradeTechNonHuman");
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_TRADE_TECH) == 0)
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 04/24/10 jdog5000 */
/* */
/* Diplomacy */
/************************************************************************************************/
int iRand = GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_TRADE_TECH);
int iTechPerc = GET_TEAM(getTeam()).getBestKnownTechScorePercent();
if( iTechPerc < 90 )
{
iRand *= std::max(1, iTechPerc - 60);
iRand /= 30;
}
if( AI_isDoVictoryStrategy(AI_VICTORY_SPACE1) )
{
iRand /= 2;
}
iRand = std::max(1, iRand);
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(iRand, "AI Diplo Trade Tech") == 0)
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
iBestValue = 0;
eBestReceiveTech = NO_TECH;
for (iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
{
TechTypes eCandidateTech = (TechTypes)((iRandomTechChoiceSeed + iJ)%GC.getNumTechInfos());
setTradeItem(&item, TRADE_TECHNOLOGIES, eCandidateTech);
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
stdext::hash_map<int,int>::const_iterator itr = receivableTechs.find(eCandidateTech);
if ( itr != receivableTechs.end() )
{
iValue = itr->second;
}
else
{
iValue = 0;
}
if ( eBestReceiveTech == NO_TECH || iValue > iBestValue )
{
eBestReceiveTech = eCandidateTech;
iBestValue = iValue;
}
}
}
if (eBestReceiveTech != NO_TECH)
{
iBestValue = 0;
eBestGiveTech = NO_TECH;
for (iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
{
TechTypes eCandidateTech = (TechTypes)((iRandomTechChoiceSeed + iJ)%GC.getNumTechInfos());
setTradeItem(&item, TRADE_TECHNOLOGIES, eCandidateTech);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
eBestGiveTech = eCandidateTech;
break;
}
}
iOurValue = GET_TEAM(getTeam()).AI_techTradeVal(eBestReceiveTech, GET_PLAYER((PlayerTypes)iI).getTeam());
if (eBestGiveTech != NO_TECH)
{
iTheirValue = GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_techTradeVal(eBestGiveTech, getTeam());
}
else
{
iTheirValue = 0;
}
iReceiveGold = 0;
iGiveGold = 0;
if (iTheirValue > iOurValue)
{
iGoldWeight = iTheirValue - iOurValue;
iGoldData = (iGoldWeight * 100) / iGoldValuePercent;
if (iGoldWeight * 100 > iGoldData * iGoldValuePercent)
{
iGoldData++;
}
iGold = std::min(iGoldData, GET_PLAYER((PlayerTypes)iI).AI_maxGoldTrade(getID()));
if (iGold > 0)
{
setTradeItem(&item, TRADE_GOLD, iGold);
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
iReceiveGold = iGold;
iOurValue += (iGold * iGoldValuePercent) / 100;
}
}
}
else if (iOurValue > iTheirValue)
{
iGoldData = ((iOurValue - iTheirValue) * 100) / iGoldValuePercent;
iGold = std::min(iGoldData, AI_maxGoldTrade((PlayerTypes)iI));
if (iGold > 0)
{
setTradeItem(&item, TRADE_GOLD, iGold);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
iGiveGold = iGold;
iTheirValue += (iGold * iGoldValuePercent) / 100;
}
}
}
if (!(GET_PLAYER((PlayerTypes)iI).isHuman()) || (iOurValue >= iTheirValue))
{
if ((iOurValue > ((iTheirValue * 2) / 3)) && (iTheirValue > ((iOurValue * 2) / 3)))
{
ourList.clear();
theirList.clear();
if (eBestGiveTech != NO_TECH)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, eBestGiveTech);
ourList.insertAtEnd(item);
}
setTradeItem(&item, TRADE_TECHNOLOGIES, eBestReceiveTech);
theirList.insertAtEnd(item);
if (iGiveGold != 0)
{
setTradeItem(&item, TRADE_GOLD, iGiveGold);
ourList.insertAtEnd(item);
}
if (iReceiveGold != 0)
{
setTradeItem(&item, TRADE_GOLD, iReceiveGold);
theirList.insertAtEnd(item);
}
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_TRADE_TECH, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_TRADE_TECH));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_DEAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/ abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
}
}
}
}
/************************************************************************************************/
/* Afforess Start 06/16/10 */
/* */
/* Advanced Diplomacy */
/************************************************************************************************/
if (GC.getGameINLINE().isOption(GAMEOPTION_ADVANCED_DIPLOMACY) || GC.getGameINLINE().isOption(GAMEOPTION_RUTHLESS_AI))
{
PROFILE("CvPlayerAI::AI_doDiplo.AdvancedDiplomacyOrRuthless");
//Purchase War Ally
if (GET_PLAYER((PlayerTypes)iI).isHuman() || ((GET_TEAM(getTeam()).getLeaderID() == getID()) && !GET_TEAM(getTeam()).isVassal(GET_PLAYER((PlayerTypes)iI).getTeam())))
{
if ((AI_getMemoryCount(((PlayerTypes)iI), MEMORY_DECLARED_WAR) == 0) && (AI_getMemoryCount(((PlayerTypes)iI), MEMORY_HIRED_WAR_ALLY) == 0))
{
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_TRADE_JOIN_WAR) == 0)
{
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_TRADE_JOIN_WAR), "AI Diplo Trade War") == 0)
{
TeamTypes eBestWarTeam = AI_bestJoinWarTeam((PlayerTypes)iI);
if (eBestWarTeam != NO_TEAM)
{
if (GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_declareWarTrade(eBestWarTeam, getTeam(), true) == NO_DENIAL)
{
if (GET_TEAM(getTeam()).isGoldTrading() || GET_TEAM(getTeam()).isTechTrading() || GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isGoldTrading() || GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isTechTrading())
{
iBestValue = 0;
eBestGiveTech = NO_TECH;
for (iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, iJ);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(10000, "AI Tech Trading #2"));
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestGiveTech = ((TechTypes)iJ);
}
}
}
iOurValue = GET_TEAM(getTeam()).AI_declareWarTradeVal(eBestWarTeam, GET_PLAYER((PlayerTypes)iI).getTeam());
if (eBestGiveTech != NO_TECH)
{
iTheirValue = GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_techTradeVal(eBestGiveTech, getTeam());
}
else
{
iTheirValue = 0;
}
iReceiveGold = 0;
iGiveGold = 0;
if (iTheirValue > iOurValue)
{
iGoldWeight = iTheirValue - iOurValue;
iGoldData = (iGoldWeight * 100) / iGoldValuePercent;
if (iGoldWeight * 100 > iGoldData * iGoldValuePercent)
{
iGoldData++;
}
iGold = std::min(iGoldData, GET_PLAYER((PlayerTypes)iI).AI_maxGoldTrade(getID()));
if (iGold > 0)
{
setTradeItem(&item, TRADE_GOLD, iGold);
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
iReceiveGold = iGold;
iOurValue += (iGold * iGoldValuePercent) / 100;
}
}
}
else if (iOurValue > iTheirValue)
{
iGoldData = ((iOurValue - iTheirValue) * 100) / iGoldValuePercent;
iGold = std::min(iGoldData, AI_maxGoldTrade((PlayerTypes)iI));
if (iGold > 0)
{
setTradeItem(&item, TRADE_GOLD, iGold);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
iGiveGold = iGold;
iTheirValue += (iGold * iGoldValuePercent) / 100;
}
}
}
if (!(GET_PLAYER((PlayerTypes)iI).isHuman()) || (iOurValue >= iTheirValue))
{
if ((iOurValue > ((iTheirValue * 2) / 3)) && (iTheirValue > ((iOurValue * 2) / 3)))
{
ourList.clear();
theirList.clear();
if (eBestGiveTech != NO_TECH)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, eBestGiveTech);
ourList.insertAtEnd(item);
}
setTradeItem(&item, TRADE_WAR, eBestWarTeam);
theirList.insertAtEnd(item);
if (iGiveGold != 0)
{
setTradeItem(&item, TRADE_GOLD, iGiveGold);
ourList.insertAtEnd(item);
}
if (iReceiveGold != 0)
{
setTradeItem(&item, TRADE_GOLD, iReceiveGold);
theirList.insertAtEnd(item);
}
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
m_eDemandWarAgainstTeam = eBestWarTeam;
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_TRADE_JOIN_WAR, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_TRADE_JOIN_WAR));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_JOIN_WAR"), GET_PLAYER(GET_TEAM(eBestWarTeam).getLeaderID()).getCivilizationAdjectiveKey());
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
if (GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_declareWarTrade(eBestWarTeam, getTeam(), true) == NO_DENIAL)
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
else
{
m_eDemandWarAgainstTeam = eBestWarTeam;
}
}
}
}
}
}
}
}
}
}
}
//Broker Peace
if (GET_TEAM(getTeam()).getLeaderID() == getID() && !GC.getGame().isPreviousRequest((PlayerTypes)iI))
{
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_PEACE_PRESSURE) == 0)
{
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_PEACE_PRESSURE), "AI Diplo End War") == 0)
{
eBestTeam = AI_bestMakePeaceTeam((PlayerTypes)iI);
if (eBestTeam != NO_TEAM)
{
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
GC.getGameINLINE().setPreviousRequest((PlayerTypes)iI, true);
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_PEACE_PRESSURE, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_PEACE_PRESSURE));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_MAKE_PEACE_WITH"), GET_PLAYER(GET_TEAM(eBestTeam).getLeaderID()).getCivilizationAdjectiveKey());
pDiplo->setAIContact(true);
pDiplo->setData(eBestTeam);
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
}
}
//Purchase Trade Embargo
if (GET_PLAYER((PlayerTypes)iI).isHuman() || ((GET_TEAM(getTeam()).getLeaderID() == getID()) && !GET_TEAM(getTeam()).isVassal(GET_PLAYER((PlayerTypes)iI).getTeam())))
{
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_TRADE_STOP_TRADING) == 0)
{
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_TRADE_JOIN_WAR), "AI Diplo Trade War") == 0)
{
TeamTypes eBestStopTradeTeam = AI_bestStopTradeTeam((PlayerTypes)iI);
if (eBestStopTradeTeam != NO_TEAM)
{
iBestValue = 0;
eBestGiveTech = NO_TECH;
for (iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, iJ);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(10000, "AI Tech Trading #2"));
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestGiveTech = ((TechTypes)iJ);
}
}
}
iOurValue = AI_stopTradingTradeVal(eBestStopTradeTeam, ((PlayerTypes)iI));
if (eBestGiveTech != NO_TECH)
{
iTheirValue = GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_techTradeVal(eBestGiveTech, getTeam());
}
else
{
iTheirValue = 0;
}
iReceiveGold = 0;
iGiveGold = 0;
if (iTheirValue > iOurValue)
{
iGoldWeight = iTheirValue - iOurValue;
iGoldData = (iGoldWeight * 100) / iGoldValuePercent;
if (iGoldWeight * 100 > iGoldData * iGoldValuePercent)
{
iGoldData++;
}
iGold = std::min(iGoldData, GET_PLAYER((PlayerTypes)iI).AI_maxGoldTrade(getID()));
if (iGold > 0)
{
setTradeItem(&item, TRADE_GOLD, iGold);
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
iReceiveGold = iGold;
iOurValue += (iGold * iGoldValuePercent) / 100;
}
}
}
else if (iOurValue > iTheirValue)
{
iGoldData = ((iOurValue - iTheirValue) * 100) / iGoldValuePercent;
iGold = std::min(iGoldData, AI_maxGoldTrade((PlayerTypes)iI));
if (iGold > 0)
{
setTradeItem(&item, TRADE_GOLD, iGold);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
iGiveGold = iGold;
iTheirValue += (iGold * iGoldValuePercent) / 100;
}
}
}
if (!(GET_PLAYER((PlayerTypes)iI).isHuman()) || (iOurValue >= iTheirValue))
{
if ((iOurValue > ((iTheirValue * 2) / 3)) && (iTheirValue > ((iOurValue * 2) / 3)))
{
ourList.clear();
theirList.clear();
if (eBestGiveTech != NO_TECH)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, eBestGiveTech);
ourList.insertAtEnd(item);
}
setTradeItem(&item, TRADE_EMBARGO, eBestStopTradeTeam);
theirList.insertAtEnd(item);
if (iGiveGold != 0)
{
setTradeItem(&item, TRADE_GOLD, iGiveGold);
ourList.insertAtEnd(item);
}
if (iReceiveGold != 0)
{
setTradeItem(&item, TRADE_GOLD, iReceiveGold);
theirList.insertAtEnd(item);
}
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_TRADE_STOP_TRADING, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_TRADE_STOP_TRADING));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_DEAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
}
}
}
}
}
if (GC.getGameINLINE().isOption(GAMEOPTION_ADVANCED_DIPLOMACY))
{
PROFILE("CvPlayerAI::AI_doDiplo.AdvancedDiplomacy");
//Establish Embassy
if (GET_TEAM(getTeam()).getLeaderID() == getID())
{
if (!GET_TEAM(getTeam()).isHasEmbassy(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_EMBASSY) == 0)
{
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_EMBASSY), "AI Diplo Open Borders") == 0)
{
setTradeItem(&item, TRADE_EMBASSY);
if (canTradeItem(((PlayerTypes)iI), item, true) && GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
ourList.clear();
theirList.clear();
ourList.insertAtEnd(item);
theirList.insertAtEnd(item);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_EMBASSY, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_EMBASSY));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_DEAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
}
}
}
//Open Free Trade
if (GET_TEAM(getTeam()).getLeaderID() == getID())
{
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_OPEN_BORDERS) == 0)
{
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_OPEN_BORDERS), "AI Diplo Limited Borders") == 0)
{
setTradeItem(&item, TRADE_FREE_TRADE_ZONE);
if (canTradeItem(((PlayerTypes)iI), item, true) && GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
ourList.clear();
theirList.clear();
ourList.insertAtEnd(item);
theirList.insertAtEnd(item);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_OPEN_BORDERS, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_OPEN_BORDERS));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_DEAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
}
}
//Open Limited Borders
if (GET_TEAM(getTeam()).getLeaderID() == getID())
{
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_OPEN_BORDERS) == 0)
{
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_OPEN_BORDERS), "AI Diplo Limited Borders") == 0)
{
setTradeItem(&item, TRADE_RITE_OF_PASSAGE);
if (canTradeItem(((PlayerTypes)iI), item, true) && GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
ourList.clear();
theirList.clear();
ourList.insertAtEnd(item);
theirList.insertAtEnd(item);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_OPEN_BORDERS, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_OPEN_BORDERS));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_DEAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
}
}
//Sell contacts to other teams
if (GET_TEAM(getTeam()).getLeaderID() == getID())
{
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_TRADE_CONTACTS) == 0)
{
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_TRADE_CONTACTS) / 2, "AI Diplo Trade Contacts") == 0)
{
TeamTypes eBestTeam = NO_TEAM;
for (iJ = 0; iJ < MAX_CIV_TEAMS; iJ++)
{
CvTeam& kTeam = GET_TEAM((TeamTypes) iJ);
if (kTeam.isAlive() && !(kTeam.isMinorCiv()))
{
setTradeItem(&item, TRADE_CONTACT, (TeamTypes)iJ);
if (canTradeItem((PlayerTypes)iI, item, true))
{
//Yes, this is the first team we can sell contact with... Not like it really matters
eBestTeam = (TeamTypes)iJ;
break;
}
}
}
if (eBestTeam != NO_TEAM)
{
iOurValue = GET_TEAM(getTeam()).AI_contactTradeVal(eBestTeam, GET_PLAYER((PlayerTypes)iI).getTeam());
iGold = std::max(0, GET_PLAYER((PlayerTypes)iI).AI_maxGoldTrade(getID()));
if (iOurValue <= iGold && iOurValue > 0)
{
setTradeItem(&item, TRADE_GOLD, iOurValue);
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
ourList.clear();
theirList.clear();
setTradeItem(&item, TRADE_CONTACT, eBestTeam);
ourList.insertAtEnd(item);
setTradeItem(&item, TRADE_GOLD, iOurValue);
theirList.insertAtEnd(item);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_TRADE_CONTACTS, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_TRADE_CONTACTS));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_DEAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
GC.getGameINLINE().logMsg("Player %d has traded contact of %d to %d for %d gold", getID(), GET_TEAM(eBestTeam).getLeaderID(), iI, iOurValue);
CvWString szBuffer;
switch (AI_getAttitude(GET_TEAM(eBestTeam).getLeaderID()))
{
case ATTITUDE_FURIOUS:
szBuffer = gDLL->getText("TXT_KEY_MISC_TRADED_CONTACT_FURIOUS", getCivilizationDescription(), GET_PLAYER((PlayerTypes)iI).getCivilizationDescription());
break;
case ATTITUDE_ANNOYED:
szBuffer = gDLL->getText("TXT_KEY_MISC_TRADED_CONTACT_ANNOYED", getCivilizationDescription(), GET_PLAYER((PlayerTypes)iI).getCivilizationDescription());
break;
case ATTITUDE_CAUTIOUS:
szBuffer = gDLL->getText("TXT_KEY_MISC_TRADED_CONTACT_CAUTIOUS", getCivilizationDescription(), GET_PLAYER((PlayerTypes)iI).getCivilizationDescription());
break;
case ATTITUDE_PLEASED:
szBuffer = gDLL->getText("TXT_KEY_MISC_TRADED_CONTACT_PLEASED", getCivilizationDescription(), GET_PLAYER((PlayerTypes)iI).getCivilizationDescription());
break;
case ATTITUDE_FRIENDLY:
szBuffer = gDLL->getText("TXT_KEY_MISC_TRADED_CONTACT_FRIENDLY", getCivilizationDescription(), GET_PLAYER((PlayerTypes)iI).getCivilizationDescription());
break;
default:
FAssertMsg(false, "No Valid Attitude");
szBuffer = gDLL->getText("TXT_KEY_MISC_TRADED_CONTACT_CAUTIOUS", getCivilizationDescription(), GET_PLAYER((PlayerTypes)iI).getCivilizationDescription());
break;
}
for (int i = 0; i < MAX_PLAYERS; i++)
{
if (GET_PLAYER((PlayerTypes)i).getTeam() == eBestTeam)
{
MEMORY_TRACK_EXEMPT();
AddDLLMessage(((PlayerTypes)i), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_FEAT_ACCOMPLISHED", MESSAGE_TYPE_MAJOR_EVENT, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"));
}
}
}
}
}
}
}
}
}
{
PROFILE("CvPlayerAI::AI_doDiplo.TradeCeaseRelations");
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_TRADE_CEASE_RELATIONS) == 0)
{
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_TRADE_WORKERS), "AI Diplo Trade Workers") == 0)
{
PlayerTypes eBestCeaseContactWith = NO_PLAYER;
int iBestValue = 750;
for (int iPlayer = 0; iPlayer < MAX_PLAYERS; iPlayer++)
{
CvPlayerAI &kPlayer = GET_PLAYER((PlayerTypes)iPlayer);
if (kPlayer.isAlive() && iPlayer != iI)
{
if (!GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isAtWar(kPlayer.getTeam()) && GET_PLAYER((PlayerTypes)iI).canContact(kPlayer.getID()))
{
if (GET_PLAYER((PlayerTypes)iI).AI_isWillingToTalk(kPlayer.getID()))
{
if (AI_getAttitude(kPlayer.getID()) <= ATTITUDE_CAUTIOUS && GET_PLAYER((PlayerTypes)iI).AI_tradeCeaseRelations(kPlayer.getID(), (PlayerTypes)iI) == NO_DENIAL)
{
int iValue = 100 + GC.getGameINLINE().getSorenRandNum(100, "AI Cease Relations Rand");
if (GET_TEAM(getTeam()).isAtWar(kPlayer.getTeam()))
{
iValue *= 5;
}
else if (GET_TEAM(getTeam()).AI_getWarPlan(kPlayer.getTeam()) != NO_WARPLAN)
{
iValue *= 2;
}
int iWarMongererDivisor = GC.getDefineINT("WARMONGERER_INDEX_ATTITUDE_DIVISOR", 500);
if (iWarMongererDivisor > 0)
{
iValue *= (1 + (2 * std::min(5, kPlayer.getWarMongererIndex() / iWarMongererDivisor)));
}
if (GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_calculateAdjacentLandPlots(kPlayer.getTeam()) > 0)
{
iValue *= 2;
}
if (AI_getAttitude(kPlayer.getID()) == ATTITUDE_FURIOUS) {
iValue *= 3;
iValue /= 2;
}
GC.getGameINLINE().logMsg("%S considering contacting %S to cease contact with %S, iValue: %d", this->getName(), GET_PLAYER((PlayerTypes)iI).getName(), kPlayer.getName(), iValue);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestCeaseContactWith = kPlayer.getID();
}
}
}
}
}
}
if (eBestCeaseContactWith != NO_PLAYER)
{
GC.getGameINLINE().logMsg("%S found best cease contact player %S, with a value of %d", this->getName(), GET_PLAYER(eBestCeaseContactWith).getName(), iBestValue);
iBestValue = 0;
eBestGiveTech = NO_TECH;
iOurValue = AI_tradeCeaseRelationsVal(eBestCeaseContactWith, (PlayerTypes)iI);
iTheirValue = 0;
//Before we give away a tech, let us see if we have the gold to afford a trade
int iOurGold = std::min(((iOurValue - iTheirValue) * 100) / iGoldValuePercent, AI_maxGoldTrade((PlayerTypes)iI));
if (iOurGold < iOurValue)
{
for (iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, iJ);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(100, "AI Tech Trading #2"));
iValue *= GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).getResearchLeft((TechTypes)iJ);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestGiveTech = ((TechTypes)iJ);
}
}
}
}
if (eBestGiveTech != NO_TECH)
{
iTheirValue = GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_techTradeVal(eBestGiveTech, getTeam());
}
else
{
iTheirValue = 0;
}
iReceiveGold = 0;
iGiveGold = 0;
if (iTheirValue > iOurValue)
{
iGold = std::min(((iTheirValue - iOurValue) * 100) / iGoldValuePercent, GET_PLAYER((PlayerTypes)iI).AI_maxGoldTrade(getID()));
if (iGold > 0)
{
setTradeItem(&item, TRADE_GOLD, iGold);
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
iReceiveGold = iGold;
iOurValue += (iGold * iGoldValuePercent) / 100;
}
}
}
else if (iOurValue > iTheirValue)
{
iGold = std::min(((iOurValue - iTheirValue) * 100) / iGoldValuePercent, AI_maxGoldTrade((PlayerTypes)iI));
if (iGold > 0)
{
setTradeItem(&item, TRADE_GOLD, iGold);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
iGiveGold = iGold;
iTheirValue += (iGold * iGoldValuePercent) / 100;
}
}
}
if (iTheirValue > (iOurValue * 3 / 4))
{
ourList.clear();
theirList.clear();
if (eBestGiveTech != NO_TECH)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, eBestGiveTech);
ourList.insertAtEnd(item);
}
setTradeItem(&item, TRADE_CEASE_RELATIONS, eBestCeaseContactWith);
theirList.insertAtEnd(item);
if (iGiveGold != 0)
{
setTradeItem(&item, TRADE_GOLD, iGiveGold);
ourList.insertAtEnd(item);
}
if (iReceiveGold != 0)
{
setTradeItem(&item, TRADE_GOLD, iReceiveGold);
theirList.insertAtEnd(item);
}
if (eBestGiveTech != NO_TECH)
GC.getGameINLINE().logMsg("%S finalizes offer to pay %S to cease contact with %S. iGiveGold: %d, iReceiveGold: %d, eBestGiveTech: %S", this->getName(), GET_PLAYER((PlayerTypes)iI).getName(), GET_PLAYER(eBestCeaseContactWith).getName(), iGiveGold, iReceiveGold, GC.getTechInfo(eBestGiveTech).getDescription());
else
GC.getGameINLINE().logMsg("%S finalizes offer to pay %S to cease contact with %S. iGiveGold: %d, iReceiveGold: %d", this->getName(), GET_PLAYER((PlayerTypes)iI).getName(), GET_PLAYER(eBestCeaseContactWith).getName(), iGiveGold, iReceiveGold);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_TRADE_CEASE_RELATIONS, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_TRADE_BUY_WAR));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_DEAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
else {
GC.getGameINLINE().logMsg("%S found no best player to request to cease contact with", this->getName());
}
}
}
}
//Purchase Workers
{
PROFILE("CvPlayerAI::AI_doDiplo.PurchaseWorker");
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_TRADE_WORKERS) == 0)
{
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_TRADE_WORKERS), "AI Diplo Trade Workers") == 0)
{
if (GET_TEAM(getTeam()).isHasEmbassy(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
CvUnit* pWorker = NULL;
int iNeededWorkers = 0;
//figure out if we need workers or not
for (CvArea* pLoopArea = GC.getMapINLINE().firstArea(&iLoop); pLoopArea != NULL; pLoopArea = GC.getMapINLINE().nextArea(&iLoop))
{
if (pLoopArea->getCitiesPerPlayer(getID()) > 0)
{
iNeededWorkers += AI_neededWorkers(pLoopArea);
}
}
//if we need workers, try and find one to buy
if (iNeededWorkers > 0)
{
for (CvUnit* pLoopUnit = GET_PLAYER((PlayerTypes)iI).firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = GET_PLAYER((PlayerTypes)iI).nextUnit(&iLoop))
{
if (pLoopUnit->canTradeUnit(getID()))
{
setTradeItem(&item, TRADE_WORKER, pLoopUnit->getID());
//if they can trade the worker to us
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
pWorker = pLoopUnit;
break;
}
}
}
if (pWorker != NULL)
{
iTheirValue = GET_PLAYER((PlayerTypes)iI).AI_workerTradeVal(pWorker);
iGold = AI_maxGoldTrade((PlayerTypes)iI);
iGold = std::min(iGold, iTheirValue);
if (iGold >= iTheirValue && iTheirValue > 0)
{
setTradeItem(&item, TRADE_GOLD, iTheirValue);
//if we can trade the gold to them
if (canTradeItem((PlayerTypes)iI, item, true))
{
ourList.clear();
theirList.clear();
setTradeItem(&item, TRADE_GOLD, iTheirValue);
ourList.insertAtEnd(item);
setTradeItem(&item, TRADE_WORKER, pWorker->getID());
theirList.insertAtEnd(item);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_TRADE_WORKERS, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_TRADE_WORKERS));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_DEAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
//GC.getGameINLINE().logMsg("Player %d has traded a worker to %d for %d gold", getID(), iI, iTheirValue);
}
}
}
}
}
}
}
}
}
{
PROFILE("CvPlayerAI::AI_doDiplo.TradeWorker");
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_TRADE_WORKERS) == 0)
{
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_TRADE_WORKERS), "AI Diplo Trade Workers") == 0)
{
if (GET_TEAM(getTeam()).isHasEmbassy(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
CvUnit* pWorker = NULL;
int iNeededWorkers = 0;
//figure out if we need workers or not
for (CvArea* pLoopArea = GC.getMapINLINE().firstArea(&iLoop); pLoopArea != NULL; pLoopArea = GC.getMapINLINE().nextArea(&iLoop))
{
if (pLoopArea->getCitiesPerPlayer(getID()) > 0)
{
iNeededWorkers += AI_neededWorkers(pLoopArea);
}
}
//if we need workers
if (iNeededWorkers > 0)
{
for (CvUnit* pLoopUnit = GET_PLAYER((PlayerTypes)iI).firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = GET_PLAYER((PlayerTypes)iI).nextUnit(&iLoop))
{
if (pLoopUnit->canTradeUnit(getID()))
{
setTradeItem(&item, TRADE_WORKER, pLoopUnit->getID());
//if they can trade the worker to us
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
pWorker = pLoopUnit;
break;
}
}
}
}
if (pWorker != NULL)
{
iTheirValue = GET_PLAYER((PlayerTypes)iI).AI_workerTradeVal(pWorker);
iGold = AI_maxGoldTrade((PlayerTypes)iI);
iGold = std::min(iGold, iTheirValue);
if (iGold >= iTheirValue && iTheirValue > 0)
{
setTradeItem(&item, TRADE_GOLD, iTheirValue);
//if we can trade the gold to them
if (canTradeItem((PlayerTypes)iI, item, true))
{
ourList.clear();
theirList.clear();
setTradeItem(&item, TRADE_GOLD, iTheirValue);
ourList.insertAtEnd(item);
setTradeItem(&item, TRADE_WORKER, pWorker->getID());
theirList.insertAtEnd(item);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_TRADE_WORKERS, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_TRADE_WORKERS));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_DEAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
//GC.getGameINLINE().logMsg("Player %d has traded a worker to %d for %d gold", getID(), iI, iTheirValue);
}
}
}
}
}
}
}
}
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_TRADE_MILITARY_UNITS) == 0)
{
PROFILE("CvPlayerAI::AI_doDiplo.TardeUnits");
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_TRADE_MILITARY_UNITS) / std::max(1, GET_TEAM(getTeam()).getAnyWarPlanCount(true)), "AI Diplo Trade Military Units") == 0)
{
if (GET_TEAM(getTeam()).isHasEmbassy(GET_PLAYER((PlayerTypes)iI).getTeam()) )
{
if (!AI_isFinancialTrouble())
{
int* paiMilitaryUnits;
CvUnit* pLoopUnit;
paiMilitaryUnits = new int[GET_PLAYER((PlayerTypes)iI).getNumUnits()];
for (iJ = 0; iJ < GET_PLAYER((PlayerTypes)iI).getNumUnits(); iJ++)
{
paiMilitaryUnits[iJ] = -1;
}
CvUnit* pBestUnit = NULL;
int iNumTradableUnits = 0;
for (iJ = 0, pLoopUnit = GET_PLAYER((PlayerTypes)iI).firstUnit(&iLoop); pLoopUnit != NULL; iJ++, pLoopUnit = GET_PLAYER((PlayerTypes)iI).nextUnit(&iLoop))
{
if (pLoopUnit->canTradeUnit(getID()))
{
setTradeItem(&item, TRADE_MILITARY_UNIT, pLoopUnit->getID());
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
paiMilitaryUnits[iJ] = pLoopUnit->getID();
iNumTradableUnits++;
}
}
}
TechTypes eBestTech = NO_TECH;
int iBestValue = 0;
if (iNumTradableUnits > 0)
{
for (iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, iJ);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(10000, "AI Tech For Military"));
iValue /= std::max(1, GC.getTechInfo((TechTypes)iJ).getFlavorValue(GC.getInfoTypeForString("FLAVOR_MILITARY")));
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestTech = (TechTypes)iJ;
}
}
}
}
if (eBestTech != NO_TECH)
{
int iUnitValue = 0;
int iTechValue = GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_techTradeVal(eBestTech, getTeam());
for (iJ = 0; iJ < GET_PLAYER((PlayerTypes)iI).getNumUnits(); iJ++)
{
if (paiMilitaryUnits[iJ] > 0)
{
if (iUnitValue > iTechValue)
{
paiMilitaryUnits[iJ] = -1;
}
else
{
iUnitValue += AI_militaryUnitTradeVal(GET_PLAYER((PlayerTypes)iI).getUnit(paiMilitaryUnits[iJ]));
}
}
}
ourList.clear();
theirList.clear();
int iNeededGold = iUnitValue - iTechValue;
//Units are worth more than the tech
if (iNeededGold > 0)
{
iGold = AI_maxGoldTrade((PlayerTypes)iI);
setTradeItem(&item, TRADE_GOLD, std::min(iNeededGold, iGold));
if (canTradeItem((PlayerTypes)iI, item, true))
{
setTradeItem(&item, TRADE_GOLD, std::min(iNeededGold, iGold));
ourList.insertAtEnd(item);
}
}
//The tech is worth more than the units
else if (iNeededGold < 0)
{
iGold = GET_PLAYER((PlayerTypes)iI).AI_maxGoldTrade(getID());
setTradeItem(&item, TRADE_GOLD, std::min(-iNeededGold, iGold));
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
setTradeItem(&item, TRADE_GOLD, std::min(-iNeededGold, iGold));
theirList.insertAtEnd(item);
}
}
//The difference is too large, the trade isn't worth it
if ( std::max(iNeededGold, -iNeededGold) - iGold < 300)
{
for (iJ = 0; iJ < GET_PLAYER((PlayerTypes)iI).getNumUnits(); iJ++)
{
if (paiMilitaryUnits[iJ] > 0)
{
setTradeItem(&item, TRADE_MILITARY_UNIT, paiMilitaryUnits[iJ]);
theirList.insertAtEnd(item);
}
}
setTradeItem(&item, TRADE_TECHNOLOGIES, eBestTech);
ourList.insertAtEnd(item);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_TRADE_MILITARY_UNITS, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_TRADE_MILITARY_UNITS));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_DEAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
//GC.getGameINLINE().logMsg("Player %d has traded military units to %d.", getID(), iI);
}
}
}
SAFE_DELETE_ARRAY(paiMilitaryUnits);
}
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_TRADE_BONUS) == 0)
{
PROFILE("CvPlayerAI::AI_doDiplo.ContactTradeBonus");
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_TRADE_BONUS), "AI Diplo Trade Bonus") == 0)
{
iBestValue = 0;
eBestReceiveBonus = NO_BONUS;
for (iJ = 0; iJ < GC.getNumBonusInfos(); iJ++)
{
if (GET_PLAYER((PlayerTypes)iI).getNumTradeableBonuses((BonusTypes)iJ) > 1)
{
if (GET_PLAYER((PlayerTypes)iI).AI_corporationBonusVal((BonusTypes)iJ) == 0)
{
if (AI_bonusTradeVal(((BonusTypes)iJ), ((PlayerTypes)iI), 1) > 0)
{
setTradeItem(&item, TRADE_RESOURCES, iJ);
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
/************************************************************************************************/
/* Afforess Start 06/16/10 */
/* */
/* Advanced Diplomacy */
/************************************************************************************************/
/*Original Code:
iValue = (1 + GC.getGameINLINE().getSorenRandNum(10000, "AI Bonus Trading #1"));
*/
iValue = (AI_bonusTradeVal(((BonusTypes)iJ), ((PlayerTypes)iI), 1) + GC.getGameINLINE().getSorenRandNum(200, "AI Bonus Trading #1"));
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestReceiveBonus = ((BonusTypes)iJ);
}
}
}
}
}
}
if (eBestReceiveBonus != NO_BONUS)
{
iBestValue = 0;
eBestGiveBonus = NO_BONUS;
for (iJ = 0; iJ < GC.getNumBonusInfos(); iJ++)
{
if (iJ != eBestReceiveBonus)
{
if (getNumTradeableBonuses((BonusTypes)iJ) > 1)
{
if (GET_PLAYER((PlayerTypes)iI).AI_bonusTradeVal(((BonusTypes)iJ), getID(), 1) > 0)
{
setTradeItem(&item, TRADE_RESOURCES, iJ);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(10000, "AI Bonus Trading #2"));
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestGiveBonus = ((BonusTypes)iJ);
}
}
}
}
}
}
if (eBestGiveBonus != NO_BONUS)
{
if (!(GET_PLAYER((PlayerTypes)iI).isHuman()) || (AI_bonusTradeVal(eBestReceiveBonus, ((PlayerTypes)iI), -1) >= GET_PLAYER((PlayerTypes)iI).AI_bonusTradeVal(eBestGiveBonus, getID(), 1)))
{
ourList.clear();
theirList.clear();
setTradeItem(&item, TRADE_RESOURCES, eBestGiveBonus);
ourList.insertAtEnd(item);
setTradeItem(&item, TRADE_RESOURCES, eBestReceiveBonus);
theirList.insertAtEnd(item);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_TRADE_BONUS, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_TRADE_BONUS));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_DEAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/ abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
}
}
}
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_TRADE_MAP) == 0)
{
PROFILE("CvPlayerAI::AI_doDiplo.TradeMaps");
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(GC.getLeaderHeadInfo(getPersonalityType()).getContactRand(CONTACT_TRADE_MAP), "AI Diplo Trade Map") == 0)
{
setTradeItem(&item, TRADE_MAPS);
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true) && canTradeItem(((PlayerTypes)iI), item, true))
{
if (!(GET_PLAYER((PlayerTypes)iI).isHuman()) || (GET_TEAM(getTeam()).AI_mapTradeVal(GET_PLAYER((PlayerTypes)iI).getTeam()) >= GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_mapTradeVal(getTeam())))
{
ourList.clear();
theirList.clear();
setTradeItem(&item, TRADE_MAPS);
ourList.insertAtEnd(item);
setTradeItem(&item, TRADE_MAPS);
theirList.insertAtEnd(item);
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_TRADE_MAP, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_TRADE_MAP));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_OFFER_DEAL"));
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
/************************************************************************************************/
/* REVOLUTIONDCM_MOD 02/04/08 Glider1 */
/* */
/* */
/************************************************************************************************/
// RevolutionDCM start - new diplomacy option
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
// gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
// RevolutionDCM end
/************************************************************************************************/
/* REVOLUTIONDCM_MOD END Glider1 */
/************************************************************************************************/ abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
}
}
if (bNoContactDelay || AI_getContactTimer(((PlayerTypes)iI), CONTACT_TRADE_BUY_WAR) == 0)
{
PROFILE("CvPlayerAI::AI_doDiplo.DeclareWar");
int iDeclareWarTradeRand = GC.getLeaderHeadInfo(getPersonalityType()).getDeclareWarTradeRand();
int iMinAtWarCounter = MAX_INT;
for (iJ = 0; iJ < MAX_CIV_TEAMS; iJ++)
{
if (GET_TEAM((TeamTypes)iJ).isAlive())
{
if (atWar(((TeamTypes)iJ), getTeam()))
{
int iAtWarCounter = GET_TEAM(getTeam()).AI_getAtWarCounter((TeamTypes)iJ);
if (GET_TEAM(getTeam()).AI_getWarPlan((TeamTypes)iJ) == WARPLAN_DOGPILE)
{
iAtWarCounter *= 2;
iAtWarCounter += 5;
}
iMinAtWarCounter = std::min(iAtWarCounter, iMinAtWarCounter);
}
}
}
GC.getGameINLINE().logMsg("%S considering contacting %S to buy a war ally, iMinAtWarCounter: %d, iDeclareWarTradeRand: %d", this->getName(), GET_PLAYER((PlayerTypes)iI).getName(), iMinAtWarCounter, iDeclareWarTradeRand);
if (iMinAtWarCounter < 10)
{
iDeclareWarTradeRand *= iMinAtWarCounter;
iDeclareWarTradeRand /= 10;
iDeclareWarTradeRand ++;
}
if (iMinAtWarCounter < 4)
{
iDeclareWarTradeRand /= 4;
iDeclareWarTradeRand ++;
}
GC.getGameINLINE().logMsg("%S considering contacting %S to buy a war ally, iMinAtWarCounter: %d, adjusted iDeclareWarTradeRand: %d", this->getName(), GET_PLAYER((PlayerTypes)iI).getName(), iMinAtWarCounter, iDeclareWarTradeRand);
if (bNoContactDelay || GC.getGameINLINE().getSorenRandNum(iDeclareWarTradeRand, "AI Diplo Declare War Trade") == 0)
{
iBestValue = 0;
eBestTeam = NO_TEAM;
GC.getGameINLINE().logMsg("%S considering contacting %S to buy a war ally - passed rand check", this->getName(), GET_PLAYER((PlayerTypes)iI).getName());
for (iJ = 0; iJ < MAX_CIV_TEAMS; iJ++)
{
if (GET_TEAM((TeamTypes)iJ).isAlive())
{
if (atWar(((TeamTypes)iJ), getTeam()) && !atWar(((TeamTypes)iJ), GET_PLAYER((PlayerTypes)iI).getTeam()))
{
if (GET_TEAM((TeamTypes)iJ).getAtWarCount(true) < std::max(2, (GC.getGameINLINE().countCivTeamsAlive() / 2)))
{
setTradeItem(&item, TRADE_WAR, iJ);
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(1000, "AI Declare War Trading"));
iValue *= (101 + GET_TEAM((TeamTypes)iJ).AI_getAttitudeWeight(getTeam()));
iValue /= 100;
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestTeam = ((TeamTypes)iJ);
}
}
}
}
}
}
if (eBestTeam != NO_TEAM)
GC.getGameINLINE().logMsg("%S considering contacting %S to buy a war ally, bestValue: %d, best team: %S", this->getName(), GET_PLAYER((PlayerTypes)iI).getName(), iBestValue, GET_PLAYER(GET_TEAM(eBestTeam).getLeaderID()).getCivilizationAdjectiveKey());
else
GC.getGameINLINE().logMsg("%S considering contacting %S to buy a war ally, no best team found!", this->getName(), GET_PLAYER((PlayerTypes)iI).getName());
if (eBestTeam != NO_TEAM)
{
iBestValue = 0;
eBestGiveTech = NO_TECH;
for (iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, iJ);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(100, "AI Tech Trading #2"));
iValue *= GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).getResearchLeft((TechTypes)iJ);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestGiveTech = ((TechTypes)iJ);
}
}
}
iOurValue = GET_TEAM(getTeam()).AI_declareWarTradeVal(eBestTeam, GET_PLAYER((PlayerTypes)iI).getTeam());
if (eBestGiveTech != NO_TECH)
{
iTheirValue = GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_techTradeVal(eBestGiveTech, getTeam());
GC.getGameINLINE().logMsg("%S considering contacting %S to buy a war ally, best tech offer %S at a value of %d", this->getName(), GET_PLAYER((PlayerTypes)iI).getName(), GC.getTechInfo((TechTypes)eBestGiveTech).getDescription(), iTheirValue);
}
else
{
iTheirValue = 0;
GC.getGameINLINE().logMsg("%S considering contacting %S to buy a war ally, no best tech offer", this->getName(), GET_PLAYER((PlayerTypes)iI).getName());
}
int iBestValue2 = 0;
TechTypes eBestGiveTech2 = NO_TECH;
if ((iTheirValue < iOurValue) && (eBestGiveTech != NO_TECH))
{
for (iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
{
if (iJ != eBestGiveTech)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, iJ);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
iValue = (1 + GC.getGameINLINE().getSorenRandNum(100, "AI Tech Trading #2"));
iValue *= GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).getResearchLeft((TechTypes)iJ);
if (iValue > iBestValue)
{
iBestValue2 = iValue;
eBestGiveTech2 = ((TechTypes)iJ);
}
}
}
}
if (eBestGiveTech2 != NO_TECH)
{
int iTechValue = GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_techTradeVal(eBestGiveTech2, getTeam());
iTheirValue += iTechValue;
GC.getGameINLINE().logMsg("%S considering contacting %S to buy a war ally, 2nd best tech offer %S at a value of %d (total: %d)", this->getName(), GET_PLAYER((PlayerTypes)iI).getName(), GC.getTechInfo((TechTypes)eBestGiveTech2).getDescription(), iTechValue, iTheirValue);
}
}
iReceiveGold = 0;
iGiveGold = 0;
if (iTheirValue > iOurValue)
{
iGold = std::min(((iTheirValue - iOurValue) * 100) / iGoldValuePercent, GET_PLAYER((PlayerTypes)iI).AI_maxGoldTrade(getID()));
GC.getGameINLINE().logMsg("%S considering contacting %S to buy a war ally, asking for %d gold", this->getName(), GET_PLAYER((PlayerTypes)iI).getName(), iGold);
if (iGold > 0)
{
setTradeItem(&item, TRADE_GOLD, iGold);
if (GET_PLAYER((PlayerTypes)iI).canTradeItem(getID(), item, true))
{
iReceiveGold = iGold;
iOurValue += (iGold * iGoldValuePercent) / 100;
}
}
}
else if (iOurValue > iTheirValue)
{
iGold = std::min(((iOurValue - iTheirValue) * 100) / iGoldValuePercent, AI_maxGoldTrade((PlayerTypes)iI));
GC.getGameINLINE().logMsg("%S considering contacting %S to buy a war ally, offering %d gold", this->getName(), GET_PLAYER((PlayerTypes)iI).getName(), iGold);
if (iGold > 0)
{
setTradeItem(&item, TRADE_GOLD, iGold);
if (canTradeItem(((PlayerTypes)iI), item, true))
{
iGiveGold = iGold;
iTheirValue += (iGold * iGoldValuePercent) / 100;
}
}
}
GC.getGameINLINE().logMsg("%S considering contacting %S to buy a war ally, iTheirValue: %d, iOurValue: %d", this->getName(), GET_PLAYER((PlayerTypes)iI).getName(), iTheirValue, iOurValue);
if (iTheirValue > (iOurValue * 3 / 4))
{
GC.getGameINLINE().logMsg("%S considering contacting %S to buy a war ally, trade offer proceeding", this->getName(), GET_PLAYER((PlayerTypes)iI).getName());
ourList.clear();
theirList.clear();
if (eBestGiveTech != NO_TECH)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, eBestGiveTech);
ourList.insertAtEnd(item);
}
if (eBestGiveTech2 != NO_TECH)
{
setTradeItem(&item, TRADE_TECHNOLOGIES, eBestGiveTech2);
ourList.insertAtEnd(item);
}
setTradeItem(&item, TRADE_WAR, eBestTeam);
theirList.insertAtEnd(item);
if (iGiveGold != 0)
{
setTradeItem(&item, TRADE_GOLD, iGiveGold);
ourList.insertAtEnd(item);
}
if (iReceiveGold != 0)
{
setTradeItem(&item, TRADE_GOLD, iReceiveGold);
theirList.insertAtEnd(item);
}
if (GET_PLAYER((PlayerTypes)iI).isHuman())
{
if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
m_eDemandWarAgainstTeam = eBestTeam;
AI_changeContactTimer(((PlayerTypes)iI), CONTACT_TRADE_BUY_WAR, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_TRADE_BUY_WAR));
pDiplo = new CvDiploParameters(getID());
FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_JOIN_WAR"), GET_PLAYER(GET_TEAM(eBestTeam).getLeaderID()).getCivilizationAdjectiveKey());
pDiplo->setAIContact(true);
pDiplo->setOurOfferList(theirList);
pDiplo->setTheirOfferList(ourList);
AI_beginDiplomacy(pDiplo, (PlayerTypes)iI);
abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
}
}
else
{
m_eDemandWarAgainstTeam = eBestTeam;
GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
}
}
}
}
}
}
}
}
}
}
}
}
}
}
void CvPlayerAI::resync(bool bWrite, ByteBuffer* pBuffer)
{
CvPlayer::resync(bWrite, pBuffer);
RESYNC_INT(bWrite, pBuffer, m_iPeaceWeight);
RESYNC_INT(bWrite, pBuffer, m_iEspionageWeight);
RESYNC_INT(bWrite, pBuffer, m_iAttackOddsChange);
RESYNC_INT(bWrite, pBuffer, m_iCivicTimer);
RESYNC_INT(bWrite, pBuffer, m_iReligionTimer);
RESYNC_INT(bWrite, pBuffer, m_iExtraGoldTarget);
RESYNC_INT(bWrite, pBuffer, m_turnsSinceLastRevolution);
RESYNC_INT(bWrite, pBuffer, m_iCivicSwitchMinDeltaThreshold);
RESYNC_INT(bWrite, pBuffer, m_iStrategyHash);
RESYNC_INT(bWrite, pBuffer, m_iStrategyHashCacheTurn);
RESYNC_INT(bWrite, pBuffer, m_iStrategyRand);
RESYNC_INT(bWrite, pBuffer, m_iVictoryStrategyHash);
RESYNC_INT(bWrite, pBuffer, m_iVictoryStrategyHashCacheTurn);
RESYNC_INT(bWrite, pBuffer, m_bPushReligiousVictory);
RESYNC_INT(bWrite, pBuffer, m_bConsiderReligiousVictory);
RESYNC_INT(bWrite, pBuffer, m_bHasInquisitionTarget);
RESYNC_INT(bWrite, pBuffer, m_iAveragesCacheTurn);
RESYNC_INT(bWrite, pBuffer, m_iAverageGreatPeopleMultiplier);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_YIELD_TYPES, m_aiAverageYieldMultiplier);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_COMMERCE_TYPES, m_aiAverageCommerceMultiplier);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_COMMERCE_TYPES, m_aiAverageCommerceExchange);
RESYNC_INT(bWrite, pBuffer, m_iUpgradeUnitsCacheTurn);
RESYNC_INT(bWrite, pBuffer, m_iUpgradeUnitsCachedExpThreshold);
RESYNC_INT(bWrite, pBuffer, m_iUpgradeUnitsCachedGold);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_UNITAI_TYPES, m_aiNumTrainAIUnits);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_UNITAI_TYPES, m_aiNumAIUnits);
RESYNC_INT_ARRAY(bWrite, pBuffer, MAX_PLAYERS, m_aiSameReligionCounter);
RESYNC_INT_ARRAY(bWrite, pBuffer, MAX_PLAYERS, m_aiDifferentReligionCounter);
RESYNC_INT_ARRAY(bWrite, pBuffer, MAX_PLAYERS, m_aiFavoriteCivicCounter);
RESYNC_INT_ARRAY(bWrite, pBuffer, MAX_PLAYERS, m_aiBonusTradeCounter);
RESYNC_INT_ARRAY(bWrite, pBuffer, MAX_PLAYERS, m_aiPeacetimeTradeValue);
RESYNC_INT_ARRAY(bWrite, pBuffer, MAX_PLAYERS, m_aiPeacetimeGrantValue);
RESYNC_INT_ARRAY(bWrite, pBuffer, MAX_PLAYERS, m_aiGoldTradedTo);
RESYNC_INT_ARRAY(bWrite, pBuffer, MAX_PLAYERS, m_aiAttitudeExtra);
RESYNC_BOOL_ARRAY(bWrite, pBuffer, MAX_PLAYERS, m_abFirstContact);
for (int i = 0; i < MAX_PLAYERS; i++)
{
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_CONTACT_TYPES, m_aaiContactTimer[i]);
}
for (int i = 0; i < MAX_PLAYERS; i++)
{
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_MEMORY_TYPES, m_aaiMemoryCount[i]);
}
RESYNC_BOOL(bWrite, pBuffer, m_bWasFinancialTrouble);
RESYNC_INT(bWrite, pBuffer, m_iTurnLastProductionDirty);
if (bWrite)
{
uint iSize = m_aiAICitySites.size();
pBuffer->putInt(iSize);
std::vector<int>::iterator it;
for (it = m_aiAICitySites.begin(); it != m_aiAICitySites.end(); ++it)
{
pBuffer->putInt(*it);
}
}
else
{
m_aiAICitySites.clear();
int iSize = pBuffer->getInt();
for (int iI = 0; iI < iSize; iI++)
{
m_aiAICitySites.push_back(pBuffer->getInt());
}
}
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumUnitClassInfos(), m_aiUnitClassWeights);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumUnitCombatInfos(), m_aiUnitCombatWeights);
RESYNC_INT_ARRAY(bWrite, pBuffer, MAX_PLAYERS, m_aiCloseBordersAttitudeCache);
RESYNC_INT_WITH_CAST(bWrite, pBuffer, m_eBestResearchTarget, TechTypes);
RESYNC_INT_WITH_CAST(bWrite, pBuffer, m_eEspionagePlan, EspionagePlans);
}
//
// read object from a stream
// used during load
//
void CvPlayerAI::read(FDataStreamBase* pStream)
{
CvTaggedSaveFormatWrapper& wrapper = CvTaggedSaveFormatWrapper::getSaveFormatWrapper();
wrapper.AttachToStream(pStream);
WRAPPER_READ_OBJECT_START(wrapper);
CvPlayer::read(pStream); // read base class data first
uint uiFlag=0;
WRAPPER_READ(wrapper, "CvPlayerAI", &uiFlag); // flags for expansion
if ( (uiFlag & PLAYER_UI_FLAG_OMITTED) == 0 )
{
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iPeaceWeight);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iEspionageWeight);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iAttackOddsChange);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iCivicTimer);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iReligionTimer);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iExtraGoldTarget);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_turnsSinceLastRevolution);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iCivicSwitchMinDeltaThreshold);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iStrategyHash);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iStrategyHashCacheTurn);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/18/10 jdog5000 */
/* */
/* Victory Strategy AI, War strategy AI */
/************************************************************************************************/
if( uiFlag < 3 )
{
m_iStrategyHash = 0;
m_iStrategyHashCacheTurn = -1;
}
if( uiFlag > 2 )
{
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iStrategyRand);
}
else
{
m_iStrategyRand = 0;
}
if( uiFlag > 0 )
{
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iVictoryStrategyHash);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iVictoryStrategyHashCacheTurn);
}
else
{
m_iVictoryStrategyHash = 0;
m_iVictoryStrategyHashCacheTurn = -1;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
/************************************************************************************************/
/* RevDCM Start 12/9/09 */
/* */
/* Inquisitions */
/************************************************************************************************/
if( uiFlag > 1 )
{
WRAPPER_READ(wrapper, "CvPlayerAI", &m_bPushReligiousVictory);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_bConsiderReligiousVictory);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_bHasInquisitionTarget);
}
else
{
m_bPushReligiousVictory = false;
m_bConsiderReligiousVictory = false;
m_bHasInquisitionTarget = false;
}
/************************************************************************************************/
/* RevDCM END */
/************************************************************************************************/
WRAPPER_READ(wrapper, "CvPlayerAI", (int*)&m_iAveragesCacheTurn);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iAverageGreatPeopleMultiplier);
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", NUM_YIELD_TYPES, m_aiAverageYieldMultiplier);
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", NUM_COMMERCE_TYPES, m_aiAverageCommerceMultiplier);
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", NUM_COMMERCE_TYPES, m_aiAverageCommerceExchange);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iUpgradeUnitsCacheTurn);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iUpgradeUnitsCachedExpThreshold);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iUpgradeUnitsCachedGold);
WRAPPER_READ_OPTIONAL_CLASS_ARRAY(wrapper, "CvPlayerAI", REMAPPED_CLASS_TYPE_UNITAIS, NUM_UNITAI_TYPES, m_aiNumTrainAIUnits);
WRAPPER_READ_OPTIONAL_CLASS_ARRAY(wrapper, "CvPlayerAI", REMAPPED_CLASS_TYPE_UNITAIS, NUM_UNITAI_TYPES, m_aiNumAIUnits);
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiSameReligionCounter);
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiDifferentReligionCounter);
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiFavoriteCivicCounter);
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiBonusTradeCounter);
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiPeacetimeTradeValue);
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiPeacetimeGrantValue);
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiGoldTradedTo);
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiAttitudeExtra);
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_abFirstContact);
for (int i = 0; i < MAX_PLAYERS; i++)
{
if (GC.getLoadedInitCore().getGameSaveSvnRev() < 688)
{
//Added new contact types, have to compensate
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", NUM_CONTACT_TYPES - 3, m_aaiContactTimer[i]);
m_aaiContactTimer[i][NUM_CONTACT_TYPES - 3] = 0;
m_aaiContactTimer[i][NUM_CONTACT_TYPES - 2] = 0;
m_aaiContactTimer[i][NUM_CONTACT_TYPES - 1] = 0;
}
else if (GC.getLoadedInitCore().getGameSaveSvnRev() < 726)
{
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", NUM_CONTACT_TYPES - 2, m_aaiContactTimer[i]);
m_aaiContactTimer[i][NUM_CONTACT_TYPES - 2] = 0;
m_aaiContactTimer[i][NUM_CONTACT_TYPES - 1] = 0;
}
else
{
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", NUM_CONTACT_TYPES, m_aaiContactTimer[i]);
}
}
for (int i = 0; i < MAX_PLAYERS; i++)
{
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", NUM_MEMORY_TYPES, m_aaiMemoryCount[i]);
}
WRAPPER_READ(wrapper, "CvPlayerAI", &m_bWasFinancialTrouble);
WRAPPER_READ(wrapper, "CvPlayerAI", &m_iTurnLastProductionDirty);
{
m_aiAICitySites.clear();
uint iSize;
WRAPPER_READ(wrapper, "CvPlayerAI", &iSize);
for (uint i = 0; i < iSize; i++)
{
int iCitySite;
WRAPPER_READ(wrapper, "CvPlayerAI", &iCitySite);
m_aiAICitySites.push_back(iCitySite);
}
}
// This is redundant - we don;t save the bonus values any more with saves, but read in case we have an old format file
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvPlayerAI", REMAPPED_CLASS_TYPE_BONUSES, GC.getNumBonusInfos(), m_aiBonusValue);
// and clear unconditioanlly
AI_updateBonusValue();
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvPlayerAI", REMAPPED_CLASS_TYPE_UNIT_CLASSES, GC.getNumUnitClassInfos(), m_aiUnitClassWeights);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvPlayerAI", REMAPPED_CLASS_TYPE_COMBATINFOS, GC.getNumUnitCombatInfos(), m_aiUnitCombatWeights);
WRAPPER_READ_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiCloseBordersAttitudeCache);
m_eBestResearchTarget = NO_TECH;
WRAPPER_READ_CLASS_ENUM(wrapper, "CvPlayerAI", REMAPPED_CLASS_TYPE_TECHS, (int*)&m_eBestResearchTarget);
WRAPPER_READ(wrapper, "CvPlayerAI", (int*)&m_eEspionagePlan);
}
WRAPPER_READ_OBJECT_END(wrapper);
// If the total number of batb units is getting dangerously close to the limit we can support
// cull some animals. Note - this has to be done in PlayerAI not Player, because kills won't
// operate correctly until AI struictures are initilaized (class counting for example)
if ( isBarbarian() && getNumUnits() > MAX_BARB_UNITS_FOR_SPAWNING )
{
int iCull = getNumUnits() - MAX_BARB_UNITS_FOR_SPAWNING;
int iLoop;
CvUnit* pLoopUnit = firstUnit(&iLoop);
while( iCull-- > 0 )
{
while( pLoopUnit != NULL && !pLoopUnit->isAnimal() )
{
pLoopUnit = nextUnit(&iLoop);
}
if ( pLoopUnit != NULL )
{
pLoopUnit->kill(false);
pLoopUnit = nextUnit(&iLoop);
}
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 09/03/09 poyuzhe & jdog5000 */
/* */
/* Efficiency */
/************************************************************************************************/
// From Sanguo Mod Performance, ie the CAR Mod
// Attitude cache
AI_invalidateAttitudeCache();
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
//
// save object to a stream
// used during save
//
void CvPlayerAI::write(FDataStreamBase* pStream)
{
PROFILE_FUNC();
CvTaggedSaveFormatWrapper& wrapper = CvTaggedSaveFormatWrapper::getSaveFormatWrapper();
wrapper.AttachToStream(pStream);
WRAPPER_WRITE_OBJECT_START(wrapper);
CvPlayer::write(pStream); // write base class data first
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/18/10 jdog5000 */
/* */
/* Victory Strategy AI */
/************************************************************************************************/
/*
uint uiFlag=0;
*/
// Flag for type of save
uint uiFlag=3;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
if ( !m_bEverAlive )
{
uiFlag |= PLAYER_UI_FLAG_OMITTED;
}
WRAPPER_WRITE(wrapper, "CvPlayerAI", uiFlag); // flag for expansion
if ( m_bEverAlive )
{
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iPeaceWeight);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iEspionageWeight);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iAttackOddsChange);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iCivicTimer);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iReligionTimer);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iExtraGoldTarget);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_turnsSinceLastRevolution);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iCivicSwitchMinDeltaThreshold);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iStrategyHash);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iStrategyHashCacheTurn);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/18/10 jdog5000 */
/* */
/* Victory Strategy AI */
/************************************************************************************************/
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iStrategyRand);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iVictoryStrategyHash);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iVictoryStrategyHashCacheTurn);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
/************************************************************************************************/
/* RevDCM Start 12/9/09 */
/* */
/* Inquisitions */
/************************************************************************************************/
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_bPushReligiousVictory);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_bConsiderReligiousVictory);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_bHasInquisitionTarget);
/************************************************************************************************/
/* RevDCM END */
/************************************************************************************************/
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iAveragesCacheTurn);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iAverageGreatPeopleMultiplier);
WRAPPER_WRITE_ARRAY(wrapper, "CvPlayerAI", NUM_YIELD_TYPES, m_aiAverageYieldMultiplier);
WRAPPER_WRITE_ARRAY(wrapper, "CvPlayerAI", NUM_COMMERCE_TYPES, m_aiAverageCommerceMultiplier);
WRAPPER_WRITE_ARRAY(wrapper, "CvPlayerAI", NUM_COMMERCE_TYPES, m_aiAverageCommerceExchange);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iUpgradeUnitsCacheTurn);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iUpgradeUnitsCachedExpThreshold);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iUpgradeUnitsCachedGold);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvPlayerAI", REMAPPED_CLASS_TYPE_UNITAIS, NUM_UNITAI_TYPES, m_aiNumTrainAIUnits);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvPlayerAI", REMAPPED_CLASS_TYPE_UNITAIS, NUM_UNITAI_TYPES, m_aiNumAIUnits);
WRAPPER_WRITE_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiSameReligionCounter);
WRAPPER_WRITE_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiDifferentReligionCounter);
WRAPPER_WRITE_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiFavoriteCivicCounter);
WRAPPER_WRITE_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiBonusTradeCounter);
WRAPPER_WRITE_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiPeacetimeTradeValue);
WRAPPER_WRITE_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiPeacetimeGrantValue);
WRAPPER_WRITE_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiGoldTradedTo);
WRAPPER_WRITE_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiAttitudeExtra);
WRAPPER_WRITE_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_abFirstContact);
for (int i = 0; i < MAX_PLAYERS; i++)
{
WRAPPER_WRITE_ARRAY(wrapper, "CvPlayerAI", NUM_CONTACT_TYPES, m_aaiContactTimer[i]);
}
for (int i = 0; i < MAX_PLAYERS; i++)
{
WRAPPER_WRITE_ARRAY(wrapper, "CvPlayerAI", NUM_MEMORY_TYPES, m_aaiMemoryCount[i]);
}
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_bWasFinancialTrouble);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_iTurnLastProductionDirty);
{
uint iSize = m_aiAICitySites.size();
WRAPPER_WRITE(wrapper, "CvPlayerAI", iSize);
std::vector<int>::iterator it;
for (it = m_aiAICitySites.begin(); it != m_aiAICitySites.end(); ++it)
{
WRAPPER_WRITE_DECORATED(wrapper, "CvPlayerAI", (*it), "iCitySite");
}
}
//Bonus values no longer saved - recalculated each turn anyway
//WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvPlayerAI", REMAPPED_CLASS_TYPE_BONUSES, GC.getNumBonusInfos(), m_aiBonusValue);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvPlayerAI", REMAPPED_CLASS_TYPE_UNIT_CLASSES, GC.getNumUnitClassInfos(), m_aiUnitClassWeights);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvPlayerAI", REMAPPED_CLASS_TYPE_COMBATINFOS, GC.getNumUnitCombatInfos(), m_aiUnitCombatWeights);
WRAPPER_WRITE_ARRAY(wrapper, "CvPlayerAI", MAX_PLAYERS, m_aiCloseBordersAttitudeCache);
WRAPPER_WRITE_CLASS_ENUM(wrapper, "CvPlayerAI", REMAPPED_CLASS_TYPE_TECHS, m_eBestResearchTarget);
WRAPPER_WRITE(wrapper, "CvPlayerAI", m_eEspionagePlan);
}
WRAPPER_WRITE_OBJECT_END(wrapper);
}
int CvPlayerAI::AI_eventValue(EventTypes eEvent, const EventTriggeredData& kTriggeredData) const
{
CvEventTriggerInfo& kTrigger = GC.getEventTriggerInfo(kTriggeredData.m_eTrigger);
CvEventInfo& kEvent = GC.getEventInfo(eEvent);
int iNumCities = getNumCities();
CvCity* pCity = getCity(kTriggeredData.m_iCityId);
CvPlot* pPlot = GC.getMapINLINE().plot(kTriggeredData.m_iPlotX, kTriggeredData.m_iPlotY);
CvUnit* pUnit = getUnit(kTriggeredData.m_iUnitId);
int iHappy = 0;
int iHealth = 0;
int aiYields[NUM_YIELD_TYPES];
int aiCommerceYields[NUM_COMMERCE_TYPES];
for (int iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
aiYields[iI] = 0;
}
for (int iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
aiCommerceYields[iI] = 0;
}
if (NO_PLAYER != kTriggeredData.m_eOtherPlayer)
{
if (kEvent.isDeclareWar())
{
switch (AI_getAttitude(kTriggeredData.m_eOtherPlayer))
{
case ATTITUDE_FURIOUS:
case ATTITUDE_ANNOYED:
case ATTITUDE_CAUTIOUS:
if (GET_TEAM(getTeam()).getDefensivePower() < GET_TEAM(GET_PLAYER(kTriggeredData.m_eOtherPlayer).getTeam()).getPower(true))
{
return -MAX_INT + 1;
}
break;
case ATTITUDE_PLEASED:
case ATTITUDE_FRIENDLY:
return -MAX_INT + 1;
break;
}
}
}
//Proportional to #turns in the game...
//(AI evaluation will generally assume proper game speed scaling!)
int iGameSpeedPercent = GC.getGameSpeedInfo(GC.getGame().getGameSpeedType()).getVictoryDelayPercent();
int iValue = GC.getGameINLINE().getSorenRandNum(kEvent.getAIValue(), "AI Event choice");
iValue += (getEventCost(eEvent, kTriggeredData.m_eOtherPlayer, false) + getEventCost(eEvent, kTriggeredData.m_eOtherPlayer, true)) / 2;
iValue += kEvent.getEspionagePoints();
if (kEvent.getTech() != NO_TECH)
{
iValue += (GET_TEAM(getTeam()).getResearchCost((TechTypes)kEvent.getTech()) * kEvent.getTechPercent()) / 100;
}
if (kEvent.getUnitClass() != NO_UNITCLASS)
{
UnitTypes eUnit = (UnitTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(kEvent.getUnitClass());
if (eUnit != NO_UNIT)
{
//Altough AI_unitValue compares well within units, the value is somewhat independent of cost
int iUnitValue = GC.getUnitInfo(eUnit).getProductionCost();
if (iUnitValue > 0)
{
iUnitValue *= 2;
}
else if (iUnitValue == -1)
{
iUnitValue = 200; //Great Person?
}
iUnitValue *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getTrainPercent();
iValue += kEvent.getNumUnits() * iUnitValue;
}
}
if (kEvent.isDisbandUnit())
{
CvUnit* pUnit = getUnit(kTriggeredData.m_iUnitId);
if (NULL != pUnit)
{
int iUnitValue = pUnit->getUnitInfo().getProductionCost();
if (iUnitValue > 0)
{
iUnitValue *= 2;
}
else if (iUnitValue == -1)
{
iUnitValue = 200; //Great Person?
}
iUnitValue *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getTrainPercent();
iValue -= iUnitValue;
}
}
if (kEvent.getBuildingClass() != NO_BUILDINGCLASS)
{
BuildingTypes eBuilding = (BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(kEvent.getBuildingClass());
if (eBuilding != NO_BUILDING)
{
if (pCity)
{
//iValue += kEvent.getBuildingChange() * pCity->AI_buildingValue(eBuilding);
int iBuildingValue = GC.getBuildingInfo(eBuilding).getProductionCost();
if (iBuildingValue > 0)
{
iBuildingValue *= 2;
}
else if (iBuildingValue == -1)
{
iBuildingValue = 300; //Shrine?
}
iBuildingValue *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getConstructPercent();
iValue += kEvent.getBuildingChange() * iBuildingValue;
}
}
}
TechTypes eBestTech = NO_TECH;
int iBestValue = 0;
for (int iTech = 0; iTech < GC.getNumTechInfos(); ++iTech)
{
if (canResearch((TechTypes)iTech))
{
if (NO_PLAYER == kTriggeredData.m_eOtherPlayer || GET_TEAM(GET_PLAYER(kTriggeredData.m_eOtherPlayer).getTeam()).isHasTech((TechTypes)iTech))
{
int iValue = 0;
for (int i = 0; i < GC.getNumFlavorTypes(); ++i)
{
iValue += kEvent.getTechFlavorValue(i) * GC.getTechInfo((TechTypes)iTech).getFlavorValue(i);
}
if (iValue > iBestValue)
{
eBestTech = (TechTypes)iTech;
iBestValue = iValue;
}
}
}
}
if (eBestTech != NO_TECH)
{
iValue += (GET_TEAM(getTeam()).getResearchCost(eBestTech) * kEvent.getTechPercent()) / 100;
}
if (kEvent.isGoldenAge())
{
iValue += AI_calculateGoldenAgeValue();
}
{ //Yield and other changes
if (kEvent.getNumBuildingYieldChanges() > 0)
{
for (int iBuildingClass = 0; iBuildingClass < GC.getNumBuildingClassInfos(); ++iBuildingClass)
{
for (int iYield = 0; iYield < NUM_YIELD_TYPES; ++iYield)
{
aiYields[iYield] += kEvent.getBuildingYieldChange(iBuildingClass, iYield);
}
}
}
if (kEvent.getNumBuildingCommerceChanges() > 0)
{
for (int iBuildingClass = 0; iBuildingClass < GC.getNumBuildingClassInfos(); ++iBuildingClass)
{
for (int iCommerce = 0; iCommerce < NUM_COMMERCE_TYPES; ++iCommerce)
{
aiCommerceYields[iCommerce] += kEvent.getBuildingCommerceChange(iBuildingClass, iCommerce);
}
}
}
if (kEvent.getNumBuildingHappyChanges() > 0)
{
for (int iBuildingClass = 0; iBuildingClass < GC.getNumBuildingClassInfos(); ++iBuildingClass)
{
iHappy += kEvent.getBuildingHappyChange(iBuildingClass);
}
}
if (kEvent.getNumBuildingHealthChanges() > 0)
{
for (int iBuildingClass = 0; iBuildingClass < GC.getNumBuildingClassInfos(); ++iBuildingClass)
{
iHealth += kEvent.getBuildingHealthChange(iBuildingClass);
}
}
}
if (kEvent.isCityEffect())
{
int iCityPopulation = -1;
int iCityTurnValue = 0;
if (NULL != pCity)
{
iCityPopulation = pCity->getPopulation();
for (int iSpecialist = 0; iSpecialist < GC.getNumSpecialistInfos(); ++iSpecialist)
{
if (kEvent.getFreeSpecialistCount(iSpecialist) > 0)
{
iCityTurnValue += (pCity->AI_specialistValue((SpecialistTypes)iSpecialist, false, false) / 300);
}
}
}
if (-1 == iCityPopulation)
{
//What is going on here?
iCityPopulation = 5;
}
iCityTurnValue += ((iHappy + kEvent.getHappy()) * 8);
iCityTurnValue += ((iHealth + kEvent.getHealth()) * 6);
iCityTurnValue += aiYields[YIELD_FOOD] * 5;
iCityTurnValue += aiYields[YIELD_PRODUCTION] * 5;
iCityTurnValue += aiYields[YIELD_COMMERCE] * 3;
iCityTurnValue += aiCommerceYields[COMMERCE_RESEARCH] * 3;
iCityTurnValue += aiCommerceYields[COMMERCE_GOLD] * 3;
iCityTurnValue += aiCommerceYields[COMMERCE_CULTURE] * 1;
iCityTurnValue += aiCommerceYields[COMMERCE_ESPIONAGE] * 2;
iValue += (iCityTurnValue * 20 * iGameSpeedPercent) / 100;
iValue += kEvent.getFood();
iValue += kEvent.getFoodPercent() / 4;
iValue += kEvent.getPopulationChange() * 30;
iValue -= kEvent.getRevoltTurns() * (12 + iCityPopulation * 16);
iValue -= (kEvent.getHurryAnger() * 6 * GC.getDefineINT("HURRY_ANGER_DIVISOR") * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getHurryConscriptAngerPercent()) / 100;
iValue += kEvent.getHappyTurns() * 10;
iValue += kEvent.getCulture() / 2;
}
else if (!kEvent.isOtherPlayerCityEffect())
{
int iPerTurnValue = 0;
iPerTurnValue += iNumCities * ((iHappy * 4) + (kEvent.getHappy() * 8));
iPerTurnValue += iNumCities * ((iHealth * 3) + (kEvent.getHealth() * 6));
iValue += (iPerTurnValue * 20 * iGameSpeedPercent) / 100;
iValue += (kEvent.getFood() * iNumCities);
iValue += (kEvent.getFoodPercent() * iNumCities) / 4;
iValue += (kEvent.getPopulationChange() * iNumCities * 40);
iValue -= (iNumCities * kEvent.getHurryAnger() * 6 * GC.getDefineINT("HURRY_ANGER_DIVISOR") * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getHurryConscriptAngerPercent()) / 100;
iValue += iNumCities * kEvent.getHappyTurns() * 10;
iValue += iNumCities * kEvent.getCulture() / 2;
}
int iBonusValue = 0;
if (NO_BONUS != kEvent.getBonus())
{
iBonusValue = AI_bonusVal((BonusTypes)kEvent.getBonus());
}
if (NULL != pPlot)
{
if (kEvent.getFeatureChange() != 0)
{
int iOldFeatureValue = 0;
int iNewFeatureValue = 0;
if (pPlot->getFeatureType() != NO_FEATURE)
{
//*grumble* who tied feature production to builds rather than the feature...
iOldFeatureValue = GC.getFeatureInfo(pPlot->getFeatureType()).getHealthPercent();
if (kEvent.getFeatureChange() > 0)
{
FeatureTypes eFeature = (FeatureTypes)kEvent.getFeature();
FAssert(eFeature != NO_FEATURE);
if (eFeature != NO_FEATURE)
{
iNewFeatureValue = GC.getFeatureInfo(eFeature).getHealthPercent();
}
}
iValue += ((iNewFeatureValue - iOldFeatureValue) * iGameSpeedPercent) / 100;
}
}
if (kEvent.getImprovementChange() > 0)
{
iValue += (30 * iGameSpeedPercent) / 100;
}
else if (kEvent.getImprovementChange() < 0)
{
iValue -= (30 * iGameSpeedPercent) / 100;
}
if (kEvent.getRouteChange() > 0)
{
iValue += (10 * iGameSpeedPercent) / 100;
}
else if (kEvent.getRouteChange() < 0)
{
iValue -= (10 * iGameSpeedPercent) / 100;
}
if (kEvent.getBonusChange() > 0)
{
iValue += (iBonusValue * 15 * iGameSpeedPercent) / 100;
}
else if (kEvent.getBonusChange() < 0)
{
iValue -= (iBonusValue * 15 * iGameSpeedPercent) / 100;
}
for (int i = 0; i < NUM_YIELD_TYPES; ++i)
{
if (0 != kEvent.getPlotExtraYield(i))
{
if (pPlot->getWorkingCity() != NULL)
{
FAssertMsg(pPlot->getWorkingCity()->getOwner() == getID(), "Event creates a boni for another player?");
aiYields[i] += kEvent.getPlotExtraYield(i);
}
else
{
iValue += (20 * 8 * kEvent.getPlotExtraYield(i) * iGameSpeedPercent) / 100;
}
}
}
}
if (NO_BONUS != kEvent.getBonusRevealed())
{
iValue += (iBonusValue * 10 * iGameSpeedPercent) / 100;
}
if (NULL != pUnit)
{
iValue += (2 * pUnit->baseCombatStr() * kEvent.getUnitExperience() * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getTrainPercent()) / 100;
iValue -= 10 * kEvent.getUnitImmobileTurns();
}
{
int iPromotionValue = 0;
for (int i = 0; i < GC.getNumUnitCombatInfos(); ++i)
{
if (NO_PROMOTION != kEvent.getUnitCombatPromotion(i))
{
int iLoop;
for (CvUnit* pLoopUnit = firstUnit(&iLoop); NULL != pLoopUnit; pLoopUnit = nextUnit(&iLoop))
{
//if (pLoopUnit->getUnitCombatType() == i)
//{
// if (!pLoopUnit->isHasPromotion((PromotionTypes)kEvent.getUnitCombatPromotion(i)))
// {
// iPromotionValue += 5 * pLoopUnit->baseCombatStr();
// }
//}
//TB SubCombat Mod Begin
if (pLoopUnit->hasCombatType((UnitCombatTypes)i))
{
if (!pLoopUnit->isHasPromotion((PromotionTypes)kEvent.getUnitCombatPromotion(i)))
{
iPromotionValue += 5 * pLoopUnit->baseCombatStr();
}
}
//TB SubCombat Mod End
}
iPromotionValue += iNumCities * 50;
}
}
iValue += (iPromotionValue * iGameSpeedPercent) / 100;
}
if (kEvent.getFreeUnitSupport() != 0)
{
iValue += (20 * kEvent.getFreeUnitSupport() * iGameSpeedPercent) / 100;
}
if (kEvent.getInflationModifier() != 0)
{
iValue -= (20 * kEvent.getInflationModifier() * calculatePreInflatedCosts() * iGameSpeedPercent) / 100;
}
if (kEvent.getSpaceProductionModifier() != 0)
{
iValue += ((20 + iNumCities) * getSpaceProductionModifier() * iGameSpeedPercent) / 100;
}
int iOtherPlayerAttitudeWeight = 0;
if (kTriggeredData.m_eOtherPlayer != NO_PLAYER)
{
iOtherPlayerAttitudeWeight = AI_getAttitudeWeight(kTriggeredData.m_eOtherPlayer);
iOtherPlayerAttitudeWeight += 10 - GC.getGame().getSorenRandNum(20, "AI event value attitude");
}
//Religion
if (kTriggeredData.m_eReligion != NO_RELIGION)
{
ReligionTypes eReligion = kTriggeredData.m_eReligion;
int iReligionValue = 15;
if (getStateReligion() == eReligion)
{
iReligionValue += 15;
}
if (hasHolyCity(eReligion))
{
iReligionValue += 15;
}
iValue += (kEvent.getConvertOwnCities() * iReligionValue * iGameSpeedPercent) / 100;
if (kEvent.getConvertOtherCities() > 0)
{
//Don't like them much = fairly indifferent, hate them = negative.
iValue += (kEvent.getConvertOtherCities() * (iOtherPlayerAttitudeWeight + 50) * iReligionValue * iGameSpeedPercent) / 15000;
}
}
if (NO_PLAYER != kTriggeredData.m_eOtherPlayer)
{
CvPlayerAI& kOtherPlayer = GET_PLAYER(kTriggeredData.m_eOtherPlayer);
int iDiploValue = 0;
//if we like this player then value positive attitude, if however we really hate them then
//actually value negative attitude.
iDiploValue += ((iOtherPlayerAttitudeWeight + 50) * kEvent.getAttitudeModifier() * GET_PLAYER(kTriggeredData.m_eOtherPlayer).getPower()) / std::max(1, getPower());
if (kEvent.getTheirEnemyAttitudeModifier() != 0)
{
//Oh wow this sure is mildly complicated.
TeamTypes eWorstEnemy = GET_TEAM(GET_PLAYER(kTriggeredData.m_eOtherPlayer).getTeam()).AI_getWorstEnemy();
if (NO_TEAM != eWorstEnemy && eWorstEnemy != getTeam())
{
int iThirdPartyAttitudeWeight = GET_TEAM(getTeam()).AI_getAttitudeWeight(eWorstEnemy);
//If we like both teams, we want them to get along.
//If we like otherPlayer but not enemy (or vice-verca), we don't want them to get along.
//If we don't like either, we don't want them to get along.
//Also just value stirring up trouble in general.
int iThirdPartyDiploValue = 50 * kEvent.getTheirEnemyAttitudeModifier();
iThirdPartyDiploValue *= (iThirdPartyAttitudeWeight - 10);
iThirdPartyDiploValue *= (iOtherPlayerAttitudeWeight - 10);
iThirdPartyDiploValue /= 10000;
if ((iOtherPlayerAttitudeWeight) < 0 && (iThirdPartyAttitudeWeight < 0))
{
iThirdPartyDiploValue *= -1;
}
iThirdPartyDiploValue /= 2;
iDiploValue += iThirdPartyDiploValue;
}
}
iDiploValue *= iGameSpeedPercent;
iDiploValue /= 100;
if (NO_BONUS != kEvent.getBonusGift())
{
int iBonusValue = -AI_bonusVal((BonusTypes)kEvent.getBonusGift(), -1);
iBonusValue += (iOtherPlayerAttitudeWeight - 40) * kOtherPlayer.AI_bonusVal((BonusTypes)kEvent.getBonusGift(), +1);
//Positive for friends, negative for enemies.
iDiploValue += (iBonusValue * getTreatyLength()) / 60;
}
if (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI))
{
//What is this "relationships" thing?
iDiploValue /= 2;
}
if (kEvent.isGoldToPlayer())
{
//If the gold goes to another player instead of the void, then this is a positive
//thing if we like the player, otherwise it's a negative thing.
int iGiftValue = (getEventCost(eEvent, kTriggeredData.m_eOtherPlayer, false) + getEventCost(eEvent, kTriggeredData.m_eOtherPlayer, true)) / 2;
iGiftValue *= -iOtherPlayerAttitudeWeight;
iGiftValue /= 110;
iValue += iGiftValue;
}
if (kEvent.isDeclareWar())
{
int iWarValue = (GET_TEAM(getTeam()).getDefensivePower() - GET_TEAM(GET_PLAYER(kTriggeredData.m_eOtherPlayer).getTeam()).getPower(true));// / std::max(1, GET_TEAM(getTeam()).getDefensivePower());
iWarValue -= 30 * AI_getAttitudeVal(kTriggeredData.m_eOtherPlayer);
}
if (kEvent.getMaxPillage() > 0)
{
int iPillageValue = (40 * (kEvent.getMinPillage() + kEvent.getMaxPillage())) / 2;
//If we hate them, this is good to do.
iPillageValue *= 25 - iOtherPlayerAttitudeWeight;
iPillageValue *= iGameSpeedPercent;
iPillageValue /= 12500;
}
iValue += (iDiploValue * iGameSpeedPercent) / 100;
}
int iThisEventValue = iValue;
//XXX THIS IS VULNERABLE TO NON-TRIVIAL RECURSIONS!
//Event A effects Event B, Event B effects Event A
for (int iEvent = 0; iEvent < GC.getNumEventInfos(); ++iEvent)
{
if (kEvent.getAdditionalEventChance(iEvent) > 0)
{
if (iEvent == eEvent)
{
//Infinite recursion is not our friend.
//Fortunately we have the event value for this event - sans values of other events
//disabled or cleared. Hopefully no events will be that complicated...
//Double the value since it's recursive.
iValue += (kEvent.getAdditionalEventChance(iEvent) * iThisEventValue) / 50;
}
else
{
iValue += (kEvent.getAdditionalEventChance(iEvent) * AI_eventValue((EventTypes)iEvent, kTriggeredData)) / 100;
}
}
if (kEvent.getClearEventChance(iEvent) > 0)
{
if (iEvent == eEvent)
{
iValue -= (kEvent.getClearEventChance(iEvent) * iThisEventValue) / 50;
}
else
{
iValue -= (kEvent.getClearEventChance(iEvent) * AI_eventValue((EventTypes)iEvent, kTriggeredData)) / 100;
}
}
}
iValue *= 100 + GC.getGameINLINE().getSorenRandNum(20, "AI Event choice");
iValue /= 100;
return iValue;
}
EventTypes CvPlayerAI::AI_chooseEvent(int iTriggeredId, int* pValue) const
{
EventTriggeredData* pTriggeredData = getEventTriggered(iTriggeredId);
if (NULL == pTriggeredData)
{
return NO_EVENT;
}
CvEventTriggerInfo& kTrigger = GC.getEventTriggerInfo(pTriggeredData->m_eTrigger);
int iBestValue = -MAX_INT;
EventTypes eBestEvent = NO_EVENT;
for (int i = 0; i < kTrigger.getNumEvents(); i++)
{
int iValue = -MAX_INT;
if (kTrigger.getEvent(i) != NO_EVENT)
{
CvEventInfo& kEvent = GC.getEventInfo((EventTypes)kTrigger.getEvent(i));
if (canDoEvent((EventTypes)kTrigger.getEvent(i), *pTriggeredData))
{
iValue = AI_eventValue((EventTypes)kTrigger.getEvent(i), *pTriggeredData);
}
}
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestEvent = (EventTypes)kTrigger.getEvent(i);
}
}
if ( pValue != NULL )
{
*pValue = iBestValue;
}
return eBestEvent;
}
void CvPlayerAI::AI_doSplit()
{
PROFILE_FUNC();
if (!canSplitEmpire())
{
return;
}
int iLoop;
std::map<int, int> mapAreaValues;
for (CvArea* pLoopArea = GC.getMapINLINE().firstArea(&iLoop); pLoopArea != NULL; pLoopArea = GC.getMapINLINE().nextArea(&iLoop))
{
mapAreaValues[pLoopArea->getID()] = 0;
}
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
mapAreaValues[pLoopCity->area()->getID()] += pLoopCity->AI_cityValue();
}
std::map<int, int>::iterator it;
for (it = mapAreaValues.begin(); it != mapAreaValues.end(); ++it)
{
if (it->second < 0)
{
int iAreaId = it->first;
if (canSplitArea(iAreaId))
{
splitEmpire(iAreaId);
for (CvUnit* pUnit = firstUnit(&iLoop); pUnit != NULL; pUnit = nextUnit(&iLoop))
{
if (pUnit->area()->getID() == iAreaId)
{
TeamTypes ePlotTeam = pUnit->plot()->getTeam();
if (NO_TEAM != ePlotTeam)
{
CvTeam& kPlotTeam = GET_TEAM(ePlotTeam);
if (kPlotTeam.isVassal(getTeam()) && GET_TEAM(getTeam()).isParent(ePlotTeam))
{
pUnit->gift();
}
}
}
}
break;
}
}
}
}
void CvPlayerAI::AI_launch(VictoryTypes eVictory)
{
if (GET_TEAM(getTeam()).isHuman())
{
return;
}
if (!GET_TEAM(getTeam()).canLaunch(eVictory))
{
return;
}
bool bLaunch = true;
int iBestArrival = MAX_INT;
TeamTypes eBestTeam = NO_TEAM;
for (int iTeam = 0; iTeam < MAX_CIV_TEAMS; ++iTeam)
{
if (iTeam != getTeam())
{
CvTeam& kTeam = GET_TEAM((TeamTypes)iTeam);
if (kTeam.isAlive())
{
int iCountdown = kTeam.getVictoryCountdown(eVictory);
if (iCountdown > 0)
{
if (iCountdown < iBestArrival)
{
iBestArrival = iCountdown;
eBestTeam = (TeamTypes)iTeam;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 12/07/09 r_rolo1 & jdog5000 */
/* */
/* */
/************************************************************************************************/
/* original BTS code
if (iCountdown < GET_TEAM(getTeam()).getVictoryDelay(eVictory) && kTeam.getLaunchSuccessRate(eVictory) == 100)
{
bLaunch = false;
break;
}
*/
// Other civs capital could still be captured, might as well send spaceship
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
}
}
}
if (bLaunch)
{
if (NO_TEAM == eBestTeam || iBestArrival > GET_TEAM(getTeam()).getVictoryDelay(eVictory))
{
if (GET_TEAM(getTeam()).getLaunchSuccessRate(eVictory) < 100)
{
bLaunch = false;
}
}
}
if (bLaunch)
{
launch(eVictory);
}
}
void CvPlayerAI::AI_doCheckFinancialTrouble()
{
PROFILE_FUNC();
// if we just ran into financial trouble this turn
bool bFinancialTrouble = AI_isFinancialTrouble();
if (bFinancialTrouble != m_bWasFinancialTrouble)
{
if (bFinancialTrouble)
{
int iGameTurn = GC.getGameINLINE().getGameTurn();
// only reset at most every 10 turns
if (iGameTurn > m_iTurnLastProductionDirty + 10)
{
// redeterimine the best things to build in each city
AI_makeProductionDirty();
m_iTurnLastProductionDirty = iGameTurn;
}
}
m_bWasFinancialTrouble = bFinancialTrouble;
}
}
bool CvPlayerAI::AI_disbandUnit(int iExpThreshold, bool bObsolete)
{
CvUnit* pLoopUnit;
CvUnit* pBestUnit;
int iValue;
int iBestValue;
int iLoop;
iBestValue = MAX_INT;
pBestUnit = NULL;
for(pLoopUnit = firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = nextUnit(&iLoop))
{
if (!(pLoopUnit->hasCargo()))
{
if (!(pLoopUnit->isGoldenAge()))
{
if (pLoopUnit->getUnitInfo().getProductionCost() > 0)
{
if ((iExpThreshold == -1) || (pLoopUnit->canFight() && pLoopUnit->getExperience() <= iExpThreshold))
{
if (!(pLoopUnit->isMilitaryHappiness()) || !(pLoopUnit->plot()->isCity()) || (pLoopUnit->plot()->plotCount(PUF_isMilitaryHappiness, -1, -1, getID()) > 2))
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 01/12/10 jdog5000 */
/* */
/* Gold AI */
/************************************************************************************************/
iValue = (10000 + GC.getGameINLINE().getSorenRandNum(1000, "Disband Unit"));
iValue *= 100 + (pLoopUnit->getUnitInfo().getProductionCost() * 3);
iValue /= 100;
iValue *= 100 + (pLoopUnit->getExperience() * 10);
iValue /= 100;
iValue *= 100 + (pLoopUnit->getLevel() * 25);
iValue /= 100;
if (pLoopUnit->plot()->getTeam() == pLoopUnit->getTeam())
{
iValue *= 3;
if (pLoopUnit->canDefend() && pLoopUnit->plot()->isCity())
{
iValue *= 2;
}
}
// Multiplying by higher number means unit has higher priority, less likely to be disbanded
switch (pLoopUnit->AI_getUnitAIType())
{
case UNITAI_UNKNOWN:
case UNITAI_ANIMAL:
break;
case UNITAI_SUBDUED_ANIMAL:
// For now make them less valuable the more you have (strictly this should depend
// on what you need in terms of their buildable buildings, but start with an
// approximation that is better than nothing
iValue *= std::min(0, getNumCities()*2 - AI_getNumAIUnits(UNITAI_SUBDUED_ANIMAL))/std::min(1,getNumCities());
break;
case UNITAI_HUNTER:
// Treat hunters like explorers for valuation, but slightly less so
if ((GC.getGame().getGameTurn() - pLoopUnit->getGameTurnCreated()) < 10
|| pLoopUnit->plot()->getTeam() != getTeam())
{
iValue *= 10;
}
else
{
iValue *= 2;
}
break;
case UNITAI_SETTLE:
iValue *= 20;
break;
case UNITAI_WORKER:
if ((GC.getGame().getGameTurn() - pLoopUnit->getGameTurnCreated()) > 10)
{
if (pLoopUnit->plot()->isCity())
{
if (pLoopUnit->plot()->getPlotCity()->AI_getWorkersNeeded() == 0)
{
iValue *= 10;
}
}
}
break;
case UNITAI_ATTACK:
case UNITAI_ATTACK_CITY:
case UNITAI_COLLATERAL:
case UNITAI_PILLAGE:
case UNITAI_RESERVE:
case UNITAI_COUNTER:
case UNITAI_PILLAGE_COUNTER:
iValue *= 2;
break;
case UNITAI_CITY_DEFENSE:
case UNITAI_CITY_COUNTER:
case UNITAI_CITY_SPECIAL:
case UNITAI_PARADROP:
iValue *= 6;
break;
case UNITAI_EXPLORE:
if ((GC.getGame().getGameTurn() - pLoopUnit->getGameTurnCreated()) < 10
|| pLoopUnit->plot()->getTeam() != getTeam())
{
iValue *= 15;
}
else
{
iValue *= 2;
}
break;
case UNITAI_MISSIONARY:
if ((GC.getGame().getGameTurn() - pLoopUnit->getGameTurnCreated()) < 10
|| pLoopUnit->plot()->getTeam() != getTeam())
{
iValue *= 8;
}
break;
case UNITAI_PROPHET:
case UNITAI_ARTIST:
case UNITAI_SCIENTIST:
case UNITAI_GENERAL:
case UNITAI_MERCHANT:
case UNITAI_ENGINEER:
iValue *= 20;
break;
case UNITAI_SPY:
iValue *= 12;
break;
case UNITAI_ICBM:
iValue *= 4;
break;
case UNITAI_WORKER_SEA:
iValue *= 18;
break;
case UNITAI_ATTACK_SEA:
case UNITAI_RESERVE_SEA:
case UNITAI_ESCORT_SEA:
break;
case UNITAI_EXPLORE_SEA:
if ((GC.getGame().getGameTurn() - pLoopUnit->getGameTurnCreated()) < 10
|| pLoopUnit->plot()->getTeam() != getTeam())
{
iValue *= 12;
}
break;
case UNITAI_SETTLER_SEA:
iValue *= 6;
case UNITAI_MISSIONARY_SEA:
case UNITAI_SPY_SEA:
iValue *= 4;
case UNITAI_ASSAULT_SEA:
case UNITAI_CARRIER_SEA:
case UNITAI_MISSILE_CARRIER_SEA:
if( GET_TEAM(getTeam()).getAnyWarPlanCount(true) > 0 )
{
iValue *= 5;
}
else
{
iValue *= 2;
}
break;
case UNITAI_PIRATE_SEA:
case UNITAI_ATTACK_AIR:
break;
case UNITAI_DEFENSE_AIR:
case UNITAI_CARRIER_AIR:
case UNITAI_MISSILE_AIR:
if( GET_TEAM(getTeam()).getAnyWarPlanCount(true) > 0 )
{
iValue *= 5;
}
else
{
iValue *= 3;
}
break;
default:
FAssert(false);
break;
}
if (pLoopUnit->getUnitInfo().getExtraCost() > 0)
{
iValue /= (pLoopUnit->getUnitInfo().getExtraCost() + 1);
}
if (iValue < iBestValue)
{
iBestValue = iValue;
pBestUnit = pLoopUnit;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
}
}
}
}
}
if (pBestUnit != NULL)
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 01/12/10 jdog5000 */
/* */
/* AI logging */
/************************************************************************************************/
if( gPlayerLogLevel >= 2 )
{
CvWString szString;
getUnitAIString(szString, pBestUnit->AI_getUnitAIType());
logBBAI(" Player %d (%S) disbanding %S with UNITAI %S to save cash", getID(), getCivilizationDescription(0), pBestUnit->getName().GetCString(), szString.GetCString());
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
pBestUnit->kill(false);
return true;
}
return false;
}
int CvPlayerAI::AI_cultureVictoryTechValue(TechTypes eTech) const
{
MEMORY_TRACK()
int iI;
if (eTech == NO_TECH)
{
return 0;
}
int iValue = 0;
if (GC.getTechInfo(eTech).isDefensivePactTrading())
{
iValue += 50;
}
if (GC.getTechInfo(eTech).isCommerceFlexible(COMMERCE_CULTURE))
{
iValue += 100;
}
//units
bool bAnyWarplan = (GET_TEAM(getTeam()).getAnyWarPlanCount(true) > 0);
int iBestUnitValue = 0;
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
UnitTypes eLoopUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI)));
if (eLoopUnit != NO_UNIT)
{
if (isTechRequiredForUnit((eTech), eLoopUnit))
{
int iTempValue = (GC.getUnitInfo(eLoopUnit).getCombat() * 100) / std::max(1, (GC.getGame().getBestLandUnitCombat()));
iTempValue *= bAnyWarplan ? 2 : 1;
iValue += iTempValue / 3;
iBestUnitValue = std::max(iBestUnitValue, iTempValue);
}
}
}
iValue += std::max(0, iBestUnitValue - 15);
//cultural things
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
BuildingTypes eLoopBuilding = ((BuildingTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iI)));
if (eLoopBuilding != NO_BUILDING)
{
if (isTechRequiredForBuilding((eTech), eLoopBuilding))
{
CvBuildingInfo& kLoopBuilding = GC.getBuildingInfo(eLoopBuilding);
if ((GC.getBuildingClassInfo((BuildingClassTypes)iI).getDefaultBuildingIndex()) != (GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iI)))
{
//UB
iValue += 100;
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 05/25/10 Fuyu & jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original bts code
iValue += (150 * kLoopBuilding.getCommerceChange(COMMERCE_CULTURE)) * 20;
*/
iValue += (150 * (kLoopBuilding.getCommerceChange(COMMERCE_CULTURE) + kLoopBuilding.getObsoleteSafeCommerceChange(COMMERCE_CULTURE))) / 20;
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
iValue += kLoopBuilding.getCommerceModifier(COMMERCE_CULTURE) * 2;
}
}
}
//important civics
for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
{
if (GC.getCivicInfo((CivicTypes)iI).getTechPrereq() == eTech)
{
iValue += GC.getCivicInfo((CivicTypes)iI).getCommerceModifier(COMMERCE_CULTURE) * 2;
}
}
return iValue;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 04/25/10 jdog5000 */
/* */
/* Victory Strategy AI */
/************************************************************************************************/
int CvPlayerAI::AI_getCultureVictoryStage() const
{
int iValue;
if( GC.getDefineINT("BBAI_VICTORY_STRATEGY_CULTURE") <= 0 )
{
return 0;
}
if (!GC.getGameINLINE().culturalVictoryValid())
{
return 0;
}
// Necessary as capital city pointer is used later
if (getCapitalCity() == NULL)
{
return 0;
}
int iHighCultureCount = 0;
int iCloseToLegendaryCount = 0;
int iLegendaryCount = 0;
int iLoop;
CvCity* pLoopCity;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (pLoopCity->getCultureLevel() >= (GC.getGameINLINE().culturalVictoryCultureLevel() - 1))
{
if (pLoopCity->getBaseCommerceRate(COMMERCE_CULTURE) > 100)
{
iHighCultureCount++;
}
// is over 1/2 of the way there?
if( 2*pLoopCity->getCulture(getID()) > pLoopCity->getCultureThreshold(GC.getGameINLINE().culturalVictoryCultureLevel()) )
{
iCloseToLegendaryCount++;
}
// is already there?
if( pLoopCity->getCulture(getID()) > pLoopCity->getCultureThreshold(GC.getGameINLINE().culturalVictoryCultureLevel()) )
{
iLegendaryCount++;
}
}
}
if( iLegendaryCount >= GC.getGameINLINE().culturalVictoryNumCultureCities() )
{
// Already won, keep playing culture heavy but do some tech to keep pace if human wants to keep playing
return 3;
}
if( iCloseToLegendaryCount >= GC.getGameINLINE().culturalVictoryNumCultureCities() )
{
return 4;
}
else if( isHuman() )
{
if( getCommercePercent(COMMERCE_CULTURE) > 50 )
{
return 3;
}
}
if( isHuman() && !(GC.getGameINLINE().isDebugMode()) )
{
return 0;
}
if (GC.getGame().getStartEra() > 1)
{
return 0;
}
if (getCapitalCity()->getGameTurnFounded() > (10 + GC.getGameINLINE().getStartTurn()))
{
if( iHighCultureCount < GC.getGameINLINE().culturalVictoryNumCultureCities() )
{
//the loss of the capital indicates it might be a good idea to abort any culture victory
return 0;
}
}
iValue = GC.getLeaderHeadInfo(getPersonalityType()).getCultureVictoryWeight();
if (GC.getGameINLINE().isOption(GAMEOPTION_ALWAYS_PEACE))
{
iValue += 30;
}
iValue += (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) ? -20 : 0);
if( iValue > 20 && getNumCities() >= GC.getGameINLINE().culturalVictoryNumCultureCities() )
{
iValue += 10*countHolyCities();
}
/*
if ((GET_TEAM(getTeam()).isAVassal()) && (getNumCities() > 5))
{
int iReligionCount = countTotalHasReligion();
if (((iReligionCount * 100) / getNumCities()) > 250)
{
iValue += 1;
iValue += ((2 * iReligionCount) + 1) / getNumCities();
}
}
*/
// K-mod
// int iNonsense = AI_getStrategyRand() + 10;
// iValue += (iNonsense % 100);
iValue += (AI_getStrategyRand(0) % 100);
// K-mod end
if (iValue < 100)
{
return 0;
}
if (getCurrentEra() >= (GC.getNumEraInfos() - (2 + AI_getStrategyRand(1) % 2))) // K-mod
{
bool bAt3 = false;
// if we have enough high culture cities, go to stage 3
if (iHighCultureCount >= GC.getGameINLINE().culturalVictoryNumCultureCities())
{
bAt3 = true;
}
// if we have a lot of religion, may be able to catch up quickly
if (countTotalHasReligion() >= getNumCities() * 3)
{
if( getNumCities() >= GC.getGameINLINE().culturalVictoryNumCultureCities() )
{
bAt3 = true;
}
}
if( bAt3 )
{
if (AI_cultureVictoryTechValue(getCurrentResearch()) < 100)
{
return 4;
}
return 3;
}
}
if (getCurrentEra() >= ((GC.getNumEraInfos() / 3) + AI_getStrategyRand(2) % 2)) // K-mod
{
return 2;
}
return 1;
}
int CvPlayerAI::AI_getSpaceVictoryStage() const
{
int iValue;
if( GC.getDefineINT("BBAI_VICTORY_STRATEGY_SPACE") <= 0 )
{
return 0;
}
if (getCapitalCity() == NULL)
{
return 0;
}
// Better way to determine if spaceship victory is valid?
VictoryTypes eSpace = NO_VICTORY;
for (int iI = 0; iI < GC.getNumVictoryInfos(); iI++)
{
if (GC.getGameINLINE().isVictoryValid((VictoryTypes) iI))
{
CvVictoryInfo& kVictoryInfo = GC.getVictoryInfo((VictoryTypes) iI);
if( kVictoryInfo.getVictoryDelayTurns() > 0 )
{
eSpace = (VictoryTypes)iI;
break;
}
}
}
if( eSpace == NO_VICTORY )
{
return 0;
}
// If have built Apollo, then the race is on!
bool bHasApollo = false;
bool bNearAllTechs = true;
for( int iI = 0; iI < GC.getNumProjectInfos(); iI++ )
{
if( GC.getProjectInfo((ProjectTypes)iI).getVictoryPrereq() == eSpace )
{
if( GET_TEAM(getTeam()).getProjectCount((ProjectTypes)iI) > 0 )
{
bHasApollo = true;
}
else
{
if( !GET_TEAM(getTeam()).isHasTech((TechTypes)GC.getProjectInfo((ProjectTypes)iI).getTechPrereq()) )
{
if( !isResearchingTech((TechTypes)GC.getProjectInfo((ProjectTypes)iI).getTechPrereq()) )
{
bNearAllTechs = false;
}
}
}
}
}
if( bHasApollo )
{
if( bNearAllTechs )
{
bool bOtherLaunched = false;
if( GET_TEAM(getTeam()).getVictoryCountdown(eSpace) >= 0 )
{
for( int iTeam = 0; iTeam < MAX_CIV_TEAMS; iTeam++ )
{
if( iTeam != getTeam() )
{
if( GET_TEAM((TeamTypes)iTeam).getVictoryCountdown(eSpace) >= 0 )
{
if( GET_TEAM((TeamTypes)iTeam).getVictoryCountdown(eSpace) < GET_TEAM(getTeam()).getVictoryCountdown(eSpace) )
{
bOtherLaunched = true;
break;
}
if( GET_TEAM((TeamTypes)iTeam).getVictoryCountdown(eSpace) == GET_TEAM(getTeam()).getVictoryCountdown(eSpace) && (iTeam < getTeam()) )
{
bOtherLaunched = true;
break;
}
}
}
}
}
else
{
for( int iTeam = 0; iTeam < MAX_CIV_TEAMS; iTeam++ )
{
if( GET_TEAM((TeamTypes)iTeam).getVictoryCountdown(eSpace) >= 0 )
{
bOtherLaunched = true;
break;
}
}
}
if( !bOtherLaunched )
{
return 4;
}
return 3;
}
if( GET_TEAM(getTeam()).getBestKnownTechScorePercent() > (m_iVictoryStrategyHash & AI_VICTORY_SPACE3 ? 80 : 85) )
{
return 3;
}
}
if( isHuman() && !(GC.getGameINLINE().isDebugMode()) )
{
return 0;
}
// If can't build Apollo yet, then consider making player push for this victory type
{
iValue = GC.getLeaderHeadInfo(getPersonalityType()).getSpaceVictoryWeight();
if (GC.getGameINLINE().isOption(GAMEOPTION_ALWAYS_PEACE))
{
iValue += 30;
}
if(GET_TEAM(getTeam()).isAVassal())
{
iValue += 20;
}
iValue += (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) ? -20 : 0);
// K-mod
//int iNonsense = AI_getStrategyRand() + 50;
iValue += (AI_getStrategyRand(3) % 100);
// K-mod end
if (iValue >= 100)
{
if( getCurrentEra() >= GC.getNumEraInfos() - 3 )
{
return 2;
}
return 1;
}
}
return 0;
}
int CvPlayerAI::AI_getConquestVictoryStage() const
{
int iValue;
if (GC.getGameINLINE().isOption(GAMEOPTION_ALWAYS_PEACE))
{
return 0;
}
if(GET_TEAM(getTeam()).isAVassal())
{
return 0;
}
if( GC.getDefineINT("BBAI_VICTORY_STRATEGY_CONQUEST") <= 0 )
{
return 0;
}
VictoryTypes eConquest = NO_VICTORY;
for (int iI = 0; iI < GC.getNumVictoryInfos(); iI++)
{
if (GC.getGameINLINE().isVictoryValid((VictoryTypes) iI))
{
CvVictoryInfo& kVictoryInfo = GC.getVictoryInfo((VictoryTypes) iI);
if( kVictoryInfo.isConquest() )
{
eConquest = (VictoryTypes)iI;
break;
}
}
}
if( eConquest == NO_VICTORY )
{
return 0;
}
// Check for whether we are very powerful, looking good for conquest
int iOurPower = GET_TEAM(getTeam()).getPower(true);
int iOurPowerRank = 1;
int iTotalPower = 0;
int iNumNonVassals = 0;
for (int iI = 0; iI < MAX_CIV_TEAMS; iI++)
{
if( iI != getTeam() )
{
CvTeam& kTeam = GET_TEAM((TeamTypes) iI);
if (kTeam.isAlive() && !(kTeam.isMinorCiv()))
{
if( !kTeam.isAVassal() )
{
iTotalPower += kTeam.getPower(true);
iNumNonVassals++;
if( GET_TEAM(getTeam()).isHasMet((TeamTypes) iI) )
{
if( 95*kTeam.getPower(false) > 100*iOurPower )
{
iOurPowerRank++;
}
}
}
}
}
}
int iAverageOtherPower = iTotalPower / std::max(1, iNumNonVassals);
if( 3*iOurPower > 4*iAverageOtherPower )
{
// BBAI TODO: Have we declared total war on anyone? Need some aggressive action taken, maybe past war success
int iOthersWarMemoryOfUs = 0;
for( int iPlayer = 0; iPlayer < MAX_CIV_PLAYERS; iPlayer++ )
{
if( GET_PLAYER((PlayerTypes)iPlayer).getTeam() != getTeam() && GET_PLAYER((PlayerTypes)iPlayer).isEverAlive() )
{
iOthersWarMemoryOfUs += GET_PLAYER((PlayerTypes)iPlayer).AI_getMemoryCount(getID(), MEMORY_DECLARED_WAR);
}
}
if( GET_TEAM(getTeam()).getHasMetCivCount(false) > GC.getGameINLINE().countCivPlayersAlive()/4 )
{
if( iOurPowerRank <= 1 + (GET_TEAM(getTeam()).getHasMetCivCount(true)/10) )
{
if( (iOurPower > 2*iAverageOtherPower) && (iOurPower - iAverageOtherPower > 100) )
{
if( iOthersWarMemoryOfUs > 0 )
{
return 4;
}
}
}
}
if (getCurrentEra() >= ((GC.getNumEraInfos() / 3)))
{
if( iOurPowerRank <= 1 + (GET_TEAM(getTeam()).getHasMetCivCount(true)/7) )
{
if( iOthersWarMemoryOfUs > 2 )
{
return 3;
}
}
}
}
if( isHuman() && !(GC.getGameINLINE().isDebugMode()) )
{
return 0;
}
// Check for whether we are inclined to pursue a conquest strategy
{
iValue = GC.getLeaderHeadInfo(getPersonalityType()).getConquestVictoryWeight();
iValue += (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) ? 20 : 0);
//int iNonsense = AI_getStrategyRand() + 30;
iValue += (AI_getStrategyRand(4) % 100); // K-mod
if (iValue >= 100)
{
if( m_iStrategyHash & AI_STRATEGY_GET_BETTER_UNITS )
{
if( (getNumCities() > 3) && (4*iOurPower > 5*iAverageOtherPower) )
{
return 2;
}
}
return 1;
}
}
return 0;
}
int CvPlayerAI::AI_getDominationVictoryStage() const
{
int iValue = 0;
if (GC.getGameINLINE().isOption(GAMEOPTION_ALWAYS_PEACE))
{
return 0;
}
if(GET_TEAM(getTeam()).isAVassal())
{
return 0;
}
if( GC.getDefineINT("BBAI_VICTORY_STRATEGY_DOMINATION") <= 0 )
{
return 0;
}
VictoryTypes eDomination = NO_VICTORY;
for (int iI = 0; iI < GC.getNumVictoryInfos(); iI++)
{
if (GC.getGameINLINE().isVictoryValid((VictoryTypes) iI))
{
CvVictoryInfo& kVictoryInfo = GC.getVictoryInfo((VictoryTypes) iI);
if( kVictoryInfo.getLandPercent() > 0 && kVictoryInfo.getPopulationPercentLead() )
{
eDomination = (VictoryTypes)iI;
break;
}
}
}
if( eDomination == NO_VICTORY )
{
return 0;
}
int iPercentOfDomination = 0;
int iOurPopPercent = (100 * GET_TEAM(getTeam()).getTotalPopulation()) / std::max(1, GC.getGameINLINE().getTotalPopulation());
int iOurLandPercent = (100 * GET_TEAM(getTeam()).getTotalLand()) / std::max(1, GC.getMapINLINE().getLandPlots());
iPercentOfDomination = (100 * iOurPopPercent) / std::max(1, GC.getGameINLINE().getAdjustedPopulationPercent(eDomination));
iPercentOfDomination = std::min( iPercentOfDomination, (100 * iOurLandPercent) / std::max(1, GC.getGameINLINE().getAdjustedLandPercent(eDomination)) );
if( iPercentOfDomination > 80 )
{
return 4;
}
if( iPercentOfDomination > 50 )
{
return 3;
}
if( isHuman() && !(GC.getGameINLINE().isDebugMode()) )
{
return 0;
}
// Check for whether we are inclined to pursue a domination strategy
{
iValue = GC.getLeaderHeadInfo(getPersonalityType()).getDominationVictoryWeight();
iValue += (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) ? 20 : 0);
// int iNonsense = AI_getStrategyRand() + 70;
// iValue += (iNonsense % 100);
iValue += (AI_getStrategyRand(5) % 100); // K-mod
if (iValue >= 100)
{
if( getNumCities() > 3 && (GC.getGameINLINE().getPlayerRank(getID()) < (GC.getGameINLINE().countCivPlayersAlive() + 1)/2) )
{
return 2;
}
return 1;
}
}
return 0;
}
int CvPlayerAI::AI_getDiplomacyVictoryStage() const
{
int iValue = 0;
if( GC.getDefineINT("BBAI_VICTORY_STRATEGY_DIPLOMACY") <= 0 )
{
return 0;
}
std::vector<VictoryTypes> veDiplomacy;
for (int iI = 0; iI < GC.getNumVictoryInfos(); iI++)
{
if (GC.getGameINLINE().isVictoryValid((VictoryTypes) iI))
{
CvVictoryInfo& kVictoryInfo = GC.getVictoryInfo((VictoryTypes) iI);
if( kVictoryInfo.isDiploVote() )
{
veDiplomacy.push_back((VictoryTypes)iI);
}
}
}
if( veDiplomacy.empty() )
{
return 0;
}
// Check for whether we are elligible for election
bool bVoteEligible = false;
for( int iVoteSource = 0; iVoteSource < GC.getNumVoteSourceInfos(); iVoteSource++ )
{
if( GC.getGameINLINE().isDiploVote((VoteSourceTypes)iVoteSource) )
{
if( GC.getGameINLINE().isTeamVoteEligible(getTeam(),(VoteSourceTypes)iVoteSource) )
{
bVoteEligible = true;
break;
}
}
}
bool bDiploInclined = false;
// Check for whether we are inclined to pursue a diplomacy strategy
{
iValue = GC.getLeaderHeadInfo(getPersonalityType()).getDiplomacyVictoryWeight();
if (GC.getGameINLINE().isOption(GAMEOPTION_ALWAYS_PEACE))
{
iValue += 30;
}
iValue += (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) ? -20 : 0);
// int iNonsense = AI_getStrategyRand() + 90;
// iValue += (iNonsense % 100);
iValue += (AI_getStrategyRand(6) % 100); // K-mod
// BBAI TODO: Level 2?
if (iValue >= 100)
{
bDiploInclined = true;
}
}
if( bVoteEligible && (bDiploInclined || isHuman()) )
{
// BBAI TODO: Level 4 - close to enough to win a vote?
return 3;
}
if( isHuman() && !(GC.getGameINLINE().isDebugMode()) )
{
return 0;
}
if( bDiploInclined )
{
return 1;
}
return 0;
}
/// \brief Returns whether player is pursuing a particular victory strategy.
///
/// Victory strategies are computed on demand once per turn and stored for the rest
/// of the turn. Each victory strategy type has 4 levels, the first two are
/// determined largely from AI tendencies and randomn dice rolls. The second
/// two are based on measurables and past actions, so the AI can use them to
/// determine what other players (including the human player) are doing.
bool CvPlayerAI::AI_isDoVictoryStrategy(int iVictoryStrategy) const
{
if( isBarbarian() || isMinorCiv() || !isAlive() )
{
return false;
}
return (iVictoryStrategy & AI_getVictoryStrategyHash());
}
bool CvPlayerAI::AI_isDoVictoryStrategyLevel4() const
{
if( AI_isDoVictoryStrategy(AI_VICTORY_SPACE4) )
{
return true;
}
if( AI_isDoVictoryStrategy(AI_VICTORY_CONQUEST4) )
{
return true;
}
if( AI_isDoVictoryStrategy(AI_VICTORY_CULTURE4) )
{
return true;
}
if( AI_isDoVictoryStrategy(AI_VICTORY_DOMINATION4) )
{
return true;
}
if( AI_isDoVictoryStrategy(AI_VICTORY_DIPLOMACY4) )
{
return true;
}
return false;
}
bool CvPlayerAI::AI_isDoVictoryStrategyLevel3() const
{
if( AI_isDoVictoryStrategy(AI_VICTORY_SPACE3) )
{
return true;
}
if( AI_isDoVictoryStrategy(AI_VICTORY_CONQUEST3) )
{
return true;
}
if( AI_isDoVictoryStrategy(AI_VICTORY_CULTURE3) )
{
return true;
}
if( AI_isDoVictoryStrategy(AI_VICTORY_DOMINATION3) )
{
return true;
}
if( AI_isDoVictoryStrategy(AI_VICTORY_DIPLOMACY3) )
{
return true;
}
return false;
}
void CvPlayerAI::AI_forceUpdateVictoryStrategies()
{
//this forces a recache.
m_iVictoryStrategyHashCacheTurn = -1;
}
int CvPlayerAI::AI_getVictoryStrategyHash() const
{
PROFILE_FUNC();
if( isBarbarian() || isMinorCiv() || !isAlive() )
{
return 0;
}
if ((m_iVictoryStrategyHash != 0) && (m_iVictoryStrategyHashCacheTurn == GC.getGameINLINE().getGameTurn()))
{
return m_iVictoryStrategyHash;
}
m_iVictoryStrategyHash = AI_DEFAULT_VICTORY_STRATEGY;
m_iVictoryStrategyHashCacheTurn = GC.getGameINLINE().getGameTurn();
if (getCapitalCity() == NULL)
{
return m_iVictoryStrategyHash;
}
bool bStartedOtherLevel3 = false;
bool bStartedOtherLevel4 = false;
// Space victory
int iVictoryStage = AI_getSpaceVictoryStage();
if( iVictoryStage >= 1 )
{
m_iVictoryStrategyHash |= AI_VICTORY_SPACE1;
if (iVictoryStage > 1)
{
m_iVictoryStrategyHash |= AI_VICTORY_SPACE2;
if (iVictoryStage > 2)
{
bStartedOtherLevel3 = true;
m_iVictoryStrategyHash |= AI_VICTORY_SPACE3;
if (iVictoryStage > 3 && !bStartedOtherLevel4)
{
bStartedOtherLevel4 = true;
m_iVictoryStrategyHash |= AI_VICTORY_SPACE4;
}
}
}
}
// Conquest victory
iVictoryStage = AI_getConquestVictoryStage();
if( iVictoryStage >= 1 )
{
m_iVictoryStrategyHash |= AI_VICTORY_CONQUEST1;
if (iVictoryStage > 1)
{
m_iVictoryStrategyHash |= AI_VICTORY_CONQUEST2;
if (iVictoryStage > 2)
{
bStartedOtherLevel3 = true;
m_iVictoryStrategyHash |= AI_VICTORY_CONQUEST3;
if (iVictoryStage > 3 && !bStartedOtherLevel4)
{
bStartedOtherLevel4 = true;
m_iVictoryStrategyHash |= AI_VICTORY_CONQUEST4;
}
}
}
}
// Domination victory
iVictoryStage = AI_getDominationVictoryStage();
if( iVictoryStage >= 1 )
{
m_iVictoryStrategyHash |= AI_VICTORY_DOMINATION1;
if (iVictoryStage > 1)
{
m_iVictoryStrategyHash |= AI_VICTORY_DOMINATION2;
if (iVictoryStage > 2)
{
bStartedOtherLevel3 = true;
m_iVictoryStrategyHash |= AI_VICTORY_DOMINATION3;
if (iVictoryStage > 3 && !bStartedOtherLevel4)
{
bStartedOtherLevel4 = true;
m_iVictoryStrategyHash |= AI_VICTORY_DOMINATION4;
}
}
}
}
// Cultural victory
iVictoryStage = AI_getCultureVictoryStage();
if( iVictoryStage >= 1 )
{
m_iVictoryStrategyHash |= AI_VICTORY_CULTURE1;
if (iVictoryStage > 1)
{
m_iVictoryStrategyHash |= AI_VICTORY_CULTURE2;
if (iVictoryStage > 2)
{
bStartedOtherLevel3 = true;
m_iVictoryStrategyHash |= AI_VICTORY_CULTURE3;
if (iVictoryStage > 3 && !bStartedOtherLevel4)
{
bStartedOtherLevel4 = true;
m_iVictoryStrategyHash |= AI_VICTORY_CULTURE4;
}
}
}
}
// Diplomacy victory
iVictoryStage = AI_getDiplomacyVictoryStage();
if( iVictoryStage >= 1 )
{
m_iVictoryStrategyHash |= AI_VICTORY_DIPLOMACY1;
if (iVictoryStage > 1)
{
m_iVictoryStrategyHash |= AI_VICTORY_DIPLOMACY2;
if (iVictoryStage > 2 && !bStartedOtherLevel3)
{
bStartedOtherLevel3 = true;
m_iVictoryStrategyHash |= AI_VICTORY_DIPLOMACY3;
if (iVictoryStage > 3 && !bStartedOtherLevel4)
{
bStartedOtherLevel4 = true;
m_iVictoryStrategyHash |= AI_VICTORY_DIPLOMACY4;
}
}
}
}
return m_iVictoryStrategyHash;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/18/10 jdog5000 */
/* */
/* War Strategy AI */
/************************************************************************************************/
// AIAndy: Calculate strategy rand in separate method to control when it is computed for MP
void CvPlayerAI::AI_calculateStrategyRand()
{
if( m_iStrategyRand <= 0 )
{
m_iStrategyRand = 1 + GC.getGameINLINE().getSorenRandNum(100000, "AI Strategy Rand");
}
}
// K-Mod, based on BBAI
int CvPlayerAI::AI_getStrategyRand(int iShift) const
{
const unsigned iBits = 16;
iShift += getCurrentEra();
while (iShift < 0)
iShift += iBits;
iShift %= iBits;
if (m_iStrategyRand <= 0)
{
m_iStrategyRand = GC.getGameINLINE().getSorenRandNum((1 << (iBits + 1)) - 1, "AI Strategy Rand");
}
return (m_iStrategyRand << iShift) + (m_iStrategyRand >> (iBits - iShift));
}
bool CvPlayerAI::AI_isDoStrategy(int iStrategy) const
{
if (isHuman())
{
return false;
}
if( isBarbarian() || isMinorCiv() || !isAlive() )
{
return false;
}
return (iStrategy & AI_getStrategyHash());
}
void CvPlayerAI::AI_forceUpdateStrategies()
{
//this forces a recache.
m_iStrategyHashCacheTurn = -1;
}
int CvPlayerAI::AI_getStrategyHash() const
{
PROFILE_FUNC();
if ((m_iStrategyHash != 0) && (m_iStrategyHashCacheTurn == GC.getGameINLINE().getGameTurn()))
{
return m_iStrategyHash;
}
const FlavorTypes AI_FLAVOR_MILITARY = (FlavorTypes)0;
const FlavorTypes AI_FLAVOR_RELIGION = (FlavorTypes)1;
const FlavorTypes AI_FLAVOR_PRODUCTION = (FlavorTypes)2;
const FlavorTypes AI_FLAVOR_GOLD = (FlavorTypes)3;
const FlavorTypes AI_FLAVOR_SCIENCE = (FlavorTypes)4;
const FlavorTypes AI_FLAVOR_CULTURE = (FlavorTypes)5;
const FlavorTypes AI_FLAVOR_GROWTH = (FlavorTypes)6;
int iI, iJ, iK;
UnitTypes eLoopUnit;
int iLastStrategyHash = m_iStrategyHash;
m_iStrategyHash = AI_DEFAULT_STRATEGY;
m_iStrategyHashCacheTurn = GC.getGameINLINE().getGameTurn();
if (AI_getFlavorValue(AI_FLAVOR_PRODUCTION) >= 2) // 0, 2, 5 or 10 in default xml [augustus 5, frederick 10, huayna 2, jc 2, chinese leader 2, qin 5, ramsess 2, roosevelt 5, stalin 2]
{
m_iStrategyHash |= AI_STRATEGY_PRODUCTION;
}
if (getCapitalCity() == NULL)
{
return m_iStrategyHash;
}
//int iNonsense = AI_getStrategyRand();
int iMetCount = GET_TEAM(getTeam()).getHasMetCivCount(true);
//Unit Analysis
int iBestSlowUnitCombat = -1;
int iBestFastUnitCombat = -1;
bool bHasMobileArtillery = false;
bool bHasMobileAntiair = false;
bool bHasBomber = false;
int iNukeCount = 0;
int iAttackUnitCount = 0;
// K-Mod
int iAverageEnemyUnit = 0;
int iTypicalAttack = getTypicalUnitValue(UNITAI_ATTACK);
int iTypicalDefence = getTypicalUnitValue(UNITAI_CITY_DEFENSE);
// K-Mod end
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
eLoopUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI)));
if (NO_UNIT != eLoopUnit)
{
if (getCapitalCity() != NULL)
{
if (getCapitalCity()->canTrain(eLoopUnit))
{
CvUnitInfo& kLoopUnit = GC.getUnitInfo(eLoopUnit);
bool bIsUU = (GC.getUnitClassInfo((UnitClassTypes)iI).getDefaultUnitIndex()) != (GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI));
if (kLoopUnit.getUnitAIType(UNITAI_RESERVE) || kLoopUnit.getUnitAIType(UNITAI_ATTACK_CITY)
|| kLoopUnit.getUnitAIType(UNITAI_COUNTER) || kLoopUnit.getUnitAIType(UNITAI_PILLAGE))
{
iAttackUnitCount++;
//UU love
if (bIsUU)
{
if (kLoopUnit.getUnitAIType(UNITAI_ATTACK_CITY) ||
(kLoopUnit.getUnitAIType(UNITAI_ATTACK) && !kLoopUnit.getUnitAIType(UNITAI_CITY_DEFENSE)))
{
iAttackUnitCount++;
}
}
/************************************************************************************************/
/* Afforess Start 6/11/11 */
/* */
/* */
/************************************************************************************************/
int iCombat = (kLoopUnit.getCombat() +
GET_TEAM(getTeam()).getUnitClassStrengthChange((UnitClassTypes)kLoopUnit.getUnitClassType()));
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
int iMoves = kLoopUnit.getMoves();
if (iMoves == 1)
{
iBestSlowUnitCombat = std::max(iBestSlowUnitCombat, iCombat);
}
else if (iMoves > 1)
{
iBestFastUnitCombat = std::max(iBestFastUnitCombat, iCombat);
if (bIsUU)
{
iBestFastUnitCombat += 1;
}
}
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 09/10/08 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original BTS code
if (kLoopUnit.getMoves() > 1)
*/
// Mobile anti-air and artillery flags only meant for land units
if ( kLoopUnit.getDomainType() == DOMAIN_LAND && kLoopUnit.getMoves() > 1 )
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
{
if (kLoopUnit.getInterceptionProbability() > 25)
{
bHasMobileAntiair = true;
}
if (kLoopUnit.getBombardRate() > 10)
{
bHasMobileArtillery = true;
}
}
if (kLoopUnit.getAirRange() > 1)
{
if (!kLoopUnit.isSuicide())
{
if ((kLoopUnit.getBombRate() > 10) && (kLoopUnit.getAirCombat() > 0))
{
bHasBomber = true;
}
}
}
if (kLoopUnit.getNukeRange() > 0)
{
iNukeCount++;
}
}
}
}
}
// K-Mod
{
int iTotalPower = 0;
int iTotalWeightedValue = 0;
for (iI = 0; iI < MAX_CIV_PLAYERS; iI++)
{
CvPlayer &kPlayer = GET_PLAYER((PlayerTypes)iI);
if (kPlayer.getTeam() != getTeam())
{
if (kPlayer.isAlive() && GET_TEAM(getTeam()).isHasMet(kPlayer.getTeam()))
{
// Attack units are scaled down to roughly reflect their limitations.
// (eg. Knights (10) vs Macemen (8). Cavalry (15) vs Rifles (14). Tank (28) vs Infantry (20) / Marine (24) )
int iValue = std::max(100 * kPlayer.getTypicalUnitValue(UNITAI_ATTACK) / 110, kPlayer.getTypicalUnitValue(UNITAI_CITY_DEFENSE));
iTotalWeightedValue += kPlayer.getPower() * iValue;
iTotalPower += kPlayer.getPower();
}
}
}
if (iTotalPower == 0)
iAverageEnemyUnit = 0;
else
iAverageEnemyUnit = iTotalWeightedValue / iTotalPower;
// A bit of random variation...
iAverageEnemyUnit *= (91 + AI_getStrategyRand(1) % 20);
iAverageEnemyUnit /= 100;
}
//if (iAttackUnitCount <= 1)
if (iAttackUnitCount <= 1 ||
100 * iAverageEnemyUnit > 140 * iTypicalAttack && 100 * iAverageEnemyUnit > 140 * iTypicalDefence)
{
m_iStrategyHash |= AI_STRATEGY_GET_BETTER_UNITS;
}
// K-Mod end
if (iBestFastUnitCombat > iBestSlowUnitCombat)
{
m_iStrategyHash |= AI_STRATEGY_FASTMOVERS;
if (bHasMobileArtillery && bHasMobileAntiair)
{
m_iStrategyHash |= AI_STRATEGY_LAND_BLITZ;
}
}
if (iNukeCount > 0)
{
if ((GC.getLeaderHeadInfo(getPersonalityType()).getBuildUnitProb() + +AI_getStrategyRand(7) % 15) >= (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) ? 37 : 43))
{
m_iStrategyHash |= AI_STRATEGY_OWABWNW;
}
}
if (bHasBomber)
{
if (!(m_iStrategyHash & AI_STRATEGY_LAND_BLITZ))
{
m_iStrategyHash |= AI_STRATEGY_AIR_BLITZ;
}
else
{
if ((AI_getStrategyRand(8) % 2) == 0)
{
m_iStrategyHash |= AI_STRATEGY_AIR_BLITZ;
m_iStrategyHash &= ~AI_STRATEGY_LAND_BLITZ;
}
}
}
if( gPlayerLogLevel >= 2 )
{
if( (m_iStrategyHash & AI_STRATEGY_LAND_BLITZ) && !(iLastStrategyHash & AI_STRATEGY_LAND_BLITZ) )
{
logBBAI( " Player %d (%S) starts strategy AI_STRATEGY_LAND_BLITZ on turn %d", getID(), getCivilizationDescription(0), GC.getGameINLINE().getGameTurn());
}
if( (m_iStrategyHash & AI_STRATEGY_AIR_BLITZ) && !(iLastStrategyHash & AI_STRATEGY_AIR_BLITZ) )
{
logBBAI( " Player %d (%S) starts strategy AI_STRATEGY_AIR_BLITZ on turn %d", getID(), getCivilizationDescription(0), GC.getGameINLINE().getGameTurn());
}
}
//missionary
{
if (getStateReligion() != NO_RELIGION)
{
int iHolyCityCount = countHolyCities();
if ((iHolyCityCount > 0) && hasHolyCity(getStateReligion()))
{
int iMissionary = 0;
//Missionary
iMissionary += AI_getFlavorValue(AI_FLAVOR_GROWTH) * 2; // up to 10
iMissionary += AI_getFlavorValue(AI_FLAVOR_CULTURE) * 4; // up to 40
iMissionary += AI_getFlavorValue(AI_FLAVOR_RELIGION) * 6; // up to 60
CivicTypes eCivic = (CivicTypes)GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivic();
if ((eCivic != NO_CIVIC) && (GC.getCivicInfo(eCivic).isNoNonStateReligionSpread()))
{
iMissionary += 20;
}
iMissionary += (iHolyCityCount - 1) * 5;
iMissionary += std::min(iMetCount, 5) * 7;
for (iI = 0; iI < MAX_CIV_PLAYERS; iI++)
{
if (iI != getID())
{
if (GET_PLAYER((PlayerTypes)iI).isAlive() && GET_TEAM(getTeam()).isHasMet(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
if (GET_TEAM(getTeam()).isOpenBorders(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
if ((GET_PLAYER((PlayerTypes)iI).getStateReligion() == getStateReligion()))
{
iMissionary += 10;
}
else if( !GET_PLAYER((PlayerTypes)iI).isNoNonStateReligionSpread() )
{
iMissionary += (GET_PLAYER((PlayerTypes)iI).countHolyCities() == 0) ? 12 : 4;
}
}
}
}
}
/************************************************************************************************/
/* Afforess Start 07/26/10 */
/* */
/* */
/************************************************************************************************/
bool bHasHolyBuilding = false;
if (getStateReligion() != NO_RELIGION)
{
if (GC.getGameINLINE().getHolyCity(getStateReligion()) != NULL && GC.getGameINLINE().getHolyCity(getStateReligion())->getOwner() == getID())
{
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (GC.getBuildingInfo((BuildingTypes)iI).getGlobalReligionCommerce() == getStateReligion())
{
if (GC.getGameINLINE().getHolyCity(getStateReligion())->getNumActiveBuilding((BuildingTypes)iI) > 0)
{
bHasHolyBuilding = true;
break;
}
}
}
}
}
if (!bHasHolyBuilding)
{
iMissionary -= 10;
}
else
{
iMissionary += 10;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iMissionary += (AI_getStrategyRand(9) % 7) * 3;
if (iMissionary > 100)
{
m_iStrategyHash |= AI_STRATEGY_MISSIONARY;
}
}
}
}
// Espionage
if (!GC.getGameINLINE().isOption(GAMEOPTION_NO_ESPIONAGE))
{
int iTempValue = 0;
if (getCommercePercent(COMMERCE_ESPIONAGE) == 0)
{
iTempValue += 4;
}
if (GET_TEAM(getTeam()).getAnyWarPlanCount(true) == 0)
{
if( GET_TEAM(getTeam()).getBestKnownTechScorePercent() < 85 )
{
iTempValue += 5;
}
else
{
iTempValue += 3;
}
}
iTempValue += (100 - AI_getEspionageWeight()) / 10;
iTempValue += AI_getStrategyRand(10) % 12; // K-mod
if (iTempValue > 10)
{
m_iStrategyHash |= AI_STRATEGY_BIG_ESPIONAGE;
}
}
// Turtle strategy
if( GET_TEAM(getTeam()).getAtWarCount(true) > 0 && getNumCities() > 0 )
{
int iMaxWarCounter = 0;
for( int iTeam = 0; iTeam < MAX_CIV_TEAMS; iTeam++ )
{
if( iTeam != getTeam() )
{
if( GET_TEAM((TeamTypes)iTeam).isAlive() && !GET_TEAM((TeamTypes)iTeam).isMinorCiv() )
{
iMaxWarCounter = std::max( iMaxWarCounter, GET_TEAM(getTeam()).AI_getAtWarCounter((TeamTypes)iTeam) );
}
}
}
// Are we losing badly or recently attacked?
if( GET_TEAM(getTeam()).AI_getWarSuccessCapitulationRatio() < -50 || iMaxWarCounter < 10 )
{
if( GET_TEAM(getTeam()).AI_getEnemyPowerPercent(true) > std::max(150, GC.getDefineINT("BBAI_TURTLE_ENEMY_POWER_RATIO")) )
{
m_iStrategyHash |= AI_STRATEGY_TURTLE;
}
}
}
if( gPlayerLogLevel >= 2 )
{
if( (m_iStrategyHash & AI_STRATEGY_TURTLE) && !(iLastStrategyHash & AI_STRATEGY_TURTLE) )
{
logBBAI( " Player %d (%S) starts strategy AI_STRATEGY_TURTLE on turn %d", getID(), getCivilizationDescription(0), GC.getGameINLINE().getGameTurn());
}
if( !(m_iStrategyHash & AI_STRATEGY_TURTLE) && (iLastStrategyHash & AI_STRATEGY_TURTLE) )
{
logBBAI( " Player %d (%S) stops strategy AI_STRATEGY_TURTLE on turn %d", getID(), getCivilizationDescription(0), GC.getGameINLINE().getGameTurn());
}
}
int iCurrentEra = getCurrentEra();
int iParanoia = 0;
int iCloseTargets = 0;
int iOurDefensivePower = GET_TEAM(getTeam()).getDefensivePower();
for (iI = 0; iI < MAX_CIV_PLAYERS; iI++)
{
if( GET_PLAYER((PlayerTypes)iI).isAlive() && !GET_PLAYER((PlayerTypes)iI).isMinorCiv() && !GET_PLAYER((PlayerTypes)iI).isBarbarian() )
{
if ((GET_PLAYER((PlayerTypes)iI).getTeam() != getTeam()) && GET_TEAM(getTeam()).isHasMet(GET_PLAYER((PlayerTypes)iI).getTeam()) )
{
if (!GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isAVassal() && !GET_TEAM(getTeam()).isVassal(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
if( GET_TEAM(getTeam()).AI_getWarPlan(GET_PLAYER((PlayerTypes)iI).getTeam()) != NO_WARPLAN )
{
iCloseTargets++;
}
else if( !GET_TEAM(getTeam()).isVassal(GET_PLAYER((PlayerTypes)iI).getTeam()) )
{
// Are they a threat?
int iTempParanoia = 0;
int iTheirPower = GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).getPower(true);
if( 4*iTheirPower > 3*iOurDefensivePower )
{
if( GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).getAtWarCount(true) == 0 || GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).AI_getEnemyPowerPercent(false) < 140 )
{
// Memory of them declaring on us and our friends
int iWarMemory = AI_getMemoryCount((PlayerTypes)iI, MEMORY_DECLARED_WAR);
iWarMemory += (AI_getMemoryCount((PlayerTypes)iI, MEMORY_DECLARED_WAR_ON_FRIEND) + 1)/2;
if (iWarMemory > 0)
{
//they are a snake
iTempParanoia += 50 + 50 * iWarMemory;
if( gPlayerLogLevel >= 2 )
{
logBBAI( " Player %d (%S) wary of %S because of war memory %d", getID(), getCivilizationDescription(0), GET_PLAYER((PlayerTypes)iI).getCivilizationDescription(0), iWarMemory);
}
}
}
}
// Do we think our relations are bad?
int iCloseness = AI_playerCloseness((PlayerTypes)iI, DEFAULT_PLAYER_CLOSENESS);
if (iCloseness > 0)
{
int iAttitudeWarProb = 100 - GC.getLeaderHeadInfo(getPersonalityType()).getNoWarAttitudeProb(AI_getAttitude((PlayerTypes)iI));
if( iAttitudeWarProb > 10 )
{
if( 4*iTheirPower > 3*iOurDefensivePower )
{
iTempParanoia += iAttitudeWarProb/2;
}
iCloseTargets++;
}
if( iTheirPower > 2*iOurDefensivePower )
{
if( AI_getAttitude((PlayerTypes)iI) != ATTITUDE_FRIENDLY )
{
iTempParanoia += 25;
}
}
}
if( iTempParanoia > 0 )
{
iTempParanoia *= iTheirPower;
iTempParanoia /= std::max(1, iOurDefensivePower);
}
// Do they look like they're going for militaristic victory?
if( GET_PLAYER((PlayerTypes)iI).AI_isDoVictoryStrategy(AI_VICTORY_CONQUEST4) )
{
iTempParanoia += 200;
}
else if( GET_PLAYER((PlayerTypes)iI).AI_isDoVictoryStrategy(AI_VICTORY_CONQUEST3) )
{
iTempParanoia += 100;
}
else if( GET_PLAYER((PlayerTypes)iI).AI_isDoVictoryStrategy(AI_VICTORY_DOMINATION3) )
{
iTempParanoia += 50;
}
if( iTempParanoia > 0 )
{
if( iCloseness == 0 )
{
iTempParanoia /= 2;
}
iParanoia += iTempParanoia;
}
}
}
}
}
}
if( m_iStrategyHash & AI_STRATEGY_GET_BETTER_UNITS )
{
iParanoia *= 3;
iParanoia /= 2;
}
// Scale paranoia in later eras/larger games
//iParanoia -= (100*(iCurrentEra + 1)) / std::max(1, GC.getNumEraInfos());
// K-Mod. You call that scaling for "later eras/larger games"? It isn't scaling, and it doesn't use the map size.
// Lets try something else. Rough and ad hoc, but hopefully a bit better.
iParanoia *= (3 * GC.getNumEraInfos() - 2 * iCurrentEra);
iParanoia /= 3 * (std::max(1, GC.getNumEraInfos()));
// That starts as a factor of 1, and drop to 1/3. And now for game size...
iParanoia *= 14;
iParanoia /= (7 + std::max(GET_TEAM(getTeam()).getHasMetCivCount(true), GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getDefaultPlayers()));
// Alert strategy
if( iParanoia >= 200 )
{
m_iStrategyHash |= AI_STRATEGY_ALERT1;
if( iParanoia >= 400 )
{
m_iStrategyHash |= AI_STRATEGY_ALERT2;
}
}
// Economic focus (K-Mod) - Note: this strategy is a gambit. The goal is catch up in tech by avoiding building units.
if (GET_TEAM(getTeam()).getAnyWarPlanCount(true) == 0 &&
(100 * iAverageEnemyUnit >= 150 * iTypicalAttack && 100 * iAverageEnemyUnit >= 180 * iTypicalDefence))
{
m_iStrategyHash |= AI_STRATEGY_ECONOMY_FOCUS;
}
if( gPlayerLogLevel >= 2 )
{
if( (m_iStrategyHash & AI_STRATEGY_ALERT1) && !(iLastStrategyHash & AI_STRATEGY_ALERT1) )
{
logBBAI( " Player %d (%S) starts strategy AI_STRATEGY_ALERT1 on turn %d with iParanoia %d", getID(), getCivilizationDescription(0), GC.getGameINLINE().getGameTurn(), iParanoia);
}
if( !(m_iStrategyHash & AI_STRATEGY_ALERT1) && (iLastStrategyHash & AI_STRATEGY_ALERT1) )
{
logBBAI( " Player %d (%S) stops strategy AI_STRATEGY_ALERT1 on turn %d with iParanoia %d", getID(), getCivilizationDescription(0), GC.getGameINLINE().getGameTurn(), iParanoia);
}
if( (m_iStrategyHash & AI_STRATEGY_ALERT2) && !(iLastStrategyHash & AI_STRATEGY_ALERT2) )
{
logBBAI( " Player %d (%S) starts strategy AI_STRATEGY_ALERT2 on turn %d with iParanoia %d", getID(), getCivilizationDescription(0), GC.getGameINLINE().getGameTurn(), iParanoia);
}
if( !(m_iStrategyHash & AI_STRATEGY_ALERT2) && (iLastStrategyHash & AI_STRATEGY_ALERT2) )
{
logBBAI( " Player %d (%S) stops strategy AI_STRATEGY_ALERT2 on turn %d with iParanoia %d", getID(), getCivilizationDescription(0), GC.getGameINLINE().getGameTurn(), iParanoia);
}
}
// BBAI TODO: Integrate Dagger with new conquest victory strategy, have Dagger focus on early rushes
//dagger
if( !(AI_isDoVictoryStrategy(AI_VICTORY_CULTURE2))
&& !(m_iStrategyHash & AI_STRATEGY_MISSIONARY)
&& (iCurrentEra <= (2 + (AI_getStrategyRand(11) % 2))) && (iCloseTargets > 0)) {
int iDagger = 0;
iDagger += 12000 / std::max(100, (50 + GC.getLeaderHeadInfo(getPersonalityType()).getMaxWarRand()));
iDagger *= (AI_getStrategyRand(12) % 11);
iDagger /= 10;
iDagger += 5 * std::min(8, AI_getFlavorValue(AI_FLAVOR_MILITARY));
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
eLoopUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI)));
if ((eLoopUnit != NO_UNIT) && (GC.getUnitInfo(eLoopUnit).getCombat() > 0))
{
if ((GC.getUnitClassInfo((UnitClassTypes)iI).getDefaultUnitIndex()) != (GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI)))
{
bool bIsDefensive = (GC.getUnitInfo(eLoopUnit).getUnitAIType(UNITAI_CITY_DEFENSE) &&
!GC.getUnitInfo(eLoopUnit).getUnitAIType(UNITAI_RESERVE));
iDagger += bIsDefensive ? -10 : 0;
if (getCapitalCity()->canTrain(eLoopUnit))
{
iDagger += bIsDefensive ? 10 : 40;
int iUUStr = GC.getUnitInfo(eLoopUnit).getCombat();
int iNormalStr = GC.getUnitInfo((UnitTypes)(GC.getUnitClassInfo((UnitClassTypes)iI).getDefaultUnitIndex())).getCombat();
iDagger += 20 * range((iUUStr - iNormalStr), 0, 2);
if (GC.getUnitInfo(eLoopUnit).getPrereqAndTech() == NO_TECH)
{
iDagger += 20;
}
}
else
{
if (GC.getUnitInfo(eLoopUnit).getPrereqAndTech() != NO_TECH)
{
if (GC.getTechInfo((TechTypes)(GC.getUnitInfo(eLoopUnit).getPrereqAndTech())).getEra() <= (iCurrentEra + 1))
{
if (GET_TEAM(getTeam()).isHasTech((TechTypes)GC.getUnitInfo(eLoopUnit).getPrereqAndTech()))
{
//we have the tech but can't train the unit, dejection.
iDagger += 10;
}
else
{
//we don't have the tech, it's understandable we can't train.
iDagger += 30;
}
}
}
bool bNeedsAndBonus = false;
int iOrBonusCount = 0;
int iOrBonusHave = 0;
for (iJ = 0; iJ < GC.getNumBonusInfos(); iJ++)
{
BonusTypes eBonus = (BonusTypes)iJ;
if (eBonus != NO_BONUS)
{
if (GC.getUnitInfo(eLoopUnit).getPrereqAndBonus() == eBonus)
{
if (getNumTradeableBonuses(eBonus) == 0)
{
bNeedsAndBonus = true;
}
}
for (iK = 0; iK < GC.getNUM_UNIT_PREREQ_OR_BONUSES(); iK++)
{
if (GC.getUnitInfo(eLoopUnit).getPrereqOrBonuses(iK) == eBonus)
{
iOrBonusCount++;
if (getNumTradeableBonuses(eBonus) > 0)
{
iOrBonusHave++;
}
}
}
}
}
iDagger += 20;
if (bNeedsAndBonus)
{
iDagger -= 20;
}
if ((iOrBonusCount > 0) && (iOrBonusHave == 0))
{
iDagger -= 20;
}
}
}
}
}
if (!GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI))
{
iDagger += range(100 - GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAITrainPercent(), 0, 15);
}
if ((getCapitalCity()->area()->getAreaAIType(getTeam()) == AREAAI_OFFENSIVE) || (getCapitalCity()->area()->getAreaAIType(getTeam()) == AREAAI_DEFENSIVE))
{
iDagger += (iAttackUnitCount > 0) ? 40 : 20;
}
/************************************************************************************************/
/* REVOLUTION_MOD 05/22/08 jdog5000 */
/* */
/* Revolution AI */
/************************************************************************************************/
if( isRebel() )
{
iDagger += 30;
}
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
if (iDagger >= AI_DAGGER_THRESHOLD)
{
m_iStrategyHash |= AI_STRATEGY_DAGGER;
}
else
{
if( iLastStrategyHash &= AI_STRATEGY_DAGGER )
{
if (iDagger >= (9*AI_DAGGER_THRESHOLD)/10)
{
m_iStrategyHash |= AI_STRATEGY_DAGGER;
}
}
}
if( gPlayerLogLevel >= 2 )
{
if( (m_iStrategyHash & AI_STRATEGY_DAGGER) && !(iLastStrategyHash & AI_STRATEGY_DAGGER) )
{
logBBAI( " Player %d (%S) starts strategy AI_STRATEGY_DAGGER on turn %d with iDagger %d", getID(), getCivilizationDescription(0), GC.getGameINLINE().getGameTurn(), iDagger);
}
if( !(m_iStrategyHash & AI_STRATEGY_DAGGER) && (iLastStrategyHash & AI_STRATEGY_DAGGER) )
{
logBBAI( " Player %d (%S) stops strategy AI_STRATEGY_DAGGER on turn %d with iDagger %d", getID(), getCivilizationDescription(0), GC.getGameINLINE().getGameTurn(), iDagger);
}
}
}
if (!(m_iStrategyHash & AI_STRATEGY_ALERT2) && !(m_iStrategyHash & AI_STRATEGY_TURTLE))
{//Crush
int iWarCount = 0;
int iCrushValue = 0;
// K-Mod. (experimental)
//iCrushValue += (iNonsense % 4);
// A leader dependant value. (MaxWarRand is roughly between 50 and 200. Gandi is 400.)
//iCrushValue += (iNonsense % 3000) / (400+GC.getLeaderHeadInfo(getPersonalityType()).getMaxWarRand());
// On second thought, lets try this
iCrushValue += AI_getStrategyRand(13) % (4 + AI_getFlavorValue(AI_FLAVOR_MILITARY) / 2);
if (m_iStrategyHash & AI_STRATEGY_DAGGER)
{
iCrushValue += 3;
}
if (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI))
{
iCrushValue += 3;
}
for (iI = 0; iI < MAX_CIV_TEAMS; iI++)
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH 02/14/10 denev & jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original bts code
if ((GET_TEAM((TeamTypes)iI).isAlive()) && (iI != getID()))
*/
if ((GET_TEAM((TeamTypes)iI).isAlive()) && (iI != getTeam()))
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
{
if (GET_TEAM(getTeam()).AI_getWarPlan((TeamTypes)iI) != NO_WARPLAN)
{
if (!GET_TEAM((TeamTypes)iI).isAVassal())
{
if (GET_TEAM(getTeam()).AI_teamCloseness((TeamTypes)iI) > 0)
{
iWarCount++;
}
// K-Mod. (if we attack with our defenders, would they be beat their defenders?)
if (100 * iTypicalDefence >= 110 * GET_TEAM((TeamTypes)iI).getTypicalUnitValue(UNITAI_CITY_DEFENSE))
{
iCrushValue += 2;
}
}
if (GET_TEAM(getTeam()).AI_getWarPlan((TeamTypes)iI) == WARPLAN_PREPARING_TOTAL)
{
iCrushValue += 6;
}
else if ((GET_TEAM(getTeam()).AI_getWarPlan((TeamTypes)iI) == WARPLAN_TOTAL) && (GET_TEAM(getTeam()).AI_getWarPlanStateCounter((TeamTypes)iI) < 20))
{
iCrushValue += 6;
}
if ((GET_TEAM(getTeam()).AI_getWarPlan((TeamTypes)iI) == WARPLAN_DOGPILE) && (GET_TEAM(getTeam()).AI_getWarPlanStateCounter((TeamTypes)iI) < 20))
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH 02/14/10 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original bts code
for (iJ = 0; iJ < MAX_TEAMS; iJ++)
{
if ((iJ != iI) && iJ != getID())
*/
for (iJ = 0; iJ < MAX_CIV_TEAMS; iJ++)
{
if ((iJ != iI) && iJ != getTeam() && GET_TEAM((TeamTypes)iJ).isAlive())
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
{
if ((atWar((TeamTypes)iI, (TeamTypes)iJ)) && !GET_TEAM((TeamTypes)iI).isAVassal())
{
iCrushValue += 4;
}
}
}
}
/************************************************************************************************/
/* REVOLUTION_MOD 05/18/08 jdog5000 */
/* */
/* Revolution AI */
/************************************************************************************************/
if( GET_TEAM((TeamTypes)iI).isRebelAgainst(getTeam()) )
{
iCrushValue += 4;
}
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
}
}
}
if ((iWarCount <= 1) && (iCrushValue >= ((iLastStrategyHash & AI_STRATEGY_CRUSH) ? 9 :10)))
{
m_iStrategyHash |= AI_STRATEGY_CRUSH;
}
if( gPlayerLogLevel >= 2 )
{
if( (m_iStrategyHash & AI_STRATEGY_CRUSH) && !(iLastStrategyHash & AI_STRATEGY_CRUSH) )
{
logBBAI( " Player %d (%S) starts strategy AI_STRATEGY_CRUSH on turn %d with iCrushValue %d", getID(), getCivilizationDescription(0), GC.getGameINLINE().getGameTurn(), iCrushValue);
}
if( !(m_iStrategyHash & AI_STRATEGY_CRUSH) && (iLastStrategyHash & AI_STRATEGY_CRUSH) )
{
logBBAI( " Player %d (%S) stops strategy AI_STRATEGY_CRUSH on turn %d with iCrushValue %d", getID(), getCivilizationDescription(0), GC.getGameINLINE().getGameTurn(), iCrushValue);
}
}
}
{
CvTeamAI& kTeam = GET_TEAM(getTeam());
int iOurVictoryCountdown = kTeam.AI_getLowestVictoryCountdown();
int iTheirVictoryCountdown = MAX_INT;
for (iI = 0; iI < MAX_CIV_TEAMS; iI++)
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH 02/14/10 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original bts code
if ((GET_TEAM((TeamTypes)iI).isAlive()) && (iI != getID()))
*/
if ((GET_TEAM((TeamTypes)iI).isAlive()) && (iI != getTeam()))
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
{
CvTeamAI& kOtherTeam = GET_TEAM((TeamTypes)iI);
iTheirVictoryCountdown = std::min(iTheirVictoryCountdown, kOtherTeam.AI_getLowestVictoryCountdown());
}
}
if (MAX_INT == iTheirVictoryCountdown)
{
iTheirVictoryCountdown = -1;
}
if ((iOurVictoryCountdown >= 0) && (iTheirVictoryCountdown < 0 || iOurVictoryCountdown <= iTheirVictoryCountdown))
{
m_iStrategyHash |= AI_STRATEGY_LAST_STAND;
}
else if ((iTheirVictoryCountdown >= 0))
{
if((iTheirVictoryCountdown < iOurVictoryCountdown))
{
m_iStrategyHash |= AI_STRATEGY_FINAL_WAR;
}
else if( GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) )
{
m_iStrategyHash |= AI_STRATEGY_FINAL_WAR;
}
else if( AI_isDoVictoryStrategyLevel4() || AI_isDoVictoryStrategy(AI_VICTORY_SPACE3) )
{
m_iStrategyHash |= AI_STRATEGY_FINAL_WAR;
}
}
if (iOurVictoryCountdown < 0)
{
if (isCurrentResearchRepeat())
{
int iStronger = 0;
int iAlive = 1;
for (int iTeam = 0; iTeam < MAX_CIV_TEAMS; iTeam++)
{
if (iTeam != getTeam())
{
CvTeamAI& kLoopTeam = GET_TEAM((TeamTypes)iTeam);
if (kLoopTeam.isAlive())
{
iAlive++;
if (kTeam.getPower(true) < kLoopTeam.getPower(true))
{
iStronger++;
}
}
}
}
if ((iStronger <= 1) || (iStronger <= iAlive / 4))
{
m_iStrategyHash |= AI_STRATEGY_FINAL_WAR;
}
}
}
}
if (isCurrentResearchRepeat())
{
int iTotalVictories = 0;
int iAchieveVictories = 0;
int iWarVictories = 0;
int iThreshold = std::max(1, (GC.getGame().countCivTeamsAlive() + 1) / 4);
CvTeamAI& kTeam = GET_TEAM(getTeam());
for (int iVictory = 0; iVictory < GC.getNumVictoryInfos(); iVictory++)
{
CvVictoryInfo& kVictory = GC.getVictoryInfo((VictoryTypes)iVictory);
if (GC.getGame().isVictoryValid((VictoryTypes)iVictory))
{
iTotalVictories ++;
if (kVictory.isDiploVote())
{
//
}
else if (kVictory.isEndScore())
{
int iHigherCount = 0;
int IWeakerCount = 0;
for (int iTeam = 0; iTeam < MAX_CIV_TEAMS; iTeam++)
{
if (iTeam != getTeam())
{
CvTeamAI& kLoopTeam = GET_TEAM((TeamTypes)iTeam);
if (kLoopTeam.isAlive())
{
if (GC.getGame().getTeamScore(getTeam()) < ((GC.getGame().getTeamScore((TeamTypes)iTeam) * 90) / 100))
{
iHigherCount++;
if (kTeam.getPower(true) > kLoopTeam.getPower(true))
{
IWeakerCount++;
}
}
}
}
}
if (iHigherCount > 0)
{
if (IWeakerCount == iHigherCount)
{
iWarVictories++;
}
}
}
else if (kVictory.getCityCulture() > 0)
{
if (m_iStrategyHash & AI_VICTORY_CULTURE1)
{
iAchieveVictories++;
}
}
else if (kVictory.getMinLandPercent() > 0 || kVictory.getLandPercent() > 0)
{
int iLargerCount = 0;
for (int iTeam = 0; iTeam < MAX_CIV_TEAMS; iTeam++)
{
if (iTeam != getTeam())
{
CvTeamAI& kLoopTeam = GET_TEAM((TeamTypes)iTeam);
if (kLoopTeam.isAlive())
{
if (kTeam.getTotalLand(true) < kLoopTeam.getTotalLand(true))
{
iLargerCount++;
}
}
}
}
if (iLargerCount <= iThreshold)
{
iWarVictories++;
}
}
else if (kVictory.isConquest())
{
int iStrongerCount = 0;
for (int iTeam = 0; iTeam < MAX_CIV_TEAMS; iTeam++)
{
if (iTeam != getTeam())
{
CvTeamAI& kLoopTeam = GET_TEAM((TeamTypes)iTeam);
if (kLoopTeam.isAlive())
{
if (kTeam.getPower(true) < kLoopTeam.getPower(true))
{
iStrongerCount++;
}
}
}
}
if (iStrongerCount <= iThreshold)
{
iWarVictories++;
}
}
else
{
if (kTeam.getVictoryCountdown((VictoryTypes)iVictory) > 0)
{
iAchieveVictories++;
}
}
}
}
if (iAchieveVictories == 0)
{
if (iWarVictories > 0)
{
m_iStrategyHash |= AI_STRATEGY_FINAL_WAR;
}
}
}
//Turn off inappropriate strategies.
if (GC.getGameINLINE().isOption(GAMEOPTION_ALWAYS_PEACE))
{
m_iStrategyHash &= ~AI_STRATEGY_DAGGER;
m_iStrategyHash &= ~AI_STRATEGY_CRUSH;
m_iStrategyHash &= ~AI_STRATEGY_ALERT1;
m_iStrategyHash &= ~AI_STRATEGY_ALERT2;
m_iStrategyHash &= ~AI_STRATEGY_TURTLE;
m_iStrategyHash &= ~AI_STRATEGY_FINAL_WAR;
m_iStrategyHash &= ~AI_STRATEGY_LAST_STAND;
m_iStrategyHash &= ~AI_STRATEGY_OWABWNW;
m_iStrategyHash &= ~AI_STRATEGY_FASTMOVERS;
}
return m_iStrategyHash;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
void CvPlayerAI::AI_nowHasTech(TechTypes eTech)
{
// while its _possible_ to do checks, for financial trouble, and this tech adds financial buildings
// if in war and this tech adds important war units
// etc
// it makes more sense to just redetermine what to produce
// that is already done every time a civ meets a new civ, it makes sense to do it when a new tech is learned
// if this is changed, then at a minimum, AI_isFinancialTrouble should be checked
if (!isHuman())
{
int iGameTurn = GC.getGameINLINE().getGameTurn();
// only reset at most every 10 turns
if (iGameTurn > m_iTurnLastProductionDirty + 10)
{
// redeterimine the best things to build in each city
AI_makeProductionDirty();
m_iTurnLastProductionDirty = iGameTurn;
}
}
}
int CvPlayerAI::AI_countDeadlockedBonuses(CvPlot* pPlot) const
{
CvPlot* pLoopPlot;
CvPlot* pLoopPlot2;
int iDX, iDY;
int iI;
int iMinRange = GC.getMIN_CITY_RANGE();
int iRange = iMinRange * 2;
int iCount = 0;
for (iDX = -(iRange); iDX <= iRange; iDX++)
{
for (iDY = -(iRange); iDY <= iRange; iDY++)
{
if (plotDistance(iDX, iDY, 0, 0) > CITY_PLOTS_RADIUS)
{
pLoopPlot = plotXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getBonusType(getTeam()) != NO_BONUS)
{
if (!pLoopPlot->isCityRadius() && ((pLoopPlot->area() == pPlot->area()) || pLoopPlot->isWater()))
{
bool bCanFound = false;
bool bNeverFound = true;
//potentially blockable resource
//look for a city site within a city radius
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
{
pLoopPlot2 = plotCity(pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE(), iI);
if (pLoopPlot2 != NULL)
{
//canFound usually returns very quickly
if (canFound(pLoopPlot2->getX_INLINE(), pLoopPlot2->getY_INLINE(), false))
{
bNeverFound = false;
if (stepDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pLoopPlot2->getX_INLINE(), pLoopPlot2->getY_INLINE()) > iMinRange)
{
bCanFound = true;
break;
}
}
}
}
if (!bNeverFound && !bCanFound)
{
iCount++;
}
}
}
}
}
}
}
return iCount;
}
int CvPlayerAI::AI_getOurPlotStrength(CvPlot* pPlot, int iRange, bool bDefensiveBonuses, bool bTestMoves) const
{
PROFILE_FUNC();
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
CvPlot* pLoopPlot;
int iValue;
int iDistance;
int iDX, iDY;
iValue = 0;
for (iDX = -(iRange); iDX <= iRange; iDX++)
{
for (iDY = -(iRange); iDY <= iRange; iDY++)
{
pLoopPlot = plotXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->area() == pPlot->area())
{
iDistance = stepDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE());
pUnitNode = pLoopPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getOwnerINLINE() == getID())
{
if ((bDefensiveBonuses && pLoopUnit->canDefend()) || pLoopUnit->canAttack())
{
if (!(pLoopUnit->isInvisible(getTeam(), false)))
{
if (pLoopUnit->atPlot(pPlot) || pLoopUnit->canMoveInto(pPlot) || pLoopUnit->canMoveInto(pPlot, /*bAttack*/ true))
{
if (!bTestMoves)
{
iValue += pLoopUnit->currEffectiveStr((bDefensiveBonuses ? pPlot : NULL), NULL);
}
else
{
if (pLoopUnit->baseMoves() >= iDistance)
{
iValue += pLoopUnit->currEffectiveStr((bDefensiveBonuses ? pPlot : NULL), NULL);
}
}
}
}
}
}
}
}
}
}
}
return iValue;
}
int CvPlayerAI::AI_getEnemyPlotStrength(CvPlot* pPlot, int iRange, bool bDefensiveBonuses, bool bTestMoves) const
{
PROFILE_FUNC();
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
CvPlot* pLoopPlot;
int iValue;
int iDistance;
int iDX, iDY;
iValue = 0;
for (iDX = -(iRange); iDX <= iRange; iDX++)
{
for (iDY = -(iRange); iDY <= iRange; iDY++)
{
pLoopPlot = plotXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->area() == pPlot->area())
{
iDistance = stepDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE());
pUnitNode = pLoopPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
if (atWar(pLoopUnit->getTeam(), getTeam()))
{
if ((bDefensiveBonuses && pLoopUnit->canDefend()) || pLoopUnit->canAttack())
{
if (!(pLoopUnit->isInvisible(getTeam(), false)))
{
if (pPlot->isValidDomainForAction(*pLoopUnit))
{
if (!bTestMoves)
{
iValue += pLoopUnit->currEffectiveStr((bDefensiveBonuses ? pPlot : NULL), NULL);
}
else
{
int iDangerRange = pLoopUnit->baseMoves();
iDangerRange += ((pLoopPlot->isValidRoute(pLoopUnit)) ? 1 : 0);
if (iDangerRange >= iDistance)
{
iValue += pLoopUnit->currEffectiveStr((bDefensiveBonuses ? pPlot : NULL), NULL);
}
}
}
}
}
}
}
}
}
}
}
return iValue;
}
int CvPlayerAI::AI_goldToUpgradeAllUnits(int iExpThreshold) const
{
PROFILE_FUNC();
if (m_iUpgradeUnitsCacheTurn == GC.getGameINLINE().getGameTurn() && m_iUpgradeUnitsCachedExpThreshold == iExpThreshold)
{
return m_iUpgradeUnitsCachedGold;
}
int iTotalGold = 0;
CvCivilizationInfo& kCivilizationInfo = GC.getCivilizationInfo(getCivilizationType());
// cache the value for each unit type
std::vector<int> aiUnitUpgradePrice(GC.getNumUnitInfos(), 0); // initializes to zeros
int iLoop;
for (CvUnit* pLoopUnit = firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = nextUnit(&iLoop))
{
// if experience is below threshold, skip this unit
if (pLoopUnit->getExperience() < iExpThreshold)
{
continue;
}
UnitTypes eUnitType = pLoopUnit->getUnitType();
// check cached value for this unit type
int iCachedUnitGold = aiUnitUpgradePrice[eUnitType];
if (iCachedUnitGold != 0)
{
// if positive, add it to the sum
if (iCachedUnitGold > 0)
{
iTotalGold += iCachedUnitGold;
}
// either way, done with this unit
continue;
}
int iUnitGold = 0;
int iUnitUpgradePossibilities = 0;
UnitAITypes eUnitAIType = pLoopUnit->AI_getUnitAIType();
CvArea* pUnitArea = pLoopUnit->area();
int iUnitValue = AI_unitValue(eUnitType, eUnitAIType, pUnitArea);
for (int iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
UnitClassTypes eUpgradeUnitClassType = (UnitClassTypes) iI;
UnitTypes eUpgradeUnitType = (UnitTypes)(kCivilizationInfo.getCivilizationUnits(iI));
if (NO_UNIT != eUpgradeUnitType)
{
// is this a valid upgrade?
if (pLoopUnit->upgradeAvailable(eUnitType, eUpgradeUnitClassType))
{
// is it better?
int iUpgradeValue = AI_unitValue(eUpgradeUnitType, eUnitAIType, pUnitArea);
if (iUpgradeValue > iUnitValue)
{
// can we actually make this upgrade?
bool bCanUpgrade = false;
CvCity* pCapitalCity = getCapitalCity();
if (pCapitalCity != NULL && pCapitalCity->canTrain(eUpgradeUnitType))
{
bCanUpgrade = true;
}
else
{
CvCity* pCloseCity = GC.getMapINLINE().findCity(pLoopUnit->getX_INLINE(), pLoopUnit->getY_INLINE(), getID(), NO_TEAM, true, (pLoopUnit->getDomainType() == DOMAIN_SEA));
if (pCloseCity != NULL && pCloseCity->canTrain(eUpgradeUnitType))
{
bCanUpgrade = true;
}
}
if (bCanUpgrade)
{
iUnitGold += pLoopUnit->upgradePrice(eUpgradeUnitType);
iUnitUpgradePossibilities++;
}
}
}
}
}
// if we found any, find average and add to total
if (iUnitUpgradePossibilities > 0)
{
iUnitGold /= iUnitUpgradePossibilities;
// add to cache
aiUnitUpgradePrice[eUnitType] = iUnitGold;
// add to sum
iTotalGold += iUnitGold;
}
else
{
// add to cache, dont upgrade to this type
aiUnitUpgradePrice[eUnitType] = -1;
}
}
m_iUpgradeUnitsCacheTurn = GC.getGameINLINE().getGameTurn();
m_iUpgradeUnitsCachedExpThreshold = iExpThreshold;
m_iUpgradeUnitsCachedGold = iTotalGold;
return iTotalGold;
}
int CvPlayerAI::AI_goldTradeValuePercent() const
{
int iValue = 2;
if (AI_isFinancialTrouble())
{
iValue += 1;
}
return 100 * iValue;
}
int CvPlayerAI::AI_averageYieldMultiplier(YieldTypes eYield) const
{
FAssert(eYield > -1);
FAssert(eYield < NUM_YIELD_TYPES);
if (m_iAveragesCacheTurn != GC.getGameINLINE().getGameTurn())
{
AI_calculateAverages();
}
FAssert(m_aiAverageYieldMultiplier[eYield] > 0);
return m_aiAverageYieldMultiplier[eYield];
}
int CvPlayerAI::AI_averageCommerceMultiplier(CommerceTypes eCommerce) const
{
FAssert(eCommerce > -1);
FAssert(eCommerce < NUM_COMMERCE_TYPES);
if (m_iAveragesCacheTurn != GC.getGameINLINE().getGameTurn())
{
AI_calculateAverages();
}
return m_aiAverageCommerceMultiplier[eCommerce];
}
int CvPlayerAI::AI_averageGreatPeopleMultiplier() const
{
if (m_iAveragesCacheTurn != GC.getGameINLINE().getGameTurn())
{
AI_calculateAverages();
}
return m_iAverageGreatPeopleMultiplier;
}
//"100 eCommerce is worth (return) raw YIELD_COMMERCE
int CvPlayerAI::AI_averageCommerceExchange(CommerceTypes eCommerce) const
{
FAssert(eCommerce > -1);
FAssert(eCommerce < NUM_COMMERCE_TYPES);
if (m_iAveragesCacheTurn != GC.getGameINLINE().getGameTurn())
{
AI_calculateAverages();
}
return m_aiAverageCommerceExchange[eCommerce];
}
void CvPlayerAI::AI_calculateAverages() const
{
CvCity* pLoopCity;
int iLoop;
int iI;
int iPopulation;
int iTotalPopulation;
EnterCriticalSection(&g_cAveragesCalculationSection);
if ( m_iAveragesCacheTurn != GC.getGameINLINE().getGameTurn() )
{
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
m_aiAverageYieldMultiplier[iI] = 0;
}
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
m_aiAverageCommerceMultiplier[iI] = 0;
}
m_iAverageGreatPeopleMultiplier = 0;
iTotalPopulation = 0;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
iPopulation = std::max(pLoopCity->getPopulation(), NUM_CITY_PLOTS);
iTotalPopulation += iPopulation;
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
m_aiAverageYieldMultiplier[iI] += iPopulation * pLoopCity->AI_yieldMultiplier((YieldTypes)iI);
}
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
m_aiAverageCommerceMultiplier[iI] += iPopulation * pLoopCity->getTotalCommerceRateModifier((CommerceTypes)iI);
}
m_iAverageGreatPeopleMultiplier += iPopulation * pLoopCity->getTotalGreatPeopleRateModifier();
}
if (iTotalPopulation > 0)
{
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
m_aiAverageYieldMultiplier[iI] /= iTotalPopulation;
FAssert(m_aiAverageYieldMultiplier[iI] > 0);
/************************************************************************************************/
/* Afforess Start 06/04/10 */
/* */
/* */
/************************************************************************************************/
m_aiAverageYieldMultiplier[iI] = std::max(1, m_aiAverageYieldMultiplier[iI]);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
m_aiAverageCommerceMultiplier[iI] /= iTotalPopulation;
FAssert(m_aiAverageCommerceMultiplier[iI] > 0);
/************************************************************************************************/
/* Afforess Start 06/04/10 */
/* */
/* */
/************************************************************************************************/
m_aiAverageCommerceMultiplier[iI] = std::max(1, m_aiAverageCommerceMultiplier[iI]);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
m_iAverageGreatPeopleMultiplier /= iTotalPopulation;
/************************************************************************************************/
/* Afforess Start 06/04/10 */
/* */
/* */
/************************************************************************************************/
m_iAverageGreatPeopleMultiplier = std::max(1, m_iAverageGreatPeopleMultiplier);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
else
{
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
m_aiAverageYieldMultiplier[iI] = 100;
}
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
m_aiAverageCommerceMultiplier[iI] = 100;
}
m_iAverageGreatPeopleMultiplier = 100;
}
//Calculate Exchange Rate
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
m_aiAverageCommerceExchange[iI] = 0;
}
int iCommerce = 0;
int iTotalCommerce = 0;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
iCommerce = pLoopCity->getYieldRate(YIELD_COMMERCE);
iTotalCommerce += iCommerce;
int iExtraCommerce = 0;
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
iExtraCommerce +=((pLoopCity->getSpecialistPopulation() + pLoopCity->getNumGreatPeople()) * getSpecialistExtraCommerce((CommerceTypes)iI));
iExtraCommerce += (pLoopCity->getBuildingCommerce((CommerceTypes)iI) + pLoopCity->getSpecialistCommerce((CommerceTypes)iI) + pLoopCity->getReligionCommerce((CommerceTypes)iI) + getFreeCityCommerce((CommerceTypes)iI));
}
iTotalCommerce += iExtraCommerce;
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
m_aiAverageCommerceExchange[iI] += ((iCommerce + iExtraCommerce) * pLoopCity->getTotalCommerceRateModifier((CommerceTypes)iI)) / 100;
}
}
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
if (m_aiAverageCommerceExchange[iI] > 0)
{
m_aiAverageCommerceExchange[iI] = (100 * iTotalCommerce) / m_aiAverageCommerceExchange[iI];
}
else
{
m_aiAverageCommerceExchange[iI] = 100;
}
}
m_iAveragesCacheTurn = GC.getGameINLINE().getGameTurn();
}
LeaveCriticalSection(&g_cAveragesCalculationSection);
}
// K-Mod edition
void CvPlayerAI::AI_convertUnitAITypesForCrush()
{
int iLoop;
std::map<int, int> spare_units;
std::multimap<int, CvUnit*> ordered_units;
CvArea *pLoopArea = NULL;
for (pLoopArea = GC.getMapINLINE().firstArea(&iLoop); pLoopArea != NULL; pLoopArea = GC.getMapINLINE().nextArea(&iLoop))
{
// Keep 1/2 of recommended floating defenders.
if (!pLoopArea || pLoopArea->getAreaAIType(getTeam()) == AREAAI_ASSAULT
|| pLoopArea->getAreaAIType(getTeam()) == AREAAI_DEFENSIVE)
{
spare_units[pLoopArea->getID()] = 0;
}
else
{
spare_units[pLoopArea->getID()] = (2 * AI_getTotalFloatingDefenders(pLoopArea) - AI_getTotalFloatingDefendersNeeded(pLoopArea)) / 2;
}
}
CvUnit* pLoopUnit;
for (pLoopUnit = firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = nextUnit(&iLoop))
{
bool bValid = false;
/* if (pLoopUnit->AI_getUnitAIType() == UNITAI_RESERVE
|| pLoopUnit->AI_isCityAIType() && (pLoopUnit->getExtraCityDefensePercent() <= 0)) */
// K-Mod, protective leaders might still want to use their gunpowder units...
if (pLoopUnit->AI_getUnitAIType() == UNITAI_RESERVE || pLoopUnit->AI_getUnitAIType() == UNITAI_COLLATERAL
|| (pLoopUnit->AI_isCityAIType() && pLoopUnit->getExtraCityDefensePercent() <= 30))
{
bValid = true;
}
/*if ((pLoopUnit->area()->getAreaAIType(getTeam()) == AREAAI_ASSAULT)
|| (pLoopUnit->area()->getAreaAIType(getTeam()) == AREAAI_DEFENSIVE))
{
bValid = false;
}*/
if (!pLoopUnit->canAttack() || (pLoopUnit->AI_getUnitAIType() == UNITAI_CITY_SPECIAL))
{
bValid = false;
}
if (spare_units[pLoopUnit->area()->getID()] <= 0)
bValid = false;
if (bValid)
{
if (pLoopUnit->plot()->isCity())
{
if (pLoopUnit->plot()->getPlotCity()->getOwner() == getID())
{
if (pLoopUnit->plot()->getBestDefender(getID()) == pLoopUnit)
{
bValid = false;
}
}
}
}
if (bValid)
{
int iValue = AI_unitValue(pLoopUnit->getUnitType(), UNITAI_ATTACK_CITY, pLoopUnit->area());
ordered_units.insert(std::make_pair(iValue, pLoopUnit));
}
}
// convert the highest scoring units first.
std::multimap<int, CvUnit*>::reverse_iterator rit;
for (rit = ordered_units.rbegin(); rit != ordered_units.rend(); ++rit)
{
if (rit->first > 0 && spare_units[rit->second->area()->getID()] > 0)
{
rit->second->AI_setUnitAIType(UNITAI_ATTACK_CITY);
spare_units[rit->second->area()->getID()]--;
}
}
}
int CvPlayerAI::AI_playerCloseness(PlayerTypes eIndex, int iMaxDistance) const
{
PROFILE_FUNC();
FAssert(GET_PLAYER(eIndex).isAlive());
FAssert(eIndex != getID());
int iValue = 0;
int iLoop;
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
iValue += pLoopCity->AI_playerCloseness(eIndex, iMaxDistance);
}
return iValue;
}
int CvPlayerAI::AI_getTotalAreaCityThreat(CvArea* pArea, int* piLargestThreat) const
{
PROFILE_FUNC();
CvCity* pLoopCity;
int iLoop;
int iValue = 0;
int iLargestThreat = 0;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (pLoopCity->getArea() == pArea->getID())
{
int iThreat = pLoopCity->AI_cityThreat();
iValue += iThreat;
if ( iThreat > iLargestThreat )
{
iLargestThreat = iThreat;
}
}
}
if ( piLargestThreat != NULL )
{
*piLargestThreat = iLargestThreat;
}
return iValue;
}
int CvPlayerAI::AI_countNumAreaHostileUnits(CvArea* pArea, bool bPlayer, bool bTeam, bool bNeutral, bool bHostile, CvPlot* pPlot, int iMaxDistance) const
{
PROFILE_FUNC();
CvPlot* pLoopPlot;
int iCount;
int iI;
iCount = 0;
for (iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
{
pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
if ((pLoopPlot->area() == pArea) && pLoopPlot->isVisible(getTeam(), false) && stepDistance(pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE(), pPlot->getX_INLINE(), pPlot->getY_INLINE()) <= iMaxDistance &&
((bPlayer && pLoopPlot->getOwnerINLINE() == getID()) || (bTeam && pLoopPlot->getTeam() == getTeam())
|| (bNeutral && !pLoopPlot->isOwned()) || (bHostile && pLoopPlot->isOwned() && GET_TEAM(getTeam()).isAtWar(pLoopPlot->getTeam()))))
{
iCount += pLoopPlot->plotCount(PUF_isEnemy, getID(), 0, NO_PLAYER, NO_TEAM, PUF_isVisible, getID());
}
}
return iCount;
}
//this doesn't include the minimal one or two garrison units in each city.
int CvPlayerAI::AI_getTotalFloatingDefendersNeeded(CvArea* pArea) const
{
PROFILE_FUNC();
int iDefenders;
int iCurrentEra = getCurrentEra();
int iAreaCities = pArea->getCitiesPerPlayer(getID());
iCurrentEra = std::max(0, iCurrentEra - GC.getGame().getStartEra() / 2);
iDefenders = 1 + ((iCurrentEra + ((GC.getGameINLINE().getMaxCityElimination() > 0) ? 3 : 2)) * iAreaCities);
iDefenders /= 3;
iDefenders += pArea->getPopulationPerPlayer(getID()) / 7;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 04/01/10 jdog5000 */
/* */
/* War strategy AI, Victory Strategy AI */
/************************************************************************************************/
if( pArea->getAreaAIType(getTeam()) == AREAAI_DEFENSIVE || pArea->getAreaAIType(getTeam()) == AREAAI_OFFENSIVE || pArea->getAreaAIType(getTeam()) == AREAAI_MASSING )
{
if( !pArea->isHomeArea(getID()) && iAreaCities <= std::min(4, pArea->getNumCities()/3) )
{
// Land war here, as floating defenders are based on cities/population need to make sure
// AI defends its footholds in new continents well.
iDefenders += GET_TEAM(getTeam()).countEnemyPopulationByArea(pArea) / 14;
}
}
if (pArea->getAreaAIType(getTeam()) == AREAAI_DEFENSIVE)
{
iDefenders *= 2;
}
else
{
if( AI_isDoStrategy(AI_STRATEGY_ALERT2) )
{
iDefenders *= 2;
}
else if( AI_isDoStrategy(AI_STRATEGY_ALERT1) )
{
iDefenders *= 3;
iDefenders /= 2;
}
else if (pArea->getAreaAIType(getTeam()) == AREAAI_OFFENSIVE)
{
iDefenders *= 2;
iDefenders /= 3;
}
else if (pArea->getAreaAIType(getTeam()) == AREAAI_MASSING)
{
if( GET_TEAM(getTeam()).AI_getEnemyPowerPercent(true) < (10 + GC.getLeaderHeadInfo(getPersonalityType()).getMaxWarNearbyPowerRatio()) )
{
iDefenders *= 2;
iDefenders /= 3;
}
}
}
if (AI_getTotalAreaCityThreat(pArea) == 0)
{
iDefenders /= 2;
}
if (!GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI))
{
iDefenders *= 2;
iDefenders /= 3;
}
// Afforess - check finances
if (GET_TEAM(getTeam()).getAnyWarPlanCount(true) == 0 && AI_isFinancialTrouble())
{
iDefenders = std::max(1, iDefenders / 2);
}
// Removed AI_STRATEGY_GET_BETTER_UNITS reduction, it was reducing defenses twice
if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE3))
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
iDefenders += 2 * iAreaCities;
if (pArea->getAreaAIType(getTeam()) == AREAAI_DEFENSIVE)
{
iDefenders *= 2; //go crazy
}
}
iDefenders *= 60;
iDefenders /= std::max(30, (GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAITrainPercent() - 20));
if ((iCurrentEra < 3) && (GC.getGameINLINE().isOption(GAMEOPTION_RAGING_BARBARIANS)))
{
iDefenders += 2;
}
if (getCapitalCity() != NULL)
{
if (getCapitalCity()->area() != pArea)
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH 01/23/09 jdog5000 */
/* */
/* Bugfix, War tactics AI */
/************************************************************************************************/
/* original BTS code
//Defend offshore islands only lightly.
iDefenders = std::min(iDefenders, iAreaCities * iAreaCities - 1);
*/
// Lessen defensive requirements only if not being attacked locally
if( pArea->getAreaAIType(getTeam()) != AREAAI_DEFENSIVE )
{
// This may be our first city captured on a large enemy continent, need defenses to scale up based
// on total number of area cities not just ours
iDefenders = std::min(iDefenders, iAreaCities * iAreaCities + pArea->getNumCities() - iAreaCities - 1);
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
}
}
return iDefenders;
}
int CvPlayerAI::AI_getTotalFloatingDefenders(CvArea* pArea) const
{
PROFILE_FUNC();
int iCount = 0;
iCount += AI_totalAreaUnitAIs(pArea, UNITAI_COLLATERAL);
iCount += AI_totalAreaUnitAIs(pArea, UNITAI_RESERVE);
iCount += std::max(0, (AI_totalAreaUnitAIs(pArea, UNITAI_CITY_DEFENSE) - (pArea->getCitiesPerPlayer(getID()) * 2)));
iCount += AI_totalAreaUnitAIs(pArea, UNITAI_CITY_COUNTER);
iCount += AI_totalAreaUnitAIs(pArea, UNITAI_CITY_SPECIAL);
// BBAI TODO: Defense air? Is this outdated?
iCount += AI_totalAreaUnitAIs(pArea, UNITAI_DEFENSE_AIR);
return iCount;
}
RouteTypes CvPlayerAI::AI_bestAdvancedStartRoute(CvPlot* pPlot, int* piYieldValue) const
{
RouteTypes eBestRoute = NO_ROUTE;
int iBestValue = -1;
for (int iI = 0; iI < GC.getNumRouteInfos(); iI++)
{
RouteTypes eRoute = (RouteTypes)iI;
int iValue = 0;
int iCost = getAdvancedStartRouteCost(eRoute, true, pPlot);
if (iCost >= 0)
{
iValue += GC.getRouteInfo(eRoute).getValue();
if (iValue > 0)
{
int iYieldValue = 0;
if (pPlot->getImprovementType() != NO_IMPROVEMENT)
{
iYieldValue += ((GC.getImprovementInfo(pPlot->getImprovementType()).getRouteYieldChanges(eRoute, YIELD_FOOD)) * 100);
iYieldValue += ((GC.getImprovementInfo(pPlot->getImprovementType()).getRouteYieldChanges(eRoute, YIELD_PRODUCTION)) * 60);
iYieldValue += ((GC.getImprovementInfo(pPlot->getImprovementType()).getRouteYieldChanges(eRoute, YIELD_COMMERCE)) * 40);
}
iValue *= 1000;
iValue /= (1 + iCost);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestRoute = eRoute;
if (piYieldValue != NULL)
{
*piYieldValue = iYieldValue;
}
}
}
}
}
return eBestRoute;
}
UnitTypes CvPlayerAI::AI_bestAdvancedStartUnitAI(CvPlot* pPlot, UnitAITypes eUnitAI) const
{
UnitTypes eLoopUnit;
UnitTypes eBestUnit;
int iValue;
int iBestValue;
int iI, iJ, iK;
FAssertMsg(eUnitAI != NO_UNITAI, "UnitAI is not assigned a valid value");
iBestValue = 0;
eBestUnit = NO_UNIT;
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
eLoopUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI)));
if (eLoopUnit != NO_UNIT)
{
//if (!isHuman() || (GC.getUnitInfo(eLoopUnit).getDefaultUnitAIType() == eUnitAI))
{
int iUnitCost = getAdvancedStartUnitCost(eLoopUnit, true, pPlot);
if (iUnitCost >= 0)
{
iValue = AI_unitValue(eLoopUnit, eUnitAI, pPlot->area());
if (iValue > 0)
{
//free promotions. slow?
//only 1 promotion per source is counted (ie protective isn't counted twice)
int iPromotionValue = 0;
//special to the unit
for (iJ = 0; iJ < GC.getNumPromotionInfos(); iJ++)
{
if (GC.getUnitInfo(eLoopUnit).getFreePromotions(iJ))
{
iPromotionValue += 15;
break;
}
}
for (iK = 0; iK < GC.getNumPromotionInfos(); iK++)
{
if (isFreePromotion((UnitCombatTypes)GC.getUnitInfo(eLoopUnit).getUnitCombatType(), (PromotionTypes)iK))
{
iPromotionValue += 15;
break;
}
if (isFreePromotion((UnitClassTypes)GC.getUnitInfo(eLoopUnit).getUnitClassType(), (PromotionTypes)iK))
{
iPromotionValue += 15;
break;
}
}
//traits
/************************************************************************************************/
/* Afforess Start 08/26/10 */
/* */
/* */
/************************************************************************************************/
/* for (iJ = 0; iJ < GC.getNumTraitInfos(); iJ++)
{
if (hasTrait((TraitTypes)iJ))
{
for (iK = 0; iK < GC.getNumPromotionInfos(); iK++)
{
if (GC.getTraitInfo((TraitTypes) iJ).isFreePromotion(iK))
{
if ((GC.getUnitInfo(eLoopUnit).getUnitCombatType() != NO_UNITCOMBAT) && GC.getTraitInfo((TraitTypes) iJ).isFreePromotionUnitCombat(GC.getUnitInfo(eLoopUnit).getUnitCombatType()))
{
iPromotionValue += 15;
break;
}
}
}
}
}
*/
if (GC.getUnitInfo(eLoopUnit).getUnitCombatType() != NO_UNITCOMBAT)
{
for (iJ = 0; iJ < GC.getNumTraitInfos(); iJ++)
{
if (hasTrait((TraitTypes)iJ))
{
for (iK = 0; iK < GC.getNumPromotionInfos(); iK++)
{
if (GC.getTraitInfo((TraitTypes) iJ).isFreePromotionUnitCombats(iK, GC.getUnitInfo(eLoopUnit).getUnitCombatType()))
{
iPromotionValue += 15;
}
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iValue *= (iPromotionValue + 100);
iValue /= 100;
iValue *= (GC.getGameINLINE().getSorenRandNum(40, "AI Best Advanced Start Unit") + 100);
iValue /= 100;
iValue *= (getNumCities() + 2);
iValue /= (getUnitClassCountPlusMaking((UnitClassTypes)iI) + getNumCities() + 2);
FAssert((MAX_INT / 1000) > iValue);
iValue *= 1000;
iValue /= 1 + iUnitCost;
iValue = std::max(1, iValue);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestUnit = eLoopUnit;
}
}
}
}
}
}
return eBestUnit;
}
CvPlot* CvPlayerAI::AI_advancedStartFindCapitalPlot() const
{
CvPlot* pBestPlot = NULL;
int iBestValue = -1;
for (int iPlayer = 0; iPlayer < MAX_PLAYERS; iPlayer++)
{
CvPlayer& kPlayer = GET_PLAYER((PlayerTypes)iPlayer);
if (kPlayer.isAlive())
{
if (kPlayer.getTeam() == getTeam())
{
CvPlot* pLoopPlot = kPlayer.getStartingPlot();
if (pLoopPlot != NULL)
{
if (getAdvancedStartCityCost(true, pLoopPlot) > 0)
{
int iX = pLoopPlot->getX_INLINE();
int iY = pLoopPlot->getY_INLINE();
int iValue = 1000;
if (iPlayer == getID())
{
iValue += 1000;
}
else
{
iValue += GC.getGame().getSorenRandNum(100, "AI Advanced Start Choose Team Start");
}
CvCity * pNearestCity = GC.getMapINLINE().findCity(iX, iY, NO_PLAYER, getTeam());
if (NULL != pNearestCity)
{
FAssert(pNearestCity->getTeam() == getTeam());
int iDistance = stepDistance(iX, iY, pNearestCity->getX_INLINE(), pNearestCity->getY_INLINE());
if (iDistance < 10)
{
iValue /= (10 - iDistance);
}
}
if (iValue > iBestValue)
{
iBestValue = iValue;
pBestPlot = pLoopPlot;
}
}
}
else
{
FAssertMsg(false, "StartingPlot for a live player is NULL!");
}
}
}
}
if (pBestPlot != NULL)
{
return pBestPlot;
}
FAssertMsg(false, "AS: Failed to find a starting plot for a player");
//Execution should almost never reach here.
//Update found values just in case - particulary important for simultaneous turns.
AI_updateFoundValues(true);
pBestPlot = NULL;
iBestValue = -1;
if (NULL != getStartingPlot())
{
for (int iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
{
CvPlot* pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
if (pLoopPlot->getArea() == getStartingPlot()->getArea())
{
int iValue = pLoopPlot->getFoundValue(getID());
if (iValue > 0)
{
if (getAdvancedStartCityCost(true, pLoopPlot) > 0)
{
if (iValue > iBestValue)
{
iBestValue = iValue;
pBestPlot = pLoopPlot;
}
}
}
}
}
}
if (pBestPlot != NULL)
{
return pBestPlot;
}
//Commence panic.
FAssertMsg(false, "Failed to find an advanced start starting plot");
return NULL;
}
bool CvPlayerAI::AI_advancedStartPlaceExploreUnits(bool bLand)
{
CvPlot* pBestExplorePlot = NULL;
int iBestExploreValue = 0;
UnitTypes eBestUnitType = NO_UNIT;
UnitAITypes eUnitAI = NO_UNITAI;
if (bLand)
{
eUnitAI = UNITAI_EXPLORE;
}
else
{
eUnitAI = UNITAI_EXPLORE_SEA;
}
int iLoop;
CvCity* pLoopCity;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
CvPlot* pLoopPlot = pLoopCity->plot();
CvArea* pLoopArea = bLand ? pLoopCity->area() : pLoopPlot->waterArea();
if (pLoopArea != NULL)
{
int iValue = std::max(0, pLoopArea->getNumUnrevealedTiles(getTeam()) - 10) * 10;
iValue += std::max(0, pLoopArea->getNumTiles() - 50);
if (iValue > 0)
{
int iOtherPlotCount = 0;
int iGoodyCount = 0;
int iExplorerCount = 0;
int iAreaId = pLoopArea->getID();
int iRange = 4;
for (int iX = -iRange; iX <= iRange; iX++)
{
for (int iY = -iRange; iY <= iRange; iY++)
{
CvPlot* pLoopPlot2 = plotXY(pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE(), iX, iY);
if (NULL != pLoopPlot2)
{
iExplorerCount += pLoopPlot2->plotCount(PUF_isUnitAIType, eUnitAI, -1, NO_PLAYER, getTeam());
if (pLoopPlot2->getArea() == iAreaId)
{
if (pLoopPlot2->isGoody())
{
iGoodyCount++;
}
if (pLoopPlot2->getTeam() != getTeam())
{
iOtherPlotCount++;
}
}
}
}
}
iValue -= 300 * iExplorerCount;
iValue += 200 * iGoodyCount;
iValue += 10 * iOtherPlotCount;
if (iValue > iBestExploreValue)
{
UnitTypes eUnit = AI_bestAdvancedStartUnitAI(pLoopPlot, eUnitAI);
if (eUnit != NO_UNIT)
{
eBestUnitType = eUnit;
iBestExploreValue = iValue;
pBestExplorePlot = pLoopPlot;
}
}
}
}
}
if (pBestExplorePlot != NULL)
{
doAdvancedStartAction(ADVANCEDSTARTACTION_UNIT, pBestExplorePlot->getX_INLINE(), pBestExplorePlot->getY_INLINE(), eBestUnitType, true);
return true;
}
return false;
}
void CvPlayerAI::AI_advancedStartRevealRadius(CvPlot* pPlot, int iRadius)
{
for (int iRange = 1; iRange <=iRadius; iRange++)
{
for (int iX = -iRange; iX <= iRange; iX++)
{
for (int iY = -iRange; iY <= iRange; iY++)
{
if (plotDistance(0, 0, iX, iY) <= iRadius)
{
CvPlot* pLoopPlot = plotXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iX, iY);
if (NULL != pLoopPlot)
{
if (getAdvancedStartVisibilityCost(true, pLoopPlot) > 0)
{
doAdvancedStartAction(ADVANCEDSTARTACTION_VISIBILITY, pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE(), -1, true);
}
}
}
}
}
}
}
bool CvPlayerAI::AI_advancedStartPlaceCity(CvPlot* pPlot)
{
//If there is already a city, then improve it.
CvCity* pCity = pPlot->getPlotCity();
if (pCity == NULL)
{
doAdvancedStartAction(ADVANCEDSTARTACTION_CITY, pPlot->getX(), pPlot->getY(), -1, true);
pCity = pPlot->getPlotCity();
if ((pCity == NULL) || (pCity->getOwnerINLINE() != getID()))
{
//this should never happen since the cost for a city should be 0 if
//the city can't be placed.
//(It can happen if another player has placed a city in the fog)
FAssertMsg(false, "ADVANCEDSTARTACTION_CITY failed in unexpected way");
return false;
}
}
/************************************************************************************************/
/* Afforess Start 06/07/10 */
/************************************************************************************************/
//Only expand culture when we have lots to spare. Never expand for the capital, the palace works fine on it's own
if (pCity != getCapitalCity() && getAdvancedStartPoints() > getAdvancedStartCultureCost(true, pCity) * 50)
{
if (pCity->getCultureLevel() <= 1)
{
doAdvancedStartAction(ADVANCEDSTARTACTION_CULTURE, pPlot->getX(), pPlot->getY(), -1, true);
//to account for culture expansion.
pCity->AI_updateBestBuild();
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
int iPlotsImproved = 0;
/************************************************************************************************/
/* Afforess Start 06/07/10 */
/* */
/* */
/************************************************************************************************/
for (int iI = 0; iI < pCity->getNumCityPlots(); iI++)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
if (iI != CITY_HOME_PLOT)
{
CvPlot* pLoopPlot = plotCity(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iI);
if ((pLoopPlot != NULL) && (pLoopPlot->getWorkingCity() == pCity))
{
if (pLoopPlot->getImprovementType() != NO_IMPROVEMENT)
{
iPlotsImproved++;
}
}
}
}
/************************************************************************************************/
/* Afforess Start 06/07/10 */
/************************************************************************************************/
int iDivisor = std::max(1, 2000 / std::max(1, getAdvancedStartPoints()));
int iTargetPopulation = pCity->happyLevel() + (getCurrentEra() / 2);
iTargetPopulation /= iDivisor;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
while (iPlotsImproved < iTargetPopulation)
{
CvPlot* pBestPlot;
ImprovementTypes eBestImprovement = NO_IMPROVEMENT;
int iBestValue = 0;
/************************************************************************************************/
/* Afforess Start 06/07/10 */
/* */
/* */
/************************************************************************************************/
for (int iI = 0; iI < pCity->getNumCityPlots(); iI++)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
int iValue = pCity->AI_getBestBuildValue(iI);
if (iValue > iBestValue)
{
BuildTypes eBuild = pCity->AI_getBestBuild(iI);
if (eBuild != NO_BUILD)
{
ImprovementTypes eImprovement = (ImprovementTypes)GC.getBuildInfo(eBuild).getImprovement();
if (eImprovement != NO_IMPROVEMENT)
{
CvPlot* pLoopPlot = plotCity(pCity->getX_INLINE(), pCity->getY_INLINE(), iI);
if ((pLoopPlot != NULL) && (pLoopPlot->getImprovementType() != eImprovement))
{
eBestImprovement = eImprovement;
pBestPlot = pLoopPlot;
iBestValue = iValue;
}
}
}
}
}
if (iBestValue > 0)
{
FAssert(pBestPlot != NULL);
doAdvancedStartAction(ADVANCEDSTARTACTION_IMPROVEMENT, pBestPlot->getX_INLINE(), pBestPlot->getY_INLINE(), eBestImprovement, true);
iPlotsImproved++;
if (pCity->getPopulation() < iPlotsImproved)
{
doAdvancedStartAction(ADVANCEDSTARTACTION_POP, pBestPlot->getX_INLINE(), pBestPlot->getY_INLINE(), -1, true);
}
}
else
{
break;
}
}
while (iPlotsImproved > pCity->getPopulation())
{
int iPopCost = getAdvancedStartPopCost(true, pCity);
if (iPopCost <= 0 || iPopCost > getAdvancedStartPoints())
{
break;
}
if (pCity->healthRate() < 0)
{
break;
}
doAdvancedStartAction(ADVANCEDSTARTACTION_POP, pPlot->getX_INLINE(), pPlot->getY_INLINE(), -1, true);
}
pCity->AI_updateAssignWork();
return true;
}
//Returns false if we have no more points.
bool CvPlayerAI::AI_advancedStartDoRoute(CvPlot* pFromPlot, CvPlot* pToPlot)
{
FAssert(pFromPlot != NULL);
FAssert(pToPlot != NULL);
FAStarNode* pNode;
gDLL->getFAStarIFace()->ForceReset(&GC.getStepFinder());
if (gDLL->getFAStarIFace()->GeneratePath(&GC.getStepFinder(), pFromPlot->getX_INLINE(), pFromPlot->getY_INLINE(), pToPlot->getX_INLINE(), pToPlot->getY_INLINE(), false, -1, true))
{
pNode = gDLL->getFAStarIFace()->GetLastNode(&GC.getStepFinder());
if (pNode != NULL)
{
if (pNode->m_iData1 > (1 + stepDistance(pFromPlot->getX(), pFromPlot->getY(), pToPlot->getX(), pToPlot->getY())))
{
//Don't build convulted paths.
return true;
}
}
while (pNode != NULL)
{
CvPlot* pPlot = GC.getMapINLINE().plotSorenINLINE(pNode->m_iX, pNode->m_iY);
RouteTypes eRoute = AI_bestAdvancedStartRoute(pPlot);
if (eRoute != NO_ROUTE)
{
if (getAdvancedStartRouteCost(eRoute, true, pPlot) > getAdvancedStartPoints())
{
return false;
}
doAdvancedStartAction(ADVANCEDSTARTACTION_ROUTE, pNode->m_iX, pNode->m_iY, eRoute, true);
}
pNode = pNode->m_pParent;
}
}
return true;
}
void CvPlayerAI::AI_advancedStartRouteTerritory()
{
// //This uses a heuristic to create a road network
// //which is at least effecient if not all inclusive
// //Basically a human will place roads where they connect
// //the maximum number of trade groups and this
// //mimics that.
//
//
// CvPlot* pLoopPlot;
// CvPlot* pLoopPlot2;
// int iI, iJ;
// int iPass;
//
//
// std::vector<int> aiPlotGroups;
// for (iPass = 4; iPass > 1; --iPass)
// {
// for (iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
// {
// pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
// if ((pLoopPlot != NULL) && (pLoopPlot->getOwner() == getID()) && (pLoopPlot->getRouteType() == NO_ROUTE))
// {
// aiPlotGroups.clear();
// if (pLoopPlot->getPlotGroup(getID()) != NULL)
// {
// aiPlotGroups.push_back(pLoopPlot->getPlotGroup(getID())->getID());
// }
// for (iJ = 0; iJ < NUM_DIRECTION_TYPES; iJ++)
// {
// pLoopPlot2 = plotDirection(pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE(), (DirectionTypes)iJ);
// if ((pLoopPlot2 != NULL) && (pLoopPlot2->getRouteType() != NO_ROUTE))
// {
// CvPlotGroup* pPlotGroup = pLoopPlot2->getPlotGroup(getID());
// if (pPlotGroup != NULL)
// {
// if (std::find(aiPlotGroups.begin(),aiPlotGroups.end(), pPlotGroup->getID()) == aiPlotGroups.end())
// {
// aiPlotGroups.push_back(pPlotGroup->getID());
// }
// }
// }
// }
// if ((int)aiPlotGroups.size() >= iPass)
// {
// RouteTypes eBestRoute = AI_bestAdvancedStartRoute(pLoopPlot);
// if (eBestRoute != NO_ROUTE)
// {
// doAdvancedStartAction(ADVANCEDSTARTACTION_ROUTE, pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE(), eBestRoute, true);
// }
// }
// }
// }
// }
//
// //Maybe try to build road network for mobility but bearing in mind
// //that routes can't be built outside culture atm. I think workers
// //can do that just fine.
CvPlot* pLoopPlot;
int iI;
for (iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
{
pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
if ((pLoopPlot != NULL) && (pLoopPlot->getOwner() == getID()) && (pLoopPlot->getRouteType() == NO_ROUTE))
{
if (pLoopPlot->getImprovementType() != NO_IMPROVEMENT)
{
BonusTypes eBonus = pLoopPlot->getBonusType(getTeam());
if (eBonus != NO_BONUS)
{
if (GC.getImprovementInfo(pLoopPlot->getImprovementType()).isImprovementBonusTrade(eBonus))
{
int iBonusValue = AI_bonusVal(eBonus, 1);
if (iBonusValue > 9)
{
int iBestValue = 0;
CvPlot* pBestPlot = NULL;
int iRange = 2;
for (int iX = -iRange; iX <= iRange; iX++)
{
for (int iY = -iRange; iY <= iRange; iY++)
{
CvPlot* pLoopPlot2 = plotXY(pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE(), iX, iY);
if (pLoopPlot2 != NULL)
{
if (pLoopPlot2->getOwner() == getID())
{
if ((pLoopPlot2->isConnectedToCapital()) || pLoopPlot2->isCity())
{
int iValue = 1000;
if (pLoopPlot2->isCity())
{
iValue += 100;
if (pLoopPlot2->getPlotCity()->isCapital())
{
iValue += 100;
}
}
if (pLoopPlot2->isRoute())
{
iValue += 100;
}
int iDistance = GC.getMapINLINE().calculatePathDistance(pLoopPlot, pLoopPlot2);
if (iDistance > 0)
{
iValue /= (1 + iDistance);
if (iValue > iBestValue)
{
iBestValue = iValue;
pBestPlot = pLoopPlot2;
}
}
}
}
}
}
}
if (pBestPlot != NULL)
{
if (!AI_advancedStartDoRoute(pLoopPlot, pBestPlot))
{
return;
}
}
}
}
}
if (pLoopPlot->getRouteType() == NO_ROUTE)
{
int iRouteYieldValue = 0;
RouteTypes eRoute = (AI_bestAdvancedStartRoute(pLoopPlot, &iRouteYieldValue));
if (eRoute != NO_ROUTE && iRouteYieldValue > 0)
{
doAdvancedStartAction(ADVANCEDSTARTACTION_ROUTE, pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE(), eRoute, true);
}
}
}
}
}
//Connect Cities
int iLoop;
CvCity* pLoopCity;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (!pLoopCity->isCapital() && !pLoopCity->isConnectedToCapital())
{
int iBestValue = 0;
CvPlot* pBestPlot = NULL;
int iRange = 5;
for (int iX = -iRange; iX <= iRange; iX++)
{
for (int iY = -iRange; iY <= iRange; iY++)
{
CvPlot* pLoopPlot = plotXY(pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE(), iX, iY);
if ((pLoopPlot != NULL) && (pLoopPlot->getOwner() == getID()))
{
if ((pLoopPlot->isConnectedToCapital()) || pLoopPlot->isCity())
{
int iValue = 1000;
if (pLoopPlot->isCity())
{
iValue += 500;
if (pLoopPlot->getPlotCity()->isCapital())
{
iValue += 500;
}
}
if (pLoopPlot->isRoute())
{
iValue += 100;
}
int iDistance = GC.getMapINLINE().calculatePathDistance(pLoopCity->plot(), pLoopPlot);
if (iDistance > 0)
{
iValue /= (1 + iDistance);
if (iValue > iBestValue)
{
iBestValue = iValue;
pBestPlot = pLoopPlot;
}
}
}
}
}
}
if (NULL != pBestPlot)
{
if (!AI_advancedStartDoRoute(pBestPlot, pLoopCity->plot()))
{
return;
}
}
}
}
}
void CvPlayerAI::AI_doAdvancedStart(bool bNoExit)
{
FAssertMsg(!isBarbarian(), "Should not be called for barbarians!");
if (NULL == getStartingPlot())
{
FAssert(false);
return;
}
int iLoop;
CvCity* pLoopCity;
int iStartingPoints = getAdvancedStartPoints();
int iRevealPoints = (iStartingPoints * 10) / 100;
int iMilitaryPoints = (iStartingPoints * (isHuman() ? 17 : (10 + (GC.getLeaderHeadInfo(getPersonalityType()).getBuildUnitProb() / 3)))) / 100;
int iCityPoints = iStartingPoints - (iMilitaryPoints + iRevealPoints);
if (getCapitalCity() != NULL)
{
AI_advancedStartPlaceCity(getCapitalCity()->plot());
}
else
{
for (int iPass = 0; iPass < 2 && NULL == getCapitalCity(); ++iPass)
{
CvPlot* pBestCapitalPlot = AI_advancedStartFindCapitalPlot();
if (pBestCapitalPlot != NULL)
{
if (!AI_advancedStartPlaceCity(pBestCapitalPlot))
{
FAssertMsg(false, "AS AI: Unexpected failure placing capital");
}
break;
}
else
{
//If this point is reached, the advanced start system is broken.
//Find a new starting plot for this player
setStartingPlot(findStartingPlot(false), true);
//Redo Starting visibility
CvPlot* pStartingPlot = getStartingPlot();
if (NULL != pStartingPlot)
{
for (int iPlotLoop = 0; iPlotLoop < GC.getMapINLINE().numPlots(); ++iPlotLoop)
{
CvPlot* pPlot = GC.getMapINLINE().plotByIndex(iPlotLoop);
if (plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pStartingPlot->getX_INLINE(), pStartingPlot->getY_INLINE()) <= GC.getDefineINT("ADVANCED_START_SIGHT_RANGE"))
{
pPlot->setRevealed(getTeam(), true, false, NO_TEAM, false);
}
}
}
}
}
if (getCapitalCity() == NULL)
{
if (!bNoExit)
{
doAdvancedStartAction(ADVANCEDSTARTACTION_EXIT, -1, -1, -1, true);
}
return;
}
}
iCityPoints -= (iStartingPoints - getAdvancedStartPoints());
int iLastPointsTotal = getAdvancedStartPoints();
for (int iPass = 0; iPass < 6; iPass++)
{
for (int iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
{
CvPlot* pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
if (pLoopPlot->isRevealed(getTeam(), false))
{
if (pLoopPlot->getBonusType(getTeam()) != NO_BONUS)
{
AI_advancedStartRevealRadius(pLoopPlot, CITY_PLOTS_RADIUS);
}
else
{
for (int iJ = 0; iJ < NUM_CARDINALDIRECTION_TYPES; iJ++)
{
CvPlot* pLoopPlot2 = plotCardinalDirection(pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE(), (CardinalDirectionTypes)iJ);
if ((pLoopPlot2 != NULL) && (getAdvancedStartVisibilityCost(true, pLoopPlot2) > 0))
{
//Mildly maphackery but any smart human can see the terrain type of a tile.
pLoopPlot2->getTerrainType();
int iFoodYield = GC.getTerrainInfo(pLoopPlot2->getTerrainType()).getYield(YIELD_FOOD);
if (pLoopPlot2->getFeatureType() != NO_FEATURE)
{
iFoodYield += GC.getFeatureInfo(pLoopPlot2->getFeatureType()).getYieldChange(YIELD_FOOD);
}
if (((iFoodYield >= 2) && !pLoopPlot2->isFreshWater()) || pLoopPlot2->isHills() || pLoopPlot2->isRiver())
{
doAdvancedStartAction(ADVANCEDSTARTACTION_VISIBILITY, pLoopPlot2->getX_INLINE(), pLoopPlot2->getY_INLINE(), -1, true);
}
}
}
}
}
if ((iLastPointsTotal - getAdvancedStartPoints()) > iRevealPoints)
{
break;
}
}
}
iLastPointsTotal = getAdvancedStartPoints();
iCityPoints = std::min(iCityPoints, iLastPointsTotal);
int iArea = -1; //getStartingPlot()->getArea();
//Spend econ points on a tech?
int iTechRand = 90 + GC.getGame().getSorenRandNum(20, "AI AS Buy Tech 1");
int iTotalTechSpending = 0;
if (getCurrentEra() == 0)
{
TechTypes eTech = AI_bestTech(1);
if ((eTech != NO_TECH) && !GC.getTechInfo(eTech).isRepeat())
{
int iTechCost = getAdvancedStartTechCost(eTech, true);
if (iTechCost > 0)
{
doAdvancedStartAction(ADVANCEDSTARTACTION_TECH, -1, -1, eTech, true);
iTechRand -= 50;
iTotalTechSpending += iTechCost;
}
}
}
bool bDonePlacingCities = false;
for (int iPass = 0; iPass < 100; ++iPass)
{
int iRand = iTechRand + 10 * getNumCities();
if ((iRand > 0) && (GC.getGame().getSorenRandNum(100, "AI AS Buy Tech 2") < iRand))
{
TechTypes eTech = AI_bestTech(1);
if ((eTech != NO_TECH) && !GC.getTechInfo(eTech).isRepeat())
{
int iTechCost = getAdvancedStartTechCost(eTech, true);
if ((iTechCost > 0) && ((iTechCost + iTotalTechSpending) < (iCityPoints / 4)))
{
doAdvancedStartAction(ADVANCEDSTARTACTION_TECH, -1, -1, eTech, true);
iTechRand -= 50;
iTotalTechSpending += iTechCost;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
AI_advancedStartPlaceCity(pLoopCity->plot());
}
}
}
}
int iBestFoundValue = 0;
CvPlot* pBestFoundPlot = NULL;
AI_updateFoundValues(true);
for (int iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
{
CvPlot* pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
//if (pLoopPlot->area() == getStartingPlot()->area())
{
if (plotDistance(getStartingPlot()->getX_INLINE(), getStartingPlot()->getY_INLINE(), pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE()) < 9)
{
if (pLoopPlot->getFoundValue(getID()) > iBestFoundValue)
{
if (getAdvancedStartCityCost(true, pLoopPlot) > 0)
{
pBestFoundPlot = pLoopPlot;
iBestFoundValue = pLoopPlot->getFoundValue(getID());
}
}
}
}
}
if (iBestFoundValue < ((getNumCities() == 0) ? 1 : (500 + 250 * getNumCities())))
{
bDonePlacingCities = true;
}
if (!bDonePlacingCities)
{
int iCost = getAdvancedStartCityCost(true, pBestFoundPlot);
if (iCost > getAdvancedStartPoints())
{
bDonePlacingCities = true;
}// at 500pts, we have 200, we spend 100.
else if (((iLastPointsTotal - getAdvancedStartPoints()) + iCost) > iCityPoints)
{
bDonePlacingCities = true;
}
}
if (!bDonePlacingCities)
{
if (!AI_advancedStartPlaceCity(pBestFoundPlot))
{
FAssertMsg(false, "AS AI: Failed to place city (non-capital)");
bDonePlacingCities = true;
}
}
if (bDonePlacingCities)
{
break;
}
}
bool bDoneWithTechs = false;
while (!bDoneWithTechs)
{
bDoneWithTechs = true;
TechTypes eTech = AI_bestTech(1);
if (eTech != NO_TECH && !GC.getTechInfo(eTech).isRepeat())
{
int iTechCost = getAdvancedStartTechCost(eTech, true);
if ((iTechCost > 0) && ((iTechCost + iLastPointsTotal - getAdvancedStartPoints()) <= iCityPoints))
{
doAdvancedStartAction(ADVANCEDSTARTACTION_TECH, -1, -1, eTech, true);
bDoneWithTechs = false;
}
}
}
{
//Land
AI_advancedStartPlaceExploreUnits(true);
if (getCurrentEra() > 2)
{
//Sea
AI_advancedStartPlaceExploreUnits(false);
if (GC.getGameINLINE().circumnavigationAvailable())
{
if (GC.getGameINLINE().getSorenRandNum(GC.getGameINLINE().countCivPlayersAlive(), "AI AS buy 2nd sea explorer") < 2)
{
AI_advancedStartPlaceExploreUnits(false);
}
}
}
}
AI_advancedStartRouteTerritory();
bool bDoneBuildings = (iLastPointsTotal - getAdvancedStartPoints()) > iCityPoints;
for (int iPass = 0; iPass < 10 && !bDoneBuildings; ++iPass)
{
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
BuildingTypes eBuilding = pLoopCity->AI_bestAdvancedStartBuilding(iPass);
if (eBuilding != NO_BUILDING)
{
bDoneBuildings = (iLastPointsTotal - (getAdvancedStartPoints() - getAdvancedStartBuildingCost(eBuilding, true, pLoopCity))) > iCityPoints;
if (!bDoneBuildings)
{
doAdvancedStartAction(ADVANCEDSTARTACTION_BUILDING, pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE(), eBuilding, true);
}
else
{
//continue there might be cheaper buildings in other cities we can afford
}
}
}
}
//Units
std::vector<UnitAITypes> aeUnitAITypes;
aeUnitAITypes.push_back(UNITAI_CITY_DEFENSE);
aeUnitAITypes.push_back(UNITAI_WORKER);
aeUnitAITypes.push_back(UNITAI_RESERVE);
aeUnitAITypes.push_back(UNITAI_COUNTER);
bool bDone = false;
for (int iPass = 0; iPass < 10; ++iPass)
{
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if ((iPass == 0) || (pLoopCity->getArea() == getStartingPlot()->getArea()))
{
CvPlot* pUnitPlot = pLoopCity->plot();
//Token defender
UnitTypes eBestUnit = AI_bestAdvancedStartUnitAI(pUnitPlot, aeUnitAITypes[iPass % aeUnitAITypes.size()]);
if (eBestUnit != NO_UNIT)
{
if (getAdvancedStartUnitCost(eBestUnit, true, pUnitPlot) > getAdvancedStartPoints())
{
bDone = true;
break;
}
doAdvancedStartAction(ADVANCEDSTARTACTION_UNIT, pUnitPlot->getX(), pUnitPlot->getY(), eBestUnit, true);
}
}
}
}
if (isHuman())
{
// remove unhappy population
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
while (pLoopCity->angryPopulation() > 0 && getAdvancedStartPopCost(false, pLoopCity) > 0)
{
doAdvancedStartAction(ADVANCEDSTARTACTION_POP, pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE(), -1, false);
}
}
}
if (!bNoExit)
{
doAdvancedStartAction(ADVANCEDSTARTACTION_EXIT, -1, -1, -1, true);
}
}
void CvPlayerAI::AI_recalculateFoundValues(int iX, int iY, int iInnerRadius, int iOuterRadius) const
{
CvPlot* pLoopPlot;
int iLoopX, iLoopY;
int iValue;
for (iLoopX = -iOuterRadius; iLoopX <= iOuterRadius; iLoopX++)
{
for (iLoopY = -iOuterRadius; iLoopY <= iOuterRadius; iLoopY++)
{
pLoopPlot = plotXY(iX, iY, iLoopX, iLoopY);
if ((NULL != pLoopPlot) && !AI_isPlotCitySite(pLoopPlot))
{
if (stepDistance(0, 0, iLoopX, iLoopY) <= iInnerRadius)
{
if (!((iLoopX == 0) && (iLoopY == 0)))
{
pLoopPlot->setFoundValue(getID(), 0);
}
}
else
{
if ((pLoopPlot != NULL) && (pLoopPlot->isRevealed(getTeam(), false)))
{
long lResult=-1;
if(GC.getUSE_GET_CITY_FOUND_VALUE_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyArgsList argsList;
argsList.add((int)getID());
argsList.add(pLoopPlot->getX());
argsList.add(pLoopPlot->getY());
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "getCityFoundValue", argsList.makeFunctionArgs(), &lResult);
}
if (lResult == -1)
{
iValue = AI_foundValue(pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE());
}
else
{
iValue = lResult;
}
pLoopPlot->setFoundValue(getID(), iValue);
if (iValue > pLoopPlot->area()->getBestFoundValue(getID()))
{
pLoopPlot->area()->setBestFoundValue(getID(), iValue);
}
}
}
}
}
}
}
int CvPlayerAI::AI_getMinFoundValue() const
{
int iValue = 600;
int iNetCommerce = 1 + getCommerceRate(COMMERCE_GOLD) + getCommerceRate(COMMERCE_RESEARCH) + std::max(0, getGoldPerTurn());
/************************************************************************************************/
/* UNOFFICIAL_PATCH 06/11/09 jdog5000 & DanF5771 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original BTS code
int iNetExpenses = calculateInflatedCosts() + std::min(0, getGoldPerTurn());
*/
int iNetExpenses = calculateInflatedCosts() + std::max(0, -getGoldPerTurn());
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
iValue *= iNetCommerce;
iValue /= std::max(std::max(1, iNetCommerce / 4), iNetCommerce - iNetExpenses);
if (GET_TEAM(getTeam()).getAnyWarPlanCount(1) > 0)
{
iValue *= 2;
}
return iValue;
}
int CvPlayerAI::AI_bestCityValue(CvPlot* pPlot, int iFoundValue) const
{
return iFoundValue * std::min(NUM_CITY_PLOTS * 2, pPlot->area()->getNumUnownedTiles());
}
void CvPlayerAI::AI_updateCitySites(int iMinFoundValueThreshold, int iMaxSites) const
{
std::vector<int>::iterator it;
int iValue;
int iI;
int iPass = 0;
while (iPass < iMaxSites)
{
//Add a city to the list.
int iBestFoundValue = 0;
CvPlot* pBestFoundPlot = NULL;
for (iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
{
CvPlot* pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
if (pLoopPlot->isRevealed(getTeam(), false))
{
iValue = pLoopPlot->getFoundValue(getID());
if (iValue > iMinFoundValueThreshold)
{
if (!AI_isPlotCitySite(pLoopPlot))
{
iValue = AI_bestCityValue(pLoopPlot, iValue);
if (iValue > iBestFoundValue)
{
iBestFoundValue = iValue;
pBestFoundPlot = pLoopPlot;
}
}
}
}
}
if (pBestFoundPlot != NULL)
{
MEMORY_TRACK_EXEMPT();
m_aiAICitySites.push_back(GC.getMapINLINE().plotNum(pBestFoundPlot->getX_INLINE(), pBestFoundPlot->getY_INLINE()));
AI_recalculateFoundValues(pBestFoundPlot->getX_INLINE(), pBestFoundPlot->getY_INLINE(), CITY_PLOTS_RADIUS, 2 * CITY_PLOTS_RADIUS);
}
else
{
break;
}
iPass++;
}
}
void CvPlayerAI::calculateCitySites(void) const
{
static bool isCalulatingCitySites = false;
// Re-entrancy protection
if ( !isCalulatingCitySites )
{
isCalulatingCitySites = true;
AI_updateFoundValues(false, NULL);
m_bCitySitesNotCalculated = false;
m_aiAICitySites.clear();
AI_updateCitySites(AI_getMinFoundValue(), 4);
isCalulatingCitySites = false;
}
}
int CvPlayerAI::AI_getNumCitySites() const
{
if ( m_bCitySitesNotCalculated )
{
calculateCitySites();
}
return m_aiAICitySites.size();
}
bool CvPlayerAI::AI_isPlotCitySite(CvPlot* pPlot) const
{
std::vector<int>::iterator it;
int iPlotIndex = GC.getMapINLINE().plotNumINLINE(pPlot->getX_INLINE(), pPlot->getY_INLINE());
if ( m_bCitySitesNotCalculated )
{
calculateCitySites();
}
for (it = m_aiAICitySites.begin(); it != m_aiAICitySites.end(); ++it)
{
if ((*it) == iPlotIndex)
{
return true;
}
}
return false;
}
int CvPlayerAI::AI_getNumAreaCitySites(int iAreaID, int& iBestValue) const
{
std::vector<int>::iterator it;
int iCount = 0;
iBestValue = 0;
if ( m_bCitySitesNotCalculated )
{
calculateCitySites();
}
for (it = m_aiAICitySites.begin(); it != m_aiAICitySites.end(); ++it)
{
CvPlot* pCitySitePlot = GC.getMapINLINE().plotByIndex((*it));
if (pCitySitePlot->getArea() == iAreaID)
{
iCount++;
iBestValue = std::max(iBestValue, pCitySitePlot->getFoundValue(getID()));
}
}
return iCount;
}
int CvPlayerAI::AI_getNumAdjacentAreaCitySites(int iWaterAreaID, int iExcludeArea, int& iBestValue) const
{
std::vector<int>::iterator it;
int iCount = 0;
iBestValue = 0;
if ( m_bCitySitesNotCalculated )
{
calculateCitySites();
}
for (it = m_aiAICitySites.begin(); it != m_aiAICitySites.end(); ++it)
{
CvPlot* pCitySitePlot = GC.getMapINLINE().plotByIndex((*it));
if (pCitySitePlot->getArea() != iExcludeArea)
{
if (pCitySitePlot->isAdjacentToArea(iWaterAreaID))
{
iCount++;
iBestValue = std::max(iBestValue, pCitySitePlot->getFoundValue(getID()));
}
}
}
return iCount;
}
CvPlot* CvPlayerAI::AI_getCitySite(int iIndex) const
{
FAssert(iIndex < (int)m_aiAICitySites.size());
return GC.getMapINLINE().plotByIndex(m_aiAICitySites[iIndex]);
}
int CvPlayerAI::AI_bestAreaUnitAIValue(UnitAITypes eUnitAI, CvArea* pArea, UnitTypes* peBestUnitType) const
{
CvCity* pCity = NULL;
if (pArea != NULL)
{
if (getCapitalCity() != NULL)
{
if (pArea->isWater())
{
if (getCapitalCity()->plot()->isAdjacentToArea(pArea))
{
pCity = getCapitalCity();
}
}
else
{
if (getCapitalCity()->getArea() == pArea->getID())
{
pCity = getCapitalCity();
}
}
}
if (NULL == pCity)
{
CvCity* pLoopCity;
int iLoop;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (pArea->isWater())
{
if (pLoopCity->plot()->isAdjacentToArea(pArea))
{
pCity = pLoopCity;
break;
}
}
else
{
if (pLoopCity->getArea() == pArea->getID())
{
pCity = pLoopCity;
break;
}
}
}
}
}
return AI_bestCityUnitAIValue(eUnitAI, pCity, peBestUnitType);
}
int CvPlayerAI::AI_bestCityUnitAIValue(UnitAITypes eUnitAI, CvCity* pCity, UnitTypes* peBestUnitType) const
{
PROFILE_FUNC();
UnitTypes eLoopUnit;
int iValue;
int iBestValue;
int iI;
FAssertMsg(eUnitAI != NO_UNITAI, "UnitAI is not assigned a valid value");
iBestValue = 0;
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
eLoopUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI)));
if (eLoopUnit != NO_UNIT)
{
if (!isHuman() || (GC.getUnitInfo(eLoopUnit).getDefaultUnitAIType() == eUnitAI))
{
if (NULL == pCity ? canTrain(eLoopUnit) : pCity->canTrain(eLoopUnit))
{
iValue = AI_unitValue(eLoopUnit, eUnitAI, (pCity == NULL) ? NULL : pCity->area());
if (iValue > iBestValue)
{
iBestValue = iValue;
if (peBestUnitType != NULL)
{
*peBestUnitType = eLoopUnit;
}
}
}
}
}
}
return iBestValue;
}
int CvPlayerAI::AI_calculateTotalBombard(DomainTypes eDomain) const
{
int iI;
int iTotalBombard = 0;
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
UnitTypes eLoopUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI)));
if (eLoopUnit != NO_UNIT)
{
if (GC.getUnitInfo(eLoopUnit).getDomainType() == eDomain)
{
int iBombardRate = GC.getUnitInfo(eLoopUnit).getBombardRate();
if (iBombardRate > 0)
{
if( GC.getUnitInfo(eLoopUnit).isIgnoreBuildingDefense() )
{
iBombardRate *= 3;
iBombardRate /= 2;
}
iTotalBombard += iBombardRate * getUnitClassCount((UnitClassTypes)iI);
}
int iBombRate = GC.getUnitInfo(eLoopUnit).getBombRate();
if (iBombRate > 0)
{
iTotalBombard += iBombRate * getUnitClassCount((UnitClassTypes)iI);
}
}
}
}
return iTotalBombard;
}
void CvPlayerAI::AI_updateBonusValue(BonusTypes eBonus)
{
FAssert(m_aiBonusValue != NULL);
//reset
m_aiBonusValue[eBonus] = -1;
m_abNonTradeBonusCalculated[eBonus] = false;
}
void CvPlayerAI::AI_updateBonusValue()
{
PROFILE_FUNC();
for (int iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
AI_updateBonusValue((BonusTypes)iI);
}
}
int CvPlayerAI::AI_getUnitClassWeight(UnitClassTypes eUnitClass) const
{
return m_aiUnitClassWeights[eUnitClass] / 100;
}
int CvPlayerAI::AI_getUnitCombatWeight(UnitCombatTypes eUnitCombat) const
{
return m_aiUnitCombatWeights[eUnitCombat] / 100;
}
void CvPlayerAI::AI_doEnemyUnitData()
{
PROFILE_FUNC();
std::vector<int> aiUnitCounts(GC.getNumUnitInfos(), 0);
std::vector<int> aiDomainSums(NUM_DOMAIN_TYPES, 0);
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
int iI;
int iOldTotal = 0;
int iNewTotal = 0;
// Count enemy land and sea units visible to us
for (iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
{
CvPlot* pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
int iAdjacentAttackers = -1;
if (pLoopPlot->isVisible(getTeam(), false))
{
pUnitNode = pLoopPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->canFight())
{
int iUnitValue = 1;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 03/18/10 jdog5000 */
/* */
/* War Strategy AI */
/************************************************************************************************/
//if (atWar(getTeam(), pLoopUnit->getTeam()))
if( GET_TEAM(getTeam()).AI_getWarPlan(pLoopUnit->getTeam()) != NO_WARPLAN )
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
{
iUnitValue += 10;
if ((pLoopPlot->getOwnerINLINE() == getID()))
{
iUnitValue += 15;
}
else if (atWar(getTeam(), pLoopPlot->getTeam()))
{
if (iAdjacentAttackers == -1)
{
iAdjacentAttackers = GET_PLAYER(pLoopPlot->getOwnerINLINE()).AI_adjacentPotentialAttackers(pLoopPlot);
}
if (iAdjacentAttackers > 0)
{
iUnitValue += 15;
}
}
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 07/16/10 Maniac */
/* */
/* Bugfix */
/************************************************************************************************/
/* original bts code
else if (pLoopUnit->getOwnerINLINE() != getID())
*/
else if (pLoopUnit->getTeam() != getTeam())
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
{
iUnitValue += pLoopUnit->canAttack() ? 4 : 1;
if (pLoopPlot->getCulture(getID()) > 0)
{
iUnitValue += pLoopUnit->canAttack() ? 4 : 1;
}
}
// If we hadn't seen any of this class before
if (m_aiUnitClassWeights[pLoopUnit->getUnitClassType()] == 0)
{
iUnitValue *= 4;
}
iUnitValue *= pLoopUnit->baseCombatStr();
aiUnitCounts[pLoopUnit->getUnitType()] += iUnitValue;
aiDomainSums[pLoopUnit->getDomainType()] += iUnitValue;
iNewTotal += iUnitValue;
}
}
}
}
if (iNewTotal == 0)
{
//This should rarely happen.
return;
}
//Decay
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
m_aiUnitClassWeights[iI] -= 100;
m_aiUnitClassWeights[iI] *= 3;
m_aiUnitClassWeights[iI] /= 4;
m_aiUnitClassWeights[iI] = std::max(0, m_aiUnitClassWeights[iI]);
}
for (iI = 0; iI < GC.getNumUnitInfos(); iI++)
{
if (aiUnitCounts[iI] > 0)
{
UnitTypes eLoopUnit = (UnitTypes)iI;
TechTypes eTech = (TechTypes)GC.getUnitInfo((UnitTypes)iI).getPrereqAndTech();
int iEraDiff = (eTech == NO_TECH) ? 4 : std::min(4, getCurrentEra() - GC.getTechInfo(eTech).getEra());
if (iEraDiff > 1)
{
iEraDiff -= 1;
aiUnitCounts[iI] *= 3 - iEraDiff;
aiUnitCounts[iI] /= 3;
}
FAssert(aiDomainSums[GC.getUnitInfo(eLoopUnit).getDomainType()] > 0);
m_aiUnitClassWeights[GC.getUnitInfo(eLoopUnit).getUnitClassType()] += (5000 * aiUnitCounts[iI]) / std::max(1, aiDomainSums[GC.getUnitInfo(eLoopUnit).getDomainType()]);
}
}
for (iI = 0; iI < GC.getNumUnitCombatInfos(); ++iI)
{
m_aiUnitCombatWeights[iI] = 0;
}
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
if (m_aiUnitClassWeights[iI] > 0)
{
UnitTypes eUnit = (UnitTypes)GC.getUnitClassInfo((UnitClassTypes)iI).getDefaultUnitIndex();
/************************************************************************************************/
/* Afforess Start 01/17/10 */
/* */
/* TakBal Crash Fix when NO_UNITCOMBAT has a defense value */
/************************************************************************************************/
/* original code
m_aiUnitCombatWeights[GC.getUnitInfo(eUnit).getUnitCombatType()] += m_aiUnitClassWeights[iI];
*/
int ctype = GC.getUnitInfo(eUnit).getUnitCombatType();
if (ctype >= 0 && ctype < GC.getNumUnitCombatInfos())
{
m_aiUnitCombatWeights[ctype] += m_aiUnitClassWeights[iI];
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
}
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH 07/16/10 Maniac */
/* */
/* Bugfix */
/************************************************************************************************/
/* original bts code
if (m_aiUnitCombatWeights[iI] > 25)
*/
if (m_aiUnitCombatWeights[iI] > 2500)
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
{
m_aiUnitCombatWeights[iI] += 2500;
}
else if (m_aiUnitCombatWeights[iI] > 0)
{
m_aiUnitCombatWeights[iI] += 1000;
}
}
}
int CvPlayerAI::AI_calculateUnitAIViability(UnitAITypes eUnitAI, DomainTypes eDomain) const
{
int iBestUnitAIStrength = 0;
int iBestOtherStrength = 0;
for (int iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
UnitTypes eLoopUnit = (UnitTypes)GC.getUnitClassInfo((UnitClassTypes)iI).getDefaultUnitIndex();
/************************************************************************************************/
/* UNOFFICIAL_PATCH 01/15/09 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original BTS code
CvUnitInfo& kUnitInfo = GC.getUnitInfo((UnitTypes)iI);
*/
//Afforess CTD Fix:
if (eLoopUnit == NO_UNIT)
continue;
CvUnitInfo& kUnitInfo = GC.getUnitInfo(eLoopUnit);
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
if (kUnitInfo.getDomainType() == eDomain)
{
TechTypes eTech = (TechTypes)kUnitInfo.getPrereqAndTech();
if ((m_aiUnitClassWeights[iI] > 0) || GET_TEAM(getTeam()).isHasTech((eTech)))
{
if (kUnitInfo.getUnitAIType(eUnitAI))
{
iBestUnitAIStrength = std::max(iBestUnitAIStrength, kUnitInfo.getCombat());
}
iBestOtherStrength = std::max(iBestOtherStrength, kUnitInfo.getCombat());
}
}
}
return (100 * iBestUnitAIStrength) / std::max(1, iBestOtherStrength);
}
ReligionTypes CvPlayerAI::AI_chooseReligion()
{
ReligionTypes eFavorite = (ReligionTypes)GC.getLeaderHeadInfo(getLeaderType()).getFavoriteReligion();
if (NO_RELIGION != eFavorite && !GC.getGameINLINE().isReligionFounded(eFavorite))
{
return eFavorite;
}
std::vector<ReligionTypes> aeReligions;
for (int iReligion = 0; iReligion < GC.getNumReligionInfos(); ++iReligion)
{
if (!GC.getGameINLINE().isReligionFounded((ReligionTypes)iReligion))
{
aeReligions.push_back((ReligionTypes)iReligion);
}
}
if (!aeReligions.empty())
{
return aeReligions[GC.getGameINLINE().getSorenRandNum(aeReligions.size(), "AI pick religion")];
}
return NO_RELIGION;
}
int CvPlayerAI::AI_getAttitudeWeight(PlayerTypes ePlayer) const
{
int iAttitudeWeight = 0;
switch (AI_getAttitude(ePlayer))
{
case ATTITUDE_FURIOUS:
iAttitudeWeight = -100;
break;
case ATTITUDE_ANNOYED:
iAttitudeWeight = -50;
break;
case ATTITUDE_CAUTIOUS:
iAttitudeWeight = 0;
break;
case ATTITUDE_PLEASED:
iAttitudeWeight = 50;
break;
case ATTITUDE_FRIENDLY:
iAttitudeWeight = 100;
break;
}
return iAttitudeWeight;
}
int CvPlayerAI::AI_getPlotAirbaseValue(CvPlot* pPlot) const
{
PROFILE_FUNC();
FAssert(pPlot != NULL);
/************************************************************************************************/
/* Afforess Start 02/15/10 */
/* */
/* */
/************************************************************************************************/
if (pPlot->isOwned() && (pPlot->getTeam() != getTeam()))
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
return 0;
}
if (pPlot->isCityRadius())
{
CvCity* pWorkingCity = pPlot->getWorkingCity();
if (pWorkingCity != NULL)
{
if (pWorkingCity->AI_getBestBuild(pWorkingCity->getCityPlotIndex(pPlot)) != NO_BUILD)
{
return 0;
}
if (pPlot->getImprovementType() != NO_IMPROVEMENT)
{
CvImprovementInfo &kImprovementInfo = GC.getImprovementInfo(pPlot->getImprovementType());
if (!kImprovementInfo.isActsAsCity())
{
return 0;
}
}
}
}
int iMinOtherCityDistance = MAX_INT;
CvPlot* iMinOtherCityPlot = NULL;
int iMinFriendlyCityDistance = MAX_INT;
CvPlot* iMinFriendlyCityPlot = NULL;
int iOtherCityCount = 0;
int iRange = 4;
for (int iX = -iRange; iX <= iRange; iX++)
{
for (int iY = -iRange; iY <= iRange; iY++)
{
CvPlot* pLoopPlot = plotXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iX, iY);
if ((pLoopPlot != NULL) && (pPlot != pLoopPlot))
{
int iDistance = plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE());
if (pLoopPlot->getTeam() == getTeam())
{
if (pLoopPlot->isCity(true))
{
if (1 == iDistance)
{
return 0;
}
if (iDistance < iMinFriendlyCityDistance)
{
iMinFriendlyCityDistance = iDistance;
iMinFriendlyCityPlot = pLoopPlot;
}
}
}
else
{
if (pLoopPlot->isOwned())
{
if (pLoopPlot->isCity(false))
{
if (iDistance < iMinOtherCityDistance)
{
iMinOtherCityDistance = iDistance;
iMinOtherCityPlot = pLoopPlot;
iOtherCityCount++;
}
}
}
}
}
}
}
if (0 == iOtherCityCount)
{
return 0;
}
// if (iMinFriendlyCityPlot != NULL)
// {
// FAssert(iMinOtherCityPlot != NULL);
// if (plotDistance(iMinFriendlyCityPlot->getX_INLINE(), iMinFriendlyCityPlot->getY_INLINE(), iMinOtherCityPlot->getX_INLINE(), iMinOtherCityPlot->getY_INLINE()) < (iMinOtherCityDistance - 1))
// {
// return 0;
// }
// }
// if (iMinOtherCityPlot != NULL)
// {
// CvCity* pNearestCity = GC.getMapINLINE().findCity(iMinOtherCityPlot->getX_INLINE(), iMinOtherCityPlot->getY_INLINE(), NO_PLAYER, getTeam(), false);
// if (NULL == pNearestCity)
// {
// return 0;
// }
// if (plotDistance(pNearestCity->getX_INLINE(), pNearestCity->getY_INLINE(), iMinOtherCityPlot->getX_INLINE(), iMinOtherCityPlot->getY_INLINE()) < iRange)
// {
// return 0;
// }
// }
int iDefenseModifier = pPlot->defenseModifier(getTeam(), false);
// if (iDefenseModifier <= 0)
// {
// return 0;
// }
int iValue = iOtherCityCount * 50;
iValue *= 100 + (2 * (iDefenseModifier + (pPlot->isHills() ? 25 : 0)));
iValue /= 100;
return iValue;
}
int CvPlayerAI::AI_getPlotCanalValue(CvPlot* pPlot) const
{
PROFILE_FUNC();
FAssert(pPlot != NULL);
if (pPlot->isOwned())
{
if (pPlot->getTeam() != getTeam())
{
return 0;
}
if (pPlot->isCityRadius())
{
CvCity* pWorkingCity = pPlot->getWorkingCity();
if (pWorkingCity != NULL)
{
if (pWorkingCity->AI_getBestBuild(pWorkingCity->getCityPlotIndex(pPlot)) != NO_BUILD)
{
return 0;
}
if (pPlot->getImprovementType() != NO_IMPROVEMENT)
{
CvImprovementInfo &kImprovementInfo = GC.getImprovementInfo(pPlot->getImprovementType());
if (!kImprovementInfo.isActsAsCity())
{
return 0;
}
}
}
}
}
for (int iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
CvPlot* pLoopPlot = plotDirection(pPlot->getX_INLINE(), pPlot->getY_INLINE(), (DirectionTypes)iI);
if (pLoopPlot != NULL)
{
if (pLoopPlot->isCity(true))
{
return 0;
}
}
}
CvArea* pSecondWaterArea = pPlot->secondWaterArea();
if (pSecondWaterArea == NULL)
{
return 0;
}
return 10 * std::min(0, pSecondWaterArea->getNumTiles() - 2);
}
/********************************************************************************/
/* New Civic AI 05.08.2010 Fuyu */
/********************************************************************************/
bool CvPlayerAI::AI_isCivicCanChangeOtherValues(CivicTypes eCivicSelected, ReligionTypes eAssumedReligion)
{
if (eCivicSelected == NO_CIVIC)
{
return false;
}
CvCivicInfo& kCivicSelected = GC.getCivicInfo(eCivicSelected);
//happiness
if ( (kCivicSelected.getCivicPercentAnger() != 0 && getCivicPercentAnger(eCivicSelected, true) != 0)
|| kCivicSelected.getHappyPerMilitaryUnit() != 0 || kCivicSelected.getLargestCityHappiness() != 0
|| (kCivicSelected.getWarWearinessModifier() != 0 && getWarWearinessPercentAnger() != 0)
|| kCivicSelected.isAnyBuildingHappinessChange() || kCivicSelected.isAnyFeatureHappinessChange()
|| kCivicSelected.getNonStateReligionHappiness() != 0
|| (kCivicSelected.getStateReligionHappiness() != 0 && (kCivicSelected.isStateReligion() || eAssumedReligion != NO_RELIGION)) )
{
return true;
}
//health
if ( kCivicSelected.getExtraHealth() != 0 || kCivicSelected.isNoUnhealthyPopulation() || kCivicSelected.isBuildingOnlyHealthy() || kCivicSelected.isAnyBuildingHealthChange() )
{
return true;
}
//trade
if (kCivicSelected.isNoForeignTrade())
{
return true;
}
//corporation
if (kCivicSelected.isNoCorporations() || kCivicSelected.isNoForeignCorporations() || kCivicSelected.getCorporationMaintenanceModifier() != 0)
{
return true;
}
//religion
if (kCivicSelected.isStateReligion())
{
return true;
}
if (kCivicSelected.isNoNonStateReligionSpread())
{
return true;
}
//other
if (kCivicSelected.isMilitaryFoodProduction())
{
return true;
}
int iI;
for (iI = 0; iI < GC.getNumHurryInfos(); iI++)
{
if (kCivicSelected.isHurry(iI))
{
return true;
}
}
for (iI = 0; iI < GC.getNumSpecialBuildingInfos(); iI++)
{
if (kCivicSelected.isSpecialBuildingNotRequired(iI))
{
return true;
}
}
return false;
}
bool CvPlayerAI::AI_isCivicValueRecalculationRequired(CivicTypes eCivic, CivicTypes eCivicSelected, ReligionTypes eAssumedReligion)
{
if (eCivicSelected == NO_CIVIC || eCivic == NO_CIVIC)
{
return false;
}
CvCivicInfo& kCivic = GC.getCivicInfo(eCivic);
CvCivicInfo& kCivicSelected = GC.getCivicInfo(eCivicSelected);
//happiness
if ( (kCivic.getCivicPercentAnger() != 0 && getCivicPercentAnger(eCivic, true) != 0)
|| kCivic.getHappyPerMilitaryUnit() != 0 || kCivic.getLargestCityHappiness() != 0
|| (kCivic.getWarWearinessModifier() != 0 && getWarWearinessPercentAnger() != 0)
|| kCivic.isAnyBuildingHappinessChange() || kCivic.isAnyFeatureHappinessChange()
|| kCivic.getNonStateReligionHappiness() != 0
|| (kCivic.getStateReligionHappiness() != 0 && (kCivic.isStateReligion() || eAssumedReligion != NO_RELIGION)) )
{
if ( (kCivicSelected.getCivicPercentAnger() != 0 && getCivicPercentAnger(eCivicSelected, true) != 0)
|| kCivicSelected.getHappyPerMilitaryUnit() != 0 || kCivicSelected.getLargestCityHappiness() != 0
|| (kCivicSelected.getWarWearinessModifier() != 0 && getWarWearinessPercentAnger() != 0)
|| kCivicSelected.isAnyBuildingHappinessChange() || kCivicSelected.isAnyFeatureHappinessChange()
|| kCivicSelected.getNonStateReligionHappiness() != 0
|| (kCivicSelected.getStateReligionHappiness() != 0 && (kCivicSelected.isStateReligion() || eAssumedReligion != NO_RELIGION)) )
{
return true;
}
}
//health
if ( kCivic.getExtraHealth() != 0 || kCivic.isNoUnhealthyPopulation() || kCivic.isBuildingOnlyHealthy() || kCivic.isAnyBuildingHealthChange() )
{
if ( kCivicSelected.getExtraHealth() != 0 || kCivicSelected.isNoUnhealthyPopulation() || kCivicSelected.isBuildingOnlyHealthy() || kCivicSelected.isAnyBuildingHealthChange() )
{
return true;
}
}
//trade
if (kCivic.isNoForeignTrade())
{
if (kCivicSelected.isNoForeignTrade())
{
return true;
}
}
//corporation
if (kCivic.isNoCorporations() || kCivic.isNoForeignCorporations() || kCivic.getCorporationMaintenanceModifier() != 0)
{
if (kCivicSelected.isNoCorporations() || kCivicSelected.isNoForeignCorporations() || kCivicSelected.getCorporationMaintenanceModifier() != 0)
{
return true;
}
}
//religion
if (kCivic.isStateReligion())
{
if (kCivicSelected.isStateReligion())
{
return true;
}
if (kCivic.isNoNonStateReligionSpread() && kCivicSelected.isNoNonStateReligionSpread())
{
return true;
}
}
else
{
if (kCivicSelected.isStateReligion())
{
if (kCivic.isNoNonStateReligionSpread())
{
return true;
}
if (getStateReligionCount() > 1)
{
if ( (kCivic.getStateReligionHappiness() != 0 && kCivic.getStateReligionHappiness() != kCivic.getNonStateReligionHappiness())
|| kCivic.getStateReligionGreatPeopleRateModifier() != 0 || kCivic.getStateReligionUnitProductionModifier() != 0
|| kCivic.getStateReligionBuildingProductionModifier() != 0 || kCivic.getStateReligionFreeExperience() != 0 )
{
return true;
}
}
}
}
//other kCivicSelected
if (kCivic.isMilitaryFoodProduction() && kCivicSelected.isMilitaryFoodProduction())
{
return true;
}
int iI;
for (iI = 0; iI < GC.getNumHurryInfos(); iI++)
{
if (kCivic.isHurry(iI) && kCivicSelected.isHurry(iI))
{
return true;
}
}
for (iI = 0; iI < GC.getNumSpecialBuildingInfos(); iI++)
{
if (kCivic.isSpecialBuildingNotRequired(iI) && kCivicSelected.isSpecialBuildingNotRequired(iI))
{
return true;
}
}
return false;
}
/********************************************************************************/
/* New Civic AI END */
/********************************************************************************/
//This returns a positive number equal approximately to the sum
//of the percentage values of each unit (there is no need to scale the output by iHappy)
//100 * iHappy means a high value.
int CvPlayerAI::AI_getHappinessWeight(int iHappy, int iExtraPop) const
{
int iWorstHappy = 0;
int iBestHappy = 0;
int iTotalUnhappy = 0;
int iTotalHappy = 0;
int iLoop;
CvCity* pLoopCity;
int iCount = 0;
if (0 == iHappy)
{
iHappy = 1;
}
int iValue = 0;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
int iCityHappy = pLoopCity->happyLevel() - pLoopCity->unhappyLevel(iExtraPop);
iCityHappy -= std::max(0, pLoopCity->getCommerceHappiness());
//Fuyu: max happy 5
int iHappyNow = std::min(5, iCityHappy);
int iHappyThen = std::min(5, iCityHappy + iHappy);
//Integration
int iTempValue = (((100 * iHappyThen - 10 * iHappyThen * iHappyThen)) - (100 * iHappyNow - 10 * iHappyNow * iHappyNow));
/************************************************************************************************/
/* Afforess Start 07/22/10 */
/* */
/* */
/************************************************************************************************/
if (pLoopCity->isCapital() && getNoCapitalUnhappiness())
{
iTempValue /= 3;
}
if (!GC.getGameINLINE().isOption(GAMEOPTION_NO_REVOLUTION))
{
iTempValue *= 3;
iTempValue /= 2;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (iHappy > 0)
{
//Fuyu weighting
//iValue += std::max(0, iTempValue);
iValue += std::max(0, iTempValue) * (pLoopCity->getPopulation() + iExtraPop + 2);
}
else
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH 10/21/09 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* orginal bts code
iValue += std::max(0, -iTempValue);
*/
// Negative happy changes should produce a negative value, not the same value as positive
//Fuyu weighting
//iValue += std::min(0, iTempValue);
iValue += std::min(0, iTempValue) * (pLoopCity->getPopulation() + iExtraPop + 2);
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
}
//Fuyu weighting
//iCount++
iCount += (pLoopCity->getPopulation() + iExtraPop + 2);
/*
if (iCount > 6)
{
break;
}
*/
}
return (0 == iCount) ? 50 * iHappy : iValue / iCount;
}
int CvPlayerAI::AI_getHealthWeight(int iHealth, int iExtraPop) const
{
int iWorstHealth = 0;
int iBestHealth = 0;
int iTotalUnhappy = 0;
int iTotalHealth = 0;
int iLoop;
CvCity* pLoopCity;
int iCount = 0;
if (0 == iHealth)
{
iHealth = 1;
}
int iValue = 0;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
int iCityHealth = pLoopCity->goodHealth() - pLoopCity->badHealth(false, iExtraPop);
//Fuyu: max health 8
int iHealthNow = std::min(8, iCityHealth);
int iHealthThen = std::min(8, iCityHealth + iHealth);
//Integration
int iTempValue = (((100 * iHealthThen - 6 * iHealthThen * iHealthThen)) - (100 * iHealthNow - 6 * iHealthNow * iHealthNow));
if (iHealth > 0)
{
//Fuyu weighting
//iValue += std::max(0, iTempValue);
iValue += std::max(0, iTempValue) * (pLoopCity->getPopulation() + iExtraPop + 2);
}
else
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH 10/21/09 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* orginal bts code
iValue += std::max(0, -iTempValue);
*/
// Negative health changes should produce a negative value, not the same value as positive
//Fuyu weighting
//iValue += std::min(0, iTempValue);
iValue += std::min(0, iTempValue) * (pLoopCity->getPopulation() + iExtraPop + 2);
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
}
//Fuyu weighting
//iCount++;
iCount += (pLoopCity->getPopulation() + iExtraPop + 2);
/*
if (iCount > 6)
{
break;
}
*/
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 10/21/09 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* orginal bts code
return (0 == iCount) ? 50 : iValue / iCount;
*/
// Mirror happiness valuation code
return (0 == iCount) ? 50*iHealth : iValue / iCount;
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
}
void CvPlayerAI::AI_invalidateCloseBordersAttitudeCache()
{
for (int i = 0; i < MAX_PLAYERS; ++i)
{
m_aiCloseBordersAttitudeCache[i] = MAX_INT;
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 09/03/09 poyuzhe & jdog5000 */
/* */
/* Efficiency */
/************************************************************************************************/
// From Sanguo Mod Performance, ie the CAR Mod
// Attitude cache
AI_invalidateAttitudeCache((PlayerTypes)i);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
}
bool CvPlayerAI::AI_isPlotThreatened(CvPlot* pPlot, int iRange, bool bTestMoves) const
{
PROFILE_FUNC();
CvArea *pPlotArea = pPlot->area();
if (iRange == -1)
{
iRange = DANGER_RANGE;
}
for (int iDX = -iRange; iDX <= iRange; iDX++)
{
for (int iDY = -iRange; iDY <= iRange; iDY++)
{
CvPlot* pLoopPlot = plotXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->area() == pPlotArea)
{
CvSelectionGroup* pLoopGroup = NULL;
do
{
CvSelectionGroup* pNextGroup = NULL;
for (CLLNode<IDInfo>* pUnitNode = pLoopPlot->headUnitNode(); pUnitNode != NULL; pUnitNode = pLoopPlot->nextUnitNode(pUnitNode))
{
CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
if (pLoopUnit->isEnemy(getTeam()) && pLoopUnit->canAttack() && !pLoopUnit->isInvisible(getTeam(), false))
{
if ( pLoopGroup == NULL ||
pLoopUnit->getOwnerINLINE() > pLoopGroup->getOwnerINLINE() ||
(pLoopUnit->getOwnerINLINE() == pLoopGroup->getOwnerINLINE() && pLoopUnit->getGroupID() > pLoopGroup->getID()) )
{
if ( pNextGroup == NULL ||
pLoopUnit->getOwnerINLINE() < pNextGroup->getOwnerINLINE() ||
(pLoopUnit->getOwnerINLINE() == pNextGroup->getOwnerINLINE() && pLoopUnit->getGroupID() < pNextGroup->getID()) )
{
pNextGroup = pLoopUnit->getGroup();
}
}
}
}
pLoopGroup = pNextGroup;
if ( pLoopGroup != NULL )
{
if (pLoopGroup->canMoveOrAttackInto(pPlot))
{
int iPathTurns = 0;
if (bTestMoves)
{
iPathTurns = pLoopGroup->canPathDirectlyTo(pLoopPlot, pPlot) ? 1 : MAX_INT;
//if (!pLoopUnit->getGroup()->generatePath(pLoopPlot, pPlot, MOVE_MAX_MOVES | MOVE_IGNORE_DANGER, false, &iPathTurns))
//{
// iPathTurns = MAX_INT;
//}
}
if (iPathTurns <= 1)
{
return true;
}
}
}
} while( pLoopGroup != NULL );
}
}
}
}
return false;
}
bool CvPlayerAI::AI_isFirstTech(TechTypes eTech) const
{
for (int iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (GC.getReligionInfo((ReligionTypes)iI).getTechPrereq() == eTech)
{
/************************************************************************************************/
/* RevDCM Start 09/08/10 */
/* */
/* OC_LIMITED_RELIGIONS */
/************************************************************************************************/
if (!(GC.getGameINLINE().isReligionSlotTaken((ReligionTypes)iI)))
{
/************************************************************************************************/
/* RevDCM Start 09/08/10 */
/* */
/* OC_LIMITED_RELIGIONS */
/************************************************************************************************/
if(canFoundReligion())
{
return true;
}
/************************************************************************************************/
/* REVDCM END */
/************************************************************************************************/
}
}
}
if (GC.getGameINLINE().countKnownTechNumTeams(eTech) == 0)
{
if ((getTechFreeUnit(eTech) != NO_UNIT) ||
(GC.getTechInfo(eTech).getFirstFreeTechs() > 0))
{
return true;
}
}
return false;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 09/03/09 poyuzhe & jdog5000 */
/* */
/* Efficiency */
/************************************************************************************************/
// From Sanguo Mod Performance, ie the CAR Mod
// Attitude cache
void CvPlayerAI::AI_invalidateAttitudeCache(PlayerTypes ePlayer)
{
m_aiAttitudeCache[ePlayer] = MAX_INT;
}
void CvPlayerAI::AI_invalidateAttitudeCache()
{
for( int iI = 0; iI < MAX_PLAYERS; iI++ )
{
AI_invalidateAttitudeCache((PlayerTypes)iI);
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
/************************************************************************************************/
/* RevDCM Start 5/1/09 */
/* */
/* Inquisitions */
/************************************************************************************************/
CvCity* CvPlayerAI::getInquisitionRevoltCity(CvUnit *pUnit, bool bNoUnit, int iRevIndexThreshold, int iTrendThreshold)
{
FAssert(pUnit != NULL);
if(!(hasInquisitionTarget()))
{
return NULL;
}
CvCity* pLoopCity;
CvCity* pBestCity = NULL;
CvPlot* pUnitPlot = pUnit->plot();
int iLoop;
int iBestRevoltIndex = 100;
int iTempCityValue = 0;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (pLoopCity->isInquisitionConditions())
{
if( (pLoopCity->getRevTrend() > iTrendThreshold)
|| (pLoopCity->getRevolutionIndex() > 1000) )
{
iTempCityValue = pLoopCity->getRevolutionIndex() + 7*(pLoopCity->getRevTrend());
iTempCityValue -= 10*(pUnitPlot->calculatePathDistanceToPlot(getTeam(), pLoopCity->plot()));
if(iTempCityValue > iBestRevoltIndex)
{
if ((bNoUnit) || (pUnit->generatePath(pLoopCity->plot(), 0, false)))
{
iBestRevoltIndex = iTempCityValue;
pBestCity = pLoopCity;
}
}
}
}
}
return pBestCity;
}
CvCity* CvPlayerAI::getTeamInquisitionRevoltCity(CvUnit *pUnit, bool bNoUnit, int iRevIndexThreshold, int iTrendThreshold)
{
FAssert(pUnit != NULL);
if(!(hasInquisitionTarget()))
{
return NULL;
}
CvCity* pLoopCity;
CvCity* pBestCity = NULL;
CvPlot* pUnitPlot = pUnit->plot();
int iI, iLoop;
int iBestRevoltIndex = 100;
int iTempCityValue = 0;
for(iI = 0; iI < MAX_PLAYERS; iI++)
{
CvPlayer& kLoopPlayer = GET_PLAYER(PlayerTypes(iI));
if(kLoopPlayer.isAlive())
{
if( (kLoopPlayer.getTeam() == getTeam()) ||
(GET_TEAM(getTeam()).isVassal((TeamTypes)kLoopPlayer.getTeam())) )
{
for (pLoopCity = kLoopPlayer.firstCity(&iLoop); pLoopCity != NULL; pLoopCity = kLoopPlayer.nextCity(&iLoop))
{
if(pLoopCity->isInquisitionConditions())
{
if( (pLoopCity->getRevTrend() > iTrendThreshold)
|| (pLoopCity->getRevolutionIndex() > 1000) )
{
iTempCityValue = pLoopCity->getRevolutionIndex() + 7*(pLoopCity->getRevTrend());
iTempCityValue -= 10*(pUnitPlot->calculatePathDistanceToPlot(getTeam(), pLoopCity->plot()));
if(iTempCityValue > iBestRevoltIndex)
{
if ((bNoUnit) || (pUnit->generatePath(pLoopCity->plot(), 0, false)))
{
iBestRevoltIndex = iTempCityValue;
pBestCity = pLoopCity;
}
}
}
}
}
}
}
}
return pBestCity;
}
CvCity* CvPlayerAI::getReligiousVictoryTarget(CvUnit *pUnit, bool bNoUnit)
{
FAssert(pUnit != NULL);
if(!(hasInquisitionTarget()))
{
return NULL;
}
if( !(isPushReligiousVictory()) && !(isConsiderReligiousVictory()) )
{
return NULL;
}
CvCity* pLoopCity;
CvCity* pBestCity = NULL;
int iI, iJ, iLoop;
int iBestCityValue = MAX_INT;
int tempCityValue;
CvPlot* pUnitPlot = pUnit->plot();
CvPlot* pLoopPlot = NULL;
CvTeam& pTeam = GET_TEAM(getTeam());
ReligionTypes eStateReligion = getStateReligion();
for(iI = 0; iI < MAX_PLAYERS; iI++)
{
CvPlayer& kLoopPlayer = GET_PLAYER(PlayerTypes(iI));
CvTeam& kLoopTeam = GET_TEAM(kLoopPlayer.getTeam());
if(kLoopPlayer.isAlive())
{
if( (TeamTypes(kLoopPlayer.getTeam()) == getTeam()) || kLoopTeam.isVassal((TeamTypes)kLoopPlayer.getTeam()) )
{
if(pUnitPlot->isHasPathToPlayerCity(getTeam(), PlayerTypes(iI)))
{
for(pLoopCity = kLoopPlayer.firstCity(&iLoop); pLoopCity != NULL; pLoopCity = kLoopPlayer.nextCity(&iLoop))
{
if(pLoopCity->isInquisitionConditions())
{
pLoopPlot = pLoopCity->plot();
if ((bNoUnit) || (pUnit->generatePath(pLoopPlot, 0, false)))
{
tempCityValue = pUnitPlot->calculatePathDistanceToPlot(getTeam(), pLoopPlot);
if( isNonStateReligionCommerce()
&& (kLoopPlayer.getID() == getID()) )
{
tempCityValue *= 2;
}
if(kLoopTeam.isVassal((TeamTypes)kLoopPlayer.getTeam()))
{
tempCityValue -= 12;
}
for(iJ = 0; iJ < GC.getNumReligionInfos(); iJ++)
{
if(ReligionTypes(iJ) != eStateReligion)
{
if(hasHolyCity(ReligionTypes(iJ)))
{
if(pLoopCity->isHasReligion(ReligionTypes(iJ)))
{
tempCityValue += 13;
}
}
}
}
if(tempCityValue < iBestCityValue)
{
pBestCity = pLoopCity;
iBestCityValue = tempCityValue;
}
}
}
}
}
}
}
}
return pBestCity;
}
bool CvPlayerAI::isPushReligiousVictory() const
{
return m_bPushReligiousVictory;
}
void CvPlayerAI::AI_setPushReligiousVictory()
{
PROFILE_FUNC();
m_bPushReligiousVictory = false;
if(getStateReligion() == NO_RELIGION)
{
return;
}
if(AI_getCultureVictoryStage() > 1)
{
return;
}
ReligionTypes eStateReligion = getStateReligion();
int iStateReligionInfluence = GC.getGameINLINE().calculateReligionPercent(eStateReligion);
int iI;
int iVictoryTarget;
CvTeamAI& pTeamAI = GET_TEAM(getTeam());
if(!hasHolyCity(eStateReligion))
{
return;
}
// Better way to determine if religious victory is valid?
bool bValid = false;
for (iI = 0; iI < GC.getNumVictoryInfos(); iI++)
{
if (GC.getGameINLINE().isVictoryValid((VictoryTypes) iI))
{
CvVictoryInfo& kVictoryInfo = GC.getVictoryInfo((VictoryTypes) iI);
if( kVictoryInfo.getReligionPercent() > 0 )
{
iVictoryTarget = kVictoryInfo.getReligionPercent();
bValid = true;
break;
}
}
}
if (!bValid)
{
m_bPushReligiousVictory = false;
return;
}
if(iStateReligionInfluence > (3*iVictoryTarget) / 4)
{
m_bPushReligiousVictory = true;
return;
}
bool bStateReligionBest = true;
for(iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if(eStateReligion != (ReligionTypes)iI)
{
if(GC.getGameINLINE().calculateReligionPercent((ReligionTypes)iI) > iStateReligionInfluence)
{
bStateReligionBest = false;
break;
}
}
}
/************************************************************************************************/
/* Afforess Start 07/26/10 */
/* */
/* */
/************************************************************************************************/
int iPercentThreshold = (2*iVictoryTarget) / 3;
bool bHasHolyBuilding = false;
if (getStateReligion() != NO_RELIGION)
{
if (GC.getGameINLINE().getHolyCity(getStateReligion()) != NULL && GC.getGameINLINE().getHolyCity(getStateReligion())->getOwner() == getID())
{
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (GC.getBuildingInfo((BuildingTypes)iI).getGlobalReligionCommerce() == getStateReligion())
{
if (GC.getGameINLINE().getHolyCity(getStateReligion())->getNumActiveBuilding((BuildingTypes)iI) > 0)
{
bHasHolyBuilding = true;
break;
}
}
}
}
}
if (bHasHolyBuilding)
{
iPercentThreshold /= 2;
}
if(bStateReligionBest)
{
if( (iStateReligionInfluence > iPercentThreshold) || (pTeamAI.getTotalLand(true) > 50) )
{
m_bPushReligiousVictory = true;
return;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
bool CvPlayerAI::isConsiderReligiousVictory() const
{
return m_bConsiderReligiousVictory;
}
void CvPlayerAI::AI_setConsiderReligiousVictory()
{
PROFILE_FUNC();
if(isPushReligiousVictory())
{
m_bConsiderReligiousVictory = true;
return;
}
m_bConsiderReligiousVictory = false;
if(getStateReligion() == NO_RELIGION)
{
return;
}
if(AI_getCultureVictoryStage() > 1)
{
return;
}
ReligionTypes eStateReligion = getStateReligion();
int iStateReligionInfluence = GC.getGameINLINE().calculateReligionPercent(eStateReligion);
int iI;
int iVictoryTarget;
CvTeamAI& pTeamAI = GET_TEAM(getTeam());
if(!hasHolyCity(eStateReligion))
{
return;
}
// Better way to determine if religious victory is valid?
bool bValid = false;
for (iI = 0; iI < GC.getNumVictoryInfos(); iI++)
{
if (GC.getGameINLINE().isVictoryValid((VictoryTypes) iI))
{
CvVictoryInfo& kVictoryInfo = GC.getVictoryInfo((VictoryTypes) iI);
if( kVictoryInfo.getReligionPercent() > 0 )
{
iVictoryTarget = kVictoryInfo.getReligionPercent();
bValid = true;
break;
}
}
}
if (!bValid)
{
return;
}
if(iStateReligionInfluence > ((2*iVictoryTarget)/ 3))
{
m_bConsiderReligiousVictory = true;
return;
}
bool eStateReligionBest = true;
for(iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if(eStateReligion != (ReligionTypes)iI)
{
if(GC.getGameINLINE().calculateReligionPercent((ReligionTypes)iI) > iStateReligionInfluence)
{
eStateReligionBest = false;
break;
}
}
}
if(eStateReligionBest)
{
m_bConsiderReligiousVictory = true;
return;
} else
{
if( (iStateReligionInfluence > (iVictoryTarget / 2)))
{
m_bConsiderReligiousVictory = true;
return;
}
}
}
bool CvPlayerAI::hasInquisitionTarget() const
{
return m_bHasInquisitionTarget;
}
void CvPlayerAI::AI_setHasInquisitionTarget()
{
PROFILE_FUNC();
m_bHasInquisitionTarget = false;
if(!(isInquisitionConditions()))
{
return;
}
CvCity* pLoopCity = NULL;
int iI, iLoop;
for(iI = 0; iI < MAX_PLAYERS; iI++)
{
CvPlayer& kLoopPlayer = GET_PLAYER(PlayerTypes(iI));
if(kLoopPlayer.isAlive())
{
if( (kLoopPlayer.getTeam() == getTeam()) ||
(GET_TEAM(getTeam()).isVassal((TeamTypes)kLoopPlayer.getTeam())) )
{
for(pLoopCity = kLoopPlayer.firstCity(&iLoop); pLoopCity != NULL; pLoopCity = kLoopPlayer.nextCity(&iLoop))
{
if (pLoopCity->isInquisitionConditions())
{
if( (pLoopCity->getRevTrend() > 0) && (pLoopCity->getRevolutionIndex() > 500) )
{
m_bHasInquisitionTarget = true;
return;
}else if(pLoopCity->getRevolutionIndex() > 1000)
{
m_bHasInquisitionTarget = true;
return;
}
}
}
}
}
}
pLoopCity = NULL;
if(isPushReligiousVictory() || isConsiderReligiousVictory())
{
for(iI = 0; iI < MAX_PLAYERS; iI++)
{
CvPlayer& kLoopPlayer = GET_PLAYER(PlayerTypes(iI));
CvTeam& kLoopTeam = GET_TEAM(kLoopPlayer.getTeam());
if(kLoopPlayer.isAlive())
{
if( (TeamTypes(kLoopPlayer.getTeam()) == getTeam()) || kLoopTeam.isVassal((TeamTypes)kLoopPlayer.getTeam()) )
{
for(pLoopCity = kLoopPlayer.firstCity(&iLoop); pLoopCity != NULL; pLoopCity = kLoopPlayer.nextCity(&iLoop))
{
if(pLoopCity->isInquisitionConditions())
{
m_bHasInquisitionTarget = true;
return;
}
}
}
}
}
}
}
int CvPlayerAI::countCityReligionRevolts() const
{
CvCity* pLoopCity = NULL;
int iLoop;
int iCount = 0;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (pLoopCity->isInquisitionConditions())
{
if( (pLoopCity->getRevTrend() > 0) && (pLoopCity->getRevolutionIndex() > 500) )
{
iCount++;
}else if(pLoopCity->getRevolutionIndex() > 1000)
{
iCount++;
}
}
}
return iCount;
}
/************************************************************************************************/
/* Inquisitions END */
/************************************************************************************************/
/************************************************************************************************/
/* Afforess Start 12/9/09 */
/* */
/* */
/************************************************************************************************/
int CvPlayerAI::AI_getEmbassyAttitude(PlayerTypes ePlayer) const
{
PROFILE_FUNC();
int iAttitude;
bool bVictim = ((GET_TEAM(getTeam()).AI_getMemoryCount((GET_PLAYER(ePlayer).getTeam()), MEMORY_DECLARED_WAR) == 0) && (GET_TEAM(getTeam()).isAtWar(GET_PLAYER(ePlayer).getTeam())));
iAttitude = 0;
if (GET_TEAM(getTeam()).isHasEmbassy(GET_PLAYER(ePlayer).getTeam()))
{
iAttitude = 1;
}
else if (((GET_TEAM(getTeam())).AI_getMemoryCount(GET_PLAYER(ePlayer).getTeam(), MEMORY_RECALLED_AMBASSADOR) > 0) && !bVictim)
{
iAttitude = -2;
}
return iAttitude;
}
int CvPlayerAI::AI_workerTradeVal(CvUnit* pUnit) const
{
PROFILE_FUNC();
int iValue = 0;
int iLoop;
CvArea* pLoopArea;
int iNeededWorkers = 0;
if (!(GC.getUnitInfo(pUnit->getUnitType()).isWorkerTrade()))
{//It's not a worker, so it's worthless
return 0;
}
// Also scale by relative merits of this unit compared to the best worker we can produce in our capital
UnitTypes eBestWorker = NO_UNIT;
if ( getCapitalCity() != NULL )
{
int iDummyValue;
// This can be called synchronmously orm asynchronously so set to no-rand
eBestWorker = getCapitalCity()->AI_bestUnitAI(UNITAI_WORKER, iDummyValue, true, true);
}
if ( eBestWorker != NO_UNIT )
{
iValue = (GC.getUnitInfo(eBestWorker).getProductionCost() > 0) ? GC.getUnitInfo(eBestWorker).getProductionCost() : 500;
int iBestUnitAIValue = AI_unitValue(eBestWorker, UNITAI_WORKER, getCapitalCity()->area());
int iThisUnitAIValue = AI_unitValue(pUnit->getUnitType(), UNITAI_WORKER, getCapitalCity()->area());
// Value as cost of production of the unit we can build scaled by their relative AI value
iValue = (iThisUnitAIValue * iValue)/std::max(1,iBestUnitAIValue);
}
else
{
iValue = (GC.getUnitInfo(pUnit->getUnitType()).getProductionCost() > 0) ? GC.getUnitInfo(pUnit->getUnitType()).getProductionCost() : 500;
iValue *= 3; // We can't build workers at all so value this up
}
// Normalise for game speed
iValue = (iValue * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getTrainPercent()) / 100;
for (pLoopArea = GC.getMapINLINE().firstArea(&iLoop); pLoopArea != NULL; pLoopArea = GC.getMapINLINE().nextArea(&iLoop))
{
if (pLoopArea->getCitiesPerPlayer(getID()) > 0)
{
iNeededWorkers += AI_neededWorkers(pLoopArea);
}
}
if (iNeededWorkers > 0)
{
// If we could use a large number of workers that dosn't means the first one
// is worth a huge multiple - scale non-linearly up to 3 times base cost
int iScalingPercent = 300 - 400/(1+iNeededWorkers);
iValue = 2*(iValue * iScalingPercent)/100; // Double final result as approx hammer->gold conversion
}
else
{
// We don't really know what to do with it, so can sell cheap
iValue /= 2;
}
return iValue;
}
CvCity* CvPlayerAI::findBestCoastalCity(void) const
{
CvCity* pBestCity = NULL;
bool bFoundConnected = false;
int iLoop;
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (pLoopCity->isCoastal(GC.getMIN_WATER_SIZE_FOR_OCEAN()))
{
bool bValid = false;
bool bConnected = pLoopCity->isConnectedToCapital(getID());
if (bConnected)
{
bValid = true;
}
else if (!bFoundConnected)
{
bValid = true;
}
if ( bValid && (pBestCity == NULL || (bConnected && !bFoundConnected) || pLoopCity->getPopulation() > pBestCity->getPopulation()) )
{
pBestCity = pLoopCity;
}
if ( bConnected )
{
bFoundConnected = true;
}
}
}
return pBestCity;
}
int CvPlayerAI::strengthOfBestUnitAI(DomainTypes eDomain, UnitAITypes eUnitAIType) const
{
PROFILE_FUNC();
CvUnitSelectionCriteria noGrowthCriteria;
noGrowthCriteria.m_bIgnoreGrowth = true;
UnitTypes eBestUnit = bestBuildableUnitForAIType(eDomain, eUnitAIType, &noGrowthCriteria);
if ( eBestUnit == NO_UNIT )
{
// We cannot build any! Take the average of any we already have
int iLoop;
int iTotal = 0;
int iCount = 0;
for(CvUnit* pLoopUnit = firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = nextUnit(&iLoop))
{
UnitAITypes eAIType = pLoopUnit->AI_getUnitAIType();
if ( eAIType == eUnitAIType || eUnitAIType == NO_UNITAI )
{
CvUnitInfo& kUnit = pLoopUnit->getUnitInfo();
iCount++;
iTotal += kUnit.getCombat() + GET_TEAM(getTeam()).getUnitClassStrengthChange((UnitClassTypes)kUnit.getUnitClassType());
}
}
if ( iCount > 0 )
{
iTotal /= iCount;
}
return std::max(1, iTotal);
}
else
{
CvUnitInfo& kUnit = GC.getUnitInfo(eBestUnit);
return (kUnit.getCombat() + GET_TEAM(getTeam()).getUnitClassStrengthChange((UnitClassTypes)kUnit.getUnitClassType()));
}
}
UnitTypes CvPlayerAI::bestBuildableUnitForAIType(DomainTypes eDomain, UnitAITypes eUnitAIType, CvUnitSelectionCriteria* criteria) const
{
PROFILE_FUNC();
UnitTypes eBestUnit = NO_UNIT;
// What is the best unit we can produce of this unitAI type
CvCity* pCapitalCity = getCapitalCity();
CvCity* pCoastalCity = NULL;
int iDummyValue;
// Handle capital-less civs (aka barbs) by just using an arbitrary city
if ( pCapitalCity == NULL )
{
int iDummy;
pCapitalCity = firstCity(&iDummy);
}
if (pCapitalCity != NULL)
{
switch(eDomain)
{
case NO_DOMAIN:
eBestUnit = pCapitalCity->AI_bestUnitAI(eUnitAIType, iDummyValue, false, true, criteria);
if ( eBestUnit != NO_UNIT || pCapitalCity->isCoastal(GC.getMIN_WATER_SIZE_FOR_OCEAN()) )
{
break;
}
// Drop through and check coastal
case DOMAIN_SEA:
if ( pCapitalCity->isCoastal(GC.getMIN_WATER_SIZE_FOR_OCEAN()) )
{
pCoastalCity = pCapitalCity;
}
else
{
pCoastalCity = findBestCoastalCity();
}
if ( pCoastalCity != NULL )
{
eBestUnit = pCoastalCity->AI_bestUnitAI(eUnitAIType, iDummyValue, false, true, criteria);
}
break;
case DOMAIN_LAND:
case DOMAIN_AIR:
eBestUnit = pCapitalCity->AI_bestUnitAI(eUnitAIType, iDummyValue, false, true, criteria);
break;
}
}
return eBestUnit;
}
int CvPlayerAI::AI_militaryUnitTradeVal(CvUnit* pUnit) const
{
PROFILE_FUNC();
int iValue;
UnitTypes eUnit = pUnit->getUnitType();
UnitAITypes eAIType = (UnitAITypes)GC.getUnitInfo(eUnit).getDefaultUnitAIType();
if ( eAIType == UNITAI_SUBDUED_ANIMAL )
{
int iBestValue = 0;
int iI;
int iLoop;
CvCity* pEvaluationCity = getCapitalCity();
if ( pEvaluationCity == NULL || (pUnit->getDomainType() == DOMAIN_SEA && !pEvaluationCity->isCoastal(GC.getMIN_WATER_SIZE_FOR_OCEAN())) )
{
pEvaluationCity = findBestCoastalCity();
}
if ( pEvaluationCity != NULL )
{
CvUnitInfo& kUnit = GC.getUnitInfo(eUnit);
// Subdued animals are rated primarily on what they can construct
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
BuildingTypes eBuilding = (BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iI);
if (NO_BUILDING != eBuilding)
{
if ((kUnit.getForceBuildings(eBuilding) || kUnit.getBuildings(eBuilding)) && canConstruct(eBuilding,false,false,true))
{
if (AI_getNumBuildingsNeeded(eBuilding, (pUnit->getDomainType() == DOMAIN_SEA) ) > 0)
{
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (pLoopCity->area() == pEvaluationCity->area() && pLoopCity->getNumBuilding(eBuilding) == 0)
{
iValue = pLoopCity->AI_buildingValue(eBuilding);
if ( iValue > iBestValue )
{
iBestValue = iValue;
}
}
}
}
}
}
}
// Also check their action outcomes (in the capital)
for (int iI = 0; iI < kUnit.getNumActionOutcomes(); iI++)
{
MissionTypes eMission = kUnit.getActionOutcomeMission(iI);
CvOutcomeList* pOutcomeList = kUnit.getActionOutcomeList(iI);
if (eMission != NO_MISSION)
{
if (pOutcomeList->isPossibleInPlot(*pUnit, *(pEvaluationCity->plot()), true))
{
int iValue = pOutcomeList->AI_getValueInPlot(*pUnit, *(pEvaluationCity->plot()), true);
if (iValue > iBestValue)
{
iBestValue = iValue;
}
}
}
}
for (int iJ = 0; iJ < GC.getNumUnitCombatInfos(); iJ++)
{
if (pUnit->hasCombatType((UnitCombatTypes)iJ))
{
CvUnitCombatInfo& kInfo = GC.getUnitCombatInfo((UnitCombatTypes)iJ);
for (int iI = 0; iI < kInfo.getNumActionOutcomes(); iI++)
{
MissionTypes eMission = kInfo.getActionOutcomeMission(iI);
CvOutcomeList* pOutcomeList = kInfo.getActionOutcomeList(iI);
if (eMission != NO_MISSION)
{
if (pOutcomeList->isPossibleInPlot(*pUnit, *(pEvaluationCity->plot()), true))
{
int iValue = pOutcomeList->AI_getValueInPlot(*pUnit, *(pEvaluationCity->plot()), true);
if (iValue > iBestValue)
{
iBestValue = iValue;
}
}
}
}
}
}
}
iValue = iBestValue;
}
else
{
UnitTypes eBestUnit = bestBuildableUnitForAIType(pUnit->getDomainType(), eAIType);
if ( eBestUnit == NO_UNIT )
{
iValue = 2 * GC.getUnitInfo(eUnit).getProductionCost(); // We can't build anything like this so double its value
}
else
{
int iBestUnitAIValue = AI_unitValue(eBestUnit, (UnitAITypes)GC.getUnitInfo(eUnit).getDefaultUnitAIType(), getCapitalCity()->area());
int iThisUnitAIValue = AI_unitValue(eUnit, (UnitAITypes)GC.getUnitInfo(eUnit).getDefaultUnitAIType(), getCapitalCity()->area());
// Value as cost of production of the unit we can build scaled by their relative AI value
iValue = (iThisUnitAIValue * GC.getUnitInfo(eBestUnit).getProductionCost())/std::max(1,iBestUnitAIValue);
}
// Normalise for game speed, and double as approximate hammer->gold conversion
iValue = (2 * iValue * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getTrainPercent()) / 100;
}
GC.getGameINLINE().logMsg("AI Unit Value For %S is %d", GC.getUnitInfo(eUnit).getDescription(), iValue);
return iValue;
}
int CvPlayerAI::AI_pledgeVoteTradeVal(VoteTriggeredData* kData, PlayerVoteTypes ePlayerVote, PlayerTypes ePlayer) const
{
return 1;
}
int CvPlayerAI::AI_corporationTradeVal(CorporationTypes eCorporation, PlayerTypes ePlayer) const
{
PROFILE_FUNC();
int iValue = 100;
CvCity* pLoopCity;
int iLoop;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
int iTempValue = 2*AI_corporationValue(eCorporation, pLoopCity);
if (!pLoopCity->isHasCorporation(eCorporation))
{
iTempValue /= 2;
}
iValue += iTempValue;
}
int iCompetitorCount = 0;
for(int iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
CvCorporationInfo& kCorp = GC.getCorporationInfo((CorporationTypes)iI);
if (iI != eCorporation)
{
if (hasHeadquarters((CorporationTypes)iI))
{
if (GC.getGame().isCompetingCorporation(eCorporation, (CorporationTypes)iI))
{
iCompetitorCount++;
}
}
}
}
iValue = (iValue*3)/(3+iCompetitorCount);
return iValue;
}
int CvPlayerAI::AI_secretaryGeneralTradeVal(VoteSourceTypes eVoteSource, PlayerTypes ePlayer) const
{
TeamTypes eBestTeam;
int iValue = 0;
int iAttitude;
int iBestAttitude;
int aiVotes[MAX_TEAMS];
int iI, iJ;
for (iI = 0; iI < MAX_TEAMS; iI++)
{
aiVotes[iI] = 0;
}
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (GET_TEAM((TeamTypes)iI).isAlive())
{
if (GET_TEAM((TeamTypes)iI).isVotingMember(eVoteSource) && !(getTeam() == iI))
{
for (iJ = 0; iJ < MAX_TEAMS; iJ++)
{
iBestAttitude = 0;
if (iI != iJ)
{
if (GET_TEAM((TeamTypes)iJ).isAlive())
{
if (GC.getGameINLINE().isTeamVoteEligible((TeamTypes)iJ, eVoteSource))
{
if (GET_TEAM((TeamTypes)iI).isVassal((TeamTypes)iJ))
{
aiVotes[iJ] += GET_TEAM((TeamTypes)iI).getVotes(NO_VOTE, eVoteSource);
}
iAttitude = GET_TEAM((TeamTypes)iI).AI_getAttitudeVal((TeamTypes)iJ);
if (iAttitude > iBestAttitude)
{
iBestAttitude = iAttitude;
eBestTeam = (TeamTypes)iJ;
}
}
}
}
}
aiVotes[eBestTeam] += GET_TEAM((TeamTypes)iI).getVotes(NO_VOTE, eVoteSource);
}
}
}
int iMostVotes = 0;
TeamTypes eLikelyWinner = NO_TEAM;
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (aiVotes[iI] > iMostVotes)
{
iMostVotes = aiVotes[iI];
eLikelyWinner = (TeamTypes)iI;
}
}
bool bKingMaker = false;
int iOurVotes = 0;
int iTheirVotes = 0;
if (eLikelyWinner != GET_PLAYER(ePlayer).getTeam())
{
iOurVotes = GET_TEAM(getTeam()).getVotes(NO_VOTE, eVoteSource);
iTheirVotes = aiVotes[GET_PLAYER(ePlayer).getTeam()];
if ((iOurVotes + iTheirVotes) > iMostVotes)
{
bKingMaker = true;
}
int iOurSharedCivics = 0;
int iTheirSharedCivics = 0;
for (iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
if (getCivics((CivicOptionTypes)iI) == GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)iI))
{
iOurSharedCivics++;
}
if (getCivics((CivicOptionTypes)iI) == GET_PLAYER(GET_TEAM(eLikelyWinner).getLeaderID()).getCivics((CivicOptionTypes)iI))
{
iTheirSharedCivics++;
}
}
iValue *= std::max(1, iOurSharedCivics);
iValue /= std::max(1, iTheirSharedCivics);
}
else
{
bKingMaker = false;
}
if (bKingMaker)
{
int iExtraVotes = ((iOurVotes + iTheirVotes) - iMostVotes);
iValue *= iExtraVotes;
}
iValue *= 2;
return iValue;
}
int CvPlayerAI::AI_getCivicAttitudeChange(PlayerTypes ePlayer) const
{
int iAttitude = 0;
for (int iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
if ( getCivics((CivicOptionTypes)iI) != NO_CIVIC )
{
CvCivicInfo& kCivicOption = GC.getCivicInfo(getCivics((CivicOptionTypes)iI));
for (int iJ = 0; iJ < GC.getNumCivicOptionInfos(); iJ++)
{
int eCivic = GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)iJ);
if ( eCivic != NO_CIVIC )
{
iAttitude += kCivicOption.getCivicAttitudeChange(eCivic);
}
}
}
}
return iAttitude;
}
bool CvPlayerAI::AI_avoidIncreasingTaxes() const
{
if ((getTaxationAnger() > 0) && !isGoldenAge())
{
return true;
}
return false;
}
int CvPlayerAI::AI_getCivicShareAttitude(PlayerTypes ePlayer) const
{
int iAttitude = 0;
for (int iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
if (NO_CIVIC != getCivics((CivicOptionTypes)iI) && getCivics((CivicOptionTypes)iI) == GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)iI))
{
iAttitude += GC.getCivicInfo((CivicTypes)getCivics((CivicOptionTypes)iI)).getAttitudeShareMod();
}
}
return iAttitude;
}
/************************************************************************************************/
/* Afforess Start 06/16/10 */
/* */
/* Advanced Diplomacy */
/************************************************************************************************/
TeamTypes CvPlayerAI::AI_bestJoinWarTeam(PlayerTypes ePlayer)
{
int iValue = 0;
int iBestValue = 0;
TeamTypes eWarTeam = NO_TEAM;
for (int iI = 0; iI < MAX_CIV_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
//if we are at war, or they have backstabbed a friend, or they are a mutual enemy
if (atWar(GET_PLAYER((PlayerTypes)iI).getTeam(), getTeam()) || (AI_getMemoryCount(ePlayer, MEMORY_BACKSTAB_FRIEND) > 0) || (GET_PLAYER(ePlayer).AI_getAttitude((PlayerTypes)iI) < ATTITUDE_CAUTIOUS && AI_getAttitude((PlayerTypes)iI) < ATTITUDE_CAUTIOUS))
{
if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
if (!GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isVassal(GET_PLAYER(ePlayer).getTeam()) && !GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isDefensivePact(GET_PLAYER(ePlayer).getTeam()))
{
if (GET_PLAYER(ePlayer).isHuman() ||
(GET_PLAYER(ePlayer).AI_getAttitude(getID()) > GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPersonalityType()).getDeclareWarRefuseAttitudeThreshold()))
{
if (GET_PLAYER(ePlayer).isHuman() || (GET_PLAYER(ePlayer).AI_getAttitude((PlayerTypes)iI) < ATTITUDE_CAUTIOUS))
{
iValue = GET_TEAM(getTeam()).AI_declareWarTradeVal(GET_PLAYER((PlayerTypes)iI).getTeam(), GET_PLAYER(ePlayer).getTeam());
//Favor teams we are already at war with
if (!atWar(GET_PLAYER((PlayerTypes)iI).getTeam(), getTeam())) {
iValue *= 2;
iValue /= 3;
}
if (iBestValue < iValue)
{
iBestValue = iValue;
eWarTeam = GET_PLAYER((PlayerTypes)iI).getTeam();
}
}
}
}
}
}
}
}
return eWarTeam;
}
TeamTypes CvPlayerAI::AI_bestMakePeaceTeam(PlayerTypes ePlayer)
{
//kWarTeam is the team that is at war with the player we want them to make peace with
CvTeamAI& kWarTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam());
int iTheirPower = kWarTeam.getPower(true);
int iBestValue = 250; //set a small threshold
TeamTypes eBestTeam = NO_TEAM;
for (int iI = 0; iI < MAX_CIV_PLAYERS; iI++)
{
if ((iI != ePlayer) && (iI != getID()))
{
//kPlayer is who we want kWarTeam to make peace with...
CvPlayer& kPlayer = GET_PLAYER((PlayerTypes)iI);
if (kPlayer.isAlive() && !kPlayer.isMinorCiv())
{
if (GET_TEAM(getTeam()).isHasMet(kPlayer.getTeam()))
{
if (atWar(kPlayer.getTeam(), GET_PLAYER(ePlayer).getTeam()) && !atWar(getTeam(), kPlayer.getTeam()))
{
if (GET_PLAYER(ePlayer).AI_isWillingToTalk((PlayerTypes)iI) && kWarTeam.AI_makePeaceTrade(kPlayer.getTeam(), getTeam()) == NO_DENIAL)
{
int iValue = std::max(0, AI_getAttitudeVal((PlayerTypes)iI) * 100);
if (GET_TEAM(getTeam()).AI_getWarPlan(kPlayer.getTeam()) != NO_WARPLAN)
{
//We are planning war, peace is not "that" valuable after all
iValue /= 10;
}
if (iTheirPower > GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).getPower(true) * 3)
{
//They are likely to crush the enemy player, we don't want them to win
iValue *= 4;
}
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestTeam = kPlayer.getTeam();
}
}
}
}
}
}
}
return eBestTeam;
}
TeamTypes CvPlayerAI::AI_bestStopTradeTeam(PlayerTypes ePlayer)
{
int iValue;
int iBestValue = 0;
TeamTypes eBestTeam = NO_TEAM;
TeamTypes eWorstEnemy = GET_TEAM(getTeam()).AI_getWorstEnemy();
for (int iI = 0; iI < MAX_CIV_TEAMS; iI++)
{
TeamTypes eTeam = ((TeamTypes)iI);
bool bAtWar = atWar(eTeam, getTeam());
if ((getTeam() != eTeam) && (eTeam != GET_PLAYER(ePlayer).getTeam()))
{
if ((eWorstEnemy == eTeam) || bAtWar ||
((GET_TEAM(getTeam()).AI_getAttitude(eTeam) == ATTITUDE_FURIOUS) || (GET_TEAM(getTeam()).AI_getAttitude(eTeam) == ATTITUDE_ANNOYED)
|| (AI_getMemoryCount(ePlayer, MEMORY_BACKSTAB_FRIEND) > 0)))
{
if (GET_PLAYER(ePlayer).getTeam() != eTeam)
{
if (GET_TEAM(eTeam).isAlive())
{
if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isHasMet(eTeam))
{
if (!atWar(GET_PLAYER(ePlayer).getTeam(), eTeam))
{
if (GET_TEAM(eTeam).isVassal(GET_PLAYER(ePlayer).getTeam()))
{
if (GET_PLAYER(ePlayer).canStopTradingWithTeam(eTeam))
{
if (GET_PLAYER(ePlayer).AI_stopTradingTrade(eTeam, getID()) == NO_DENIAL)
{
if ((bAtWar && GET_PLAYER(ePlayer).isTradingMilitaryBonus(GET_TEAM(eTeam).getLeaderID()))
|| (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).AI_getAttitude(eTeam) != ATTITUDE_FRIENDLY))
{
iValue = 0;
iValue = AI_stopTradingTradeVal(eTeam, ePlayer);
if ((iBestValue == 0) || (iValue < iBestValue))
{
iBestValue = iValue;
eBestTeam = eTeam;
}
}
}
}
}
}
}
}
}
}
}
}
return eBestTeam;
}
int CvPlayerAI::AI_militaryBonusVal(BonusTypes eBonus)
{
int iValue = 0;
int iHasOrBonusCount;
bool bFound = false;
UnitTypes eUnit = NO_UNIT;
for (int iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
eUnit = (UnitTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI);
if (canTrain(eUnit))
{
if (GC.getUnitInfo(eUnit).getPrereqAndBonus() == eBonus)
{
iValue += 1000;
}
iHasOrBonusCount = 0;
bFound = false;
for (int iK = 0; iK < GC.getNUM_UNIT_PREREQ_OR_BONUSES(); iK++)
{
if (GC.getUnitInfo(eUnit).getPrereqOrBonuses(iK) == eBonus)
{
bFound = true;
}
else if (hasBonus((BonusTypes)GC.getUnitInfo(eUnit).getPrereqOrBonuses(iK)))
{
iHasOrBonusCount++;
}
}
if (bFound)
{
iValue += 300;
iValue /= iHasOrBonusCount;
}
}
}
return iValue;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
//Slightly altered form of CvUnitAI::AI_promotionValue()
int CvPlayerAI::AI_promotionValue(PromotionTypes ePromotion, UnitTypes eUnit, const CvUnit* pUnit, UnitAITypes eUnitAI) const
{
MEMORY_TRACK()
int iValue;
int iTemp;
int iExtra;
int iI;
iValue = 0;
CvPromotionInfo &kPromotion = GC.getPromotionInfo(ePromotion);
CvUnitInfo &kUnit = GC.getUnitInfo(eUnit);
int iMoves;
if (pUnit == NULL)
{
iMoves = kUnit.getMoves();
}
else
{
//TB note: changed from baseMoves to maxMoves
iMoves = pUnit->maxMoves();
}
if (eUnitAI == NO_UNITAI)
{
eUnitAI = (UnitAITypes)kUnit.getDefaultUnitAIType();
}
/************************************************************************************************/
/* SUPER SPIES 05/24/08 TSheep */
/* */
/* */
/************************************************************************************************/
//TSHEEP Setup AI values for promotions
if(kUnit.isSpy())
{
/*********************************************************************************************/
/* REVOLUTIONDCM 24/09/09 glider1 */
/** */
/*********************************************************************************************/
//Readjust promotion choices favouring security, deception, logistics, escape, improvise,
//filling in other promotions very lightly because the AI does not yet have situational awareness
//when using spy promotions at the moment of mission execution.
//Logistics
//I & III
iValue += (kPromotion.getMovesChange()*20);
//II
if (kPromotion.isEnemyRoute()) iValue += 20;
iValue += (kPromotion.getMoveDiscountChange()*10);
//total 20, 30, 20 points
//Deception
if (kPromotion.getEvasionChange())
{
//Lean towards more deception if deception is already present
iValue += ((kPromotion.getEvasionChange() * 2) + pUnit == NULL ? 0 : pUnit->evasionProbability());
}//total 20, 30, 40 points
//Security
iValue += (kPromotion.getVisibilityChange()*10);
//Lean towards more security if security is already present
iValue += (kPromotion.getInterceptChange() + pUnit == NULL ? kUnit.getInterceptionProbability() : pUnit->currInterceptionProbability());
//total 20, 30, 40 points
//Escape
if (kPromotion.getWithdrawalChange())
{
iValue += 30;
}
//Improvise
if (kPromotion.getUpgradeDiscount())
{
iValue += 20;
}
//Loyalty
if (kPromotion.isAlwaysHeal())
{
iValue += 15;
}
//Instigator
//I & II
if (kPromotion.getEnemyHealChange())
{
iValue += 15;
}
//III
if (kPromotion.getNeutralHealChange())
{
iValue += 15;
}
//Alchemist
if (kPromotion.getFriendlyHealChange())
{
iValue += 15;
}
if (iValue > 0)
{
iValue += GC.getGameINLINE().getSorenRandNum(15, "AI Promote");
}
return iValue;
/****************************************************************************************/
/* REVOLUTIONDCM END glider1 */
/****************************************************************************************/
}
/********************************************************************************************/
/* SUPER SPIES END TSheep */
/********************************************************************************************/
if (kPromotion.isLeader())
{
// Don't consume the leader as a regular promotion
return 0;
}
// Koshling - this is a horrible kludge really to get the AI to ralise that promotions
// with a subdue animal bonus are VERY good on hunters. However, it's VERY hard to figure
// this out from first principals because its very indirect (outcome has per-promotion value,
// but the outcome itself just defines a unit, whuich then has a build, which than has value!)
// As a result I am just giving a bonus to outcome modifiers for hunters generically, which
// CURRENTLY works ok since the available outcomes are basically the animal subdues
if ( eUnitAI == UNITAI_HUNTER )
{
for( iI = 0; iI < GC.getNumOutcomeInfos(); iI++ )
{
for ( int iJ = 0; iJ < GC.getOutcomeInfo((OutcomeTypes)iI).getNumExtraChancePromotions(); iJ++ )
{
if ( GC.getOutcomeInfo((OutcomeTypes)iI).getExtraChancePromotion(iJ) == ePromotion )
{
iValue += 2*GC.getOutcomeInfo((OutcomeTypes)iI).getExtraChancePromotionChance(iJ);
break;
}
}
}
}
if (kPromotion.isBlitz())
{
//ls612: AI to know that Blitz is only useful on units with more than one move now that the filter is gone
if (iMoves > 1)
{
if ((eUnitAI == UNITAI_RESERVE ||
eUnitAI == UNITAI_ATTACK ||
eUnitAI == UNITAI_ATTACK_CITY ||
eUnitAI == UNITAI_PARADROP))
{
iValue += (10 * iMoves);
}
else
{
iValue += 2;
}
}
else
{
iValue += 0;
}
}
if (kPromotion.isAmphib())
{
if ((eUnitAI == UNITAI_ATTACK) ||
(eUnitAI == UNITAI_ATTACK_CITY))
{
iValue += 10;
}
else
{
iValue++;
}
}
if (kPromotion.isRiver())
{
if ((eUnitAI == UNITAI_ATTACK) ||
(eUnitAI == UNITAI_ATTACK_CITY))
{
iValue += 5;
}
else
{
iValue++;
}
}
if (kPromotion.isEnemyRoute())
{
if (eUnitAI == UNITAI_PILLAGE)
{
iValue += (50 + (4 * iMoves));
}
else if ((eUnitAI == UNITAI_ATTACK) ||
(eUnitAI == UNITAI_ATTACK_CITY))
{
iValue += (30 + (4 * iMoves));
}
else if (eUnitAI == UNITAI_PARADROP)
{
iValue += (20 + (4 * iMoves));
}
else
{
iValue += (4 * iMoves);
}
}
if (kPromotion.isAlwaysHeal())
{
if ((eUnitAI == UNITAI_ATTACK) ||
(eUnitAI == UNITAI_ATTACK_CITY) ||
(eUnitAI == UNITAI_PILLAGE) ||
(eUnitAI == UNITAI_COUNTER) ||
(eUnitAI == UNITAI_ATTACK_SEA) ||
(eUnitAI == UNITAI_PIRATE_SEA) ||
(eUnitAI == UNITAI_ESCORT_SEA) ||
(eUnitAI == UNITAI_PARADROP))
{
iValue += 10;
}
else
{
iValue += 8;
}
}
if (kPromotion.isHillsDoubleMove())
{
if (eUnitAI == UNITAI_EXPLORE)
{
iValue += 50;
}
else
{
iValue += 10;
}
}
/************************************************************************************************/
/* Afforess Start 06/04/10 */
/* */
/* */
/************************************************************************************************/
if (kPromotion.isCanMovePeaks())
{
if (GC.getGameINLINE().isOption(GAMEOPTION_MOUNTAINS))
{
for (iI = 0; iI < GC.getNumTechInfos(); iI++)
{
if (!(GET_TEAM(getTeam()).isHasTech((TechTypes)iI)))
{
if (GC.getTechInfo((TechTypes)iI).isCanPassPeaks())
{
// Koshling - changed the semantics of this somewhat. Wheer before
// it allowed a unit to lead a stack through mountains (very valuable)
// it now only allows this unit to pass through mountains
// The new promotion PROMOTION_MOUNTAIN_LEADER has the old semantics and
// retains the old AI value (next clause)
iValue += 10;
}
}
}
}
}
// Koshling - enhanced mountaineering mode to differentiate between ability to move through
// mountains, and ability to lead a stack through mountains
if (kPromotion.isCanLeadThroughPeaks())
{
if (GC.getGameINLINE().isOption(GAMEOPTION_MOUNTAINS))
{
for (iI = 0; iI < GC.getNumTechInfos(); iI++)
{
if (!(GET_TEAM(getTeam()).isHasTech((TechTypes)iI)))
{
if (GC.getTechInfo((TechTypes)iI).isCanPassPeaks())
{
iValue += 75;
}
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (kPromotion.isImmuneToFirstStrikes()
&& (pUnit == NULL || !pUnit->immuneToFirstStrikes()))
{
if ((eUnitAI == UNITAI_ATTACK_CITY))
{
iValue += 25;
}
else if ((eUnitAI == UNITAI_ATTACK))
{
iValue += 15;
}
else
{
iValue += 10;
}
}
iTemp = kPromotion.getVisibilityChange();
if ((eUnitAI == UNITAI_EXPLORE_SEA) ||
(eUnitAI == UNITAI_EXPLORE))
{
iValue += (iTemp * 40);
}
else if (eUnitAI == UNITAI_PIRATE_SEA)
{
iValue += (iTemp * 20);
}
iTemp = kPromotion.getMovesChange();
if ((eUnitAI == UNITAI_ATTACK_SEA) ||
(eUnitAI == UNITAI_PIRATE_SEA) ||
(eUnitAI == UNITAI_RESERVE_SEA) ||
(eUnitAI == UNITAI_ESCORT_SEA) ||
(eUnitAI == UNITAI_EXPLORE_SEA) ||
(eUnitAI == UNITAI_EXPLORE) ||
(eUnitAI == UNITAI_ASSAULT_SEA) ||
(eUnitAI == UNITAI_SETTLER_SEA) ||
(eUnitAI == UNITAI_PILLAGE) ||
(eUnitAI == UNITAI_ATTACK) ||
(eUnitAI == UNITAI_PARADROP))
{
iValue += (iTemp * 40);
}
else
{
iValue += (iTemp * 25);
}
iTemp = kPromotion.getMoveDiscountChange();
if ((eUnitAI == UNITAI_PILLAGE) ||
(eUnitAI == UNITAI_EXPLORE))
{
iValue += (iTemp * 20);
}
else
{
iValue += (iTemp * 10);
}
iTemp = kPromotion.getAirRangeChange();
if (eUnitAI == UNITAI_ATTACK_AIR ||
eUnitAI == UNITAI_CARRIER_AIR)
{
iValue += (iTemp * 20);
}
else if (eUnitAI == UNITAI_DEFENSE_AIR)
{
iValue += (iTemp * 10);
}
iTemp = kPromotion.getInterceptChange();
if (eUnitAI == UNITAI_DEFENSE_AIR)
{
iValue += (iTemp * 4);
}
else if (eUnitAI == UNITAI_CITY_SPECIAL || eUnitAI == UNITAI_CARRIER_AIR)
{
iValue += (iTemp * 3);
}
else
{
iValue += (iTemp / 10);
}
iTemp = kPromotion.getEvasionChange();
if (eUnitAI == UNITAI_ATTACK_AIR || eUnitAI == UNITAI_CARRIER_AIR)
{
iValue += (iTemp * 4);
}
else
{
iValue += (iTemp / 10);
}
iTemp = kPromotion.getFirstStrikesChange() * 2;
iTemp += kPromotion.getChanceFirstStrikesChange();
if ((eUnitAI == UNITAI_RESERVE) ||
(eUnitAI == UNITAI_COUNTER) ||
(eUnitAI == UNITAI_CITY_DEFENSE) ||
(eUnitAI == UNITAI_CITY_COUNTER) ||
(eUnitAI == UNITAI_CITY_SPECIAL) ||
(eUnitAI == UNITAI_ATTACK))
{
iTemp *= 25;
iExtra = pUnit == NULL ? kUnit.getChanceFirstStrikes() + kUnit.getFirstStrikes() * 2 : pUnit->getExtraChanceFirstStrikes() + pUnit->getExtraFirstStrikes() * 2;
iTemp *= 100 + iExtra * 15;
iTemp /= 100;
iValue += iTemp;
}
else
{
iValue += (iTemp * 5);
}
iTemp = kPromotion.getWithdrawalChange();
if (iTemp != 0)
{
iExtra = (kUnit.getWithdrawalProbability() + (pUnit == NULL ? 0 : pUnit->getExtraWithdrawal() * 4));
iTemp *= (100 + iExtra);
iTemp /= 100;
if (eUnitAI == UNITAI_ATTACK_CITY)
{
iValue += (iTemp * 4) / 3;
}
else if ((eUnitAI == UNITAI_COLLATERAL) ||
(eUnitAI == UNITAI_RESERVE) ||
(eUnitAI == UNITAI_RESERVE_SEA) ||
(eUnitAI == UNITAI_EXPLORE) ||
(pUnit != NULL && pUnit->getLeaderUnitType() != NO_UNIT))
{
iValue += iTemp * 1;
}
else
{
iValue += (iTemp / 4);
}
}
iTemp = kPromotion.getCollateralDamageChange();
if (iTemp != 0)
{
iExtra = pUnit == NULL ? kUnit.getCollateralDamage() : pUnit->getExtraCollateralDamage(); //collateral has no strong synergy (not like retreat)
iTemp *= (100 + iExtra);
iTemp /= 100;
if (eUnitAI == UNITAI_COLLATERAL)
{
iValue += (iTemp * 1);
}
else if (eUnitAI == UNITAI_ATTACK_CITY)
{
iValue += ((iTemp * 2) / 3);
}
else
{
iValue += (iTemp / 8);
}
}
iTemp = kPromotion.getBombardRateChange();
if (eUnitAI == UNITAI_ATTACK_CITY)
{
iValue += (iTemp * 2);
}
else
{
iValue += (iTemp / 8);
}
iTemp = kPromotion.getEnemyHealChange();
if ((eUnitAI == UNITAI_ATTACK) ||
(eUnitAI == UNITAI_ATTACK_SEA) ||
(eUnitAI == UNITAI_PARADROP) ||
(eUnitAI == UNITAI_PIRATE_SEA))
{
iValue += (iTemp / 4);
}
else
{
iValue += (iTemp / 8);
}
iTemp = kPromotion.getNeutralHealChange();
iValue += (iTemp / 8);
iTemp = kPromotion.getFriendlyHealChange();
if ((eUnitAI == UNITAI_CITY_DEFENSE) ||
(eUnitAI == UNITAI_CITY_COUNTER) ||
(eUnitAI == UNITAI_CITY_SPECIAL))
{
iValue += (iTemp / 4);
}
else
{
iValue += (iTemp / 8);
}
if ( pUnit != NULL && ( pUnit->getDamage() > 0 || ((eUnitAI == UNITAI_COUNTER ||
eUnitAI == UNITAI_PILLAGE ||
eUnitAI == UNITAI_ATTACK_CITY ||
eUnitAI == UNITAI_RESERVE )) ))
{
iTemp = kPromotion.getSameTileHealChange() + pUnit->getSameTileHeal();
iExtra = pUnit->getSameTileHeal();
iTemp *= (100 + iExtra * 5);
iTemp /= 100;
if (iTemp > 0)
{
if (pUnit->healRate(pUnit->plot()) < iTemp)
{
iValue += iTemp * ((pUnit->getGroup()->getNumUnits() > 4) ? 4 : 2);
}
else
{
iValue += (iTemp / 8);
}
}
iTemp = kPromotion.getAdjacentTileHealChange();
iExtra = pUnit->getAdjacentTileHeal();
iTemp *= (100 + iExtra * 5);
iTemp /= 100;
if (pUnit->getSameTileHeal() >= iTemp)
{
iValue += (iTemp * ((pUnit->getGroup()->getNumUnits() > 9) ? 4 : 2));
}
else
{
iValue += (iTemp / 4);
}
}
// try to use Warlords to create super-medic units
if (pUnit != NULL && (kPromotion.getAdjacentTileHealChange() > 0 || kPromotion.getSameTileHealChange() > 0))
{
PromotionTypes eLeader = NO_PROMOTION;
for (iI = 0; iI < GC.getNumPromotionInfos(); iI++)
{
if (GC.getPromotionInfo((PromotionTypes)iI).isLeader())
{
eLeader = (PromotionTypes)iI;
}
}
if (eLeader != NO_PROMOTION && pUnit->isHasPromotion(eLeader))
{
iValue += kPromotion.getAdjacentTileHealChange() + kPromotion.getSameTileHealChange();
}
}
/************************************************************************************************/
/* Afforess Start 03/1/10 Coded By: KillMePlease */
/* */
/* Great Commanders */
/************************************************************************************************/
//@MOD Commanders: command range promotion AI value
iTemp = kPromotion.getCommandRange();
iValue += iTemp * 2;
//end mod
//@MOD Commanders: control points promotion AI value
iTemp = kPromotion.getControlPoints();
if (pUnit != NULL && pUnit->plot()->getNumUnits() > pUnit->controlPoints())
{
iValue += iTemp * 100;
}
//end mod
iTemp = kPromotion.getCombatPercent();
//@MOD Commanders: combat promotion value
if (eUnitAI == UNITAI_GENERAL)
{
iValue += (iTemp * 3);
}
else
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if ((eUnitAI == UNITAI_ATTACK) ||
(eUnitAI == UNITAI_COUNTER) ||
(eUnitAI == UNITAI_CITY_COUNTER) ||
(eUnitAI == UNITAI_ATTACK_SEA) ||
(eUnitAI == UNITAI_RESERVE_SEA) ||
(eUnitAI == UNITAI_ATTACK_SEA) ||
(eUnitAI == UNITAI_PARADROP) ||
(eUnitAI == UNITAI_PIRATE_SEA) ||
(eUnitAI == UNITAI_RESERVE_SEA) ||
(eUnitAI == UNITAI_ESCORT_SEA) ||
(eUnitAI == UNITAI_CARRIER_SEA) ||
(eUnitAI == UNITAI_ATTACK_AIR) ||
(eUnitAI == UNITAI_CARRIER_AIR))
{
iValue += (iTemp * 3);
}
else
{
iValue += (iTemp * 1);
}
iTemp = kPromotion.getCityAttackPercent();
if (iTemp != 0)
{
if (kUnit.getUnitAIType(UNITAI_ATTACK) || kUnit.getUnitAIType(UNITAI_ATTACK_CITY) || kUnit.getUnitAIType(UNITAI_ATTACK_CITY_LEMMING))
{
iExtra = (kUnit.getCityAttackModifier() + (pUnit == NULL ? 0 : pUnit->getExtraCityAttackPercent()*4));
iTemp *= (100 + iExtra);
iTemp /= 100;
if (eUnitAI == UNITAI_ATTACK_CITY)
{
iValue += ((iTemp * 4)/3);
}
else
{
iValue -= iTemp / 4;
}
}
}
iTemp = kPromotion.getCityDefensePercent();
if (iTemp != 0)
{
if ((eUnitAI == UNITAI_CITY_DEFENSE) ||
(eUnitAI == UNITAI_CITY_SPECIAL))
{
iExtra = kUnit.getCityDefenseModifier() + (pUnit == NULL ? 0 : pUnit->getExtraCityDefensePercent() * 2);
iValue += ((((iTemp * (100 + iExtra)) / 100)*4)/3);
}
else
{
iValue += (iTemp / 4);
}
}
iTemp = kPromotion.getHillsAttackPercent();
if (iTemp != 0)
{
iExtra = pUnit == NULL ? kUnit.getHillsAttackModifier() : pUnit->getExtraHillsAttackPercent();
iTemp *= (100 + iExtra * 2);
iTemp /= 100;
if ((eUnitAI == UNITAI_ATTACK) ||
(eUnitAI == UNITAI_COUNTER))
{
iValue += (iTemp / 4);
}
else
{
iValue += (iTemp / 16);
}
}
iTemp = kPromotion.getHillsDefensePercent();
if (iTemp != 0)
{
iExtra = (kUnit.getHillsDefenseModifier() + (pUnit == NULL ? 0 : pUnit->getExtraHillsDefensePercent() * 2));
iTemp *= (100 + iExtra);
iTemp /= 100;
if (eUnitAI == UNITAI_CITY_DEFENSE)
{
if (pUnit != NULL && pUnit->plot()->isCity() && pUnit->plot()->isHills())
{
iValue += (iTemp * 4) / 3;
}
}
else if (eUnitAI == UNITAI_COUNTER)
{
if (pUnit != NULL && pUnit->plot()->isHills())
{
iValue += (iTemp / 4);
}
else
{
iValue++;
}
}
else
{
iValue += (iTemp / 16);
}
}
iTemp = kPromotion.getRevoltProtection();
if ((eUnitAI == UNITAI_CITY_DEFENSE) ||
(eUnitAI == UNITAI_CITY_COUNTER) ||
(eUnitAI == UNITAI_CITY_SPECIAL))
{
if (pUnit != NULL && iTemp > 0)
{
PlayerTypes eOwner = pUnit->plot()->calculateCulturalOwner();
if (eOwner != NO_PLAYER && GET_PLAYER(eOwner).getTeam() != getTeam())
{
iValue += (iTemp / 2);
}
/************************************************************************************************/
/* Afforess Start 07/12/10 */
/* */
/* */
/************************************************************************************************/
if (pUnit->plot()->isCity())
{
iValue += pUnit->plot()->getPlotCity()->getRevolutionIndex() * iTemp / 1000;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
}
iTemp = kPromotion.getCollateralDamageProtection();
if ((eUnitAI == UNITAI_CITY_DEFENSE) ||
(eUnitAI == UNITAI_CITY_COUNTER) ||
(eUnitAI == UNITAI_CITY_SPECIAL))
{
iValue += (iTemp / 3);
}
else if ((eUnitAI == UNITAI_ATTACK) ||
(eUnitAI == UNITAI_COUNTER))
{
iValue += (iTemp / 4);
}
else
{
iValue += (iTemp / 8);
}
iTemp = kPromotion.getPillageChange();
if (eUnitAI == UNITAI_PILLAGE ||
eUnitAI == UNITAI_ATTACK_SEA ||
eUnitAI == UNITAI_PIRATE_SEA)
{
iValue += (iTemp / 4);
}
else
{
iValue += (iTemp / 16);
}
iTemp = kPromotion.getUpgradeDiscount();
iValue += (iTemp / 16);
iTemp = kPromotion.getExperiencePercent();
if ((eUnitAI == UNITAI_ATTACK) ||
(eUnitAI == UNITAI_ATTACK_SEA) ||
(eUnitAI == UNITAI_PIRATE_SEA) ||
(eUnitAI == UNITAI_RESERVE_SEA) ||
(eUnitAI == UNITAI_ESCORT_SEA) ||
(eUnitAI == UNITAI_CARRIER_SEA) ||
(eUnitAI == UNITAI_MISSILE_CARRIER_SEA))
{
iValue += (iTemp * 1);
}
else
{
iValue += (iTemp / 2);
}
iTemp = kPromotion.getKamikazePercent();
if (eUnitAI == UNITAI_ATTACK_CITY)
{
iValue += (iTemp / 16);
}
else
{
iValue += (iTemp / 64);
}
for (iI = 0; iI < GC.getNumTerrainInfos(); iI++)
{
iTemp = kPromotion.getTerrainAttackPercent(iI);
if (iTemp != 0)
{
/************************************************************************************************/
/* Afforess Start 07/12/10 */
/* */
/* End Overvalue of Terrain Promotions */
/************************************************************************************************/
/*
iExtra = pUnit == NULL ? kUnit.getTerrainAttackModifier(iI) : pUnit->getExtraTerrainAttackPercent((TerrainTypes)iI);
iTemp *= (100 + iExtra * 2);
iTemp /= 100;
*/
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if ((eUnitAI == UNITAI_ATTACK) ||
(eUnitAI == UNITAI_COUNTER))
{
iValue += (iTemp / 4);
}
else
{
iValue += (iTemp / 25);
}
}
iTemp = kPromotion.getTerrainDefensePercent(iI);
if (iTemp != 0)
{
/************************************************************************************************/
/* Afforess Start 07/12/10 */
/* */
/* End Overvalue of Terrain Promotions */
/************************************************************************************************/
/*
iExtra = pUnit == NULL ? kUnit.getTerrainDefenseModifier(iI) : pUnit->getExtraTerrainDefensePercent((TerrainTypes)iI);
iTemp *= (100 + iExtra);
iTemp /= 100;
*/
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (eUnitAI == UNITAI_COUNTER)
{
if (pUnit != NULL && pUnit->plot()->getTerrainType() == (TerrainTypes)iI)
{
iValue += (iTemp / 12);
}
else
{
iValue++;
}
}
else
{
iValue += (iTemp / 50);
}
}
if (kPromotion.getTerrainDoubleMove(iI))
{
if (eUnitAI == UNITAI_EXPLORE)
{
iValue += 50;
}
else if ((eUnitAI == UNITAI_ATTACK) || (eUnitAI == UNITAI_PILLAGE) || (eUnitAI == UNITAI_COUNTER))
{
iValue += 15;
}
else
{
iValue += 1;
}
}
}
for (iI = 0; iI < GC.getNumFeatureInfos(); iI++)
{
iTemp = kPromotion.getFeatureAttackPercent(iI);
if (iTemp != 0)
{
/************************************************************************************************/
/* Afforess Start 07/12/10 */
/* */
/* End Overvalue of Terrain Promotions */
/************************************************************************************************/
/*
iExtra = pUnit == NULL ? kUnit.getFeatureAttackModifier(iI) : pUnit->getExtraFeatureAttackPercent((FeatureTypes)iI);
iTemp *= (100 + iExtra * 2);
iTemp /= 100;
*/
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if ((eUnitAI == UNITAI_ATTACK) ||
(eUnitAI == UNITAI_COUNTER))
{
iValue += (iTemp / 4);
}
else
{
iValue += (iTemp / 16);
}
}
iTemp = kPromotion.getFeatureDefensePercent(iI);
if (iTemp != 0)
{
/************************************************************************************************/
/* Afforess Start 07/12/10 */
/* */
/* End Overvalue of Terrain Promotions */
/************************************************************************************************/
/*
iExtra = pUnit == NULL ? kUnit.getFeatureDefenseModifier(iI) : pUnit->getExtraFeatureDefensePercent((FeatureTypes)iI);
iTemp *= (100 + iExtra * 2);
iTemp /= 100;
*/
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (pUnit != NULL && !pUnit->noDefensiveBonus() || (!kUnit.isNoDefensiveBonus()))
{
if (eUnitAI == UNITAI_COUNTER)
{
if (pUnit != NULL && pUnit->plot()->getFeatureType() == (FeatureTypes)iI)
{
iValue += (iTemp / 12);
}
else
{
iValue++;
}
}
else
{
iValue += (iTemp / 16);
}
}
}
if (kPromotion.getFeatureDoubleMove(iI))
{
if (eUnitAI == UNITAI_EXPLORE)
{
iValue += 50;
}
else if ((eUnitAI == UNITAI_ATTACK) || (eUnitAI == UNITAI_PILLAGE) || (eUnitAI == UNITAI_COUNTER))
{
iValue += 25;
}
else
{
iValue += 1;
}
}
}
int iOtherCombat = 0;
int iSameCombat = 0;
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
if ((UnitCombatTypes)iI == kUnit.getUnitCombatType())
{
iSameCombat += pUnit == NULL ? kUnit.getUnitCombatModifier(iI) : pUnit->unitCombatModifier((UnitCombatTypes)iI);
}
else
{
iOtherCombat += pUnit == NULL ? kUnit.getUnitCombatModifier(iI) : pUnit->unitCombatModifier((UnitCombatTypes)iI);
}
}
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
iTemp = kPromotion.getUnitCombatModifierPercent(iI);
int iCombatWeight = 0;
//Fighting their own kind
if ((UnitCombatTypes)iI == kUnit.getUnitCombatType())
{
if (iSameCombat >= iOtherCombat)
{
iCombatWeight = 70;//"axeman takes formation"
}
else
{
iCombatWeight = 30;
}
}
else
{
//fighting other kinds
if ((pUnit != NULL && pUnit->unitCombatModifier((UnitCombatTypes)iI) > 10) || (pUnit == NULL && kUnit.getUnitCombatModifier(iI) > 10))
{
iCombatWeight = 70;//"spearman takes formation"
}
else
{
iCombatWeight = 30;
}
}
iCombatWeight *= AI_getUnitCombatWeight((UnitCombatTypes)iI);
iCombatWeight /= 100;
if ((eUnitAI == UNITAI_COUNTER) || (eUnitAI == UNITAI_CITY_COUNTER))
{
iValue += (iTemp * iCombatWeight) / 50;
}
else if ((eUnitAI == UNITAI_ATTACK) ||
(eUnitAI == UNITAI_RESERVE))
{
iValue += (iTemp * iCombatWeight) / 100;
}
else
{
iValue += (iTemp * iCombatWeight) / 200;
}
}
for (iI = 0; iI < NUM_DOMAIN_TYPES; iI++)
{
iTemp = kPromotion.getDomainModifierPercent(iI);
if (eUnitAI == UNITAI_COUNTER)
{
iValue += (iTemp * 1);
}
else if ((eUnitAI == UNITAI_ATTACK) ||
(eUnitAI == UNITAI_RESERVE))
{
iValue += (iTemp / 2);
}
else
{
iValue += (iTemp / 8);
}
}
if (kPromotion.getIgnoreTerrainDamage() != NO_TERRAIN)
{
iValue += -GC.getTerrainInfo((TerrainTypes)kPromotion.getIgnoreTerrainDamage()).getHealthPercent();
}
if (kPromotion.isZoneOfControl())
{
iValue += 250;
}
if (pUnit != NULL && iValue > 0)
{
iValue += GC.getGameINLINE().getSorenRandNum(15, "AI Promote");
}
return iValue;
}
TechTypes CvPlayerAI::AI_bestReligiousTech(int iMaxPathLength, TechTypes eIgnoreTech, AdvisorTypes eIgnoreAdvisor) const
{
PROFILE("CvPlayerAI::AI_bestReligiousTech");
int iValue;
int iBestValue = 0;
TechTypes eBestTech = NO_TECH;
int iPathLength;
for (int iI = 0; iI < GC.getNumTechInfos(); iI++)
{
if ((eIgnoreTech == NO_TECH) || (iI != eIgnoreTech))
{
if ((eIgnoreAdvisor == NO_ADVISOR) || (GC.getTechInfo((TechTypes)iI).getAdvisorType() != eIgnoreAdvisor))
{
if (canEverResearch((TechTypes)iI))
{
if (!GET_TEAM(getTeam()).isHasTech((TechTypes)iI))
{
if (GC.getTechInfo((TechTypes)iI).getEra() <= (getCurrentEra()))
{
iPathLength = findPathLength(((TechTypes)iI), false);
if (iPathLength <= iMaxPathLength)
{
iValue = AI_religiousTechValue((TechTypes)iI);
iValue /= std::max(1, iPathLength);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestTech = ((TechTypes)iI);
}
}
}
}
}
}
}
}
return eBestTech;
}
int CvPlayerAI::AI_religiousTechValue(TechTypes eTech) const
{
PROFILE_FUNC();
int iReligionValue = 0;
if (GC.getGameINLINE().countKnownTechNumTeams(eTech) == 0)
{
for (int iJ = 0; iJ < GC.getNumReligionInfos(); iJ++)
{
TechTypes eReligionTech = (TechTypes)GC.getReligionInfo((ReligionTypes)iJ).getTechPrereq();
if (eReligionTech == eTech)
{
if (!(GC.getGameINLINE().isReligionSlotTaken((ReligionTypes)iJ)))
{
if (!GC.getGame().isOption(GAMEOPTION_PICK_RELIGION))
{
ReligionTypes eFavorite = (ReligionTypes)GC.getLeaderHeadInfo(getLeaderType()).getFavoriteReligion();
if (eFavorite != NO_RELIGION)
{
if (iJ == eFavorite)
{
iReligionValue += 1000;
}
else
{
iReligionValue += 750;
}
}
}
iReligionValue += 250;
}
}
}
if (iReligionValue > 0)
{
if (AI_isDoVictoryStrategy(AI_VICTORY_CULTURE1))
{
iReligionValue += 500;
}
}
}
return iReligionValue;
}
void CvPlayerAI::AI_doMilitaryProductionCity()
{
PROFILE_FUNC();
CvCity* pLoopCity;
int iLoop;
//invalidate cache
m_iMilitaryProductionCityCount = -1;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
pLoopCity->AI_setMilitaryProductionCity(false);
}
if (getNumCities() < 4)
{
return;
}
int iNumMilitaryProdCitiesNeeded = getNumCities() / 4;
bool bPlanningWar = GET_TEAM(getTeam()).getAnyWarPlanCount(true) > 0 && GET_TEAM(getTeam()).getAtWarCount(true, false) == 0;
bool bAtWar = GET_TEAM(getTeam()).getAtWarCount(true, false) > 0;
bool bFinancialTrouble = AI_isFinancialTrouble();
FAssertMsg(!(bAtWar && bPlanningWar), "Should either be planning war, or at war, but not both at the same time");
if (bPlanningWar)
{
iNumMilitaryProdCitiesNeeded += getNumCities() / 8;
}
if (bAtWar)
{
iNumMilitaryProdCitiesNeeded += getNumCities() / 4;
}
if (bFinancialTrouble)
{
iNumMilitaryProdCitiesNeeded = std::max(1, iNumMilitaryProdCitiesNeeded / 2);
}
for (int iPass = iNumMilitaryProdCitiesNeeded; iPass > 0; iPass--)
{
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (pLoopCity->AI_getMilitaryProductionRateRank() == iPass)
{
pLoopCity->AI_setMilitaryProductionCity(true);
break;
}
}
}
}
int CvPlayerAI::AI_getMilitaryProductionCityCount() const
{
if (m_iMilitaryProductionCityCount != -1)
{
return m_iMilitaryProductionCityCount;
}
int iCount = 0;
CvCity* pLoopCity;
int iLoop;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (pLoopCity->AI_isMilitaryProductionCity())
iCount++;
}
m_iMilitaryProductionCityCount = iCount;
return iCount;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
int CvPlayerAI::AI_getNumBuildingsNeeded(BuildingTypes eBuilding, bool bCoastal) const
{
// Total needed to have one in every city
std::map<BuildingTypes,int>::const_iterator itr = m_numBuildingsNeeded.find(eBuilding);
if ( itr == m_numBuildingsNeeded.end() )
{
CvCity* pLoopCity;
int iLoop;
int result = 0;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if ( (!bCoastal || pLoopCity->isCoastal(GC.getMIN_WATER_SIZE_FOR_OCEAN())) && pLoopCity->getNumBuilding(eBuilding) == 0 )
{
result++;
}
}
{
MEMORY_TRACK_EXEMPT();
m_numBuildingsNeeded[eBuilding] = result;
}
return result;
}
else
{
return itr->second;
}
}
void CvPlayerAI::AI_changeNumBuildingsNeeded(BuildingTypes eBuilding, int iChange)
{
m_numBuildingsNeeded[eBuilding] += iChange;
}
void CvPlayerAI::AI_noteUnitRecalcNeeded(void)
{
bUnitRecalcNeeded = true;
}
void CvPlayerAI::AI_recalculateUnitCounts(void)
{
PROFILE_FUNC();
CvUnit* pLoopUnit;
CvArea* pLoopArea;
int iLoop;
for(int iI = 0; iI < NUM_UNITAI_TYPES; iI++)
{
AI_changeNumAIUnits( (UnitAITypes)iI, -AI_getNumAIUnits((UnitAITypes)iI) );
for(pLoopArea = GC.getMapINLINE().firstArea(&iLoop); pLoopArea != NULL; pLoopArea = GC.getMapINLINE().nextArea(&iLoop))
{
pLoopArea->changeNumAIUnits(m_eID, (UnitAITypes)iI, -pLoopArea->getNumAIUnits(m_eID, (UnitAITypes)iI));
}
}
for(pLoopUnit = firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = nextUnit(&iLoop))
{
UnitAITypes eAIType = pLoopUnit->AI_getUnitAIType();
if ( NO_UNITAI != eAIType )
{
AI_changeNumAIUnits( eAIType, 1 );
pLoopUnit->area()->changeNumAIUnits(m_eID, eAIType, 1);
}
}
bUnitRecalcNeeded = false;
}
int CvPlayerAI::AI_calculateAverageLocalInstability() const
{
CvCity* pLoopCity;
int iLoop;
int result = 0;
int iNum = 0;
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
// Count big cities more than small ones to some exent as a revolution there hurts more
int iWeight = (pLoopCity->getPopulation() < 6 ? 1 : 2);
result += iWeight*pLoopCity->getLocalRevIndex();
iNum += iWeight;
}
return (iNum == 0 ? 0 : result/iNum);
}
int CvPlayerAI::AI_calculateAverageCityDistance() const
{
CvCity* pLoopCity;
int iLoop;
int result = 0;
int iNum = 0;
CvCity* pCapital = getCapitalCity();
if ( pCapital != NULL )
{
for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if (pLoopCity != pCapital)
{
int iCapitalDistance = ::plotDistance(pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE(), pCapital->getX_INLINE(), pCapital->getY_INLINE());
// Count big cities more than small ones to some exent as a revolution there hurts more
int iWeight = (pLoopCity->getPopulation() < 6 ? 1 : 2);
result += iWeight*iCapitalDistance;
iNum += iWeight;
}
}
}
return (iNum == 0 ? 0 : result/iNum);
}
void CvPlayerAI::AI_noteWarStatusChange(TeamTypes eTeam, bool bAtWar)
{
// Cancel any existing cached beeline tech target if war status changes
m_eBestResearchTarget = NO_TECH;
}
int CvPlayerAI::AI_tradeWarReparationsVal(PlayerTypes ePlayer) const
{
int iValue = GC.getGameINLINE().getElapsedGameTurns() * 3;
iValue += AI_getMemoryCount(ePlayer, MEMORY_DECLARED_WAR) * 1000;
iValue += AI_getMemoryCount(ePlayer, MEMORY_HIRED_WAR_ALLY) * 500;
iValue += AI_getMemoryCount(ePlayer, MEMORY_NUKED_US) * 2000;
iValue += AI_getMemoryCount(ePlayer, MEMORY_RAZED_CITY) * 2000;
iValue += AI_getMemoryCount(ePlayer, MEMORY_RAZED_HOLY_CITY) * 5000;
iValue += AI_getMemoryCount(ePlayer, MEMORY_BACKSTAB) * 2500;
return iValue;
}
int CvPlayerAI::AI_tradeCeaseRelationsVal(PlayerTypes ePlayer, PlayerTypes eRequestFrom) const
{
int iValue = AI_stopTradingTradeVal(GET_PLAYER(ePlayer).getTeam(), eRequestFrom);
iValue *= 10;
int iWarMongererDivisor = GC.getDefineINT("WARMONGERER_INDEX_ATTITUDE_DIVISOR", 500);
if (iWarMongererDivisor > 0)
{
iValue /= (1 + std::min(5, GET_PLAYER(ePlayer).getWarMongererIndex() / iWarMongererDivisor));
}
return iValue;
}
int CvPlayerAI::AI_getWeakestRelationsAttitudeChange(PlayerTypes ePlayer) const
{
if (!GET_TEAM(getTeam()).isAtWar(GET_PLAYER(ePlayer).getTeam()) && GC.getGameINLINE().isInWeakestPlayerThreshold(getID()))
{
int iAttitude = 0;
for (int iCivicOption = 0; iCivicOption < GC.getNumCivicOptionInfos(); iCivicOption++)
{
CivicTypes eCivic = GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)iCivicOption);
if (eCivic != NO_CIVIC)
{
iAttitude += GC.getCivicInfo(eCivic).getWeakestRelationsChange();
}
}
return iAttitude;
}
return 0;
}
int CvPlayerAI::AI_getPowerfulRelationsAttitudeChange(PlayerTypes ePlayer) const
{
if (!GET_TEAM(getTeam()).isAtWar(GET_PLAYER(ePlayer).getTeam()) && GC.getGameINLINE().isInStrongestPlayerThreshold(getID()))
{
int iAttitude = 0;
for (int iCivicOption = 0; iCivicOption < GC.getNumCivicOptionInfos(); iCivicOption++)
{
CivicTypes eCivic = GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)iCivicOption);
if (eCivic != NO_CIVIC)
{
iAttitude += GC.getCivicInfo(eCivic).getPowerfulRelationsChange();
}
}
return iAttitude;
}
return 0;
}
int CvPlayerAI::AI_getLessPowerfulRelationsAttitudeChange(PlayerTypes ePlayer) const
{
if (GET_TEAM(getTeam()).getPower(true) < GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPower(true))
{
int iAttitude = 0;
for (int iCivicOption = 0; iCivicOption < GC.getNumCivicOptionInfos(); iCivicOption++)
{
CivicTypes eCivic = GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)iCivicOption);
if (eCivic != NO_CIVIC)
{
iAttitude += GC.getCivicInfo(eCivic).getLessPowerfulRelationsChange();
}
}
return iAttitude;
}
return 0;
}
int CvPlayerAI::AI_getCivicRelationsAttitudeChange() const
{
int iAttitude = 0;
for (int iCivicOption = 0; iCivicOption < GC.getNumCivicOptionInfos(); iCivicOption++)
{
CivicTypes eCivic = getCivics((CivicOptionTypes)iCivicOption);
if (eCivic != NO_CIVIC)
{
iAttitude += GC.getCivicInfo(eCivic).getGlobalRelationsChange();
}
}
return iAttitude;
}
int CvPlayerAI::AI_getNumNeededSpies(CvArea* pArea, bool bDefensiveOnly) const
{
if (GC.getGameINLINE().isOption(GAMEOPTION_NO_ESPIONAGE))
{
return 0;
}
if (m_eEspionagePlan != NO_ESPIONAGE_PLAN)
{
int iDefenseSpiesNeeded = 0;
int iMinValue = GC.getDefineINT("MIN_CITY_ESPIONAGE_VALUE_FOR_DEFENSIVE_SPY", 150);
int iLoop;
for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
{
if ((pArea == NULL || pLoopCity->area() == pArea) && ((CvCityAI*)pLoopCity)->AI_getDefensiveSpyValue() >= iMinValue)
{
iDefenseSpiesNeeded++;
}
}
int iOffensiveSpiesNeeded = 0;
if (!bDefensiveOnly)
{
switch (m_eEspionagePlan)
{
case NO_ESPIONAGE_PLAN: break;
case ESPIONAGE_TECHNOLOGY: break;
case ESPIONAGE_DEFENSIVE: break;
case ESPIONAGE_PREPARE_OFFENSIVE:
case ESPIONAGE_DISRUPTIVE:
case ESPIONAGE_FINANCIAL:
{
iOffensiveSpiesNeeded = GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities();
break;
}
case ESPIONAGE_OFFENSIVE:
{
iOffensiveSpiesNeeded = GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities() * 3;
break;
}
default: break;
}
}
return iDefenseSpiesNeeded + iOffensiveSpiesNeeded;
}
return 0;
}
#define ESPIONAGE_PLAN_NAME(ePlan) \
(ePlan == NO_ESPIONAGE_PLAN ? "NO_ESPIONAGE_PLAN" : (ePlan == ESPIONAGE_DEFENSIVE ? "ESPIONAGE_DEFENSIVE" : (ePlan == ESPIONAGE_DISRUPTIVE ? "ESPIONAGE_DISRUPTIVE" : (ePlan == ESPIONAGE_OFFENSIVE ? "ESPIONAGE_OFFENSIVE" : (ePlan == ESPIONAGE_PREPARE_OFFENSIVE ? "ESPIONAGE_PREPARE_OFFENSIVE" : "ESPIONAGE_FINANCIAL")))))
void CvPlayerAI::AI_updateEspionagePlan()
{
if (GC.getGameINLINE().isOption(GAMEOPTION_NO_ESPIONAGE))
{
return;
}
bool bMetAnyone = false;
for (int iI = 0; iI < MAX_TEAMS; iI++)
{
if (iI != getTeam() && GET_TEAM(getTeam()).isHasMet((TeamTypes)iI))
{
bMetAnyone = true;
break;
}
}
if (!bMetAnyone)
{
m_eEspionagePlan = NO_ESPIONAGE_PLAN;
return;
}
EspionagePlans ePrevious = m_eEspionagePlan;
if (AI_isFinancialTrouble())
{
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (iI != getID() && GET_PLAYER((PlayerTypes)iI).isAlive() && GET_TEAM(getTeam()).isHasMet(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
if (GET_PLAYER((PlayerTypes)iI).getGold() > (250 + (100 * GET_PLAYER((PlayerTypes)iI).getNumCities())))
{
m_eEspionagePlan = ESPIONAGE_FINANCIAL;
if (gPlayerLogLevel > 2 && ePrevious != m_eEspionagePlan)
{
logBBAI(" %S switches from espionage plan %s to %s due to financial trouble", getCivilizationDescription(0), ESPIONAGE_PLAN_NAME(ePrevious), ESPIONAGE_PLAN_NAME(m_eEspionagePlan));
}
return;
}
}
}
m_eEspionagePlan = ESPIONAGE_DEFENSIVE;
if (gPlayerLogLevel > 2 && ePrevious != m_eEspionagePlan)
{
logBBAI(" %S switches from espionage plan %s to %s due to financial trouble", getCivilizationDescription(0), ESPIONAGE_PLAN_NAME(ePrevious), ESPIONAGE_PLAN_NAME(m_eEspionagePlan));
}
return;
}
bool bAtWarWithAny = false;
for (int iI = 0; iI < MAX_TEAMS; iI++)
{
if (iI != getTeam() && GET_TEAM(getTeam()).isAtWar((TeamTypes)iI))
{
bAtWarWithAny = true;
int iTheirSuccess = std::max(10, GET_TEAM((TeamTypes)iI).AI_getWarSuccess(getTeam()));
int iOurSuccess = std::max(10, GET_TEAM(getTeam()).AI_getWarSuccess((TeamTypes)iI));
if (iTheirSuccess * 2 < iOurSuccess * 3)
{
m_eEspionagePlan = ESPIONAGE_OFFENSIVE;
if (gPlayerLogLevel > 2 && ePrevious != m_eEspionagePlan)
{
logBBAI(" %S switches from espionage plan %s to %s due to war success", getCivilizationDescription(0), ESPIONAGE_PLAN_NAME(ePrevious), ESPIONAGE_PLAN_NAME(m_eEspionagePlan));
}
return;
}
}
}
if (bAtWarWithAny)
{
m_eEspionagePlan = ESPIONAGE_DEFENSIVE;
if (gPlayerLogLevel > 2 && ePrevious != m_eEspionagePlan)
{
logBBAI(" %S switches from espionage plan %s to %s due to lack of war success", getCivilizationDescription(0), ESPIONAGE_PLAN_NAME(ePrevious), ESPIONAGE_PLAN_NAME(m_eEspionagePlan));
}
return;
}
for (int iI = 0; iI < MAX_TEAMS; iI++)
{
if (iI != getTeam() && GET_TEAM(getTeam()).AI_getWarPlan((TeamTypes)iI) != NO_WARPLAN)
{
m_eEspionagePlan = ESPIONAGE_PREPARE_OFFENSIVE;
if (gPlayerLogLevel > 2 && ePrevious != m_eEspionagePlan)
{
logBBAI(" %S switches from espionage plan %s to %s due to warplans", getCivilizationDescription(0), ESPIONAGE_PLAN_NAME(ePrevious), ESPIONAGE_PLAN_NAME(m_eEspionagePlan));
}
return;
}
}
if (GC.getLeaderHeadInfo(getLeaderType()).getWarmongerRespect() >= 2 || GC.getGameINLINE().isOption(GAMEOPTION_RUTHLESS_AI))
{
m_eEspionagePlan = ESPIONAGE_DISRUPTIVE;
if (gPlayerLogLevel > 2 && ePrevious != m_eEspionagePlan)
{
logBBAI(" %S switches from espionage plan %s to %s due to warmongering", getCivilizationDescription(0), ESPIONAGE_PLAN_NAME(ePrevious), ESPIONAGE_PLAN_NAME(m_eEspionagePlan));
}
}
else
{
m_eEspionagePlan = ESPIONAGE_DEFENSIVE;
if (gPlayerLogLevel > 2 && ePrevious != m_eEspionagePlan)
{
logBBAI(" %S switches from espionage plan %s to %s - no plans", getCivilizationDescription(0), ESPIONAGE_PLAN_NAME(ePrevious), ESPIONAGE_PLAN_NAME(m_eEspionagePlan));
}
}
}
EspionagePlans CvPlayerAI::AI_getEspionagePlan()
{
return m_eEspionagePlan;
}