View Full Version : Advice on modding civics...


Event Horizon
Dec 20, 2007, 12:37 PM
I've added one more Civic to each cathegory, but not without some problems. Testing shows that some are always adopted by the AI as soon as available, and some never are.

I'll begin with the new Economy one:

- Unlimited Merchants.
- 50% less Corporation maintenance.
- +3 Commerce from Workshop and Offshore Platform.
- +3 unhealthiness in all cities.

Seems very attractive to the AI. Please help me at correcting unbalances.

Many thanks.

ori
Dec 20, 2007, 12:47 PM
this is not an easy question, here is the code the AI uses to value civics:


int CvPlayerAI::AI_civicValue(CivicTypes eCivic)
{
PROFILE_FUNC();

bool bWarPlan;
int iConnectedForeignCities;
int iTotalReligonCount;
int iHighestReligionCount;
int iWarmongerPercent;
int iHappiness;
int iValue;
int iTempValue;
int iI, iJ;

bool bCultureVictory3 = AI_isDoStrategy(AI_STRATEGY_CULTURE3);
bool bCultureVictory2 = AI_isDoStrategy(AI_STRATEGY_CULTURE2);

FAssertMsg(eCivic < GC.getNumCivicInfos(), "eCivic is expected to be within maximum bounds (invalid Index)");
FAssertMsg(eCivic >= 0, "eCivic is expected to be non-negative (invalid Index)");

CvCivicInfo& kCivic = GC.getCivicInfo(eCivic);

bWarPlan = (GET_TEAM(getTeam()).getAnyWarPlanCount(true) > 0);

iConnectedForeignCities = countPotentialForeignTradeCitiesConnected();
iTotalReligonCount = countTotalHasReligion();
iHighestReligionCount = findHighestHasReligionCount();
iWarmongerPercent = 25000 / std::max(100, (100 + GC.getLeaderHeadInfo(getPersonalityType()).getMaxW arRand()));

iValue = (getNumCities() * 6);

iValue += (GC.getCivicInfo(eCivic).getAIWeight() * getNumCities());

iValue += (getCivicPercentAnger(eCivic) / 10);

iValue += -(GC.getCivicInfo(eCivic).getAnarchyLength() * getNumCities());

iValue += -(getSingleCivicUpkeep(eCivic, true));

iValue += ((kCivic.getGreatPeopleRateModifier() * getNumCities()) / 10);
iValue += ((kCivic.getGreatGeneralRateModifier() * getNumMilitaryUnits()) / 50);
iValue += ((kCivic.getDomesticGreatGeneralRateModifier() * getNumMilitaryUnits()) / 100);
iValue += -((kCivic.getDistanceMaintenanceModifier() * std::max(0, (getNumCities() - 3))) / 8);
iValue += -((kCivic.getNumCitiesMaintenanceModifier() * std::max(0, (getNumCities() - 3))) / 8);
iValue += (kCivic.getFreeExperience() * getNumCities() * (bWarPlan ? 8 : 5) * iWarmongerPercent) / 100;
iValue += ((kCivic.getWorkerSpeedModifier() * AI_getNumAIUnits(UNITAI_WORKER)) / 15);
iValue += ((kCivic.getImprovementUpgradeRateModifier() * getNumCities()) / 50);
iValue += (kCivic.getMilitaryProductionModifier() * getNumCities() * iWarmongerPercent) / (bWarPlan ? 300 : 500 );
iValue += (kCivic.getBaseFreeUnits() / 2);
iValue += (kCivic.getBaseFreeMilitaryUnits() / 3);
iValue += ((kCivic.getFreeUnitsPopulationPercent() * getTotalPopulation()) / 200);
iValue += ((kCivic.getFreeMilitaryUnitsPopulationPercent() * getTotalPopulation()) / 300);
iValue += -(kCivic.getGoldPerUnit() * getNumUnits());
iValue += -(kCivic.getGoldPerMilitaryUnit() * getNumMilitaryUnits() * iWarmongerPercent) / 200;

//iValue += ((kCivic.isMilitaryFoodProduction()) ? 0 : 0);
iValue += (getWorldSizeMaxConscript(eCivic) * ((bWarPlan) ? (20 + getNumCities()) : ((8 + getNumCities()) / 2)));
iValue += ((kCivic.isNoUnhealthyPopulation()) ? (getTotalPopulation() / 3) : 0);
if (bWarPlan)
{
iValue += ((kCivic.getExpInBorderModifier() * getNumMilitaryUnits()) / 200);
}
iValue += ((kCivic.isBuildingOnlyHealthy()) ? (getNumCities() * 3) : 0);
iValue += -((kCivic.getWarWearinessModifier() * getNumCities()) / ((bWarPlan) ? 10 : 50));
iValue += (kCivic.getFreeSpecialist() * getNumCities() * 12);
iValue += ((kCivic.getTradeRoutes() * std::max(0, iConnectedForeignCities - getNumCities() * 3) * 6) + (getNumCities() * 2));
iValue += -((kCivic.isNoForeignTrade()) ? (iConnectedForeignCities * 3) : 0);
if (kCivic.isNoCorporations())
{
iValue -= countHeadquarters() * (40 + 3 * getNumCities());
}
if (kCivic.isNoForeignCorporations())
{
for (int iCorp = 0; iCorp < GC.getNumCorporationInfos(); ++iCorp)
{
if (!GET_TEAM(getTeam()).hasHeadquarters((Corporation Types)iCorp))
{
iValue += countCorporations((CorporationTypes)iCorp) * 3;
}
}
}
if (kCivic.getCorporationMaintenanceModifier() != 0)
{
int iCorpCount = 0;
int iHQCount = 0;
for (int iCorp = 0; iCorp < GC.getNumCorporationInfos(); ++iCorp)
{
if (GET_TEAM(getTeam()).hasHeadquarters((CorporationT ypes)iCorp))
{
iHQCount++;
}
iCorpCount += countCorporations((CorporationTypes)iCorp);
}
iValue += (-kCivic.getCorporationMaintenanceModifier() * (iHQCount * (25 + getNumCities() * 2) + iCorpCount * 7)) / 25;

}

if (kCivic.getCivicPercentAnger() != 0)
{
int iNumOtherCities = GC.getGameINLINE().getNumCities() - getNumCities();
iValue += (30 * getNumCities() * getCivicPercentAnger(eCivic, true)) / kCivic.getCivicPercentAnger();

int iTargetGameTurn = 2 * getNumCities() * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpee dType()).getGrowthPercent();
iTargetGameTurn /= GC.getGame().countCivPlayersEverAlive();
iTargetGameTurn += GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpee dType()).getGrowthPercent() * 30;

iTargetGameTurn /= 100;
iTargetGameTurn = std::max(10, iTargetGameTurn);

int iElapsedTurns = GC.getGame().getElapsedGameTurns();

if (iElapsedTurns > iTargetGameTurn)
{
iValue += (std::min(iTargetGameTurn, iElapsedTurns - iTargetGameTurn) * (iNumOtherCities * kCivic.getCivicPercentAnger())) / (15 * iTargetGameTurn);
}
}

if (kCivic.getExtraHealth() != 0)
{
iValue += (getNumCities() * 6 * AI_getHealthWeight(isCivic(eCivic) ? -kCivic.getExtraHealth() : kCivic.getExtraHealth(), 1)) / 100;
}

iTempValue = kCivic.getHappyPerMilitaryUnit() * 3;
if (iTempValue != 0)
{
iValue += (getNumCities() * 9 * AI_getHappinessWeight(isCivic(eCivic) ? -iTempValue : iTempValue, 1)) / 100;
}

iTempValue = kCivic.getLargestCityHappiness();
if (iTempValue != 0)
{
iValue += (12 * std::min(getNumCities(), GC.getWorldInfo(GC.getMapINLINE().getWorldSize()). getTargetNumCities()) * AI_getHappinessWeight(isCivic(eCivic) ? -iTempValue : iTempValue, 1)) / 100;
}

if (kCivic.getWarWearinessModifier() != 0)
{
int iAngerPercent = getWarWearinessPercentAnger();
int iPopulation = 3 + (getTotalPopulation() / std::max(1, getNumCities()));

int iTempValue = (-kCivic.getWarWearinessModifier() * iAngerPercent * iPopulation) / (GC.getPERCENT_ANGER_DIVISOR() * 100);
if (iTempValue != 0)
{
iValue += (11 * getNumCities() * AI_getHappinessWeight(isCivic(eCivic) ? -iTempValue : iTempValue, 1)) / 100;
}
}

iValue += (kCivic.getNonStateReligionHappiness() * (iTotalReligonCount - iHighestReligionCount) * 5);

if (kCivic.isStateReligion())
{
if (iHighestReligionCount > 0)
{
iValue += iHighestReligionCount;

iValue += ((kCivic.isNoNonStateReligionSpread()) ? ((getNumCities() - iHighestReligionCount) * 2) : 0);
iValue += (kCivic.getStateReligionHappiness() * iHighestReligionCount * 4);
iValue += ((kCivic.getStateReligionGreatPeopleRateModifier() * iHighestReligionCount) / 20);
iValue += (kCivic.getStateReligionGreatPeopleRateModifier() / 4);
iValue += ((kCivic.getStateReligionUnitProductionModifier() * iHighestReligionCount) / 4);
iValue += ((kCivic.getStateReligionBuildingProductionModifie r() * iHighestReligionCount) / 3);
iValue += (kCivic.getStateReligionFreeExperience() * iHighestReligionCount * ((bWarPlan) ? 6 : 2));
}
}

for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
iTempValue = 0;

iTempValue += ((kCivic.getYieldModifier(iI) * getNumCities()) / 2);
iTempValue += ((kCivic.getCapitalYieldModifier(iI) * 3) / 4);
CvCity* pCapital = getCapitalCity();
if (pCapital)
{
iTempValue += ((kCivic.getCapitalYieldModifier(iI) * pCapital->getBaseYieldRate((YieldTypes)iI)) / 80);
}
iTempValue += ((kCivic.getTradeYieldModifier(iI) * getNumCities()) / 11);

for (iJ = 0; iJ < GC.getNumImprovementInfos(); iJ++)
{
iTempValue += (AI_averageYieldMultiplier((YieldTypes)iI) * (kCivic.getImprovementYieldChanges(iJ, iI) * (getImprovementCount((ImprovementTypes)iJ) + getNumCities() * 2))) / 100;
}

if (iI == YIELD_FOOD)
{
iTempValue *= 3;
}
else if (iI == YIELD_PRODUCTION)
{
iTempValue *= ((AI_avoidScience()) ? 6 : 2);
}
else if (iI == YIELD_COMMERCE)
{
iTempValue *= ((AI_avoidScience()) ? 1 : 2);
}

iValue += iTempValue;
}

for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
iTempValue = 0;

iTempValue += ((kCivic.getCommerceModifier(iI) * getNumCities()) / 3);
iTempValue += (kCivic.getCapitalCommerceModifier(iI) / 2);
if (iI == COMMERCE_ESPIONAGE)
{
iTempValue *= AI_getEspionageWeight();
iTempValue /= 500;
}
iTempValue += ((kCivic.getSpecialistExtraCommerce(iI) * getTotalPopulation()) / 15);

iTempValue *= AI_commerceWeight((CommerceTypes)iI);

if ((iI == COMMERCE_CULTURE) && bCultureVictory2)
{
iTempValue *= 2;
if (bCultureVictory3)
{
iTempValue *= 2;
}
}
iTempValue /= 100;

iValue += iTempValue;
}

for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
{
if (kCivic.getBuildingHappinessChanges(iI) != 0)
{
iValue += (kCivic.getBuildingHappinessChanges(iI) * getBuildingClassCount((BuildingClassTypes)iI) * 3);
}
}

for (iI = 0; iI < GC.getNumFeatureInfos(); iI++)
{
iHappiness = kCivic.getFeatureHappinessChanges(iI);

if (iHappiness != 0)
{
iValue += (iHappiness * countCityFeatures((FeatureTypes)iI) * 5);
}
}

for (iI = 0; iI < GC.getNumHurryInfos(); iI++)
{
if (kCivic.isHurry(iI))
{
iTempValue = 0;

if (GC.getHurryInfo((HurryTypes)iI).getGoldPerProduct ion() > 0)
{
iTempValue += ((((AI_avoidScience()) ? 50 : 25) * getNumCities()) / GC.getHurryInfo((HurryTypes)iI).getGoldPerProducti on());
}
iTempValue += (GC.getHurryInfo((HurryTypes)iI).getProductionPerP opulation() * getNumCities() * (bWarPlan ? 2 : 1)) / 5;
iValue += iTempValue;
}
}

for (iI = 0; iI < GC.getNumSpecialBuildingInfos(); iI++)
{
if (kCivic.isSpecialBuildingNotRequired(iI))
{
iValue += ((getNumCities() / 2) + 1); // XXX
}
}

for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
{
iTempValue = 0;
if (kCivic.isSpecialistValid(iI))
{
iTempValue += ((getNumCities() * (bCultureVictory3 ? 10 : 1)) + 6);
}
iValue += (iTempValue / 2);
}

if (GC.getLeaderHeadInfo(getPersonalityType()).getFav oriteCivic() == eCivic)
{
if (!kCivic.isStateReligion() || iHighestReligionCount > 0)
{
iValue *= 5;
iValue /= 4;
iValue += 6 * getNumCities();
iValue += 20;
}
}

if (AI_isDoStrategy(AI_STRATEGY_CULTURE2) && (GC.getCivicInfo(eCivic).isNoNonStateReligionSprea d()))
{
iValue /= 10;
}

return iValue;
}




as you can see the way it values stuff is very detailed but some of the stuff you are adding is not really included, especially the additional :yuck:

Event Horizon
Dec 22, 2007, 02:25 PM
Sad.

I'll try putting only Unlimited Merchants and 1/2 Corporation maintenance in...