Map scripts and space terrains

pepper2000

King
Joined
Apr 14, 2013
Messages
899
There is much to be said about designing map scripts that create maps to incorporate the soon-to-be-added space terrains. But right now I have one question.

It seems like most of the C2C map scripts surround the starting players with some of the highest-yielding terrains available. This is a problem if I want to add special high-yield terrains in space (e.g. Green Mars, which can be created at Planetary Terraforming). Does anyone know a way to avoid this behavior?
 
There is much to be said about designing map scripts that create maps to incorporate the soon-to-be-added space terrains. But right now I have one question.

It seems like most of the C2C map scripts surround the starting players with some of the highest-yielding terrains available. This is a problem if I want to add special high-yield terrains in space (e.g. Green Mars, which can be created at Planetary Terraforming). Does anyone know a way to avoid this behavior?
The "world" map-script doesn't do this, so it can be avoided by making some kind of change to the other map-scripts.
Map scripts that doesn't run this function: normalizeAddGoodTerrain() will do what you describe.

If the map script doesn't run that function a default version of that function will be run inside the dll.
Spoiler Relevant code in CvGame.cpp :
Code:
    if (!PYTHON_CALL_FUNCTION(__FUNCTION__, gDLL->getPythonIFace()->getMapScriptModule(), "normalizeAddGoodTerrain", NULL)  || gDLL->getPythonIFace()->pythonUsingDefaultImpl())
    {
        normalizeAddGoodTerrain();
    }
If python didn't run a function called normalizeAddGoodTerrain (A function defined in python)
Run normalizeAddGoodTerrain (A different function defined in the dll)​
Code:
void CvGame::normalizeAddGoodTerrain()
{
    CvPlot* pStartingPlot;
    CvPlot* pLoopPlot;
    bool bChanged;
    int iGoodPlot;
    int iI, iJ, iK;

    for (iI = 0; iI < MAX_PC_PLAYERS; iI++)
    {
        if (GET_PLAYER((PlayerTypes)iI).isAlive())
        {
            pStartingPlot = GET_PLAYER((PlayerTypes)iI).getStartingPlot();

            if (pStartingPlot != NULL)
            {
                iGoodPlot = 0;

                for (iJ = 0; iJ < NUM_CITY_PLOTS; iJ++)
                {
                    pLoopPlot = plotCity(pStartingPlot->getX_INLINE(), pStartingPlot->getY_INLINE(), iJ);

                    if (pLoopPlot != NULL)
                    {
                        if (pLoopPlot != pStartingPlot)
                        {
                            if ((pLoopPlot->calculateNatureYield(YIELD_FOOD, GET_PLAYER((PlayerTypes)iI).getTeam()) >= GC.getFOOD_CONSUMPTION_PER_POPULATION()) &&
                                  (pLoopPlot->calculateNatureYield(YIELD_PRODUCTION, GET_PLAYER((PlayerTypes)iI).getTeam()) > 0))
                            {
                                iGoodPlot++;
                            }
                        }
                    }
                }

                for (iJ = 0; iJ < NUM_CITY_PLOTS; iJ++)
                {
                    if (iGoodPlot >= 4)
                    {
                        break;
                    }

                    pLoopPlot = plotCity(pStartingPlot->getX_INLINE(), pStartingPlot->getY_INLINE(), iJ);

                    if (pLoopPlot != NULL)
                    {
                        if (pLoopPlot != pStartingPlot)
                        {
                            if (!(pLoopPlot->isWater()))
                            {
                                if (!(pLoopPlot->isHills()))
                                {
                                    if (pLoopPlot->getBonusType() == NO_BONUS)
                                    {
                                        bChanged = false;

                                        if (pLoopPlot->calculateNatureYield(YIELD_FOOD, GET_PLAYER((PlayerTypes)iI).getTeam()) < GC.getFOOD_CONSUMPTION_PER_POPULATION())
                                        {
                                            for (iK = 0; iK < GC.getNumTerrainInfos(); iK++)
                                            {
                                                if (!(GC.getTerrainInfo((TerrainTypes)iK).isWater()))
                                                {
                                                    if (GC.getTerrainInfo((TerrainTypes)iK).getYield(YIELD_FOOD) >= GC.getFOOD_CONSUMPTION_PER_POPULATION())
                                                    {
                                                        pLoopPlot->setTerrainType((TerrainTypes)iK);
                                                        bChanged = true;
                                                        break;
                                                    }
                                                }
                                            }
                                        }

                                        if (pLoopPlot->calculateNatureYield(YIELD_PRODUCTION, GET_PLAYER((PlayerTypes)iI).getTeam()) == 0)
                                        {
                                            for (iK = 0; iK < GC.getNumFeatureInfos(); iK++)
                                            {
                                                if ((GC.getFeatureInfo((FeatureTypes)iK).getYieldChange(YIELD_FOOD) >= 0) &&
                                                      (GC.getFeatureInfo((FeatureTypes)iK).getYieldChange(YIELD_PRODUCTION) > 0))
                                                {
                                                    if (GC.getFeatureInfo((FeatureTypes)iK).isTerrain(pLoopPlot->getTerrainType()))
                                                    {
                                                        pLoopPlot->setFeatureType((FeatureTypes)iK);
                                                        bChanged = true;
                                                        break;
                                                    }
                                                }
                                            }
                                        }

                                        if (bChanged)
                                        {
                                            iGoodPlot++;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

 
Last edited:
The current mapscripts add sea grass or kelp into all the sea plots near a city because they are features and add food. They don't get added elsewhere. We had the same problem with reefs when we first added them but got much better placement when we took all the yields off them. The same probably needs to be done with the sea grass and kelp features. It is only the water terrain features that show this strange behaviour suggesting that the way those plots are done is different to the way land plots are done.
 
The current mapscripts add sea grass or kelp into all the sea plots near a city because they are features and add food. They don't get added elsewhere. We had the same problem with reefs when we first added them but got much better placement when we took all the yields off them. The same probably needs to be done with the sea grass and kelp features. It is only the water terrain features that show this strange behaviour suggesting that the way those plots are done is different to the way land plots are done.
True, some map scripts does this directly, but some of them doesn't touch the starting positions at all, and for those it happens within the dll instead.
The normalizeAddGoodTerrain function I mentioned in my last post is one source of this event.

Another source for this is if the map script doesn't define a function called normalizeAddExtras.
Spoiler Then this function in the dll is called: :

Code:
void CvGame::normalizeAddExtras()
{
    bool bIgnoreLatitude = pythonIsBonusIgnoreLatitudes();

    int iTotalValue = 0;
    int iPlayerCount = 0;
    int iBestValue = 0;
    int iWorstValue = MAX_INT;
    int    iI, iJ, iK;

    for (iI = 0; iI < MAX_PC_PLAYERS; iI++)
    {
        if (GET_PLAYER((PlayerTypes)iI).isAlive())
        {
            CvPlot* pStartingPlot = GET_PLAYER((PlayerTypes)iI).getStartingPlot();

            if (pStartingPlot != NULL)
            {
                int iValue = GET_PLAYER((PlayerTypes)iI).AI_foundValue(pStartingPlot->getX_INLINE(), pStartingPlot->getY_INLINE(), -1, true);
                iTotalValue += iValue;
                iPlayerCount++;
     
                iBestValue = std::max(iValue, iBestValue);
                iWorstValue = std::min(iValue, iWorstValue);
            }
        }
    }

    //iTargetValue = (iTotalValue + iBestValue) / (iPlayerCount + 1);
    int iTargetValue = (iBestValue * 4) / 5;
 
    for (iI = 0; iI < MAX_PC_PLAYERS; iI++)
    {
        if (GET_PLAYER((PlayerTypes)iI).isAlive())
        {
            gDLL->callUpdater();    // allow window to update during launch
            CvPlot* pStartingPlot = GET_PLAYER((PlayerTypes)iI).getStartingPlot();

            if (pStartingPlot != NULL)
            {
                int iCount = 0;
                int iFeatureCount = 0;
                int aiShuffle[NUM_CITY_PLOTS];
                shuffleArray(aiShuffle, NUM_CITY_PLOTS, getMapRand());

                for (iJ = 0; iJ < NUM_CITY_PLOTS; iJ++)
                {
                    if (GET_PLAYER((PlayerTypes)iI).AI_foundValue(pStartingPlot->getX_INLINE(), pStartingPlot->getY_INLINE(), -1, true) >= iTargetValue)
                    {
                        break;
                    }
                    if (getSorenRandNum((iCount + 2), "Setting Feature Type") <= 1)
                    {
                        CvPlot* pLoopPlot = plotCity(pStartingPlot->getX_INLINE(), pStartingPlot->getY_INLINE(), aiShuffle[iJ]);

                        if (pLoopPlot != NULL)
                        {
                            if (pLoopPlot != pStartingPlot)
                            {
                                if (pLoopPlot->getBonusType() == NO_BONUS)
                                {
                                    if (pLoopPlot->getFeatureType() == NO_FEATURE)
                                    {
                                        for (int iK = 0; iK < GC.getNumFeatureInfos(); iK++)
                                        {
                                            if ((GC.getFeatureInfo((FeatureTypes)iK).getYieldChange(YIELD_FOOD) + GC.getFeatureInfo((FeatureTypes)iK).getYieldChange(YIELD_PRODUCTION)) > 0)
                                            {
                                                if (pLoopPlot->canHaveFeature((FeatureTypes)iK))
                                                {
                                                    pLoopPlot->setFeatureType((FeatureTypes)iK);
                                                    iCount++;
                                                    break;
                                                }
                                            }
                                        }
                                    }
                         
                                    iFeatureCount += (pLoopPlot->getFeatureType() != NO_FEATURE) ? 1 : 0;
                                }
                            }
                        }
                    }
                }
     
                int iCoastFoodCount = 0;
                int iOceanFoodCount = 0;
                int iOtherCount = 0;
                int iWaterCount = 0;
                for (iJ = 0; iJ < NUM_CITY_PLOTS; iJ++)
                {
                    CvPlot* pLoopPlot = plotCity(pStartingPlot->getX_INLINE(), pStartingPlot->getY_INLINE(), iJ);
                    if (pLoopPlot != NULL)
                    {
                        if (pLoopPlot != pStartingPlot)
                        {
                            if (pLoopPlot->isWater())
                            {
                                iWaterCount++;
                                if (pLoopPlot->getBonusType() != NO_BONUS)
                                {
                                    if (pLoopPlot->isAdjacentToLand())
                                    {
                                        iCoastFoodCount++;
                                    }
                                    else
                                    {
                                        iOceanFoodCount++;                            
                                    }
                                }
                            }
                            else
                            {
                                if (pLoopPlot->getBonusType() != NO_BONUS)
                                {
                                    iOtherCount++;
                                }
                            }
                        }
                    }
                }
     
                bool bLandBias = (iWaterCount > NUM_CITY_PLOTS / 2);
     
                shuffleArray(aiShuffle, NUM_CITY_PLOTS, getMapRand());    

                for (iJ = 0; iJ < NUM_CITY_PLOTS; iJ++)
                {
                    CvPlot* pLoopPlot = plotCity(pStartingPlot->getX_INLINE(), pStartingPlot->getY_INLINE(), aiShuffle[iJ]);

                    if ((pLoopPlot != NULL) && (pLoopPlot != pStartingPlot))
                    {
                        if (getSorenRandNum(((bLandBias && pLoopPlot->isWater()) ? 2 : 1), "Placing Bonuses") == 0)
                        {
                            if ((iOtherCount * 3 + iOceanFoodCount * 2 + iCoastFoodCount * 2) >= 12)
                            {
                                break;
                            }
                 
                            if (GET_PLAYER((PlayerTypes)iI).AI_foundValue(pStartingPlot->getX_INLINE(), pStartingPlot->getY_INLINE(), -1, true) >= iTargetValue)
                            {
                                break;
                            }

                            bool bCoast = (pLoopPlot->isWater() && pLoopPlot->isAdjacentToLand());
                            bool bOcean = (pLoopPlot->isWater() && !bCoast);
                            if ((pLoopPlot != pStartingPlot)
                                && !(bCoast && (iCoastFoodCount > 2))
                                && !(bOcean && (iOceanFoodCount > 2)))
                            {
                                for (int iPass = 0; iPass < 2; iPass++)
                                {
                                    if (pLoopPlot->getBonusType() == NO_BONUS)
                                    {
                                        for (iK = 0; iK < GC.getNumBonusInfos(); iK++)
                                        {
                                            if (GC.getBonusInfo((BonusTypes)iK).isNormalize())
                                            {
                                                //???no bonuses with negative yields?
                                                if ((GC.getBonusInfo((BonusTypes)iK).getYieldChange(YIELD_FOOD) >= 0) &&
                                                      (GC.getBonusInfo((BonusTypes)iK).getYieldChange(YIELD_PRODUCTION) >= 0))
                                                {
                                                    if ((GC.getBonusInfo((BonusTypes)iK).getTechCityTrade() == NO_TECH) || (GC.getTechInfo((TechTypes)(GC.getBonusInfo((BonusTypes)iK).getTechCityTrade())).getEra() <= getStartEra()))
                                                    {
                                                        if (GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isHasTech((TechTypes)(GC.getBonusInfo((BonusTypes)iK).getTechReveal())))
                                                        {
                                                            if ((iPass == 0) ? CvMapGenerator::GetInstance().canPlaceBonusAt(((BonusTypes)iK), pLoopPlot->getX(), pLoopPlot->getY(), bIgnoreLatitude) : pLoopPlot->canHaveBonus(((BonusTypes)iK), bIgnoreLatitude))
                                                            {
                                                                pLoopPlot->setBonusType((BonusTypes)iK);
                                                                iCoastFoodCount += bCoast ? 1 : 0;
                                                                iOceanFoodCount += bOcean ? 1 : 0;
                                                                iOtherCount += !(bCoast || bOcean) ? 1 : 0;
                                                                break;
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                             
                                        if (bLandBias && !pLoopPlot->isWater() && pLoopPlot->getBonusType() == NO_BONUS)
                                        {
                                            if (((iFeatureCount > 4) && (pLoopPlot->getFeatureType() != NO_FEATURE))
                                                && ((iCoastFoodCount + iOceanFoodCount) > 2))
                                            {
                                                if (getSorenRandNum(2, "Clear feature to add bonus") == 0)
                                                {
                                                    pLoopPlot->setFeatureType(NO_FEATURE);

                                                    for (iK = 0; iK < GC.getNumBonusInfos(); iK++)
                                                    {
                                                        if (GC.getBonusInfo((BonusTypes)iK).isNormalize())
                                                        {
                                                            //???no bonuses with negative yields?
                                                            if ((GC.getBonusInfo((BonusTypes)iK).getYieldChange(YIELD_FOOD) >= 0) &&
                                                                  (GC.getBonusInfo((BonusTypes)iK).getYieldChange(YIELD_PRODUCTION) >= 0))
                                                            {
                                                                if ((GC.getBonusInfo((BonusTypes)iK).getTechCityTrade() == NO_TECH) || (GC.getTechInfo((TechTypes)(GC.getBonusInfo((BonusTypes)iK).getTechCityTrade())).getEra() <= getStartEra()))
                                                                {
                                                                    if ((iPass == 0) ? CvMapGenerator::GetInstance().canPlaceBonusAt(((BonusTypes)iK), pLoopPlot->getX(), pLoopPlot->getY(), bIgnoreLatitude) : pLoopPlot->canHaveBonus(((BonusTypes)iK), bIgnoreLatitude))
                                                                    {
                                                                        pLoopPlot->setBonusType((BonusTypes)iK);
                                                                        iOtherCount++;
                                                                        break;
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }                            
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
     
                shuffleArray(aiShuffle, NUM_CITY_PLOTS, getMapRand());

                for (iJ = 0; iJ < NUM_CITY_PLOTS; iJ++)
                {
                    if (GET_PLAYER((PlayerTypes)iI).AI_foundValue(pStartingPlot->getX_INLINE(), pStartingPlot->getY_INLINE(), -1, true) >= iTargetValue)
                    {
                        break;
                    }
     
                    CvPlot* pLoopPlot = plotCity(pStartingPlot->getX_INLINE(), pStartingPlot->getY_INLINE(), aiShuffle[iJ]);

                    if (pLoopPlot != NULL)
                    {
                        if (pLoopPlot != pStartingPlot)
                        {
                            if (pLoopPlot->getBonusType() == NO_BONUS)
                            {
                                if (pLoopPlot->getFeatureType() == NO_FEATURE)
                                {
                                    for (iK = 0; iK < GC.getNumFeatureInfos(); iK++)
                                    {
                                        if ((GC.getFeatureInfo((FeatureTypes)iK).getYieldChange(YIELD_FOOD) + GC.getFeatureInfo((FeatureTypes)iK).getYieldChange(YIELD_PRODUCTION)) > 0)
                                        {
                                            if (pLoopPlot->canHaveFeature((FeatureTypes)iK))
                                            {
                                                pLoopPlot->setFeatureType((FeatureTypes)iK);
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
     
                int iHillsCount = 0;
     
                for (iJ = 0; iJ < NUM_CITY_PLOTS; iJ++)
                {
                    CvPlot* pLoopPlot =plotCity(pStartingPlot->getX_INLINE(), pStartingPlot->getY_INLINE(), iJ);
                    if (pLoopPlot != NULL)
                    {
                        if (pLoopPlot->isHills())
                        {
                            iHillsCount++;
                        }
                    }
                }
                shuffleArray(aiShuffle, NUM_CITY_PLOTS, getMapRand());
                for (iJ = 0; iJ < NUM_CITY_PLOTS; iJ++)
                {
                    if (iHillsCount >= 3)
                    {
                        break;
                    }
                    CvPlot* pLoopPlot = plotCity(pStartingPlot->getX_INLINE(), pStartingPlot->getY_INLINE(), aiShuffle[iJ]);
                    if (pLoopPlot != NULL)
                    {
                        if (!pLoopPlot->isWater())
                        {
                            if (!pLoopPlot->isHills())
                            {
                                if ((pLoopPlot->getFeatureType() == NO_FEATURE) ||
                                    !GC.getFeatureInfo(pLoopPlot->getFeatureType()).isRequiresFlatlands())
                                {
                                    if ((pLoopPlot->getBonusType() == NO_BONUS) ||
                                        GC.getBonusInfo(pLoopPlot->getBonusType()).isHills())
                                    {
                                        pLoopPlot->setPlotType(PLOT_HILLS, false, true);                        
                                        iHillsCount++;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
Adding these lines at the end of a mapscript will stop these starting position buffs from happening:
Code:
def normalizeAddExtras():
    return
def normalizeAddGoodTerrain():
    return
If the map script already has defined these functions somewhere, one would have to look for the places in the script where it directly influence the tiles surrounding starting positions.

My point here is mainly that we do have full capability to manipulate these things, this stuff is not a mystery; the exe is not involved in the generation of maps.
 
Last edited:
Thanks a lot. I probably wouldn't want to get rid of adding the good terrains completely, but just not allow space terrains to be considered for this purpose. I'll take a look and see if I can figure out a solution.
 
Since you already working on a mapscript, do you have a general idea on how the layout would look like?
I'm trying to make a scenario so I can test it all and see it ingame, but some things confuse me as we are dealing with many different scales (such as, between Mars and Earth, is there supposed to be "Deep Space" or "Inner Solar System"?

Probably just start playing now and add terrains and features as needed when I progress further through the tech tree ;)
 
No but I will!
No AI is fine, but gigantic bugs me a bit. I know there are a lot of different terrains needed to be placed, but
a) I don't want to manage too many cities on earth
b) the bigger the map size, the higher to modifier on how many cities you need on each step at space colonization to unlock more stuff.
 
No but I will!
No AI is fine, but gigantic bugs me a bit. I know there are a lot of different terrains needed to be placed, but
a) I don't want to manage too many cities on earth
b) the bigger the map size, the higher to modifier on how many cities you need on each step at space colonization to unlock more stuff.
Earth itself is 40x40, and this map has Gigantic modifier, but map is actually 280 long and 40 tiles high.
Probably it could work well with lower map size modifier

This On earth there is space for 30+ megacities. - I wanted terrain for each city 2x so it would be symmetric.
 
Top Bottom