def getWidgetHelp(self, argsList):
		eWidgetType, iData1, iData2, bOption = argsList
		
		printd("eWidgetType %d, iData1 %d, iData2 %d, bOption %d" % (eWidgetType, iData1, iData2, bOption))
		
		#PB Mod begin
		if (iData1 == 302016):
			return localText.getText("TXT_KEY_MOD_UNPAUSE_DESC", ())
		#PB Mod end
		
		szHelpText = u""
		
		# All the FF specific widget help is for widgets with a type of WidgetTypes.WIDGET_GENERAL (FFP post 1.81, for B5, by G-E)
		if (eWidgetType == WidgetTypes.WIDGET_GENERAL):
		
			# Only show tool tip help when players have the tutorial on
			if (not CyUserProfile().getPlayerOption(PlayerOptionTypes.PLAYEROPTION_MODDER_1)):
				
				# Selected Planet
				if (iData1 == 666):
					szHelpText = localText.getText("TXT_KEY_FF_INTERFACE_SELECTED_PLANET_HELP", ())
					
				# Planet Population
				elif (iData1 == 667):
					szHelpText = localText.getText("TXT_KEY_FF_INTERFACE_PLANET_POPULATION_HELP", ())

				# Planet Yield
				elif (iData1 == 668):
					szHelpText = localText.getText("TXT_KEY_FF_INTERFACE_PRODUCES_HELP", ())
					
				# Planet Assign Building
				elif (iData1 == 669):
					szHelpText = localText.getText("TXT_KEY_FF_INTERFACE_PLANET_BUILDINGS_HELP", ())
				
			# Planet Widgets in Lower-Right
			if (iData1 >= 671 and iData1 <= 678):
				
				pHeadSelectedCity = CyInterface().getHeadSelectedCity()
				pSystem = CvSolarSystem.getSystemAt(pHeadSelectedCity.getX(), pHeadSelectedCity.getY()) #FFPBUG
				
				iPlanetRing = iData1 - 670
				pPlanet = pSystem.getPlanet(iPlanetRing)
				iFood = pPlanet.getTotalYield(pHeadSelectedCity.getOwner(), 0)
				iProduction = pPlanet.getTotalYield(pHeadSelectedCity.getOwner(), 1)
				iCommerce = pPlanet.getTotalYield(pHeadSelectedCity.getOwner(), 2)
				
				iCultureLevel = pPlanet.getPlanetCulturalRange()

				if pPlanet.isBonus():
					szHelpText = pPlanet.getName() + "\n" + localText.getText("TXT_KEY_FF_INTERFACE_PLANET_SELECTION_HELP_0", (iFood, iProduction, iCommerce)) + "\n" + localText.getText("TXT_KEY_FFPLUS_PLANET_SELECTION_HELP", (iCultureLevel,)) + "\n" + u"%c " % gc.getBonusInfo(pPlanet.getBonusType()).getChar() + localText.getText("TXT_KEY_FFPLUS_PLANET_SELECTION_BONUS", (gc.getBonusInfo(pPlanet.getBonusType()).getDescription(),)) # Planetary Resource Indicator
				else:
					szHelpText = pPlanet.getName() + "\n" + localText.getText("TXT_KEY_FF_INTERFACE_PLANET_SELECTION_HELP_0", (iFood, iProduction, iCommerce)) + "\n" + localText.getText("TXT_KEY_FFPLUS_PLANET_SELECTION_HELP", (iCultureLevel,))
				if (not CyUserProfile().getPlayerOption(PlayerOptionTypes.PLAYEROPTION_MODDER_1)):
					szHelpText += "\n" + localText.getText("TXT_KEY_FF_INTERFACE_PLANET_SELECTION_HELP", ())
			
		return szHelpText
	def doMeltdown(self,argsList):
		pCity = argsList[0]
		pSystem = CvSolarSystem.getSystemAt(pCity.getX(), pCity.getY())
		iMeltPlanet = -1
		
		# check to see if there is a meltdown
		for iBuilding in range(gc.getNumBuildingInfos()):
			if pCity.getNumBuilding(iBuilding) > 0 :
				iBuildingNukeExRand = gc.getBuildingInfo(iBuilding).getNukeExplosionRand()
				if iBuildingNukeExRand != 0 :
					if CyGame().getSorenRandNum(iBuildingNukeExRand, "FFP: meltdown check in doMeltdown callback") == 0 :
						# a building has had a meltdown, check planets for buildings of this type and pick one
						lPlanets = []
						for iPlanet in range(pSystem.getNumPlanets()):
							pPlanet = pSystem.getPlanetByIndex(iPlanet)
							if pPlanet.isHasBuilding(iBuilding) :
								lPlanets.append(pPlanet)
						
						iMeltPlanet = CyGame().getSorenRandNum(len(lPlanets), "FPP: meltdown planet")
						pMeltPlanet = lPlanets[iMeltPlanet]
						
						# for the sake of simplicity, have it so that no star system will get more than one meltdown in a turn
						break # break out of building loop
		
		if iMeltPlanet == -1 :
			# no meltdown so exit now (always return True to avoid DLL's meltdown processing)
			return True
		
		pPlayer = gc.getPlayer(pCity.getOwner()) # defined now since we don't need it above
		pCityPlot = pCity.plot()
		
		# Meltdown effects (same as nuke effects, but applied to meltdown planet instead of best planet) 
		# 1) set the planet to be disabled
		# 2) remove all buildings from the now dead planet, dealing with capitol if on this planet
		# 3) if thre was a bonus on the planet, remove it from planet and plot
		# 4) chance of specified feature being distributed around system
		# 5) unit damage (possibly killed)
		# 6) population reduction
		
		# 1) disable
		pMeltPlanet.setDisabled(true)
		pMeltPlanet.setPopulation(0)
		
		# 2) remove buildings
		for iBuilding in range(gc.getNumBuildingInfos()):
			if pMeltPlanet.isHasBuilding(iBuilding) and not gc.getBuildingInfo(iBuilding).isNukeImmune():
				bRemove = True
				if (gc.getBuildingInfo(iBuilding).isCapital()):
					if (pPlayer.getNumCities () > 1):
						# The following call moves the capitol building, removing it from this city's data
						# in the DLL (which is why there is no manual setNumRealBuilding in here)
						# and adding it to the new capital city's data in the DLL plus adding it to that system's
						# current build planet to get it into the Python planet data.
						printd("Meltdown: finding new capial system")
						pPlayer.findNewCapital()
					else:
						# This is this civ's only system so we can't move the capitol building to a different one.
						# Try to move it to a different planet instead.
						printd("Meltdown: moving capitol to different planet in same system")
						# Select the planet that is the largest population limit planet
						#  (using production as tie breaker) that is not the planet being wiped out
						bRemove = False
						aiPlanetList = pSystem.getSizeYieldPlanetIndexList(1) # 1 is production, arbitrarily selected
						for iLoopPlanet in range( len(aiPlanetList)):
							pLoopPlanet = pSystem.getPlanetByIndex(aiPlanetList[iLoopPlanet][2])
							if (pLoopPlanet.getOrbitRing() != pMeltPlanet.getOrbitRing()):
								pLoopPlanet.setHasBuilding(iBuilding, true)
								printd("Meltdown: moved Capitol to planet at ring %d" % pLoopPlanet.getOrbitRing())
								bRemove = True
								break

				else:
					pCity.setNumRealBuilding(iBuilding, pCity.getNumRealBuilding(iBuilding)-1)

				if bRemove :
					# The only time this is not the case is when it is the capitol and there is no other
					# planet it can be moved to. You always need a Capitol, so it stays on the dead planet
					pMeltPlanet.setHasBuilding(iBuilding, false)

		# 2.5) unlisted above, but here anyway
		if (pSystem.getBuildingPlanetRing() == pMeltPlanet.getOrbitRing()):
			# This system's current build planet is the planet being wiped out,
			# change it to some other planet, like the new "best" planet.
			# There is an issue if every planet in the current infuence range is dead -
			# in such a case there is no planet that should be having things built on it.
			# With any luck, this will never come up.
			pSystem.setBuildingPlanetRing(pSystem.getPlanetByIndex(CvSolarSystem.getBestPlanetInSystem(pSystem)).getOrbitRing())
					
		# 3) remove bonus
		if (pMeltPlanet.isBonus()): # planet being melted has a bonus, remove it from the planet and the plot
			pMeltPlanet.setBonusType(-1)
			pCityPlot.setBonusType(-1)			
		
		# 4) feature spread
		for iDX in range(-1, 2) :
			for iDY in range (-1, 2) :
				if (iDX != 0) and (iDY != 0) :
					pPlot = plotXY( pCity.getX(), pCity.getY(), iDX, iDY)
					if pPlot and not pPlot.isNone():
						if not pPlot.isImpassable() and (FeatureTypes.NO_FEATURE == pPlot.getFeatureType()) :
							if (CyGame().getSorenRandNum(100, "Meltdown Fallout") < gc.getDefineINT("NUKE_FALLOUT_PROB")) :
								pPlot.setImprovementType(ImprovementTypes.NO_IMPROVEMENT)
								pPlot.setFeatureType(gc.getDefineINT("NUKE_FEATURE"), 0)
							
		# 5) unit damage - only check units on this plot!
		lUnit = []
		for i in range(pCityPlot.getNumUnits()):
			pLoopUnit = pCityPlot.getUnit(i)
			if ( not pLoopUnit.isDead()): #is the unit alive?
				lUnit.append(pLoopUnit) #add unit instance to list

		for pUnit in lUnit :
			if not pUnit.isDead() and not pUnit.isNukeImmune() :
				iNukeDamage = gc.getDefineINT("NUKE_UNIT_DAMAGE_BASE") + CyGame().getSorenRandNum(gc.getDefineINT("NUKE_UNIT_DAMAGE_RAND_1"), "Nuke Damage 1") + CyGame().getSorenRandNum(gc.getDefineINT("NUKE_UNIT_DAMAGE_RAND_2"), "Nuke Damage 2")
				iNukeDamage *= max(0, (pCity.getNukeModifier() + 100))
				iNukeDamage /= 100
				if pUnit.canFight() or (pUnit.airBaseCombatStr() > 0) :
					printd("Meltdown: unit %s damaged for %d" % (pUnit.getName().encode('unicode_escape'), iNukeDamage))
					pUnit.changeDamage(iNukeDamage, PlayerTypes.NO_PLAYER)
				elif iNukeDamage >= gc.getDefineINT("NUKE_NON_COMBAT_DEATH_THRESHOLD") :
					printd("Meltdown: non-combat unit %s killed from damage over threshold" % (pUnit.getName().encode('unicode_escape'),))
					pUnit.kill(false, PlayerTypes.NO_PLAYER)
					
		# 6) population reduction
		iNukedPopulation = (pCity.getPopulation() * (gc.getDefineINT("NUKE_POPULATION_DEATH_BASE") + CyGame().getSorenRandNum(gc.getDefineINT("NUKE_POPULATION_DEATH_RAND_1"), "Population Nuked 1") + CyGame().getSorenRandNum(gc.getDefineINT("NUKE_POPULATION_DEATH_RAND_2"), "Population Nuked 2"))) / 100

		iNukedPopulation *= max(0, (pCity.getNukeModifier() + 100))
		iNukedPopulation /= 100

		pCity.changePopulation(-(min((pCity.getPopulation() - 1), iNukedPopulation)))
				
		# Now update the city and display
		FinalFrontier = CvEventInterface.getEventManager().FinalFrontier
		FinalFrontier.getAI().doCityAIUpdate(pCity)
			
		pSystem.updateDisplay()
		
		# give message that this has happened
		szBuffer = localText.getText("TXT_KEY_MISC_MELTDOWN_CITY", (pCity.getName(),))
		CyInterface().addMessage( pCity.getOwner(), False, gc.getDefineINT("EVENT_MESSAGE_TIME"), szBuffer, 
				"AS2D_MELTDOWN", InterfaceMessageTypes.MESSAGE_TYPE_MINOR_EVENT,
				CyArtFileMgr().getInterfaceArtInfo("INTERFACE_UNHEALTHY_PERSON").getPath(), gc.getInfoTypeForString("COLOR_RED"),
				pCity.getX(), pCity.getY(), True, True)

		return True
	def getCityFoundValue(self, argsList):
		iPlayer, iPlotX, iPlotY = argsList
		iFoundValue = -1 # Any value besides -1 will be used
		
		pPlot = CyMap().plot(iPlotX, iPlotY)
		iFeatureIDSolarSystem = gc.getInfoTypeForString('FEATURE_SOLAR_SYSTEM')
		
		if (pPlot.getFeatureType() == iFeatureIDSolarSystem and not pPlot.isCity()):
			
			printd("Determing Found Value for Plot at %d, %d" %(iPlotX, iPlotY))
			
			# CP - Adjust base value based on path distance from capital (not straight line distance)
			pCapCity = gc.getPlayer(iPlayer).getCapitalCity()
			if pCapCity and not pCapCity.isNone() :
				iDistance = CyMap().calculatePathDistance(pCapCity.plot(), pPlot)
				if iDistance == -1 : # can't get there from here
					return 0
			else : # no capital city, - would normally mean first city not founded yet, I suppose.
				iDistance = 1
				
			iFoundValue = 10000 / (iDistance + 1) # CP - this provides only a small distance penalty
			
			# Determine System value by planetary composition
			if (CyGame().getElapsedGameTurns() > 0):
				
				#iFoundValue = 0
				
				FinalFrontier = CvEventInterface.getEventManager().FinalFrontier
				pSystem = CvSolarSystem.getSystemAt(iPlotX, iPlotY) #FFPBUG
				
				for iPlanetLoop in range(pSystem.getNumPlanets()):
					pPlanet = pSystem.getPlanetByIndex(iPlanetLoop)
					
					iPlanetValue = 0
					
					# Green Planet
					if (pPlanet.getPlanetType() == CvSolarSystem.iPlanetTypeGreen):
						iPlanetValue = 400
					# Not Green Planet
					else:
						iPlanetValue = 200
						
					# CP - Give some value to planetary resources (God-Emperor, for version 1.6)
					if (pPlanet.isBonus()):
						iPlanetValue += 100 # This may need adjusting
						
						# Two Civ-specific increase to this.
						# The Forge gets -1 food in each system, so extra food is good for them meaning +50 for the resources that give extra food
						# Trade routes are very good for the Red Syndicate, so +50 for each trade route
						iTraitForge = gc.getInfoTypeForString('TRAIT_THE_FORGE')
						iTraitSyndicate = gc.getInfoTypeForString('TRAIT_SYNDICATE')
						pPlayer = gc.getPlayer(iPlayer)
						pBonus = gc.getBonusInfo(pPlanet.getBonusType())
						if (pPlayer.hasTrait(iTraitForge) and pBonus.getHealth() > 0): # all heath providing bonuses increase the planet's food
							iPlanetValue += 50
						elif (pPlayer.hasTrait(iTraitSyndicate) and pBonus.getHappiness() > 0): # all happy providing bonuses' related buildings give at least 1 extra trade route
							pBonusBuilding = gc.getBuildingInfo(FinalFrontier.dBonusBuildings[pPlanet.getBonusType()])
							iPlanetValue += 50 * pBonusBuilding.getTradeRoutes()
					
					# Planet Size
					iPlanetValue *= (pPlanet.getPlanetSize() + 1) + 2 # The +2 is to simply weight things a bit so that large planets aren't THAT much more valuable than small ones
					
					# Can it be used right away?
					if (pPlanet.getOrbitRing() < CvSolarSystem.g_iPlanetRange3):
						iPlanetValue *= 2
					
					printd("Orbit Ring %d iPlanetValue: %d" %(pPlanet.getOrbitRing(), iPlanetValue))
					
					iFoundValue += iPlanetValue
			
		else:
			iFoundValue = 0
		
		return iFoundValue
	def cannotConstruct(self,argsList):
		pCity = argsList[0]
		eBuilding = argsList[1]
		bContinue = argsList[2]
		bTestVisible = argsList[3]
		bIgnoreCost = argsList[4]

		# CP - validate eBuilding (I don't know why this is needed - if it is calling this with an 
		#      invalid building code then something in the caller is broken)
		if (eBuilding < 0):
			CvSolarSystem.printd("CvGameUtils.cannotConstruct, passed eBuilding < 0 (%d)" % (eBuilding) )
			return True
		
		#FinalFrontier = CvEventInterface.getEventManager().FinalFrontier #FFPBUG
		
		pSystem = CvSolarSystem.getSystemAt(pCity.getX(), pCity.getY()) #FFPBUG
		pPlanet = pSystem.getPlanet(pSystem.getBuildingPlanetRing())
		pCivilization = gc.getCivilizationInfo( gc.getPlayer(pCity.getOwner()).getCivilizationType())
		bNotHere = False
		pBuildingInfo = gc.getBuildingInfo(eBuilding)
		
		# Cannot build a building if it already exists on the currently "Building" Planet
		if (pPlanet):
			if (pPlanet.isHasBuilding(eBuilding)):
				bNotHere = True
		else:
			return True # this should never happen
		
		#Moon and Rings restrictors moved to XML tags: TC01
		if (pBuildingInfo.isMoon()) and (not pPlanet.isMoon()):
			bNotHere = true
			
		if (pBuildingInfo.isRings()) and (not pPlanet.isRings()):
			bNotHere = true

		if (eBuilding in self.dBuildingBonuses):
			if not pPlanet.isBonus():
				bNotHere = True
			elif (pPlanet.getBonusType() != self.dBuildingBonuses[eBuilding]):
				# the planet has a bonus, but it is the wrong one - since there can be only one bonus in a system
				# and we know it has the wrong one, theis building can not be built anywhere in this system
				return True
					
		# On a disabled planet we can only build nuke immune buildings
		if (not pBuildingInfo.isNukeImmune()) and pPlanet.isDisabled():
			bNotHere = True
		
		# Prereqs are required for the Planet
		for iNeededBuildingClassLoop in range(gc.getNumBuildingClassInfos()):
			if (pBuildingInfo.isBuildingClassNeededOnPlanet(iNeededBuildingClassLoop)):
				iNeededBuildingLoop = pCivilization.getCivilizationBuildings(iNeededBuildingClassLoop )
				if (not pPlanet.isHasBuilding(iNeededBuildingLoop)):
					bNotHere = True
					break
		
		# CP - now for the "if we can't build it here, try it on some other planet in the system" upgrade.
		#      This is the core functionality of the improvement, and is all new.
		#      Skip if this if this is for a player, not an AI.
		#
		#      Note: I'm not positive that this is working properly. If the AI ever calls this function when
		#      the build queue is not empty, then it can change the current build planet out from under whatever
		#      is currently being built, possibly continuing to build the thing currently in the queue on an
		#      invalid planet (such as one that already has the building, or that does not have a prereq building
		#      on it). So far I have not seen any mysterious bad behavior, but I can't be certain.
		if bNotHere and (not gc.getPlayer(pCity.getOwner()).isHuman()):
			for iPlanetLoop in range(pSystem.getNumPlanets()):
				pLoopPlanet = pSystem.getPlanetByIndex(iPlanetLoop)
				if (pLoopPlanet != pPlanet) and (pLoopPlanet.isPlanetWithinCulturalRange()) : 
					# skip the planet we are already using and planets not in the cultural range
					
					if (pBuildingInfo.isMoon()) and (not pLoopPlanet.isMoon()):
						continue
						
					if (pBuildingInfo.isRings()) and (not pLoopPlanet.isRings()):
						continue
						
					if (pLoopPlanet.isHasBuilding(eBuilding)):
						continue # already has one here, skip rest of loop
					
					if (eBuilding in self.dBuildingBonuses):
						if not pPlanet.isBonus():
							# planet does not have bonus but one is required by this building
							continue
						elif (pPlanet.getBonusType() != self.dBuildingBonuses[eBuilding]):
							# the planet has a bonus, but it is the wrong one - since there can be only one bonus in a system
							# and we know it has the wrong one, this building can not be built anywhere in this system
							return True
					
					if (not pBuildingInfo.isNukeImmune()) and pLoopPlanet.isDisabled():
						continue
							
					hasPrereq = True
					for iNeededBuildingClassLoop in range(gc.getNumBuildingClassInfos()):
						if (pBuildingInfo.isBuildingClassNeededInCity(iNeededBuildingClassLoop)):
							iNeededBuildingLoop = pCivilization.getCivilizationBuildings(iNeededBuildingClassLoop ) # get this civilization's building for this buildingclass
							if (not pLoopPlanet.isHasBuilding(iNeededBuildingLoop)):
								hasPrereq = False
								break # one missing prereq is all that has to happen so skip checking for more
					# If we get here with hasPrereq == True then we are at a planet that is 
					# not the one we started with, is within the cultural/influence range, 
					# does not already have a building of this type, and does have all prereq buildings.
					# At this point we may, in the furture, want to add some additional checks, in particular
					# we may want to check if the planet has any population assigned to it and skip if if
					# it doesn't but the building type is one of the ones that modifies the planet's yield
					# (which gains you nothing if the planet has no people on it). At the moment I'm not doing
					# this because it may work fine without it - when the system's population assignments are
					# recalculated it may shift people to the planet after the improvement is done.
					# Also, we may want to add the planet to a list and then do some sort of selection
					# process to determine which of the possible planets to use - for now, this is just
					# using the first planet that it finds.
					if (hasPrereq == True) :
						bNotHere = False # don't block the build
						if (not bTestVisible) : # only actually change the ring if not just testing "visibility"
							pSystem.setBuildingPlanetRing(pLoopPlanet.getOrbitRing()) # switch to this new planet
							CvSolarSystem.printd("CvGameUtils.cannotConstruct, %s active planet ring set to %d (turn = %d, owner = %s, building = %s, bContinue = %d, bTestVisible = %d)" % (pCity.getName(), pLoopPlanet.getOrbitRing(), CyGame().getElapsedGameTurns(), gc.getPlayer(pCity.getOwner()).getName(), pBuildingInfo.getType(), bContinue, bTestVisible) )
						else:
							CvSolarSystem.printd("CvGameUtils.cannotConstruct, %s active planet ring not set, bTestVisible = true (turn = %d, owner = %s, building = %s, bContinue = %d, bTestVisible = %d)" % (pCity.getName(), CyGame().getElapsedGameTurns(), gc.getPlayer(pCity.getOwner()).getName(), pBuildingInfo.getType(), bContinue, bTestVisible) )
						break # we have done what we needed to do, bail out of the loop entirely
	
		return bNotHere
    def getWidgetHelp(self, argsList):
        eWidgetType, iData1, iData2, bOption = argsList

        printd("eWidgetType %d, iData1 %d, iData2 %d, bOption %d" %
               (eWidgetType, iData1, iData2, bOption))

        #PB Mod begin
        if (iData1 == 302016):
            return localText.getText("TXT_KEY_MOD_UNPAUSE_DESC", ())
        #PB Mod end

        szHelpText = u""

        # All the FF specific widget help is for widgets with a type of WidgetTypes.WIDGET_GENERAL (FFP post 1.81, for B5, by G-E)
        if (eWidgetType == WidgetTypes.WIDGET_GENERAL):

            # Only show tool tip help when players have the tutorial on
            if (not CyUserProfile().getPlayerOption(
                    PlayerOptionTypes.PLAYEROPTION_MODDER_1)):

                # Selected Planet
                if (iData1 == 666):
                    szHelpText = localText.getText(
                        "TXT_KEY_FF_INTERFACE_SELECTED_PLANET_HELP", ())

                # Planet Population
                elif (iData1 == 667):
                    szHelpText = localText.getText(
                        "TXT_KEY_FF_INTERFACE_PLANET_POPULATION_HELP", ())

                # Planet Yield
                elif (iData1 == 668):
                    szHelpText = localText.getText(
                        "TXT_KEY_FF_INTERFACE_PRODUCES_HELP", ())

                # Planet Assign Building
                elif (iData1 == 669):
                    szHelpText = localText.getText(
                        "TXT_KEY_FF_INTERFACE_PLANET_BUILDINGS_HELP", ())

            # Planet Widgets in Lower-Right
            if (iData1 >= 671 and iData1 <= 678):

                pHeadSelectedCity = CyInterface().getHeadSelectedCity()
                pSystem = CvSolarSystem.getSystemAt(
                    pHeadSelectedCity.getX(),
                    pHeadSelectedCity.getY())  #FFPBUG

                iPlanetRing = iData1 - 670
                pPlanet = pSystem.getPlanet(iPlanetRing)
                iFood = pPlanet.getTotalYield(pHeadSelectedCity.getOwner(), 0)
                iProduction = pPlanet.getTotalYield(
                    pHeadSelectedCity.getOwner(), 1)
                iCommerce = pPlanet.getTotalYield(pHeadSelectedCity.getOwner(),
                                                  2)

                iCultureLevel = pPlanet.getPlanetCulturalRange()

                if pPlanet.isBonus():
                    szHelpText = pPlanet.getName() + "\n" + localText.getText(
                        "TXT_KEY_FF_INTERFACE_PLANET_SELECTION_HELP_0",
                        (iFood, iProduction, iCommerce)
                    ) + "\n" + localText.getText(
                        "TXT_KEY_FFPLUS_PLANET_SELECTION_HELP",
                        (iCultureLevel, )
                    ) + "\n" + u"%c " % gc.getBonusInfo(
                        pPlanet.getBonusType()).getChar() + localText.getText(
                            "TXT_KEY_FFPLUS_PLANET_SELECTION_BONUS",
                            (gc.getBonusInfo(
                                pPlanet.getBonusType()).getDescription(),
                             ))  # Planetary Resource Indicator
                else:
                    szHelpText = pPlanet.getName() + "\n" + localText.getText(
                        "TXT_KEY_FF_INTERFACE_PLANET_SELECTION_HELP_0",
                        (iFood, iProduction,
                         iCommerce)) + "\n" + localText.getText(
                             "TXT_KEY_FFPLUS_PLANET_SELECTION_HELP",
                             (iCultureLevel, ))
                if (not CyUserProfile().getPlayerOption(
                        PlayerOptionTypes.PLAYEROPTION_MODDER_1)):
                    szHelpText += "\n" + localText.getText(
                        "TXT_KEY_FF_INTERFACE_PLANET_SELECTION_HELP", ())

        return szHelpText
    def getCityFoundValue(self, argsList):
        iPlayer, iPlotX, iPlotY = argsList
        iFoundValue = -1  # Any value besides -1 will be used

        pPlot = CyMap().plot(iPlotX, iPlotY)
        iFeatureIDSolarSystem = gc.getInfoTypeForString('FEATURE_SOLAR_SYSTEM')

        if (pPlot.getFeatureType() == iFeatureIDSolarSystem
                and not pPlot.isCity()):

            printd("Determing Found Value for Plot at %d, %d" %
                   (iPlotX, iPlotY))

            # CP - Adjust base value based on path distance from capital (not straight line distance)
            pCapCity = gc.getPlayer(iPlayer).getCapitalCity()
            if pCapCity and not pCapCity.isNone():
                iDistance = CyMap().calculatePathDistance(
                    pCapCity.plot(), pPlot)
                if iDistance == -1:  # can't get there from here
                    return 0
            else:  # no capital city, - would normally mean first city not founded yet, I suppose.
                iDistance = 1

            iFoundValue = 10000 / (
                iDistance + 1
            )  # CP - this provides only a small distance penalty

            # Determine System value by planetary composition
            if (CyGame().getElapsedGameTurns() > 0):

                #iFoundValue = 0

                FinalFrontier = CvEventInterface.getEventManager(
                ).FinalFrontier
                pSystem = CvSolarSystem.getSystemAt(iPlotX, iPlotY)  #FFPBUG

                for iPlanetLoop in range(pSystem.getNumPlanets()):
                    pPlanet = pSystem.getPlanetByIndex(iPlanetLoop)

                    iPlanetValue = 0

                    # Green Planet
                    if (pPlanet.getPlanetType() ==
                            CvSolarSystem.iPlanetTypeGreen):
                        iPlanetValue = 400
                    # Not Green Planet
                    else:
                        iPlanetValue = 200

                    # CP - Give some value to planetary resources (God-Emperor, for version 1.6)
                    if (pPlanet.isBonus()):
                        iPlanetValue += 100  # This may need adjusting

                        # Two Civ-specific increase to this.
                        # The Forge gets -1 food in each system, so extra food is good for them meaning +50 for the resources that give extra food
                        # Trade routes are very good for the Red Syndicate, so +50 for each trade route
                        iTraitForge = gc.getInfoTypeForString(
                            'TRAIT_THE_FORGE')
                        iTraitSyndicate = gc.getInfoTypeForString(
                            'TRAIT_SYNDICATE')
                        pPlayer = gc.getPlayer(iPlayer)
                        pBonus = gc.getBonusInfo(pPlanet.getBonusType())
                        if (
                                pPlayer.hasTrait(iTraitForge)
                                and pBonus.getHealth() > 0
                        ):  # all heath providing bonuses increase the planet's food
                            iPlanetValue += 50
                        elif (
                                pPlayer.hasTrait(iTraitSyndicate)
                                and pBonus.getHappiness() > 0
                        ):  # all happy providing bonuses' related buildings give at least 1 extra trade route
                            pBonusBuilding = gc.getBuildingInfo(
                                FinalFrontier.dBonusBuildings[
                                    pPlanet.getBonusType()])
                            iPlanetValue += 50 * pBonusBuilding.getTradeRoutes(
                            )

                    # Planet Size
                    iPlanetValue *= (
                        pPlanet.getPlanetSize() + 1
                    ) + 2  # The +2 is to simply weight things a bit so that large planets aren't THAT much more valuable than small ones

                    # Can it be used right away?
                    if (pPlanet.getOrbitRing() <
                            CvSolarSystem.g_iPlanetRange3):
                        iPlanetValue *= 2

                    printd("Orbit Ring %d iPlanetValue: %d" %
                           (pPlanet.getOrbitRing(), iPlanetValue))

                    iFoundValue += iPlanetValue

        else:
            iFoundValue = 0

        return iFoundValue
    def doMeltdown(self, argsList):
        pCity = argsList[0]
        pSystem = CvSolarSystem.getSystemAt(pCity.getX(), pCity.getY())
        iMeltPlanet = -1

        # check to see if there is a meltdown
        for iBuilding in range(gc.getNumBuildingInfos()):
            if pCity.getNumBuilding(iBuilding) > 0:
                iBuildingNukeExRand = gc.getBuildingInfo(
                    iBuilding).getNukeExplosionRand()
                if iBuildingNukeExRand != 0:
                    if CyGame().getSorenRandNum(
                            iBuildingNukeExRand,
                            "FFP: meltdown check in doMeltdown callback") == 0:
                        # a building has had a meltdown, check planets for buildings of this type and pick one
                        lPlanets = []
                        for iPlanet in range(pSystem.getNumPlanets()):
                            pPlanet = pSystem.getPlanetByIndex(iPlanet)
                            if pPlanet.isHasBuilding(iBuilding):
                                lPlanets.append(pPlanet)

                        iMeltPlanet = CyGame().getSorenRandNum(
                            len(lPlanets), "FPP: meltdown planet")
                        pMeltPlanet = lPlanets[iMeltPlanet]

                        # for the sake of simplicity, have it so that no star system will get more than one meltdown in a turn
                        break  # break out of building loop

        if iMeltPlanet == -1:
            # no meltdown so exit now (always return True to avoid DLL's meltdown processing)
            return True

        pPlayer = gc.getPlayer(
            pCity.getOwner())  # defined now since we don't need it above
        pCityPlot = pCity.plot()

        # Meltdown effects (same as nuke effects, but applied to meltdown planet instead of best planet)
        # 1) set the planet to be disabled
        # 2) remove all buildings from the now dead planet, dealing with capitol if on this planet
        # 3) if thre was a bonus on the planet, remove it from planet and plot
        # 4) chance of specified feature being distributed around system
        # 5) unit damage (possibly killed)
        # 6) population reduction

        # 1) disable
        pMeltPlanet.setDisabled(true)
        pMeltPlanet.setPopulation(0)

        # 2) remove buildings
        for iBuilding in range(gc.getNumBuildingInfos()):
            if pMeltPlanet.isHasBuilding(iBuilding) and not gc.getBuildingInfo(
                    iBuilding).isNukeImmune():
                bRemove = True
                if (gc.getBuildingInfo(iBuilding).isCapital()):
                    if (pPlayer.getNumCities() > 1):
                        # The following call moves the capitol building, removing it from this city's data
                        # in the DLL (which is why there is no manual setNumRealBuilding in here)
                        # and adding it to the new capital city's data in the DLL plus adding it to that system's
                        # current build planet to get it into the Python planet data.
                        printd("Meltdown: finding new capial system")
                        pPlayer.findNewCapital()
                    else:
                        # This is this civ's only system so we can't move the capitol building to a different one.
                        # Try to move it to a different planet instead.
                        printd(
                            "Meltdown: moving capitol to different planet in same system"
                        )
                        # Select the planet that is the largest population limit planet
                        #  (using production as tie breaker) that is not the planet being wiped out
                        bRemove = False
                        aiPlanetList = pSystem.getSizeYieldPlanetIndexList(
                            1)  # 1 is production, arbitrarily selected
                        for iLoopPlanet in range(len(aiPlanetList)):
                            pLoopPlanet = pSystem.getPlanetByIndex(
                                aiPlanetList[iLoopPlanet][2])
                            if (pLoopPlanet.getOrbitRing() !=
                                    pMeltPlanet.getOrbitRing()):
                                pLoopPlanet.setHasBuilding(iBuilding, true)
                                printd(
                                    "Meltdown: moved Capitol to planet at ring %d"
                                    % pLoopPlanet.getOrbitRing())
                                bRemove = True
                                break

                else:
                    pCity.setNumRealBuilding(
                        iBuilding,
                        pCity.getNumRealBuilding(iBuilding) - 1)

                if bRemove:
                    # The only time this is not the case is when it is the capitol and there is no other
                    # planet it can be moved to. You always need a Capitol, so it stays on the dead planet
                    pMeltPlanet.setHasBuilding(iBuilding, false)

        # 2.5) unlisted above, but here anyway
        if (pSystem.getBuildingPlanetRing() == pMeltPlanet.getOrbitRing()):
            # This system's current build planet is the planet being wiped out,
            # change it to some other planet, like the new "best" planet.
            # There is an issue if every planet in the current infuence range is dead -
            # in such a case there is no planet that should be having things built on it.
            # With any luck, this will never come up.
            pSystem.setBuildingPlanetRing(
                pSystem.getPlanetByIndex(
                    CvSolarSystem.getBestPlanetInSystem(
                        pSystem)).getOrbitRing())

        # 3) remove bonus
        if (
                pMeltPlanet.isBonus()
        ):  # planet being melted has a bonus, remove it from the planet and the plot
            pMeltPlanet.setBonusType(-1)
            pCityPlot.setBonusType(-1)

        # 4) feature spread
        for iDX in range(-1, 2):
            for iDY in range(-1, 2):
                if (iDX != 0) and (iDY != 0):
                    pPlot = plotXY(pCity.getX(), pCity.getY(), iDX, iDY)
                    if pPlot and not pPlot.isNone():
                        if not pPlot.isImpassable() and (
                                FeatureTypes.NO_FEATURE
                                == pPlot.getFeatureType()):
                            if (CyGame().getSorenRandNum(
                                    100, "Meltdown Fallout") <
                                    gc.getDefineINT("NUKE_FALLOUT_PROB")):
                                pPlot.setImprovementType(
                                    ImprovementTypes.NO_IMPROVEMENT)
                                pPlot.setFeatureType(
                                    gc.getDefineINT("NUKE_FEATURE"), 0)

        # 5) unit damage - only check units on this plot!
        lUnit = []
        for i in range(pCityPlot.getNumUnits()):
            pLoopUnit = pCityPlot.getUnit(i)
            if (not pLoopUnit.isDead()):  #is the unit alive?
                lUnit.append(pLoopUnit)  #add unit instance to list

        for pUnit in lUnit:
            if not pUnit.isDead() and not pUnit.isNukeImmune():
                iNukeDamage = gc.getDefineINT(
                    "NUKE_UNIT_DAMAGE_BASE") + CyGame().getSorenRandNum(
                        gc.getDefineINT("NUKE_UNIT_DAMAGE_RAND_1"),
                        "Nuke Damage 1") + CyGame().getSorenRandNum(
                            gc.getDefineINT("NUKE_UNIT_DAMAGE_RAND_2"),
                            "Nuke Damage 2")
                iNukeDamage *= max(0, (pCity.getNukeModifier() + 100))
                iNukeDamage /= 100
                if pUnit.canFight() or (pUnit.airBaseCombatStr() > 0):
                    printd("Meltdown: unit %s damaged for %d" %
                           (pUnit.getName().encode('unicode_escape'),
                            iNukeDamage))
                    pUnit.changeDamage(iNukeDamage, PlayerTypes.NO_PLAYER)
                elif iNukeDamage >= gc.getDefineINT(
                        "NUKE_NON_COMBAT_DEATH_THRESHOLD"):
                    printd(
                        "Meltdown: non-combat unit %s killed from damage over threshold"
                        % (pUnit.getName().encode('unicode_escape'), ))
                    pUnit.kill(false, PlayerTypes.NO_PLAYER)

        # 6) population reduction
        iNukedPopulation = (
            pCity.getPopulation() *
            (gc.getDefineINT("NUKE_POPULATION_DEATH_BASE") +
             CyGame().getSorenRandNum(
                 gc.getDefineINT("NUKE_POPULATION_DEATH_RAND_1"),
                 "Population Nuked 1") + CyGame().getSorenRandNum(
                     gc.getDefineINT("NUKE_POPULATION_DEATH_RAND_2"),
                     "Population Nuked 2"))) / 100

        iNukedPopulation *= max(0, (pCity.getNukeModifier() + 100))
        iNukedPopulation /= 100

        pCity.changePopulation(-(min((pCity.getPopulation() -
                                      1), iNukedPopulation)))

        # Now update the city and display
        FinalFrontier = CvEventInterface.getEventManager().FinalFrontier
        FinalFrontier.getAI().doCityAIUpdate(pCity)

        pSystem.updateDisplay()

        # give message that this has happened
        szBuffer = localText.getText("TXT_KEY_MISC_MELTDOWN_CITY",
                                     (pCity.getName(), ))
        CyInterface().addMessage(
            pCity.getOwner(), False, gc.getDefineINT("EVENT_MESSAGE_TIME"),
            szBuffer, "AS2D_MELTDOWN",
            InterfaceMessageTypes.MESSAGE_TYPE_MINOR_EVENT,
            CyArtFileMgr().getInterfaceArtInfo(
                "INTERFACE_UNHEALTHY_PERSON").getPath(),
            gc.getInfoTypeForString("COLOR_RED"), pCity.getX(), pCity.getY(),
            True, True)

        return True
    def cannotConstruct(self, argsList):
        pCity = argsList[0]
        eBuilding = argsList[1]
        bContinue = argsList[2]
        bTestVisible = argsList[3]
        bIgnoreCost = argsList[4]

        # CP - validate eBuilding (I don't know why this is needed - if it is calling this with an
        #      invalid building code then something in the caller is broken)
        if (eBuilding < 0):
            CvSolarSystem.printd(
                "CvGameUtils.cannotConstruct, passed eBuilding < 0 (%d)" %
                (eBuilding))
            return True

        #FinalFrontier = CvEventInterface.getEventManager().FinalFrontier #FFPBUG

        pSystem = CvSolarSystem.getSystemAt(pCity.getX(),
                                            pCity.getY())  #FFPBUG
        pPlanet = pSystem.getPlanet(pSystem.getBuildingPlanetRing())
        pCivilization = gc.getCivilizationInfo(
            gc.getPlayer(pCity.getOwner()).getCivilizationType())
        bNotHere = False
        pBuildingInfo = gc.getBuildingInfo(eBuilding)

        # Cannot build a building if it already exists on the currently "Building" Planet
        if (pPlanet):
            if (pPlanet.isHasBuilding(eBuilding)):
                bNotHere = True
        else:
            return True  # this should never happen

        #Moon and Rings restrictors moved to XML tags: TC01
        if (pBuildingInfo.isMoon()) and (not pPlanet.isMoon()):
            bNotHere = true

        if (pBuildingInfo.isRings()) and (not pPlanet.isRings()):
            bNotHere = true

        if (eBuilding in self.dBuildingBonuses):
            if not pPlanet.isBonus():
                bNotHere = True
            elif (pPlanet.getBonusType() != self.dBuildingBonuses[eBuilding]):
                # the planet has a bonus, but it is the wrong one - since there can be only one bonus in a system
                # and we know it has the wrong one, theis building can not be built anywhere in this system
                return True

        # On a disabled planet we can only build nuke immune buildings
        if (not pBuildingInfo.isNukeImmune()) and pPlanet.isDisabled():
            bNotHere = True

        # Prereqs are required for the Planet
        for iNeededBuildingClassLoop in range(gc.getNumBuildingClassInfos()):
            if (pBuildingInfo.isBuildingClassNeededOnPlanet(
                    iNeededBuildingClassLoop)):
                iNeededBuildingLoop = pCivilization.getCivilizationBuildings(
                    iNeededBuildingClassLoop)
                if (not pPlanet.isHasBuilding(iNeededBuildingLoop)):
                    bNotHere = True
                    break

        # CP - now for the "if we can't build it here, try it on some other planet in the system" upgrade.
        #      This is the core functionality of the improvement, and is all new.
        #      Skip if this if this is for a player, not an AI.
        #
        #      Note: I'm not positive that this is working properly. If the AI ever calls this function when
        #      the build queue is not empty, then it can change the current build planet out from under whatever
        #      is currently being built, possibly continuing to build the thing currently in the queue on an
        #      invalid planet (such as one that already has the building, or that does not have a prereq building
        #      on it). So far I have not seen any mysterious bad behavior, but I can't be certain.
        if bNotHere and (not gc.getPlayer(pCity.getOwner()).isHuman()):
            for iPlanetLoop in range(pSystem.getNumPlanets()):
                pLoopPlanet = pSystem.getPlanetByIndex(iPlanetLoop)
                if (pLoopPlanet != pPlanet) and (
                        pLoopPlanet.isPlanetWithinCulturalRange()):
                    # skip the planet we are already using and planets not in the cultural range

                    if (pBuildingInfo.isMoon()) and (not pLoopPlanet.isMoon()):
                        continue

                    if (pBuildingInfo.isRings()) and (
                            not pLoopPlanet.isRings()):
                        continue

                    if (pLoopPlanet.isHasBuilding(eBuilding)):
                        continue  # already has one here, skip rest of loop

                    if (eBuilding in self.dBuildingBonuses):
                        if not pPlanet.isBonus():
                            # planet does not have bonus but one is required by this building
                            continue
                        elif (pPlanet.getBonusType() !=
                              self.dBuildingBonuses[eBuilding]):
                            # the planet has a bonus, but it is the wrong one - since there can be only one bonus in a system
                            # and we know it has the wrong one, this building can not be built anywhere in this system
                            return True

                    if (not pBuildingInfo.isNukeImmune()
                        ) and pLoopPlanet.isDisabled():
                        continue

                    hasPrereq = True
                    for iNeededBuildingClassLoop in range(
                            gc.getNumBuildingClassInfos()):
                        if (pBuildingInfo.isBuildingClassNeededInCity(
                                iNeededBuildingClassLoop)):
                            iNeededBuildingLoop = pCivilization.getCivilizationBuildings(
                                iNeededBuildingClassLoop
                            )  # get this civilization's building for this buildingclass
                            if (not pLoopPlanet.isHasBuilding(
                                    iNeededBuildingLoop)):
                                hasPrereq = False
                                break  # one missing prereq is all that has to happen so skip checking for more
                    # If we get here with hasPrereq == True then we are at a planet that is
                    # not the one we started with, is within the cultural/influence range,
                    # does not already have a building of this type, and does have all prereq buildings.
                    # At this point we may, in the furture, want to add some additional checks, in particular
                    # we may want to check if the planet has any population assigned to it and skip if if
                    # it doesn't but the building type is one of the ones that modifies the planet's yield
                    # (which gains you nothing if the planet has no people on it). At the moment I'm not doing
                    # this because it may work fine without it - when the system's population assignments are
                    # recalculated it may shift people to the planet after the improvement is done.
                    # Also, we may want to add the planet to a list and then do some sort of selection
                    # process to determine which of the possible planets to use - for now, this is just
                    # using the first planet that it finds.
                    if (hasPrereq == True):
                        bNotHere = False  # don't block the build
                        if (
                                not bTestVisible
                        ):  # only actually change the ring if not just testing "visibility"
                            pSystem.setBuildingPlanetRing(
                                pLoopPlanet.getOrbitRing()
                            )  # switch to this new planet
                            CvSolarSystem.printd(
                                "CvGameUtils.cannotConstruct, %s active planet ring set to %d (turn = %d, owner = %s, building = %s, bContinue = %d, bTestVisible = %d)"
                                % (pCity.getName(), pLoopPlanet.getOrbitRing(),
                                   CyGame().getElapsedGameTurns(),
                                   gc.getPlayer(pCity.getOwner()).getName(),
                                   pBuildingInfo.getType(), bContinue,
                                   bTestVisible))
                        else:
                            CvSolarSystem.printd(
                                "CvGameUtils.cannotConstruct, %s active planet ring not set, bTestVisible = true (turn = %d, owner = %s, building = %s, bContinue = %d, bTestVisible = %d)"
                                % (pCity.getName(),
                                   CyGame().getElapsedGameTurns(),
                                   gc.getPlayer(pCity.getOwner()).getName(),
                                   pBuildingInfo.getType(), bContinue,
                                   bTestVisible))
                        break  # we have done what we needed to do, bail out of the loop entirely

        return bNotHere