From 458b8e7da33b056dc0b7c1c55823e395375862d4 Mon Sep 17 00:00:00 2001 From: KungCheops Date: Wed, 5 Jun 2024 23:48:08 +0200 Subject: [PATCH] Worker AI tweaks (#10954) Workers will now consider tiles safe if there's a friendly military unit that's able to defend it. Improvement planner AI will now: * try to put GP improvements where no other good improvements can be put, * take not yet unlocked improvements into account when planning improvements, * take chop production into account, * be better in general at planning GP improvements, no-two-adjacent improvements, and two-same-adjacent buffing improvements. Should now be better at taking future city connections into account when planning improvements. Slightly reduce value of food when evaluating improvements. Don't plan improvements on antiquity sites. Workers now less eager to build improvements far away. --- .../Core Files/Core Values/CoreDefines.sql | 2 +- .../CvBuilderTaskingAI.cpp | 709 +++++++++--------- CvGameCoreDLL_Expansion2/CvBuilderTaskingAI.h | 37 +- CvGameCoreDLL_Expansion2/CvGlobals.cpp | 2 +- CvGameCoreDLL_Expansion2/CvHomelandAI.cpp | 115 ++- CvGameCoreDLL_Expansion2/CvPlot.cpp | 127 +++- CvGameCoreDLL_Expansion2/CvPlot.h | 13 +- CvGameCoreDLL_Expansion2/CvUnit.cpp | 4 +- CvGameCoreDLL_Expansion2/CvUnit.h | 2 +- CvGameCoreDLL_Expansion2/Lua/CvLuaPlot.cpp | 8 +- 10 files changed, 577 insertions(+), 442 deletions(-) diff --git a/(1) Community Patch/Core Files/Core Values/CoreDefines.sql b/(1) Community Patch/Core Files/Core Values/CoreDefines.sql index 62fecc0e98..b3082d50e0 100644 --- a/(1) Community Patch/Core Files/Core Values/CoreDefines.sql +++ b/(1) Community Patch/Core Files/Core Values/CoreDefines.sql @@ -206,7 +206,7 @@ UPDATE Defines SET Value = 1000 WHERE Name = 'BUILDER_TASKING_BASELINE_REPAIR'; UPDATE Defines SET Value = 750 WHERE Name = 'BUILDER_TASKING_BASELINE_BUILD_ROUTES'; UPDATE Defines SET Value = 300 WHERE Name = 'BUILDER_TASKING_BASELINE_BUILD_RESOURCE_IMPROVEMENTS'; UPDATE Defines SET Value = 100 WHERE Name = 'BUILDER_TASKING_BASELINE_BUILD_IMPROVEMENTS'; -INSERT INTO Defines (Name, Value) SELECT 'BUILDER_TASKING_BASELINE_ADDS_FOOD', 200; +INSERT INTO Defines (Name, Value) SELECT 'BUILDER_TASKING_BASELINE_ADDS_FOOD', 180; INSERT INTO Defines (Name, Value) SELECT 'BUILDER_TASKING_BASELINE_ADDS_GOLD', 40; INSERT INTO Defines (Name, Value) SELECT 'BUILDER_TASKING_BASELINE_ADDS_FAITH', 150; INSERT INTO Defines (Name, Value) SELECT 'BUILDER_TASKING_BASELINE_ADDS_PRODUCTION', 200; diff --git a/CvGameCoreDLL_Expansion2/CvBuilderTaskingAI.cpp b/CvGameCoreDLL_Expansion2/CvBuilderTaskingAI.cpp index 1bd824ca4e..dde9846551 100644 --- a/CvGameCoreDLL_Expansion2/CvBuilderTaskingAI.cpp +++ b/CvGameCoreDLL_Expansion2/CvBuilderTaskingAI.cpp @@ -47,13 +47,13 @@ void CvBuilderTaskingAI::Init(CvPlayer* pPlayer) // if we get a unique improvement that needs a certain feature, leave that feature until tech is researched for (int i = 0; i < NUM_FEATURE_TYPES; i++) { - m_aeSaveFeatureForImprovementUntilTech[i] = make_pair(NO_IMPROVEMENT,NO_TECH); + m_aeUniqueFeatureImprovement[i] = NO_IMPROVEMENT; } // special case to evaluate plots adjacent to city - m_eSaveCityAdjacentForImprovement = NO_IMPROVEMENT; - m_eSaveCoastalForImprovement = NO_IMPROVEMENT; - m_eSaveHillsForImprovement = NO_IMPROVEMENT; + m_eUniqueCityAdjacentImprovement = NO_IMPROVEMENT; + m_eUniqueCostalImprovement = NO_IMPROVEMENT; + m_eUniqueHillImprovement = NO_IMPROVEMENT; CivilizationTypes eCiv = m_pPlayer->getCivilizationType(); @@ -87,20 +87,20 @@ void CvBuilderTaskingAI::Init(CvPlayer* pPlayer) { if (pkImprovementInfo->GetFeatureMakesValid(i)) { - m_aeSaveFeatureForImprovementUntilTech[i] = make_pair(eImprovement, (TechTypes)pkBuild->getTechPrereq()); + m_aeUniqueFeatureImprovement[i] = eImprovement; } } if (pkImprovementInfo->IsAdjacentCity()) { - m_eSaveCityAdjacentForImprovement = eImprovement; + m_eUniqueCityAdjacentImprovement = eImprovement; } if (pkImprovementInfo->IsCoastal()) { - m_eSaveCoastalForImprovement = eImprovement; + m_eUniqueCostalImprovement = eImprovement; } if (pkImprovementInfo->IsHillsMakesValid()) { - m_eSaveHillsForImprovement = eImprovement; + m_eUniqueHillImprovement = eImprovement; } } } @@ -118,10 +118,10 @@ void CvBuilderTaskingAI::Uninit(void) m_eFalloutRemove = NO_BUILD; for (int i = 0; i < NUM_FEATURE_TYPES; i++) - m_aeSaveFeatureForImprovementUntilTech[i] = make_pair(NO_IMPROVEMENT, NO_TECH); - m_eSaveCityAdjacentForImprovement = NO_IMPROVEMENT; - m_eSaveCoastalForImprovement = NO_IMPROVEMENT; - m_eSaveHillsForImprovement = NO_IMPROVEMENT; + m_aeUniqueFeatureImprovement[i] = NO_IMPROVEMENT; + m_eUniqueCityAdjacentImprovement = NO_IMPROVEMENT; + m_eUniqueCostalImprovement = NO_IMPROVEMENT; + m_eUniqueHillImprovement = NO_IMPROVEMENT; } template @@ -131,10 +131,10 @@ void CvBuilderTaskingAI::Serialize(BuilderTaskingAI& builderTaskingAI, Visitor& visitor(builderTaskingAI.m_plotRoutePurposes); visitor(builderTaskingAI.m_anyRoutePlanned); visitor(builderTaskingAI.m_canalWantedPlots); - visitor(builderTaskingAI.m_aeSaveFeatureForImprovementUntilTech); - visitor(builderTaskingAI.m_eSaveCityAdjacentForImprovement); - visitor(builderTaskingAI.m_eSaveCoastalForImprovement); - visitor(builderTaskingAI.m_eSaveHillsForImprovement); + visitor(builderTaskingAI.m_aeUniqueFeatureImprovement); + visitor(builderTaskingAI.m_eUniqueCityAdjacentImprovement); + visitor(builderTaskingAI.m_eUniqueCostalImprovement); + visitor(builderTaskingAI.m_eUniqueHillImprovement); } /// Serialization read @@ -331,7 +331,7 @@ static int GetYieldBaseModifierTimes100(YieldTypes eYield) case NO_YIELD: UNREACHABLE(); case YIELD_FOOD: - return /*200*/ GD_INT_GET(BUILDER_TASKING_BASELINE_ADDS_FOOD); + return /*180*/ GD_INT_GET(BUILDER_TASKING_BASELINE_ADDS_FOOD); case YIELD_PRODUCTION: return /*200*/ GD_INT_GET(BUILDER_TASKING_BASELINE_ADDS_PRODUCTION); case YIELD_GOLD: @@ -1017,71 +1017,25 @@ bool CvBuilderTaskingAI::WillNeverBuildVillageOnPlot(CvPlot* pPlot, RouteTypes e ImprovementTypes CvBuilderTaskingAI::SavePlotForUniqueImprovement(CvPlot* pPlot) const { FeatureTypes eFeature = pPlot->getFeatureType(); - ImprovementTypes eSaveForImprovement = NO_IMPROVEMENT; - - if (eFeature != NO_FEATURE && m_aeSaveFeatureForImprovementUntilTech[eFeature].second != NO_TECH && pPlot->canHaveImprovement(m_aeSaveFeatureForImprovementUntilTech[eFeature].first, m_pPlayer->GetID())) - eSaveForImprovement = m_aeSaveFeatureForImprovementUntilTech[eFeature].first; - else if (m_eSaveCityAdjacentForImprovement != NO_IMPROVEMENT && pPlot->IsAdjacentCity() && pPlot->canHaveImprovement(m_eSaveCityAdjacentForImprovement, m_pPlayer->GetID())) - eSaveForImprovement = m_eSaveCityAdjacentForImprovement; - else if (m_eSaveCoastalForImprovement != NO_IMPROVEMENT && pPlot->isCoastalLand() && pPlot->canHaveImprovement(m_eSaveCoastalForImprovement, m_pPlayer->GetID())) - eSaveForImprovement = m_eSaveCoastalForImprovement; - else if (m_eSaveHillsForImprovement != NO_IMPROVEMENT && pPlot->getPlotType() == PLOT_HILLS && pPlot->canHaveImprovement(m_eSaveHillsForImprovement, m_pPlayer->GetID())) - eSaveForImprovement = m_eSaveHillsForImprovement; - - // Adjacency checks are not done in canHaveImprovement, need to do them here - CvImprovementEntry* pkImprovementInfo = GC.getImprovementInfo(eSaveForImprovement); - if (pkImprovementInfo) - { - bool bHasLuxuryRequirement = pkImprovementInfo->IsAdjacentLuxury(); - bool bHasNoAdjacencyRequirement = pkImprovementInfo->IsNoTwoAdjacent(); - if (pkImprovementInfo && (bHasLuxuryRequirement || bHasNoAdjacencyRequirement)) - { - bool bLuxuryRequirementMet = !bHasLuxuryRequirement; - for (int iI = 0; iI < NUM_DIRECTION_TYPES; ++iI) - { - CvPlot* pAdjacentPlot = plotDirection(pPlot->getX(), pPlot->getY(), ((DirectionTypes)iI)); - if (pAdjacentPlot != NULL) - { - if (bHasLuxuryRequirement) - { - ResourceTypes eResource = pAdjacentPlot->getResourceType(m_pPlayer->getTeam()); - if (eResource != NO_RESOURCE) - { - CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource); - if (pkResourceInfo && pkResourceInfo->getResourceUsage() == RESOURCEUSAGE_LUXURY) - { - bLuxuryRequirementMet = true; - } - } - } - if (bHasNoAdjacencyRequirement) - { - ImprovementTypes eAdjacentImprovement = pAdjacentPlot->getImprovementType(); - if (eAdjacentImprovement != NO_IMPROVEMENT && eAdjacentImprovement == eSaveForImprovement) - { - return NO_IMPROVEMENT; - } - CvImprovementEntry* pkImprovement2 = GC.getImprovementInfo(eAdjacentImprovement); - if (pkImprovement2 && eAdjacentImprovement != NO_IMPROVEMENT && pkImprovement2->GetImprovementMakesValid(eSaveForImprovement)) - { - return NO_IMPROVEMENT; - } - int iBuildProgress = pAdjacentPlot->getBuildProgress(GetBuildTypeFromImprovement(eSaveForImprovement)); - if (iBuildProgress > 0) - { - return NO_IMPROVEMENT; - } - } - } - } - if (bHasLuxuryRequirement && !bLuxuryRequirementMet) - { - return NO_IMPROVEMENT; - } - } + + if (eFeature != NO_FEATURE && pPlot->canHaveImprovement(m_aeUniqueFeatureImprovement[eFeature], m_pPlayer->GetID(), false, true)) + { + return m_aeUniqueFeatureImprovement[eFeature]; + } + else if (m_eUniqueCityAdjacentImprovement != NO_IMPROVEMENT && pPlot->IsAdjacentCity() && pPlot->canHaveImprovement(m_eUniqueCityAdjacentImprovement, m_pPlayer->GetID(), false, true)) + { + return m_eUniqueCityAdjacentImprovement; + } + else if (m_eUniqueCostalImprovement != NO_IMPROVEMENT && pPlot->isCoastalLand() && pPlot->canHaveImprovement(m_eUniqueCostalImprovement, m_pPlayer->GetID(), false, true)) + { + return m_eUniqueCostalImprovement; + } + else if (m_eUniqueHillImprovement != NO_IMPROVEMENT && pPlot->getPlotType() == PLOT_HILLS && pPlot->canHaveImprovement(m_eUniqueHillImprovement, m_pPlayer->GetID(), false, true)) + { + return m_eUniqueHillImprovement; } - return eSaveForImprovement; + return NO_IMPROVEMENT; } void CvBuilderTaskingAI::UpdateCanalPlots() @@ -1167,14 +1121,14 @@ void CvBuilderTaskingAI::SetAssignedDirective(CvUnit* pUnit, BuilderDirective eD m_assignedDirectives[pUnit->GetID()] = eDirective; } -bool CvBuilderTaskingAI::CanUnitPerformDirective(CvUnit* pUnit, BuilderDirective eDirective) +bool CvBuilderTaskingAI::CanUnitPerformDirective(CvUnit* pUnit, BuilderDirective eDirective, bool bTestEra) { CvPlot* pTargetPlot = GC.getMap().plot(eDirective.m_sX, eDirective.m_sY); if (!ShouldBuilderConsiderPlot(pUnit, pTargetPlot)) return false; - if (!pUnit->canBuild(pTargetPlot, eDirective.m_eBuild)) + if (!pUnit->canBuild(pTargetPlot, eDirective.m_eBuild, false, true, bTestEra)) return false; return true; @@ -1732,6 +1686,190 @@ vector> CvBuilderTaskingAI::GetRouteDirectives return aDirectives; } +static vector> FilterNonOptimalNoTwoAdjacentImprovements(vector> aDirectives, bool bIgnoreNoTwoAdjacent) +{ + vector> aNewDirectives; + + for (vector>::const_iterator it = aDirectives.begin(); it != aDirectives.end(); ++it) + { + BuildTypes eBuild = it->option.m_eBuild; + CvBuildInfo* pkBuild = GC.getBuildInfo(eBuild); + + if (!pkBuild) + { + aNewDirectives.push_back(*it); + continue; + } + + ImprovementTypes eImprovement = (ImprovementTypes)pkBuild->getImprovement(); + CvImprovementEntry* pkImprovementInfo = GC.getImprovementInfo(eImprovement); + + if (!pkImprovementInfo || !pkImprovementInfo->IsNoTwoAdjacent()) + { + aNewDirectives.push_back(*it); + continue; + } + + bool bInclude = true; + for (vector>::const_iterator it2 = aDirectives.begin(); it2 != aDirectives.end(); ++it2) + { + if (it->option == it2->option) + continue; + + if (it->option.m_sX != it2->option.m_sX || it->option.m_sY != it2->option.m_sY) + continue; + + if (it->score > it2->score) + continue; + + BuildTypes eOtherBuild = it2->option.m_eBuild; + CvBuildInfo* pkOtherBuild = GC.getBuildInfo(eOtherBuild); + + ImprovementTypes eOtherImprovement = (ImprovementTypes)pkOtherBuild->getImprovement(); + if (eOtherImprovement == NO_IMPROVEMENT) + continue; + + CvImprovementEntry* pkOtherImprovementInfo = GC.getImprovementInfo(eOtherImprovement); + if (!pkOtherImprovementInfo || pkOtherImprovementInfo->IsCreatedByGreatPerson() || (bIgnoreNoTwoAdjacent && pkOtherImprovementInfo->IsNoTwoAdjacent())) + continue; + + bInclude = false; + break; + } + + if (bInclude) + aNewDirectives.push_back(*it); + } + + return aNewDirectives; +} + +static vector> FilterNoTwoAdjacentDirectives(vector> aDirectives) +{ + vector> aNewDirectives; + vector> aNoTwoAdjacentDirectives; + for (vector>::iterator it = aDirectives.begin(); it != aDirectives.end(); ++it) + { + BuildTypes eBuild = it->option.m_eBuild; + CvBuildInfo* pkBuild = GC.getBuildInfo(eBuild); + + ImprovementTypes eImprovement = (ImprovementTypes)pkBuild->getImprovement(); + if (eImprovement == NO_IMPROVEMENT) + { + aNewDirectives.push_back(*it); + continue; + } + + CvImprovementEntry* pkImprovementInfo = GC.getImprovementInfo(eImprovement); + if (!pkImprovementInfo || !pkImprovementInfo->IsNoTwoAdjacent()) + { + aNewDirectives.push_back(*it); + continue; + } + + // Reduce score if there are many adjacent tiles where we may want to build the same improvement + CvPlot* pPlot = GC.getMap().plot(it->option.m_sX, it->option.m_sY); + int iNeighboringSameScore = 0; + for (vector>::iterator it2 = aDirectives.begin(); it2 != aDirectives.end(); ++it2) + { + if (it->option == it2->option) + continue; + + if (it->option.m_eBuild != it2->option.m_eBuild) + continue; + + CvPlot* pOtherPlot = GC.getMap().plot(it2->option.m_sX, it2->option.m_sY); + if (plotDistance(*pPlot, *pOtherPlot) != 1) + continue; + + iNeighboringSameScore += it2->option.m_iScore; + } + + int iNewScore = it->option.m_iScore - iNeighboringSameScore; + it->score = iNewScore; + + aNoTwoAdjacentDirectives.push_back(*it); + } + + std::stable_sort(aNoTwoAdjacentDirectives.begin(), aNoTwoAdjacentDirectives.end()); + + // filter out non-optimal no-two-adjacent improvements + for (vector>::const_iterator it = aNoTwoAdjacentDirectives.begin(); it != aNoTwoAdjacentDirectives.end(); ++it) + { + OptionWithScore bestDirectiveWithScore = *it; + bestDirectiveWithScore.score = bestDirectiveWithScore.option.GetScore(); + CvPlot* pPlot = GC.getMap().plot(bestDirectiveWithScore.option.m_sX, bestDirectiveWithScore.option.m_sY); + bool bAddDirective = true; + + for (vector>::const_iterator it2 = aNewDirectives.begin(); it2 != aNewDirectives.end(); ++it2) + { + if (it->option.m_eBuild != it2->option.m_eBuild) + continue; + + if (it->score > it2->score) + continue; + + CvPlot* pOtherPlot = GC.getMap().plot(it2->option.m_sX, it2->option.m_sY); + if (plotDistance(*pPlot, *pOtherPlot) != 1) + continue; + + bAddDirective = false; + break; + } + + if (bAddDirective) + { + aNewDirectives.push_back(bestDirectiveWithScore); + } + } + + return aNewDirectives; +} + +static void UpdateGreatPersonDirectives(vector>& aDirectives) +{ + for (vector>::iterator it = aDirectives.begin(); it != aDirectives.end(); ++it) + { + BuildTypes eBuild = it->option.m_eBuild; + CvBuildInfo* pkBuild = GC.getBuildInfo(eBuild); + + ImprovementTypes eImprovement = (ImprovementTypes)pkBuild->getImprovement(); + if (eImprovement == NO_IMPROVEMENT) + continue; + + CvImprovementEntry* pkImprovementInfo = GC.getImprovementInfo(eImprovement); + if (!pkImprovementInfo || !pkImprovementInfo->IsCreatedByGreatPerson()) + continue; + + int iBestScoreInPlot = 0; + for (vector>::const_iterator it2 = aDirectives.begin(); it2 != aDirectives.end(); ++it2) + { + if (it->option == it2->option) + continue; + + if (it->option.m_sX != it2->option.m_sX || it->option.m_sY != it2->option.m_sY) + continue; + + BuildTypes eOtherBuild = it2->option.m_eBuild; + CvBuildInfo* pkOtherBuild = GC.getBuildInfo(eOtherBuild); + + ImprovementTypes eOtherImprovement = (ImprovementTypes)pkOtherBuild->getImprovement(); + if (eOtherImprovement == NO_IMPROVEMENT) + continue; + + CvImprovementEntry* pkOtherImprovementInfo = GC.getImprovementInfo(eOtherImprovement); + if (!pkOtherImprovementInfo || pkOtherImprovementInfo->IsCreatedByGreatPerson()) + continue; + + if (it2->score > iBestScoreInPlot) + iBestScoreInPlot = it2->score; + } + + it->option.m_iScorePenalty = iBestScoreInPlot; + it->score = it->option.GetScore(); + } +} + vector> CvBuilderTaskingAI::GetImprovementDirectives() { PlayerTypes ePlayer = m_pPlayer->GetID(); @@ -1743,7 +1881,7 @@ vector> CvBuilderTaskingAI::GetImprovementDire { BuildTypes eBuild = (BuildTypes)iBuildIndex; - if (!m_pPlayer->canBuild(NULL, eBuild)) + if (!m_pPlayer->canBuild(NULL, eBuild, true)) continue; int iLoopUnit = 0; @@ -1794,7 +1932,6 @@ vector> CvBuilderTaskingAI::GetImprovementDire { UpdateCurrentPlotYields(pPlot); - AddImprovingResourcesDirective(aDirectives, pPlot, pWorkingCity, aPossibleBuilds, iBestScore); AddImprovingPlotsDirective(aDirectives, pPlot, pWorkingCity, aPossibleBuilds, iBestScore); AddScrubFalloutDirectives(aDirectives, pPlot, pWorkingCity); AddRepairImprovementDirective(aDirectives, pPlot, pWorkingCity); @@ -1815,100 +1952,13 @@ vector> CvBuilderTaskingAI::GetImprovementDire } } - return aDirectives; -} - -/// Evaluating a plot to see if we can build resources there -void CvBuilderTaskingAI::AddImprovingResourcesDirective(vector> &aDirectives, CvPlot* pPlot, CvCity* pCity, const vector aBuildsToConsider, int iMinScore) -{ - // check to see if a resource is here. If not, bail out! - ResourceTypes eResource = pPlot->getResourceType(m_pPlayer->getTeam()); - if(eResource == NO_RESOURCE) - return; - - ImprovementTypes eExistingPlotImprovement = pPlot->getImprovementType(); - if (eExistingPlotImprovement != NO_IMPROVEMENT) - { - // Do we have a special improvement here? (great person improvement, gifted improvement from major civ) - if (pPlot->HasSpecialImprovement() || (m_pPlayer->isOption(PLAYEROPTION_SAFE_AUTOMATION) && m_pPlayer->isHuman())) - { - if (m_bLogging) - { - CvString strTemp; - strTemp.Format( - "%Weight,Improvement Blocked by Special Improvement,%s,%i,%i", - GC.getImprovementInfo(pPlot->getImprovementType())->GetType(), - pPlot->getX(), - pPlot->getY() - ); - LogInfo(strTemp, m_pPlayer); - } - return; - } - } - - // if city owning this plot is being razed, ignore this plot - if (pCity && pCity->IsRazing()) - return; - - // loop through the build types to find one that we can use - for(vector::const_iterator it = aBuildsToConsider.begin(); it != aBuildsToConsider.end(); ++it) - { - BuildTypes eBuild = *it; - CvBuildInfo* pkBuild = GC.getBuildInfo(eBuild); - if(pkBuild == NULL) - continue; - - ImprovementTypes eImprovement = (ImprovementTypes)pkBuild->getImprovement(); - if(eImprovement == NO_IMPROVEMENT) - continue; - - if (eImprovement == eExistingPlotImprovement) - continue; - - CvImprovementEntry* pkImprovementInfo = GC.getImprovementInfo(eImprovement); - if(pkImprovementInfo == NULL) - continue; - -#if defined(MOD_BALANCE_CORE) - //Check for test below. - if(pkImprovementInfo->IsSpecificCivRequired() && m_pPlayer->getCivilizationType() != pkImprovementInfo->GetRequiredCivilization()) - continue; -#endif - - if (pkImprovementInfo->IsConnectsResource(eResource)) - { - if (!m_pPlayer->canBuild(pPlot, eBuild)) - continue; - - int iScore = ScorePlotBuild(pPlot, eImprovement, eBuild); - - if (iScore <= 0) - { - continue; - } - - iScore /= 10; - - BuilderDirective directive(BuilderDirective::BUILD_IMPROVEMENT_ON_RESOURCE, eBuild, eResource, pPlot->getX(), pPlot->getY(), iScore); - - if (m_bLogging) - { - CvString strTemp; - strTemp.Format( - "Weight,Directive Score Added,%s,%i,%i,%d", - GC.getBuildInfo(eBuild)->GetType(), - directive.m_sX, - directive.m_sY, - iScore - ); - LogInfo(strTemp, m_pPlayer); - } + // Special handling for no-two-adjacent and great person improvements + aDirectives = FilterNonOptimalNoTwoAdjacentImprovements(aDirectives, true); + aDirectives = FilterNoTwoAdjacentDirectives(aDirectives); + aDirectives = FilterNonOptimalNoTwoAdjacentImprovements(aDirectives, false); + UpdateGreatPersonDirectives(aDirectives); - if (iScore > iMinScore) - aDirectives.push_back(OptionWithScore(directive, iScore)); - } - } + return aDirectives; } /// Evaluating a plot to determine what improvement could be best there @@ -1961,29 +2011,24 @@ void CvBuilderTaskingAI::AddImprovingPlotsDirective(vectorgetResourceType(m_pPlayer->getTeam()); - if (pkImprovementInfo->IsConnectsResource(eResource)) - continue; - - if (!m_pPlayer->canBuild(pPlot, eBuild)) + if (!m_pPlayer->canBuild(pPlot, eBuild, true)) continue; -#if defined(MOD_BALANCE_CORE) //Check for test below. if (pkImprovementInfo->IsSpecificCivRequired() && m_pPlayer->getCivilizationType() != pkImprovementInfo->GetRequiredCivilization()) continue; -#endif + + ResourceTypes eResource = pPlot->getResourceType(m_pPlayer->getTeam()); + bool bWillConnectResource = eResource != NO_RESOURCE && pkImprovementInfo->IsConnectsResource(eResource); FeatureTypes eFeature = pPlot->getFeatureType(); bool bWillRemoveFeature = eFeature != NO_FEATURE && pkBuild->isFeatureRemove(eFeature); - if (bWillRemoveFeature) + if (!bWillConnectResource && bWillRemoveFeature) { - if (m_aeSaveFeatureForImprovementUntilTech[eFeature].second != NO_TECH && !GET_TEAM(m_pPlayer->getTeam()).GetTeamTechs()->HasTech(m_aeSaveFeatureForImprovementUntilTech[eFeature].second)) + if (m_pPlayer->isOption(PLAYEROPTION_LEAVE_FORESTS) && m_pPlayer->isHuman()) { - if (eResource == NO_RESOURCE) - { - if (m_bLogging) { + if (m_bLogging) { CvString strTemp; strTemp.Format( "Feature Remove Blocked,%s,%s,%i,%i", @@ -1993,26 +2038,6 @@ void CvBuilderTaskingAI::AddImprovingPlotsDirective(vectorgetY() ); LogInfo(strTemp, m_pPlayer); - } - continue; - } - } - } - - if(m_pPlayer->isOption(PLAYEROPTION_LEAVE_FORESTS) && m_pPlayer->isHuman()) - { - if(bWillRemoveFeature && eResource == NO_RESOURCE) - { - if (m_bLogging) { - CvString strTemp; - strTemp.Format( - "Feature Remove Blocked,%s,%s,%i,%i", - GC.getFeatureInfo(eFeature)->GetType(), - GC.getBuildInfo(eBuild)->GetType(), - pPlot->getX(), - pPlot->getY() - ); - LogInfo(strTemp, m_pPlayer); } continue; } @@ -2026,36 +2051,31 @@ void CvBuilderTaskingAI::AddImprovingPlotsDirective(vector iMinScore) { - if (m_pPlayer->GetPlayerTraits()->IsWoodlandMovementBonus()) - { - if (pPlot->IsCityConnection(m_pPlayer->GetID())) - iScore /= 100; - else - iScore /= 10; - } - } - iScore /= 10; + BuilderDirective::BuilderDirectiveType eDirectiveType = bWillConnectResource ? BuilderDirective::BUILD_IMPROVEMENT_ON_RESOURCE : BuilderDirective::BUILD_IMPROVEMENT; + ResourceTypes eConnectedResource = bWillConnectResource ? eResource : NO_RESOURCE; - BuilderDirective directive(BuilderDirective::BUILD_IMPROVEMENT, eBuild, NO_RESOURCE, pPlot->getX(), pPlot->getY(), iScore); + BuilderDirective directive(eDirectiveType, eBuild, eConnectedResource, pkImprovementInfo->IsCreatedByGreatPerson(), pPlot->getX(), pPlot->getY(), iScore); + + if (m_bLogging) + { + CvString strTemp; + strTemp.Format( + "Weight,Directive Score Added,%s,%i,%i,%d", + GC.getBuildInfo(eBuild)->GetType(), + directive.m_sX, + directive.m_sY, + iScore + ); + LogInfo(strTemp, m_pPlayer); + } - if(m_bLogging) - { - CvString strTemp; - strTemp.Format( - "Weight,Directive Score Added,%s,%i,%i,%d", - GC.getBuildInfo(eBuild)->GetType(), - directive.m_sX, - directive.m_sY, - iScore - ); - LogInfo(strTemp, m_pPlayer); - } - - if (iScore > iMinScore) aDirectives.push_back(OptionWithScore(directive, iScore)); + } } } @@ -2123,7 +2143,7 @@ void CvBuilderTaskingAI::AddRemoveRouteDirective(vectorgetX(), pPlot->getY(), iWeight); + BuilderDirective directive(BuilderDirective::REMOVE_ROAD, m_eRemoveRouteBuild, NO_RESOURCE, false, pPlot->getX(), pPlot->getY(), iWeight); if (m_bLogging) { @@ -2191,7 +2211,7 @@ void CvBuilderTaskingAI::AddRouteDirective(vectorgetX(), pPlot->getY(), iValue); + BuilderDirective directive(BuilderDirective::BUILD_ROUTE, eRouteBuild, NO_RESOURCE, false, pPlot->getX(), pPlot->getY(), iValue); if(m_bLogging) { @@ -2380,7 +2400,7 @@ void CvBuilderTaskingAI::AddChopDirectives(vector 0) { - BuilderDirective directive(BuilderDirective::CHOP, eChopBuild, NO_RESOURCE, pPlot->getX(), pPlot->getY(), iWeight); + BuilderDirective directive(BuilderDirective::CHOP, eChopBuild, NO_RESOURCE, false, pPlot->getX(), pPlot->getY(), iWeight); aDirectives.push_back(OptionWithScore(directive, iWeight)); } @@ -2408,7 +2428,7 @@ void CvBuilderTaskingAI::AddRepairImprovementDirective(vector 0) { - BuilderDirective directive(BuilderDirective::REPAIR, m_eRepairBuild, NO_RESOURCE, pPlot->getX(), pPlot->getY(), iWeight); + BuilderDirective directive(BuilderDirective::REPAIR, m_eRepairBuild, NO_RESOURCE, false, pPlot->getX(), pPlot->getY(), iWeight); aDirectives.push_back(OptionWithScore(directive, iWeight)); } @@ -2429,7 +2449,7 @@ void CvBuilderTaskingAI::AddRepairRouteDirective(vector 0) { - BuilderDirective directive(BuilderDirective::REPAIR, m_eRepairBuild, NO_RESOURCE, pPlot->getX(), pPlot->getY(), iValue); + BuilderDirective directive(BuilderDirective::REPAIR, m_eRepairBuild, NO_RESOURCE, false, pPlot->getX(), pPlot->getY(), iValue); aDirectives.push_back(OptionWithScore(directive, iValue)); } @@ -2458,7 +2478,7 @@ void CvBuilderTaskingAI::AddScrubFalloutDirectives(vectorgetX(), pPlot->getY(), iWeight); + BuilderDirective directive(BuilderDirective::CHOP, m_eFalloutRemove, NO_RESOURCE, false, pPlot->getX(), pPlot->getY(), iWeight); aDirectives.push_back(OptionWithScore(directive, iWeight)); } } @@ -2619,10 +2639,6 @@ bool CvBuilderTaskingAI::DoesBuildHelpRush(CvPlot* pPlot, BuildTypes eBuild) return false; } - //don't care about rush if this connects a resource. - if (pPlot->getResourceType(m_pPlayer->getTeam()) != NO_RESOURCE) - return true; - if(!(pCity->getOrderFromQueue(0)->bRush)) { // this order should not be rushed @@ -2632,6 +2648,33 @@ bool CvBuilderTaskingAI::DoesBuildHelpRush(CvPlot* pPlot, BuildTypes eBuild) return false; } +static int GetNumAdjacent(CvPlot* pPlot, ImprovementTypes eImprovement, SBuilderState sState) +{ + int iNumAdjacent = 0; + + CvPlot** pAdjacentPlots = GC.getMap().getNeighborsUnchecked(pPlot->GetPlotIndex()); + for (int iI = 0; iI < NUM_DIRECTION_TYPES; iI++) + { + CvPlot* pAdjacentPlot = pAdjacentPlots[iI]; + + if (!pAdjacentPlot) + continue; + + int iAdjacentPlotIndex = pAdjacentPlot->GetPlotIndex(); + + ImprovementTypes eAdjacentImprovement = sState.mChangedPlotImprovements.find(iAdjacentPlotIndex) != sState.mChangedPlotImprovements.end() ? sState.mChangedPlotImprovements[iAdjacentPlotIndex] : NO_IMPROVEMENT; + + // If we are not planning on building an improvement here, use the one that exists already + if (eAdjacentImprovement == NO_IMPROVEMENT && !pAdjacentPlot->IsImprovementPillaged()) + eAdjacentImprovement = pAdjacentPlot->getImprovementType(); + + if (eAdjacentImprovement == eImprovement) + iNumAdjacent++; + } + + return iNumAdjacent; +} + int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovement, BuildTypes eBuild, SBuilderState sState) { const CvBuildInfo* pkBuildInfo = GC.getBuildInfo(eBuild); @@ -2644,7 +2687,7 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem const ResourceTypes eResource = pPlot->getResourceType(m_pPlayer->getTeam()); const FeatureTypes eFeature = pPlot->getFeatureType(); - const ImprovementTypes eOldImprovement = pPlot->getImprovementType(); + const ImprovementTypes eOldImprovement = !pPlot->IsImprovementPillaged() ? pPlot->getImprovementType() : NO_IMPROVEMENT; const CvImprovementEntry* pkOldImprovementInfo = GC.getImprovementInfo(eOldImprovement); const ResourceTypes eResourceFromOldImprovement = pkOldImprovementInfo ? (ResourceTypes)pkOldImprovementInfo->GetResourceFromImprovement() : NO_RESOURCE; const FeatureTypes eFeatureFromOldImprovement = pkOldImprovementInfo ? pkOldImprovementInfo->GetCreatedFeature() : NO_FEATURE; @@ -2693,11 +2736,11 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem // Do we have or will we build a road here? RouteTypes eRouteNeeded = GetBestRouteTypeAndValue(pPlot).first; - bool bWillBeCityConnectingRoad = eRouteNeeded != NO_ROUTE; + RouteTypes eForceCityConnection = eRouteNeeded; - if (!IsRoutePlanned(pPlot, eRouteNeeded, PURPOSE_SHORTCUT) && !IsRoutePlanned(pPlot, eRouteNeeded, PURPOSE_CONNECT_CAPITAL)) + if (eRouteNeeded != NO_ROUTE && !IsRoutePlanned(pPlot, eRouteNeeded, PURPOSE_SHORTCUT) && !IsRoutePlanned(pPlot, eRouteNeeded, PURPOSE_CONNECT_CAPITAL)) { - bWillBeCityConnectingRoad = false; + eForceCityConnection = NO_ROUTE; } const bool bIsWithinWorkRange = pOwningCity && pOwningCity->IsWithinWorkRange(pPlot); @@ -2713,6 +2756,14 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem const FeatureTypes eNaturalFeature = eFeatureFromOldImprovement == NO_FEATURE ? eFeature : NO_FEATURE; const ResourceTypes eNaturalResource = eResourceFromOldImprovement == NO_RESOURCE ? eResource : NO_RESOURCE; + // Give a bonus for chopping + if (eFeature != NO_FEATURE && pkBuildInfo && pkBuildInfo->isFeatureRemove(eFeature) && DoesBuildHelpRush(pPlot, eBuild)) + { + int iProductionFromChop = pPlot->getFeatureProduction(eBuild, m_pPlayer->GetID(), NULL); + if (iProductionFromChop > 0) + iSecondaryScore += iProductionFromChop * GetYieldBaseModifierTimes100(YIELD_PRODUCTION) / 50; + } + int iExtraResource = 0; if ((pkImprovementInfo && pkImprovementInfo->IsConnectsResource(eResource)) || eResourceFromImprovement != NO_RESOURCE) { @@ -2722,6 +2773,8 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem iExtraResource = sState.mExtraResources[eResource]; } + /* + * TODO fix sated logic if (pOwningCity) { // A city is considered "sated" if all the plots they are working have an improvement on them @@ -2752,6 +2805,9 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem if (pWorkedPlot->isWater()) continue; + if (pWorkedPlot->IsFeatureOasis()) + continue; + if (pWorkedPlot->getImprovementType() == NO_IMPROVEMENT || pWorkedPlot->IsImprovementPillaged()) iWorkedUnimproved++; } @@ -2773,13 +2829,12 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem if (iWorkedUnimproved > iImprovementsPlanned) bCityIsSated = false; } + */ if (bIsWithinWorkRange) { - bool bIgnoreCityConnection = eRouteNeeded == NO_ROUTE; - if (eBuild != NO_BUILD) - UpdateProjectedPlotYields(pPlot, eBuild, bIgnoreCityConnection); + UpdateProjectedPlotYields(pPlot, eBuild, eForceCityConnection); else UpdateCurrentPlotYields(pPlot); } @@ -2789,7 +2844,7 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem YieldTypes eYield = (YieldTypes)ui; // Simplification - errata yields not worth considering. - if (eYield > YIELD_GOLDEN_AGE_POINTS && !MOD_BALANCE_CORE_JFD) + if (eYield > YIELD_CULTURE_LOCAL && !MOD_BALANCE_CORE_JFD) break; int iBasePlotYield = bIsWithinWorkRange ? pPlot->calculateNatureYield(eYield, m_pPlayer->GetID(), eNaturalFeature, eNaturalResource, pOwningCity) : 0; @@ -2809,6 +2864,13 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem else iNewYieldTimes100 -= 15 * pkImprovementInfo->GetWLTKDYieldChange(eYield); } + if (pkOldImprovementInfo) + { + if (pOwningCity->GetWeLoveTheKingDayCounter() == 0) + iNewYieldTimes100 -= 85 * pkOldImprovementInfo->GetWLTKDYieldChange(eYield); + else + iNewYieldTimes100 += 15 * pkOldImprovementInfo->GetWLTKDYieldChange(eYield); + } if (bChangedFeatureState) { @@ -2895,9 +2957,9 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem // How much extra yield we give to adjacent tiles with a certain terrain if (pAdjacentPlot->getTerrainType() != NO_TERRAIN) { - int iAdjacentTerrainYieldChange = pkImprovementInfo ? pkImprovementInfo->GetAdjacentTerrainYieldChanges(pAdjacentPlot->getTerrainType(), eYield) * 2 : 0; + int iAdjacentTerrainYieldChange = pkImprovementInfo ? pkImprovementInfo->GetAdjacentTerrainYieldChanges(pAdjacentPlot->getTerrainType(), eYield) : 0; // Losing yield from removing old improvement - iAdjacentTerrainYieldChange -= pkOldImprovementInfo ? pkOldImprovementInfo->GetAdjacentTerrainYieldChanges(pAdjacentPlot->getTerrainType(), eYield) * 2 : 0; + iAdjacentTerrainYieldChange -= pkOldImprovementInfo ? pkOldImprovementInfo->GetAdjacentTerrainYieldChanges(pAdjacentPlot->getTerrainType(), eYield) : 0; if (iAdjacentTerrainYieldChange != 0) iTempYieldScore += iAdjacentTerrainYieldChange; } @@ -2911,34 +2973,42 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem if (eAdjacentImprovement != NO_IMPROVEMENT) { // How much extra yield we give to adjacent tiles with a certain improvement - int iAdjacentImprovementYieldChange = pkImprovementInfo ? pkImprovementInfo->GetAdjacentImprovementYieldChanges(eAdjacentImprovement, eYield) * 2 : 0; + int iAdjacentImprovementYieldChange = pkImprovementInfo ? pkImprovementInfo->GetAdjacentImprovementYieldChanges(eAdjacentImprovement, eYield) : 0; // Losing yield from removing old improvement - iAdjacentImprovementYieldChange -= pkOldImprovementInfo ? pkOldImprovementInfo->GetAdjacentImprovementYieldChanges(eAdjacentImprovement, eYield) * 2 : 0; + iAdjacentImprovementYieldChange -= pkOldImprovementInfo ? pkOldImprovementInfo->GetAdjacentImprovementYieldChanges(eAdjacentImprovement, eYield) : 0; if (iAdjacentImprovementYieldChange != 0) iTempYieldScore += iAdjacentImprovementYieldChange; // How much extra yield we give to an adjacent tile with the same improvement if (eAdjacentImprovement == eImprovement) { - int iAdjacentSameTypeYield = pkImprovementInfo ? pkImprovementInfo->GetYieldAdjacentSameType(eYield) * 2 : 0; + int iAdjacentSameTypeYield = pkImprovementInfo ? pkImprovementInfo->GetYieldAdjacentSameType(eYield) : 0; if (iAdjacentSameTypeYield != 0) iTempYieldScore += iAdjacentSameTypeYield; - // Add half a yield if there's one adjacent when two are needed + int iAdjacentTwoSameTypeYield = pkImprovementInfo ? pkImprovementInfo->GetYieldAdjacentTwoSameType(eYield) : 0; if (iAdjacentTwoSameTypeYield != 0) - iTempYieldScore += iAdjacentTwoSameTypeYield; + { + int iDoubleAdjacentSame = GetNumAdjacent(pAdjacentPlot, eImprovement, sState); + if (iDoubleAdjacentSame % 2 == 1) + iTempYieldScore += iAdjacentTwoSameTypeYield; + } } // Losing adjacency yield from removing old improvement if (eOldImprovement != NO_IMPROVEMENT && eAdjacentImprovement == eOldImprovement) { - int iAdjacentSameTypeYield = pkOldImprovementInfo->GetYieldAdjacentSameType(eYield) * 2; + int iAdjacentSameTypeYield = pkOldImprovementInfo->GetYieldAdjacentSameType(eYield); if (iAdjacentSameTypeYield != 0) iTempYieldScore -= iAdjacentSameTypeYield; - // Add half a yield if there's one adjacent when two are needed + int iAdjacentTwoSameTypeYield = pkOldImprovementInfo->GetYieldAdjacentTwoSameType(eYield); if (iAdjacentTwoSameTypeYield != 0) - iTempYieldScore -= iAdjacentTwoSameTypeYield; + { + int iDoubleAdjacentSame = GetNumAdjacent(pAdjacentPlot, eOldImprovement, sState); + if (iDoubleAdjacentSame % 2 == 0) + iTempYieldScore -= iAdjacentTwoSameTypeYield; + } } CvImprovementEntry* pkAdjacentImprovementInfo = GC.getImprovementInfo(eAdjacentImprovement); @@ -2948,7 +3018,7 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem // How much extra yield an adjacent improvement will get if we create a resource if (eResourceFromImprovement != NO_RESOURCE) { - int iAdjacentResourceYieldChanges = pkAdjacentImprovementInfo->GetAdjacentResourceYieldChanges(eResourceFromImprovement, eYield) * 2; + int iAdjacentResourceYieldChanges = pkAdjacentImprovementInfo->GetAdjacentResourceYieldChanges(eResourceFromImprovement, eYield); if (iAdjacentResourceYieldChanges != 0) iTempYieldScore += iAdjacentResourceYieldChanges; } @@ -2956,7 +3026,7 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem if (eResourceFromOldImprovement != NO_RESOURCE || (eResource != NO_RESOURCE && eResourceFromImprovement != NO_RESOURCE)) { ResourceTypes eOldResource = eResourceFromOldImprovement != NO_RESOURCE ? eResourceFromOldImprovement : eResource; - int iAdjacentResourceYieldChanges = pkAdjacentImprovementInfo->GetAdjacentResourceYieldChanges(eOldResource, eYield) * 2; + int iAdjacentResourceYieldChanges = pkAdjacentImprovementInfo->GetAdjacentResourceYieldChanges(eOldResource, eYield); if (iAdjacentResourceYieldChanges != 0) iTempYieldScore -= iAdjacentResourceYieldChanges; } @@ -2964,7 +3034,7 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem // How much extra yield an adjacent improvement will get if we create a feature if (eFeatureFromImprovement != NO_FEATURE) { - int iAdjacentFeatureYieldChanges = pkAdjacentImprovementInfo->GetAdjacentFeatureYieldChanges(eFeatureFromImprovement, eYield) * 2; + int iAdjacentFeatureYieldChanges = pkAdjacentImprovementInfo->GetAdjacentFeatureYieldChanges(eFeatureFromImprovement, eYield); if (iAdjacentFeatureYieldChanges != 0) iTempYieldScore += iAdjacentFeatureYieldChanges; } @@ -2972,7 +3042,7 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem if (eFeatureFromOldImprovement != NO_FEATURE || (eFeature != NO_FEATURE && (eFeatureFromImprovement != NO_FEATURE || (pkBuildInfo && pkBuildInfo->isFeatureRemove(eFeature))))) { FeatureTypes eOldFeature = eFeatureFromOldImprovement != NO_FEATURE ? eFeatureFromOldImprovement : eFeature; - int iAdjacentFeatureYieldChanges = pkAdjacentImprovementInfo->GetAdjacentFeatureYieldChanges(eOldFeature, eYield) * 2; + int iAdjacentFeatureYieldChanges = pkAdjacentImprovementInfo->GetAdjacentFeatureYieldChanges(eOldFeature, eYield); if (iAdjacentFeatureYieldChanges != 0) iTempYieldScore -= iAdjacentFeatureYieldChanges; } @@ -2985,7 +3055,7 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem // Use base modifier to avoid expensive computation int iYieldModifier = GetYieldBaseModifierTimes100(eYield); - iYieldScore += (iTempYieldScore * iYieldModifier) / 2; + iYieldScore += (iTempYieldScore * iYieldModifier); } } } @@ -3007,7 +3077,7 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem //Improvement grants or connects resource //Don't give any bonus if we are creating a resource on top of an existing one - if (pOwningCity && ((eResourceFromImprovement != NO_RESOURCE && eResource == NO_RESOURCE) || (eResource != NO_RESOURCE && (pkImprovementInfo && pkImprovementInfo->IsConnectsResource(eResource) && !pkImprovementInfo->IsCreatedByGreatPerson())))) + if (pOwningCity && ((eResourceFromImprovement != NO_RESOURCE && eResource == NO_RESOURCE) || (eResource != NO_RESOURCE && pkImprovementInfo && pkImprovementInfo->IsConnectsResource(eResource)))) { ResourceTypes eConnectedResource = eResourceFromImprovement != NO_RESOURCE ? eResourceFromImprovement : eResource; int iResourceAmount = eResourceFromImprovement != NO_RESOURCE ? pkImprovementInfo->GetResourceQuantityFromImprovement() : pPlot->getNumResource(); @@ -3031,72 +3101,27 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem } } - // Avoid building improvements in certain spots - if (pOwningCity && pkImprovementInfo && bIsWithinWorkRange) + bool bBenefitsFromRoads = false; + for (int iI = 0; iI <= YIELD_FAITH; iI++) { - bool bBenefitsFromRoads = false; - for (int iI = 0; iI <= YIELD_FAITH; iI++) + if (pkImprovementInfo->GetRouteYieldChanges(ROUTE_ROAD, iI) > 0) { - if (pkImprovementInfo->GetRouteYieldChanges(ROUTE_ROAD, iI) > 0 || pkImprovementInfo->GetRouteYieldChanges(ROUTE_RAILROAD, iI) > 0) - { - bBenefitsFromRoads = true; - break; - } + bBenefitsFromRoads = true; + break; } + } - ImprovementTypes eImprovementToSavePlotFor = SavePlotForUniqueImprovement(pPlot); - - if (pkImprovementInfo->IsCreatedByGreatPerson()) - { - // Great person improvements special considerations - bool bGreatPersonAvoidLarge = false; - if (MOD_BALANCE_VP) - { - if (bWillBeCityConnectingRoad && !bBenefitsFromRoads) - bGreatPersonAvoidLarge = true; - else if (!bWillBeCityConnectingRoad && bBenefitsFromRoads) - bGreatPersonAvoidLarge = true; - } - - if (bGreatPersonAvoidLarge) - { - iYieldScore /= 2; - } - else - { - bool bGreatPersonAvoidSmall = false; - - if (eFeature != NO_FEATURE && m_aeSaveFeatureForImprovementUntilTech[eFeature].second != NO_TECH) - bGreatPersonAvoidSmall = true; - else if (eImprovementToSavePlotFor != NO_IMPROVEMENT) - bGreatPersonAvoidSmall = true; - - if (bGreatPersonAvoidSmall) - iYieldScore = (iYieldScore * 9) / 10; - } - } - else - { - // Non-great person improvements considerations - bool bAvoidPlot = false; - if (eResource != NO_RESOURCE && pkImprovementInfo && !pkImprovementInfo->IsConnectsResource(eResource)) - { - // Avoid building improvements on resources until we have the technology to improve that resource - CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource); - if (!m_pPlayer->HasTech((TechTypes)pkResourceInfo->getImproveTech())) - bAvoidPlot = true; - } - else if ((bWillBeCityConnectingRoad && !bBenefitsFromRoads) || (!bWillBeCityConnectingRoad && bBenefitsFromRoads)) - { - if (MOD_BALANCE_VP) - { - bAvoidPlot = true; - } - } + if (bBenefitsFromRoads && eForceCityConnection == NO_ROUTE) + iSecondaryScore -= 100; + else if (!bBenefitsFromRoads && (eForceCityConnection == ROUTE_ROAD || eForceCityConnection == ROUTE_RAILROAD)) + iSecondaryScore -= 100; - if (bAvoidPlot) - iYieldScore = (iYieldScore * 9) / 10; - } + // Don't build on antiquity sites + if (eResource != NO_RESOURCE) + { + BuildTypes eDigBuild = (BuildTypes)GC.getInfoTypeForString("BUILD_ARCHAEOLOGY_DIG"); + if (m_pPlayer->canBuild(pPlot, eDigBuild)) + iSecondaryScore -= 1000; } #if defined(MOD_IMPROVEMENTS_EXTENSIONS) @@ -3158,22 +3183,6 @@ int CvBuilderTaskingAI::ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovem return iYieldScore + iBaseYieldScore + iSecondaryScore; } -BuildTypes CvBuilderTaskingAI::GetBuildTypeFromImprovement(ImprovementTypes eImprovement) const -{ - for(int iBuildIndex = 0; iBuildIndex < GC.getNumBuildInfos(); iBuildIndex++) - { - BuildTypes eBuild = (BuildTypes)iBuildIndex; - CvBuildInfo* pkBuild = GC.getBuildInfo(eBuild); - - if(NULL != pkBuild && eImprovement == (ImprovementTypes)pkBuild->getImprovement()) - { - return eBuild; - } - } - - return NO_BUILD; -} - BuildTypes CvBuilderTaskingAI::GetRepairBuild(void) { for(int i = 0; i < GC.getNumBuildInfos(); i++) @@ -3449,7 +3458,7 @@ void CvBuilderTaskingAI::UpdateCurrentPlotYields(CvPlot* pPlot) for(uint ui = 0; ui < NUM_YIELD_TYPES; ui++) { #if defined(MOD_BALANCE_CORE) - if((YieldTypes)ui <= YIELD_FAITH) + if((YieldTypes)ui <= YIELD_CULTURE_LOCAL) { #endif m_aiCurrentPlotYields[ui] = pPlot->getYield((YieldTypes)ui); @@ -3470,7 +3479,7 @@ void CvBuilderTaskingAI::UpdateCurrentPlotYields(CvPlot* pPlot) } // looks at the current plot assuming the build to see what it's worth -void CvBuilderTaskingAI::UpdateProjectedPlotYields(CvPlot* pPlot, BuildTypes eBuild, bool bIgnoreCityConnection) +void CvBuilderTaskingAI::UpdateProjectedPlotYields(CvPlot* pPlot, BuildTypes eBuild, RouteTypes eForceCityConnection) { UpdateCurrentPlotYields(pPlot); @@ -3500,7 +3509,7 @@ void CvBuilderTaskingAI::UpdateProjectedPlotYields(CvPlot* pPlot, BuildTypes eBu if ((YieldTypes)ui <= YIELD_GOLDEN_AGE_POINTS || MOD_BALANCE_CORE_JFD) { #endif - m_aiProjectedPlotYields[ui] = pPlot->getYieldWithBuild(eBuild, (YieldTypes)ui, false, bIgnoreCityConnection, m_pPlayer->GetID(), pOwningCity, pReligion, pBelief); + m_aiProjectedPlotYields[ui] = pPlot->getYieldWithBuild(eBuild, (YieldTypes)ui, false, eForceCityConnection, m_pPlayer->GetID(), pOwningCity, pReligion, pBelief); m_aiProjectedPlotYields[ui] = max(m_aiProjectedPlotYields[ui], 0); #if defined(MOD_RELIGION_PERMANENT_PANTHEON) @@ -3512,7 +3521,7 @@ void CvBuilderTaskingAI::UpdateProjectedPlotYields(CvPlot* pPlot, BuildTypes eBu { if (pReligion == NULL || (pReligion != NULL && !pReligion->m_Beliefs.IsPantheonBeliefInReligion(ePantheonBelief, eMajority, m_pPlayer->GetID()))) // check that the our religion does not have our belief, to prevent double counting { - m_aiProjectedPlotYields[ui] += pPlot->getYieldWithBuild(eBuild, (YieldTypes)ui, false, bIgnoreCityConnection, m_pPlayer->GetID(), pOwningCity, pPantheon, NULL); + m_aiProjectedPlotYields[ui] += pPlot->getYieldWithBuild(eBuild, (YieldTypes)ui, false, eForceCityConnection, m_pPlayer->GetID(), pOwningCity, pPantheon, NULL); m_aiProjectedPlotYields[ui] = max(m_aiProjectedPlotYields[ui], 0); } } @@ -3542,7 +3551,7 @@ void CvBuilderTaskingAI::UpdateProjectedPlotYields(CvPlot* pPlot, BuildTypes eBu if ((YieldTypes)ui <= YIELD_GOLDEN_AGE_POINTS || MOD_BALANCE_CORE_JFD) { #endif - m_aiProjectedPlotYields[ui] = pPlot->getYieldWithBuild(eBuild, (YieldTypes)ui, false, bIgnoreCityConnection, m_pPlayer->GetID(), NULL, NULL, NULL); + m_aiProjectedPlotYields[ui] = pPlot->getYieldWithBuild(eBuild, (YieldTypes)ui, false, eForceCityConnection, m_pPlayer->GetID(), NULL, NULL, NULL); m_aiProjectedPlotYields[ui] = max(m_aiProjectedPlotYields[ui], 0); if (m_bLogging){ diff --git a/CvGameCoreDLL_Expansion2/CvBuilderTaskingAI.h b/CvGameCoreDLL_Expansion2/CvBuilderTaskingAI.h index 05661e959b..9479450d13 100644 --- a/CvGameCoreDLL_Expansion2/CvBuilderTaskingAI.h +++ b/CvGameCoreDLL_Expansion2/CvBuilderTaskingAI.h @@ -30,34 +30,47 @@ struct BuilderDirective NUM_DIRECTIVES ENUM_META_VALUE }; - BuilderDirective() : m_eDirectiveType(NUM_DIRECTIVES), m_eBuild(NO_BUILD), m_eResource(NO_RESOURCE), m_sX(-1), m_sY(-1), m_iScore(-1) {} - BuilderDirective(BuilderDirectiveType eDirective, BuildTypes eBuild, ResourceTypes eResource, short sX, short sY, int iScore) + BuilderDirective() : m_eDirectiveType(NUM_DIRECTIVES), m_eBuild(NO_BUILD), m_eResource(NO_RESOURCE), m_bIsGreatPerson(false), m_sX(-1), m_sY(-1), m_iScore(-1), m_iScorePenalty(-1) {} + BuilderDirective(BuilderDirectiveType eDirective, BuildTypes eBuild, ResourceTypes eResource, bool bIsGreatPerson, short sX, short sY, int iScore) { m_eDirectiveType = eDirective; m_eBuild = eBuild; m_eResource = eResource; + m_bIsGreatPerson = bIsGreatPerson; m_sX = sX; m_sY = sY; m_iScore = iScore; + m_iScorePenalty = 0; } BuilderDirectiveType m_eDirectiveType; BuildTypes m_eBuild; ResourceTypes m_eResource; + bool m_bIsGreatPerson; short m_sX; short m_sY; int m_iScore; + int m_iScorePenalty; //int m_iGoldCost; //short m_sMoveTurnsAway; bool operator==(const BuilderDirective& rhs) const { - return m_eDirectiveType == rhs.m_eDirectiveType && m_eBuild == rhs.m_eBuild && m_eResource == rhs.m_eResource && m_sX == rhs.m_sX && m_sY == rhs.m_sY && m_iScore == rhs.m_iScore; + return m_eDirectiveType == rhs.m_eDirectiveType && m_eBuild == rhs.m_eBuild && m_eResource == rhs.m_eResource && m_sX == rhs.m_sX && m_sY == rhs.m_sY && m_bIsGreatPerson == rhs.m_bIsGreatPerson; }; bool operator<(const BuilderDirective& rhs) const { - return m_eDirectiveType < rhs.m_eDirectiveType || m_eBuild < rhs.m_eBuild || m_eResource < rhs.m_eResource || m_sX < rhs.m_sX || m_sY < rhs.m_sY || m_iScore < rhs.m_iScore; - }; + if (m_eDirectiveType != rhs.m_eDirectiveType) return m_eDirectiveType < rhs.m_eDirectiveType; + if (m_eBuild != rhs.m_eBuild) return m_eBuild < rhs.m_eBuild; + if (m_eResource != rhs.m_eResource) return m_eResource < rhs.m_eResource; + if (m_sX != rhs.m_sX) return m_sX < rhs.m_sX; + if (m_sY != rhs.m_sY) return m_sY < rhs.m_sY; + return m_bIsGreatPerson < rhs.m_bIsGreatPerson; + } + int GetScore() const + { + return m_iScore - m_iScorePenalty; + } }; FDataStream& operator<<(FDataStream&, const BuilderDirective&); FDataStream& operator>>(FDataStream&, BuilderDirective&); @@ -87,13 +100,12 @@ class CvBuilderTaskingAI void UpdateRoutePlots(void); void UpdateImprovementPlots(void); - bool CanUnitPerformDirective(CvUnit* pUnit, BuilderDirective eDirective); + bool CanUnitPerformDirective(CvUnit* pUnit, BuilderDirective eDirective, bool bTestEra = false); int GetBuilderNumTurnsAway(CvUnit* pUnit, BuilderDirective eDirective, int iMaxDistance=INT_MAX); int GetTurnsToBuild(const CvUnit* pUnit, BuildTypes eBuild, const CvPlot* pPlot) const; vector GetDirectives(); bool ExecuteWorkerMove(CvUnit* pUnit, BuilderDirective aDirective); - void AddImprovingResourcesDirective(vector> &aDirectives, CvPlot* pPlot, CvCity* pWorkingCity, const vector aBuildsToConsider, int iMinScore); void AddImprovingPlotsDirective(vector> &aDirectives, CvPlot* pPlot, CvCity* pWorkingCity, const vector aBuildsToConsider, int iMinScore); void AddRouteOrRepairDirective(vector>& aDirectives, CvPlot* pPlot, RouteTypes eRoute, int iValue, RoutePurpose ePurpose); void AddRouteDirective(vector>& aDirectives, CvPlot* pPlot, RouteTypes eRoute, int iValue); @@ -118,7 +130,6 @@ class CvBuilderTaskingAI int ScorePlotBuild(CvPlot* pPlot, ImprovementTypes eImprovement, BuildTypes eBuild, SBuilderState sState=SBuilderState()); int GetTotalRouteBuildTime(const CvUnit* pUnit, const CvPlot* pPlot) const; - BuildTypes GetBuildTypeFromImprovement(ImprovementTypes eImprovement) const; BuildTypes GetRepairBuild(void); FeatureTypes GetFalloutFeature(void); BuildTypes GetFalloutRemove(void); @@ -155,7 +166,7 @@ class CvBuilderTaskingAI vector> GetImprovementDirectives(); void UpdateCurrentPlotYields(CvPlot* pPlot); - void UpdateProjectedPlotYields(CvPlot* pPlot, BuildTypes eBuild, bool bIgnoreCityConnection); + void UpdateProjectedPlotYields(CvPlot* pPlot, BuildTypes eBuild, RouteTypes eForceCityConnection); bool IsPlannedRouteForPurpose(const CvPlot* pPlot, RoutePurpose ePurpose) const; void AddRoutePlots(CvPlot* pStartPlot, CvPlot* pTargetPlot, RouteTypes eRoute, int iValue, const SPath& path, RoutePurpose ePurpose, bool bUseRivers); @@ -196,10 +207,10 @@ class CvBuilderTaskingAI BuildTypes m_eRemoveRouteBuild; //some player dependent flags for unique improvements - pair m_aeSaveFeatureForImprovementUntilTech[NUM_FEATURE_TYPES]; // serialized, can't be recreated on game load (eID is NO_PLAYER) - ImprovementTypes m_eSaveCityAdjacentForImprovement; // serialized - ImprovementTypes m_eSaveCoastalForImprovement; // serialized - ImprovementTypes m_eSaveHillsForImprovement; // serialized + ImprovementTypes m_aeUniqueFeatureImprovement[NUM_FEATURE_TYPES]; // serialized, can't be recreated on game load (eID is NO_PLAYER) + ImprovementTypes m_eUniqueCityAdjacentImprovement; // serialized + ImprovementTypes m_eUniqueCostalImprovement; // serialized + ImprovementTypes m_eUniqueHillImprovement; // serialized }; FDataStream& operator>>(FDataStream&, CvBuilderTaskingAI&); diff --git a/CvGameCoreDLL_Expansion2/CvGlobals.cpp b/CvGameCoreDLL_Expansion2/CvGlobals.cpp index 0522418d79..c34d98d3bc 100644 --- a/CvGameCoreDLL_Expansion2/CvGlobals.cpp +++ b/CvGameCoreDLL_Expansion2/CvGlobals.cpp @@ -178,7 +178,7 @@ CvGlobals::CvGlobals() : GD_INT_INIT(BUILDER_TASKING_BASELINE_BUILD_ROUTES, 750), GD_INT_INIT(BUILDER_TASKING_BASELINE_REPAIR, 1000), GD_INT_INIT(BUILDER_TASKING_BASELINE_SCRUB_FALLOUT, 20000), - GD_INT_INIT(BUILDER_TASKING_BASELINE_ADDS_FOOD, 200), + GD_INT_INIT(BUILDER_TASKING_BASELINE_ADDS_FOOD, 180), GD_INT_INIT(BUILDER_TASKING_BASELINE_ADDS_GOLD, 40), GD_INT_INIT(BUILDER_TASKING_BASELINE_ADDS_FAITH, 150), GD_INT_INIT(BUILDER_TASKING_BASELINE_ADDS_PRODUCTION, 200), diff --git a/CvGameCoreDLL_Expansion2/CvHomelandAI.cpp b/CvGameCoreDLL_Expansion2/CvHomelandAI.cpp index 72c2df86e2..9710616865 100644 --- a/CvGameCoreDLL_Expansion2/CvHomelandAI.cpp +++ b/CvGameCoreDLL_Expansion2/CvHomelandAI.cpp @@ -2558,7 +2558,12 @@ bool CvHomelandAI::ExecuteExplorerMoves(CvUnit* pUnit) // Higher weight means better directive static int GetDirectiveWeight(BuilderDirective eDirective, int iBuildTurns, int iMoveTurns) { - int iScore = eDirective.m_iScore; + int iScore = eDirective.GetScore(); + + // Try to avoid moving around too much with workers + if (!eDirective.m_bIsGreatPerson) + iMoveTurns *= 2; + int iBuildTime = iBuildTurns + iMoveTurns; // Precomputed square root of INT_MAX @@ -2571,7 +2576,7 @@ static int GetDirectiveWeight(BuilderDirective eDirective, int iBuildTurns, int return iScore * iScore; const int iScoreWeightSquared = 1; - const int iBuildTimeWeightSquared = 100; + int iBuildTimeWeightSquared = eDirective.m_bIsGreatPerson ? 16 : 100; return (iScore * iScore) * iScoreWeightSquared - (iBuildTime * iBuildTime) * iBuildTimeWeightSquared; } @@ -2605,10 +2610,10 @@ static bool IsBestDirectiveForBuilderAndPlot(BuilderDirective eDirective, CvUnit if ((eRoute != NO_ROUTE && eOtherRoute == NO_ROUTE) || (eRoute == NO_ROUTE && eOtherRoute != NO_ROUTE)) continue; - if (eOtherDirective.m_iScore <= eDirective.m_iScore) + if (eOtherDirective.GetScore() <= eDirective.GetScore()) continue; - if (!pPlayer->GetBuilderTaskingAI()->CanUnitPerformDirective(pUnit, eOtherDirective)) + if (!pPlayer->GetBuilderTaskingAI()->CanUnitPerformDirective(pUnit, eOtherDirective, true)) continue; return false; @@ -2649,7 +2654,7 @@ static bool IsBestWeightedDirectiveForBuilderAndPlot(BuilderDirective eDirective if (iOtherDirectiveWeightedScore <= iDirectiveWeightedScore) continue; - if (!pPlayer->GetBuilderTaskingAI()->CanUnitPerformDirective(pUnit, eOtherDirective)) + if (!pPlayer->GetBuilderTaskingAI()->CanUnitPerformDirective(pUnit, eOtherDirective, true)) continue; return false; @@ -2698,12 +2703,18 @@ static vector>> GetWeightedDirec if (ignoredWorkers.find(pUnit->GetID()) != ignoredWorkers.end()) continue; - if (!pPlayer->GetBuilderTaskingAI()->CanUnitPerformDirective(pUnit, eDirective)) + if (!pPlayer->GetBuilderTaskingAI()->CanUnitPerformDirective(pUnit, eDirective, true)) continue; if (pUnit->GetDanger(pDirectivePlot) > pUnit->GetCurrHitPoints()) { - continue; + CvUnit* pBestDefender = pDirectivePlot->getBestDefender(pPlayer->GetID()); + + if (!pBestDefender) + continue; + + if (pBestDefender->GetDanger(pDirectivePlot) > pBestDefender->GetCurrHitPoints() / 2) + continue; } int iPlotDistance = plotDistance(pDirectivePlot->getX(), pDirectivePlot->getY(), pUnit->getX(), pUnit->getY()); @@ -2905,33 +2916,47 @@ void CvHomelandAI::ExecuteWorkerMoves() BuilderDirective eDirective = pUnitAndDirectiveWithScore.option.second; int iBuilderID = pBuilder->GetID(); - bool bMoveExecuted = false; - bool bIsAutomated = !m_pPlayer->isHuman() || pBuilder->IsAutomated(); - if (bIsAutomated) + // We may have planned an improvement that we can't build yet, but should still update other plots as if we've built it + // E.g. if we're planning to build an improvement with a no-two-adjacent requirement, we still want to build other improvements next to it. + bool bCanBuild = pBuilder->canBuild(GC.getMap().plot(eDirective.m_sX, eDirective.m_sY), eDirective.m_eBuild); + + if (bCanBuild) { - if (pBuilderTaskingAI->ExecuteWorkerMove(pBuilder, eDirective)) + bool bIsAutomated = !m_pPlayer->isHuman() || pBuilder->IsAutomated(); + if (bIsAutomated) + { + if (pBuilderTaskingAI->ExecuteWorkerMove(pBuilder, eDirective)) + { + UnitProcessed(iBuilderID); + + processedWorkers.insert(iBuilderID); + m_workedPlots.insert(GC.getMap().plot(eDirective.m_sX, eDirective.m_sY)->GetPlotIndex()); + } + ignoredDirectives.insert(eDirective); + } + else { - UnitProcessed(iBuilderID); + // Assign non-automated worker to this directive + pBuilderTaskingAI->SetAssignedDirective(pBuilder, eDirective); processedWorkers.insert(iBuilderID); + ignoredDirectives.insert(eDirective); m_workedPlots.insert(GC.getMap().plot(eDirective.m_sX, eDirective.m_sY)->GetPlotIndex()); - - bMoveExecuted = true; } - ignoredDirectives.insert(eDirective); } else { - // Assign non-automated worker to this directive - pBuilderTaskingAI->SetAssignedDirective(pBuilder, eDirective); - - processedWorkers.insert(iBuilderID); ignoredDirectives.insert(eDirective); - m_workedPlots.insert(GC.getMap().plot(eDirective.m_sX, eDirective.m_sY)->GetPlotIndex()); - - bMoveExecuted = true; + if (GC.getLogging() && GC.getAILogging()) + { + CvString strLogString; + CvBuildInfo* pkBuild = GC.getBuildInfo(eDirective.m_eBuild); + CvString strTemp = pkBuild->GetDescription(); + strLogString.Format("Planning to %s at (%d, %d), but we can not build it yet", strTemp.GetCString(), eDirective.m_sX, eDirective.m_sY); + LogHomelandMessage(strLogString); + } } - if (bMoveExecuted && allWorkers.size() > processedWorkers.size()) + if (allWorkers.size() > processedWorkers.size()) { // We may want to recalculate some of the other directive scores CvBuildInfo* pkBuildInfo = GC.getBuildInfo(eDirective.m_eBuild); @@ -3016,11 +3041,40 @@ void CvHomelandAI::ExecuteWorkerMoves() OptionWithScore> eOtherDirectiveWithScore = *it; BuilderDirective eOtherDirective = eOtherDirectiveWithScore.option.second; + bool bDirectiveUpdated = false; + + CvPlot* pOtherPlot = GC.getMap().plot(eOtherDirective.m_sX, eOtherDirective.m_sY); + CvBuildInfo* pkOtherBuildInfo = GC.getBuildInfo(eOtherDirective.m_eBuild); + ImprovementTypes eOtherImprovement = (ImprovementTypes)pkOtherBuildInfo->getImprovement(); + + if (eOtherImprovement == NO_IMPROVEMENT && pkOtherBuildInfo->isRepair() && pOtherPlot->IsImprovementPillaged()) + eOtherImprovement = pOtherPlot->getImprovementType(); + + CvImprovementEntry* pkOtherImprovementInfo = GC.getImprovementInfo(eOtherImprovement); + // Pruning if (eDirective.m_sX == eOtherDirective.m_sX && eDirective.m_sY == eOtherDirective.m_sY) { // Prune directives that are in the same plot - continue; + if (bCanBuild || eDirective == eOtherDirective) + { + continue; + } + else + { + if (eOtherImprovement != NO_IMPROVEMENT) + { + int iScore = pBuilderTaskingAI->ScorePlotBuild(pDirectivePlot, eOtherImprovement, eOtherDirective.m_eBuild, sState); + + iScore /= 10; + // If we are planning to build something else here in the future, downscale the priority of this by 1/3 + eOtherDirective.m_iScore = iScore; + eOtherDirective.m_iScorePenalty += iScore / 3; + if (eOtherDirective.m_iScorePenalty >= eOtherDirective.m_iScore) + continue; + bDirectiveUpdated = true; + } + } } else if (pkImprovementInfo && pkImprovementInfo->IsNoTwoAdjacent()) { @@ -3031,19 +3085,8 @@ void CvHomelandAI::ExecuteWorkerMoves() } } - bool bDirectiveUpdated = false; - - CvPlot* pOtherPlot = GC.getMap().plot(eOtherDirective.m_sX, eOtherDirective.m_sY); - CvBuildInfo* pkOtherBuildInfo = GC.getBuildInfo(eOtherDirective.m_eBuild); - ImprovementTypes eOtherImprovement = (ImprovementTypes)pkOtherBuildInfo->getImprovement(); - - if (eOtherImprovement == NO_IMPROVEMENT && pkOtherBuildInfo->isRepair() && pOtherPlot->IsImprovementPillaged()) - eOtherImprovement = pOtherPlot->getImprovementType(); - - CvImprovementEntry* pkOtherImprovementInfo = GC.getImprovementInfo(eOtherImprovement); - // Reevaluating - if (eOtherImprovement != NO_IMPROVEMENT && bResourceStateChanged) + if (eOtherImprovement != NO_IMPROVEMENT && bResourceStateChanged && !bDirectiveUpdated) { // Connecting a resource may reduce or increase the value of connecting more instances of the same resource if ((pOtherPlot->getResourceType(m_pPlayer->getTeam()) == eResource && pkOtherImprovementInfo && pkOtherImprovementInfo->IsConnectsResource(eResource)) || (pkOtherImprovementInfo && pkOtherImprovementInfo->GetResourceFromImprovement() == eResource)) diff --git a/CvGameCoreDLL_Expansion2/CvPlot.cpp b/CvGameCoreDLL_Expansion2/CvPlot.cpp index 8478afb970..f07d17817b 100644 --- a/CvGameCoreDLL_Expansion2/CvPlot.cpp +++ b/CvGameCoreDLL_Expansion2/CvPlot.cpp @@ -2488,7 +2488,7 @@ bool CvPlot::canHaveResource(ResourceTypes eResource, bool bIgnoreLatitude, bool // -------------------------------------------------------------------------------- -bool CvPlot::canHaveImprovement(ImprovementTypes eImprovement, PlayerTypes ePlayer, bool) const +bool CvPlot::canHaveImprovement(ImprovementTypes eImprovement, PlayerTypes ePlayer, bool, bool bCheckAdjacency) const { CvPlot* pLoopPlot = NULL; bool bValid = false; @@ -2808,9 +2808,76 @@ bool CvPlot::canHaveImprovement(ImprovementTypes eImprovement, PlayerTypes ePlay } #endif + if (!bCheckAdjacency) + return true; + + bool bHasLuxuryRequirement = pkImprovementInfo->IsAdjacentLuxury(); + bool bHasNoTwoAdjacencyRequirement = pkImprovementInfo->IsNoTwoAdjacent(); + if (bHasLuxuryRequirement || bHasNoTwoAdjacencyRequirement) + { + bool bLuxuryRequirementMet = !bHasLuxuryRequirement; + for (int iI = 0; iI < NUM_DIRECTION_TYPES; ++iI) + { + CvPlot* pAdjacentPlot = plotDirection(getX(), getY(), ((DirectionTypes)iI)); + if (pAdjacentPlot != NULL) + { + if (bHasLuxuryRequirement) + { + ResourceTypes eResource = pAdjacentPlot->getResourceType(GET_PLAYER(ePlayer).getTeam()); + if (eResource != NO_RESOURCE) + { + CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource); + if (pkResourceInfo && pkResourceInfo->getResourceUsage() == RESOURCEUSAGE_LUXURY) + { + bLuxuryRequirementMet = true; + } + } + } + if (bHasNoTwoAdjacencyRequirement) + { + ImprovementTypes eAdjacentImprovement = pAdjacentPlot->getImprovementType(); + if (eAdjacentImprovement != NO_IMPROVEMENT && eAdjacentImprovement == eImprovement) + { + return false; + } + CvImprovementEntry* pkImprovement2 = GC.getImprovementInfo(eAdjacentImprovement); + if (pkImprovement2 && eAdjacentImprovement != NO_IMPROVEMENT && pkImprovement2->GetImprovementMakesValid(eImprovement)) + { + return false; + } + int iBuildProgress = pAdjacentPlot->getBuildProgress(GetBuildTypeFromImprovement(eImprovement)); + if (iBuildProgress > 0) + { + return false; + } + } + } + } + if (bHasLuxuryRequirement && !bLuxuryRequirementMet) + { + return false; + } + } + return true; } +BuildTypes CvPlot::GetBuildTypeFromImprovement(ImprovementTypes eImprovement) const +{ + for (int iBuildIndex = 0; iBuildIndex < GC.getNumBuildInfos(); iBuildIndex++) + { + BuildTypes eBuild = (BuildTypes)iBuildIndex; + CvBuildInfo* pkBuild = GC.getBuildInfo(eBuild); + + if (NULL != pkBuild && eImprovement == (ImprovementTypes)pkBuild->getImprovement()) + { + return eBuild; + } + } + + return NO_BUILD; +} + // -------------------------------------------------------------------------------- bool CvPlot::canBuild(BuildTypes eBuild, PlayerTypes ePlayer, bool bTestVisible, bool bTestPlotOwner) const @@ -10330,7 +10397,7 @@ int CvPlot::calculateReligionImprovementYield(YieldTypes eYield, PlayerTypes ePl return iReligionChange; } // -------------------------------------------------------------------------------- -int CvPlot::calculateImprovementYield(YieldTypes eYield, PlayerTypes ePlayer, ImprovementTypes eImprovement, RouteTypes eRoute, FeatureTypes eFeature, ResourceTypes eResource, bool bIgnoreCityConnection, const CvCity* pOwningCity, bool bOptimal) const +int CvPlot::calculateImprovementYield(YieldTypes eYield, PlayerTypes ePlayer, ImprovementTypes eImprovement, RouteTypes eRoute, FeatureTypes eFeature, ResourceTypes eResource, RouteTypes eForceCityConnection, const CvCity* pOwningCity, bool bOptimal) const { int iBestYield = 0; int iYield = 0; @@ -10497,24 +10564,24 @@ int CvPlot::calculateImprovementYield(YieldTypes eYield, PlayerTypes ePlayer, Im } } - if (eRoute != NO_ROUTE) + if (eRoute != NO_ROUTE || (eForceCityConnection != NUM_ROUTE_TYPES && eForceCityConnection != NO_ROUTE)) { - if (!bIgnoreCityConnection) + if ((eForceCityConnection == ROUTE_RAILROAD || (eForceCityConnection == NUM_ROUTE_TYPES && IsCityConnection(ePlayer, true /*bIndustrial*/))) && MOD_BALANCE_YIELD_SCALE_ERA) { - if (IsCityConnection(ePlayer, true /*bIndustrial*/) && MOD_BALANCE_YIELD_SCALE_ERA) - { - iYield += pkImprovementInfo->GetRouteYieldChanges(ROUTE_RAILROAD, eYield); - } - else if (IsCityConnection(ePlayer, false /*bIndustrial*/)) - { - iYield += pkImprovementInfo->GetRouteYieldChanges(ROUTE_ROAD, eYield); - } + iYield += pkImprovementInfo->GetRouteYieldChanges(ROUTE_RAILROAD, eYield); + } + else if (eForceCityConnection == ROUTE_ROAD || eForceCityConnection == ROUTE_RAILROAD || (eForceCityConnection == NUM_ROUTE_TYPES && IsCityConnection(ePlayer, false /*bIndustrial*/))) + { + iYield += pkImprovementInfo->GetRouteYieldChanges(ROUTE_ROAD, eYield); } - CvRouteInfo* pkRouteInfo = GC.getRouteInfo(eRoute); - if (pkRouteInfo) + if (eRoute != NO_ROUTE) { - iYield += pkRouteInfo->getYieldChange(eYield); + CvRouteInfo* pkRouteInfo = GC.getRouteInfo(eRoute); + if (pkRouteInfo) + { + iYield += pkRouteInfo->getYieldChange(eYield); + } } } } @@ -10551,9 +10618,9 @@ int CvPlot::calculateImprovementYield(YieldTypes eYield, PlayerTypes ePlayer, Im } #if defined(MOD_RELIGION_PERMANENT_PANTHEON) -int CvPlot::calculatePlayerYield(YieldTypes eYield, int iCurrentYield, PlayerTypes ePlayer, ImprovementTypes eImprovement, FeatureTypes eFeature, ResourceTypes eResource, bool bIgnoreCityConnection, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon, const CvReligion* pPlayerPantheon, bool bDisplay) const +int CvPlot::calculatePlayerYield(YieldTypes eYield, int iCurrentYield, PlayerTypes ePlayer, ImprovementTypes eImprovement, FeatureTypes eFeature, ResourceTypes eResource, RouteTypes eForceCityConnection, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon, const CvReligion* pPlayerPantheon, bool bDisplay) const #else -int CvPlot::calculatePlayerYield(YieldTypes eYield, int iCurrentYield, PlayerTypes ePlayer, ImprovementTypes eImprovement, FeatureTypes eFeature, ResourceTypes eResource, bool bIgnoreCityConnection, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon, bool bDisplay) const +int CvPlot::calculatePlayerYield(YieldTypes eYield, int iCurrentYield, PlayerTypes ePlayer, ImprovementTypes eImprovement, FeatureTypes eFeature, ResourceTypes eResource, RouteTypes eForceCityConnection, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon, bool bDisplay) const #endif { if (ePlayer == NO_PLAYER) @@ -10696,6 +10763,10 @@ int CvPlot::calculatePlayerYield(YieldTypes eYield, int iCurrentYield, PlayerTyp iYield += kTeam.getImprovementNoFreshWaterYieldChange(eImprovement, eYield); } } + + bool bIsCityConnection = IsCityConnection(ePlayer); + if (eForceCityConnection != NUM_ROUTE_TYPES) + bIsCityConnection = eForceCityConnection != NO_ROUTE; // Trait player terrain/improvement (for features handled below) yield changes that don't require a trade route connection if (pTraits->IsTradeRouteOnly() && getOwner() == ePlayer) @@ -10705,7 +10776,7 @@ int CvPlot::calculatePlayerYield(YieldTypes eYield, int iCurrentYield, PlayerTyp int iBonus = pTraits->GetTerrainYieldChange(eTerrain, eYield); if (iBonus > 0) { - if ((IsCityConnection(ePlayer) && !bIgnoreCityConnection) || IsTradeUnitRoute()) + if (bIsCityConnection || IsTradeUnitRoute()) { int iScale = 0; int iEra = (kPlayer.GetCurrentEra() + 1); @@ -10725,7 +10796,7 @@ int CvPlot::calculatePlayerYield(YieldTypes eYield, int iCurrentYield, PlayerTyp int iBonus = pTraits->GetTerrainYieldChange(eTerrain, eYield); if (iBonus > 0) { - if ((IsCityConnection(ePlayer) && !bIgnoreCityConnection) || IsTradeUnitRoute()) + if (bIsCityConnection || IsTradeUnitRoute()) { int iScale = 0; int iEra = (kPlayer.GetCurrentEra() + 1); @@ -10745,7 +10816,7 @@ int CvPlot::calculatePlayerYield(YieldTypes eYield, int iCurrentYield, PlayerTyp int iBonus2 = pTraits->GetImprovementYieldChange(eImprovement, eYield); if (iBonus2 > 0) { - if ((IsCityConnection(ePlayer) && !bIgnoreCityConnection) || IsTradeUnitRoute() || IsAdjacentToTradeRoute()) + if (bIsCityConnection || IsTradeUnitRoute() || IsAdjacentToTradeRoute()) { int iScale = 0; int iEra = (kPlayer.GetCurrentEra() + 1); @@ -11071,8 +11142,8 @@ int CvPlot::calculateYieldFast(YieldTypes eYield, bool bDisplay, const CvCity* p int iYield = calculateNatureYield(eYield, ePlayer, eFeature, eResource, pOwningCity, bDisplay); iYield += calculateReligionImprovementYield(eYield, ePlayer, eImprovement, eResource, pOwningCity, pMajorityReligion, pSecondaryPantheon); iYield += calculateReligionNatureYield(eYield, ePlayer, eImprovement, eFeature, eResource, pOwningCity, pMajorityReligion, pSecondaryPantheon); - iYield += calculateImprovementYield(eYield, ePlayer, eImprovement, eRoute, eFeature, eResource, false, pOwningCity, false); - iYield += calculatePlayerYield(eYield, iYield, ePlayer, eImprovement, eFeature, eResource, false, pOwningCity, pMajorityReligion, pSecondaryPantheon, pPlayerPantheon, bDisplay); + iYield += calculateImprovementYield(eYield, ePlayer, eImprovement, eRoute, eFeature, eResource, NUM_ROUTE_TYPES, pOwningCity, false); + iYield += calculatePlayerYield(eYield, iYield, ePlayer, eImprovement, eFeature, eResource, NUM_ROUTE_TYPES, pOwningCity, pMajorityReligion, pSecondaryPantheon, pPlayerPantheon, bDisplay); #if defined(MOD_RELIGION_PERMANENT_PANTHEON) if (MOD_RELIGION_PERMANENT_PANTHEON && pPlayerPantheon != NULL) { @@ -13753,9 +13824,9 @@ void CvPlot::getVisibleResourceState(ResourceTypes& eType, bool& bImproved, bool // -------------------------------------------------------------------------------- #if defined(MOD_RELIGION_PERMANENT_PANTHEON) -int CvPlot::getYieldWithBuild(BuildTypes eBuild, YieldTypes eYield, bool bWithUpgrade, bool bIgnoreCityConnection, PlayerTypes ePlayer, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon, const CvReligion* pPlayerPantheon) const +int CvPlot::getYieldWithBuild(BuildTypes eBuild, YieldTypes eYield, bool bWithUpgrade, RouteTypes eForceCityConnection, PlayerTypes ePlayer, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon, const CvReligion* pPlayerPantheon) const #else -int CvPlot::getYieldWithBuild(BuildTypes eBuild, YieldTypes eYield, bool bWithUpgrade, bool bIgnoreCityConnection, PlayerTypes ePlayer, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon) const +int CvPlot::getYieldWithBuild(BuildTypes eBuild, YieldTypes eYield, bool bWithUpgrade, RouteTypes eForceCityConnection, PlayerTypes ePlayer, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon) const #endif { int iYield = 0; @@ -13798,7 +13869,7 @@ int CvPlot::getYieldWithBuild(BuildTypes eBuild, YieldTypes eYield, bool bWithUp if (GC.getBuildInfo(eBuild)->isFeatureRemove(getFeatureType())) { if (GET_PLAYER(ePlayer).GetPlayerTraits()->IsWoodlandMovementBonus() && (eFeature == FEATURE_FOREST || eFeature == FEATURE_JUNGLE) && eNewRoute == NO_ROUTE) - bIgnoreCityConnection = true; + eForceCityConnection = NO_ROUTE; eFeature = NO_FEATURE; } @@ -13870,7 +13941,7 @@ int CvPlot::getYieldWithBuild(BuildTypes eBuild, YieldTypes eYield, bool bWithUp } } - iYield += calculateImprovementYield(eYield, ePlayer, eNewImprovement, eNewRoute, eFeature, eResource, bIgnoreCityConnection, pOwningCity, false) + calculateReligionImprovementYield(eYield, ePlayer, eNewImprovement, eResource, pOwningCity, pMajorityReligion, pSecondaryPantheon); + iYield += calculateImprovementYield(eYield, ePlayer, eNewImprovement, eNewRoute, eFeature, eResource, eForceCityConnection, pOwningCity, false) + calculateReligionImprovementYield(eYield, ePlayer, eNewImprovement, eResource, pOwningCity, pMajorityReligion, pSecondaryPantheon); #if defined(MOD_RELIGION_PERMANENT_PANTHEON) if (MOD_RELIGION_PERMANENT_PANTHEON && pPlayerPantheon != NULL) { @@ -13880,9 +13951,9 @@ int CvPlot::getYieldWithBuild(BuildTypes eBuild, YieldTypes eYield, bool bWithUp } #if defined(MOD_RELIGION_PERMANENT_PANTHEON) - iYield += calculatePlayerYield(eYield, iYield, ePlayer, eNewImprovement, eFeature, eResource, bIgnoreCityConnection, pOwningCity, pMajorityReligion, pSecondaryPantheon, pPlayerPantheon, false); + iYield += calculatePlayerYield(eYield, iYield, ePlayer, eNewImprovement, eFeature, eResource, eForceCityConnection, pOwningCity, pMajorityReligion, pSecondaryPantheon, pPlayerPantheon, false); #else - iYield += calculatePlayerYield(eYield, iYield, ePlayer, eNewImprovement, eFeature, eResource, bIgnoreCityConnection, pOwningCity, pMajorityReligion, pSecondaryPantheon, false); + iYield += calculatePlayerYield(eYield, iYield, ePlayer, eNewImprovement, eFeature, eResource, eForceCityConnection, pOwningCity, pMajorityReligion, pSecondaryPantheon, false); #endif //no overhead if empty diff --git a/CvGameCoreDLL_Expansion2/CvPlot.h b/CvGameCoreDLL_Expansion2/CvPlot.h index f2b7409a31..2f4d4c5834 100644 --- a/CvGameCoreDLL_Expansion2/CvPlot.h +++ b/CvGameCoreDLL_Expansion2/CvPlot.h @@ -168,7 +168,8 @@ class CvPlot void updateSeeFromSight(bool bIncrement, bool bRecalculate); bool canHaveResource(ResourceTypes eResource, bool bIgnoreLatitude = false, bool bIgnoreCiv = false) const; - bool canHaveImprovement(ImprovementTypes eImprovement, PlayerTypes ePlayer = NO_PLAYER, bool bOnlyTestVisible = false) const; + bool canHaveImprovement(ImprovementTypes eImprovement, PlayerTypes ePlayer = NO_PLAYER, bool bOnlyTestVisible = false, bool bCheckAdjacency = false) const; + BuildTypes GetBuildTypeFromImprovement(ImprovementTypes eImprovement) const; bool canBuild(BuildTypes eBuild, PlayerTypes ePlayer = NO_PLAYER, bool bTestVisible = false, bool bTestPlotOwner = true) const; int getBuildTime(BuildTypes eBuild, PlayerTypes ePlayer) const; @@ -585,12 +586,12 @@ class CvPlot int calculateBestNatureYield(YieldTypes eYield, PlayerTypes ePlayer) const; int calculateTotalBestNatureYield(PlayerTypes ePlayer) const; - int calculateImprovementYield(YieldTypes eYield, PlayerTypes ePlayer, ImprovementTypes eImprovement, RouteTypes eRoute, FeatureTypes eFeature, ResourceTypes eResource, bool bIgnoreCityConnection, const CvCity* pOwningCity, bool bOptimal = false) const; + int calculateImprovementYield(YieldTypes eYield, PlayerTypes ePlayer, ImprovementTypes eImprovement, RouteTypes eRoute, FeatureTypes eFeature, ResourceTypes eResource, RouteTypes eForceCityConnection, const CvCity* pOwningCity, bool bOptimal = false) const; #if defined(MOD_RELIGION_PERMANENT_PANTHEON) - int calculatePlayerYield(YieldTypes eYield, int iCurrentYield, PlayerTypes ePlayer, ImprovementTypes eImprovement, FeatureTypes eFeature, ResourceTypes eResource, bool bIgnoreCityConnection, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon, const CvReligion* pPlayerPantheon, bool bDisplay) const; + int calculatePlayerYield(YieldTypes eYield, int iCurrentYield, PlayerTypes ePlayer, ImprovementTypes eImprovement, FeatureTypes eFeature, ResourceTypes eResource, RouteTypes eForceCityConnection, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon, const CvReligion* pPlayerPantheon, bool bDisplay) const; #else - int calculatePlayerYield(YieldTypes eYield, int iCurrentYield, PlayerTypes ePlayer, ImprovementTypes eImprovement, FeatureTypes eFeature, ResourceTypes eResource, bool bIgnoreCityConnection, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon, bool bDisplay) const; + int calculatePlayerYield(YieldTypes eYield, int iCurrentYield, PlayerTypes ePlayer, ImprovementTypes eImprovement, FeatureTypes eFeature, ResourceTypes eResource, RouteTypes eForceCityConnection, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon, bool bDisplay) const; #endif int calculateReligionNatureYield(YieldTypes eYield, PlayerTypes ePlayer, ImprovementTypes eImprovement, FeatureTypes eFeature, ResourceTypes eResource, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon) const; @@ -613,9 +614,9 @@ class CvPlot #endif #if defined(MOD_RELIGION_PERMANENT_PANTHEON) - int getYieldWithBuild(BuildTypes eBuild, YieldTypes eYield, bool bWithUpgrade, bool bIgnoreCityConnection, PlayerTypes ePlayer, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon, const CvReligion* pPlayerPantheon = NULL) const; + int getYieldWithBuild(BuildTypes eBuild, YieldTypes eYield, bool bWithUpgrade, RouteTypes eForceCityConnection, PlayerTypes ePlayer, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon, const CvReligion* pPlayerPantheon = NULL) const; #else - int getYieldWithBuild(BuildTypes eBuild, YieldTypes eYield, bool bWithUpgrade, bool bIgnoreCityConnection, PlayerTypes ePlayer, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon) const; + int getYieldWithBuild(BuildTypes eBuild, YieldTypes eYield, bool bWithUpgrade, RouteTypes eForceCityConnection, PlayerTypes ePlayer, const CvCity* pOwningCity, const CvReligion* pMajorityReligion, const CvBeliefEntry* pSecondaryPantheon) const; #endif int countNumAirUnits(TeamTypes eTeam, bool bNoSuicide = false) const; diff --git a/CvGameCoreDLL_Expansion2/CvUnit.cpp b/CvGameCoreDLL_Expansion2/CvUnit.cpp index af8b87362c..35c950a4b1 100644 --- a/CvGameCoreDLL_Expansion2/CvUnit.cpp +++ b/CvGameCoreDLL_Expansion2/CvUnit.cpp @@ -13756,7 +13756,7 @@ bool CvUnit::blastTourism() // -------------------------------------------------------------------------------- -bool CvUnit::canBuild(const CvPlot* pPlot, BuildTypes eBuild, bool bTestVisible, bool bTestGold) const +bool CvUnit::canBuild(const CvPlot* pPlot, BuildTypes eBuild, bool bTestVisible, bool bTestGold, bool bTestEra) const { VALIDATE_OBJECT CvAssertMsg(eBuild < GC.getNumBuildInfos() && eBuild >= 0, "Index out of bounds"); @@ -13793,7 +13793,7 @@ bool CvUnit::canBuild(const CvPlot* pPlot, BuildTypes eBuild, bool bTestVisible, if (!pPlot) return true; - if (!(GET_PLAYER(getOwner()).canBuild(pPlot, eBuild, false, bTestVisible, bTestGold, true, this))) + if (!(GET_PLAYER(getOwner()).canBuild(pPlot, eBuild, bTestEra, bTestVisible, bTestGold, true, this))) { return false; } diff --git a/CvGameCoreDLL_Expansion2/CvUnit.h b/CvGameCoreDLL_Expansion2/CvUnit.h index ff8038b8aa..4c00060bde 100644 --- a/CvGameCoreDLL_Expansion2/CvUnit.h +++ b/CvGameCoreDLL_Expansion2/CvUnit.h @@ -555,7 +555,7 @@ class CvUnit int getBlastTourismTurns(); bool blastTourism(); - bool canBuild(const CvPlot* pPlot, BuildTypes eBuild, bool bTestVisible = false, bool bTestGold = true) const; + bool canBuild(const CvPlot* pPlot, BuildTypes eBuild, bool bTestVisible = false, bool bTestGold = true, bool bTestEra = false) const; bool build(BuildTypes eBuild); #if defined(MOD_CIV6_WORKER) diff --git a/CvGameCoreDLL_Expansion2/Lua/CvLuaPlot.cpp b/CvGameCoreDLL_Expansion2/Lua/CvLuaPlot.cpp index f998b0f1b5..56aad9e104 100644 --- a/CvGameCoreDLL_Expansion2/Lua/CvLuaPlot.cpp +++ b/CvGameCoreDLL_Expansion2/Lua/CvLuaPlot.cpp @@ -1714,7 +1714,7 @@ int CvLuaPlot::lCalculateImprovementYieldChange(lua_State* L) if (pkPlot->IsRoutePillaged()) eRoute = NO_ROUTE; - const int iResult = pkPlot->calculateImprovementYield(eYield, ePlayer, eImprovement, eRoute, pkPlot->getFeatureType(), pkPlot->getResourceType(GET_PLAYER(ePlayer).getTeam()), false, pkPlot->getEffectiveOwningCity(), bOptimal); + const int iResult = pkPlot->calculateImprovementYield(eYield, ePlayer, eImprovement, eRoute, pkPlot->getFeatureType(), pkPlot->getResourceType(GET_PLAYER(ePlayer).getTeam()), NUM_ROUTE_TYPES, pkPlot->getEffectiveOwningCity(), bOptimal); lua_pushinteger(L, iResult); return 1; } @@ -1753,7 +1753,7 @@ int CvLuaPlot::lGetYieldWithBuild(lua_State* L) const CvReligion* pReligion = (eMajority != NO_RELIGION) ? GC.getGame().GetGameReligions()->GetReligion(eMajority, pOwningCity->getOwner()) : 0; const CvBeliefEntry* pBelief = (eSecondaryPantheon != NO_BELIEF) ? GC.GetGameBeliefs()->GetEntry(eSecondaryPantheon) : 0; - int iResult = pkPlot->getYieldWithBuild(eBuild, eYield, bUpgrade, false, ePlayer, pOwningCity, pReligion, pBelief); + int iResult = pkPlot->getYieldWithBuild(eBuild, eYield, bUpgrade, NUM_ROUTE_TYPES, ePlayer, pOwningCity, pReligion, pBelief); #if defined(MOD_RELIGION_PERMANENT_PANTHEON) // Mod for civs keeping their pantheon belief forever if (MOD_RELIGION_PERMANENT_PANTHEON) @@ -1766,7 +1766,7 @@ int CvLuaPlot::lGetYieldWithBuild(lua_State* L) { if (pReligion == NULL || (pReligion != NULL && !pReligion->m_Beliefs.IsPantheonBeliefInReligion(ePantheonBelief, eMajority, pOwningCity->getOwner()))) // check that the our religion does not have our belief, to prevent double counting { - iResult += pkPlot->getYieldWithBuild(eBuild, eYield, bUpgrade, false, ePlayer, pOwningCity, pPantheon, NULL); + iResult += pkPlot->getYieldWithBuild(eBuild, eYield, bUpgrade, NUM_ROUTE_TYPES, ePlayer, pOwningCity, pPantheon, NULL); } } } @@ -1777,7 +1777,7 @@ int CvLuaPlot::lGetYieldWithBuild(lua_State* L) } else { - const int iResult = pkPlot->getYieldWithBuild(eBuild, eYield, bUpgrade, false, ePlayer, NULL, NULL, NULL); + const int iResult = pkPlot->getYieldWithBuild(eBuild, eYield, bUpgrade, NUM_ROUTE_TYPES, ePlayer, NULL, NULL, NULL); lua_pushinteger(L, iResult); return 1; }