Map Scripts: How do I .... ?

Jubala

Chieftain
Joined
Feb 2, 2006
Messages
7
How do I make/modify a map script to do some or all of the following:

1. Place civilizations that know fishing at the coast.

2. Make sure civilizations starting on the coast have at least one sea resource (fish/clam/crabs).

3. Make sure that civilizations that need a resource for their unique unit has that resource available within x plots of the starting location.

4. Place marble and stone so that they are halfway between two starting locations but on opposite sides of any given starting location. Example: The Greeks would find marble to the east halfway to The Indians and stone to the west halfway to The Romans.

5. Clump a resource to encourage trading.

6. Make sure two resources are placed close to each other while maintaining the standard amount of said resources on the map. For example have marble and stone close to each other without having more or less of them compared to a standard map script. Preferrably between two or more starting locations to encourage competition over the resources.

7. Make sure a specific civilization has a specific resource close to their starting position. Example: Rome has Iron, India has Ivory and so on.

8. Make sure the Player has a specific resource close to the starting location. Example: The player starts with cows close by.

9. Make all of the above as options to set when starting a new custom game.

10. Make a script that has options for which of the standard map types to use as well as the above.

11. Make a script which randomly pick one of the standard map types to generate and has the above options. With an option to pick which types to randomly choose from so I can exclude archipelago but maybe get terra, pangaea or continents.

How do I do all this? Where do I do all this? What if anything can be done in the .xml files and what needs to be done in Python? For python example code with comments would be appreciated. Thanks!
 
1. Place civilizations that know fishing at the coast.

override assignStartingPlots() and make sure that the plot assigned to each fishing civ touches at least one ocean.

2. Make sure civilizations starting on the coast have at least one sea resource (fish/clam/crabs).

During assignStartingPlots(), after assigning the start plots, make sure that the nearby ocean plot has a resource by calling pPlot.setBonusType(bonusId) with the fish/clam bonusId.

3. Make sure that civilizations that need a resource for their unique unit has that resource available within x plots of the starting location.

Same as #2, just check in a loop over x tiles relative to the start position to see if they already have the resource, and if not, add it.

4. Place marble and stone so that they are halfway between two starting locations but on opposite sides of any given starting location. Example: The Greeks would find marble to the east halfway to The Indians and stone to the west halfway to The Romans.

Again, after deciding the starting plots, remove any existing stone or marble that are in the wrong places, and re-place them where you'd like. In this case, find the valid tile (plot.canHaveBonus(bonus,True)) nearest to the halfway mark between each civ you're interested in, and set it to have that bonus.

5. Clump a resource to encourage trading.

This one presumably does not require waiting til the civs have been placed and can be done during addBonuses(). Just find all valid tiles for a resource, but only place it on tiles that are sufficiently close together (or in groups, for however many clumps you want, ie pick, say 3, start spots for the resource that are far apart, but then only allow additional start spots within 5 tiles of the original 3 spots)

6. Make sure two resources are placed close to each other while maintaining the standard amount of said resources on the map. For example have marble and stone close to each other without having more or less of them compared to a standard map script. Preferrably between two or more starting locations to encourage competition over the resources.


Let the standard resource generator place that bonus. Any time after it is done (for example, during player placement), you can then move one resource closer to the other, by unsetting it in one place, and resetting it at a tile near the other.

7. Make sure a specific civilization has a specific resource close to their starting position. Example: Rome has Iron, India has Ivory and so on.

8. Make sure the Player has a specific resource close to the starting location. Example: The player starts with cows close by.


Same as #2.

9. Make all of the above as options to set when starting a new custom game.

10. Make a script that has options for which of the standard map types to use as well as the above.

11. Make a script which randomly pick one of the standard map types to generate and has the above options. With an option to pick which types to randomly choose from so I can exclude archipelago but maybe get terra, pangaea or continents.


Have a look at the python code in SmartMap. It adds a number of new options, and also shows how you can include logic from existing scripts (look at how it includes great plains terrain generation).

How do I do all this? Where do I do all this? What if anything can be done in the .xml files and what needs to be done in Python? For python example code with comments would be appreciated. Thanks!

All of this can be done in a map script, much of it must be. You can cause resources to clump together in XML if you prefer, by changing their settings to be closer to how the existing happiness resources are set up (see Civ4BonusInfo.xml).
 
Surt, thank you for your reply. I have looked at smartmap as well at the map scripts that came with the game but I am having trouble figuring what does what. I have no prior experience with python but I have programmed in c++, ada, basic and some other language I can't remember the name of now. Smartmap is a little daunting to hunt through with over 5000 lines of code (wow!) and the scripts that came with the game are somewhat lacking in comments. I am a firm believer in commented code, at least when reading others code. :)

Anyway, what I am aiming at doing for starters is simply to slightly modify an existing script like Terra and add some or all of the stuff I mentioned in the first post. Most important for me would be 1, 2 and 4. As for 4 it doesn't matter which specific civ the stone and marble are placed between, just that they are and that the player is between the resources. If you can give some sample code for that I would appreciate it very much. Thanks in advance.
 
I've not tested any of this, I'm only providing this as a guide.

What you'll want to do for the items you're most interested in is to modify what happens after starting plots are assigned for the players.

As an overview, here are the steps in map generation:
1) the grid size is selected (how big is the map)
2) the base map tile types are determined (water, land, hills, or mountains)
3) terrain is differentiated (desert/land, grass/hills etc)
4) features are added (forest added to the grass/hills)
5) bonuses are placed (where do the stone and iron go?)
6) goody huts are placed
7) players are placed

After step 7, the game starts up. You can do a find for 'step 1' through 'step 7' in smartmap to look at how I handle each of these.

So what we're going to do is add some enhancement to step 7, after the players are placed, to correct the issues you have with the resources that were placed in step 5.

So, we'll be overriding the default player placement method, and doing some extra work.

PHP:
def assignStartingPlots():
	OutputMessage("In Step 7 Assign starting plots")
	cgc = CyGlobalContext()
	cymap = CyMap()
	dice = cgc.getGame().getMapRand()
	width = cymap.getWidth()
	height = cymap.getHeight()
	#this will get the players placed by the default method
	CyPythonMgr().allowDefaultImpl()

	#now you do your work


	players = cgc.getGame().countCivPlayersEverAlive()
	for playerIndex in range(players):
		pPlayer = cgc.getPlayer(playerIndex)
		pStartPlot = pPlayer.getStartingPlot()
		#if player has tech fishing, and start plot is not coastal, find coastal start plot and call pPlayer.setStartingPlot(newPlotThatIsCoastal)

		#decide if they need a sea resource added, and add it here
		pNearByWaterPlotYouFound.setBonusType(bonusIdOfFishResource)
Here is a handy method from smartmap for finding out if a plot is coastal (note this makes use of nearDirs (set of tiles within one tile of current tile) and getTileCoordXYWithWrap() which you'll also need, or otherwise replace the logic (this just loops over the nearby tiles, and decides if one of them is an ocean tile, the requirement for a coastal tile):
PHP:
def isCoastalPlot(px,py):
	cymap = CyMap()
	for nearDir in nearDirs:
		newX, newY = getTileCoordXYWithWrap((px,py), nearDir)
		if newX == px and newY == py:
			continue
		pTouchPlot = cymap.plot(newX,newY)
		
		#coastal is true if we touch a water that is not a lake
		if pTouchPlot.isWater() and not pTouchPlot.isLake():
			return True
	return False

Hopefully this is enough to get you started. Be sure to turn on all the python debugging stuff in your config file (see some of the other posts in map scripts if you don't know how).
 
Thank you surt! :thanx: I will try and get this to work for me and let you know how it goes.
 
I've been messing around with the map scripts some, wading through several satandard and mod scripts to try and figure stuff out. I decided I would create a script that is as standard as possible before I try adding any of the stuff I want so I have the basics down. I started with the continents.py script that came with the game and took stuff from other scripts. I have attached what I came up with and would appreciate it if you could take a look and tell me if I missed anything. I have tested it and it works.
 

Attachments

Oh, forgot to ask. Is there a list somewhere of the functions that can be called and what they do? I have searched on the wikis but I didn't find anything there.
 
Jubala said:
I've been messing around with the map scripts some, wading through several satandard and mod scripts to try and figure stuff out. I decided I would create a script that is as standard as possible before I try adding any of the stuff I want so I have the basics down. I started with the continents.py script that came with the game and took stuff from other scripts. I have attached what I came up with and would appreciate it if you could take a look and tell me if I missed anything. I have tested it and it works.

That looks fine except for your grid size function, which isn't returning a value (which works only because when a function fails to return a valid value, civiv uses a default value). If, for example, you were to double all the numbers in there, it would have no effect (though presumably you'd be expecting your maps to get twice as big).
 
Jubala, I said this on poly' too: there's a method in Archipelago.py that gives the results for coastal starts. Also, you can check CvMapScriptInterface.py in Assets\EntryPoints. It's good for documentation.
Apart of that, just about everything Surt said's right.
 
Back
Top Bottom