Making WB make random solar systems

TC01

Deity
Joined
Jun 28, 2009
Messages
2,216
Location
Irregularly Online
I've been trying to make Worldbuilder in Final Frontier (well, Star Trek actually) to place a randomized solar system when you place a solar system on a plot. Such code exists; the game uses it in onGameStart to generate random systems after starting a mapscript:

Code:
		else:
			
			# 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)

So I went into CvWBDesc.py and edited class CvPlotDesc def apply here:

Code:
		if (self.featureType):
			featureTypeNum = CvUtil.findInfoTypeNum(gc.getFeatureInfo, gc.getNumFeatureInfos(), self.featureType)
			plot.setFeatureType(featureTypeNum, self.featureVariety)
[COLOR="Red"]			print "Post setFeatureType code reached"
			if self.featureType == 'FEATURE_SOLAR_SYSTEM':
				print "Solar System Placed by Worldbuilder"
				FinalFrontier = CvEventInterface.getEventManager()
				pSystem = createRandomSystem(plot.getX(), plot.getY(), -1, 3)
				FinalFrontier.addSystem(pSystem)[/COLOR]

No system was generated, and neither print statement appears in PythonDbg.log. The debug confirms that I have selected the Solar System button to place, but not that anything else has happened. Thinking perhaps that this meant that no code after the setFeatureType() statement was executed, I tried this:

Code:
		if (self.featureType):
			featureTypeNum = CvUtil.findInfoTypeNum(gc.getFeatureInfo, gc.getNumFeatureInfos(), self.featureType)
[COLOR="red"]			print "Post setFeatureType code reached"
			if self.featureType == 'FEATURE_SOLAR_SYSTEM':
				print "Solar System Placed by Worldbuilder"
				FinalFrontier = CvEventInterface.getEventManager()
				pSystem = createRandomSystem(plot.getX(), plot.getY(), -1, 3)
				FinalFrontier.addSystem(pSystem)
			plot.setFeatureType(featureTypeNum, self.featureVariety)[/COLOR]

However, again, no print statements appeared, and no solar system was generated. Is there something wrong with my code or my print statements, or both? Is there a way to make this work? I really have no clue where to go from here.

Thanks in advance if you can help.
 
Ups...
Mmmhh..that doesn't make any sense.

Instead of comparing the strings, i would use
PHP:
if featureTypeNum == gc.getInfoTypeForString("FEATURE_SOLAR_SYSTEM")

just because comparing integers is simpler than strings, but that shouldn't make any difference.
 
I can't tell from the fragment you have posted; what *is* self.featureType? Although python does not require it, most python code uses some single character prefix to tell what type is expected, such as bFeatureType for boolean, iFeatureType for integer, etc. You are using it as a boolean like "if self.bFeatureType". Are you expecting a string null comparison, or an integer zero comparison? For any type except boolean, I find it helpful to write out the comparison, such as "if self.iFeatureType != 0".
 
This line

Code:
featureTypeNum = CvUtil.findInfoTypeNum(gc.getFeatureInfo, gc.getNumFeatureInfos(), [B]self.featureType[/B])

implies that self.featureType is a string.

I often use "if <string>" to test for <string> being None or empty. I never use "== true/false", but I always specify an actual number when doing integer comparison.

Fun fact: Using type-based prefixes on variable names is called Hungarian notation.​
 
I recommend to add a print statement just above the "if self.featureType" test; is that executed? I was not able to easily find out what "if string" does; I assume that it is true if the string is not null; but that is an "ass"umption. For whatever reason the code fragment and discussion implies this test is not working as expected. I would write:

featureTypeNum = gc.getInfoTypeForString(self.featureType)
if featureTypeNum != -1: ...
 
In Python, "if <string>" evaluates to True if and only if <string> is not "None" and not empty (""). In general "None" evaluates to False. You can use IDLE to safely test out these things so they are no longer assumptions. ;)

@TC01 - Are you really finding that when it hits these two lines

Code:
plot.setFeatureType(featureTypeNum, self.featureVariety)
print "Post setFeatureType code reached"

the first one executes but the second one doesn't? Are you able to get any print statements to work? Perhaps try

Code:
CyInterface().addImmediateMessage("got here!", "")

instead of print.
 
Actually, none of my print statements have worked... so I'll try CyInterface.addImmediateMessage instead (and also try to switching to the gc.getInfoTypeForString).
 
I have never quite understood the various logging mechanisms or where they go. I always use:

f = open ("c:\\myfile.txt", "a")
f.write ("hello world\n")
f.close()

Or I put a function for that in the local scope.
 
If you are not seeing the output from print statments in the PythonDbg.log file, perhaps you haven't enabled logging. In your CivilizationIV.ini file do you have LoggingEnabled = 1? (I think that's the right debuging related ini setting for this.)
 
I do have logging enabled:

Code:
; Enable the logging system
LoggingEnabled = 1

; Enable synchronization logging
SynchLog = 1

; Overwrite old network and message logs
OverwriteLogs = 1

; Enable rand event logging
RandLog = 1

; Enable message logging
MessageLog = 1
 
Are you sure that code is run when you use the "Solar System button" in WB?

I think it might only be run when loading a world builder save file.
 
Ah- I believe you may be right, since the immediate message wasn't added either. However, implementing code in that file will help Final Frontier- by shrinking the amount of stuff that has to be done in onGameStart. Although I don't know if there's any advantage to running the "make solar system stuff" code in onGameStart in the event manager or in CvWBDesc.py?

So- a better question might be: how can I make code execute when Worldbuilder places a feature? In CvWBInterface.py? CvWBPopups? Somewhere else?

Or is this impossible?
 
I think the basic addition would be in CvWorldBuilderScreen.py.

In the placeObject function, the part of the nested if blocks that adds the feature, you could generate the solar system.

You'd then need to modify the saving and reading of the WB save files. I expect this would all be in CvWBDesc.py. I'd suggest doing it as script data on the plots, exactly like saveSystemsToPlots and loadSystemsFromPlots (which are called for normal game saves) do this in CvFinalFrontierEvents.py for the sake of simplicty.
 
I'll look at CvWorldBuilderScreen.py to see how this would work. However, I don't have to edit the saving functions, since Jon Shafer already modified write and read for CvPlotDesc to save and read all attached solar system information to the plot. (The code is in the spoiler).

Spoiler :
Code:
		if (plot.getFeatureType()!=-1):
			szType = gc.getFeatureInfo(plot.getFeatureType()).getType()
			f.write("\tFeatureType=%s, FeatureVariety=%d\n" 
			%(szType, plot.getFeatureVariety(), ) )
			
			if (szType == 'FEATURE_SOLAR_SYSTEM'):
				
				FinalFrontier = CvEventInterface.getEventManager()
				
				pSystem = FinalFrontier.getSystemAt(plot.getX(), plot.getY())
				
				szStarType = aszSunTypeNames[pSystem.getSunType()]
				f.write("\t\tStarType=%s\n" %(szStarType))
				
				# Loop through all planets in this system
				for iPlanetLoop in range(pSystem.getNumPlanets()):
					pPlanet = pSystem.getPlanetByIndex(iPlanetLoop)
					
					szPlanetType = aszPlanetTypeNames[pPlanet.getPlanetType()]
					
					f.write("\t\tPlanetType=%s, OrbitRing=%d, PlanetSize=%d, HasMoon=%d, HasRings=%d\n" %(szPlanetType, pPlanet.getOrbitRing(), pPlanet.getPlanetSize(), pPlanet.isMoon(), pPlanet.isRings()))

Code:
			if (self.featureType == 'FEATURE_SOLAR_SYSTEM'):
				
				v = parser.findTokenValue(toks, "StarType")
				if v!=-1:
					self.szStarType = v
					continue
				
				v = parser.findTokenValue(toks, "PlanetType")
				if v!=-1:
					self.aszPlanetType.append(v)
					self.iNumPlanets += 1
				
				v = parser.findTokenValue(toks, "OrbitRing")
				if v!=-1:
					self.aiOrbitRing.append(int(v))
				
				v = parser.findTokenValue(toks, "PlanetSize")
				if v!=-1:
					self.aiPlanetSize.append(int(v))
				
				v = parser.findTokenValue(toks, "HasMoon")
				if v!=-1:
					self.aiMoon.append(int(v))
				
				v = parser.findTokenValue(toks, "HasRings")
				if v!=-1:
					self.aiRings.append(int(v))
					continue
 
I don't have to edit the saving functions, since Jon Shafer already modified write and read for CvPlotDesc to save and read all attached
solar system information to the plot. (The code is in the spoiler).

Well, duh. I already knew that - it is why FF has its own CvWBDesc.py.
I'm going to have to plead not-guilty by reason of temporary stupidity.
 
Okay, that feature (creating random solar systems) works properly. Now, I'm trying to fix the bug where when you remove a solar system, it reappears the next turn. I'm using God-Emperor's method from here (at the bottom of the page).

I added the code to the removeObject function of CvWorldBuilderScreen.py. My code is between the comments.

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
				if iFeatureType == 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("")
#End of Final Frontier Worldbuilder
				self.m_pCurrentPlot.setFeatureType(FeatureTypes.NO_FEATURE, -1)

However, it doesn't work. Am I in the wrong place in CvWorldBuilderScreen.py? Am I identifying the Solar System as a feature incorrectly?
 
In the system adding code, the value of self.m_iNormalMapCurrentIndexes[self.m_normalMapTabCtrl.getActiveTab()] is soem sort of button index, which is identified by going through that double loop to get the actual feature and variety type.

You may have to do the same looping thing to get the actual feature type when doing the removal since m_iNormalMapCurrentIndexes apparently merges the feature and variety together into a single number. Because of this, the "button number" or the solar system probably doesn't match the feature type number for a solar system. (The variable in the remove code appears to be misnamed, it should be iButtonIndex like in the feature adding code.)

Here's a question - what happens if you add a feature when a feature of the same type is already there? I.E. add a solar system when there is already a solar system present? Does it blindly add the feature, or does it run the remove routine to get rid of the existing feature? For solar systems this is important because you need to wipe out the local data or you'll have extraneous junk hanging around that could mess things up pretty badly. If it does not run the remove function, you should check the plot for an existing solar system befiore you add a solar system and, if found, remove the old one from the local python data.
 
If that's how the loop is working (getting both the variety and number at once), could I just get the current feature type of self.m_pCurrentPlot instead of checking gc.getInfoTypeForString(SOLAR_SYSTEM) against iFeatureType?

You are correct, though- if you add another feature, I believe the game does not run the removeObject function. It just blindly overwrites the existing feature. So I'll have to replicate this code there as well.
 
Top Bottom