[Map Script] PerfectWorld.py

Ah yes, I see now. It starts them halfway across the map from each other, but their progress from there on is randomized, which allows for the "X" or "Y" formations.

I guess my issue is that, when I wrote the Hemispheres script, one goal was to make each continent equal in landmass. Otherwise you end up condemning the civs on the smaller landmass to technological inferiority.

I'll go ahead and give your code a shot, though. The hemispheres logic has always been a hack to me, and I think a more elegant solution is possible here.
 
You can change the code so they mirror each other very easily by changing this:
Code:
            long1 = PWRand.randint(long1 - 1,long1 + 1)
            long2 = PWRand.randint(long2 - 1,long2 + 1)
To something more like this:
Code:
            long1 = PWRand.randint(long1 - 1,long1 + 1)
            long2 = long1 + int(self.mapWidth * 0.5)
I don't think you need to check if Long2 is 'within' the map width since GetIndex handles wrapping itself. With a little more work you can keep them independant but enforce a minimum and maximum difference.
 
I'm actually relatively pleased with the results on huge maps, without your techtonics code. The techtonics code makes pangeas too often, so I took it out. On huge maps, since there's so much more area, the canyon code tends to be a more uniform distribution, and still plenty random. Since the land area is normalized across the map and techtonics are random anyways, it wouldn't help much to create an even split.

Ultimately, I might end up refactoring a bit more so the height map and techtonics placement are actually done twice, once per hemisphere, so it's basically creating 2 maps and joining them together, much like the hemispheres script does.
 
I'm actually relatively pleased with the results on huge maps, without your techtonics code. The techtonics code makes pangeas too often, so I took it out. On huge maps, since there's so much more area, the canyon code tends to be a more uniform distribution, and still plenty random. Since the land area is normalized across the map and techtonics are random anyways, it wouldn't help much to create an even split.

Ultimately, I might end up refactoring a bit more so the height map and techtonics placement are actually done twice, once per hemisphere, so it's basically creating 2 maps and joining them together, much like the hemispheres script does.

The plate tectonics are largely responsible for the mountain ranges though, you wouldn't want to lose that I would think. There is a place where I stagger the heights of the plates, you might want to play around with that and decide some other way to stagger them.
 
The plate tectonics are largely responsible for the mountain ranges though, you wouldn't want to lose that I would think. There is a place where I stagger the heights of the plates, you might want to play around with that and decide some other way to stagger them.

Yep, making one slight change to my altered tectonics code (in the staggering) makes Pangea maps all but impossible without disrupting the mountains. Also, the tectonics code can produce some very good island chains and clusters, with small island chains being a continuation of a mountain range on the landmass nearby. IMO the tectonics are crucial to making good maps and changes in that section of the code have the biggest impact. By 'forcibly disrupting' the default tectonics code I can encourage the map to disperse the landmasses differently. The number of plates on the map (or any part of the map) has a big impact on the 'detail' of the landmass coastlines in that area with fewer plates resulting in less detail. You can enhance this effect with additional noise or smoothing passes on the underlying height map.

Anyway, the result I've been trying to achieve is a little bit of diversity in the landmasses. Or more consistent diversity (if that makes any sense) since I like to discover new worlds that are different than the old world in many ways.

So, if you curious about the 'simple change' I mentioned above here it is, just replace these lines:
Code:
            for i in range(0,self.mapHeight*self.mapWidth):
                if self.plateMap[i] == iPlate:
                    if polarPlate == False:
                        self.map[i] += adjPlate
                    else:
                        self.map[i] -= adjPlate

With this:
Code:
            for i in range(0,self.mapHeight*self.mapWidth):
                if self.plateMap[i] == iPlate:
                    if polarPlate == False and PWRand.random() < 0.5:
                        self.map[i] += adjPlate
                    else:
                        self.map[i] -= adjPlate

That will change it so non-polar plates can rise or lower instead of always rising. There are some other ways to do it, but I went with adding and subtracting rather than multiplying so it has a 'better' effect. I thought I had fixed this before posting it last time but apparently not :)
 
You know, I have one other change you guys might like too. I don't like the land tiles touching the north and south map edges (looks ugly IMO) but I do like the land mixing in with the ice features near the ice caps. This makes it difficult to circumnavigate early in the game and gives the subs a tactical advantage later in the game. In fact, I've had some cool maps where a coastal city or two are locked in by the ice that later turn into my primary submarine production cities.

It's difficult to force the landmasses away from the ice caps without creating pangea maps so I incorporated a trick. When placing terrain types any land tile that is colder than the ice threshold is converted to coast. It's as simple as this:
Code:
                    if tm.tempMap[i] < IceTemp + (PWRand.random() * 0.1):
                        if PolarLand == False:
                            hm.plotMap[i] = hm.OCEAN
                            rm.rainMap[i] = 0.0
                            self.terrainMap[i] = self.COAST
                        else:
                            self.terrainMap[i] = self.ICE
You'll have to repeat that code three times, once for each rainfall threshold (< desert, < plains and the last 'else' at the end). If you still want some 'ice' on the land you can blend it in with the tundra, I thought it looked fine without it so I left land 'ice' out completely. You can also remove the check for PolarLand, I put that in so that one day I could add in-game map options :)
 
The plate tectonics are largely responsible for the mountain ranges though, you wouldn't want to lose that I would think. There is a place where I stagger the heights of the plates, you might want to play around with that and decide some other way to stagger them.

Yeah, I was referring to Seven05's tectonics code that removes plates in one region of the map. I found that this code tended to create a pangea too often. I'm actually really liking the maps made with your default techtonics and his canyon digging in the height map. Still not exactly perfect, but very close.
 
That's been my eternal struggle... still not exactly perfect but so very close. :)

All things considered, I've been really happy with the results. Interestingly enough though, I don't get pangea worlds too often. What I get insted of pangea worlds are several landmasses that are only seperated by coast, not ocean (hense my ocean canyon code). But, just as often as I get them I get nicely dispersed and seperate landmasses with some scattered islands.

What I would really like to change is the tendancy to have all N-S continents or all E-W continents. My maps always look like they're stretched in one direction or the other.

Another thing I'd like to work out is shifting players around so their starting position is appropriate for the civ. Ethnic clusters would also be cool to add but I'm not sure I could accomplish both at the same time. Some examples would be ensuring civs like the Vikings, Dutch and Portugese start on costal tiles, the Celts start in hilly areas, etc...
 
Interestingly enough though, I don't get pangea worlds too often. What I get insted of pangea worlds are several landmasses that are only seperated by coast, not ocean (hense my ocean canyon code). But, just as often as I get them I get nicely dispersed and seperate landmasses with some scattered islands.

Yeah, you're right actually, I still think of those as pangeas, since the gameplay effect is mostly the same. I think the reason is that the reduction of plates in one reagion means the plates in the others grow larger and are more likely to connect. I might experiment a bit with altering the techtonics placement so it chooses 2-4 locations to add clusters of plates, rather than each plate scattering randomly.

Another thing I'd like to work out is shifting players around so their starting position is appropriate for the civ. Ethnic clusters would also be cool to add but I'm not sure I could accomplish both at the same time. Some examples would be ensuring civs like the Vikings, Dutch and Portugese start on costal tiles, the Celts start in hilly areas, etc...

Yeah, I played with the script in the other thread here that does that, and I ultimately didn't like the results. I think it was really along the right track though, and I'd like to alter the PW placement scripts with some of the ideas.

Basically, my thought is to keep the starting plot selection code as it is, but then add extra logic where it assigns them to the players. Each art style would have weighted factors, and a few individual civs would as well, so you could increase the chance of UU and UB relevant positions, and also take into account starting techs (give fishing civs seafood, mining civs hills, etc.).

I think the logic would work something like this:

Code:
Create a set of starting position "buckets" by art style
Iterate through starting locations
    Get a relative terrain value for each art style
    Place into the bucket with the highest art style
Iterate buckets
    Iterate positions in this bucket
        Iterate the other items in this bucket
            Add to a relative proximity value
            Add terrain value to this value
        Iterate other buckets
            Iterate items in this bucket
                Add to a relative proximity value for this bucket
                Add terrain value for bucket to this value
        Compare values of this bucket to other buckets
        If greater, switch buckets
Iterate buckets
    Check number of players for this bucket
    If too many, take lowest combined value items, and and put them in a "bone pile"
Iterate buckets
    If extra spaces, pull greatest value items from bone pile

Iterate players
    Iterate positions
        Generate values based on terrain for this civ
    Assign civ highest valued terrain in their bucket

That's off the top of my head, at least. I'm sure it could be more elegant and efficient. I'd have to make terrain and proximity value functions, since they're done over and over again. It shouldn't be much of a performance hit, since the starting positions pool is already narrowed down.

Ideally, it should group by art style, sort of like culturally linked starts did in Civ3, and then give each civ for that style the most logical start within.

The proximity function would also work better if it took water and mountains into account. For simplicity, it might work well to just draw a straight line, and add a total value based on the terrain (maybe flat=1,hill=1.5,mountain=3,coast=3,ocean=5), which would mostly confine cultures to a single continent, but still allow them on nearby islands or continents to an extent (think Japan).
 
Yeah, that'll be a project in and of itself... so I'll save it for last. Also, with some minor tweaking here before I go off to work I think I have hit on landmasses that I like. I've attached a couple of screenshots from my last test builds. Unfortunately I can't just upload the file, well I could but it wouldn't be usable for most people since I have edited a lot of it specifically for my mod, in particular I have climate based terrains so that entire block of code would have to be skipped and as a result I don't use altitude based temperature adjustments the same way Cephalo did (I apply them while assigning terrains, not directly to the temperature map).

Next up is getting rid of those straight lined jungles that plague about half of my maps.
 

Attachments

  • Civ4ScreenShot0004.JPG
    Civ4ScreenShot0004.JPG
    233.6 KB · Views: 232
  • Civ4ScreenShot0005.JPG
    Civ4ScreenShot0005.JPG
    232.7 KB · Views: 350
Found a little bug in AddBonusType.

You have:

if self.CanPlaceBonusAt(plot,eBonus,False,False) and y > 1 and y < self.mapHeight - 2:

But it should read:

if self.CanPlaceBonusAt(plot,eBonus,False,False) and y > 1 and y < hm.mapHeight - 2:

At least the debug log is complaining about it with:

AttributeError: BonusPlacer instance has no attribute 'mapHeight'

It doesn't happen all the time, in fact before updating my resources and changing the bNormalize on several of them from 1 to 0, I never got this error. Anyway, it's easy to fix.
 
Found a little bug in AddBonusType.

You have:

if self.CanPlaceBonusAt(plot,eBonus,False,False) and y > 1 and y < self.mapHeight - 2:

But it should read:

if self.CanPlaceBonusAt(plot,eBonus,False,False) and y > 1 and y < hm.mapHeight - 2:

At least the debug log is complaining about it with:

AttributeError: BonusPlacer instance has no attribute 'mapHeight'

It doesn't happen all the time, in fact before updating my resources and changing the bNormalize on several of them from 1 to 0, I never got this error. Anyway, it's easy to fix.

I can't find this code in the latest version. It looks like someone is trying to avoid resources being placed on the edge of the map, but that's something that never bothered me.
 
I can't find this code in the latest version. It looks like someone is trying to avoid resources being placed on the edge of the map, but that's something that never bothered me.

Hmm... maybe that was me... oops :)
 
Regarding resource placement here's an excerpt from PythonDbg.log:

Code:
trying to place 18 of BONUS_RICE
Could not place 11 of BONUS_RICE
trying to place 29 of BONUS_SHEEP
Could not place 19 of BONUS_SHEEP
trying to place 22 of BONUS_PIG
Could not place 16 of BONUS_PIG
trying to place 20 of BONUS_CORN
Could not place 14 of BONUS_CORN
trying to place 9 of BONUS_BANANA
Could not place 1 of BONUS_BANANA
trying to place 6 of BONUS_DYE
Could not place 3 of BONUS_DYE

It seems that several health / food resources were not placed. Does this mean that the map has fewer of these resources than normally expected on the map of this size (large)? As all other resources were fully placed, could be that food and health is more of an issue on PW maps than on other maps?
 
It seems that several health / food resources were not placed. Does this mean that the map has fewer of these resources than normally expected on the map of this size (large)? As all other resources were fully placed, could be that food and health is more of an issue on PW maps than on other maps?

There are two steps to placement that are used with the default method. First you decide how many are to be placed, then you decide where to place them. The quantity of ag resources are decided in relation to how many suitable tiles exist on the map. Most of them want 16 suitable tiles per resource. Once you get down to actually placing them however, the rules are different, and rather more restrictive. They cannot be placed within 5 tiles of any other same class resource. Each placement gobbles up a huge amount of real-estate, disproportionally so on the duel and tiny sized maps, but dats da rulz.

Adding to the problem is that the resources are placed in order. I randomize the order, but you will still get perhaps all the corn placed before any wheat has the chance, or vice versa. If they need to be placed on the same continent, then one of them is going to win over the other. They are designed to prefer different continents, if possible, and that's not always possible.

The only thing I'm doing differently to the default placement rules is that I am preventing features from blocking a resource, and also allowing them on more than one continent. One not so nice result of this is that you sometimes see corn in jungle areas. I figured that was an ok trade-off as now there are many more ag resources on the map than there were.

There are two reasons I believe that this placement method is still somewhat problematic with PW. One is that the landforms are much more unpredictable. The bArea rules seem to assume that there are 2 or 3 large bulbous continents with plenty of room for several of a unique bonus. Second, since terrains are normally very randomly scattered, instead of being in their proper place according to climate conditions, you probably have a much higher chance of seeing more plains 5 tiles away from a placement spot since the area being filled up is not a huge clump of plains, but random plains/grassland/desert.

In my testing, I found that only duel sized maps sometimes went without any of a particular resource, usually there was at least one. Not that surprising since any placement eats up half the map.
 
Error in starting a new game.

PythonErr.Log:

Code:
Traceback (most recent call last):

  File "PerfectWorld", line 3207, in generateTerrainTypes

  File "PerfectWorld", line 1554, in generateContinentMap

  File "PerfectWorld", line 804, in getNewWorldID

  File "PerfectWorld", line 1013, in PrintList

  File "CvUtil", line 108, in write

RuntimeError: unidentifiable C++ exception
ERR: Python function generateTerrainTypes failed, module PerfectWorld
Traceback (most recent call last):

  File "PerfectWorld", line 3466, in assignStartingPlots

  File "PerfectWorld", line 2104, in SetStartingPlots

  File "PerfectWorld", line 2263, in setupOldWorldAreaList

AttributeError: TerrainMap instance has no attribute 'newWorldID'
ERR: Python function assignStartingPlots failed, module PerfectWorld

Do you need save file or is the error message enough?

I didn't bother playing that map in case this has an effect other than that there simply is no "new world".
 
The stock map starting location normalizer modifies terrain where needed so that the starting location only has "good" terrain (grassland, plains, floodplains) not any of the "bad" types (desert, tundra, snow - all have FP<2), as well as removing jungle. PerfectWorld doesn't modify terrain, and limits jungle removal to one ring (three in stock normalizer).

Is this intended behaviour? If so, how does the normalizer count the starting location value so that it's within something (80%?) of best starting location? Simply by adding resources to push tile values up?
 
Error in starting a new game.

PythonErr.Log:

Code:
Traceback (most recent call last):

  File "PerfectWorld", line 3207, in generateTerrainTypes

  File "PerfectWorld", line 1554, in generateContinentMap

  File "PerfectWorld", line 804, in getNewWorldID

  File "PerfectWorld", line 1013, in PrintList

  File "CvUtil", line 108, in write

RuntimeError: unidentifiable C++ exception
ERR: Python function generateTerrainTypes failed, module PerfectWorld
Traceback (most recent call last):

  File "PerfectWorld", line 3466, in assignStartingPlots

  File "PerfectWorld", line 2104, in SetStartingPlots

  File "PerfectWorld", line 2263, in setupOldWorldAreaList

AttributeError: TerrainMap instance has no attribute 'newWorldID'
ERR: Python function assignStartingPlots failed, module PerfectWorld

Do you need save file or is the error message enough?

I didn't bother playing that map in case this has an effect other than that there simply is no "new world".

I don't know what caused the first error, I think I have seen that before once in a while and I have not been able to investigate the cause. Why would it sometimes not be able to print a list in the debug log? Anyway that also causes the second error. I would imagine that map would be messed up in some way, if the terrain is correct then it's probably using the default starting code which will not respect the new world.

The stock map starting location normalizer modifies terrain where needed so that the starting location only has "good" terrain (grassland, plains, floodplains) not any of the "bad" types (desert, tundra, snow - all have FP<2), as well as removing jungle. PerfectWorld doesn't modify terrain, and limits jungle removal to one ring (three in stock normalizer).

Is this intended behaviour? If so, how does the normalizer count the starting location value so that it's within something (80%?) of best starting location? Simply by adding resources to push tile values up?

I worked my butt off trying to make starting locations as fair as possible, and the end result contains remnants of many failed experiments.:blush: I really don't like changing the terrain and features on the starting area because I think it looks terrible, and is a permanent ugly scar on the map. My normalizer will use bonuses to even things out as much as possible, and it works pretty well except sometimes it neglects to put enough food. I'll need to fix it when I have a chance. I also didn't like the way the default jungle remover completely denuded all the visible jungle in a starting area. One ring is enough to get you to iron working, and after that you have a great commerce generating city.
 
I agree that not modifying the terrain makes for more beautiful map. I've had starts with lots of tundra, while desert starts usually have several floodplains that make them more useful (although health is immediate and grave problem in that case). Usually these can be used only as civ kickoffs - they send some settling parties out and palace will later be moved.

I'm not so sure about one ring only with jungle removal. It causes health trouble (so hacking more jungle than needed for improvements may be required), it grows over other tiles (twice as fast as forest), and when jungle is removed you're left with bare earth - great as it is there's no chopping speedup in the early expansion. I've had several grassland / forest starts as well and the grassland below jungle is no better than grassland below coniferious forest - unless jungle is sprinkled with higher concentrations of resources.

Of course neither of these problems is big - I can always regenerate the map if I want to.

Is there any guide to reading mapscripts? I have quickly scanned through some, but not sure about where to start with them if I want to look specifically for starting location assignment and normalization for example.
 
Top Bottom