[Map Script] PerfectWorld2.py

OK, thanks for the expenation. If that's the case, then how do I convert Desert, Arctic, and Tundra in the BFC?
 
OK, thanks for the expenation. If that's the case, then how do I convert Desert, Arctic, and Tundra in the BFC?

It sounds like what you're trying to do is what the original normalization functions did. You can reenable the functions like normalizeAddGoodTerrain etc. The reason why I disabled those though is that I worked hard to enforce the climate rules to make a more believable map, and these functions undid all that work right on everyones capital! You can put them back in, but it will stick out like a sore thumb.

If you want more control, you can search up those variables like MaxBadFeaturesInFC and do something similar.
 
~
My request for a version that doesn't use wonderclass resources for sweetening still stands, and I still have no idea why I'm getting floodplains on hills.

I thought maybe I can at least remove stone/marble sweetening myself because I simply don't want to keep the edited xml file around, I failed though.
Code:
                    if bonusInfo.getBonusClassType() == BonusClassTypes.BONUSCLASS_WONDER:
                        continue
Adding this code between lines 4499 and 4500 simply causes the error "StartingPlotfinder failed" at appear every single, and since that doesn't happen with the xml change, so clearly this doesn't work the way I want.

The error:
Traceback (most recent call last):

File "PerfectWorld205c2", line 5730, in assignStartingPlots

File "PerfectWorld205c2", line 4240, in SetStartingPlots

AttributeError: type object 'CvPythonExtensions.BonusClassTypes' has no attribute 'BONUSCLASS_WONDER'
ERR: Python function assignStartingPlots failed, module PerfectWorld205c2
 
~
My request for a version that doesn't use wonderclass resources for sweetening still stands, and I still have no idea why I'm getting floodplains on hills.

I thought maybe I can at least remove stone/marble sweetening myself because I simply don't want to keep the edited xml file around, I failed though.
Code:
                    if bonusInfo.getBonusClassType() == BonusClassTypes.BONUSCLASS_WONDER:
                        continue
Adding this code between lines 4499 and 4500 simply causes the error "StartingPlotfinder failed" at appear every single, and since that doesn't happen with the xml change, so clearly this doesn't work the way I want.

The error:
Traceback (most recent call last):

File "PerfectWorld205c2", line 5730, in assignStartingPlots

File "PerfectWorld205c2", line 4240, in SetStartingPlots

AttributeError: type object 'CvPythonExtensions.BonusClassTypes' has no attribute 'BONUSCLASS_WONDER'
ERR: Python function assignStartingPlots failed, module PerfectWorld205c2

I have not fixed the floodplain on hill issue.

The BonusClassTypes is defined to only have NO_BONUSCLASS. In order to get a proper info type from python, you have to use the global context. So, it would be something like gc.getInfoTypeForString("BONUSCLASS_WONDER"), then the game will look up the proper type for you.
 
Thank you, as expected that worked instantly.

On the dessert/hill/floodplains front I tried again today, with higher dessert propability, and finally got what I wanted to see: a starting location with riverside dessert hills, some with, some without floodplains. I am now pretty certain that this has nothing to do with your riversystem and eveything to do with minhills.
Of course, that was your first assumption too. But I counted hills in the BFC and it was always bigger than minhills, making both of us, or at least me, think minhills couldn't have anything to do with it - PythonDbg.log tells another story however.
In your infinite wisdom you added debug messages to that part, I dust didn't read them .. until today.
This map has no starting position with less than 5 hills, minhills is 4. This is the interesting part of the debug output:

Spoiler :
[QUOTE='"PythonDbg.log"]number of starting areas is 1

badFeaturesFound=6

badFeaturesFound=6

badFeaturesFound=5

badFeaturesFound=4

hills found = 0, hills needed = 4

adding hill

adding hill

adding hill

adding hill

badFeaturesFound=0

hills found = 0, hills needed = 4

adding hill

adding hill

adding hill

adding hill

badFeaturesFound=3

hills found = 0, hills needed = 4

adding hill

adding hill

adding hill

adding hill

badFeaturesFound=0

hills found = 0, hills needed = 4

adding hill

adding hill

adding hill

adding hill

badFeaturesFound=4

hills found = 2, hills needed = 2

adding hill

adding hill

badFeaturesFound=0

hills found = 0, hills needed = 4

adding hill

adding hill

adding hill

adding hill

badFeaturesFound=3

hills found = 0, hills needed = 4

adding hill

adding hill

adding hill

adding hill

bestTotalValue = 27245

boosting plot by 0

boosting plot by 1

boosting plot by 1

boosting plot by 2

boosting plot by 2

boosting plot by 4[/QUOTE]
 
I wonder what's going on there. Somehow I'm adding 5 hills, even though the debug messages say I'm adding 4.

Actually, I may not be counting the hills properly.
 
I am pretty sure you are adding 4 hills when the message says you are adding 4 hills, but I really doubt that out of 7 starting locations 6 were completely without hills, so yes, counting is what I think is broken.
And of course, the actual adding hills function should 1.) at least try not to select tiles with floodplains (check for feature with isRequiresRiver()) and if a floodplains tile has to be selected because it's the only option left then it should 2.) remove flood plains.

edit: make the check isRequiresFlatlands(), that fits best here.

edit2: after staring at the code for a while I noticed
Code:
            if plot.getPlotType() == PlotTypes.PLOT_HILLS and gameMap.plot(x,y).[B]getArea[/B] == plot.[B]getArea()[/B]:
                hillsFound += 1
getArea without the () doesn't seem to work as expected, unsurprisingly. Adding them fixes hill counting.

And here is my code that should not touch bonus tiles at all, and should leave tiles with floodplains alone and look for other tiles first, only when there is no other option it will use them and at the same time remove the floodplains.
Code:
        #Ensure minimum number of hills
        hillsNeeded = mc.MinHillsInFC - hillsFound
        print "hills found = %d, hills needed = %d" % (hillsFound,hillsNeeded)
        if hillsNeeded > 0:
            for plot in plotList:
                if hillsNeeded <= 0:
                    break
[B]                featureInfo = gc.getFeatureInfo(plot.getFeatureType())
                requiresFlatlands = False
                if featureInfo != None:
                    requiresFlatlands = featureInfo.isRequiresFlatlands()[/B]
                if plot.getPlotType() != PlotTypes.PLOT_HILLS and \
                plot.getArea() == gameMap.plot(x,y).getArea() [B]and \
                plot.getBonusType(TeamTypes.NO_TEAM) == BonusTypes.NO_BONUS and \
                not requiresFlatlands[/B]:
                    plot.setPlotType(PlotTypes.PLOT_HILLS,True,True)
                    hillsNeeded -= 1
                    print "adding hill"
            if hillsNeeded > 0:
                print "failed to add minimum hills[B], trying again[/B]"
                for plot in plotList:
                    if plot.getPlotType() != PlotTypes.PLOT_HILLS and \
                    plot.getArea() == gameMap.plot(x,y).getArea() [B]and \
                    plot.getBonusType(TeamTypes.NO_TEAM) == BonusTypes.NO_BONUS:
                        plot.setFeatureType(FeatureTypes.NO_FEATURE,-1)[/B]
                        plot.setPlotType(PlotTypes.PLOT_HILLS,True,True)
                        hillsNeeded -= 1
                        print "adding hill, removing feature"
            if hillsNeeded > 0:
                print "failed to add minimum hills!!!!!!!!!!"



edit3: Last but not least, the parts that prevent sweeting with Stone and Marble:
Code:
        #This variable adjusts the amount of bonuses on the map. Values above 1.0 will add bonus
        #bonuses. People often want lots of bonuses, and for those people, this variable is definately
        #a bonus.
        self.BonusBonus = 1.0
        
[B]        #Disallows Stone and Marble to be used to sweeting starting positions.
        self.noBonusWonderClass = True[/B]
        
        #How many squares are added to a lake for each unit of drainage flowing
        #into it.
        self.LakeSizePerDrainage = 14.0
Code:
                for b in range(gc.getNumBonusInfos()):
                    bonusEnum = shuffledBonuses[b]
                    bonusInfo = gc.getBonusInfo(bonusEnum)
                    if bonusInfo.isNormalize() == False:
                        continue
[B]                    if bonusInfo.getBonusClassType() == gc.getInfoTypeForString("BONUSCLASS_WONDER") and mc.noBonusWonderClass:
                        continue[/B]
                    if bonusInfo.getYieldChange(yields[n]) < 1:
                        continue
 
Wow, thanks for finding that bug with the hill counting. I might have stared at that forever.

requiresFlatlands sounds like the way to handle the floodplain issue.

I'm still skeptical about not using the wonder resources for sweetening. I think it can be a needed boost on an otherwise bad start. Clustering wonder resources is not a problem in my mind.

Another issue that I've seen is that my bad feature remover will sometimes only remove jungle on the hills, and leave all the flats convered in jungle, resulting in a no growth until iron working situation.:lol:

EDIT: As you mentioned I also forgot to check for bonuses on prospective hills, so some might see rice on hills or something undesirable. I think I have collected enough bugs to start on an update.

EDIT2: Looking through the code I realized that I used the same plot shuffle order to both remove bad features and to add hills! No wonder I was seeing bare hills and jungle flats!
 
I'm still skeptical about not using the wonder resources for sweetening. I think it can be a needed boost on an otherwise bad start. Clustering wonder resources is not a problem in my mind.
That's why it's an option. Just turn it off by default if you still don't like it.

Another issue that I've seen is that my bad feature remover will sometimes only remove jungle on the hills, and leave all the flats convered in jungle, resulting in a no growth until iron working situation.:lol:
That's an evil that comes with randomness. One solution would be to do it in 2 phases, like the hills adding: in the first round, only flatlands or hills with bonus are valid targets to remove bad features.

The only other issue I know of is that minhills will always fail if there are not enough land tiles. (I already had a start like that once.) But unless you want to add a minLandTiles variable plus the function to take care of that (count land tiles, if not enough add land: only water next to land is valid, gets turned to flatland, terrain type determined by neighbouring land tile(s)), I think that one will remain.
 
That's why it's an option. Just turn it off by default if you still don't like it.

That's an evil that comes with randomness. One solution would be to do it in 2 phases, like the hills adding: in the first round, only flatlands or hills with bonus are valid targets to remove bad features.

The only other issue I know of is that minhills will always fail if there are not enough land tiles. (I already had a start like that once.) But unless you want to add a minLandTiles variable plus the function to take care of that (count land tiles, if not enough add land: only water next to land is valid, gets turned to flatland, terrain type determined by neighbouring land tile(s)), I think that one will remain.

Yeah, I'm handling it in two passes, half will be removed on the hills, and the other half on the flats, that way you can build a mine and a cottage/farm. Any rounding error will favor the flats, so if you're removing 3 bad features, 1 will be hill and 2 will be flats. If there's only one hill, then you will remove from one hill and the rest flats.

I'm not too worried about minhills failing when there's not enough land tiles. I figure that if you have that much water, there's a mother lode of fish or something, and as long as you have a hill you should be able to expand. That's gotta be pretty rare I would think unless there's a bug. Water is considered low value if there isnt an ancient/starting era resource there.

I respectfully disagree on the wonder resource issue. I don't want that option in there because I don't want to encourage people to use it. I think it would only cause problems without giving a benefit. It's a big part of my normalization strategy. I'm not convinced that this should be done.

I have a new version done and I'm going to begin testing on my lunch break.
 
Bad feature removing sounds good the way you describe it, looking forward to that new version :)
(edit: and in case you aren't releasing today, could you just post the code?)

Yeah, water-heavy starts are automatically seafood-heavy because of low value of water causing lots of additional bonuses, which will with a very high (or even too high?) propability land on water.

Wonder resource skipping will remain unique to my personal copy then. I'm not trying to encourage anything, I'm just really happy that I have it now.
 
I would post the code, except I never get this sort of thing right the first time. I'm 90% certain that this code will crash the map. There might be nothing similar between what I have now and what I end up publishing. There will be not only typos, but just plain old bad ideas.
 
I would post the code, except I never get this sort of thing right the first time. I'm 90% certain that this code will crash the map.
Glad I'm not alone there, I blame Python and it's goofey syntax that they designed to be as close to the opposite of C as they possibly could :)
 
You didn't move it to after the minhills part, so your code will happily remove bad features from flatlands that could get turned into hills a few lines later.

Also I think your code can't handle having not enough land tiles to remove bad features from, it removes half from hills, as much as it can from flatlands, but if badFeaturesFound is still > 1 after that it won't try again.

Here is my version:
Code:
        #Ensure minimum number of hills
        hillsNeeded = mc.MinHillsInFC - hillsFound
        print "hills found = %d, hills needed = %d" % (hillsFound,hillsNeeded)
        if hillsNeeded > 0:
            for plot in plotList:
                if hillsNeeded <= 0:
                    break
                featureInfo = gc.getFeatureInfo(plot.getFeatureType())
                requiresFlatlands = (featureInfo != None and featureInfo.isRequiresFlatlands())
                bonusInfo = gc.getBonusInfo(plot.getBonusType(TeamTypes.NO_TEAM))
                if plot.getPlotType() != PlotTypes.PLOT_HILLS and \
                plot.getArea() == gameMap.plot(x,y).getArea() and \
                bonusInfo == None and \
                not requiresFlatlands:
                    plot.setPlotType(PlotTypes.PLOT_HILLS,True,True)
                    hillsNeeded -= 1
                    print "adding hill"
            if hillsNeeded > 0:
                print "failed to add minimum hills, trying again"
                for plot in plotList:
                    if plot.getPlotType() != PlotTypes.PLOT_HILLS and \
                    plot.getArea() == gameMap.plot(x,y).getArea() and \
                    (bonusInfo == None or not bonusInfo.isRequiresFlatlands()):
                        plot.setPlotType(PlotTypes.PLOT_HILLS,True,True)
                        hillsNeeded -= 1
                        if requiresFlatlands:
                            plot.setFeatureType(FeatureTypes.NO_FEATURE,-1)
                            print "adding hill, removing feature"
                        else:
                            print "adding hill to bonus tile"
            if hillsNeeded > 0:
                print "failed to add minimum hills!!!!!!!!!!"
                    
        #ensure maximum number of bad features
        badFeaturesToRemove = badFeaturesFound - mc.MaxBadFeaturesInFC
        print "badFeaturesFound = %d, badFeaturesToRemove = %d" % (badFeaturesFound,badFeaturesToRemove)
        if badFeaturesToRemove > 0:
            #remove half from flatlands, the rest from hills
            badFeaturesToRemoveFromFlatlands = badFeaturesToRemove/2 + badFeaturesToRemove%2
            badFeaturesToRemove -= badFeaturesToRemoveFromFlatlands
            for plot in plotList:
                if badFeaturesToRemoveFromFlatlands <= 0 and badFeaturesToRemove <= 0:
                    break
                featureInfo = gc.getFeatureInfo(plot.getFeatureType())
                if featureInfo != None:
                    totalYield = 0
                    for yi in range(YieldTypes.NUM_YIELD_TYPES):
                        totalYield += featureInfo.getYieldChange(YieldTypes(yi))
                    if totalYield <= 0:#bad feature
                        if plot.getPlotType() == PlotTypes.PLOT_LAND and badFeaturesToRemoveFromFlatlands > 0:
                            print "removing bad feature from flatlands"
                            badFeaturesToRemoveFromFlatlands -= 1
                        if plot.getPlotType() == PlotTypes.PLOT_HILLS and badFeaturesToRemove > 0:
                            print "removing bad feature from hills"
                            badFeaturesToRemove -= 1
                        plot.setFeatureType(FeatureTypes.NO_FEATURE,-1)
            #if there are not enough hills or flatlands, there will be leftovers
            badFeaturesToRemove += badFeaturesToRemoveFromFlatlands
            for plot in plotList:
                if badFeaturesToRemove <= 0:
                    break
                featureInfo = gc.getFeatureInfo(plot.getFeatureType())
                if featureInfo != None:
                    totalYield = 0
                    for yi in range(YieldTypes.NUM_YIELD_TYPES):
                        totalYield += featureInfo.getYieldChange(YieldTypes(yi))
                    if totalYield <= 0:#bad feature
                        print "removing bad feature"
                        badFeaturesToRemove -= 1
                        plot.setFeatureType(FeatureTypes.NO_FEATURE,-1)
 
You didn't move it to after the minhills part, so your code will happily remove bad features from flatlands that could get turned into hills a few lines later.

Also I think your code can't handle having not enough land tiles to remove bad features from, it removes half from hills, as much as it can from flatlands, but if badFeaturesFound is still > 1 after that it won't try again.

Yeah, it would have been better to do hills first. It is still possible to have no jungle free flatlands.

Also, you're right that if you have a bunch of jungle hills and one jungle flat, you won't remove all the desired jungle.

I think this will work better than it did though, and those scenarios should be very rare. This thread will remind me to work on it again for next update.
 
Is there any way to add more islands to the game, like there were in PerfectWorld? I liked those maps better.

Try setting self.hmMaxGrain to 8 or 4 and let me know if you like that better.
 
Also, you're right that if you have a bunch of jungle hills and one jungle flat, you won't remove all the desired jungle.

I think this will work better than it did though, and those scenarios should be very rare. This thread will remind me to work on it again for next update.
Yes it's an improvement, no doubt. Don't mind my perfectionalism, I just can't help it.:hammer2:

If you are surrounded by only water and land with features or bonuses that all require flatland, your minhills won't finish either but yes, that too should be rare enough - with normal settings at least.

My previous post now contains the updated versions of both, just in case you might find it useful.
 
Top Bottom