[Map Script] PerfectWorld.py

Yes, they're always rivers, at least in all (old and new) scripts in BtS (don't know about Vanilla)

The function that determines river crossings and thus river plots is called updateRiverCrossings in the SDK dll. I'll post the code.

Code:
void CvPlot::updateRiverCrossing(DirectionTypes eIndex)
{
	CvPlot* pNorthEastPlot;
	CvPlot* pSouthEastPlot;
	CvPlot* pSouthWestPlot;
	CvPlot* pNorthWestPlot;
	CvPlot* pCornerPlot;
	CvPlot* pPlot;
	bool bValid;

	FAssertMsg(eIndex >= 0, "eTeam is expected to be non-negative (invalid Index)");
	FAssertMsg(eIndex < NUM_DIRECTION_TYPES, "eTeam is expected to be within maximum bounds (invalid Index)");

	pCornerPlot = NULL;
	bValid = false;

	switch (eIndex)
	{
	case DIRECTION_NORTH:
		pPlot = plotDirection(getX_INLINE(), getY_INLINE(), DIRECTION_NORTH);
		if (pPlot != NULL)
		{
			bValid = pPlot->isNOfRiver();
		}
		break;

	case DIRECTION_NORTHEAST:
		pCornerPlot = plotDirection(getX_INLINE(), getY_INLINE(), DIRECTION_NORTH);
		break;

	case DIRECTION_EAST:
		bValid = isWOfRiver();
		break;

	case DIRECTION_SOUTHEAST:
		pCornerPlot = this;
		break;

	case DIRECTION_SOUTH:
		bValid = isNOfRiver();
		break;

	case DIRECTION_SOUTHWEST:
		pCornerPlot = plotDirection(getX_INLINE(), getY_INLINE(), DIRECTION_WEST);
		break;

	case DIRECTION_WEST:
		pPlot = plotDirection(getX_INLINE(), getY_INLINE(), DIRECTION_WEST);
		if (pPlot != NULL)
		{
			bValid = pPlot->isWOfRiver();
		}
		break;

	case DIRECTION_NORTHWEST:
		pCornerPlot = plotDirection(getX_INLINE(), getY_INLINE(), DIRECTION_NORTHWEST);
		break;

	default:
		FAssert(false);
		break;
	}

	if (pCornerPlot != NULL)
	{
		pNorthEastPlot = plotDirection(pCornerPlot->getX_INLINE(), pCornerPlot->getY_INLINE(), DIRECTION_EAST);
		pSouthEastPlot = plotDirection(pCornerPlot->getX_INLINE(), pCornerPlot->getY_INLINE(), DIRECTION_SOUTHEAST);
		pSouthWestPlot = plotDirection(pCornerPlot->getX_INLINE(), pCornerPlot->getY_INLINE(), DIRECTION_SOUTH);
		pNorthWestPlot = pCornerPlot;

		if ((pSouthWestPlot != NULL) && (pNorthWestPlot != NULL))
		{
			if (pSouthWestPlot->isWOfRiver() && pNorthWestPlot->isWOfRiver())
			{
				bValid = true;
			}
		}

		if ((pNorthEastPlot != NULL) && (pNorthWestPlot != NULL))
		{
			if (pNorthEastPlot->isNOfRiver() && pNorthWestPlot->isNOfRiver())
			{
				bValid = true;
			}
		}

		if ((eIndex == DIRECTION_NORTHEAST) || (eIndex == DIRECTION_SOUTHWEST))
		{
			if ((pNorthEastPlot != NULL) && (pNorthWestPlot != NULL))
			{
				if (pNorthEastPlot->isNOfRiver() && pNorthWestPlot->isWOfRiver())
				{
					bValid = true;
				}
			}

			if ((pSouthWestPlot != NULL) && (pNorthWestPlot != NULL))
			{
				if (pSouthWestPlot->isWOfRiver() && pNorthWestPlot->isNOfRiver())
				{
					bValid = true;
				}
			}
		}
		else
		{
			FAssert((eIndex == DIRECTION_SOUTHEAST) || (eIndex == DIRECTION_NORTHWEST));

			if (pNorthWestPlot != NULL)
			{
				if (pNorthWestPlot->isNOfRiver() && pNorthWestPlot->isWOfRiver())
				{
					bValid = true;
				}
			}

			if ((pNorthEastPlot != NULL) && (pSouthWestPlot != NULL))
			{
				if (pNorthEastPlot->isNOfRiver() && pSouthWestPlot->isWOfRiver())
				{
					bValid = true;
				}
			}
		}
	}

	if (isRiverCrossing(eIndex) != bValid)
	{
		m_abRiverCrossing[eIndex] = bValid;

		changeRiverCrossingCount((isRiverCrossing(eIndex)) ? 1 : -1);
	}
}

Basically, in order to consider diagonal motion a river crossing, you need two river lengths. According to this code, and correct me if I'm wrong because this is not a simple issue and I may very well be misreading this code, crossing diagonally across the mouth of a river is *not* considered a river crossing, although it really should be considered one.

In vanilla, I was able to verify this behavior on other maps. The CvPlot::isRiver() function checks to count of all river crossings that are precomputed by the above. Diagonal from a river mouth does not create fresh water, which means the CvPlot::isRiver() function returns false. Here's the code for CvPlot::isFreshWater().
Code:
bool CvPlot::isFreshWater() const
{
	CvPlot* pLoopPlot;
	int iDX, iDY;

	if (isWater())
	{
		return false;
	}

	if (isImpassable())
	{
		return false;
	}

	if (isRiver())
	{
		return true;
	}

	for (iDX = -1; iDX <= 1; iDX++)
	{
		for (iDY = -1; iDY <= 1; iDY++)
		{
			pLoopPlot	= plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);

			if (pLoopPlot != NULL)
			{
				if (pLoopPlot->isLake())
				{
					return true;
				}

				if (pLoopPlot->getFeatureType() != NO_FEATURE)
				{
					if (GC.getFeatureInfo(pLoopPlot->getFeatureType()).isAddsFreshWater())
					{
						return true;
					}
				}
			}
		}
	}

	return false;
}

The thing is, if they changed the rules for BtS, why would my map behave differently than other older maps like Terra? Did they publish new versions for the old map scripts? I gues the obvious solution for me would be to go out and buy BtS but I'm sooooo broke.
 
1.04 is available. This is a pretty big update. No starting location stuff yet. Made the number of rivers in the world tunable. Also added a tuner for how quickly floodplains are made in desert rivers. Fixed the bug where floodplains weren't appearing on river corners. I still haven't been able to figure out why my river graphics get wonky when the map is regenerated without going back to the main menu. Saving and reloading should fix that.

I added an option to use either Python's random number generator or the one given by the getMapRand function. Interestingly, the character of the maps generated by each randomizer is very different from the other. The Python generator is vastly superior with 53 bit precision. The Civ rands packs a 32 bit rand into a 16 bit integer. Call me crazy, but I think the Python random creates much more organic looking maps. They both generate random maps, but the difference is like looking at paisley compared to looking at plaid. Switch back and forth to see for yourself.

I actually have alot of experience randomly generating stuff, it's been a hobby of mine for a long time. Psuedo-random numbers are a very weird science. It's shockingly easy to accidently find your processes 'in tune' with an otherwise highly random generator, making it completely un-random for the particular thing you are trying to use it for. One time I was making a heightmap for a project of mine, and I discovered that one of the variables I was randomizing was always an even number, never an odd. Very strange! I switched to a different generator and the problem went away. When you're creating fractal art such as these maps are, you can't skimp on your random generator.

The only good thing about the getMapRand is that hopefully, Seven05's MP bug will go away! Seven05, set the UsePythonRandom variable to 'False'. If that doesn't fix the MP bug, I don't think I have the skills to fix it.
 
I looked at the same part of code in BtS and it's a bit different. That's the reason for all the misunderstanding. Anyway, great update, congratulations:goodjob:
 
I looked at the same part of code in BtS and it's a bit different. That's the reason for all the misunderstanding. Anyway, great update, congratulations:goodjob:

Someday I'll get BtS, and try to go for better compatibility.
 
I want to share a few screenshots from your map script that shows why I love this so much. The first shows a very interesting coast. It is evident that your tectonics method formed a ridge where the island formed.



The second image shows a very fertile area with a large lake. Almost every map has a lot of character and it makes it fun to explore.



BTW If you want I could send you some .py of some BTS map scripts if you want to compare, or is that a no no?
 
Thanks for the compliments MrPopov. Ahh, smell those pine trees on the wind. Sometimes the best thing about Civilization is being away from it.

:lol: :lol: :lol: :crazyeye:

Do you know of another map script that generates it's own rivers for BtS? Most map scripts use the default river generator in some way.
 
Did you have a chance to comment out the starting plot code? That's the thing that I needed most.
Wow, it hasn't been easy to connect to CF lately... anyway, yes we went OOS even with all of your starting plot functions commented out, although I neglected to have them re-connect to see if they ended up in a different plot.

As for the rivers, none of the official BTS scripts use custom river generation.
 
Do you know of another map script that generates it's own rivers for BtS? Most map scripts use the default river generator in some way.
There's mine (Tectonics) which generates rivers 'backwards' (from sea to source). The river code is a bit of a mess that I'd change if I weren't so lazy, but you can always look at it.
 
There's mine (Tectonics) which generates rivers 'backwards' (from sea to source). The river code is a bit of a mess that I'd change if I weren't so lazy, but you can always look at it.

How does your script work with BtS LDiCesare? Can you build a levee on Raels x-marked square in his screenshot? Another less permanent, but still annoying problem I'm having with the delta graphics only happens on lakes that have rivers going into them. Since you don't have that particular combo of features, I guess it just doesn't come up on yours. I studied yours quite a bit while learning the ropes. Thanks for all your hard work.

Seven05 said:
Wow, it hasn't been easy to connect to CF lately... anyway, yes we went OOS even with all of your starting plot functions commented out, although I neglected to have them re-connect to see if they ended up in a different plot.

As for the rivers, none of the official BTS scripts use custom river generation.

Try turning off the Python randoms in the future, I can't imagine what else it could be.
 
So I've got my starting plot code all torn to pieces right now and I need some advice. This should take a couple of weeks I imagine because I'm generating starting plots in a completely different way and there's bound to be obstacles I can't even imagine right now.

In normal map scripts that come with the game, the climates are strewn about in a highly random way that makes most land masses fairly homogenous. You can be sure that wherever you start, you are never too far away from some productive map squares, and you have a good chance that you will have first dibs on some good land.

On my map script, continents have good parts and bad parts. If you start on some tiny grassland penninsula connected to a vast desert, many of your descendents might be eating sand beetles for a thousand years before they find another patch of green, and it might all be occupied by someone who started closer to the good lands.

Take the following scenario, lets say we have a continent where we would like to place two players. The continent has an area of grassland on one end, but the rest is hard scrabble desert with some scattered plains. You can build cities there to be sure, and there's some valueable resources, but you wouldn't want to start there.

Here's the big question, is it ok to start the two players in the grassland fairly close to each other, facing possible war in the warrior stage? Starting them any other way would be hugely unfair, as one player would likely place a city or two in the productive area before the other even knew where to look. Right now, I'm thinking that if I place them near each other with equal access to the continents value, it would be ok. Keep in mind that this scenario can also happen on very large continents with 4 or 5 civs.

How important is starting distance, and why is it important?
 
Here's an off-the-wall idea...

Evaluate the 'value' of entire areas (landmasses) before defining old/new world areas. Things to consider for establishing a value would be peaks (no value), desert without flood plains (very low value), features (trees=good, jungle=bad) and so on. Then, once you have a value per landmass, and you know the size of the landmass you can calculate the average value per tile and space the players out according to that. So a large landmass with a low value per tile would have fewer players than a smaller landmass with a higher value per tile. You could also ensure that the 'old world' is a better overall landmass than the 'new world' or the other way around if you prefer.

For final plot selection there are a few things to consider. First, since the game will generate additional bonuses around starting plots and the advantage of starting on a coast with clams/crabs/fish is HUGE in the early game due to their commerce AND food bonus a coastal start doesnt need as much 'good' land in the area as a non-coastal start. Second, hills are nice for production but only if you also have enough food, so a starting position with a food bonus or two and multiple hills is very good while a start position with hills and only production bonuses is not as good. Starting in, or even close to tundra can be very rough as that likely means the majority of your territory will be useless until you get lumbermills up and running. Rivers are critical in deserts & plains, but not as important in grasslands or hilly areas.

Now the one thing that would be really nice, but I'm not sure if it's even possible, would be to place civs based on their starting techs. So if a civ has fishing, for one example, they should always start on a coast.

The inequality of starting plots is a big part of what makes the game fun, but it's also a big part of what makes the game frustrating. An in-game option for 'balanced' or 'wild' starting locations would be nice to have.

Edit: Missed the last question about starting distance :)
Starting distance is very important for a few different reasons. Nearby neighbors can be a threat if they're agressive or cornered with nowhere else to go. Nearby neighbors are a boon otherwise, the advantage of open borders and somebody to trade techs with is pretty big. So more important that the actual distance is the ability to establish contact and trade early. Of course if you start alone on a good landmass you'll do just fine, but the AI in the same situation may be crippled depending on which leader it is.

And, very quickly, since I'm a C++ guy, not a python guy... how do I turn off the python random?
 
How does your script work with BtS LDiCesare? Can you build a levee on Raels x-marked square in his screenshot?
I should test but I think I probably can't. The lake plot is water and has no reason to have a river on its border, whereas I suppose in BtS, there may be a river hidden below the lake. Maybe world editing the tile to change it to grass would show a river?
Furthermore, one must be careful with the default scripts, as they add lakes and rivers after player placement, and the code being called late can cause some artefacts (like desert rivers with no floodplains).
 
I'm particularly interested in ending this occurance as well.
Code:
X = Land, O = Starting Plot
............
...XOXXXOX..
..XX.....XX.
.XXXX.......
.XXXXXX.....
XXXXXXXX....
.XXXXX......
............

I see this alot on my map and it must stop. I think I know how to prevent it.
 
I should test but I think I probably can't. The lake plot is water and has no reason to have a river on its border, whereas I suppose in BtS, there may be a river hidden below the lake. Maybe world editing the tile to change it to grass would show a river?
Furthermore, one must be careful with the default scripts, as they add lakes and rivers after player placement, and the code being called late can cause some artefacts (like desert rivers with no floodplains).

I think you might be able to actually. I'm big on text graphics today so I'll show you what I mean. I'm talking about corners across river mouths.
Code:
X = Land, x = non river intersection, r = river
XxX......
xxx......
XxXxX....
rrrrrr...
XxXxXxX..<---River Here?

I have disabled the normalize functions that add lakes and rivers, so they all should be added in a way that I intended.
 
And, very quickly, since I'm a C++ guy, not a python guy... how do I turn off the python random?

The latest version has a global variable that I made just for you. It's called UsePythonRandom, and you can set it to 'False'. ;)
 
I'm running Civ IV on a Mac (PPC). When I try PerfectWorld, even version 1.04a, I get no elevations. I get small lakes and some forestation, but the world is all grasslands and two-square-long rivers.

Any ideas?

-K
 
as far as starting location goes. I agree with Seven in that starting too close (ie warrior combat close) is bad. It just encourages the player to rush and can dictate the flow of the rest of the game to unfavorable results.

On the other hand though, unfavorable starts can end up being a very fun game with a unique and exciting story. It's hard to pin down the best solution but IMHO if someone wants a truly balanced game for competitive MP or HOF reasons, that's what the vanilla scripts are for. However if it is something you think you can tackle, add some options for it for things like balanced starting plots, new world/old world, etc. so if we want a random map still we can have it :)
 
Top Bottom