Rise of Mankind: A New Dawn Code
Brought to you by:
afforess,
n4538-e1347
// city.cpp
#include "CvGameCoreDLL.h"
#include "CvGlobals.h"
#include "CvCity.h"
#include "CvArea.h"
#include "CvGameAI.h"
#include "CvMap.h"
#include "CvViewport.h"
#include "CvPlot.h"
#include "CvTeamAI.h"
#include "CvGameCoreUtils.h"
#include "CvPlayerAI.h"
#include "CvUnit.h"
#include "CvInfos.h"
#include "CvRandom.h"
#include "CvArtFileMgr.h"
#include "CvPopupInfo.h"
#include "CyCity.h"
#include "CyArgsList.h"
#include "FProfiler.h"
#include "CvGameTextMgr.h"
#include "CvReachablePlotSet.h"
// interfaces used
#include "CvDLLEngineIFaceBase.h"
#include "CvDLLPythonIFaceBase.h"
#include "CvDLLEntityIFaceBase.h"
#include "CvDLLInterfaceIFaceBase.h"
#include "CvEventReporter.h"
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 10/02/09 jdog5000 */
/* */
/* AI logging */
/************************************************************************************************/
#include "BetterBTSAI.h"
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
#include <boost/bind.hpp>
#include "FDataStreamBuffer.h"
//Disable this passed in initialization list warning, as it is only stored in the constructor of CvBuildingList and not used
#pragma warning( disable : 4355 )
//bool CvCityTurnPipelineWorkItem::m_bHasProcessedDeferalOfVoteSources = false;
//bool CvCityTurnPipelineWorkItem::m_bHasProcessedUndeferalOfVoteSources = false;
static CRITICAL_SECTION g_cPropertyManipulationSection;
static bool bStaticsInitialized = false;
// Public Functions...
CvCity::CvCity()
: m_GameObject(this),
m_BuildingList(NULL, this),
m_UnitList(NULL, this),
m_Properties(this)
{
if ( !bStaticsInitialized )
{
InitializeCriticalSectionAndSpinCount(&g_cPropertyManipulationSection, 4000);
bStaticsInitialized = true;
}
InitializeCriticalSectionAndSpinCount(&m_cCanConstructCacheSection,2000);
InitializeCriticalSectionAndSpinCount(&m_cUpgradeCacheSection,4000);
InitializeCriticalSectionAndSpinCount(&m_cCanTrainCacheSection,2000);
InitializeCriticalSectionAndSpinCount(&m_cBuildingCommerceChangeSection,4000);
m_aiSeaPlotYield = new int[NUM_YIELD_TYPES];
m_aiRiverPlotYield = new int[NUM_YIELD_TYPES];
m_aiBaseYieldRate = new int[NUM_YIELD_TYPES];
m_aiYieldRateModifier = new int[NUM_YIELD_TYPES];
m_aiPowerYieldRateModifier = new int[NUM_YIELD_TYPES];
m_aiBonusYieldRateModifier = new int[NUM_YIELD_TYPES];
m_aiTradeYield = new int[NUM_YIELD_TYPES];
m_aiCorporationYield = new int[NUM_YIELD_TYPES];
m_aiExtraSpecialistYield = new int[NUM_YIELD_TYPES];
m_aiCommerceRate = new int[NUM_COMMERCE_TYPES];
m_abCommerceRateDirty = new bool[NUM_COMMERCE_TYPES];
m_aiProductionToCommerceModifier = new int[NUM_COMMERCE_TYPES];
m_aiBuildingCommerce = new int[NUM_COMMERCE_TYPES];
m_aiSpecialistCommerce = new int[NUM_COMMERCE_TYPES];
m_aiReligionCommerce = new int[NUM_COMMERCE_TYPES];
m_aiCorporationCommerce = new int[NUM_COMMERCE_TYPES];
m_aiCommerceRateModifier = new int[NUM_COMMERCE_TYPES];
m_aiCommerceHappinessPer = new int[NUM_COMMERCE_TYPES];
m_aiDomainFreeExperience = new int[NUM_DOMAIN_TYPES];
m_aiDomainProductionModifier = new int[NUM_DOMAIN_TYPES];
m_aiCulture = new int[MAX_PLAYERS];
m_aiNumRevolts = new int[MAX_PLAYERS];
m_abEverOwned = new bool[MAX_PLAYERS];
m_abTradeRoute = new bool[MAX_PLAYERS];
m_abRevealed = new bool[MAX_TEAMS];
m_abEspionageVisibility = new bool[MAX_TEAMS];
m_paiNoBonus = NULL;
m_paiFreeBonus = NULL;
m_paiNumBonuses = NULL;
m_paiNumCorpProducedBonuses = NULL;
m_paiProjectProduction = NULL;
m_paiBuildingProduction = NULL;
m_paiBuildingProductionTime = NULL;
m_paiBuildingOriginalOwner = NULL;
m_paiBuildingOriginalTime = NULL;
m_paiUnitProduction = NULL;
m_paiUnitProductionTime = NULL;
m_paiGreatPeopleUnitRate = NULL;
m_paiGreatPeopleUnitProgress = NULL;
m_paiSpecialistCount = NULL;
m_paiMaxSpecialistCount = NULL;
m_paiForceSpecialistCount = NULL;
m_paiFreeSpecialistCount = NULL;
m_paiFreeSpecialistCountUnattributed = NULL;
m_paiImprovementFreeSpecialists = NULL;
m_paiReligionInfluence = NULL;
m_paiStateReligionHappiness = NULL;
m_paiUnitCombatFreeExperience = NULL;
m_paiFreePromotionCount = NULL;
m_paiNumRealBuilding = NULL;
m_paiNumFreeBuilding = NULL;
m_paiNumFreeAreaBuilding = NULL;
m_paiNumFreeTradeRegionBuilding = NULL;
m_paiBuildingReplaced = NULL;
m_bHasCalculatedBuildingReplacement = FALSE;
m_pabWorkingPlot = NULL;
m_pabHasReligion = NULL;
m_pabHasCorporation = NULL;
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
m_paiUnitClassProductionModifier = NULL;
m_aiBonusCommerceRateModifier = new int[NUM_COMMERCE_TYPES];
m_aiBonusCommercePercentChanges = new int[NUM_COMMERCE_TYPES];
m_aiCommerceAttacks = new int[NUM_COMMERCE_TYPES];
m_aiMaxCommerceAttacks = new int[NUM_COMMERCE_TYPES];
m_paiBuildingClassProductionModifier = NULL;
m_paiBonusDefenseChanges = NULL;
m_pabHadVicinityBonus = NULL;
m_pabHasVicinityBonus = NULL;
m_pabHasVicinityBonusCached = NULL;
m_pabDisabledBuilding = NULL;
m_paiUnitCombatExtraStrength = NULL;
m_pabAutomatedCanBuild = NULL;
m_aiSeizedForeignConnectedness = new int[MAX_PLAYERS];
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
m_paTradeCities = NULL;
#ifdef CAN_TRAIN_CACHING
// KOSHLING - clear canTrain cache validity
m_canTrainCachePopulated = false;
#endif
m_bCanConstruct = NULL;
m_workItem = NULL;
//CvDLLEntity::createCityEntity(this); // create and attach entity to city
m_aiBaseYieldRank = new int[NUM_YIELD_TYPES];
m_abBaseYieldRankValid = new bool[NUM_YIELD_TYPES];
m_aiYieldRank = new int[NUM_YIELD_TYPES];
m_abYieldRankValid = new bool[NUM_YIELD_TYPES];
m_aiCommerceRank = new int[NUM_COMMERCE_TYPES];
m_abCommerceRankValid = new bool[NUM_COMMERCE_TYPES];
m_deferringBonusProcessingCount = 0;
m_paiStartDeferredSectionNumBonuses = NULL;
m_bFreeTradeBuildingProcessingDeferred = false;
m_paiDeferredNumFreeTradeRegionBuilding = NULL;
reset(0, NO_PLAYER, 0, 0, true);
}
CvCity::~CvCity()
{
if ( getEntity() != NULL )
{
CvDLLEntity::removeEntity(); // remove entity from engine
CvDLLEntity::destroyEntity(); // delete CvCityEntity and detach from us
}
uninit();
SAFE_DELETE_ARRAY(m_aiBaseYieldRank);
SAFE_DELETE_ARRAY(m_abBaseYieldRankValid);
SAFE_DELETE_ARRAY(m_aiYieldRank);
SAFE_DELETE_ARRAY(m_abYieldRankValid);
SAFE_DELETE_ARRAY(m_aiCommerceRank);
SAFE_DELETE_ARRAY(m_abCommerceRankValid);
SAFE_DELETE_ARRAY(m_aiSeaPlotYield);
SAFE_DELETE_ARRAY(m_aiRiverPlotYield);
SAFE_DELETE_ARRAY(m_aiBaseYieldRate);
SAFE_DELETE_ARRAY(m_aiYieldRateModifier);
SAFE_DELETE_ARRAY(m_aiPowerYieldRateModifier);
SAFE_DELETE_ARRAY(m_aiBonusYieldRateModifier);
SAFE_DELETE_ARRAY(m_aiTradeYield);
SAFE_DELETE_ARRAY(m_aiCorporationYield);
SAFE_DELETE_ARRAY(m_aiExtraSpecialistYield);
SAFE_DELETE_ARRAY(m_aiCommerceRate);
SAFE_DELETE_ARRAY(m_abCommerceRateDirty);
SAFE_DELETE_ARRAY(m_aiProductionToCommerceModifier);
SAFE_DELETE_ARRAY(m_aiBuildingCommerce);
SAFE_DELETE_ARRAY(m_aiSpecialistCommerce);
SAFE_DELETE_ARRAY(m_aiReligionCommerce);
SAFE_DELETE_ARRAY(m_aiCorporationCommerce);
SAFE_DELETE_ARRAY(m_aiCommerceRateModifier);
SAFE_DELETE_ARRAY(m_aiCommerceHappinessPer);
SAFE_DELETE_ARRAY(m_aiDomainFreeExperience);
SAFE_DELETE_ARRAY(m_aiDomainProductionModifier);
SAFE_DELETE_ARRAY(m_aiCulture);
SAFE_DELETE_ARRAY(m_aiNumRevolts);
SAFE_DELETE_ARRAY(m_abEverOwned);
SAFE_DELETE_ARRAY(m_abTradeRoute);
SAFE_DELETE_ARRAY(m_abRevealed);
SAFE_DELETE_ARRAY(m_abEspionageVisibility);
/************************************************************************************************/
/* Afforess Start 05/19/10 */
/* */
/* */
/************************************************************************************************/
SAFE_DELETE_ARRAY(m_aiBonusCommerceRateModifier);
SAFE_DELETE_ARRAY(m_aiBonusCommercePercentChanges);
SAFE_DELETE_ARRAY(m_aiCommerceAttacks);
SAFE_DELETE_ARRAY(m_aiMaxCommerceAttacks);
SAFE_DELETE_ARRAY(m_aiSeizedForeignConnectedness);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
DeleteCriticalSection(&m_cCanConstructCacheSection);
DeleteCriticalSection(&m_cCanTrainCacheSection);
DeleteCriticalSection(&m_cUpgradeCacheSection);
DeleteCriticalSection(&m_cBuildingCommerceChangeSection);
}
void CvCity::init(int iID, PlayerTypes eOwner, int iX, int iY, bool bBumpUnits, bool bUpdatePlotGroups)
{
PROFILE_FUNC();
CvPlot* pAdjacentPlot;
CvPlot* pPlot;
BuildingTypes eLoopBuilding;
int iI;
pPlot = GC.getMapINLINE().plotINLINE(iX, iY);
//--------------------------------
// Log this event
if (GC.getLogging())
{
if (gDLL->getChtLvl() > 0)
{
TCHAR szOut[1024];
sprintf(szOut, "Player %d City %d built at %d:%d\n", eOwner, iID, iX, iY);
gDLL->messageControlLog(szOut);
}
}
//--------------------------------
// Init saved data
reset(iID, eOwner, pPlot->getX_INLINE(), pPlot->getY_INLINE());
//--------------------------------
// Init non-saved data
setupGraphical();
//--------------------------------
// Init other game data
/************************************************************************************************/
/* Afforess Start 04/13/10 */
/* */
/* */
/************************************************************************************************/
bool bFound = false;
if (GC.getGameINLINE().isOption(GAMEOPTION_PERSONALIZED_MAP) && GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_USE_LANDMARK_NAMES))
{
for (int iI = 0; iI < NUM_CITY_PLOTS_2; iI++)
{
CvPlot* pLoopPlot = getCityIndexPlot(iI);
if (!pLoopPlot->getLandmarkName().empty() && pLoopPlot->getLandmarkType() != NO_LANDMARK)
{
setName(pLoopPlot->getLandmarkName());
if (!getName().empty())
{
bFound = true;
break;
}
}
}
}
if (!bFound)
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
setName(GET_PLAYER(getOwnerINLINE()).getNewCityName());
setEverOwned(getOwnerINLINE(), true);
pPlot->setImprovementType(NO_IMPROVEMENT);
pPlot->setOwner(getOwnerINLINE(), bBumpUnits, false);
pPlot->setPlotCity(this);
updateCultureLevel(false);
if (pPlot->getCulture(getOwnerINLINE()) < GC.getDefineINT("FREE_CITY_CULTURE"))
{
pPlot->setCulture(getOwnerINLINE(), GC.getDefineINT("FREE_CITY_CULTURE"), bBumpUnits, false);
}
for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
pAdjacentPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));
if (pAdjacentPlot != NULL)
{
if (pAdjacentPlot->getCulture(getOwnerINLINE()) < GC.getDefineINT("FREE_CITY_ADJACENT_CULTURE"))
{
pAdjacentPlot->setCulture(getOwnerINLINE(), GC.getDefineINT("FREE_CITY_ADJACENT_CULTURE"), bBumpUnits, false);
}
pAdjacentPlot->updateCulture(bBumpUnits, false);
}
}
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (GET_TEAM(getTeam()).isVassal((TeamTypes)iI))
{
pPlot->changeAdjacentSight((TeamTypes)iI, GC.getDefineINT("PLOT_VISIBILITY_RANGE"), true, NULL, false);
}
}
long lResult=0;
{
PYTHON_ACCESS_LOCK_SCOPE
CyArgsList argsList;
argsList.add(iX);
argsList.add(iY);
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "citiesDestroyFeatures", argsList.makeFunctionArgs(), &lResult);
}
if (lResult == 1)
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH 10/30/09 Mongoose & jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* original bts code
if (pPlot->getFeatureType() != NO_FEATURE)
*/
// From Mongoose SDK
// Don't remove floodplains from tiles when founding city
if ((pPlot->getFeatureType() != NO_FEATURE) && (pPlot->getFeatureType() != (FeatureTypes)GC.getInfoTypeForString("FEATURE_FLOOD_PLAINS")))
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
{
/************************************************************************************************/
/* Afforess Start 02/09/10 */
/* */
/* Extra Hammer from Settling on Forest, Extra Food from Settling on Jungle */
/************************************************************************************************/
int iProduction;
BuildTypes eChopBuild = findChopBuild(pPlot->getFeatureType());
if (eChopBuild > -1 && eChopBuild < GC.getNumBuildInfos())
{
if (GC.getInfoTypeForString("FEATURE_FOREST", true) > 0)
{
if ((pPlot->getFeatureType() == (FeatureTypes)GC.getInfoTypeForString("FEATURE_FOREST")) && (GET_TEAM(getTeam()).isHasTech((TechTypes)GC.getBuildInfo(eChopBuild).getTechPrereq())))
{
iProduction = GC.getBuildInfo(findChopBuild(pPlot->getFeatureType())).getFeatureProduction(pPlot->getFeatureType());
iProduction *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getFeatureProductionPercent();
iProduction /= 100;
setExtraYieldTurns(iProduction);
}
}
if (GC.getInfoTypeForString("FEATURE_JUNGLE", true) > 0)
{
if ((pPlot->getFeatureType() == (FeatureTypes)GC.getInfoTypeForString("FEATURE_JUNGLE")) && (GET_TEAM(getTeam()).isHasTech((TechTypes)GC.getBuildInfo(eChopBuild).getTechPrereq())))
{
iProduction = GC.getBuildInfo(findChopBuild(pPlot->getFeatureType())).getFeatureProduction(pPlot->getFeatureType());
iProduction *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getFeatureProductionPercent();
iProduction /= 100;
setExtraYieldTurns(-iProduction);
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
#ifdef MULTI_FEATURE_MOD
pPlot->removeAllFeatures();
#else
pPlot->setFeatureType(NO_FEATURE);
#endif
}
}
pPlot->updateCityRoute(false);
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (GET_TEAM((TeamTypes)iI).isAlive())
{
if (pPlot->isVisible(((TeamTypes)iI), false))
{
setRevealed(((TeamTypes)iI), true);
}
}
}
changeMilitaryHappinessUnits(pPlot->plotCount(PUF_isMilitaryHappiness));
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
changeCommerceHappinessPer(((CommerceTypes)iI), GC.getCommerceInfo((CommerceTypes)iI).getInitialHappiness());
}
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (GET_PLAYER(getOwnerINLINE()).isBuildingFree((BuildingTypes)iI))
{
setNumFreeBuilding(((BuildingTypes)iI), 1);
}
else if (GET_PLAYER(getOwnerINLINE()).isBuildingFree((BuildingTypes)iI, area()))
{
setNumFreeAreaBuilding(((BuildingTypes)iI), 1);
}
}
area()->changeCitiesPerPlayer(getOwnerINLINE(), 1);
GET_TEAM(getTeam()).changeNumCities(1);
GC.getGameINLINE().changeNumCities(1);
setGameTurnFounded(GC.getGameINLINE().getGameTurn());
setGameTurnAcquired(GC.getGameINLINE().getGameTurn());
changePopulation(GC.getDefineINT("INITIAL_CITY_POPULATION") + GC.getEraInfo(GC.getGameINLINE().getStartEra()).getFreePopulation());
changeAirUnitCapacity(GC.getDefineINT("CITY_AIR_UNIT_CAPACITY"));
updateFreshWaterHealth();
updateFeatureHealth();
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/19/09 */
/* */
/* */
/************************************************************************************************/
updateImprovementHealth();
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
updateFeatureHappiness();
updatePowerHealth();
GET_PLAYER(getOwnerINLINE()).setMaintenanceDirty(true);
GC.getMapINLINE().updateWorkingCity();
GC.getGameINLINE().AI_makeAssignWorkDirty();
GET_PLAYER(getOwnerINLINE()).setFoundedFirstCity(true);
if (GC.getGameINLINE().isFinalInitialized())
{
if (GET_PLAYER(getOwnerINLINE()).getNumCities() == 1)
{
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
if (GC.getCivilizationInfo(getCivilizationType()).isCivilizationFreeBuildingClass(iI))
{
eLoopBuilding = ((BuildingTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iI)));
if (eLoopBuilding != NO_BUILDING)
{
setNumRealBuilding(eLoopBuilding, 1);
}
}
}
if (!isHuman())
{
changeOverflowProduction(GC.getDefineINT("INITIAL_AI_CITY_PRODUCTION"), 0);
}
}
}
updateEspionageVisibility(false);
if (bUpdatePlotGroups)
{
GC.getGameINLINE().updatePlotGroups();
}
/************************************************************************************************/
/* Afforess Start 01/12/10 */
/* */
/* Assimilation */
/************************************************************************************************/
// Koshliong - do this unconditionally - it dopesn; nmatter if assimilation is off because in
// that case we're setting the value it would have anyway and on any change of ownership
// acquireCity() is called which initializes a new CvCity instance anyway
setCivilizationType(GET_PLAYER(getOwnerINLINE()).getCivilizationType());
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
m_UnitList.init();
AI_init();
}
void CvCity::uninit()
{
SAFE_DELETE_ARRAY(m_paiNoBonus);
SAFE_DELETE_ARRAY(m_paiFreeBonus);
SAFE_DELETE_ARRAY(m_paiNumBonuses);
SAFE_DELETE_ARRAY(m_paiNumCorpProducedBonuses);
SAFE_DELETE_ARRAY(m_paiProjectProduction);
SAFE_DELETE_ARRAY(m_paiBuildingProduction);
SAFE_DELETE_ARRAY(m_paiBuildingProductionTime);
SAFE_DELETE_ARRAY(m_paiBuildingOriginalOwner);
SAFE_DELETE_ARRAY(m_paiBuildingOriginalTime);
SAFE_DELETE_ARRAY(m_paiUnitProduction);
SAFE_DELETE_ARRAY(m_paiUnitProductionTime);
SAFE_DELETE_ARRAY(m_paiGreatPeopleUnitRate);
SAFE_DELETE_ARRAY(m_paiGreatPeopleUnitProgress);
SAFE_DELETE_ARRAY(m_paiSpecialistCount);
SAFE_DELETE_ARRAY(m_paiMaxSpecialistCount);
SAFE_DELETE_ARRAY(m_paiForceSpecialistCount);
SAFE_DELETE_ARRAY(m_paiFreeSpecialistCount);
SAFE_DELETE_ARRAY(m_paiFreeSpecialistCountUnattributed);
SAFE_DELETE_ARRAY(m_paiImprovementFreeSpecialists);
SAFE_DELETE_ARRAY(m_paiReligionInfluence);
SAFE_DELETE_ARRAY(m_paiStateReligionHappiness);
SAFE_DELETE_ARRAY(m_paiUnitCombatFreeExperience);
SAFE_DELETE_ARRAY(m_paiFreePromotionCount);
SAFE_DELETE_ARRAY(m_paiNumRealBuilding);
SAFE_DELETE_ARRAY(m_paiNumFreeBuilding);
SAFE_DELETE_ARRAY(m_paiNumFreeAreaBuilding);
SAFE_DELETE_ARRAY(m_paiNumFreeTradeRegionBuilding);
SAFE_DELETE_ARRAY(m_paiBuildingReplaced);
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
SAFE_DELETE_ARRAY(m_paiUnitClassProductionModifier);
SAFE_DELETE_ARRAY(m_paiBuildingClassProductionModifier);
SAFE_DELETE_ARRAY(m_paiBonusDefenseChanges);
SAFE_DELETE_ARRAY(m_pabHadVicinityBonus);
SAFE_DELETE_ARRAY(m_pabHasVicinityBonus);
SAFE_DELETE_ARRAY(m_pabHasVicinityBonusCached);
SAFE_DELETE_ARRAY(m_pabDisabledBuilding);
SAFE_DELETE_ARRAY(m_paiUnitCombatExtraStrength);
SAFE_DELETE_ARRAY(m_pabAutomatedCanBuild);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
SAFE_DELETE_ARRAY(m_pabWorkingPlot);
SAFE_DELETE_ARRAY(m_pabHasReligion);
SAFE_DELETE_ARRAY(m_pabHasCorporation);
SAFE_DELETE_ARRAY(m_paTradeCities);
SAFE_DELETE_ARRAY(m_paiStartDeferredSectionNumBonuses);
m_orderQueue.clear();
}
// FUNCTION: reset()
// Initializes data members that are serialized.
void CvCity::reset(int iID, PlayerTypes eOwner, int iX, int iY, bool bConstructorCall)
{
int iI;
//--------------------------------
// Uninit class
uninit();
if (!bConstructorCall)
{
for(iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
setCommerceModifierDirty((CommerceTypes)iI);
}
}
m_iID = iID;
m_iX = iX;
m_iY = iY;
m_iRallyX = INVALID_PLOT_COORD;
m_iRallyY = INVALID_PLOT_COORD;
m_iGameTurnFounded = 0;
m_iGameTurnAcquired = 0;
m_iPopulation = 0;
m_iHighestPopulation = 0;
m_iWorkingPopulation = 0;
m_iSpecialistPopulation = 0;
m_iNumGreatPeople = 0;
m_iBaseGreatPeopleRate = 0;
m_iGreatPeopleRateModifier = 0;
m_iGreatPeopleProgress = 0;
m_iNumWorldWonders = 0;
m_iNumTeamWonders = 0;
m_iNumNationalWonders = 0;
m_iNumBuildings = 0;
m_iGovernmentCenterCount = 0;
m_iMaintenance = 0;
m_iMaintenanceModifier = 0;
m_iWarWearinessModifier = 0;
m_iHurryAngerModifier = 0;
m_iHealRate = 0;
m_iEspionageHealthCounter = 0;
m_iEspionageHappinessCounter = 0;
m_iFreshWaterGoodHealth = 0;
m_iFreshWaterBadHealth = 0;
m_iFeatureGoodHealth = 0;
m_iFeatureBadHealth = 0;
m_iBuildingGoodHealth = 0;
m_iBuildingBadHealth = 0;
m_iPowerGoodHealth = 0;
m_iPowerBadHealth = 0;
m_iBonusGoodHealth = 0;
m_iBonusBadHealth = 0;
m_iHurryAngerTimer = 0;
/************************************************************************************************/
/* REVOLUTION_MOD 04/19/08 jdog5000 */
/* */
/* */
/************************************************************************************************/
m_iRevRequestAngerTimer = 0;
m_iRevSuccessTimer = 0;
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
m_iConscriptAngerTimer = 0;
m_iDefyResolutionAngerTimer = 0;
m_iHappinessTimer = 0;
m_iMilitaryHappinessUnits = 0;
m_iBuildingGoodHappiness = 0;
m_iBuildingBadHappiness = 0;
m_iExtraBuildingGoodHappiness = 0;
m_iExtraBuildingBadHappiness = 0;
m_iExtraBuildingGoodHealth = 0;
m_iExtraBuildingBadHealth = 0;
m_iFeatureGoodHappiness = 0;
m_iFeatureBadHappiness = 0;
m_iBonusGoodHappiness = 0;
m_iBonusBadHappiness = 0;
m_iReligionGoodHappiness = 0;
m_iReligionBadHappiness = 0;
m_iExtraHappiness = 0;
m_iExtraHealth = 0;
m_iNoUnhappinessCount = 0;
m_iNoUnhealthyPopulationCount = 0;
m_iBuildingOnlyHealthyCount = 0;
m_iFood = 0;
m_iFoodKept = 0;
m_fMaxFoodKeptMultiplierLog = 0.0;
m_iOverflowProduction = 0;
m_iFeatureProduction = 0;
m_iMilitaryProductionModifier = 0;
m_iSpaceProductionModifier = 0;
m_iExtraTradeRoutes = 0;
m_iTradeRouteModifier = 0;
m_iForeignTradeRouteModifier = 0;
m_iBuildingDefense = 0;
m_iBuildingBombardDefense = 0;
m_iFreeExperience = 0;
m_iCurrAirlift = 0;
m_iMaxAirlift = 0;
m_iAirModifier = 0;
m_iAirUnitCapacity = 0;
m_iWonderCapacityIncrement = 0;
m_iNukeModifier = 0;
m_iFreeSpecialist = 0;
m_iPowerCount = 0;
m_iDirtyPowerCount = 0;
m_iDefenseDamage = 0;
m_iLastDefenseDamage = 0;
m_iOccupationTimer = 0;
m_iCultureUpdateTimer = 0;
m_iCitySizeBoost = 0;
m_iSpecialistFreeExperience = 0;
m_iEspionageDefenseModifier = 0;
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
m_fPopulationgrowthratepercentageLog = 0.0;
m_iImprovementGoodHealth = 0;
m_iImprovementBadHealth = 0;
m_iSpecialistGoodHealth = 0;
m_iSpecialistBadHealth = 0;
m_iSpecialistHappiness = 0;
m_iSpecialistUnhappiness = 0;
m_iCiv = NO_CIVILIZATION;
m_iExtraYieldTurns = 0;
m_eOccupationCultureLevel = NO_CULTURELEVEL;
m_iLineOfSight = 0;
m_iLandmarkAngerTimer = 0;
m_iInvasionChance = 0;
m_iInvasionTimer = 0;
m_iFreshWater = 0;
m_iAdjacentDamagePercent = 0;
m_iLostProduction = 0;
m_iWorkableRadiusOverride = 0;
m_iProtectedCultureCount = 0;
m_iNumUnitFullHeal = 0;
m_iDisabledPowerTimer = 0;
m_iWarWearinessTimer = 0;
m_iEventAnger = 0;
m_iMinimumDefenseLevel = 0;
m_iNumPopulationEmployed = 0;
m_iHealthPercentPerPopulation = 0;
m_iHappinessPercentPerPopulation = 0;
m_iExtraCapitalCommerce = 0;
m_iExtraForeignCapitalCommerce = 0;
m_iPreviousExtraCommerce = 0;
m_iPreviousConnectedCommerce = 0;
m_iPreviousForeignConnectedCommerce = 0;
m_iMADIncoming = 0;
m_iForeignConnectednessNeeded = 0;
m_iZoCCount = 0;
clearSeizedForeignConnectedness();
m_iForcedRevolutionCounter = 0;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
m_bNeverLost = true;
m_bBombarded = false;
m_bDrafted = false;
m_bAirliftTargeted = false;
m_bWeLoveTheKingDay = false;
m_bCitizensAutomated = true;
m_bProductionAutomated = false;
m_bWallOverride = false;
m_bInfoDirty = true;
m_bLayoutDirty = false;
m_bMaintenanceDirty = false;
m_bPlundered = false;
m_recalcBuilding = MAX_INT;
m_bPlotWorkingMasked = false;
m_Properties.clear();
/************************************************************************************************/
/* UNOFFICIAL_PATCH 12/07/09 denev & jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
m_bPopProductionProcess = false;
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
m_eOwner = eOwner;
m_ePreviousOwner = NO_PLAYER;
m_eOriginalOwner = eOwner;
m_eCultureLevel = NO_CULTURELEVEL;
/************************************************************************************************/
/* REVOLUTION_MOD 06/10/08 jdog5000 */
/* */
/* */
/************************************************************************************************/
m_iRevolutionIndex = 0;
m_iLocalRevIndex = -1;
m_iRevIndexAverage = 0;
m_iRevolutionCounter = 0;
m_iReinforcementCounter = 0;
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
m_aiSeaPlotYield[iI] = 0;
m_aiRiverPlotYield[iI] = 0;
m_aiBaseYieldRate[iI] = 0;
m_aiYieldRateModifier[iI] = 0;
m_aiPowerYieldRateModifier[iI] = 0;
m_aiBonusYieldRateModifier[iI] = 0;
m_aiTradeYield[iI] = 0;
m_aiCorporationYield[iI] = 0;
m_aiExtraSpecialistYield[iI] = 0;
m_cachedBuildingYieldModifers[iI] = 0;
}
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
m_aiCommerceRate[iI] = 0;
m_abCommerceRateDirty[iI] = false;
m_aiProductionToCommerceModifier[iI] = 0;
m_aiBuildingCommerce[iI] = 0;
m_aiSpecialistCommerce[iI] = 0;
m_aiReligionCommerce[iI] = 0;
m_aiCorporationCommerce[iI] = 0;
m_aiCommerceRateModifier[iI] = 0;
m_aiCommerceHappinessPer[iI] = 0;
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
m_aiBonusCommerceRateModifier[iI] = 0;
m_aiBonusCommercePercentChanges[iI] = 0;
m_aiCommerceAttacks[iI] = 0;
m_aiMaxCommerceAttacks[iI] = 0;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
for (iI = 0; iI < NUM_DOMAIN_TYPES; iI++)
{
m_aiDomainFreeExperience[iI] = 0;
m_aiDomainProductionModifier[iI] = 0;
}
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
m_aiCulture[iI] = 0;
m_aiNumRevolts[iI] = 0;
}
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
m_abEverOwned[iI] = false;
m_abTradeRoute[iI] = false;
}
for (iI = 0; iI < MAX_TEAMS; iI++)
{
m_abRevealed[iI] = false;
}
for (iI = 0; iI < MAX_TEAMS; iI++)
{
m_abEspionageVisibility[iI] = false;
}
m_szName.clear();
m_szScriptData = "";
m_bPopulationRankValid = false;
m_iPopulationRank = -1;
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
m_abBaseYieldRankValid[iI] = false;
m_abYieldRankValid[iI] = false;
m_aiBaseYieldRank[iI] = -1;
m_aiYieldRank[iI] = -1;
}
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
m_abCommerceRankValid[iI] = false;
m_aiCommerceRank[iI] = -1;
}
if (!bConstructorCall)
{
FAssertMsg((0 < GC.getNumBonusInfos()), "GC.getNumBonusInfos() is not greater than zero but an array is being allocated in CvCity::reset");
m_paiNoBonus = new int[GC.getNumBonusInfos()];
m_paiFreeBonus = new int[GC.getNumBonusInfos()];
m_paiNumBonuses = new int[GC.getNumBonusInfos()];
m_paiNumCorpProducedBonuses = new int[GC.getNumBonusInfos()];
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
m_paiNoBonus[iI] = 0;
m_paiFreeBonus[iI] = 0;
m_paiNumBonuses[iI] = 0;
m_paiNumCorpProducedBonuses[iI] = 0;
}
m_paiProjectProduction = new int[GC.getNumProjectInfos()];
for (iI = 0; iI < GC.getNumProjectInfos(); iI++)
{
m_paiProjectProduction[iI] = 0;
}
FAssertMsg((0 < GC.getNumBuildingInfos()), "GC.getNumBuildingInfos() is not greater than zero but an array is being allocated in CvCity::reset");
//m_ppBuildings = new CvBuilding *[GC.getNumBuildingInfos()];
m_paiBuildingProduction = new int[GC.getNumBuildingInfos()];
m_paiBuildingProductionTime = new int[GC.getNumBuildingInfos()];
m_paiBuildingOriginalOwner = new int[GC.getNumBuildingInfos()];
m_paiBuildingOriginalTime = new int[GC.getNumBuildingInfos()];
m_paiNumRealBuilding = new int[GC.getNumBuildingInfos()];
m_paiNumFreeBuilding = new int[GC.getNumBuildingInfos()];
m_paiNumFreeAreaBuilding = new int[GC.getNumBuildingInfos()];
m_paiNumFreeTradeRegionBuilding = new int[GC.getNumBuildingInfos()];
/************************************************************************************************/
/* Afforess Start 05/19/10 */
/* */
/* */
/************************************************************************************************/
m_pabDisabledBuilding = new bool[GC.getNumBuildingInfos()];
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
//m_ppBuildings[iI] = NULL;
m_paiBuildingProduction[iI] = 0;
m_paiBuildingProductionTime[iI] = 0;
m_paiBuildingOriginalOwner[iI] = -1;
m_paiBuildingOriginalTime[iI] = MIN_INT;
m_paiNumRealBuilding[iI] = 0;
m_paiNumFreeBuilding[iI] = 0;
m_paiNumFreeAreaBuilding[iI] = 0;
m_paiNumFreeTradeRegionBuilding[iI] = 0;
/************************************************************************************************/
/* Afforess Start 05/19/10 */
/* */
/* */
/************************************************************************************************/
m_pabDisabledBuilding[iI] = false;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
FAssertMsg((0 < GC.getNumUnitInfos()), "GC.getNumUnitInfos() is not greater than zero but an array is being allocated in CvCity::reset");
m_paiUnitProduction = new int[GC.getNumUnitInfos()];
m_paiUnitProductionTime = new int[GC.getNumUnitInfos()];
m_paiGreatPeopleUnitRate = new int[GC.getNumUnitInfos()];
m_paiGreatPeopleUnitProgress = new int[GC.getNumUnitInfos()];
for (iI = 0;iI < GC.getNumUnitInfos();iI++)
{
m_paiUnitProduction[iI] = 0;
m_paiUnitProductionTime[iI] = 0;
m_paiGreatPeopleUnitRate[iI] = 0;
m_paiGreatPeopleUnitProgress[iI] = 0;
}
FAssertMsg((0 < GC.getNumSpecialistInfos()), "GC.getNumSpecialistInfos() is not greater than zero but an array is being allocated in CvCity::reset");
m_paiSpecialistCount = new int[GC.getNumSpecialistInfos()];
m_paiMaxSpecialistCount = new int[GC.getNumSpecialistInfos()];
m_paiForceSpecialistCount = new int[GC.getNumSpecialistInfos()];
m_paiFreeSpecialistCount = new int[GC.getNumSpecialistInfos()];
m_paiFreeSpecialistCountUnattributed = new int[GC.getNumSpecialistInfos()];
for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
{
m_paiSpecialistCount[iI] = 0;
m_paiMaxSpecialistCount[iI] = 0;
m_paiForceSpecialistCount[iI] = 0;
m_paiFreeSpecialistCount[iI] = 0;
m_paiFreeSpecialistCountUnattributed[iI] = 0;
}
FAssertMsg((0 < GC.getNumImprovementInfos()), "GC.getNumImprovementInfos() is not greater than zero but an array is being allocated in CvCity::reset");
m_paiImprovementFreeSpecialists = new int[GC.getNumImprovementInfos()];
for (iI = 0; iI < GC.getNumImprovementInfos(); iI++)
{
m_paiImprovementFreeSpecialists[iI] = 0;
}
m_paiReligionInfluence = new int[GC.getNumReligionInfos()];
m_paiStateReligionHappiness = new int[GC.getNumReligionInfos()];
m_pabHasReligion = new bool[GC.getNumReligionInfos()];
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
m_paiReligionInfluence[iI] = 0;
m_paiStateReligionHappiness[iI] = 0;
m_pabHasReligion[iI] = false;
}
m_pabHasCorporation = new bool[GC.getNumCorporationInfos()];
for (iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
m_pabHasCorporation[iI] = false;
}
FAssertMsg((0 < GC.getNumUnitCombatInfos()), "GC.getNumUnitCombatInfos() is not greater than zero but an array is being allocated in CvCity::reset");
m_paiUnitCombatFreeExperience = new int[GC.getNumUnitCombatInfos()];
/************************************************************************************************/
/* Afforess Start 05/22/10 */
/* */
/* */
/************************************************************************************************/
m_paiUnitCombatExtraStrength = new int[GC.getNumUnitCombatInfos()];
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
m_paiUnitCombatFreeExperience[iI] = 0;
/************************************************************************************************/
/* Afforess Start 05/22/10 */
/* */
/* */
/************************************************************************************************/
m_paiUnitCombatExtraStrength[iI] = 0;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
FAssertMsg((0 < GC.getNumPromotionInfos()), "GC.getNumPromotionInfos() is not greater than zero but an array is being allocated in CvCity::reset");
m_paiFreePromotionCount = new int[GC.getNumPromotionInfos()];
for (iI = 0; iI < GC.getNumPromotionInfos(); iI++)
{
m_paiFreePromotionCount[iI] = 0;
}
FAssertMsg((0 < NUM_CITY_PLOTS), "NUM_CITY_PLOTS is not greater than zero but an array is being allocated in CvCity::reset");
m_pabWorkingPlot = new bool[NUM_CITY_PLOTS];
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
{
m_pabWorkingPlot[iI] = false;
}
//Afforess allow zero trade routes
if (GC.getMAX_TRADE_ROUTES() <= 0)
{
m_paTradeCities = NULL;
}
else
{
FAssertMsg((0 < GC.getMAX_TRADE_ROUTES()), "GC.getMAX_TRADE_ROUTES() is not greater than zero but an array is being allocated in CvCity::reset");
m_paTradeCities = new IDInfo[GC.getMAX_TRADE_ROUTES()];
for (iI = 0; iI < GC.getMAX_TRADE_ROUTES(); iI++)
{
m_paTradeCities[iI].reset();
}
}
m_aEventsOccured.clear();
m_aBuildingYieldChange.clear();
m_aBuildingCommerceChange.clear();
m_aBuildingHappyChange.clear();
m_aBuildingHealthChange.clear();
/************************************************************************************************/
/* Afforess Start 04/29/10 */
/* */
/* */
/************************************************************************************************/
m_paiUnitClassProductionModifier = new int[GC.getNumUnitClassInfos()];
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
m_paiUnitClassProductionModifier[iI] = 0;
}
m_paiBuildingClassProductionModifier = new int[GC.getNumBuildingClassInfos()];
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
m_paiBuildingClassProductionModifier[iI] = 0;
}
m_paiBonusDefenseChanges = new int[GC.getNumBonusInfos()];
m_pabHadVicinityBonus = new bool[GC.getNumBonusInfos()];
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
m_paiBonusDefenseChanges[iI] = 0;
m_pabHadVicinityBonus[iI] = false;
}
FAssertMsg(m_pabAutomatedCanBuild==NULL, "about to leak memory, CvCity::m_pabAutomatedCanBuild");
m_pabAutomatedCanBuild = new bool [GC.getNumBuildInfos()];
for (iI = 0; iI < GC.getNumBuildInfos(); iI++)
{
m_pabAutomatedCanBuild[iI] = true;
}
m_aBuildingCommerceModifier.clear();
m_aBuildingYieldModifier.clear();
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
if ( eOwner != NO_PLAYER )
{
m_BuildingList.setPlayerToOwner();
m_UnitList.setPlayerToOwner();
m_UnitList.init();
}
if (!bConstructorCall)
{
m_BuildingList.init();
AI_reset();
}
m_eConscriptUnitType = NO_UNIT;
m_bIsGreatWallSeed = false;
m_deferringBonusProcessingCount = 0;
}
//////////////////////////////////////
// graphical only setup
//////////////////////////////////////
void CvCity::setupGraphical()
{
PROFILE_FUNC();
if (!GC.IsGraphicsInitialized())
{
return;
}
if (!isInViewport())
{
return;
}
if ( getEntity() == NULL )
{
createCityEntity(this);
}
CvDLLEntity::setup();
setInfoDirty(true);
setLayoutDirty(true);
}
/************************************************************************************************/
/* REVOLUTION_MOD 03/29/09 jdog5000 */
/* */
/* */
/************************************************************************************************/
int CvCity::getRevolutionIndex() const
{
return m_iRevolutionIndex;
}
void CvCity::setRevolutionIndex( int iNewValue )
{
if( iNewValue < 0 )
iNewValue = 0;
m_iRevolutionIndex = iNewValue;
}
void CvCity::changeRevolutionIndex( int iChange )
{
setRevolutionIndex( getRevolutionIndex() + iChange );
}
int CvCity::getLocalRevIndex() const
{
return m_iLocalRevIndex;
}
void CvCity::setLocalRevIndex( int iNewValue )
{
m_iLocalRevIndex = iNewValue;
}
void CvCity::changeLocalRevIndex( int iChange )
{
setLocalRevIndex( getLocalRevIndex() + iChange );
}
int CvCity::getRevIndexAverage() const
{
return m_iRevIndexAverage;
}
void CvCity::setRevIndexAverage( int iNewValue )
{
m_iRevIndexAverage = range(iNewValue,0,3400);
}
void CvCity::updateRevIndexAverage( )
{
setRevIndexAverage( (2*getRevIndexAverage() + getRevolutionIndex())/3 );
}
int CvCity::getRevolutionCounter() const
{
return m_iRevolutionCounter;
}
void CvCity::setRevolutionCounter( int iNewValue )
{
if( iNewValue < 0 )
iNewValue = 0;
m_iRevolutionCounter = iNewValue;
}
void CvCity::changeRevolutionCounter( int iChange )
{
setRevolutionCounter( getRevolutionCounter() + iChange );
}
int CvCity::getReinforcementCounter() const
{
return m_iReinforcementCounter;
}
void CvCity::setReinforcementCounter( int iNewValue )
{
if( iNewValue < 0 )
iNewValue = 0;
m_iReinforcementCounter = iNewValue;
}
void CvCity::changeReinforcementCounter( int iChange )
{
setReinforcementCounter( getReinforcementCounter() + iChange );
}
// AIAndy: This function seems incomplete and unused
int CvCity::getRevIndexHappinessVal()
{
int iVal = 0;
double fHappyMod = 1.0;
int iUnhappy = unhappyLevel(-1);
int iHappy = happyLevel();
if( iUnhappy > iHappy )
{
double unhappy = iUnhappy;
// Lower unhappiness from war weariness
if( getWarWearinessPercentAnger() > 0 )
{
unhappy -= (1.0 * getPopulation() * (double)(getWarWearinessPercentAnger()/2)) / GC.getPERCENT_ANGER_DIVISOR();
}
// Lower unhappiness from Rev index (avoid spiral)
unhappy -= (1.0 * getPopulation() * getRevIndexPercentAnger()) / GC.getPERCENT_ANGER_DIVISOR();
unhappy -= iHappy;
if( unhappy > 0 )
{
// Lower unhappiness from espionage missions
if( getEspionageHappinessCounter() )
{
unhappy -= std::min( (-unhappy)/3.0, getEspionageHappinessCounter()/3.0 );
}
if( getOccupationTimer() > 0 )
{
unhappy = unhappy/3.0;
}
else if( isRecentlyAcquired() )
{
unhappy = unhappy/2.0;
}
}
}
else
{
}
return iVal;
}
int CvCity::getRevIndexDistanceVal()
{
int iVal = 0;
return iVal;
}
int CvCity::getRevIndexColonyVal()
{
int iVal = 0;
return iVal;
}
int CvCity::getRevIndexReligionVal()
{
int iVal = 0;
return iVal;
}
int CvCity::getRevIndexNationalityVal()
{
int iVal = 0;
return iVal;
}
int CvCity::getRevIndexHealthVal()
{
int iVal = 0;
return iVal;
}
int CvCity::getRevIndexGarrisonVal()
{
int iVal = 0;
return iVal;
}
int CvCity::getRevIndexDisorderVal()
{
int iVal = 0;
return iVal;
}
bool CvCity::isRecentlyAcquired()
{
return ((GC.getGameINLINE().getGameTurn() - getGameTurnAcquired()) < (12*GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getConstructPercent())/100);
}
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
void CvCity::kill(bool bUpdatePlotGroups, bool bUpdateCulture)
{
PROFILE_FUNC();
CvPlot* pPlot;
CvPlot* pAdjacentPlot;
CvPlot* pLoopPlot;
PlayerTypes eOwner;
bool bCapital;
// < M.A.D. Nukes Start >
int iI, iJ;
// < M.A.D. Nukes End >
CvPlotGroup* originalTradeNetworkConnectivity[MAX_PLAYERS];
if (isCitySelected())
{
gDLL->getInterfaceIFace()->clearSelectedCities();
}
pPlot = plot();
// Take this plot out of zobrist hashes for local plot groups
pPlot->ToggleInPlotGroupsZobristContributors();
// Whose trade networks was this city relevant to prior to razing
if ( bUpdatePlotGroups )
{
for(int iI = 0; iI < MAX_PLAYERS; iI++)
{
originalTradeNetworkConnectivity[iI] = GET_PLAYER((PlayerTypes)iI).isAlive() ? pPlot->getPlotGroup((PlayerTypes)iI) : NULL;
}
}
// < M.A.D. Nukes Start >
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if(!GET_PLAYER((PlayerTypes)iI).isAlive())
{
continue;
}
for(iJ = 0; iJ < GET_PLAYER((PlayerTypes)iI).getNumUnits() ; iJ++)
{
if(GET_PLAYER((PlayerTypes)iI).getUnit(iJ) == NULL)
{
continue;
}
if(!GET_PLAYER((PlayerTypes)iI).getUnit(iJ)->getUnitInfo().getUnitAIType(UNITAI_ICBM))
{
continue;
}
if(!GET_PLAYER((PlayerTypes)iI).getUnit(iJ)->isMADEnabled())
{
continue;
}
if(!at(GET_PLAYER((PlayerTypes)iI).getUnit(iJ)->getMADTargetPlotX(), GET_PLAYER((PlayerTypes)iI).getUnit(iJ)->getMADTargetPlotY()))
{
continue;
}
if(GET_PLAYER((PlayerTypes)iI).getUnit(iJ)->getX_INLINE() == INVALID_PLOT_COORD &&
GET_PLAYER((PlayerTypes)iI).getUnit(iJ)->getY_INLINE() == INVALID_PLOT_COORD)
{
continue;
}
GET_PLAYER((PlayerTypes)iI).getUnit(iJ)->clearMADTargetPlot();
}
}
// < M.A.D. Nukes End >
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
{
pLoopPlot = getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getWorkingCityOverride() == this)
{
pLoopPlot->setWorkingCityOverride(NULL);
}
}
}
setCultureLevel(NO_CULTURELEVEL, false);
/************************************************************************************************/
/* Afforess Start 02/15/10 */
/* */
/* */
/************************************************************************************************/
setOccupationCultureLevel(NO_CULTURELEVEL);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
setNumRealBuilding(((BuildingTypes)iI), 0);
setNumFreeBuilding(((BuildingTypes)iI), 0);
}
for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
{
setFreeSpecialistCount(((SpecialistTypes)iI), 0);
}
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
setTradeYield(((YieldTypes)iI), 0);
setCorporationYield(((YieldTypes) iI), 0);
}
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
setHasReligion(((ReligionTypes)iI), false, false, true);
if (isHolyCity((ReligionTypes)iI))
{
GC.getGameINLINE().setHolyCity(((ReligionTypes)iI), NULL, false);
}
}
for (iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
setHasCorporation(((CorporationTypes)iI), false, false);
if (isHeadquarters((CorporationTypes)iI))
{
GC.getGameINLINE().setHeadquarters(((CorporationTypes)iI), NULL, false);
}
}
setPopulation(0);
AI_assignWorkingPlots();
clearOrderQueue();
// remember the visibility before we take away the city from the plot below
std::vector<bool> abEspionageVisibility;
for (iI = 0; iI < MAX_TEAMS; iI++)
{
abEspionageVisibility.push_back(getEspionageVisibility((TeamTypes)iI));
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 08/04/09 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
// Need to clear trade routes of dead city, else they'll be claimed for the owner forever
clearTradeRoutes();
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
pPlot->setPlotCity(NULL);
/************************************************************************************************/
/* UNOFFICIAL_PATCH 03/04/10 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
// Replace floodplains after city is removed
if (pPlot->getBonusType() == NO_BONUS)
{
for (int iJ = 0; iJ < GC.getNumFeatureInfos(); iJ++)
{
//Fuyu: don't limit to riverside
/*
if (GC.getFeatureInfo((FeatureTypes)iJ).isRequiresRiver())
*/
{
#ifdef MULTI_FEATURE_MOD
if (pPlot->canHaveFeature((FeatureTypes)iJ), GC.getFeatureInfo((FeatureTypes)iJ).canBeSecondary())
#else
if (pPlot->canHaveFeature((FeatureTypes)iJ))
#endif
{
if (GC.getFeatureInfo((FeatureTypes)iJ).getAppearanceProbability() == 10000)
{
#ifdef MULTI_FEATURE_MOD
if (GC.getFeatureInfo((FeatureTypes)iJ).canBeSecondary())
{
pPlot->setHasFeature((FeatureTypes)iJ, true);
}
else
{
pPlot->setFeatureType((FeatureTypes)iJ);
}
#else
pPlot->setFeatureType((FeatureTypes)iJ);
break;
#endif
}
}
}
}
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
area()->changeCitiesPerPlayer(getOwnerINLINE(), -1);
GET_TEAM(getTeam()).changeNumCities(-1);
GC.getGameINLINE().changeNumCities(-1);
FAssertMsg(getWorkingPopulation() == 0, "getWorkingPopulation is expected to be 0");
FAssertMsg(!isWorkingPlot(CITY_HOME_PLOT), "isWorkingPlot(CITY_HOME_PLOT) is expected to be false");
FAssertMsg(getSpecialistPopulation() == 0, "getSpecialistPopulation is expected to be 0");
FAssertMsg(getNumGreatPeople() == 0, "getNumGreatPeople is expected to be 0");
FAssertMsg(getBaseYieldRate(YIELD_FOOD) == 0, "getBaseYieldRate(YIELD_FOOD) is expected to be 0");
FAssertMsg(getBaseYieldRate(YIELD_PRODUCTION) == 0, "getBaseYieldRate(YIELD_PRODUCTION) is expected to be 0");
FAssertMsg(getBaseYieldRate(YIELD_COMMERCE) == 0, "getBaseYieldRate(YIELD_COMMERCE) is expected to be 0");
FAssertMsg(!isProduction(), "isProduction is expected to be false");
eOwner = getOwnerINLINE();
bCapital = isCapital();
pPlot->setImprovementType((ImprovementTypes)(GC.getDefineINT("RUINS_IMPROVEMENT")));
CvEventReporter::getInstance().cityLost(this);
GET_PLAYER(getOwnerINLINE()).deleteCity(getID());
if ( bUpdateCulture )
{
pPlot->updateCulture(true, false);
for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
pAdjacentPlot = plotDirection(pPlot->getX_INLINE(), pPlot->getY_INLINE(), ((DirectionTypes)iI));
if (pAdjacentPlot != NULL)
{
pAdjacentPlot->updateCulture(true, false);
}
}
}
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (GET_TEAM(GET_PLAYER(eOwner).getTeam()).isVassal((TeamTypes)iI))
{
pPlot->changeAdjacentSight((TeamTypes)iI, GC.getDefineINT("PLOT_VISIBILITY_RANGE"), false, NULL, false);
}
}
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (abEspionageVisibility[iI])
{
pPlot->changeAdjacentSight((TeamTypes)iI, GC.getDefineINT("PLOT_VISIBILITY_RANGE"), false, NULL, false);
}
}
//ls612: Embassy visibility fix (by Damgo)
if (bCapital)
{
for (iI = 0; iI < MAX_TEAMS; iI++)
{
if (GET_TEAM(GET_PLAYER(eOwner).getTeam()).isHasEmbassy((TeamTypes)iI))
{
pPlot->changeAdjacentSight((TeamTypes)iI, GC.getDefineINT("PLOT_VISIBILITY_RANGE"), false, NULL, false);
}
}
}
GET_PLAYER(eOwner).setMaintenanceDirty(true);
GC.getMapINLINE().updateWorkingCity();
GC.getGameINLINE().AI_makeAssignWorkDirty();
if (bCapital)
{
GET_PLAYER(eOwner).findNewCapital();
GET_TEAM(GET_PLAYER(eOwner).getTeam()).resetVictoryProgress();
}
if (bUpdatePlotGroups)
{
PROFILE("CvCity::kill.UpdatePlotGroups");
for(int iI = 0; iI < MAX_PLAYERS; iI++)
{
if ( GET_PLAYER((PlayerTypes)iI).isAlive() )
{
if ( originalTradeNetworkConnectivity[iI] == NULL )
{
if ( pPlot->isTradeNetwork(GET_PLAYER((PlayerTypes)iI).getTeam()) )
{
GET_PLAYER((PlayerTypes)iI).updatePlotGroups(pPlot->area());
}
}
else
{
originalTradeNetworkConnectivity[iI]->recalculatePlots();
}
}
}
}
if (eOwner == GC.getGameINLINE().getActivePlayer())
{
gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
}
}
void CvCity::killTestCheap()
{
//CvPlot* pPlot;
//CvPlot* pAdjacentPlot;
//CvPlot* pLoopPlot;
//PlayerTypes eOwner;
//bool bCapital;
// < M.A.D. Nukes Start >
//int iI, iJ;
// < M.A.D. Nukes End >
if (isCitySelected())
{
gDLL->getInterfaceIFace()->clearSelectedCities();
}
// pPlot = plot();
// for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
// {
// pLoopPlot = getCityIndexPlot(iI);
//
// if (pLoopPlot != NULL)
// {
// if (pLoopPlot->getWorkingCityOverride() == this)
// {
// pLoopPlot->setWorkingCityOverride(NULL);
// }
// }
// }
//clearOrderQueue();
//pPlot->setPlotCity(NULL);
// area()->changeCitiesPerPlayer(getOwnerINLINE(), -1);
// GET_TEAM(getTeam()).changeNumCities(-1);
// GC.getGameINLINE().changeNumCities(-1);
// eOwner = getOwnerINLINE();
// bCapital = isCapital();
GET_PLAYER(getOwnerINLINE()).deleteCity(getID());
// pPlot->updateCulture(true, false);
//for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
//{
// pAdjacentPlot = plotDirection(pPlot->getX_INLINE(), pPlot->getY_INLINE(), ((DirectionTypes)iI));
// if (pAdjacentPlot != NULL)
// {
// pAdjacentPlot->updateCulture(true, false);
// }
//}
//GC.getMapINLINE().updateWorkingCity();
//GC.getGameINLINE().AI_makeAssignWorkDirty();
//if (bCapital)
//{
// GET_PLAYER(eOwner).findNewCapital();
// GET_TEAM(GET_PLAYER(eOwner).getTeam()).resetVictoryProgress();
//}
//if (eOwner == GC.getGameINLINE().getActivePlayer())
//{
// gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
//}
}
void CvCity::doTurnBeginProcessing()
{
PROFILE_FUNC();
FAssert(m_deferringBonusProcessingCount == 0);
// Allow the player to determine the type of conscripted units
{
PYTHON_ACCESS_LOCK_SCOPE
long lConscriptUnit;
CyArgsList argsList;
argsList.add(getOwnerINLINE()); // pass in player
lConscriptUnit = -1;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "getConscriptUnitType", argsList.makeFunctionArgs(),&lConscriptUnit);
m_eConscriptUnitType = (lConscriptUnit == -1 ? NO_UNIT : (UnitTypes)lConscriptUnit);
}
AI_prepareForTurnProcessing();
// UPT: Check if units are over the limit, then distribute these through free plots
distributeUnitsOverFreeTiles();
// Fail safe
m_deferringBonusProcessingCount = 0;
CvPlot::setDeferredPlotGroupRecalculationMode(true);
startDeferredBonusProcessing();
beginDeferredFreeTradeRegionBuildingProcessing();
}
void CvCity::doTurnMiscAsync()
{
PROFILE_FUNC();
AI_FlushBuildingValueCache();
FlushCanConstructCache();
setBuildingListInvalid();
setUnitListInvalid();
#ifdef CAN_TRAIN_CACHING
populateCanTrainCache(false);
#endif
m_unitSourcedPropertyCache.clear();
int iI;
for(iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
m_cachedBuildingYieldModifers[iI] = -1;
}
if (!isBombarded())
{
changeDefenseDamage(- cityDefenseRecoveryRate());
}
setLastDefenseDamage(getDefenseDamage());
setBombarded(false);
setPlundered(false);
setDrafted(false);
setAirliftTargeted(false);
setBuiltFoodProducedUnit(false);
/************************************************************************************************/
/* Afforess 9/7/10 */
/* */
/* */
/************************************************************************************************/
doConnectednessCalculations();
//Promotes Units if there is a building that allows it
doPromotion();
//Does vicinity bonus checks
doVicinityBonus();
//Checks conditions of buildings, may disable or enable some
checkBuildings(true, false, true, true, false);
//Extra Hammer from settling on Forest
if (getExtraYieldTurns() > 0)
{
changeExtraYieldTurns(-1);
}
//Extra Food from settling on Jungle
else if (getExtraYieldTurns() < 0)
{
changeExtraYieldTurns(1);
}
//Checks if enemy troops have found secret entrance into the city
doInvasion();
//Damages enemy units around the city, if applicable
doAttack();
//Heals friendly units in the city extra, if applicable
doHeal();
//Decays the CommerceAttacks
decayCommerce();
//Spreads corporations
doCorporation();
//Counts down the disable power timer
doDisabledPower();
//Recalculate growth thresholds/food storage
recalculateMaxFoodKeptPercent();
recalculatePopulationgrowthratepercentage();
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
doWarWeariness();
setCurrAirlift(0);
AI_doTurn();
}
void CvCity::doTurnPreProduction()
{
MEMORY_TRACE_FUNCTION();
PROFILE("CvCity::doTurnPreProduction()");
endDeferredBonusProcessing();
endDeferredFreeTradeRegionBuildingProcessing();
// Report to Python any events queued during the previous async stage (since we
// cannot call Python from background threads)
GC.getGameINLINE().reportQueuedEvents();
doGrowth();
/*************************************************************************************************/
/** SPEEDTWEAK (BarbCities) Sephi **/
/** This function can be very slow for barbarian cities(adds 1-3sec to turn time).Reason unknown**/
/** **/
/*************************************************************************************************/
/** orig
doCulture();
**/
if (!isBarbarian())
{
doCulture();
}
/*************************************************************************************************/
/** END **/
/*************************************************************************************************/
doPlotCulture(false, getOwnerINLINE(), getCommerceRate(COMMERCE_CULTURE));
// Force deferred plot group recalculation to happen now before we assess production
CvPlot::setDeferredPlotGroupRecalculationMode(false);
doAutobuild();
/* if ( !CvCityTurnPipelineWorkItem::m_bHasProcessedDeferalOfVoteSources )
{
GC.getGameINLINE().startDeferredVoteSourceProcessing();
CvCityTurnPipelineWorkItem::m_bHasProcessedDeferalOfVoteSources = true;
}
*/
// Defer all bonus processing until after all buildings have been added
startDeferredBonusProcessing();
beginDeferredFreeTradeRegionBuildingProcessing();
}
void CvCity::doAutobuild(void)
{
int iI;
// Auto-build any auto-build buildings we can
for(iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if ( GC.getBuildingInfo((BuildingTypes)iI).isAutoBuild() &&
getNumBuilding((BuildingTypes)iI) == 0 &&
canConstruct((BuildingTypes)iI, false, false, true) )
{
CvWString szBuffer;
setNumRealBuilding((BuildingTypes)iI, 1);
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_COMPLETED_AUTO_BUILD", GC.getBuildingInfo((BuildingTypes)iI).getTextKeyWide(), getName().GetCString()));
AddDLLMessage(getOwnerINLINE(), true, GC.getEVENT_MESSAGE_TIME(), szBuffer, NULL, MESSAGE_TYPE_INFO, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"));
}
}
}
void CvCity::doTurnEnactCurrentProduction(CvCityTurnPipelineWorkItem* item)
{
PROFILE_FUNC();
item->m_bAllowNoProduction = !doCheckProduction();
/************************************************************************************************/
/* Afforess Start 12/21/09 */
/* */
/* */
/************************************************************************************************/
if(GC.getUSE_CAN_DO_PRODUCTION_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyCity* pyCity = new CyCity(this);
CyArgsList argsList;
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "doProduction", argsList.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (!isHuman() || isProductionAutomated())
{
// Koshling - with the unit contracting system we only build units to contractual
// orders (apart from a few emergency cases) and we should not change from building
// them due to new techs etc
//if (!isProduction() || isProductionProcess() || (AI_isChooseProductionDirty() && !isProductionUnit()))
if (!isProduction() || isProductionProcess())
{
if ( AI_changeProduction() )
{
clearOrderQueue();
}
else
{
// Enact existing production
doProduction(item, true);
}
}
}
}
void CvCity::doTurnChooseProduction(CvCityTurnPipelineWorkItem* item)
{
PROFILE_FUNC();
if(m_workItem != NULL)
{
// Clear things out before considering what to propose or
// else we'll have stale proposal data after a multi-production
// requeue
m_workItem->setProposedProduction(NO_ORDER, -1, -1);
}
if (!isHuman() || isProductionAutomated())
{
// Koshling - with the unit contracting system we only build units to contractual
// orders (apart from a few emergency cases) and we should not change from building
// them due to new techs etc
//if (!isProduction() || isProductionProcess() || (AI_isChooseProductionDirty() && !isProductionUnit()))
if (!isProduction() || isProductionProcess())
{
AI_chooseProduction();
}
}
OrderTypes proposedOrder;
int proposedInstance;
int proposedData2;
item->getProposedProduction(proposedOrder, proposedInstance, proposedData2);
if ( proposedOrder == NO_ORDER )
{
// If that caused a requeue we can stop now
if ( item->GetState() != WORKITEM_STATE_PROCESSING )
{
return;
}
if (!item->m_bAllowNoProduction && !isProduction() )
{
return;
}
}
if (isProductionProcess())
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH 12/07/09 denev & jdog5000 */
/* */
/* Bugfix, Odd behavior */
/************************************************************************************************/
if (m_bPopProductionProcess)
{
popOrder(0, false, true);
m_bPopProductionProcess = false;
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
}
}
void CvCity::doTurnEnactNewProduction(CvCityTurnPipelineWorkItem* item)
{
PROFILE_FUNC();
doProduction(item, true);
if ( item->GetState() == WORKITEM_STATE_PROCESSING )
{
GET_PLAYER(getOwnerINLINE()).getContractBroker().advertiseTender(this, AI_getBuildPriority());
}
}
void CvCity::doPrepareToCompleteProduction()
{
PROFILE_FUNC();
for( std::vector<OrderData>::iterator itr = m_inProcessOrders.begin(); itr != m_inProcessOrders.end(); ++itr )
{
if ( itr->eOrderType == ORDER_CREATE )
{
// Event reported to Python before the project is built, so that we can show the movie before awarding free techs, for example
CvEventReporter::getInstance().projectBuilt(this, (ProjectTypes)itr->iData1);
}
}
}
void CvCity::doTurnCompleteProduction()
{
PROFILE_FUNC();
completeOrderProcessing();
}
void CvCity::doTurnPostProduction()
{
PROFILE_FUNC();
int iI;
CvPlot* pLoopPlot;
// Perform deferred bonus procesing from building changes
endDeferredFreeTradeRegionBuildingProcessing();
endDeferredBonusProcessing();
// Now we're back on the main thread perform any necessary UI manipulations
GC.getGameINLINE().doQueuedUIActivity();
/* if ( !CvCityTurnPipelineWorkItem::m_bHasProcessedUndeferalOfVoteSources )
{
GC.getGameINLINE().endDeferredVoteSourceProcessing();
CvCityTurnPipelineWorkItem::m_bHasProcessedUndeferalOfVoteSources = true;
}
*/
// Fire off any events to PYthon for completed orders (this has to be on the main thread)
for( std::vector<OrderData>::iterator itr = m_completedOrders.begin(); itr != m_completedOrders.end(); ++itr )
{
switch(itr->eOrderType)
{
case ORDER_TRAIN:
{
CvUnit* pUnit = GET_PLAYER(getOwnerINLINE()).getUnit(itr->iData2);
if ( pUnit != NULL )
{
CvEventReporter::getInstance().unitBuilt(this, pUnit);
}
}
break;
case ORDER_CONSTRUCT:
CvEventReporter::getInstance().buildingBuilt(this, (BuildingTypes)itr->iData1);
break;
default:
break;
}
}
m_completedOrders.clear();
// Release cache memory
AI_FlushBuildingValueCache();
doDecay();
doReligion();
doGreatPeople();
doMeltdown();
updateEspionageVisibility(true);
if (!isDisorder())
{
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/17/09 */
/* */
/* */
/************************************************************************************************/
for (iI = 0; iI < getNumCityPlots(); iI++)
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
{
pLoopPlot = getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getWorkingCity() == this)
{
if (pLoopPlot->isBeingWorked())
{
pLoopPlot->doImprovement();
}
}
}
}
}
if (getCultureUpdateTimer() > 0)
{
changeCultureUpdateTimer(-1);
}
if (getOccupationTimer() > 0)
{
changeOccupationTimer(-1);
}
if (getHurryAngerTimer() > 0)
{
changeHurryAngerTimer(-1);
}
/************************************************************************************************/
/* REVOLUTION_MOD 04/28/08 jdog5000 */
/* */
/* */
/************************************************************************************************/
if( getRevRequestAngerTimer() > 0 )
{
changeRevRequestAngerTimer(-1);
}
if (getRevSuccessTimer() > 0)
{
changeRevSuccessTimer(-1);
}
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
if (getConscriptAngerTimer() > 0)
{
changeConscriptAngerTimer(-1);
}
if (getDefyResolutionAngerTimer() > 0)
{
changeDefyResolutionAngerTimer(-1);
}
if (getHappinessTimer() > 0)
{
changeHappinessTimer(-1);
}
/************************************************************************************************/
/* Afforess Start 04/15/10 */
/* */
/* */
/************************************************************************************************/
if (getLandmarkAngerTimer() > 0)
{
changeLandmarkAngerTimer(-1);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (getEspionageHealthCounter() > 0)
{
changeEspionageHealthCounter(-1);
}
if (getEspionageHappinessCounter() > 0)
{
changeEspionageHappinessCounter(-1);
}
doForcedRevolutionUpdate();
if (isOccupation() || (angryPopulation() > 0) || (healthRate() < 0))
{
setWeLoveTheKingDay(false);
}
else if ((getPopulation() >= GC.getDefineINT("WE_LOVE_THE_KING_POPULATION_MIN_POPULATION")) && (getCitySorenRandNum(GC.getDefineINT("WE_LOVE_THE_KING_RAND"), "Do We Love The King?") < getPopulation()))
{
setWeLoveTheKingDay(true);
}
else
{
setWeLoveTheKingDay(false);
}
/************************************************************************************************/
/* Afforess Start 04/30/10 */
/* */
/* */
/************************************************************************************************/
for (int iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
m_pabHadVicinityBonus[iI] = hasVicinityBonus((BonusTypes)iI);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
#ifdef CAN_TRAIN_CACHING
// Outside the scope of the city's turn where we expect to be using cached values turn
// the cache off (we may choose to widden the scope of usability later but for now this is safer)
clearCanTrainCache(false);
#endif
// ONEVENT - Do turn
CvEventReporter::getInstance().cityDoTurn(this, getOwnerINLINE());
// XXX
#ifdef _DEBUG
{
CvPlot* pPlot;
int iCount;
int iI, iJ;
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
//FAssert(getBaseYieldRate((YieldTypes)iI) >= 0);
//FAssert(getYieldRate((YieldTypes)iI) >= 0);
iCount = 0;
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/17/09 */
/* */
/* */
/************************************************************************************************/
for (iJ = 0; iJ < getNumCityPlots(); iJ++)
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
{
if (isWorkingPlot(iJ))
{
pPlot = getCityIndexPlot(iJ);
if (pPlot != NULL)
{
iCount += pPlot->getYield((YieldTypes)iI);
}
}
}
for (iJ = 0; iJ < GC.getNumSpecialistInfos(); iJ++)
{
iCount += (GET_PLAYER(getOwnerINLINE()).specialistYield(((SpecialistTypes)iJ), ((YieldTypes)iI)) * (getSpecialistCount((SpecialistTypes)iJ) + getFreeSpecialistCount((SpecialistTypes)iJ)));
}
for (iJ = 0; iJ < GC.getNumBuildingInfos(); iJ++)
{
iCount += getNumActiveBuilding((BuildingTypes)iJ) * (GC.getBuildingInfo((BuildingTypes) iJ).getYieldChange(iI) + getBuildingYieldChange((BuildingClassTypes)GC.getBuildingInfo((BuildingTypes) iJ).getBuildingClassType(), (YieldTypes)iI));
}
iCount += getTradeYield((YieldTypes)iI);
iCount += getCorporationYield((YieldTypes)iI);
// FAssert(iCount == getBaseYieldRate((YieldTypes)iI));
}
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
// FAssert(getBuildingCommerce((CommerceTypes)iI) >= 0);
// FAssert(getSpecialistCommerce((CommerceTypes)iI) >= 0);
// FAssert(getReligionCommerce((CommerceTypes)iI) >= 0);
// FAssert(getCorporationCommerce((CommerceTypes)iI) >= 0);
// FAssert(GET_PLAYER(getOwnerINLINE()).getFreeCityCommerce((CommerceTypes)iI) >= 0);
}
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
// FAssert(isNoBonus((BonusTypes)iI) || getNumBonuses((BonusTypes)iI) >= ((isConnectedToCapital()) ? (GET_PLAYER(getOwnerINLINE()).getBonusImport((BonusTypes)iI) - GET_PLAYER(getOwnerINLINE()).getBonusExport((BonusTypes)iI)) : 0));
}
}
#endif
// XXX
}
bool CvCity::isCitySelected() const
{
return gDLL->getInterfaceIFace()->isCitySelected((CvCity*)this);
}
bool CvCity::canBeSelected() const
{
if ((getTeam() == GC.getGameINLINE().getActiveTeam()) || GC.getGameINLINE().isDebugMode())
{
return true;
}
if (GC.getGameINLINE().getActiveTeam() != NO_TEAM)
{
if (plot()->isInvestigate(GC.getGameINLINE().getActiveTeam()))
{
return true;
}
}
// EspionageEffect
for (int iLoop = 0; iLoop < GC.getNumEspionageMissionInfos(); iLoop++)
{
// Check the XML
if (GC.getEspionageMissionInfo((EspionageMissionTypes)iLoop).isPassive() && GC.getEspionageMissionInfo((EspionageMissionTypes)iLoop).isInvestigateCity())
{
// Is Mission good?
if (GET_PLAYER(GC.getGameINLINE().getActivePlayer()).canDoEspionageMission((EspionageMissionTypes)iLoop, getOwnerINLINE(), plot(), -1, NULL))
{
return true;
}
}
}
return false;
}
void CvCity::updateSelectedCity(bool bTestProduction)
{
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/17/09 */
/* */
/* */
/************************************************************************************************/
for (int iI = 0; iI < getNumCityPlots(); iI++)
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
{
CvPlot* pLoopPlot = getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
pLoopPlot->updateShowCitySymbols();
}
}
if (bTestProduction)
{
if ((getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) && !isProduction())
{
chooseProduction(NO_UNIT, NO_BUILDING, NO_PROJECT, false, true);
}
}
}
void CvCity::updateYield()
{
CvPlot* pLoopPlot;
int iI;
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/17/09 */
/* */
/* */
/************************************************************************************************/
for (iI = 0; iI < getNumCityPlots(); iI++)
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
{
pLoopPlot = getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
pLoopPlot->updateYield();
}
}
}
// XXX kill this?
void CvCity::updateVisibility()
{
PROFILE_FUNC();
if (!GC.IsGraphicsInitialized())
{
return;
}
if (!isInViewport())
{
return;
}
FAssert(GC.getGameINLINE().getActiveTeam() != NO_TEAM);
CvDLLEntity::setVisible(isInViewport() && isRevealed(GC.getGameINLINE().getActiveTeam(), true));
}
void CvCity::createGreatPeople(UnitTypes eGreatPersonUnit, bool bIncrementThreshold, bool bIncrementExperience)
{
GET_PLAYER(getOwnerINLINE()).createGreatPeople(eGreatPersonUnit, bIncrementThreshold, bIncrementExperience, getX_INLINE(), getY_INLINE());
}
void CvCity::doTask(TaskTypes eTask, int iData1, int iData2, bool bOption, bool bAlt, bool bShift, bool bCtrl)
{
switch (eTask)
{
case TASK_RAZE:
GET_PLAYER(getOwnerINLINE()).raze(this, iData1, iData2);
break;
case TASK_DISBAND:
GET_PLAYER(getOwnerINLINE()).disband(this);
break;
case TASK_GIFT:
if (getLiberationPlayer(false) == iData1)
{
liberate(false);
}
else
{
GET_PLAYER((PlayerTypes)iData1).acquireCity(this, false, true, true);
}
break;
case TASK_KEEP:
CvEventReporter::getInstance().cityAcquiredAndKept((PlayerTypes)iData1, this);
break;
case TASK_LIBERATE:
liberate(iData1 != 0);
break;
case TASK_SET_AUTOMATED_CITIZENS:
setCitizensAutomated(bOption);
break;
case TASK_SET_AUTOMATED_PRODUCTION:
setProductionAutomated(bOption, bAlt && bShift && bCtrl);
break;
case TASK_SET_EMPHASIZE:
AI_setEmphasize(((EmphasizeTypes)iData1), bOption);
break;
case TASK_EMPHASIZE_SPECIALIST:
AI_setEmphasizeSpecialist((SpecialistTypes)iData1, bOption);
break;
case TASK_CHANGE_SPECIALIST:
alterSpecialistCount(((SpecialistTypes)iData1), iData2);
break;
case TASK_CHANGE_WORKING_PLOT:
alterWorkingPlot(iData1);
break;
case TASK_CLEAR_WORKING_OVERRIDE:
clearWorkingOverride(iData1);
break;
case TASK_HURRY:
hurry((HurryTypes)iData1);
break;
case TASK_CONSCRIPT:
conscript();
break;
case TASK_CLEAR_ORDERS:
clearOrderQueue();
break;
case TASK_RALLY_PLOT:
setRallyPlot(GC.getMapINLINE().plotINLINE(iData1, iData2));
break;
case TASK_CLEAR_RALLY_PLOT:
setRallyPlot(NULL);
break;
default:
FAssertMsg(false, "eTask failed to match a valid option");
break;
}
}
void CvCity::chooseProduction(UnitTypes eTrainUnit, BuildingTypes eConstructBuilding, ProjectTypes eCreateProject, bool bFinish, bool bFront)
{
CvPopupInfo* pPopupInfo = new CvPopupInfo(BUTTONPOPUP_CHOOSEPRODUCTION);
if (NULL == pPopupInfo)
{
return;
}
pPopupInfo->setData1(getID());
pPopupInfo->setOption1(bFinish);
if (eTrainUnit != NO_UNIT)
{
pPopupInfo->setData2(ORDER_TRAIN);
pPopupInfo->setData3(eTrainUnit);
}
else if (eConstructBuilding != NO_BUILDING)
{
pPopupInfo->setData2(ORDER_CONSTRUCT);
pPopupInfo->setData3(eConstructBuilding);
}
else if (eCreateProject != NO_PROJECT)
{
pPopupInfo->setData2(ORDER_CREATE);
pPopupInfo->setData3(eCreateProject);
}
else
{
pPopupInfo->setData2(NO_ORDER);
pPopupInfo->setData3(NO_UNIT);
}
gDLL->getInterfaceIFace()->addPopup(pPopupInfo, getOwnerINLINE(), false, bFront);
}
int CvCity::getCityPlotIndex(const CvPlot* pPlot) const
{
return plotCityXY(this, pPlot);
}
CvPlot* CvCity::getCityIndexPlot(int iIndex) const
{
return plotCity(getX_INLINE(), getY_INLINE(), iIndex);
}
bool CvCity::canWork(CvPlot* pPlot) const
{
if (pPlot->getWorkingCity() != this)
{
return false;
}
FAssertMsg(getCityPlotIndex(pPlot) != -1, "getCityPlotIndex(pPlot) is expected to be assigned (not -1)");
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/17/09 */
/* */
/* */
/************************************************************************************************/
if (getCityPlotIndex(pPlot) >= getNumCityPlots()) return false; // Just in case FAssertMsg doesn't end the function.
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
if (pPlot->plotCheck(PUF_canSiege, getOwnerINLINE()) != NULL)
{
return false;
}
if (pPlot->isWater())
{
if (!(GET_TEAM(getTeam()).isWaterWork()))
{
return false;
}
if (pPlot->getBlockadedCount(getTeam()) > 0)
{
return false;
}
/* Replaced by blockade mission, above
if (!(pPlot->plotCheck(PUF_canDefend, -1, -1, NO_PLAYER, getTeam())))
{
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->isWater())
{
if (pLoopPlot->plotCheck(PUF_canSiege, getOwnerINLINE()) != NULL)
{
return false;
}
}
}
}
}
*/
}
if (!(pPlot->hasYield()))
{
return false;
}
return true;
}
void CvCity::verifyWorkingPlot(int iIndex)
{
CvPlot* pPlot;
FAssertMsg(iIndex >= 0, "iIndex expected to be >= 0");
FAssertMsg(iIndex < NUM_CITY_PLOTS, "iIndex expected to be < NUM_CITY_PLOTS");
if (isWorkingPlot(iIndex))
{
pPlot = getCityIndexPlot(iIndex);
if (pPlot != NULL)
{
if (!canWork(pPlot))
{
setWorkingPlot(iIndex, false);
AI_setAssignWorkDirty(true);
}
}
}
}
void CvCity::verifyWorkingPlots()
{
int iI;
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/17/09 */
/* */
/* */
/************************************************************************************************/
for (iI = 0; iI < getNumCityPlots(); iI++)
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
{
verifyWorkingPlot(iI);
}
}
void CvCity::clearWorkingOverride(int iIndex)
{
CvPlot* pPlot;
pPlot = getCityIndexPlot(iIndex);
if (pPlot != NULL)
{
pPlot->setWorkingCityOverride(NULL);
}
}
int CvCity::countNumImprovedPlots(ImprovementTypes eImprovement, bool bPotential) const
{
CvPlot* pLoopPlot;
int iCount;
int iI;
iCount = 0;
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/17/09 */
/* */
/* */
/************************************************************************************************/
for (iI = 0; iI < getNumCityPlots(); iI++)
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
{
pLoopPlot = getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getWorkingCity() == this)
{
if (eImprovement != NO_IMPROVEMENT)
{
if (pLoopPlot->getImprovementType() == eImprovement ||
(bPotential && pLoopPlot->canHaveImprovement(eImprovement, getTeam())))
{
++iCount;
}
}
else if (pLoopPlot->getImprovementType() != NO_IMPROVEMENT)
{
iCount++;
}
}
}
}
return iCount;
}
int CvCity::countNumWaterPlots() const
{
CvPlot* pLoopPlot;
int iCount;
int iI;
iCount = 0;
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/17/09 */
/* */
/* */
/************************************************************************************************/
for (iI = 0; iI < getNumCityPlots(); iI++)
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
{
pLoopPlot = getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getWorkingCity() == this)
{
if (pLoopPlot->isWater())
{
iCount++;
}
}
}
}
return iCount;
}
int CvCity::countNumRiverPlots() const
{
int iCount = 0;
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/17/09 */
/* */
/* */
/************************************************************************************************/
for (int iI = 0; iI < getNumCityPlots(); iI++)
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
{
CvPlot* pLoopPlot = getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getWorkingCity() == this)
{
if (pLoopPlot->isRiver())
{
++iCount;
}
}
}
}
return iCount;
}
int CvCity::findPopulationRank() const
{
if (!m_bPopulationRankValid)
{
int iRank = 1;
int iLoop;
CvCity* pLoopCity;
for (pLoopCity = GET_PLAYER(getOwnerINLINE()).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(getOwnerINLINE()).nextCity(&iLoop))
{
if ((pLoopCity->getPopulation() > getPopulation()) ||
((pLoopCity->getPopulation() == getPopulation()) && (pLoopCity->getID() < getID())))
{
iRank++;
}
}
// shenanigans are to get around the const check
m_bPopulationRankValid = true;
m_iPopulationRank = iRank;
}
return m_iPopulationRank;
}
int CvCity::findBaseYieldRateRank(YieldTypes eYield) const
{
if (!m_abBaseYieldRankValid[eYield])
{
int iRate = getBaseYieldRate(eYield);
int iRank = 1;
int iLoop;
CvCity* pLoopCity;
for (pLoopCity = GET_PLAYER(getOwnerINLINE()).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(getOwnerINLINE()).nextCity(&iLoop))
{
if ((pLoopCity->getBaseYieldRate(eYield) > iRate) ||
((pLoopCity->getBaseYieldRate(eYield) == iRate) && (pLoopCity->getID() < getID())))
{
iRank++;
}
}
m_abBaseYieldRankValid[eYield] = true;
m_aiBaseYieldRank[eYield] = iRank;
}
return m_aiBaseYieldRank[eYield];
}
int CvCity::findYieldRateRank(YieldTypes eYield) const
{
PROFILE_FUNC()
if (!m_abYieldRankValid[eYield])
{
int iRate = getYieldRate(eYield);
int iRank = 1;
int iLoop;
CvCity* pLoopCity;
for (pLoopCity = GET_PLAYER(getOwnerINLINE()).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(getOwnerINLINE()).nextCity(&iLoop))
{
if ((pLoopCity->getYieldRate(eYield) > iRate) ||
((pLoopCity->getYieldRate(eYield) == iRate) && (pLoopCity->getID() < getID())))
{
iRank++;
}
}
m_abYieldRankValid[eYield] = true;
m_aiYieldRank[eYield] = iRank;
}
return m_aiYieldRank[eYield];
}
int CvCity::findCommerceRateRank(CommerceTypes eCommerce) const
{
if (!m_abCommerceRankValid[eCommerce])
{
int iRate = getCommerceRateTimes100(eCommerce);
int iRank = 1;
int iLoop;
CvCity* pLoopCity;
for (pLoopCity = GET_PLAYER(getOwnerINLINE()).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(getOwnerINLINE()).nextCity(&iLoop))
{
if ((pLoopCity->getCommerceRateTimes100(eCommerce) > iRate) ||
((pLoopCity->getCommerceRateTimes100(eCommerce) == iRate) && (pLoopCity->getID() < getID())))
{
iRank++;
}
}
m_abCommerceRankValid[eCommerce] = true;
m_aiCommerceRank[eCommerce] = iRank;
}
return m_aiCommerceRank[eCommerce];
}
/************************************************************************************************/
/* REVDCM 02/16/10 phungus420 */
/* */
/* CanTrain */
/************************************************************************************************/
bool CvCity::isPlotTrainable(UnitTypes eUnit, bool bContinue, bool bTestVisible) const
{
PROFILE_FUNC();
CvUnitInfo& kUnit = GC.getUnitInfo(eUnit);
CvPlayer& pPlayer = GET_PLAYER(getOwnerINLINE());
int iI;
if (!bTestVisible)
{
if (kUnit.isStateReligion())
{
if (pPlayer.getStateReligion() != NO_RELIGION)
{
if(!(isHasReligion(pPlayer.getStateReligion())))
{
return false;
}
}
}
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
if (kUnit.isPrereqBuildingClass(iI))
{
if(pPlayer.isBuildingClassRequiredToTrain(BuildingClassTypes(iI), eUnit))
{
if (!(getNumBuilding((BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iI))))
{
return false;
}
}
}
}
if (kUnit.getPrereqBuilding() != NO_BUILDING)
{
if(!(getNumBuilding((BuildingTypes)(kUnit.getPrereqBuilding()))))
{
SpecialBuildingTypes eSpecialBuilding = ((SpecialBuildingTypes)(GC.getBuildingInfo((BuildingTypes)(kUnit.getPrereqBuilding())).getSpecialBuildingType()));
if ((eSpecialBuilding == NO_SPECIALBUILDING) || !(pPlayer.isSpecialBuildingNotRequired(eSpecialBuilding)))
{
return false;
}
}
}
if (kUnit.getPrereqOrBuildingsNum() > 0)
{
bool bFound = false;
int iNum = kUnit.getPrereqOrBuildingsNum();
for (int i = 0; i < iNum; i++)
{
if(getNumBuilding(kUnit.getPrereqOrBuilding(i)) > 0)
{
bFound = true;
break;
}
}
if (!bFound)
return false;
}
if (kUnit.getTrainCondition())
{
if (!kUnit.getTrainCondition()->evaluate(const_cast<CvGameObjectCity*>(getGameObjectConst())))
{
return false;
}
}
}
return true;
}
//Returns true if the city can train a unit, or any upgrade for that unit that forces it obsolete
bool CvCity::isForceObsoleteUnitClassAvailable(UnitTypes eUnit) const
{
PROFILE_FUNC();
UnitTypes eLoopUnit = NO_UNIT;
int iI;
CvUnitInfo& kUnit = GC.getUnitInfo(eUnit);
CvCivilizationInfo& kCivilization = GC.getCivilizationInfo(getCivilizationType());
FAssertMsg(eUnit != NO_UNIT, "eUnit is expected to be assigned (not NO_UNIT)");
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
if (kUnit.getForceObsoleteUnitClass(iI))
{
eLoopUnit = (UnitTypes)kCivilization.getCivilizationUnits(iI);
if(eLoopUnit == NO_UNIT)
{
continue;
}
CvUnitInfo& kLoopUnit = GC.getUnitInfo(eLoopUnit);
if (canTrain(eLoopUnit, false, false, false, true))
{
return true;
}
int iJ;
for (iJ = 0; iJ < GC.getNumUnitClassInfos(); iJ++)
{
if (kLoopUnit.getUpgradeUnitClass(iJ))
{
eLoopUnit = (UnitTypes)kCivilization.getCivilizationUnits(iJ);
if (eLoopUnit != NO_UNIT)
{
if (canTrain(eLoopUnit, false, false, false, true))
{
return true;
}
}
}
}
}
}
return false;
}
/************************************************************************************************/
/* REVDCM END */
/************************************************************************************************/
// Returns one of the upgrades...
UnitTypes CvCity::allUpgradesAvailable(UnitTypes eUnit, int iUpgradeCount) const
{
PROFILE_FUNC();
UnitTypes eUpgradeUnit;
UnitTypes eTempUnit;
UnitTypes eLoopUnit;
UnitTypes eResult = NO_UNIT;
bool bUpgradeFound;
bool bUpgradeAvailable;
bool bUpgradeUnavailable;
int iI;
bool bHasCachedResult = false;
EnterCriticalSection(&m_cUpgradeCacheSection);
//OutputDebugString(CvString::format("allUpgradesAvailable for %d (recursion depth %d)\n", eUnit, iUpgradeCount).c_str());
if ( iUpgradeCount == 0 )
{
stdext::hash_map<UnitTypes,UnitTypes>::const_iterator itr = m_eCachedAllUpgradesResultsRoot.find(eUnit);
if ( itr != m_eCachedAllUpgradesResultsRoot.end() )
{
//OutputDebugString(CvString::format("\t...cached result %d\n", itr->second).c_str());
eResult = itr->second;
bHasCachedResult = true;
}
}
else
{
stdext::hash_map<UnitTypes,UnitTypes>::const_iterator itr = m_eCachedAllUpgradesResults.find(eUnit);
if ( itr != m_eCachedAllUpgradesResults.end() )
{
//OutputDebugString(CvString::format("\t...cached result %d\n", itr->second).c_str());
eResult = itr->second;
bHasCachedResult = true;
}
}
if ( !bHasCachedResult )
{
/************************************************************************************************/
/* REVDCM 04/16/10 phungus420 */
/* */
/* CanTrain Performance */
/************************************************************************************************/
CvUnitInfo& kUnit = GC.getUnitInfo(eUnit);
CvCivilizationInfo& kCivilization = GC.getCivilizationInfo(getCivilizationType());
FAssertMsg(eUnit != NO_UNIT, "eUnit is expected to be assigned (not NO_UNIT)");
if (iUpgradeCount <= GC.getNumUnitClassInfos())
{
eUpgradeUnit = NO_UNIT;
bUpgradeFound = false;
bUpgradeAvailable = false;
bUpgradeUnavailable = false;
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
if (kUnit.getUpgradeUnitClass(iI))
{
eLoopUnit = (UnitTypes)kCivilization.getCivilizationUnits(iI);
/************************************************************************************************/
/* REVDCM END Performance */
/************************************************************************************************/
if (eLoopUnit != NO_UNIT)
{
bUpgradeFound = true;
eTempUnit = allUpgradesAvailable(eLoopUnit, (iUpgradeCount + 1));
if (eTempUnit != NO_UNIT)
{
eUpgradeUnit = eTempUnit;
bUpgradeAvailable = true;
}
else
{
bUpgradeUnavailable = true;
}
}
}
}
if (iUpgradeCount > 0)
{
if (bUpgradeFound && bUpgradeAvailable)
{
FAssertMsg(eUpgradeUnit != NO_UNIT, "eUpgradeUnit is expected to be assigned (not NO_UNIT)");
eResult = eUpgradeUnit;
}
else if (canTrain(eUnit, false, false, false, true))
{
eResult = eUnit;
}
// Cache the result so that we don't have to recalculate it multiple times
//OutputDebugString(CvString::format("\t...caching result %d\n", eResult).c_str());
m_eCachedAllUpgradesResults[eUnit] = eResult;
}
else
{
if (bUpgradeFound && !bUpgradeUnavailable)
{
eResult = eUpgradeUnit;
}
// Cache the result so that we don't have to recalculate it multiple times
//OutputDebugString(CvString::format("\t...caching result %d\n", eResult).c_str());
m_eCachedAllUpgradesResultsRoot[eUnit] = eResult;
}
}
}
LeaveCriticalSection(&m_cUpgradeCacheSection);
return eResult;
}
int CvCity::getMaxNumWorldWonders() const
{
return GC.getCultureLevelInfo(getCultureLevel()).getMaxWorldWonders() + getWonderCapacityIncrement();
}
bool CvCity::isWorldWondersMaxed() const
{
if (GC.getGameINLINE().isOption(GAMEOPTION_ONE_CITY_CHALLENGE) && isHuman())
{
return false;
}
//if (GC.getDefineINT("MAX_WORLD_WONDERS_PER_CITY") == -1)
//{
// return false;
//}
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
if (GC.getGameINLINE().isOption(GAMEOPTION_UNLIMITED_WONDERS))
{
return false;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
//if (getNumWorldWonders() >= GC.getDefineINT("MAX_WORLD_WONDERS_PER_CITY"))
if (getNumWorldWonders() >= getMaxNumWorldWonders())
{
return true;
}
return false;
}
int CvCity::getMaxNumTeamWonders() const
{
return GC.getCultureLevelInfo(getCultureLevel()).getMaxTeamWonders();
}
bool CvCity::isTeamWondersMaxed() const
{
if (GC.getGameINLINE().isOption(GAMEOPTION_ONE_CITY_CHALLENGE) && isHuman())
{
return false;
}
//if (GC.getDefineINT("MAX_TEAM_WONDERS_PER_CITY") == -1)
//{
// return false;
//}
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
if (GC.getGameINLINE().isOption(GAMEOPTION_UNLIMITED_WONDERS))
{
return false;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
//if (getNumTeamWonders() >= GC.getDefineINT("MAX_TEAM_WONDERS_PER_CITY"))
if (getNumTeamWonders() >= getMaxNumTeamWonders())
{
return true;
}
return false;
}
int CvCity::getMaxNumNationalWonders() const
{
return (GC.getGameINLINE().isOption(GAMEOPTION_ONE_CITY_CHALLENGE) && isHuman()) ? GC.getCultureLevelInfo(getCultureLevel()).getMaxNationalWondersOCC() : GC.getCultureLevelInfo(getCultureLevel()).getMaxNationalWonders();
}
bool CvCity::isNationalWondersMaxed() const
{
//int iMaxNumWonders = (GC.getGameINLINE().isOption(GAMEOPTION_ONE_CITY_CHALLENGE) && isHuman()) ? GC.getDefineINT("MAX_NATIONAL_WONDERS_PER_CITY_FOR_OCC") : GC.getDefineINT("MAX_NATIONAL_WONDERS_PER_CITY");
int iMaxNumWonders = getMaxNumNationalWonders();
if (iMaxNumWonders == -1)
{
return false;
}
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
if (GC.getGameINLINE().isOption(GAMEOPTION_UNLIMITED_WONDERS))
{
return false;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (getNumNationalWonders() >= iMaxNumWonders)
{
return true;
}
return false;
}
bool CvCity::isBuildingsMaxed() const
{
if (GC.getGameINLINE().isOption(GAMEOPTION_ONE_CITY_CHALLENGE) && isHuman())
{
return false;
}
if (GC.getDefineINT("MAX_BUILDINGS_PER_CITY") == -1)
{
return false;
}
if (getNumBuildings() >= GC.getDefineINT("MAX_BUILDINGS_PER_CITY"))
{
return true;
}
return false;
}
bool CvCity::canTrainInternal(UnitTypes eUnit, bool bContinue, bool bTestVisible, bool bIgnoreCost, bool bIgnoreUpgrades) const
{
PROFILE("CvCity::canTrainInternal (units)");
if (eUnit == NO_UNIT)
{
return false;
}
if(GC.getUSE_CAN_TRAIN_CALLBACK(eUnit))
{
PYTHON_ACCESS_LOCK_SCOPE
PROFILE("canTrain.Python");
CyCity* pyCity = new CyCity((CvCity*)this);
CyArgsList argsList;
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
argsList.add(eUnit);
argsList.add(bContinue);
argsList.add(bTestVisible);
argsList.add(bIgnoreCost);
argsList.add(bIgnoreUpgrades);
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "canTrain", argsList.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return true;
}
}
if (!plot()->canTrain(eUnit, bContinue, bTestVisible))
{
return false;
}
/************************************************************************************************/
/* REVDCM 02/16/10 phungus420 */
/* */
/* CanTrain */
/************************************************************************************************/
if (isForceObsoleteUnitClassAvailable(eUnit))
{
return false;
}
if (!(isPlotTrainable(eUnit, bContinue, bTestVisible)))
{
return false;
}
/************************************************************************************************/
/* REVDCM END */
/************************************************************************************************/
/************************************************************************************************/
/* Afforess Start 6/17/10 */
/* */
/* */
/************************************************************************************************/
CvUnitInfo &kUnit = GC.getUnitInfo(eUnit);
if (kUnit.isForceUpgrade())
{
if(canUpgradeUnit(eUnit))
{
return false;
}
}
if (!bTestVisible)
{
ReligionTypes eStateReligion = GET_PLAYER(getOwnerINLINE()).getStateReligion();
if (kUnit.isRequiresStateReligionInCity())
{
if (NO_RELIGION == eStateReligion || !isHasReligion(eStateReligion))
{
return false;
}
}
}
if (GC.getGameINLINE().isOption(GAMEOPTION_ASSIMILATION))
{
UnitClassTypes eUnitClass = ((UnitClassTypes)(kUnit.getUnitClassType()));
if (GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(eUnitClass) != eUnit)
{
return false;
}
}
if (kUnit.getPrereqVicinityBonus() != NO_BONUS)
{
if (!hasVicinityBonus((BonusTypes)kUnit.getPrereqVicinityBonus()))
{
return false;
}
}
if (!bTestVisible)
{
bool bHasAnyVicinityBonus = false;
bool bRequiresAnyVicinityBonus = false;
for (int iI = 0; iI < GC.getNUM_UNIT_PREREQ_OR_BONUSES(); iI++)
{
if (kUnit.getPrereqOrVicinityBonuses(iI) != NO_BONUS)
{
bRequiresAnyVicinityBonus = true;
if (hasVicinityBonus((BonusTypes)kUnit.getPrereqOrVicinityBonuses(iI)))
{
bHasAnyVicinityBonus = true;
break;
}
}
}
if (bRequiresAnyVicinityBonus && !bHasAnyVicinityBonus)
{
return false;
}
}
if (GC.getGameINLINE().isOption(GAMEOPTION_REALISTIC_CORPORATIONS))
{
if (GET_PLAYER(getOwnerINLINE()).isNoForeignCorporations())
{
for (int iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
if (kUnit.getCorporationSpreads(iI) > 0)
{
return false;
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (!bIgnoreUpgrades)
{
if (allUpgradesAvailable(eUnit) != NO_UNIT)
{
return false;
}
}
if(GC.getUSE_CANNOT_TRAIN_CALLBACK(eUnit))
{
PYTHON_ACCESS_LOCK_SCOPE
CyCity *pyCity = new CyCity((CvCity*)this);
CyArgsList argsList2; // XXX
argsList2.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
argsList2.add(eUnit);
argsList2.add(bContinue);
argsList2.add(bTestVisible);
argsList2.add(bIgnoreCost);
argsList2.add(bIgnoreUpgrades);
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "cannotTrain", argsList2.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return false;
}
}
return true;
}
#ifdef CAN_TRAIN_CACHING
#ifdef _DEBUG
// Uncomment this for consistency testing of the canTrain cache
//#define VALIDATE_CAN_TRAIN_CACHE_CONSISTENCY
#endif
void CvCity::populateCanTrainCache(bool bUnconditional) const
{
PROFILE_FUNC();
MEMORY_TRACK_EXEMPT();
EnterCriticalSection(&m_cCanTrainCacheSection);
if ( bUnconditional || !m_canTrainCachePopulated )
{
clearCanTrainCache(true);
int iCount = 0;
for (int iI = 0; iI < GC.getNumUnitInfos(); iI++)
{
if (canTrain((UnitTypes)iI))
{
m_canTrainCacheUnits[(UnitTypes)iI] = true;
iCount++;
}
}
if ( iCount == 0 )
{
OutputDebugString("Nothing trainable!\n");
}
// Set it populated now so that the calls to canTrainInternal for the combat types
// below can use the already constructed cache values for units
m_canTrainCachePopulated = true;
m_canTrainCacheGloballyValid = true;
m_canTrainCacheDirty = false;
for (int iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
if (canTrainInternal((UnitCombatTypes)iI))
{
m_canTrainCache[(UnitCombatTypes)iI] = true;
}
}
}
LeaveCriticalSection(&m_cCanTrainCacheSection);
}
void CvCity::clearCanTrainCache(bool fullClear) const
{
EnterCriticalSection(&m_cUpgradeCacheSection);
m_eCachedAllUpgradesResults.clear();
m_eCachedAllUpgradesResultsRoot.clear();
LeaveCriticalSection(&m_cUpgradeCacheSection);
EnterCriticalSection(&m_cCanTrainCacheSection);
m_canTrainCache.clear();
if ( fullClear )
{
m_canTrainCachePopulated = false;
m_canTrainCacheUnits.clear();
}
else
{
m_canTrainCacheGloballyValid = false;
}
LeaveCriticalSection(&m_cCanTrainCacheSection);
}
void CvCity::invalidateCachedCanTrainForUnit(UnitCombatTypes eUnitCombat) const
{
PROFILE_FUNC();
if ( m_canTrainCachePopulated )
{
PROFILE("CvCity::invalidateCachedCanTrainForUnit");
EnterCriticalSection(&m_cCanTrainCacheSection);
if ( eUnitCombat == NO_UNITCOMBAT )
{
m_canTrainCacheDirty = true; // Entire map dirty
}
else
{
stdext::hash_map<UnitCombatTypes,bool>::iterator itr = m_canTrainCache.find(eUnitCombat);
if ( itr != m_canTrainCache.end() )
{
itr->second = false; // In map but with false => dirty
}
for(stdext::hash_map<UnitTypes,bool>::iterator unitItr = m_canTrainCacheUnits.begin(); unitItr != m_canTrainCacheUnits.end(); ++unitItr)
{
if ( unitItr->second )
{
CvUnitInfo& kUnit = GC.getUnitInfo(unitItr->first);
//TB SubCombat Mod Begin
if ( kUnit.hasUnitCombat(eUnitCombat) )
{
unitItr->second = false;
}
//TB SubCombat Mod End
}
}
}
LeaveCriticalSection(&m_cCanTrainCacheSection);
}
EnterCriticalSection(&m_cUpgradeCacheSection);
if ( eUnitCombat == NO_UNITCOMBAT )
{
m_eCachedAllUpgradesResults.clear();
m_eCachedAllUpgradesResultsRoot.clear();
}
else
{
stdext::hash_map<UnitTypes,UnitTypes>::iterator itr = m_eCachedAllUpgradesResults.begin();
while(itr != m_eCachedAllUpgradesResults.end())
{
bool bErased = false;
CvUnitInfo& kUnit = GC.getUnitInfo(itr->first);
//TB SubCombat Mod Begin
if ( kUnit.hasUnitCombat(eUnitCombat) )
//TB SubCombat Mod End
{
itr = m_eCachedAllUpgradesResults.erase(itr);
bErased = true;
}
if ( !bErased )
{
++itr;
}
}
stdext::hash_map<UnitTypes,UnitTypes>::iterator rootItr = m_eCachedAllUpgradesResultsRoot.begin();
while(rootItr != m_eCachedAllUpgradesResultsRoot.end())
{
bool bErased = false;
CvUnitInfo& kUnit = GC.getUnitInfo(rootItr->first);
//TB SubCombat Mod Begin
if ( kUnit.hasUnitCombat(eUnitCombat) )
//TB SubCombat Mod End
{
rootItr = m_eCachedAllUpgradesResultsRoot.erase(rootItr);
bErased = true;
}
if ( !bErased )
{
++rootItr;
}
}
}
LeaveCriticalSection(&m_cUpgradeCacheSection);
}
#endif
bool CvCity::canTrain(UnitCombatTypes eUnitCombat) const
{
#ifdef CAN_TRAIN_CACHING
if ( m_canTrainCachePopulated )
{
if ( m_canTrainCacheGloballyValid )
{
bool bHaveCachedResult;
bool bResult;
EnterCriticalSection(&m_cCanTrainCacheSection);
if ( m_canTrainCacheDirty )
{
// Needs repopulating
populateCanTrainCache();
}
stdext::hash_map<UnitCombatTypes,bool>::iterator itr = m_canTrainCache.find(eUnitCombat);
if ( itr == m_canTrainCache.end() )
{
#ifdef VALIDATE_CAN_TRAIN_CACHE_CONSISTENCY
if ( canTrainInternal(eUnitCombat) )
{
FAssertMsg(false, "Consistency check failure in canTrain cache - false negative\n");
}
#endif
bResult = false;
bHaveCachedResult = true;
}
else if ( itr->second )
{
#ifdef VALIDATE_CAN_TRAIN_CACHE_CONSISTENCY
if ( !canTrainInternal(eUnitCombat) )
{
FAssertMsg(false, "Consistency check failure in canTrain cache - false positive\n");
}
#endif
bResult = true;
bHaveCachedResult = true;
}
else
{
bHaveCachedResult = false; // In map but with false => recalculate
}
LeaveCriticalSection(&m_cCanTrainCacheSection);
if ( !bHaveCachedResult )
{
bResult = canTrainInternal(eUnitCombat);
EnterCriticalSection(&m_cCanTrainCacheSection);
if ( bResult )
{
m_canTrainCache[eUnitCombat] = true;
}
else
{
m_canTrainCache.erase(eUnitCombat);
}
LeaveCriticalSection(&m_cCanTrainCacheSection);
}
return bResult;
}
}
#endif
{
PROFILE("CvCity::canTrain.cacheMiss");
return canTrainInternal(eUnitCombat);
}
}
bool CvCity::canTrain(UnitTypes eUnit, bool bContinue, bool bTestVisible, bool bIgnoreCost, bool bIgnoreUpgrades) const
{
if ( !GET_PLAYER(getOwnerINLINE()).canTrain(eUnit, bContinue, bTestVisible, bIgnoreCost) )
{
return false;
}
else
#ifdef CAN_TRAIN_CACHING
if ( !bContinue && !bTestVisible && !bIgnoreCost && !bIgnoreUpgrades )
{
if ( m_canTrainCachePopulated )
{
bool bHaveCachedResult;
bool bResult;
EnterCriticalSection(&m_cCanTrainCacheSection);
if ( m_canTrainCacheDirty )
{
PROFILE("CvCity::canTrain.ProcessDirtyCache");
// Needs repopulating
populateCanTrainCache();
}
stdext::hash_map<UnitTypes,bool>::iterator itr = m_canTrainCacheUnits.find(eUnit);
if ( itr == m_canTrainCacheUnits.end() )
{
#ifdef VALIDATE_CAN_TRAIN_CACHE_CONSISTENCY
if ( canTrainInternal(eUnit) )
{
FAssertMsg(false, "Consistency check failure in canTrain cache - false negative\n");
}
#endif
bResult = false;
bHaveCachedResult = true;
}
else if ( itr->second )
{
#ifdef VALIDATE_CAN_TRAIN_CACHE_CONSISTENCY
if ( !canTrainInternal(eUnit) )
{
FAssertMsg(false, "Consistency check failure in canTrain cache - false positive\n");
}
#endif
bResult = true;
bHaveCachedResult = true;
}
else // In map but with false => recalculate
{
bHaveCachedResult = false;
}
LeaveCriticalSection(&m_cCanTrainCacheSection);
if ( !bHaveCachedResult )
{
bResult = canTrainInternal(eUnit);
EnterCriticalSection(&m_cCanTrainCacheSection);
if ( bResult )
{
m_canTrainCacheUnits[eUnit] = true;
}
else
{
m_canTrainCacheUnits.erase(eUnit);
}
LeaveCriticalSection(&m_cCanTrainCacheSection);
}
return bResult;
}
}
#endif
PROFILE("canTrain.NonStandard");
return canTrainInternal(eUnit, bContinue, bTestVisible, bIgnoreCost, bIgnoreUpgrades);
}
bool CvCity::canTrainInternal(UnitCombatTypes eUnitCombat) const
{
PROFILE_FUNC();
//TB SubCombat Mod Begin
int iI;
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
UnitTypes eUnit = (UnitTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI);
if (NO_UNIT != eUnit &&
GC.getUnitInfo(eUnit).hasUnitCombat(eUnitCombat) &&
canTrain(eUnit))
{
return true;
}
}
//TB SubCombat Mod end
return false;
}
// KOSHLING - cache can construct values
#ifdef _DEBUG
// Uncomment to add runtime results checking
//#define VALIDATE_CAN_CONSTRUCT_CACHE
#endif
void CvCity::FlushCanConstructCache(BuildingTypes eBuilding)
{
//OutputDebugString(CvString::format("[%d] FlushCanConstructCache (%d), workitem priority = %08lx\n", GetCurrentThreadId(), eBuilding, (m_workItem == NULL ? -1 : m_workItem->GetPriority())).c_str());
EnterCriticalSection(&m_cCanConstructCacheSection);
if ( eBuilding == NO_BUILDING )
{
SAFE_DELETE(m_bCanConstruct);
}
else if ( m_bCanConstruct != NULL )
{
(*m_bCanConstruct).erase(eBuilding);
}
LeaveCriticalSection(&m_cCanConstructCacheSection);
}
void CvCity::NoteBuildingNoLongerConstructable(BuildingTypes eBuilding) const
{
EnterCriticalSection(&m_cCanConstructCacheSection);
if ( m_bCanConstruct != NULL )
{
(*m_bCanConstruct)[eBuilding] = false;
}
LeaveCriticalSection(&m_cCanConstructCacheSection);
}
bool CvCity::canConstruct(BuildingTypes eBuilding, bool bContinue, bool bTestVisible, bool bIgnoreCost, bool bIgnoreAmount, bool bIgnoreBuildings, TechTypes eIgnoreTechReq, int* probabilityEverConstructable) const
{
PROFILE_FUNC();
if ( eBuilding == NO_BUILDING )
{
return false;
}
else if ( !bContinue && !bTestVisible && !bIgnoreCost && !bIgnoreAmount && !bIgnoreBuildings && eIgnoreTechReq == NO_TECH && probabilityEverConstructable == NULL)
{
bool bResult;
bool bHaveCachedResult;
EnterCriticalSection(&m_cCanConstructCacheSection);
if ( m_bCanConstruct == NULL )
{
MEMORY_TRACK_EXEMPT()
m_bCanConstruct = new std::map<int,bool>();
bHaveCachedResult = false;
}
else
{
std::map<int,bool>::const_iterator itr = m_bCanConstruct->find(eBuilding);
if ( itr == m_bCanConstruct->end() )
{
bHaveCachedResult = false;
}
else
{
bResult = itr->second;
bHaveCachedResult = true;
#ifdef VALIDATE_CAN_CONSTRUCT_CACHE
// Verify if required
if ( bResult != canConstructInternal(eBuilding, bContinue, bTestVisible, bIgnoreCost, bIgnoreAmount) )
{
MessageBox(NULL,"canConstruct cached result mismatch","cvGameCore",MB_OK);
FAssertMsg(false, "canConstruct cached result mismatch");
}
#endif
}
}
LeaveCriticalSection(&m_cCanConstructCacheSection);
if ( !bHaveCachedResult )
{
bResult = canConstructInternal(eBuilding, bContinue, bTestVisible, bIgnoreCost, bIgnoreAmount);
{
MEMORY_TRACK_EXEMPT()
EnterCriticalSection(&m_cCanConstructCacheSection);
if ( m_bCanConstruct == NULL )
{
MEMORY_TRACK_EXEMPT()
m_bCanConstruct = new std::map<int,bool>();
}
(*m_bCanConstruct)[eBuilding] = bResult;
LeaveCriticalSection(&m_cCanConstructCacheSection);
}
}
return bResult;
}
else
{
return canConstructInternal(eBuilding, bContinue, bTestVisible, bIgnoreCost, bIgnoreAmount, NO_BUILDINGCLASS, bIgnoreBuildings, eIgnoreTechReq, probabilityEverConstructable);
}
}
// KOSHLING - Can construct cache end
/************************************************************************************************/
/* Afforess Start 05/20/10 */
/* */
/* */
/************************************************************************************************/
bool CvCity::canConstructInternal(BuildingTypes eBuilding, bool bContinue, bool bTestVisible, bool bIgnoreCost, bool bIgnoreAmount, BuildingClassTypes withExtraBuildingClass, bool bIgnoreBuildings, TechTypes eIgnoreTechReq, int* probabilityEverConstructable) const
{
PROFILE_FUNC()
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
BuildingTypes ePrereqBuilding;
bool bRequiresBonus;
bool bNeedsBonus;
int iI;
CorporationTypes eCorporation;
/************************************************************************************************/
/* REVDCM 04/16/10 phungus420 */
/* */
/* CanConstruct Performance */
/************************************************************************************************/
CvCivilizationInfo& kCivilization = GC.getCivilizationInfo(getCivilizationType());
if ( probabilityEverConstructable != NULL )
{
*probabilityEverConstructable = 0;
}
if (eBuilding == NO_BUILDING)
{
return false;
}
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
if(GC.getUSE_CAN_CONSTRUCT_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyCity* pyCity = new CyCity((CvCity*)this);
CyArgsList argsList;
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
argsList.add(eBuilding);
argsList.add(bContinue);
argsList.add(bTestVisible);
argsList.add(bIgnoreCost);
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "canConstruct", argsList.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return true;
}
}
//ls612: No Holy City Tag
if (GC.getBuildingInfo(eBuilding).isNoHolyCity() && isHolyCity())
{
return false;
}
if (!(GET_PLAYER(getOwnerINLINE()).canConstruct(eBuilding, bContinue, bTestVisible, bIgnoreCost, eIgnoreTechReq, probabilityEverConstructable)))
{
return false;
}
/************************************************************************************************/
/* Afforess Start 05/20/10 */
/* */
/* */
/************************************************************************************************/
if (!bIgnoreAmount)
{
if (getNumBuilding(eBuilding) >= GC.getCITY_MAX_NUM_BUILDINGS())
{
return false;
}
}
if (isDisabledBuilding(eBuilding))
{
return false;
}
if (GC.getGameINLINE().isOption(GAMEOPTION_ASSIMILATION))
{
BuildingClassTypes eBuildingClass = ((BuildingClassTypes)(GC.getBuildingInfo(eBuilding).getBuildingClassType()));
if (GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(eBuildingClass) != eBuilding)
{
return false;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (kBuilding.isPrereqReligion())
{
if (getReligionCount() > 0)
{
if ( probabilityEverConstructable != NULL )
{
*probabilityEverConstructable = 80;
}
return false;
}
}
if (kBuilding.isStateReligion())
{
ReligionTypes eStateReligion = GET_PLAYER(getOwnerINLINE()).getStateReligion();
if (NO_RELIGION == eStateReligion || !isHasReligion(eStateReligion))
{
if ( probabilityEverConstructable != NULL )
{
*probabilityEverConstructable = 80;
}
return false;
}
}
if (kBuilding.getPrereqReligion() != NO_RELIGION)
{
if (!(isHasReligion((ReligionTypes)(kBuilding.getPrereqReligion()))))
{
if ( probabilityEverConstructable != NULL )
{
*probabilityEverConstructable = 50;
}
return false;
}
}
eCorporation = (CorporationTypes)kBuilding.getPrereqCorporation();
if (eCorporation != NO_CORPORATION)
{
if (!isHasCorporation(eCorporation))
{
if ( probabilityEverConstructable != NULL )
{
*probabilityEverConstructable = 30;
}
return false;
}
}
eCorporation = (CorporationTypes)kBuilding.getFoundsCorporation();
if (eCorporation != NO_CORPORATION)
{
if (GC.getGameINLINE().isCorporationFounded(eCorporation))
{
return false;
}
for (int iCorporation = 0; iCorporation < GC.getNumCorporationInfos(); ++iCorporation)
{
if (isHeadquarters((CorporationTypes)iCorporation))
{
if (GC.getGameINLINE().isCompetingCorporation((CorporationTypes)iCorporation, eCorporation))
{
return false;
}
}
}
}
if (!isValidBuildingLocation(eBuilding))
{
return false;
}
if (kBuilding.isGovernmentCenter())
{
if (isGovernmentCenter())
{
if ( probabilityEverConstructable != NULL )
{
*probabilityEverConstructable = 10;
}
return false;
}
}
if (!bTestVisible)
{
if (!bContinue)
{
if (getFirstBuildingOrder(eBuilding) != -1)
{
return false;
}
}
if (!(GC.getBuildingClassInfo((BuildingClassTypes)(kBuilding.getBuildingClassType())).isNoLimit()))
{
if (isWorldWonderClass((BuildingClassTypes)(kBuilding.getBuildingClassType())))
{
if (isWorldWondersMaxed())
{
return false;
}
}
else if (isTeamWonderClass((BuildingClassTypes)(kBuilding.getBuildingClassType())))
{
if (isTeamWondersMaxed())
{
return false;
}
}
else if (isNationalWonderClass((BuildingClassTypes)(kBuilding.getBuildingClassType())))
{
if (isNationalWondersMaxed())
{
return false;
}
}
else
{
if (isBuildingsMaxed())
{
return false;
}
}
}
if (kBuilding.getHolyCity() != NO_RELIGION)
{
if (!isHolyCity(((ReligionTypes)(kBuilding.getHolyCity()))))
{
return false;
}
}
if (kBuilding.getPrereqAndBonus() != NO_BONUS)
{
if (!hasBonus((BonusTypes)kBuilding.getPrereqAndBonus()))
{
if ( probabilityEverConstructable != NULL )
{
*probabilityEverConstructable = 50;
}
return false;
}
}
eCorporation = (CorporationTypes)kBuilding.getFoundsCorporation();
if (eCorporation != NO_CORPORATION)
{
if (GC.getGameINLINE().isCorporationFounded(eCorporation))
{
return false;
}
if (GET_PLAYER(getOwnerINLINE()).isNoCorporations())
{
return false;
}
/************************************************************************************************/
/* Afforess Start 02/17/10 */
/* */
/* */
/************************************************************************************************/
bool bValid = false;
bool bRequiresBonus = false;
for (int i = 0; i < GC.getNUM_CORPORATION_PREREQ_BONUSES(); ++i)
{
BonusTypes eBonus = (BonusTypes)GC.getCorporationInfo(eCorporation).getPrereqBonus(i);
if (NO_BONUS != eBonus)
{
bRequiresBonus = true;
if (hasBonus(eBonus))
{
bValid = true;
break;
}
}
}
if (!bValid && bRequiresBonus)
{
if ( probabilityEverConstructable != NULL )
{
*probabilityEverConstructable = 30;
}
return false;
}
if (!(*getPropertiesConst() <= *(kBuilding.getPrereqMaxProperties())))
return false;
if (!(*getPropertiesConst() >= *(kBuilding.getPrereqMinProperties())))
return false;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
if (plot()->getLatitude() > kBuilding.getMaxLatitude())
{
return false;
}
if (plot()->getLatitude() < kBuilding.getMinLatitude())
{
return false;
}
/************************************************************************************************/
/* Afforess Start 06/13/10 */
/* */
/* */
/************************************************************************************************/
if (kBuilding.isBuildOnlyOnPeaks())
{
if (!(plot()->isPeak()))
{
return false;
}
}
int iPrereqPopulation = std::max(kBuilding.getPrereqPopulation(), getNumPopulationEmployed() + kBuilding.getNumPopulationEmployed() + 1);
if (iPrereqPopulation > 0)
{
if (getPopulation() < iPrereqPopulation)
{
if ( probabilityEverConstructable != NULL )
{
*probabilityEverConstructable = 50;
}
return false;
}
}
if (kBuilding.getPrereqCultureLevel() != NO_CULTURELEVEL)
{
if (getCultureLevel() < kBuilding.getPrereqCultureLevel())
{
if ( probabilityEverConstructable != NULL )
{
*probabilityEverConstructable = 50;
}
return false;
}
}
if (kBuilding.getPrereqAnyoneBuildingClass() != NO_BUILDINGCLASS)
{
BuildingClassTypes ePrereqBuildingClass = (BuildingClassTypes)kBuilding.getPrereqAnyoneBuildingClass();
if (GC.getGameINLINE().getBuildingClassCreatedCount(ePrereqBuildingClass) == 0)
{
return false;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
bRequiresBonus = false;
bNeedsBonus = true;
for (iI = 0; iI < GC.getNUM_BUILDING_PREREQ_OR_BONUSES(); iI++)
{
if (kBuilding.getPrereqOrBonuses(iI) != NO_BONUS)
{
bRequiresBonus = true;
if (hasBonus((BonusTypes)kBuilding.getPrereqOrBonuses(iI)))
{
bNeedsBonus = false;
}
}
}
if (bRequiresBonus && bNeedsBonus)
{
if ( probabilityEverConstructable != NULL )
{
*probabilityEverConstructable = 50;
}
return false;
}
/************************************************************************************************/
/* Afforess Start 06/15/10 */
/* */
/* */
/************************************************************************************************/
}
//Hide Buildings that shouldn't appear in the early game and require other buildings
bool bOldTestVisible = bTestVisible;
if (kBuilding.getPrereqAndTech() == NO_TECH && kBuilding.getObsoleteTech() == NO_TECH)
{
bTestVisible = false;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
/************************************************************************************************/
/* Koshling - moved this so non-met vicinity required buildings get hidden in city screen even */
/* when not ordinarily hiding uncosntructable (since you cannot influence your vicinity bonuses */
/* so its not useful information for the most part */
/* */
/* Afforess Start 06/13/10 */
/* */
/* */
/************************************************************************************************/
if (kBuilding.getPrereqVicinityBonus() != NO_BONUS)
{
if (!hasVicinityBonus((BonusTypes)kBuilding.getPrereqVicinityBonus()))
{
if ( probabilityEverConstructable != NULL )
{
*probabilityEverConstructable = 5;
}
return false;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (!bTestVisible && !bIgnoreBuildings)
{
if (kBuilding.isBuildingClassNeededInCity(NO_BUILDINGCLASS))
{
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
if (kBuilding.isBuildingClassNeededInCity(iI))
{
ePrereqBuilding = (BuildingTypes)(kCivilization.getCivilizationBuildings(iI));
/************************************************************************************************/
/* REVDCM END Peformance */
/************************************************************************************************/
if (ePrereqBuilding != NO_BUILDING)
{
if (0 == getNumBuilding(ePrereqBuilding) /* && (bContinue || (getFirstBuildingOrder(ePrereqBuilding) == -1))*/ &&
(BuildingClassTypes)iI != withExtraBuildingClass)
{
if ( probabilityEverConstructable != NULL )
{
*probabilityEverConstructable = 25;
}
return false;
}
}
}
}
}
}
/************************************************************************************************/
/* Afforess Start 06/15/10 */
/* */
/* */
/************************************************************************************************/
bTestVisible = bOldTestVisible;
if (!bTestVisible && !bIgnoreBuildings)
{
if (kBuilding.isPrereqNotBuildingClass(NO_BUILDINGCLASS))
{
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
BuildingTypes eNotPrereqBuilding;
if (kBuilding.isPrereqNotBuildingClass(iI))
{
eNotPrereqBuilding = ((BuildingTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iI)));
if (eNotPrereqBuilding != NO_BUILDING)
{
if (getNumActiveBuilding(eNotPrereqBuilding) > 0)
{
return false;
}
}
}
}
}
bool bValid = false;
bool bRequires = false;
if (kBuilding.isPrereqOrBuildingClass(NO_BUILDINGCLASS))
{
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
if (kBuilding.isPrereqOrBuildingClass(iI))
{
bRequires = true;
ePrereqBuilding = ((BuildingTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iI)));
if (ePrereqBuilding != NO_BUILDING)
{
if (withExtraBuildingClass == (BuildingClassTypes)iI || getNumActiveBuilding(ePrereqBuilding) > 0)
{
bValid = true;
break;
}
}
}
}
}
if (bRequires && !bValid)
{
if ( probabilityEverConstructable != NULL )
{
*probabilityEverConstructable = 25;
}
return false;
}
if (kBuilding.isPrereqPower())
{
if(!isPower())
{
if ( probabilityEverConstructable != NULL )
{
*probabilityEverConstructable = 25;
}
return false;
}
}
}
//Can not construct replaced buildings.
if (kBuilding.isReplaceBuildingClass(NO_BUILDINGCLASS))
{
BuildingTypes eLoopBuilding;
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
eLoopBuilding = (BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iI);
if (eLoopBuilding != NO_BUILDING)
{
if (kBuilding.isReplaceBuildingClass(iI))
{
if (getNumActiveBuilding(eLoopBuilding) > 0 || (GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_HIDE_REPLACED_BUILDINGS) && canConstruct(eLoopBuilding, true, false, false, true)))
{
return false;
}
}
}
}
}
// Koshling - always hide things not buildable due to vicinity bonuses - its
// not really useful to see them
//if (!bTestVisible)
{
bool bHasAnyVicinityBonus = false;
bool bRequiresAnyVicinityBonus = false;
for (iI = 0; iI < GC.getNUM_BUILDING_PREREQ_OR_BONUSES(); iI++)
{
if (kBuilding.getPrereqOrVicinityBonuses(iI) != NO_BONUS)
{
bRequiresAnyVicinityBonus = true;
if (hasVicinityBonus((BonusTypes)kBuilding.getPrereqOrVicinityBonuses(iI)))
{
bHasAnyVicinityBonus = true;
break;
}
}
}
if (bRequiresAnyVicinityBonus && !bHasAnyVicinityBonus)
{
if ( probabilityEverConstructable != NULL )
{
*probabilityEverConstructable = 5;
}
return false;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (!bTestVisible && kBuilding.getConstructCondition())
{
if (!kBuilding.getConstructCondition()->evaluate(const_cast<CvGameObjectCity*>(getGameObjectConst()))) // Const wegcasten ist hier ok da evaluate nicht wirklich etwas �ndert
{
return false;
}
}
if(GC.getUSE_CANNOT_CONSTRUCT_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyCity *pyCity = new CyCity((CvCity*)this);
CyArgsList argsList2; // XXX
argsList2.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
argsList2.add(eBuilding);
argsList2.add(bContinue);
argsList2.add(bTestVisible);
argsList2.add(bIgnoreCost);
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "cannotConstruct", argsList2.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return false;
}
}
return true;
}
bool CvCity::canCreate(ProjectTypes eProject, bool bContinue, bool bTestVisible) const
{
/************************************************************************************************/
/* Afforess Start 12/21/09 */
/* */
/* */
/************************************************************************************************/
if(GC.getUSE_CAN_CREATE_PROJECT_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyCity* pyCity = new CyCity((CvCity*)this);
CyArgsList argsList;
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
argsList.add(eProject);
argsList.add(bContinue);
argsList.add(bTestVisible);
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "canCreate", argsList.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return true;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (!(GET_PLAYER(getOwnerINLINE()).canCreate(eProject, bContinue, bTestVisible)))
{
return false;
}
/************************************************************************************************/
/* Afforess Start 12/21/09 */
/* */
/* */
/************************************************************************************************/
if(GC.getUSE_CANNOT_CREATE_PROJECT_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyCity* pyCity = new CyCity((CvCity*)this);
CyArgsList argsList2; // XXX
argsList2.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
argsList2.add(eProject);
argsList2.add(bContinue);
argsList2.add(bTestVisible);
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "cannotCreate", argsList2.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return false;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return true;
}
bool CvCity::canMaintain(ProcessTypes eProcess, bool bContinue) const
{
/************************************************************************************************/
/* Afforess Start 12/21/09 */
/* */
/* */
/************************************************************************************************/
if(GC.getUSE_CAN_MAINTAIN_PROCESS_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyCity* pyCity = new CyCity((CvCity*)this);
CyArgsList argsList;
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
argsList.add(eProcess);
argsList.add(bContinue);
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "canMaintain", argsList.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return true;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (!(GET_PLAYER(getOwnerINLINE()).canMaintain(eProcess, bContinue)))
{
return false;
}
/************************************************************************************************/
/* Afforess Start 12/21/09 */
/* */
/* */
/************************************************************************************************/
if(GC.getUSE_CANNOT_MAINTAIN_PROCESS_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyCity* pyCity = new CyCity((CvCity*)this);
CyArgsList argsList2; // XXX
argsList2.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
argsList2.add(eProcess);
argsList2.add(bContinue);
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "cannotMaintain", argsList2.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return false;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return true;
}
bool CvCity::canJoin() const
{
return true;
}
int CvCity::getFoodTurnsLeft() const
{
int iFoodLeft;
int iTurnsLeft;
iFoodLeft = (growthThreshold() - getFood());
if (foodDifference() <= 0)
{
return iFoodLeft;
}
iTurnsLeft = (iFoodLeft / foodDifference());
if ((iTurnsLeft * foodDifference()) < iFoodLeft)
{
iTurnsLeft++;
}
return std::max(1, iTurnsLeft);
}
bool CvCity::isProduction() const
{
return (headOrderQueueNode() != NULL);
}
bool CvCity::isProductionLimited() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
return isLimitedUnitClass((UnitClassTypes)(GC.getUnitInfo((UnitTypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1)).getUnitClassType()));
break;
case ORDER_CONSTRUCT:
return isLimitedWonderClass((BuildingClassTypes)(GC.getBuildingInfo((BuildingTypes)(pOrderNode->m_data.iData1)).getBuildingClassType()));
break;
case ORDER_CREATE:
return isLimitedProject((ProjectTypes)(pOrderNode->m_data.iData1));
break;
case ORDER_MAINTAIN:
case ORDER_LIST:
break;
default:
FAssertMsg(false, "pOrderNode->m_data.eOrderType failed to match a valid option");
break;
}
}
return false;
}
bool CvCity::isProductionUnit() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
return (pOrderNode->m_data.eOrderType == ORDER_TRAIN);
}
return false;
}
bool CvCity::isProductionBuilding() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
return (pOrderNode->m_data.eOrderType == ORDER_CONSTRUCT);
}
return false;
}
bool CvCity::isProductionProject() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
return (pOrderNode->m_data.eOrderType == ORDER_CREATE);
}
return false;
}
bool CvCity::isProductionProcess() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
return (pOrderNode->m_data.eOrderType == ORDER_MAINTAIN);
}
return false;
}
bool CvCity::canContinueProduction(OrderData order)
{
switch (order.eOrderType)
{
case ORDER_CONSTRUCT:
return canConstruct((BuildingTypes)(order.iData1), true);
break;
case ORDER_TRAIN:
// xUPT: Prevent the city to build an unit if the city area is already at max unit-per-tile (dbkblk, 2015-02)
// Before to loop, check if the unit to build is a land military then civilian and if it's not a sea unit (for which space is assumed unlimited) and test if the area is saturated.
if (GC.getGameINLINE().getModderGameOption(MODDERGAMEOPTION_MAX_UNITS_PER_TILES) > 0)
{
if ((GC.getUnitInfo((UnitTypes)EXTERNAL_ORDER_IDATA(order.iData1)).isMilitaryProduction()) && (GC.getUnitInfo((UnitTypes)EXTERNAL_ORDER_IDATA(order.iData1)).getDomainType() == DOMAIN_LAND)){
if (m_bAreaSaturatedOfLandMilitaryUnits){
return false;
break;
}
}
else if ((!GC.getUnitInfo((UnitTypes)EXTERNAL_ORDER_IDATA(order.iData1)).isMilitaryProduction()) && (GC.getUnitInfo((UnitTypes)EXTERNAL_ORDER_IDATA(order.iData1)).getDomainType() != DOMAIN_SEA)){
if (m_bAreaSaturatedOfCivilianUnits){
return false;
break;
}
}
}
// If not saturated, do the standard way
return canTrain((UnitTypes)EXTERNAL_ORDER_IDATA(order.iData1), true);
break;
case ORDER_CREATE:
return canCreate((ProjectTypes)(order.iData1), true);
break;
case ORDER_MAINTAIN:
return canMaintain((ProcessTypes)(order.iData1), true);
break;
case ORDER_LIST:
return true;
break;
default:
FAssertMsg(false, "order.eOrderType failed to match a valid option");
break;
}
return false;
}
int CvCity::getProductionExperience(UnitTypes eUnit)
{
int iExperience;
iExperience = getFreeExperience();
iExperience += GET_PLAYER(getOwnerINLINE()).getFreeExperience();
if (eUnit != NO_UNIT)
{
if (GC.getUnitInfo(eUnit).getUnitCombatType() != NO_UNITCOMBAT)
{
iExperience += getUnitCombatFreeExperience((UnitCombatTypes)(GC.getUnitInfo(eUnit).getUnitCombatType()));
}
iExperience += getDomainFreeExperience((DomainTypes)(GC.getUnitInfo(eUnit).getDomainType()));
iExperience += getSpecialistFreeExperience();
}
if (GET_PLAYER(getOwnerINLINE()).getStateReligion() != NO_RELIGION)
{
if (isHasReligion(GET_PLAYER(getOwnerINLINE()).getStateReligion()))
{
iExperience += GET_PLAYER(getOwnerINLINE()).getStateReligionFreeExperience();
}
}
/************************************************************************************************/
/* SUPER_SPIES 04/05/08 Faichele */
/* originally by TSHEEP */
/* */
/************************************************************************************************/
if (eUnit != NO_UNIT)
{
if(GC.getUnitInfo(eUnit).isSpy() && GC.isSS_ENABLED())
{
iExperience = 0;
if (GC.getUnitInfo(eUnit).getUnitCombatType() != NO_UNITCOMBAT)
{
iExperience += getUnitCombatFreeExperience((UnitCombatTypes)(GC.getUnitInfo(eUnit).getUnitCombatType()));
}
}
}
/************************************************************************************************/
/* SUPER_SPIES END */
/************************************************************************************************/
return std::max(0, iExperience);
}
void CvCity::addProductionExperience(CvUnit* pUnit, bool bConscript)
{
PROFILE_FUNC();
int iI;
if (pUnit->canAcquirePromotionAny())
{
pUnit->changeExperience(getProductionExperience(pUnit->getUnitType()) / ((bConscript) ? 2 : 1));
}
for (iI = 0; iI < GC.getNumPromotionInfos(); iI++)
{
if (isFreePromotion((PromotionTypes)iI))
{
if ((pUnit->getUnitCombatType() != NO_UNITCOMBAT) && GC.getPromotionInfo((PromotionTypes)iI).getUnitCombat(pUnit->getUnitCombatType()))
{
pUnit->setHasPromotion(((PromotionTypes)iI), true);
}
}
}
// Koshling - testing promotion readiness here is uneccessary since CvUnit::doTurn
// will do it. It is alo now dangerous to do it here (or indeed anywhere but controlled
// places) becaue it is not thread-safe and needs to run strictly on the main thread
//pUnit->testPromotionReady();
}
UnitTypes CvCity::getProductionUnit() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
return ((UnitTypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1));
break;
case ORDER_CONSTRUCT:
case ORDER_CREATE:
case ORDER_MAINTAIN:
case ORDER_LIST:
break;
default:
FAssertMsg(false, "pOrderNode->m_data.eOrderType failed to match a valid option");
break;
}
}
return NO_UNIT;
}
UnitAITypes CvCity::getProductionUnitAI() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
return ((UnitAITypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData2));
break;
case ORDER_CONSTRUCT:
case ORDER_CREATE:
case ORDER_MAINTAIN:
case ORDER_LIST:
break;
default:
FAssertMsg(false, "pOrderNode->m_data.eOrderType failed to match a valid option");
break;
}
}
return NO_UNITAI;
}
BuildingTypes CvCity::getProductionBuilding() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
break;
case ORDER_CONSTRUCT:
return ((BuildingTypes)(pOrderNode->m_data.iData1));
break;
case ORDER_CREATE:
case ORDER_MAINTAIN:
case ORDER_LIST:
break;
default:
FAssertMsg(false, "pOrderNode->m_data.eOrderType failed to match a valid option");
break;
}
}
return NO_BUILDING;
}
ProjectTypes CvCity::getProductionProject() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
case ORDER_CONSTRUCT:
break;
case ORDER_CREATE:
return ((ProjectTypes)(pOrderNode->m_data.iData1));
break;
case ORDER_MAINTAIN:
case ORDER_LIST:
break;
default:
FAssertMsg(false, "pOrderNode->m_data.eOrderType failed to match a valid option");
break;
}
}
return NO_PROJECT;
}
ProcessTypes CvCity::getProductionProcess() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
case ORDER_CONSTRUCT:
case ORDER_CREATE:
case ORDER_LIST:
break;
case ORDER_MAINTAIN:
return ((ProcessTypes)(pOrderNode->m_data.iData1));
break;
default:
FAssertMsg(false, "pOrderNode->m_data.eOrderType failed to match a valid option");
break;
}
}
return NO_PROCESS;
}
const wchar* CvCity::getProductionName() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
return GC.getUnitInfo((UnitTypes) EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1)).getDescription(getCivilizationType());
break;
case ORDER_CONSTRUCT:
return GC.getBuildingInfo((BuildingTypes) pOrderNode->m_data.iData1).getDescription();
break;
case ORDER_CREATE:
return GC.getProjectInfo((ProjectTypes) pOrderNode->m_data.iData1).getDescription();
break;
case ORDER_MAINTAIN:
return GC.getProcessInfo((ProcessTypes) pOrderNode->m_data.iData1).getDescription();
break;
case ORDER_LIST:
break; // It is never at the head of the list
default:
FAssertMsg(false, "pOrderNode->m_data.eOrderType failed to match a valid option");
break;
}
}
return L"";
}
int CvCity::getGeneralProductionTurnsLeft() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
return getProductionTurnsLeft((UnitTypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1), 0);
break;
case ORDER_CONSTRUCT:
return getProductionTurnsLeft((BuildingTypes)pOrderNode->m_data.iData1, 0);
break;
case ORDER_CREATE:
return getProductionTurnsLeft((ProjectTypes)pOrderNode->m_data.iData1, 0);
break;
case ORDER_MAINTAIN:
case ORDER_LIST:
return 0;
break;
default:
FAssertMsg(false, "pOrderNode->m_data.eOrderType failed to match a valid option");
break;
}
}
return 0;
}
const wchar* CvCity::getProductionNameKey() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
return GC.getUnitInfo((UnitTypes) EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1)).getTextKeyWide();
break;
case ORDER_CONSTRUCT:
return GC.getBuildingInfo((BuildingTypes) pOrderNode->m_data.iData1).getTextKeyWide();
break;
case ORDER_CREATE:
return GC.getProjectInfo((ProjectTypes) pOrderNode->m_data.iData1).getTextKeyWide();
break;
case ORDER_MAINTAIN:
return GC.getProcessInfo((ProcessTypes) pOrderNode->m_data.iData1).getTextKeyWide();
break;
case ORDER_LIST:
break; // It is never at the head of the list
default:
FAssertMsg(false, "pOrderNode->m_data.eOrderType failed to match a valid option");
break;
}
}
return L"";
}
bool CvCity::isFoodProduction() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
return isFoodProduction((UnitTypes)(EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1)));
break;
case ORDER_CONSTRUCT:
case ORDER_CREATE:
case ORDER_MAINTAIN:
case ORDER_LIST:
break;
default:
FAssertMsg(false, "pOrderNode->m_data.eOrderType failed to match a valid option");
break;
}
}
return false;
}
bool CvCity::isFoodProduction(UnitTypes eUnit) const
{
if (GC.getUnitInfo(eUnit).isFoodProduction())
{
return true;
}
if (GET_PLAYER(getOwnerINLINE()).isMilitaryFoodProduction())
{
if (GC.getUnitInfo(eUnit).isMilitaryProduction())
{
return true;
}
}
return false;
}
int CvCity::getFirstUnitOrder(UnitTypes eUnit) const
{
int iCount = 0;
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
while (pOrderNode != NULL)
{
if (pOrderNode->m_data.eOrderType == ORDER_TRAIN)
{
if (EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1) == eUnit)
{
return iCount;
}
}
iCount++;
pOrderNode = nextOrderQueueNode(pOrderNode);
}
return -1;
}
int CvCity::getFirstBuildingOrder(BuildingTypes eBuilding) const
{
int iCount = 0;
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
while (pOrderNode != NULL)
{
if (pOrderNode->m_data.eOrderType == ORDER_CONSTRUCT)
{
if (pOrderNode->m_data.iData1 == eBuilding)
{
return iCount;
}
}
iCount++;
pOrderNode = nextOrderQueueNode(pOrderNode);
}
return -1;
}
int CvCity::getFirstProjectOrder(ProjectTypes eProject) const
{
int iCount = 0;
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
while (pOrderNode != NULL)
{
if (pOrderNode->m_data.eOrderType == ORDER_CREATE)
{
if (pOrderNode->m_data.iData1 == eProject)
{
return iCount;
}
}
iCount++;
pOrderNode = nextOrderQueueNode(pOrderNode);
}
return -1;
}
int CvCity::getNumTrainUnitAI(UnitAITypes eUnitAI) const
{
int iCount = 0;
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
while (pOrderNode != NULL)
{
if (pOrderNode->m_data.eOrderType == ORDER_TRAIN)
{
if (EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData2) == eUnitAI)
{
iCount++;
}
}
pOrderNode = nextOrderQueueNode(pOrderNode);
}
return iCount;
}
int CvCity::getProduction() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
return getUnitProduction((UnitTypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1));
break;
case ORDER_CONSTRUCT:
return getBuildingProduction((BuildingTypes)(pOrderNode->m_data.iData1));
break;
case ORDER_CREATE:
return getProjectProduction((ProjectTypes)(pOrderNode->m_data.iData1));
break;
case ORDER_MAINTAIN:
case ORDER_LIST:
break;
default:
FAssertMsg(false, "pOrderNode->m_data.eOrderType failed to match a valid option");
break;
}
}
return 0;
}
int CvCity::getProductionNeeded() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
return getProductionNeeded((UnitTypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1));
break;
case ORDER_CONSTRUCT:
return getProductionNeeded((BuildingTypes)(pOrderNode->m_data.iData1));
break;
case ORDER_CREATE:
return getProductionNeeded((ProjectTypes)(pOrderNode->m_data.iData1));
break;
case ORDER_MAINTAIN:
case ORDER_LIST:
break;
default:
FAssertMsg(false, "pOrderNode->m_data.eOrderType failed to match a valid option");
break;
}
}
return MAX_INT;
}
int CvCity::getProductionNeeded(UnitTypes eUnit) const
{
return GET_PLAYER(getOwnerINLINE()).getProductionNeeded(eUnit);
}
int CvCity::getProductionNeeded(BuildingTypes eBuilding) const
{
int iProductionNeeded = GET_PLAYER(getOwnerINLINE()).getProductionNeeded(eBuilding);
// Python cost modifier
if (GC.getUSE_GET_BUILDING_COST_MOD_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyArgsList argsList;
argsList.add(getOwnerINLINE()); // Player ID
argsList.add(getID()); // City ID
argsList.add(eBuilding); // Building ID
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "getBuildingCostMod", argsList.makeFunctionArgs(), &lResult);
if (lResult > 1)
{
iProductionNeeded *= lResult;
iProductionNeeded /= 100;
}
}
return iProductionNeeded;
}
int CvCity::getProductionNeeded(ProjectTypes eProject) const
{
return GET_PLAYER(getOwnerINLINE()).getProductionNeeded(eProject);
}
int CvCity::getProductionTurnsLeft() const
{
return getQueueNodeProductionTurnsLeft(headOrderQueueNode());
}
int CvCity::getQueueNodeProductionTurnsLeft(CLLNode<OrderData>* pOrderNode, int iIndex) const
{
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
return getProductionTurnsLeft(((UnitTypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1)), iIndex);
break;
case ORDER_CONSTRUCT:
return getProductionTurnsLeft(((BuildingTypes)(pOrderNode->m_data.iData1)), iIndex);
break;
case ORDER_CREATE:
return getProductionTurnsLeft(((ProjectTypes)(pOrderNode->m_data.iData1)), iIndex);
break;
case ORDER_MAINTAIN:
break;
case ORDER_LIST:
return 0;
break;
default:
FAssertMsg(false, "pOrderNode->m_data.eOrderType failed to match a valid option");
break;
}
}
return MAX_INT;
}
int CvCity::getProductionTurnsLeft(UnitTypes eUnit, int iNum) const
{
int iProduction;
int iFirstUnitOrder;
int iProductionNeeded;
int iProductionModifier;
iProduction = 0;
iFirstUnitOrder = getFirstUnitOrder(eUnit);
if ((iFirstUnitOrder == -1) || (iFirstUnitOrder == iNum))
{
iProduction += getUnitProduction(eUnit);
}
iProductionNeeded = getProductionNeeded(eUnit);
iProductionModifier = getProductionModifier(eUnit);
return getProductionTurnsLeft(iProductionNeeded, iProduction, getProductionDifference(iProductionNeeded, iProduction, iProductionModifier, isFoodProduction(eUnit), (iNum == 0)), getProductionDifference(iProductionNeeded, iProduction, iProductionModifier, isFoodProduction(eUnit), false));
}
int CvCity::getTotalProductionQueueTurnsLeft(void) const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
int iResult = 0;
int iIndex = 0;
while(pOrderNode != NULL)
{
iResult += getQueueNodeProductionTurnsLeft( pOrderNode, iIndex++ );
pOrderNode = nextOrderQueueNode(pOrderNode);
}
return iResult;
}
int CvCity::numQueuedUnits(UnitAITypes eUnitAI, CvPlot* pDestPlot)
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
int iResult = 0;
while(pOrderNode != NULL)
{
if ( pOrderNode->m_data.eOrderType == ORDER_TRAIN &&
INTERNAL_AUXILIARY_ORDER_IDATA(pOrderNode->m_data.iData1) == GC.getMapINLINE().plotNumINLINE(pDestPlot->getX_INLINE(), pDestPlot->getY_INLINE()) &&
((INTERNAL_AUXILIARY_ORDER_IDATA(pOrderNode->m_data.iData2) & 0xFF) == (eUnitAI & 0xFF)))
{
iResult++;
}
pOrderNode = nextOrderQueueNode(pOrderNode);
}
return iResult;
}
int CvCity::getProductionTurnsLeft(BuildingTypes eBuilding, int iNum) const
{
int iProduction;
int iFirstBuildingOrder;
int iProductionNeeded;
int iProductionModifier;
iProduction = 0;
iFirstBuildingOrder = getFirstBuildingOrder(eBuilding);
if ((iFirstBuildingOrder == -1) || (iFirstBuildingOrder == iNum))
{
iProduction += getBuildingProduction(eBuilding);
}
iProductionNeeded = getProductionNeeded(eBuilding);
iProductionModifier = getProductionModifier(eBuilding);
return getProductionTurnsLeft(iProductionNeeded, iProduction, getProductionDifference(iProductionNeeded, iProduction, iProductionModifier, false, (iNum == 0)), getProductionDifference(iProductionNeeded, iProduction, iProductionModifier, false, false));
}
int CvCity::getProductionTurnsLeft(ProjectTypes eProject, int iNum) const
{
int iProduction;
int iFirstProjectOrder;
int iProductionNeeded;
int iProductionModifier;
iProduction = 0;
iFirstProjectOrder = getFirstProjectOrder(eProject);
if ((iFirstProjectOrder == -1) || (iFirstProjectOrder == iNum))
{
iProduction += getProjectProduction(eProject);
}
iProductionNeeded = getProductionNeeded(eProject);
iProductionModifier = getProductionModifier(eProject);
return getProductionTurnsLeft(iProductionNeeded, iProduction, getProductionDifference(iProductionNeeded, iProduction, iProductionModifier, false, (iNum == 0)), getProductionDifference(iProductionNeeded, iProduction, iProductionModifier, false, false));
}
int CvCity::getProductionTurnsLeft(int iProductionNeeded, int iProduction, int iFirstProductionDifference, int iProductionDifference) const
{
int iProductionLeft;
int iTurnsLeft;
iProductionLeft = std::max(0, (iProductionNeeded - iProduction - iFirstProductionDifference));
if (iProductionDifference == 0)
{
return iProductionLeft + 1;
}
iTurnsLeft = (iProductionLeft / iProductionDifference);
if ((iTurnsLeft * iProductionDifference) < iProductionLeft)
{
iTurnsLeft++;
}
iTurnsLeft++;
return std::max(1, iTurnsLeft);
}
void CvCity::setProduction(int iNewValue)
{
if (isProductionUnit())
{
setUnitProduction(getProductionUnit(), iNewValue);
}
else if (isProductionBuilding())
{
setBuildingProduction(getProductionBuilding(), iNewValue);
}
else if (isProductionProject())
{
setProjectProduction(getProductionProject(), iNewValue);
}
}
void CvCity::changeProduction(int iChange)
{
if (isProductionUnit())
{
changeUnitProduction(getProductionUnit(), iChange);
}
else if (isProductionBuilding())
{
changeBuildingProduction(getProductionBuilding(), iChange);
}
else if (isProductionProject())
{
changeProjectProduction(getProductionProject(), iChange);
}
else if (isProductionProcess())
{
CvProcessInfo& kProcess = GC.getProcessInfo(getProductionProcess());
// Add gold and espionage directly to player totals
GET_PLAYER(getOwnerINLINE()).changeGold((kProcess.getProductionToCommerceModifier(COMMERCE_GOLD) * iChange)/100);
GET_PLAYER(getOwnerINLINE()).doEspionageOneOffPoints((kProcess.getProductionToCommerceModifier(COMMERCE_ESPIONAGE) * iChange)/100);
// Research accrues to the team
TechTypes eCurrentTech = GET_PLAYER(getOwnerINLINE()).getCurrentResearch();
if (eCurrentTech != NO_TECH)
{
GET_TEAM(getTeam()).changeResearchProgress(eCurrentTech,
(kProcess.getProductionToCommerceModifier(COMMERCE_RESEARCH) * iChange)/100,
getOwnerINLINE());
}
// Culture to the city itself
changeCulture(getOwnerINLINE(), (kProcess.getProductionToCommerceModifier(COMMERCE_CULTURE) * iChange)/100, false, false);
}
}
int CvCity::getProductionModifier() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
return getProductionModifier((UnitTypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1));
break;
case ORDER_CONSTRUCT:
return getProductionModifier((BuildingTypes)(pOrderNode->m_data.iData1));
break;
case ORDER_CREATE:
return getProductionModifier((ProjectTypes)(pOrderNode->m_data.iData1));
break;
case ORDER_MAINTAIN:
case ORDER_LIST:
break;
default:
FAssertMsg(false, "pOrderNode->m_data.eOrderType failed to match a valid option");
break;
}
}
return 0;
}
int CvCity::getProductionModifier(UnitTypes eUnit) const
{
int iI;
int iMultiplier = GET_PLAYER(getOwnerINLINE()).getProductionModifier(eUnit);
iMultiplier += getDomainProductionModifier((DomainTypes)(GC.getUnitInfo(eUnit).getDomainType()));
/************************************************************************************************/
/* Afforess ProductionModifier Start 5/29/11 */
/* */
/* */
/************************************************************************************************/
if (GC.getUnitInfo(eUnit).getUnitCombatType() != NO_UNITCOMBAT)
{
iMultiplier += GET_PLAYER(getOwnerINLINE()).getUnitCombatProductionModifier((UnitCombatTypes)(GC.getUnitInfo(eUnit).getUnitCombatType()));
}
iMultiplier += getUnitClassProductionModifier((UnitClassTypes)(GC.getUnitInfo(eUnit).getUnitClassType()));
iMultiplier += GET_PLAYER(getOwnerINLINE()).getUnitClassProductionModifier((UnitClassTypes)(GC.getUnitInfo(eUnit).getUnitClassType()));
/************************************************************************************************/
/* Afforess ProductionModifier END */
/************************************************************************************************/
if (GC.getUnitInfo(eUnit).isMilitaryProduction())
{
iMultiplier += getMilitaryProductionModifier();
}
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
if (hasBonus((BonusTypes)iI))
{
iMultiplier += GC.getUnitInfo(eUnit).getBonusProductionModifier(iI);
}
}
if (GET_PLAYER(getOwnerINLINE()).getStateReligion() != NO_RELIGION)
{
if (isHasReligion(GET_PLAYER(getOwnerINLINE()).getStateReligion()))
{
iMultiplier += GET_PLAYER(getOwnerINLINE()).getStateReligionUnitProductionModifier();
}
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 05/10/10 jdog5000 */
/* */
/* For mods */
/************************************************************************************************/
//original code, restored:
return std::max(0, iMultiplier);
/* Fuyu: unofficial patch code removed. If modders need this, let them do it. UP is not the place
return std::max(-50, iMultiplier);
*/
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
}
int CvCity::getProductionModifier(BuildingTypes eBuilding) const
{
int iMultiplier = GET_PLAYER(getOwnerINLINE()).getProductionModifier(eBuilding);
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
iMultiplier += getBuildingClassProductionModifier((BuildingClassTypes)(GC.getBuildingInfo(eBuilding).getBuildingClassType()));
iMultiplier += GET_PLAYER(getOwnerINLINE()).getBuildingClassProductionModifier((BuildingClassTypes)(GC.getBuildingInfo(eBuilding).getBuildingClassType()));
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
for (int iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
if (hasBonus((BonusTypes)iI))
{
iMultiplier += GC.getBuildingInfo(eBuilding).getBonusProductionModifier(iI);
}
}
if (GET_PLAYER(getOwnerINLINE()).getStateReligion() != NO_RELIGION)
{
if (isHasReligion(GET_PLAYER(getOwnerINLINE()).getStateReligion()))
{
iMultiplier += GET_PLAYER(getOwnerINLINE()).getStateReligionBuildingProductionModifier();
}
}
return std::max(0, iMultiplier);
}
int CvCity::getProductionModifier(ProjectTypes eProject) const
{
int iMultiplier = GET_PLAYER(getOwnerINLINE()).getProductionModifier(eProject);
if (GC.getProjectInfo(eProject).isSpaceship())
{
iMultiplier += getSpaceProductionModifier();
}
for (int iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
if (hasBonus((BonusTypes)iI))
{
iMultiplier += GC.getProjectInfo(eProject).getBonusProductionModifier(iI);
}
}
return std::max(0, iMultiplier);
}
/************************************************************************************************/
/* Afforess Multiple Production Mod 08/23/09 */
/* */
/* */
/************************************************************************************************/
int CvCity::getOverflowProductionDifference() const
{
return getProductionDifference(getProductionNeeded(), getProduction(), getProductionModifier(), false, true, false);
}
/*
int CvCity::getProductionDifference(int iProductionNeeded, int iProduction, int iProductionModifier, bool bFoodProduction, bool bOverflow) const
*/
int CvCity::getProductionDifference(int iProductionNeeded, int iProduction, int iProductionModifier, bool bFoodProduction, bool bOverflow, bool bYield) const
/************************************************************************************************/
/* Afforess Multiple Production Mod END */
/************************************************************************************************/
{
if (isDisorder())
{
return 0;
}
/************************************************************************************************/
/* Afforess Multiple Production Mod 07/20/14 */
/* */
/* */
/************************************************************************************************/
int iFoodProduction = ((bYield && bFoodProduction) ? std::max(0, (getYieldRate(YIELD_FOOD) - foodConsumption(true))) : 0);
int iOverflow = ((bOverflow) ? (getOverflowProduction() + getFeatureProduction()) : 0);
int iYield = iOverflow * 100;
if (bYield)
{
iYield = (getBaseYieldRate(YIELD_PRODUCTION) + iOverflow) * getBaseYieldRateModifier(YIELD_PRODUCTION, iProductionModifier);
/*
return (((getBaseYieldRate(YIELD_PRODUCTION) + iOverflow) * getBaseYieldRateModifier(YIELD_PRODUCTION, iProductionModifier)) / 100 + iFoodProduction);
*/
if (GC.getGameINLINE().isOption(GAMEOPTION_MULTIPLE_PRODUCTION))
{
iYield -= (iOverflow * getBaseYieldRateModifier(YIELD_PRODUCTION, iProductionModifier));
iYield += iOverflow * 100;
}
}
return (iYield / 100 + iFoodProduction);
/************************************************************************************************/
/* Afforess Multiple Production Mod END */
/************************************************************************************************/
}
int CvCity::getCurrentProductionDifference(bool bIgnoreFood, bool bOverflow) const
{
return getProductionDifference(getProductionNeeded(), getProduction(), getProductionModifier(), (!bIgnoreFood && isFoodProduction()), bOverflow);
}
int CvCity::getExtraProductionDifference(int iExtra) const
{
return getExtraProductionDifference(iExtra, getProductionModifier());
}
int CvCity::getExtraProductionDifference(int iExtra, UnitTypes eUnit) const
{
return getExtraProductionDifference(iExtra, getProductionModifier(eUnit));
}
int CvCity::getExtraProductionDifference(int iExtra, BuildingTypes eBuilding) const
{
return getExtraProductionDifference(iExtra, getProductionModifier(eBuilding));
}
int CvCity::getExtraProductionDifference(int iExtra, ProjectTypes eProject) const
{
return getExtraProductionDifference(iExtra, getProductionModifier(eProject));
}
int CvCity::getExtraProductionDifference(int iExtra, int iModifier) const
{
return ((iExtra * getBaseYieldRateModifier(YIELD_PRODUCTION, iModifier)) / 100);
}
bool CvCity::canHurry(HurryTypes eHurry, bool bTestVisible) const
{
if (!(GET_PLAYER(getOwnerINLINE()).canHurry(eHurry)))
{
return false;
}
if (isDisorder())
{
return false;
}
if (getProduction() >= getProductionNeeded())
{
return false;
}
if (!bTestVisible)
{
if (!isProductionUnit() && !isProductionBuilding())
{
return false;
}
if (GET_PLAYER(getOwnerINLINE()).getGold() < hurryGold(eHurry))
{
return false;
}
if (maxHurryPopulation() < hurryPopulation(eHurry))
{
return false;
}
}
return true;
}
bool CvCity::canHurryUnit(HurryTypes eHurry, UnitTypes eUnit, bool bIgnoreNew) const
{
if (!(GET_PLAYER(getOwnerINLINE()).canHurry(eHurry)))
{
return false;
}
if (isDisorder())
{
return false;
}
if (getUnitProduction(eUnit) >= getProductionNeeded(eUnit))
{
return false;
}
if (GET_PLAYER(getOwnerINLINE()).getGold() < getHurryGold(eHurry, getHurryCost(false, eUnit, bIgnoreNew)))
{
return false;
}
if (maxHurryPopulation() < getHurryPopulation(eHurry, getHurryCost(true, eUnit, bIgnoreNew)))
{
return false;
}
return true;
}
bool CvCity::canHurryBuilding(HurryTypes eHurry, BuildingTypes eBuilding, bool bIgnoreNew) const
{
if (!(GET_PLAYER(getOwnerINLINE()).canHurry(eHurry)))
{
return false;
}
if (isDisorder())
{
return false;
}
if (getBuildingProduction(eBuilding) >= getProductionNeeded(eBuilding))
{
return false;
}
if (GET_PLAYER(getOwnerINLINE()).getGold() < getHurryGold(eHurry, getHurryCost(false, eBuilding, bIgnoreNew)))
{
return false;
}
if (maxHurryPopulation() < getHurryPopulation(eHurry, getHurryCost(true, eBuilding, bIgnoreNew)))
{
return false;
}
return true;
}
void CvCity::hurry(HurryTypes eHurry)
{
int iHurryGold;
int iHurryPopulation;
int iHurryAngerLength;
if (!canHurry(eHurry))
{
return;
}
/************************************************************************************************/
/* Afforess Start 06/27/10 */
/* */
/* */
/************************************************************************************************/
GET_PLAYER(getOwnerINLINE()).changeHurriedCount(1);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iHurryGold = hurryGold(eHurry);
iHurryPopulation = hurryPopulation(eHurry);
iHurryAngerLength = hurryAngerLength(eHurry);
changeProduction(hurryProduction(eHurry));
GET_PLAYER(getOwnerINLINE()).changeGold(-(iHurryGold));
changePopulation(-(iHurryPopulation));
changeHurryAngerTimer(iHurryAngerLength);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 10/02/09 jdog5000 */
/* */
/* AI logging */
/************************************************************************************************/
if( gCityLogLevel >= 2 )
{
CvWStringBuffer szBuffer;
CvWString szString;
if (isProductionUnit())
{
szString = GC.getUnitInfo(getProductionUnit()).getDescription(getCivilizationType());
}
else if (isProductionBuilding())
{
szString = GC.getBuildingInfo(getProductionBuilding()).getDescription();
}
else if (isProductionProject())
{
szString = GC.getProjectInfo(getProductionProject()).getDescription();
}
logBBAIForTeam(getTeam(), " City %S hurrying production of %S at cost of %d pop, %d gold, %d anger length", getName().GetCString(), szString.GetCString(), iHurryPopulation, iHurryGold, iHurryAngerLength );
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
if ((getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) && isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
}
GC.getGameINLINE().reportEvent(CITY_EVENT_HURRY, getOwnerINLINE(), eHurry, getID());
// Python Event
//CvEventReporter::getInstance().cityHurry(this, eHurry);
}
// BUG - Hurry Assist - start
bool CvCity::hurryOverflow(HurryTypes eHurry, int* iProduction, int* iGold, bool bCountThisTurn) const
{
if (!canHurry(eHurry))
{
return false;
}
if (GC.getHurryInfo(eHurry).getProductionPerPopulation() == 0)
{
*iProduction = 0;
*iGold = 0;
return true;
}
int iTotal, iCurrent, iModifier, iGoldPercent;
if (isProductionUnit())
{
UnitTypes eUnit = getProductionUnit();
FAssertMsg(eUnit != NO_UNIT, "eUnit is expected to be assigned a valid unit type");
iTotal = getProductionNeeded(eUnit);
iCurrent = getUnitProduction(eUnit);
iModifier = getProductionModifier(eUnit);
iGoldPercent = GC.getDefineINT("MAXED_UNIT_GOLD_PERCENT");
}
else if (isProductionBuilding())
{
BuildingTypes eBuilding = getProductionBuilding();
FAssertMsg(eBuilding != NO_BUILDING, "eBuilding is expected to be assigned a valid building type");
iTotal = getProductionNeeded(eBuilding);
iCurrent = getBuildingProduction(eBuilding);
iModifier = getProductionModifier(eBuilding);
iGoldPercent = GC.getDefineINT("MAXED_BUILDING_GOLD_PERCENT");
}
else if (isProductionProject())
{
ProjectTypes eProject = getProductionProject();
FAssertMsg(eProject != NO_PROJECT, "eProject is expected to be assigned a valid project type");
iTotal = getProductionNeeded(eProject);
iCurrent = getProjectProduction(eProject);
iModifier = getProductionModifier(eProject);
iGoldPercent = GC.getDefineINT("MAXED_PROJECT_GOLD_PERCENT");
}
else
{
return false;
}
int iHurry = hurryProduction(eHurry);
int iOverflow = iCurrent + iHurry - iTotal;
if (bCountThisTurn)
{
// include chops and previous overflow here
iOverflow += getCurrentProductionDifference(false, true);
}
int iMaxOverflow = std::max(iTotal, getCurrentProductionDifference(false, false));
int iLostProduction = std::max(0, iOverflow - iMaxOverflow);
int iBaseModifier = getBaseYieldRateModifier(YIELD_PRODUCTION);
int iTotalModifier = getBaseYieldRateModifier(YIELD_PRODUCTION, iModifier);
iOverflow = std::min(iOverflow, iMaxOverflow);
iLostProduction *= iBaseModifier;
iLostProduction /= std::max(1, iTotalModifier);
*iProduction = (iBaseModifier * iOverflow) / std::max(1, iTotalModifier);
*iGold = ((iLostProduction * iGoldPercent) / 100);
return true;
}
// BUG - Hurry Assist - end
UnitTypes CvCity::getConscriptUnit() const
{
UnitTypes eBestUnit;
if (m_eConscriptUnitType != NO_UNIT)
{
eBestUnit = m_eConscriptUnitType;
}
else
{
UnitTypes eLoopUnit;
int iValue;
int iBestValue;
int iI;
iBestValue = 0;
eBestUnit = NO_UNIT;
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
eLoopUnit = (UnitTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI);
if (eLoopUnit != NO_UNIT)
{
if (canTrain(eLoopUnit))
{
iValue = GC.getUnitInfo(eLoopUnit).getConscriptionValue();
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestUnit = eLoopUnit;
}
}
}
}
}
return eBestUnit;
}
int CvCity::getConscriptPopulation() const
{
UnitTypes eConscriptUnit;
eConscriptUnit = getConscriptUnit();
if (eConscriptUnit == NO_UNIT)
{
return 0;
}
if (GC.getDefineINT("CONSCRIPT_POPULATION_PER_COST") == 0)
{
return 0;
}
return std::max(1, ((GC.getUnitInfo(eConscriptUnit).getProductionCost()) / GC.getDefineINT("CONSCRIPT_POPULATION_PER_COST")));
}
int CvCity::conscriptMinCityPopulation() const
{
int iPopulation;
iPopulation = GC.getDefineINT("CONSCRIPT_MIN_CITY_POPULATION");
iPopulation += getConscriptPopulation();
return iPopulation;
}
int CvCity::flatConscriptAngerLength() const
{
int iAnger;
iAnger = GC.getDefineINT("CONSCRIPT_ANGER_DIVISOR");
iAnger *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getHurryConscriptAngerPercent();
iAnger /= 100;
return std::max(1, iAnger);
}
bool CvCity::canConscript() const
{
if (isDisorder())
{
return false;
}
if (isDrafted())
{
return false;
}
if (GET_PLAYER(getOwnerINLINE()).getConscriptCount() >= GET_PLAYER(getOwnerINLINE()).getMaxConscript())
{
return false;
}
if (getPopulation() <= getConscriptPopulation())
{
return false;
}
if (getPopulation() < conscriptMinCityPopulation())
{
return false;
}
if (plot()->calculateTeamCulturePercent(getTeam()) < GC.getDefineINT("CONSCRIPT_MIN_CULTURE_PERCENT"))
{
return false;
}
if (getConscriptUnit() == NO_UNIT)
{
return false;
}
return true;
}
CvUnit* CvCity::initConscriptedUnit()
{
UnitAITypes eCityAI = NO_UNITAI;
UnitTypes eConscriptUnit = getConscriptUnit();
if (NO_UNIT == eConscriptUnit)
{
return NULL;
}
if (GET_PLAYER(getOwnerINLINE()).AI_unitValue(eConscriptUnit, UNITAI_ATTACK, area()) > 0)
{
eCityAI = UNITAI_ATTACK;
}
else if (GET_PLAYER(getOwnerINLINE()).AI_unitValue(eConscriptUnit, UNITAI_CITY_DEFENSE, area()) > 0)
{
eCityAI = UNITAI_CITY_DEFENSE;
}
else if (GET_PLAYER(getOwnerINLINE()).AI_unitValue(eConscriptUnit, UNITAI_CITY_COUNTER, area()) > 0)
{
eCityAI = UNITAI_CITY_COUNTER;
}
else if (GET_PLAYER(getOwnerINLINE()).AI_unitValue(eConscriptUnit, UNITAI_CITY_SPECIAL, area()) > 0)
{
eCityAI = UNITAI_CITY_SPECIAL;
}
else
{
eCityAI = NO_UNITAI;
}
CvUnit* pUnit = GET_PLAYER(getOwnerINLINE()).initUnit(eConscriptUnit, getX_INLINE(), getY_INLINE(), eCityAI, NO_DIRECTION, getCitySorenRandNum(10000, "AI Unit Birthmark"));
FAssertMsg(pUnit != NULL, "pUnit expected to be assigned (not NULL)");
if (NULL != pUnit)
{
addProductionExperience(pUnit, true);
pUnit->setMoves(0);
}
return pUnit;
}
void CvCity::conscript()
{
if (!canConscript())
{
return;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 10/02/09 jdog5000 */
/* */
/* AI logging */
/************************************************************************************************/
int iPopChange = -(getConscriptPopulation());
int iAngerLength = flatConscriptAngerLength();
changePopulation(iPopChange);
changeConscriptAngerTimer(iAngerLength);
setDrafted(true);
GET_PLAYER(getOwnerINLINE()).changeConscriptCount(1);
CvUnit* pUnit = initConscriptedUnit();
FAssertMsg(pUnit != NULL, "pUnit expected to be assigned (not NULL)");
if (NULL != pUnit)
{
if (GC.getGameINLINE().getActivePlayer() == getOwnerINLINE() && isInViewport())
{
gDLL->getInterfaceIFace()->selectUnit(pUnit, true, false, true);
}
if( gCityLogLevel >= 2 )
{
logBBAIForTeam(getTeam(), " City %S does conscript of a %S at cost of %d pop, %d anger", getName().GetCString(), pUnit->getName().GetCString(), iPopChange, iAngerLength );
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
}
int CvCity::getBonusHealth(BonusTypes eBonus) const
{
int iHealth;
int iI;
iHealth = GC.getBonusInfo(eBonus).getHealth();
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
iHealth += getNumActiveBuilding((BuildingTypes)iI) * GC.getBuildingInfo((BuildingTypes) iI).getBonusHealthChanges(eBonus);
}
return iHealth;
}
int CvCity::getBonusHappiness(BonusTypes eBonus) const
{
int iHappiness;
int iI;
iHappiness = GC.getBonusInfo(eBonus).getHappiness();
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
iHappiness += getNumActiveBuilding((BuildingTypes)iI) * GC.getBuildingInfo((BuildingTypes) iI).getBonusHappinessChanges(eBonus);
}
/************************************************************************************************/
/* Afforess Start 08/26/10 */
/* */
/* */
/************************************************************************************************/
for (iI = 0; iI < GC.getNumTraitInfos(); iI++)
{
if (GET_PLAYER(getOwnerINLINE()).hasTrait((TraitTypes)iI) && hasBonus(eBonus))
{
iHappiness += GC.getTraitInfo((TraitTypes)iI).getBonusHappinessChanges(eBonus);
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iHappiness;
}
int CvCity::getBonusPower(BonusTypes eBonus, bool bDirty) const
{
int iCount;
int iI;
iCount = 0;
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (getNumActiveBuilding((BuildingTypes)iI) > 0)
{
if (GC.getBuildingInfo((BuildingTypes) iI).getPowerBonus() == eBonus)
{
if (GC.getBuildingInfo((BuildingTypes) iI).isDirtyPower() == bDirty)
{
iCount += getNumActiveBuilding((BuildingTypes)iI);
}
}
}
}
return iCount;
}
int CvCity::getBonusYieldRateModifier(YieldTypes eIndex, BonusTypes eBonus) const
{
int iModifier;
int iI;
iModifier = 0;
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
iModifier += getNumActiveBuilding((BuildingTypes)iI) * GC.getBuildingInfo((BuildingTypes) iI).getBonusYieldModifier(eBonus, eIndex);
}
return iModifier;
}
void CvCity::processBonus(BonusTypes eBonus, int iChange)
{
PROFILE_FUNC();
int iI;
int iValue;
int iGoodValue;
int iBadValue;
if ( m_paiBuildingReplaced == NULL )
{
calculateBuildingReplacements();
}
iValue = GC.getBonusInfo(eBonus).getHealth();
iGoodValue = std::max(0, iValue);
iBadValue = std::min(0, iValue);
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
iValue = GC.getBuildingInfo((BuildingTypes) iI).getBonusHealthChanges(eBonus) * getNumActiveBuilding((BuildingTypes)iI);
if (iValue >= 0)
{
iGoodValue += iValue;
}
else
{
iBadValue += iValue;
}
}
changeBonusGoodHealth(iGoodValue * iChange);
changeBonusBadHealth(iBadValue * iChange);
iValue = GC.getBonusInfo(eBonus).getHappiness();
iGoodValue = std::max(0, iValue);
iBadValue = std::min(0, iValue);
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
iValue = getNumActiveBuilding((BuildingTypes)iI) * GC.getBuildingInfo((BuildingTypes) iI).getBonusHappinessChanges(eBonus);
if (iValue >= 0)
{
iGoodValue += iValue;
}
else
{
iBadValue += iValue;
}
}
/************************************************************************************************/
/* Afforess Start 08/26/10 */
/* */
/* */
/************************************************************************************************/
for (iI = 0; iI < GC.getNumTraitInfos(); iI++)
{
if (GET_PLAYER(getOwnerINLINE()).hasTrait((TraitTypes)iI))
{
iValue = GC.getTraitInfo((TraitTypes)iI).getBonusHappinessChanges(eBonus);
if (iValue >= 0)
{
iGoodValue += iValue;
}
else
{
iBadValue += iValue;
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
changeBonusGoodHappiness(iGoodValue * iChange);
changeBonusBadHappiness(iBadValue * iChange);
changePowerCount((getBonusPower(eBonus, true) * iChange), true);
changePowerCount((getBonusPower(eBonus, false) * iChange), false);
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
changeBonusYieldRateModifier(((YieldTypes)iI), (getBonusYieldRateModifier(((YieldTypes)iI), eBonus) * iChange));
}
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
changeBonusCommerceRateModifier(((CommerceTypes)iI), (getBonusCommerceRateModifier(((CommerceTypes)iI), eBonus) * iChange));
changeBonusCommercePercentChanges(((CommerceTypes)iI), (getBonusCommercePercentChanges(((CommerceTypes)iI), eBonus) * iChange));
}
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (getNumActiveBuilding((BuildingTypes)iI) > 0)
{
for (int iJ = 0; iJ < NUM_YIELD_TYPES; iJ++)
{
updateYieldRate((BuildingTypes)iI, (YieldTypes)iJ, ((getBuildingYieldChange((BuildingClassTypes)(GC.getBuildingInfo((BuildingTypes)iI).getBuildingClassType()), (YieldTypes)iJ)) + (GC.getBuildingInfo((BuildingTypes)iI).getBonusYieldChanges(eBonus, iJ) * getNumActiveBuilding((BuildingTypes)iI) * iChange)));
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
void CvCity::processBuilding(BuildingTypes eBuilding, int iChange, bool bObsolete, bool bReplacingNow)
{
PROFILE_FUNC();
UnitTypes eGreatPeopleUnit;
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding); //Afforess
int iI, iJ;
FAssert(iChange == 1 || iChange == -1);
// We don't need to process the building effects in or out if it is
// * obsolete (unless GOING obsolete explicitly now)
// * has already been replaced, except in the middle of a modifier recalc where we might be processing it out
// due to that replacement being re-detected
if ((!(GET_TEAM(getTeam()).isObsoleteBuilding(eBuilding)) && (m_paiBuildingReplaced == NULL || m_paiBuildingReplaced[eBuilding] == 0 || bReplacingNow || (m_recalcBuilding != MAX_INT && iChange < 0))) || bObsolete)
{
{
PROFILE("CvCity::processBuilding.properties");
if (iChange > 0)
{
CorporationTypes eCorporation = (CorporationTypes)GC.getBuildingInfo(eBuilding).getFoundsCorporation();
if (NO_CORPORATION != eCorporation && !GC.getGameINLINE().isCorporationFounded(eCorporation))
{
setHeadquarters(eCorporation);
}
if (kBuilding.getFreeSpecialTech() != NO_TECH)
{
GET_TEAM(getTeam()).setHasTech(kBuilding.getFreeSpecialTech(), true, getOwner(), true, true);
}
getProperties()->addProperties(kBuilding.getProperties());
//GET_PLAYER(getOwnerINLINE()).getProperties()->addProperties(kBuilding.getProperties());
CvProperties* pProp = kBuilding.getPropertiesAllCities();
if (!pProp->isEmpty())
{
GET_TEAM(getTeam()).addPropertiesAllCities(pProp);
}
}
else
{
getProperties()->subtractProperties(kBuilding.getProperties());
//GET_PLAYER(getOwnerINLINE()).getProperties()->subtractProperties(kBuilding.getProperties());
CvProperties* pProp = kBuilding.getPropertiesAllCities();
if (!pProp->isEmpty())
{
GET_TEAM(getTeam()).subtractPropertiesAllCities(pProp);
}
}
}
{
PROFILE("CvCity::processBuilding.part2");
if (GC.getBuildingInfo(eBuilding).getNoBonus() != NO_BONUS)
{
changeNoBonusCount(((BonusTypes)(GC.getBuildingInfo(eBuilding).getNoBonus())), iChange);
}
if (GC.getBuildingInfo(eBuilding).getFreeBonus() != NO_BONUS)
{
changeFreeBonus(((BonusTypes)(GC.getBuildingInfo(eBuilding).getFreeBonus())), (GC.getGameINLINE().getNumFreeBonuses(eBuilding) * iChange));
clearVicinityBonusCache((BonusTypes)(GC.getBuildingInfo(eBuilding).getFreeBonus()));
}
int iNum = kBuilding.getNumExtraFreeBonuses();
for (iI=0; iI<iNum; iI++)
{
changeFreeBonus(kBuilding.getExtraFreeBonus(iI), kBuilding.getExtraFreeBonusNum(iI) * iChange);
clearVicinityBonusCache(kBuilding.getExtraFreeBonus(iI));
}
if (GC.getBuildingInfo(eBuilding).getFreePromotion() != NO_PROMOTION)
{
changeFreePromotionCount(((PromotionTypes)(GC.getBuildingInfo(eBuilding).getFreePromotion())), iChange);
}
//TB Nukefix reset nuke validation
if (GC.getBuildingInfo(eBuilding).isAllowsNukes())
{//TB Nukefix (changed to GET_PLAYER(getOwnerINLINE() rather than GC.getGameINLINE)
GET_PLAYER(getOwnerINLINE()).makeNukesValid(true);
}
/************************************************************************************************/
/* Afforess Meteorology Modmod addon 10/13/09 */
/* */
/* */
/************************************************************************************************/
if (kBuilding.getFreePromotion_2() != NO_PROMOTION)
{
changeFreePromotionCount(((PromotionTypes)(kBuilding.getFreePromotion_2())), iChange);
}
if (kBuilding.getFreePromotion_3() != NO_PROMOTION)
{
changeFreePromotionCount(((PromotionTypes)(kBuilding.getFreePromotion_3())), iChange);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
changeEspionageDefenseModifier(GC.getBuildingInfo(eBuilding).getEspionageDefenseModifier() * iChange);
/************************************************************************************************/
/* Afforess Food Threshold Modifier Start 10/16/09 */
/* */
/* */
/************************************************************************************************/
changePopulationgrowthratepercentage(kBuilding.getPopulationgrowthratepercentage(),(iChange==1));
/************************************************************************************************/
/* Afforess Food Threshold Modifier END */
/************************************************************************************************/
changeGreatPeopleRateModifier(GC.getBuildingInfo(eBuilding).getGreatPeopleRateModifier() * iChange);
changeFreeExperience(GC.getBuildingInfo(eBuilding).getFreeExperience() * iChange);
changeMaxFoodKeptPercent(GC.getBuildingInfo(eBuilding).getFoodKept(),(iChange==1));
changeMaxAirlift(GC.getBuildingInfo(eBuilding).getAirlift() * iChange);
changeAirModifier(GC.getBuildingInfo(eBuilding).getAirModifier() * iChange);
changeAirUnitCapacity(GC.getBuildingInfo(eBuilding).getAirUnitCapacity() * iChange);
changeWonderCapacityIncrement(GC.getBuildingInfo(eBuilding).getWonderCapacityIncrement() * iChange);
changeNukeModifier(GC.getBuildingInfo(eBuilding).getNukeModifier() * iChange);
changeFreeSpecialist(GC.getBuildingInfo(eBuilding).getFreeSpecialist() * iChange);
changeMaintenanceModifier(GC.getBuildingInfo(eBuilding).getMaintenanceModifier() * iChange);
changeWarWearinessModifier(GC.getBuildingInfo(eBuilding).getWarWearinessModifier() * iChange);
changeHurryAngerModifier(GC.getBuildingInfo(eBuilding).getHurryAngerModifier() * iChange);
changeHealRate(GC.getBuildingInfo(eBuilding).getHealRateChange() * iChange);
if (GC.getBuildingInfo(eBuilding).getHealth() > 0)
{
changeBuildingGoodHealth(GC.getBuildingInfo(eBuilding).getHealth() * iChange);
}
else
{
changeBuildingBadHealth(GC.getBuildingInfo(eBuilding).getHealth() * iChange);
}
if (GC.getBuildingInfo(eBuilding).getHappiness() > 0)
{
changeBuildingGoodHappiness(GC.getBuildingInfo(eBuilding).getHappiness() * iChange);
}
else
{
changeBuildingBadHappiness(GC.getBuildingInfo(eBuilding).getHappiness() * iChange);
}
if (GC.getBuildingInfo(eBuilding).getReligionType() != NO_RELIGION)
{
changeStateReligionHappiness(((ReligionTypes)(GC.getBuildingInfo(eBuilding).getReligionType())), (GC.getBuildingInfo(eBuilding).getStateReligionHappiness() * iChange));
}
changeMilitaryProductionModifier(GC.getBuildingInfo(eBuilding).getMilitaryProductionModifier() * iChange);
changeSpaceProductionModifier(GC.getBuildingInfo(eBuilding).getSpaceProductionModifier() * iChange);
changeExtraTradeRoutes(GC.getBuildingInfo(eBuilding).getTradeRoutes() * iChange);
changeTradeRouteModifier(GC.getBuildingInfo(eBuilding).getTradeRouteModifier() * iChange);
changeForeignTradeRouteModifier(GC.getBuildingInfo(eBuilding).getForeignTradeRouteModifier() * iChange);
changePowerCount(((GC.getBuildingInfo(eBuilding).isPower()) ? iChange : 0), GC.getBuildingInfo(eBuilding).isDirtyPower());
changeGovernmentCenterCount((GC.getBuildingInfo(eBuilding).isGovernmentCenter()) ? iChange : 0);
changeNoUnhappinessCount((GC.getBuildingInfo(eBuilding).isNoUnhappiness()) ? iChange : 0);
changeNoUnhealthyPopulationCount((GC.getBuildingInfo(eBuilding).isNoUnhealthyPopulation()) ? iChange : 0);
changeBuildingOnlyHealthyCount((GC.getBuildingInfo(eBuilding).isBuildingOnlyHealthy()) ? iChange : 0);
}
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
PROFILE("CvCity::processBuilding.Yields");
changeSeaPlotYield(((YieldTypes)iI), (GC.getBuildingInfo(eBuilding).getSeaPlotYieldChange(iI) * iChange));
changeRiverPlotYield(((YieldTypes)iI), (GC.getBuildingInfo(eBuilding).getRiverPlotYieldChange(iI) * iChange));
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
/* changeBaseYieldRate(((YieldTypes)iI), ((GC.getBuildingInfo(eBuilding).getYieldChange(iI) + getBuildingYieldChange((BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType(), (YieldTypes)iI) + GET_TEAM(getTeam()).getBuildingYieldChange(eBuilding, (YieldTypes)iI))* iChange));
changeYieldRateModifier(((YieldTypes)iI), ((GC.getBuildingInfo(eBuilding).getYieldModifier(iI) + GET_TEAM(getTeam()).getBuildingYieldModifier(eBuilding, (YieldTypes)iI))* iChange));
*/
changeBaseYieldRate(((YieldTypes)iI), ((kBuilding.getYieldChange(iI) + getBuildingYieldChange((BuildingClassTypes)kBuilding.getBuildingClassType(), (YieldTypes)iI))* iChange));
changeYieldRateModifier(((YieldTypes)iI), (kBuilding.getYieldModifier(iI) * iChange));
updateYieldRate(eBuilding, (YieldTypes)iI, ((getBuildingYieldChange((BuildingClassTypes)(kBuilding.getBuildingClassType()), (YieldTypes)iI)) + (GET_TEAM(getTeam()).getBuildingYieldChange(eBuilding, (YieldTypes)iI) * iChange)));
updateYieldModifierByBuilding(eBuilding, (YieldTypes)iI, GET_TEAM(getTeam()).getBuildingYieldModifier(eBuilding, (YieldTypes)iI) * iChange);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
changePowerYieldRateModifier(((YieldTypes)iI), (GC.getBuildingInfo(eBuilding).getPowerYieldModifier(iI) * iChange));
}
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
PROFILE("CvCity::processBuilding.Commerces");
/************************************************************************************************/
/* Afforess Start 5/28/11 */
/* */
/* */
/************************************************************************************************/
updateCommerceRateByBuilding(eBuilding, (CommerceTypes)iI, ((GET_TEAM(getTeam()).getBuildingCommerceChange(eBuilding, (CommerceTypes)iI) + GET_PLAYER(getOwnerINLINE()).getBuildingClassCommerceChange((BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType(), (CommerceTypes)iI)) * iChange));
changeCommerceRateModifier(((CommerceTypes)iI), (GC.getBuildingInfo(eBuilding).getCommerceModifier(iI) * iChange));
updateCommerceModifierByBuilding(eBuilding, (CommerceTypes)iI, (GET_TEAM(getTeam()).getBuildingCommerceModifier(eBuilding, (CommerceTypes)iI) + GET_PLAYER(getOwnerINLINE()).getBuildingCommerceModifier(eBuilding, (CommerceTypes)iI)) * iChange);
changeMaxCommerceAttacks((CommerceTypes)iI, kBuilding.getCommerceAttacks(iI) * iChange);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
changeCommerceHappinessPer(((CommerceTypes)iI), (GC.getBuildingInfo(eBuilding).getCommerceHappiness(iI) * iChange));
}
changeExtraCapitalCommerce(kBuilding.getExtraCapitalCommerce() * iChange);
changeExtraForeignCapitalCommerce(kBuilding.getExtraForeignCapitalCommerce() * iChange);
if ((kBuilding.getDomesticConnectedCommerce() != 0 || kBuilding.getForeignConnectedCommerce() != 0 )&& iChange != 0)
{
doConnectednessCalculations();
}
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
PROFILE("CvCity::processBuilding.Religions");
changeReligionInfluence(((ReligionTypes)iI), (GC.getBuildingInfo(eBuilding).getReligionChange(iI) * iChange));
}
for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
{
PROFILE("CvCity::processBuilding.Specialists");
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
changeMaxSpecialistCount(((SpecialistTypes)iI), (kBuilding.getSpecialistCount(iI) + GET_TEAM(getTeam()).getBuildingSpecialistChange(eBuilding, (SpecialistTypes)iI)) * iChange);
/* Was:
changeMaxSpecialistCount(((SpecialistTypes)iI), GC.getBuildingInfo(eBuilding).getSpecialistCount(iI) * iChange);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
changeFreeSpecialistCount(((SpecialistTypes)iI), GC.getBuildingInfo(eBuilding).getFreeSpecialistCount(iI) * iChange);
}
{
PROFILE("CvCity::processBuilding.Part3");
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
int iTechBuildingHealth = GET_TEAM(getTeam()).getTechExtraBuildingHealth(eBuilding);
if (iTechBuildingHealth > 0)
{
changeBuildingGoodHealth(iTechBuildingHealth * iChange);
}
else if (iTechBuildingHealth < 0)
{
changeBuildingBadHealth(iTechBuildingHealth * iChange);
}
int iTechBuildingHappiness = GET_TEAM(getTeam()).getTechExtraBuildingHappiness(eBuilding);
if (iTechBuildingHappiness > 0)
{
changeBuildingGoodHappiness(iTechBuildingHappiness * iChange);
}
else if (iTechBuildingHappiness < 0)
{
changeBuildingGoodHappiness(iTechBuildingHappiness * iChange);
}
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
changeUnitCombatExtraStrength((UnitCombatTypes)iI, kBuilding.getUnitCombatExtraStrength(iI) * iChange);
}
changeInvasionChance(kBuilding.getInvasionChance() * iChange);
changeLineOfSight(kBuilding.getLineOfSight() * iChange);
changeAdjacentDamagePercent(kBuilding.getAdjacentDamagePercent() * iChange);
changeNumUnitFullHeal(kBuilding.getNumUnitFullHeal() * iChange);
changeNumPopulationEmployed(kBuilding.getNumPopulationEmployed() * iChange);
changeHealthPercentPerPopulation(kBuilding.getHealthPercentPerPopulation() * iChange);
changeHappinessPercentPerPopulation(kBuilding.getHappinessPercentPerPopulation() * iChange);
}
{
PROFILE("CvCity::processBuilding.Part4");
int iMinBuildingDefenseLevel = kBuilding.getNoEntryDefenseLevel();
if (iMinBuildingDefenseLevel > 0)
{
int iCurrentMinDefenseLevel = getMinimumDefenseLevel();
if ( iChange > 0 )
{
if ( iMinBuildingDefenseLevel > iCurrentMinDefenseLevel )
{
setMinimumDefenseLevel(iMinBuildingDefenseLevel);
}
}
else
{
if ( iMinBuildingDefenseLevel == iCurrentMinDefenseLevel )
{
int iNewMinDefenseLevel = 0;
for( iJ = 0; iJ < GC.getNumBuildingInfos(); iJ++ )
{
if ( getNumActiveBuilding((BuildingTypes)iJ) > 0 )
{
int iLevel = GC.getBuildingInfo((BuildingTypes)iJ).getNoEntryDefenseLevel();
if ( iLevel > iNewMinDefenseLevel )
{
iNewMinDefenseLevel = iLevel;
}
}
}
setMinimumDefenseLevel(iNewMinDefenseLevel);
}
}
}
if ( kBuilding.isZoneOfControl() )
{
changeZoCCount(iChange);
}
if (kBuilding.isProtectedCulture())
{
changeProtectedCultureCount(iChange > 0 ? 1 : -1 );
}
if (kBuilding.getWorkableRadius() > 0)
{
setWorkableRadiusOverride(iChange > 0 ? kBuilding.getWorkableRadius() : 0);
}
if (kBuilding.isProvidesFreshWater())
{
changeFreshWater(iChange);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
for (iI = 0; iI < GC.getNumImprovementInfos(); ++iI)
{
PROFILE("CvCity::processBuilding.Improvements");
changeImprovementFreeSpecialists((ImprovementTypes)iI, GC.getBuildingInfo(eBuilding).getImprovementFreeSpecialist(iI) * iChange);
}
FAssertMsg((0 < GC.getNumBonusInfos()) && "GC.getNumBonusInfos() is not greater than zero but an array is being allocated in CvPlotGroup::reset", "GC.getNumBonusInfos() is not greater than zero but an array is being allocated in CvPlotGroup::reset");
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
PROFILE("CvCity::processBuilding.Bonuses2");
if (hasBonus((BonusTypes)iI))
{
if (GC.getBuildingInfo(eBuilding).getBonusHealthChanges(iI) > 0)
{
changeBonusGoodHealth(GC.getBuildingInfo(eBuilding).getBonusHealthChanges(iI) * iChange);
}
else
{
changeBonusBadHealth(GC.getBuildingInfo(eBuilding).getBonusHealthChanges(iI) * iChange);
}
if (GC.getBuildingInfo(eBuilding).getBonusHappinessChanges(iI) > 0)
{
changeBonusGoodHappiness(GC.getBuildingInfo(eBuilding).getBonusHappinessChanges(iI) * iChange);
}
else
{
changeBonusBadHappiness(GC.getBuildingInfo(eBuilding).getBonusHappinessChanges(iI) * iChange);
}
if (GC.getBuildingInfo(eBuilding).getPowerBonus() == iI)
{
changePowerCount(iChange, GC.getBuildingInfo(eBuilding).isDirtyPower());
}
for (iJ = 0; iJ < NUM_YIELD_TYPES; iJ++)
{
changeBonusYieldRateModifier(((YieldTypes)iJ), (GC.getBuildingInfo(eBuilding).getBonusYieldModifier(iI, iJ) * iChange));
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
int iBonusChange = kBuilding.getBonusYieldChanges(iI, iJ);
if (hasVicinityBonus((BonusTypes)iI))
{
iBonusChange += kBuilding.getVicinityBonusYieldChanges(iI, iJ);
}
updateYieldRate(eBuilding, ((YieldTypes)iJ), ((getBuildingYieldChange((BuildingClassTypes)(kBuilding.getBuildingClassType()), (YieldTypes)iJ)) + (iBonusChange * iChange)));
}
for (iJ = 0; iJ < NUM_COMMERCE_TYPES; iJ++)
{
changeBonusCommerceRateModifier(((CommerceTypes)iJ), (kBuilding.getBonusCommerceModifier(iI, iJ) * iChange));
changeBonusCommercePercentChanges(((CommerceTypes)iJ), (kBuilding.getBonusCommercePercentChanges(iI, iJ) * iChange));
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
}
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
PROFILE("CvCity::processBuilding.UnitCombatFreeExp");
changeUnitCombatFreeExperience(((UnitCombatTypes)iI), GC.getBuildingInfo(eBuilding).getUnitCombatFreeExperience(iI) * iChange);
}
for (iI = 0; iI < NUM_DOMAIN_TYPES; iI++)
{
PROFILE("CvCity::processBuilding.Domains");
changeDomainFreeExperience(((DomainTypes)iI), GC.getBuildingInfo(eBuilding).getDomainFreeExperience(iI) * iChange);
changeDomainProductionModifier(((DomainTypes)iI), GC.getBuildingInfo(eBuilding).getDomainProductionModifier(iI) * iChange);
}
/************************************************************************************************/
/* Afforess ProductionModifier 10/13/09 */
/* */
/* */
/************************************************************************************************/
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
PROFILE("CvCity::processBuilding.UniClasses");
changeUnitClassProductionModifier(((UnitClassTypes)iI), kBuilding.getUnitClassProductionModifier(iI) * iChange);
}
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
PROFILE("CvCity::processBuilding.BuildingClasses");
changeBuildingClassProductionModifier(((BuildingClassTypes)iI), kBuilding.getBuildingClassProductionModifier(iI) * iChange);
for(int iCommerce = 0; iCommerce < NUM_COMMERCE_TYPES; iCommerce++ )
{
int iCommerceChange = kBuilding.getGlobalBuildingCommerceChange(iI, iCommerce);
if ( iCommerceChange != 0 )
{
GET_PLAYER(getOwnerINLINE()).changeBuildingClassCommerceChange((BuildingClassTypes)iI, (CommerceTypes)iCommerce, iCommerceChange * iChange);
}
}
}
/* for (iI = 0; iI < GC.getNumImprovementInfos(); iI++)
{
for (iJ = 0; iJ < NUM_YIELD_TYPES; iJ++)
{
changeImprovementYieldChange(((ImprovementTypes)iI), ((YieldTypes)iJ), (GC.getBuildingInfo(eBuilding).getImprovementYieldChanges(iI, iJ) * iChange));
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
PROFILE("CvCity::processBuilding.Part5");
updateExtraBuildingHappiness();
updateExtraBuildingHealth();
GET_PLAYER(getOwnerINLINE()).changeAssets(GC.getBuildingInfo(eBuilding).getAssetValue() * iChange);
area()->changePower(getOwnerINLINE(), (GC.getBuildingInfo(eBuilding).getPowerValue() * iChange));
GET_PLAYER(getOwnerINLINE()).changePower(GC.getBuildingInfo(eBuilding).getPowerValue() * iChange);
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).getTeam() == getTeam())
{
if (GC.getBuildingInfo(eBuilding).isTeamShare() || (iI == getOwnerINLINE()))
{
if ( GET_PLAYER((PlayerTypes)iI).isAlive() )
{
GET_PLAYER((PlayerTypes)iI).processBuilding(eBuilding, iChange, area());
}
}
}
}
if (GC.getBuildingInfo(eBuilding).getFreeTradeRegionBuildingClass() != NO_BUILDINGCLASS)
{
CvPlotGroup* plotGroup = plot()->getPlotGroup(getOwnerINLINE());
if ( plotGroup != NULL )
{
BuildingTypes eFreeBuilding = (BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(GC.getBuildingInfo(eBuilding).getFreeTradeRegionBuildingClass());
if (eFreeBuilding != NO_BUILDING)
{
plotGroup->changeNumFreeTradeRegionBuildings(eFreeBuilding, iChange);
}
}
}
}
GET_TEAM(getTeam()).processBuilding(eBuilding, iChange);
GC.getGameINLINE().processBuilding(eBuilding, iChange);
}
if (!bObsolete)
{
PROFILE("CvCity::processBuilding.NotObsolete");
//TB DEFENSEBUG: The following building defense line is allowing buildings that are replaced to continue to function!
//We can only assume this entire section therefore gets around replaced buildings, particularly after a recalc.
changeBuildingDefense(GC.getBuildingInfo(eBuilding).getDefenseModifier() * iChange);
changeBuildingBombardDefense(GC.getBuildingInfo(eBuilding).getBombardDefenseModifier() * iChange);
changeBaseGreatPeopleRate(GC.getBuildingInfo(eBuilding).getGreatPeopleRateChange() * iChange);
if (GC.getBuildingInfo(eBuilding).getGreatPeopleUnitClass() != NO_UNITCLASS)
{
eGreatPeopleUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(GC.getBuildingInfo(eBuilding).getGreatPeopleUnitClass())));
if (eGreatPeopleUnit != NO_UNIT)
{
changeGreatPeopleUnitRate(eGreatPeopleUnit, GC.getBuildingInfo(eBuilding).getGreatPeopleRateChange() * iChange);
}
}
GET_TEAM(getTeam()).changeBuildingClassCount((BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType(), iChange);
GET_PLAYER(getOwnerINLINE()).changeBuildingClassCount((BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType(), iChange);
GET_PLAYER(getOwnerINLINE()).changeWondersScore(getWonderScore((BuildingClassTypes)(GC.getBuildingInfo(eBuilding).getBuildingClassType())) * iChange);
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
if (hasBonus((BonusTypes)iI))
{
if (kBuilding.getBonusDefenseChanges(iI) != 0)
{
changeBonusDefenseChanges((BonusTypes)iI, kBuilding.getBonusDefenseChanges(iI) * iChange);
}
}
}
}
changeBuildingReplacementCount(eBuilding, (iChange > 0));
setMaintenanceDirty(true); // Always assume a chnage in buildings can change maintenance
updateBuildingCommerce();
m_buildingSourcedPropertyCache.clear();
// New or removed buildings can affect the assessment of the best plot builds
AI_markBestBuildValuesStale();
setLayoutDirty(true);
}
void CvCity::processProcess(ProcessTypes eProcess, int iChange)
{
int iI;
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
changeProductionToCommerceModifier(((CommerceTypes)iI), (GC.getProcessInfo(eProcess).getProductionToCommerceModifier(iI) * iChange));
}
}
void CvCity::processSpecialist(SpecialistTypes eSpecialist, int iChange)
{
UnitTypes eGreatPeopleUnit;
int iI;
if (GC.getSpecialistInfo(eSpecialist).getGreatPeopleUnitClass() != NO_UNITCLASS)
{
eGreatPeopleUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(GC.getSpecialistInfo(eSpecialist).getGreatPeopleUnitClass())));
if (eGreatPeopleUnit != NO_UNIT)
{
changeGreatPeopleUnitRate(eGreatPeopleUnit, GC.getSpecialistInfo(eSpecialist).getGreatPeopleRateChange() * iChange);
}
}
changeBaseGreatPeopleRate(GC.getSpecialistInfo(eSpecialist).getGreatPeopleRateChange() * iChange);
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
/************************************************************************************************/
/* Afforess Start 07/20/10 */
/* */
/* */
/************************************************************************************************/
/*
changeBaseYieldRate(((YieldTypes)iI), (GC.getSpecialistInfo(eSpecialist).getYieldChange(iI) * iChange));
*/
changeBaseYieldRate(((YieldTypes)iI), ((GC.getSpecialistInfo(eSpecialist).getYieldChange(iI) + (GET_PLAYER(getOwnerINLINE()).getSpecialistYieldPercentChanges(eSpecialist, (YieldTypes)iI) / 100)) * iChange));
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
/************************************************************************************************/
/* Afforess Start 07/20/10 */
/* */
/* */
/************************************************************************************************/
/*
changeSpecialistCommerce(((CommerceTypes)iI), (GC.getSpecialistInfo(eSpecialist).getCommerceChange(iI) * iChange));
*/
changeSpecialistCommerceTimes100(((CommerceTypes)iI), ((100*GC.getSpecialistInfo(eSpecialist).getCommerceChange(iI) + (GET_PLAYER(getOwnerINLINE()).getSpecialistCommercePercentChanges(eSpecialist, (CommerceTypes)iI))) * iChange));
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
updateExtraSpecialistYield();
/************************************************************************************************/
/* Specialists Enhancements, by Supercheese 10/9/09 */
/* */
/* */
/************************************************************************************************/
if (GC.getSpecialistInfo(eSpecialist).getHealthPercent() > 0)
{
changeSpecialistGoodHealth(GC.getSpecialistInfo(eSpecialist).getHealthPercent() * iChange);
}
else
{
changeSpecialistBadHealth(GC.getSpecialistInfo(eSpecialist).getHealthPercent() * iChange);
}
if (GC.getSpecialistInfo(eSpecialist).getHappinessPercent() > 0)
{
changeSpecialistHappiness(GC.getSpecialistInfo(eSpecialist).getHappinessPercent() * iChange);
}
else
{
changeSpecialistUnhappiness(GC.getSpecialistInfo(eSpecialist).getHappinessPercent() * iChange);
}
/************************************************************************************************/
/* Specialists Enhancements END */
/************************************************************************************************/
changeSpecialistFreeExperience(GC.getSpecialistInfo(eSpecialist).getExperience() * iChange);
}
HandicapTypes CvCity::getHandicapType() const
{
return GET_PLAYER(getOwnerINLINE()).getHandicapType();
}
CivilizationTypes CvCity::getCivilizationType() const
{
/************************************************************************************************/
/* Afforess Start 01/14/10 */
/* */
/* */
/************************************************************************************************/
//45deg: Original code restored here because it was causing troubles with city ownership when assimilation is off
if (!GC.getGameINLINE().isOption(GAMEOPTION_ASSIMILATION) || m_iCiv == NO_CIVILIZATION)
{
return GET_PLAYER(getOwnerINLINE()).getCivilizationType();
}
return (CivilizationTypes)m_iCiv;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
LeaderHeadTypes CvCity::getPersonalityType() const
{
return GET_PLAYER(getOwnerINLINE()).getPersonalityType();
}
ArtStyleTypes CvCity::getArtStyleType() const
{
/************************************************************************************************/
/* Afforess Start 07/12/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getGameINLINE().isOption(GAMEOPTION_ASSIMILATION))
{
if (getOriginalOwner() != NO_PLAYER)
{
return GET_PLAYER(getOriginalOwner()).getArtStyleType();
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return GET_PLAYER(getOwnerINLINE()).getArtStyleType();
}
CitySizeTypes CvCity::getCitySizeType() const
{
return ((CitySizeTypes)(range((getPopulation() / 7), 0, (NUM_CITYSIZE_TYPES - 1))));
}
const CvArtInfoBuilding* CvCity::getBuildingArtInfo(BuildingTypes eBuilding) const
{
return GC.getBuildingInfo(eBuilding).getArtInfo();
}
float CvCity::getBuildingVisibilityPriority(BuildingTypes eBuilding) const
{
return GC.getBuildingInfo(eBuilding).getVisibilityPriority();
}
bool CvCity::hasTrait(TraitTypes eTrait) const
{
return GET_PLAYER(getOwnerINLINE()).hasTrait(eTrait);
}
bool CvCity::isBarbarian() const
{
return GET_PLAYER(getOwnerINLINE()).isBarbarian();
}
bool CvCity::isHuman() const
{
return GET_PLAYER(getOwnerINLINE()).isHuman();
}
bool CvCity::isVisible(TeamTypes eTeam, bool bDebug) const
{
return plot()->isVisible(eTeam, bDebug);
}
bool CvCity::isCapital() const
{
return (GET_PLAYER(getOwnerINLINE()).getCapitalCity() == this);
}
bool CvCity::isCoastal(int iMinWaterSize) const
{
return plot()->isCoastalLand(iMinWaterSize);
}
bool CvCity::isDisorder() const
{
return (isOccupation() || GET_PLAYER(getOwnerINLINE()).isAnarchy());
}
bool CvCity::isHolyCity(ReligionTypes eIndex) const
{
return (GC.getGameINLINE().getHolyCity(eIndex) == this);
}
bool CvCity::isHolyCity() const
{
int iI;
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (isHolyCity((ReligionTypes)iI))
{
return true;
}
}
return false;
}
bool CvCity::isHeadquarters(CorporationTypes eIndex) const
{
return (GC.getGameINLINE().getHeadquarters(eIndex) == this);
}
void CvCity::setHeadquarters(CorporationTypes eIndex)
{
if ( !isHeadquarters(eIndex) )
{
GC.getGameINLINE().setHeadquarters(eIndex, this, true);
if (GC.getCorporationInfo(eIndex).getFreeUnitClass() != NO_UNITCLASS)
{
UnitTypes eFreeUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(GC.getCorporationInfo(eIndex).getFreeUnitClass())));
if (eFreeUnit != NO_UNIT)
{
GET_PLAYER(getOwnerINLINE()).initUnit(eFreeUnit, getX_INLINE(), getY_INLINE(), NO_UNITAI, NO_DIRECTION, getCitySorenRandNum(10000, "AI Unit Birthmark"));
}
}
}
}
bool CvCity::isHeadquarters() const
{
int iI;
for (iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
if (isHeadquarters((CorporationTypes)iI))
{
return true;
}
}
return false;
}
int CvCity::getOvercrowdingPercentAnger(int iExtra) const
{
int iOvercrowding;
int iAnger;
iAnger = 0;
iOvercrowding = (getPopulation() + iExtra);
if (iOvercrowding > 0)
{
iAnger += (((iOvercrowding * GC.getPERCENT_ANGER_DIVISOR()) / std::max(1, (getPopulation() + iExtra))) + 1);
}
//Afforess: civic population anger modifier start
int iCivicPopulationAngerModifier = 100;
CvPlayer& kPlayer = GET_PLAYER(getOwnerINLINE());
for (int iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
CivicTypes eCivic = kPlayer.getCivics((CivicOptionTypes)iI);
if (eCivic != NO_CIVIC)
{
iCivicPopulationAngerModifier += GC.getCivicInfo(eCivic).getPopulationAngerModifier();
}
}
iAnger *= iCivicPopulationAngerModifier;
iAnger += 50; //Round up to the nearest anger
iAnger /= 100;
//Afforess end
return iAnger;
}
int CvCity::getNoMilitaryPercentAnger() const
{
int iAnger;
iAnger = 0;
if (getMilitaryHappinessUnits() == 0)
{
iAnger += GC.getDefineINT("NO_MILITARY_PERCENT_ANGER");
}
return iAnger;
}
int CvCity::getCulturePercentAnger() const
{
int iTotalCulture;
int iAngryCulture;
int iCulture;
int iI;
iTotalCulture = plot()->countTotalCulture();
if (iTotalCulture == 0)
{
return 0;
}
iAngryCulture = 0;
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (GET_PLAYER((PlayerTypes)iI).getTeam() != getTeam())
{
iCulture = plot()->getCulture((PlayerTypes)iI);
if (iCulture > 0)
{
if (atWar(GET_PLAYER((PlayerTypes)iI).getTeam(), getTeam()))
{
if ( iCulture > MAX_INT/1000 )
{
iCulture /= 100;
iCulture *= std::max(0, (GC.getDefineINT("AT_WAR_CULTURE_ANGER_MODIFIER") + 100));
iCulture *= 100;
}
else
{
iCulture *= std::max(0, (GC.getDefineINT("AT_WAR_CULTURE_ANGER_MODIFIER") + 100));
iCulture /= 100;
}
}
iAngryCulture += iCulture;
}
}
}
}
return ((GC.getDefineINT("CULTURE_PERCENT_ANGER") * iAngryCulture) / iTotalCulture);
}
int CvCity::getReligionPercentAnger() const
{
int iCount;
int iAnger;
int iI;
if (GC.getGameINLINE().getNumCities() == 0)
{
return 0;
}
if (getReligionCount() == 0)
{
return 0;
}
iCount = 0;
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (atWar(GET_PLAYER((PlayerTypes)iI).getTeam(), getTeam()))
{
FAssertMsg(GET_PLAYER((PlayerTypes)iI).getTeam() != getTeam(), "Player is at war with himself! :O");
if (GET_PLAYER((PlayerTypes)iI).getStateReligion() != NO_RELIGION)
{
if (isHasReligion(GET_PLAYER((PlayerTypes)iI).getStateReligion()))
{
iCount += GET_PLAYER((PlayerTypes)iI).getHasReligionCount(GET_PLAYER((PlayerTypes)iI).getStateReligion());
}
}
}
}
}
iAnger = GC.getDefineINT("RELIGION_PERCENT_ANGER");
iAnger *= iCount;
iAnger /= GC.getGameINLINE().getNumCities();
iAnger /= getReligionCount();
return iAnger;
}
int CvCity::getHurryPercentAnger(int iExtra) const
{
if (getHurryAngerTimer() == 0)
{
return 0;
}
return ((((((getHurryAngerTimer() - 1) / flatHurryAngerLength()) + 1) * GC.getDefineINT("HURRY_POP_ANGER") * GC.getPERCENT_ANGER_DIVISOR()) / std::max(1, getPopulation() + iExtra)) + 1);
}
int CvCity::getConscriptPercentAnger(int iExtra) const
{
if (getConscriptAngerTimer() == 0)
{
return 0;
}
return ((((((getConscriptAngerTimer() - 1) / flatConscriptAngerLength()) + 1) * GC.getDefineINT("CONSCRIPT_POP_ANGER") * GC.getPERCENT_ANGER_DIVISOR()) / std::max(1, getPopulation() + iExtra)) + 1);
}
int CvCity::getDefyResolutionPercentAnger(int iExtra) const
{
if (getDefyResolutionAngerTimer() == 0)
{
return 0;
}
return ((((((getDefyResolutionAngerTimer() - 1) / flatDefyResolutionAngerLength()) + 1) * GC.getDefineINT("DEFY_RESOLUTION_POP_ANGER") * GC.getPERCENT_ANGER_DIVISOR()) / std::max(1, getPopulation() + iExtra)) + 1);
}
int CvCity::getWarWearinessPercentAnger() const
{
int iAnger;
iAnger = GET_PLAYER(getOwnerINLINE()).getWarWearinessPercentAnger();
iAnger *= std::max(0, (getWarWearinessModifier() + GET_PLAYER(getOwnerINLINE()).getWarWearinessModifier() + 100));
iAnger /= 100;
/************************************************************************************************/
/* Afforess Start 06/29/10 */
/* */
/* */
/************************************************************************************************/
iAnger *= std::max(0, (getWarWearinessTimer() + 100));
iAnger /= 100;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iAnger;
}
/************************************************************************************************/
/* REVOLUTION_MOD 04/26/08 jdog5000 */
/* */
/* */
/************************************************************************************************/
int CvCity::getRevRequestPercentAnger(int iExtra) const
{
if (getRevRequestAngerTimer() == 0)
{
return 0;
}
int iAnger, iAngerPercent;
iAnger = GC.getDefineINT("HURRY_ANGER_DIVISOR");
iAnger *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getHurryConscriptAngerPercent();
iAnger /= 100;
iAnger = std::max(1, iAnger);
iAngerPercent = 2*((((((getRevRequestAngerTimer() - 1) / iAnger) + 1) * GC.getDefineINT("HURRY_POP_ANGER") * GC.getPERCENT_ANGER_DIVISOR()) / std::max(1, getPopulation() + iExtra)) + 1);
return iAngerPercent;
}
int CvCity::getRevIndexPercentAnger(int iExtra) const
{
int iAnger = 0;
int iLocalAdjust = std::min((getLocalRevIndex()*3)/4,getLocalRevIndex()/2);
iLocalAdjust = std::min(iLocalAdjust,10);
iAnger = (int)((12.5+iLocalAdjust)*(getRevolutionIndex() - 325))/750;
iAnger = std::max(0,iAnger);
iAnger = std::min(iAnger, 40);
return (GC.getPERCENT_ANGER_DIVISOR()/100)*iAnger;
}
int CvCity::getRevSuccessHappiness() const
{
if (getRevSuccessTimer() == 0)
{
return 0;
}
int iHappy, iHappyPercent;
iHappy = GC.getDefineINT("HURRY_ANGER_DIVISOR");
iHappy *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getHurryConscriptAngerPercent();
iHappy /= 100;
iHappy = std::max(1, iHappy);
iHappyPercent = 2*((((((getRevSuccessTimer() - 1) / iHappy) + 1) * GC.getDefineINT("HURRY_POP_ANGER") * GC.getPERCENT_ANGER_DIVISOR()) / std::max(1, getPopulation())) + 1);
return (iHappyPercent*getPopulation())/GC.getPERCENT_ANGER_DIVISOR();
}
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
int CvCity::getLargestCityHappiness() const
{
if (findPopulationRank() <= GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities())
{
return GET_PLAYER(getOwnerINLINE()).getLargestCityHappiness();
}
return 0;
}
int CvCity::getVassalHappiness() const
{
int iHappy = 0;
for (int i = 0; i < MAX_TEAMS; i++)
{
if (getTeam() != i)
{
if (GET_TEAM((TeamTypes)i).isVassal(getTeam()))
{
iHappy += GC.getDefineINT("VASSAL_HAPPINESS");
}
}
}
return iHappy;
}
int CvCity::getVassalUnhappiness() const
{
int iUnhappy = 0;
for (int i = 0; i < MAX_TEAMS; i++)
{
if (getTeam() != i)
{
if (GET_TEAM(getTeam()).isVassal((TeamTypes)i))
{
iUnhappy += GC.getDefineINT("VASSAL_HAPPINESS");
}
}
}
return iUnhappy;
}
int CvCity::unhappyLevel(int iExtra) const
{
PROFILE_FUNC();
int iAngerPercent;
int iUnhappiness;
int iI;
iUnhappiness = 0;
/************************************************************************************************/
/* Afforess Start 01/09/10 */
/* */
/* */
/************************************************************************************************/
if (!isNoUnhappiness())
{
if (!isCapital() || GET_PLAYER(getOwnerINLINE()).getNoCapitalUnhappiness() == 0)
{
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iAngerPercent = 0;
iAngerPercent += getOvercrowdingPercentAnger(iExtra);
iAngerPercent += getNoMilitaryPercentAnger();
iAngerPercent += getCulturePercentAnger();
iAngerPercent += getReligionPercentAnger();
iAngerPercent += getHurryPercentAnger(iExtra);
iAngerPercent += getConscriptPercentAnger(iExtra);
iAngerPercent += getDefyResolutionPercentAnger(iExtra);
iAngerPercent += getWarWearinessPercentAnger();
/************************************************************************************************/
/* REVOLUTION_MOD 04/26/08 jdog5000 */
/* */
/* */
/************************************************************************************************/
iAngerPercent += getRevRequestPercentAnger(iExtra);
iAngerPercent += getRevIndexPercentAnger(iExtra);
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
{
iAngerPercent += GET_PLAYER(getOwnerINLINE()).getCivicPercentAnger((CivicTypes)iI);
}
iUnhappiness = ((iAngerPercent * (getPopulation() + iExtra)) / GC.getPERCENT_ANGER_DIVISOR());
iUnhappiness -= std::min(0, getLargestCityHappiness());
iUnhappiness -= std::min(0, getMilitaryHappiness());
iUnhappiness -= std::min(0, getCurrentStateReligionHappiness());
iUnhappiness -= std::min(0, getBuildingBadHappiness());
iUnhappiness -= std::min(0, getExtraBuildingBadHappiness());
iUnhappiness -= std::min(0, getFeatureBadHappiness());
iUnhappiness -= std::min(0, getBonusBadHappiness());
iUnhappiness -= std::min(0, getReligionBadHappiness());
iUnhappiness -= std::min(0, getCommerceHappiness());
iUnhappiness -= std::min(0, area()->getBuildingHappiness(getOwnerINLINE()));
iUnhappiness -= std::min(0, GET_PLAYER(getOwnerINLINE()).getBuildingHappiness());
iUnhappiness -= std::min(0, (getExtraHappiness() + GET_PLAYER(getOwnerINLINE()).getExtraHappiness()));
iUnhappiness -= std::min(0, GC.getHandicapInfo(getHandicapType()).getHappyBonus());
iUnhappiness += std::max(0, getVassalUnhappiness());
iUnhappiness += std::max(0, getEspionageHappinessCounter());
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
iUnhappiness -= std::min(0, getCivicHappiness());
iUnhappiness -= std::min(0, getSpecialistUnhappiness() / 100);
iUnhappiness -= std::min(0, (GET_PLAYER(getOwnerINLINE()).getWorldHappiness()));
iUnhappiness -= std::min(0, (GET_PLAYER(getOwnerINLINE()).getProjectHappiness()));
iUnhappiness += std::max(0, GET_PLAYER(getOwnerINLINE()).calculateTaxRateUnhappiness());
iUnhappiness -= std::min(0, calculateCorporationHappiness());
iUnhappiness += std::max(0, getEventAnger());
iUnhappiness += std::max(0, GET_PLAYER(getOwnerINLINE()).getNumCities() / std::max(1, GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getNumCityUnhappinessDivisor()));
int iForeignAnger = GET_PLAYER(getOwnerINLINE()).getForeignUnhappyPercent();
if (iForeignAnger != 0) {
iForeignAnger = 100 / iForeignAnger;
iForeignAnger = ((100 - plot()->calculateCulturePercent(getOwnerINLINE())) * iForeignAnger) / 100;
iUnhappiness += std::max(0, iForeignAnger);
}
if (GC.getGameINLINE().isOption(GAMEOPTION_PERSONALIZED_MAP))
{
if (!GET_PLAYER(getOwnerINLINE()).isNoLandmarkAnger())
{
iUnhappiness += std::max(0, getLandmarkAnger());
}
iUnhappiness -= std::min(0, GET_PLAYER(getOwnerINLINE()).getLandmarkHappiness());
}
if ( GET_PLAYER(getOwnerINLINE()).getCityLimit() != 0 &&
GET_PLAYER(getOwnerINLINE()).getCityOverLimitUnhappy() != 0 )
{
int overLimitCities = GET_PLAYER(getOwnerINLINE()).getNumCities() - GET_PLAYER(getOwnerINLINE()).getCityLimit();
if ( overLimitCities > 0 )
{
iUnhappiness += GET_PLAYER(getOwnerINLINE()).getCityOverLimitUnhappy()*overLimitCities;
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
return std::max(0, iUnhappiness);
}
int CvCity::happyLevel() const
{
PROFILE_FUNC();
int iHappiness;
iHappiness = 0;
/************************************************************************************************/
/* REVOLUTION_MOD 04/28/08 jdog5000 */
/* */
/* */
/************************************************************************************************/
iHappiness += std::max(0, getRevSuccessHappiness());
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
iHappiness += std::max(0, getLargestCityHappiness());
iHappiness += std::max(0, getMilitaryHappiness());
iHappiness += std::max(0, getCurrentStateReligionHappiness());
iHappiness += std::max(0, getBuildingGoodHappiness());
iHappiness += std::max(0, getExtraBuildingGoodHappiness());
iHappiness += std::max(0, getFeatureGoodHappiness());
iHappiness += std::max(0, getBonusGoodHappiness());
iHappiness += std::max(0, getReligionGoodHappiness());
iHappiness += std::max(0, getCommerceHappiness());
iHappiness += std::max(0, area()->getBuildingHappiness(getOwnerINLINE()));
iHappiness += std::max(0, GET_PLAYER(getOwnerINLINE()).getBuildingHappiness());
iHappiness += std::max(0, (getExtraHappiness() + GET_PLAYER(getOwnerINLINE()).getExtraHappiness()));
iHappiness += std::max(0, GC.getHandicapInfo(getHandicapType()).getHappyBonus());
iHappiness += std::max(0, getVassalHappiness());
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
iHappiness += std::max(0, getCivicHappiness());
iHappiness += std::max(0, getSpecialistHappiness() / 100);
iHappiness += std::max(0, (GET_PLAYER(getOwnerINLINE()).getWorldHappiness()));
iHappiness += std::max(0, (GET_PLAYER(getOwnerINLINE()).getProjectHappiness()));
iHappiness += std::max(0, calculateCorporationHappiness());
if (GC.getGameINLINE().isOption(GAMEOPTION_PERSONALIZED_MAP))
{
iHappiness += std::max(0, GET_PLAYER(getOwnerINLINE()).getLandmarkHappiness());
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (getHappinessTimer() > 0)
{
iHappiness += GC.getDefineINT("TEMP_HAPPY");
}
return std::max(0, iHappiness);
}
int CvCity::angryPopulation(int iExtra) const
{
PROFILE("CvCityAI::angryPopulation");
return range((unhappyLevel(iExtra) - happyLevel()), 0, (getPopulation() + iExtra));
}
int CvCity::visiblePopulation() const
{
/************************************************************************************************/
/* Afforess Start 08/23/10 */
/* */
/* */
/************************************************************************************************/
return (getPopulation() - angryPopulation() - getWorkingPopulation() - getNumPopulationEmployed());
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
int CvCity::totalFreeSpecialists() const
{
int iCount = 0;
if (getPopulation() > 0)
{
iCount += getFreeSpecialist();
iCount += area()->getFreeSpecialist(getOwnerINLINE());
iCount += GET_PLAYER(getOwnerINLINE()).getFreeSpecialist();
for (int iImprovement = 0; iImprovement < GC.getNumImprovementInfos(); ++iImprovement)
{
int iNumSpecialistsPerImprovement = getImprovementFreeSpecialists((ImprovementTypes)iImprovement);
if (iNumSpecialistsPerImprovement > 0)
{
iCount += iNumSpecialistsPerImprovement * countNumImprovedPlots((ImprovementTypes)iImprovement);
}
}
}
return iCount;
}
int CvCity::extraPopulation() const
{
return (visiblePopulation() + std::min(0, extraFreeSpecialists()));
}
int CvCity::extraSpecialists() const
{
return (visiblePopulation() + extraFreeSpecialists());
}
int CvCity::extraFreeSpecialists() const
{
return (totalFreeSpecialists() - getSpecialistPopulation());
}
int CvCity::unhealthyPopulation(bool bNoAngry, int iExtra) const
{
if (isNoUnhealthyPopulation())
{
return 0;
}
return std::max(0, (getPopulation() + iExtra - ( bNoAngry ? angryPopulation(iExtra) : 0)));
}
int CvCity::totalGoodBuildingHealth() const
{
/************************************************************************************************/
/* Afforess Start 08/31/10 */
/* */
/* */
/************************************************************************************************/
/*
return (getBuildingGoodHealth() + area()->getBuildingGoodHealth(getOwnerINLINE()) + GET_PLAYER(getOwnerINLINE()).getBuildingGoodHealth() + getExtraBuildingGoodHealth());
*/
int iHealth = getBuildingGoodHealth();
iHealth += area()->getBuildingGoodHealth(getOwnerINLINE());
iHealth += GET_PLAYER(getOwnerINLINE()).getBuildingGoodHealth();
iHealth += getExtraBuildingGoodHealth();
iHealth += std::max(0, calculatePopulationHealth());
return iHealth;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
int CvCity::totalBadBuildingHealth() const
{
/************************************************************************************************/
/* Afforess Start 08/31/10 */
/* */
/* */
/************************************************************************************************/
/*
if (!isBuildingOnlyHealthy())
{
return (getBuildingBadHealth() + area()->getBuildingBadHealth(getOwnerINLINE()) + GET_PLAYER(getOwnerINLINE()).getBuildingBadHealth() + getExtraBuildingBadHealth());
}
*/
if (!isBuildingOnlyHealthy())
{
int iHealth = getBuildingBadHealth();
iHealth += area()->getBuildingBadHealth(getOwnerINLINE());
iHealth += GET_PLAYER(getOwnerINLINE()).getBuildingBadHealth();
iHealth += getExtraBuildingBadHealth();
iHealth += std::min(0, calculatePopulationHealth());
return iHealth;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return 0;
}
int CvCity::goodHealth() const
{
PROFILE_FUNC();
int iTotalHealth;
int iHealth;
iTotalHealth = 0;
iHealth = getFreshWaterGoodHealth();
if (iHealth > 0)
{
iTotalHealth += iHealth;
}
iHealth = getFeatureGoodHealth();
if (iHealth > 0)
{
iTotalHealth += iHealth;
}
iHealth = getPowerGoodHealth();
if (iHealth > 0)
{
iTotalHealth += iHealth;
}
iHealth = getBonusGoodHealth();
if (iHealth > 0)
{
iTotalHealth += iHealth;
}
iHealth = totalGoodBuildingHealth();
if (iHealth > 0)
{
iTotalHealth += iHealth;
}
iHealth = GET_PLAYER(getOwnerINLINE()).getExtraHealth() + getExtraHealth();
if (iHealth > 0)
{
iTotalHealth += iHealth;
}
iHealth = GC.getHandicapInfo(getHandicapType()).getHealthBonus();
if (iHealth > 0)
{
iTotalHealth += iHealth;
}
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
iHealth = getImprovementGoodHealth() / 100;
if (iHealth > 0)
{
iTotalHealth += iHealth;
}
iHealth = getSpecialistGoodHealth() / 100;
if (iHealth > 0)
{
iTotalHealth += iHealth;
}
iHealth = GET_PLAYER(getOwnerINLINE()).getCivilizationHealth();
if (iHealth > 0)
{
iTotalHealth += iHealth;
}
iHealth = calculateCorporationHealth();
if (iHealth > 0)
{
iTotalHealth += iHealth;
}
iHealth = GET_PLAYER(getOwnerINLINE()).getWorldHealth();
if (iHealth > 0)
{
iTotalHealth += iHealth;
}
iHealth = GET_PLAYER(getOwnerINLINE()).getProjectHealth();
if (iHealth > 0)
{
iTotalHealth += iHealth;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iTotalHealth;
}
int CvCity::badHealth(bool bNoAngry, int iExtra) const
{
PROFILE_FUNC();
int iTotalHealth;
int iHealth;
iTotalHealth = 0;
iHealth = getEspionageHealthCounter();
if (iHealth > 0)
{
iTotalHealth -= iHealth;
}
iHealth = getFreshWaterBadHealth();
if (iHealth < 0)
{
iTotalHealth += iHealth;
}
iHealth = getFeatureBadHealth();
if (iHealth < 0)
{
iTotalHealth += iHealth;
}
iHealth = getPowerBadHealth();
if (iHealth < 0)
{
iTotalHealth += iHealth;
}
iHealth = getBonusBadHealth();
if (iHealth < 0)
{
iTotalHealth += iHealth;
}
iHealth = totalBadBuildingHealth();
if (iHealth < 0)
{
iTotalHealth += iHealth;
}
iHealth = GET_PLAYER(getOwnerINLINE()).getExtraHealth() + getExtraHealth();
if (iHealth < 0)
{
iTotalHealth += iHealth;
}
iHealth = GC.getHandicapInfo(getHandicapType()).getHealthBonus();
if (iHealth < 0)
{
iTotalHealth += iHealth;
}
iHealth = getExtraBuildingBadHealth();
if (iHealth < 0)
{
iTotalHealth += iHealth;
}
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
iHealth = getImprovementBadHealth() / 100;
if (iHealth < 0)
{
iTotalHealth += iHealth;
}
iHealth = getSpecialistBadHealth() / 100;
if (iHealth < 0)
{
iTotalHealth += iHealth;
}
iHealth = GET_PLAYER(getOwnerINLINE()).getCivilizationHealth();
if (iHealth < 0)
{
iTotalHealth += iHealth;
}
iHealth = calculateCorporationHealth();
if (iHealth < 0)
{
iTotalHealth += iHealth;
}
iHealth = GET_PLAYER(getOwnerINLINE()).getWorldHealth();
if (iHealth < 0)
{
iTotalHealth += iHealth;
}
iHealth = GET_PLAYER(getOwnerINLINE()).getProjectHealth();
if (iHealth < 0)
{
iTotalHealth += iHealth;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return (unhealthyPopulation(bNoAngry, iExtra) - iTotalHealth);
}
int CvCity::healthRate(bool bNoAngry, int iExtra) const
{
return std::min(0, (goodHealth() - badHealth(bNoAngry, iExtra)));
}
int CvCity::foodConsumption(bool bNoAngry, int iExtra, bool bIncludeWastage) const
{
int result = ((((getPopulation() + iExtra) - ((bNoAngry) ? angryPopulation(iExtra) : 0)) * GC.getFOOD_CONSUMPTION_PER_POPULATION()) - healthRate(bNoAngry, iExtra) + (bIncludeWastage ? (int)foodWastage() : 0));
return result;
}
float CvCity::foodWastage(int surplass) const
{
#define MAX_SURPLASS 500
static float calculatedWaste[MAX_SURPLASS];
static int calculatedTo = -1;
int startWasteAtConsumptionPercent = GC.getDefineINT("WASTAGE_START_CONSUMPTION_PERCENT");
float wastageGrowthFactor = GC.getDefineFLOAT("WASTAGE_GROWTH_FACTOR");
if ( wastageGrowthFactor == 0 )
{
wastageGrowthFactor = (float)0.05; // default
}
if ( startWasteAtConsumptionPercent >= 0 )
{
if ( surplass == -1 )
{
surplass = foodDifference(true, false) - (GC.getFOOD_CONSUMPTION_PER_POPULATION()*getPopulation()*startWasteAtConsumptionPercent)/100;
}
}
else
{
surplass = -1;
}
// Nothing wasted if there is no surplass
if ( surplass <= 0 )
{
return 0;
}
// Cache what we can as it's not a trivially cheap computation
else if ( surplass <= calculatedTo )
{
return calculatedWaste[surplass];
}
else
{
if ( surplass >= MAX_SURPLASS )
{
// After the max we bother calculating it all gets wasted
return calculatedWaste[MAX_SURPLASS-1] + (surplass - MAX_SURPLASS + 1);
}
else
{
calculatedWaste[surplass] = foodWastage(surplass-1) + ((float)1 - (wastageGrowthFactor + pow((float)1.0 - wastageGrowthFactor, surplass))/((float)1.0+wastageGrowthFactor));
calculatedTo = surplass;
return calculatedWaste[surplass];
}
}
}
int CvCity::foodDifference(bool bBottom, bool bIncludeWastage, bool bIgnoreFoodBuildOrRev) const
{
int iDifference;
if (!bIgnoreFoodBuildOrRev && isDisorder())
{
return 0;
}
if (!bIgnoreFoodBuildOrRev && isFoodProduction())
{
iDifference = std::min(0, (getYieldRate(YIELD_FOOD) - foodConsumption(false,0,bIncludeWastage)));
}
else
{
iDifference = (getYieldRate(YIELD_FOOD) - foodConsumption(false,0,bIncludeWastage));
}
if (bBottom)
{
if ((getPopulation() == 1) && (getFood() == 0))
{
iDifference = std::max(0, iDifference);
}
}
return iDifference;
}
/************************************************************************************************/
/* Afforess Food Threshold Modifier Start 10/16/09 */
/* */
/* */
/************************************************************************************************/
int CvCity::growthThreshold() const
{
int iThreshold = GET_PLAYER(getOwnerINLINE()).getGrowthThreshold(getPopulation());
iThreshold *= (GET_PLAYER(getOwnerINLINE()).getPopulationgrowthratepercentage() + 100);
iThreshold /= 100;
iThreshold *= (getPopulationgrowthratepercentage() + 100);
iThreshold /= 100;
if (getNumCityPlots() == NUM_CITY_PLOTS)
{
iThreshold = iThreshold*(100+GC.getDefineINT("CITY_THIRD_RING_EXTRA_GROWTH_THRESHOLD_PERCENT"))/100;
}
if ( isBarbarian() )
{
iThreshold /= 2; // Those barbarians are just so damned fecund!
}
return std::max(1,iThreshold);
}
/************************************************************************************************/
/* Afforess Food Threshold Modifier END */
/************************************************************************************************/
int CvCity::productionLeft() const
{
return (getProductionNeeded() - getProduction());
}
int CvCity::getHurryCostModifier(bool bIgnoreNew) const
{
int iModifier = 100;
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
iModifier = getHurryCostModifier((UnitTypes) EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1), bIgnoreNew);
break;
case ORDER_CONSTRUCT:
iModifier = getHurryCostModifier((BuildingTypes) pOrderNode->m_data.iData1, bIgnoreNew);
break;
case ORDER_CREATE:
case ORDER_MAINTAIN:
case ORDER_LIST:
break;
default:
FAssertMsg(false, "pOrderNode->m_data.eOrderType did not match a valid option");
break;
}
}
return iModifier;
}
int CvCity::getHurryCostModifier(UnitTypes eUnit, bool bIgnoreNew) const
{
return getHurryCostModifier(GC.getUnitInfo(eUnit).getHurryCostModifier(), getUnitProduction(eUnit), bIgnoreNew);
}
int CvCity::getHurryCostModifier(BuildingTypes eBuilding, bool bIgnoreNew) const
{
return getHurryCostModifier(GC.getBuildingInfo(eBuilding).getHurryCostModifier(), getBuildingProduction(eBuilding), bIgnoreNew);
}
int CvCity::getHurryCostModifier(int iBaseModifier, int iProduction, bool bIgnoreNew) const
{
int iModifier = 100;
iModifier *= std::max(0, iBaseModifier + 100);
iModifier /= 100;
if (iProduction == 0 && !bIgnoreNew)
{
iModifier *= std::max(0, (GC.getDefineINT("NEW_HURRY_MODIFIER") + 100));
iModifier /= 100;
}
iModifier *= std::max(0, (GET_PLAYER(getOwnerINLINE()).getHurryModifier() + 100));
iModifier /= 100;
/************************************************************************************************/
/* Afforess Start 06/27/10 */
/* */
/* */
/************************************************************************************************/
iModifier *= std::max(0, (GET_PLAYER(getOwnerINLINE()).getHurryCostModifier() + 100));
iModifier /= 100;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return std::max(1,iModifier); // Avoid potential divide by 0s
}
int CvCity::hurryCost(bool bExtra) const
{
return (getHurryCost(bExtra, productionLeft(), getHurryCostModifier(), getProductionModifier()));
}
int CvCity::getHurryCost(bool bExtra, UnitTypes eUnit, bool bIgnoreNew) const
{
int iProductionLeft = getProductionNeeded(eUnit) - getUnitProduction(eUnit);
return getHurryCost(bExtra, iProductionLeft, getHurryCostModifier(eUnit, bIgnoreNew), getProductionModifier(eUnit));
}
int CvCity::getHurryCost(bool bExtra, BuildingTypes eBuilding, bool bIgnoreNew) const
{
int iProductionLeft = getProductionNeeded(eBuilding) - getBuildingProduction(eBuilding);
return getHurryCost(bExtra, iProductionLeft, getHurryCostModifier(eBuilding, bIgnoreNew), getProductionModifier(eBuilding));
}
int CvCity::getHurryCost(bool bExtra, int iProductionLeft, int iHurryModifier, int iModifier) const
{
int iProduction = (iProductionLeft * iHurryModifier + 99) / 100; // round up
if (bExtra)
{
int iExtraProduction = getExtraProductionDifference(iProduction, iModifier);
if (iExtraProduction > 0)
{
int iAdjustedProd = iProduction * iProduction;
// round up
iProduction = (iAdjustedProd + (iExtraProduction - 1)) / iExtraProduction;
}
}
return std::max(0, iProduction);
}
int CvCity::hurryGold(HurryTypes eHurry) const
{
return getHurryGold(eHurry, hurryCost(false));
}
int CvCity::getHurryGold(HurryTypes eHurry, int iHurryCost) const
{
int iGold;
if (GC.getHurryInfo(eHurry).getGoldPerProduction() == 0)
{
return 0;
}
iGold = (iHurryCost * GC.getHurryInfo(eHurry).getGoldPerProduction());
return std::max(1, iGold);
}
int CvCity::hurryPopulation(HurryTypes eHurry) const
{
return (getHurryPopulation(eHurry, hurryCost(true)));
}
int CvCity::getHurryPopulation(HurryTypes eHurry, int iHurryCost) const
{
if (GC.getHurryInfo(eHurry).getProductionPerPopulation() == 0)
{
return 0;
}
int iPopulation = (iHurryCost - 1) / GC.getGameINLINE().getProductionPerPopulation(eHurry);
return std::max(1, (iPopulation + 1));
}
int CvCity::hurryProduction(HurryTypes eHurry) const
{
int iProduction;
if (GC.getHurryInfo(eHurry).getProductionPerPopulation() > 0)
{
iProduction = (100 * getExtraProductionDifference(hurryPopulation(eHurry) * GC.getGameINLINE().getProductionPerPopulation(eHurry))) / std::max(1, getHurryCostModifier());
FAssert(iProduction >= productionLeft());
}
else
{
iProduction = productionLeft();
}
return iProduction;
}
int CvCity::flatHurryAngerLength() const
{
int iAnger;
iAnger = GC.getDefineINT("HURRY_ANGER_DIVISOR");
iAnger *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getHurryConscriptAngerPercent();
iAnger /= 100;
iAnger *= std::max(0, 100 + getHurryAngerModifier());
iAnger /= 100;
return std::max(1, iAnger);
}
int CvCity::hurryAngerLength(HurryTypes eHurry) const
{
if (GC.getHurryInfo(eHurry).isAnger())
{
return flatHurryAngerLength();
}
else
{
return 0;
}
}
int CvCity::maxHurryPopulation() const
{
return (getPopulation() / 2);
}
/************************************************************************************************/
/* phunny_pharmer Start 04/20/10 */
/* the goal of this modification is to make it so that difficult tiles (ie hills, mountains) */
/* receive less culture per turn; this will make cultural borders grow more slowly on these */
/* plots and will lead to cultural borders at mountains and other key features */
/************************************************************************************************/
#define MAX_CLOSE_DIST 100 // Arbitrary to some extent
#define HASH_RELATIVE_CLOSE_DIST(x,y) (((x) + MAX_CLOSE_DIST) + 2*MAX_CLOSE_DIST*((y) + MAX_CLOSE_DIST))
void CvCity::recalculateCultureDistances(int iMaxDistance) const
{
PROFILE_FUNC();
MEMORY_TRACK_EXEMPT();
// if the point is within one square of the city center
for(int iDX = -1; iDX <= 1; ++iDX)
{
for(int iDY = -1; iDY <= 1; ++iDY)
{
int iPlotIndex = HASH_RELATIVE_CLOSE_DIST(iDX,iDY);
// then the plot distance should be set to one
// as all points one away from the city have this default value
m_aCultureDistances[iPlotIndex] = 1;
}
}
// determine whether or not any value has changed when calculating distances
// note that this is initially set to true as long as the maximum distance is not 1
// when values cease to change, the final distances have been calculated
bool bHasChanged = (iMaxDistance != 1);
// as long as there are changes during the last iteration
while(bHasChanged)
{
// reset the has changed variable to note a new loop cycle has begun
bHasChanged = false;
for(int iDX = -iMaxDistance; iDX <= iMaxDistance; ++iDX)
{
for(int iDY = -iMaxDistance; iDY <= iMaxDistance; ++iDY)
{
int iPlotIndex = HASH_RELATIVE_CLOSE_DIST(iDX,iDY);
// find the distance to the current plot
int distance = plotDistance(0, 0, iDX, iDY);
// if the point is more than one cell away from the city center
// then it should be recalculated, as cells closer to the city
// will have already been fixed
if(distance > 1)
{
// recalculate the value to determine if it has changed
int iNewValue = calculateCultureDistance(iDX, iDY, iMaxDistance);
// if it has changed, save the value and mark that
// all values should be recomputed since they
// may depend on this value
if(m_aCultureDistances[iPlotIndex] != iNewValue)
{
m_aCultureDistances[iPlotIndex] = iNewValue;
bHasChanged = true;
}
}
}
}
}
}
int CvCity::calculateCultureDistance(int iDX, int iDY, int iMaxDistance) const
{
PROFILE_FUNC();
// find the current plot and the distance to it
CvPlot* pPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);
// if the plot distance is greater than the maximum desired plot distance
// or if the plot does not exist, then the plot distance is maximal
if(plotDistance(0, 0, iDX, iDY) > iMaxDistance || pPlot == NULL) return MAX_INT;
// determine the distance from the square to the city from all directions
// it is entirely possible that the shortest distance to a city may come
// from an unusual direction, eg if there is a mountain range in the way
int distance = MAX_INT;
// check all directions to determine the cultural distance
// note 1: all directions are checked
// note 2: the distance to the plot is defined as the distance to the
// neighbor, plus any penalty for crossing a river, plus an additional
// one for reaching that particular square
// note 3: if a neighbor has a distance of MAX_INT, then it is ignored
// since that means that the neighbor is defined
int iPlotIndex = HASH_RELATIVE_CLOSE_DIST(iDX+1,iDY);
int iEastDist = m_aCultureDistances[iPlotIndex];
if(iEastDist != 0 && iEastDist != MAX_INT)
{
iEastDist += pPlot->isRiverCrossing(DIRECTION_EAST);
distance = std::min(distance, iEastDist+1);
}
iPlotIndex = HASH_RELATIVE_CLOSE_DIST(iDX,iDY-1);
int iSouthDist = m_aCultureDistances[iPlotIndex];
if(iSouthDist != 0 && iSouthDist != MAX_INT)
{
iSouthDist += pPlot->isRiverCrossing(DIRECTION_SOUTH);
distance = std::min(distance, iSouthDist+1);
}
iPlotIndex = HASH_RELATIVE_CLOSE_DIST(iDX-1,iDY);
int iWestDist = m_aCultureDistances[iPlotIndex];
if(iWestDist != 0 && iWestDist != MAX_INT)
{
iWestDist += pPlot->isRiverCrossing(DIRECTION_WEST);
distance = std::min(distance, iWestDist+1);
}
iPlotIndex = HASH_RELATIVE_CLOSE_DIST(iDX,iDY+1);
int iNorthDist = m_aCultureDistances[iPlotIndex];
if(iNorthDist != 0 && iNorthDist != MAX_INT)
{
iNorthDist += pPlot->isRiverCrossing(DIRECTION_NORTH);
distance = std::min(distance, iNorthDist+1);
}
// if the distance to the plot is unchanged, perhaps because all the
// neighbors have distance MAX_INT, return the value of MAX_INT in
// order to ensure that the culture distance is recalculated
if(distance == MAX_INT) return MAX_INT;
// increase the cultural distance for the tile if it is difficult terrain
// key idea: distant and difficult terrain will accumulate less culture
// each turn, making cultural borders grow more slowly on these terrain
// if the plot is a hills, tundra, or marsh, or coast plot
// increase the effective distance by one
int terrainDistance = 0;
TerrainTypes eTerrain;
if(pPlot->isHills())
{
eTerrain = (TerrainTypes)GC.getInfoTypeForString("TERRAIN_HILL");
}
else if(pPlot->isPeak())
{
eTerrain = (TerrainTypes)GC.getInfoTypeForString("TERRAIN_PEAK");
}
else
{
eTerrain = pPlot->getTerrainType();
}
terrainDistance += GC.getTerrainInfo(eTerrain).getCultureDistance();
if (pPlot->getFeatureType() != NO_FEATURE)
{
//used for floodplains
if (GC.getFeatureInfo(pPlot->getFeatureType()).isIgnoreTerrainCulture())
{
terrainDistance = 0;
}
terrainDistance += GC.getFeatureInfo(pPlot->getFeatureType()).getCultureDistance();
}
distance += std::max(0, terrainDistance);
// at this point, we are done
// save the cached distance in the m_aCultureDistances structure
// in order to facilitate the next step of the dynamic programming
return distance;
}
/************************************************************************************************/
/* phunny_pharmer END */
/************************************************************************************************/
/************************************************************************************************/
/* phunny_pharmer Start 05/03/10 */
/* clear all the values in the culture distance cache; these values will have to be recom- */
/* puted on the next relevant call to cultureDistance() */
/************************************************************************************************/
void CvCity::clearCultureDistanceCache()
{
m_aCultureDistances.clear();
}
/************************************************************************************************/
/* phunny_pharmer END */
/************************************************************************************************/
/************************************************************************************************/
/* phunny_pharmer Start 04/20/10 */
/* the cache of culture distances precomputed by recalculateCultureDistance is used in order */
/* to determine the culture distance from the city center the plot location */
/************************************************************************************************/
int CvCity::cultureDistance(int iDX, int iDY, bool bForce) const
{
PROFILE_FUNC();
if (!bForce && GC.getGameINLINE().isOption(GAMEOPTION_REALISTIC_CULTURE_SPREAD))
{
// This is not a true plot index but we can assume iDX and iDY are less than the total map dimensions so
// it is a unique index
int iPlotIndex = HASH_RELATIVE_CLOSE_DIST(iDX,iDY);
std::map<int,int>::const_iterator itr = m_aCultureDistances.find(iPlotIndex);
if ( itr == m_aCultureDistances.end() )
{
recalculateCultureDistances(plotDistance(0, 0, iDX, iDY));
return m_aCultureDistances[iPlotIndex];
}
else
{
return itr->second;
}
}
return plotDistance(0, 0, iDX, iDY);
}
/************************************************************************************************/
/* phunny_pharmer END */
/************************************************************************************************/
int CvCity::cultureStrength(PlayerTypes ePlayer) const
{
CvPlot* pLoopPlot;
int iStrength;
int iI;
iStrength = 1;
iStrength += (getHighestPopulation() * 2);
for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
pLoopPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));
if (pLoopPlot != NULL)
{
if (pLoopPlot->getOwnerINLINE() == ePlayer)
{
iStrength += (GC.getGameINLINE().getCurrentEra() + 1);
}
}
}
// Handle culture getting very large
int iPlayerCulture = plot()->getCulture(ePlayer);
int iOwnerCulture = plot()->getCulture(getOwnerINLINE());
if ( iPlayerCulture > MAX_INT/1000 || iOwnerCulture > MAX_INT/1000 )
{
iPlayerCulture /= 1000;
iOwnerCulture /= 1000;
FAssert(GC.getDefineINT("REVOLT_TOTAL_CULTURE_MODIFIER") < 1000);
}
iStrength *= std::max(0, (((GC.getDefineINT("REVOLT_TOTAL_CULTURE_MODIFIER") * (iPlayerCulture - iOwnerCulture + 1)) / (iPlayerCulture + 1)) + 100));
iStrength /= 100;
if (GET_PLAYER(ePlayer).getStateReligion() != NO_RELIGION)
{
if (isHasReligion(GET_PLAYER(ePlayer).getStateReligion()))
{
iStrength *= std::max(0, (GC.getDefineINT("REVOLT_OFFENSE_STATE_RELIGION_MODIFIER") + 100));
iStrength /= 100;
}
}
if (GET_PLAYER(getOwnerINLINE()).getStateReligion() != NO_RELIGION)
{
if (isHasReligion(GET_PLAYER(getOwnerINLINE()).getStateReligion()))
{
iStrength *= std::max(0, (GC.getDefineINT("REVOLT_DEFENSE_STATE_RELIGION_MODIFIER") + 100));
iStrength /= 100;
}
}
return iStrength;
}
int CvCity::cultureGarrison(PlayerTypes ePlayer) const
{
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
int iGarrison;
iGarrison = 1;
pUnitNode = plot()->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = plot()->nextUnitNode(pUnitNode);
iGarrison += pLoopUnit->getUnitInfo().getCultureGarrisonValue();
}
if (atWar(GET_PLAYER(ePlayer).getTeam(), getTeam()))
{
iGarrison *= 2;
}
return iGarrison;
}
// This routine is basically used after an old format load, or for a new city
void CvCity::calculateBuildingReplacements() const
{
PROFILE_FUNC();
m_paiBuildingReplaced = new int[GC.getNumBuildingInfos()];
memset(m_paiBuildingReplaced,0,sizeof(int)*GC.getNumBuildingInfos());
for(int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if ( getNumActiveBuilding((BuildingTypes)iI) > 0 )
{
for(int iJ = 0; iJ < GC.getNumBuildingInfos(); iJ++)
{
if (GC.getBuildingInfo((BuildingTypes)iJ).isReplaceBuildingClass(GC.getBuildingInfo((BuildingTypes)iI).getBuildingClassType()))
{
// Cope with old format saves where the calculation will not previously
// have been done and so the effects need to be processed
if ( !m_bHasCalculatedBuildingReplacement && getNumBuilding((BuildingTypes)iJ) > 0 )
{
((CvCity*)this)->processBuilding((BuildingTypes)iJ, -1);
}
m_paiBuildingReplaced[iJ]++;
}
}
}
}
m_bHasCalculatedBuildingReplacement = TRUE;
}
void CvCity::changeBuildingReplacementCount(BuildingTypes eBuilding, bool bAdd)
{
FAssert(m_paiBuildingReplaced != NULL);
for(int iJ = 0; iJ < GC.getNumBuildingInfos(); iJ++)
{
if (GC.getBuildingInfo((BuildingTypes)iJ).isReplaceBuildingClass(GC.getBuildingInfo(eBuilding).getBuildingClassType()))
{
// During modifier recalculation don't count extant buildings we haven't yet
// processed as already being present
bool bHad = (m_recalcBuilding >= iJ && getNumBuilding((BuildingTypes)iJ) > 0);
if ( bAdd )
{
m_paiBuildingReplaced[iJ]++;
}
else
{
// During recalculation after loading an old format save game
// this can go negative due to chains that lead from lower numbered
// buildings to higher, so just cap at 0 on the bottom
if ( m_paiBuildingReplaced[iJ] > 0 )
{
m_paiBuildingReplaced[iJ]--;
}
}
bool bHas = (m_recalcBuilding >= iJ && getNumBuilding((BuildingTypes)iJ) > 0);
if ( bHad != bHas )
{
processBuilding((BuildingTypes)iJ, bHas ? 1 : -1, false, true);
}
}
}
}
int CvCity::getNumBuilding(BuildingTypes eIndex) const
{
FAssertMsg(eIndex != NO_BUILDING, "BuildingType eIndex is expected to not be NO_BUILDING");
if ( m_paiBuildingReplaced == NULL )
{
calculateBuildingReplacements();
}
if ( m_paiBuildingReplaced[eIndex] > 0 )
{
return 0;
}
else
{
for( std::vector<OrderData>::const_iterator itr = m_inProcessOrders.begin(); itr != m_inProcessOrders.end(); ++itr )
{
if ( itr->eOrderType == ORDER_CONSTRUCT && itr->iData1 == (int)eIndex )
{
return 1;
}
}
return std::min(GC.getCITY_MAX_NUM_BUILDINGS(), getNumRealBuilding(eIndex) + getNumFreeBuilding(eIndex) + getNumFreeAreaBuilding(eIndex) + getNumFreeTradeRegionBuilding(eIndex));
}
}
int CvCity::getNumActiveBuilding(BuildingTypes eIndex) const
{
FAssertMsg(eIndex != NO_BUILDING, "BuildingType eIndex is expected to not be NO_BUILDING");
if (GET_TEAM(getTeam()).isObsoleteBuilding(eIndex))
{
return 0;
}
return (getNumBuilding(eIndex));
}
bool CvCity::hasActiveWorldWonder() const
{
int iI;
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (isWorldWonderClass((BuildingClassTypes)(GC.getBuildingInfo((BuildingTypes)iI).getBuildingClassType())))
{
if (getNumRealBuilding((BuildingTypes)iI) > 0 && !(GET_TEAM(getTeam()).isObsoleteBuilding((BuildingTypes)iI)))
{
return true;
}
}
}
return false;
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 03/04/10 Mongoose & jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
// From Mongoose SDK
int CvCity::getNumActiveWorldWonders() const
{
int iI;
int iCount = 0;
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (isWorldWonderClass((BuildingClassTypes)(GC.getBuildingInfo((BuildingTypes)iI).getBuildingClassType())))
{
if (getNumRealBuilding((BuildingTypes)iI) > 0 && !(GET_TEAM(getTeam()).isObsoleteBuilding((BuildingTypes)iI)))
{
iCount++;
}
}
}
return iCount;
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
int CvCity::getReligionCount() const
{
int iCount;
int iI;
iCount = 0;
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (isHasReligion((ReligionTypes)iI))
{
iCount++;
}
}
return iCount;
}
int CvCity::getCorporationCount() const
{
int iCount;
int iI;
iCount = 0;
for (iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
if (isHasCorporation((CorporationTypes)iI))
{
iCount++;
}
}
return iCount;
}
int CvCity::getID() const
{
return m_iID;
}
int CvCity::getIndex() const
{
return (getID() & FLTA_INDEX_MASK);
}
IDInfo CvCity::getIDInfo() const
{
IDInfo city(getOwnerINLINE(), getID());
return city;
}
void CvCity::setID(int iID)
{
m_iID = iID;
}
int CvCity::getViewportX() const
{
CvViewport* pCurrentViewPort = GC.getCurrentViewport();
FAssert(pCurrentViewPort != NULL);
FAssert(isInViewport());
return pCurrentViewPort->getViewportXFromMapX(m_iX);
}
int CvCity::getViewportY() const
{
CvViewport* pCurrentViewPort = GC.getCurrentViewport();
FAssert(pCurrentViewPort != NULL);
FAssert(isInViewport());
return pCurrentViewPort->getViewportYFromMapY(m_iY);
}
bool CvCity::isInViewport(void) const
{
return GC.getCurrentViewport()->isInViewport(m_iX, m_iY);
}
int CvCity::getX() const
{
return m_iX;
}
int CvCity::getY() const
{
return m_iY;
}
bool CvCity::at(int iX, int iY) const
{
return ((getX_INLINE() == iX) && (getY_INLINE() == iY));
}
bool CvCity::at(CvPlot* pPlot) const
{
return (plot() == pPlot);
}
CvPlot* CvCity::plot() const
{
return GC.getMapINLINE().plotSorenINLINE(getX_INLINE(), getY_INLINE());
}
CvPlot* CvCity::plotExternal() const
{
FAssert(isInViewport());
return GC.getMapINLINE().plotSorenINLINE(getX_INLINE(), getY_INLINE());
}
CvPlotGroup* CvCity::plotGroup(PlayerTypes ePlayer) const
{
return plot()->getPlotGroup(ePlayer);
}
bool CvCity::isConnectedTo(CvCity* pCity) const
{
return plot()->isConnectedTo(pCity);
}
bool CvCity::isConnectedToCapital(PlayerTypes ePlayer) const
{
return plot()->isConnectedToCapital(ePlayer);
}
int CvCity::getArea() const
{
return plot()->getArea();
}
CvArea* CvCity::area() const
{
return plot()->area();
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 01/02/09 jdog5000 */
/* */
/* General AI */
/************************************************************************************************/
CvArea* CvCity::waterArea(bool bNoImpassable) const
{
return plot()->waterArea(bNoImpassable);
}
// Expose plot function through city
CvArea* CvCity::secondWaterArea() const
{
return plot()->secondWaterArea();
}
// Find the largest water area shared by this city and other city, if any
CvArea* CvCity::sharedWaterArea(CvCity* pOtherCity) const
{
CvArea* pWaterArea = waterArea(true);
if( pWaterArea != NULL )
{
CvArea* pOtherWaterArea = pOtherCity->waterArea(true);
if( pOtherWaterArea != NULL )
{
if( pWaterArea == pOtherWaterArea )
{
return pWaterArea;
}
else
{
CvArea* pSecondWaterArea = secondWaterArea();
CvArea* pOtherSecondWaterArea = pOtherCity->secondWaterArea();
if( pSecondWaterArea != NULL && pSecondWaterArea == pOtherWaterArea )
{
return pSecondWaterArea;
}
else if( pOtherSecondWaterArea != NULL && pWaterArea == pOtherSecondWaterArea )
{
return pWaterArea;
}
else if( pSecondWaterArea != NULL && pOtherSecondWaterArea != NULL && pSecondWaterArea == pOtherSecondWaterArea )
{
return pSecondWaterArea;
}
}
}
}
return NULL;
}
bool CvCity::isBlockaded() const
{
int iI;
CvPlot* pAdjacentPlot;
for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
pAdjacentPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));
if (pAdjacentPlot != NULL)
{
if( pAdjacentPlot->getBlockadedCount(getTeam()) > 0 )
{
return true;
}
}
}
return false;
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
CvPlot* CvCity::getRallyPlot() const
{
return GC.getMapINLINE().plotSorenINLINE(m_iRallyX, m_iRallyY);
}
void CvCity::setRallyPlot(CvPlot* pPlot)
{
if (getRallyPlot() != pPlot)
{
if (pPlot != NULL)
{
m_iRallyX = pPlot->getX_INLINE();
m_iRallyY = pPlot->getY_INLINE();
}
else
{
m_iRallyX = INVALID_PLOT_COORD;
m_iRallyY = INVALID_PLOT_COORD;
}
if (isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(ColoredPlots_DIRTY_BIT, true);
}
}
}
int CvCity::getGameTurnFounded() const
{
return m_iGameTurnFounded;
}
void CvCity::setGameTurnFounded(int iNewValue)
{
if (getGameTurnFounded() != iNewValue)
{
m_iGameTurnFounded = iNewValue;
FAssert(getGameTurnFounded() >= 0);
GC.getMapINLINE().updateWorkingCity();
}
}
int CvCity::getGameTurnAcquired() const
{
return m_iGameTurnAcquired;
}
void CvCity::setGameTurnAcquired(int iNewValue)
{
m_iGameTurnAcquired = iNewValue;
FAssert(getGameTurnAcquired() >= 0);
}
int CvCity::getPopulation() const
{
return m_iPopulation;
}
void CvCity::setPopulation(int iNewValue)
{
int iOldPopulation;
iOldPopulation = getPopulation();
if (iOldPopulation != iNewValue)
{
m_iPopulation = iNewValue;
FAssert(getPopulation() >= 0);
GET_PLAYER(getOwnerINLINE()).invalidatePopulationRankCache();
if (getPopulation() > getHighestPopulation())
{
setHighestPopulation(getPopulation());
}
area()->changePopulationPerPlayer(getOwnerINLINE(), (getPopulation() - iOldPopulation));
GET_PLAYER(getOwnerINLINE()).changeTotalPopulation(getPopulation() - iOldPopulation);
GET_TEAM(getTeam()).changeTotalPopulation(getPopulation() - iOldPopulation);
GC.getGameINLINE().changeTotalPopulation(getPopulation() - iOldPopulation);
if (iOldPopulation > 0)
{
area()->changePower(getOwnerINLINE(), -(getPopulationPower(iOldPopulation)));
}
if (getPopulation() > 0)
{
area()->changePower(getOwnerINLINE(), getPopulationPower(getPopulation()));
}
/************************************************************************************************/
/* Afforess Start 08/29/10 */
/* */
/* */
/************************************************************************************************/
checkBuildings(false, false, false, false, true);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
plot()->updateYield();
updateFeatureHealth();
setMaintenanceDirty(true);
if (((iOldPopulation == 1) && (getPopulation() > 1)) ||
((getPopulation() == 1) && (iOldPopulation > 1))
|| ((getPopulation() > iOldPopulation) && (GET_PLAYER(getOwnerINLINE()).getNumCities() <= 2)))
{
if (!isHuman())
{
AI_setChooseProductionDirty(true);
}
}
GET_PLAYER(getOwnerINLINE()).AI_makeAssignWorkDirty();
setInfoDirty(true);
setLayoutDirty(true);
plot()->plotAction(PUF_makeInfoBarDirty);
if ((getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) && isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
gDLL->getInterfaceIFace()->setDirty(CityScreen_DIRTY_BIT, true);
}
//Afforess: pop affects yield value
ClearYieldValueCache();
//updateGenericBuildings();
}
}
void CvCity::changePopulation(int iChange)
{
setPopulation(getPopulation() + iChange);
}
long CvCity::getRealPopulation() const
{
//return (((long)(pow((float)getPopulation(), 2.8f))) * 1000);
// Koshling - using table provided by Praetyre to give more realistic results
static long realPopulationTable[] =
{
0,
500,
1000,
5000,
10000,
20000,
30000,
50000,
75000,
100000,
125000,
150000,
200000,
250000,
300000,
350000,
400000,
500000,
650000,
800000,
1000000,
1200000,
1400000,
1600000,
1800000,
2000000,
2200000,
2450000,
2600000,
2800000,
3000000,
3300000,
3600000,
3900000,
4200000,
4500000,
5000000,
6000000,
7000000,
8000000,
9000000,
10000000,
11000000,
12000000,
13000000,
14000000,
15000000,
17500000,
20000000,
22500000,
25000000,
27500000,
30000000,
32500000,
35000000,
37500000,
40000000,
42500000,
45000000,
47500000,
50000000,
52500000,
55000000,
57500000,
60000000,
62500000,
65000000,
67500000,
70000000,
75000000,
80000000,
85000000,
90000000,
95000000,
100000000,
110000000,
120000000,
130000000,
140000000,
150000000,
160000000,
170000000,
180000000,
190000000,
200000000,
210000000,
220000000,
230000000,
240000000,
250000000,
260000000,
270000000,
280000000,
290000000,
300000000,
320000000,
340000000,
360000000,
380000000,
400000000,
500000000
};
#define NUM_POP_TABLE_ENTRIES (sizeof(realPopulationTable)/sizeof(long))
if ( getPopulation() < NUM_POP_TABLE_ENTRIES )
{
return realPopulationTable[getPopulation()];
}
else
{
return realPopulationTable[NUM_POP_TABLE_ENTRIES-1] + ((realPopulationTable[NUM_POP_TABLE_ENTRIES-1]-realPopulationTable[NUM_POP_TABLE_ENTRIES-2])*(getPopulation() - NUM_POP_TABLE_ENTRIES));
}
}
int CvCity::getHighestPopulation() const
{
return m_iHighestPopulation;
}
void CvCity::setHighestPopulation(int iNewValue)
{
m_iHighestPopulation = iNewValue;
FAssert(getHighestPopulation() >= 0);
}
int CvCity::getWorkingPopulation() const
{
return m_iWorkingPopulation;
}
void CvCity::changeWorkingPopulation(int iChange)
{
m_iWorkingPopulation = (m_iWorkingPopulation + iChange);
FAssert(getWorkingPopulation() >= 0);
}
int CvCity::getSpecialistPopulation() const
{
return m_iSpecialistPopulation;
}
void CvCity::changeSpecialistPopulation(int iChange)
{
if (iChange != 0)
{
m_iSpecialistPopulation = (m_iSpecialistPopulation + iChange);
FAssert(getSpecialistPopulation() >= 0);
GET_PLAYER(getOwnerINLINE()).invalidateYieldRankCache();
setCommerceDirty(NO_COMMERCE);
}
}
int CvCity::getNumGreatPeople() const
{
return m_iNumGreatPeople;
}
void CvCity::changeNumGreatPeople(int iChange)
{
if (iChange != 0)
{
m_iNumGreatPeople = (m_iNumGreatPeople + iChange);
FAssert(getNumGreatPeople() >= 0);
setCommerceDirty(NO_COMMERCE);
}
}
int CvCity::getBaseGreatPeopleRate() const
{
return m_iBaseGreatPeopleRate;
}
int CvCity::getGreatPeopleRate() const
{
if (isDisorder())
{
return 0;
}
return ((getBaseGreatPeopleRate() * getTotalGreatPeopleRateModifier()) / 100);
}
int CvCity::getTotalGreatPeopleRateModifier() const
{
int iModifier;
iModifier = getGreatPeopleRateModifier();
iModifier += GET_PLAYER(getOwnerINLINE()).getGreatPeopleRateModifier();
if (GET_PLAYER(getOwnerINLINE()).getStateReligion() != NO_RELIGION)
{
if (isHasReligion(GET_PLAYER(getOwnerINLINE()).getStateReligion()))
{
iModifier += GET_PLAYER(getOwnerINLINE()).getStateReligionGreatPeopleRateModifier();
}
}
if (GET_PLAYER(getOwnerINLINE()).isGoldenAge())
{
iModifier += GC.getDefineINT("GOLDEN_AGE_GREAT_PEOPLE_MODIFIER");
}
return std::max(0, (iModifier + 100));
}
void CvCity::changeBaseGreatPeopleRate(int iChange)
{
m_iBaseGreatPeopleRate = (m_iBaseGreatPeopleRate + iChange);
FAssert(getBaseGreatPeopleRate() >= 0);
}
int CvCity::getGreatPeopleRateModifier() const
{
return m_iGreatPeopleRateModifier;
}
void CvCity::changeGreatPeopleRateModifier(int iChange)
{
m_iGreatPeopleRateModifier = (m_iGreatPeopleRateModifier + iChange);
}
int CvCity::getGreatPeopleProgress() const
{
return m_iGreatPeopleProgress;
}
void CvCity::changeGreatPeopleProgress(int iChange)
{
m_iGreatPeopleProgress = (m_iGreatPeopleProgress + iChange);
FAssert(getGreatPeopleProgress() >= 0);
}
CvProperties* CvCity::getProperties()
{
return &m_Properties;
}
const CvProperties* CvCity::getPropertiesConst() const
{
return &m_Properties;
}
// BUG - Building Additional Great People - start
/*
* Returns the total additional great people rate that adding one of the given buildings will provide.
*
* Doesn't check if the building can be constructed in this city.
*/
int CvCity::getAdditionalGreatPeopleRateByBuilding(BuildingTypes eBuilding)
{
FAssertMsg(eBuilding >= 0, "eBuilding expected to be >= 0");
FAssertMsg(eBuilding < GC.getNumBuildingInfos(), "eBuilding expected to be < GC.getNumBuildingInfos()");
int iRate = getBaseGreatPeopleRate();
int iModifier = getTotalGreatPeopleRateModifier();
int iExtra = ((iRate + getAdditionalBaseGreatPeopleRateByBuilding(eBuilding)) * (iModifier + getAdditionalGreatPeopleRateModifierByBuilding(eBuilding)) / 100) - (iRate * iModifier / 100);
/************************************************************************************************/
/* Afforess Start 4/22/10 */
/* */
/* */
/************************************************************************************************/
for (int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (getNumActiveBuilding((BuildingTypes)iI) > 0)
{
if (GC.getBuildingInfo((BuildingTypes)iI).isReplaceBuildingClass((BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType()))
{
iExtra -= getAdditionalGreatPeopleRateByBuilding((BuildingTypes)iI);
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iExtra;
}
/*
* Returns the additional great people rate that adding one of the given buildings will provide.
*
* Doesn't check if the building can be constructed in this city.
*/
int CvCity::getAdditionalBaseGreatPeopleRateByBuilding(BuildingTypes eBuilding)
{
FAssertMsg(eBuilding >= 0, "eBuilding expected to be >= 0");
FAssertMsg(eBuilding < GC.getNumBuildingInfos(), "eBuilding expected to be < GC.getNumBuildingInfos()");
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
bool bObsolete = GET_TEAM(getTeam()).isObsoleteBuilding(eBuilding);
int iExtraRate = 0;
iExtraRate += kBuilding.getGreatPeopleRateChange();
// Specialists
if (!bObsolete)
{
for (int iI = 0; iI < GC.getNumSpecialistInfos(); ++iI)
{
if (kBuilding.getFreeSpecialistCount((SpecialistTypes)iI) != 0)
{
iExtraRate += getAdditionalBaseGreatPeopleRateBySpecialist((SpecialistTypes)iI, kBuilding.getFreeSpecialistCount((SpecialistTypes)iI));
}
}
}
/************************************************************************************************/
/* Afforess Start 08/18/10 */
/* */
/* */
/************************************************************************************************/
int iSpecialistGreatPeopleRate = 0;
for (int iI = 1; iI < kBuilding.getFreeSpecialist() + 1; iI++)
{
SpecialistTypes eNewSpecialist = getBestSpecialist(iI);
if (eNewSpecialist == NO_SPECIALIST) break;
CvSpecialistInfo& kSpecialist = GC.getSpecialistInfo(eNewSpecialist);
iSpecialistGreatPeopleRate += kSpecialist.getGreatPeopleRateChange();
}
iExtraRate += iSpecialistGreatPeopleRate;
if (kBuilding.getNumPopulationEmployed() > 0)
{
int* paiCommerce = new int[NUM_COMMERCE_TYPES];
int* paiYield = new int[NUM_YIELD_TYPES];
int iGreatPeopleRate;
int iHappiness;
int iHealthiness;
removeWorstCitizenActualEffects(kBuilding.getNumPopulationEmployed(), iGreatPeopleRate, iHappiness, iHealthiness, paiYield, paiCommerce);
SAFE_DELETE_ARRAY(paiCommerce);
SAFE_DELETE_ARRAY(paiYield);
iExtraRate += iGreatPeopleRate;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iExtraRate;
}
/*
* Returns the additional great people rate modifier that adding one of the given buildings will provide.
*
* Doesn't check if the building can be constructed in this city.
*/
int CvCity::getAdditionalGreatPeopleRateModifierByBuilding(BuildingTypes eBuilding)
{
FAssertMsg(eBuilding >= 0, "eBuilding expected to be >= 0");
FAssertMsg(eBuilding < GC.getNumBuildingInfos(), "eBuilding expected to be < GC.getNumBuildingInfos()");
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
bool bObsolete = GET_TEAM(getTeam()).isObsoleteBuilding(eBuilding);
int iExtraModifier = 0;
if (!bObsolete)
{
iExtraModifier += kBuilding.getGreatPeopleRateModifier();
iExtraModifier += kBuilding.getGlobalGreatPeopleRateModifier();
}
return iExtraModifier;
}
// BUG - Building Additional Great People - end
// BUG - Specialist Additional Great People - start
/*
* Returns the total additional great people rate that changing the number of the given specialist will provide/remove.
*/
int CvCity::getAdditionalGreatPeopleRateBySpecialist(SpecialistTypes eSpecialist, int iChange) const
{
int iRate = getBaseGreatPeopleRate();
int iModifier = getTotalGreatPeopleRateModifier();
int iExtraRate = getAdditionalBaseGreatPeopleRateBySpecialist(eSpecialist, iChange);
int iExtra = ((iRate + iExtraRate) * iModifier / 100) - (iRate * iModifier / 100);
return iExtra;
}
/*
* Returns the additional great people rate that changing the number of the given specialist will provide/remove.
*/
int CvCity::getAdditionalBaseGreatPeopleRateBySpecialist(SpecialistTypes eSpecialist, int iChange) const
{
FAssertMsg(eSpecialist >= 0, "eSpecialist expected to be >= 0");
FAssertMsg(eSpecialist < GC.getNumSpecialistInfos(), "eSpecialist expected to be < GC.getNumSpecialistInfos()");
return iChange * GC.getSpecialistInfo(eSpecialist).getGreatPeopleRateChange();
}
// BUG - Specialist Additional Great People - end
int CvCity::getNumWorldWonders() const
{
return m_iNumWorldWonders;
}
void CvCity::changeNumWorldWonders(int iChange)
{
m_iNumWorldWonders = (m_iNumWorldWonders + iChange);
FAssert(getNumWorldWonders() >= 0);
}
int CvCity::getNumTeamWonders() const
{
return m_iNumTeamWonders;
}
void CvCity::changeNumTeamWonders(int iChange)
{
m_iNumTeamWonders = (m_iNumTeamWonders + iChange);
FAssert(getNumTeamWonders() >= 0);
}
int CvCity::getNumNationalWonders() const
{
return m_iNumNationalWonders;
}
void CvCity::changeNumNationalWonders(int iChange)
{
m_iNumNationalWonders = (m_iNumNationalWonders + iChange);
FAssert(getNumNationalWonders() >= 0);
}
int CvCity::getNumBuildings() const
{
return m_iNumBuildings;
}
void CvCity::changeNumBuildings(int iChange)
{
m_iNumBuildings = (m_iNumBuildings + iChange);
FAssert(getNumBuildings() >= 0);
}
int CvCity::getGovernmentCenterCount() const
{
return m_iGovernmentCenterCount;
}
bool CvCity::isGovernmentCenter() const
{
return (getGovernmentCenterCount() > 0);
}
void CvCity::changeGovernmentCenterCount(int iChange)
{
if (iChange != 0)
{
m_iGovernmentCenterCount = (m_iGovernmentCenterCount + iChange);
FAssert(getGovernmentCenterCount() >= 0);
setMaintenanceDirty(true);
}
}
// BUG - Building Saved Maintenance - start
/*
* Returns the rounded total additional gold from saved maintenance that adding one of the given buildings will provide.
*
* Doesn't check if the building can be constructed in this city.
*/
int CvCity::getSavedMaintenanceByBuilding(BuildingTypes eBuilding) const
{
return getSavedMaintenanceTimes100ByBuilding(eBuilding) / 100;
}
/*
* Returns the total additional gold from saved maintenance times 100 that adding one of the given buildings will provide.
*
* Doesn't check if the building can be constructed in this city.
*/
int CvCity::getSavedMaintenanceTimes100ByBuilding(BuildingTypes eBuilding) const
{
FAssertMsg(eBuilding >= 0, "eBuilding expected to be >= 0");
FAssertMsg(eBuilding < GC.getNumBuildingInfos(), "eBuilding expected to be < GC.getNumBuildingInfos()");
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
int iModifier = kBuilding.getMaintenanceModifier();
int iDirectMaintenance = 0;
iModifier += kBuilding.getGlobalMaintenanceModifier();
iModifier += kBuilding.getAreaMaintenanceModifier();
if (isConnectedToCapital() && !(isCapital()))
{
iModifier += kBuilding.getConnectedCityMaintenanceModifier();
}
if ( kBuilding.getCommerceChange(COMMERCE_GOLD) < 0 && GC.getDefineINT("TREAT_NEGATIVE_GOLD_AS_MAINTENANCE"))
{
iDirectMaintenance = -kBuilding.getCommerceChange(COMMERCE_GOLD);
iDirectMaintenance *= GC.getHandicapInfo(getHandicapType()).getCorporationMaintenancePercent();
}
if ((iModifier != 0 || iDirectMaintenance > 0) && !isDisorder() && !isWeLoveTheKingDay())
{
// Koshling - the calculation below was incorrect because calculateBaseMaintenanceTimes100() itself includes
// num cities and distance maintainence effects
long long iOldBaseMaintenance = calculateBaseMaintenanceTimes100();
long long iNewBaseMaintenance = iOldBaseMaintenance + iDirectMaintenance;
long long iOldModifiedBaseMaintenance = std::max((long long)0, iOldBaseMaintenance * (getEffectiveMaintenanceModifier() + 100) / 100);
long long iNewModifiedBaseMaintenance = std::max((long long)0, iNewBaseMaintenance * (getEffectiveMaintenanceModifier() + iModifier + 100) / 100);
long long iSavedMaintenance = (iOldModifiedBaseMaintenance - iNewModifiedBaseMaintenance);
return (int)iSavedMaintenance;
//int iNumCitiesMaintenanceTimes100 = calculateNumCitiesMaintenanceTimes100();
//iNumCitiesMaintenanceTimes100 *= std::max(0, 100 + kBuilding.getNumCitiesMaintenanceModifier());
//iNumCitiesMaintenanceTimes100 /= 100;
//iNumCitiesMaintenanceTimes100 -= calculateNumCitiesMaintenanceTimes100();
//iNewMaintenance += iNumCitiesMaintenanceTimes100;
//iNewMaintenance += calculateDistanceMaintenanceTimes100(0, 0) - calculateDistanceMaintenanceTimes100(kBuilding.getDistanceMaintenanceModifier(), kBuilding.getCoastalDistanceMaintenanceModifier());
/************************************************************************************************/
/* Afforess Start 6/20/11 */
/* */
/* Protect against negative maintenance */
/************************************************************************************************/
//return getMaintenanceTimes100() - std::max(0, iNewMaintenance);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
return 0;
}
// BUG - Building Saved Maintenance - end
int CvCity::getDistanceMaintenanceSavedTimes100ByCivic(CivicTypes eCivic) const
{
FAssertMsg(eCivic >= 0, "eCivic expected to be >= 0");
FAssertMsg(eCivic < GC.getNumCivicInfos(), "eBuilding expected to be < GC.getNumCivicInfos()");
CvCivicInfo& kCivic = GC.getCivicInfo(eCivic);
int iModifier = kCivic.getDistanceMaintenanceModifier();
//Subtract the maintenance from our current civic
if (GET_PLAYER(getOwnerINLINE()).getCivics((CivicOptionTypes)kCivic.getCivicOptionType()) != NO_CIVIC)
{
iModifier -= GC.getCivicInfo(GET_PLAYER(getOwnerINLINE()).getCivics((CivicOptionTypes)kCivic.getCivicOptionType())).getDistanceMaintenanceModifier();
}
if (iModifier != 0 && !isDisorder() && !isWeLoveTheKingDay())
{
int iOldBaseMaintenance = calculateDistanceMaintenanceTimes100();
int iNewBaseMaintenance = calculateDistanceMaintenanceTimes100(iModifier);
int iOldModifiedBaseMaintenance = std::max(0, iOldBaseMaintenance * (getEffectiveMaintenanceModifier() + 100) / 100);
int iNewModifiedBaseMaintenance = std::max(0, iNewBaseMaintenance * (getEffectiveMaintenanceModifier() + 100) / 100);
return iOldModifiedBaseMaintenance - iNewModifiedBaseMaintenance;
}
return 0;
}
int CvCity::getNumCitiesMaintenanceSavedTimes100ByCivic(CivicTypes eCivic) const
{
FAssertMsg(eCivic >= 0, "eCivic expected to be >= 0");
FAssertMsg(eCivic < GC.getNumCivicInfos(), "eBuilding expected to be < GC.getNumCivicInfos()");
CvCivicInfo& kCivic = GC.getCivicInfo(eCivic);
int iModifier = kCivic.getNumCitiesMaintenanceModifier();
//Subtract the maintenance from our current civic
if (GET_PLAYER(getOwnerINLINE()).getCivics((CivicOptionTypes)kCivic.getCivicOptionType()) != NO_CIVIC)
{
iModifier -= GC.getCivicInfo(GET_PLAYER(getOwnerINLINE()).getCivics((CivicOptionTypes)kCivic.getCivicOptionType())).getNumCitiesMaintenanceModifier();
}
if (iModifier != 0 && !isDisorder() && !isWeLoveTheKingDay())
{
long long iOldBaseMaintenance = calculateNumCitiesMaintenanceTimes100();
long long iNewBaseMaintenance = calculateNumCitiesMaintenanceTimes100(iModifier);
long long iOldModifiedBaseMaintenance = std::max((long long)0, iOldBaseMaintenance * (getEffectiveMaintenanceModifier() + 100) / 100);
long long iNewModifiedBaseMaintenance = std::max((long long)0, iNewBaseMaintenance * (getEffectiveMaintenanceModifier() + 100) / 100);
return (int)(iOldModifiedBaseMaintenance - iNewModifiedBaseMaintenance);
}
return 0;
}
int CvCity::getHomeAreaMaintenanceSavedTimes100ByCivic(CivicTypes eCivic) const
{
FAssertMsg(eCivic >= 0, "eCivic expected to be >= 0");
FAssertMsg(eCivic < GC.getNumCivicInfos(), "eBuilding expected to be < GC.getNumCivicInfos()");
CvCivicInfo& kCivic = GC.getCivicInfo(eCivic);
int iModifier = kCivic.getHomeAreaMaintenanceModifier();
//Subtract the maintenance from our current civic
if (GET_PLAYER(getOwnerINLINE()).getCivics((CivicOptionTypes)kCivic.getCivicOptionType()) != NO_CIVIC)
{
iModifier -= GC.getCivicInfo(GET_PLAYER(getOwnerINLINE()).getCivics((CivicOptionTypes)kCivic.getCivicOptionType())).getHomeAreaMaintenanceModifier();
}
if (iModifier != 0 && area()->isHomeArea(getOwnerINLINE()) && !isDisorder() && !isWeLoveTheKingDay())
{
long long iOldBaseMaintenance = calculateBaseMaintenanceTimes100();
long long iOldModifiedBaseMaintenance = std::max((long long)0, iOldBaseMaintenance * (getEffectiveMaintenanceModifier() + 100) / 100);
long long iNewModifiedBaseMaintenance = std::max((long long)0, iOldBaseMaintenance * (getEffectiveMaintenanceModifier() + iModifier + 100) / 100);
return (int)(iOldModifiedBaseMaintenance - iNewModifiedBaseMaintenance);
}
return 0;
}
int CvCity::getOtherAreaMaintenanceSavedTimes100ByCivic(CivicTypes eCivic) const
{
FAssertMsg(eCivic >= 0, "eCivic expected to be >= 0");
FAssertMsg(eCivic < GC.getNumCivicInfos(), "eBuilding expected to be < GC.getNumCivicInfos()");
CvCivicInfo& kCivic = GC.getCivicInfo(eCivic);
int iModifier = kCivic.getOtherAreaMaintenanceModifier();
//Subtract the maintenance from our current civic
if (GET_PLAYER(getOwnerINLINE()).getCivics((CivicOptionTypes)kCivic.getCivicOptionType()) != NO_CIVIC)
{
iModifier -= GC.getCivicInfo(GET_PLAYER(getOwnerINLINE()).getCivics((CivicOptionTypes)kCivic.getCivicOptionType())).getOtherAreaMaintenanceModifier();
}
if (iModifier != 0 && !area()->isHomeArea(getOwnerINLINE()) && !isDisorder() && !isWeLoveTheKingDay())
{
long long iOldBaseMaintenance = calculateBaseMaintenanceTimes100();
long long iOldModifiedBaseMaintenance = std::max((long long)0, iOldBaseMaintenance * (getEffectiveMaintenanceModifier() + 100) / 100);
long long iNewModifiedBaseMaintenance = std::max((long long)0, iOldBaseMaintenance * (getEffectiveMaintenanceModifier() + iModifier + 100) / 100);
return (int)(iOldModifiedBaseMaintenance - iNewModifiedBaseMaintenance);
}
return 0;
}
int CvCity::getMaintenance() const
{
if ( m_bMaintenanceDirty )
{
updateMaintenance();
}
return m_iMaintenance / 100;
}
int CvCity::getMaintenanceTimes100() const
{
if ( m_bMaintenanceDirty )
{
updateMaintenance();
}
return m_iMaintenance;
}
int CvCity::getEffectiveMaintenanceModifier() const
{
int iModifier = getMaintenanceModifier() + GET_PLAYER(getOwnerINLINE()).getMaintenanceModifier() + area()->getTotalAreaMaintenanceModifier(GET_PLAYER(getOwnerINLINE()).getID());
if (isConnectedToCapital() && !(isCapital()))
{
iModifier += GET_PLAYER(getOwnerINLINE()).getConnectedCityMaintenanceModifier();
}
return iModifier;
}
void CvCity::setMaintenanceDirty(bool bDirty) const
{
m_bMaintenanceDirty = bDirty;
if (bDirty)
{
GET_PLAYER(getOwnerINLINE()).setMaintenanceDirty(true);
}
}
void CvCity::updateMaintenance() const
{
int iOldMaintenance;
long long iNewMaintenance;
//DPII < Maintenance Modifiers >
int iModifier;
//DPII < Maintenance Modifiers >
setMaintenanceDirty(false);
iOldMaintenance = getMaintenanceTimes100();
iNewMaintenance = 0;
/************************************************************************************************/
/* Afforess Start 09/12/10 */
/* */
/* */
/************************************************************************************************/
int iEraMaintanceTimes100 = GC.getEraInfo(GET_PLAYER(getOwnerINLINE()).getCurrentEra()).getInitialCityMaintenancePercent();
// iEraMaintanceTimes100 -= GC.getEraInfo(GC.getGameINLINE().getStartEra()).getInitialCityMaintenancePercent();
iNewMaintenance += std::max(0, iEraMaintanceTimes100);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (!isDisorder() && !isWeLoveTheKingDay() && (getPopulation() > 0))
{
//DPII < Maintenance Modifiers >
iModifier = getEffectiveMaintenanceModifier();
iNewMaintenance += (calculateBaseMaintenanceTimes100() * std::max(0, (iModifier + 100))) / 100;
//DPII < Maintenance Modifiers >
}
/************************************************************************************************/
/* REVOLUTION_MOD 01/31/08 jdog5000 */
/* */
/* Rebels pay less maintenance */
/************************************************************************************************/
if( GET_PLAYER(getOwnerINLINE()).isRebel() )
iNewMaintenance /= 2;
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
int iRealMaintenance;
if (iNewMaintenance > MAX_INT)
{
iRealMaintenance = MAX_INT;
}
else
{
iRealMaintenance = (int) iNewMaintenance;
}
if (iOldMaintenance != iRealMaintenance)
{
FAssert(iOldMaintenance >= 0);
FAssert(iRealMaintenance >= 0);
m_iMaintenance = iRealMaintenance;
FAssert(getMaintenance() >= 0);
/************************************************************************************************/
/* Afforess Start 6/20/11 */
/* */
/* Protect against negative maintenance */
/************************************************************************************************/
m_iMaintenance = std::max(0, m_iMaintenance);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
}
int CvCity::calculateDistanceMaintenance() const
{
return (calculateDistanceMaintenanceTimes100() / 100);
}
/************************************************************************************************/
/* Afforess Start 09/07/10 */
/* */
/* */
/************************************************************************************************/
int CvCity::calculateDistanceMaintenanceTimes100(int iExtraDistanceModifier, int iExtraCoastalDistanceModifier) const
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
CvCity* pLoopCity;
int iWorstCityMaintenance;
int iBestCapitalMaintenance;
int iTempMaintenance;
int iLoop;
iWorstCityMaintenance = 0;
iBestCapitalMaintenance = MAX_INT;
for (pLoopCity = GET_PLAYER(getOwnerINLINE()).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(getOwnerINLINE()).nextCity(&iLoop))
{
iTempMaintenance = 100 * (GC.getDefineINT("MAX_DISTANCE_CITY_MAINTENANCE") * plotDistance(getX_INLINE(), getY_INLINE(), pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE()));
iTempMaintenance *= (getPopulation() + 7);
iTempMaintenance /= 10;
/************************************************************************************************/
/* Afforess Start 09/08/10 */
/* */
/* */
/************************************************************************************************/
iTempMaintenance *= std::max(0, (GET_PLAYER(getOwnerINLINE()).getDistanceMaintenanceModifier() + iExtraDistanceModifier + 100));
iTempMaintenance /= 100;
if (isCoastal(GC.getMIN_WATER_SIZE_FOR_OCEAN()))
{
iTempMaintenance *= std::max(0, (GET_PLAYER(getOwnerINLINE()).getCoastalDistanceMaintenanceModifier() + iExtraCoastalDistanceModifier + 100));
iTempMaintenance /= 100;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iTempMaintenance *= GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getDistanceMaintenancePercent();
iTempMaintenance /= 100;
iTempMaintenance *= GC.getHandicapInfo(getHandicapType()).getDistanceMaintenancePercent();
iTempMaintenance /= 100;
iTempMaintenance /= GC.getMapINLINE().maxPlotDistance();
// To cope with the AI getting 2 starting cities on deity we greatly reduce
// distance maintenance for the AI until it builds its third city
if ( !isHuman() )
{
if (GET_PLAYER(getOwnerINLINE()).getNumCities() < 3)
{
iTempMaintenance /= 3;
}
}
iWorstCityMaintenance = std::max(iWorstCityMaintenance, iTempMaintenance);
if (pLoopCity->isGovernmentCenter())
{
iBestCapitalMaintenance = std::min(iBestCapitalMaintenance, iTempMaintenance);
}
}
iTempMaintenance = std::min(iWorstCityMaintenance, iBestCapitalMaintenance);
FAssert(iTempMaintenance >= 0);
return iTempMaintenance;
}
int CvCity::calculateNumCitiesMaintenance() const
{
return (calculateNumCitiesMaintenanceTimes100() / 100);
}
int CvCity::calculateNumCitiesMaintenanceTimes100(int iExtraModifier) const
{
int iNumCitiesPercent = 100;
iNumCitiesPercent *= (getPopulation() + 17);
iNumCitiesPercent /= 18;
iNumCitiesPercent *= GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getNumCitiesMaintenancePercent();
iNumCitiesPercent /= 100;
iNumCitiesPercent *= GC.getHandicapInfo(getHandicapType()).getNumCitiesMaintenancePercent();
iNumCitiesPercent /= 100;
double fVassalMaintenance = 0;
for (int iPlayer = 0; iPlayer < MAX_CIV_PLAYERS; iPlayer++)
{
CvPlayer& kLoopPlayer = GET_PLAYER((PlayerTypes)iPlayer);
if (kLoopPlayer.getTeam() != getTeam() && GET_TEAM(kLoopPlayer.getTeam()).isVassal(getTeam()))
{
int iLoop;
for (CvCity* pLoopCity = kLoopPlayer.firstCity(&iLoop); pLoopCity != NULL; pLoopCity = kLoopPlayer.nextCity(&iLoop))
{
fVassalMaintenance += pLoopCity->calculateNumCitiesMaintenanceTimes100();
}
}
}
fVassalMaintenance /= std::max(1, GET_TEAM(getTeam()).getNumMembers());
int iNumCities = GET_PLAYER(getOwnerINLINE()).getNumCities();
double fSum = 0;
double fBaseNumCityMaintenanceValue = (double)GC.getDefineINT("BASE_NUM_CITY_MAINTENANCE_VALUE", 5);
double fTargetCities = (double)(GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities() + 1);
for (int iI = 0; iI < iNumCities; iI++)
{
fSum += pow(fBaseNumCityMaintenanceValue, iI / fTargetCities);
}
//Vassals get 50% off number of city maintenance, vassals liege picks up the extra 50% vassal maintenance
if (GET_TEAM(getTeam()).isAVassal())
{
iNumCities *= 3;
iNumCities /= 2;
fVassalMaintenance = 0;
}
else
{
fVassalMaintenance /= 2;
}
double fNumCitiesMaintenance = ((fSum / iNumCities) * iNumCitiesPercent);
fNumCitiesMaintenance *= std::max(0, (GET_PLAYER(getOwnerINLINE()).getNumCitiesMaintenanceModifier() + iExtraModifier + 100));
fNumCitiesMaintenance /= 100;
//Average vassal costs over each city
fNumCitiesMaintenance += (fVassalMaintenance / iNumCities);
fNumCitiesMaintenance = std::min(fNumCitiesMaintenance, (double)GC.getHandicapInfo(getHandicapType()).getMaxNumCitiesMaintenance() * 100);
if (GC.getGameINLINE().getElapsedGameTurns() < 100)
{
// Koshling - count one less city for the AI - this is primarily to allow
// for the AI having 2 cities from the start on deity, but gives it a slight
// boost
fNumCitiesMaintenance *= (50 + (GC.getGameINLINE().getElapsedGameTurns() / 2));
fNumCitiesMaintenance /= 100;
}
FAssert(fNumCitiesMaintenance >= 0);
if (fNumCitiesMaintenance > MAX_INT)
{
FAssertMsg(false, "City Maintenance is >= MAX_INT");
return MAX_INT;
}
return (int)fNumCitiesMaintenance;
}
int CvCity::calculateColonyMaintenance() const
{
return (calculateColonyMaintenanceTimes100() / 100);
}
int CvCity::calculateColonyMaintenanceTimes100() const
{
if (GC.getGameINLINE().isOption(GAMEOPTION_NO_VASSAL_STATES))
{
return 0;
}
CvCity* pCapital = GET_PLAYER(getOwnerINLINE()).getCapitalCity();
if (pCapital && pCapital->area() == area())
{
return 0;
}
int iNumCitiesPercent = 100;
iNumCitiesPercent *= (getPopulation() + 17);
iNumCitiesPercent /= 18;
iNumCitiesPercent *= GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getColonyMaintenancePercent();
iNumCitiesPercent /= 100;
iNumCitiesPercent *= GC.getHandicapInfo(getHandicapType()).getColonyMaintenancePercent();
iNumCitiesPercent /= 100;
int iNumCities = (area()->getCitiesPerPlayer(getOwnerINLINE()) - 1) * iNumCitiesPercent;
int iMaintenance = (iNumCities * iNumCities) / 100;
iMaintenance = std::min(iMaintenance, (GC.getHandicapInfo(getHandicapType()).getMaxColonyMaintenance() * calculateDistanceMaintenanceTimes100()) / 100);
FAssert(iMaintenance >= 0);
return iMaintenance;
}
int CvCity::calculateCorporationMaintenance() const
{
return (calculateCorporationMaintenanceTimes100() / 100);
}
int CvCity::calculateCorporationMaintenanceTimes100() const
{
int iMaintenance = 0;
for (int iCorporation = 0; iCorporation < GC.getNumCorporationInfos(); ++iCorporation)
{
if (isActiveCorporation((CorporationTypes)iCorporation))
{
iMaintenance += calculateCorporationMaintenanceTimes100((CorporationTypes)iCorporation);
}
}
return iMaintenance;
}
int CvCity::calculateCorporationMaintenanceTimes100(CorporationTypes eCorporation) const
{
int iMaintenance = 0;
for (int iCommerce = 0; iCommerce < NUM_COMMERCE_TYPES; ++iCommerce)
{
iMaintenance += 100 * GC.getCorporationInfo(eCorporation).getHeadquarterCommerce(iCommerce);
}
int iNumBonuses = 0;
for (int i = 0; i < GC.getNUM_CORPORATION_PREREQ_BONUSES(); ++i)
{
BonusTypes eBonus = (BonusTypes)GC.getCorporationInfo(eCorporation).getPrereqBonus(i);
if (NO_BONUS != eBonus)
{
iNumBonuses += getNumBonuses(eBonus);
}
}
int iBonusMaintenance = GC.getCorporationInfo(eCorporation).getMaintenance() * iNumBonuses;
iBonusMaintenance *= GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getCorporationMaintenancePercent();
iBonusMaintenance /= 100;
iMaintenance += iBonusMaintenance;
iMaintenance *= (getPopulation() + 17);
iMaintenance /= 18;
iMaintenance *= GC.getHandicapInfo(getHandicapType()).getCorporationMaintenancePercent();
iMaintenance /= 100;
iMaintenance *= std::max(0, (GET_PLAYER(getOwnerINLINE()).getCorporationMaintenanceModifier() + 100));
iMaintenance /= 100;
int iInflation = GET_PLAYER(getOwnerINLINE()).calculateInflationRate() + 100;
if (iInflation > 0)
{
iMaintenance *= 100;
iMaintenance /= iInflation;
}
/************************************************************************************************/
/* Afforess Start 06/17/10 */
/* */
/* */
/************************************************************************************************/
iMaintenance *= (GET_TEAM(getTeam()).getCorporationMaintenanceModifier() + 100);
iMaintenance /= 100;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
/************************************************************************************************/
/* Afforess Start 06/19/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getGameINLINE().isOption(GAMEOPTION_REALISTIC_CORPORATIONS))
{
if (GC.getHandicapInfo(getHandicapType()).getCorporationMaintenancePercent() > 80)
{
iMaintenance *= (GC.getHandicapInfo(getHandicapType()).getCorporationMaintenancePercent());
iMaintenance /= 80;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
FAssert(iMaintenance >= 0);
return iMaintenance;
}
int CvCity::calculateBuildingMaintenanceTimes100() const
{
if ( GC.getDefineINT("TREAT_NEGATIVE_GOLD_AS_MAINTENANCE") )
{
int iResult = 0;
for (int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
BuildingTypes eBuilding = (BuildingTypes)iI;
if (getNumActiveBuilding(eBuilding) > 0)
{
int iBaseCommerceChange = GC.getBuildingInfo(eBuilding).getCommerceChange(COMMERCE_GOLD);
if ( iBaseCommerceChange < 0 )
{
iResult -= iBaseCommerceChange*100;
}
}
}
iResult = (GC.getHandicapInfo(getHandicapType()).getCorporationMaintenancePercent() * iResult) / 100;
return iResult;
}
else
{
return 0;
}
}
int CvCity::calculateBaseMaintenanceTimes100() const
{
//Each maintenance function returns int, sum all integers may exceed the max int. Have to store in a variable
//Larger than an int, and check for overflow.
long long iMaintenance = calculateBuildingMaintenanceTimes100();
iMaintenance += calculateBuildingMaintenanceTimes100();
iMaintenance += calculateDistanceMaintenanceTimes100();
iMaintenance += calculateNumCitiesMaintenanceTimes100();
iMaintenance += calculateColonyMaintenanceTimes100();
iMaintenance += calculateCorporationMaintenanceTimes100();
if (iMaintenance > MAX_INT)
{
return MAX_INT;
}
return (int) iMaintenance;
}
int CvCity::getMaintenanceModifier() const
{
return m_iMaintenanceModifier;
}
void CvCity::changeMaintenanceModifier(int iChange)
{
if (iChange != 0)
{
m_iMaintenanceModifier = (m_iMaintenanceModifier + iChange);
setMaintenanceDirty(true);
}
}
int CvCity::getWarWearinessModifier() const
{
return m_iWarWearinessModifier;
}
void CvCity::changeWarWearinessModifier(int iChange)
{
if (iChange != 0)
{
m_iWarWearinessModifier = (m_iWarWearinessModifier + iChange);
AI_setAssignWorkDirty(true);
}
}
int CvCity::getHurryAngerModifier() const
{
int iTotalHurryAngerModifier = m_iHurryAngerModifier;
iTotalHurryAngerModifier += GET_PLAYER(getOwnerINLINE()).getNationalHurryAngerModifier();
// AIAndy: This used to return m_iHurryAngerModifier instead of the calculated iTotalHurryAngerModifier which I don't assume is correct
return iTotalHurryAngerModifier;
}
void CvCity::changeHurryAngerModifier(int iChange)
{
if (0 != iChange)
{
int iRatio = 0;
// Foregiveness for crazily long hurry anger times because its was bugged in older
// versions!
if (m_iHurryAngerTimer > 1000)
{
m_iHurryAngerTimer = 0;
}
if (m_iHurryAngerTimer > 0 && !GC.getGameINLINE().isRecalculatingModifiers())
{
iRatio = (100 * (m_iHurryAngerTimer - 1)) / std::max(1, 100 + getHurryAngerModifier());
}
m_iHurryAngerModifier += iChange;
if (m_iHurryAngerTimer > 0 && !GC.getGameINLINE().isRecalculatingModifiers())
{
m_iHurryAngerTimer = (iRatio * std::max(1, 100 + getHurryAngerModifier())) / 100 + 1;
}
}
}
int CvCity::getHealRate() const
{
return m_iHealRate;
}
void CvCity::changeHealRate(int iChange)
{
m_iHealRate = (m_iHealRate + iChange);
FAssert(getHealRate() >= 0);
}
int CvCity::getEspionageHealthCounter() const
{
/************************************************************************************************/
/* Afforess Start 06/29/10 */
/* */
/* */
/************************************************************************************************/
/*
return m_iEspionageHealthCounter;
*/
return std::min(8, m_iEspionageHealthCounter);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
void CvCity::changeEspionageHealthCounter(int iChange)
{
if (iChange != 0)
{
m_iEspionageHealthCounter += iChange;
}
}
int CvCity::getEspionageHappinessCounter() const
{
/************************************************************************************************/
/* Afforess Start 06/29/10 */
/* */
/* */
/************************************************************************************************/
/*
return m_iEspionageHappinessCounter;
*/
return std::min(8, m_iEspionageHappinessCounter);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
void CvCity::changeEspionageHappinessCounter(int iChange)
{
if (iChange != 0)
{
m_iEspionageHappinessCounter += iChange;
}
}
int CvCity::getFreshWaterGoodHealth() const
{
return m_iFreshWaterGoodHealth;
}
int CvCity::getFreshWaterBadHealth() const
{
return m_iFreshWaterBadHealth;
}
void CvCity::updateFreshWaterHealth()
{
int iNewGoodHealth;
int iNewBadHealth;
iNewGoodHealth = 0;
iNewBadHealth = 0;
if (plot()->isFreshWater())
{
if (GC.getDefineINT("FRESH_WATER_HEALTH_CHANGE") > 0)
{
iNewGoodHealth += GC.getDefineINT("FRESH_WATER_HEALTH_CHANGE");
}
else
{
iNewBadHealth += GC.getDefineINT("FRESH_WATER_HEALTH_CHANGE");
}
}
if ((getFreshWaterGoodHealth() != iNewGoodHealth) || (getFreshWaterBadHealth() != iNewBadHealth))
{
m_iFreshWaterGoodHealth = iNewGoodHealth;
m_iFreshWaterBadHealth = iNewBadHealth;
FAssert(getFreshWaterGoodHealth() >= 0);
FAssert(getFreshWaterBadHealth() <= 0);
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
int CvCity::getFeatureGoodHealth() const
{
return m_iFeatureGoodHealth;
}
int CvCity::getFeatureBadHealth() const
{
return m_iFeatureBadHealth;
}
// BUG - Feature Health - start
/*
* Recalculates the total percentage health effects from existing features
* and updates the values if they have changed.
*
* Bad health is stored as a negative value.
*/
void CvCity::updateFeatureHealth()
{
int iNewGoodHealth;
int iNewBadHealth;
iNewGoodHealth = 0;
iNewBadHealth = 0;
calculateFeatureHealthPercent(iNewGoodHealth, iNewBadHealth);
iNewBadHealth = -iNewBadHealth; // convert to "negative is bad"
iNewGoodHealth /= 100;
// AIAndy: Feature unhealthiness reduced for small cities
iNewBadHealth *= std::max(std::min(getPopulation()-2, 5), 0);
iNewBadHealth /= 500;
if ((getFeatureGoodHealth() != iNewGoodHealth) || (getFeatureBadHealth() != iNewBadHealth))
{
m_iFeatureGoodHealth = iNewGoodHealth;
m_iFeatureBadHealth = iNewBadHealth;
FAssert(getFeatureGoodHealth() >= 0);
FAssert(getFeatureBadHealth() <= 0);
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
/*
* Adds the total percentage health effects from existing features to iGood and iBad.
*
* Positive values for iBad mean an increase in unhealthiness.
*/
void CvCity::calculateFeatureHealthPercent(int& iGood, int& iBad) const
{
CvPlot* pLoopPlot;
FeatureTypes eFeature;
int iI;
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
{
pLoopPlot = getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
#ifdef MULTI_FEATURE_MOD
for (int i=0; i<pLoopPlot->getNumFeatures(); i++)
{
eFeature = pLoopPlot->getFeatureByIndex(i);
int iHealth = GC.getFeatureInfo(eFeature).getHealthPercent();
if (iHealth > 0)
{
iGood += iHealth;
}
else
{
iBad -= iHealth;
}
}
#else
eFeature = pLoopPlot->getFeatureType();
if (eFeature != NO_FEATURE)
{
int iHealth = GC.getFeatureInfo(eFeature).getHealthPercent();
if (iHealth > 0)
{
iGood += iHealth;
}
else
{
iBad -= iHealth;
}
}
#endif
}
}
}
/*
* Subtracts the total percentage health effects of features currently being removed to iGood and iBad.
* If pIgnorePlot is not NULL, it is not checked for feature removal.
* Checks only plots visible to this city's owner.
*
* Positive values for iBad mean an increase in unhealthiness.
*/
void CvCity::calculateFeatureHealthPercentChange(int& iGood, int& iBad, CvPlot* pIgnorePlot) const
{
CvPlot* pLoopPlot;
FeatureTypes eFeature;
int iI;
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
{
pLoopPlot = getCityIndexPlot(iI);
if (pLoopPlot != NULL && pLoopPlot != pIgnorePlot && pLoopPlot->isVisible(getTeam(), true))
{
eFeature = pLoopPlot->getFeatureType();
if (eFeature != NO_FEATURE)
{
int iHealth = GC.getFeatureInfo(eFeature).getHealthPercent();
if (iHealth != 0)
{
int iNumUnits = pLoopPlot->getNumUnits();
if (iNumUnits > 0)
{
CLLNode<IDInfo>* pUnitNode = pLoopPlot->headUnitNode();
while (pUnitNode != NULL)
{
CvUnit* pUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pLoopPlot->nextUnitNode(pUnitNode);
BuildTypes eBuild = pUnit->getBuildType();
if (eBuild != NO_BUILD)
{
CvBuildInfo& kBuild = GC.getBuildInfo(eBuild);
if (kBuild.isFeatureRemove(eFeature))
{
if (iHealth > 0)
{
iGood += iHealth;
}
else
{
iBad -= iHealth;
}
break;
}
}
}
}
}
}
}
}
}
/*
* Returns the total additional health that adding or removing iChange eFeatures will provide.
*/
int CvCity::getAdditionalHealthByFeature(FeatureTypes eFeature, int iChange) const
{
int iGood = 0, iBad = 0;
return getAdditionalHealthByFeature(eFeature, iChange, iGood, iBad);
}
/*
* Returns the total additional health that adding or removing iChange eFeatures will provide
* and sets the good and bad levels individually.
*
* Doesn't reset iGood or iBad to zero.
* Positive values for iBad mean an increase in unhealthiness.
*/
int CvCity::getAdditionalHealthByFeature(FeatureTypes eFeature, int iChange, int& iGood, int& iBad) const
{
FAssertMsg(eFeature >= 0, "eFeature expected to be >= 0");
FAssertMsg(eFeature < GC.getNumFeatureInfos(), "eFeature expected to be < GC.getNumFeatureInfos()");
//CvFeatureInfo& kFeature = GC.getFeatureInfo(eFeature);
int iHealth = GC.getFeatureInfo(eFeature).getHealthPercent();
if (iHealth > 0)
{
return getAdditionalHealth(iChange * iHealth, 0, iGood, iBad);
}
else
{
return getAdditionalHealth(0, - iChange * iHealth, iGood, iBad);
}
}
/*
* Returns the total additional health that adding or removing a good or bad health percent will provide
* and sets the good and bad levels individually.
*
* Doesn't reset iGood or iBad to zero.
* Positive values for iBad and iBadPercent mean an increase in unhealthiness.
*/
int CvCity::getAdditionalHealth(int iGoodPercent, int iBadPercent, int& iGood, int& iBad) const
{
int iStarting = iGood - iBad;
// Add current
calculateFeatureHealthPercent(iGoodPercent, iBadPercent);
// Delta
iGood += (iGoodPercent / 100) - getFeatureGoodHealth();
iBad += (iBadPercent / 100) + getFeatureBadHealth(); // bad health is stored as negative
return iGood - iBad - iStarting;
}
// BUG - Feature Health - end
// BUG - Actual Effects - start
/*
* Returns the additional angry population caused by the given happiness changes.
*
* Positive values for iBad mean an increase in unhappiness.
*/
int CvCity::getAdditionalAngryPopuplation(int iGood, int iBad) const
{
int iHappy = happyLevel();
int iUnhappy = unhappyLevel();
int iPop = getPopulation();
return range((iUnhappy + iBad) - (iHappy + iGood), 0, iPop) - range(iUnhappy - iHappy, 0, iPop);
}
/*
* Returns the additional spoiled food caused by the given health changes.
*
* Positive values for iBad mean an increase in unhealthiness.
*/
int CvCity::getAdditionalSpoiledFood(int iGood, int iBad, int iHealthAdjust) const
{
int iHealthy = goodHealth();
int iUnhealthy = badHealth();
int iRate = iHealthy - iUnhealthy + iHealthAdjust;
return std::min(0, iRate) - std::min(0, iRate + iGood - iBad);
}
/*
* Returns the additional starvation caused by the given spoiled food.
*/
int CvCity::getAdditionalStarvation(int iSpoiledFood, int iFoodAdjust) const
{
int iFood = getYieldRate(YIELD_FOOD) - foodConsumption() + iFoodAdjust;
if (iSpoiledFood > 0)
{
if (iFood <= 0)
{
return iSpoiledFood;
}
else if (iSpoiledFood > iFood)
{
return iSpoiledFood - iFood;
}
}
else if (iSpoiledFood < 0)
{
if (iFood < 0)
{
return std::max(iFood, iSpoiledFood);
}
}
return 0;
}
// BUG - Actual Effects - start
int CvCity::getBuildingGoodHealth() const
{
return m_iBuildingGoodHealth;
}
int CvCity::getBuildingBadHealth() const
{
return m_iBuildingBadHealth;
}
int CvCity::getBuildingHealth(BuildingTypes eBuilding) const
{
int iHealth = getBuildingGoodHealth(eBuilding);
if (!isBuildingOnlyHealthy())
{
iHealth += getBuildingBadHealth(eBuilding);
}
return iHealth;
}
int CvCity::getBuildingGoodHealth(BuildingTypes eBuilding) const
{
int iHealth = std::max(0, GC.getBuildingInfo(eBuilding).getHealth());
iHealth += std::max(0, getBuildingHealthChange((BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType()));
iHealth += std::max(0, GET_PLAYER(getOwnerINLINE()).getExtraBuildingHealth(eBuilding));
/************************************************************************************************/
/* Afforess Start 08/24/10 */
/* */
/* */
/************************************************************************************************/
iHealth += std::max(0, GET_TEAM(getTeam()).getTechExtraBuildingHealth(eBuilding));
iHealth += std::max(0, (GC.getBuildingInfo(eBuilding).getHealthPercentPerPopulation() * getPopulation()) / 100);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iHealth;
}
int CvCity::getBuildingBadHealth(BuildingTypes eBuilding) const
{
if (isBuildingOnlyHealthy())
{
return 0;
}
int iHealth = std::min(0, GC.getBuildingInfo(eBuilding).getHealth());
iHealth += std::min(0, getBuildingHealthChange((BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType()));
iHealth += std::min(0, GET_PLAYER(getOwnerINLINE()).getExtraBuildingHealth(eBuilding));
/************************************************************************************************/
/* Afforess Start 08/24/10 */
/* */
/* */
/************************************************************************************************/
iHealth += std::min(0, GET_TEAM(getTeam()).getTechExtraBuildingHealth(eBuilding));
iHealth += std::min(0, (GC.getBuildingInfo(eBuilding).getHealthPercentPerPopulation() * getPopulation()) / 100);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iHealth;
}
void CvCity::changeBuildingGoodHealth(int iChange)
{
if (iChange != 0)
{
m_iBuildingGoodHealth = (m_iBuildingGoodHealth + iChange);
FAssert(getBuildingGoodHealth() >= 0);
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
void CvCity::changeBuildingBadHealth(int iChange)
{
if (iChange != 0)
{
m_iBuildingBadHealth += iChange;
FAssert(getBuildingBadHealth() <= 0);
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
int CvCity::getPowerGoodHealth() const
{
return m_iPowerGoodHealth;
}
int CvCity::getPowerBadHealth() const
{
return m_iPowerBadHealth;
}
void CvCity::updatePowerHealth()
{
int iNewGoodHealth;
int iNewBadHealth;
iNewGoodHealth = 0;
iNewBadHealth = 0;
if (isPower())
{
int iPowerHealth = GC.getDefineINT("POWER_HEALTH_CHANGE");
if (iPowerHealth > 0)
{
iNewGoodHealth += iPowerHealth;
}
else
{
iNewBadHealth += iPowerHealth;
}
}
if (isDirtyPower())
{
int iDirtyPowerHealth = GC.getDefineINT("DIRTY_POWER_HEALTH_CHANGE");
if (iDirtyPowerHealth > 0)
{
iNewGoodHealth += iDirtyPowerHealth;
}
else
{
iNewBadHealth += iDirtyPowerHealth;
}
}
if ((getPowerGoodHealth() != iNewGoodHealth) || (getPowerBadHealth() != iNewBadHealth))
{
m_iPowerGoodHealth = iNewGoodHealth;
m_iPowerBadHealth = iNewBadHealth;
FAssert(getPowerGoodHealth() >= 0);
FAssert(getPowerBadHealth() <= 0);
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
int CvCity::getBonusGoodHealth() const
{
return m_iBonusGoodHealth;
}
int CvCity::getBonusBadHealth() const
{
return m_iBonusBadHealth;
}
void CvCity::changeBonusGoodHealth(int iChange)
{
if (iChange != 0)
{
m_iBonusGoodHealth += iChange;
FAssert(getBonusGoodHealth() >= 0);
FAssertMsg(getBonusGoodHealth() >= 0, "getBonusGoodHealth is expected to be >= 0");
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
void CvCity::changeBonusBadHealth(int iChange)
{
if (iChange != 0)
{
m_iBonusBadHealth += iChange;
FAssert(getBonusBadHealth() <= 0);
FAssertMsg(getBonusBadHealth() <= 0, "getBonusBadHealth is expected to be <= 0");
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
int CvCity::getMilitaryHappinessUnits() const
{
return m_iMilitaryHappinessUnits;
}
int CvCity::getMilitaryHappiness() const
{
return (getMilitaryHappinessUnits() * GET_PLAYER(getOwnerINLINE()).getHappyPerMilitaryUnit());
}
void CvCity::changeMilitaryHappinessUnits(int iChange)
{
if (iChange != 0)
{
m_iMilitaryHappinessUnits = (m_iMilitaryHappinessUnits + iChange);
FAssert(getMilitaryHappinessUnits() >= 0);
AI_setAssignWorkDirty(true);
}
}
int CvCity::getBuildingGoodHappiness() const
{
/************************************************************************************************/
/* Afforess Start 08/29/10 */
/* */
/* */
/************************************************************************************************/
/*
return m_iBuildingGoodHappiness
*/
return m_iBuildingGoodHappiness + std::max(0, calculatePopulationHappiness());
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
int CvCity::getBuildingBadHappiness() const
{
/************************************************************************************************/
/* Afforess Start 08/29/10 */
/* */
/* */
/************************************************************************************************/
/*
return m_iBuildingBadHappiness
*/
return m_iBuildingBadHappiness + std::min(0, calculatePopulationHappiness());
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
int CvCity::getBuildingHappiness(BuildingTypes eBuilding) const
{
int iHappiness;
int iI;
iHappiness = GC.getBuildingInfo(eBuilding).getHappiness();
if (GC.getBuildingInfo(eBuilding).getReligionType() != NO_RELIGION)
{
if (GC.getBuildingInfo(eBuilding).getReligionType() == GET_PLAYER(getOwnerINLINE()).getStateReligion())
{
iHappiness += GC.getBuildingInfo(eBuilding).getStateReligionHappiness();
}
}
iHappiness += GET_PLAYER(getOwnerINLINE()).getExtraBuildingHappiness(eBuilding);
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
iHappiness += ((GC.getBuildingInfo(eBuilding).getCommerceHappiness(iI) * GET_PLAYER(getOwnerINLINE()).getCommercePercent((CommerceTypes)iI)) / 100);
}
iHappiness += getBuildingHappyChange((BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType());
/************************************************************************************************/
/* Afforess Start 08/24/10 */
/* */
/* */
/************************************************************************************************/
iHappiness += (GC.getBuildingInfo(eBuilding).getHappinessPercentPerPopulation() * getPopulation()) / 100;
iHappiness += GET_TEAM(getTeam()).getTechExtraBuildingHappiness(eBuilding);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iHappiness;
}
void CvCity::changeBuildingGoodHappiness(int iChange)
{
if (iChange != 0)
{
m_iBuildingGoodHappiness = (m_iBuildingGoodHappiness + iChange);
FAssert(getBuildingGoodHappiness() >= 0);
AI_setAssignWorkDirty(true);
}
}
void CvCity::changeBuildingBadHappiness(int iChange)
{
if (iChange != 0)
{
m_iBuildingBadHappiness = (m_iBuildingBadHappiness + iChange);
FAssert(getBuildingBadHappiness() <= 0);
AI_setAssignWorkDirty(true);
}
}
int CvCity::getExtraBuildingGoodHappiness() const
{
return m_iExtraBuildingGoodHappiness;
}
int CvCity::getExtraBuildingBadHappiness() const
{
return m_iExtraBuildingBadHappiness;
}
/********************************************************************************/
/* New Civic AI 19.08.2010 Fuyu */
/********************************************************************************/
//Fuyu bLimited
void CvCity::updateExtraBuildingHappiness(bool bLimited)
{
int iNewExtraBuildingGoodHappiness;
int iNewExtraBuildingBadHappiness;
int iChange;
int iI;
iNewExtraBuildingGoodHappiness = 0;
iNewExtraBuildingBadHappiness = 0;
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
iChange = getNumActiveBuilding((BuildingTypes)iI) * GET_PLAYER(getOwnerINLINE()).getExtraBuildingHappiness((BuildingTypes)iI);
if (iChange > 0)
{
iNewExtraBuildingGoodHappiness += iChange;
}
else
{
iNewExtraBuildingBadHappiness += iChange;
}
}
if (getExtraBuildingGoodHappiness() != iNewExtraBuildingGoodHappiness)
{
m_iExtraBuildingGoodHappiness = iNewExtraBuildingGoodHappiness;
FAssert(getExtraBuildingGoodHappiness() >= 0);
if (!bLimited)
{
AI_setAssignWorkDirty(true);
}
}
if (getExtraBuildingBadHappiness() != iNewExtraBuildingBadHappiness)
{
m_iExtraBuildingBadHappiness = iNewExtraBuildingBadHappiness;
FAssert(getExtraBuildingBadHappiness() <= 0);
if (!bLimited)
{
AI_setAssignWorkDirty(true);
}
}
}
int CvCity::getAdditionalHappinessByCivic(CivicTypes eCivic, bool bDifferenceToCurrent, bool bCivicOptionVacuum, ReligionTypes eStateReligion, int iExtraPop, int iMilitaryHappinessUnits) const
{
if (bDifferenceToCurrent)
{
return getAdditionalHappinessByCivic(eCivic, false, bCivicOptionVacuum, eStateReligion, iExtraPop, iMilitaryHappinessUnits) - getAdditionalHappinessByCivic( GET_PLAYER(getOwnerINLINE()).getCivics((CivicOptionTypes)(GC.getCivicInfo(eCivic).getCivicOptionType())) , false, bCivicOptionVacuum, eStateReligion, iExtraPop, iMilitaryHappinessUnits);
}
if (eCivic == NO_CIVIC)
{
return 0;
}
CvCivicInfo& kCivic = GC.getCivicInfo(eCivic);
CvPlayer& kOwner = GET_PLAYER(getOwnerINLINE());
if (eStateReligion == NO_RELIGION)
{
eStateReligion = kOwner.getStateReligion();
}
if (!bCivicOptionVacuum && !kCivic.isStateReligion() && kOwner.getStateReligionCount() == 1)
{
if ( GC.getCivicInfo( kOwner.getCivics((CivicOptionTypes)(kCivic.getCivicOptionType())) ).isStateReligion() )
{
eStateReligion = NO_RELIGION;
}
}
int iHappy = 0;
int iI = 0;
//#1.a: Military Happiness
if (kCivic.getHappyPerMilitaryUnit() != 0)
{
if (iMilitaryHappinessUnits < 0) //default -1
{
iMilitaryHappinessUnits = getMilitaryHappinessUnits();
}
iHappy += iMilitaryHappinessUnits * kCivic.getHappyPerMilitaryUnit();
}
//#1.b: CivicPercentAnger and WarWearinessModifier
if ((kCivic.getCivicPercentAnger() != 0 && kOwner.getCivicPercentAnger(eCivic, true) != 0)
|| (kCivic.getWarWearinessModifier() != 0 && kOwner.getWarWearinessPercentAnger() != 0))
{
//int CvCity::unhappyLevel(int iExtra) const
int iAngerPercent = 0;
iAngerPercent += getOvercrowdingPercentAnger(iExtraPop);
iAngerPercent += getNoMilitaryPercentAnger();
iAngerPercent += getCulturePercentAnger();
iAngerPercent += getReligionPercentAnger();
iAngerPercent += getHurryPercentAnger(iExtraPop);
iAngerPercent += getConscriptPercentAnger(iExtraPop);
iAngerPercent += getDefyResolutionPercentAnger(iExtraPop);
int iOldWarWearinessAngerPercent;
if ( !bCivicOptionVacuum && kOwner.getWarWearinessPercentAnger() != 0
&& GC.getCivicInfo( kOwner.getCivics((CivicOptionTypes)(kCivic.getCivicOptionType())) ).getWarWearinessModifier() != 0 )
{
//int CvCity::getWarWearinessPercentAnger() const
iOldWarWearinessAngerPercent = kOwner.getWarWearinessPercentAnger();
iOldWarWearinessAngerPercent *= std::max(0, (getWarWearinessModifier() + kOwner.getWarWearinessModifier() - GC.getCivicInfo( kOwner.getCivics((CivicOptionTypes)(kCivic.getCivicOptionType())) ).getWarWearinessModifier() + 100));
iOldWarWearinessAngerPercent /= 100;
}
else
{
iOldWarWearinessAngerPercent = getWarWearinessPercentAnger();
}
iAngerPercent += iOldWarWearinessAngerPercent;
for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
{
iAngerPercent += kOwner.getCivicPercentAnger((CivicTypes)iI, ( (CivicTypes)iI == eCivic || (!bCivicOptionVacuum && (CivicTypes)iI == kOwner.getCivics((CivicOptionTypes)(kCivic.getCivicOptionType()))) ));
}
int iUnhappinessNow = ((iAngerPercent * (getPopulation() + iExtraPop)) / GC.getPERCENT_ANGER_DIVISOR());
iAngerPercent -= kOwner.getCivicPercentAnger(eCivic, true);
if (kOwner.getWarWearinessPercentAnger() != 0 && kCivic.getWarWearinessModifier() != 0)
{
//int CvCity::getWarWearinessPercentAnger() const
int iNewWarWearinessAngerPercent = kOwner.getWarWearinessPercentAnger();
iNewWarWearinessAngerPercent *= std::max(0, (getWarWearinessModifier() + kOwner.getWarWearinessModifier() + kCivic.getWarWearinessModifier() - ((bCivicOptionVacuum)? 0 : GC.getCivicInfo( kOwner.getCivics((CivicOptionTypes)(kCivic.getCivicOptionType())) ).getWarWearinessModifier() ) + 100));
iNewWarWearinessAngerPercent /= 100;
iAngerPercent -= iOldWarWearinessAngerPercent;
iAngerPercent += iNewWarWearinessAngerPercent;
}
int iUnhappinessThen = ((iAngerPercent * (getPopulation() + iExtraPop)) / GC.getPERCENT_ANGER_DIVISOR());
iHappy += iUnhappinessNow - iUnhappinessThen;
}
//#1.c: LargestCityHappiness
if (kCivic.getLargestCityHappiness() != 0)
{
//int CvCity::getLargestCityHappiness() const
if (findPopulationRank() <= GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities())
{
iHappy += kCivic.getLargestCityHappiness();
}
}
if (kCivic.getCivicHappiness() != 0)
{
iHappy += kCivic.getCivicHappiness();
}
if (kCivic.getCityLimit(getOwnerINLINE()) > 0 && kCivic.getCityOverLimitUnhappy() > 0 && kOwner.getNumCities() > kCivic.getCityLimit(getOwnerINLINE()))
{
iHappy -= kCivic.getCityOverLimitUnhappy()*(kOwner.getNumCities() - kCivic.getCityLimit(getOwnerINLINE()));
}
if (kCivic.getForeignerUnhappyPercent() > 0)
{
iHappy -= ((100 / kCivic.getForeignerUnhappyPercent()) * (100 - plot()->calculateCulturePercent(getOwnerINLINE()))) / 100;
}
//#1.d: BuildingHappinessChanges
if (kCivic.isAnyBuildingHappinessChange())
{
CvCivilizationInfo& kCivilization = GC.getCivilizationInfo(getCivilizationType());
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
int iTempHappy = kCivic.getBuildingHappinessChanges(iI);
if (iTempHappy != 0)
{
BuildingTypes eLoopBuilding = (BuildingTypes)kCivilization.getCivilizationBuildings(iI);
if (eLoopBuilding != NO_BUILDING)
{
iHappy += iTempHappy * getNumBuilding(eLoopBuilding);
}
}
}
}
//#1.e: FeatureHappinessChanges
if (kCivic.isAnyFeatureHappinessChange())
{
CvPlot* pLoopPlot;
for (iI = 0; iI < GC.getNumFeatureInfos(); iI++)
{
int iTempHappy = kCivic.getFeatureHappinessChanges(iI);
if (iTempHappy != 0)
{
int iCount = 0;
for (int iJ = 0; iJ < NUM_CITY_PLOTS; iJ++)
{
pLoopPlot = plotCity(getX_INLINE(), getY_INLINE(), iJ);
if (pLoopPlot != NULL)
{
#ifdef MULTI_FEATURE_MOD
if (pLoopPlot->getHasFeature((FeatureTypes)iI))
#else
if (pLoopPlot->getFeatureType() == (FeatureTypes)iI)
#endif
{
iCount++;
}
}
}
iHappy += iTempHappy * iCount;
}
}
}
//#1.f: Religious Happiness
if (kCivic.getStateReligionHappiness() != 0 || kCivic.getNonStateReligionHappiness() != 0)
{
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (isHasReligion((ReligionTypes)iI))
{
if ((ReligionTypes)iI == eStateReligion /* redundant? -> */ && (ReligionTypes)iI != NO_RELIGION)
{
iHappy += kCivic.getStateReligionHappiness();
}
else
{
iHappy += kCivic.getNonStateReligionHappiness();
}
}
}
}
if (isCapital() && kCivic.isNoCapitalUnhappiness())
{
iHappy = std::max(unhappyLevel(0),iHappy);
}
return iHappy;
}
int CvCity::getAdditionalHealthByCivic(CivicTypes eCivic, bool bDifferenceToCurrent) const
{
int iGood = 0; int iBad = 0; int iBadBuilding = 0;
return getAdditionalHealthByCivic(eCivic, iGood, iBad, iBadBuilding, bDifferenceToCurrent);
}
int CvCity::getAdditionalHealthByCivic(CivicTypes eCivic, int& iGood, int& iBad, int& iBadBuilding, bool bDifferenceToCurrent, int iExtraPop, bool bCivicOptionVacuum, int iIgnoreNoUnhealthyPopulationCount, int iIgnoreBuildingOnlyHealthyCount) const
{
if (bDifferenceToCurrent)
{
int iHealthNew = getAdditionalHealthByCivic(eCivic, iGood, iBad, iBadBuilding, false, iExtraPop, bCivicOptionVacuum, iIgnoreNoUnhealthyPopulationCount, iIgnoreBuildingOnlyHealthyCount);
int iTempGood = 0; int iTempBad = 0; int iTempBadBuilding = 0;
int iHealthOld = getAdditionalHealthByCivic( GET_PLAYER(getOwnerINLINE()).getCivics((CivicOptionTypes)(GC.getCivicInfo(eCivic).getCivicOptionType())) , iTempGood, iTempBad, iTempBadBuilding, false, iExtraPop, bCivicOptionVacuum, iIgnoreNoUnhealthyPopulationCount, iIgnoreBuildingOnlyHealthyCount);
iGood += iTempBad;
iBad += iTempGood;
iBadBuilding -= iTempBadBuilding; //can become negative
return iHealthNew - iHealthOld;
}
if (eCivic == NO_CIVIC)
{
return 0;
}
CvCivicInfo& kCivic = GC.getCivicInfo(eCivic);
CvPlayer& kOwner = GET_PLAYER(getOwnerINLINE());
int iHealth = 0;
if (!bCivicOptionVacuum)
{
if ( kOwner.isCivic(eCivic) || GC.getCivicInfo(kOwner.getCivics((CivicOptionTypes)kCivic.getCivicOptionType())).isNoUnhealthyPopulation() )
{
iIgnoreNoUnhealthyPopulationCount++;
}
if ( kOwner.isCivic(eCivic) || GC.getCivicInfo(kOwner.getCivics((CivicOptionTypes)kCivic.getCivicOptionType())).isBuildingOnlyHealthy() )
{
iIgnoreBuildingOnlyHealthyCount++;
}
}
//#2.a: ExtraHealth
addGoodOrBad(kCivic.getExtraHealth(), iGood, iBad);
//#2.b: NoUnhealthyPopulation
if (kCivic.isNoUnhealthyPopulation())
{
if ((iIgnoreNoUnhealthyPopulationCount != 0) )
{
if (kOwner.getNoUnhealthyPopulationCount() <= iIgnoreNoUnhealthyPopulationCount && getNoUnhealthyPopulationCount() <= 0)
{
//std::max(0, ((getPopulation() + iExtra - ((bNoAngry) ? angryPopulation(iExtra) : 0))))
iGood += std::max(0, ((getPopulation() + iExtraPop)));
}
}
else
{
iGood += unhealthyPopulation(false, iExtraPop);
}
}
//#2.c: BuildingOnlyHealthy
if (kCivic.isBuildingOnlyHealthy())
{
if (iIgnoreBuildingOnlyHealthyCount != 0 )
{
int iOwnerBuildingOnlyHealthyCount = kOwner.getBuildingOnlyHealthyCount();
if (iOwnerBuildingOnlyHealthyCount <= iIgnoreBuildingOnlyHealthyCount && getBuildingOnlyHealthyCount() <= 0)
{
kOwner.changeBuildingOnlyHealthyCount(-iOwnerBuildingOnlyHealthyCount, true);
iGood -= totalBadBuildingHealth();
kOwner.changeBuildingOnlyHealthyCount( iOwnerBuildingOnlyHealthyCount, true);
}
}
else
{
iGood -= totalBadBuildingHealth();
}
}
//#2.d: BuildingHealthChanges
if (kCivic.isAnyBuildingHealthChange())
{
CvCivilizationInfo& kCivilization = GC.getCivilizationInfo(getCivilizationType());
for (int iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
int iTempHealth = kCivic.getBuildingHealthChanges(iI);
if (iTempHealth > 0)
{
BuildingTypes eLoopBuilding = (BuildingTypes)kCivilization.getCivilizationBuildings(iI);
if (eLoopBuilding != NO_BUILDING)
{
iGood += iTempHealth * getNumBuilding(eLoopBuilding);
}
}
else if (iTempHealth < 0 && (getBuildingOnlyHealthyCount() <= 0 || kOwner.getBuildingOnlyHealthyCount() <= iIgnoreBuildingOnlyHealthyCount))
{
BuildingTypes eLoopBuilding = (BuildingTypes)kCivilization.getCivilizationBuildings(iI);
if (eLoopBuilding != NO_BUILDING)
{
iTempHealth *= getNumBuilding(eLoopBuilding);
iBad -= iTempHealth;
iBadBuilding -= iTempHealth;
if (kCivic.isBuildingOnlyHealthy())
{
iGood += iTempHealth;
}
}
}
}
}
iHealth = iGood - iBad;
return iHealth;
}
int CvCity::getAdditionalHealthByPlayerNoUnhealthyPopulation(int iExtraPop, int iIgnoreNoUnhealthyPopulationCount) const
{
int iHealth = 0;
if ((iIgnoreNoUnhealthyPopulationCount != 0) )
{
if (GET_PLAYER(getOwnerINLINE()).getNoUnhealthyPopulationCount() <= iIgnoreNoUnhealthyPopulationCount && getNoUnhealthyPopulationCount() <= 0)
{
//std::max(0, ((getPopulation() + iExtra - ((bNoAngry) ? angryPopulation(iExtra) : 0))))
iHealth += std::max(0, ((getPopulation() + iExtraPop)));
}
}
else
{
iHealth += unhealthyPopulation(false, iExtraPop);
}
return iHealth;
}
int CvCity::getAdditionalHealthByPlayerBuildingOnlyHealthy(int iIgnoreBuildingOnlyHealthyCount) const
{
int iHealth = 0;
if (iIgnoreBuildingOnlyHealthyCount != 0 )
{
CvPlayer& kOwner = GET_PLAYER(getOwnerINLINE());
int iOwnerBuildingOnlyHealthyCount = kOwner.getBuildingOnlyHealthyCount();
if (iOwnerBuildingOnlyHealthyCount <= iIgnoreBuildingOnlyHealthyCount && getBuildingOnlyHealthyCount() <= 0)
{
kOwner.changeBuildingOnlyHealthyCount(-iOwnerBuildingOnlyHealthyCount, true);
iHealth -= totalBadBuildingHealth();
kOwner.changeBuildingOnlyHealthyCount( iOwnerBuildingOnlyHealthyCount, true);
}
}
else
{
iHealth -= totalBadBuildingHealth();
}
return iHealth;
}
/********************************************************************************/
/* New Civic AI END */
/********************************************************************************/
// BUG - Building Additional Happiness - start
/*
* Returns the total additional happiness that adding one of the given buildings will provide.
*
* Doesn't check if the building can be constructed in this city.
*/
int CvCity::getAdditionalHappinessByBuilding(BuildingTypes eBuilding)
{
int iGood = 0, iBad = 0, iAngryPop = 0;
return getAdditionalHappinessByBuilding(eBuilding, iGood, iBad, iAngryPop);
}
/*
* Returns the total additional happiness that adding one of the given buildings will provide
* and sets the good and bad levels individually and any resulting additional angry population.
*
* Doesn't reset iGood or iBad to zero.
* Doesn't check if the building can be constructed in this city.
*/
int CvCity::getAdditionalHappinessByBuilding(BuildingTypes eBuilding, int& iGood, int& iBad, int& iAngryPop)
{
PROFILE_FUNC();
FAssertMsg(eBuilding >= 0, "eBuilding expected to be >= 0");
FAssertMsg(eBuilding < GC.getNumBuildingInfos(), "eBuilding expected to be < GC.getNumBuildingInfos()");
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
int iI;
int iStarting = iGood - iBad;
int iStartingBad = iBad;
// Basic
addGoodOrBad(kBuilding.getHappiness(), iGood, iBad);
// Building Class
addGoodOrBad(getBuildingHappyChange((BuildingClassTypes)kBuilding.getBuildingClassType()), iGood, iBad);
// Other Building Classes
CvCivilizationInfo& kCivilization = GC.getCivilizationInfo(getCivilizationType());
if ( kBuilding.getBuildingHappinessChanges(NO_BUILDING) != 0 )
{
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
int iBuildingHappinessChanges = kBuilding.getBuildingHappinessChanges(iI);
if (iBuildingHappinessChanges != 0)
{
BuildingTypes eLoopBuilding = (BuildingTypes)kCivilization.getCivilizationBuildings(iI);
if (eLoopBuilding != NO_BUILDING)
{
addGoodOrBad(iBuildingHappinessChanges * (getNumBuilding(eLoopBuilding) + (eBuilding == eLoopBuilding ? 1 : 0)), iGood, iBad);
}
}
}
}
// Player Building
addGoodOrBad(GET_PLAYER(getOwnerINLINE()).getExtraBuildingHappiness(eBuilding), iGood, iBad);
// Area
addGoodOrBad(kBuilding.getAreaHappiness(), iGood, iBad);
// Global
addGoodOrBad(kBuilding.getGlobalHappiness(), iGood, iBad);
// Religion
if (kBuilding.getReligionType() != NO_RELIGION && kBuilding.getReligionType() == GET_PLAYER(getOwnerINLINE()).getStateReligion())
{
iGood += kBuilding.getStateReligionHappiness();
}
// Bonus
if ( kBuilding.getBonusHappinessChanges(NO_BONUS) != 0 )
{
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
if ((hasBonus((BonusTypes)iI) || kBuilding.getFreeBonus() == iI || kBuilding.hasExtraFreeBonus((BonusTypes)iI)) && kBuilding.getNoBonus() != iI)
{
addGoodOrBad(kBuilding.getBonusHappinessChanges(iI), iGood, iBad);
}
}
}
// Commerce
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
int iCommerceHappiness = kBuilding.getCommerceHappiness(iI);
if ( iCommerceHappiness != 0 )
{
addGoodOrBad( iCommerceHappiness * GET_PLAYER(getOwnerINLINE()).getCommercePercent((CommerceTypes)iI) / 100, iGood, iBad);
}
}
// War Weariness Modifier
int iWarWearinessModifier = kBuilding.getWarWearinessModifier() + kBuilding.getGlobalWarWearinessModifier();
if (iWarWearinessModifier != 0)
{
int iBaseAngerPercent = 0;
iBaseAngerPercent += getOvercrowdingPercentAnger();
iBaseAngerPercent += getNoMilitaryPercentAnger();
iBaseAngerPercent += getCulturePercentAnger();
iBaseAngerPercent += getReligionPercentAnger();
iBaseAngerPercent += getHurryPercentAnger();
iBaseAngerPercent += getConscriptPercentAnger();
iBaseAngerPercent += getDefyResolutionPercentAnger();
for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
{
iBaseAngerPercent += GET_PLAYER(getOwnerINLINE()).getCivicPercentAnger((CivicTypes)iI);
}
int iCurrentAngerPercent = iBaseAngerPercent + getWarWearinessPercentAnger();
int iCurrentUnhappiness = iCurrentAngerPercent * getPopulation() / GC.getPERCENT_ANGER_DIVISOR();
int iNewWarAngerPercent = GET_PLAYER(getOwnerINLINE()).getWarWearinessPercentAnger();
iNewWarAngerPercent *= std::max(0, (iWarWearinessModifier + getWarWearinessModifier() + GET_PLAYER(getOwnerINLINE()).getWarWearinessModifier() + 100));
iNewWarAngerPercent /= 100;
int iNewAngerPercent = iBaseAngerPercent + iNewWarAngerPercent;
int iNewUnhappiness = iNewAngerPercent * getPopulation() / GC.getPERCENT_ANGER_DIVISOR();
iBad += iNewUnhappiness - iCurrentUnhappiness;
}
// KOSHLING - port from K-mod - no unhappiness already => we don't care what this building does
if ( isNoUnhappiness() )
{
iBad = iStartingBad;
}
// No Unhappiness
else if (kBuilding.isNoUnhappiness())
{
// override extra unhappiness and completely negate all existing unhappiness
iBad = iStartingBad - unhappyLevel();
}
// Effect on Angry Population
int iHappy = happyLevel();
int iUnhappy = unhappyLevel();
int iPop = getPopulation();
iAngryPop += range((iUnhappy + iBad) - (iHappy + iGood), 0, iPop) - range(iUnhappy - iHappy, 0, iPop);
/************************************************************************************************/
/* Afforess Start 12/20/09 */
/* */
/* */
/************************************************************************************************/
if ( kBuilding.getTechHappinessChanges(NO_TECH) != 0 )
{
for (iI = 0; iI < GC.getNumTechInfos(); iI++)
{
if (GET_TEAM(getTeam()).isHasTech((TechTypes)iI))
{
addGoodOrBad(kBuilding.getTechHappinessChanges(iI), iGood, iBad);
}
}
}
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (getNumActiveBuilding((BuildingTypes)iI) > 0)
{
if (GC.getBuildingInfo((BuildingTypes)iI).isReplaceBuildingClass((BuildingClassTypes)kBuilding.getBuildingClassType()))
{
addGoodOrBad(-getBuildingHappiness((BuildingTypes)iI), iGood, iBad);
}
}
}
if (kBuilding.getHappinessPercentPerPopulation() > 0)
{
addGoodOrBad(kBuilding.getHappinessPercentPerPopulation() * getPopulation() / 100, iGood, iBad);
}
int iSpecialistExtraHappy = 0;
for (int iI = 1; iI < kBuilding.getFreeSpecialist() + 1; iI++)
{
SpecialistTypes eNewSpecialist = getBestSpecialist(iI);
if (eNewSpecialist == NO_SPECIALIST) break;
CvSpecialistInfo& kSpecialist = GC.getSpecialistInfo(eNewSpecialist);
iSpecialistExtraHappy += kSpecialist.getHappinessPercent();
}
iSpecialistExtraHappy /= 100;
addGoodOrBad(iSpecialistExtraHappy, iGood, iBad);
if (kBuilding.getNumPopulationEmployed() > 0)
{
int* paiCommerce = new int[NUM_COMMERCE_TYPES];
int* paiYield = new int[NUM_YIELD_TYPES];
int iGreatPeopleRate;
int iHappiness;
int iHealthiness;
removeWorstCitizenActualEffects(kBuilding.getNumPopulationEmployed(), iGreatPeopleRate, iHappiness, iHealthiness, paiYield, paiCommerce);
SAFE_DELETE_ARRAY(paiCommerce);
SAFE_DELETE_ARRAY(paiYield);
addGoodOrBad(iHappiness, iGood, iBad);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iGood - iBad - iStarting;
}
/*
* Returns the total additional health that adding one of the given buildings will provide.
*
* Doesn't check if the building can be constructed in this city.
*/
int CvCity::getAdditionalHealthByBuilding(BuildingTypes eBuilding)
{
int iGood = 0, iBad = 0, iSpoiledFood = 0, iStarvation = 0;
return getAdditionalHealthByBuilding(eBuilding, iGood, iBad, iSpoiledFood, iStarvation);
}
/*
* Returns the total additional health that adding one of the given buildings will provide
* and sets the good and bad levels individually and any resulting additional spoiled food.
*
* Doesn't reset iGood, iBad, iSpoiledFood, iStarvation to zero.
* Doesn't check if the building can be constructed in this city.
*/
int CvCity::getAdditionalHealthByBuilding(BuildingTypes eBuilding, int& iGood, int& iBad, int& iSpoiledFood, int& iStarvation)
{
PROFILE_FUNC();
FAssertMsg(eBuilding >= 0, "eBuilding expected to be >= 0");
FAssertMsg(eBuilding < GC.getNumBuildingInfos(), "eBuilding expected to be < GC.getNumBuildingInfos()");
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
int iI;
int iStarting = iGood - iBad;
int iStartingBad = iBad;
// Basic
addGoodOrBad(kBuilding.getHealth(), iGood, iBad);
// Building Class
addGoodOrBad(getBuildingHealthChange((BuildingClassTypes)kBuilding.getBuildingClassType()), iGood, iBad);
// Player Building
addGoodOrBad(GET_PLAYER(getOwnerINLINE()).getExtraBuildingHealth(eBuilding), iGood, iBad);
// Area
addGoodOrBad(kBuilding.getAreaHealth(), iGood, iBad);
// Global
addGoodOrBad(kBuilding.getGlobalHealth(), iGood, iBad);
// No Unhealthiness from Buildings
if (isBuildingOnlyHealthy())
{
// undo bad from this building
iBad = iStartingBad;
}
if (kBuilding.isBuildingOnlyHealthy())
{
// undo bad from this and all existing buildings
iBad = iStartingBad + totalBadBuildingHealth();
}
// Bonus
if ( kBuilding.getBonusHealthChanges(NO_BONUS) != 0)
{
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
if ((hasBonus((BonusTypes)iI) || kBuilding.getFreeBonus() == iI || kBuilding.hasExtraFreeBonus((BonusTypes)iI)) && kBuilding.getNoBonus() != iI)
{
addGoodOrBad(kBuilding.getBonusHealthChanges(iI), iGood, iBad);
}
}
}
// Power
if (kBuilding.isPower() || kBuilding.isAreaCleanPower() || (kBuilding.getPowerBonus() != NO_BONUS && hasBonus((BonusTypes)kBuilding.getPowerBonus())))
{
// adding power
if (!isPower())
{
addGoodOrBad(GC.getDefineINT("POWER_HEALTH_CHANGE"), iGood, iBad);
// adding dirty power
if (kBuilding.isDirtyPower())
{
addGoodOrBad(GC.getDefineINT("DIRTY_POWER_HEALTH_CHANGE"), iGood, iBad);
}
}
else
{
// replacing dirty power with clean power
if (isDirtyPower() && !kBuilding.isDirtyPower())
{
subtractGoodOrBad(GC.getDefineINT("DIRTY_POWER_HEALTH_CHANGE"), iGood, iBad);
}
}
}
// No Unhealthiness from Population
if (kBuilding.isNoUnhealthyPopulation())
{
iBad -= getPopulation();
}
// Effect on Spoiled Food
int iHealthy = goodHealth();
int iUnhealthy = badHealth();
int iFood = getYieldRate(YIELD_FOOD) - foodConsumption();
iSpoiledFood -= std::min(0, (iHealthy + iGood) - (iUnhealthy + iBad)) - std::min(0, iHealthy - iUnhealthy);
if (iSpoiledFood > 0)
{
if (iFood <= 0)
{
iStarvation += iSpoiledFood;
}
else if (iSpoiledFood > iFood)
{
iStarvation += iSpoiledFood - iFood;
}
}
else if (iSpoiledFood < 0)
{
if (iFood < 0)
{
iStarvation += std::max(iFood, iSpoiledFood);
}
}
/************************************************************************************************/
/* Afforess Start 12/20/09 */
/* */
/* */
/************************************************************************************************/
if ( kBuilding.getTechHealthChanges(NO_TECH) != 0 )
{
for (iI = 0; iI < GC.getNumTechInfos(); iI++)
{
if (GET_TEAM(getTeam()).isHasTech((TechTypes)iI))
{
addGoodOrBad(kBuilding.getTechHealthChanges(iI), iGood, iBad);
}
}
}
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (getNumActiveBuilding((BuildingTypes)iI) > 0)
{
if (GC.getBuildingInfo((BuildingTypes)iI).isReplaceBuildingClass((BuildingClassTypes)kBuilding.getBuildingClassType()))
{
addGoodOrBad(-getBuildingHealth((BuildingTypes)iI), iGood, iBad);
}
}
}
if (kBuilding.getHealthPercentPerPopulation() > 0)
{
addGoodOrBad(kBuilding.getHealthPercentPerPopulation() * getPopulation() / 100, iGood, iBad);
}
int iSpecialistExtraHealth = 0;
for (iI = 1; iI < kBuilding.getFreeSpecialist() + 1; iI++)
{
SpecialistTypes eNewSpecialist = getBestSpecialist(iI);
if (eNewSpecialist == NO_SPECIALIST) break;
CvSpecialistInfo& kSpecialist = GC.getSpecialistInfo(eNewSpecialist);
iSpecialistExtraHealth += kSpecialist.getHealthPercent();
}
iSpecialistExtraHealth /= 100;
addGoodOrBad(iSpecialistExtraHealth, iGood, iBad);
if (kBuilding.getNumPopulationEmployed() > 0)
{
int* paiCommerce = new int[NUM_COMMERCE_TYPES];
int* paiYield = new int[NUM_YIELD_TYPES];
int iGreatPeopleRate;
int iHappiness;
int iHealthiness;
removeWorstCitizenActualEffects(kBuilding.getNumPopulationEmployed(), iGreatPeopleRate, iHappiness, iHealthiness, paiYield, paiCommerce);
SAFE_DELETE_ARRAY(paiCommerce);
SAFE_DELETE_ARRAY(paiYield);
addGoodOrBad(iHealthiness, iGood, iBad);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iGood - iBad - iStarting;
}
/*
* Adds iValue to iGood if it is positive or its negative to iBad if it is negative.
*/
void addGoodOrBad(int iValue, int& iGood, int& iBad)
{
if (iValue > 0)
{
iGood += iValue;
}
else if (iValue < 0)
{
iBad -= iValue;
}
}
/*
* Subtracts iValue from iGood if it is positive or its negative from iBad if it is negative.
*/
void subtractGoodOrBad(int iValue, int& iGood, int& iBad)
{
if (iValue > 0)
{
iGood -= iValue;
}
else if (iValue < 0)
{
iBad += iValue;
}
}
// BUG - Building Additional Happiness - end
int CvCity::getExtraBuildingGoodHealth() const
{
return m_iExtraBuildingGoodHealth;
}
int CvCity::getExtraBuildingBadHealth() const
{
return m_iExtraBuildingBadHealth;
}
/********************************************************************************/
/* New Civic AI 02.08.2010 Fuyu */
/********************************************************************************/
//Fuyu bLimited
void CvCity::updateExtraBuildingHealth(bool bLimited)
{
int iNewExtraBuildingGoodHealth = 0;
int iNewExtraBuildingBadHealth = 0;
int iChange;
int iI;
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
iChange = getNumActiveBuilding((BuildingTypes)iI) * GET_PLAYER(getOwnerINLINE()).getExtraBuildingHealth((BuildingTypes)iI);
if (iChange > 0)
{
iNewExtraBuildingGoodHealth += iChange;
}
else
{
iNewExtraBuildingBadHealth += iChange;
}
}
if (getExtraBuildingGoodHealth() != iNewExtraBuildingGoodHealth)
{
m_iExtraBuildingGoodHealth = iNewExtraBuildingGoodHealth;
FAssert(getExtraBuildingGoodHealth() >= 0);
if (!bLimited)
{
AI_setAssignWorkDirty(true);
}
}
if (getExtraBuildingBadHealth() != iNewExtraBuildingBadHealth)
{
m_iExtraBuildingBadHealth = iNewExtraBuildingBadHealth;
FAssert(getExtraBuildingBadHealth() <= 0);
if (!bLimited)
{
AI_setAssignWorkDirty(true);
}
}
}
/********************************************************************************/
/* New Civic AI END */
/********************************************************************************/
int CvCity::getFeatureGoodHappiness() const
{
return m_iFeatureGoodHappiness;
}
int CvCity::getFeatureBadHappiness() const
{
return m_iFeatureBadHappiness;
}
/********************************************************************************/
/* New Civic AI 02.08.2010 Fuyu */
/********************************************************************************/
//Fuyu bLimited
void CvCity::updateFeatureHappiness(bool bLimited)
{
int iNewFeatureGoodHappiness = 0;
int iNewFeatureBadHappiness = 0;
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/17/09 */
/* */
/* */
/************************************************************************************************/
for (int iI = 0; iI < getNumCityPlots(); iI++)
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
{
CvPlot* pLoopPlot = getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
#ifdef MULTI_FEATURE_MOD
for (int j=0; j<pLoopPlot->getNumFeatures(); j++)
{
FeatureTypes eFeature = pLoopPlot->getFeatureByIndex(j);
int iHappy = GET_PLAYER(getOwnerINLINE()).getFeatureHappiness(eFeature);
if (iHappy > 0)
{
iNewFeatureGoodHappiness += iHappy;
}
else
{
iNewFeatureBadHappiness += iHappy;
}
}
#else
FeatureTypes eFeature = pLoopPlot->getFeatureType();
if (eFeature != NO_FEATURE)
{
int iHappy = GET_PLAYER(getOwnerINLINE()).getFeatureHappiness(eFeature);
if (iHappy > 0)
{
iNewFeatureGoodHappiness += iHappy;
}
else
{
iNewFeatureBadHappiness += iHappy;
}
}
#endif
ImprovementTypes eImprovement = pLoopPlot->getImprovementType();
if (NO_IMPROVEMENT != eImprovement)
{
int iHappy = GC.getImprovementInfo(eImprovement).getHappiness();
/************************************************************************************************/
/* Afforess Start 07/20/10 */
/* */
/* */
/************************************************************************************************/
CvPlayer &kPlayer = GET_PLAYER(getOwnerINLINE());
for (int iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
if (kPlayer.getCivics((CivicOptionTypes)iI) != NO_CIVIC)
{
iHappy += GC.getCivicInfo(kPlayer.getCivics((CivicOptionTypes)iI)).getImprovementHappinessChanges(eImprovement);
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (iHappy > 0)
{
iNewFeatureGoodHappiness += iHappy;
}
else
{
iNewFeatureBadHappiness += iHappy;
}
}
}
}
if (getFeatureGoodHappiness() != iNewFeatureGoodHappiness)
{
m_iFeatureGoodHappiness = iNewFeatureGoodHappiness;
FAssert(getFeatureGoodHappiness() >= 0);
if (!bLimited)
{
AI_setAssignWorkDirty(true);
}
}
if (getFeatureBadHappiness() != iNewFeatureBadHappiness)
{
m_iFeatureBadHappiness = iNewFeatureBadHappiness;
FAssert(getFeatureBadHappiness() <= 0);
if (!bLimited)
{
AI_setAssignWorkDirty(true);
}
}
}
/********************************************************************************/
/* New Civic AI END */
/********************************************************************************/
int CvCity::getBonusGoodHappiness() const
{
return m_iBonusGoodHappiness;
}
int CvCity::getBonusBadHappiness() const
{
return m_iBonusBadHappiness;
}
void CvCity::changeBonusGoodHappiness(int iChange)
{
if (iChange != 0)
{
m_iBonusGoodHappiness = (m_iBonusGoodHappiness + iChange);
FAssert(getBonusGoodHappiness() >= 0);
AI_setAssignWorkDirty(true);
}
}
void CvCity::changeBonusBadHappiness(int iChange)
{
if (iChange != 0)
{
m_iBonusBadHappiness = (m_iBonusBadHappiness + iChange);
FAssert(getBonusBadHappiness() <= 0);
AI_setAssignWorkDirty(true);
}
}
int CvCity::getReligionGoodHappiness() const
{
return m_iReligionGoodHappiness;
}
int CvCity::getReligionBadHappiness() const
{
return m_iReligionBadHappiness;
}
int CvCity::getReligionHappiness(ReligionTypes eReligion) const
{
int iHappiness;
iHappiness = 0;
if (isHasReligion(eReligion))
{
if (eReligion == GET_PLAYER(getOwnerINLINE()).getStateReligion())
{
iHappiness += GET_PLAYER(getOwnerINLINE()).getStateReligionHappiness();
}
else
{
iHappiness += GET_PLAYER(getOwnerINLINE()).getNonStateReligionHappiness();
}
}
return iHappiness;
}
/********************************************************************************/
/* New Civic AI 02.08.2010 Fuyu */
/********************************************************************************/
//Fuyu bLimited
void CvCity::updateReligionHappiness(bool bLimited)
{
int iNewReligionGoodHappiness;
int iNewReligionBadHappiness;
int iChange;
int iI;
iNewReligionGoodHappiness = 0;
iNewReligionBadHappiness = 0;
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
iChange = getReligionHappiness((ReligionTypes)iI);
if (iChange > 0)
{
iNewReligionGoodHappiness += iChange;
}
else
{
iNewReligionBadHappiness += iChange;
}
}
if (getReligionGoodHappiness() != iNewReligionGoodHappiness)
{
m_iReligionGoodHappiness = iNewReligionGoodHappiness;
FAssert(getReligionGoodHappiness() >= 0);
if (!bLimited)
{
AI_setAssignWorkDirty(true);
}
}
if (getReligionBadHappiness() != iNewReligionBadHappiness)
{
m_iReligionBadHappiness = iNewReligionBadHappiness;
FAssert(getReligionBadHappiness() <= 0);
if (!bLimited)
{
AI_setAssignWorkDirty(true);
}
}
}
/********************************************************************************/
/* New Civic AI END */
/********************************************************************************/
int CvCity::getExtraHappiness() const
{
return m_iExtraHappiness;
}
void CvCity::changeExtraHappiness(int iChange)
{
if (iChange != 0)
{
m_iExtraHappiness += iChange;
AI_setAssignWorkDirty(true);
}
}
int CvCity::getExtraHealth() const
{
return m_iExtraHealth;
}
void CvCity::changeExtraHealth(int iChange)
{
if (iChange != 0)
{
m_iExtraHealth += iChange;
AI_setAssignWorkDirty(true);
}
}
int CvCity::getHurryAngerTimer() const
{
return m_iHurryAngerTimer;
}
void CvCity::changeHurryAngerTimer(int iChange)
{
if (iChange != 0)
{
m_iHurryAngerTimer = (m_iHurryAngerTimer + iChange);
FAssert(getHurryAngerTimer() >= 0);
AI_setAssignWorkDirty(true);
}
}
/************************************************************************************************/
/* REVOLUTION_MOD 04/19/08 jdog5000 */
/* */
/* */
/************************************************************************************************/
int CvCity::getRevRequestAngerTimer() const
{
return m_iRevRequestAngerTimer;
}
void CvCity::changeRevRequestAngerTimer(int iChange)
{
if (iChange != 0)
{
m_iRevRequestAngerTimer = (m_iRevRequestAngerTimer + iChange);
FAssert(getRevRequestAngerTimer() >= 0);
AI_setAssignWorkDirty(true);
}
}
int CvCity::getRevSuccessTimer() const
{
return m_iRevSuccessTimer;
}
void CvCity::changeRevSuccessTimer(int iChange)
{
if (iChange != 0)
{
m_iRevSuccessTimer = (m_iRevSuccessTimer + iChange);
FAssert(getRevSuccessTimer() >= 0);
AI_setAssignWorkDirty(true);
}
}
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
int CvCity::getConscriptAngerTimer() const
{
return m_iConscriptAngerTimer;
}
void CvCity::changeConscriptAngerTimer(int iChange)
{
if (iChange != 0)
{
m_iConscriptAngerTimer = (m_iConscriptAngerTimer + iChange);
FAssert(getConscriptAngerTimer() >= 0);
AI_setAssignWorkDirty(true);
}
}
int CvCity::getDefyResolutionAngerTimer() const
{
return m_iDefyResolutionAngerTimer;
}
void CvCity::changeDefyResolutionAngerTimer(int iChange)
{
if (iChange != 0)
{
m_iDefyResolutionAngerTimer += iChange;
FAssert(getDefyResolutionAngerTimer() >= 0);
AI_setAssignWorkDirty(true);
}
}
int CvCity::flatDefyResolutionAngerLength() const
{
int iAnger;
iAnger = GC.getDefineINT("DEFY_RESOLUTION_ANGER_DIVISOR");
iAnger *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getHurryConscriptAngerPercent();
iAnger /= 100;
return std::max(1, iAnger);
}
int CvCity::getHappinessTimer() const
{
return m_iHappinessTimer;
}
void CvCity::changeHappinessTimer(int iChange)
{
if (iChange != 0)
{
m_iHappinessTimer += iChange;
FAssert(getHappinessTimer() >= 0);
AI_setAssignWorkDirty(true);
}
}
int CvCity::getNoUnhappinessCount() const
{
return m_iNoUnhappinessCount;
}
bool CvCity::isNoUnhappiness() const
{
return (getNoUnhappinessCount() > 0);
}
void CvCity::changeNoUnhappinessCount(int iChange)
{
if (iChange != 0)
{
m_iNoUnhappinessCount = (m_iNoUnhappinessCount + iChange);
FAssert(getNoUnhappinessCount() >= 0);
AI_setAssignWorkDirty(true);
}
}
int CvCity::getNoUnhealthyPopulationCount() const
{
return m_iNoUnhealthyPopulationCount;
}
bool CvCity::isNoUnhealthyPopulation() const
{
if (GET_PLAYER(getOwnerINLINE()).isNoUnhealthyPopulation())
{
return true;
}
return (getNoUnhealthyPopulationCount() > 0);
}
void CvCity::changeNoUnhealthyPopulationCount(int iChange)
{
if (iChange != 0)
{
m_iNoUnhealthyPopulationCount = (m_iNoUnhealthyPopulationCount + iChange);
FAssert(getNoUnhealthyPopulationCount() >= 0);
AI_setAssignWorkDirty(true);
}
}
int CvCity::getBuildingOnlyHealthyCount() const
{
return m_iBuildingOnlyHealthyCount;
}
bool CvCity::isBuildingOnlyHealthy() const
{
if (GET_PLAYER(getOwnerINLINE()).isBuildingOnlyHealthy())
{
return true;
}
return (getBuildingOnlyHealthyCount() > 0);
}
void CvCity::changeBuildingOnlyHealthyCount(int iChange)
{
if (iChange != 0)
{
m_iBuildingOnlyHealthyCount = (m_iBuildingOnlyHealthyCount + iChange);
FAssert(getBuildingOnlyHealthyCount() >= 0);
AI_setAssignWorkDirty(true);
}
}
int CvCity::getFood() const
{
return m_iFood;
}
void CvCity::setFood(int iNewValue)
{
if (getFood() != iNewValue)
{
m_iFood = iNewValue;
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
void CvCity::changeFood(int iChange)
{
setFood(getFood() + iChange);
#ifdef YIELD_VALUE_CACHING
if ( iChange != 0 )
{
// Yield calculation depends on city food stores so invalidate cache
ClearYieldValueCache();
}
#endif
}
int CvCity::getFoodKept() const
{
return m_iFoodKept;
}
void CvCity::setFoodKept(int iNewValue)
{
m_iFoodKept = iNewValue;
}
void CvCity::changeFoodKept(int iChange)
{
setFoodKept(getFoodKept() + iChange);
}
void CvCity::recalculateMaxFoodKeptPercent()
{
PROFILE_FUNC();
m_fMaxFoodKeptMultiplierLog = 0;
// Game has been restored from an old save format so we have to calculate
// from first principles
BuildingTypes eLoopBuilding;
for (int iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
eLoopBuilding = (BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iI);
if (eLoopBuilding != NO_BUILDING)
{
if (getNumBuilding(eLoopBuilding) > 0)
{
if ( GC.getBuildingInfo(eLoopBuilding).getFoodKept() != 0 )
{
changeMaxFoodKeptPercent(GC.getBuildingInfo(eLoopBuilding).getFoodKept(),true);
}
}
}
}
}
int CvCity::getMaxFoodKeptPercent() const
{
if ( m_fMaxFoodKeptMultiplierLog == INVALID_STORED_FOOD_PERCENT_LOG )
{
((CvCity*)this)->recalculateMaxFoodKeptPercent();
}
float fMultiplier = exp(m_fMaxFoodKeptMultiplierLog);
return (int)(100 - fMultiplier*100);
}
void CvCity::changeMaxFoodKeptPercent(int iChange, bool bAdd)
{
FAssert(iChange >= 0);
if ( m_fMaxFoodKeptMultiplierLog == INVALID_STORED_FOOD_PERCENT_LOG )
{
recalculateMaxFoodKeptPercent();
}
float logdiff = (bAdd ? 1 : -1)*log((100-(float)iChange)/100);
m_fMaxFoodKeptMultiplierLog += logdiff;
}
int CvCity::getOverflowProduction() const
{
return m_iOverflowProduction;
}
void CvCity::setOverflowProduction(int iNewValue)
{
m_iOverflowProduction = iNewValue;
FAssert(getOverflowProduction() >= 0);
}
void CvCity::changeOverflowProduction(int iChange, int iProductionModifier)
{
int iOverflow = (100 * iChange) / std::max(1, getBaseYieldRateModifier(YIELD_PRODUCTION, iProductionModifier));
setOverflowProduction(getOverflowProduction() + iOverflow);
}
int CvCity::getFeatureProduction() const
{
return m_iFeatureProduction;
}
void CvCity::setFeatureProduction(int iNewValue)
{
m_iFeatureProduction = iNewValue;
FAssert(getFeatureProduction() >= 0);
}
void CvCity::changeFeatureProduction(int iChange)
{
setFeatureProduction(getFeatureProduction() + iChange);
}
int CvCity::getMilitaryProductionModifier() const
{
return m_iMilitaryProductionModifier;
}
void CvCity::changeMilitaryProductionModifier(int iChange)
{
m_iMilitaryProductionModifier = (m_iMilitaryProductionModifier + iChange);
}
int CvCity::getSpaceProductionModifier() const
{
return m_iSpaceProductionModifier;
}
void CvCity::changeSpaceProductionModifier(int iChange)
{
m_iSpaceProductionModifier = (m_iSpaceProductionModifier + iChange);
}
int CvCity::getExtraTradeRoutes() const
{
return m_iExtraTradeRoutes;
}
void CvCity::changeExtraTradeRoutes(int iChange)
{
if (iChange != 0)
{
m_iExtraTradeRoutes = (m_iExtraTradeRoutes + iChange);
FAssert(getExtraTradeRoutes() >= 0);
updateTradeRoutes();
}
}
int CvCity::getTradeRouteModifier() const
{
return m_iTradeRouteModifier;
}
void CvCity::changeTradeRouteModifier(int iChange)
{
if (iChange != 0)
{
m_iTradeRouteModifier = (m_iTradeRouteModifier + iChange);
updateTradeRoutes();
}
}
int CvCity::getForeignTradeRouteModifier() const
{
return m_iForeignTradeRouteModifier;
}
void CvCity::changeForeignTradeRouteModifier(int iChange)
{
if (iChange != 0)
{
m_iForeignTradeRouteModifier = (m_iForeignTradeRouteModifier + iChange);
updateTradeRoutes();
}
}
int CvCity::getBuildingDefense() const
{
return m_iBuildingDefense;
}
void CvCity::changeBuildingDefense(int iChange)
{
if (iChange != 0)
{
m_iBuildingDefense = (m_iBuildingDefense + iChange);
setInfoDirty(true);
plot()->plotAction(PUF_makeInfoBarDirty);
}
}
int CvCity::getBuildingBombardDefense() const
{
return std::min(GC.getGameINLINE().getModderGameOption(MODDERGAMEOPTION_MAX_BOMBARD_DEFENSE), m_iBuildingBombardDefense);
}
void CvCity::changeBuildingBombardDefense(int iChange)
{
if (iChange != 0)
{
m_iBuildingBombardDefense += iChange;
FAssert(getBuildingBombardDefense() >= 0);
}
}
// BUG - Building Additional Bombard Defense - start
int CvCity::getAdditionalBombardDefenseByBuilding(BuildingTypes eBuilding) const
{
FAssertMsg(eBuilding >= 0, "eBuilding expected to be >= 0");
FAssertMsg(eBuilding < GC.getNumBuildingInfos(), "eBuilding expected to be < GC.getNumBuildingInfos()");
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
int iBaseDefense = getBuildingBombardDefense();
// cap total bombard defense at 100
return std::min(kBuilding.getBombardDefenseModifier() + iBaseDefense, 100) - iBaseDefense;
}
// BUG - Building Additional Bombard Defense - end
int CvCity::getFreeExperience() const
{
return m_iFreeExperience;
}
void CvCity::changeFreeExperience(int iChange)
{
m_iFreeExperience = (m_iFreeExperience + iChange);
FAssert(getFreeExperience() >= 0);
}
int CvCity::getCurrAirlift() const
{
return m_iCurrAirlift;
}
void CvCity::setCurrAirlift(int iNewValue)
{
m_iCurrAirlift = iNewValue;
FAssert(getCurrAirlift() >= 0);
}
void CvCity::changeCurrAirlift(int iChange)
{
setCurrAirlift(getCurrAirlift() + iChange);
}
int CvCity::getMaxAirlift() const
{
return m_iMaxAirlift;
}
void CvCity::changeMaxAirlift(int iChange)
{
m_iMaxAirlift = (m_iMaxAirlift + iChange);
FAssert(getMaxAirlift() >= 0);
}
int CvCity::getAirModifier() const
{
return m_iAirModifier;
}
void CvCity::changeAirModifier(int iChange)
{
m_iAirModifier += iChange;
}
int CvCity::getAirUnitCapacity(TeamTypes eTeam) const
{
return (getTeam() == eTeam ? m_iAirUnitCapacity : GC.getDefineINT("CITY_AIR_UNIT_CAPACITY"));
}
int CvCity::getWonderCapacityIncrement() const
{
return m_iWonderCapacityIncrement;
}
void CvCity::changeAirUnitCapacity(int iChange)
{
m_iAirUnitCapacity += iChange;
FAssert(getAirUnitCapacity(getTeam()) >= 0);
}
void CvCity::changeWonderCapacityIncrement(int iChange)
{
m_iWonderCapacityIncrement += iChange;
FAssert(getWonderCapacityIncrement() >= 0);
}
int CvCity::getNukeModifier() const
{
return m_iNukeModifier;
}
void CvCity::changeNukeModifier(int iChange)
{
m_iNukeModifier = (m_iNukeModifier + iChange);
}
int CvCity::getFreeSpecialist() const
{
return m_iFreeSpecialist;
}
void CvCity::changeFreeSpecialist(int iChange)
{
if (iChange != 0)
{
m_iFreeSpecialist = (m_iFreeSpecialist + iChange);
FAssert(getFreeSpecialist() >= 0);
AI_setAssignWorkDirty(true);
}
}
int CvCity::getPowerCount() const
{
return m_iPowerCount;
}
bool CvCity::isPower() const
{
/************************************************************************************************/
/* Afforess Start 06/29/10 */
/* */
/* */
/************************************************************************************************/
if (getDisabledPowerTimer() > 0) return false;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return ((getPowerCount() > 0) || isAreaCleanPower());
}
bool CvCity::isAreaCleanPower() const
{
if (area() == NULL)
{
return false;
}
return area()->isCleanPower(getTeam());
}
bool CvCity::isAreaSaturatedOfLandMilitaryUnits()
{
// xUPT: (dbkblk, 2015-02)
if (GC.getGameINLINE().getModderGameOption(MODDERGAMEOPTION_MAX_UNITS_PER_TILES) == 0){
// Option disabled
return false;
}
int iI;
CvPlot* pLoopPlot;
PlayerTypes owner = this->getOwner();
// Loop through all the plots of the same area (separate conditions for faster checking)
for (iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
{
pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
if (pLoopPlot->getWorkingCity() == this) // Check if the plot is in the city area
{
if (pLoopPlot->area() == this->area() && !pLoopPlot->isPeak()) // Check if the plot is on the same continent and is not a peak
{
if (!pLoopPlot->isAtMaxLandMilitaryUnitsPerTiles()){ // Cut the checking if there is at least one tile free
return false;
}
}
}
}
return true;
}
bool CvCity::isAreaSaturatedOfCivilianUnits()
{
// xUPT: (dbkblk, 2015-02)
if (GC.getGameINLINE().getModderGameOption(MODDERGAMEOPTION_MAX_UNITS_PER_TILES) == 0){
// Option disabled
return false;
}
int iI;
CvPlot* pLoopPlot;
PlayerTypes owner = this->getOwner();
// Loop through all the plots of the same area (separate conditions for faster checking)
for (iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
{
pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
if (pLoopPlot->getWorkingCity() == this) // Check if the plot is in the city area
{
if (pLoopPlot->area() == this->area() && !pLoopPlot->isPeak()) // Check if the plot is on the same continent and is not a peak
{
if (!pLoopPlot->isAtMaxCivilianUnitsPerTiles()){ // Cut the checking if there is at least one tile free
return false;
}
}
}
}
return true;
}
int CvCity::getDirtyPowerCount() const
{
return m_iDirtyPowerCount;
}
bool CvCity::isDirtyPower() const
{
return (isPower() && (getDirtyPowerCount() == getPowerCount()) && !isAreaCleanPower());
}
void CvCity::changePowerCount(int iChange, bool bDirty)
{
bool bOldPower;
bool bOldDirtyPower;
if (iChange != 0)
{
bOldPower = isPower();
bOldDirtyPower = isDirtyPower();
m_iPowerCount = (m_iPowerCount + iChange);
FAssert(getPowerCount() >= 0);
if (bDirty)
{
m_iDirtyPowerCount += iChange;
FAssert(getDirtyPowerCount() >= 0);
}
if (bOldPower != isPower())
{
GET_PLAYER(getOwnerINLINE()).invalidateYieldRankCache();
setCommerceDirty();
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
if (bOldDirtyPower != isDirtyPower() || bOldPower != isPower())
{
updatePowerHealth();
}
}
}
int CvCity::getDefenseDamage() const
{
return m_iDefenseDamage;
}
void CvCity::changeDefenseDamage(int iChange)
{
if (iChange != 0)
{
m_iDefenseDamage = range((m_iDefenseDamage + iChange), 0, GC.getMAX_CITY_DEFENSE_DAMAGE());
if (iChange > 0)
{
setBombarded(true);
}
setInfoDirty(true);
plot()->plotAction(PUF_makeInfoBarDirty);
}
}
void CvCity::changeDefenseModifier(int iChange)
{
if (iChange != 0)
{
int iTotalDefense = getTotalDefense(false);
if (iTotalDefense > 0)
{
changeDefenseDamage(-(GC.getMAX_CITY_DEFENSE_DAMAGE() * iChange + (iChange > 0 ? iTotalDefense : -iTotalDefense)/2) / iTotalDefense);
}
}
}
int CvCity::getLastDefenseDamage() const
{
return m_iLastDefenseDamage;
}
void CvCity::setLastDefenseDamage(int iNewValue)
{
m_iLastDefenseDamage = iNewValue;
}
bool CvCity::isBombardable(const CvUnit* pUnit) const
{
if (NULL != pUnit && !pUnit->isEnemy(getTeam()))
{
return false;
}
return (getDefenseModifier(false) > 0);
}
int CvCity::getNaturalDefense() const
{
if (getCultureLevel() == NO_CULTURELEVEL)
{
return 0;
}
return GC.getCultureLevelInfo(getCultureLevel()).getCityDefenseModifier();
}
int CvCity::getTotalDefense(bool bIgnoreBuilding) const
{
return (std::max(((bIgnoreBuilding) ? 0 : getBuildingDefense()), getNaturalDefense()) + GET_PLAYER(getOwnerINLINE()).getCityDefenseModifier() + calculateBonusDefense());
}
int CvCity::getDefenseModifier(bool bIgnoreBuilding) const
{
if (isOccupation())
{
return 0;
}
return ((getTotalDefense(bIgnoreBuilding) * (GC.getMAX_CITY_DEFENSE_DAMAGE() - getDefenseDamage())) / GC.getMAX_CITY_DEFENSE_DAMAGE());
}
int CvCity::getOccupationTimer() const
{
return m_iOccupationTimer;
}
bool CvCity::isOccupation() const
{
return (getOccupationTimer() > 0);
}
void CvCity::setOccupationTimer(int iNewValue)
{
bool bOldOccupation;
if (getOccupationTimer() != iNewValue)
{
bOldOccupation = isOccupation();
m_iOccupationTimer = iNewValue;
FAssert(getOccupationTimer() >= 0);
if (bOldOccupation != isOccupation())
{
updateCorporation();
setMaintenanceDirty(true);
updateTradeRoutes();
updateCultureLevel(true);
AI_setAssignWorkDirty(true);
}
setInfoDirty(true);
}
}
void CvCity::changeOccupationTimer(int iChange)
{
setOccupationTimer(getOccupationTimer() + iChange);
}
int CvCity::getCultureUpdateTimer() const
{
return m_iCultureUpdateTimer;
}
void CvCity::setCultureUpdateTimer(int iNewValue)
{
m_iCultureUpdateTimer = iNewValue;
FAssert(getOccupationTimer() >= 0);
}
void CvCity::changeCultureUpdateTimer(int iChange)
{
setCultureUpdateTimer(getCultureUpdateTimer() + iChange);
}
int CvCity::getCitySizeBoost() const
{
return m_iCitySizeBoost;
}
void CvCity::setCitySizeBoost(int iBoost)
{
if (getCitySizeBoost() != iBoost)
{
m_iCitySizeBoost = iBoost;
setLayoutDirty(true);
}
}
// < M.A.D. Nukes Start >
int CvCity::getMADIncoming()
{
return m_iMADIncoming;
}
void CvCity::setMADIncoming(int iValue)
{
m_iMADIncoming = iValue;
}
void CvCity::changeMADIncoming(int iValue)
{
m_iMADIncoming += iValue;
}
// < M.A.D. Nukes End >
bool CvCity::isNeverLost() const
{
return m_bNeverLost;
}
void CvCity::setNeverLost(bool bNewValue)
{
m_bNeverLost = bNewValue;
}
bool CvCity::isBombarded() const
{
return m_bBombarded;
}
void CvCity::setBombarded(bool bNewValue)
{
m_bBombarded = bNewValue;
}
bool CvCity::isDrafted() const
{
return m_bDrafted;
}
void CvCity::setDrafted(bool bNewValue)
{
m_bDrafted = bNewValue;
}
bool CvCity::isAirliftTargeted() const
{
return m_bAirliftTargeted;
}
void CvCity::setAirliftTargeted(bool bNewValue)
{
m_bAirliftTargeted = bNewValue;
}
bool CvCity::isPlundered() const
{
return m_bPlundered;
}
void CvCity::setPlundered(bool bNewValue)
{
if (bNewValue != isPlundered())
{
m_bPlundered = bNewValue;
updateTradeRoutes();
//Afforess: connectedness
doConnectednessCalculations();
}
}
bool CvCity::isWeLoveTheKingDay() const
{
return m_bWeLoveTheKingDay;
}
void CvCity::setWeLoveTheKingDay(bool bNewValue)
{
CvWString szBuffer;
CivicTypes eCivic;
int iI;
if (isWeLoveTheKingDay() != bNewValue)
{
m_bWeLoveTheKingDay = bNewValue;
setMaintenanceDirty(true);
eCivic = NO_CIVIC;
for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
{
if (GET_PLAYER(getOwnerINLINE()).isCivic((CivicTypes)iI))
{
if (!CvWString(GC.getCivicInfo((CivicTypes)iI).getWeLoveTheKing()).empty())
{
eCivic = ((CivicTypes)iI);
break;
}
}
}
if (eCivic != NO_CIVIC)
{
MEMORY_TRACK_EXEMPT();
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_CITY_CELEBRATE", getNameKey(), GC.getCivicInfo(eCivic).getWeLoveTheKing()));
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_WELOVEKING", MESSAGE_TYPE_MINOR_EVENT, ARTFILEMGR.getInterfaceArtInfo("INTERFACE_HAPPY_PERSON")->getPath(), (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), getX_INLINE(), getY_INLINE(), true, true);
}
}
}
bool CvCity::isCitizensAutomated() const
{
return m_bCitizensAutomated;
}
void CvCity::setCitizensAutomated(bool bNewValue)
{
int iI;
if (isCitizensAutomated() != bNewValue)
{
m_bCitizensAutomated = bNewValue;
if (isCitizensAutomated())
{
AI_assignWorkingPlots();
}
else
{
for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
{
setForceSpecialistCount(((SpecialistTypes)iI), 0);
}
}
if ((getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) && isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
}
}
}
bool CvCity::isProductionAutomated() const
{
return m_bProductionAutomated;
}
void CvCity::setProductionAutomated(bool bNewValue, bool bClear)
{
if (isProductionAutomated() != bNewValue)
{
m_bProductionAutomated = bNewValue;
if ((getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) && isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
// if automated and not network game and all 3 modifiers down, clear the queue and choose again
if (bNewValue && bClear)
{
clearOrderQueue();
}
}
if (!isProduction())
{
AI_chooseProduction();
}
}
}
bool CvCity::isWallOverride() const
{
return m_bWallOverride;
}
void CvCity::setWallOverride(bool bOverride)
{
if (isWallOverride() != bOverride)
{
m_bWallOverride = bOverride;
setLayoutDirty(true);
}
}
bool CvCity::isInfoDirty() const
{
return m_bInfoDirty;
}
void CvCity::setInfoDirty(bool bNewValue)
{
m_bInfoDirty = bNewValue;
}
bool CvCity::isLayoutDirty() const
{
return m_bLayoutDirty;
}
void CvCity::setLayoutDirty(bool bNewValue)
{
m_bLayoutDirty = bNewValue;
}
PlayerTypes CvCity::getOwner() const
{
return getOwnerINLINE();
}
PlayerTypes CvCity::getPreviousOwner() const
{
return m_ePreviousOwner;
}
void CvCity::setPreviousOwner(PlayerTypes eNewValue)
{
m_ePreviousOwner = eNewValue;
}
PlayerTypes CvCity::getOriginalOwner() const
{
return m_eOriginalOwner;
}
void CvCity::setOriginalOwner(PlayerTypes eNewValue)
{
m_eOriginalOwner = eNewValue;
}
TeamTypes CvCity::getTeam() const
{
return GET_PLAYER(getOwnerINLINE()).getTeam();
}
CultureLevelTypes CvCity::getCultureLevel() const
{
return m_eCultureLevel;
}
int CvCity::getCultureThreshold() const
{
return getCultureThreshold(getCultureLevel());
}
int CvCity::getCultureThreshold(CultureLevelTypes eLevel) const
{
if (eLevel == NO_CULTURELEVEL)
{
return 1;
}
/************************************************************************************************/
/* Afforess Start 07/22/10 */
/* */
/* */
/************************************************************************************************/
/*
return std::max(1, GC.getGameINLINE().getCultureThreshold((CultureLevelTypes)(std::min((eLevel + 1), (GC.getNumCultureLevelInfos() - 1)))));
*/
CultureLevelTypes eCultureLevel;
eCultureLevel = ((CultureLevelTypes)(GC.getNumCultureLevelInfos() - 1));
for (int iI = 0; iI < GC.getNumCultureLevelInfos(); iI++)
{
if (GC.getCultureLevelInfo((CultureLevelTypes)iI).getPrereqGameOption() == NO_GAMEOPTION || GC.getGameINLINE().isOption((GameOptionTypes)GC.getCultureLevelInfo((CultureLevelTypes)iI).getPrereqGameOption()))
{
if (getCultureTimes100(getOwnerINLINE())/100 < GC.getGameINLINE().getCultureThreshold((CultureLevelTypes)iI))
{
eCultureLevel = ((CultureLevelTypes)iI);
break;
}
}
}
return std::max(1, GC.getGameINLINE().getCultureThreshold(eCultureLevel));
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
void CvCity::setCultureLevel(CultureLevelTypes eNewValue, bool bUpdatePlotGroups)
{
PROFILE_FUNC();
CvPlot* pLoopPlot;
CvWString szBuffer;
CultureLevelTypes eOldValue;
int iCultureRange;
int iDX, iDY;
int iI;
eOldValue = getCultureLevel();
if (eOldValue != eNewValue)
{
//Afforess start
if (eNewValue != NO_CULTURELEVEL)
{
recalculateZoomLevel(eNewValue);
}
// Afforess end
m_eCultureLevel = eNewValue;
// Culture level chnage can change our radius so that requires
// recalculation of best builds
AI_markBestBuildValuesStale();
if (eOldValue != NO_CULTURELEVEL)
{
for (iDX = -eOldValue; iDX <= eOldValue; iDX++)
{
for (iDY = -eOldValue; iDY <= eOldValue; iDY++)
{
iCultureRange = cultureDistance(iDX, iDY, true);
if (iCultureRange > getCultureLevel())
{
if (iCultureRange <= eOldValue)
{
FAssert(iCultureRange <= GC.getNumCultureLevelInfos());
pLoopPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
pLoopPlot->changeCultureRangeCities(getOwnerINLINE(), iCultureRange, -1, bUpdatePlotGroups);
}
}
}
}
}
}
/************************************************************************************************/
/* phunny_pharmer Start 04/21/10 */
/* ensure that the scaling back of cultural distances occurs before caching */
/* note that the first time the city is built, no caching is necessary since the above loop */
/* will not be entered (the old value is equal to NO_CULTURELEVEL) */
/* however, on all subsequent calls, the cultureDistance function refers to the culture */
/* level m_eCultureLevel, so that cannot be changed until after the function completes */
/************************************************************************************************/
m_eCultureLevel = eNewValue;
clearCultureDistanceCache();
/************************************************************************************************/
/* phunny_pharmer END */
/************************************************************************************************/
if (getCultureLevel() != NO_CULTURELEVEL)
{
for (iDX = -getCultureLevel(); iDX <= getCultureLevel(); iDX++)
{
for (iDY = -getCultureLevel(); iDY <= getCultureLevel(); iDY++)
{
iCultureRange = cultureDistance(iDX, iDY, true);
if (iCultureRange > eOldValue)
{
if (iCultureRange <= getCultureLevel())
{
FAssert(iCultureRange <= GC.getNumCultureLevelInfos());
pLoopPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
pLoopPlot->changeCultureRangeCities(getOwnerINLINE(), iCultureRange, 1, bUpdatePlotGroups);
}
}
}
}
}
}
if (GC.getGameINLINE().isFinalInitialized())
{
if ((getCultureLevel() > eOldValue) && (getCultureLevel() > 1))
{
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_BORDERS_EXPANDED", getNameKey()));
{
MEMORY_TRACK_EXEMPT();
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_CULTUREEXPANDS", MESSAGE_TYPE_MINOR_EVENT, GC.getCommerceInfo(COMMERCE_CULTURE).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), getX_INLINE(), getY_INLINE(), true, true);
}
/************************************************************************************************/
/* Afforess Start 01/20/10 */
/* */
/* Update Health and Happiness when culture expands */
/************************************************************************************************/
updateFeatureHappiness();
updateFeatureHealth();
updateImprovementHealth();
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (getCultureLevel() == (GC.getNumCultureLevelInfos() - 1))
{
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
MEMORY_TRACK_EXEMPT();
if (isRevealed(GET_PLAYER((PlayerTypes)iI).getTeam(), false))
{
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_CULTURE_LEVEL", getNameKey(), GC.getCultureLevelInfo(getCultureLevel()).getTextKeyWide()));
AddDLLMessage(((PlayerTypes)iI), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_CULTURELEVEL", MESSAGE_TYPE_MAJOR_EVENT, GC.getCommerceInfo(COMMERCE_CULTURE).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_HIGHLIGHT_TEXT"), getX_INLINE(), getY_INLINE(), true, true);
}
else
{
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_CULTURE_LEVEL_UNKNOWN", GC.getCultureLevelInfo(getCultureLevel()).getTextKeyWide()));
AddDLLMessage(((PlayerTypes)iI), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_CULTURELEVEL", MESSAGE_TYPE_MAJOR_EVENT, GC.getCommerceInfo(COMMERCE_CULTURE).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_HIGHLIGHT_TEXT"));
}
}
}
}
// ONEVENT - Culture growth
CvEventReporter::getInstance().cultureExpansion(this, getOwnerINLINE());
//Stop Build Culture
/************************************************************************************************/
/* UNOFFICIAL_PATCH 12/07/09 denev & jdog5000 */
/* */
/* Bugfix, Odd behavior */
/************************************************************************************************/
/* original BTS code
if (isProductionProcess())
{
if (GC.getProcessInfo(getProductionProcess()).getProductionToCommerceModifier(COMMERCE_CULTURE) > 0)
{
popOrder(0, false, true);
}
}
*/
// For AI this is completely unnecessary. Timing also appears to cause bug with overflow production,
// giving extra hammers inappropriately.
if( isHuman() && !isProductionAutomated() )
{
if (isProductionProcess())
{
if (GC.getProcessInfo(getProductionProcess()).getProductionToCommerceModifier(COMMERCE_CULTURE) > 0)
{
//popOrder(0, false, true);
m_bPopProductionProcess = true;
}
}
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
}
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 08/08/10 EmperorFool */
/* */
/* Bugfix, already called by AI_doTurn() */
/************************************************************************************************/
AI_updateBestBuild();
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
}
}
void CvCity::updateCultureLevel(bool bUpdatePlotGroups)
{
if (getCultureUpdateTimer() > 0)
{
return;
}
CultureLevelTypes eCultureLevel = ((CultureLevelTypes)0);
/************************************************************************************************/
/* REVOLUTION_MOD 02/08/08 jdog5000 */
/* */
/* */
/************************************************************************************************/
// City borders don't collapse during a revolt, only during occupation right after capture
/* original BTS
if (!isOccupation())
*/
int iMaxOccupationTimer = GC.getDefineINT("BASE_OCCUPATION_TURNS") + ((getHighestPopulation() * GC.getDefineINT("OCCUPATION_TURNS_POPULATION_PERCENT")) / 100);
if( !isOccupation() || ((!GC.getGameINLINE().isOption(GAMEOPTION_NO_REVOLUTION) && (GC.getGameINLINE().getGameTurn() - getGameTurnAcquired()) > iMaxOccupationTimer)) )
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
{
for (int iI = (GC.getNumCultureLevelInfos() - 1); iI > 0; iI--)
{
/************************************************************************************************/
/* Afforess Start 07/22/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getCultureLevelInfo((CultureLevelTypes)iI).getPrereqGameOption() == NO_GAMEOPTION || GC.getGameINLINE().isOption((GameOptionTypes)GC.getCultureLevelInfo((CultureLevelTypes)iI).getPrereqGameOption()))
{
if (getCultureTimes100(getOwnerINLINE())/100 >= GC.getGameINLINE().getCultureThreshold((CultureLevelTypes)iI))
{
eCultureLevel = ((CultureLevelTypes)iI);
break;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
}
setCultureLevel(eCultureLevel, bUpdatePlotGroups);
}
int CvCity::getSeaPlotYield(YieldTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
//TB Debug
//Somehow we are getting under 0 values here and that could cause problems down the road
//This is definately an xml value caused by the current settings on the Garbage Dock and as a result should actually be a fairly safe fix in this case.
//This method enforces minimum of 0 without changing the actual value of m_aiSeaPlotYield[eIndex] as the integrity of that value should be maintained.
int iValue = 0;
if (m_aiSeaPlotYield[eIndex] < 0)
{
iValue = 0;
}
else
{
iValue = m_aiSeaPlotYield[eIndex];
}
return iValue;
}
void CvCity::changeSeaPlotYield(YieldTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
if (iChange != 0)
{
m_aiSeaPlotYield[eIndex] = (m_aiSeaPlotYield[eIndex] + iChange);
FAssert(getSeaPlotYield(eIndex) >= 0);
updateYield();
}
}
int CvCity::getRiverPlotYield(YieldTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
return m_aiRiverPlotYield[eIndex];
}
void CvCity::changeRiverPlotYield(YieldTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
if (iChange != 0)
{
m_aiRiverPlotYield[eIndex] += iChange;
updateYield();
}
}
// BUG - Building Additional Yield - start
/*
* Returns the total additional yield that adding one of the given buildings will provide.
*
* Doesn't check if the building can be constructed in this city.
*/
int CvCity::getAdditionalYieldByBuilding(YieldTypes eIndex, BuildingTypes eBuilding)
{
int iRate = getBaseYieldRate(eIndex);
int iModifier = getBaseYieldRateModifier(eIndex);
int iExtra = ((iRate + getAdditionalBaseYieldRateByBuilding(eIndex, eBuilding)) * (iModifier + getAdditionalYieldRateModifierByBuilding(eIndex, eBuilding)) / 100) - (iRate * iModifier / 100);
/************************************************************************************************/
/* Afforess Start 4/22/10 */
/* */
/* */
/************************************************************************************************/
for (int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (getNumActiveBuilding((BuildingTypes)iI) > 0)
{
if (GC.getBuildingInfo((BuildingTypes)iI).isReplaceBuildingClass((BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType()))
{
iExtra -= getAdditionalYieldByBuilding(eIndex, (BuildingTypes)iI);
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iExtra;
}
/*
* Returns the additional yield rate that adding one of the given buildings will provide.
*
* Doesn't check if the building can be constructed in this city.
*/
int CvCity::getAdditionalBaseYieldRateByBuilding(YieldTypes eIndex, BuildingTypes eBuilding)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
FAssertMsg(eBuilding >= 0, "eBuilding expected to be >= 0");
FAssertMsg(eBuilding < GC.getNumBuildingInfos(), "eBuilding expected to be < GC.getNumBuildingInfos()");
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
bool bObsolete = GET_TEAM(getTeam()).isObsoleteBuilding(eBuilding);
int iExtraRate = 0;
if (!bObsolete)
{
if (kBuilding.getSeaPlotYieldChange(eIndex) != 0)
{
int iChange = kBuilding.getSeaPlotYieldChange(eIndex);
for (int iI = 0; iI < NUM_CITY_PLOTS; ++iI)
{
if (isWorkingPlot(iI) && getCityIndexPlot(iI)->isWater())
{
iExtraRate += iChange;
}
}
}
if (kBuilding.getRiverPlotYieldChange(eIndex) != 0)
{
int iChange = kBuilding.getRiverPlotYieldChange(eIndex);
for (int iI = 0; iI < NUM_CITY_PLOTS; ++iI)
{
if (isWorkingPlot(iI) && getCityIndexPlot(iI)->isRiver())
{
iExtraRate += iChange;
}
}
}
iExtraRate += kBuilding.getYieldChange(eIndex);
iExtraRate += getBuildingYieldChange((BuildingClassTypes)kBuilding.getBuildingClassType(), eIndex);
// Trade
int iPlayerTradeYieldModifier = GET_PLAYER(getOwnerINLINE()).getTradeYieldModifier(eIndex);
if (iPlayerTradeYieldModifier > 0 && (kBuilding.getTradeRouteModifier() != 0 || kBuilding.getForeignTradeRouteModifier() != 0))
{
int iTotalTradeYield = 0;
int iNewTotalTradeYield = 0;
// BUG - Fractional Trade Routes - start
#ifdef _MOD_FRACTRADE
int iTradeProfitDivisor = 100;
#else
int iTradeProfitDivisor = 10000;
#endif
// BUG - Fractional Trade Routes - end
for (int iI = 0; iI < getTradeRoutes(); ++iI)
{
CvCity* pCity = getTradeCity(iI);
if (pCity)
{
int iTradeProfit = getBaseTradeProfit(pCity);
int iTradeModifier = totalTradeModifier(pCity);
int iTradeYield = iTradeProfit * iTradeModifier / iTradeProfitDivisor * iPlayerTradeYieldModifier / 100;
iTotalTradeYield += iTradeYield;
iTradeModifier += kBuilding.getTradeRouteModifier();
if (pCity->getOwnerINLINE() != getOwnerINLINE())
{
iTradeModifier += kBuilding.getForeignTradeRouteModifier();
}
int iNewTradeYield = iTradeProfit * iTradeModifier / iTradeProfitDivisor * iPlayerTradeYieldModifier / 100;
iNewTotalTradeYield += iNewTradeYield;
}
}
// BUG - Fractional Trade Routes - start
#ifdef _MOD_FRACTRADE
iTotalTradeYield /= 100;
iNewTotalTradeYield /= 100;
#endif
// BUG - Fractional Trade Routes - end
iExtraRate += iNewTotalTradeYield - iTotalTradeYield;
}
// Specialists
for (int iI = 0; iI < GC.getNumSpecialistInfos(); ++iI)
{
if (kBuilding.getFreeSpecialistCount((SpecialistTypes)iI) != 0)
{
iExtraRate += getAdditionalBaseYieldRateBySpecialist(eIndex, (SpecialistTypes)iI, kBuilding.getFreeSpecialistCount((SpecialistTypes)iI));
}
}
/************************************************************************************************/
/* Afforess Start 12/20/09 */
/* */
/* */
/************************************************************************************************/
iExtraRate += getConnectednessYield(eBuilding, eIndex, true);
for (int iI = 0; iI < GC.getNumBonusInfos(); ++iI)
{
if (hasBonus((BonusTypes)iI))
{
iExtraRate += kBuilding.getBonusYieldChanges(iI, eIndex);
}
if (kBuilding.getVicinityBonusYieldChanges(iI, eIndex) != 0 && hasVicinityBonus((BonusTypes)iI))
{
iExtraRate += kBuilding.getVicinityBonusYieldChanges(iI, eIndex);
}
}
for (int iI = 0; iI < GC.getNumTechInfos(); iI++)
{
if (GET_TEAM(getTeam()).isHasTech((TechTypes)iI))
{
iExtraRate += kBuilding.getTechYieldChange(iI, eIndex);
}
}
if ((kBuilding.getGlobalTradeRoutes() + kBuilding.getCoastalTradeRoutes() + kBuilding.getTradeRoutes()) != 0)
{
int* paiTradeYields;
paiTradeYields = new int[NUM_YIELD_TYPES];
calculateExtraTradeRouteProfit((kBuilding.getGlobalTradeRoutes() + kBuilding.getCoastalTradeRoutes() + kBuilding.getTradeRoutes()), paiTradeYields);
iExtraRate += paiTradeYields[eIndex];
SAFE_DELETE_ARRAY(paiTradeYields);
}
int iFreeSpecialistYield = 0;
for (int iI = 1; iI < kBuilding.getFreeSpecialist() + 1; iI++)
{
SpecialistTypes eNewSpecialist = getBestSpecialist(iI);
if (eNewSpecialist == NO_SPECIALIST) break;
iFreeSpecialistYield += GET_PLAYER(getOwnerINLINE()).specialistYield(eNewSpecialist, eIndex);
}
iExtraRate += iFreeSpecialistYield;
if (kBuilding.getNumPopulationEmployed() > 0)
{
int* paiCommerce = new int[NUM_COMMERCE_TYPES];
int* paiYield = new int[NUM_YIELD_TYPES];
int iGreatPeopleRate;
int iHappiness;
int iHealthiness;
removeWorstCitizenActualEffects(kBuilding.getNumPopulationEmployed(), iGreatPeopleRate, iHappiness, iHealthiness, paiYield, paiCommerce);
iExtraRate += paiYield[eIndex];
SAFE_DELETE_ARRAY(paiCommerce);
SAFE_DELETE_ARRAY(paiYield);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
return iExtraRate;
}
/*
* Returns the additional yield rate modifier that adding one of the given buildings will provide.
*
* Doesn't check if the building can be constructed in this city.
*/
int CvCity::getAdditionalYieldRateModifierByBuilding(YieldTypes eIndex, BuildingTypes eBuilding) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
FAssertMsg(eBuilding >= 0, "eBuilding expected to be >= 0");
FAssertMsg(eBuilding < GC.getNumBuildingInfos(), "eBuilding expected to be < GC.getNumBuildingInfos()");
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
bool bObsolete = GET_TEAM(getTeam()).isObsoleteBuilding(eBuilding);
int iExtraModifier = 0;
if (!bObsolete)
{
iExtraModifier += kBuilding.getYieldModifier(eIndex);
if (isPower())
{
iExtraModifier += kBuilding.getPowerYieldModifier(eIndex);
}
else
{
if (kBuilding.isPower() || kBuilding.isAreaCleanPower() || (kBuilding.getPowerBonus() != NO_BONUS && hasBonus((BonusTypes)kBuilding.getPowerBonus())))
{
for (int i = 0; i < GC.getNumBuildingInfos(); i++)
{
iExtraModifier += getNumActiveBuilding((BuildingTypes)i) * GC.getBuildingInfo((BuildingTypes)i).getPowerYieldModifier(eIndex);
}
}
}
if (eIndex == YIELD_PRODUCTION)
{
iExtraModifier += kBuilding.getMilitaryProductionModifier() / 2; // AIAndy: It does not make sense to count the production increases for specific domains fully
iExtraModifier += kBuilding.getSpaceProductionModifier() / 4;
iExtraModifier += kBuilding.getGlobalSpaceProductionModifier() / 2;
int iMaxModifier = 0;
for (int i = 0; i < NUM_DOMAIN_TYPES; i++)
{
iMaxModifier = std::max(iMaxModifier, kBuilding.getDomainProductionModifier((DomainTypes)i));
}
iExtraModifier += iMaxModifier / 4;
}
for (int iI = 0; iI < GC.getNumBonusInfos(); ++iI)
{
if (hasBonus((BonusTypes)iI))
{
iExtraModifier += kBuilding.getBonusYieldModifier(iI, eIndex);
}
}
/************************************************************************************************/
/* Afforess Start 12/20/09 */
/* */
/* */
/************************************************************************************************/
for (int iI = 0; iI < GC.getNumTechInfos(); iI++)
{
if (GET_TEAM(getTeam()).isHasTech((TechTypes)iI))
{
iExtraModifier += kBuilding.getTechYieldModifier(iI, eIndex);
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
return iExtraModifier;
}
// BUG - Building Additional Yield - end
// BUG - Specialist Additional Yield - start
/*
* Returns the total additional yield that changing the number of given specialists will provide/remove.
*/
int CvCity::getAdditionalYieldBySpecialist(YieldTypes eIndex, SpecialistTypes eSpecialist, int iChange) const
{
int iRate = getBaseYieldRate(eIndex);
int iModifier = getBaseYieldRateModifier(eIndex);
int iExtra = ((iRate + getAdditionalBaseYieldRateBySpecialist(eIndex, eSpecialist, iChange)) * iModifier / 100) - (iRate * iModifier / 100);
return iExtra;
}
/*
* Returns the additional yield rate that changing the number of given specialists will provide/remove.
*/
int CvCity::getAdditionalBaseYieldRateBySpecialist(YieldTypes eIndex, SpecialistTypes eSpecialist, int iChange) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
FAssertMsg(eSpecialist >= 0, "eSpecialist expected to be >= 0");
FAssertMsg(eSpecialist < GC.getNumSpecialistInfos(), "eSpecialist expected to be < GC.getNumSpecialistInfos()");
CvSpecialistInfo& kSpecialist = GC.getSpecialistInfo(eSpecialist);
/************************************************************************************************/
/* Afforess Start 07/20/10 */
/* */
/* */
/************************************************************************************************/
/*
return iChange * (kSpecialist.getYieldChange(eIndex) + GET_PLAYER(getOwnerINLINE()).getSpecialistExtraYield(eSpecialist, eIndex));
*/
//TB Traits begin
return iChange * (kSpecialist.getYieldChange(eIndex) + GET_PLAYER(getOwnerINLINE()).getExtraSpecialistYield(eSpecialist, eIndex) + (GET_PLAYER(getOwnerINLINE()).getSpecialistYieldPercentChanges(eSpecialist, eIndex) / 100));
//TB Traits end
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
// BUG - Specialist Additional Yield - end
int CvCity::getBaseYieldRate(YieldTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
return m_aiBaseYieldRate[eIndex];
}
int CvCity::getBaseYieldRateModifier(YieldTypes eIndex, int iExtra) const
{
int iModifier;
iModifier = getYieldRateModifier(eIndex);
iModifier += getBonusYieldRateModifier(eIndex);
if (isPower())
{
iModifier += getPowerYieldRateModifier(eIndex);
}
if (area() != NULL)
{
iModifier += area()->getYieldRateModifier(getOwnerINLINE(), eIndex);
}
iModifier += GET_PLAYER(getOwnerINLINE()).getYieldRateModifier(eIndex);
if (isCapital())
{
iModifier += GET_PLAYER(getOwnerINLINE()).getCapitalYieldRateModifier(eIndex);
}
/************************************************************************************************/
/* Afforess Start 01/25/10 */
/* */
/* */
/************************************************************************************************/
iModifier += calculateBuildingYieldModifier(eIndex);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iModifier += iExtra;
// note: player->invalidateYieldRankCache() must be called for anything that is checked here
// so if any extra checked things are added here, the cache needs to be invalidated
return std::max(0, (iModifier + 100));
}
//Used for python city screen text
int CvCity::getConnectednessYield(BuildingTypes eBuilding, YieldTypes eYield, bool bIncludeExtraCapitalCommerce)
{
int iYield = 0;
if (eYield == YIELD_COMMERCE)
{
if (bIncludeExtraCapitalCommerce)
{
if (GC.getBuildingInfo(eBuilding).getExtraCapitalCommerce() != 0)
{
if (isCapital() || isConnectedToCapital())
{
iYield += GC.getBuildingInfo(eBuilding).getExtraCapitalCommerce();
}
}
if (GC.getBuildingInfo(eBuilding).getExtraForeignCapitalCommerce() != 0)
{
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (iI != getOwnerINLINE() && GET_PLAYER((PlayerTypes)iI).isAlive() && GET_PLAYER((PlayerTypes)iI).getCapitalCity() != NULL)
{
if (isConnectedTo(GET_PLAYER((PlayerTypes)iI).getCapitalCity()))
{
iYield += GC.getBuildingInfo(eBuilding).getExtraForeignCapitalCommerce();
}
}
}
}
}
if (GC.getBuildingInfo(eBuilding).getDomesticConnectedCommerce() > 0)
{
int iCommerceAmt = GC.getBuildingInfo(eBuilding).getDomesticConnectedCommerce();
int iMaxCommerce = GC.getBuildingInfo(eBuilding).getMaxDomesticConnectedCommerce();
if (iMaxCommerce <= 0) iMaxCommerce = MAX_INT;
int iCitiesRequiredPerCommerce = GC.getBuildingInfo(eBuilding).getCitiesRequiredPerDomesticConnectedCommerce();
//Scale for mapsize, if iCitiesRequiredPerCommerce > 1
if (iCitiesRequiredPerCommerce > 1)
{
iCitiesRequiredPerCommerce += GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities();
iCitiesRequiredPerCommerce -= getAvgTargetCities();
}
iCitiesRequiredPerCommerce = std::max(1, iCitiesRequiredPerCommerce);
int iAmt = getConnectedDomesticCities() / iCitiesRequiredPerCommerce;
iYield += std::min(iAmt * iCommerceAmt, iMaxCommerce);
}
if (GC.getBuildingInfo(eBuilding).getForeignConnectedCommerce() > 0)
{
int iCommerceAmt = GC.getBuildingInfo(eBuilding).getForeignConnectedCommerce();
int iMaxCommerce = GC.getBuildingInfo(eBuilding).getMaxForeignConnectedCommerce();
if (iMaxCommerce <= 0) iMaxCommerce = MAX_INT;
int iCitiesRequiredPerCommerce = GC.getBuildingInfo(eBuilding).getCitiesRequiredPerForeignConnectedCommerce();
//Scale for mapsize, if iCitiesRequiredPerCommerce > 1
if (iCitiesRequiredPerCommerce > 1)
{
iCitiesRequiredPerCommerce += GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities();
iCitiesRequiredPerCommerce -= getAvgTargetCities();
}
iCitiesRequiredPerCommerce = std::max(1, iCitiesRequiredPerCommerce);
int iAmt = getConnectedForeignCities() / iCitiesRequiredPerCommerce;
iYield += std::min(iAmt * iCommerceAmt, iMaxCommerce);
}
}
return iYield;
}
int CvCity::getDomesticConnectednessCommerce() const
{
return m_iPreviousConnectedCommerce;
}
int CvCity::getForeignConnectednessCommerce() const
{
return m_iPreviousForeignConnectedCommerce;
}
int CvCity::getForeignConnectednessNeeded() const
{
return m_iForeignConnectednessNeeded;
}
void CvCity::doConnectednessCalculations()
{
m_iForeignConnectednessNeeded = 0;
int iExtra = calculateExtraCapitalCommerce() + calculateExtraForeignCapitalCommerce();
if (iExtra != m_iPreviousExtraCommerce)
{
changeBaseYieldRate(YIELD_COMMERCE, iExtra - m_iPreviousExtraCommerce);
}
m_iPreviousExtraCommerce = iExtra;
int iConnectedDomesticCommerce = 0;
int iConnectedForeignCommerce = 0;
int iConnectedDomesticCities = -1;
int iConnectedForeignCities = -1;
int iAvgTargetCities = -1;
for (int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (getNumRealBuilding((BuildingTypes)iI) > 0)
{
if (GC.getBuildingInfo((BuildingTypes)iI).getDomesticConnectedCommerce() > 0)
{
int iCommerceAmt = GC.getBuildingInfo((BuildingTypes)iI).getDomesticConnectedCommerce();
int iMaxCommerce = GC.getBuildingInfo((BuildingTypes)iI).getMaxDomesticConnectedCommerce();
if (iMaxCommerce <= 0) iMaxCommerce = MAX_INT;
int iCitiesRequiredPerCommerce = GC.getBuildingInfo((BuildingTypes)iI).getCitiesRequiredPerDomesticConnectedCommerce();
//Scale for mapsize, if iCitiesRequiredPerCommerce > 1
if (iCitiesRequiredPerCommerce > 1)
{
//Avoid calculating multiple times for each building
if (iAvgTargetCities == -1) iAvgTargetCities = getAvgTargetCities();
//Scale based on number of target cities (5 is avg with standard assets, 3 min, 10 max)
iCitiesRequiredPerCommerce += GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities();
iCitiesRequiredPerCommerce -= iAvgTargetCities;
}
iCitiesRequiredPerCommerce = std::max(1, iCitiesRequiredPerCommerce);
//Avoid calculating multiple times for each building
if (iConnectedDomesticCities == -1) iConnectedDomesticCities = getConnectedDomesticCities();
int iAmt = iConnectedDomesticCities / iCitiesRequiredPerCommerce;
iConnectedDomesticCommerce += std::min(iAmt * iCommerceAmt, iMaxCommerce);
}
if (GC.getBuildingInfo((BuildingTypes)iI).getForeignConnectedCommerce() > 0)
{
int iCommerceAmt = GC.getBuildingInfo((BuildingTypes)iI).getForeignConnectedCommerce();
int iMaxCommerce = GC.getBuildingInfo((BuildingTypes)iI).getMaxForeignConnectedCommerce();
if (iMaxCommerce <= 0) iMaxCommerce = MAX_INT;
int iCitiesRequiredPerCommerce = GC.getBuildingInfo((BuildingTypes)iI).getCitiesRequiredPerForeignConnectedCommerce();
//Scale for mapsize, if iCitiesRequiredPerCommerce > 1
if (iCitiesRequiredPerCommerce > 1)
{
//Avoid calculating multiple times for each building
if (iAvgTargetCities == -1) iAvgTargetCities = getAvgTargetCities();
//Scale based on number of target cities (5 is avg with standard assets, 3 min, 10 max)
iCitiesRequiredPerCommerce += GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities();
iCitiesRequiredPerCommerce -= iAvgTargetCities;
}
iCitiesRequiredPerCommerce = std::max(1, iCitiesRequiredPerCommerce);
//Avoid calculating multiple times for each building
if (iConnectedForeignCities == -1) iConnectedForeignCities = getConnectedForeignCities();
int iAmt = iConnectedForeignCities / iCitiesRequiredPerCommerce;
m_iForeignConnectednessNeeded = std::max(m_iForeignConnectednessNeeded, iCitiesRequiredPerCommerce * iMaxCommerce - iConnectedForeignCities);
iConnectedForeignCommerce += std::min(iAmt * iCommerceAmt, iMaxCommerce);
}
}
}
//Apply civic modifiers
for (int iCivicOption = 0; iCivicOption < GC.getNumCivicOptionInfos(); iCivicOption++)
{
CivicTypes eCivic = GET_PLAYER(getOwnerINLINE()).getCivics((CivicOptionTypes)iCivicOption);
if (eCivic != NO_CIVIC)
{
iConnectedDomesticCommerce *= (100 + GC.getCivicInfo(eCivic).getDomesticConnectednessModifier());
iConnectedDomesticCommerce /= 100;
iConnectedForeignCommerce *= (100 + GC.getCivicInfo(eCivic).getForeignConnectednessModifier());
iConnectedForeignCommerce /= 100;
}
}
//Apply trait modifiers
for (int iTrait = 0; iTrait < GC.getNumTraitInfos(); iTrait++)
{
if (GET_PLAYER(getOwnerINLINE()).hasTrait((TraitTypes)iTrait))
{
iConnectedDomesticCommerce *= (100 + (GC.getTraitInfo((TraitTypes)iTrait).getTradeYieldModifier(YIELD_COMMERCE) * 15));
iConnectedDomesticCommerce /= 100;
}
}
if (m_iPreviousConnectedCommerce != iConnectedDomesticCommerce)
{
changeBaseYieldRate(YIELD_COMMERCE, iConnectedDomesticCommerce - m_iPreviousConnectedCommerce);
}
m_iPreviousConnectedCommerce = iConnectedDomesticCommerce;
if (m_iPreviousForeignConnectedCommerce != iConnectedForeignCommerce)
{
changeBaseYieldRate(YIELD_COMMERCE, iConnectedForeignCommerce - m_iPreviousForeignConnectedCommerce);
}
m_iPreviousForeignConnectedCommerce = iConnectedForeignCommerce;
updateSeizedForeignConnectedness();
}
int CvCity::getAvgTargetCities() const
{
int iTargetCities = 0;
for (int iI = 0; iI < GC.getNumWorldInfos(); iI++)
{
iTargetCities += GC.getWorldInfo((WorldSizeTypes)iI).getTargetNumCities();
}
return iTargetCities / GC.getNumWorldInfos();
}
int CvCity::getConnectedDomesticCities() const
{
int iLoop;
int iConnectedCities = 0;
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (GET_PLAYER((PlayerTypes)iI).getTeam() == getTeam() || GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isVassal(getTeam()) || GET_TEAM(getTeam()).isVassal(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
for (CvCity *pLoopCity = GET_PLAYER((PlayerTypes)iI).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iI).nextCity(&iLoop))
{
if (pLoopCity->getID() != getID() && isConnectedTo(pLoopCity))
{
iConnectedCities++;
}
}
}
}
}
return iConnectedCities;
}
int CvCity::getConnectedForeignCities() const
{
int iLoop;
int iConnectedCities = 0;
if (GET_PLAYER(getOwnerINLINE()).isNoForeignTrade())
{
return 0;
}
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (iI != getOwnerINLINE() && GET_PLAYER((PlayerTypes)iI).isAlive() && GET_PLAYER((PlayerTypes)iI).getTeam() != getTeam())
{
if (GET_PLAYER(getOwnerINLINE()).canHaveTradeRoutesWith((PlayerTypes)iI))
{
if (!GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isVassal(getTeam()) && !GET_TEAM(getTeam()).isVassal(GET_PLAYER((PlayerTypes)iI).getTeam()))
{
for (CvCity *pLoopCity = GET_PLAYER((PlayerTypes)iI).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iI).nextCity(&iLoop))
{
if (isConnectedTo(pLoopCity))
{
iConnectedCities++;
}
}
}
}
}
}
return iConnectedCities;
}
int CvCity::getYieldRate(YieldTypes eIndex) const
{
PROFILE_FUNC();
return ((getBaseYieldRate(eIndex) * getBaseYieldRateModifier(eIndex)) / 100) + GET_PLAYER(getOwnerINLINE()).getFreeCityYield(eIndex);
}
void CvCity::setBaseYieldRate(YieldTypes eIndex, int iNewValue)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
if (getBaseYieldRate(eIndex) != iNewValue)
{
m_aiBaseYieldRate[eIndex] = iNewValue;
FAssert(getYieldRate(eIndex) >= 0);
#ifdef YIELD_VALUE_CACHING
ClearYieldValueCache();
#endif
setCommerceDirty();
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
if (isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(CityScreen_DIRTY_BIT, true);
gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true );
}
}
}
}
void CvCity::changeBaseYieldRate(YieldTypes eIndex, int iChange)
{
setBaseYieldRate(eIndex, (getBaseYieldRate(eIndex) + iChange));
}
int CvCity::getYieldRateModifier(YieldTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
return m_aiYieldRateModifier[eIndex];
}
void CvCity::changeYieldRateModifier(YieldTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
if (iChange != 0)
{
m_aiYieldRateModifier[eIndex] = (m_aiYieldRateModifier[eIndex] + iChange);
FAssert(getYieldRate(eIndex) >= 0);
GET_PLAYER(getOwnerINLINE()).invalidateYieldRankCache(eIndex);
if (eIndex == YIELD_COMMERCE)
{
setCommerceDirty();
}
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
int CvCity::getPowerYieldRateModifier(YieldTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
return m_aiPowerYieldRateModifier[eIndex];
}
void CvCity::changePowerYieldRateModifier(YieldTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
if (iChange != 0)
{
m_aiPowerYieldRateModifier[eIndex] = (m_aiPowerYieldRateModifier[eIndex] + iChange);
FAssert(getYieldRate(eIndex) >= 0);
GET_PLAYER(getOwnerINLINE()).invalidateYieldRankCache(eIndex);
if (eIndex == YIELD_COMMERCE)
{
setCommerceDirty();
}
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
int CvCity::getBonusYieldRateModifier(YieldTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
return m_aiBonusYieldRateModifier[eIndex];
}
void CvCity::changeBonusYieldRateModifier(YieldTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
if (iChange != 0)
{
m_aiBonusYieldRateModifier[eIndex] = (m_aiBonusYieldRateModifier[eIndex] + iChange);
FAssert(getYieldRate(eIndex) >= 0);
GET_PLAYER(getOwnerINLINE()).invalidateYieldRankCache(eIndex);
if (eIndex == YIELD_COMMERCE)
{
setCommerceDirty();
}
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
int CvCity::getTradeYield(YieldTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
return m_aiTradeYield[eIndex];
}
int CvCity::totalTradeModifier(CvCity* pOtherCity) const
{
int iModifier = 100;
iModifier += getTradeRouteModifier();
iModifier += getPopulationTradeModifier();
/************************************************************************************************/
/* Afforess Start 03/8/10 */
/* */
/* */
/************************************************************************************************/
iModifier += GET_TEAM(getTeam()).getTradeModifier();
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (isConnectedToCapital())
{
iModifier += GC.getDefineINT("CAPITAL_TRADE_MODIFIER");
}
if (NULL != pOtherCity)
{
if (area() != pOtherCity->area())
{
iModifier += GC.getDefineINT("OVERSEAS_TRADE_MODIFIER");
}
if (getTeam() != pOtherCity->getTeam())
{
iModifier += getForeignTradeRouteModifier();
/************************************************************************************************/
/* Afforess Start 3/8/09 */
/* */
/* */
/************************************************************************************************/
iModifier += GET_PLAYER(getOwnerINLINE()).getForeignTradeRouteModifier();
iModifier += GET_TEAM(getTeam()).getForeignTradeModifier();
CvPlayer &kOtherPlayer = GET_PLAYER(pOtherCity->getOwnerINLINE());
CvPlayer &kPlayer = GET_PLAYER(getOwnerINLINE());
for (int iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
if (kPlayer.getCivics((CivicOptionTypes)iI) == kOtherPlayer.getCivics((CivicOptionTypes)iI))
{
iModifier += GC.getCivicInfo(kPlayer.getCivics((CivicOptionTypes)iI)).getSharedCivicTradeRouteModifier();
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iModifier += getPeaceTradeModifier(pOtherCity->getTeam());
}
}
return iModifier;
}
int CvCity::getPopulationTradeModifier() const
{
return std::max(0, (getPopulation() + GC.getDefineINT("OUR_POPULATION_TRADE_MODIFIER_OFFSET")) * GC.getDefineINT("OUR_POPULATION_TRADE_MODIFIER"));
}
int CvCity::getPeaceTradeModifier(TeamTypes eTeam) const
{
FAssert(NO_TEAM != eTeam);
FAssert(eTeam != getTeam());
if (atWar(eTeam, getTeam()))
{
return 0;
}
int iPeaceTurns = std::min(GC.getDefineINT("FOREIGN_TRADE_FULL_CREDIT_PEACE_TURNS"), GET_TEAM(getTeam()).AI_getAtPeaceCounter(eTeam));
if (GC.getGameINLINE().getElapsedGameTurns() <= iPeaceTurns)
{
return GC.getDefineINT("FOREIGN_TRADE_MODIFIER");
}
return ((GC.getDefineINT("FOREIGN_TRADE_MODIFIER") * iPeaceTurns) / std::max(1, GC.getDefineINT("FOREIGN_TRADE_FULL_CREDIT_PEACE_TURNS")));
}
int CvCity::getBaseTradeProfit(CvCity* pCity) const
{
int iProfit = std::min(pCity->getPopulation() * GC.getDefineINT("THEIR_POPULATION_TRADE_PERCENT"), plotDistance(getX_INLINE(), getY_INLINE(), pCity->getX_INLINE(), pCity->getY_INLINE()) * GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTradeProfitPercent());
iProfit *= GC.getDefineINT("TRADE_PROFIT_PERCENT");
iProfit /= 100;
iProfit = std::max(100, iProfit);
return iProfit;
}
// BUG - Fractional Trade Routes - start
#ifdef _MOD_FRACTRADE
// Note: getBaseTradeProfit() already returns a times-100 value.
/*
* Returns the fractional (times 100) trade profit for the route to the given city.
*/
int CvCity::calculateTradeProfitTimes100(CvCity* pCity) const
{
int iProfit = getBaseTradeProfit(pCity);
iProfit *= totalTradeModifier(pCity);
iProfit /= 100;
return iProfit;
}
/*
* Returns the truncated trade profit for the route to the given city.
*
* This function is kept only for old Python code.
*/
int CvCity::calculateTradeProfit(CvCity* pCity) const
{
return calculateTradeProfitTimes100(pCity) / 100;
}
#else
// unchanged
int CvCity::calculateTradeProfit(CvCity* pCity) const
{
int iProfit = getBaseTradeProfit(pCity);
iProfit *= totalTradeModifier(pCity);
iProfit /= 10000;
return iProfit;
}
#endif
// BUG - Fractional Trade Routes - end
int CvCity::calculateTradeYield(YieldTypes eIndex, int iTradeProfit) const
{
if ((iTradeProfit != 0) && (GET_PLAYER(getOwnerINLINE()).getTradeYieldModifier(eIndex) > 0))
{
return ((iTradeProfit * GET_PLAYER(getOwnerINLINE()).getTradeYieldModifier(eIndex)) / 100);
}
else
{
return 0;
}
}
// BUG - Trade Totals - start
/*
* Adds the yield and count for each trade route with eWithPlayer.
*
* The yield and counts are not reset to zero.
* If Fractional Trade Routes is enabled and bRound is false, or if bBase if true, the yield values are left times 100.
*/
void CvCity::calculateTradeTotals(YieldTypes eIndex, int& iDomesticYield, int& iDomesticRoutes, int& iForeignYield, int& iForeignRoutes, PlayerTypes eWithPlayer, bool bRound, bool bBase) const
{
if (!isDisorder())
{
int iCityDomesticYield = 0;
int iCityDomesticRoutes = 0;
int iCityForeignYield = 0;
int iCityForeignRoutes = 0;
int iNumTradeRoutes = getTradeRoutes();
PlayerTypes ePlayer = getOwnerINLINE();
for (int iI = 0; iI < iNumTradeRoutes; ++iI)
{
CvCity* pTradeCity = getTradeCity(iI);
if (pTradeCity && pTradeCity->getOwnerINLINE() >= 0 && (NO_PLAYER == eWithPlayer || pTradeCity->getOwnerINLINE() == eWithPlayer))
{
int iTradeYield;
if (bBase)
{
iTradeYield = getBaseTradeProfit(pTradeCity);
}
else
{
// BUG - Fractional Trade Routes - start
#ifdef _MOD_FRACTRADE
int iTradeProfit = calculateTradeProfitTimes100(pTradeCity);
#else
int iTradeProfit = calculateTradeProfit(pTradeCity);
#endif
// BUG - Fractional Trade Routes - end
iTradeYield = calculateTradeYield(YIELD_COMMERCE, iTradeProfit);
}
if (pTradeCity->getOwnerINLINE() == ePlayer)
{
iCityDomesticYield += iTradeYield;
iCityDomesticRoutes++;
}
else
{
iCityForeignYield += iTradeYield;
iCityForeignRoutes++;
}
}
}
// BUG - Fractional Trade Routes - start
#ifdef _MOD_FRACTRADE
if (bRound)
{
iDomesticYield += iCityDomesticYield / 100;
iDomesticRoutes += iCityDomesticRoutes / 100;
iForeignYield += iCityForeignYield / 100;
iForeignRoutes += iCityForeignRoutes / 100;
}
else
#endif
// BUG - Fractional Trade Routes - end
{
iDomesticYield += iCityDomesticYield;
iDomesticRoutes += iCityDomesticRoutes;
iForeignYield += iCityForeignYield;
iForeignRoutes += iCityForeignRoutes;
}
}
}
/*
* Returns the total trade yield.
*
* If Fractional Trade Routes is enabled or bBase is true, the yield value is left times 100.
* UNUSED
*/
int CvCity::calculateTotalTradeYield(YieldTypes eIndex, PlayerTypes eWithPlayer, bool bRound, bool bBase) const
{
int iDomesticYield = 0;
int iDomesticRoutes = 0;
int iForeignYield = 0;
int iForeignRoutes = 0;
calculateTradeTotals(eIndex, iDomesticYield, iDomesticRoutes, iForeignYield, iForeignRoutes, eWithPlayer, bRound, bBase);
return iDomesticYield + iForeignRoutes;
}
// BUG - Trade Totals - end
void CvCity::setTradeYield(YieldTypes eIndex, int iNewValue)
{
int iOldValue;
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
iOldValue = getTradeYield(eIndex);
if (iOldValue != iNewValue)
{
m_aiTradeYield[eIndex] = iNewValue;
FAssert(getTradeYield(eIndex) >= 0);
changeBaseYieldRate(eIndex, (iNewValue - iOldValue));
}
}
int CvCity::getExtraSpecialistYield(YieldTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
return m_aiExtraSpecialistYield[eIndex];
}
int CvCity::getExtraSpecialistYield(YieldTypes eIndex, SpecialistTypes eSpecialist) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
FAssertMsg(eSpecialist >= 0, "eSpecialist expected to be >= 0");
FAssertMsg(eSpecialist < GC.getNumSpecialistInfos(), "GC.getNumSpecialistInfos expected to be >= 0");
return ((getSpecialistCount(eSpecialist) + getFreeSpecialistCount(eSpecialist)) * (GET_PLAYER(getOwnerINLINE()).getExtraSpecialistYield(eSpecialist, eIndex) + GET_PLAYER(getOwnerINLINE()).getSpecialistExtraYield(eIndex)));
}
void CvCity::updateExtraSpecialistYield(YieldTypes eYield)
{
int iOldYield;
int iNewYield;
int iI;
FAssertMsg(eYield >= 0, "eYield expected to be >= 0");
FAssertMsg(eYield < NUM_YIELD_TYPES, "eYield expected to be < NUM_YIELD_TYPES");
iOldYield = getExtraSpecialistYield(eYield);
iNewYield = 0;
for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
{
iNewYield += getExtraSpecialistYield(eYield, ((SpecialistTypes)iI));
}
if (iOldYield != iNewYield)
{
m_aiExtraSpecialistYield[eYield] = iNewYield;
changeBaseYieldRate(eYield, (iNewYield - iOldYield));
}
}
void CvCity::updateExtraSpecialistYield()
{
int iI;
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
updateExtraSpecialistYield((YieldTypes)iI);
}
}
int CvCity::getCommerceRate(CommerceTypes eIndex) const
{
return getCommerceRateTimes100(eIndex) / 100;
}
int CvCity::getCommerceRateTimes100(CommerceTypes eIndex) const
{
PROFILE_FUNC();
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
if ( m_abCommerceRateDirty[eIndex] )
{
updateCommerce(eIndex);
}
int iRate = m_aiCommerceRate[eIndex];
if (GC.getGameINLINE().isOption(GAMEOPTION_NO_ESPIONAGE))
{
if (eIndex == COMMERCE_CULTURE)
{
iRate += m_aiCommerceRate[COMMERCE_ESPIONAGE];
}
else if (eIndex == COMMERCE_ESPIONAGE)
{
iRate = 0;
}
}
return iRate;
}
int CvCity::getCommerceFromPercent(CommerceTypes eIndex, int iYieldRate) const
{
int iCommerce;
iCommerce = ((iYieldRate * GET_PLAYER(getOwnerINLINE()).getCommercePercent(eIndex)) / 100);
if (eIndex == COMMERCE_GOLD)
{
iCommerce += (iYieldRate - iCommerce - getCommerceFromPercent(COMMERCE_RESEARCH, iYieldRate) - getCommerceFromPercent(COMMERCE_CULTURE, iYieldRate) - getCommerceFromPercent(COMMERCE_ESPIONAGE, iYieldRate));
}
return iCommerce;
}
int CvCity::getBaseCommerceRate(CommerceTypes eIndex) const
{
return (getBaseCommerceRateTimes100(eIndex) / 100);
}
int CvCity::getBaseCommerceRateTimes100(CommerceTypes eIndex) const
{
PROFILE_FUNC();
int iBaseCommerceRate;
if (m_abCommerceRateDirty[eIndex])
{
updateCommerce(eIndex);
}
iBaseCommerceRate = getCommerceFromPercent(eIndex, getBaseYieldRate(YIELD_COMMERCE) * getBaseYieldRateModifier(YIELD_COMMERCE)); //Afforess: use fractional yields when calculating commerce
iBaseCommerceRate += 100 * ((getSpecialistPopulation() + getNumGreatPeople()) * GET_PLAYER(getOwnerINLINE()).getSpecialistExtraCommerce(eIndex));
iBaseCommerceRate += 100 * (getBuildingCommerce(eIndex) + getSpecialistCommerce(eIndex) + getReligionCommerce(eIndex) + getCorporationCommerce(eIndex) + GET_PLAYER(getOwnerINLINE()).getFreeCityCommerce(eIndex));
/************************************************************************************************/
/* Afforess Start 06/15/10 */
/* */
/* */
/************************************************************************************************/
int iTemp = 0;
iTemp += 100 * getCommerceAttacks(eIndex);
iTemp += getBonusCommercePercentChanges(eIndex);
if (eIndex == COMMERCE_GOLD)
{
iTemp += getMintedCommerceTimes100();
}
iBaseCommerceRate += iTemp;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iBaseCommerceRate;
}
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
/*Was:
int CvCity::getTotalCommerceRateModifier(CommerceTypes eIndex) const
{
return std::max(0, (getCommerceRateModifier(eIndex) + GET_PLAYER(getOwnerINLINE()).getCommerceRateModifier(eIndex) + ((isCapital()) ? GET_PLAYER(getOwnerINLINE()).getCapitalCommerceRateModifier(eIndex) : 0) + 100));
}
*/
int CvCity::getTotalCommerceRateModifier(CommerceTypes eIndex) const
{
PROFILE_FUNC();
if ( m_totalCommerceRateModifier[eIndex] == MIN_INT )
{
m_totalCommerceRateModifier[eIndex] = std::max(1, (calculateBuildingCommerceModifier(eIndex) + getCommerceRateModifier(eIndex) + getBonusCommerceRateModifier(eIndex) + GET_PLAYER(getOwnerINLINE()).getCommerceRateModifier(eIndex) + calculateBonusCommerceRateModifier(eIndex) + ((isCapital()) ? GET_PLAYER(getOwnerINLINE()).getCapitalCommerceRateModifier(eIndex) : 0) + 100));
}
return m_totalCommerceRateModifier[eIndex];
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
void CvCity::setCommerceModifierDirty(CommerceTypes eCommerce)
{
m_totalCommerceRateModifier[eCommerce] = MIN_INT;
setCommerceDirty(eCommerce);
}
void CvCity::setCommerceDirty(CommerceTypes eCommerce)
{
if ( eCommerce == NO_COMMERCE )
{
for(int iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
setCommerceDirty((CommerceTypes)iI);
}
}
else
{
m_abCommerceRateDirty[eCommerce] = true;
if (getOwnerINLINE() != NO_PLAYER)
{
GET_PLAYER(getOwnerINLINE()).setCommerceDirty(eCommerce, true);
}
}
}
void CvCity::updateCommerce(CommerceTypes eIndex, bool bForce) const
{
int iOldCommerce;
int iNewCommerce;
if ( !GC.getGameINLINE().isRecalculatingModifiers() )
{
if ( eIndex == NO_COMMERCE )
{
GET_PLAYER(getOwnerINLINE()).invalidateYieldRankCache();
for (int iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
updateCommerce((CommerceTypes)iI, bForce);
}
}
else
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
if (bForce || m_abCommerceRateDirty[eIndex])
{
m_abCommerceRateDirty[eIndex] = false;
iOldCommerce = m_aiCommerceRate[eIndex];
if (isDisorder())
{
iNewCommerce = 0;
}
else
{
iNewCommerce = getBaseCommerceRateTimes100(eIndex);
// Don't apply rate modifiers to negative commerce or you get counter-intuitive results
// like intelligence agencies makign your negative espionage worse!
if ( iNewCommerce > 0 )
{
iNewCommerce = (iNewCommerce * getTotalCommerceRateModifier(eIndex)) / 100;
}
else
{
iNewCommerce = (iNewCommerce * 100) / getTotalCommerceRateModifier(eIndex);
}
iNewCommerce += getYieldRate(YIELD_PRODUCTION) * getProductionToCommerceModifier(eIndex);
}
// Culture and science cannot be negative
if ( iNewCommerce < 0 && (eIndex == COMMERCE_CULTURE || eIndex == COMMERCE_RESEARCH) )
{
iNewCommerce = 0;
}
if (iOldCommerce != iNewCommerce)
{
m_aiCommerceRate[eIndex] = iNewCommerce;
GET_PLAYER(getOwnerINLINE()).invalidateCommerceRankCache(eIndex);
GET_PLAYER(getOwnerINLINE()).changeCommerceRate(eIndex, (iNewCommerce - iOldCommerce));
if (isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true );
gDLL->getInterfaceIFace()->setDirty(CityScreen_DIRTY_BIT, true);
}
}
}
}
}
}
int CvCity::getProductionToCommerceModifier(CommerceTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
return m_aiProductionToCommerceModifier[eIndex];
}
void CvCity::changeProductionToCommerceModifier(CommerceTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
if (iChange != 0)
{
m_aiProductionToCommerceModifier[eIndex] = (m_aiProductionToCommerceModifier[eIndex] + iChange);
setCommerceDirty(eIndex);
}
}
int CvCity::getBuildingCommerce(CommerceTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
return m_aiBuildingCommerce[eIndex];
}
int CvCity::getBuildingCommerceByBuilding(CommerceTypes eIndex, BuildingTypes eBuilding) const
{
PROFILE_FUNC();
int iCommerce;
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
FAssertMsg(eBuilding >= 0, "eBuilding expected to be >= 0");
FAssertMsg(eBuilding < GC.getNumBuildingInfos(), "GC.getNumBuildingInfos expected to be >= 0");
iCommerce = 0;
if (getNumBuilding(eBuilding) > 0)
{
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
if (!(kBuilding.isCommerceChangeOriginalOwner(eIndex)) || (getBuildingOriginalOwner(eBuilding) == getOwnerINLINE()))
{
iCommerce += kBuilding.getObsoleteSafeCommerceChange(eIndex) * getNumBuilding(eBuilding);
if (getNumActiveBuilding(eBuilding) > 0)
{
int iBaseCommerceChange = GC.getBuildingInfo(eBuilding).getCommerceChange(eIndex);
if ( iBaseCommerceChange < 0 && eIndex == COMMERCE_GOLD && GC.getDefineINT("TREAT_NEGATIVE_GOLD_AS_MAINTENANCE") )
{
iBaseCommerceChange = 0;
}
iCommerce += (iBaseCommerceChange + getBuildingCommerceChange((BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType(), eIndex)) * getNumActiveBuilding(eBuilding);
if (GC.getBuildingInfo(eBuilding).getReligionType() != NO_RELIGION)
{
if (GC.getBuildingInfo(eBuilding).getReligionType() == GET_PLAYER(getOwnerINLINE()).getStateReligion())
{
iCommerce += GET_PLAYER(getOwnerINLINE()).getStateReligionBuildingCommerce(eIndex) * getNumActiveBuilding(eBuilding);
}
}
if (GC.getBuildingInfo(eBuilding).getGlobalReligionCommerce() != NO_RELIGION)
{
iCommerce += (GC.getReligionInfo((ReligionTypes)(GC.getBuildingInfo(eBuilding).getGlobalReligionCommerce())).getGlobalReligionCommerce(eIndex) * GC.getGameINLINE().countReligionLevels((ReligionTypes)(GC.getBuildingInfo(eBuilding).getGlobalReligionCommerce()))) * getNumActiveBuilding(eBuilding);
}
if (GC.getBuildingInfo(eBuilding).getGlobalCorporationCommerce() != NO_CORPORATION)
{
iCommerce += (GC.getCorporationInfo((CorporationTypes)(GC.getBuildingInfo(eBuilding).getGlobalCorporationCommerce())).getHeadquarterCommerce(eIndex) * GC.getGameINLINE().countCorporationLevels((CorporationTypes)(GC.getBuildingInfo(eBuilding).getGlobalCorporationCommerce()))) * getNumActiveBuilding(eBuilding);
}
}
if ((GC.getBuildingInfo(eBuilding).getCommerceChangeDoubleTime(eIndex) != 0) &&
(getBuildingOriginalTime(eBuilding) != MIN_INT) &&
((GC.getGameINLINE().getGameTurnYear() - getBuildingOriginalTime(eBuilding)) >= GC.getBuildingInfo(eBuilding).getCommerceChangeDoubleTime(eIndex)))
{
return (iCommerce * 2);
}
return iCommerce;
}
}
return 0;
}
// BUG - Building Additional Commerce - start
/*
* Returns the rounded total additional commerce that adding one of the given buildings will provide.
*
* Doesn't check if the building can be constructed in this city.
* Takes the NO_ESPIONAGE game option into account for CULTURE and ESPIONAGE.
*/
int CvCity::getAdditionalCommerceByBuilding(CommerceTypes eIndex, BuildingTypes eBuilding)
{
return getAdditionalCommerceTimes100ByBuilding(eIndex, eBuilding) / 100;
}
/************************************************************************************************/
/* Afforess Start 09/07/10 */
/* */
/* Originally By the BUG Mod - Heavily Modified to Include new XML Tags */
/************************************************************************************************/
/*
* Returns the total additional commerce times 100 that adding one of the given buildings will provide.
*
* Doesn't check if the building can be constructed in this city.
* Takes the NO_ESPIONAGE game option into account for CULTURE and ESPIONAGE.
*/
int CvCity::getAdditionalCommerceTimes100ByBuilding(CommerceTypes eIndex, BuildingTypes eBuilding)
{
int iExtraRateTimes100 = getAdditionalBaseCommerceRateByBuildingTimes100(eIndex, eBuilding);
int iExtraModifier = getAdditionalCommerceRateModifierByBuilding(eIndex, eBuilding);
if (iExtraRateTimes100 == 0 && iExtraModifier == 0)
{
return 0;
}
int iRateTimes100 = getBaseCommerceRateTimes100(eIndex);
int iModifier = getTotalCommerceRateModifier(eIndex);
int iCommerceWithoutBuilding;
int iCommerceWithBuilding;
if ( iRateTimes100 > 0 )
{
iCommerceWithoutBuilding = (iRateTimes100 * iModifier) / 100;
}
else
{
iCommerceWithoutBuilding = (iRateTimes100 * 100) / iModifier;
}
if ( iRateTimes100 + iExtraRateTimes100 > 0 )
{
iCommerceWithBuilding = ((iRateTimes100 + iExtraRateTimes100) * (iModifier + iExtraModifier)) / 100;
}
else
{
iCommerceWithBuilding = ((iRateTimes100 + iExtraRateTimes100) * 100) / (iModifier + iExtraModifier);
}
int iExtraTimes100 = iCommerceWithBuilding - iCommerceWithoutBuilding;
for (int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (getNumActiveBuilding((BuildingTypes)iI) > 0)
{
if (GC.getBuildingInfo((BuildingTypes)iI).isReplaceBuildingClass((BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType()))
{
iExtraTimes100 -= getAdditionalCommerceTimes100ByBuilding(eIndex, (BuildingTypes)iI);
}
}
}
return iExtraTimes100;
}
/*
* Returns the additional base commerce rate constructing the given building will provide.
*
* Doesn't check if the building can be constructed in this city.
* Takes the NO_ESPIONAGE game option into account for CULTURE and ESPIONAGE.
*/
int CvCity::getAdditionalBaseCommerceRateByBuilding(CommerceTypes eIndex, BuildingTypes eBuilding)
{
bool bNoEspionage = GC.getGameINLINE().isOption(GAMEOPTION_NO_ESPIONAGE);
if (bNoEspionage && eIndex == COMMERCE_ESPIONAGE)
{
return 0;
}
int iExtraRate = getAdditionalBaseCommerceRateByBuildingImpl(eIndex, eBuilding);
if (bNoEspionage && eIndex == COMMERCE_CULTURE)
{
iExtraRate += getAdditionalBaseCommerceRateByBuildingImpl(COMMERCE_ESPIONAGE, eBuilding);
}
return iExtraRate;
}
int CvCity::getAdditionalBaseCommerceRateByBuildingTimes100(CommerceTypes eIndex, BuildingTypes eBuilding)
{
int iExtraRateTimes100 = 100 * getAdditionalBaseCommerceRateByBuilding(eIndex, eBuilding);
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
for (int iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
if (hasBonus((BonusTypes)iI))
{
iExtraRateTimes100 += (kBuilding.getBonusCommercePercentChanges(iI, eIndex));
}
}
return iExtraRateTimes100;
}
/*
* Returns the additional base commerce rate constructing the given building will provide.
*
* Doesn't check if the building can be constructed in this city.
*/
int CvCity::getAdditionalBaseCommerceRateByBuildingImpl(CommerceTypes eIndex, BuildingTypes eBuilding)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
FAssertMsg(eBuilding >= 0, "eBuilding expected to be >= 0");
FAssertMsg(eBuilding < GC.getNumBuildingInfos(), "eBuilding expected to be < GC.getNumBuildingInfos()");
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
bool bObsolete = GET_TEAM(getTeam()).isObsoleteBuilding(eBuilding);
int iExtraRate = 0;
iExtraRate += kBuilding.getObsoleteSafeCommerceChange(eIndex);
if (!bObsolete)
{
int iBaseCommerceChange = kBuilding.getCommerceChange(eIndex);
if ( iBaseCommerceChange < 0 && eIndex == COMMERCE_GOLD && GC.getDefineINT("TREAT_NEGATIVE_GOLD_AS_MAINTENANCE") )
{
iBaseCommerceChange = 0;
}
iExtraRate += iBaseCommerceChange;
iExtraRate += getBuildingCommerceChange((BuildingClassTypes)kBuilding.getBuildingClassType(), eIndex);
if (kBuilding.getReligionType() != NO_RELIGION)
{
if (kBuilding.getReligionType() == GET_PLAYER(getOwnerINLINE()).getStateReligion())
{
iExtraRate += GET_PLAYER(getOwnerINLINE()).getStateReligionBuildingCommerce(eIndex);
}
}
if (kBuilding.getGlobalReligionCommerce() != NO_RELIGION)
{
iExtraRate += GC.getReligionInfo((ReligionTypes)(kBuilding.getGlobalReligionCommerce())).getGlobalReligionCommerce(eIndex) * GC.getGameINLINE().countReligionLevels((ReligionTypes)(kBuilding.getGlobalReligionCommerce()));
}
if (kBuilding.getGlobalCorporationCommerce() != NO_CORPORATION)
{
iExtraRate += GC.getCorporationInfo((CorporationTypes)(kBuilding.getGlobalCorporationCommerce())).getHeadquarterCommerce(eIndex) * GC.getGameINLINE().countCorporationLevels((CorporationTypes)(kBuilding.getGlobalCorporationCommerce()));
}
// ignore double-time check since this assumes you are building it this turn
// Specialists
for (int iI = 0; iI < GC.getNumSpecialistInfos(); ++iI)
{
if (kBuilding.getFreeSpecialistCount((SpecialistTypes)iI) != 0)
{
iExtraRate += getAdditionalBaseCommerceRateBySpecialistImpl(eIndex, (SpecialistTypes)iI, kBuilding.getFreeSpecialistCount((SpecialistTypes)iI));
}
}
for (int iI = 0; iI < GC.getNumTechInfos(); iI++)
{
if (GET_TEAM(GET_PLAYER(getOwnerINLINE()).getTeam()).isHasTech((TechTypes)iI))
{
iExtraRate += (kBuilding.getTechCommerceChange(iI, eIndex));
}
}
if (GC.getBuildingInfo(eBuilding).isForceAllTradeRoutes())
{
int iCurrentTradeRevenue = GET_PLAYER(getOwnerINLINE()).calculateTotalExports(YIELD_COMMERCE);
GET_PLAYER(getOwnerINLINE()).changeForceAllTradeRoutes(1);
int iFutureTradeRevenue = GET_PLAYER(getOwnerINLINE()).calculateTotalExports(YIELD_COMMERCE);
GET_PLAYER(getOwnerINLINE()).changeForceAllTradeRoutes(-1);
iExtraRate += (iFutureTradeRevenue - iCurrentTradeRevenue) * GET_PLAYER(getOwnerINLINE()).getCommercePercent(eIndex) / 100;
}
int iFreeSpecialistCommerce = 0;
for (int iI = 1; iI < kBuilding.getFreeSpecialist() + 1; iI++)
{
SpecialistTypes eNewSpecialist = getBestSpecialist(iI);
if (eNewSpecialist == NO_SPECIALIST) break;
iFreeSpecialistCommerce += GET_PLAYER(getOwnerINLINE()).specialistCommerce(eNewSpecialist, eIndex);
}
iExtraRate += iFreeSpecialistCommerce;
if (kBuilding.getNumPopulationEmployed() > 0)
{
int* paiCommerce = new int[NUM_COMMERCE_TYPES];
int* paiYield = new int[NUM_YIELD_TYPES];
int iGreatPeopleRate;
int iHappiness;
int iHealthiness;
removeWorstCitizenActualEffects(kBuilding.getNumPopulationEmployed(), iGreatPeopleRate, iHappiness, iHealthiness, paiYield, paiCommerce);
iExtraRate += paiCommerce[eIndex];
SAFE_DELETE_ARRAY(paiCommerce);
SAFE_DELETE_ARRAY(paiYield);
}
}
return iExtraRate;
}
/*
* Returns the additional commerce rate modifier constructing the given building will provide.
*
* Doesn't check if the building can be constructed in this city.
* Takes the NO_ESPIONAGE game option into account for CULTURE and ESPIONAGE.
*/
int CvCity::getAdditionalCommerceRateModifierByBuilding(CommerceTypes eIndex, BuildingTypes eBuilding)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
FAssertMsg(eBuilding >= 0, "eBuilding expected to be >= 0");
FAssertMsg(eBuilding < GC.getNumBuildingInfos(), "eBuilding expected to be < GC.getNumBuildingInfos()");
bool bNoEspionage = GC.getGameINLINE().isOption(GAMEOPTION_NO_ESPIONAGE);
if (bNoEspionage && eIndex == COMMERCE_ESPIONAGE)
{
return 0;
}
int iExtraModifier = getAdditionalCommerceRateModifierByBuildingImpl(eIndex, eBuilding);
if (bNoEspionage && eIndex == COMMERCE_CULTURE)
{
iExtraModifier += getAdditionalCommerceRateModifierByBuildingImpl(COMMERCE_ESPIONAGE, eBuilding);
}
return iExtraModifier;
}
/*
* Returns the additional commerce rate modifier constructing the given building will provide.
*
* Doesn't check if the building can be constructed in this city.
*/
int CvCity::getAdditionalCommerceRateModifierByBuildingImpl(CommerceTypes eIndex, BuildingTypes eBuilding)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
FAssertMsg(eBuilding >= 0, "eBuilding expected to be >= 0");
FAssertMsg(eBuilding < GC.getNumBuildingInfos(), "eBuilding expected to be < GC.getNumBuildingInfos()");
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
bool bObsolete = GET_TEAM(getTeam()).isObsoleteBuilding(eBuilding);
int iExtraModifier = 0;
if (!bObsolete)
{
iExtraModifier += kBuilding.getCommerceModifier(eIndex);
iExtraModifier += kBuilding.getGlobalCommerceModifier(eIndex);
for (int iI = 0; iI < GC.getNumTechInfos(); iI++)
{
if (GET_TEAM(GET_PLAYER(getOwnerINLINE()).getTeam()).isHasTech((TechTypes)iI))
{
iExtraModifier += (kBuilding.getTechCommerceModifier(iI, eIndex));
}
}
for (int iI = 0; iI < GC.getNumBonusInfos(); ++iI)
{
if (hasBonus((BonusTypes)iI))
{
iExtraModifier += (kBuilding.getBonusCommerceModifier(iI, eIndex));
}
}
}
return iExtraModifier;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
// BUG - Building Additional Commerce - end
void CvCity::updateBuildingCommerce()
{
PROFILE_FUNC();
int iNewBuildingCommerce;
int iI, iJ;
// Disabled during modifier recalc (and called explicitly there after re-enabling)
if ( !GC.getGameINLINE().isRecalculatingModifiers() )
{
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
iNewBuildingCommerce = 0;
for (iJ = 0; iJ < GC.getNumBuildingInfos(); iJ++)
{
iNewBuildingCommerce += getBuildingCommerceByBuilding(((CommerceTypes)iI), ((BuildingTypes)iJ));
}
if (getBuildingCommerce((CommerceTypes)iI) != iNewBuildingCommerce)
{
m_aiBuildingCommerce[iI] = iNewBuildingCommerce;
setCommerceDirty((CommerceTypes)iI);
}
}
}
}
int CvCity::getSpecialistCommerce(CommerceTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
return m_aiSpecialistCommerce[eIndex]/100;
}
void CvCity::changeSpecialistCommerceTimes100(CommerceTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
if (iChange != 0)
{
m_aiSpecialistCommerce[eIndex] = (m_aiSpecialistCommerce[eIndex] + iChange);
FAssert(getSpecialistCommerce(eIndex) >= 0);
setCommerceDirty(eIndex);
}
}
// BUG - Specialist Additional Commerce - start
/*
* Returns the total additional commerce that changing the number of given specialists will provide/remove.
*
* Takes the NO_ESPIONAGE game option into account for CULTURE and ESPIONAGE.
*/
int CvCity::getAdditionalCommerceBySpecialist(CommerceTypes eIndex, SpecialistTypes eSpecialist, int iChange) const
{
return getAdditionalCommerceTimes100BySpecialist(eIndex, eSpecialist, iChange) / 100;
}
/*
* Returns the total additional commerce times 100 that changing the number of given specialists will provide/remove.
*
* Takes the NO_ESPIONAGE game option into account for CULTURE and ESPIONAGE.
*/
int CvCity::getAdditionalCommerceTimes100BySpecialist(CommerceTypes eIndex, SpecialistTypes eSpecialist, int iChange) const
{
int iExtraRate = getAdditionalBaseCommerceRateBySpecialist(eIndex, eSpecialist, iChange);
if (iExtraRate == 0)
{
return 0;
}
/************************************************************************************************/
/* Afforess Start 07/20/10 */
/* */
/* */
/************************************************************************************************/
int iCivicCommerce = GET_PLAYER(getOwnerINLINE()).getSpecialistCommercePercentChanges(eSpecialist, eIndex);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
int iRateTimes100 = getBaseCommerceRateTimes100(eIndex);
int iModifier = getTotalCommerceRateModifier(eIndex);
/************************************************************************************************/
/* Afforess Start 07/20/10 */
/* */
/* */
/************************************************************************************************/
/*
int iExtraTimes100 = (iModifier * (100 * iExtraRate + iRateTimes100) / 100) - (iModifier * iRateTimes100 / 100);
*/
int iExtraTimes100 = (iModifier * (100 * iExtraRate + iRateTimes100 + iCivicCommerce) / 100) - (iModifier * iRateTimes100 / 100);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iExtraTimes100;
}
/*
* Returns the additional base commerce rate that changing the number of given specialists will provide/remove.
*
* Takes the NO_ESPIONAGE game option into account for CULTURE and ESPIONAGE.
*/
int CvCity::getAdditionalBaseCommerceRateBySpecialist(CommerceTypes eIndex, SpecialistTypes eSpecialist, int iChange) const
{
bool bNoEspionage = GC.getGameINLINE().isOption(GAMEOPTION_NO_ESPIONAGE);
if (bNoEspionage && eIndex == COMMERCE_ESPIONAGE)
{
return 0;
}
int iExtraRate = getAdditionalBaseCommerceRateBySpecialistImpl(eIndex, eSpecialist, iChange);
if (bNoEspionage && eIndex == COMMERCE_CULTURE)
{
iExtraRate += getAdditionalBaseCommerceRateBySpecialistImpl(COMMERCE_ESPIONAGE, eSpecialist, iChange);
}
return iExtraRate;
}
/*
* Returns the additional base commerce rate that changing the number of given specialists will provide/remove.
*/
int CvCity::getAdditionalBaseCommerceRateBySpecialistImpl(CommerceTypes eIndex, SpecialistTypes eSpecialist, int iChange) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
FAssertMsg(eSpecialist >= 0, "eSpecialist expected to be >= 0");
FAssertMsg(eSpecialist < GC.getNumSpecialistInfos(), "eSpecialist expected to be < GC.getNumSpecialistInfos()");
CvSpecialistInfo& kSpecialist = GC.getSpecialistInfo(eSpecialist);
return iChange * (kSpecialist.getCommerceChange(eIndex) + GET_PLAYER(getOwnerINLINE()).getSpecialistExtraCommerce(eIndex)/*TB Traits begin*/ + getExtraSpecialistCommerce(eIndex, eSpecialist)/*TB Traits end*/);
}
// BUG - Specialist Additional Commerce - end
int CvCity::getReligionCommerce(CommerceTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
return m_aiReligionCommerce[eIndex];
}
int CvCity::getReligionCommerceByReligion(CommerceTypes eIndex, ReligionTypes eReligion) const
{
int iCommerce;
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
FAssertMsg(eReligion >= 0, "eReligion expected to be >= 0");
FAssertMsg(eReligion < GC.getNumReligionInfos(), "GC.getNumReligionInfos expected to be >= 0");
iCommerce = 0;
/************************************************************************************************/
/* REVDCM 02/16/10 phungus420 */
/* */
/* RevTrait Effects */
/************************************************************************************************/
if ((GET_PLAYER(getOwnerINLINE()).getStateReligion() == eReligion)
|| (GET_PLAYER(getOwnerINLINE()).getStateReligion() == NO_RELIGION)
|| (GET_PLAYER(getOwnerINLINE()).isNonStateReligionCommerce())) //phungus enlightened
/************************************************************************************************/
/* REVDCM END */
/************************************************************************************************/
{
if (isHasReligion(eReligion))
{
iCommerce += GC.getReligionInfo(eReligion).getStateReligionCommerce(eIndex);
if (isHolyCity(eReligion))
{
iCommerce += GC.getReligionInfo(eReligion).getHolyCityCommerce(eIndex);
}
}
}
return iCommerce;
}
// XXX can this be simplified???
void CvCity::updateReligionCommerce(CommerceTypes eIndex)
{
int iNewReligionCommerce;
int iI;
iNewReligionCommerce = 0;
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
iNewReligionCommerce += getReligionCommerceByReligion(eIndex, ((ReligionTypes)iI));
}
if (getReligionCommerce(eIndex) != iNewReligionCommerce)
{
m_aiReligionCommerce[eIndex] = iNewReligionCommerce;
FAssert(getReligionCommerce(eIndex) >= 0);
setCommerceDirty(eIndex);
}
}
void CvCity::updateReligionCommerce()
{
int iI;
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
updateReligionCommerce((CommerceTypes)iI);
}
}
int CvCity::getCorporationYield(YieldTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
return m_aiCorporationYield[eIndex];
}
void CvCity::setCorporationYield(YieldTypes eIndex, int iNewValue)
{
int iOldValue;
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
iOldValue = getCorporationYield(eIndex);
if (iOldValue != iNewValue)
{
m_aiCorporationYield[eIndex] = iNewValue;
FAssert(getCorporationYield(eIndex) >= 0);
changeBaseYieldRate(eIndex, (iNewValue - iOldValue));
}
}
int CvCity::getCorporationCommerce(CommerceTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
return m_aiCorporationCommerce[eIndex];
}
int CvCity::getCorporationYieldByCorporation(YieldTypes eIndex, CorporationTypes eCorporation) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
FAssertMsg(eCorporation >= 0, "eCorporation expected to be >= 0");
FAssertMsg(eCorporation < GC.getNumCorporationInfos(), "GC.getNumCorporationInfos expected to be >= 0");
int iYield = 0;
if (isActiveCorporation(eCorporation) && !isDisorder())
{
for (int i = 0; i < GC.getNUM_CORPORATION_PREREQ_BONUSES(); ++i)
{
BonusTypes eBonus = (BonusTypes)GC.getCorporationInfo(eCorporation).getPrereqBonus(i);
if (NO_BONUS != eBonus && getNumBonuses(eBonus) > 0)
{
iYield += (GC.getCorporationInfo(eCorporation).getYieldProduced(eIndex) * getNumBonuses(eBonus) * GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getCorporationMaintenancePercent()) / 100;
}
}
/************************************************************************************************/
/* Afforess Start 02/09/10 */
/* */
/* */
/************************************************************************************************/
iYield += GC.getCorporationInfo(eCorporation).getYieldChange(eIndex) * 100;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
return (iYield + 99) / 100;
}
int CvCity::getCorporationCommerceByCorporation(CommerceTypes eIndex, CorporationTypes eCorporation) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
FAssertMsg(eCorporation >= 0, "eCorporation expected to be >= 0");
FAssertMsg(eCorporation < GC.getNumCorporationInfos(), "GC.getNumCorporationInfos expected to be >= 0");
int iCommerce = 0;
if (isActiveCorporation(eCorporation) && !isDisorder())
{
for (int i = 0; i < GC.getNUM_CORPORATION_PREREQ_BONUSES(); ++i)
{
BonusTypes eBonus = (BonusTypes)GC.getCorporationInfo(eCorporation).getPrereqBonus(i);
if (NO_BONUS != eBonus && getNumBonuses(eBonus) > 0)
{
iCommerce += (GC.getCorporationInfo(eCorporation).getCommerceProduced(eIndex) * getNumBonuses(eBonus) * GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getCorporationMaintenancePercent()) / 100;
}
}
/************************************************************************************************/
/* Afforess Start 02/09/10 */
/* */
/* */
/************************************************************************************************/
iCommerce += GC.getCorporationInfo(eCorporation).getCommerceChange(eIndex) * 100;
iCommerce *= (GET_TEAM(getTeam()).getCorporationRevenueModifier() + 100);
iCommerce /= 100;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
return (iCommerce + 99) / 100;
}
void CvCity::updateCorporationCommerce(CommerceTypes eIndex)
{
int iNewCommerce = 0;
for (int iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
iNewCommerce += getCorporationCommerceByCorporation(eIndex, ((CorporationTypes)iI));
}
if (getCorporationCommerce(eIndex) != iNewCommerce)
{
m_aiCorporationCommerce[eIndex] = iNewCommerce;
FAssert(getCorporationCommerce(eIndex) >= 0);
setCommerceDirty(eIndex);
}
}
void CvCity::updateCorporationYield(YieldTypes eIndex)
{
int iOldYield = getCorporationYield(eIndex);
int iNewYield = 0;
for (int iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
iNewYield += getCorporationYieldByCorporation(eIndex, (CorporationTypes)iI);
}
if (iOldYield != iNewYield)
{
m_aiCorporationYield[eIndex] = iNewYield;
FAssert(getCorporationYield(eIndex) >= 0);
changeBaseYieldRate(eIndex, (iNewYield - iOldYield));
}
}
void CvCity::updateCorporation()
{
updateCorporationBonus();
updateBuildingCommerce();
for (int iI = 0; iI < NUM_YIELD_TYPES; ++iI)
{
updateCorporationYield((YieldTypes)iI);
}
for (int iI = 0; iI < NUM_COMMERCE_TYPES; ++iI)
{
updateCorporationCommerce((CommerceTypes)iI);
}
setMaintenanceDirty(true);
}
void CvCity::updateCorporationBonus()
{
std::vector<int> aiExtraCorpProducedBonuses;
std::vector<int> aiLastCorpProducedBonuses;
std::vector<bool> abHadBonuses;
for (int iI = 0; iI < GC.getNumBonusInfos(); ++iI)
{
abHadBonuses.push_back(hasBonus((BonusTypes)iI));
m_paiNumCorpProducedBonuses[iI] = 0;
aiLastCorpProducedBonuses.push_back(getNumBonuses((BonusTypes)iI));
aiExtraCorpProducedBonuses.push_back(0);
}
for (int iIter = 0; iIter < GC.getNumCorporationInfos(); ++iIter)
{
for (int iCorp = 0; iCorp < GC.getNumCorporationInfos(); ++iCorp)
{
int iBonusProduced = GC.getCorporationInfo((CorporationTypes)iCorp).getBonusProduced();
if (NO_BONUS != iBonusProduced)
{
if (!GET_TEAM(getTeam()).isBonusObsolete((BonusTypes)iBonusProduced))
{
if (GET_TEAM(getTeam()).isHasTech((TechTypes)(GC.getBonusInfo((BonusTypes)iBonusProduced).getTechCityTrade())))
{
if (isHasCorporation((CorporationTypes)iCorp) && GET_PLAYER(getOwnerINLINE()).isActiveCorporation((CorporationTypes)iCorp))
{
/************************************************************************************************/
/* Afforess Start 07/19/10 */
/* */
/* */
/************************************************************************************************/
bool bConsumes = false;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
for (int i = 0; i < GC.getNUM_CORPORATION_PREREQ_BONUSES(); ++i)
{
int iBonusConsumed = GC.getCorporationInfo((CorporationTypes)iCorp).getPrereqBonus(i);
if (NO_BONUS != iBonusConsumed)
{
/************************************************************************************************/
/* Afforess Start 07/19/10 */
/* */
/* */
/************************************************************************************************/
bConsumes = true;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
aiExtraCorpProducedBonuses[iBonusProduced] += aiLastCorpProducedBonuses[iBonusConsumed];
}
}
/************************************************************************************************/
/* Afforess Start 06/19/10 */
/* */
/* */
/************************************************************************************************/
if (!bConsumes && iBonusProduced != -1)
{
aiExtraCorpProducedBonuses[iBonusProduced] = 1;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
}
}
}
}
bool bChanged = false;
for (int iI = 0; iI < GC.getNumBonusInfos(); ++iI)
{
if (aiExtraCorpProducedBonuses[iI] != 0)
{
m_paiNumCorpProducedBonuses[iI] += aiExtraCorpProducedBonuses[iI];
bChanged = true;
}
aiLastCorpProducedBonuses[iI] = aiExtraCorpProducedBonuses[iI];
aiExtraCorpProducedBonuses[iI] = 0;
}
if (!bChanged)
{
break;
}
FAssertMsg(iIter < GC.getNumCorporationInfos() - 1, "Corporation cyclical resource dependency");
}
for (int iI = 0; iI < GC.getNumBonusInfos(); ++iI)
{
if (abHadBonuses[iI] != hasBonus((BonusTypes)iI))
{
if (hasBonus((BonusTypes)iI))
{
processBonus((BonusTypes)iI, 1);
}
else
{
processBonus((BonusTypes)iI, -1);
}
}
}
}
int CvCity::getCommerceRateModifier(CommerceTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
return m_aiCommerceRateModifier[eIndex];
}
void CvCity::changeCommerceRateModifier(CommerceTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
if (iChange != 0)
{
m_aiCommerceRateModifier[eIndex] = (m_aiCommerceRateModifier[eIndex] + iChange);
setCommerceModifierDirty(eIndex);
AI_setAssignWorkDirty(true);
}
}
int CvCity::getCommerceHappinessPer(CommerceTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
return m_aiCommerceHappinessPer[eIndex];
}
int CvCity::getCommerceHappinessByType(CommerceTypes eCommerce) const
{
return ((getCommerceHappinessPer(eCommerce) * GET_PLAYER(getOwnerINLINE()).getCommercePercent(eCommerce)) / 100);
}
int CvCity::getCommerceHappiness() const
{
int iHappiness;
int iI;
iHappiness = 0;
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
iHappiness += getCommerceHappinessByType((CommerceTypes)iI);
}
return iHappiness;
}
void CvCity::changeCommerceHappinessPer(CommerceTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
if (iChange != 0)
{
m_aiCommerceHappinessPer[eIndex] = (m_aiCommerceHappinessPer[eIndex] + iChange);
FAssert(getCommerceHappinessPer(eIndex) >= 0);
AI_setAssignWorkDirty(true);
}
}
int CvCity::getDomainFreeExperience(DomainTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_DOMAIN_TYPES, "eIndex expected to be < NUM_DOMAIN_TYPES");
return m_aiDomainFreeExperience[eIndex];
}
void CvCity::changeDomainFreeExperience(DomainTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_DOMAIN_TYPES, "eIndex expected to be < NUM_DOMAIN_TYPES");
m_aiDomainFreeExperience[eIndex] = (m_aiDomainFreeExperience[eIndex] + iChange);
FAssert(getDomainFreeExperience(eIndex) >= 0);
}
int CvCity::getDomainProductionModifier(DomainTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_DOMAIN_TYPES, "eIndex expected to be < NUM_DOMAIN_TYPES");
return m_aiDomainProductionModifier[eIndex];
}
void CvCity::changeDomainProductionModifier(DomainTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_DOMAIN_TYPES, "eIndex expected to be < NUM_DOMAIN_TYPES");
m_aiDomainProductionModifier[eIndex] = (m_aiDomainProductionModifier[eIndex] + iChange);
}
int CvCity::getCulture(PlayerTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex expected to be < MAX_PLAYERS");
return (m_aiCulture[eIndex] < 0 ? MAX_INT/100 : m_aiCulture[eIndex] / 100);
}
int CvCity::getCultureTimes100(PlayerTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex expected to be < MAX_PLAYERS");
return (m_aiCulture[eIndex] < 0 ? MAX_INT : m_aiCulture[eIndex]);
}
int CvCity::countTotalCultureTimes100() const
{
int iTotalCulture;
int iI;
iTotalCulture = 0;
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
iTotalCulture += getCultureTimes100((PlayerTypes)iI);
}
}
return iTotalCulture;
}
PlayerTypes CvCity::findHighestCulture() const
{
PlayerTypes eBestPlayer;
int iValue;
int iBestValue;
int iI;
iBestValue = 0;
eBestPlayer = NO_PLAYER;
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
iValue = getCultureTimes100((PlayerTypes)iI);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestPlayer = ((PlayerTypes)iI);
}
}
}
return eBestPlayer;
}
int CvCity::calculateCulturePercent(PlayerTypes eIndex) const
{
int iTotalCulture;
iTotalCulture = countTotalCultureTimes100();
if (iTotalCulture > 0)
{
return ((getCultureTimes100(eIndex) * 100) / iTotalCulture);
}
return 0;
}
int CvCity::calculateTeamCulturePercent(TeamTypes eIndex) const
{
int iTeamCulturePercent;
int iI;
iTeamCulturePercent = 0;
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (GET_PLAYER((PlayerTypes)iI).getTeam() == eIndex)
{
iTeamCulturePercent += calculateCulturePercent((PlayerTypes)iI);
}
}
}
return iTeamCulturePercent;
}
void CvCity::setCulture(PlayerTypes eIndex, int iNewValue, bool bPlots, bool bUpdatePlotGroups)
{
if ( iNewValue > MAX_INT/100 )
{
iNewValue = MAX_INT/100;
}
setCultureTimes100(eIndex, 100 * iNewValue, bPlots, bUpdatePlotGroups);
}
void CvCity::setCultureTimes100(PlayerTypes eIndex, int iNewValue, bool bPlots, bool bUpdatePlotGroups)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex expected to be < MAX_PLAYERS");
int iOldCulture = getCultureTimes100(eIndex);
if (iOldCulture != iNewValue)
{
m_aiCulture[eIndex] = iNewValue;
FAssert(getCultureTimes100(eIndex) >= 0);
updateCultureLevel(bUpdatePlotGroups);
if (bPlots)
{
doPlotCulture(true, eIndex, 0);
}
}
if (iOldCulture < iNewValue)
{
GET_PLAYER(getOwner()).changeCulture((iNewValue - iOldCulture) / 100);
}
}
void CvCity::changeCulture(PlayerTypes eIndex, int iChange, bool bPlots, bool bUpdatePlotGroups)
{
int iOld = getCultureTimes100(eIndex);
int iNew;
if ( iChange > 0 )
{
if ( MAX_INT - 100*iChange > iOld )
{
iNew = iOld + 100*iChange;
}
else
{
iNew = MAX_INT;
}
}
else
{
iNew = iOld + 100 * iChange;
}
setCultureTimes100(eIndex, iNew, bPlots, bUpdatePlotGroups);
}
void CvCity::changeCultureTimes100(PlayerTypes eIndex, int iChange, bool bPlots, bool bUpdatePlotGroups)
{
int iOld = getCultureTimes100(eIndex);
int iNew;
if ( iChange > 0 )
{
if ( MAX_INT - iChange > iOld )
{
iNew = iOld + iChange;
}
else
{
iNew = MAX_INT;
}
}
else
{
iNew = iOld + iChange;
}
setCultureTimes100(eIndex, iNew, bPlots, bUpdatePlotGroups);
}
int CvCity::getNumRevolts(PlayerTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex expected to be < MAX_PLAYERS");
return m_aiNumRevolts[eIndex];
}
void CvCity::changeNumRevolts(PlayerTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex expected to be < MAX_PLAYERS");
m_aiNumRevolts[eIndex] = (m_aiNumRevolts[eIndex] + iChange);
FAssert(getNumRevolts(eIndex) >= 0);
}
int CvCity::getRevoltTestProbability() const
{
int iBestModifier = 0;
CLLNode<IDInfo>* pUnitNode = plot()->headUnitNode();
while (pUnitNode)
{
CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = plot()->nextUnitNode(pUnitNode);
if (pLoopUnit->getRevoltProtection() > iBestModifier)
{
iBestModifier = pLoopUnit->getRevoltProtection();
}
}
iBestModifier = range(iBestModifier, 0, 100);
return ((GC.getDefineINT("REVOLT_TEST_PROB") * (100 - iBestModifier)) / 100);
}
bool CvCity::isEverOwned(PlayerTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex expected to be < MAX_PLAYERS");
return m_abEverOwned[eIndex];
}
void CvCity::setEverOwned(PlayerTypes eIndex, bool bNewValue)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex expected to be < MAX_PLAYERS");
m_abEverOwned[eIndex] = bNewValue;
}
bool CvCity::isTradeRoute(PlayerTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex expected to be < MAX_PLAYERS");
return m_abTradeRoute[eIndex];
}
void CvCity::setTradeRoute(PlayerTypes eIndex, bool bNewValue)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < MAX_PLAYERS, "eIndex expected to be < MAX_PLAYERS");
if (m_abTradeRoute[eIndex] != bNewValue)
{
m_abTradeRoute[eIndex] = bNewValue;
}
}
bool CvCity::isRevealed(TeamTypes eIndex, bool bDebug) const
{
if (bDebug && GC.getGameINLINE().isDebugMode())
{
return true;
}
else
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < MAX_TEAMS, "eIndex expected to be < MAX_TEAMS");
return m_abRevealed[eIndex];
}
}
void CvCity::setRevealed(TeamTypes eIndex, bool bNewValue)
{
CvPlot* pLoopPlot;
int iI;
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < MAX_TEAMS, "eIndex expected to be < MAX_TEAMS");
if (isRevealed(eIndex, false) != bNewValue)
{
m_abRevealed[eIndex] = bNewValue;
updateVisibility();
if (eIndex == GC.getGameINLINE().getActiveTeam())
{
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/17/09 */
/* */
/* */
/************************************************************************************************/
for (iI = 0; iI < getNumCityPlots(); iI++)
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
{
pLoopPlot = getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
pLoopPlot->updateSymbols();
}
}
}
}
}
bool CvCity::getEspionageVisibility(TeamTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < MAX_TEAMS, "eIndex expected to be < MAX_TEAMS");
return m_abEspionageVisibility[eIndex];
}
void CvCity::setEspionageVisibility(TeamTypes eIndex, bool bNewValue, bool bUpdatePlotGroups)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < MAX_TEAMS, "eIndex expected to be < MAX_TEAMS");
if (getEspionageVisibility(eIndex) != bNewValue)
{
plot()->updateSight(false, bUpdatePlotGroups);
m_abEspionageVisibility[eIndex] = bNewValue;
plot()->updateSight(true, bUpdatePlotGroups);
}
}
void CvCity::updateEspionageVisibility(bool bUpdatePlotGroups)
{
std::vector<EspionageMissionTypes> aMission;
for (int iMission = 0; iMission < GC.getNumEspionageMissionInfos(); ++iMission)
{
if (GC.getEspionageMissionInfo((EspionageMissionTypes)iMission).isPassive() && GC.getEspionageMissionInfo((EspionageMissionTypes)iMission).getVisibilityLevel() > 0)
{
aMission.push_back((EspionageMissionTypes)iMission);
}
}
for (int iTeam = 0; iTeam < MAX_CIV_TEAMS; ++iTeam)
{
bool bVisibility = false;
if (iTeam != getTeam())
{
if (isRevealed((TeamTypes)iTeam, false))
{
for (int iPlayer = 0; iPlayer < MAX_CIV_PLAYERS; ++iPlayer)
{
CvPlayer& kPlayer = GET_PLAYER((PlayerTypes)iPlayer);
if (kPlayer.isAlive() && kPlayer.getTeam() == iTeam)
{
for (std::vector<EspionageMissionTypes>::iterator it = aMission.begin(); it != aMission.end(); ++it)
{
if (kPlayer.canDoEspionageMission(*it, getOwnerINLINE(), plot(), -1, NULL))
{
bVisibility = true;
break;
}
}
if (bVisibility)
{
break;
}
}
}
}
}
setEspionageVisibility((TeamTypes)iTeam, bVisibility, bUpdatePlotGroups);
}
}
const wchar* CvCity::getNameKey() const
{
return m_szName;
}
const CvWString CvCity::getName(uint uiForm) const
{
return gDLL->getObjectText(m_szName, uiForm, true);
}
void CvCity::setName(const wchar* szNewValue, bool bFound)
{
CvWString szName(szNewValue);
gDLL->stripSpecialCharacters(szName);
if (!szName.empty())
{
if (GET_PLAYER(getOwnerINLINE()).isCityNameValid(szName, false))
{
m_szName = szName;
setInfoDirty(true);
if (isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(CityScreen_DIRTY_BIT, true);
}
}
if (bFound)
{
doFoundMessage();
}
}
}
void CvCity::doFoundMessage()
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer;
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_CITY_HAS_BEEN_FOUNDED", getNameKey()));
AddDLLMessage(getOwnerINLINE(), false, -1, szBuffer, ARTFILEMGR.getInterfaceArtInfo("WORLDBUILDER_CITY_EDIT")->getPath(), MESSAGE_TYPE_MAJOR_EVENT, NULL, NO_COLOR, getX_INLINE(), getY_INLINE());
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_CITY_IS_FOUNDED", getNameKey()));
GC.getGameINLINE().addReplayMessage(REPLAY_MESSAGE_CITY_FOUNDED, getOwnerINLINE(), szBuffer, getX_INLINE(), getY_INLINE(), (ColorTypes)GC.getInfoTypeForString("COLOR_ALT_HIGHLIGHT_TEXT"));
}
std::string CvCity::getScriptData() const
{
return m_szScriptData;
}
void CvCity::setScriptData(std::string szNewValue)
{
m_szScriptData = szNewValue;
}
int CvCity::getNoBonusCount(BonusTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBonusInfos(), "eIndex expected to be < GC.getNumBonusInfos()");
return m_paiNoBonus[eIndex];
}
bool CvCity::isNoBonus(BonusTypes eIndex) const
{
return (getNoBonusCount(eIndex) > 0);
}
void CvCity::changeNoBonusCount(BonusTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBonusInfos(), "eIndex expected to be < GC.getNumBonusInfos()");
if (iChange != 0)
{
if (getNumBonuses(eIndex) > 0)
{
processBonus(eIndex, -1);
}
m_paiNoBonus[eIndex] += iChange;
FAssert(getNoBonusCount(eIndex) >= 0);
if (getNumBonuses(eIndex) > 0)
{
processBonus(eIndex, 1);
}
updateCorporation();
AI_setAssignWorkDirty(true);
setInfoDirty(true);
}
}
int CvCity::getFreeBonus(BonusTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBonusInfos(), "eIndex expected to be < GC.getNumBonusInfos()");
return m_paiFreeBonus[eIndex];
}
void CvCity::changeFreeBonus(BonusTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBonusInfos(), "eIndex expected to be < GC.getNumBonusInfos()");
if (iChange != 0)
{
GET_PLAYER(getOwnerINLINE()).startDeferredPlotGroupBonusCalculation();
plot()->updatePlotGroupBonus(false);
m_paiFreeBonus[eIndex] += iChange;
FAssert(getFreeBonus(eIndex) >= 0);
plot()->updatePlotGroupBonus(true);
GET_PLAYER(getOwnerINLINE()).endDeferredPlotGroupBonusCalculation();
}
}
int CvCity::getNumBonusesFromBase(BonusTypes eIndex, int iBaseNum) const
{
if (isNoBonus(eIndex))
{
return 0;
}
/************************************************************************************************/
/* Afforess Start 5/30/11 */
/* */
/* */
/************************************************************************************************/
if (GET_PLAYER(getOwnerINLINE()).getBonusMintedPercent(eIndex) > 0)
{
return 0;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return iBaseNum + m_paiNumCorpProducedBonuses[eIndex];
}
int CvCity::getNumBonuses(BonusTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBonusInfos(), "eIndex expected to be < GC.getNumBonusInfos()");
return getNumBonusesFromBase(eIndex, m_paiNumBonuses[eIndex]) + m_paiNumCorpProducedBonuses[eIndex];
}
bool CvCity::hasBonus(BonusTypes eIndex) const
{
return (getNumBonuses(eIndex) > 0);
}
void CvCity::startDeferredBonusProcessing(void)
{
//OutputDebugString(CvString::format("[%d] City (%S): m_deferringBonusProcessingCount (inc)...\n", GetCurrentThreadId(), getName().GetCString()).c_str());
int iCount = InterlockedIncrement((volatile LONG*)&m_deferringBonusProcessingCount);
//OutputDebugString(CvString::format("[%d] Work item %08lx (%S): m_deferringBonusProcessingCount (inc) now %d\n", GetCurrentThreadId(), m_workItem, getName().GetCString(), iCount).c_str());
if ( 1 == iCount )
{
//OutputDebugString("Begin deferred bonus processing\n");
SAFE_DELETE_ARRAY(m_paiStartDeferredSectionNumBonuses);
m_paiStartDeferredSectionNumBonuses = new int[GC.getNumBonusInfos()];
for(int iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
m_paiStartDeferredSectionNumBonuses[iI] = m_paiNumBonuses[iI];
}
}
}
void CvCity::endDeferredBonusProcessing(void)
{
//OutputDebugString(CvString::format("[%d] City (%S): m_deferringBonusProcessingCount (dec)...\n", GetCurrentThreadId(), getName().GetCString()).c_str());
int iCount = InterlockedDecrement((volatile LONG*)&m_deferringBonusProcessingCount);
//OutputDebugString(CvString::format("[%d] Work item %08lx (%S): m_deferringBonusProcessingCount (dec) now %d\n", GetCurrentThreadId(), m_workItem, getName().GetCString(), iCount).c_str());
if ( 0 == iCount )
{
//OutputDebugString("End deferred bonus processing\n");
for(int iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
processNumBonusChange( (BonusTypes)iI, m_paiStartDeferredSectionNumBonuses[iI], m_paiNumBonuses[iI] );
}
SAFE_DELETE_ARRAY(m_paiStartDeferredSectionNumBonuses);
}
}
void CvCity::processNumBonusChange(BonusTypes eIndex, int iOldValue, int iNewValue)
{
if ( iOldValue != iNewValue )
{
bool bOldHasBonus = (getNumBonusesFromBase(eIndex, iOldValue) !=0 );
bool bNewHasBonus = (getNumBonusesFromBase(eIndex, iNewValue) !=0 );
if (bOldHasBonus != bNewHasBonus)
{
if (bNewHasBonus)
{
processBonus(eIndex, 1);
}
else
{
processBonus(eIndex, -1);
}
}
if (isCorporationBonus(eIndex))
{
updateCorporation();
}
// Linking bonuses may change what is buildable
FlushCanConstructCache();
}
}
void CvCity::changeNumBonuses(BonusTypes eIndex, int iChange)
{
PROFILE_FUNC();
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBonusInfos(), "eIndex expected to be < GC.getNumBonusInfos()");
if (iChange != 0)
{
//bool bOldHasBonus = hasBonus(eIndex);
m_paiNumBonuses[eIndex] += iChange;
if ( m_deferringBonusProcessingCount == 0 )
{
processNumBonusChange(eIndex, m_paiNumBonuses[eIndex] - iChange, m_paiNumBonuses[eIndex]);
}
}
}
int CvCity::getNumCorpProducedBonuses(BonusTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBonusInfos(), "eIndex expected to be < GC.getNumBonusInfos()");
return m_paiNumCorpProducedBonuses[eIndex];
}
bool CvCity::isCorporationBonus(BonusTypes eBonus) const
{
FAssert(eBonus >= 0);
FAssert(eBonus < GC.getNumBonusInfos());
for (int iCorp = 0; iCorp < GC.getNumCorporationInfos(); ++iCorp)
{
if (GET_PLAYER(getOwnerINLINE()).isActiveCorporation((CorporationTypes)iCorp))
{
for (int i = 0; i < GC.getNUM_CORPORATION_PREREQ_BONUSES(); ++i)
{
if (NO_BONUS != GC.getCorporationInfo((CorporationTypes)iCorp).getPrereqBonus(i))
{
if (GC.getCorporationInfo((CorporationTypes)iCorp).getPrereqBonus(i) == eBonus && isHasCorporation((CorporationTypes)iCorp))
{
return true;
}
}
}
}
}
return false;
}
bool CvCity::isActiveCorporation(CorporationTypes eCorporation) const
{
FAssert(eCorporation >= 0 && eCorporation < GC.getNumCorporationInfos());
if (!isHasCorporation(eCorporation))
{
return false;
}
if (!GET_PLAYER(getOwnerINLINE()).isActiveCorporation(eCorporation))
{
return false;
}
/************************************************************************************************/
/* Afforess Start 02/17/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getCorporationInfo(eCorporation).getObsoleteTech() != NO_TECH)
{
if (GET_TEAM(getTeam()).isHasTech((TechTypes)GC.getCorporationInfo(eCorporation).getObsoleteTech()))
{
return false;
}
}
bool bRequiresBonus = false;
bool bHasRequiredBonus = false;
for (int i = 0; i < GC.getNUM_CORPORATION_PREREQ_BONUSES(); ++i)
{
BonusTypes eBonus = (BonusTypes)GC.getCorporationInfo(eCorporation).getPrereqBonus(i);
if (NO_BONUS != eBonus)
{
bRequiresBonus = true;
if (getNumBonuses(eBonus) > 0)
{
bHasRequiredBonus = true;
break;
}
}
}
if (bRequiresBonus && bHasRequiredBonus)
{
return true;
}
if (!bRequiresBonus)
{
return true;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return false;
}
int CvCity::getBuildingProduction(BuildingTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
return std::max(0, m_paiBuildingProduction[eIndex]);
}
void CvCity::setBuildingProduction(BuildingTypes eIndex, int iNewValue)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
if (getBuildingProduction(eIndex) != iNewValue)
{
m_paiBuildingProduction[eIndex] = iNewValue;
FAssert(getBuildingProduction(eIndex) >= 0);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
if ((getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) && isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(CityScreen_DIRTY_BIT, true);
}
}
}
void CvCity::changeBuildingProduction(BuildingTypes eIndex, int iChange)
{
setBuildingProduction(eIndex, (getBuildingProduction(eIndex) + iChange));
}
int CvCity::getBuildingProductionTime(BuildingTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
return m_paiBuildingProductionTime[eIndex];
}
void CvCity::setBuildingProductionTime(BuildingTypes eIndex, int iNewValue)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
m_paiBuildingProductionTime[eIndex] = iNewValue;
FAssert(getBuildingProductionTime(eIndex) >= 0);
}
void CvCity::changeBuildingProductionTime(BuildingTypes eIndex, int iChange)
{
setBuildingProductionTime(eIndex, (getBuildingProductionTime(eIndex) + iChange));
}
// BUG - Production Decay - start
/*
* Returns true if the given building will decay this turn.
*/
bool CvCity::isBuildingProductionDecay(BuildingTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
return isHuman() && getProductionBuilding() != eIndex && getBuildingProduction(eIndex) > 0
&& 100 * getBuildingProductionTime(eIndex) >= GC.getDefineINT("BUILDING_PRODUCTION_DECAY_TIME") * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getConstructPercent();
}
/*
* Returns the amount by which the given building will decay once it reaches the limit.
* Ignores whether or not the building will actually decay this turn.
*/
int CvCity::getBuildingProductionDecay(BuildingTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
int iProduction = getBuildingProduction(eIndex);
return iProduction - ((iProduction * GC.getDefineINT("BUILDING_PRODUCTION_DECAY_PERCENT")) / 100);
}
/*
* Returns the number of turns left before the given building will decay.
*/
int CvCity::getBuildingProductionDecayTurns(BuildingTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
return std::max(0, (GC.getDefineINT("BUILDING_PRODUCTION_DECAY_TIME") * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getConstructPercent() + 99) / 100 - getBuildingProductionTime(eIndex)) + 1;
}
// BUG - Production Decay - end
int CvCity::getProjectProduction(ProjectTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumProjectInfos(), "eIndex expected to be < GC.getNumProjectInfos()");
return m_paiProjectProduction[eIndex];
}
void CvCity::setProjectProduction(ProjectTypes eIndex, int iNewValue)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumProjectInfos(), "eIndex expected to be < GC.getNumProjectInfos()");
if (getProjectProduction(eIndex) != iNewValue)
{
m_paiProjectProduction[eIndex] = iNewValue;
FAssert(getProjectProduction(eIndex) >= 0);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
if ((getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) && isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(CityScreen_DIRTY_BIT, true);
}
}
}
void CvCity::changeProjectProduction(ProjectTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumProjectInfos(), "eIndex expected to be < GC.getNumProjectInfos()");
setProjectProduction(eIndex, (getProjectProduction(eIndex) + iChange));
}
int CvCity::getBuildingOriginalOwner(BuildingTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
return m_paiBuildingOriginalOwner[eIndex];
}
int CvCity::getBuildingOriginalTime(BuildingTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
return m_paiBuildingOriginalTime[eIndex];
}
int CvCity::getUnitProduction(UnitTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumUnitInfos(), "eIndex expected to be < GC.getNumUnitInfos()");
return m_paiUnitProduction[eIndex];
}
void CvCity::setUnitProduction(UnitTypes eIndex, int iNewValue)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumUnitInfos(), "eIndex expected to be < GC.getNumUnitInfos()");
if (getUnitProduction(eIndex) != iNewValue)
{
m_paiUnitProduction[eIndex] = iNewValue;
FAssert(getUnitProduction(eIndex) >= 0);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
if ((getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) && isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(CityScreen_DIRTY_BIT, true);
}
}
}
void CvCity::changeUnitProduction(UnitTypes eIndex, int iChange)
{
setUnitProduction(eIndex, (getUnitProduction(eIndex) + iChange));
}
int CvCity::getUnitProductionTime(UnitTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumUnitInfos(), "eIndex expected to be < GC.getNumUnitInfos()");
return m_paiUnitProductionTime[eIndex];
}
void CvCity::setUnitProductionTime(UnitTypes eIndex, int iNewValue)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumUnitInfos(), "eIndex expected to be < GC.getNumUnitInfos()");
m_paiUnitProductionTime[eIndex] = iNewValue;
FAssert(getUnitProductionTime(eIndex) >= 0);
}
void CvCity::changeUnitProductionTime(UnitTypes eIndex, int iChange)
{
setUnitProductionTime(eIndex, (getUnitProductionTime(eIndex) + iChange));
}
// BUG - Production Decay - start
/*
* Returns true if the given unit will decay this turn.
*/
bool CvCity::isUnitProductionDecay(UnitTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumUnitInfos(), "eIndex expected to be < GC.getNumUnitInfos()");
return isHuman() && getProductionUnit() != eIndex && getUnitProduction(eIndex) > 0
&& 100 * getUnitProductionTime(eIndex) >= GC.getDefineINT("UNIT_PRODUCTION_DECAY_TIME") * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getTrainPercent();
}
/*
* Returns the amount by which the given unit will decay once it reaches the limit.
* Ignores whether or not the unit will actually decay this turn.
*/
int CvCity::getUnitProductionDecay(UnitTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumUnitInfos(), "eIndex expected to be < GC.getNumUnitInfos()");
int iProduction = getUnitProduction(eIndex);
return iProduction - ((iProduction * GC.getDefineINT("UNIT_PRODUCTION_DECAY_PERCENT")) / 100);
}
/*
* Returns the number of turns left before the given unit will decay.
*/
int CvCity::getUnitProductionDecayTurns(UnitTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumUnitInfos(), "eIndex expected to be < GC.getNumUnitInfos()");
return std::max(0, (GC.getDefineINT("UNIT_PRODUCTION_DECAY_TIME") * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getTrainPercent() + 99) / 100 - getUnitProductionTime(eIndex)) + 1;
}
// BUG - Production Decay - end
int CvCity::getGreatPeopleUnitRate(UnitTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumUnitInfos(), "eIndex expected to be < GC.getNumUnitInfos()");
return m_paiGreatPeopleUnitRate[eIndex];
}
void CvCity::setGreatPeopleUnitRate(UnitTypes eIndex, int iNewValue)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumUnitInfos(), "eIndex expected to be < GC.getNumUnitInfos()");
if (GC.getGameINLINE().isOption(GAMEOPTION_NO_ESPIONAGE) && GC.getUnitInfo(eIndex).getEspionagePoints() > 0)
{
return;
}
m_paiGreatPeopleUnitRate[eIndex] = iNewValue;
FAssert(getGreatPeopleUnitRate(eIndex) >= 0);
}
void CvCity::changeGreatPeopleUnitRate(UnitTypes eIndex, int iChange)
{
setGreatPeopleUnitRate(eIndex, (getGreatPeopleUnitRate(eIndex) + iChange));
}
int CvCity::getGreatPeopleUnitProgress(UnitTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumUnitInfos(), "eIndex expected to be < GC.getNumUnitInfos()");
return m_paiGreatPeopleUnitProgress[eIndex];
}
void CvCity::setGreatPeopleUnitProgress(UnitTypes eIndex, int iNewValue)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumUnitInfos(), "eIndex expected to be < GC.getNumUnitInfos()");
m_paiGreatPeopleUnitProgress[eIndex] = iNewValue;
FAssert(getGreatPeopleUnitProgress(eIndex) >= 0);
}
void CvCity::changeGreatPeopleUnitProgress(UnitTypes eIndex, int iChange)
{
setGreatPeopleUnitProgress(eIndex, (getGreatPeopleUnitProgress(eIndex) + iChange));
}
int CvCity::getSpecialistCount(SpecialistTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumSpecialistInfos(), "eIndex expected to be < GC.getNumSpecialistInfos()");
return m_paiSpecialistCount[eIndex];
}
void CvCity::setSpecialistCount(SpecialistTypes eIndex, int iNewValue)
{
int iOldValue;
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumSpecialistInfos(), "eIndex expected to be < GC.getNumSpecialistInfos()");
iOldValue = getSpecialistCount(eIndex);
if (iOldValue != iNewValue)
{
m_paiSpecialistCount[eIndex] = iNewValue;
FAssert(getSpecialistCount(eIndex) >= 0);
changeSpecialistPopulation(iNewValue - iOldValue);
processSpecialist(eIndex, (iNewValue - iOldValue));
if (isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(CitizenButtons_DIRTY_BIT, true);
}
#ifdef YIELD_VALUE_CACHING
AI_NoteSpecialistChange();
#endif
}
}
void CvCity::changeSpecialistCount(SpecialistTypes eIndex, int iChange)
{
setSpecialistCount(eIndex, (getSpecialistCount(eIndex) + iChange));
}
void CvCity::alterSpecialistCount(SpecialistTypes eIndex, int iChange)
{
int iI;
if (iChange != 0)
{
if (isCitizensAutomated())
{
if ((getForceSpecialistCount(eIndex) + iChange) < 0)
{
setCitizensAutomated(false);
}
}
if (isCitizensAutomated())
{
changeForceSpecialistCount(eIndex, iChange);
}
else
{
if (iChange > 0)
{
for (iI = 0; iI < iChange; iI++)
{
if ((extraPopulation() > 0) || AI_removeWorstCitizen(eIndex))
{
if (isSpecialistValid(eIndex, 1))
{
changeSpecialistCount(eIndex, 1);
}
}
}
}
else
{
for (iI = 0; iI < -(iChange); iI++)
{
if (getSpecialistCount(eIndex) > 0)
{
changeSpecialistCount(eIndex, -1);
if ((eIndex != GC.getDefineINT("DEFAULT_SPECIALIST")) && (GC.getDefineINT("DEFAULT_SPECIALIST") != NO_SPECIALIST))
{
changeSpecialistCount(((SpecialistTypes)GC.getDefineINT("DEFAULT_SPECIALIST")), 1);
}
else if (extraFreeSpecialists() > 0)
{
AI_addBestCitizen(false, true);
}
else
{
int iNumCanWorkPlots = 0;
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/17/09 */
/* */
/* */
/************************************************************************************************/
for (int iI = 0; iI < getNumCityPlots(); iI++)
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
{
if (iI != CITY_HOME_PLOT)
{
if (!isWorkingPlot(iI))
{
CvPlot* pLoopPlot = getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
if (canWork(pLoopPlot))
{
++iNumCanWorkPlots;
}
}
}
}
}
if (iNumCanWorkPlots > 0)
{
AI_addBestCitizen(true, false);
}
else
{
AI_addBestCitizen(false, true);
}
}
}
}
}
}
}
}
int CvCity::getMaxSpecialistCount(SpecialistTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumSpecialistInfos(), "eIndex expected to be < GC.getNumSpecialistInfos()");
return m_paiMaxSpecialistCount[eIndex];
}
bool CvCity::isSpecialistValid(SpecialistTypes eIndex, int iExtra) const
{
return (((getSpecialistCount(eIndex) + iExtra) <= getMaxSpecialistCount(eIndex)) || GET_PLAYER(getOwnerINLINE()).isSpecialistValid(eIndex) || (eIndex == GC.getDefineINT("DEFAULT_SPECIALIST")));
}
void CvCity::changeMaxSpecialistCount(SpecialistTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumSpecialistInfos(), "eIndex expected to be < GC.getNumSpecialistInfos()");
if (iChange != 0)
{
m_paiMaxSpecialistCount[eIndex] = std::max(0, (m_paiMaxSpecialistCount[eIndex] + iChange));
AI_setAssignWorkDirty(true);
}
}
int CvCity::getForceSpecialistCount(SpecialistTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumSpecialistInfos(), "eIndex expected to be < GC.getNumSpecialistInfos()");
return m_paiForceSpecialistCount[eIndex];
}
bool CvCity::isSpecialistForced() const
{
int iI;
for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
{
if (getForceSpecialistCount((SpecialistTypes)iI) > 0)
{
return true;
}
}
return false;
}
void CvCity::setForceSpecialistCount(SpecialistTypes eIndex, int iNewValue)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumSpecialistInfos(), "eIndex expected to be < GC.getNumSpecialistInfos()");
if (getForceSpecialistCount(eIndex) != iNewValue)
{
m_paiForceSpecialistCount[eIndex] = std::max(0, iNewValue);
if (isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(Help_DIRTY_BIT, true);
}
AI_setAssignWorkDirty(true);
}
}
void CvCity::changeForceSpecialistCount(SpecialistTypes eIndex, int iChange)
{
setForceSpecialistCount(eIndex, (getForceSpecialistCount(eIndex) + iChange));
}
int CvCity::getFreeSpecialistCount(SpecialistTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumSpecialistInfos(), "eIndex expected to be < GC.getNumSpecialistInfos()");
return m_paiFreeSpecialistCount[eIndex];
}
int CvCity::getAddedFreeSpecialistCount(SpecialistTypes eIndex) const
{
int iNumAddedSpecialists = getFreeSpecialistCount(eIndex);
for (int iJ = 0; iJ < GC.getNumBuildingInfos(); ++iJ)
{
CvBuildingInfo& kBuilding = GC.getBuildingInfo((BuildingTypes)iJ);
if (kBuilding.getFreeSpecialistCount(eIndex) > 0)
{
iNumAddedSpecialists -= getNumActiveBuilding((BuildingTypes)iJ) * kBuilding.getFreeSpecialistCount(eIndex);
}
}
FAssert(iNumAddedSpecialists >= 0);
return std::max(0, iNumAddedSpecialists);
}
void CvCity::setFreeSpecialistCount(SpecialistTypes eIndex, int iNewValue)
{
int iOldValue;
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumSpecialistInfos(), "eIndex expected to be < GC.getNumSpecialistInfos()");
iOldValue = getFreeSpecialistCount(eIndex);
/************************************************************************************************/
/* Afforess Start 03/27/10 */
/* */
/* */
/************************************************************************************************/
iNewValue += GET_PLAYER(getOwnerINLINE()).getFreeSpecialistCount(eIndex);
iNewValue += GET_TEAM(getTeam()).getFreeSpecialistCount(eIndex);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (iOldValue != iNewValue)
{
m_paiFreeSpecialistCount[eIndex] = iNewValue;
FAssert(getFreeSpecialistCount(eIndex) >= 0);
changeNumGreatPeople(iNewValue - iOldValue);
processSpecialist(eIndex, (iNewValue - iOldValue));
if (isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(CitizenButtons_DIRTY_BIT, true);
}
}
}
void CvCity::changeFreeSpecialistCount(SpecialistTypes eIndex, int iChange, bool bUnattributed)
{
/************************************************************************************************/
/* Afforess Start 03/27/10 */
/* */
/* */
/************************************************************************************************/
setFreeSpecialistCount(eIndex, (getFreeSpecialistCount(eIndex) + iChange - GET_PLAYER(getOwnerINLINE()).getFreeSpecialistCount(eIndex)) - GET_TEAM(getTeam()).getFreeSpecialistCount(eIndex));
if ( bUnattributed )
{
m_paiFreeSpecialistCountUnattributed[eIndex] += iChange;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
int CvCity::getImprovementFreeSpecialists(ImprovementTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumImprovementInfos(), "eIndex expected to be < GC.getNumImprovementInfos()");
return m_paiImprovementFreeSpecialists[eIndex];
}
void CvCity::changeImprovementFreeSpecialists(ImprovementTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumImprovementInfos(), "eIndex expected to be < GC.getNumImprovementInfos()");
if (iChange != 0)
{
m_paiImprovementFreeSpecialists[eIndex] = std::max(0, (m_paiImprovementFreeSpecialists[eIndex] + iChange));
}
}
int CvCity::getReligionInfluence(ReligionTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumReligionInfos(), "eIndex expected to be < GC.getNumReligionInfos()");
//TB Debug
//Somehow we are getting under 0 values here and that could cause problems down the road
//This method enforces minimum of 0 without changing the actual value of m_paiReligionInfluence[eIndex] as the integrity of that value should be maintained.
int iValue = 0;
if (m_paiReligionInfluence[eIndex] < 0)
{
iValue = 0;
}
else
{
iValue = m_paiReligionInfluence[eIndex];
}
return iValue;
}
void CvCity::changeReligionInfluence(ReligionTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumReligionInfos(), "eIndex expected to be < GC.getNumReligionInfos()");
m_paiReligionInfluence[eIndex] = m_paiReligionInfluence[eIndex] + iChange;
FAssert(getReligionInfluence(eIndex) >= 0);
}
int CvCity::getCurrentStateReligionHappiness() const
{
if (GET_PLAYER(getOwnerINLINE()).getStateReligion() != NO_RELIGION)
{
return getStateReligionHappiness(GET_PLAYER(getOwnerINLINE()).getStateReligion());
}
return 0;
}
int CvCity::getStateReligionHappiness(ReligionTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumReligionInfos(), "eIndex expected to be < GC.getNumReligionInfos()");
return m_paiStateReligionHappiness[eIndex];
}
void CvCity::changeStateReligionHappiness(ReligionTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumReligionInfos(), "eIndex expected to be < GC.getNumReligionInfos()");
if (iChange != 0)
{
m_paiStateReligionHappiness[eIndex] = (m_paiStateReligionHappiness[eIndex] + iChange);
AI_setAssignWorkDirty(true);
}
}
int CvCity::getUnitCombatFreeExperience(UnitCombatTypes eIndex) const
{
FAssertMsg(eIndex >= -1, "eIndex expected to be >= -1");
FAssertMsg(eIndex < GC.getNumUnitCombatInfos(), "eIndex expected to be < GC.getNumUnitCombatInfos()");
if (eIndex == -1) return 0;
return m_paiUnitCombatFreeExperience[eIndex];
}
void CvCity::changeUnitCombatFreeExperience(UnitCombatTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumUnitCombatInfos(), "eIndex expected to be < GC.getNumUnitCombatInfos()");
m_paiUnitCombatFreeExperience[eIndex] = (m_paiUnitCombatFreeExperience[eIndex] + iChange);
FAssert(getUnitCombatFreeExperience(eIndex) >= 0);
}
int CvCity::getFreePromotionCount(PromotionTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumPromotionInfos(), "eIndex expected to be < GC.getNumPromotionInfos()");
//TB Debug
//Somehow we are getting under 0 values here and that could cause problems down the road
//This method enforces minimum of 0 without changing the actual value of m_paiFreePromotionCount[eIndex](particularly puzzling) as the integrity of that value should be maintained.
int iValue = 0;
if (m_paiFreePromotionCount[eIndex] < 0)
{
iValue = 0;
}
else
{
iValue = m_paiFreePromotionCount[eIndex];
}
return iValue;
}
bool CvCity::isFreePromotion(PromotionTypes eIndex) const
{
return (getFreePromotionCount(eIndex) > 0);
}
void CvCity::changeFreePromotionCount(PromotionTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumPromotionInfos(), "eIndex expected to be < GC.getNumPromotionInfos()");
m_paiFreePromotionCount[eIndex] = (m_paiFreePromotionCount[eIndex] + iChange);
FAssert(getFreePromotionCount(eIndex) >= 0);
}
int CvCity::getSpecialistFreeExperience() const
{
return m_iSpecialistFreeExperience;
}
void CvCity::changeSpecialistFreeExperience(int iChange)
{
m_iSpecialistFreeExperience += iChange;
FAssert(m_iSpecialistFreeExperience >= 0);
}
int CvCity::getEspionageDefenseModifier() const
{
return m_iEspionageDefenseModifier;
}
void CvCity::changeEspionageDefenseModifier(int iChange)
{
if (0 != iChange)
{
m_iEspionageDefenseModifier += iChange;
}
}
bool CvCity::isWorkingPlot(int iIndex) const
{
FAssertMsg(iIndex >= 0, "iIndex expected to be >= 0");
FAssertMsg(iIndex < NUM_CITY_PLOTS, "iIndex expected to be < NUM_CITY_PLOTS");
return (m_bPlotWorkingMasked ? 0 : m_pabWorkingPlot[iIndex]);
}
bool CvCity::isWorkingPlot(const CvPlot* pPlot) const
{
int iIndex;
iIndex = getCityPlotIndex(pPlot);
if (iIndex != -1)
{
return isWorkingPlot(iIndex);
}
return false;
}
void CvCity::processWorkingPlot(int iPlot, int iChange, bool yieldsOnly)
{
int iI;
CvPlot* pPlot = getCityIndexPlot(iPlot);
if (pPlot != NULL)
{
FAssertMsg(pPlot->getWorkingCity() == this, "WorkingCity is expected to be this");
if (!yieldsOnly)
{
if (iPlot != CITY_HOME_PLOT)
{
changeWorkingPopulation(iChange);
}
// update plot builder special case where a plot is being worked but is (a) unimproved or (b) un-bonus'ed
pPlot->updatePlotBuilder();
if ((getTeam() == GC.getGameINLINE().getActiveTeam()) || GC.getGameINLINE().isDebugMode())
{
pPlot->updateSymbolDisplay();
}
}
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
changeBaseYieldRate(((YieldTypes)iI), iChange*pPlot->getYield((YieldTypes)iI));
}
}
if (isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true );
gDLL->getInterfaceIFace()->setDirty(CityScreen_DIRTY_BIT, true);
gDLL->getInterfaceIFace()->setDirty(ColoredPlots_DIRTY_BIT, true);
}
#ifdef YIELD_VALUE_CACHING
AI_NoteWorkerChange();
#endif
}
void CvCity::setWorkingPlot(int iIndex, bool bNewValue)
{
FAssertMsg(iIndex >= 0, "iIndex expected to be >= 0");
FAssertMsg(iIndex < NUM_CITY_PLOTS, "iIndex expected to be < NUM_CITY_PLOTS");
if (isWorkingPlot(iIndex) != bNewValue)
{
m_pabWorkingPlot[iIndex] = bNewValue;
processWorkingPlot(iIndex, bNewValue ? 1 : -1);
}
}
void CvCity::setWorkingPlot(CvPlot* pPlot, bool bNewValue)
{
setWorkingPlot(getCityPlotIndex(pPlot), bNewValue);
}
void CvCity::alterWorkingPlot(int iIndex)
{
CvPlot* pPlot;
FAssertMsg(iIndex >= 0, "iIndex expected to be >= 0");
FAssertMsg(iIndex < NUM_CITY_PLOTS, "iIndex expected to be < NUM_CITY_PLOTS");
if (iIndex == CITY_HOME_PLOT)
{
setCitizensAutomated(true);
}
else
{
pPlot = getCityIndexPlot(iIndex);
if (pPlot != NULL)
{
if (canWork(pPlot))
{
setCitizensAutomated(false);
if (isWorkingPlot(iIndex))
{
setWorkingPlot(iIndex, false);
if (GC.getDefineINT("DEFAULT_SPECIALIST") != NO_SPECIALIST)
{
changeSpecialistCount(((SpecialistTypes)GC.getDefineINT("DEFAULT_SPECIALIST")), 1);
}
else
{
AI_addBestCitizen(false, true);
}
}
else
{
if ((extraPopulation() > 0) || AI_removeWorstCitizen())
{
setWorkingPlot(iIndex, true);
}
}
}
else if (pPlot->getOwnerINLINE() == getOwnerINLINE())
{
pPlot->setWorkingCityOverride(this);
}
}
}
}
int CvCity::getNumRealBuilding(BuildingTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
return isDisabledBuilding(eIndex) ? 0 : m_paiNumRealBuilding[eIndex];
}
void CvCity::setNumRealBuilding(BuildingTypes eIndex, int iNewValue)
{
setNumRealBuildingTimed(eIndex, iNewValue, true, getOwnerINLINE(), GC.getGameINLINE().getGameTurnYear());
}
void CvCity::setNumRealBuildingTimed(BuildingTypes eIndex, int iNewValue, bool bFirst, PlayerTypes eOriginalOwner, int iOriginalTime)
{
CvCity* pLoopCity;
CvWString szBuffer;
int iOldNumBuilding;
int iChangeNumRealBuilding;
int iLoop;
int iI;
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
iChangeNumRealBuilding = iNewValue - getNumRealBuilding(eIndex);
if (iChangeNumRealBuilding != 0)
{
iOldNumBuilding = getNumBuilding(eIndex);
if ( m_paiNumRealBuilding[eIndex] != iNewValue )
{
// Changing the buildings in a city invaldiates lots of cached data so flush the caches
if ( GC.getBuildingInfo(eIndex).EnablesOtherBuildings() )
{
AI_FlushBuildingValueCache(true);
FlushCanConstructCache();
}
#ifdef YIELD_VALUE_CACHING
ClearYieldValueCache(); // A new building can change yield rates
#endif
#ifdef CAN_TRAIN_CACHING
// Mark all unit canTrain values cached as dirty
invalidateCachedCanTrainForUnit(NO_UNITCOMBAT);
#endif
}
m_paiNumRealBuilding[eIndex] = iNewValue;
if (getNumRealBuilding(eIndex) > 0)
{
m_paiBuildingOriginalOwner[eIndex] = eOriginalOwner;
m_paiBuildingOriginalTime[eIndex] = iOriginalTime;
}
else
{
m_paiBuildingOriginalOwner[eIndex] = NO_PLAYER;
m_paiBuildingOriginalTime[eIndex] = MIN_INT;
}
if (iOldNumBuilding != getNumBuilding(eIndex))
{
if (getNumRealBuilding(eIndex) > 0)
{
if (GC.getBuildingInfo(eIndex).isStateReligion())
{
for (iI = 0; iI < GC.getNumVoteSourceInfos(); ++iI)
{
if (GC.getBuildingInfo(eIndex).getVoteSourceType() == (VoteSourceTypes)iI)
{
if (GC.getGameINLINE().getVoteSourceReligion((VoteSourceTypes)iI) == NO_RELIGION)
{
FAssert(GET_PLAYER(getOwnerINLINE()).getStateReligion() != NO_RELIGION);
GC.getGameINLINE().setVoteSourceReligion((VoteSourceTypes)iI, GET_PLAYER(getOwnerINLINE()).getStateReligion(), true);
}
}
}
}
}
processBuilding(eIndex, getNumBuilding(eIndex) - iOldNumBuilding);
}
/************************************************************************************************/
/* Afforess Start 06/15/10 */
/* */
/* */
/************************************************************************************************/
//Remove any extensions of this buildingclass
if (iOldNumBuilding > m_paiNumRealBuilding[eIndex])
{
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (getNumRealBuilding((BuildingTypes)iI) > 0 && iI != eIndex)
{
if (GC.getBuildingInfo((BuildingTypes)iI).getExtendsBuildingClass() == GC.getBuildingInfo(eIndex).getBuildingClassType())
{
//avoid infinite recursion
if (GC.getBuildingInfo(eIndex).getExtendsBuildingClass() != GC.getBuildingInfo((BuildingTypes)iI).getBuildingClassType())
{
setNumRealBuilding((BuildingTypes)iI, 0);
}
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (!(GC.getBuildingClassInfo((BuildingClassTypes)(GC.getBuildingInfo(eIndex).getBuildingClassType())).isNoLimit()))
{
if (isWorldWonderClass((BuildingClassTypes)(GC.getBuildingInfo(eIndex).getBuildingClassType())))
{
changeNumWorldWonders(iChangeNumRealBuilding);
}
else if (isTeamWonderClass((BuildingClassTypes)(GC.getBuildingInfo(eIndex).getBuildingClassType())))
{
changeNumTeamWonders(iChangeNumRealBuilding);
}
else if (isNationalWonderClass((BuildingClassTypes)(GC.getBuildingInfo(eIndex).getBuildingClassType())))
{
changeNumNationalWonders(iChangeNumRealBuilding);
}
else
{
changeNumBuildings(iChangeNumRealBuilding);
}
}
if (iChangeNumRealBuilding > 0)
{
if (bFirst)
{
if (GC.getBuildingInfo(eIndex).isCapital())
{
GET_PLAYER(getOwnerINLINE()).setCapitalCity(this);
}
if (GC.getGameINLINE().isFinalInitialized() && !(gDLL->GetWorldBuilderMode()))
{
if (GC.getBuildingInfo(eIndex).isGoldenAge())
{
GET_PLAYER(getOwnerINLINE()).changeGoldenAgeTurns(iChangeNumRealBuilding * (GET_PLAYER(getOwnerINLINE()).getGoldenAgeLength() + 1));
}
if (GC.getBuildingInfo(eIndex).getGlobalPopulationChange() != 0)
{
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (GET_PLAYER((PlayerTypes)iI).getTeam() == getTeam())
{
if (GC.getBuildingInfo(eIndex).isTeamShare() || (iI == getOwnerINLINE()))
{
for (pLoopCity = GET_PLAYER((PlayerTypes)iI).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iI).nextCity(&iLoop))
{
pLoopCity->setPopulation(std::max(1, (pLoopCity->getPopulation() + iChangeNumRealBuilding * GC.getBuildingInfo(eIndex).getGlobalPopulationChange())));
pLoopCity->AI_updateAssignWork(); // so subsequent cities don't starve with the extra citizen working nothing
}
}
}
}
}
}
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (GC.getBuildingInfo(eIndex).getReligionChange(iI) > 0)
{
setHasReligion(((ReligionTypes)iI), true, true, true);
}
}
if (GC.getBuildingInfo(eIndex).getFreeTechs() > 0)
{
if (!isHuman())
{
for (iI = 0; iI < GC.getBuildingInfo(eIndex).getFreeTechs(); iI++)
{
for (int iLoop = 0; iLoop < iChangeNumRealBuilding; iLoop++)
{
GET_PLAYER(getOwnerINLINE()).AI_chooseFreeTech();
}
}
}
else
{
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_COMPLETED_WONDER_CHOOSE_TECH", GC.getBuildingInfo(eIndex).getTextKeyWide()));
GET_PLAYER(getOwnerINLINE()).chooseTech(GC.getBuildingInfo(eIndex).getFreeTechs() * iChangeNumRealBuilding, szBuffer.GetCString());
}
}
if (isWorldWonderClass((BuildingClassTypes)(GC.getBuildingInfo(eIndex).getBuildingClassType())))
{
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_COMPLETES_WONDER", GET_PLAYER(getOwnerINLINE()).getNameKey(), GC.getBuildingInfo(eIndex).getTextKeyWide()));
GC.getGameINLINE().addReplayMessage(REPLAY_MESSAGE_MAJOR_EVENT, getOwnerINLINE(), szBuffer, getX_INLINE(), getY_INLINE(), (ColorTypes)GC.getInfoTypeForString("COLOR_BUILDING_TEXT"));
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
MEMORY_TRACK_EXEMPT();
if (isRevealed(GET_PLAYER((PlayerTypes)iI).getTeam(), false))
{
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_WONDER_COMPLETED", GET_PLAYER(getOwnerINLINE()).getNameKey(), GC.getBuildingInfo(eIndex).getTextKeyWide()));
AddDLLMessage(((PlayerTypes)iI), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_WONDER_BUILDING_BUILD", MESSAGE_TYPE_MAJOR_EVENT, GC.getBuildingInfo(eIndex).getArtInfo()->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_BUILDING_TEXT"), getX_INLINE(), getY_INLINE(), true, true);
}
else
{
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_WONDER_COMPLETED_UNKNOWN", GC.getBuildingInfo(eIndex).getTextKeyWide()));
AddDLLMessage(((PlayerTypes)iI), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_WONDER_BUILDING_BUILD", MESSAGE_TYPE_MAJOR_EVENT, GC.getBuildingInfo(eIndex).getArtInfo()->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_BUILDING_TEXT"));
}
}
}
}
}
GC.getGameINLINE().incrementBuildingClassCreatedCount((BuildingClassTypes)(GC.getBuildingInfo(eIndex).getBuildingClassType()));
}
if (GC.getBuildingInfo(eIndex).isAllowsNukes())
{//TB Nukefix (changed to GET_PLAYER(getOwnerINLINE() rather than GC.getGameINLINE and moved down outside of if bfirst.)
GET_PLAYER(getOwnerINLINE()).makeNukesValid(true);
}
}
//great wall
if (bFirst)
{
if (GC.getBuildingInfo(eIndex).isAreaBorderObstacle())
{
int iCountExisting = 0;
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (eIndex != iI && GC.getBuildingInfo((BuildingTypes)iI).isAreaBorderObstacle())
{
iCountExisting += getNumRealBuilding((BuildingTypes)iI);
}
}
if (iCountExisting == 1 && iNewValue == 0)
{
UIActivityInfo info;
info.eType = UIACTIVITY_PLACE_GREAT_WALL;
info.ePlayer = getOwnerINLINE();
info.iData1 = 0; // Process out
info.iData2 = getID();
GC.getGameINLINE().queueUIActivity(info);
}
else if (iCountExisting == 0 && iNewValue > 0)
{
UIActivityInfo info;
info.eType = UIACTIVITY_PLACE_GREAT_WALL;
info.ePlayer = getOwnerINLINE();
info.iData1 = 1; // Process in
info.iData2 = getID();
GC.getGameINLINE().queueUIActivity(info);
}
}
}
}
}
bool CvCity::processGreatWall(bool bIn, bool bForce, bool bSeeded)
{
bool bHasGreatWall = false;
if ( !bForce && !GC.getReprocessGreatWallDynamically() )
{
return true;
}
if ( bIn || !bSeeded )
{
for(int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if ( getNumRealBuilding((BuildingTypes)iI) > 0 )
{
if ( GC.getBuildingInfo((BuildingTypes)iI).isAreaBorderObstacle())
{
bHasGreatWall = true;
break;
}
}
}
}
else
{
bHasGreatWall = m_bIsGreatWallSeed;
}
if ( bHasGreatWall )
{
CvCity* pUseCity = NULL;
if ( isInViewport() )
{
pUseCity = this;
}
else
{
// Need to find a culturally connected city that IS in the current viewport
int iDummyVal;
CvUnitSelectionCriteria noGrowthCriteria;
noGrowthCriteria.m_bIgnoreGrowth = true;
UnitTypes eDummyUnit = AI_bestUnitAI(UNITAI_ATTACK, iDummyVal, true, true, &noGrowthCriteria);
if ( eDummyUnit == NO_UNIT )
{
eDummyUnit = AI_bestUnitAI(UNITAI_CITY_DEFENSE, iDummyVal, true, true, &noGrowthCriteria);
FAssert(eDummyUnit != NO_UNIT);
}
CvUnit* pTempUnit = GET_PLAYER(getOwnerINLINE()).getTempUnit(eDummyUnit, getX_INLINE(), getY_INLINE());
CvReachablePlotSet plotSet(pTempUnit->getGroup(), MOVE_OUR_TERRITORY, MAX_INT);
for(CvReachablePlotSet::const_iterator itr = plotSet.begin(); itr != plotSet.end(); ++itr)
{
CvCity* pCity = itr.plot()->getPlotCity();
if ( pCity != NULL && pCity->isInViewport() )
{
pUseCity = pCity;
break;
}
}
GET_PLAYER(getOwnerINLINE()).releaseTempUnit();
}
// If no suitable city is within the viewport we'll have to move the viewport
bool bViewportMoved = false;
int iOldViewportXOffset = 0;
int iOldViewportYOffset = 0;
if ( pUseCity == NULL && !bSeeded )
{
pUseCity = this;
bViewportMoved = true;
GC.getCurrentViewport()->getMapOffset(iOldViewportXOffset, iOldViewportYOffset);
GC.getCurrentViewport()->setOffsetToShow(getX_INLINE(), getY_INLINE());
}
// remove or re-add
if ( pUseCity != NULL )
{
if ( bIn )
{
pUseCity->m_bIsGreatWallSeed = true;
gDLL->getEngineIFace()->AddGreatWall(pUseCity);
}
else
{
pUseCity->m_bIsGreatWallSeed = false;
gDLL->getEngineIFace()->RemoveGreatWall(pUseCity);
}
}
if ( bViewportMoved )
{
GC.getCurrentViewport()->setMapOffset(iOldViewportXOffset, iOldViewportYOffset);
}
return true;
}
return false;
}
int CvCity::getNumFreeBuilding(BuildingTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
return m_paiNumFreeBuilding[eIndex];
}
int CvCity::getNumFreeAreaBuilding(BuildingTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
return m_paiNumFreeAreaBuilding[eIndex];
}
int CvCity::getNumFreeTradeRegionBuilding(BuildingTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
return m_paiNumFreeTradeRegionBuilding[eIndex];
}
void CvCity::setNumFreeBuilding(BuildingTypes eIndex, int iNewValue)
{
int iOldNumBuilding;
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
if (getNumFreeBuilding(eIndex) != iNewValue)
{
iOldNumBuilding = getNumBuilding(eIndex);
m_paiNumFreeBuilding[eIndex] = iNewValue;
if (iOldNumBuilding != getNumBuilding(eIndex))
{
processBuilding(eIndex, iNewValue - iOldNumBuilding);
}
}
}
void CvCity::setNumFreeAreaBuilding(BuildingTypes eIndex, int iNewValue)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
if (getNumFreeAreaBuilding(eIndex) != iNewValue)
{
bool bHad = (getNumBuilding(eIndex) > 0);
m_paiNumFreeAreaBuilding[eIndex] = iNewValue;
bool bHas = (getNumBuilding(eIndex) > 0);
if (bHas != bHad)
{
processBuilding(eIndex, bHas ? 1 : -1);
}
}
}
void CvCity::setNumFreeTradeRegionBuilding(BuildingTypes eIndex, int iNewValue)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
if (getNumFreeTradeRegionBuilding(eIndex) != iNewValue)
{
bool bHad = (getNumBuilding(eIndex) > 0);
m_paiNumFreeTradeRegionBuilding[eIndex] = iNewValue;
bool bHas = (getNumBuilding(eIndex) > 0);
if (bHas != bHad)
{
processBuilding(eIndex, bHas ? 1 : -1);
}
}
}
void CvCity::beginDeferredFreeTradeRegionBuildingProcessing()
{
FAssert(!m_bFreeTradeBuildingProcessingDeferred);
m_bFreeTradeBuildingProcessingDeferred = true;
m_paiDeferredNumFreeTradeRegionBuilding = new int[GC.getNumBuildingInfos()];
for(int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
m_paiDeferredNumFreeTradeRegionBuilding[iI] = (m_paiNumFreeTradeRegionBuilding == NULL ? 0 : m_paiNumFreeTradeRegionBuilding[iI]);
}
}
void CvCity::endDeferredFreeTradeRegionBuildingProcessing()
{
FAssert(m_bFreeTradeBuildingProcessingDeferred);
for(int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
// Can go negative in games that started under assets that didn't have the same
// free building sets as current assets - prevent this
setNumFreeTradeRegionBuilding((BuildingTypes)iI, std::max(0,(int)m_paiDeferredNumFreeTradeRegionBuilding[iI]));
}
SAFE_DELETE_ARRAY(m_paiDeferredNumFreeTradeRegionBuilding);
m_bFreeTradeBuildingProcessingDeferred = false;
}
void CvCity::changeNumFreeTradeRegionBuilding(BuildingTypes eIndex, int iChange)
{
if ( m_bFreeTradeBuildingProcessingDeferred )
{
int iOldValue;
do
{
iOldValue = m_paiDeferredNumFreeTradeRegionBuilding[eIndex];
} while(InterlockedCompareExchange((volatile LONG*)&m_paiDeferredNumFreeTradeRegionBuilding[eIndex], iOldValue + iChange, iOldValue) != iOldValue);
}
else
{
// Can go negative in games that started under assets that didn't have the same
// free building sets as current assets - prevent this
setNumFreeTradeRegionBuilding(eIndex, std::max(0,getNumFreeTradeRegionBuilding(eIndex) + iChange));
}
}
bool CvCity::isHasReligion(ReligionTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumReligionInfos(), "eIndex expected to be < GC.getNumReligionInfos()");
return m_pabHasReligion[eIndex];
}
void CvCity::applyReligionModifiers(ReligionTypes eIndex, bool bValue)
{
for (int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (GC.getBuildingInfo((BuildingTypes)iI).getPrereqReligion() == eIndex)
{
if (bValue && isDisabledBuilding((BuildingTypes)iI))
{
setDisabledBuilding((BuildingTypes)iI, false);
}
else if (!bValue && getNumBuilding((BuildingTypes)iI) > 0)
{
setDisabledBuilding((BuildingTypes)iI, true);
}
}
}
setMaintenanceDirty(true);
updateReligionHappiness();
updateReligionCommerce();
}
void CvCity::setHasReligion(ReligionTypes eIndex, bool bNewValue, bool bAnnounce, bool bArrows)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumReligionInfos(), "eIndex expected to be < GC.getNumReligionInfos()");
if (isHasReligion(eIndex) != bNewValue)
{
for (int iVoteSource = 0; iVoteSource < GC.getNumVoteSourceInfos(); ++iVoteSource)
{
processVoteSourceBonus((VoteSourceTypes)iVoteSource, false);
}
m_pabHasReligion[eIndex] = bNewValue;
for (int iVoteSource = 0; iVoteSource < GC.getNumVoteSourceInfos(); ++iVoteSource)
{
processVoteSourceBonus((VoteSourceTypes)iVoteSource, true);
}
GET_PLAYER(getOwnerINLINE()).changeHasReligionCount(eIndex, ((isHasReligion(eIndex)) ? 1 : -1));
// Religion changes may change what is buildable
FlushCanConstructCache();
AI_setAssignWorkDirty(true);
setInfoDirty(true);
if (isHasReligion(eIndex))
{
GC.getGameINLINE().makeReligionFounded(eIndex, getOwnerINLINE());
if (bAnnounce)
{
if (GC.getGameINLINE().getHolyCity(eIndex) != this)
{
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (isRevealed(GET_PLAYER((PlayerTypes)iI).getTeam(), false))
{
if ((getOwnerINLINE() == iI) || (GET_PLAYER((PlayerTypes)iI).getStateReligion() == eIndex) || GET_PLAYER((PlayerTypes)iI).hasHolyCity(eIndex))
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_RELIGION_SPREAD", GC.getReligionInfo(eIndex).getTextKeyWide(), getNameKey()));
AddDLLMessage(((PlayerTypes)iI), false, GC.getDefineINT("EVENT_MESSAGE_TIME_LONG"), szBuffer, GC.getReligionInfo(eIndex).getSound(), MESSAGE_TYPE_MAJOR_EVENT, GC.getReligionInfo(eIndex).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), getX_INLINE(), getY_INLINE(), bArrows, bArrows);
}
}
}
}
}
if (isHuman())
{
if (GET_PLAYER(getOwnerINLINE()).getHasReligionCount(eIndex) == 1)
{
if (GET_PLAYER(getOwnerINLINE()).canConvert(eIndex) && (GET_PLAYER(getOwnerINLINE()).getStateReligion() == NO_RELIGION))
{
CvPopupInfo* pInfo = new CvPopupInfo(BUTTONPOPUP_CHANGERELIGION);
if (NULL != pInfo)
{
pInfo->setData1(eIndex);
gDLL->getInterfaceIFace()->addPopup(pInfo, getOwnerINLINE());
}
}
}
}
}
}
/************************************************************************************************/
/* Afforess Start 06/24/10 */
/* */
/* */
/************************************************************************************************/
else
{
if (bAnnounce)
{
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (isRevealed(GET_PLAYER((PlayerTypes)iI).getTeam(), false))
{
if ((getOwnerINLINE() == iI) || (GET_PLAYER((PlayerTypes)iI).getStateReligion() == eIndex) || GET_PLAYER((PlayerTypes)iI).hasHolyCity(eIndex))
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_RELIGION_DECAY", getNameKey(), GC.getReligionInfo(eIndex).getTextKeyWide()));
AddDLLMessage(((PlayerTypes)iI), false, GC.getDefineINT("EVENT_MESSAGE_TIME_LONG"), szBuffer, GC.getReligionInfo(eIndex).getSound(), MESSAGE_TYPE_MAJOR_EVENT, GC.getReligionInfo(eIndex).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE(), bArrows, bArrows);
}
}
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (bNewValue)
{
// Python Event
GC.getGameINLINE().reportEvent(CITY_EVENT_RELIGION_SPREAD, getOwnerINLINE(), eIndex, getID());
}
else
{
// Python Event
GC.getGameINLINE().reportEvent(CITY_EVENT_RELIGION_REMOVE, getOwnerINLINE(), eIndex, getID());
}
applyReligionModifiers(eIndex, bNewValue);
}
/************************************************************************************************/
/* RevolutionDCM Inquisitions 29/05/10 phungus420 */
/* */
/* */
/************************************************************************************************/
int iI;
for(iI = 0; iI < MAX_PLAYERS; iI++)
{
if(!(GET_PLAYER((PlayerTypes)iI)).isHuman())
{
if( getTeam() == GET_PLAYER((PlayerTypes)iI).getTeam()
|| GET_TEAM(getTeam()).isVassal(GET_PLAYER((PlayerTypes)iI).getTeam()) )
{
GET_PLAYER((PlayerTypes)iI).AI_setHasInquisitionTarget();
}
}
}
/************************************************************************************************/
/* RevolutionDCM END */
/************************************************************************************************/
}
void CvCity::processVoteSourceBonus(VoteSourceTypes eVoteSource, bool bActive)
{
if (!GET_PLAYER(getOwnerINLINE()).isLoyalMember(eVoteSource))
{
return;
}
if (GC.getGameINLINE().isDiploVote(eVoteSource))
{
ReligionTypes eReligion = GC.getGameINLINE().getVoteSourceReligion(eVoteSource);
SpecialistTypes eSpecialist = (SpecialistTypes)GC.getVoteSourceInfo(eVoteSource).getFreeSpecialist();
if (NO_SPECIALIST != eSpecialist)
{
if (NO_RELIGION == eReligion || isHasReligion(eReligion))
{
changeFreeSpecialistCount(eSpecialist, bActive ? 1 : -1);
}
}
if (NO_RELIGION != eReligion && isHasReligion(eReligion))
{
for (int iYield = 0; iYield < NUM_YIELD_TYPES; ++iYield)
{
int iChange = GC.getVoteSourceInfo(eVoteSource).getReligionYield(iYield);
if (!bActive)
{
iChange = -iChange;
}
if (0 != iChange)
{
for (int iBuilding = 0; iBuilding < GC.getNumBuildingInfos(); ++iBuilding)
{
if (GC.getBuildingInfo((BuildingTypes)iBuilding).getReligionType() == eReligion)
{
changeBuildingYieldChange((BuildingClassTypes)GC.getBuildingInfo((BuildingTypes)iBuilding).getBuildingClassType(), (YieldTypes)iYield, iChange);
}
}
}
}
for (int iCommerce = 0; iCommerce < NUM_COMMERCE_TYPES; ++iCommerce)
{
int iChange = GC.getVoteSourceInfo(eVoteSource).getReligionCommerce(iCommerce);
if (!bActive)
{
iChange = -iChange;
}
if (0 != iChange)
{
for (int iBuilding = 0; iBuilding < GC.getNumBuildingInfos(); ++iBuilding)
{
if (GC.getBuildingInfo((BuildingTypes)iBuilding).getReligionType() == eReligion)
{
changeBuildingCommerceChange((BuildingClassTypes)GC.getBuildingInfo((BuildingTypes)iBuilding).getBuildingClassType(), (CommerceTypes)iCommerce, iChange);
}
}
}
}
}
}
}
bool CvCity::isHasCorporation(CorporationTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumCorporationInfos(), "eIndex expected to be < GC.getNumCorporationInfos()");
return m_pabHasCorporation[eIndex];
}
void CvCity::applyCorporationModifiers(CorporationTypes eIndex, bool bValue)
{
for (int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (GC.getBuildingInfo((BuildingTypes)iI).getPrereqCorporation() == eIndex)
{
if (bValue && isDisabledBuilding((BuildingTypes)iI))
{
setDisabledBuilding((BuildingTypes)iI, false);
}
else if (!bValue && getNumBuilding((BuildingTypes)iI) > 0)
{
setDisabledBuilding((BuildingTypes)iI, true);
}
}
}
changeMilitaryProductionModifier(GC.getCorporationInfo(eIndex).getMilitaryProductionModifier() * (bValue ? 1 : -1));
changeFreeExperience(GC.getCorporationInfo(eIndex).getFreeXP() * (bValue ? 1 : -1));
CvCity* pHeadquarters = GC.getGameINLINE().getHeadquarters(eIndex);
if (NULL != pHeadquarters)
{
pHeadquarters->updateCorporation();
}
updateCorporation();
}
void CvCity::setHasCorporation(CorporationTypes eIndex, bool bNewValue, bool bAnnounce, bool bArrows)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumCorporationInfos(), "eIndex expected to be < GC.getNumCorporationInfos()");
if (isHasCorporation(eIndex) != bNewValue)
{
if (bNewValue)
{
bool bReplacedHeadquarters = false;
for (int iCorp = 0; iCorp < GC.getNumCorporationInfos(); ++iCorp)
{
if (iCorp != eIndex && isHasCorporation((CorporationTypes)iCorp))
{
if (GC.getGameINLINE().isCompetingCorporation((CorporationTypes)iCorp, eIndex))
{
if (GC.getGameINLINE().getHeadquarters((CorporationTypes)iCorp) == this)
{
GC.getGameINLINE().replaceCorporation((CorporationTypes)iCorp, eIndex);
bReplacedHeadquarters = true;
}
else
{
setHasCorporation((CorporationTypes)iCorp, false, false);
}
}
}
}
if (bReplacedHeadquarters)
{
return; // already set the corporation in this city
}
}
m_pabHasCorporation[eIndex] = bNewValue;
GET_PLAYER(getOwnerINLINE()).changeHasCorporationCount(eIndex, ((isHasCorporation(eIndex)) ? 1 : -1));
applyCorporationModifiers(eIndex, bNewValue);
AI_setAssignWorkDirty(true);
setInfoDirty(true);
if (isHasCorporation(eIndex))
{
GC.getGameINLINE().makeCorporationFounded(eIndex, getOwnerINLINE());
}
if (bAnnounce)
{
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (getOwnerINLINE() == iI || GET_PLAYER((PlayerTypes)iI).hasHeadquarters(eIndex))
{
/************************************************************************************************/
/* Afforess Start 06/17/10 */
/* */
/* */
/************************************************************************************************/
if (bNewValue)
{
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_CORPORATION_SPREAD", GC.getCorporationInfo(eIndex).getTextKeyWide(), getNameKey()));
AddDLLMessage(((PlayerTypes)iI), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getCorporationInfo(eIndex).getSound(), MESSAGE_TYPE_MAJOR_EVENT, GC.getCorporationInfo(eIndex).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), getX_INLINE(), getY_INLINE(), bArrows, bArrows);
}
if (getOwnerINLINE() == iI)
{
CvWStringBuffer szBonusString;
GAMETEXT.setCorporationHelpCity(szBonusString, eIndex, this);
CvWString szBonusList;
bool bFirst = true;
for (int iJ = 0; iJ < GC.getNUM_CORPORATION_PREREQ_BONUSES(); ++iJ)
{
int iBonus = GC.getCorporationInfo(eIndex).getPrereqBonus(iJ);
if (iBonus != NO_BONUS)
{
CvWString szTemp;
szTemp.Format(L"%s", GC.getBonusInfo((BonusTypes)iBonus).getDescription());
setListHelp(szBonusList, L"", szTemp, L", ", bFirst);
bFirst = false;
}
}
if (!bFirst)
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_CORPORATION_SPREAD_BONUS", GC.getCorporationInfo(eIndex).getTextKeyWide(), szBonusString.getCString(), getNameKey(), szBonusList.GetCString()));
AddDLLMessage(((PlayerTypes)iI), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getCorporationInfo(eIndex).getSound(), MESSAGE_TYPE_MINOR_EVENT, GC.getCorporationInfo(eIndex).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), getX_INLINE(), getY_INLINE(), bArrows, bArrows);
}
}
/************************************************************************************************/
/* Afforess Start 06/17/10 */
/* */
/* */
/************************************************************************************************/
}
else
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_CORPORATION_DECAY", GC.getCorporationInfo(eIndex).getTextKeyWide(), getNameKey()));
AddDLLMessage(((PlayerTypes)iI), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getCorporationInfo(eIndex).getSound(), MESSAGE_TYPE_MAJOR_EVENT, GC.getCorporationInfo(eIndex).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE(), bArrows, bArrows);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
}
}
}
if (bNewValue)
{
// Python Event
GC.getGameINLINE().reportEvent(CITY_EVENT_CORP_SPREAD, getOwnerINLINE(), eIndex, getID());
}
else
{
// Python Event
GC.getGameINLINE().reportEvent(CITY_EVENT_CORP_REMOVE, getOwnerINLINE(), eIndex, getID());
}
}
}
CvCity* CvCity::getTradeCity(int iIndex) const
{
if (m_paTradeCities == NULL)
{
return NULL;
}
FAssert(iIndex >= 0);
FAssert(iIndex < GC.getMAX_TRADE_ROUTES());
return getCity(m_paTradeCities[iIndex]);
}
int CvCity::getTradeRoutes() const
{
if (GC.getMAX_TRADE_ROUTES() <= 0)
{
return 0;
}
int iTradeRoutes;
iTradeRoutes = GC.getGameINLINE().getTradeRoutes();
iTradeRoutes += GET_PLAYER(getOwnerINLINE()).getTradeRoutes();
/************************************************************************************************/
/* Afforess Start 01/03/10 */
/* */
/* */
/************************************************************************************************/
iTradeRoutes += GET_PLAYER(getOwnerINLINE()).getWorldTradeRoutes();
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (isCoastal(GC.getMIN_WATER_SIZE_FOR_OCEAN()))
{
iTradeRoutes += GET_PLAYER(getOwnerINLINE()).getCoastalTradeRoutes();
}
iTradeRoutes += getExtraTradeRoutes();
return std::min(iTradeRoutes, GC.getMAX_TRADE_ROUTES());
}
void CvCity::clearTradeRoutes()
{
CvCity* pLoopCity;
int iI;
for (iI = 0; iI < GC.getMAX_TRADE_ROUTES(); iI++)
{
pLoopCity = getTradeCity(iI);
if (pLoopCity != NULL)
{
pLoopCity->setTradeRoute(getOwnerINLINE(), false);
}
m_paTradeCities[iI].reset();
}
}
// XXX eventually, this needs to be done when roads are built/destroyed...
void CvCity::updateTradeRoutes()
{
PROFILE_FUNC();
if (GC.getMAX_TRADE_ROUTES() <= 0)
{
return;
}
int* paiBestValue;
CvCity* pLoopCity;
int iTradeRoutes;
int iTradeProfit;
int iValue;
int iLoop;
int iI, iJ, iK;
paiBestValue = new int[GC.getMAX_TRADE_ROUTES()];
for (iI = 0; iI < GC.getMAX_TRADE_ROUTES(); iI++)
{
paiBestValue[iI] = 0;
}
clearTradeRoutes();
if (!isDisorder() && !isPlundered() && !isBlockaded())
{
iTradeRoutes = getTradeRoutes();
FAssert(iTradeRoutes <= GC.getMAX_TRADE_ROUTES());
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER(getOwnerINLINE()).canHaveTradeRoutesWith((PlayerTypes)iI))
{
for (pLoopCity = GET_PLAYER((PlayerTypes)iI).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iI).nextCity(&iLoop))
{
if (pLoopCity != this)
{
if (!(pLoopCity->isTradeRoute(getOwnerINLINE())) || (getTeam() == GET_PLAYER((PlayerTypes)iI).getTeam()))
{
if (pLoopCity->plotGroup(getOwnerINLINE()) == plotGroup(getOwnerINLINE()) || GC.getDefineINT("IGNORE_PLOT_GROUP_FOR_TRADE_ROUTES"))
{
// BUG - Fractional Trade Routes - start
#ifdef _MOD_FRACTRADE
iValue = calculateTradeProfitTimes100(pLoopCity);
#else
iValue = calculateTradeProfit(pLoopCity);
#endif
// BUG - Fractional Trade Routes - end
for (iJ = 0; iJ < iTradeRoutes; iJ++)
{
//Afforess: Trade fairness fix (see: http://realmsbeyond.net/forums/showthread.php?tid=4353&pid=192636#pid192636 )
if (iValue > paiBestValue[iJ] || iValue == paiBestValue[iJ] && (getTeam() == GET_PLAYER((PlayerTypes)iI).getTeam()))
{
for (iK = (iTradeRoutes - 1); iK > iJ; iK--)
{
paiBestValue[iK] = paiBestValue[(iK - 1)];
m_paTradeCities[iK] = m_paTradeCities[(iK - 1)];
}
paiBestValue[iJ] = iValue;
m_paTradeCities[iJ] = pLoopCity->getIDInfo();
break;
}
}
}
}
}
}
}
}
}
iTradeProfit = 0;
for (iI = 0; iI < GC.getMAX_TRADE_ROUTES(); iI++)
{
pLoopCity = getTradeCity(iI);
if (pLoopCity != NULL)
{
pLoopCity->setTradeRoute(getOwnerINLINE(), true);
// BUG - Fractional Trade Routes - start
#ifdef _MOD_FRACTRADE
iTradeProfit += calculateTradeProfitTimes100(pLoopCity);
#else
iTradeProfit += calculateTradeProfit(pLoopCity);
#endif
// BUG - Fractional Trade Routes - end
}
}
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
// BUG - Fractional Trade Routes - start
#ifdef _MOD_FRACTRADE
setTradeYield(((YieldTypes)iI), calculateTradeYield(((YieldTypes)iI), iTradeProfit) / 100); // XXX could take this out if handled when CvPlotGroup changes...
#else
setTradeYield(((YieldTypes)iI), calculateTradeYield(((YieldTypes)iI), iTradeProfit)); // XXX could take this out if handled when CvPlotGroup changes...
#endif
// BUG - Fractional Trade Routes - end
}
SAFE_DELETE_ARRAY(paiBestValue);
}
void CvCity::clearOrderQueue()
{
while (headOrderQueueNode() != NULL)
{
popOrder(0, false, false, false);
}
if ((getTeam() == GC.getGameINLINE().getActiveTeam()) || GC.getGameINLINE().isDebugMode())
{
setInfoDirty(true);
}
}
bool CvCity::pushFirstValidBuildListOrder(int iListID)
{
CvPlayerAI& kPlayer = GET_PLAYER(getOwner());
int index = kPlayer.m_pBuildLists->getIndexByID(iListID);
if (index < 0)
return false;
int iNum = kPlayer.m_pBuildLists->getListLength(index);
for (int i=0; i<iNum; i++)
{
OrderData* pOrder = kPlayer.m_pBuildLists->getOrder(index,i);
if (canContinueProduction(*pOrder))
{
pushOrder(pOrder->eOrderType, pOrder->iData1, pOrder->iData2, pOrder->bSave, false, false);
return true;
}
}
return false;
}
void CvCity::setProposedOrder(OrderTypes eOrder, int iData1, int iData2)
{
if(m_workItem != NULL)
{
m_workItem->setProposedProduction(eOrder, iData1, iData2);
}
else
{
pushOrder(eOrder, iData1, iData2, false, false, false);
}
}
bool CvCity::pushOrder(OrderTypes eOrder, int iData1, int iData2, bool bSave, bool bPop, bool bAppend, bool bForce, CvPlot* deliveryDestination, UnitAITypes contractedAIType, byte contractFlags)
{
OrderData order;
bool bValid;
//bool bBuildingUnit = false;
//bool bBuildingBuilding = false;
if (bPop)
{
popOrder(0, false, false, false);
}
bValid = false;
switch (eOrder)
{
case ORDER_TRAIN:
if (canTrain((UnitTypes)iData1) || bForce)
{
unsigned short iAIType = EXTERNAL_ORDER_IDATA(iData2);
unsigned short iAux;
if ( iAIType == 0xFFFF )
{
iAIType = (short)GC.getUnitInfo((UnitTypes)iData1).getDefaultUnitAIType();
iAux = ((contractFlags & 0xFF) << 8) + 0xFF;
}
else
{
FAssert(contractedAIType < 0xFF);
iAux = ((contractFlags & 0xFF) << 8) + (short)(contractedAIType & 0xFF);
}
iData2 = PACK_INTERNAL_ORDER_IDATA(iAIType, iAux);
if ( deliveryDestination != NULL )
{
int iPlotIndex = GC.getMapINLINE().plotNumINLINE(deliveryDestination->getX_INLINE(), deliveryDestination->getY_INLINE());
FAssert(iPlotIndex <= 0xFFFF);
iData1 = PACK_INTERNAL_ORDER_IDATA(iData1, iPlotIndex);
}
else
{
iData1 = PACK_INTERNAL_ORDER_IDATA(iData1, 0xFFFF);
}
GET_PLAYER(getOwnerINLINE()).changeUnitClassMaking(((UnitClassTypes)(GC.getUnitInfo((UnitTypes)EXTERNAL_ORDER_IDATA(iData1)).getUnitClassType())), 1);
area()->changeNumTrainAIUnits(getOwnerINLINE(), ((UnitAITypes)EXTERNAL_ORDER_IDATA(iData2)), 1);
GET_PLAYER(getOwnerINLINE()).AI_changeNumTrainAIUnits(((UnitAITypes)EXTERNAL_ORDER_IDATA(iData2)), 1);
bValid = true;
//bBuildingUnit = true;
CvEventReporter::getInstance().cityBuildingUnit(this, (UnitTypes)EXTERNAL_ORDER_IDATA(iData1));
setUnitListInvalid();
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 10/02/09 jdog5000 */
/* */
/* AI logging */
/************************************************************************************************/
if( gCityLogLevel >= 1 )
{
CvWString szString;
getUnitAIString(szString, (UnitAITypes)EXTERNAL_ORDER_IDATA(iData2));
logBBAIForTeam(getTeam(), " City %S pushes production of unit %S with UNITAI %S", getName().GetCString(), GC.getUnitInfo((UnitTypes)EXTERNAL_ORDER_IDATA(iData1)).getDescription(getCivilizationType()), szString.GetCString() );
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
break;
case ORDER_CONSTRUCT:
if (canConstruct((BuildingTypes)iData1) || bForce)
{
NoteBuildingNoLongerConstructable((BuildingTypes)iData1);
GET_PLAYER(getOwnerINLINE()).changeBuildingClassMaking(((BuildingClassTypes)(GC.getBuildingInfo((BuildingTypes) iData1).getBuildingClassType())), 1);
bValid = true;
//bBuildingBuilding = true;
CvEventReporter::getInstance().cityBuildingBuilding(this, (BuildingTypes)iData1);
setBuildingListInvalid();
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 10/02/09 jdog5000 */
/* */
/* AI logging */
/************************************************************************************************/
if( gCityLogLevel >= 1 )
{
logBBAIForTeam(getTeam(), " City %S pushes production of building %S", getName().GetCString(), GC.getBuildingInfo((BuildingTypes)iData1).getDescription() );
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
break;
case ORDER_CREATE:
if (canCreate((ProjectTypes)iData1) || bForce)
{
GET_TEAM(getTeam()).changeProjectMaking(((ProjectTypes)iData1), 1);
bValid = true;
// BUG - Project Started Event - start
CvEventReporter::getInstance().cityBuildingProject(this, (ProjectTypes)iData1);
// BUG - Project Started Event - end
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 10/02/09 jdog5000 */
/* */
/* AI logging */
/************************************************************************************************/
if( gCityLogLevel >= 1 )
{
logBBAIForTeam(getTeam(), " City %S pushes production of project %S", getName().GetCString(), GC.getProjectInfo((ProjectTypes)iData1).getDescription() );
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
break;
case ORDER_MAINTAIN:
if (canMaintain((ProcessTypes)iData1) || bForce)
{
bValid = true;
// BUG - Process Started Event - start
CvEventReporter::getInstance().cityBuildingProcess(this, (ProcessTypes)iData1);
// BUG - Process Started Event - end
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 10/02/09 jdog5000 */
/* */
/* AI logging */
/************************************************************************************************/
if( gCityLogLevel >= 1 )
{
logBBAIForTeam(getTeam(), " City %S pushes production of process %S", getName().GetCString(), GC.getProcessInfo((ProcessTypes)iData1).getDescription() );
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
}
break;
case ORDER_LIST:
bValid = true;
break;
default:
FAssertMsg(false, "iOrder did not match a valid option");
break;
}
if (!bValid)
{
return false;
}
order.eOrderType = eOrder;
order.iData1 = iData1;
order.iData2 = iData2;
order.bSave = bSave;
if (bAppend)
{
m_orderQueue.insertAtEnd(order);
}
else
{
stopHeadOrder();
m_orderQueue.insertAtBeginning(order);
}
if (!bAppend || (getOrderQueueLength() == 1))
{
// If the head order is a build list, resolve it
if (eOrder == ORDER_LIST)
{
if (!pushFirstValidBuildListOrder(iData1))
{
// pop the list if there is nothing to construct on it any more
popOrder(0);
}
else
{
if (!bSave)
{
popOrder(1);
}
}
}
else
{
startHeadOrder();
}
}
// Why does this cause a crash???
/* if (bBuildingUnit)
{
CvEventReporter::getInstance().cityBuildingUnit(this, (UnitTypes)iData1);
}
else if (bBuildingBuilding)
{
CvEventReporter::getInstance().cityBuildingBuilding(this, (BuildingTypes)iData1);
}*/
if ((getTeam() == GC.getGameINLINE().getActiveTeam()) || GC.getGameINLINE().isDebugMode())
{
setInfoDirty(true);
if (isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true );
gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
gDLL->getInterfaceIFace()->setDirty(CityScreen_DIRTY_BIT, true);
gDLL->getInterfaceIFace()->setDirty(PlotListButtons_DIRTY_BIT, true);
}
}
return true;
}
void CvCity::completeOrderProcessing(const OrderData* pOrderData)
{
int iUnitId = -1;
PROFILE_FUNC();
switch (pOrderData->eOrderType)
{
case ORDER_TRAIN:
{
UnitTypes eTrainUnit = ((UnitTypes)EXTERNAL_ORDER_IDATA(pOrderData->iData1));
UnitAITypes eTrainAIUnit = ((UnitAITypes)EXTERNAL_ORDER_IDATA(pOrderData->iData2));
FAssertMsg(eTrainUnit != NO_UNIT, "eTrainUnit is expected to be assigned a valid unit type");
FAssertMsg(eTrainAIUnit != NO_UNITAI, "eTrainAIUnit is expected to be assigned a valid unit AI type");
GET_PLAYER(getOwnerINLINE()).changeUnitClassMaking(((UnitClassTypes)(GC.getUnitInfo(eTrainUnit).getUnitClassType())), -1);
area()->changeNumTrainAIUnits(getOwnerINLINE(), eTrainAIUnit, -1);
GET_PLAYER(getOwnerINLINE()).AI_changeNumTrainAIUnits(eTrainAIUnit, -1);
AI_trained(eTrainUnit, eTrainAIUnit);
if( gCityLogLevel >= 2 )
{
logBBAIForTeam(getTeam(), " City %S builds unit %S",
getName().GetCString(),
GC.getUnitInfo(eTrainUnit).getDescription());
}
CvUnit* pUnit = GET_PLAYER(getOwnerINLINE()).initUnit(eTrainUnit, getX_INLINE(), getY_INLINE(), eTrainAIUnit, NO_DIRECTION, getCitySorenRandNum(10000, "AI Unit Birthmark"));
FAssertMsg(pUnit != NULL, "pUnit is expected to be assigned a valid unit object");
/************************************************************************************************/
/* Afforess Start 06/22/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_MAX_UNITS_PER_TILES))
{
if (!pUnit->canMoveInto(plot(), false, false, false, false, true))
{
pUnit->jumpToNearestValidPlot(false);
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
pUnit->finishMoves();
addProductionExperience(pUnit);
short iPlotIndex = INTERNAL_AUXILIARY_ORDER_IDATA(pOrderData->iData1);
int iFlags;
CvPlot* pRallyPlot;
if ( iPlotIndex != (short)0xFFFF )
{
iFlags = MOVE_NO_ENEMY_TERRITORY;
pRallyPlot = GC.getMapINLINE().plotByIndexINLINE(iPlotIndex);
if( pRallyPlot != NULL && gUnitLogLevel >= 3 )
{
logBBAIForTeam(getTeam(), " New unit %S at (%d,%d) headed to contractual delivery plot (%d,%d)",
pUnit->getUnitInfo().getDescription(),
plot()->getX_INLINE(),
plot()->getY_INLINE(),
pRallyPlot->getX_INLINE(),
pRallyPlot->getY_INLINE());
}
}
else
{
iFlags = 0;
pRallyPlot = getRallyPlot();
}
if (pRallyPlot != NULL)
{
bool bIsUnitMission = (((INTERNAL_AUXILIARY_ORDER_IDATA(pOrderData->iData2) >> 8) & AUX_CONTRACT_FLAG_IS_UNIT_CONTRACT) != 0);
if (pRallyPlot != plot())
{
pUnit->getGroup()->pushMission(MISSION_MOVE_TO,
pRallyPlot->getX_INLINE(),
pRallyPlot->getY_INLINE(),
iFlags,
false,
false,
bIsUnitMission ? MISSIONAI_CONTRACT_UNIT : MISSIONAI_CONTRACT,
pRallyPlot);
}
else
{
pUnit->getGroup()->AI_setMissionAI(bIsUnitMission ? MISSIONAI_CONTRACT_UNIT : MISSIONAI_CONTRACT, plot(), NULL);
}
}
/************************************************************************************************/
/* Afforess Start 09/15/10 */
/* */
/* There seems to be an issue with AI missionaries not working correctly - forcing automation */
/* is a kludgy way to fix this */
/************************************************************************************************/
if (!isHuman())
{
pUnit->automate(AUTOMATE_RELIGION);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (isHuman())
{
if (GET_PLAYER(getOwnerINLINE()).isOption(PLAYEROPTION_START_AUTOMATED))
{
pUnit->automate(AUTOMATE_BUILD);
}
if (GET_PLAYER(getOwnerINLINE()).isOption(PLAYEROPTION_MISSIONARIES_AUTOMATED))
{
pUnit->automate(AUTOMATE_RELIGION);
}
/************************************************************************************************/
/* Afforess Start 01/14/10 */
/* */
/* */
/************************************************************************************************/
if (GET_PLAYER(getOwnerINLINE()).isOption(PLAYEROPTION_MODDER_2))
{
CvPlot* pPlot = plot();
if (pPlot != NULL)
{
if (pUnit->canSleep(pPlot))
{
pUnit->getGroup()->setActivityType(ACTIVITY_SLEEP);
}
else if (pUnit->canFortify(pPlot))
{
pUnit->getGroup()->setActivityType(ACTIVITY_SLEEP);
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
#ifdef CAN_TRAIN_CACHING
// Training a unit can mean we might not be abel to build any more of them
// so clear its entry in the canTrain cache to force recalculation
invalidateCachedCanTrainForUnit(pUnit->getUnitCombatType());
//TB SubCombat Mod begin
int iI;
UnitCombatTypes eSubCombatType;
for (iI = 0; iI < pUnit->getUnitInfo().getNumSubCombatTypes(); iI++)
{
eSubCombatType = ((UnitCombatTypes)pUnit->getUnitInfo().getSubCombatType(iI));
invalidateCachedCanTrainForUnit(eSubCombatType);
}
//TB SubCombat Mod end
#endif
// KOSHLING - must not hold onto the pointer after the Python call or
// a crash occurs if that Python decides to destroy the just-built unit
int iUnitID = pUnit->getID();
iUnitId = pUnit->getID();
// Python may have destroyed the unit we just built so refind by id
pUnit = GET_PLAYER(getOwnerINLINE()).getUnit(iUnitID);
if ( pUnit != NULL )
{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 10/02/09 jdog5000 */
/* */
/* AI logging */
/************************************************************************************************/
if( gCityLogLevel >= 1 )
{
CvWString szString;
getUnitAIString(szString, pUnit->AI_getUnitAIType());
logBBAIForTeam(getTeam(), " City %S finishes production of unit %S with UNITAI %S", getName().GetCString(), pUnit->getName(0).GetCString(), szString.GetCString() );
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
if (GC.getUnitInfo(eTrainUnit).getDomainType() == DOMAIN_AIR)
{
if (plot()->countNumAirUnits(getTeam()) > getAirUnitCapacity(getTeam()))
{
pUnit->jumpToNearestValidPlot(); // can destroy unit
}
}
}
}
break;
case ORDER_CONSTRUCT:
{
BuildingTypes eConstructBuilding = ((BuildingTypes)(pOrderData->iData1));
GET_PLAYER(getOwnerINLINE()).changeBuildingClassMaking(((BuildingClassTypes)(GC.getBuildingInfo(eConstructBuilding).getBuildingClassType())), -1);
setNumRealBuilding(eConstructBuilding, getNumRealBuilding(eConstructBuilding) + 1);
if( gCityLogLevel >= 2 )
{
logBBAIForTeam(getTeam(), " City %S builds building %S",
getName().GetCString(),
GC.getBuildingInfo(eConstructBuilding).getDescription());
}
}
break;
case ORDER_CREATE:
{
ProjectTypes eCreateProject = ((ProjectTypes)(pOrderData->iData1));
GET_TEAM(getTeam()).changeProjectMaking(eCreateProject, -1);
OutputDebugString(CvString::format("Project %d (%S) built\n", eCreateProject, GC.getProjectInfo(eCreateProject).getDescription()).c_str());
GET_TEAM(getTeam()).changeProjectCount(eCreateProject, 1);
if (GC.getProjectInfo(eCreateProject).isSpaceship())
{
bool needsArtType = true;
VictoryTypes eVictory = (VictoryTypes)GC.getProjectInfo(eCreateProject).getVictoryPrereq();
if (NO_VICTORY != eVictory && GET_TEAM(getTeam()).canLaunch(eVictory))
{
if (isHuman())
{
CvPopupInfo* pInfo = NULL;
if (GC.getGameINLINE().isNetworkMultiPlayer())
{
pInfo = new CvPopupInfo(BUTTONPOPUP_LAUNCH, GC.getProjectInfo(eCreateProject).getVictoryPrereq());
}
else
{
pInfo = new CvPopupInfo(BUTTONPOPUP_PYTHON_SCREEN, eCreateProject);
pInfo->setText(L"showSpaceShip");
needsArtType = false;
}
gDLL->getInterfaceIFace()->addPopup(pInfo, getOwnerINLINE());
}
else
{
GET_PLAYER(getOwnerINLINE()).AI_launch(eVictory);
}
}
else
{
//show the spaceship progress
if(isHuman())
{
if(!GC.getGameINLINE().isNetworkMultiPlayer())
{
CvPopupInfo* pInfo = new CvPopupInfo(BUTTONPOPUP_PYTHON_SCREEN, eCreateProject);
pInfo->setText(L"showSpaceShip");
gDLL->getInterfaceIFace()->addPopup(pInfo, getOwnerINLINE());
needsArtType = false;
}
}
}
if(needsArtType)
{
int defaultArtType = GET_TEAM(getTeam()).getProjectDefaultArtType(eCreateProject);
int projectCount = GET_TEAM(getTeam()).getProjectCount(eCreateProject);
GET_TEAM(getTeam()).setProjectArtType(eCreateProject, projectCount - 1, defaultArtType);
}
}
}
break;
default:
break;
}
OrderData completed = *pOrderData;
completed.iData2 = iUnitId;
m_completedOrders.push_back(completed);
}
void CvCity::completeOrderProcessing(void)
{
for( std::vector<OrderData>::iterator itr = m_inProcessOrders.begin(); itr != m_inProcessOrders.end(); ++itr )
{
OrderData order = *itr;
(*itr).eOrderType = NO_ORDER;
completeOrderProcessing(&order);
}
m_inProcessOrders.clear();
}
void CvCity::popOrder(int iNum, bool bFinish, bool bChoose, bool bResolveList, bool bPipelineCompletion)
{
PROFILE_FUNC();
CLLNode<OrderData>* pOrderNode;
wchar szBuffer[1024];
wchar szTempBuffer[1024];
TCHAR szSound[1024];
ProjectTypes eCreateProject;
BuildingTypes eConstructBuilding;
UnitTypes eTrainUnit;
UnitAITypes eTrainAIUnit;
bool bWasFoodProduction;
bool bStart;
bool bMessage;
int iCount;
int iProductionNeeded;
//int iOverflow;
bWasFoodProduction = isFoodProduction();
if (iNum == -1)
{
iNum = (getOrderQueueLength() - 1);
}
iCount = 0;
pOrderNode = headOrderQueueNode();
while (pOrderNode != NULL)
{
if (iCount == iNum)
{
break;
}
iCount++;
pOrderNode = nextOrderQueueNode(pOrderNode);
}
if (pOrderNode == NULL)
{
return;
}
if (bFinish && pOrderNode->m_data.bSave)
{
pushOrder(pOrderNode->m_data.eOrderType, EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1), EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData2), true, false, true);
}
eTrainUnit = NO_UNIT;
eConstructBuilding = NO_BUILDING;
eCreateProject = NO_PROJECT;
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
eTrainUnit = ((UnitTypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1));
eTrainAIUnit = ((UnitAITypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData2));
FAssertMsg(eTrainUnit != NO_UNIT, "eTrainUnit is expected to be assigned a valid unit type");
FAssertMsg(eTrainAIUnit != NO_UNITAI, "eTrainAIUnit is expected to be assigned a valid unit AI type");
if ( !bFinish )
{
GET_PLAYER(getOwnerINLINE()).changeUnitClassMaking(((UnitClassTypes)(GC.getUnitInfo(eTrainUnit).getUnitClassType())), -1);
area()->changeNumTrainAIUnits(getOwnerINLINE(), eTrainAIUnit, -1);
GET_PLAYER(getOwnerINLINE()).AI_changeNumTrainAIUnits(eTrainAIUnit, -1);
}
setUnitListInvalid();
if (bFinish)
{
iProductionNeeded = getProductionNeeded(eTrainUnit);
// max overflow is the value of the item produced (to eliminate prebuild exploits)
/************************************************************************************************/
/* Afforess Multiple Production Mod 08/23/09 */
/* */
/* */
/************************************************************************************************/
// iOverflow = getUnitProduction(eTrainUnit) - iProductionNeeded;
int iUnlimitedOverflow = getUnitProduction(eTrainUnit) - iProductionNeeded;
/************************************************************************************************/
/* Afforess Multiple Production Mod END */
/************************************************************************************************/
int iMaxOverflow = std::max(iProductionNeeded, getCurrentProductionDifference(false, false));
/************************************************************************************************/
/* Afforess Multiple Production Mod 08/23/09 */
/* */
/* */
/************************************************************************************************/
// int iMaxOverflowForGold = std::max(iProductionNeeded, getProductionDifference(getProductionNeeded(), getProduction(), 0, isFoodProduction(), false));
// iOverflow = std::min(iMaxOverflow, iOverflow);
int iLostProduction = std::max(0, iUnlimitedOverflow - iMaxOverflow);
m_iLostProductionModified = iLostProduction;
m_iLostProductionBase = (100 * iLostProduction) / std::max(1, getBaseYieldRateModifier(YIELD_PRODUCTION, getProductionModifier(eTrainUnit)));
int iOverflow = std::min(iMaxOverflow, iUnlimitedOverflow);
/************************************************************************************************/
/* Afforess Multiple Production Mod END */
/************************************************************************************************/
if (iOverflow > 0)
{
if (GC.getGameINLINE().isOption(GAMEOPTION_MULTIPLE_PRODUCTION))
{
setOverflowProduction(getOverflowProduction() + iOverflow);
}
else
{
changeOverflowProduction(iOverflow, getProductionModifier(eTrainUnit));
}
}
setUnitProduction(eTrainUnit, 0);
// Unofficial Patch Start
// * Limited which production modifiers affect gold from production overflow. 1/3
iLostProduction *= getBaseYieldRateModifier(YIELD_PRODUCTION);
iLostProduction /= std::max(1, getBaseYieldRateModifier(YIELD_PRODUCTION, getProductionModifier(eTrainUnit)));
// Unofficial Patch End
/************************************************************************************************/
/* Afforess Multiple Production Mod 08/23/09 */
/* */
/* */
/************************************************************************************************/
// int iProductionGold = std::max(0, iOverflow - iMaxOverflowForGold) * GC.getDefineINT("MAXED_UNIT_GOLD_PERCENT") / 100;
int iProductionGold = ((iLostProduction * GC.getDefineINT("MAXED_UNIT_GOLD_PERCENT")) / 100);
m_iGoldFromLostProduction = iProductionGold;
/************************************************************************************************/
/* Afforess Multiple Production Mod END */
/************************************************************************************************/
/************************************************************************************************/
/* Afforess Multiple Production Mod 08/23/09 */
/* */
/* */
/************************************************************************************************/
// if (iProductionGold > 0)
// {
// GET_PLAYER(getOwnerINLINE()).changeGold(iProductionGold);
// }
/************************************************************************************************/
/* Afforess Multiple Production Mod END */
/************************************************************************************************/
}
break;
case ORDER_CONSTRUCT:
eConstructBuilding = ((BuildingTypes)(pOrderNode->m_data.iData1));
if ( !bFinish )
{
GET_PLAYER(getOwnerINLINE()).changeBuildingClassMaking(((BuildingClassTypes)(GC.getBuildingInfo(eConstructBuilding).getBuildingClassType())), -1);
}
if (bFinish)
{
/*************************************************************************************************/
/* UNOFFICIAL_PATCH 10/08/09 davidlallen & jdog5000 */
/* */
/* Bugfix */
/*************************************************************************************************/
/* original bts code
if (GET_PLAYER(getOwnerINLINE()).isBuildingClassMaxedOut(((BuildingClassTypes)(GC.getBuildingInfo(eConstructBuilding).getBuildingClassType())), 1))
*/
if (GET_PLAYER(getOwnerINLINE()).isBuildingClassMaxedOut(((BuildingClassTypes)(GC.getBuildingInfo(eConstructBuilding).getBuildingClassType())), GC.getBuildingClassInfo((BuildingClassTypes)(GC.getBuildingInfo(eConstructBuilding).getBuildingClassType())).getExtraPlayerInstances()))
/*************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/*************************************************************************************************/
{
GET_PLAYER(getOwnerINLINE()).removeBuildingClass((BuildingClassTypes)(GC.getBuildingInfo(eConstructBuilding).getBuildingClassType()));
}
iProductionNeeded = getProductionNeeded(eConstructBuilding);
// max overflow is the value of the item produced (to eliminate prebuild exploits)
/************************************************************************************************/
/* Afforess Multiple Production Mod 08/23/09 */
/* */
/* */
/************************************************************************************************/
// int iOverflow = getBuildingProduction(eConstructBuilding) - iProductionNeeded;
int iUnlimitedOverflow = getBuildingProduction(eConstructBuilding) - iProductionNeeded;
/************************************************************************************************/
/* Afforess Multiple Production Mod END */
/************************************************************************************************/
int iMaxOverflow = std::max(iProductionNeeded, getCurrentProductionDifference(false, false));
/************************************************************************************************/
/* Afforess Multiple Production Mod 08/23/09 */
/* */
/* */
/************************************************************************************************/
// int iMaxOverflowForGold = std::max(iProductionNeeded, getProductionDifference(getProductionNeeded(), getProduction(), 0, isFoodProduction(), false));
// iOverflow = std::min(iMaxOverflow, iOverflow);
int iLostProduction = std::max(0, iUnlimitedOverflow - iMaxOverflow);
m_iLostProductionModified = iLostProduction;
m_iLostProductionBase = (100 * iLostProduction) / std::max(1, getBaseYieldRateModifier(YIELD_PRODUCTION, getProductionModifier(eConstructBuilding)));
int iOverflow = std::min(iMaxOverflow, iUnlimitedOverflow);
/************************************************************************************************/
/* Afforess Multiple Production Mod END */
/************************************************************************************************/
if (iOverflow > 0)
{
if (GC.getGameINLINE().isOption(GAMEOPTION_MULTIPLE_PRODUCTION))
{
setOverflowProduction(getOverflowProduction() + iOverflow);
}
else
{
changeOverflowProduction(iOverflow, getProductionModifier(eConstructBuilding));
}
}
setBuildingProduction(eConstructBuilding, 0);
/*************************************************************************************************/
/* UNOFFICIAL_PATCH 06/10/10 EmperorFool */
/* */
/* Bugfix */
/*************************************************************************************************/
setBuildingProductionTime(eConstructBuilding, 0);
/*************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/*************************************************************************************************/
// Unofficial Patch Start
// * Limited which production modifiers affect gold from production overflow. 2/3
iLostProduction *= getBaseYieldRateModifier(YIELD_PRODUCTION);
iLostProduction /= std::max(1, getBaseYieldRateModifier(YIELD_PRODUCTION, getProductionModifier(eConstructBuilding)));
// Unofficial Patch End
/************************************************************************************************/
/* Afforess Multiple Production Mod 08/23/09 */
/* */
/* */
/************************************************************************************************/
// int iProductionGold = std::max(0, iOverflow - iMaxOverflowForGold) * GC.getDefineINT("MAXED_BUILDING_GOLD_PERCENT") / 100;
int iProductionGold = ((iLostProduction * GC.getDefineINT("MAXED_BUILDING_GOLD_PERCENT")) / 100);
m_iGoldFromLostProduction = iProductionGold;
// if (iProductionGold > 0)
// {
// GET_PLAYER(getOwnerINLINE()).changeGold(iProductionGold);
// }
/************************************************************************************************/
/* Afforess Multiple Production Mod END */
/************************************************************************************************/
}
/************************************************************************************************/
/* Afforess Start 06/13/10 */
/* */
/* */
/************************************************************************************************/
else if (!canConstruct(eConstructBuilding))
{
if (GC.getBuildingInfo(eConstructBuilding).getProductionContinueBuildingClass() != NO_BUILDINGCLASS)
{
BuildingTypes eBuilding = (BuildingTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings((BuildingClassTypes)GC.getBuildingInfo(eConstructBuilding).getProductionContinueBuildingClass()));
if (eBuilding != NO_BUILDING)
{
if (canConstruct(eBuilding, true, false, false, false))
{
if (m_iLostProduction == 0)
{
m_iLostProduction = getBuildingProduction(eConstructBuilding);
setBuildingProduction(eConstructBuilding, 0);
}
if (m_iLostProduction > 0)
{
MEMORY_TRACK_EXEMPT();
setBuildingProduction(eBuilding, m_iLostProduction);
CvWString szMessage;
szMessage = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_PROD_CONVERTED", m_iLostProduction, GC.getBuildingInfo(eConstructBuilding).getTextKeyWide(), GC.getBuildingInfo(eBuilding).getTextKeyWide()));
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szMessage, "AS2D_WONDERGOLD", MESSAGE_TYPE_MINOR_EVENT, GC.getYieldInfo(YIELD_PRODUCTION).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), true, true);
m_iLostProduction = 0;
}
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
FlushCanConstructCache(eConstructBuilding); // Flush value for this building
setBuildingListInvalid();
break;
case ORDER_CREATE:
eCreateProject = ((ProjectTypes)(pOrderNode->m_data.iData1));
if ( !bFinish )
{
GET_TEAM(getTeam()).changeProjectMaking(eCreateProject, -1);
}
if (bFinish)
{
iProductionNeeded = getProductionNeeded(eCreateProject);
// max overflow is the value of the item produced (to eliminate pre-build exploits)
/************************************************************************************************/
/* Afforess Multiple Production Mod 08/23/09 */
/* */
/* */
/************************************************************************************************/
// iOverflow = getProjectProduction(eCreateProject) - iProductionNeeded;
int iUnlimitedOverflow = getProjectProduction(eCreateProject) - iProductionNeeded;
/************************************************************************************************/
/* Afforess Multiple Production Mod END */
/************************************************************************************************/
int iMaxOverflow = std::max(iProductionNeeded, getCurrentProductionDifference(false, false));
/************************************************************************************************/
/* Afforess Multiple Production Mod 08/23/09 */
/* */
/* */
/************************************************************************************************/
// int iMaxOverflowForGold = std::max(iProductionNeeded, getProductionDifference(getProductionNeeded(), getProduction(), 0, isFoodProduction(), false));
// iOverflow = std::min(iMaxOverflow, iOverflow);
int iLostProduction = std::max(0, iUnlimitedOverflow - iMaxOverflow);
m_iLostProductionModified = iLostProduction;
m_iLostProductionBase = (100 * iLostProduction) / std::max(1, getBaseYieldRateModifier(YIELD_PRODUCTION, getProductionModifier(eCreateProject)));
int iOverflow = std::min(iMaxOverflow, iUnlimitedOverflow);
/************************************************************************************************/
/* Afforess Multiple Production Mod END */
/************************************************************************************************/
if (iOverflow > 0)
{
if (GC.getGameINLINE().isOption(GAMEOPTION_MULTIPLE_PRODUCTION))
{
setOverflowProduction(getOverflowProduction() + iOverflow);
}
else
{
changeOverflowProduction(iOverflow, getProductionModifier(eCreateProject));
}
}
setProjectProduction(eCreateProject, 0);
// Unofficial Patch Start
// * Limited which production modifiers affect gold from production overflow. 3/3
iLostProduction *= getBaseYieldRateModifier(YIELD_PRODUCTION);
iLostProduction /= std::max(1, getBaseYieldRateModifier(YIELD_PRODUCTION, getProductionModifier(eCreateProject)));
// Unofficial Patch End
/************************************************************************************************/
/* Afforess Multiple Production Mod 08/23/09 */
/* */
/* */
/************************************************************************************************/
// int iProductionGold = std::max(0, iOverflow - iMaxOverflowForGold) * GC.getDefineINT("MAXED_PROJECT_GOLD_PERCENT") / 100;
int iProductionGold = ((iLostProduction * GC.getDefineINT("MAXED_PROJECT_GOLD_PERCENT")) / 100);
m_iGoldFromLostProduction = iProductionGold;
// if (iProductionGold > 0)
// {
// GET_PLAYER(getOwnerINLINE()).changeGold(iProductionGold);
// }
/************************************************************************************************/
/* Afforess Multiple Production Mod END */
/************************************************************************************************/
}
break;
case ORDER_MAINTAIN:
case ORDER_LIST:
break;
default:
FAssertMsg(false, "pOrderNode->m_data.eOrderType is not a valid option");
break;
}
if ( bFinish )
{
if ( bPipelineCompletion )
{
m_inProcessOrders.push_back(pOrderNode->m_data);
}
else
{
completeOrderProcessing(&pOrderNode->m_data);
}
}
if (pOrderNode == headOrderQueueNode())
{
bStart = true;
stopHeadOrder();
}
else
{
bStart = false;
}
m_orderQueue.deleteNode(pOrderNode);
pOrderNode = NULL;
if (bStart)
{
startHeadOrder();
}
//if ((getTeam() == GC.getGameINLINE().getActiveTeam()) || GC.getGameINLINE().isDebugMode())
//{
// setInfoDirty(true);
// if (isCitySelected())
// {
// gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true );
// gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
// //gDLL->getInterfaceIFace()->setDirty(CityScreen_DIRTY_BIT, true);
// }
//}
bMessage = false;
if (bResolveList)
{
// Check if head of queue is a build list and resolve it in that case
pOrderNode = headOrderQueueNode();
if (pOrderNode)
{
if (pOrderNode->m_data.eOrderType == ORDER_LIST)
{
bool bSave = pOrderNode->m_data.bSave;
int iData1 = pOrderNode->m_data.iData1;
if (!pushFirstValidBuildListOrder(iData1))
{
// pop the list if there is nothing to construct on it any more
popOrder(0);
}
else
{
if (!bSave)
{
popOrder(1);
}
}
}
}
}
if (bChoose)
{
if (getOrderQueueLength() == 0)
{
if (!isHuman() || isProductionAutomated())
{
//AI_chooseProduction();
}
else
{
if (bWasFoodProduction)
{
AI_assignWorkingPlots();
}
chooseProduction(eTrainUnit, eConstructBuilding, eCreateProject, bFinish);
//Calvitix : why can't we have a message when production is done, even if we have to choose a new one ?
//It can be interesting for logging, and will not annoy a lot with 'spamming' messages.
//bMessage = true;
}
}
}
LPCSTR szIcon = NULL;
if (bFinish && !bMessage)
{
if (eTrainUnit != NO_UNIT)
{
swprintf(szBuffer, DLL_SERIALIZE(gDLL->getText(((isLimitedUnitClass((UnitClassTypes)(GC.getUnitInfo(eTrainUnit).getUnitClassType()))) ? "TXT_KEY_MISC_TRAINED_UNIT_IN_LIMITED" : "TXT_KEY_MISC_TRAINED_UNIT_IN"), GC.getUnitInfo(eTrainUnit).getTextKeyWide(), getNameKey()).GetCString()));
strcpy( szSound, GC.getUnitInfo(eTrainUnit).getArtInfo(0,GET_PLAYER(getOwnerINLINE()).getCurrentEra(), NO_UNIT_ARTSTYLE)->getTrainSound() );
szIcon = GET_PLAYER(getOwnerINLINE()).getUnitButton(eTrainUnit);
}
else if (eConstructBuilding != NO_BUILDING)
{
swprintf(szBuffer, DLL_SERIALIZE(gDLL->getText(((isLimitedWonderClass((BuildingClassTypes)(GC.getBuildingInfo(eConstructBuilding).getBuildingClassType()))) ? "TXT_KEY_MISC_CONSTRUCTED_BUILD_IN_LIMITED" : "TXT_KEY_MISC_CONSTRUCTED_BUILD_IN"), GC.getBuildingInfo(eConstructBuilding).getTextKeyWide(), getNameKey()).GetCString()));
strcpy(szSound, GC.getBuildingInfo(eConstructBuilding).getConstructSound());
szIcon = GC.getBuildingInfo(eConstructBuilding).getButton();
}
else if (eCreateProject != NO_PROJECT)
{
swprintf(szBuffer, DLL_SERIALIZE(gDLL->getText(((isLimitedProject(eCreateProject)) ? "TXT_KEY_MISC_CREATED_PROJECT_IN_LIMITED" : "TXT_KEY_MISC_CREATED_PROJECT_IN"), GC.getProjectInfo(eCreateProject).getTextKeyWide(), getNameKey()).GetCString()));
strcpy(szSound, GC.getProjectInfo(eCreateProject).getCreateSound());
szIcon = GC.getProjectInfo(eCreateProject).getButton();
}
if (isProduction())
{
swprintf(szTempBuffer, DLL_SERIALIZE(gDLL->getText(((isProductionLimited()) ? "TXT_KEY_MISC_WORK_HAS_BEGUN_LIMITED" : "TXT_KEY_MISC_WORK_HAS_BEGUN"), getProductionNameKey()).GetCString()));
wcscat(szBuffer, szTempBuffer);
}
{
MEMORY_TRACK_EXEMPT();
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, szSound, MESSAGE_TYPE_MINOR_EVENT, szIcon, (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), getX_INLINE(), getY_INLINE(), true, true);
}
}
if ((getTeam() == GC.getGameINLINE().getActiveTeam()) || GC.getGameINLINE().isDebugMode())
{
setInfoDirty(true);
if (isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true );
gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
//gDLL->getInterfaceIFace()->setDirty(CityScreen_DIRTY_BIT, true);
//gDLL->getInterfaceIFace()->setDirty(PlotListButtons_DIRTY_BIT, true);
}
}
}
void CvCity::startHeadOrder()
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
if (pOrderNode->m_data.eOrderType == ORDER_MAINTAIN)
{
processProcess(((ProcessTypes)(pOrderNode->m_data.iData1)), 1);
}
AI_setAssignWorkDirty(true);
}
}
void CvCity::stopHeadOrder()
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
if (pOrderNode->m_data.eOrderType == ORDER_MAINTAIN)
{
processProcess(((ProcessTypes)(pOrderNode->m_data.iData1)), -1);
}
}
}
int CvCity::getOrderQueueLength()
{
return m_orderQueue.getLength();
}
OrderData* CvCity::getOrderFromQueue(int iIndex)
{
CLLNode<OrderData>* pOrderNode;
pOrderNode = m_orderQueue.nodeNum(iIndex);
if (pOrderNode != NULL)
{
return &(pOrderNode->m_data);
}
else
{
return NULL;
}
}
CLLNode<OrderData>* CvCity::nextOrderQueueNode(CLLNode<OrderData>* pNode) const
{
return m_orderQueue.next(pNode);
}
CLLNode<OrderData>* CvCity::headOrderQueueNode() const
{
return m_orderQueue.head();
}
int CvCity::getNumOrdersQueued() const
{
return m_orderQueue.getLength();
}
OrderData CvCity::getOrderDataInternal(int iIndex, bool externalView) const
{
OrderData kData;
int iCount = 0;
CLLNode<OrderData>* pNode = headOrderQueueNode();
while (pNode != NULL)
{
if (iIndex == iCount)
{
kData.eOrderType = pNode->m_data.eOrderType;
kData.iData1 = externalView ? EXTERNAL_ORDER_IDATA(pNode->m_data.iData1) : pNode->m_data.iData1;
kData.iData2 = externalView ? EXTERNAL_ORDER_IDATA(pNode->m_data.iData2) : pNode->m_data.iData2;
kData.bSave = pNode->m_data.bSave;
return kData;
}
iCount++;
pNode = nextOrderQueueNode(pNode);
}
kData.eOrderType = NO_ORDER;
kData.iData1 = -1;
kData.iData2 = -1;
kData.bSave = false;
return kData;
}
OrderData CvCity::getOrderData(int iIndex) const
{
return getOrderDataInternal(iIndex);
}
void CvCity::setWallOverridePoints(const std::vector< std::pair<float, float> >& kPoints)
{
m_kWallOverridePoints = kPoints;
setLayoutDirty(true);
}
const std::vector< std::pair<float, float> >& CvCity::getWallOverridePoints() const
{
return m_kWallOverridePoints;
}
// Protected Functions...
void CvCity::doGrowth()
{
int iDiff;
/************************************************************************************************/
/* Afforess Start 12/21/09 */
/* */
/* */
/************************************************************************************************/
if(GC.getUSE_CAN_DO_GROWTH_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyCity* pyCity = new CyCity(this);
CyArgsList argsList;
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "doGrowth", argsList.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iDiff = foodDifference();
changeFood(iDiff);
changeFoodKept(iDiff);
setFoodKept(range(getFoodKept(), 0, ((growthThreshold() * getMaxFoodKeptPercent()) / 100)));
if (getFood() >= growthThreshold())
{
/************************************************************************************************/
/* Afforess Start 06/28/10 */
/* */
/* */
/************************************************************************************************/
/*
if (AI_isEmphasizeAvoidGrowth())
*/
if ((isHuman() && AI_avoidGrowth()) || AI_isEmphasizeAvoidGrowth())
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
setFood(growthThreshold());
}
else
{
changeFood(-(std::max(0, (growthThreshold() - getFoodKept()))));
changePopulation(1);
// ONEVENT - City growth
CvEventReporter::getInstance().cityGrowth(this, getOwnerINLINE());
}
}
else if (getFood() < 0)
{
changeFood(-(getFood()));
if (getPopulation() > 1)
{
changePopulation(-1);
}
}
}
void CvCity::doCulture()
{
PROFILE_FUNC();
/************************************************************************************************/
/* Afforess Start 12/21/09 */
/* */
/* */
/************************************************************************************************/
if(GC.getUSE_CAN_DO_CULTURE_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyCity* pyCity = new CyCity(this);
CyArgsList argsList;
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "doCulture", argsList.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
changeCultureTimes100(getOwnerINLINE(), getCommerceRateTimes100(COMMERCE_CULTURE), false, true);
}
void CvCity::doPlotCulture(bool bUpdate, PlayerTypes ePlayer, int iCultureRate)
{
PROFILE_FUNC();
CvPlot* pLoopPlot;
int iDX, iDY;
int iCultureRange;
CultureLevelTypes eCultureLevel = (CultureLevelTypes)0;
/************************************************************************************************/
/* Afforess Start 12/21/09 */
/* */
/* */
/************************************************************************************************/
if(GC.getUSE_CAN_DO_PLOT_CULTURE_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyCity* pyCity = new CyCity(this);
CyArgsList argsList;
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
argsList.add(bUpdate);
argsList.add(ePlayer);
argsList.add(iCultureRate);
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "doPlotCulture", argsList.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
FAssert(NO_PLAYER != ePlayer);
if (getOwnerINLINE() == ePlayer)
{
eCultureLevel = getCultureLevel();
}
else
{
for (int iI = (GC.getNumCultureLevelInfos() - 1); iI > 0; iI--)
{
if (getCultureTimes100(ePlayer) >= 100 * GC.getGameINLINE().getCultureThreshold((CultureLevelTypes)iI))
{
eCultureLevel = (CultureLevelTypes)iI;
break;
}
}
}
// Afforess
//int iFreeCultureRate = GC.getDefineINT("CITY_FREE_CULTURE_GROWTH_FACTOR");
int iFreeCultureRate = GC.getCITY_FREE_CULTURE_GROWTH_FACTOR();
if (getCultureTimes100(ePlayer) > 0)
{
if (eCultureLevel != NO_CULTURELEVEL)
{
clearCultureDistanceCache();
for (iDX = -eCultureLevel; iDX <= eCultureLevel; iDX++)
{
for (iDY = -eCultureLevel; iDY <= eCultureLevel; iDY++)
{
iCultureRange = cultureDistance(iDX, iDY);
if (iCultureRange <= eCultureLevel)
{
pLoopPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->isPotentialCityWorkForArea(area()))
{
pLoopPlot->changeCulture(ePlayer, (((eCultureLevel - iCultureRange + 1) * iFreeCultureRate) + iCultureRate + 1), (bUpdate || !(pLoopPlot->isOwned())));
}
}
}
}
}
}
}
}
bool CvCity::doCheckProduction()
{
CLLNode<OrderData>* pOrderNode;
OrderData* pOrder;
UnitTypes eUpgradeUnit;
int iUpgradeProduction;
int iProductionGold;
CvWString szBuffer;
int iI;
bool bOK = true;
for (iI = 0; iI < GC.getNumUnitInfos(); iI++)
{
if (getUnitProduction((UnitTypes)iI) > 0)
{
if (GET_PLAYER(getOwnerINLINE()).isProductionMaxedUnitClass((UnitClassTypes)(GC.getUnitInfo((UnitTypes)iI).getUnitClassType())))
{
iProductionGold = ((getUnitProduction((UnitTypes)iI) * GC.getDefineINT("MAXED_UNIT_GOLD_PERCENT")) / 100);
if (iProductionGold > 0)
{
GET_PLAYER(getOwnerINLINE()).changeGold(iProductionGold);
MEMORY_TRACK_EXEMPT();
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_LOST_WONDER_PROD_CONVERTED", getNameKey(), GC.getUnitInfo((UnitTypes)iI).getTextKeyWide(), iProductionGold));
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_WONDERGOLD", MESSAGE_TYPE_MINOR_EVENT, GC.getCommerceInfo(COMMERCE_GOLD).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE(), true, true);
}
setUnitProduction(((UnitTypes)iI), 0);
}
}
}
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (getBuildingProduction((BuildingTypes)iI) > 0)
{
if (GET_PLAYER(getOwnerINLINE()).isProductionMaxedBuildingClass((BuildingClassTypes)(GC.getBuildingInfo((BuildingTypes)iI).getBuildingClassType())))
{
iProductionGold = ((getBuildingProduction((BuildingTypes)iI) * GC.getDefineINT("MAXED_BUILDING_GOLD_PERCENT")) / 100);
/************************************************************************************************/
/* Afforess Start 06/13/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getBuildingInfo((BuildingTypes)iI).getProductionContinueBuildingClass() != NO_BUILDINGCLASS)
{
BuildingTypes eBuilding = (BuildingTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings((BuildingClassTypes)GC.getBuildingInfo((BuildingTypes)iI).getProductionContinueBuildingClass()));
if (eBuilding != NO_BUILDING)
{
if (canConstruct(eBuilding))
{
//setBuildingProduction(eBuilding, getBuildingProduction((BuildingTypes)iI));
iProductionGold = 0;
m_iLostProduction = getBuildingProduction((BuildingTypes)iI);
//szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_PROD_CONVERTED", getBuildingProduction((BuildingTypes)iI), GC.getBuildingInfo((BuildingTypes)iI).getTextKeyWide(), GC.getBuildingInfo(eBuilding).getTextKeyWide()));
//AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_WONDERGOLD", MESSAGE_TYPE_MINOR_EVENT, GC.getYieldInfo(YIELD_PRODUCTION).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), true, true);
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (iProductionGold > 0)
{
GET_PLAYER(getOwnerINLINE()).changeGold(iProductionGold);
MEMORY_TRACK_EXEMPT();
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_LOST_WONDER_PROD_CONVERTED", getNameKey(), GC.getBuildingInfo((BuildingTypes)iI).getTextKeyWide(), iProductionGold));
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_WONDERGOLD", MESSAGE_TYPE_MINOR_EVENT, GC.getCommerceInfo(COMMERCE_GOLD).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE(), true, true);
}
setBuildingProduction(((BuildingTypes)iI), 0);
}
}
}
for (iI = 0; iI < GC.getNumProjectInfos(); iI++)
{
if (getProjectProduction((ProjectTypes)iI) > 0)
{
if (GET_PLAYER(getOwnerINLINE()).isProductionMaxedProject((ProjectTypes)iI))
{
iProductionGold = ((getProjectProduction((ProjectTypes)iI) * GC.getDefineINT("MAXED_PROJECT_GOLD_PERCENT")) / 100);
if (iProductionGold > 0)
{
GET_PLAYER(getOwnerINLINE()).changeGold(iProductionGold);
MEMORY_TRACK_EXEMPT();
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_LOST_WONDER_PROD_CONVERTED", getNameKey(), GC.getProjectInfo((ProjectTypes)iI).getTextKeyWide(), iProductionGold));
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_WONDERGOLD", MESSAGE_TYPE_MINOR_EVENT, GC.getCommerceInfo(COMMERCE_GOLD).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE(), true, true);
}
setProjectProduction(((ProjectTypes)iI), 0);
}
}
}
if (!isProduction() && !isDisorder() && isHuman() && !isProductionAutomated())
{
//chooseProduction();
return bOK;
}
for (iI = 0; iI < GC.getNumUnitInfos(); iI++)
{
if (getFirstUnitOrder((UnitTypes)iI) != -1)
{
eUpgradeUnit = allUpgradesAvailable((UnitTypes)iI);
if (eUpgradeUnit != NO_UNIT)
{
FAssertMsg(eUpgradeUnit != iI, "eUpgradeUnit is expected to be different from iI");
iUpgradeProduction = getUnitProduction((UnitTypes)iI);
setUnitProduction(((UnitTypes)iI), 0);
setUnitProduction(eUpgradeUnit, iUpgradeProduction);
pOrderNode = headOrderQueueNode();
while (pOrderNode != NULL)
{
if (pOrderNode->m_data.eOrderType == ORDER_TRAIN)
{
if (EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1) == iI)
{
GET_PLAYER(getOwnerINLINE()).changeUnitClassMaking(((UnitClassTypes)(GC.getUnitInfo((UnitTypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1)).getUnitClassType())), -1);
pOrderNode->m_data.iData1 = PACK_INTERNAL_ORDER_IDATA(eUpgradeUnit, INTERNAL_AUXILIARY_ORDER_IDATA(pOrderNode->m_data.iData1));
if (GET_PLAYER(getOwnerINLINE()).AI_unitValue(eUpgradeUnit, ((UnitAITypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData2)), area()) == 0)
{
area()->changeNumTrainAIUnits(getOwnerINLINE(), ((UnitAITypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData2)), -1);
GET_PLAYER(getOwnerINLINE()).AI_changeNumTrainAIUnits(((UnitAITypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData2)), -1);
pOrderNode->m_data.iData2 = GC.getUnitInfo(eUpgradeUnit).getDefaultUnitAIType();
area()->changeNumTrainAIUnits(getOwnerINLINE(), ((UnitAITypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData2)), 1);
GET_PLAYER(getOwnerINLINE()).AI_changeNumTrainAIUnits(((UnitAITypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData2)), 1);
}
GET_PLAYER(getOwnerINLINE()).changeUnitClassMaking(((UnitClassTypes)(GC.getUnitInfo((UnitTypes)EXTERNAL_ORDER_IDATA(pOrderNode->m_data.iData1)).getUnitClassType())), 1);
}
}
pOrderNode = nextOrderQueueNode(pOrderNode);
}
}
}
}
for (iI = (getOrderQueueLength() - 1); iI >= 0; iI--)
{
pOrder = getOrderFromQueue(iI);
if (pOrder != NULL)
{
if (!canContinueProduction(*pOrder))
{
popOrder(iI, false, true);
bOK = false;
}
}
}
return bOK;
}
void CvCity::doProduction(CvCityTurnPipelineWorkItem* workItem, bool bRequeueOnProduce)
{
PROFILE_FUNC();
if (isDisorder())
{
return;
}
OrderTypes proposedOrder;
int proposedInstance;
int proposedData2;
workItem->getProposedProduction(proposedOrder, proposedInstance, proposedData2);
if ( proposedOrder != NO_ORDER )
{
if (!pushOrder(proposedOrder, proposedInstance, proposedData2, false, false, false))
{
// Clear the stale order or we'll potentially loop forever
m_workItem->setProposedProduction(NO_ORDER, -1, -1);
workItem->Requeue();
return;
}
}
if (isProduction())
{
if ( isProductionProcess() )
{
// Nothing to do - already included in city commerce
return;
}
if ( !workItem->m_bHasIncludedTurnProduction )
{
changeProduction(getCurrentProductionDifference(false, true));
setOverflowProduction(0);
setFeatureProduction(0);
workItem->m_bHasIncludedTurnProduction = true;
}
/************************************************************************************************/
/* Afforess Multiple Production Mod 08/23/09 */
/* */
/* */
/************************************************************************************************/
setBuiltFoodProducedUnit(isFoodProduction());
//clearLostProduction();
if (!GC.getGameINLINE().isOption(GAMEOPTION_MULTIPLE_PRODUCTION))
{
if (getProduction() >= getProductionNeeded())
{
popOrder(0, true, true);
}
}
else
{
while (isProduction() )
{
if (m_iLostProductionBase > 0)
{
changeProduction(getExtraProductionDifference(m_iLostProductionBase));
clearLostProduction();
}
changeProduction(getOverflowProductionDifference());
setOverflowProduction(0);
if (productionLeft() <= 0)
{
popOrder(0, true, true, true, bRequeueOnProduce);
//to eliminate pre-build exploits for all Wonders and all Projects
if ((!isProductionWonder() && !isProductionProject()) &&
(!isFoodProduction() || isBuiltFoodProducedUnit()) &&
!isProductionProcess())
{
if (bRequeueOnProduce && (!isHuman() || isProductionAutomated()) && !isProduction())
{
workItem->Requeue();
return;
}
}
else
{
break;
}
}
else
{
break;
}
}
}
if (m_iGoldFromLostProduction > 0)
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_LOST_PROD_CONVERTED", getNameKey(), m_iLostProductionModified, m_iGoldFromLostProduction));
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_WONDERGOLD", MESSAGE_TYPE_MINOR_EVENT, GC.getCommerceInfo(COMMERCE_GOLD).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE(), true, true);
GET_PLAYER(getOwnerINLINE()).changeGold(m_iGoldFromLostProduction);
clearLostProduction();
}
/************************************************************************************************/
/* Afforess Multiple Production Mod END */
/************************************************************************************************/
}
else
{
changeOverflowProduction(getCurrentProductionDifference(false, false), getProductionModifier());
}
}
void CvCity::doDecay()
{
int iI;
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
BuildingTypes eBuilding = (BuildingTypes) iI;
if (getProductionBuilding() != eBuilding)
{
if (getBuildingProduction(eBuilding) > 0)
{
changeBuildingProductionTime(eBuilding, 1);
if (isHuman())
{
int iGameSpeedPercent = GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getConstructPercent();
if (100 * getBuildingProductionTime(eBuilding) > GC.getDefineINT("BUILDING_PRODUCTION_DECAY_TIME") * iGameSpeedPercent)
{
int iProduction = getBuildingProduction(eBuilding);
setBuildingProduction(eBuilding, iProduction - (iProduction * (100 - GC.getDefineINT("BUILDING_PRODUCTION_DECAY_PERCENT")) + iGameSpeedPercent - 1) / iGameSpeedPercent);
}
}
}
else
{
setBuildingProductionTime(eBuilding, 0);
}
}
}
for (iI = 0; iI < GC.getNumUnitInfos(); iI++)
{
UnitTypes eUnit = (UnitTypes) iI;
if (getProductionUnit() != eUnit)
{
if (getUnitProduction(eUnit) > 0)
{
changeUnitProductionTime(eUnit, 1);
if (isHuman())
{
int iGameSpeedPercent = GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getTrainPercent();
if (100 * getUnitProductionTime(eUnit) > GC.getDefineINT("UNIT_PRODUCTION_DECAY_TIME") * iGameSpeedPercent)
{
int iProduction = getUnitProduction(eUnit);
setUnitProduction(eUnit, iProduction - (iProduction * (100 - GC.getDefineINT("UNIT_PRODUCTION_DECAY_PERCENT")) + iGameSpeedPercent - 1) / iGameSpeedPercent);
}
}
}
else
{
setUnitProductionTime(eUnit, 0);
}
}
}
}
void CvCity::doReligion()
{
CvCity* pLoopCity;
int iRandThreshold;
int iSpread;
int iLoop;
int iI, iJ;
/************************************************************************************************/
/* Afforess Start 12/21/09 */
/* */
/* */
/************************************************************************************************/
if(GC.getUSE_CAN_DO_RELIGION_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyCity* pyCity = new CyCity(this);
CyArgsList argsList;
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "doReligion", argsList.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
/************************************************************************************************/
/* Afforess Start 06/09/10 */
/* */
/* */
/************************************************************************************************/
/*
if (getReligionCount() == 0)
*/
if (getReligionCount() == 0 || GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_MULTIPLE_RELIGION_SPREAD))
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
{
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (!isHasReligion((ReligionTypes)iI))
{
if ((iI == GET_PLAYER(getOwnerINLINE()).getStateReligion()) || !(GET_PLAYER(getOwnerINLINE()).isNoNonStateReligionSpread()))
{
iRandThreshold = 0;
for (iJ = 0; iJ < MAX_PLAYERS; iJ++)
{
if (GET_PLAYER((PlayerTypes)iJ).isAlive())
{
for (pLoopCity = GET_PLAYER((PlayerTypes)iJ).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iJ).nextCity(&iLoop))
{
if (pLoopCity->isConnectedTo(this))
{
iSpread = pLoopCity->getReligionInfluence((ReligionTypes)iI);
iSpread *= GC.getReligionInfo((ReligionTypes)iI).getSpreadFactor();
if (iSpread > 0)
{
iSpread /= std::max(1, (((GC.getDefineINT("RELIGION_SPREAD_DISTANCE_DIVISOR") * plotDistance(getX_INLINE(), getY_INLINE(), pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE())) / GC.getMapINLINE().maxPlotDistance()) - 5));
/************************************************************************************************/
/* Afforess Start 06/09/10 */
/* */
/* */
/************************************************************************************************/
iSpread /= (getReligionCount() + 1);
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
iRandThreshold = std::max(iRandThreshold, iSpread + 1);
}
}
}
}
}
/************************************************************************************************/
/* Afforess Start 06/09/10 */
/* */
/* */
/************************************************************************************************/
int iSpreadMod = (GET_PLAYER(getOwnerINLINE()).getReligionSpreadRate()) + 100;
if (iSpreadMod < -100)
{
iRandThreshold = 0;
}
iRandThreshold *= iSpreadMod;
iRandThreshold /= 100;
//Afforess: boost new religion spread rates
int iTurnsAgoFounded = GC.getGameINLINE().getReligionGameTurnFounded((ReligionTypes)iI) - GC.getGameINLINE().getGameTurn();
int iThreshold = GC.getDefineINT("NEW_RELIGION_SPREAD_TURN_THRESHOLD", 50);
iThreshold *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getGrowthPercent();
iThreshold /= 100;
if (iTurnsAgoFounded < iThreshold)
{
iRandThreshold *= GC.getDefineINT("BOOST_NEW_RELIGION_SPREAD_PERCENT", 500);
iRandThreshold /= 100;
}
//Decrease older religion spread rates slightly
iThreshold = GC.getDefineINT("OLD_RELIGION_SPREAD_TURN_THRESHOLD", 250);
iThreshold *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getGrowthPercent();
iThreshold /= 100;
if (iTurnsAgoFounded > iThreshold)
{
iRandThreshold /= iTurnsAgoFounded;
iRandThreshold *= iThreshold;
}
int iSpreadRand = GC.getDefineINT("RELIGION_SPREAD_RAND");
iSpreadRand *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getGrowthPercent();
iSpreadRand /= 100;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (getCitySorenRandNum(iSpreadRand, "Religion Spread") < iRandThreshold)
{
setHasReligion(((ReligionTypes)iI), true, true, true);
break;
}
}
}
/************************************************************************************************/
/* Afforess Start 06/24/10 */
/* */
/* Religion Decay */
/************************************************************************************************/
else if (GC.getGameINLINE().isOption(GAMEOPTION_RELIGION_DECAY))
{
int iDecay = 0;
CvCity* pHolyCity = GC.getGameINLINE().getHolyCity((ReligionTypes)iI);
if (iI != GET_PLAYER(getOwnerINLINE()).getStateReligion())
{
bool bExcessReligion = getReligionCount() > 2;
bool bAtheist = GET_PLAYER(getOwnerINLINE()).getStateReligion() == NO_RELIGION && GET_PLAYER(getOwnerINLINE()).getCurrentEra() > 0;
bool bStateReligionPresent = GET_PLAYER(getOwnerINLINE()).getStateReligion() != NO_RELIGION && isHasReligion(GET_PLAYER(getOwnerINLINE()).getStateReligion());
if (getReligionCount() > 0 && (bExcessReligion || bAtheist || bStateReligionPresent))
{
if (pHolyCity != this)
{
iDecay = GC.getReligionInfo((ReligionTypes)iI).getSpreadFactor();
//more decay for each religion above 3
iDecay += 10 * std::max(0, getReligionCount() - 3);
if (getReligionCount() < 3)
{
iDecay *= getReligionCount();
iDecay /= 3;
}
//Afforess: boost new religion spread rates
int iTurnsAgoFounded = GC.getGameINLINE().getReligionGameTurnFounded((ReligionTypes)iI) - GC.getGameINLINE().getGameTurn();
int iThreshold = GC.getDefineINT("NEW_RELIGION_SPREAD_TURN_THRESHOLD", 50);
iThreshold *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getGrowthPercent();
iThreshold /= 100;
if (iTurnsAgoFounded < iThreshold)
{
iDecay /= 10;
}
//Decrease older religion spread rates slightly
iThreshold = GC.getDefineINT("OLD_RELIGION_SPREAD_TURN_THRESHOLD", 250);
iThreshold *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getGrowthPercent();
iThreshold /= 100;
if (iTurnsAgoFounded > iThreshold)
{
iDecay *= 4;
iDecay /= 3;
}
for (pLoopCity = GET_PLAYER(getOwnerINLINE()).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(getOwnerINLINE()).nextCity(&iLoop))
{
if (pLoopCity->isConnectedTo(this))
{
if (pLoopCity->isHasReligion((ReligionTypes)iI))
{
iDecay *= 9;
iDecay /= (10 + std::max(0, pLoopCity->getReligionInfluence((ReligionTypes)iI)));
}
}
}
iDecay /= std::max(1, 1 + getReligionInfluence((ReligionTypes)iI));
if (pHolyCity != NULL)
{
if (pHolyCity->getOwnerINLINE() == getOwnerINLINE())
{
iDecay /= 2;
}
else if (GET_TEAM(getTeam()).isAtWar(pHolyCity->getTeam()))
{
iDecay *= 4;
iDecay /= 3;
}
}
}
int iSpreadRand = GC.getDefineINT("RELIGION_SPREAD_RAND") * 2;
iSpreadRand *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getGrowthPercent();
iSpreadRand /= 100;
if (getCitySorenRandNum(iSpreadRand, "Religion Decay") < iDecay)
{
setHasReligion(((ReligionTypes)iI), false, true, false);
for (int iJ = 0; iJ < GC.getNumBuildingInfos(); iJ++)
{
if (getNumBuilding((BuildingTypes)iJ) > 0)
{
if (GC.getBuildingInfo((BuildingTypes)iJ).getPrereqReligion() == iI)
{
setNumRealBuilding((BuildingTypes)iJ, 0);
}
}
}
break;
}
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
}
}
void CvCity::doGreatPeople()
{
/************************************************************************************************/
/* Afforess Start 12/21/09 */
/* */
/* */
/************************************************************************************************/
if(GC.getUSE_CAN_DO_GREATPEOPLE_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyCity* pyCity = new CyCity(this);
CyArgsList argsList;
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "doGreatPeople", argsList.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (isDisorder())
{
return;
}
changeGreatPeopleProgress(getGreatPeopleRate());
for (int iI = 0; iI < GC.getNumUnitInfos(); iI++)
{
changeGreatPeopleUnitProgress(((UnitTypes)iI), getGreatPeopleUnitRate((UnitTypes)iI));
}
if (getGreatPeopleProgress() >= GET_PLAYER(getOwnerINLINE()).greatPeopleThreshold(false))
{
int iTotalGreatPeopleUnitProgress = 0;
for (int iI = 0; iI < GC.getNumUnitInfos(); iI++)
{
iTotalGreatPeopleUnitProgress += getGreatPeopleUnitProgress((UnitTypes)iI);
}
int iGreatPeopleUnitRand = getCitySorenRandNum(iTotalGreatPeopleUnitProgress, "Great Person");
UnitTypes eGreatPeopleUnit = NO_UNIT;
for (int iI = 0; iI < GC.getNumUnitInfos(); iI++)
{
if (iGreatPeopleUnitRand < getGreatPeopleUnitProgress((UnitTypes)iI))
{
eGreatPeopleUnit = ((UnitTypes)iI);
break;
}
else
{
iGreatPeopleUnitRand -= getGreatPeopleUnitProgress((UnitTypes)iI);
}
}
if (eGreatPeopleUnit != NO_UNIT)
{
changeGreatPeopleProgress(-(GET_PLAYER(getOwnerINLINE()).greatPeopleThreshold(false)));
for (int iI = 0; iI < GC.getNumUnitInfos(); iI++)
{
setGreatPeopleUnitProgress(((UnitTypes)iI), 0);
}
createGreatPeople(eGreatPeopleUnit, true, false);
}
}
}
void CvCity::doMeltdown()
{
CvWString szBuffer;
int iI;
/************************************************************************************************/
/* Afforess Start 12/21/09 */
/* */
/* */
/************************************************************************************************/
if(GC.getUSE_CAN_DO_MELTDOWN_CALLBACK())
{
PYTHON_ACCESS_LOCK_SCOPE
CyCity* pyCity = new CyCity(this);
CyArgsList argsList;
argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity)); // pass in city class
long lResult=0;
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "doMeltdown", argsList.makeFunctionArgs(), &lResult);
delete pyCity; // python fxn must not hold on to this pointer
if (lResult == 1)
{
return;
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (getNumBuilding((BuildingTypes)iI) > 0)
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH jdog5000 */
/* */
/* Gamespeed scaling */
/************************************************************************************************/
/* original bts code
if (GC.getBuildingInfo((BuildingTypes)iI).getNukeExplosionRand() != 0)
{
if (getCitySorenRandNum(GC.getBuildingInfo((BuildingTypes)iI).getNukeExplosionRand(), "Meltdown!!!") == 0)
{
*/
int iOdds = GC.getBuildingInfo((BuildingTypes)iI).getNukeExplosionRand();
if( iOdds > 0 )
{
iOdds *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getVictoryDelayPercent();
iOdds /= 100;
if( getCitySorenRandNum(iOdds, "Meltdown!!!") == 0)
{
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
if (getNumRealBuilding((BuildingTypes)iI) > 0)
{
setNumRealBuilding(((BuildingTypes)iI), 0);
}
plot()->nukeExplosion(1);
{
MEMORY_TRACK_EXEMPT();
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_MELTDOWN_CITY", getNameKey()));
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_MELTDOWN", MESSAGE_TYPE_MINOR_EVENT, ARTFILEMGR.getInterfaceArtInfo("INTERFACE_UNHEALTHY_PERSON")->getPath(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), getX_INLINE(), getY_INLINE(), true, true);
}
break;
}
}
}
}
}
void CvCity::resync(bool bWrite, ByteBuffer* pBuffer)
{
RESYNC_INT(bWrite, pBuffer, m_iID);
RESYNC_INT(bWrite, pBuffer, m_iX);
RESYNC_INT(bWrite, pBuffer, m_iY);
RESYNC_INT(bWrite, pBuffer, m_iRallyX);
RESYNC_INT(bWrite, pBuffer, m_iRallyY);
RESYNC_INT(bWrite, pBuffer, m_iGameTurnFounded);
RESYNC_INT(bWrite, pBuffer, m_iGameTurnAcquired);
RESYNC_INT(bWrite, pBuffer, m_iPopulation);
RESYNC_INT(bWrite, pBuffer, m_iHighestPopulation);
RESYNC_INT(bWrite, pBuffer, m_iWorkingPopulation);
RESYNC_INT(bWrite, pBuffer, m_iSpecialistPopulation);
RESYNC_INT(bWrite, pBuffer, m_iNumGreatPeople);
RESYNC_INT(bWrite, pBuffer, m_iBaseGreatPeopleRate);
RESYNC_INT(bWrite, pBuffer, m_iGreatPeopleRateModifier);
RESYNC_INT(bWrite, pBuffer, m_iGreatPeopleProgress);
RESYNC_INT(bWrite, pBuffer, m_iNumWorldWonders);
RESYNC_INT(bWrite, pBuffer, m_iNumTeamWonders);
RESYNC_INT(bWrite, pBuffer, m_iNumNationalWonders);
RESYNC_INT(bWrite, pBuffer, m_iNumBuildings);
RESYNC_INT(bWrite, pBuffer, m_iGovernmentCenterCount);
RESYNC_INT(bWrite, pBuffer, m_iMaintenance);
RESYNC_INT(bWrite, pBuffer, m_iMaintenanceModifier);
RESYNC_INT(bWrite, pBuffer, m_iWarWearinessModifier);
RESYNC_INT(bWrite, pBuffer, m_iHurryAngerModifier);
RESYNC_INT(bWrite, pBuffer, m_iHealRate);
RESYNC_INT(bWrite, pBuffer, m_iEspionageHealthCounter);
RESYNC_INT(bWrite, pBuffer, m_iEspionageHappinessCounter);
RESYNC_INT(bWrite, pBuffer, m_iFreshWaterGoodHealth);
RESYNC_INT(bWrite, pBuffer, m_iFreshWaterBadHealth);
RESYNC_INT(bWrite, pBuffer, m_iFeatureGoodHealth);
RESYNC_INT(bWrite, pBuffer, m_iFeatureBadHealth);
RESYNC_INT(bWrite, pBuffer, m_iBuildingGoodHealth);
RESYNC_INT(bWrite, pBuffer, m_iBuildingBadHealth);
RESYNC_INT(bWrite, pBuffer, m_iPowerGoodHealth);
RESYNC_INT(bWrite, pBuffer, m_iPowerBadHealth);
RESYNC_INT(bWrite, pBuffer, m_iBonusGoodHealth);
RESYNC_INT(bWrite, pBuffer, m_iBonusBadHealth);
RESYNC_INT(bWrite, pBuffer, m_iHurryAngerTimer);
RESYNC_INT(bWrite, pBuffer, m_iRevRequestAngerTimer);
RESYNC_INT(bWrite, pBuffer, m_iRevSuccessTimer);
RESYNC_INT(bWrite, pBuffer, m_iConscriptAngerTimer);
RESYNC_INT(bWrite, pBuffer, m_iDefyResolutionAngerTimer);
RESYNC_INT(bWrite, pBuffer, m_iHappinessTimer);
RESYNC_INT(bWrite, pBuffer, m_iMilitaryHappinessUnits);
RESYNC_INT(bWrite, pBuffer, m_iBuildingGoodHappiness);
RESYNC_INT(bWrite, pBuffer, m_iBuildingBadHappiness);
RESYNC_INT(bWrite, pBuffer, m_iExtraBuildingGoodHappiness);
RESYNC_INT(bWrite, pBuffer, m_iExtraBuildingBadHappiness);
RESYNC_INT(bWrite, pBuffer, m_iExtraBuildingGoodHealth);
RESYNC_INT(bWrite, pBuffer, m_iExtraBuildingBadHealth);
RESYNC_INT(bWrite, pBuffer, m_iFeatureGoodHappiness);
RESYNC_INT(bWrite, pBuffer, m_iFeatureBadHappiness);
RESYNC_INT(bWrite, pBuffer, m_iBonusGoodHappiness);
RESYNC_INT(bWrite, pBuffer, m_iBonusBadHappiness);
RESYNC_INT(bWrite, pBuffer, m_iReligionGoodHappiness);
RESYNC_INT(bWrite, pBuffer, m_iReligionBadHappiness);
RESYNC_INT(bWrite, pBuffer, m_iExtraHappiness);
RESYNC_INT(bWrite, pBuffer, m_iExtraHealth);
RESYNC_INT(bWrite, pBuffer, m_iNoUnhappinessCount);
RESYNC_INT(bWrite, pBuffer, m_iNoUnhealthyPopulationCount);
RESYNC_INT(bWrite, pBuffer, m_iBuildingOnlyHealthyCount);
RESYNC_INT(bWrite, pBuffer, m_iFood);
RESYNC_INT(bWrite, pBuffer, m_iFoodKept);
RESYNC_FLOAT(bWrite, pBuffer, m_fMaxFoodKeptMultiplierLog);
RESYNC_INT(bWrite, pBuffer, m_iOverflowProduction);
RESYNC_INT(bWrite, pBuffer, m_iFeatureProduction);
RESYNC_INT(bWrite, pBuffer, m_iMilitaryProductionModifier);
RESYNC_INT(bWrite, pBuffer, m_iSpaceProductionModifier);
RESYNC_INT(bWrite, pBuffer, m_iExtraTradeRoutes);
RESYNC_INT(bWrite, pBuffer, m_iTradeRouteModifier);
RESYNC_INT(bWrite, pBuffer, m_iForeignTradeRouteModifier);
RESYNC_INT(bWrite, pBuffer, m_iBuildingDefense);
RESYNC_INT(bWrite, pBuffer, m_iBuildingBombardDefense);
RESYNC_INT(bWrite, pBuffer, m_iFreeExperience);
RESYNC_INT(bWrite, pBuffer, m_iCurrAirlift);
RESYNC_INT(bWrite, pBuffer, m_iMaxAirlift);
RESYNC_INT(bWrite, pBuffer, m_iAirModifier);
RESYNC_INT(bWrite, pBuffer, m_iAirUnitCapacity);
RESYNC_INT(bWrite, pBuffer, m_iWonderCapacityIncrement);
RESYNC_INT(bWrite, pBuffer, m_iNukeModifier);
RESYNC_INT(bWrite, pBuffer, m_iFreeSpecialist);
RESYNC_INT(bWrite, pBuffer, m_iPowerCount);
RESYNC_INT(bWrite, pBuffer, m_iDirtyPowerCount);
RESYNC_INT(bWrite, pBuffer, m_iDefenseDamage);
RESYNC_INT(bWrite, pBuffer, m_iLastDefenseDamage);
RESYNC_INT(bWrite, pBuffer, m_iOccupationTimer);
RESYNC_INT(bWrite, pBuffer, m_iCultureUpdateTimer);
RESYNC_INT(bWrite, pBuffer, m_iCitySizeBoost);
RESYNC_INT(bWrite, pBuffer, m_iSpecialistFreeExperience);
RESYNC_INT(bWrite, pBuffer, m_iEspionageDefenseModifier);
RESYNC_INT(bWrite, pBuffer, m_iMADIncoming);
RESYNC_BOOL(bWrite, pBuffer, m_bNeverLost);
RESYNC_BOOL(bWrite, pBuffer, m_bBombarded);
RESYNC_BOOL(bWrite, pBuffer, m_bDrafted);
RESYNC_BOOL(bWrite, pBuffer, m_bAirliftTargeted);
RESYNC_BOOL(bWrite, pBuffer, m_bWeLoveTheKingDay);
RESYNC_BOOL(bWrite, pBuffer, m_bCitizensAutomated);
RESYNC_BOOL(bWrite, pBuffer, m_bProductionAutomated);
RESYNC_BOOL(bWrite, pBuffer, m_bWallOverride);
RESYNC_BOOL(bWrite, pBuffer, m_bPlundered);
RESYNC_INT_WITH_CAST(bWrite, pBuffer, m_eOwner, PlayerTypes);
RESYNC_INT_WITH_CAST(bWrite, pBuffer, m_ePreviousOwner, PlayerTypes);
RESYNC_INT_WITH_CAST(bWrite, pBuffer, m_eOriginalOwner, PlayerTypes);
RESYNC_INT_WITH_CAST(bWrite, pBuffer, m_eCultureLevel, CultureLevelTypes);
RESYNC_INT(bWrite, pBuffer, m_iRevolutionIndex);
RESYNC_INT(bWrite, pBuffer, m_iLocalRevIndex);
RESYNC_INT(bWrite, pBuffer, m_iRevIndexAverage);
RESYNC_INT(bWrite, pBuffer, m_iRevolutionCounter);
RESYNC_INT(bWrite, pBuffer, m_iReinforcementCounter);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_YIELD_TYPES, m_aiSeaPlotYield);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_YIELD_TYPES, m_aiRiverPlotYield);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_YIELD_TYPES, m_aiBaseYieldRate);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_YIELD_TYPES, m_aiYieldRateModifier);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_YIELD_TYPES, m_aiPowerYieldRateModifier);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_YIELD_TYPES, m_aiBonusYieldRateModifier);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_YIELD_TYPES, m_aiTradeYield);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_YIELD_TYPES, m_aiCorporationYield);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_YIELD_TYPES, m_aiExtraSpecialistYield);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_COMMERCE_TYPES, m_aiCommerceRate);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_COMMERCE_TYPES, m_aiProductionToCommerceModifier);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_COMMERCE_TYPES, m_aiBuildingCommerce);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_COMMERCE_TYPES, m_aiSpecialistCommerce);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_COMMERCE_TYPES, m_aiReligionCommerce);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_COMMERCE_TYPES, m_aiCorporationCommerce);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_COMMERCE_TYPES, m_aiCommerceRateModifier);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_COMMERCE_TYPES, m_aiCommerceHappinessPer);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_DOMAIN_TYPES, m_aiDomainFreeExperience);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_DOMAIN_TYPES, m_aiDomainProductionModifier);
RESYNC_INT_ARRAY(bWrite, pBuffer, MAX_PLAYERS, m_aiCulture);
RESYNC_INT_ARRAY(bWrite, pBuffer, MAX_PLAYERS, m_aiNumRevolts);
RESYNC_BOOL_ARRAY(bWrite, pBuffer, MAX_PLAYERS, m_abEverOwned);
RESYNC_BOOL_ARRAY(bWrite, pBuffer, MAX_PLAYERS, m_abTradeRoute);
RESYNC_BOOL_ARRAY(bWrite, pBuffer, MAX_TEAMS, m_abRevealed);
RESYNC_BOOL_ARRAY(bWrite, pBuffer, MAX_TEAMS, m_abEspionageVisibility);
RESYNC_STRING(bWrite, pBuffer, m_szName);
RESYNC_STRING(bWrite, pBuffer, m_szScriptData);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumBonusInfos(), m_paiNoBonus);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumBonusInfos(), m_paiFreeBonus);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumBonusInfos(), m_paiNumBonuses);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumBonusInfos(), m_paiNumCorpProducedBonuses);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumProjectInfos(), m_paiProjectProduction);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumBuildingInfos(), m_paiBuildingProduction);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumBuildingInfos(), m_paiBuildingProductionTime);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumBuildingInfos(), m_paiBuildingOriginalOwner);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumBuildingInfos(), m_paiBuildingOriginalTime);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumUnitInfos(), m_paiUnitProduction);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumUnitInfos(), m_paiUnitProductionTime);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumUnitInfos(), m_paiGreatPeopleUnitRate);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumUnitInfos(), m_paiGreatPeopleUnitProgress);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumSpecialistInfos(), m_paiSpecialistCount);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumSpecialistInfos(), m_paiMaxSpecialistCount);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumSpecialistInfos(), m_paiForceSpecialistCount);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumSpecialistInfos(), m_paiFreeSpecialistCount);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumSpecialistInfos(), m_paiFreeSpecialistCountUnattributed);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumImprovementInfos(), m_paiImprovementFreeSpecialists);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumReligionInfos(), m_paiReligionInfluence);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumReligionInfos(), m_paiStateReligionHappiness);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumUnitCombatInfos(), m_paiUnitCombatFreeExperience);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumPromotionInfos(), m_paiFreePromotionCount);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumBuildingInfos(), m_paiNumRealBuilding);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumBuildingInfos(), m_paiNumFreeBuilding);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumBuildingInfos(), m_paiNumFreeAreaBuilding);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumBuildingInfos(), m_paiNumFreeTradeRegionBuilding);
RESYNC_INT(bWrite, pBuffer, m_bHasCalculatedBuildingReplacement);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_CITY_PLOTS, m_pabWorkingPlot);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumReligionInfos(), m_pabHasReligion);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumCorporationInfos(), m_pabHasCorporation);
RESYNC_INT(bWrite, pBuffer, m_iImprovementGoodHealth);
RESYNC_INT(bWrite, pBuffer, m_iImprovementBadHealth);
RESYNC_INT(bWrite, pBuffer, m_iSpecialistGoodHealth);
RESYNC_INT(bWrite, pBuffer, m_iSpecialistBadHealth);
RESYNC_INT(bWrite, pBuffer, m_iSpecialistHappiness);
RESYNC_INT(bWrite, pBuffer, m_iSpecialistUnhappiness);
RESYNC_INT(bWrite, pBuffer, m_iEventAnger);
RESYNC_FLOAT(bWrite, pBuffer, m_fPopulationgrowthratepercentageLog);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_COMMERCE_TYPES, m_aiBonusCommerceRateModifier);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_COMMERCE_TYPES, m_aiBonusCommercePercentChanges);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_COMMERCE_TYPES, m_aiCommerceAttacks);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_COMMERCE_TYPES, m_aiMaxCommerceAttacks);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumUnitClassInfos(), m_paiUnitClassProductionModifier);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumBuildingClassInfos(), m_paiBuildingClassProductionModifier);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumBonusInfos(), m_paiBonusDefenseChanges);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumBonusInfos(), m_pabHadVicinityBonus);
RESYNC_INT(bWrite, pBuffer, m_iCiv);
RESYNC_INT(bWrite, pBuffer, m_iExtraYieldTurns);
RESYNC_INT_WITH_CAST(bWrite, pBuffer, m_eOccupationCultureLevel, CultureLevelTypes);
RESYNC_INT(bWrite, pBuffer, m_iLineOfSight);
RESYNC_INT(bWrite, pBuffer, m_iLandmarkAngerTimer);
RESYNC_INT(bWrite, pBuffer, m_iInvasionChance);
RESYNC_INT(bWrite, pBuffer, m_iInvasionTimer);
RESYNC_INT(bWrite, pBuffer, m_iFreshWater);
RESYNC_INT(bWrite, pBuffer, m_iAdjacentDamagePercent);
RESYNC_INT(bWrite, pBuffer, m_iWorkableRadiusOverride);
RESYNC_INT(bWrite, pBuffer, m_iProtectedCultureCount);
RESYNC_INT(bWrite, pBuffer, m_iNumUnitFullHeal);
RESYNC_INT(bWrite, pBuffer, m_iDisabledPowerTimer);
RESYNC_INT(bWrite, pBuffer, m_iWarWearinessTimer);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumBuildingInfos(), m_pabDisabledBuilding);
RESYNC_INT_ARRAY(bWrite, pBuffer, GC.getNumUnitCombatInfos(), m_paiUnitCombatExtraStrength);
RESYNC_BOOL_ARRAY(bWrite, pBuffer, GC.getNumBuildInfos(), m_pabAutomatedCanBuild);
RESYNC_INT(bWrite, pBuffer, m_iMinimumDefenseLevel);
RESYNC_INT(bWrite, pBuffer, m_iNumPopulationEmployed);
RESYNC_INT(bWrite, pBuffer, m_iHappinessPercentPerPopulation);
RESYNC_INT(bWrite, pBuffer, m_iHealthPercentPerPopulation);
RESYNC_INT(bWrite, pBuffer, m_iExtraCapitalCommerce);
RESYNC_INT(bWrite, pBuffer, m_iExtraForeignCapitalCommerce);
RESYNC_INT(bWrite, pBuffer, m_iPreviousExtraCommerce);
RESYNC_INT(bWrite, pBuffer, m_iPreviousConnectedCommerce);
RESYNC_INT(bWrite, pBuffer, m_iPreviousForeignConnectedCommerce);
RESYNC_INT(bWrite, pBuffer, m_iForeignConnectednessNeeded);
RESYNC_INT(bWrite, pBuffer, m_iForcedRevolutionCounter);
RESYNC_INT_ARRAY(bWrite, pBuffer, MAX_PLAYERS, m_aiSeizedForeignConnectedness);
if (bWrite)
{
pBuffer->putInt(m_aBuildingCommerceModifier.size());
for (std::vector<BuildingCommerceModifier>::iterator it = m_aBuildingCommerceModifier.begin(); it != m_aBuildingCommerceModifier.end(); ++it)
{
pBuffer->putInt((*it).eBuildingClass);
pBuffer->putInt((*it).eCommerce);
pBuffer->putInt((*it).iChange);
}
}
else
{
m_aBuildingCommerceModifier.clear();
int iSize = pBuffer->getInt();
for (int i = 0; i < iSize; i++)
{
BuildingClassTypes eBuildingClass = (BuildingClassTypes)pBuffer->getInt();
CommerceTypes eCommerce = (CommerceTypes)pBuffer->getInt();
int iChange = pBuffer->getInt();
BuildingCommerceModifier eChange;
eChange.eBuildingClass = eBuildingClass;
eChange.eCommerce = eCommerce;
eChange.iChange = iChange;
m_aBuildingCommerceModifier.push_back(eChange);
}
}
if (bWrite)
{
pBuffer->putInt(m_aBuildingYieldModifier.size());
for (std::vector<BuildingYieldModifier>::iterator it = m_aBuildingYieldModifier.begin(); it != m_aBuildingYieldModifier.end(); ++it)
{
pBuffer->putInt((*it).eBuildingClass);
pBuffer->putInt((*it).eYield);
pBuffer->putInt((*it).iChange);
}
}
else
{
m_aBuildingYieldModifier.clear();
int iSize = pBuffer->getInt();
for (int i = 0; i < iSize; i++)
{
BuildingClassTypes eBuildingClass = (BuildingClassTypes)pBuffer->getInt();
YieldTypes eYield = (YieldTypes)pBuffer->getInt();
int iChange = pBuffer->getInt();
BuildingYieldModifier eChange;
eChange.eBuildingClass = eBuildingClass;
eChange.eYield = eYield;
eChange.iChange = iChange;
m_aBuildingYieldModifier.push_back(eChange);
}
}
for (int iI = 0; iI < GC.getMAX_TRADE_ROUTES(); iI++)
{
RESYNC_INT_WITH_CAST(bWrite, pBuffer, m_paTradeCities[iI].eOwner, PlayerTypes);
RESYNC_INT(bWrite, pBuffer, m_paTradeCities[iI].iID);
}
if (bWrite)
{
FDataStreamBuffer fBuffer;
m_orderQueue.WriteNonWrapper(&fBuffer);
pBuffer->putInt(fBuffer.m_pByteBuffer->size());
for (int i = 0; i < (int)fBuffer.m_pByteBuffer->size(); i++)
{
pBuffer->put(fBuffer.m_pByteBuffer->get(i));
}
}
else
{
FDataStreamBuffer fBuffer;
int iSize = pBuffer->getInt();
for (int i = 0; i < iSize; i++)
{
fBuffer.m_pByteBuffer->put(pBuffer->get());
}
m_orderQueue.ReadNonWrapper(&fBuffer);
}
RESYNC_INT(bWrite, pBuffer, m_iPopulationRank);
RESYNC_BOOL(bWrite, pBuffer, m_bPopulationRankValid);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_YIELD_TYPES, m_aiBaseYieldRank);
RESYNC_BOOL_ARRAY(bWrite, pBuffer, NUM_YIELD_TYPES, m_abBaseYieldRankValid);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_YIELD_TYPES, m_aiYieldRank);
RESYNC_BOOL_ARRAY(bWrite, pBuffer, NUM_YIELD_TYPES, m_abYieldRankValid);
RESYNC_INT_ARRAY(bWrite, pBuffer, NUM_COMMERCE_TYPES, m_aiCommerceRank);
RESYNC_BOOL_ARRAY(bWrite, pBuffer, NUM_COMMERCE_TYPES, m_abCommerceRankValid);
//m_Properties.writeWrapper(pStream);
if (bWrite)
{
pBuffer->putInt(m_aEventsOccured.size());
for (std::vector<EventTypes>::iterator it = m_aEventsOccured.begin(); it != m_aEventsOccured.end(); ++it)
{
pBuffer->putInt(*it);
}
}
else
{
m_aEventsOccured.clear();
int iSize = pBuffer->getInt();
for (int i = 0; i < iSize; i++)
{
m_aEventsOccured.push_back((EventTypes)pBuffer->getInt());
}
}
if (bWrite)
{
pBuffer->putInt(m_aBuildingYieldChange.size());
for(std::vector<BuildingYieldChange>::iterator it = m_aBuildingYieldChange.begin(); it != m_aBuildingYieldChange.end(); ++it)
{
pBuffer->putInt((*it).eBuildingClass);
pBuffer->putInt((*it).eYield);
pBuffer->putInt((*it).iChange);
}
}
else
{
m_aBuildingYieldChange.clear();
int iSize = pBuffer->getInt();
for (int i = 0; i < iSize; i++)
{
BuildingClassTypes eBuildingClass = (BuildingClassTypes)pBuffer->getInt();
YieldTypes eYield = (YieldTypes)pBuffer->getInt();
int iChange = pBuffer->getInt();
BuildingYieldChange eChange;
eChange.eBuildingClass = eBuildingClass;
eChange.eYield = eYield;
eChange.iChange = iChange;
m_aBuildingYieldChange.push_back(eChange);
}
}
if (bWrite)
{
pBuffer->putInt(m_aBuildingCommerceChange.size());
for (std::vector<BuildingCommerceChange>::iterator it = m_aBuildingCommerceChange.begin(); it != m_aBuildingCommerceChange.end(); ++it)
{
pBuffer->putInt((*it).eBuildingClass);
pBuffer->putInt((*it).eCommerce);
pBuffer->putInt((*it).iChange);
}
}
else
{
m_aBuildingCommerceChange.clear();
int iSize = pBuffer->getInt();
for (int i = 0; i < iSize; i++)
{
BuildingClassTypes eBuildingClass = (BuildingClassTypes)pBuffer->getInt();
CommerceTypes eCommerce = (CommerceTypes)pBuffer->getInt();
int iChange = pBuffer->getInt();
BuildingCommerceChange eChange;
eChange.eBuildingClass = eBuildingClass;
eChange.eCommerce = eCommerce;
eChange.iChange = iChange;
m_aBuildingCommerceChange.push_back(eChange);
}
}
if (bWrite)
{
pBuffer->putInt(m_aBuildingHappyChange.size());
for (BuildingChangeArray::iterator it = m_aBuildingHappyChange.begin(); it != m_aBuildingHappyChange.end(); ++it)
{
pBuffer->putInt((*it).first);
pBuffer->putInt((*it).second);
}
}
else
{
m_aBuildingHappyChange.clear();
int iSize = pBuffer->getInt();
for (int i = 0; i < iSize; i++)
{
BuildingClassTypes eBuildingClass = (BuildingClassTypes)pBuffer->getInt();
int iChange = pBuffer->getInt();
m_aBuildingHappyChange.push_back(std::make_pair(eBuildingClass, iChange));
}
}
if (bWrite)
{
pBuffer->putInt(m_aBuildingHealthChange.size());
for (BuildingChangeArray::iterator it = m_aBuildingHealthChange.begin(); it != m_aBuildingHealthChange.end(); ++it)
{
pBuffer->putInt((*it).first);
pBuffer->putInt((*it).second);
}
}
else
{
m_aBuildingHealthChange.clear();
int iSize = pBuffer->getInt();
for (int i = 0; i < iSize; i++)
{
BuildingClassTypes eBuildingClass = (BuildingClassTypes)pBuffer->getInt();
int iChange = pBuffer->getInt();
m_aBuildingHealthChange.push_back(std::make_pair(eBuildingClass, iChange));
}
}
RESYNC_INT(bWrite, pBuffer, m_iExtraBuildingDefenseRecoverySpeedModifier);
RESYNC_INT(bWrite, pBuffer, m_iModifiedBuildingDefenseRecoverySpeedCap);
RESYNC_INT(bWrite, pBuffer, m_iExtraCityDefenseRecoverySpeedModifier);
RESYNC_INT(bWrite, pBuffer, m_iZoCCount);
}
// Private Functions...
void CvCity::read(FDataStreamBase* pStream)
{
MEMORY_TRACE_FUNCTION();
int iI;
unsigned int iNumElts;
CvTaggedSaveFormatWrapper& wrapper = CvTaggedSaveFormatWrapper::getSaveFormatWrapper();
wrapper.AttachToStream(pStream);
WRAPPER_READ_OBJECT_START(wrapper);
// Init data before load
reset();
uint uiFlag=0;
WRAPPER_READ(wrapper, "CvCity", &uiFlag); // flags for expansion
WRAPPER_READ(wrapper, "CvCity", &m_iID);
WRAPPER_READ(wrapper, "CvCity", &m_iX);
WRAPPER_READ(wrapper, "CvCity", &m_iY);
WRAPPER_READ(wrapper, "CvCity", &m_iRallyX);
WRAPPER_READ(wrapper, "CvCity", &m_iRallyY);
WRAPPER_READ(wrapper, "CvCity", &m_iGameTurnFounded);
WRAPPER_READ(wrapper, "CvCity", &m_iGameTurnAcquired);
WRAPPER_READ(wrapper, "CvCity", &m_iPopulation);
WRAPPER_READ(wrapper, "CvCity", &m_iHighestPopulation);
WRAPPER_READ(wrapper, "CvCity", &m_iWorkingPopulation);
WRAPPER_READ(wrapper, "CvCity", &m_iSpecialistPopulation);
WRAPPER_READ(wrapper, "CvCity", &m_iNumGreatPeople);
WRAPPER_READ(wrapper, "CvCity", &m_iBaseGreatPeopleRate);
WRAPPER_READ(wrapper, "CvCity", &m_iGreatPeopleRateModifier);
WRAPPER_READ(wrapper, "CvCity", &m_iGreatPeopleProgress);
WRAPPER_READ(wrapper, "CvCity", &m_iNumWorldWonders);
WRAPPER_READ(wrapper, "CvCity", &m_iNumTeamWonders);
WRAPPER_READ(wrapper, "CvCity", &m_iNumNationalWonders);
WRAPPER_READ(wrapper, "CvCity", &m_iNumBuildings);
WRAPPER_READ(wrapper, "CvCity", &m_iGovernmentCenterCount);
WRAPPER_READ(wrapper, "CvCity", &m_iMaintenance);
WRAPPER_READ(wrapper, "CvCity", &m_iMaintenanceModifier);
WRAPPER_READ(wrapper, "CvCity", &m_iWarWearinessModifier);
WRAPPER_READ(wrapper, "CvCity", &m_iHurryAngerModifier);
WRAPPER_READ(wrapper, "CvCity", &m_iHealRate);
WRAPPER_READ(wrapper, "CvCity", &m_iEspionageHealthCounter);
WRAPPER_READ(wrapper, "CvCity", &m_iEspionageHappinessCounter);
WRAPPER_READ(wrapper, "CvCity", &m_iFreshWaterGoodHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iFreshWaterBadHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iFeatureGoodHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iFeatureBadHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iBuildingGoodHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iBuildingBadHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iPowerGoodHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iPowerBadHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iBonusGoodHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iBonusBadHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iHurryAngerTimer);
/************************************************************************************************/
/* REVOLUTION_MOD 04/28/08 jdog5000 */
/* */
/* */
/************************************************************************************************/
WRAPPER_READ(wrapper, "CvCity", &m_iRevRequestAngerTimer);
WRAPPER_READ(wrapper, "CvCity", &m_iRevSuccessTimer);
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
WRAPPER_READ(wrapper, "CvCity", &m_iConscriptAngerTimer);
WRAPPER_READ(wrapper, "CvCity", &m_iDefyResolutionAngerTimer);
WRAPPER_READ(wrapper, "CvCity", &m_iHappinessTimer);
WRAPPER_READ(wrapper, "CvCity", &m_iMilitaryHappinessUnits);
WRAPPER_READ(wrapper, "CvCity", &m_iBuildingGoodHappiness);
WRAPPER_READ(wrapper, "CvCity", &m_iBuildingBadHappiness);
WRAPPER_READ(wrapper, "CvCity", &m_iExtraBuildingGoodHappiness);
WRAPPER_READ(wrapper, "CvCity", &m_iExtraBuildingBadHappiness);
WRAPPER_READ(wrapper, "CvCity", &m_iExtraBuildingGoodHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iExtraBuildingBadHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iFeatureGoodHappiness);
WRAPPER_READ(wrapper, "CvCity", &m_iFeatureBadHappiness);
WRAPPER_READ(wrapper, "CvCity", &m_iBonusGoodHappiness);
WRAPPER_READ(wrapper, "CvCity", &m_iBonusBadHappiness);
WRAPPER_READ(wrapper, "CvCity", &m_iReligionGoodHappiness);
WRAPPER_READ(wrapper, "CvCity", &m_iReligionBadHappiness);
WRAPPER_READ(wrapper, "CvCity", &m_iExtraHappiness);
WRAPPER_READ(wrapper, "CvCity", &m_iExtraHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iNoUnhappinessCount);
WRAPPER_READ(wrapper, "CvCity", &m_iNoUnhealthyPopulationCount);
WRAPPER_READ(wrapper, "CvCity", &m_iBuildingOnlyHealthyCount);
WRAPPER_READ(wrapper, "CvCity", &m_iFood);
WRAPPER_READ(wrapper, "CvCity", &m_iFoodKept);
// Old format save before saved food chnages became multiplicative and the stored value
// became a logarithm
m_fMaxFoodKeptMultiplierLog = INVALID_STORED_FOOD_PERCENT_LOG;
WRAPPER_SKIP_ELEMENT(wrapper, "CvCity", m_iMaxFoodKeptPercent, SAVE_VALUE_ANY); // was present in old formats
WRAPPER_READ(wrapper, "CvCity", &m_fMaxFoodKeptMultiplierLog);
WRAPPER_READ(wrapper, "CvCity", &m_iOverflowProduction);
WRAPPER_READ(wrapper, "CvCity", &m_iFeatureProduction);
WRAPPER_READ(wrapper, "CvCity", &m_iMilitaryProductionModifier);
WRAPPER_READ(wrapper, "CvCity", &m_iSpaceProductionModifier);
WRAPPER_READ(wrapper, "CvCity", &m_iExtraTradeRoutes);
WRAPPER_READ(wrapper, "CvCity", &m_iTradeRouteModifier);
WRAPPER_READ(wrapper, "CvCity", &m_iForeignTradeRouteModifier);
WRAPPER_READ(wrapper, "CvCity", &m_iBuildingDefense);
WRAPPER_READ(wrapper, "CvCity", &m_iBuildingBombardDefense);
WRAPPER_READ(wrapper, "CvCity", &m_iFreeExperience);
WRAPPER_READ(wrapper, "CvCity", &m_iCurrAirlift);
WRAPPER_READ(wrapper, "CvCity", &m_iMaxAirlift);
WRAPPER_READ(wrapper, "CvCity", &m_iAirModifier);
WRAPPER_READ(wrapper, "CvCity", &m_iAirUnitCapacity);
WRAPPER_READ(wrapper, "CvCity", &m_iWonderCapacityIncrement);
WRAPPER_READ(wrapper, "CvCity", &m_iNukeModifier);
WRAPPER_READ(wrapper, "CvCity", &m_iFreeSpecialist);
WRAPPER_READ(wrapper, "CvCity", &m_iPowerCount);
WRAPPER_READ(wrapper, "CvCity", &m_iDirtyPowerCount);
WRAPPER_READ(wrapper, "CvCity", &m_iDefenseDamage);
WRAPPER_READ(wrapper, "CvCity", &m_iLastDefenseDamage);
WRAPPER_READ(wrapper, "CvCity", &m_iOccupationTimer);
WRAPPER_READ(wrapper, "CvCity", &m_iCultureUpdateTimer);
WRAPPER_READ(wrapper, "CvCity", &m_iCitySizeBoost);
WRAPPER_READ(wrapper, "CvCity", &m_iSpecialistFreeExperience);
WRAPPER_READ(wrapper, "CvCity", &m_iEspionageDefenseModifier);
// < M.A.D. Nukes Start >
WRAPPER_READ(wrapper, "CvCity", &m_iMADIncoming);
// < M.A.D. Nukes End >
WRAPPER_READ(wrapper, "CvCity", &m_bNeverLost);
WRAPPER_READ(wrapper, "CvCity", &m_bBombarded);
WRAPPER_READ(wrapper, "CvCity", &m_bDrafted);
WRAPPER_READ(wrapper, "CvCity", &m_bAirliftTargeted);
WRAPPER_READ(wrapper, "CvCity", &m_bWeLoveTheKingDay);
WRAPPER_READ(wrapper, "CvCity", &m_bCitizensAutomated);
WRAPPER_READ(wrapper, "CvCity", &m_bProductionAutomated);
WRAPPER_READ(wrapper, "CvCity", &m_bWallOverride);
// m_bInfoDirty not saved...
// m_bLayoutDirty not saved...
WRAPPER_READ(wrapper, "CvCity", &m_bPlundered);
WRAPPER_READ(wrapper, "CvCity", (int*)&m_eOwner);
WRAPPER_READ(wrapper, "CvCity", (int*)&m_ePreviousOwner);
WRAPPER_READ(wrapper, "CvCity", (int*)&m_eOriginalOwner);
WRAPPER_READ(wrapper, "CvCity", (int*)&m_eCultureLevel);
if ( m_eCultureLevel >= GC.getNumCultureLevelInfos() )
{
m_eCultureLevel = (CultureLevelTypes)(GC.getNumCultureLevelInfos() - 1);
}
recalculateZoomLevel(m_eCultureLevel);
/************************************************************************************************/
/* REVOLUTION_MOD 06/10/08 jdog5000 */
/* */
/* */
/************************************************************************************************/
WRAPPER_READ(wrapper, "CvCity", &m_iRevolutionIndex);
WRAPPER_READ(wrapper, "CvCity", &m_iLocalRevIndex);
WRAPPER_READ(wrapper, "CvCity", &m_iRevIndexAverage);
WRAPPER_READ(wrapper, "CvCity", &m_iRevolutionCounter);
WRAPPER_READ(wrapper, "CvCity", &m_iReinforcementCounter);
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiSeaPlotYield);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiRiverPlotYield);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiBaseYieldRate);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiYieldRateModifier);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiPowerYieldRateModifier);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiBonusYieldRateModifier);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiTradeYield);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiCorporationYield);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiExtraSpecialistYield);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiCommerceRate);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiProductionToCommerceModifier);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiBuildingCommerce);
// Old style was in units of gold - if we still have that load it and multiply it up...
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiSpecialistCommerce);
for(iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
m_aiSpecialistCommerce[iI] *= 100;
}
// ...but new style is in 100ths to avoid rounding issues, so overwrite with that if present
WRAPPER_READ_ARRAY_DECORATED(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiSpecialistCommerce, "m_aiSpecialistCommerceTimes100");
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiReligionCommerce);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiCorporationCommerce);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiCommerceRateModifier);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiCommerceHappinessPer);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_DOMAIN_TYPES, m_aiDomainFreeExperience);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_DOMAIN_TYPES, m_aiDomainProductionModifier);
WRAPPER_READ_ARRAY(wrapper, "CvCity", MAX_PLAYERS, m_aiCulture);
WRAPPER_READ_ARRAY(wrapper, "CvCity", MAX_PLAYERS, m_aiNumRevolts);
WRAPPER_READ_ARRAY(wrapper, "CvCity", MAX_PLAYERS, m_abEverOwned);
WRAPPER_READ_ARRAY(wrapper, "CvCity", MAX_PLAYERS, m_abTradeRoute);
WRAPPER_READ_ARRAY(wrapper, "CvCity", MAX_TEAMS, m_abRevealed);
WRAPPER_READ_ARRAY(wrapper, "CvCity", MAX_TEAMS, m_abEspionageVisibility);
WRAPPER_READ_STRING(wrapper, "CvCity", m_szName);
WRAPPER_READ_STRING(wrapper, "CvCity", m_szScriptData);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BONUSES, GC.getNumBonusInfos(), m_paiNoBonus);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BONUSES, GC.getNumBonusInfos(), m_paiFreeBonus);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BONUSES, GC.getNumBonusInfos(), m_paiNumBonuses);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BONUSES, GC.getNumBonusInfos(), m_paiNumCorpProducedBonuses);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_PROJECTS, GC.getNumProjectInfos(), m_paiProjectProduction);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_paiBuildingProduction);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_paiBuildingProductionTime);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_paiBuildingOriginalOwner);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_paiBuildingOriginalTime);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_UNITS, GC.getNumUnitInfos(), m_paiUnitProduction);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_UNITS, GC.getNumUnitInfos(), m_paiUnitProductionTime);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_UNITS, GC.getNumUnitInfos(), m_paiGreatPeopleUnitRate);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_UNITS, GC.getNumUnitInfos(), m_paiGreatPeopleUnitProgress);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_SPECIALISTS, GC.getNumSpecialistInfos(), m_paiSpecialistCount);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_SPECIALISTS, GC.getNumSpecialistInfos(), m_paiMaxSpecialistCount);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_SPECIALISTS, GC.getNumSpecialistInfos(), m_paiForceSpecialistCount);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_SPECIALISTS, GC.getNumSpecialistInfos(), m_paiFreeSpecialistCount);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_SPECIALISTS, GC.getNumSpecialistInfos(), m_paiFreeSpecialistCountUnattributed);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_IMPROVEMENTS, GC.getNumImprovementInfos(), m_paiImprovementFreeSpecialists);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_RELIGIONS, GC.getNumReligionInfos(), m_paiReligionInfluence);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_RELIGIONS, GC.getNumReligionInfos(), m_paiStateReligionHappiness);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_COMBATINFOS, GC.getNumUnitCombatInfos(), m_paiUnitCombatFreeExperience);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_PROMOTIONS, GC.getNumPromotionInfos(), m_paiFreePromotionCount);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_paiNumRealBuilding);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_paiNumFreeBuilding);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_paiNumFreeAreaBuilding);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_paiNumFreeTradeRegionBuilding);
WRAPPER_READ(wrapper, "CvCity", &m_bHasCalculatedBuildingReplacement);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_CITY_PLOTS, m_pabWorkingPlot);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_RELIGIONS, GC.getNumReligionInfos(), m_pabHasReligion);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_CORPORATIONS, GC.getNumCorporationInfos(), m_pabHasCorporation);
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
WRAPPER_READ(wrapper, "CvCity", &m_iImprovementGoodHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iImprovementBadHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iSpecialistGoodHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iSpecialistBadHealth);
WRAPPER_READ(wrapper, "CvCity", &m_iSpecialistHappiness);
WRAPPER_READ(wrapper, "CvCity", &m_iSpecialistUnhappiness);
WRAPPER_READ(wrapper, "CvCity", &m_iEventAnger);
// Must recalculate from first principles if loading an old format save
m_fPopulationgrowthratepercentageLog = INVALID_GROWTH_PERCENT_LOG;
WRAPPER_SKIP_ELEMENT(wrapper, "CvCity", m_iPopulationgrowthratepercentage, SAVE_VALUE_ANY);
WRAPPER_READ(wrapper, "CvCity", &m_fPopulationgrowthratepercentageLog);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiBonusCommerceRateModifier);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiBonusCommercePercentChanges);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiCommerceAttacks);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiMaxCommerceAttacks);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_UNIT_CLASSES, GC.getNumUnitClassInfos(), m_paiUnitClassProductionModifier);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDING_CLASSES, GC.getNumBuildingClassInfos(), m_paiBuildingClassProductionModifier);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BONUSES, GC.getNumBonusInfos(), m_paiBonusDefenseChanges);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BONUSES, GC.getNumBonusInfos(), m_pabHadVicinityBonus);
WRAPPER_READ_CLASS_ENUM(wrapper, "CvCity", REMAPPED_CLASS_TYPE_CIVILIZATIONS, &m_iCiv);
WRAPPER_READ(wrapper, "CvCity", &m_iExtraYieldTurns);
WRAPPER_READ(wrapper, "CvCity", (int*)&m_eOccupationCultureLevel);
WRAPPER_READ(wrapper, "CvCity", &m_iLineOfSight);
WRAPPER_READ(wrapper, "CvCity", &m_iLandmarkAngerTimer);
WRAPPER_READ(wrapper, "CvCity", &m_iInvasionChance);
WRAPPER_READ(wrapper, "CvCity", &m_iInvasionTimer);
WRAPPER_READ(wrapper, "CvCity", &m_iFreshWater);
// Cope with older saves
if ( wrapper.isUsingTaggedFormat() )
{
bool bFreshWater = false;
WRAPPER_READ_DECORATED(wrapper, "CvCity", &bFreshWater, "m_bFreshWater");
if ( 0 == m_iFreshWater && bFreshWater )
{
m_iFreshWater = 1;
}
}
WRAPPER_READ(wrapper, "CvCity", &m_iAdjacentDamagePercent);
WRAPPER_READ(wrapper, "CvCity", &m_iWorkableRadiusOverride);
WRAPPER_READ(wrapper, "CvCity", &m_iProtectedCultureCount);
WRAPPER_READ(wrapper, "CvCity", &m_iNumUnitFullHeal);
WRAPPER_READ(wrapper, "CvCity", &m_iDisabledPowerTimer);
WRAPPER_READ(wrapper, "CvCity", &m_iWarWearinessTimer);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_pabDisabledBuilding);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_COMBATINFOS, GC.getNumUnitCombatInfos(), m_paiUnitCombatExtraStrength);
WRAPPER_READ_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDS, GC.getNumBuildInfos(), m_pabAutomatedCanBuild);
WRAPPER_READ(wrapper, "CvCity", &m_iMinimumDefenseLevel);
WRAPPER_READ(wrapper, "CvCity", &m_iNumPopulationEmployed);
WRAPPER_READ(wrapper, "CvCity", &m_iHappinessPercentPerPopulation);
WRAPPER_READ(wrapper, "CvCity", &m_iHealthPercentPerPopulation);
WRAPPER_READ(wrapper, "CvCity", &m_iExtraCapitalCommerce);
WRAPPER_READ(wrapper, "CvCity", &m_iExtraForeignCapitalCommerce);
WRAPPER_READ(wrapper, "CvCity", &m_iPreviousExtraCommerce);
WRAPPER_READ(wrapper, "CvCity", &m_iPreviousConnectedCommerce);
WRAPPER_READ(wrapper, "CvCity", &m_iPreviousForeignConnectedCommerce);
WRAPPER_READ(wrapper, "CvCity", &m_iForeignConnectednessNeeded);
WRAPPER_READ(wrapper, "CvCity", &m_iForcedRevolutionCounter);
WRAPPER_READ_ARRAY(wrapper, "CvCity", MAX_PLAYERS, m_aiSeizedForeignConnectedness);
WRAPPER_READ(wrapper, "CvCity", &iNumElts);
m_aBuildingCommerceModifier.clear();
for (unsigned int i = 0; i < iNumElts; ++i)
{
BuildingCommerceModifier kChange;
kChange.read(pStream);
m_aBuildingCommerceModifier.push_back(kChange);
}
WRAPPER_READ(wrapper, "CvCity", &iNumElts);
m_aBuildingYieldModifier.clear();
for (unsigned int i = 0; i < iNumElts; ++i)
{
BuildingYieldModifier kChange;
kChange.read(pStream);
m_aBuildingYieldModifier.push_back(kChange);
}
/* for (iI=0;iI<GC.getNumImprovementInfos();iI++)
{
WRAPPER_READ(wrapper, "CvCity", NUM_YIELD_TYPES, m_ppaaiImprovementYieldChange[iI]);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
int iMaxTradeRoutes = GC.getMAX_TRADE_ROUTES();
WRAPPER_READ(wrapper, "CvCity", &iMaxTradeRoutes);
for (iI=0;iI<iMaxTradeRoutes;iI++)
{
if (iI < GC.getMAX_TRADE_ROUTES())
{
WRAPPER_READ(wrapper, "CvCity", (int*)&m_paTradeCities[iI].eOwner);
WRAPPER_READ(wrapper, "CvCity", &m_paTradeCities[iI].iID);
}
else
{
WRAPPER_SKIP_ELEMENT(wrapper, "CvCity", m_paTradeCities[iI].eOwner, SAVE_VALUE_TYPE_INT);
WRAPPER_SKIP_ELEMENT(wrapper, "CvCity", m_paTradeCities[iI].iID, SAVE_VALUE_TYPE_INT);
}
}
while (iI < GC.getMAX_TRADE_ROUTES())
{
m_paTradeCities[iI].eOwner = NO_PLAYER;
m_paTradeCities[iI++].iID = -1;
}
bool bOrdersHaveContractInfo = false;
WRAPPER_READ(wrapper, "CvCity", &bOrdersHaveContractInfo);
m_orderQueue.Read(pStream);
// The order queue itself is not a streamable type so is serialized in raw
// binary image, which means we need to do some explicit tranlsation on load
// if we are using the tagged format
if ( wrapper.isUsingTaggedFormat() )
{
CLLNode<OrderData>* pNode = headOrderQueueNode();
while (pNode != NULL)
{
bool bDeleteNode = false;
switch(pNode->m_data.eOrderType)
{
case ORDER_TRAIN:
{
int eUnit = wrapper.getNewClassEnumValue(REMAPPED_CLASS_TYPE_UNITS, EXTERNAL_ORDER_IDATA(pNode->m_data.iData1), true);
bDeleteNode = (eUnit == -1);
pNode->m_data.iData1 = PACK_INTERNAL_ORDER_IDATA(eUnit,
bOrdersHaveContractInfo ? INTERNAL_AUXILIARY_ORDER_IDATA(pNode->m_data.iData1) : 0xFFFF);
}
break;
case ORDER_CONSTRUCT:
bDeleteNode = ((pNode->m_data.iData1 = wrapper.getNewClassEnumValue(REMAPPED_CLASS_TYPE_BUILDINGS,pNode->m_data.iData1, true)) == -1);
break;
case ORDER_CREATE:
bDeleteNode = ((pNode->m_data.iData1 = wrapper.getNewClassEnumValue(REMAPPED_CLASS_TYPE_PROJECTS,pNode->m_data.iData1, true)) == -1);
break;
case ORDER_MAINTAIN:
case ORDER_LIST:
break;
default:
break;
}
if ( bDeleteNode )
{
pNode = m_orderQueue.deleteNode(pNode);
}
else
{
pNode = nextOrderQueueNode(pNode);
}
}
}
WRAPPER_READ(wrapper, "CvCity", &m_iPopulationRank);
WRAPPER_READ(wrapper, "CvCity", &m_bPopulationRankValid);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiBaseYieldRank);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_abBaseYieldRankValid);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiYieldRank);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_abYieldRankValid);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiCommerceRank);
WRAPPER_READ_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_abCommerceRankValid);
m_Properties.readWrapper(pStream);
WRAPPER_READ(wrapper, "CvCity", &iNumElts);
m_aEventsOccured.clear();
for (unsigned int i = 0; i < iNumElts; ++i)
{
EventTypes eEvent = NO_EVENT;
WRAPPER_READ_CLASS_ENUM_ALLOW_MISSING(wrapper, "CvCity", REMAPPED_CLASS_TYPE_EVENTS, (int*)&eEvent);
if ( eEvent == NO_EVENT )
{
// Old format so go for a raw read as the best we can do
WRAPPER_READ(wrapper, "CvCity", (int*)&eEvent);
}
if ( eEvent != NO_EVENT )
{
m_aEventsOccured.push_back(eEvent);
}
}
WRAPPER_READ(wrapper, "CvCity", &iNumElts);
m_aBuildingYieldChange.clear();
for (unsigned int i = 0; i < iNumElts; ++i)
{
BuildingYieldChange kChange;
kChange.read(pStream);
m_aBuildingYieldChange.push_back(kChange);
}
WRAPPER_READ(wrapper, "CvCity", &iNumElts);
m_aBuildingCommerceChange.clear();
for (unsigned int i = 0; i < iNumElts; ++i)
{
BuildingCommerceChange kChange;
kChange.read(pStream);
m_aBuildingCommerceChange.push_back(kChange);
}
WRAPPER_READ(wrapper, "CvCity", &iNumElts);
m_aBuildingHappyChange.clear();
for (unsigned int i = 0; i < iNumElts; ++i)
{
int iBuildingClass;
WRAPPER_READ_CLASS_ENUM(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDING_CLASSES, &iBuildingClass);
int iChange;
WRAPPER_READ(wrapper, "CvCity", &iChange);
m_aBuildingHappyChange.push_back(std::make_pair((BuildingClassTypes)iBuildingClass, iChange));
}
WRAPPER_READ(wrapper, "CvCity", &iNumElts);
m_aBuildingHealthChange.clear();
for (unsigned int i = 0; i < iNumElts; ++i)
{
int iBuildingClass;
WRAPPER_READ_CLASS_ENUM(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDING_CLASSES, &iBuildingClass);
int iChange;
WRAPPER_READ(wrapper, "CvCity", &iChange);
m_aBuildingHealthChange.push_back(std::make_pair((BuildingClassTypes)iBuildingClass, iChange));
}
// Now the owner has been restored from the save set the info on the building list
m_BuildingList.setPlayerToOwner();
m_UnitList.setPlayerToOwner();
/************************************************************************************************/
/* phunny_pharmer Start 05/01/10 */
/* clear the culture distance cache (note that it is not saved in the .sav file) */
/************************************************************************************************/
clearCultureDistanceCache();
/************************************************************************************************/
/* phunny_pharmer END */
/************************************************************************************************/
WRAPPER_READ(wrapper, "CvCity", &m_iExtraBuildingDefenseRecoverySpeedModifier);
WRAPPER_READ(wrapper, "CvCity", &m_iModifiedBuildingDefenseRecoverySpeedCap);
WRAPPER_READ(wrapper, "CvCity", &m_iExtraCityDefenseRecoverySpeedModifier);
WRAPPER_READ(wrapper, "CvCity", &m_iZoCCount);
WRAPPER_READ_OBJECT_END(wrapper);
}
void CvCity::write(FDataStreamBase* pStream)
{
int iI;
uint uiFlag=0;
CvTaggedSaveFormatWrapper& wrapper = CvTaggedSaveFormatWrapper::getSaveFormatWrapper();
wrapper.AttachToStream(pStream);
WRAPPER_WRITE_OBJECT_START(wrapper);
WRAPPER_WRITE(wrapper, "CvCity", uiFlag); // flag for expansion
WRAPPER_WRITE(wrapper, "CvCity", m_iID);
WRAPPER_WRITE(wrapper, "CvCity", m_iX);
WRAPPER_WRITE(wrapper, "CvCity", m_iY);
WRAPPER_WRITE(wrapper, "CvCity", m_iRallyX);
WRAPPER_WRITE(wrapper, "CvCity", m_iRallyY);
WRAPPER_WRITE(wrapper, "CvCity", m_iGameTurnFounded);
WRAPPER_WRITE(wrapper, "CvCity", m_iGameTurnAcquired);
WRAPPER_WRITE(wrapper, "CvCity", m_iPopulation);
WRAPPER_WRITE(wrapper, "CvCity", m_iHighestPopulation);
WRAPPER_WRITE(wrapper, "CvCity", m_iWorkingPopulation);
WRAPPER_WRITE(wrapper, "CvCity", m_iSpecialistPopulation);
WRAPPER_WRITE(wrapper, "CvCity", m_iNumGreatPeople);
WRAPPER_WRITE(wrapper, "CvCity", m_iBaseGreatPeopleRate);
WRAPPER_WRITE(wrapper, "CvCity", m_iGreatPeopleRateModifier);
WRAPPER_WRITE(wrapper, "CvCity", m_iGreatPeopleProgress);
WRAPPER_WRITE(wrapper, "CvCity", m_iNumWorldWonders);
WRAPPER_WRITE(wrapper, "CvCity", m_iNumTeamWonders);
WRAPPER_WRITE(wrapper, "CvCity", m_iNumNationalWonders);
WRAPPER_WRITE(wrapper, "CvCity", m_iNumBuildings);
WRAPPER_WRITE(wrapper, "CvCity", m_iGovernmentCenterCount);
WRAPPER_WRITE(wrapper, "CvCity", m_iMaintenance);
WRAPPER_WRITE(wrapper, "CvCity", m_iMaintenanceModifier);
WRAPPER_WRITE(wrapper, "CvCity", m_iWarWearinessModifier);
WRAPPER_WRITE(wrapper, "CvCity", m_iHurryAngerModifier);
WRAPPER_WRITE(wrapper, "CvCity", m_iHealRate);
WRAPPER_WRITE(wrapper, "CvCity", m_iEspionageHealthCounter);
WRAPPER_WRITE(wrapper, "CvCity", m_iEspionageHappinessCounter);
WRAPPER_WRITE(wrapper, "CvCity", m_iFreshWaterGoodHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iFreshWaterBadHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iFeatureGoodHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iFeatureBadHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iBuildingGoodHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iBuildingBadHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iPowerGoodHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iPowerBadHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iBonusGoodHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iBonusBadHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iHurryAngerTimer);
/************************************************************************************************/
/* REVOLUTION_MOD 04/28/08 jdog5000 */
/* */
/* */
/************************************************************************************************/
WRAPPER_WRITE(wrapper, "CvCity", m_iRevRequestAngerTimer);
WRAPPER_WRITE(wrapper, "CvCity", m_iRevSuccessTimer);
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
WRAPPER_WRITE(wrapper, "CvCity", m_iConscriptAngerTimer);
WRAPPER_WRITE(wrapper, "CvCity", m_iDefyResolutionAngerTimer);
WRAPPER_WRITE(wrapper, "CvCity", m_iHappinessTimer);
WRAPPER_WRITE(wrapper, "CvCity", m_iMilitaryHappinessUnits);
WRAPPER_WRITE(wrapper, "CvCity", m_iBuildingGoodHappiness);
WRAPPER_WRITE(wrapper, "CvCity", m_iBuildingBadHappiness);
WRAPPER_WRITE(wrapper, "CvCity", m_iExtraBuildingGoodHappiness);
WRAPPER_WRITE(wrapper, "CvCity", m_iExtraBuildingBadHappiness);
WRAPPER_WRITE(wrapper, "CvCity", m_iExtraBuildingGoodHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iExtraBuildingBadHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iFeatureGoodHappiness);
WRAPPER_WRITE(wrapper, "CvCity", m_iFeatureBadHappiness);
WRAPPER_WRITE(wrapper, "CvCity", m_iBonusGoodHappiness);
WRAPPER_WRITE(wrapper, "CvCity", m_iBonusBadHappiness);
WRAPPER_WRITE(wrapper, "CvCity", m_iReligionGoodHappiness);
WRAPPER_WRITE(wrapper, "CvCity", m_iReligionBadHappiness);
WRAPPER_WRITE(wrapper, "CvCity", m_iExtraHappiness);
WRAPPER_WRITE(wrapper, "CvCity", m_iExtraHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iNoUnhappinessCount);
WRAPPER_WRITE(wrapper, "CvCity", m_iNoUnhealthyPopulationCount);
WRAPPER_WRITE(wrapper, "CvCity", m_iBuildingOnlyHealthyCount);
WRAPPER_WRITE(wrapper, "CvCity", m_iFood);
WRAPPER_WRITE(wrapper, "CvCity", m_iFoodKept);
WRAPPER_WRITE(wrapper, "CvCity", m_fMaxFoodKeptMultiplierLog);
WRAPPER_WRITE(wrapper, "CvCity", m_iOverflowProduction);
WRAPPER_WRITE(wrapper, "CvCity", m_iFeatureProduction);
WRAPPER_WRITE(wrapper, "CvCity", m_iMilitaryProductionModifier);
WRAPPER_WRITE(wrapper, "CvCity", m_iSpaceProductionModifier);
WRAPPER_WRITE(wrapper, "CvCity", m_iExtraTradeRoutes);
WRAPPER_WRITE(wrapper, "CvCity", m_iTradeRouteModifier);
WRAPPER_WRITE(wrapper, "CvCity", m_iForeignTradeRouteModifier);
WRAPPER_WRITE(wrapper, "CvCity", m_iBuildingDefense);
WRAPPER_WRITE(wrapper, "CvCity", m_iBuildingBombardDefense);
WRAPPER_WRITE(wrapper, "CvCity", m_iFreeExperience);
WRAPPER_WRITE(wrapper, "CvCity", m_iCurrAirlift);
WRAPPER_WRITE(wrapper, "CvCity", m_iMaxAirlift);
WRAPPER_WRITE(wrapper, "CvCity", m_iAirModifier);
WRAPPER_WRITE(wrapper, "CvCity", m_iAirUnitCapacity);
WRAPPER_WRITE(wrapper, "CvCity", m_iWonderCapacityIncrement);
WRAPPER_WRITE(wrapper, "CvCity", m_iNukeModifier);
WRAPPER_WRITE(wrapper, "CvCity", m_iFreeSpecialist);
WRAPPER_WRITE(wrapper, "CvCity", m_iPowerCount);
WRAPPER_WRITE(wrapper, "CvCity", m_iDirtyPowerCount);
WRAPPER_WRITE(wrapper, "CvCity", m_iDefenseDamage);
WRAPPER_WRITE(wrapper, "CvCity", m_iLastDefenseDamage);
WRAPPER_WRITE(wrapper, "CvCity", m_iOccupationTimer);
WRAPPER_WRITE(wrapper, "CvCity", m_iCultureUpdateTimer);
WRAPPER_WRITE(wrapper, "CvCity", m_iCitySizeBoost);
WRAPPER_WRITE(wrapper, "CvCity", m_iSpecialistFreeExperience);
WRAPPER_WRITE(wrapper, "CvCity", m_iEspionageDefenseModifier);
// < M.A.D. Nukes Start >
WRAPPER_WRITE(wrapper, "CvCity", m_iMADIncoming);
// < M.A.D. Nukes End >
WRAPPER_WRITE(wrapper, "CvCity", m_bNeverLost);
WRAPPER_WRITE(wrapper, "CvCity", m_bBombarded);
WRAPPER_WRITE(wrapper, "CvCity", m_bDrafted);
WRAPPER_WRITE(wrapper, "CvCity", m_bAirliftTargeted);
WRAPPER_WRITE(wrapper, "CvCity", m_bWeLoveTheKingDay);
WRAPPER_WRITE(wrapper, "CvCity", m_bCitizensAutomated);
WRAPPER_WRITE(wrapper, "CvCity", m_bProductionAutomated);
WRAPPER_WRITE(wrapper, "CvCity", m_bWallOverride);
// m_bInfoDirty not saved...
// m_bLayoutDirty not saved...
WRAPPER_WRITE(wrapper, "CvCity", m_bPlundered);
WRAPPER_WRITE(wrapper, "CvCity", m_eOwner);
WRAPPER_WRITE(wrapper, "CvCity", m_ePreviousOwner);
WRAPPER_WRITE(wrapper, "CvCity", m_eOriginalOwner);
WRAPPER_WRITE(wrapper, "CvCity", m_eCultureLevel);
/************************************************************************************************/
/* REVOLUTION_MOD 06/10/08 jdog5000 */
/* */
/* */
/************************************************************************************************/
WRAPPER_WRITE(wrapper, "CvCity", m_iRevolutionIndex);
WRAPPER_WRITE(wrapper, "CvCity", m_iLocalRevIndex);
WRAPPER_WRITE(wrapper, "CvCity", m_iRevIndexAverage);
WRAPPER_WRITE(wrapper, "CvCity", m_iRevolutionCounter);
WRAPPER_WRITE(wrapper, "CvCity", m_iReinforcementCounter);
/************************************************************************************************/
/* REVOLUTION_MOD END */
/************************************************************************************************/
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiSeaPlotYield);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiRiverPlotYield);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiBaseYieldRate);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiYieldRateModifier);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiPowerYieldRateModifier);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiBonusYieldRateModifier);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiTradeYield);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiCorporationYield);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiExtraSpecialistYield);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiCommerceRate);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiProductionToCommerceModifier);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiBuildingCommerce);
WRAPPER_WRITE_ARRAY_DECORATED(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiSpecialistCommerce, "m_aiSpecialistCommerceTimes100");
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiReligionCommerce);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiCorporationCommerce);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiCommerceRateModifier);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiCommerceHappinessPer);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_DOMAIN_TYPES, m_aiDomainFreeExperience);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_DOMAIN_TYPES, m_aiDomainProductionModifier);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", MAX_PLAYERS, m_aiCulture);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", MAX_PLAYERS, m_aiNumRevolts);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", MAX_PLAYERS, m_abEverOwned);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", MAX_PLAYERS, m_abTradeRoute);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", MAX_TEAMS, m_abRevealed);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", MAX_TEAMS, m_abEspionageVisibility);
WRAPPER_WRITE_STRING(wrapper, "CvCity", m_szName);
WRAPPER_WRITE_STRING(wrapper, "CvCity", m_szScriptData);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BONUSES, GC.getNumBonusInfos(), m_paiNoBonus);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BONUSES, GC.getNumBonusInfos(), m_paiFreeBonus);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BONUSES, GC.getNumBonusInfos(), m_paiNumBonuses);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BONUSES, GC.getNumBonusInfos(), m_paiNumCorpProducedBonuses);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_PROJECTS, GC.getNumProjectInfos(), m_paiProjectProduction);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_paiBuildingProduction);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_paiBuildingProductionTime);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_paiBuildingOriginalOwner);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_paiBuildingOriginalTime);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_UNITS, GC.getNumUnitInfos(), m_paiUnitProduction);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_UNITS, GC.getNumUnitInfos(), m_paiUnitProductionTime);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_UNITS, GC.getNumUnitInfos(), m_paiGreatPeopleUnitRate);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_UNITS, GC.getNumUnitInfos(), m_paiGreatPeopleUnitProgress);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_SPECIALISTS, GC.getNumSpecialistInfos(), m_paiSpecialistCount);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_SPECIALISTS, GC.getNumSpecialistInfos(), m_paiMaxSpecialistCount);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_SPECIALISTS, GC.getNumSpecialistInfos(), m_paiForceSpecialistCount);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_SPECIALISTS, GC.getNumSpecialistInfos(), m_paiFreeSpecialistCount);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_SPECIALISTS, GC.getNumSpecialistInfos(), m_paiFreeSpecialistCountUnattributed);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_IMPROVEMENTS, GC.getNumImprovementInfos(), m_paiImprovementFreeSpecialists);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_RELIGIONS, GC.getNumReligionInfos(), m_paiReligionInfluence);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_RELIGIONS, GC.getNumReligionInfos(), m_paiStateReligionHappiness);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_COMBATINFOS, GC.getNumUnitCombatInfos(), m_paiUnitCombatFreeExperience);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_PROMOTIONS, GC.getNumPromotionInfos(), m_paiFreePromotionCount);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_paiNumRealBuilding);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_paiNumFreeBuilding);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_paiNumFreeAreaBuilding);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_paiNumFreeTradeRegionBuilding);
WRAPPER_WRITE(wrapper, "CvCity", m_bHasCalculatedBuildingReplacement);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_CITY_PLOTS, m_pabWorkingPlot);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_RELIGIONS, GC.getNumReligionInfos(), m_pabHasReligion);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_CORPORATIONS, GC.getNumCorporationInfos(), m_pabHasCorporation);
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
WRAPPER_WRITE(wrapper, "CvCity", m_iImprovementGoodHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iImprovementBadHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iSpecialistGoodHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iSpecialistBadHealth);
WRAPPER_WRITE(wrapper, "CvCity", m_iSpecialistHappiness);
WRAPPER_WRITE(wrapper, "CvCity", m_iSpecialistUnhappiness);
WRAPPER_WRITE(wrapper, "CvCity", m_iEventAnger);
WRAPPER_WRITE(wrapper, "CvCity", m_fPopulationgrowthratepercentageLog);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiBonusCommerceRateModifier);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiBonusCommercePercentChanges);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiCommerceAttacks);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiMaxCommerceAttacks);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_UNIT_CLASSES, GC.getNumUnitClassInfos(), m_paiUnitClassProductionModifier);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDING_CLASSES, GC.getNumBuildingClassInfos(), m_paiBuildingClassProductionModifier);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BONUSES, GC.getNumBonusInfos(), m_paiBonusDefenseChanges);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BONUSES, GC.getNumBonusInfos(), m_pabHadVicinityBonus);
WRAPPER_WRITE_CLASS_ENUM(wrapper, "CvCity", REMAPPED_CLASS_TYPE_CIVILIZATIONS, m_iCiv);
WRAPPER_WRITE(wrapper, "CvCity", m_iExtraYieldTurns);
WRAPPER_WRITE(wrapper, "CvCity", m_eOccupationCultureLevel);
WRAPPER_WRITE(wrapper, "CvCity", m_iLineOfSight);
WRAPPER_WRITE(wrapper, "CvCity", m_iLandmarkAngerTimer);
WRAPPER_WRITE(wrapper, "CvCity", m_iInvasionChance);
WRAPPER_WRITE(wrapper, "CvCity", m_iInvasionTimer);
WRAPPER_WRITE(wrapper, "CvCity", m_iFreshWater);
WRAPPER_WRITE(wrapper, "CvCity", m_iAdjacentDamagePercent);
WRAPPER_WRITE(wrapper, "CvCity", m_iWorkableRadiusOverride);
WRAPPER_WRITE(wrapper, "CvCity", m_iProtectedCultureCount);
WRAPPER_WRITE(wrapper, "CvCity", m_iNumUnitFullHeal);
WRAPPER_WRITE(wrapper, "CvCity", m_iDisabledPowerTimer);
WRAPPER_WRITE(wrapper, "CvCity", m_iWarWearinessTimer);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDINGS, GC.getNumBuildingInfos(), m_pabDisabledBuilding);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_COMBATINFOS, GC.getNumUnitCombatInfos(), m_paiUnitCombatExtraStrength);
WRAPPER_WRITE_CLASS_ARRAY(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDS, GC.getNumBuildInfos(), m_pabAutomatedCanBuild);
WRAPPER_WRITE(wrapper, "CvCity", m_iMinimumDefenseLevel);
WRAPPER_WRITE(wrapper, "CvCity", m_iNumPopulationEmployed);
WRAPPER_WRITE(wrapper, "CvCity", m_iHappinessPercentPerPopulation);
WRAPPER_WRITE(wrapper, "CvCity", m_iHealthPercentPerPopulation);
WRAPPER_WRITE(wrapper, "CvCity", m_iExtraCapitalCommerce);
WRAPPER_WRITE(wrapper, "CvCity", m_iExtraForeignCapitalCommerce);
WRAPPER_WRITE(wrapper, "CvCity", m_iPreviousExtraCommerce);
WRAPPER_WRITE(wrapper, "CvCity", m_iPreviousConnectedCommerce);
WRAPPER_WRITE(wrapper, "CvCity", m_iPreviousForeignConnectedCommerce);
WRAPPER_WRITE(wrapper, "CvCity", m_iForeignConnectednessNeeded);
WRAPPER_WRITE(wrapper, "CvCity", m_iForcedRevolutionCounter);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", MAX_PLAYERS, m_aiSeizedForeignConnectedness);
WRAPPER_WRITE_DECORATED(wrapper, "CvCity", m_aBuildingCommerceModifier.size(), "iNumElts");
for (std::vector<BuildingCommerceModifier>::iterator it = m_aBuildingCommerceModifier.begin(); it != m_aBuildingCommerceModifier.end(); ++it)
{
(*it).write(pStream);
}
WRAPPER_WRITE_DECORATED(wrapper, "CvCity", m_aBuildingYieldModifier.size(), "iNumElts");
for (std::vector<BuildingYieldModifier>::iterator it = m_aBuildingYieldModifier.begin(); it != m_aBuildingYieldModifier.end(); ++it)
{
(*it).write(pStream);
}
/* for (iI=0;iI<GC.getNumImprovementInfos();iI++)
{
WRAPPER_WRITE(wrapper, "CvCity", NUM_YIELD_TYPES, m_ppaaiImprovementYieldChange[iI]);
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
WRAPPER_WRITE_DECORATED(wrapper, "CvCity", GC.getMAX_TRADE_ROUTES(), "iMaxTradeRoutes");
for (iI = 0; iI<GC.getMAX_TRADE_ROUTES(); iI++)
{
WRAPPER_WRITE(wrapper, "CvCity", m_paTradeCities[iI].eOwner);
WRAPPER_WRITE(wrapper, "CvCity", m_paTradeCities[iI].iID);
}
WRAPPER_WRITE_DECORATED(wrapper, "CvCity", true, "bOrdersHaveContractInfo");
m_orderQueue.Write(pStream);
WRAPPER_WRITE(wrapper, "CvCity", m_iPopulationRank);
WRAPPER_WRITE(wrapper, "CvCity", m_bPopulationRankValid);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiBaseYieldRank);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_abBaseYieldRankValid);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_aiYieldRank);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_YIELD_TYPES, m_abYieldRankValid);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_aiCommerceRank);
WRAPPER_WRITE_ARRAY(wrapper, "CvCity", NUM_COMMERCE_TYPES, m_abCommerceRankValid);
m_Properties.writeWrapper(pStream);
WRAPPER_WRITE_DECORATED(wrapper, "CvCity", m_aEventsOccured.size(), "iNumElts");
for (std::vector<EventTypes>::iterator it = m_aEventsOccured.begin(); it != m_aEventsOccured.end(); ++it)
{
WRAPPER_WRITE_CLASS_ENUM_DECORATED(wrapper, "CvCity", REMAPPED_CLASS_TYPE_EVENTS, *it, "eEvent");
}
WRAPPER_WRITE_DECORATED(wrapper, "CvCity", m_aBuildingYieldChange.size(), "iNumElts");
for (std::vector<BuildingYieldChange>::iterator it = m_aBuildingYieldChange.begin(); it != m_aBuildingYieldChange.end(); ++it)
{
(*it).write(pStream);
}
WRAPPER_WRITE_DECORATED(wrapper, "CvCity", m_aBuildingCommerceChange.size(), "iNumElts");
for (std::vector<BuildingCommerceChange>::iterator it = m_aBuildingCommerceChange.begin(); it != m_aBuildingCommerceChange.end(); ++it)
{
(*it).write(pStream);
}
WRAPPER_WRITE_DECORATED(wrapper, "CvCity", m_aBuildingHappyChange.size(), "iNumElts");
for (BuildingChangeArray::iterator it = m_aBuildingHappyChange.begin(); it != m_aBuildingHappyChange.end(); ++it)
{
WRAPPER_WRITE_CLASS_ENUM_DECORATED(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDING_CLASSES, (*it).first, "iBuildingClass");
WRAPPER_WRITE_DECORATED(wrapper, "CvCity", (*it).second, "iChange");
}
WRAPPER_WRITE_DECORATED(wrapper, "CvCity", m_aBuildingHealthChange.size(), "iNumElts");
for (BuildingChangeArray::iterator it = m_aBuildingHealthChange.begin(); it != m_aBuildingHealthChange.end(); ++it)
{
WRAPPER_WRITE_CLASS_ENUM_DECORATED(wrapper, "CvCity", REMAPPED_CLASS_TYPE_BUILDING_CLASSES, (*it).first, "iBuildingClass");
WRAPPER_WRITE_DECORATED(wrapper, "CvCity", (*it).second, "iChange");
}
WRAPPER_WRITE(wrapper, "CvCity", m_iExtraBuildingDefenseRecoverySpeedModifier);
WRAPPER_WRITE(wrapper, "CvCity", m_iModifiedBuildingDefenseRecoverySpeedCap);
WRAPPER_WRITE(wrapper, "CvCity", m_iExtraCityDefenseRecoverySpeedModifier);
WRAPPER_WRITE(wrapper, "CvCity", m_iZoCCount);
WRAPPER_WRITE_OBJECT_END(wrapper);
}
//------------------------------------------------------------------------------------------------
class VisibleBuildingComparator
{
public:
bool operator() (BuildingTypes e1, BuildingTypes e2)
{
if(GC.getBuildingInfo(e1).getVisibilityPriority() > GC.getBuildingInfo(e2).getVisibilityPriority())
return true;
else if(GC.getBuildingInfo(e1).getVisibilityPriority() == GC.getBuildingInfo(e2).getVisibilityPriority())
{
//break ties by building type higher building type
if(e1 > e2)
return true;
}
return false;
}
};
// Flags to determine which building types are displayed
#define SHOW_BUILDINGS_WONDERS 1
#define SHOW_BUILDINGS_DEFENSES 2
#define SHOW_BUILDINGS_OTHER 128
void CvCity::getVisibleBuildings(std::list<BuildingTypes>& kChosenVisible, int& iChosenNumGenerics)
{
int iNumBuildings;
BuildingTypes eCurType;
std::vector<BuildingTypes> kVisible;
int iShowFlags = GC.getDefineINT("SHOW_BUILDINGS_LEVEL");
if ( !plot()->shouldHaveFullGraphics() )
{
iChosenNumGenerics = 0;
return;
}
iNumBuildings = GC.getNumBuildingInfos();
for(int i = 0; i < iNumBuildings; i++)
{
eCurType = (BuildingTypes) i;
if(getNumBuilding(eCurType) > 0)
{
bool bValid = false;
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eCurType);
if (kBuilding.getNotShowInCity()) continue;
bool bIsWonder = isLimitedWonderClass((BuildingClassTypes)kBuilding.getBuildingClassType());
bool bIsDefense = (kBuilding.getDefenseModifier() > 0);
if ( (iShowFlags & SHOW_BUILDINGS_WONDERS) != 0 )
{
// Wonders
bValid |= bIsWonder;
}
if ( (iShowFlags & SHOW_BUILDINGS_DEFENSES) != 0 )
{
// Wonders
bValid |= bIsDefense;
}
if ( (iShowFlags & SHOW_BUILDINGS_OTHER) != 0 )
{
// Wonders
bValid |= (!bIsWonder && !bIsDefense);
}
if ( bValid )
{
kVisible.push_back(eCurType);
}
}
}
// sort the visible ones by decreasing priority
VisibleBuildingComparator kComp;
std::sort(kVisible.begin(), kVisible.end(), kComp);
// how big is this city, in terms of buildings?
// general rule: no more than fPercentUnique percent of a city can be uniques
int iTotalVisibleBuildings;
if(stricmp(GC.getDefineSTRING("GAME_CITY_SIZE_METHOD"), "METHOD_EXPONENTIAL") == 0)
{
int iCityScaleMod = ((int)(pow((float)getPopulation(), GC.getDefineFLOAT("GAME_CITY_SIZE_EXP_MODIFIER")))) * 2;
iTotalVisibleBuildings = (10 + iCityScaleMod);
}
else
{
float fLo = GC.getDefineFLOAT("GAME_CITY_SIZE_LINMAP_AT_0");
float fHi = GC.getDefineFLOAT("GAME_CITY_SIZE_LINMAP_AT_50");
float fCurSize = (float)getPopulation();
iTotalVisibleBuildings = int(((fHi - fLo) / 50.0f) * fCurSize + fLo);
}
float fMaxUniquePercent = GC.getDefineFLOAT("GAME_CITY_SIZE_MAX_PERCENT_UNIQUE");
int iMaxNumUniques = (int)(fMaxUniquePercent * iTotalVisibleBuildings);
// compute how many buildings are generics vs. unique Civ buildings?
int iNumGenerics;
int iNumUniques;
if((int)kVisible.size() > iMaxNumUniques)
{
iNumUniques = iMaxNumUniques;
}
else
{
iNumUniques = kVisible.size();
}
iNumGenerics = iTotalVisibleBuildings - iNumUniques + getCitySizeBoost();
// return
iChosenNumGenerics = iNumGenerics;
for(int i = 0; i < iNumUniques; i++)
{
kChosenVisible.push_back(kVisible[i]);
}
}
static int natGetDeterministicRandom(int iMin, int iMax, int iSeedX, int iSeedY)
{
srand(7297 * iSeedX + 2909 * iSeedY);
return (rand() % (iMax - iMin)) + iMin;
}
void CvCity::getVisibleEffects(ZoomLevelTypes eCurZoom, std::vector<const TCHAR*>& kEffectNames)
{
if (isOccupation() && isVisible(getTeam(), false) == true)
{
if (eCurZoom == ZOOM_DETAIL)
{
kEffectNames.push_back("EFFECT_CITY_BIG_BURNING_SMOKE");
kEffectNames.push_back("EFFECT_CITY_FIRE");
}
else
{
kEffectNames.push_back("EFFECT_CITY_BIG_BURNING_SMOKE");
}
return;
}
if ((getTeam() == GC.getGameINLINE().getActiveTeam()) || GC.getGameINLINE().isDebugMode())
{
if (angryPopulation() > 0)
{
kEffectNames.push_back("EFFECT_CITY_BURNING_SMOKE");
}
if (healthRate() < 0)
{
kEffectNames.push_back("EFFECT_CITY_DISEASED");
}
if (isWeLoveTheKingDay())
{
int iSeed = natGetDeterministicRandom(0, 32767, getX_INLINE(), getY_INLINE());
CvRandom kRand;
kRand.init(iSeed);
// fireworks
const TCHAR* szFireworkEffects[] =
{"FIREWORKS_RED_LARGE_SLOW",
"FIREWORKS_RED_SMALL_FAST",
"FIREWORKS_GREEN_LARGE_SLOW",
"FIREWORKS_GREEN_SMALL_FAST",
"FIREWORKS_PURPLE_LARGE_SLOW",
"FIREWORKS_PURPLE_SMALL_FAST",
"FIREWORKS_YELLOW_LARGE_SLOW",
"FIREWORKS_YELLOW_SMALL_FAST",
"FIREWORKS_BLUE_LARGE_SLOW",
"FIREWORKS_BLUE_SMALL_FAST"};
int iNumEffects = sizeof(szFireworkEffects) / sizeof(TCHAR*);
for(int i = 0; i < (iNumEffects < 3 ? iNumEffects : 3); i++)
{
kEffectNames.push_back(szFireworkEffects[kRand.get(iNumEffects)]);
}
}
}
}
void CvCity::getCityBillboardSizeIconColors(NiColorA& kDotColor, NiColorA& kTextColor) const
{
NiColorA kPlayerColor = GC.getColorInfo((ColorTypes) GC.getPlayerColorInfo(GET_PLAYER(getOwnerINLINE()).getPlayerColor()).getColorTypePrimary()).getColor();
NiColorA kGrowing;
kGrowing = NiColorA(0.73f,1,0.73f,1);
NiColorA kShrinking(1,0.73f,0.73f,1);
NiColorA kStagnant(0.83f,0.83f,0.83f,1);
NiColorA kUnknown(.5f,.5f,.5f,1);
NiColorA kWhite(1,1,1,1);
NiColorA kBlack(0,0,0,1);
if ((getTeam() == GC.getGameINLINE().getActiveTeam()))
{
if (foodDifference() < 0)
{
if ((foodDifference() == -1) && (getFood() >= ((75 * growthThreshold()) / 100)))
{
kDotColor = kStagnant;
kTextColor = kBlack;
}
else
{
kDotColor = kShrinking;
kTextColor = kBlack;
}
}
else if (foodDifference() > 0)
{
kDotColor = kGrowing;
kTextColor = kBlack;
}
else if (foodDifference() == 0)
{
kDotColor = kStagnant;
kTextColor = kBlack;
}
}
else
{
kDotColor = kPlayerColor;
NiColorA kPlayerSecondaryColor = GC.getColorInfo((ColorTypes) GC.getPlayerColorInfo(GET_PLAYER(getOwnerINLINE()).getPlayerColor()).getColorTypeSecondary()).getColor();
kTextColor = kPlayerSecondaryColor;
}
}
const TCHAR* CvCity::getCityBillboardProductionIcon() const
{
if (canBeSelected() && isProduction())
{
CLLNode<OrderData>* pOrderNode;
pOrderNode = headOrderQueueNode();
FAssert(pOrderNode != NULL);
const TCHAR* szIcon = NULL;
switch(pOrderNode->m_data.eOrderType)
{
case ORDER_TRAIN:
{
UnitTypes eType = getProductionUnit();
FAssert(eType != NO_UNIT);
szIcon = GET_PLAYER(getOwnerINLINE()).getUnitButton(eType);
break;
}
case ORDER_CONSTRUCT:
{
BuildingTypes eType = getProductionBuilding();
FAssert(eType != NO_BUILDING);
szIcon = GC.getBuildingInfo(eType).getButton();
break;
}
case ORDER_CREATE:
{
ProjectTypes eType = getProductionProject();
FAssert(eType != NO_PROJECT);
szIcon = GC.getProjectInfo(eType).getButton();
break;
}
case ORDER_MAINTAIN:
{
ProcessTypes eType = getProductionProcess();
FAssert(eType != NO_PROCESS);
szIcon = GC.getProcessInfo(eType).getButton();
break;
}
case ORDER_LIST:
{
// Should not happen
break;
}
default:
{
FAssert(false);
}
}
return szIcon;
}
else
{
return ARTFILEMGR.getInterfaceArtInfo("INTERFACE_BUTTONS_NOPRODUCTION")->getPath();
}
}
bool CvCity::getFoodBarPercentages(std::vector<float>& afPercentages) const
{
if (!canBeSelected())
{
return false;
}
afPercentages.resize(NUM_INFOBAR_TYPES, 0.0f);
if (foodDifference() < 0)
{
afPercentages[INFOBAR_STORED] = std::max(0, (getFood() + foodDifference())) / (float) growthThreshold();
afPercentages[INFOBAR_RATE_EXTRA] = std::min(-foodDifference(), getFood()) / (float) growthThreshold();
}
else
{
afPercentages[INFOBAR_STORED] = getFood() / (float) growthThreshold();
afPercentages[INFOBAR_RATE] = foodDifference() / (float) growthThreshold();
}
return true;
}
bool CvCity::getProductionBarPercentages(std::vector<float>& afPercentages) const
{
if (!canBeSelected())
{
return false;
}
if (!isProductionProcess())
{
afPercentages.resize(NUM_INFOBAR_TYPES, 0.0f);
int iProductionDiffNoFood = getCurrentProductionDifference(true, true);
int iProductionDiffJustFood = getCurrentProductionDifference(false, true) - iProductionDiffNoFood;
afPercentages[INFOBAR_STORED] = getProduction() / (float) getProductionNeeded();
afPercentages[INFOBAR_RATE] = iProductionDiffNoFood / (float) getProductionNeeded();
afPercentages[INFOBAR_RATE_EXTRA] = iProductionDiffJustFood / (float) getProductionNeeded();
}
return true;
}
NiColorA CvCity::getBarBackgroundColor() const
{
if (atWar(getTeam(), GC.getGameINLINE().getActiveTeam()))
{
return NiColorA(0.5f, 0, 0, 0.5f); // red
}
return NiColorA(0, 0, 0, 0.5f);
}
bool CvCity::isStarCity() const
{
return isCapital();
}
bool CvCity::isValidBuildingLocation(BuildingTypes eBuilding) const
{
PROFILE_FUNC();
// if both the river and water flags are set, we require one of the two conditions, not both
if (GC.getBuildingInfo(eBuilding).isWater())
{
if (!GC.getBuildingInfo(eBuilding).isRiver() || !plot()->isRiver())
{
if (!isCoastal(GC.getBuildingInfo(eBuilding).getMinAreaSize()))
{
return false;
}
}
}
else
{
if (area()->getNumTiles() < GC.getBuildingInfo(eBuilding).getMinAreaSize())
{
return false;
}
if (GC.getBuildingInfo(eBuilding).isRiver())
{
if (!(plot()->isRiver()))
{
return false;
}
}
}
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
if (!isValidTerrainForBuildings(eBuilding))
{
return false;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (GC.getBuildingInfo(eBuilding).isFreshWater())
{
if (!(plot()->isFreshWater()))
{
return false;
}
}
return true;
}
bool CvCity::isEventTriggerPossible(EventTriggerTypes eTrigger) const
{
FAssert(eTrigger >= 0);
FAssert(eTrigger < GC.getNumEventTriggerInfos());
CvEventTriggerInfo& kTrigger = GC.getEventTriggerInfo(eTrigger);
if (!CvString(kTrigger.getPythonCanDoCity()).empty())
{
PYTHON_ACCESS_LOCK_SCOPE
long lResult;
CyArgsList argsList;
argsList.add(eTrigger);
argsList.add(getOwnerINLINE());
argsList.add(getID());
PYTHON_CALL_FUNCTION4(__FUNCTION__, PYRandomEventModule, kTrigger.getPythonCanDoCity(), argsList.makeFunctionArgs(), &lResult);
if (0 == lResult)
{
return false;
}
}
if (kTrigger.getNumBuildings() > 0 && kTrigger.getNumBuildingsRequired() > 0)
{
bool bFoundValid = false;
for (int i = 0; i < kTrigger.getNumBuildingsRequired(); ++i)
{
if (kTrigger.getBuildingRequired(i) != NO_BUILDINGCLASS)
{
BuildingTypes eBuilding = (BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(kTrigger.getBuildingRequired(i));
if (NO_BUILDING != eBuilding)
{
if (getNumRealBuilding(eBuilding) > 0)
{
bFoundValid = true;
}
}
}
}
if (!bFoundValid)
{
return false;
}
}
if (getReligionCount() < kTrigger.getNumReligions())
{
return false;
}
if (kTrigger.getNumReligions() > 0 && kTrigger.getNumReligionsRequired() > 0)
{
bool bFoundValid = false;
for (int i = 0; i < kTrigger.getNumReligionsRequired(); ++i)
{
if (!kTrigger.isStateReligion() || kTrigger.getReligionRequired(i) == GET_PLAYER(getOwnerINLINE()).getStateReligion())
{
if (isHasReligion((ReligionTypes)kTrigger.getReligionRequired(i)))
{
if (!kTrigger.isHolyCity() || isHolyCity((ReligionTypes)kTrigger.getReligionRequired(i)))
{
bFoundValid = true;
}
}
}
}
if (!bFoundValid)
{
return false;
}
}
if (getCorporationCount() < kTrigger.getNumCorporations())
{
return false;
}
if (kTrigger.getNumCorporations() > 0 && kTrigger.getNumCorporationsRequired() > 0)
{
bool bFoundValid = false;
for (int i = 0; i < kTrigger.getNumCorporationsRequired(); ++i)
{
if (isHasCorporation((CorporationTypes)kTrigger.getCorporationRequired(i)))
{
if (!kTrigger.isHeadquarters() || isHeadquarters((CorporationTypes)kTrigger.getCorporationRequired(i)))
{
bFoundValid = true;
}
}
}
if (!bFoundValid)
{
return false;
}
}
if (kTrigger.getMinPopulation() > 0)
{
if (getPopulation() < kTrigger.getMinPopulation())
{
return false;
}
}
if (kTrigger.getMaxPopulation() > 0)
{
if (getPopulation() > kTrigger.getMaxPopulation())
{
return false;
}
}
if (kTrigger.getAngry() > 0)
{
if (unhappyLevel(0) - happyLevel() < kTrigger.getAngry())
{
return false;
}
}
else if (kTrigger.getAngry() < 0)
{
if (happyLevel() - unhappyLevel(0) < -kTrigger.getAngry())
{
return false;
}
}
if (kTrigger.getUnhealthy() > 0)
{
if (badHealth(false, 0) - goodHealth() < kTrigger.getUnhealthy())
{
return false;
}
}
else if (kTrigger.getUnhealthy() < 0)
{
if (goodHealth() - badHealth(false, 0) < -kTrigger.getUnhealthy())
{
return false;
}
}
if (kTrigger.isPrereqEventCity() && kTrigger.getNumPrereqEvents() > 0)
{
bool bFoundValid = true;
for (int iI = 0; iI < kTrigger.getNumPrereqEvents(); ++iI)
{
if (!isEventOccured((EventTypes)kTrigger.getPrereqEvent(iI)))
{
bFoundValid = false;
break;
}
}
if (!bFoundValid)
{
return false;
}
}
if (!((*getPropertiesConst()) >= *kTrigger.getPrereqMinProperties()))
return false;
if (!((*getPropertiesConst()) <= *kTrigger.getPrereqMaxProperties()))
return false;
if (0 == getFood() && kTrigger.getCityFoodWeight() > 0)
{
return false;
}
return true;
}
int CvCity::getTriggerValue(EventTriggerTypes eTrigger) const
{
FAssert(eTrigger >= 0);
FAssert(eTrigger < GC.getNumEventTriggerInfos());
CvEventTriggerInfo& kTrigger = GC.getEventTriggerInfo(eTrigger);
if (!isEventTriggerPossible(eTrigger))
{
return MIN_INT;
}
int iValue = 0;
iValue += getFood() * kTrigger.getCityFoodWeight();
return iValue;
}
bool CvCity::canApplyEvent(EventTypes eEvent, const EventTriggeredData& kTriggeredData) const
{
CvEventInfo& kEvent = GC.getEventInfo(eEvent);
if (!kEvent.isCityEffect() && !kEvent.isOtherPlayerCityEffect())
{
return true;
}
if (-1 == kTriggeredData.m_iCityId && kEvent.isCityEffect())
{
return false;
}
if (-1 == kTriggeredData.m_iOtherPlayerCityId && kEvent.isOtherPlayerCityEffect())
{
return false;
}
if (kEvent.getFood() + ((100 + kEvent.getFoodPercent()) * getFood()) / 100 < 0)
{
return false;
}
if (kEvent.getPopulationChange() + getPopulation() <= 0)
{
return false;
}
if (100 * kEvent.getCulture() + getCultureTimes100(getOwnerINLINE()) < 0)
{
return false;
}
if (kEvent.getBuildingClass() != NO_BUILDINGCLASS)
{
BuildingTypes eBuilding = (BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(kEvent.getBuildingClass());
if (eBuilding == NO_BUILDING)
{
return false;
}
if (kEvent.getBuildingChange() > 0)
{
if (getNumBuilding(eBuilding) >= GC.getCITY_MAX_NUM_BUILDINGS())
{
return false;
}
}
else if (kEvent.getBuildingChange() < 0)
{
if (getNumRealBuilding(eBuilding) + kEvent.getBuildingChange() < 0)
{
return false;
}
}
}
if (-1 != kEvent.getMaxNumReligions() && getReligionCount() > kEvent.getMaxNumReligions())
{
return false;
}
if (kEvent.getMinPillage() > 0)
{
int iNumImprovements = 0;
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/17/09 */
/* */
/* */
/************************************************************************************************/
for (int i = 0; i < getNumCityPlots(); ++i)
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
{
if (CITY_HOME_PLOT != i)
{
CvPlot* pPlot = getCityIndexPlot(i);
if (NULL != pPlot && pPlot->getOwnerINLINE() == getOwnerINLINE())
{
if (NO_IMPROVEMENT != pPlot->getImprovementType() && !GC.getImprovementInfo(pPlot->getImprovementType()).isPermanent())
{
++iNumImprovements;
}
}
}
}
if (iNumImprovements < kEvent.getMinPillage())
{
return false;
}
}
return true;
}
void CvCity::applyEvent(EventTypes eEvent, const EventTriggeredData* pTriggeredData)
{
// NULL pTriggeredData implies a replay after a reset of modifiers and only modifier effects
// should be applied
bool adjustModifiersOnly = (pTriggeredData == NULL);
const EventTriggeredData& kTriggeredData = *pTriggeredData;
if ( !adjustModifiersOnly )
{
if (!canApplyEvent(eEvent, kTriggeredData))
{
return;
}
setEventOccured(eEvent, true);
}
CvEventInfo& kEvent = GC.getEventInfo(eEvent);
if (kEvent.isCityEffect() || kEvent.isOtherPlayerCityEffect())
{
if (kEvent.getHappy() != 0)
{
changeExtraHappiness(kEvent.getHappy());
}
if (kEvent.getHealth() != 0)
{
changeExtraHealth(kEvent.getHealth());
}
if (kEvent.getHurryAnger() != 0 && !adjustModifiersOnly)
{
changeHurryAngerTimer(kEvent.getHurryAnger() * flatHurryAngerLength());
}
if (kEvent.getHappyTurns() != 0 && !adjustModifiersOnly)
{
changeHappinessTimer(kEvent.getHappyTurns());
}
if (kEvent.getFood() != 0 || kEvent.getFoodPercent() != 0)
{
changeFood(kEvent.getFood() + (kEvent.getFoodPercent() * getFood()) / 100);
}
if (kEvent.getPopulationChange() != 0 && !adjustModifiersOnly)
{
changePopulation(kEvent.getPopulationChange());
}
if (kEvent.getRevoltTurns() > 0 && !adjustModifiersOnly)
{
changeCultureUpdateTimer(kEvent.getRevoltTurns());
changeOccupationTimer(kEvent.getRevoltTurns());
}
if (0 != kEvent.getSpaceProductionModifier())
{
changeSpaceProductionModifier(kEvent.getSpaceProductionModifier());
}
if (kEvent.getMaxPillage() > 0 && !adjustModifiersOnly)
{
FAssert(kEvent.getMaxPillage() >= kEvent.getMinPillage());
int iNumPillage = kEvent.getMinPillage() + getCitySorenRandNum(kEvent.getMaxPillage() - kEvent.getMinPillage(), "Pick number of event pillaged plots");
int iNumPillaged = 0;
for (int i = 0; i < iNumPillage; ++i)
{
int iRandOffset = getCitySorenRandNum(NUM_CITY_PLOTS, "Pick event pillage plot");
for (int j = 0; j < NUM_CITY_PLOTS; ++j)
{
int iPlot = (j + iRandOffset) % NUM_CITY_PLOTS;
if (CITY_HOME_PLOT != iPlot)
{
CvPlot* pPlot = getCityIndexPlot(iPlot);
if (NULL != pPlot && pPlot->getOwnerINLINE() == getOwnerINLINE())
{
if (NO_IMPROVEMENT != pPlot->getImprovementType() && !GC.getImprovementInfo(pPlot->getImprovementType()).isPermanent())
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_EVENT_CITY_IMPROVEMENT_DESTROYED", GC.getImprovementInfo(pPlot->getImprovementType()).getTextKeyWide()));
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_PILLAGED", MESSAGE_TYPE_INFO, GC.getImprovementInfo(pPlot->getImprovementType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true);
pPlot->setImprovementType(NO_IMPROVEMENT);
++iNumPillaged;
break;
}
}
}
}
}
PlayerTypes eOtherPlayer = kTriggeredData.m_eOtherPlayer;
if (!kEvent.isCityEffect() && kEvent.isOtherPlayerCityEffect())
{
eOtherPlayer = kTriggeredData.m_ePlayer;
}
if (NO_PLAYER != eOtherPlayer)
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_EVENT_NUM_CITY_IMPROVEMENTS_DESTROYED", iNumPillaged, GET_PLAYER(getOwnerINLINE()).getCivilizationAdjectiveKey()));
AddDLLMessage(eOtherPlayer, false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_PILLAGED", MESSAGE_TYPE_INFO);
}
}
for (int i = 0; i < NUM_COMMERCE_TYPES; ++i)
{
if (kEvent.getCommerceModifier(i) != 0)
{
changeCommerceRateModifier((CommerceTypes)i, kEvent.getCommerceModifier(i));
}
}
for (int i = 0; i < NUM_YIELD_TYPES; ++i)
{
if (kEvent.getYieldModifier(i) != 0)
{
changeYieldRateModifier((YieldTypes)i, kEvent.getYieldModifier(i));
}
}
for (int i = 0; i < GC.getNumSpecialistInfos(); ++i)
{
if (kEvent.getFreeSpecialistCount(i) > 0)
{
changeFreeSpecialistCount((SpecialistTypes)i, kEvent.getFreeSpecialistCount(i));
}
}
if (kEvent.getCulture() != 0 && !adjustModifiersOnly)
{
changeCulture(getOwnerINLINE(), kEvent.getCulture(), true, true);
}
/************************************************************************************************/
/* Afforess Start 01/20/10 */
/* */
/* */
/************************************************************************************************/
if (kEvent.getRevolutionIndexChange() > 0 && !adjustModifiersOnly)
{
changeLocalRevIndex(kEvent.getRevolutionIndexChange());
}
else if (kEvent.getRevolutionIndexChange() < 0 && !adjustModifiersOnly)
{
changeLocalRevIndex(std::max(-getLocalRevIndex(), kEvent.getRevolutionIndexChange()));
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
if (kEvent.getUnitClass() != NO_UNITCLASS && !adjustModifiersOnly)
{
UnitTypes eUnit = (UnitTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(kEvent.getUnitClass());
if (eUnit != NO_UNIT)
{
for (int i = 0; i < kEvent.getNumUnits(); ++i)
{
GET_PLAYER(getOwnerINLINE()).initUnit(eUnit, getX_INLINE(), getY_INLINE(), NO_UNITAI, NO_DIRECTION, getCitySorenRandNum(10000, "AI Unit Birthmark"));
}
}
}
if (kEvent.getBuildingClass() != NO_BUILDINGCLASS && !adjustModifiersOnly)
{
BuildingTypes eBuilding = (BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(kEvent.getBuildingClass());
if (eBuilding != NO_BUILDING)
{
if (0 != kEvent.getBuildingChange())
{
setNumRealBuilding(eBuilding, getNumRealBuilding(eBuilding) + kEvent.getBuildingChange());
}
}
}
if (kEvent.getNumBuildingYieldChanges() > 0)
{
for (int iBuildingClass = 0; iBuildingClass < GC.getNumBuildingClassInfos(); ++iBuildingClass)
{
for (int iYield = 0; iYield < NUM_YIELD_TYPES; ++iYield)
{
setBuildingYieldChange((BuildingClassTypes)iBuildingClass, (YieldTypes)iYield, getBuildingYieldChange((BuildingClassTypes)iBuildingClass, (YieldTypes)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)
{
setBuildingCommerceChange((BuildingClassTypes)iBuildingClass, (CommerceTypes)iCommerce, getBuildingCommerceChange((BuildingClassTypes)iBuildingClass, (CommerceTypes)iCommerce) + kEvent.getBuildingCommerceChange(iBuildingClass, iCommerce));
}
}
}
if (kEvent.getNumBuildingHappyChanges() > 0)
{
for (int iBuildingClass = 0; iBuildingClass < GC.getNumBuildingClassInfos(); ++iBuildingClass)
{
setBuildingHappyChange((BuildingClassTypes)iBuildingClass, kEvent.getBuildingHappyChange(iBuildingClass));
}
}
if (kEvent.getNumBuildingHealthChanges() > 0)
{
for (int iBuildingClass = 0; iBuildingClass < GC.getNumBuildingClassInfos(); ++iBuildingClass)
{
setBuildingHealthChange((BuildingClassTypes)iBuildingClass, kEvent.getBuildingHealthChange(iBuildingClass));
}
}
getProperties()->addProperties(kEvent.getProperties());
//GET_PLAYER(getOwnerINLINE()).getProperties()->addProperties(kEvent.getProperties());
}
bool CvCity::isEventOccured(EventTypes eEvent) const
{
for (std::vector<EventTypes>::const_iterator it = m_aEventsOccured.begin(); it != m_aEventsOccured.end(); ++it)
{
if (*it == eEvent)
{
return true;
}
}
return false;
}
void CvCity::setEventOccured(EventTypes eEvent, bool bOccured)
{
for (std::vector<EventTypes>::iterator it = m_aEventsOccured.begin(); it != m_aEventsOccured.end(); ++it)
{
if (*it == eEvent)
{
if (!bOccured)
{
m_aEventsOccured.erase(it);
}
return;
}
}
if (bOccured)
{
m_aEventsOccured.push_back(eEvent);
}
}
// CACHE: cache frequently used values
///////////////////////////////////////
bool CvCity::hasShrine(ReligionTypes eReligion)
{
bool bHasShrine = false;
// note, for normal XML, this count will be one, there is only one shrine of each religion
int shrineBuildingCount = GC.getGameINLINE().getShrineBuildingCount(eReligion);
for (int iI = 0; iI < shrineBuildingCount; iI++)
{
BuildingTypes eBuilding = GC.getGameINLINE().getShrineBuilding(iI, eReligion);
if (getNumBuilding(eBuilding) > 0)
{
bHasShrine = true;
break;
}
}
return bHasShrine;
}
void CvCity::invalidatePopulationRankCache()
{
m_bPopulationRankValid = false;
}
void CvCity::invalidateYieldRankCache(YieldTypes eYield)
{
FAssertMsg(eYield >= NO_YIELD && eYield < NUM_YIELD_TYPES, "invalidateYieldRankCache passed bogus yield index");
if (eYield == NO_YIELD)
{
for (int iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
m_abBaseYieldRankValid[iI] = false;
m_abYieldRankValid[iI] = false;
}
}
else
{
m_abBaseYieldRankValid[eYield] = false;
m_abYieldRankValid[eYield] = false;
}
}
void CvCity::invalidateCommerceRankCache(CommerceTypes eCommerce)
{
FAssertMsg(eCommerce >= NO_COMMERCE && eCommerce < NUM_COMMERCE_TYPES, "invalidateCommerceRankCache passed bogus commerce index");
if (eCommerce == NO_COMMERCE)
{
for (int iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
m_abCommerceRankValid[iI] = false;
}
}
else
{
m_abCommerceRankValid[eCommerce] = false;
}
}
int CvCity::getBuildingYieldChange(BuildingClassTypes eBuildingClass, YieldTypes eYield) const
{
for (std::vector<BuildingYieldChange>::const_iterator it = m_aBuildingYieldChange.begin(); it != m_aBuildingYieldChange.end(); ++it)
{
if ((*it).eBuildingClass == eBuildingClass && (*it).eYield == eYield)
{
return (*it).iChange;
}
}
return 0;
}
void CvCity::setBuildingYieldChange(BuildingClassTypes eBuildingClass, YieldTypes eYield, int iChange)
{
for (std::vector<BuildingYieldChange>::iterator it = m_aBuildingYieldChange.begin(); it != m_aBuildingYieldChange.end(); ++it)
{
if ((*it).eBuildingClass == eBuildingClass && (*it).eYield == eYield)
{
int iOldChange = (*it).iChange;
if (iOldChange != iChange)
{
if (iChange == 0)
{
m_aBuildingYieldChange.erase(it);
}
else
{
(*it).iChange = iChange;
}
BuildingTypes eBuilding = (BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(eBuildingClass);
if (NO_BUILDING != eBuilding)
{
if (getNumActiveBuilding(eBuilding) > 0)
{
changeBaseYieldRate(eYield, (iChange - iOldChange) * getNumActiveBuilding(eBuilding));
}
}
}
return;
}
}
if (0 != iChange)
{
BuildingYieldChange kChange;
kChange.eBuildingClass = eBuildingClass;
kChange.eYield = eYield;
kChange.iChange = iChange;
m_aBuildingYieldChange.push_back(kChange);
BuildingTypes eBuilding = (BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(eBuildingClass);
if (NO_BUILDING != eBuilding)
{
if (getNumActiveBuilding(eBuilding) > 0)
{
changeBaseYieldRate(eYield, iChange * getNumActiveBuilding(eBuilding));
}
}
}
}
void CvCity::changeBuildingYieldChange(BuildingClassTypes eBuildingClass, YieldTypes eYield, int iChange)
{
setBuildingYieldChange(eBuildingClass, eYield, getBuildingYieldChange(eBuildingClass, eYield) + iChange);
}
int CvCity::getBuildingCommerceChange(BuildingClassTypes eBuildingClass, CommerceTypes eCommerce) const
{
for (std::vector<BuildingCommerceChange>::const_iterator it = m_aBuildingCommerceChange.begin(); it != m_aBuildingCommerceChange.end(); ++it)
{
if ((*it).eBuildingClass == eBuildingClass && (*it).eCommerce == eCommerce)
{
return (*it).iChange;
}
}
return 0;
}
void CvCity::setBuildingCommerceChange(BuildingClassTypes eBuildingClass, CommerceTypes eCommerce, int iChange)
{
for (std::vector<BuildingCommerceChange>::iterator it = m_aBuildingCommerceChange.begin(); it != m_aBuildingCommerceChange.end(); ++it)
{
if ((*it).eBuildingClass == eBuildingClass && (*it).eCommerce == eCommerce)
{
if ((*it).iChange != iChange)
{
if (iChange == 0)
{
m_aBuildingCommerceChange.erase(it);
}
else
{
(*it).iChange = iChange;
}
updateBuildingCommerce();
}
return;
}
}
if (0 != iChange)
{
BuildingCommerceChange kChange;
kChange.eBuildingClass = eBuildingClass;
kChange.eCommerce = eCommerce;
kChange.iChange = iChange;
m_aBuildingCommerceChange.push_back(kChange);
updateBuildingCommerce();
}
}
void CvCity::changeBuildingCommerceChange(BuildingClassTypes eBuildingClass, CommerceTypes eCommerce, int iChange)
{
EnterCriticalSection(&m_cBuildingCommerceChangeSection);
setBuildingCommerceChange(eBuildingClass, eCommerce, getBuildingCommerceChange(eBuildingClass, eCommerce) + iChange);
LeaveCriticalSection(&m_cBuildingCommerceChangeSection);
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 10/22/09 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* orginal bts code
void CvCity::setBuildingHappyChange(BuildingClassTypes eBuildingClass, int iChange)
{
for (BuildingChangeArray::iterator it = m_aBuildingHappyChange.begin(); it != m_aBuildingHappyChange.end(); ++it)
{
if ((*it).first == eBuildingClass)
{
if ((*it).second != iChange)
{
if ((*it).second > 0)
{
changeBuildingGoodHappiness(-(*it).second);
}
else if ((*it).second < 0)
{
changeBuildingBadHappiness((*it).second);
}
if (iChange == 0)
{
m_aBuildingHappyChange.erase(it);
}
else
{
(*it).second = iChange;
}
if (iChange > 0)
{
changeBuildingGoodHappiness(iChange);
}
else if (iChange < 0)
{
changeBuildingGoodHappiness(-iChange);
}
}
return;
}
}
if (0 != iChange)
{
m_aBuildingHappyChange.push_back(std::make_pair(eBuildingClass, iChange));
if (iChange > 0)
{
changeBuildingGoodHappiness(iChange);
}
else if (iChange < 0)
{
changeBuildingGoodHappiness(-iChange);
}
}
}
*/
void CvCity::setBuildingHappyChange(BuildingClassTypes eBuildingClass, int iChange)
{
for (BuildingChangeArray::iterator it = m_aBuildingHappyChange.begin(); it != m_aBuildingHappyChange.end(); ++it)
{
if ((*it).first == eBuildingClass)
{
if ((*it).second != iChange)
{
int iOldChange = (*it).second;
m_aBuildingHappyChange.erase(it);
BuildingTypes eBuilding = (BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(eBuildingClass);
if (NO_BUILDING != eBuilding)
{
if (getNumActiveBuilding(eBuilding) > 0)
{
if (iOldChange > 0)
{
changeBuildingGoodHappiness(-iOldChange);
}
else if (iOldChange < 0)
{
changeBuildingBadHappiness(-iOldChange);
}
if( iChange != 0 )
{
m_aBuildingHappyChange.push_back(std::make_pair(eBuildingClass, iChange));
if (iChange > 0)
{
changeBuildingGoodHappiness(iChange);
}
else if (iChange < 0)
{
changeBuildingBadHappiness(iChange);
}
}
}
}
}
return;
}
}
if (0 != iChange)
{
BuildingTypes eBuilding = (BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(eBuildingClass);
if (NO_BUILDING != eBuilding)
{
if (getNumActiveBuilding(eBuilding) > 0)
{
m_aBuildingHappyChange.push_back(std::make_pair(eBuildingClass, iChange));
if (iChange > 0)
{
changeBuildingGoodHappiness(iChange);
}
else if (iChange < 0)
{
changeBuildingBadHappiness(iChange);
}
}
}
}
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
int CvCity::getBuildingHappyChange(BuildingClassTypes eBuildingClass) const
{
for (BuildingChangeArray::const_iterator it = m_aBuildingHappyChange.begin(); it != m_aBuildingHappyChange.end(); ++it)
{
if ((*it).first == eBuildingClass)
{
return (*it).second;
}
}
return 0;
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH 10/22/09 jdog5000 */
/* */
/* Bugfix */
/************************************************************************************************/
/* orginal bts code
void CvCity::setBuildingHealthChange(BuildingClassTypes eBuildingClass, int iChange)
{
for (BuildingChangeArray::iterator it = m_aBuildingHealthChange.begin(); it != m_aBuildingHealthChange.end(); ++it)
{
if ((*it).first == eBuildingClass)
{
if ((*it).second != iChange)
{
if ((*it).second > 0)
{
changeBuildingGoodHealth(-(*it).second);
}
else if ((*it).second < 0)
{
changeBuildingBadHealth((*it).second);
}
if (iChange == 0)
{
m_aBuildingHealthChange.erase(it);
}
else
{
(*it).second = iChange;
}
if (iChange > 0)
{
changeBuildingGoodHealth(iChange);
}
else if (iChange < 0)
{
changeBuildingBadHealth(-iChange);
}
}
return;
}
}
if (0 != iChange)
{
m_aBuildingHealthChange.push_back(std::make_pair(eBuildingClass, iChange));
if (iChange > 0)
{
changeBuildingGoodHappiness(iChange);
}
else if (iChange < 0)
{
changeBuildingGoodHappiness(-iChange);
}
}
}
*/
void CvCity::setBuildingHealthChange(BuildingClassTypes eBuildingClass, int iChange)
{
for (BuildingChangeArray::iterator it = m_aBuildingHealthChange.begin(); it != m_aBuildingHealthChange.end(); ++it)
{
if ((*it).first == eBuildingClass)
{
if ((*it).second != iChange)
{
int iOldChange = (*it).second;
m_aBuildingHealthChange.erase(it);
BuildingTypes eBuilding = (BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(eBuildingClass);
if (NO_BUILDING != eBuilding)
{
if (getNumActiveBuilding(eBuilding) > 0)
{
if (iOldChange > 0)
{
changeBuildingGoodHealth(-iOldChange);
}
else if (iOldChange < 0)
{
changeBuildingBadHealth(-iOldChange);
}
if( iChange != 0 )
{
m_aBuildingHealthChange.push_back(std::make_pair(eBuildingClass, iChange));
if (iChange > 0)
{
changeBuildingGoodHealth(iChange);
}
else if (iChange < 0)
{
changeBuildingBadHealth(iChange);
}
}
}
}
}
return;
}
}
if (0 != iChange)
{
BuildingTypes eBuilding = (BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(eBuildingClass);
if (NO_BUILDING != eBuilding)
{
if (getNumActiveBuilding(eBuilding) > 0)
{
m_aBuildingHealthChange.push_back(std::make_pair(eBuildingClass, iChange));
if (iChange > 0)
{
changeBuildingGoodHealth(iChange);
}
else if (iChange < 0)
{
changeBuildingBadHealth(iChange);
}
}
}
}
}
/************************************************************************************************/
/* UNOFFICIAL_PATCH END */
/************************************************************************************************/
int CvCity::getBuildingHealthChange(BuildingClassTypes eBuildingClass) const
{
for (BuildingChangeArray::const_iterator it = m_aBuildingHealthChange.begin(); it != m_aBuildingHealthChange.end(); ++it)
{
if ((*it).first == eBuildingClass)
{
return (*it).second;
}
}
return 0;
}
void CvCity::liberate(bool bConquest)
{
CvPlot* pPlot = plot();
PlayerTypes ePlayer = getLiberationPlayer(bConquest);
PlayerTypes eOwner = getOwnerINLINE();
if (NO_PLAYER != ePlayer)
{
int iOldOwnerCulture = getCultureTimes100(eOwner);
int iOldMasterLand = 0;
int iOldVassalLand = 0;
if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isVassal(GET_PLAYER(eOwner).getTeam()))
{
iOldMasterLand = GET_TEAM(GET_PLAYER(eOwner).getTeam()).getTotalLand();
iOldVassalLand = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getTotalLand(false);
}
CvWString szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_CITY_LIBERATED", getNameKey(), GET_PLAYER(eOwner).getNameKey(), GET_PLAYER(ePlayer).getCivilizationAdjectiveKey()));
for (int iI = 0; iI < MAX_PLAYERS; ++iI)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (isRevealed(GET_PLAYER((PlayerTypes)iI).getTeam(), false))
{
MEMORY_TRACK_EXEMPT();
AddDLLMessage(((PlayerTypes)iI), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_REVOLTEND", MESSAGE_TYPE_MAJOR_EVENT, ARTFILEMGR.getInterfaceArtInfo("WORLDBUILDER_CITY_EDIT")->getPath(), (ColorTypes)GC.getInfoTypeForString("COLOR_HIGHLIGHT_TEXT"), getX_INLINE(), getY_INLINE(), true, true);
}
}
}
GC.getGameINLINE().addReplayMessage(REPLAY_MESSAGE_MAJOR_EVENT, eOwner, szBuffer, getX_INLINE(), getY_INLINE(), (ColorTypes)GC.getInfoTypeForString("COLOR_HIGHLIGHT_TEXT"));
GET_PLAYER(ePlayer).acquireCity(this, false, true, true);
GET_PLAYER(ePlayer).AI_changeMemoryCount(eOwner, MEMORY_LIBERATED_CITIES, 1);
if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isVassal(GET_PLAYER(eOwner).getTeam()))
{
int iNewMasterLand = GET_TEAM(GET_PLAYER(eOwner).getTeam()).getTotalLand();
int iNewVassalLand = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getTotalLand(false);
GET_TEAM(GET_PLAYER(ePlayer).getTeam()).setMasterPower(GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getMasterPower() + iNewMasterLand - iOldMasterLand);
GET_TEAM(GET_PLAYER(ePlayer).getTeam()).setVassalPower(GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getVassalPower() + iNewVassalLand - iOldVassalLand);
}
if (NULL != pPlot)
{
CvCity* pCity = pPlot->getPlotCity();
if (NULL != pCity)
{
pCity->setCultureTimes100(ePlayer, pCity->getCultureTimes100(ePlayer) + iOldOwnerCulture / 2, true, true);
}
if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isAVassal())
{
for (int i = 0; i < GC.getDefineINT("COLONY_NUM_FREE_DEFENDERS"); ++i)
{
pCity->initConscriptedUnit();
}
}
}
}
}
PlayerTypes CvCity::getLiberationPlayer(bool bConquest) const
{
if (isCapital())
{
return NO_PLAYER;
}
for (int iPlayer = 0; iPlayer < MAX_CIV_PLAYERS; ++iPlayer)
{
CvPlayer& kLoopPlayer = GET_PLAYER((PlayerTypes)iPlayer);
if (kLoopPlayer.isAlive() && kLoopPlayer.getParent() == getOwnerINLINE())
{
CvCity* pLoopCapital = kLoopPlayer.getCapitalCity();
if (NULL != pLoopCapital)
{
if (pLoopCapital->area() == area())
{
return (PlayerTypes)iPlayer;
}
}
}
}
CvPlayer& kOwner = GET_PLAYER(getOwnerINLINE());
if (kOwner.canSplitEmpire() && kOwner.canSplitArea(area()->getID()))
{
PlayerTypes ePlayer = GET_PLAYER(getOwnerINLINE()).getSplitEmpirePlayer(area()->getID());
if (NO_PLAYER != ePlayer)
{
if (GET_PLAYER(ePlayer).isAlive())
{
return ePlayer;
}
}
}
PlayerTypes eBestPlayer = NO_PLAYER;
int iBestValue = 0;
int iTotalCultureTimes100 = countTotalCultureTimes100();
for (int iPlayer = 0; iPlayer < MAX_CIV_PLAYERS; ++iPlayer)
{
CvPlayer& kLoopPlayer = GET_PLAYER((PlayerTypes)iPlayer);
if (kLoopPlayer.isAlive())
{
if (kLoopPlayer.canReceiveTradeCity())
{
CvCity* pCapital = kLoopPlayer.getCapitalCity();
if (NULL != pCapital)
{
int iCapitalDistance = ::plotDistance(getX_INLINE(), getY_INLINE(), pCapital->getX_INLINE(), pCapital->getY_INLINE());
if (area() != pCapital->area())
{
iCapitalDistance *= 2;
}
int iCultureTimes100 = getCultureTimes100((PlayerTypes)iPlayer);
if (bConquest)
{
if (iPlayer == getOriginalOwner())
{
iCultureTimes100 *= 3;
iCultureTimes100 /= 2;
}
}
if (GET_PLAYER((PlayerTypes)iPlayer).getTeam() == getTeam()
|| GET_TEAM(GET_PLAYER((PlayerTypes)iPlayer).getTeam()).isVassal(getTeam())
|| GET_TEAM(getTeam()).isVassal(GET_PLAYER((PlayerTypes)iPlayer).getTeam()))
{
iCultureTimes100 *= 2;
iCultureTimes100 = (iCultureTimes100 + iTotalCultureTimes100) / 2;
}
int iValue = std::max(100, iCultureTimes100) / std::max(1, iCapitalDistance);
if (iValue > iBestValue)
{
iBestValue = iValue;
eBestPlayer = (PlayerTypes)iPlayer;
}
}
}
}
}
if (NO_PLAYER != eBestPlayer)
{
if (getOwnerINLINE() == eBestPlayer)
{
return NO_PLAYER;
}
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/17/09 */
/* */
/* */
/************************************************************************************************/
for (int iPlot = 0; iPlot < getNumCityPlots(); ++iPlot)
/************************************************************************************************/
/* JOOYO_ADDON END */
/************************************************************************************************/
{
CvPlot* pLoopPlot = ::plotCity(getX_INLINE(), getY_INLINE(), iPlot);
if (NULL != pLoopPlot)
{
if (pLoopPlot->isVisibleEnemyUnit(eBestPlayer))
{
return NO_PLAYER;
}
}
}
}
return eBestPlayer;
}
int CvCity::getBestYieldAvailable(YieldTypes eYield) const
{
int iBestYieldAvailable = 0;
for (int iJ = 0; iJ < NUM_CITY_PLOTS; ++iJ)
{
if (iJ != CITY_HOME_PLOT)
{
if (!isWorkingPlot(iJ))
{
CvPlot* pPlot = getCityIndexPlot(iJ);
if (NULL != pPlot && canWork(pPlot))
{
if (pPlot->getYield(eYield) > iBestYieldAvailable)
{
iBestYieldAvailable = pPlot->getYield(eYield);
}
}
}
}
}
for (int iJ = 0; iJ < GC.getNumSpecialistInfos(); ++iJ)
{
if (isSpecialistValid((SpecialistTypes)iJ, 1))
{
int iYield = GC.getSpecialistInfo((SpecialistTypes)iJ).getYieldChange(eYield);
if (iYield > iBestYieldAvailable)
{
iBestYieldAvailable = iYield;
}
}
}
return iBestYieldAvailable;
}
bool CvCity::isAutoRaze() const
{
if (!GC.getGameINLINE().isOption(GAMEOPTION_NO_CITY_RAZING))
{
if (getHighestPopulation() == 1)
{
return true;
}
if (GC.getGameINLINE().getMaxCityElimination() > 0)
{
return true;
}
}
if (GC.getGameINLINE().isOption(GAMEOPTION_ONE_CITY_CHALLENGE) && isHuman())
{
return true;
}
/************************************************************************************************/
/* Afforess Start 06/26/10 */
/* */
/* */
/************************************************************************************************/
if (GC.getGameINLINE().isOption(GAMEOPTION_BARBARIANS_ALWAYS_RAZE) && isBarbarian())
{
return true;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
return false;
}
int CvCity::getMusicScriptId() const
{
bool bIsHappy = true;
if (GC.getGameINLINE().getActiveTeam() == getTeam())
{
if (angryPopulation() > 0)
{
bIsHappy = false;
}
}
else
{
if (GET_TEAM(GC.getGameINLINE().getActiveTeam()).isAtWar(getTeam()))
{
bIsHappy = false;
}
}
CvLeaderHeadInfo& kLeaderInfo = GC.getLeaderHeadInfo(GET_PLAYER(getOwnerINLINE()).getLeaderType());
EraTypes eCurEra = GET_PLAYER(getOwnerINLINE()).getCurrentEra();
if (bIsHappy)
{
return (kLeaderInfo.getDiploPeaceMusicScriptIds(eCurEra));
}
else
{
return (kLeaderInfo.getDiploWarMusicScriptIds(eCurEra));
}
}
int CvCity::getSoundscapeScriptId() const
{
return GC.getEraInfo(GET_PLAYER(getOwnerINLINE()).getCurrentEra()).getCitySoundscapeSciptId(getCitySizeType());
}
void CvCity::cheat(bool bCtrl, bool bAlt, bool bShift)
{
if (gDLL->getChtLvl() > 0)
{
if (bCtrl)
{
changeCulture(getOwnerINLINE(), 10, true, true);
}
else if (bShift)
{
changePopulation(1);
}
else
{
popOrder(0, true);
}
}
}
void CvCity::getBuildQueue(std::vector<std::string>& astrQueue) const
{
CLLNode<OrderData>* pNode = headOrderQueueNode();
while (pNode != NULL)
{
switch (pNode->m_data.eOrderType)
{
case ORDER_TRAIN:
astrQueue.push_back(GC.getUnitInfo((UnitTypes)EXTERNAL_ORDER_IDATA(pNode->m_data.iData1)).getType());
break;
case ORDER_CONSTRUCT:
astrQueue.push_back(GC.getBuildingInfo((BuildingTypes)(pNode->m_data.iData1)).getType());
break;
case ORDER_CREATE:
astrQueue.push_back(GC.getProjectInfo((ProjectTypes)(pNode->m_data.iData1)).getType());
break;
case ORDER_MAINTAIN:
astrQueue.push_back(GC.getProcessInfo((ProcessTypes)(pNode->m_data.iData1)).getType());
break;
case ORDER_LIST:
astrQueue.push_back("List");
default:
FAssert(false);
break;
}
pNode = nextOrderQueueNode(pNode);
}
}
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma Start */
/************************************************************************************************/
// ------ BEGIN InfluenceDrivenWar -------------------------------
void CvCity::emergencyConscript()
{
CvUnit* pUnit;
UnitAITypes eCityAI;
UnitTypes eConscriptUnit;
int iEmergencyDraftMinPopulation = GC.getIDW_EMERGENCY_DRAFT_MIN_POPULATION();
if (getPopulation() < iEmergencyDraftMinPopulation)
{
return;
}
if (getConscriptUnit() == NO_UNIT)
{
return;
}
changePopulation(-1);
float fEmergencyDraftAngerMultiplier = GC.getIDW_EMERGENCY_DRAFT_ANGER_MULTIPLIER();
changeConscriptAngerTimer(int(flatConscriptAngerLength() * fEmergencyDraftAngerMultiplier));
eConscriptUnit = getConscriptUnit();
if (GET_PLAYER(getOwnerINLINE()).AI_unitValue(eConscriptUnit, UNITAI_CITY_DEFENSE, area()) > 0)
{
eCityAI = UNITAI_CITY_DEFENSE;
}
else if (GET_PLAYER(getOwnerINLINE()).AI_unitValue(eConscriptUnit, UNITAI_CITY_COUNTER, area()) > 0)
{
eCityAI = UNITAI_CITY_COUNTER;
}
else if (GET_PLAYER(getOwnerINLINE()).AI_unitValue(eConscriptUnit, UNITAI_CITY_SPECIAL, area()) > 0)
{
eCityAI = UNITAI_CITY_SPECIAL;
}
else
{
eCityAI = NO_UNITAI;
}
pUnit = GET_PLAYER(getOwnerINLINE()).initUnit(eConscriptUnit, getX_INLINE(), getY_INLINE(), eCityAI, NO_DIRECTION, getCitySorenRandNum(10000, "AI Unit Birthmark"));
FAssertMsg(pUnit != NULL, "pUnit expected to be assigned (not NULL)");
addProductionExperience(pUnit, true);
pUnit->setMoves(0);
float fEmergencyDraftStrength = GC.getIDW_EMERGENCY_DRAFT_STRENGTH();
pUnit->setDamage(int((1 - fEmergencyDraftStrength) * pUnit->maxHitPoints()), getOwnerINLINE());
}
// ------ END InfluenceDrivenWar ---------------------------------
/************************************************************************************************/
/* INFLUENCE_DRIVEN_WAR 04/16/09 johnysmith */
/* */
/* Original Author Moctezuma End */
/************************************************************************************************/
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* New Functions */
/************************************************************************************************/
int CvCity::getRevTrend()
{
if (GC.getGameINLINE().isOption(GAMEOPTION_NO_REVOLUTION))
return 0;
//This is the value from python
int iRevInsigatorThreshold = 1000;
int iRevIndex = std::min(getRevolutionIndex(), iRevInsigatorThreshold);
int iDeltaTrend = iRevIndex - getRevIndexAverage();
if (iDeltaTrend != 0)
iDeltaTrend *= std::max(abs(iDeltaTrend), (iRevInsigatorThreshold / 100) + 1);
return iDeltaTrend;
}
bool CvCity::isInquisitionConditions()
{
int iI;
bool bValid = false;
ReligionTypes eStateReligion = GET_PLAYER(getOwnerINLINE()).getStateReligion();
if (eStateReligion == NO_RELIGION)
return false;
if (isHasReligion(eStateReligion))
{
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if ( isHasReligion(ReligionTypes(iI))
&& (ReligionTypes(iI) != eStateReligion) )
{
if ( !(isHolyCity(ReligionTypes(iI)))
|| (isHolyCity(ReligionTypes(iI)) && GC.isOC_RESPAWN_HOLY_CITIES()) )
{
bValid = true;
break;
}
}
}
if (bValid)
return true;
}
return false;
}
/*
Checks the cities culture level and if it meets the criteria specified in the CultureLevelsInfo.xml, the city radius expands.
NUM_CITY_PLOTS is the largest city size, a radius of 3, NUM_CITY_PLOTS_2 is the standard BTS city size ( a radius of 2), and NUM_CITY_PLOTS_3
is a city size of 1.
*/
int CvCity::getNumCityPlots() const
{
if (getWorkableRadiusOverride() == 0)
{
if (!GC.getGameINLINE().isOption(GAMEOPTION_LARGER_CITIES))
{
return NUM_CITY_PLOTS_2;
}
}
int iRadius;
int var_city_plots;
if (getCultureLevel() == -1)
{
return NUM_CITY_PLOTS_1;
}
iRadius = GC.getCultureLevelInfo(getCultureLevel()).getCityRadius();
if (getWorkableRadiusOverride() > 0)
{
iRadius = getWorkableRadiusOverride();
}
switch (iRadius)
{
case 3:
var_city_plots = NUM_CITY_PLOTS;
break;
case 2:
var_city_plots = NUM_CITY_PLOTS_2;
break;
case 1:
var_city_plots = NUM_CITY_PLOTS_1;
break;
default:
var_city_plots = NUM_CITY_PLOTS_2;
break;
}
return (var_city_plots);
}
/*
updateYieldRate(...) checks to see if the building given is in the city, and if it is, updates the yield by the iChange amount.
*/
void CvCity::updateYieldRate(BuildingTypes eBuilding, YieldTypes eYield, int iChange)
{
BuildingClassTypes eBuildingClass = (BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType();
setBuildingYieldChange(eBuildingClass, eYield, iChange);
}
/*
Given a building type, specialist type and the amount of specialists to change, updateMaxSpecialistCount changes the specialist count by the iChange value.
*/
void CvCity::updateMaxSpecialistCount(BuildingTypes eBuilding, SpecialistTypes eSpecialist, int iChange)
{
if (getNumActiveBuilding(eBuilding) > 0)
{
changeMaxSpecialistCount(eSpecialist, iChange);
}
}
int CvCity::getBuildingCommerceModifier(BuildingClassTypes eBuildingClass, CommerceTypes eCommerce) const
{
for (std::vector<BuildingCommerceModifier>::const_iterator it = m_aBuildingCommerceModifier.begin(); it != m_aBuildingCommerceModifier.end(); ++it)
{
if ((*it).eBuildingClass == eBuildingClass && (*it).eCommerce == eCommerce)
{
return (*it).iChange;
}
}
return 0;
}
void CvCity::updateCommerceModifierByBuilding(BuildingTypes eBuilding, CommerceTypes eCommerce, int iChange)
{
BuildingClassTypes eBuildingClass = (BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType();
for (std::vector<BuildingCommerceModifier>::iterator it = m_aBuildingCommerceModifier.begin(); it != m_aBuildingCommerceModifier.end(); ++it)
{
if ((*it).eBuildingClass == eBuildingClass && (*it).eCommerce == eCommerce)
{
if ((*it).iChange != iChange)
{
if (iChange == 0)
{
m_aBuildingCommerceModifier.erase(it);
}
else
{
(*it).iChange = iChange;
}
setCommerceModifierDirty(eCommerce);
//updateBuildingCommerce();
}
return;
}
}
if (0 != iChange)
{
BuildingCommerceModifier kChange;
kChange.eBuildingClass = eBuildingClass;
kChange.eCommerce = eCommerce;
kChange.iChange = iChange;
m_aBuildingCommerceModifier.push_back(kChange);
setCommerceModifierDirty(eCommerce);
//updateBuildingCommerce();
GET_PLAYER(getOwnerINLINE()).invalidateCommerceRankCache();
}
}
/*
*/
void CvCity::updateCommerceRateByBuilding(BuildingTypes eBuilding, CommerceTypes eCommerce, int iChange)
{
BuildingClassTypes eBuildingClass = (BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType();
for (std::vector<BuildingCommerceChange>::iterator it = m_aBuildingCommerceChange.begin(); it != m_aBuildingCommerceChange.end(); ++it)
{
if ((*it).eBuildingClass == eBuildingClass && (*it).eCommerce == eCommerce)
{
if ((*it).iChange != iChange)
{
if (iChange == 0)
{
m_aBuildingCommerceChange.erase(it);
}
else
{
(*it).iChange = iChange;
}
updateBuildingCommerce();
}
return;
}
}
if (0 != iChange)
{
BuildingCommerceChange kChange;
kChange.eBuildingClass = eBuildingClass;
kChange.eCommerce = eCommerce;
kChange.iChange = iChange;
m_aBuildingCommerceChange.push_back(kChange);
updateBuildingCommerce();
}
}
int CvCity::calculateBuildingCommerceModifier(CommerceTypes eCommerce) const
{
int iI;
int iTotalModifier = 0;
BuildingTypes eLoopBuilding;
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
eLoopBuilding = ((BuildingTypes)(GC.getCivilizationInfo(GET_PLAYER(getOwnerINLINE()).getCivilizationType()).getCivilizationBuildings(iI)));
if (eLoopBuilding != NO_BUILDING)
{
if (getNumActiveBuilding(eLoopBuilding))
{
iTotalModifier += getBuildingCommerceModifier((BuildingClassTypes)iI, eCommerce);
}
}
}
return iTotalModifier;
}
int CvCity::calculateBuildingYieldModifier(YieldTypes eYield) const
{
PROFILE_FUNC();
if ( m_cachedBuildingYieldModifers[eYield] == -1 )
{
int iI;
int iTotalModifier = 0;
BuildingTypes eLoopBuilding;
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
eLoopBuilding = ((BuildingTypes)(GC.getCivilizationInfo(GET_PLAYER(getOwnerINLINE()).getCivilizationType()).getCivilizationBuildings(iI)));
if (eLoopBuilding != NO_BUILDING)
{
if (getNumActiveBuilding(eLoopBuilding))
{
iTotalModifier += getBuildingYieldModifier((BuildingClassTypes)iI, eYield);
}
}
}
m_cachedBuildingYieldModifers[eYield] = iTotalModifier;
}
return m_cachedBuildingYieldModifers[eYield];
}
int CvCity::getBuildingYieldModifier(BuildingClassTypes eBuildingClass, YieldTypes eYield) const
{
PROFILE_FUNC();
for (std::vector<BuildingYieldModifier>::const_iterator it = m_aBuildingYieldModifier.begin(); it != m_aBuildingYieldModifier.end(); ++it)
{
if ((*it).eBuildingClass == eBuildingClass && (*it).eYield == eYield)
{
return (*it).iChange;
}
}
return 0;
}
void CvCity::updateYieldModifierByBuilding(BuildingTypes eBuilding, YieldTypes eYield, int iChange)
{
BuildingClassTypes eBuildingClass = (BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType();
for (std::vector<BuildingYieldModifier>::iterator it = m_aBuildingYieldModifier.begin(); it != m_aBuildingYieldModifier.end(); ++it)
{
if ((*it).eBuildingClass == eBuildingClass && (*it).eYield == eYield)
{
int iOldChange = (*it).iChange;
if (iOldChange != iChange)
{
// Clear cached yield modifier
m_cachedBuildingYieldModifers[eYield] = -1;
if (iChange == 0)
{
m_aBuildingYieldModifier.erase(it);
}
else
{
(*it).iChange = iChange;
}
if (NO_BUILDING != eBuilding)
{
if (getNumActiveBuilding(eBuilding) > 0)
{
changeBaseYieldRate(eYield, (iChange - iOldChange) * getNumActiveBuilding(eBuilding));
}
}
}
return;
}
}
if (0 != iChange)
{
BuildingYieldModifier kChange;
kChange.eBuildingClass = eBuildingClass;
kChange.eYield = eYield;
kChange.iChange = iChange;
m_aBuildingYieldModifier.push_back(kChange);
if (NO_BUILDING != eBuilding)
{
if (getNumActiveBuilding(eBuilding) > 0)
{
changeYieldRateModifier(eYield, iChange * getNumActiveBuilding(eBuilding));
}
}
GET_PLAYER(getOwnerINLINE()).invalidateYieldRankCache();
}
}
/*
Checks to see if the city is producing a wonder
*/
bool CvCity::isProductionWonder() const
{
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
if (pOrderNode->m_data.eOrderType == ORDER_CONSTRUCT)
{
BuildingTypes eBuilding = (BuildingTypes)(pOrderNode->m_data.iData1);
BuildingClassTypes eBuildingClass = (BuildingClassTypes)(GC.getBuildingInfo(eBuilding).getBuildingClassType());
return (isWorldWonderClass(eBuildingClass) || isTeamWonderClass(eBuildingClass) || isNationalWonderClass(eBuildingClass));
}
}
return false;
}
void CvCity::clearLostProduction()
{
m_iLostProductionBase = 0;
m_iLostProductionModified = 0;
m_iGoldFromLostProduction = 0;
}
int CvCity::getSpecialistGoodHealth() const
{
return m_iSpecialistGoodHealth;
}
int CvCity::getSpecialistBadHealth() const
{
return m_iSpecialistBadHealth;
}
int CvCity::getSpecialistHappiness() const
{
return m_iSpecialistHappiness;
}
int CvCity::getSpecialistUnhappiness() const
{
return m_iSpecialistUnhappiness;
}
void CvCity::changeSpecialistGoodHealth(int iChange)
{
if (iChange != 0)
{
m_iSpecialistGoodHealth += iChange;
FAssert(getSpecialistGoodHealth() >= 0);
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
void CvCity::changeSpecialistBadHealth(int iChange)
{
if (iChange != 0)
{
m_iSpecialistBadHealth += iChange;
FAssert(getSpecialistBadHealth() <= 0);
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
void CvCity::changeSpecialistHappiness(int iChange)
{
if (iChange != 0)
{
m_iSpecialistHappiness += iChange;
FAssert(getSpecialistHappiness() >= 0);
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
void CvCity::changeSpecialistUnhappiness(int iChange)
{
if (iChange != 0)
{
m_iSpecialistUnhappiness += iChange;
FAssert(getSpecialistUnhappiness() >= 0);
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
int CvCity::getImprovementGoodHealth() const
{
return m_iImprovementGoodHealth;
}
int CvCity::getImprovementBadHealth() const
{
return m_iImprovementBadHealth;
}
void CvCity::updateImprovementHealth()
{
CvPlayer &kPlayer = GET_PLAYER(getOwnerINLINE());
CvPlot* pLoopPlot;
ImprovementTypes eImprovement;
int iNewGoodHealthPercent;
int iNewBadHealthPercent;
int iI;
iNewGoodHealthPercent = 0;
iNewBadHealthPercent = 0;
for (iI = 0; iI < getNumCityPlots(); iI++)
{
pLoopPlot = getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getOwner() != NO_PLAYER)
{
if (pLoopPlot->getOwner() == getOwnerINLINE())
{
eImprovement = pLoopPlot->getImprovementType();
if (eImprovement != NO_IMPROVEMENT)
{
if (GC.getImprovementInfo(eImprovement).getHealthPercent() > 0)
{
iNewGoodHealthPercent += GC.getImprovementInfo(eImprovement).getHealthPercent();
for (int iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
if (kPlayer.getCivics((CivicOptionTypes)iI) != NO_CIVIC)
{
iNewGoodHealthPercent += std::max(0, GC.getCivicInfo(kPlayer.getCivics((CivicOptionTypes)iI)).getImprovementHealthPercentChanges(eImprovement));
}
}
}
else
{
iNewBadHealthPercent += GC.getImprovementInfo(eImprovement).getHealthPercent();
for (int iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
{
if (kPlayer.getCivics((CivicOptionTypes)iI) != NO_CIVIC)
{
iNewBadHealthPercent += std::min(0, GC.getCivicInfo(kPlayer.getCivics((CivicOptionTypes)iI)).getImprovementHealthPercentChanges(eImprovement));
}
}
}
}
}
}
}
}
if ((getImprovementGoodHealth() != iNewGoodHealthPercent) || (getImprovementBadHealth() != iNewBadHealthPercent))
{
m_iImprovementGoodHealth = iNewGoodHealthPercent;
m_iImprovementBadHealth = iNewBadHealthPercent;
FAssert(getImprovementGoodHealth() >= 0);
FAssert(getImprovementBadHealth() <= 0);
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
int CvCity::getInvasionChance() const
{
return m_iInvasionChance;
}
void CvCity::changeInvasionChance(int iChange)
{
m_iInvasionChance += iChange;
}
int CvCity::getInvasionTimer() const
{
return m_iInvasionTimer;
}
void CvCity::changeInvasionTimer(int iChange)
{
m_iInvasionTimer += iChange;
}
bool CvCity::isInvaded() const
{
return getInvasionTimer() > 0;
}
int CvCity::getLandmarkAngerTimer() const
{
return m_iLandmarkAngerTimer;
}
void CvCity::changeLandmarkAngerTimer(int iChange)
{
if (iChange != 0)
{
m_iLandmarkAngerTimer += iChange;
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
int CvCity::getLandmarkAnger() const
{
int iAnger = 0;
if (getLandmarkAngerTimer() > 0)
{
iAnger++;
}
int iDivisor = std::max(1, GC.getDefineINT("LANDMARK_ANGER_DIVISOR"));
iDivisor *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getHurryConscriptAngerPercent();
iDivisor /= 100;
iAnger += getLandmarkAngerTimer() / std::max(1, iDivisor);
return iAnger;
}
int CvCity::getCivicHappiness() const
{
return GET_PLAYER(getOwnerINLINE()).getCivicHappiness();
}
bool CvCity::isBuiltFoodProducedUnit() const
{
return m_bBuiltFoodProducedUnit;
}
void CvCity::setBuiltFoodProducedUnit(bool bNewValue)
{
m_bBuiltFoodProducedUnit = bNewValue;
}
int CvCity::getBonusCommerceRateModifier(CommerceTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
return m_aiBonusCommerceRateModifier[eIndex];
}
void CvCity::changeBonusCommerceRateModifier(CommerceTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
if (iChange != 0)
{
m_aiBonusCommerceRateModifier[eIndex] = (m_aiBonusCommerceRateModifier[eIndex] + iChange);
FAssert(getCommerceRate(eIndex) >= 0);
GET_PLAYER(getOwnerINLINE()).invalidateCommerceRankCache(eIndex);
setCommerceModifierDirty(eIndex);
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
int CvCity::getBonusCommerceRateModifier(CommerceTypes eIndex, BonusTypes eBonus) const
{
int iModifier;
int iI;
iModifier = 0;
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
iModifier += getNumActiveBuilding((BuildingTypes)iI) * GC.getBuildingInfo((BuildingTypes) iI).getBonusCommerceModifier(eBonus, eIndex);
}
return iModifier;
}
int CvCity::getBonusCommercePercentChanges(CommerceTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
return m_aiBonusCommercePercentChanges[eIndex];
}
void CvCity::changeBonusCommercePercentChanges(CommerceTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
if (iChange != 0)
{
m_aiBonusCommercePercentChanges[eIndex] = (m_aiBonusCommercePercentChanges[eIndex] + iChange);
FAssert(getCommerceRate(eIndex) >= 0);
GET_PLAYER(getOwnerINLINE()).invalidateCommerceRankCache(eIndex);
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
int CvCity::getBonusCommercePercentChanges(CommerceTypes eIndex, BonusTypes eBonus) const
{
int iPercentCommerce;
int iI;
iPercentCommerce = 0;
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
iPercentCommerce += getNumActiveBuilding((BuildingTypes)iI) * GC.getBuildingInfo((BuildingTypes) iI).getBonusCommercePercentChanges(eBonus, eIndex);
}
return iPercentCommerce;
}
int CvCity::getBonusCommercePercentChanges(CommerceTypes eIndex, BuildingTypes eBuilding) const
{
int iPercentCommerce;
int iI;
iPercentCommerce = 0;
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
if (hasBonus((BonusTypes)iI))
iPercentCommerce += getNumActiveBuilding(eBuilding) * GC.getBuildingInfo(eBuilding).getBonusCommercePercentChanges((BonusTypes)iI, eIndex);
}
return iPercentCommerce;
}
int CvCity::getCommerceAttacks(CommerceTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
return std::min(getMaxCommerceAttacks(eIndex), m_aiCommerceAttacks[eIndex]);
}
void CvCity::changeCommerceAttacks(CommerceTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
if (iChange != 0)
{
m_aiCommerceAttacks[eIndex] = (m_aiCommerceAttacks[eIndex] + iChange);
FAssert(getCommerceRate(eIndex) >= 0);
GET_PLAYER(getOwnerINLINE()).invalidateCommerceRankCache(eIndex);
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
int CvCity::getMaxCommerceAttacks(CommerceTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
return m_aiMaxCommerceAttacks[eIndex];
}
void CvCity::changeMaxCommerceAttacks(CommerceTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
if (iChange != 0)
{
m_aiMaxCommerceAttacks[eIndex] = (m_aiMaxCommerceAttacks[eIndex] + iChange);
}
}
int CvCity::getUnitClassProductionModifier(UnitClassTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumUnitClassInfos(), "eIndex expected to be < GC.getNumUnitClassInfos()");
return m_paiUnitClassProductionModifier[eIndex];
}
void CvCity::changeUnitClassProductionModifier(UnitClassTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumUnitClassInfos(), "eIndex expected to be < GC.getNumUnitClassInfos()");
m_paiUnitClassProductionModifier[eIndex] = (m_paiUnitClassProductionModifier[eIndex] + iChange);
}
int CvCity::getBuildingClassProductionModifier(BuildingClassTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingClassInfos(), "eIndex expected to be < GC.getNumBuildingClassInfos()");
return m_paiBuildingClassProductionModifier[eIndex];
}
void CvCity::changeBuildingClassProductionModifier(BuildingClassTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingClassInfos(), "eIndex expected to be < GC.getNumBuildingClassInfos()");
m_paiBuildingClassProductionModifier[eIndex] = (m_paiBuildingClassProductionModifier[eIndex] + iChange);
}
int CvCity::getBonusDefenseChanges(BonusTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBonusInfos(), "eIndex expected to be < GC.getNumBonusInfos()");
return m_paiBonusDefenseChanges[eIndex];
}
void CvCity::changeBonusDefenseChanges(BonusTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBonusInfos(), "eIndex expected to be < GC.getNumBonusInfos()");
m_paiBonusDefenseChanges[eIndex] = (m_paiBonusDefenseChanges[eIndex] + iChange);
}
bool CvCity::hadVicinityBonus(BonusTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBonusInfos(), "eIndex expected to be < GC.getNumBonusInfos()");
return m_pabHadVicinityBonus[eIndex];
}
void CvCity::recalculatePopulationgrowthratepercentage()
{
PROFILE_FUNC();
m_fPopulationgrowthratepercentageLog = 0;
// Game has been restored from an old save format so we have to calculate
// from first principles
BuildingTypes eLoopBuilding;
for (int iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
eLoopBuilding = (BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iI);
if (eLoopBuilding != NO_BUILDING)
{
if (getNumBuilding(eLoopBuilding) > 0)
{
if ( GC.getBuildingInfo(eLoopBuilding).getPopulationgrowthratepercentage() != 0 )
{
changePopulationgrowthratepercentage(GC.getBuildingInfo(eLoopBuilding).getPopulationgrowthratepercentage(),true);
}
}
}
}
}
int CvCity::getPopulationgrowthratepercentage() const
{
if ( m_fPopulationgrowthratepercentageLog == INVALID_GROWTH_PERCENT_LOG )
{
((CvCity*)this)->recalculatePopulationgrowthratepercentage();
}
float fMultiplier = exp(m_fPopulationgrowthratepercentageLog);
return (int)(fMultiplier*100 - 100);
}
void CvCity::changePopulationgrowthratepercentage(int iChange, bool bAdd)
{
if ( m_fPopulationgrowthratepercentageLog == INVALID_GROWTH_PERCENT_LOG )
{
recalculatePopulationgrowthratepercentage();
}
float logdiff = (bAdd ? 1 : -1)*log((100+(float)iChange)/100);
m_fPopulationgrowthratepercentageLog += logdiff;
}
void CvCity::doPromotion()
{
PROFILE_FUNC();
CvUnit* pLoopUnit;
CLLNode<IDInfo>* pUnitNode;
int iI;
if (isDisorder())
{
return;
}
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if (getNumBuilding((BuildingTypes)iI) > 0)
{
if (GC.getBuildingInfo((BuildingTypes)iI).isApplyFreePromotionOnMove())
{
PromotionTypes ePromotion1 = (PromotionTypes)GC.getBuildingInfo((BuildingTypes)iI).getFreePromotion();
PromotionTypes ePromotion2 = (PromotionTypes)GC.getBuildingInfo((BuildingTypes)iI).getFreePromotion_2();
PromotionTypes ePromotion3 = (PromotionTypes)GC.getBuildingInfo((BuildingTypes)iI).getFreePromotion_3();
if (ePromotion1 != NO_PROMOTION || ePromotion2 != NO_PROMOTION || ePromotion3 != NO_PROMOTION)
{
pUnitNode = plot()->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = plot()->nextUnitNode(pUnitNode);
UnitCombatTypes eUnitCombatType = pLoopUnit->getUnitCombatType();
if ( eUnitCombatType != NO_UNITCOMBAT )
{
if (ePromotion1 != NO_PROMOTION && GC.getPromotionInfo(ePromotion1).getUnitCombat(eUnitCombatType))
{
pLoopUnit->setHasPromotion(ePromotion1, true);
}
if (ePromotion2 != NO_PROMOTION && GC.getPromotionInfo(ePromotion2).getUnitCombat(eUnitCombatType))
{
pLoopUnit->setHasPromotion(ePromotion2, true);
}
if (ePromotion3 != NO_PROMOTION && GC.getPromotionInfo(ePromotion3).getUnitCombat(eUnitCombatType))
{
pLoopUnit->setHasPromotion(ePromotion3, true);
}
}
}
}
}
}
}
}
/*
*/
bool CvCity::isValidTerrainForBuildings(BuildingTypes eBuilding) const
{
//This had to be hardcoded, since there is a terrain peak, but it is really a plot type, not a terrain.
bool bValidTerrain = false;
//int RequiresAndTerrain = 0;
bool bRequiresTerrain = false;
//bool bValidAndTerrain = false;
CvPlot* pLoopPlot;
int iTerrainPeak = GC.getInfoTypeForString("TERRAIN_PEAK");
int iTerrainHill = GC.getInfoTypeForString("TERRAIN_HILL");
bool bPeak = false, bPeak2 = false;
bool bHill = false, bHill2 = false;
bool bRequiresOrImprovement = false;
bool bHasValidImprovement = false;
bool bRequiresOrFeature = false;
bool bHasValidFeature = false;
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
for (int iI = 0; iI < GC.getNumTerrainInfos(); iI++)
{
if (kBuilding.isPrereqOrTerrain(iI))
{
bRequiresTerrain = true;
if (iI == iTerrainPeak)
bPeak = true;
else if (iI == iTerrainHill)
bHill = true;
for (int iJ = 0; iJ < getNumCityPlots(); iJ++)
{
pLoopPlot = getCityIndexPlot(iJ);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getTerrainType() == ((TerrainTypes)iI))
{
bValidTerrain = true;
break;
}
if (pLoopPlot->isPeak() && bPeak)
{
bValidTerrain = true;
break;
}
if (pLoopPlot->isHills() && bHill)
{
bValidTerrain = true;
break;
}
}
}
}
if (kBuilding.isPrereqAndTerrain(iI))
{
bool bHasAndTerrain = false;
if (iI == iTerrainPeak)
bPeak2 = true;
else if (iI == iTerrainHill)
bHill2 = true;
//Checks the city plots for a valid terrain
for (int iJ = 0; iJ < getNumCityPlots(); iJ++)
{
pLoopPlot = getCityIndexPlot(iJ);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getTerrainType() == ((TerrainTypes)iI))
{
bHasAndTerrain = true;
break;
}
if (pLoopPlot->isPeak() && bPeak2)
{
bHasAndTerrain = true;
break;
}
if (pLoopPlot->isHills() && bHill2)
{
bHasAndTerrain = true;
break;
}
}
}
if (!bHasAndTerrain)
{
return false;
}
}
}
if (!bValidTerrain && bRequiresTerrain)
{
return false;
}
for (int iI = 0; iI < GC.getNumImprovementInfos() && !bHasValidImprovement; iI++)
{
if (kBuilding.isPrereqOrImprovement(iI))
{
bRequiresOrImprovement = true;
for (int iJ = 0; iJ < getNumCityPlots(); iJ++)
{
pLoopPlot = getCityIndexPlot(iJ);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getImprovementType() != NO_IMPROVEMENT)
{
if (pLoopPlot->getImprovementType() == (ImprovementTypes)iI)
{
bHasValidImprovement = true;
break;
}
}
}
}
}
}
if (!bHasValidImprovement && bRequiresOrImprovement)
{
return false;
}
for (int iI = 0; iI < GC.getNumFeatureInfos() && !bHasValidFeature; iI++)
{
if (kBuilding.isPrereqOrFeature(iI))
{
bRequiresOrFeature = true;
for (int iJ = 0; iJ < getNumCityPlots(); iJ++)
{
pLoopPlot = getCityIndexPlot(iJ);
if (pLoopPlot != NULL)
{
#ifdef MULTI_FEATURE_MOD
if (pLoopPlot->getHasFeature((FeatureTypes)iI))
#else
if (pLoopPlot->getFeatureType() == iI)
#endif
{
bHasValidFeature = true;
break;
}
}
}
}
}
if (!bHasValidFeature && bRequiresOrFeature)
{
return false;
}
return true;
}
void CvCity::changeFreshWater(int iChange)
{
if (iChange != 0)
{
m_iFreshWater += iChange;
CvPlot* pLoopPlot;
for (int iJ = 0; iJ < getNumCityPlots(); iJ++)
{
pLoopPlot = getCityIndexPlot(iJ);
if (pLoopPlot != NULL)
{
pLoopPlot->updateIrrigated();
}
}
updateFreshWaterHealth();
}
}
bool CvCity::hasFreshWater() const
{
return (m_iFreshWater > 0);
}
/*
*/
bool CvCity::canUpgradeUnit(UnitTypes eUnit) const
{
PROFILE_FUNC();
int iI;
UnitClassTypes eUpgradeUnitClass;
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
if (GC.getUnitInfo(eUnit).getUpgradeUnitClass((UnitClassTypes)iI))
{
eUpgradeUnitClass = ((UnitClassTypes)iI);
if ((GC.getGameINLINE().isUnitClassMaxedOut(eUpgradeUnitClass)) || (GET_TEAM(GET_PLAYER(getOwnerINLINE()).getTeam()).isUnitClassMaxedOut(eUpgradeUnitClass)) || GET_PLAYER(getOwnerINLINE()).isUnitClassMaxedOut(eUpgradeUnitClass))
{//if the upgrade unitclass is maxed out, I assume you can construct them, and already have construct the max
return true;
}
}
}
return false;
}
/*
*/
int CvCity::calculateBonusDefense() const
{
int iBonusDefense = 0;
for (int iI = 0; iI < GC.getNumBonusInfos(); ++iI)
{
if (hasBonus((BonusTypes)iI))
{
iBonusDefense += getBonusDefenseChanges((BonusTypes)iI);
}
}
return iBonusDefense;
}
void CvCity::setCivilizationType(int iCiv)
{
m_iCiv = iCiv;
}
int CvCity::getAdditionalDefenseByBuilding(BuildingTypes eBuilding) const
{
FAssertMsg(eBuilding >= 0, "eBuilding expected to be >= 0");
FAssertMsg(eBuilding < GC.getNumBuildingInfos(), "eBuilding expected to be < GC.getNumBuildingInfos()");
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
bool bObsolete = GET_TEAM(getTeam()).isObsoleteBuilding(eBuilding);
int iExtraRate = 0;
int iExtraBuildingRate = 0;
if (!bObsolete)
{
if (kBuilding.getDefenseModifier() != 0)
{
//int iCultureDefense = getNaturalDefense() - getBuildingDefense();
//iExtraRate += std::max(0, kBuilding.getDefenseModifier() - std::max(0, iCultureDefense));
iExtraBuildingRate += kBuilding.getDefenseModifier();
}
for (int iJ = 0; iJ < GC.getNumBonusInfos(); iJ++)
{
if (hasBonus((BonusTypes)iJ))
{
iExtraRate += kBuilding.getBonusDefenseChanges(iJ);
}
}
if (kBuilding.getAllCityDefenseModifier() != 0)
{
iExtraRate += kBuilding.getAllCityDefenseModifier();
}
//if this new building replaces an old one, subtract the old defense rate from the new one.
BuildingTypes eLoopBuilding;
for (int iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
eLoopBuilding = ((BuildingTypes)(GC.getBuildingClassInfo((BuildingClassTypes)iI).getDefaultBuildingIndex()));
if (eLoopBuilding != NO_BUILDING)
{
if (GC.getBuildingInfo(eLoopBuilding).isReplaceBuildingClass(kBuilding.getBuildingClassType()))
{
if (getNumBuilding(eLoopBuilding) > 0)
{
iExtraBuildingRate -= GC.getBuildingInfo(eLoopBuilding).getDefenseModifier();
for (int iJ = 0; iJ < GC.getNumBonusInfos(); iJ++)
{
if (hasBonus((BonusTypes)iJ))
{
iExtraRate -= GC.getBuildingInfo(eLoopBuilding).getBonusDefenseChanges(iJ);
}
}
iExtraRate -= GC.getBuildingInfo(eLoopBuilding).getAllCityDefenseModifier();
}
}
}
}
}
int iOldEffectiveBuildingRate = std::max(getBuildingDefense(), getNaturalDefense());
int iNewEffectiveBuildingRate = std::max(getBuildingDefense() + iExtraBuildingRate, getNaturalDefense());
return iExtraRate + (iNewEffectiveBuildingRate - iOldEffectiveBuildingRate);
}
void CvCity::checkBuildings(bool bBonus, bool bCivics, bool bWar, bool bPower, bool bPopulation, bool bAlertOwner)
{
PROFILE_FUNC();
BuildingTypes eLoopBuilding;
for (int iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
bool bRestoreBuildings = false;
bool bObsoleteBuildings = false;
bool bHasVicinityBonus = false;
bool bNeedsVicinityBonus = false;
bool bHasBonus = false;
bool bNeedsBonus = false;
bool bMissingBonus = false;
bool bMissingFreshWater = false;
bool bRequiresCivics = false;
bool bRequiresWar = false;
bool bRequiresPower = false;
bool bRequiresPopulation = false;
eLoopBuilding = ((BuildingTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iI)));
if (eLoopBuilding != NO_BUILDING)
{
if (!GET_TEAM(getTeam()).isObsoleteBuilding(eLoopBuilding))
{
if (getNumBuilding(eLoopBuilding) > 0 || isDisabledBuilding(eLoopBuilding))
{
CvBuildingInfo& kBuilding = GC.getBuildingInfo(eLoopBuilding);
/* Check for Appropriate Resources */
if (bBonus)
{
if (kBuilding.getPrereqVicinityBonus() != NO_BONUS)
{
bNeedsVicinityBonus = true;
bHasVicinityBonus = hasVicinityBonus((BonusTypes)kBuilding.getPrereqVicinityBonus());
}
if ( !bNeedsVicinityBonus || bHasVicinityBonus )
{
bool bHasORVicinityBonus = false;
bool bNeedsORVicinityBonus = false;
for (int iJ = 0; iJ < GC.getNUM_BUILDING_PREREQ_OR_BONUSES(); iJ++)
{
if (kBuilding.getPrereqOrVicinityBonuses(iJ) != NO_BONUS)
{
bNeedsORVicinityBonus = true;
if (hasVicinityBonus((BonusTypes)kBuilding.getPrereqOrVicinityBonuses(iJ)))
{
bHasORVicinityBonus = true;
break;
}
}
}
bNeedsVicinityBonus |= bNeedsORVicinityBonus;
if ( bNeedsORVicinityBonus )
{
bHasVicinityBonus = bHasORVicinityBonus;
}
}
//We have the resource, turn on any buildings that are disabled
if (bNeedsVicinityBonus && bHasVicinityBonus)
{
bRestoreBuildings = true;
}
//we lack the nessecary resource, turn off the building
else if (bNeedsVicinityBonus && !bHasVicinityBonus)
{
bObsoleteBuildings = true;
bMissingBonus = true;
}
// Check trade-available resource requirements
if (kBuilding.getPrereqAndBonus() != NO_BONUS)
{
bNeedsBonus = true;
bHasBonus = hasBonus((BonusTypes)kBuilding.getPrereqAndBonus());
}
if ( !bNeedsBonus || bHasBonus )
{
bool bHasORBonus = false;
bool bNeedsORBonus = false;
for (int iJ = 0; iJ < GC.getNUM_BUILDING_PREREQ_OR_BONUSES(); iJ++)
{
if (kBuilding.getPrereqOrBonuses(iJ) != NO_BONUS)
{
bNeedsORBonus = true;
if (hasBonus((BonusTypes)kBuilding.getPrereqOrBonuses(iJ)))
{
bHasORBonus = true;
break;
}
}
}
bNeedsBonus |= bNeedsORBonus;
if ( bNeedsORBonus )
{
bHasBonus = bHasORBonus;
}
}
//We have the resource, turn on any buildings that are disabled
if (bNeedsBonus && bHasBonus)
{
bRestoreBuildings = true;
}
//we lack the nessecary resource, turn off the building
else if (bNeedsBonus && !bHasBonus)
{
bObsoleteBuildings = true;
bMissingBonus = true;
}
}
/* Check fresh water */
if (kBuilding.isFreshWater())
{
if ( plot()->isFreshWater() )
{
bRestoreBuildings = true;
}
else
{
bObsoleteBuildings = true;
bMissingFreshWater = true;
}
}
/* Check War Conditions */
if (bWar)
{
//Not at war
if (kBuilding.isPrereqWar())
{
if (GET_TEAM(getTeam()).getAtWarCount(true) > 0)
{
bRestoreBuildings = true;
}
else
{
bObsoleteBuildings = true;
bRequiresWar = true;
}
}
}
/* Check Civic Requirements */
if (bCivics)
{
if (kBuilding.isRequiresActiveCivics())
{
if (GET_PLAYER(getOwnerINLINE()).hasValidCivics(eLoopBuilding))
{
bRestoreBuildings = true;
}
else
{
bObsoleteBuildings = true;
bRequiresCivics = true;
}
}
}
/*Check Elecricity Requirements */
if (bPower)
{
if (kBuilding.isPrereqPower())
{
if (isPower())
{
bRestoreBuildings = true;
}
else
{
bObsoleteBuildings = true;
bRequiresPower = true;
}
}
}
/* Check The Employed Population */
if (bPopulation)
{
if (visiblePopulation() < 0)
{
if (kBuilding.getNumPopulationEmployed() > 0)
{
//Try and re-assign work before turning off the building
AI_assignWorkingPlots();
if (visiblePopulation() < 0)
{
bObsoleteBuildings = true;
bRequiresPopulation = true;
}
}
}
else if (visiblePopulation() - kBuilding.getNumPopulationEmployed() >= 0)
{
if (kBuilding.getNumPopulationEmployed() > 0)
{
bRestoreBuildings = true;
bRequiresPopulation = true;
}
}
}
/* Alert the Player */
CvWString szBuffer;
if (bRestoreBuildings && !bObsoleteBuildings && isDisabledBuilding(eLoopBuilding))
{
setDisabledBuilding(eLoopBuilding, false);
if ( bAlertOwner )
{
if (!GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_IGNORE_DISABLED_ALERTS))
{
MEMORY_TRACK_EXEMPT();
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_CITY_RESTORED_BUILDINGS", getNameKey(), kBuilding.getDescription(), kBuilding.getDescription()));
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, NULL, MESSAGE_TYPE_MINOR_EVENT, kBuilding.getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), getX_INLINE(), getY_INLINE(), true, true);
}
}
}
else if (bObsoleteBuildings && !isDisabledBuilding(eLoopBuilding))
{
setDisabledBuilding(eLoopBuilding, true);
if ( bAlertOwner )
{
if (bMissingBonus || bRequiresWar)
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_CITY_REMOVED_BUILDINGS_RESOURCES", kBuilding.getDescription(), getNameKey(), kBuilding.getDescription()));
else if (bRequiresCivics)
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_CITY_REMOVED_BUILDINGS_CIVICS", kBuilding.getDescription(), getNameKey(), kBuilding.getDescription()));
else if (bRequiresPower)
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_CITY_REMOVED_BUILDINGS_POWER", kBuilding.getDescription(), getNameKey()));
else if (bRequiresPopulation)
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_CITY_REMOVED_BUILDINGS_POPULATION", kBuilding.getDescription(), getNameKey()));
else if (bMissingFreshWater)
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_CITY_REMOVED_FRESH_WATER", kBuilding.getDescription(), getNameKey()));
else
FAssert(false);
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_CITY_REMOVED_BUILDINGS_RESOURCES", kBuilding.getDescription(), getNameKey(), kBuilding.getDescription()));
if (!GET_PLAYER(getOwnerINLINE()).isModderOption(MODDEROPTION_IGNORE_DISABLED_ALERTS))
{
MEMORY_TRACK_EXEMPT();
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, NULL, MESSAGE_TYPE_MINOR_EVENT, kBuilding.getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_WARNING_TEXT"), getX_INLINE(), getY_INLINE(), true, true);
}
}
}
}
}
}
}
}
int CvCity::calculateCorporationHealth() const
{
int iI;
int iHealth = 0;
for (iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
if (isActiveCorporation((CorporationTypes)iI))
{
iHealth += GC.getCorporationInfo((CorporationTypes)iI).getHealth();
}
}
return iHealth;
}
int CvCity::calculateCorporationHappiness() const
{
int iI;
int iHappiness = 0;
for (iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
if (isActiveCorporation((CorporationTypes)iI))
{
iHappiness += GC.getCorporationInfo((CorporationTypes)iI).getHappiness();
}
}
return iHappiness;
}
int CvCity::getExtraYieldTurns() const
{
return m_iExtraYieldTurns;
}
void CvCity::changeExtraYieldTurns(int iChange)
{
int iOldVal = m_iExtraYieldTurns;
CvPlot* pPlot;
pPlot = GC.getMapINLINE().plotINLINE(getX(), getY());
m_iExtraYieldTurns += iChange;
if (iOldVal == -1 && iChange == 1)
{
changeBaseYieldRate(YIELD_FOOD, -1);
pPlot->updateSymbols();
}
else if (iOldVal == 1 && iChange == -1)
{
changeBaseYieldRate(YIELD_PRODUCTION, -1);
pPlot->updateSymbols();
}
}
int CvCity::getLineOfSight() const
{
return m_iLineOfSight;
}
void CvCity::changeLineOfSight(int iChange)
{
m_iLineOfSight += iChange;
}
void CvCity::setExtraYieldTurns(int iNewVal)
{
m_iExtraYieldTurns = iNewVal;
CvPlot* pPlot;
pPlot = GC.getMapINLINE().plotINLINE(getX(), getY());
if (iNewVal > 0)
{
changeBaseYieldRate(YIELD_PRODUCTION, 1);
pPlot->updateSymbols();
}
else if (iNewVal < 0)
{
changeBaseYieldRate(YIELD_FOOD, 1);
pPlot->updateSymbols();
}
}
BuildTypes CvCity::findChopBuild(FeatureTypes eFeature)
{
int iI;
for (iI = 0; iI < GC.getNumBuildInfos(); iI++)
{
CvBuildInfo& kBuild = GC.getBuildInfo((BuildTypes)iI);
if (kBuild.getImprovement() == NO_IMPROVEMENT)
{
if (kBuild.isFeatureRemove(eFeature) && kBuild.getFeatureProduction(eFeature) != 0)
{
break;
}
}
}
return (BuildTypes)iI;
}
CultureLevelTypes CvCity::getOccupationCultureLevel() const
{
return m_eOccupationCultureLevel;
}
void CvCity::setOccupationCultureLevel(CultureLevelTypes eNewValue)
{
CvPlot* pLoopPlot;
int iCultureRange, eOldValue;
int iDX, iDY;
PROFILE_FUNC();
eOldValue = getOccupationCultureLevel();
if (eOldValue != eNewValue)
{
m_eOccupationCultureLevel = eNewValue;
if (eOldValue != NO_CULTURELEVEL)
{
clearCultureDistanceCache();
for (iDX = -eOldValue; iDX <= eOldValue; iDX++)
{
for (iDY = -eOldValue; iDY <= eOldValue; iDY++)
{
iCultureRange = cultureDistance(iDX, iDY);
if (iCultureRange > getOccupationCultureLevel())
{
if (iCultureRange <= eOldValue)
{
FAssert(iCultureRange <= GC.getNumCultureLevelInfos());
pLoopPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
pLoopPlot->changeOccupationCultureRangeCities(getOwnerINLINE(), -1);
}
}
}
}
}
}
if (getOccupationCultureLevel() != NO_CULTURELEVEL)
{
for (iDX = -getOccupationCultureLevel(); iDX <= getOccupationCultureLevel(); iDX++)
{
for (iDY = -getOccupationCultureLevel(); iDY <= getOccupationCultureLevel(); iDY++)
{
/************************************************************************************************/
/* phunny_pharmer Start 05/01/10 */
/* occupation culture doesn't play well with caching, so just use the plot distance */
/************************************************************************************************/
iCultureRange = cultureDistance(iDX, iDY, true);
// iCultureRange = plotDistance(0, 0, iDX, iDY);
/************************************************************************************************/
/* phunny_pharmer END */
/************************************************************************************************/
if (iCultureRange > eOldValue)
{
if (iCultureRange <= getOccupationCultureLevel())
{
FAssert(iCultureRange <= GC.getNumCultureLevelInfos());
pLoopPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (pLoopPlot->isPotentialCityWorkForArea(area()))
{
pLoopPlot->changeCulture(getOwnerINLINE(), 1, false);
}
pLoopPlot->changeOccupationCultureRangeCities(getOwnerINLINE(), 1);
}
}
}
}
}
}
}
}
CultureLevelTypes CvCity::getMaxCultureLevelAmongPlayers() const
{
int iMaxCulture;
iMaxCulture = getCultureTimes100((PlayerTypes)0);
for (int iI = 1; iI < MAX_PLAYERS; iI++)
{
if (getCultureTimes100((PlayerTypes)iI) > iMaxCulture)
{
iMaxCulture = getCultureTimes100((PlayerTypes)iI);
}
}
return getCultureLevelForCulture(iMaxCulture);
}
CultureLevelTypes CvCity::getCultureLevel(PlayerTypes eIndex) const
{
return getCultureLevelForCulture(getCultureTimes100(eIndex));
}
CultureLevelTypes CvCity::getCultureLevelForCulture(int iCulture) const
{
CultureLevelTypes eCultureLevel = ((CultureLevelTypes)0);
for (int iI = (GC.getNumCultureLevelInfos() - 1); iI > 0; iI--)
{
if (iCulture >= 100 * GC.getCultureLevelInfo((CultureLevelTypes)iI).getSpeedThreshold(GC.getGameINLINE().getGameSpeedType()))
{
eCultureLevel = ((CultureLevelTypes)iI);
break;
}
}
return eCultureLevel;
}
int CvCity::calculateBonusCommerceRateModifier(CommerceTypes eIndex) const
{
int iI;
int iMultiplier = 0;
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
if (hasBonus((BonusTypes)iI))
{
iMultiplier += GET_PLAYER(getOwnerINLINE()).getBonusCommerceModifier((BonusTypes)iI, eIndex);
}
}
return iMultiplier;
}
static bool bonusAvailableFromBuildings(BonusTypes eBonus)
{
static bool* bBonusAvailability = NULL;
FAssert( 0 <= eBonus );
FAssert( GC.getNumBonusInfos() > eBonus );
if ( bBonusAvailability == NULL )
{
int iI, iJ;
bBonusAvailability = new bool[GC.getNumBonusInfos()];
for(iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
bBonusAvailability[iI] = false;
}
for(iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
for(iJ = 0; iJ < GC.getNumBuildingInfos(); iJ++)
{
if ( GC.getBuildingInfo((BuildingTypes)iJ).getFreeBonus() == iI || GC.getBuildingInfo((BuildingTypes)iJ).hasExtraFreeBonus((BonusTypes)iI) )
{
bBonusAvailability[iI] = true;
break;
}
}
}
}
return bBonusAvailability[eBonus];
}
void CvCity::clearVicinityBonusCache(BonusTypes eBonus)
{
if ( m_pabHasVicinityBonusCached != NULL )
{
m_pabHasVicinityBonusCached[eBonus] = false;
}
}
bool CvCity::hasVicinityBonus(BonusTypes eBonus) const
{
PROFILE_FUNC();
if ( m_pabHasVicinityBonusCached == NULL )
{
m_pabHasVicinityBonusCached = new bool[GC.getNumBonusInfos()];
SAFE_DELETE_ARRAY(m_pabHasVicinityBonus);
m_pabHasVicinityBonus = new bool[GC.getNumBonusInfos()];
for(int iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
m_pabHasVicinityBonusCached[iI] = false;
}
}
if ( m_pabHasVicinityBonusCached[eBonus] )
{
return m_pabHasVicinityBonus[eBonus];
}
bool bResult = false;
//No sense in checking...
if (hasBonus(eBonus))
{
if (plot()->getBonusType() == eBonus)
{
bResult = true;
}
if ( !bResult )
{
for (int iI = 0; iI < getNumCityPlots(); iI++)
{
CvPlot* pLoopPlot = plotCity(getX_INLINE(), getY_INLINE(), iI);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getBonusType() == eBonus)
{
if (pLoopPlot->getOwnerINLINE() == getOwnerINLINE())
{
if (pLoopPlot->isHasValidBonus() && pLoopPlot->isConnectedTo(this))
{
bResult = true;
break;
}
}
}
}
}
}
if (!bResult)
{
if ( bonusAvailableFromBuildings(eBonus) )
{
for(int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
CvBuildingInfo& kBuilding = GC.getBuildingInfo((BuildingTypes)iI);
if ( kBuilding.getFreeBonus() == eBonus || kBuilding.hasExtraFreeBonus(eBonus) )
{
if ( getNumActiveBuilding((BuildingTypes)iI) > 0 )
{
bResult = true;
break;
}
}
}
}
}
}
m_pabHasVicinityBonus[eBonus] = bResult;
m_pabHasVicinityBonusCached[eBonus] = true;
return bResult;
}
bool CvCity::isDevelopingCity() const
{
int iNumCities = GET_PLAYER(getOwnerINLINE()).getNumCities();
int iEmpirePop;
int iPopulation;
bool bDevelopingCity = false;
if (findPopulationRank() >= ((iNumCities * 2) / 3))
{
iPopulation = getPopulation();
iEmpirePop = GET_PLAYER(getOwnerINLINE()).getTotalPopulation();
int iAvgPop = iEmpirePop / iNumCities;
if (iPopulation < ((iAvgPop * 2) / 3))
{
bDevelopingCity = true;
}
}
if (iNumCities < 3)
{
if (iPopulation <= 2)
{
if (!isCapital())
{
bDevelopingCity = true;
}
}
}
return bDevelopingCity;
}
void CvCity::doVicinityBonus()
{
PROFILE_FUNC();
for (int iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
// Clear the cache each turn before performing this calculation
clearVicinityBonusCache((BonusTypes)iI);
int iChange = 0;
bool bHadVicinityBonus = hadVicinityBonus((BonusTypes)iI);
bool bHasVicinityBonus = hasVicinityBonus((BonusTypes)iI);
if (bHadVicinityBonus && !bHasVicinityBonus)
{
iChange = -1;
}
else if (bHasVicinityBonus && !bHadVicinityBonus)
{
iChange = 1;
}
if (iChange != 0)
{
for (int iJ = 0; iJ < GC.getNumBuildingInfos(); iJ++)
{
const CvBuildingInfo& kBuilding = GC.getBuildingInfo((BuildingTypes)iJ);
if (getNumRealBuilding((BuildingTypes)iJ) > 0)
{
for (int iK = 0; iK < NUM_YIELD_TYPES; iK++)
{
int iYieldChange = kBuilding.getVicinityBonusYieldChanges((BonusTypes)iI, iK);
if ( iYieldChange != 0 )
{
updateYieldRate((BuildingTypes)iJ, (YieldTypes)iK, ((getBuildingYieldChange((BuildingClassTypes)(kBuilding.getBuildingClassType()), (YieldTypes)iK)) + (iYieldChange * iChange)));
}
}
}
}
}
}
}
bool CvCity::isDisabledBuilding(BuildingTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
return m_pabDisabledBuilding[eIndex];
}
void CvCity::setDisabledBuilding(BuildingTypes eIndex, bool bNewValue)
{
//bool bOldValue = isDisabledBuilding(eIndex);
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumBuildingInfos(), "eIndex expected to be < GC.getNumBuildingInfos()");
if (isDisabledBuilding(eIndex) != bNewValue)
{
m_pabDisabledBuilding[eIndex] = bNewValue;
processBuilding(eIndex, bNewValue ? -1 : 1);
//setNumRealBuilding(eIndex, bNewValue ? 0 : 1);
}
}
int CvCity::getUnitCombatExtraStrength(UnitCombatTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumUnitCombatInfos(), "eIndex expected to be < GC.getNumUnitCombatInfos()");
return m_paiUnitCombatExtraStrength[eIndex];
}
void CvCity::changeUnitCombatExtraStrength(UnitCombatTypes eIndex, int iChange)
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < GC.getNumUnitCombatInfos(), "eIndex expected to be < GC.getNumUnitCombatInfos()");
m_paiUnitCombatExtraStrength[eIndex] = (m_paiUnitCombatExtraStrength[eIndex] + iChange);
}
void CvCity::doInvasion()
{
PROFILE_FUNC();
bool bTestInvasion = false;
PlayerTypes ePlayer = NO_PLAYER;
if (!isInvaded())
{
if (getInvasionChance() > 0)
{
for (int iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
CvPlot* pAdjacentPlot = plotDirection(getX_INLINE(), getY_INLINE(), (DirectionTypes)iI);
if (pAdjacentPlot != NULL && !bTestInvasion)
{
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
pUnitNode = pAdjacentPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pAdjacentPlot->nextUnitNode(pUnitNode);
if (GET_TEAM(pLoopUnit->getTeam()).isAtWar(getTeam()))
{
bTestInvasion = true;
ePlayer = pLoopUnit->getOwnerINLINE();
break;
}
}
}
}
}
if (bTestInvasion)
{
if (getCitySorenRandNum(100, "Enemy Invades City Chance") < getInvasionChance())
{
int iTurns = getInvasionChance() / 2;
changeInvasionTimer(iTurns);
//Alert the Player
if (GET_PLAYER(getOwnerINLINE()).isHuman())
{
CvPopupInfo* pInfo = new CvPopupInfo(BUTTONPOPUP_INVASION);
pInfo->setData1(getID());
gDLL->getInterfaceIFace()->addPopup(pInfo, getOwnerINLINE());
}
//Alert the invader
if (GET_PLAYER(ePlayer).isHuman())
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_INVASION_SUCCESSFUL", getNameKey()));
AddDLLMessage(ePlayer, false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_BUILD_BARRACKS", MESSAGE_TYPE_INFO, ARTFILEMGR.getInterfaceArtInfo("WORLDBUILDER_CITY_EDIT")->getPath(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), true, true);
}
}
}
}
else
{
changeInvasionTimer(-1);
if (!isInvaded())
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_INVASION_ENDED", getNameKey()));
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_BUILD_BARRACKS", MESSAGE_TYPE_INFO, ARTFILEMGR.getInterfaceArtInfo("WORLDBUILDER_CITY_EDIT")->getPath(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), true, true);
}
}
}
bool CvCity::isZoneOfControl() const
{
return m_iZoCCount > 0;
}
void CvCity::changeZoCCount(short iChange)
{
m_iZoCCount += iChange;
}
int CvCity::getAdjacentDamagePercent() const
{
return m_iAdjacentDamagePercent;
}
void CvCity::changeAdjacentDamagePercent(int iChange)
{
m_iAdjacentDamagePercent += iChange;
}
int CvCity::getWorkableRadiusOverride() const
{
return m_iWorkableRadiusOverride;
}
void CvCity::setWorkableRadiusOverride(int iNewVal)
{
m_iWorkableRadiusOverride = iNewVal;
}
bool CvCity::isProtectedCulture() const
{
return getProtectedCultureCount() > 0;
}
int CvCity::getProtectedCultureCount() const
{
return m_iProtectedCultureCount;
}
void CvCity::changeProtectedCultureCount(int iChange)
{
m_iProtectedCultureCount += iChange;
}
int CvCity::getNumUnitFullHeal() const
{
return m_iNumUnitFullHeal;
}
void CvCity::changeNumUnitFullHeal(int iChange)
{
m_iNumUnitFullHeal += iChange;
}
void CvCity::doAttack()
{
PROFILE_FUNC();
CvWString szBuffer;
if (getAdjacentDamagePercent() > 0)
{
if (GET_TEAM(getTeam()).getAtWarCount(false) > 0)
{
bool abInformPlayer[MAX_PLAYERS];
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
abInformPlayer[iI] = false;
}
for (int iI = 0; iI < NUM_DIRECTION_TYPES; ++iI)
{
CvPlot* pAdjacentPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));
if (pAdjacentPlot != NULL)
{
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
pUnitNode = pAdjacentPlot->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = pAdjacentPlot->nextUnitNode(pUnitNode);
if (pLoopUnit->getTeam() != getTeam())
{
if (GET_TEAM(getTeam()).isAtWar(pLoopUnit->getTeam()))
{
// Koshling - changed city defenses to have a 1-in-4 chance of damaging each unit each turn
if (pLoopUnit->baseCombatStr() && getCitySorenRandNum(4,"City adjacent damage") == 0)
{
int iDamage = pLoopUnit->currHitPoints();
iDamage *= getAdjacentDamagePercent();
iDamage /= 100;
pLoopUnit->changeDamage(iDamage, getOwnerINLINE());
if (!abInformPlayer[pLoopUnit->getOwnerINLINE()])
{
MEMORY_TRACK_EXEMPT();
abInformPlayer[pLoopUnit->getOwnerINLINE()] = true;
szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_UNITS_DAMAGED", getNameKey()));
AddDLLMessage(pLoopUnit->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, NULL, MESSAGE_TYPE_INFO, pLoopUnit->getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pLoopUnit->getX_INLINE(), pLoopUnit->getY_INLINE(), true, true);
}
}
}
}
}
}
}
}
}
}
void CvCity::doHeal()
{
PROFILE_FUNC();
if (getNumUnitFullHeal() > 0)
{
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
int iNumHeals = getNumUnitFullHeal();
int iNumDamagedUnits = 0;
pUnitNode = plot()->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = plot()->nextUnitNode(pUnitNode);
if (pLoopUnit->getTeam() == getTeam())
{
if (pLoopUnit->getDamage() > 0)
{
iNumDamagedUnits++;
}
}
}
iNumHeals = std::min(iNumHeals, iNumDamagedUnits);
while (iNumHeals > 0)
{
pUnitNode = plot()->headUnitNode();
while (pUnitNode != NULL)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = plot()->nextUnitNode(pUnitNode);
if (pLoopUnit->getTeam() == getTeam())
{
if (pLoopUnit->getDamage() > 0)
{
if (getCitySorenRandNum(100, "Unit Full Heals") > 50)
{
pLoopUnit->setDamage(0, getOwnerINLINE(), false);
iNumHeals--;
//Recheck condition after each heal
break;
}
}
}
}
}
}
}
void CvCity::decayCommerce()
{
if (GC.getGameINLINE().getElapsedGameTurns() % std::max(1, GC.getDefineINT("COMMERCE_ATTACKS_FADE_RATE")))
{
for (int iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
if (getCommerceAttacks((CommerceTypes)iI) > 0)
{
changeCommerceAttacks((CommerceTypes)iI, -1);
}
else if (getCommerceAttacks((CommerceTypes)iI) < 0)
{
changeCommerceAttacks((CommerceTypes)iI, 1);
}
}
}
}
void CvCity::doCorporation()
{
PROFILE_FUNC();
CvCity* pLoopCity;
int iRandThreshold;
int iSpread;
int iLoop;
int iI, iJ;
int iAverageSpread;
CvPlayer& kOwner = GET_PLAYER(getOwnerINLINE());
if (!GC.getGameINLINE().isOption(GAMEOPTION_REALISTIC_CORPORATIONS))
{
return;
}
for (iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
if (GC.getGameINLINE().getHeadquarters((CorporationTypes)iI) != NULL)
{
if (!isHasCorporation((CorporationTypes)iI) && GC.getGameINLINE().canEverSpread((CorporationTypes)iI))
{
if (!kOwner.isNoCorporations() && (!kOwner.isNoForeignCorporations() || GC.getGameINLINE().getHeadquarters((CorporationTypes)iI)->getOwner() == getOwner()))
{
iRandThreshold = 0;
for (iJ = 0; iJ < MAX_PLAYERS; iJ++)
{
CvPlayer& kPlayer = GET_PLAYER((PlayerTypes)iJ);
if (kPlayer.isAlive())
{
for (pLoopCity = kPlayer.firstCity(&iLoop); pLoopCity != NULL; pLoopCity = kPlayer.nextCity(&iLoop))
{
if (pLoopCity->isConnectedTo(this))
{
iSpread = pLoopCity->getCorporationInfluence((CorporationTypes)iI);
iSpread *= GC.getCorporationInfo((CorporationTypes)iI).getSpread();
iSpread /= 100;
if (kPlayer.getID() != kOwner.getID())
{
if (GET_TEAM(kPlayer.getTeam()).isFreeTradeAgreement(kOwner.getTeam()))
{
iSpread *= (100 + GC.getDefineINT("FREE_TRADE_CORPORATION_SPREAD_MOD"));
iSpread /= 100;
}
}
if (iSpread > 0)
{
iSpread /= std::max(1, (((GC.getDefineINT("CORPORATION_SPREAD_DISTANCE_DIVISOR") * plotDistance(getX_INLINE(), getY_INLINE(), pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE())) / GC.getMapINLINE().maxPlotDistance()) - 5));
iRandThreshold = std::max(iRandThreshold, iSpread);
}
}
}
}
}
iRandThreshold *= kOwner.getCorporationSpreadModifier() + 100;
iRandThreshold /= 100;
iRandThreshold *= kOwner.getCorporationInfluence((CorporationTypes)iI);
iRandThreshold /= 100;
iRandThreshold /= (1 + (getCorporationCount() / 2));
logBBAIForTeam(getTeam(), " City (%S) Has Rand Threshold of %d for Corporation %S", getName().GetCString(), iRandThreshold, GC.getCorporationInfo((CorporationTypes)iI).getDescription());
int iRand = GC.getDefineINT("CORPORATION_SPREAD_RAND");
iRand *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getConstructPercent();
iRand /= 100;
iRand = getCitySorenRandNum(iRand, "Corporation Spread");
if (iRand < iRandThreshold)
{
//Remove Hostile Corporations
for (int iJ = 0; iJ < GC.getNumCorporationInfos(); iJ++)
{
if (iI != iJ)
{
if (GC.getGameINLINE().isCompetingCorporation((CorporationTypes)iJ, (CorporationTypes)iI))
{
if (isActiveCorporation((CorporationTypes)iJ))
{
MEMORY_TRACK_EXEMPT();
setHasCorporation((CorporationTypes)iJ, false, false, false);
CvWString szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_CORPORATION_HOSTILE_TAKEOVER", GC.getCorporationInfo((CorporationTypes)iJ).getTextKeyWide(), GC.getCorporationInfo((CorporationTypes)iI).getTextKeyWide(), getNameKey()));
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, GC.getCorporationInfo((CorporationTypes)iJ).getSound(), MESSAGE_TYPE_MAJOR_EVENT, GC.getCorporationInfo((CorporationTypes)iJ).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), getX_INLINE(), getY_INLINE(), false, false);
}
}
}
}
setHasCorporation((CorporationTypes)iI, true, true, false);
break;
}
}
}
//Decay
else
{
//TODO: Should HQ ever relocate?
if (this != GC.getGameINLINE().getHeadquarters((CorporationTypes)iI))
{
iAverageSpread = GC.getGameINLINE().getAverageCorporationInfluence(this, (CorporationTypes)iI);
iSpread = GC.getCorporationInfo((CorporationTypes)iI).getSpread();
iSpread *= getCorporationInfluence((CorporationTypes)iI);
iSpread /= 100;
iSpread *= kOwner.getCorporationInfluence((CorporationTypes)iI);
iSpread /= 100;
int iDiff = iAverageSpread - iSpread;
//Our influence is lower than average
if (iDiff > 0)
{
int iRand = GC.getDefineINT("CORPORATION_SPREAD_RAND");
iRand *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getConstructPercent();
iRand /= 100;
if (getCitySorenRandNum(iRand, "Corporation Decay") < iDiff)
{
setHasCorporation((CorporationTypes)iI, false, true, false);
break;
}
}
}
}
}
}
}
int CvCity::getCorporationInfluence(CorporationTypes eCorporation) const
{
int iInfluence = 100;
int iBonusesConsumed = 0;
int iNumAvailBonuses = 0;
//Influence scales based on the number of resources a corporation consumes
for (int iI = 0; iI < GC.getNUM_CORPORATION_PREREQ_BONUSES(); ++iI)
{
BonusTypes eBonus = (BonusTypes)GC.getCorporationInfo(eCorporation).getPrereqBonus(iI);
if (eBonus != NO_BONUS)
{
iBonusesConsumed++;
iNumAvailBonuses += getNumBonuses(eBonus);
}
}
if (iNumAvailBonuses > 0)
{
iInfluence += iNumAvailBonuses;
}
else
{
if (iNumAvailBonuses == 0 && iBonusesConsumed > 0)
{
return 0;
}
}
if (iBonusesConsumed > 0)
{
for (int iI = 0; iI < GC.getNUM_CORPORATION_PREREQ_BONUSES(); ++iI)
{
BonusTypes eBonus = (BonusTypes)GC.getCorporationInfo(eCorporation).getPrereqBonus(iI);
if (eBonus != NO_BONUS)
{
if (hasBonus(eBonus))
{
iInfluence += (GC.getDefineINT("CORPORATION_RESOURCE_BASE_INFLUENCE") / iBonusesConsumed);
}
}
}
}
for (int iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
if (iI != eCorporation)
{
if (GC.getGameINLINE().isCompetingCorporation(eCorporation, (CorporationTypes)iI))
{
if (isActiveCorporation((CorporationTypes)iI))
{
iInfluence /= 10;
}
}
}
}
for (int iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
if (iI != eCorporation)
{
if (GC.getGameINLINE().isCompetingCorporation(eCorporation, (CorporationTypes)iI))
{
if (isActiveCorporation((CorporationTypes)iI))
{
if (GC.getGameINLINE().getHeadquarters((CorporationTypes)iI) == this)
{
return 0;
}
}
}
}
}
int iAveragePopulation = GC.getGame().getTotalPopulation();
iAveragePopulation /= std::max(1, GC.getGame().getNumCivCities());
if (iAveragePopulation > 0)
{
iInfluence *= getPopulation();
iInfluence /= iAveragePopulation;
}
return iInfluence;
}
int CvCity::calculateCorporateTaxes() const
{
PROFILE_FUNC();
int iTaxes = 0;
for (int iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
if (isActiveCorporation((CorporationTypes)iI) && GET_PLAYER(getOwnerINLINE()).isActiveCorporation((CorporationTypes)iI))
{
CorporationTypes eCorporation = (CorporationTypes)iI;
for (int iCommerce = 0; iCommerce < NUM_COMMERCE_TYPES; ++iCommerce)
{
iTaxes += 100 * GC.getCorporationInfo(eCorporation).getHeadquarterCommerce(iCommerce);
}
int iNumBonuses = 0;
for (int i = 0; i < GC.getNUM_CORPORATION_PREREQ_BONUSES(); ++i)
{
BonusTypes eBonus = (BonusTypes)GC.getCorporationInfo(eCorporation).getPrereqBonus(i);
if (NO_BONUS != eBonus)
{
iNumBonuses += getNumBonuses(eBonus);
}
}
int iBonusTaxes = GC.getCorporationInfo(eCorporation).getMaintenance() * iNumBonuses;
iBonusTaxes *= GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getCorporationMaintenancePercent();
iBonusTaxes /= 200;
iTaxes += iBonusTaxes;
int iAveragePopulation = GC.getGame().getTotalPopulation();
iAveragePopulation /= std::max(1, GC.getGame().getNumCivCities());
if (iAveragePopulation > 0)
{
iTaxes *= getPopulation();
iTaxes /= iAveragePopulation;
}
iTaxes *= std::min(0, (GET_PLAYER(getOwnerINLINE()).getCorporationMaintenanceModifier() + 100));
iTaxes /= 100;
iTaxes = abs(iTaxes);
int iInflation = GET_PLAYER(getOwnerINLINE()).calculateInflationRate() + 100;
if (iInflation > 0)
{
iTaxes *= 100;
iTaxes /= iInflation;
}
iTaxes /= (1 + GET_PLAYER(getOwnerINLINE()).getHandicapType() / 2);
}
}
return iTaxes / 100;
}
int CvCity::getDisabledPowerTimer() const
{
return m_iDisabledPowerTimer;
}
void CvCity::changeDisabledPowerTimer(int iChange)
{
m_iDisabledPowerTimer += iChange;
}
int CvCity::getWarWearinessTimer() const
{
return m_iWarWearinessTimer;
}
void CvCity::changeWarWearinessTimer(int iChange)
{
m_iWarWearinessTimer += iChange;
}
void CvCity::doDisabledPower()
{
if (getDisabledPowerTimer() > 0)
{
changeDisabledPowerTimer(-1);
if (getDisabledPowerTimer() == 0)
{
MEMORY_TRACK_EXEMPT();
CvWString szBuffer = DLL_SERIALIZE(gDLL->getText("TXT_KEY_MISC_POWER_RESTORED", getNameKey()));
AddDLLMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_POSITIVE_DINK", MESSAGE_TYPE_MINOR_EVENT, ARTFILEMGR.getInterfaceArtInfo("WORLDBUILDER_CITY_EDIT")->getPath(), (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), false, false);
}
}
}
void CvCity::doWarWeariness()
{
if (getWarWearinessTimer() > 0)
{
changeWarWearinessTimer(-20);
}
if (getEventAnger() > 0)
{
int iTurnCheck = 10;
iTurnCheck *= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getAnarchyPercent();
iTurnCheck /= 100;
if (GC.getGameINLINE().getElapsedGameTurns() % iTurnCheck == 0)
{
changeEventAnger(-1);
}
}
}
int CvCity::getEventAnger() const
{
return m_iEventAnger;
}
void CvCity::changeEventAnger(int iChange)
{
if (iChange != 0)
{
m_iEventAnger += iChange;
FAssert(getEventAnger() >= 0);
AI_setAssignWorkDirty(true);
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
}
}
}
int CvCity::getNonHolyReligionCount() const
{
int iCount;
int iI;
iCount = 0;
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if (isHasReligion((ReligionTypes)iI))
{
if (!isHolyCity((ReligionTypes)iI))
{
iCount++;
}
}
}
return iCount;
}
void CvCity::calculateExtraTradeRouteProfit(int iExtra, int* &aiTradeYields) const
{
PROFILE_FUNC();
if (GC.getMAX_TRADE_ROUTES() <= 0)
{
return;
}
int* paiBestValue;
IDInfo* paTradeCities;
CvCity* pLoopCity;
int iTradeRoutes;
int iTradeProfit;
int iValue;
int iLoop;
int iI, iJ, iK;
paiBestValue = new int[GC.getMAX_TRADE_ROUTES()];
paTradeCities = new IDInfo[GC.getMAX_TRADE_ROUTES()];
for (iI = 0; iI < GC.getMAX_TRADE_ROUTES(); iI++)
{
paiBestValue[iI] = 0;
paTradeCities[iI].reset();
}
if (!isDisorder() && !isPlundered())
{
iTradeRoutes = std::min(getTradeRoutes() + std::max(0, iExtra), GC.getMAX_TRADE_ROUTES());
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER(getOwnerINLINE()).canHaveTradeRoutesWith((PlayerTypes)iI))
{
for (pLoopCity = GET_PLAYER((PlayerTypes)iI).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iI).nextCity(&iLoop))
{
if (pLoopCity != this)
{
if (!(pLoopCity->isTradeRoute(getOwnerINLINE())) || (getTeam() == GET_PLAYER((PlayerTypes)iI).getTeam()))
{
if (pLoopCity->plotGroup(getOwnerINLINE()) == plotGroup(getOwnerINLINE()) || GC.getDefineINT("IGNORE_PLOT_GROUP_FOR_TRADE_ROUTES"))
{
iValue = calculateTradeProfitTimes100(pLoopCity);
for (iJ = 0; iJ < iTradeRoutes; iJ++)
{
if (iValue > paiBestValue[iJ])
{
for (iK = (iTradeRoutes - 1); iK > iJ; iK--)
{
paiBestValue[iK] = paiBestValue[(iK - 1)];
paTradeCities[iK] = paTradeCities[(iK - 1)];
}
paiBestValue[iJ] = iValue;
paTradeCities[iJ] = pLoopCity->getIDInfo();
break;
}
}
}
}
}
}
}
}
}
iTradeProfit = 0;
for (iI = 0; iI < std::min(getTradeRoutes(), GC.getMAX_TRADE_ROUTES()); iI++)
{
pLoopCity = getCity(paTradeCities[iI]);
if (pLoopCity != NULL)
{
iTradeProfit -= calculateTradeProfitTimes100(pLoopCity);
}
}
for (iI = 0; iI < getTradeRoutes() + iExtra; iI++)
{
pLoopCity = getCity(paTradeCities[iI]);
if (pLoopCity != NULL)
{
iTradeProfit += calculateTradeProfitTimes100(pLoopCity);
}
}
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
aiTradeYields[iI] = (((YieldTypes)iI), calculateTradeYield(((YieldTypes)iI), iTradeProfit) / 100);
}
SAFE_DELETE_ARRAY(paiBestValue);
SAFE_DELETE_ARRAY(paTradeCities);
}
int CvCity::getMinimumDefenseLevel() const
{
return m_iMinimumDefenseLevel;
}
void CvCity::setMinimumDefenseLevel(int iNewValue)
{
m_iMinimumDefenseLevel = iNewValue;
}
int CvCity::getNumPopulationEmployed() const
{
return m_iNumPopulationEmployed;
}
void CvCity::setNumPopulationEmployed(int iNewValue)
{
m_iNumPopulationEmployed = iNewValue;
}
void CvCity::changeNumPopulationEmployed(int iChange)
{
setNumPopulationEmployed(iChange + getNumPopulationEmployed());
FAssert(getNumPopulationEmployed() >= 0);
}
void CvCity::removeWorstCitizenActualEffects(int iNumCitizens, int& iGreatPeopleRate, int& iHappiness, int& iHealthiness, int*& aiYields, int*& aiCommerces)
{
PROFILE_FUNC();
CvPlot* pLoopPlot;
bool bAvoidGrowth = false;
bool bIgnoreGrowth = false;
int iWorstPlot;
int iValue;
int iWorstValue;
int iI;
int iNumRemoved = 0;
int iNumSpecialistsRemoved = 0;
SpecialistTypes* paeRemovedSpecailists = new SpecialistTypes[iNumCitizens];
for (iI = 0; iI < iNumCitizens; iI++)
{
paeRemovedSpecailists[iI] = NO_SPECIALIST;
}
bool* abRemovedPlots = new bool[NUM_CITY_PLOTS];
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
{
abRemovedPlots[iI] = false;
}
iGreatPeopleRate = 0;
iHappiness = 0;
iHealthiness = 0;
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
aiYields[iI] = 0;
}
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
aiCommerces[iI] = 0;
}
int iGenericSpecialist = GC.getDefineINT("DEFAULT_SPECIALIST");
// if we are using more specialists than the free ones we get
while (getAssignedSpecialistCount() < iNumRemoved && iNumRemoved < iNumCitizens)
{
// does generic 'citizen' specialist exist?
if (iGenericSpecialist != NO_SPECIALIST)
{
// do we have at least one more generic citizen than we are forcing?
if (getSpecialistCount((SpecialistTypes)iGenericSpecialist) > getForceSpecialistCount((SpecialistTypes)iGenericSpecialist))
{
paeRemovedSpecailists[iNumRemoved] = (SpecialistTypes)(iGenericSpecialist);
iNumRemoved++;
iNumSpecialistsRemoved++;
}
}
}
while (iNumRemoved < iNumCitizens)
{
iWorstValue = MAX_INT;
SpecialistTypes eWorstSpecialist = NO_SPECIALIST;
iWorstPlot = -1;
// if we are using more specialists than the free ones we get
if (getAssignedSpecialistCount() < iNumSpecialistsRemoved)
{
for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
{
{
if (getSpecialistCount((SpecialistTypes)iI) > getForceSpecialistCount((SpecialistTypes)iI))
{
iValue = AI_specialistValue(((SpecialistTypes)iI), bAvoidGrowth, /*bRemove*/ true);
if (iValue < iWorstValue)
{
iWorstValue = iValue;
eWorstSpecialist = ((SpecialistTypes)iI);
iWorstPlot = -1;
}
}
}
}
}
// check all the plots we working
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
{
if (iI != CITY_HOME_PLOT)
{
if (isWorkingPlot(iI) && !abRemovedPlots[iI])
{
pLoopPlot = getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
iValue = AI_plotValue(pLoopPlot, bAvoidGrowth, /*bRemove*/ true, /*bIgnoreFood*/ false, bIgnoreGrowth);
if (iValue < iWorstValue)
{
iWorstValue = iValue;
eWorstSpecialist = NO_SPECIALIST;
iWorstPlot = iI;
}
}
}
}
}
if (eWorstSpecialist != NO_SPECIALIST)
{
paeRemovedSpecailists[iNumRemoved] = eWorstSpecialist;
iNumRemoved++;
iNumSpecialistsRemoved++;
}
else if (iWorstPlot != -1)
{
abRemovedPlots[iWorstPlot] = true;
iNumRemoved++;
}
else
{
break;
}
}
for (iI = 0; iI < iNumCitizens; iI++)
{
if (paeRemovedSpecailists[iI] != NO_SPECIALIST)
{
CvSpecialistInfo& kSpecialist = GC.getSpecialistInfo(paeRemovedSpecailists[iI]);
iHappiness -= kSpecialist.getHappinessPercent();
iHealthiness -= kSpecialist.getHealthPercent();
iGreatPeopleRate -= kSpecialist.getGreatPeopleRateChange();
for (int iJ = 0; iJ < NUM_YIELD_TYPES; iJ++)
{
aiYields[iJ] -= GET_PLAYER(getOwnerINLINE()).specialistYield(paeRemovedSpecailists[iI], (YieldTypes)iJ);
}
for (int iJ = 0; iJ < NUM_COMMERCE_TYPES; iJ++)
{
aiCommerces[iJ] -= GET_PLAYER(getOwnerINLINE()).specialistCommerce(paeRemovedSpecailists[iI], (CommerceTypes)iJ);
}
}
}
iHealthiness /= 100;
iHappiness /= 100;
for (int iJ = 0; iJ < NUM_CITY_PLOTS; iJ++)
{
if (abRemovedPlots[iJ])
{
pLoopPlot = getCityIndexPlot(iJ);
for (int iK = 0; iK < NUM_YIELD_TYPES; iK++)
{
aiYields[iK] -= pLoopPlot->getYield((YieldTypes)iK);
}
}
}
SAFE_DELETE_ARRAY(paeRemovedSpecailists);
SAFE_DELETE_ARRAY(abRemovedPlots);
}
int CvCity::calculateExtraCapitalCommerce() const
{
if (m_iExtraCapitalCommerce != 0)
{
if (isCapital() || isConnectedToCapital())
{
return m_iExtraCapitalCommerce;
}
}
return 0;
}
void CvCity::changeExtraCapitalCommerce(int iChange)
{
m_iExtraCapitalCommerce += iChange;
if (iChange != 0)
{
doConnectednessCalculations();
}
}
int CvCity::calculateExtraForeignCapitalCommerce() const
{
if (m_iExtraForeignCapitalCommerce != 0)
{
int iCommerce = 0;
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (iI != getOwnerINLINE() && GET_PLAYER((PlayerTypes)iI).isAlive() && GET_PLAYER((PlayerTypes)iI).getCapitalCity() != NULL)
{
if (isConnectedTo(GET_PLAYER((PlayerTypes)iI).getCapitalCity()))
{
iCommerce += m_iExtraForeignCapitalCommerce;
}
}
}
return iCommerce;
}
return 0;
}
void CvCity::changeExtraForeignCapitalCommerce(int iChange)
{
m_iExtraForeignCapitalCommerce += iChange;
if (iChange != 0)
{
doConnectednessCalculations();
}
}
int CvCity::calculatePopulationHappiness() const
{
return m_iHappinessPercentPerPopulation * getPopulation() / 100;
}
void CvCity::changeHappinessPercentPerPopulation(int iChange)
{
if (iChange != 0)
{
m_iHappinessPercentPerPopulation += iChange;
AI_setAssignWorkDirty(true);
}
}
int CvCity::calculatePopulationHealth() const
{
return m_iHealthPercentPerPopulation * getPopulation() / 100;
}
void CvCity::changeHealthPercentPerPopulation(int iChange)
{
if (iChange != 0)
{
m_iHealthPercentPerPopulation += iChange;
AI_setAssignWorkDirty(true);
}
}
int CvCity::getAssignedSpecialistCount() const
{
int iCount = 0;
for (int iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
{
iCount += getSpecialistCount((SpecialistTypes)iI);
}
return iCount;
}
bool CvCity::isAutomatedCanBuild(BuildTypes eBuild) const
{
return m_pabAutomatedCanBuild[eBuild];
}
void CvCity::setAutomatedCanBuild(BuildTypes eBuild, bool bNewValue)
{
m_pabAutomatedCanBuild[eBuild] = bNewValue;
}
int CvCity::getMintedCommerceTimes100() const
{
int iCommerceTimes100 = 0;
for (int iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
int iBonusCount = m_paiNumBonuses[iI] + m_paiNumCorpProducedBonuses[iI];
iCommerceTimes100 += iBonusCount * GET_PLAYER(getOwnerINLINE()).getBonusMintedPercent((BonusTypes)iI);
}
return iCommerceTimes100;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
void CvCity::clearModifierTotals()
{
int iI;
// If the city is running a process turn it off until the recalc completes
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_MAINTAIN:
processProcess((ProcessTypes)pOrderNode->m_data.iData1, -1);
break;
default:
break;
}
}
// Remove this city from its plot group (without bothering to do any recalculation iteratively)
CvPlotGroup* ownerPlotGroup = plotGroup(getOwnerINLINE());
if ( ownerPlotGroup != NULL )
{
ownerPlotGroup->removePlot(plot(), false);
}
for (iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
if ( isHasCorporation((CorporationTypes)iI) )
{
applyCorporationModifiers((CorporationTypes)iI, false);
}
}
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if ( isHasReligion((ReligionTypes)iI) )
{
applyReligionModifiers((ReligionTypes)iI, false);
}
}
m_iGreatPeopleRateModifier = 0;
m_iGovernmentCenterCount = 0;
m_iMaintenanceModifier = 0;
m_iWarWearinessModifier = 0;
m_iHurryAngerModifier = 0;
m_iHealRate = 0;
m_iBuildingGoodHealth = 0;
m_iBuildingBadHealth = 0;
m_iPowerGoodHealth = 0;
m_iPowerBadHealth = 0;
m_iBonusGoodHealth = 0;
m_iBonusBadHealth = 0;
m_iBuildingGoodHappiness = 0;
m_iBuildingBadHappiness = 0;
m_iExtraBuildingGoodHappiness = 0;
m_iExtraBuildingBadHappiness = 0;
m_iExtraBuildingGoodHealth = 0;
m_iExtraBuildingBadHealth = 0;
m_iBonusGoodHappiness = 0;
m_iBonusBadHappiness = 0;
m_iReligionGoodHappiness = 0;
m_iReligionBadHappiness = 0;
m_iExtraHappiness = 0;
m_iExtraHealth = 0;
m_iNoUnhappinessCount = 0;
m_iNoUnhealthyPopulationCount = 0;
m_iBuildingOnlyHealthyCount = 0;
m_fMaxFoodKeptMultiplierLog = 0.0;
m_iMilitaryProductionModifier = 0;
m_iSpaceProductionModifier = 0;
m_iExtraTradeRoutes = 0;
m_iTradeRouteModifier = 0;
m_iForeignTradeRouteModifier = 0;
m_iBuildingDefense = 0;
m_iBuildingBombardDefense = 0;
m_iFreeExperience = 0;
m_iMaxAirlift = 0;
m_iAirModifier = 0;
m_iAirUnitCapacity = 0;
m_iWonderCapacityIncrement = 0;
m_iNukeModifier = 0;
m_iFreeSpecialist = 0;
m_iPowerCount = 0;
m_iDirtyPowerCount = 0;
m_iSpecialistFreeExperience = 0;
m_iEspionageDefenseModifier = 0;
m_fPopulationgrowthratepercentageLog = 0.0;
m_iImprovementGoodHealth = 0;
m_iImprovementBadHealth = 0;
m_iSpecialistGoodHealth = 0;
m_iSpecialistBadHealth = 0;
m_iSpecialistHappiness = 0;
m_iSpecialistUnhappiness = 0;
m_iLineOfSight = 0;
m_iInvasionChance = 0;
m_iInvasionTimer = 0;
m_iAdjacentDamagePercent = 0;
m_iWorkableRadiusOverride = 0;
m_iProtectedCultureCount = 0;
m_iNumUnitFullHeal = 0;
m_iMinimumDefenseLevel = 0;
m_iHealthPercentPerPopulation = 0;
m_iHappinessPercentPerPopulation = 0;
m_iBaseGreatPeopleRate = 0;
m_iNumPopulationEmployed = 0;
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
m_paiNoBonus[iI] = 0;
m_paiFreeBonus[iI] = 0;
m_paiNumBonuses[iI] = 0;
m_paiNumCorpProducedBonuses[iI] = 0;
}
for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
m_paiNumFreeBuilding[iI] = 0;
m_paiNumFreeAreaBuilding[iI] = 0;
m_paiNumFreeTradeRegionBuilding[iI] = 0;
m_pabDisabledBuilding[iI] = false;
if ( m_paiBuildingReplaced != NULL )
{
m_paiBuildingReplaced[iI] = 0;
}
}
for (iI = 0;iI < GC.getNumUnitInfos();iI++)
{
m_paiGreatPeopleUnitRate[iI] = 0;
}
for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
{
m_paiMaxSpecialistCount[iI] = 0;
m_paiForceSpecialistCount[iI] = 0;
m_paiFreeSpecialistCount[iI] = 0;
}
for (iI = 0; iI < GC.getNumImprovementInfos(); iI++)
{
m_paiImprovementFreeSpecialists[iI] = 0;
}
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
m_paiStateReligionHappiness[iI] = 0;
}
for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
{
m_paiUnitCombatFreeExperience[iI] = 0;
m_paiUnitCombatExtraStrength[iI] = 0;
}
for (iI = 0; iI < GC.getNumPromotionInfos(); iI++)
{
m_paiFreePromotionCount[iI] = 0;
}
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
m_aiSeaPlotYield[iI] = 0;
m_aiRiverPlotYield[iI] = 0;
m_aiBaseYieldRate[iI] = 0;
m_aiYieldRateModifier[iI] = 0;
m_aiPowerYieldRateModifier[iI] = 0;
m_aiBonusYieldRateModifier[iI] = 0;
m_aiTradeYield[iI] = 0;
m_aiCorporationYield[iI] = 0;
m_aiExtraSpecialistYield[iI] = 0;
}
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
m_aiCommerceRate[iI] = 0;
m_abCommerceRateDirty[iI] = false;
m_aiProductionToCommerceModifier[iI] = 0;
m_aiBuildingCommerce[iI] = 0;
m_aiSpecialistCommerce[iI] = 0;
m_aiReligionCommerce[iI] = 0;
m_aiCorporationCommerce[iI] = 0;
m_aiCommerceRateModifier[iI] = 0;
m_aiCommerceHappinessPer[iI] = 0;
/************************************************************************************************/
/* Afforess Start 12/7/09 */
/* */
/* */
/************************************************************************************************/
m_aiBonusCommerceRateModifier[iI] = 0;
m_aiBonusCommercePercentChanges[iI] = 0;
m_aiCommerceAttacks[iI] = 0;
m_aiMaxCommerceAttacks[iI] = 0;
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
for (iI = 0; iI < NUM_DOMAIN_TYPES; iI++)
{
m_aiDomainFreeExperience[iI] = 0;
m_aiDomainProductionModifier[iI] = 0;
}
m_aBuildingYieldChange.clear();
m_aBuildingCommerceChange.clear();
m_aBuildingHappyChange.clear();
m_aBuildingHealthChange.clear();
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
{
m_paiUnitClassProductionModifier[iI] = 0;
}
for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
m_paiBuildingClassProductionModifier[iI] = 0;
}
for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
{
m_paiBonusDefenseChanges[iI] = 0;
}
m_aBuildingCommerceModifier.clear();
m_aBuildingYieldModifier.clear();
//m_Properties.clear();
// Until this city gets to process its buildings
m_recalcBuilding = -1;
// Force isWorkingPlot() to return false for now because we don't
// want chnages to other thuings like traits, adjusting trhe not-yte-set
// city yields based on plots being worked until we explicitly add them back in
m_bPlotWorkingMasked = true;
m_iExtraBuildingDefenseRecoverySpeedModifier = 0;
m_iModifiedBuildingDefenseRecoverySpeedCap = 0;
m_iExtraCityDefenseRecoverySpeedModifier = 0;
}
void CvCity::recalculateModifiers()
{
int iI;
area()->changePower(getOwnerINLINE(), getPopulationPower(getPopulation()));
m_bPlotWorkingMasked = false;
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
{
if ( isWorkingPlot(iI) )
{
processWorkingPlot(iI, 1, true);
}
}
for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
{
int iSpecialistCount = getSpecialistCount((SpecialistTypes)iI);
if ( iSpecialistCount > 0 )
{
processSpecialist((SpecialistTypes)iI, iSpecialistCount);
}
// Add back the unattributed specialists (those direct from Python or
// from GPs that joined the city)
if ( m_paiFreeSpecialistCountUnattributed[iI] != 0 )
{
m_paiFreeSpecialistCount[iI] += m_paiFreeSpecialistCountUnattributed[iI];
processSpecialist((SpecialistTypes)iI, m_paiFreeSpecialistCountUnattributed[iI]);
}
}
for(m_recalcBuilding = 0; m_recalcBuilding < GC.getNumBuildingInfos(); m_recalcBuilding++)
{
if ( getNumRealBuilding((BuildingTypes)m_recalcBuilding) > 0 )
{
// Process back the buildings we physically have. This will generate free
// buildings as it goes. Tech reprocessing will be called later which will
// re-obsolete those that need it
processBuilding((BuildingTypes)m_recalcBuilding, 1);
}
}
// After processing all buildings set the indicator that reprocessing is not in progress any more
m_recalcBuilding = MAX_INT;
// Put corporations back
for (iI = 0; iI < GC.getNumCorporationInfos(); iI++)
{
if ( isHasCorporation((CorporationTypes)iI) )
{
applyCorporationModifiers((CorporationTypes)iI, true);
}
}
updateCorporation();
// Put religions back
for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
{
if ( isHasReligion((ReligionTypes)iI) )
{
applyReligionModifiers((ReligionTypes)iI, true);
}
}
// Replay events in so far as they effect modifiers
for( iI = 0; iI < (int)m_aEventsOccured.size(); iI++ )
{
if ( m_aEventsOccured[iI] != NO_EVENT )
{
applyEvent(m_aEventsOccured[iI], NULL);
}
}
// If the city is running a process acout for it again
CLLNode<OrderData>* pOrderNode = headOrderQueueNode();
if (pOrderNode != NULL)
{
switch (pOrderNode->m_data.eOrderType)
{
case ORDER_MAINTAIN:
processProcess((ProcessTypes)pOrderNode->m_data.iData1, 1);
break;
default:
break;
}
}
updateFreshWaterHealth();
updateFeatureHealth();
updateImprovementHealth();
updateFeatureHappiness();
updatePowerHealth();
//ls612: Make Sure to keep the Air Unit capacity
changeAirUnitCapacity(GC.getDefineINT("CITY_AIR_UNIT_CAPACITY"));
}
void CvCity::setBuildingListInvalid()
{
m_BuildingList.setInvalid();
}
bool CvCity::getBuildingListFilterActive(BuildingFilterTypes eFilter)
{
return m_BuildingList.getFilterActive(eFilter);
}
void CvCity::setBuildingListFilterActive(BuildingFilterTypes eFilter, bool bActive)
{
m_BuildingList.setFilterActive(eFilter, bActive);
}
BuildingGroupingTypes CvCity::getBuildingListGrouping()
{
return m_BuildingList.getGroupingActive();
}
void CvCity::setBuildingListGrouping(BuildingGroupingTypes eGrouping)
{
m_BuildingList.setGroupingActive(eGrouping);
}
BuildingSortTypes CvCity::getBuildingListSorting()
{
return m_BuildingList.getSortingActive();
}
void CvCity::setBuildingListSorting(BuildingSortTypes eSorting)
{
m_BuildingList.setSortingActive(eSorting);
}
int CvCity::getBuildingListGroupNum()
{
return m_BuildingList.getGroupNum();
}
int CvCity::getBuildingListNumInGroup(int iGroup)
{
return m_BuildingList.getNumInGroup(iGroup);
}
BuildingTypes CvCity::getBuildingListType(int iGroup, int iPos)
{
return m_BuildingList.getBuildingType(iGroup, iPos);
}
int CvCity::getBuildingListSelectedBuildingRow()
{
return m_BuildingList.getBuildingSelectionRow();
}
int CvCity::getBuildingListSelectedWonderRow()
{
return m_BuildingList.getWonderSelectionRow();
}
BuildingTypes CvCity::getBuildingListSelectedBuilding()
{
return m_BuildingList.getSelectedBuilding();
}
BuildingTypes CvCity::getBuildingListSelectedWonder()
{
return m_BuildingList.getSelectedWonder();
}
void CvCity::setBuildingListSelectedBuilding(BuildingTypes eBuilding)
{
m_BuildingList.setSelectedBuilding(eBuilding);
}
void CvCity::setBuildingListSelectedWonder(BuildingTypes eWonder)
{
m_BuildingList.setSelectedWonder(eWonder);
}
void CvCity::setUnitListInvalid()
{
m_UnitList.setInvalid();
}
bool CvCity::getUnitListFilterActive(UnitFilterTypes eFilter)
{
return m_UnitList.getFilterActive(eFilter);
}
void CvCity::setUnitListFilterActive(UnitFilterTypes eFilter, bool bActive)
{
m_UnitList.setFilterActive(eFilter, bActive);
}
UnitGroupingTypes CvCity::getUnitListGrouping()
{
return m_UnitList.getGroupingActive();
}
void CvCity::setUnitListGrouping(UnitGroupingTypes eGrouping)
{
m_UnitList.setGroupingActive(eGrouping);
}
UnitSortTypes CvCity::getUnitListSorting()
{
return m_UnitList.getSortingActive();
}
void CvCity::setUnitListSorting(UnitSortTypes eSorting)
{
m_UnitList.setSortingActive(eSorting);
}
int CvCity::getUnitListGroupNum()
{
return m_UnitList.getGroupNum();
}
int CvCity::getUnitListNumInGroup(int iGroup)
{
return m_UnitList.getNumInGroup(iGroup);
}
UnitTypes CvCity::getUnitListType(int iGroup, int iPos)
{
return m_UnitList.getUnitType(iGroup, iPos);
}
int CvCity::getUnitListSelectedRow()
{
return m_UnitList.getSelectionRow();
}
UnitTypes CvCity::getUnitListSelected()
{
return m_UnitList.getSelectedUnit();
}
void CvCity::setUnitListSelected(UnitTypes eUnit)
{
m_UnitList.setSelectedUnit(eUnit);
}
int CvCity::getTotalBuildingSourcedProperty(PropertyTypes eProperty) const
{
std::map<int,int>::const_iterator itr = m_buildingSourcedPropertyCache.find(eProperty);
if ( itr != m_buildingSourcedPropertyCache.end() )
{
return itr->second;
}
else
{
MEMORY_TRACK_EXEMPT();
int iValue = 0;
for(int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
if ( getNumBuilding((BuildingTypes)iI) > 0 )
{
CvPropertyManipulators* pBuildingPropertyManipulators = GC.getBuildingInfo((BuildingTypes)iI).getPropertyManipulators();
int num = pBuildingPropertyManipulators->getNumSources();
for (int iJ = 0; iJ < num; iJ++)
{
CvPropertySource* pSource = pBuildingPropertyManipulators->getSource(iJ);
// For now we're only interested in constant sources
// TODO - expand this as buildings add other types
if ( pSource->getType() == PROPERTYSOURCE_CONSTANT && pSource->getProperty() == eProperty)
{
iValue += ((CvPropertySourceConstant*)pSource)->getAmountPerTurn(getGameObjectConst());
}
}
}
}
m_buildingSourcedPropertyCache[(int)eProperty] = iValue;
return iValue;
}
}
void unitSources(CvGameObject* pObject, CvPropertyManipulators* pMani, PropertyTypes eProperty, const CvCity* pCity, int* iValue)
{
int iNum = pMani->getNumSources();
for (int i=0; i<iNum; i++)
{
CvPropertySource* pSource = pMani->getSource(i);
// Sources that deliver to the city or the plot are both considered since the city plot diffuses
// to the city for most properties anyway
if (pSource->getProperty() == eProperty &&
(pSource->getObjectType() == GAMEOBJECT_CITY || pSource->getObjectType() == GAMEOBJECT_PLOT) &&
pSource->getType() == PROPERTYSOURCE_CONSTANT)
{
*iValue += ((CvPropertySourceConstant*)pSource)->getAmountPerTurn(pCity->getGameObjectConst());
}
}
}
int CvCity::getTotalUnitSourcedProperty(PropertyTypes eProperty) const
{
std::map<int,int>::const_iterator itr = m_unitSourcedPropertyCache.find(eProperty);
if ( itr != m_unitSourcedPropertyCache.end() )
{
return itr->second;
}
else
{
MEMORY_TRACK_EXEMPT();
int iValue = 0;
CLLNode<IDInfo>* pUnitNode = plot()->headUnitNode();
while (NULL != pUnitNode)
{
CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = plot()->nextUnitNode(pUnitNode);
pLoopUnit->getGameObject()->foreachManipulator(boost::bind(unitSources, _1, _2, eProperty, this, &iValue));
}
m_unitSourcedPropertyCache[(int)eProperty] = iValue;
return iValue;
}
}
void unitHasSources(CvGameObject* pObject, CvPropertyManipulators* pMani, bool* bHasSources)
{
int iNum = pMani->getNumSources();
for (int i=0; i<iNum; i++)
{
CvPropertySource* pSource = pMani->getSource(i);
// Sources that deliver to the city or the plot are both considered since the city plot diffuses
// to the city for most properties anyway
if ((pSource->getObjectType() == GAMEOBJECT_CITY || pSource->getObjectType() == GAMEOBJECT_PLOT) &&
pSource->getType() == PROPERTYSOURCE_CONSTANT)
{
*bHasSources = true;
break;
}
}
}
// Helper function to determine of a unit has any city/plot property sources
static bool unitHasCityOrPlotPropertySources(CvUnit* pUnit)
{
bool bHasSources;
pUnit->getGameObject()->foreachManipulator(boost::bind(unitHasSources, _1, _2, &bHasSources));
return bHasSources;
}
void CvCity::noteUnitMoved(CvUnit* pUnit) const
{
if ( unitHasCityOrPlotPropertySources( pUnit ) )
{
m_unitSourcedPropertyCache.clear();
}
}
void sumCitySources(CvGameObject* pObject, CvPropertyManipulators* pMani, const CvCity* pCity, int* iSum, PropertyTypes eProperty)
{
int iNum = pMani->getNumSources();
for (int i=0; i<iNum; i++)
{
CvPropertySource* pSource = pMani->getSource(i);
if (pSource->getProperty() == eProperty)
{
if (pSource->isActive(const_cast<CvGameObjectCity*>(pCity->getGameObjectConst())))
{
*iSum += pSource->getSourcePredict(pCity->getGameObjectConst(), pCity->getPropertiesConst()->getValueByProperty((int)eProperty));
}
}
}
}
int CvCity::getGlobalSourcedProperty(PropertyTypes eProperty) const
{
int iSum = 0;
CvPropertyManipulators* pMani = GC.getPropertyInfo(eProperty).getPropertyManipulators();
int iNum = pMani->getNumSources();
for (int i=0; i<iNum; i++)
{
CvPropertySource* pSource = pMani->getSource(i);
iSum += pSource->getSourcePredict(getGameObjectConst(), getPropertiesConst()->getValueByProperty((int)eProperty));
}
return iSum;
}
int CvCity::cityDefenseRecoveryRate() const
{
int iValue = GC.getDefineINT("CITY_DEFENSE_DAMAGE_HEAL_RATE");
int iRecoveryModifier = getExtraCityDefenseRecoverySpeedModifier();
if (getDefenseModifier(false) < getModifiedBuildingDefenseRecoverySpeedCap())
{
iRecoveryModifier += getExtraBuildingDefenseRecoverySpeedModifier();
}
iValue *= (100 + iRecoveryModifier);
iValue /= 100;
return iValue;
}
int CvCity::getExtraBuildingDefenseRecoverySpeedModifier() const
{
return m_iExtraBuildingDefenseRecoverySpeedModifier;
}
int CvCity::getModifiedBuildingDefenseRecoverySpeedCap() const
{
return m_iModifiedBuildingDefenseRecoverySpeedCap;
}
int CvCity::getExtraCityDefenseRecoverySpeedModifier() const
{
return m_iExtraCityDefenseRecoverySpeedModifier;
}
int CvCity::getExtraSpecialistCommerce(CommerceTypes eIndex, SpecialistTypes eSpecialist) const
{
FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex expected to be < NUM_COMMERCE_TYPES");
FAssertMsg(eSpecialist >= 0, "eSpecialist expected to be >= 0");
FAssertMsg(eSpecialist < GC.getNumSpecialistInfos(), "GC.getNumSpecialistInfos expected to be >= 0");
return ((getSpecialistCount(eSpecialist) + getFreeSpecialistCount(eSpecialist)) * GET_PLAYER(getOwnerINLINE()).getExtraSpecialistCommerce(eSpecialist, eIndex));
}
void CvCity::updateExtraSpecialistCommerce()
{
setCommerceDirty();
if (getTeam() == GC.getGameINLINE().getActiveTeam())
{
setInfoDirty(true);
if (isCitySelected())
{
gDLL->getInterfaceIFace()->setDirty(CityScreen_DIRTY_BIT, true);
gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true );
}
}
}
int CvCity::getExtraSpecialistCommerceTotal(CommerceTypes eIndex) const
{
int iI;
int iSum = 0;
for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
{
SpecialistTypes eSpecialist = ((SpecialistTypes)iI);
iSum += getExtraSpecialistCommerce(eIndex, eSpecialist);
}
return iSum;
}
int CvCity::getCitySorenRandNum(int iNum, TCHAR* pszLog)
{
if ( m_workItem != NULL )
{
return m_workItem->GetSyncRand(iNum, pszLog);
}
else
{
return GC.getGameINLINE().getSorenRandNum(iNum, pszLog);
}
}
int CvCity::getRandNum100()
{
return GC.getGameINLINE().getSorenRandNum(100, "Random number for 100");
}
bool CvCity::isDirectAttackable() const
{
int iMinimumDefenseLevel = getMinimumDefenseLevel();
if (iMinimumDefenseLevel == 0)
{
iMinimumDefenseLevel = MAX_INT;
}
//ls612 Quickfix: Cities can be attacked if they have no defenders, regardless of if there is a minimun defense level
if (getDefenseModifier(false) > iMinimumDefenseLevel && plot()->getNumDefenders(getOwnerINLINE()) > 0)
{
return false;
}
return true;
}
void CvCity::recalculateZoomLevel(CultureLevelTypes eCultureLevel)
{
if (eCultureLevel != NO_CULTURELEVEL)
{
if (GC.getCultureLevelInfo(eCultureLevel).getCityRadius() > 2)
{
float fZoom = GC.getDefineFLOAT("CAMERA_CITY_ZOOM_IN_DISTANCE");
if (fZoom < GC.getDefineFLOAT("3RADIUS_CAMERA_CITY_ZOOM_IN_DISTANCE", 5300))
{
GC.setDefineFLOAT("CAMERA_CITY_ZOOM_IN_DISTANCE", GC.getDefineFLOAT("3RADIUS_CAMERA_CITY_ZOOM_IN_DISTANCE", 5300));
}
}
}
}
void CvCity::clearSeizedForeignConnectedness()
{
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
m_aiSeizedForeignConnectedness[iI] = 0;
}
}
void CvCity::updateSeizedForeignConnectedness()
{
int iPreviousSeizedTimes100 = getTotalSeizedForeignConnectednessTimes100();
if (isDisorder() || (getForeignConnectednessCommerce() + getDomesticConnectednessCommerce()) == 0)
{
clearSeizedForeignConnectedness();
changeBaseYieldRate(YIELD_COMMERCE, iPreviousSeizedTimes100 / 100);
return;
}
int iCount = 0;
int aiSeizedCommercePercent[MAX_PLAYERS];
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
aiSeizedCommercePercent[iI] = 0;
if (GET_PLAYER(getOwnerINLINE()).isCanSeizeForeignConnectednessFromUs((PlayerTypes)iI))
{
aiSeizedCommercePercent[iI] = GET_PLAYER((PlayerTypes)iI).getSeizedForeignConnectednessPercent();
iCount++;
}
}
clearSeizedForeignConnectedness();
if (iCount > 0)
{
//MaxPercentSeized is the largest % share seized by any one player
int iMaxPercentSeized = 0;
//Total accumulated percent seized, by all players
int iTotalPercentSeized = 0;
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
iTotalPercentSeized += aiSeizedCommercePercent[iI];
if (aiSeizedCommercePercent[iI] > iMaxPercentSeized)
{
iMaxPercentSeized = aiSeizedCommercePercent[iI];
}
}
int iSeizedCommerceTimes100 = (getForeignConnectednessCommerce() + getDomesticConnectednessCommerce()) * iMaxPercentSeized;
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
m_aiSeizedForeignConnectedness[iI] = 0;
if (aiSeizedCommercePercent[iI] != 0)
{
//If multiple players are seizing commerce from this player, they get a share of the commerce
// proportional to the number of players and the amount they seized
int iSeized = iSeizedCommerceTimes100 * aiSeizedCommercePercent[iI] / 100;
m_aiSeizedForeignConnectedness[iI] = iSeized / iCount;
}
}
}
changeBaseYieldRate(YIELD_COMMERCE, (iPreviousSeizedTimes100 / 100) - (getTotalSeizedForeignConnectednessTimes100() / 100));
}
int CvCity::getSeizedForeignConnectednessTimes100(PlayerTypes ePlayer) const
{
return m_aiSeizedForeignConnectedness ? m_aiSeizedForeignConnectedness[ePlayer] : 0;
}
int CvCity::getTotalSeizedForeignConnectednessTimes100() const
{
int iTotal = 0;
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
iTotal += getSeizedForeignConnectednessTimes100((PlayerTypes)iI);
}
return iTotal;
}
int CvCity::getForcedRevolutionCounter() const
{
return m_iForcedRevolutionCounter;
}
void CvCity::changeForcedRevolutionCounter(int iChange)
{
m_iForcedRevolutionCounter += iChange;
}
void CvCity::doForcedRevolutionUpdate()
{
if (m_iForcedRevolutionCounter > 0)
{
int iExtraUnrest = GC.getDefineINT("INCITE_CITY_REVOLT_MISSION_REVINDEX_PER_TURN", 240);
iExtraUnrest *= 100;
iExtraUnrest /= GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getTrainPercent();
m_iForcedRevolutionCounter--;
}
}
void CvCity::distributeUnitsOverFreeTiles()
{ // xUPT: check city unit number over the limit then distribute these around free plots (dbkblk, 2015-02)
if (GC.getGameINLINE().getModderGameOption(MODDERGAMEOPTION_MAX_UNITS_PER_TILES) > 0)
{
CLLNode<IDInfo>* pUnitNode;
CvUnit* pLoopUnit;
// Initialize variables
int iCityLimit = GC.getGameINLINE().getModderGameOption(MODDERGAMEOPTION_MAX_UNITS_PER_TILES) * GC.getDefineINT("UNITS_PER_TILES_CITY_FACTOR", 3);
int iMilitaryLandUnits = this->plot()->getNumMilitaryLandUnits(this->getOwner());
int iMilitaryAirUnits = this->plot()->getNumMilitaryAirUnits(this->getOwner());
int iMilitaryNavalUnits = this->plot()->getNumMilitaryNavalUnits(this->getOwner());
// For each type, move the last unit first until the sum equals the limit
if (iMilitaryLandUnits > iCityLimit)
{
// Loop while over the limit
while (iMilitaryLandUnits > iCityLimit)
{
pUnitNode = this->plot()->tailUnitNode();
pLoopUnit = ::getUnit(pUnitNode->m_data);
// Loop while getting a military land unit
while (!pLoopUnit->isMilitaryLandUnit())
{
pUnitNode = this->plot()->prevUnitNode(pUnitNode);
pLoopUnit = ::getUnit(pUnitNode->m_data);
}
pLoopUnit->jumpToNearestValidPlot(true);
iMilitaryLandUnits--;
}
}
if (iMilitaryNavalUnits > iCityLimit)
{
// Loop while over the limit
while (iMilitaryNavalUnits > iCityLimit)
{
pUnitNode = this->plot()->tailUnitNode();
pLoopUnit = ::getUnit(pUnitNode->m_data);
while (!pLoopUnit->isMilitaryNavalUnit())
{
pUnitNode = this->plot()->prevUnitNode(pUnitNode);
pLoopUnit = ::getUnit(pUnitNode->m_data);
}
pLoopUnit->jumpToNearestValidPlot(true);
iMilitaryNavalUnits--;
}
}
// Do not check on civilians, we do not push civilians out of a city.
// Set the saturation state to a variable to speed things up.
if (isAreaSaturatedOfLandMilitaryUnits())
{
m_bAreaSaturatedOfLandMilitaryUnits = true;
}
else
{
m_bAreaSaturatedOfLandMilitaryUnits = false;
}
if (isAreaSaturatedOfCivilianUnits())
{
m_bAreaSaturatedOfCivilianUnits = true;
}
else
{
m_bAreaSaturatedOfCivilianUnits = false;
}
}
else{ // Set saturation state to false if the option is disabled
m_bAreaSaturatedOfLandMilitaryUnits = false;
m_bAreaSaturatedOfCivilianUnits = false;
}
}