CvWBDesc.py problems...

TC01

Deity
Joined
Jun 28, 2009
Messages
2,216
Location
Irregularly Online
Now on problem 3... see post #11.

For my Final Frontier worldbuilder project, I attempted to make buildings be stored to planets in the WBS file. The way things currently are, all buildings in the WBS are created on the system's best world, no matter which building they were originally on.

So, I added code to CvWBDesc.py to loop through each planet, get buildings, write them to the WBS, then to read them, store them to lists, and then to build them on the respective planets.

Unfortunately, while Jon Shafer made solar system data be read and stored in the CvWBDesc.py file, he made the actual solar system be created in CvEventManager's onGameStart function. And it seems that onGameStart is called for scenarios after all the apply functions in CvWBDesc.py are called (these functions take the read data from the .WBS and build the map). So I was attempting to deal with a solar system that did not yet exist.

I decided to try moving this code, which builds a solar system by calling data from CvWBDesc.py:

Code:
		# Is this a scenario file?
		if ('.CivBeyondSwordWBSave' in CyMap().getMapScriptName()):
			
			import CvWBInterface
			
			# Search through all plots on the map for Solar Systems to add content for
			for pWBPlotLoop in CvWBInterface.WBDesc.plotDesc:
				
				iX = pWBPlotLoop.iX
				iY = pWBPlotLoop.iY
				
				pPlot = CyMap().plot(iX, iY)
				
				if (pPlot.getFeatureType() == self.iFeatureIDSolarSystem):
					
					iStarType = getStarIndexFromTag(pWBPlotLoop.szStarType)
					
					pSystem = CvSystem(iX,iY,iStarType)
					
					for iPlanetLoop in range(pWBPlotLoop.iNumPlanets):
						iPlanetType = getPlanetIndexFromTag(pWBPlotLoop.aszPlanetType[iPlanetLoop])
						iOrbitRing = pWBPlotLoop.aiOrbitRing[iPlanetLoop]
						iPlanetSize = pWBPlotLoop.aiPlanetSize[iPlanetLoop]
						bMoon = pWBPlotLoop.aiMoon[iPlanetLoop]
						bRings = pWBPlotLoop.aiRings[iPlanetLoop]
						
						pSystem.addPlanet(iPlanetType, iPlanetSize, iOrbitRing, bMoon, bRings)
						
					self.addSystem(pSystem)

to CvWBDesc.py's apply function, under CvPlotDesc:

Code:
		if (self.featureType):
			featureTypeNum = CvUtil.findInfoTypeNum(gc.getFeatureInfo, gc.getNumFeatureInfos(), self.featureType)
			plot.setFeatureType(featureTypeNum, self.featureVariety)
			if plot.getFeatureType() == gc.getInfoTypeForString('FEATURE_SOLAR_SYSTEM'):
				print "Generating System"
				FinalFrontier = CvEventInterface.getEventManager()
				iSunType = getStarIndexFromTag(self.szStarType)
				print ("	System Sun = %d" % (iSunType))
				pSystem = CvSystem(self.iX, self.iY, iSunType)
				for iPlanetLoop in range(self.iNumPlanets):
					print ("	Planet %d" % (iPlanetLoop))
					iPlanetType = getPlanetIndexFromTag(self.aszPlanetType[iPlanetLoop])
					print ("		Planet Type = %d" % (iPlanetType))
					iOrbitRing = self.aiOrbitRing[iPlanetLoop]
					print ("		Orbit Ring = %d" % (iOrbitRing))
					iPlanetSize = self.aiPlanetSize[iPlanetLoop]
					print ("		Planet Size = %d" % (iPlanetSize))
					iMoon = self.aiMoon[iPlanetLoop]
					print ("		Moon = %d" % (iMoon))
					iRings = self.aiRings[iPlanetLoop]
					print ("		Rings = %d" % (iRings))
					pSystem.addPlanet(iPlanetType, iOrbitRing, iPlanetSize, iMoon, iRings)
					print ("	Planet Added")
				FinalFrontier.addSystem(pSystem)
				print ("System Generated")

However, it wasn't working. The print statements I added confirmed that the system was being made, but in the actual game, there was no solar system. (Just like what happens if you try to place a solar system in worldbuilder without using my FF worldbuilder utility). Here is the debug result for a sample system (the "adding planet to system" lines are debug statements from CvSolarSystem.py that I turned on):

Spoiler From PythonDbg.log :
Code:
Generating System

	System Sun = 3

	Planet 0

		Planet Type = 3

		Orbit Ring = 7

		Planet Size = 2

		Moon = 0

		Rings = 0

		Adding planet to (3, 6) system with: ORANGE_PLANET, 7, 2, 0, 0

	Planet Added

	Planet 1

		Planet Type = 3

		Orbit Ring = 2

		Planet Size = 2

		Moon = 0

		Rings = 0

		Adding planet to (3, 6) system with: ORANGE_PLANET, 2, 2, 0, 0

	Planet Added

	Planet 2

		Planet Type = 5

		Orbit Ring = 3

		Planet Size = 1

		Moon = 1

		Rings = 0

		Adding planet to (3, 6) system with: YELLOW_PLANET, 3, 1, 1, 0

	Planet Added

	Planet 3

		Planet Type = 1

		Orbit Ring = 8

		Planet Size = 2

		Moon = 0

		Rings = 0

		Adding planet to (3, 6) system with: GRAY_PLANET, 8, 2, 0, 0

	Planet Added

	Planet 4

		Planet Type = 5

		Orbit Ring = 5

		Planet Size = 2

		Moon = 0

		Rings = 0

		Adding planet to (3, 6) system with: YELLOW_PLANET, 5, 2, 0, 0

	Planet Added

	Planet 5

		Planet Type = 2

		Orbit Ring = 6

		Planet Size = 0

		Moon = 0

		Rings = 0

		Adding planet to (3, 6) system with: GREEN_PLANET, 6, 0, 0, 0

	Planet Added

System Generated

The code seems to be fine. What it is doing is exactly the same as what the code in onGameStart is doing, except I don't need to loop through all plots that have a solar system on them, since the code in CvWBDesc.py would be triggered whenever a feature that is a solar system is placed.

If I can't get it to work, I can put the old code back in onGameStart and just add my building code there instead. But it seems to me it would be simpler to have the code here (look at how much less code I actually need- half of my added code is all print statements...).
 
At the beginning of onGameStart is a call to self.initMembers(). This initializes the data. Or, in your case, probably re-initializes it to an empty system list and system count of 0.

The solution for this is to move that call to the part of the "if" that happens only when it is not loading a WB save, to the "else" block for when using a freshly generated system.

You might also want to call this before you load the systems in CvWBDesc.py. Just to be sure. But only once, not every time you hit a plot with a star system feature - perhaps getting the event manager and doing this before starting the plot processing loop.
 
After doing that, I'm no longer getting some python exceptions about the solar system's data (specifically, getNumPlanets being unable to work for something that was not a system). But I'm still getting the python exception below, and no systems are appearing:

Code:
Traceback (most recent call last):

  File "CvEventInterface", line 19, in onEvent

  File "CvEventManager", line 187, in handleEvent

  File "CvFinalFrontierEvents", line 164, in onGameStart

  File "CvFinalFrontierEvents", line 1520, in doBeginTurnAI

  File "CvFinalFrontierEvents", line 1196, in doCityHappinessPopLimit

  File "CvSolarSystem", line 193, in getPopulationLimit

  File "CvSolarSystem", line 482, in getPopulationLimit

IndexError: list index out of range
ERR: Python function onEvent failed, module CvEventInterface

I got rid of all of the scenario code (since I'm handling buildings in CvWBDesc.py and creating them on their appropriate planets, rather then on the best planet in the system by default), and the onGameStart section now looks like this:

Code:
	def onGameStart(self, argsList):
		'Called at the start of the game'
		self.parent.onGameStart(self, argsList)
		
		self.iWinningTeam = -1
		self.iTimeLeft = 0
		
		self.initValues()
		
		CyGame().makeNukesValid(true)
		CyGame().setStartYear(2302)
		
		AI.initPlayerAIInfos()
		
		# Give Barbs some techs
		iTechHappy0 = CvUtil.findInfoTypeNum(gc.getTechInfo,gc.getNumTechInfos(),'TECH_HAPPY_0')
		iTechIndustry0 = CvUtil.findInfoTypeNum(gc.getTechInfo,gc.getNumTechInfos(),'TECH_INDUSTRY_0')
		iTechMilitary0 = CvUtil.findInfoTypeNum(gc.getTechInfo,gc.getNumTechInfos(),'TECH_MILITARY_0')
		iTechMilitary1 = CvUtil.findInfoTypeNum(gc.getTechInfo,gc.getNumTechInfos(),'TECH_MILITARY_1')
		
		pBarbPlayer = gc.getPlayer(18)
		pBarbTeam = gc.getTeam(pBarbPlayer.getTeam())
		pBarbTeam.setHasTech(iTechHappy0, true, 18, false, false)
		pBarbTeam.setHasTech(iTechIndustry0, true, 18, false, false)
		pBarbTeam.setHasTech(iTechMilitary0, true, 18, false, false)
		pBarbTeam.setHasTech(iTechMilitary1, false, 18, false, false)

		# Is this not a scenario file?
		if not ('.CivBeyondSwordWBSave' in CyMap().getMapScriptName()):
			
			self.initMembers()
			
			# Loop through all plots, find the Solar Systems and give them randomized starting junk
			for iPlotLoop in range(CyMap().numPlots()):
				pPlot = CyMap().plotByIndex(iPlotLoop)
				
				if (pPlot.getFeatureType() == self.iFeatureIDSolarSystem):
					iYield = -1 #No preference
					pSystem = createRandomSystem(pPlot.getX(), pPlot.getY(), iYield, iPlanetQuantityTypePoor)	# Called from CvSolarSystem
					self.addSystem(pSystem)

I added a call to initMembers in the applyMap function of CvWBDesc.py, right before it loops and calls the CvPlotDesc apply function for each plot:

Code:
		print "apply plots"
#Added in Final Frontier Worldbuilder: TC01
		FinalFrontier = CvEventInterface.getEventManager()
		FinalFrontier.initMembers()
#End of Final Frontier Worldbuilder
		for pDesc in self.plotDesc:
			pDesc.apply()
 
Hmmm...

The apSystems list and iNumSystems value can also be reset via the resetSystems function. This is called only in loadSystemsFromPlots which is only used in onLoadGame. I'm not sure why that would be called.

That is probably a red herring.

I suspect something...

Assuming that your CvSolarSystem line numbers match the default's then: That index list out of range would seem to indicate that you are using an index not in the range of 0 to 2 to access the aiDefaultPopulationPlanetSizeTypes list, which is a global list that never changes, initialized near the end of CvSolarSystem. When you save/load/use (whatever) the planets' sizes are you using a number in the range from 0 to 2, or 1 through 3? The size value is apparently actually the index into that array, which (as it turns out) is one less than the base population value the planet can hold.
 
Yes, my CvSolarSystem.py's line numbers match the normal ones.

It would APPEAR that the planet size should be 0 to 2, because of this fragment from the WBS test scenario I am using, and from the log in the first post that marks planet size:

Code:
PlanetType=YELLOW_PLANET, OrbitRing=6, PlanetSize=2, HasMoon=0, HasRings=0
PlanetType=GREEN_PLANET, OrbitRing=1, PlanetSize=1, HasMoon=0, HasRings=1
PlanetType=WHITE_PLANET, OrbitRing=3, PlanetSize=2, HasMoon=0, HasRings=0
PlanetType=GRAY_PLANET, OrbitRing=4, PlanetSize=2, HasMoon=0, HasRings=1
PlanetType=BLUE_PLANET, OrbitRing=2, PlanetSize=0, HasMoon=0, HasRings=0
PlanetType=ORANGE_PLANET, OrbitRing=7, PlanetSize=2, HasMoon=1, HasRings=0

However, I checked this by putting a statement to get and print the planet size right before the line that causes the python exception.

"Planet Size = 6"

Well, that's what I call "out of range"... and I have no idea how this happened, especially since I searched the WBS file I'm using for a planet size of 6 and didn't find one.
 
You wouldn't happen to be setting the planet size to be the orbit ring value, would you?
The first one you listed is 6.

Oops.

It looks like I wrote out the call to addPlanet from memory, and flipped the correct positions of iPlanetSize and iOrbitRing. I was passing planet size as orbit ring and passing orbit ring as planet size.

Actually, I think I did that because in the original code in CvFinalFrontierEvents.py, Jon Shafer gets them in the wrong order:

Code:
iPlanetType = getPlanetIndexFromTag(pWBPlotLoop.aszPlanetType[iPlanetLoop])
iOrbitRing = pWBPlotLoop.aiOrbitRing[iPlanetLoop]
iPlanetSize = pWBPlotLoop.aiPlanetSize[iPlanetLoop]

The actual call to addPlanet is written properly, but I didn't copy that, and rewrote it from scratch...

It works now! Thanks!
 
Okay, now I'm having more problems.

The next thing I'm trying to do is make the planet's name be stored in the WBS file, and then set at the beginning of the game. I changed the write code

Code:
f.write("\t\tPlanetType=%s, OrbitRing=%d, PlanetSize=%d, HasMoon=%d, HasRings=%d, PlanetName=%s\n" %(szPlanetType, pPlanet.getOrbitRing(), pPlanet.getPlanetSize(), pPlanet.isMoon(), pPlanet.isRings(), pPlanet.getName()))

to store "pPlanet.getName()" at the end of the list, so it looks like this in the WBS:

Code:
PlanetType=YELLOW_PLANET, OrbitRing=4, PlanetSize=2, HasMoon=0, HasRings=0, PlanetName=Zeta Sagittarius

Then I added the following to the read function:

Code:
print "Checking for PlanetName tag in WBS file"
v = parser.findTokenValue(toks, "PlanetName")
print ("	Checking for data in PlanetName tag: %s" % (v))
if v!=-1:
	print ("	Data found for PlanetName tag, data = %s" % (v))
		self.aszPlanetName.append(v)
		continue

And these lines to the planet adding code I showed above:

Code:
pPlanet = pSystem.getPlanet(iOrbitRing)
pPlanet.setName(self.aszPlanetName[iPlanetLoop])

However, the data is never getting read. The print statements I added in read reveal that the read code is not finding anything for "PlanetName".... And yet, clearly there is something for PlanetName, it's in the WorldBuilderSave file. What have I done wrong?
 
You might want to print the entire line (nextLine) and then the entire list of tokens you get from the parser (toks) for that line.

And, of course, make sure you're reading the WBS file you think you are reading...

And finally, you didn't show much context for your addition. If you put it after the "if" that checks for "HasRings", did you remove the "continue" statment from the HasRings? That continue needs to be removed if you are after that code. If it is not, it will bail to the next loop iteration before your code is run.
 
You might want to print the entire line (nextLine) and then the entire list of tokens you get from the parser (toks) for that line.

And, of course, make sure you're reading the WBS file you think you are reading...

And finally, you didn't show much context for your addition. If you put it after the "if" that checks for "HasRings", did you remove the "continue" statment from the HasRings? That continue needs to be removed if you are after that code. If it is not, it will bail to the next loop iteration before your code is run.

Yes, I forgot to remove the "continue" in the "hasRings" section. Thanks again!
 
One final problem. Although this one is unrelated to CvWBDesc.py, it is the only bug that I have been unable to fix in CvWorldBuilderScreen.py, the other file the worldbuilder mod uses.

I added code to automatically add the black hole gravity field and the supernova damage area to the appropriate plots when you place the black hole or supernova in Worldbuilder. It works perfectly, and it is basically the loop from the mapscript to do the same thing, with only a few things altered. (Instead of using the static function CyMap().splot, I used CyMap().plot).

So then, I added code to remove those features when you remove a Black Hole or a supernova. I added some right before another feature is placed, like the solar system:

Spoiler placeObject :
Code:
#Modified in Final Frontier Worldbuilder: TC01
							iX = self.m_pCurrentPlot.getX()
							iY = self.m_pCurrentPlot.getY()
							if self.m_pCurrentPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_SOLAR_SYSTEM'):
								FinalFrontier = CvEventInterface.getEventManager()
								pSystem = FinalFrontier.getSystemAt(self.m_pCurrentPlot.getX(), self.m_pCurrentPlot.getY())
								FinalFrontier.apSystems.remove(pSystem)
								FinalFrontier.iNumSystems -= 1
								self.m_pCurrentPlot.setScriptData("")
							if self.m_pCurrentPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_OASIS'):
								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
										elif xLoop == iX and yLoop == iY:
											continue
										else:
											pEffectPlot = CyMap().plot(xLoop, yLoop)
											if pEffectPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_GRAV_FIELD'):
												pEffectPlot.setFeatureType(-1, -1)
							if self.m_pCurrentPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_JUNGLE'):
								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
										elif xLoop == iX and yLoop == iY:
											continue
										else:
											pEffectPlot = CyMap().plot(xLoop, yLoop)
											if pEffectPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_SUPERNOVA_AREA'):
												pEffectPlot.setFeatureType(-1, -1)
#End of Final Frontier Worldbuilder
							self.m_pCurrentPlot.setFeatureType(i, j)

And I added more code under the removeObject function and the eraseAll function, the only other places with setFeatureType statements in CvWorldBuilderScreen.py.

Spoiler removeObject :
Code:
			elif (self.m_iNormalMapCurrentList[self.m_normalMapTabCtrl.getActiveTab()] == self.m_iFeatureListID):
				iFeatureType = self.m_iNormalMapCurrentIndexes[self.m_normalMapTabCtrl.getActiveTab()]
#Added in Final Frontier Worldbuilder: TC01
				iX = self.m_pCurrentPlot.getX()
				iY = self.m_pCurrentPlot.getX()
				if self.m_pCurrentPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_SOLAR_SYSTEM'):
					FinalFrontier = CvEventInterface.getEventManager()
					pSystem = FinalFrontier.getSystemAt(self.m_pCurrentPlot.getX(), self.m_pCurrentPlot.getY())
					FinalFrontier.apSystems.remove(pSystem)
					FinalFrontier.iNumSystems -= 1
					self.m_pCurrentPlot.setScriptData("")
				if self.m_pCurrentPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_OASIS'):
					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
							elif xLoop == iX and yLoop == iY:
								continue
							else:
								pEffectPlot = CyMap().plot(xLoop, yLoop)
								if pEffectPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_GRAV_FIELD'):
									pEffectPlot.setFeatureType(-1, -1)
				if self.m_pCurrentPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_JUNGLE'):
					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
							elif xLoop == iX and yLoop == iY:
								continue
							else:
								pEffectPlot = CyMap().plot(xLoop, yLoop)
								if pEffectPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_SUPERNOVA_AREA'):
									pEffectPlot.setFeatureType(-1, -1)
#End of Final Frontier Worldbuilder
				self.m_pCurrentPlot.setFeatureType(FeatureTypes.NO_FEATURE, -1)
				return 1

Spoiler eraseAll :
Code:
#Added in Final Frontier Worldbuilder: TC01
			iX = self.m_pCurrentPlot.getX()
			iY = self.m_pCurrentPlot.getX()
			if self.m_pCurrentPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_SOLAR_SYSTEM'):
				FinalFrontier = CvEventInterface.getEventManager()
				pSystem = FinalFrontier.getSystemAt(self.m_pCurrentPlot.getX(), self.m_pCurrentPlot.getY())
				FinalFrontier.apSystems.remove(pSystem)
				FinalFrontier.iNumSystems -= 1
				self.m_pCurrentPlot.setScriptData("")
			if self.m_pCurrentPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_OASIS'):
				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
						elif xLoop == iX and yLoop == iY:
							continue
						else:
							pEffectPlot = CyMap().plot(xLoop, yLoop)
							if pEffectPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_FALLOUT'):
								pEffectPlot.setFeatureType(-1, -1)
			if self.m_pCurrentPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_JUNGLE'):
				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
						elif xLoop == iX and yLoop == iY:
							continue
						else:
							pEffectPlot = CyMap().plot(xLoop, yLoop)
							if pEffectPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_FOREST'):
								pEffectPlot.setFeatureType(-1, -1)
#End of Final Frontier Worldbuilder
			self.m_pCurrentPlot.setFeatureType(FeatureTypes.NO_FEATURE, -1)

And yet, when I delete one of these features with another feature, it works... but when I just use the Erase All or the right click... it doesn't work. But the code looks exactly the same to me, so I'm not sure what I've done wrong.

I've been testing this by using Asteroids instead of Supernova area and Radiation Clouds instead of gravity fields, so I know for sure that features are or aren't being removed.
 
Top Bottom