Star Wars Mod mapscript help

TC01

Deity
Joined
Jun 28, 2009
Messages
2,216
Location
Irregularly Online
For the Star Wars mod, I created a mapscript where instead of placing Solar Systems the way the Final Frontier places them, it places a 3x3 planet. I used the same code up to the point where it actually placed the Solar System, then I added this:

Spoiler :
Code:
					game = CyGame()
					iRnd = game.getSorenRandNum(9, "Place Planets")
					pPlanetCore = self.map.sPlot(iX, iY)
					if iRnd == 0:
						pPlanetCore.setTerrainType(self.planetClassMCore,true,true)
						pPlanetCore.setFeatureType(self.planetCore,0)
						for iPlanetX in range(iX-1, iX+2, 1):
							for iPlanetY in range(iY-1, iY+2, 1):
								pPlanetEdge = self.map.sPlot(iPlanetX, iPlanetY)
								if (iPlanetX == iX and iPlanetY == iY):
									continue
								else:
									pPlanetEdge.setTerrainType(self.planetClassMEdge,true,true)
					if 1 <= iRnd <= 3:
						pPlanetCore.setFeatureType(self.planetCore,0)
						for iPlanetX in range(iX-1, iX+2, 1):
							for iPlanetY in range(iY-1, iY+2, 1):
								pPlanetEdge = self.map.sPlot(iPlanetX, iPlanetY)
								pPlanetEdge.setTerrainType(self.planetArctic,true,true)
					if 4 <= iRnd <= 6:
						pPlanetCore.setFeatureType(self.planetCore,0)
						for iPlanetX in range(iX-1, iX+2, 1):
							for iPlanetY in range(iY-1, iY+2, 1):
								pPlanetEdge = self.map.sPlot(iPlanetX, iPlanetY)
								pPlanetEdge.setTerrainType(self.planetDesert,true,true)
					if 7 <= iRnd <= 9:
						pPlanetCore.setFeatureType(self.planetCore,0)
						for iPlanetX in range(iX-1, iX+2, 1):
							for iPlanetY in range(iY-1, iY+2, 1):
								pPlanetEdge = self.map.sPlot(iPlanetX, iPlanetY)
								pPlanetEdge.setTerrainType(self.planetVolcanic,true,true)

Now, I can't see anything wrong here. Except for the fact that the game gives this error:

Code:
Traceback (most recent call last):

  File "StarWars", line 571, in findStartingPlot

IndexError: list index out of range
ERR: Python function findStartingPlot failed, module StarWars

Here is the corresponding code, the findStartingPlot function (up to the point where the error occurs):

Spoiler :
Code:
def findStartingPlot(argsList):
	[iPlayer] = argsList
	
	gc = CyGlobalContext()
	
	iStartPlotNum = -1
	
	#print("Checking for player %d in" %(iPlayer))
	#print(g_aiPickedPlotForPlayer)
	
	# Don't find a starting location more than once for a player
	if (iPlayer in g_aiPickedPlotForPlayer):
		
		pStartPlot = gc.getPlayer(iPlayer).getStartingPlot()
		#print("pStartPlot is already assigned to %d, %d" %(pStartPlot.getX(), pStartPlot.getY()))
		iPlotNum = CyMap().plotNum(pStartPlot.getX(), pStartPlot.getY())
		
		# Reset lists... would do this elsewhere but it appears there are multiple instances of these arrays floating around so I have to clear them in this function it seems
		g_aiPickedPlotForPlayer.remove(iPlayer)
		global g_apSolarSystemPlotList
		g_apSolarSystemPlotList = []
		
		return iPlotNum
	
	#print("Solar System Plot List is of size %d" %(len(g_apSolarSystemPlotList)))
	
	# Make list of valid plots (Solar Systems), but only once
	if (len(g_apSolarSystemPlotList) == 0):
		
		for iPlotLoop in range(CyMap().numPlots()):
			pPlot = CyMap().plotByIndex(iPlotLoop)
			
			iPlanetCore = gc.getInfoTypeForString("FEATURE_PLANET_CORE")
			if (pPlot.getFeatureType() == iPlanetCore):
				g_apSolarSystemPlotList.append(pPlot)
	
	# Now pick a random solar system from this list to see if it should become the player's start location
	iNumSolarSystems = len(g_apSolarSystemPlotList)

	pPlayer = gc.getPlayer(iPlayer)
	
	iRange = pPlayer.startingPlotRange()
	iPass = 0
	
	# Loop until we find a valid plot
	while (true):
		
		iRand = CyGame().getSorenRandNum(iNumSolarSystems, "Picking random solar system to start at for player %d" %(iPlayer))
		pRandomPlot = g_apSolarSystemPlotList[iRand]

It is filling g_apSolarSystemList with every plot that has a FEATURE_PLANET_CORE... or it should be. But what the error above means is that the list is not working- possibly never getting filled, since this error repeats itself quite a number of times in the log.

Then the game crashes (because it failed to place any civ, I assume).

Can anyone help solve this problem? Is something wrong with the randomly-assigning-planets part or the filling-g_apSolarSystemList part?
 
One possibility comes immediately to mind:

Are you perhaps removing items from the g_apSolarSystemPlotList in the loop, thereby decreasing how many list element there are, but always using the iNumSolarSystems value that was the original length of the list?
 
I think so, but the FinalFrontier.py function seems to works the same way (unless I'm missing something). I can't quite puzzle out how the script works, but here's both findStartingPlot functions (in their entirety) if it helps:

Spoiler Final Frontier :
Code:
def findStartingPlot(argsList):
	[iPlayer] = argsList
	
	gc = CyGlobalContext()
	
	iStartPlotNum = -1
	
	#print("Checking for player %d in" %(iPlayer))
	#print(g_aiPickedPlotForPlayer)
	
	# Don't find a starting location more than once for a player
	if (iPlayer in g_aiPickedPlotForPlayer):
		
		pStartPlot = gc.getPlayer(iPlayer).getStartingPlot()
		#print("pStartPlot is already assigned to %d, %d" %(pStartPlot.getX(), pStartPlot.getY()))
		iPlotNum = CyMap().plotNum(pStartPlot.getX(), pStartPlot.getY())
		
		# Reset lists... would do this elsewhere but it appears there are multiple instances of these arrays floating around so I have to clear them in this function it seems
		g_aiPickedPlotForPlayer.remove(iPlayer)
		global g_apSolarSystemPlotList
		g_apSolarSystemPlotList = []
		
		return iPlotNum
	
	#print("Solar System Plot List is of size %d" %(len(g_apSolarSystemPlotList)))
	
	# Make list of valid plots (Solar Systems), but only once
	if (len(g_apSolarSystemPlotList) == 0):
		
		iFeatureSolarSystem = gc.getInfoTypeForString("FEATURE_SOLAR_SYSTEM")
		
		for iPlotLoop in range(CyMap().numPlots()):
			pPlot = CyMap().plotByIndex(iPlotLoop)
			
			if (pPlot.getFeatureType() == iFeatureSolarSystem):
				g_apSolarSystemPlotList.append(pPlot)
	
	# Now pick a random solar system from this list to see if it should become the player's start location
	iNumSolarSystems = len(g_apSolarSystemPlotList)

	pPlayer = gc.getPlayer(iPlayer)
	
	iRange = pPlayer.startingPlotRange()
	iPass = 0
	
	# Loop until we find a valid plot
	while (true):
		
		iRand = CyGame().getSorenRandNum(iNumSolarSystems, "Picking random solar system to start at for player %d" %(iPlayer))
		pRandomPlot = g_apSolarSystemPlotList[iRand]
		
#		#print("Randomly chosen plot is at %d, %d" %(pRandomPlot.getX(), pRandomPlot.getY()))
		
		bValid = True
		
		# See if the random Solar System is too close to another player's start
		for iClosePlayerLoop in range(gc.getMAX_CIV_PLAYERS()):
			if (gc.getPlayer(iClosePlayerLoop).isAlive()):
				if (iClosePlayerLoop != iPlayer):
					if startingPlotWithinRange(gc.getPlayer(iClosePlayerLoop), pRandomPlot, iPlayer, iRange, iPass):
						bValid = False
						break
		
#		#print("Valid?")
#		#print(bValid)
		
		# The one we've picked isn't too close, so use it
		if (bValid):
			
			iStartPlotNum = CyMap().plotNum(pRandomPlot.getX(), pRandomPlot.getY())
			g_apSolarSystemPlotList.remove(pRandomPlot)		# Now remove the plot from the list of valid Solar Systems which players can start at
			g_aiPickedPlotForPlayer.append(iPlayer)			# This player has now been assigned a starting plot, so this array tells the game not to do it again
			
			# Remove nearby goody huts
			for iXLoop in range(pRandomPlot.getX()-1, pRandomPlot.getX()+2):
				for iYLoop in range(pRandomPlot.getY()-1, pRandomPlot.getY()+2):
					
					iActiveX = iXLoop
					iActiveY = iYLoop
					
					if (iActiveX < 0):
						iActiveX = CyMap().getGridWidth() + iActiveX
					if (iActiveY < 0):
						iActiveY = CyMap().getGridHeight() + iActiveY
					
					pLoopPlot = CyMap().plot(iActiveX, iActiveY)
					
					# Any improvement? At this point it would only be a goody hut
					if (pLoopPlot.getImprovementType() != -1):
						pLoopPlot.setImprovementType(-1)
			
			# Add player city & Planetary Defense unit to start
			
			#print("Adding player starting city to %d, %d" %(pRandomPlot.getX(), pRandomPlot.getY()))
			pPlayer.initCity(pRandomPlot.getX(), pRandomPlot.getY())
			pCity = pRandomPlot.getPlotCity()
			
			iBuildingCapitol = gc.getInfoTypeForString("BUILDING_CAPITOL")
			pCity.setNumRealBuilding(iBuildingCapitol, 1)
			
			if (pPlayer.getNumUnits() == 0):
				iUnitType = gc.getInfoTypeForString("UNIT_PLANETARY_DEFENSE_I")
				pPlayer.initUnit(iUnitType, pRandomPlot.getX(), pRandomPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.NO_DIRECTION)
			
			break		# We're done searching, exit the while loop
			
		# This run has failed to find a starting plot far enough away, try another random plot
		print "Player ID", iPlayer, "pass", iPass, "failed"
		iPass += 1
	
	return iStartPlotNum

Spoiler Star Wars :
Code:
def findStartingPlot(argsList):
	[iPlayer] = argsList
	
	gc = CyGlobalContext()
	
	iStartPlotNum = -1
	
	#print("Checking for player %d in" %(iPlayer))
	#print(g_aiPickedPlotForPlayer)
	
	# Don't find a starting location more than once for a player
	if (iPlayer in g_aiPickedPlotForPlayer):
		
		pStartPlot = gc.getPlayer(iPlayer).getStartingPlot()
		#print("pStartPlot is already assigned to %d, %d" %(pStartPlot.getX(), pStartPlot.getY()))
		iPlotNum = CyMap().plotNum(pStartPlot.getX(), pStartPlot.getY())
		
		# Reset lists... would do this elsewhere but it appears there are multiple instances of these arrays floating around so I have to clear them in this function it seems
		g_aiPickedPlotForPlayer.remove(iPlayer)
		global g_apSolarSystemPlotList
		g_apSolarSystemPlotList = []
		
		return iPlotNum
	
	#print("Solar System Plot List is of size %d" %(len(g_apSolarSystemPlotList)))
	
	# Make list of valid plots (Solar Systems), but only once
	if (len(g_apSolarSystemPlotList) == 0):
		
		for iPlotLoop in range(CyMap().numPlots()):
			pPlot = CyMap().plotByIndex(iPlotLoop)
			
			iPlanetCore = gc.getInfoTypeForString("FEATURE_PLANET_CORE")
			if (pPlot.getFeatureType() == iPlanetCore):
				g_apSolarSystemPlotList.append(pPlot)
	
	# Now pick a random solar system from this list to see if it should become the player's start location
	iNumSolarSystems = len(g_apSolarSystemPlotList)

	pPlayer = gc.getPlayer(iPlayer)
	
	iRange = pPlayer.startingPlotRange()
	iPass = 0
	
	# Loop until we find a valid plot
	while (true):
		
		iRand = CyGame().getSorenRandNum(iNumSolarSystems, "Picking random solar system to start at for player %d" %(iPlayer))
		pRandomPlot = g_apSolarSystemPlotList[iRand]
		
#		#print("Randomly chosen plot is at %d, %d" %(pRandomPlot.getX(), pRandomPlot.getY()))
		
		bValid = True
		
		# See if the random Solar System is too close to another player's start
		for iClosePlayerLoop in range(gc.getMAX_CIV_PLAYERS()):
			if (gc.getPlayer(iClosePlayerLoop).isAlive()):
				if (iClosePlayerLoop != iPlayer):
					if startingPlotWithinRange(gc.getPlayer(iClosePlayerLoop), pRandomPlot, iPlayer, iRange, iPass):
						bValid = False
						break
		
#		#print("Valid?")
#		#print(bValid)
		
		# The one we've picked isn't too close, so use it
		if (bValid):
			
			iStartPlotNum = CyMap().plotNum(pRandomPlot.getX(), pRandomPlot.getY())
			g_apSolarSystemPlotList.remove(pRandomPlot)		# Now remove the plot from the list of valid Solar Systems which players can start at
			g_aiPickedPlotForPlayer.append(iPlayer)			# This player has now been assigned a starting plot, so this array tells the game not to do it again
			
			# Remove nearby goody huts
			for iXLoop in range(pRandomPlot.getX()-1, pRandomPlot.getX()+2):
				for iYLoop in range(pRandomPlot.getY()-1, pRandomPlot.getY()+2):
					
					iActiveX = iXLoop
					iActiveY = iYLoop
					
					if (iActiveX < 0):
						iActiveX = CyMap().getGridWidth() + iActiveX
					if (iActiveY < 0):
						iActiveY = CyMap().getGridHeight() + iActiveY
					
					pLoopPlot = CyMap().plot(iActiveX, iActiveY)
					
					# Any improvement? At this point it would only be a goody hut
					if (pLoopPlot.getImprovementType() != -1):
						pLoopPlot.setImprovementType(-1)
			
			# Add player city & Planetary Defense unit to start
			
			#print("Adding player starting city to %d, %d" %(pRandomPlot.getX(), pRandomPlot.getY()))
			pPlayer.initCity(pRandomPlot.getX(), pRandomPlot.getY())
			pCity = pRandomPlot.getPlotCity()
			
			iBuildingCapitol = gc.getInfoTypeForString("BUILDINGCLASS_PALACE")
			pCity.setNumRealBuilding(iBuildingCapitol, 1)
			
			if (pPlayer.getNumUnits() == 0):
				iUnitType = gc.getInfoTypeForString("UNIT_WOOKIE")
				pPlayer.initUnit(iUnitType, pRandomPlot.getX(), pRandomPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.NO_DIRECTION)
			
			break		# We're done searching, exit the while loop
			
		# This run has failed to find a starting plot far enough away, try another random plot
		print "Player ID", iPlayer, "pass", iPass, "failed"
		iPass += 1
	
	return iStartPlotNum
 
You should printout the value of iNumSolarSystems.

My guess is that it is 0. This would cause
Code:
iRand = CyGame().getSorenRandNum(iNumSolarSystems, "Picking random solar system to start at for player %d" %(iPlayer))
to have a problem since it returns a value that is from 0 to (limit - 1). If you specify a limit of 0, what do you gett back? Probably a -1. Then -1 is not a valid index for g_apSolarSystemPlotList.

If this does turn out to be the case, that iNumSolarSystems = 0, then I may know why...

The problem may be that when you generate the planet you use this
Code:
pPlanetCore = self.map.sPlot(iX, iY)
to get the plot (which should be OK) and then you use this sort of thing
Code:
						pPlanetCore.setTerrainType(self.planetClassMCore,true,true)
						pPlanetCore.setFeatureType(self.planetCore,0)
to set the terrain and feature.

The regular FF code, however, directly sets it like so:
Code:
					CyMap().plot(iX, iY).setFeatureType(self.featureSolarSystem, 0)

What's the difference?

Well, what data does you pPlanetCore referencing? It is referencing the copy of the plot list that is stored when the class is initialised. (Thus the "self" reference.) If you change the plot's feature type, you are changing the data in the copy of the list of plots rather than the actual list of plots. Then when you build g_apSolarSystemPlotList you refer to the actual plots, so you don't find any.

You should be changing the terrain/features of the actual plots, not the local copy.

I think.
 
Thanks, I'll test this.

Do I use CyMap() or do I use self.map? Does it matter?

Also, I note that FF doesn't bother doing this sort of thing for black holes, supernovas, etc. So when I set the plots around the planet (via a for loop) to have some such terrain type, can I just use the method pPlanetEdge.setTerrainType() or do I need to use the CyMap()/self.map method?
 
All this is pretty hazy. My explanation may be wrong. But the "IndexError: list index out of range" would be explained by having iNumSolarSystems be 0.

I think the first thing is to find out if iNumSolarSystems is 0.

If so, that is the problem. Why it would be 0 is a tad iffy at this point - the exact nature of the problem may be tricky.

The thing to do is probably to do each thing the same way the FF map script does them, since it works (although always using the actual CyMap ought to always work).
 
Thinking about it a bit more, I think my explanation for how it would happen must be wrong.

But having iNumSolarSystems is 0 would cause a problem like this.
 
Strange...

Looking at the original FinalFrontier.py, there seems to be an error inthe code on line 334.
This is lines 333 and 334:
Code:
								pEffectPlot = self.map.sPlot(xLoop, yLoop)
								pPlot.setFeatureType(self.featureFloodPlain, 0)
I would expect that "pPlot" on line 334 to be a "pEffectPlot". As far as I can tell, "pPlot" isn't even defined at that point in the code (it has been used in the function on lines 314 and 315, but those are inside an indented block of code) - I have no idea why this isn't failing at this point, but since it isn't then the thing is presumably reusing the stale pPlot from earlier in the code.

The same exact type of problem can be found at line 377, but here pPlot is defined but is the central plot in the area where the main feature is located. This would wipe out the central "oasis" feature, replacing it with a grav field feature (repeatedly). This doesn't seem to stop if from working - I don't know why, this should make it so that there is never a tile with the oasis feature type - the actual black hole tile would have the grav field feature type instead. And yet black holes do exist and do seem to work. I don't see how that is possible.
 
I noticed this while debugging my Wormholes modcomp. At first I thought I made the error, but my Wormholes were being placed, so I shrugged and gave up.

Note that Supernovas do not have this issue- they use pEffectPlot both times...

I'm wondering if this is the problem. Perhaps I should try changing the part in my function where I set surrounding tiles to flood plains to be "pEffectPlot"?
 
Have you checked to see if iNumSolarSystems is 0?

I noticed this while debugging my Wormholes modcomp. At first I thought I made the error, but my Wormholes were being placed, so I shrugged and gave up.

Note that Supernovas do not have this issue- they use pEffectPlot both times...

I'm wondering if this is the problem. Perhaps I should try changing the part in my function where I set surrounding tiles to flood plains to be "pEffectPlot"?

I would fix it. If you are using your pPlanetCore variable in the loop to place the floodplains instead of the pEffectPlot (or the current, erroneous, pPlot), then that could explain how you iNumSolarSystems might be. 0

In the original FF file it's not a major problem - at least, not the first instance in the star system placement. The reason for that is the the star system is being placed via the direct CyMap call and the pPlot is not pointing at that plot so the star system feature is left alone (it is probably using the "stale" pPlot from the earlier loop, which would leave it pointing at the nearby tile at an X+1, Y+2 offset as that is the last tile it would be set to). All of the floodplain features are deleted at the end of the funtion - they are apparently just temporarily used to help insure that things are not put too close together.

I'm still wondering how any black holes end up in the FF game. I thought they actually drew ships in and destroyed them in the game. They are certainly there visually. But as far as I can tell, the way FinalFrontier.py is set up should prevent any from making it onto the map. :confused:
 
Yes, it's 0:

[SPOILER="Loading module StarWars]
Code:
load_module StarWars

PY:Player 0's alive status set to: 1
PY:Player 1's alive status set to: 1
PY:Player 2's alive status set to: 1
PY:Player 3's alive status set to: 1
PY:Player 4's alive status set to: 1
PY:Player 5's alive status set to: 1
PY:Player 6's alive status set to: 1
PY:Player 7's alive status set to: 1
PY:Player 8's alive status set to: 1
PY:Player 9's alive status set to: 1
PY:Player 10's alive status set to: 1
PY:Player 18's alive status set to: 1
0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0
[/SPOILER]

I'm not using pPlanetCore for that part (I haven't messed with the pPlot and pEffectPlot), but I did change the way the flood plains were generated:

Code:
				iStartS = 3
				iStartL = 4

Code:
					for xLoop in range((iX - iStartS), (iX + iStartL)):
						for yLoop in range((iY - iStartS), (iY + iStartL)):
							if ((xLoop == iX - iStartS) or (xLoop == iX + iStartS)) and ((yLoop == iY - iStartS) or (yLoop == iY + iStartS)):
								continue #Skip the four corners.
							elif xLoop == iX and yLoop == iY:
								continue #Don't overwrite the original feature!
							else:
								pEffectPlot = self.map.sPlot(xLoop, yLoop)
								pPlot.setFeatureType(self.featureFloodPlain, 0)

I will try changing pPlot to pEffectPlot to see what this does.
 
Another possibility:

When generating the planets you use
Code:
						pPlanetCore.setFeatureType(self.planetCore,0)
When building the list you use
Code:
			iPlanetCore = gc.getInfoTypeForString("FEATURE_PLANET_CORE")
			if (pPlot.getFeatureType() == iPlanetCore):
				g_apSolarSystemPlotList.append(pPlot)

Are you sure that self.planetCore really is gc.getInfoTypeForString("FEATURE_PLANET_CORE")?
 
Yes, it is, I double checked:

Code:
		#Planet terrain types
		self.planetCore = self.gc.getInfoTypeForString("FEATURE_PLANET_CORE")

The self.gc. is only because Jon Shafer/other people working on the map used "self.gc" for everything except the findStartingPlot function, where they used "gc" for CyGlobalContext().
 
I'm running out of ideas, even bad ones (like the whole copy vs. reference thing, which has apparently turned out to not be a problem).

My suggestion is to add a bunch of print statements all over the place to see what is really going on.

A good first thign to do would be something like this:
Code:
	# Make list of valid plots (Solar Systems), but only once
	if (len(g_apSolarSystemPlotList) == 0):
		print( "Making list")
		iPlanetCore = gc.getInfoTypeForString("FEATURE_PLANET_CORE")
		print( "  iPlanetCore = %d" % (iPlanetCore))
		for iPlotLoop in range(CyMap().numPlots()):
			pPlot = CyMap().plotByIndex(iPlotLoop)
			print( "  checking plot %d, feature type = %d" % (iPlotLoop, pPlot.getFeatureType()))
			if (pPlot.getFeatureType() == iPlanetCore):
				print( "    plot feature matched iPlanetCore, adding to list")
				g_apSolarSystemPlotList.append(pPlot)

I just tried this, using FF (well, my own mod of it) with the only difference being that I used "FEATURE_SOLAR_SYSTEM" instead of "FEATURE_PLANET_CORE". It worked - in the copious output there were 89 things added to the list when loopping through the 10240 plots.

If this shows you adding things to the list, then the list is somehow not being preserved (or being cleared) so that nothing is in it when it is used later.

If this show that nothing is being added to the list, then somehow you have no planet cores being added to the map.
 
I may have figured out how there are actually black hole features on the map when the code would appear to be overwriting them with the grav field feature.

It is very freaky.

From what is happening, it looks like it is a behavior of the CyMap.sPlot(X, Y) function.
If you call this function to select a plot, then the other variables previously set using it point to the new plot too.

Really.

In the code where there is a "pPlot = self.map.sPlot(iX, iY)" that is later followed by a "pEffectPlot = self.map.sPlot(xLoop, yLoop)", the plot that pPlot points to is now the plot at (xLoop, yLoop), not the plot at (iX, iY).

I had been wondering why there was both a CyMap.plot(X, Y) function and a CyMap.sPlot(X,Y) function. The sPlot version is indicated as a static method. "Why would you want or need a static method for this", you might ask? I don't know. I had wondered what the difference was. My guess is that this is the difference.

I found this out by using the following code in the black hole generation part of the FinalFrontier.py map script:
Code:
				pPlot = self.map.sPlot(iX, iY)
				print( "BH - Start, feature = %d (pPlot: x = %d, y = %d)" % (pPlot.getFeatureType(), pPlot.getX(), pPlot.getY()))
				print( "    self.featureOasis = %d" % (self.featureOasis))
				if (pPlot.getFeatureType() == FeatureTypes.NO_FEATURE): #Place Black Hole here.
					pPlot.setFeatureType(self.featureOasis, 0)
					print( "BH - Oasis set, feature = %d" % (pPlot.getFeatureType()))
					#print('+++', 'Black Hole placed at', iX, iY, '---', 'Attempts used: ', (201 - iAttemptsLeft), '+++')
					for xLoop in range((iX - 2), (iX + 3)):
						for yLoop in range((iY - 2), (iY + 3)):
							if ((xLoop == iX - 2) or (xLoop == iX + 2)) and ((yLoop == iY - 2) or (yLoop == iY + 2)):
								continue #Skip the four corners.
							elif xLoop == iX and yLoop == iY:
								continue #Don't overwrite the original feature!
							else:
								pEffectPlot = self.map.sPlot(xLoop, yLoop)
#								pEffectPlot.setFeatureType(self.featureGravField, 0) # CP bug fix - was pPlot.setFeatureType(self.featureGravField, 0)
								print( "BH - pre effect, feature = %d (loop: x = %d, y = %d)" % (pPlot.getFeatureType(), xLoop, yLoop))
								pPlot.setFeatureType(self.featureGravField, 0)
								print( "BH - post effect, feature = %d (pPlot: x = %d, y = %d)" % (pPlot.getFeatureType(), pPlot.getX(), pPlot.getY()))
					break #Black Hole placed, effects placed, break loop.

Here is a sample from the resulting log file:
Code:
BH - Start, feature = -1 (pPlot: x = 62, y = 33)

    self.featureOasis = 3

BH - Oasis set, feature = 3

BH - pre effect, feature = 7 (loop: x = 60, y = 32)

BH - post effect, feature = 5 (pPlot: x = 60, y = 32)

BH - pre effect, feature = 7 (loop: x = 60, y = 33)

BH - post effect, feature = 5 (pPlot: x = 60, y = 33)

BH - pre effect, feature = -1 (loop: x = 60, y = 34)

BH - post effect, feature = 5 (pPlot: x = 60, y = 34)
Without ever touching it, the X and Y coordinates of pPlot change to match those of pEffectPlot.

This is why the original version is not overwriting the feature type at the original pPlot location - the plot that pPlot represents is automagically changing when it sets pEffectPlot.

Freaky.

This could somehow be related to why you have no planet core features on your map.
 
That is indeed freaky.

It may be the source of my problem. The debug confirmed that no Planet Cores were placed, ever, on the entire map.
 
Actually, I have an idea now.

When you change the terrain type of a plot with a feature already on it, do you destroy that feature? I had been assuming you wouldn't, but I'm no longer sure.
 
Good question. I'm not sure how it works. You can, at least, place features on terrain that is not listed as valid for the feature. I don't know what happens when you change the terrain. This does sound like a good possibility - it may validate the features and delete any that aren't, or it may just delete any feature.

It's just yet another guess, but I would not be surprised for it to wipe out any feature that is not possible to have on that terrain type. In worldbuilder, I expect that if you had a grassland tile with a forest on it and changed the grassland to ocean, the forest would go away. On the other hand...

In FF, the entire map is generated to be TERRAIN_TUNDRA.

The features in FF are still defined to be placeable on their normal terrain types. For example FEATURE_SOLAR_SYSTEM is set for TERRAIN_COAST and TERRAIN_OCEAN (so it was probably copied from fish). This does not stop it from placing the feature on plots that are TERRAIN_TUNDRA. (Which would seem to indicate that you could put fish on land on a regular map, if you really wanted to - it evidently doesnt' block placement, you have to check manually via CyPlot.canHaveFeature if you want features only on valid terrain.)

So it must let you place a feature on an invalid terrain. This doesn't answer the question of what happens when the terrain is changed, though.

Sounds like a job for yet more print statements to show the feature on the plots both before and after the terrain is changed.

Alternatively, you could just place the planet core feature after changing the terrain and see if it works. But don't forget the changing .sPlot thing I discovered - you'll want to set the plot to the right one after you do the terrain changes just before you add the feature. (Or perhaps use .plot instead, which I hope doesn't have that strange behavior.)

Yet another long rambling post just to say "I don't really know".
 
When you change the terrain type of a plot with a feature already on it, do you destroy that feature? I had been assuming you wouldn't, but I'm no longer sure.

Yes, you destroy it.

G
It's just yet another guess, but I would not be surprised for it to wipe out any feature that is not possible to have on that terrain type. In worldbuilder, I expect that if you had a grassland tile with a forest on it and changed the grassland to ocean, the forest would go away. On the other hand...

[...]

So it must let you place a feature on an invalid terrain. This doesn't answer the question of what happens when the terrain is changed, though.

You have to differentiate between land and water.
Changing a plot to water will just destroy everything on it, no matter what it is.
Changing a terrain from desert to tundra (whatever), will not cause this, all things will stay.
Even invalid features.
 
Top Bottom