Mod Component Requests Thread

I'd like something that would randomize city names when founded. The first city would always be the same (London, Washington, etc), but every other city would have a random name from the given list of city names. I find that I usually only have the first 6 or so cities, and I'd appreciate the variety. Thanks!

You could use my Dynamic City Naming mod component name to achieve that. Can even have the random selection differ by leader and/or era.
 
I'd like something that would randomize city names when founded. The first city would always be the same (London, Washington, etc), but every other city would have a random name from the given list of city names. I find that I usually only have the first 6 or so cities, and I'd appreciate the variety. Thanks!

That would require DLL modification. Looks like it's handled in the function CvPlayer::getCivilizationCityName()

Modifying the DLL may be cleaner and more efficient, but it is not actually necessary.

It is possible to use python to rename a city as soon as it is founded.

It is even possible to use python to extract a list of city names from CIV4CivilizationInfos.xml and then randomly select a one of those names for a city.


As proof, I offer you this bit of code which I just now wrote and tested. It seems to work exactly as lugialord desires.


CvEventManager.py
Code:
	def onCityBuilt(self, argsList):
		'City Built'
		city = argsList[0]
		if not city.isCapital():
			iCiv = city.getCivilizationType()
			iPlayer = city.getOwner()
			py = PyPlayer(iPlayer)
			listCityNames = []

			CivFile = open("Assets/XML/Civilizations/CIV4CivilizationInfos.xml")
			bCiv = False
			for line in CivFile.readlines():
				if "<Type>" in line:
					sCiv = line[line.find(">") +1 : line.find("</")]
					if iCiv == gc.getInfoTypeForString(sCiv):
						bCiv = True
					else:
						bCiv = False
				elif bCiv:
					if "<City>" in line:
						txtKeyCity = line[line.find(">") +1 : line.find("</")]
						sName = localText.getText(txtKeyCity, ())
						for pyCity in py.getCityList():
							if pyCity.GetCy().getName == sName:
								break
						else:
							listCityNames.append(sName)

			if len(listCityNames) > 0:
				sName = listCityNames.pop(CyGame().getSorenRandNum(len(listCityNames), "Name city"))
				city.setName(sName, False)


		if (city.getOwner() == gc.getGame().getActivePlayer()):
			self.__eventEditCityNameBegin(city, False)
		CvUtil.pyPrint('City Built Event: %s' %(city.getName()))


The first version of this code which I tested required explicitly defining a list of city names for each civ in python, which was a bit of a hassle so I only actually got it working for 2 civs. That way is not very versatile and would require reprogramming for each mod.

I would not have known how to use python to read the files like that had I not come across something similar in the code of Platy World Builder, so Platyping deserves some of the credit.

Note: This code will probably not work well in modular mods where some civs (and thus their city lists) are defined in separate files.

It would also not work great in mods that write the city names directly in the CIV4CivilizationInfos.xml rather than using TXT_KEYs. I believe that would make it possible for some ciities to be given the name "-1."
 
Appending a translated text directly will cause problems due to those special chars used, like Portuguese cities.
 
Would it be better to append the TXT_KEY and and then get the name after the random TXT-KEY is chosen?
Code:
	def onCityBuilt(self, argsList):
		'City Built'
		city = argsList[0]
		if not city.isCapital():
			iCiv = city.getCivilizationType()
			iPlayer = city.getOwner()
			py = PyPlayer(iPlayer)
			listCityNames = []

			CivFile = open("Assets/XML/Civilizations/CIV4CivilizationInfos.xml")
			bCiv = False
			for line in CivFile.readlines():
				if "<Type>" in line:
					sCiv = line[line.find(">") +1 : line.find("</")]
					if iCiv == gc.getInfoTypeForString(sCiv):
						bCiv = True
					else:
						bCiv = False
				elif bCiv:
					if "<City>" in line:
						txtKeyCity = line[line.find(">") +1 : line.find("</")]
						sName = localText.getText(txtKeyCity, ())
						if sName != -1:
							for pyCity in py.getCityList():
								if pyCity.GetCy().getName == sName:
									break
							else:
								listCityNames.append(txtKeyCity)

			if len(listCityNames) > 0:
				txtKeyCity = listCityNames.pop(CyGame().getSorenRandNum(len(listCityNames), "Name city"))
				sName = localText.getText(txtKeyCity, ())
				city.setName(sName, False)
 
Code:
							for pyCity in py.getCityList():
								if pyCity.GetCy().getName == sName:
									break
							else:
								listCityNames.append(txtKeyCity)
You tested this part?
Looks weird to see a else statement without a if statement at same indentation order.

Edit:
Anyway, one problem I can think of is that this code only looks at your city lists.
If I build a Beijing, Shanghai and Guangzhou, and Shanghai is conquered, there is a possibility that the next city will be Shanghai again, which can result is multiple Shanghai cities.
 
Code:
							for pyCity in py.getCityList():
								if pyCity.GetCy().getName == sName:
									break
							else:
								listCityNames.append(txtKeyCity)
You tested this part?
Looks weird to see a else statement without a if statement at same indentation order.

Edit:
Anyway, one problem I can think of is that this code only looks at your city lists.
If I build a Beijing, Shanghai and Guangzhou, and Shanghai is conquered, there is a possibility that the next city will be Shanghai again, which can result is multiple Shanghai cities.
Yeah, I tested it.


Python is weird in that else statements can be used for loops as well as for conditionals.

http://docs.python.org/2/tutorial/controlflow.html said:
4.4. break and continue Statements, and else Clauses on Loops

The break statement, like in C, breaks out of the smallest enclosing for or while loop.

Loop statements may have an else clause; it is executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while), but not when the loop is terminated by a break statement. This is exemplified by the following loop, which searches for prime numbers:
Code:
>>>

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print n, 'equals', x, '*', n/x
...             break
...     else:
...         # loop fell through without finding a factor
...         print n, 'is a prime number'
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3

(Yes, this is the correct code. Look closely: the else clause belongs to the for loop, not the if statement.)

When used with a loop, the else clause has more in common with the else clause of a try statement than it does that of if statements: a try statement’s else clause runs when no exception occurs, and a loop’s else clause runs when no break occurs. For more on the try statement and exceptions, see Handling Exceptions.

The continue statement, also borrowed from C, continues with the next iteration of the loop:
Code:
>>>

>>> for num in range(2, 10):
...     if num % 2 == 0:
...         print "Found an even number", num
...         continue
...     print "Found a number", num
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9

Your concern about only checking your cities is valid.

(Unfortunately I don't think there is an easy way to cycle through all players' cities in one for loop, so without defining a custom function elsewhere in order to do so I guess I'll have to define a boolean variable to say if the name is used yet. That largely defeats the point of using for-else.)

Maybe this would be better?
Code:
	def onCityBuilt(self, argsList):
		'City Built'
		city = argsList[0]
		if not city.isCapital():
			iCiv = city.getCivilizationType()
			listCityNames = []

			CivFile = open("Assets/XML/Civilizations/CIV4CivilizationInfos.xml")
			bCiv = False
			for line in CivFile.readlines():
				if "<Type>" in line:
					sCiv = line[line.find(">") +1 : line.find("</")]
					if iCiv == gc.getInfoTypeForString(sCiv):
						bCiv = True
					else:
						bCiv = False
				elif bCiv:
					if "<City>" in line:
						txtKeyCity = line[line.find(">") +1 : line.find("</")]
						sName = localText.getText(txtKeyCity, ())
						if sName == -1:
							sName = txtKeyCity #This should take care of instances where the city name is defined directly in CIV4CivilizationInfos.xml rather than a TXT_KEY
						bUsed = False
						for iPlayer in range(gc.getMAX_PLAYERS()):
							py = PyPlayer(iPlayer)
							for pyCity in py.getCityList():
								if pyCity.GetCy().getName == sName:

									bUsed = True
									break
							if bUsed:
								break
						if not bUsed:
							listCityNames.append(txtKeyCity)

			if len(listCityNames) > 0:
				txtKeyCity = listCityNames.pop(CyGame().getSorenRandNum(len(listCityNames), "Name city"))
				sName = localText.getText(txtKeyCity, ())
				if sName == -1:
					sName = txtKeyCity #This should take care of instances where the city name is defined directly in CIV4CivilizationInfos.xml rather than a TXT_KEY
				city.setName(sName, False)


		if (city.getOwner() == gc.getGame().getActivePlayer()):
			self.__eventEditCityNameBegin(city, False)
		CvUtil.pyPrint('City Built Event: %s' %(city.getName()))

Edit: Hmm...

I played a test game with 2 American civs, and gave the other one some cities in worldbuilder first. I noticed that this code does not seem to effect cities added in worldbuilder, although it still randomized those founded by settlers.

It seemed to be going fine, until I found Washington pop up as one of my city names. That was the other American capital (mine was Atlanta). I never had New York, Boston, or Philadelphia pop up randomly once those cities were already established though, and I kept going until I'd run out of American names.

I may look into this more tomorrow.
 
1)
Code:
txtKeyCity = line[line.find(">") +1 : line.find("</")]
sName = localText.getText(txtKeyCity, ())
if sName == -1:
	sName = txtKeyCity #This should take care of instances where the city name is defined directly in CIV4CivilizationInfos.xml rather than a TXT_KEY

Don't understand this.
txtKeyCity refers to the string within <City> and </City>.
Whether it is those TXT_KEY_XXX or simply "Washington"
localText.getText(txtKeyCity, ()) will still attempt to translate it.
Whichever the case, the result is either translated or remains as the string if there isn't a corresponding translated text entry.
Which means sName will never be -1.

2) I never understand why people like to use the py.getCityList() style of coding.
It is actually an inefficient double looping.
The code itself uses a while loop to loop through all the cities of the player and store it in a list.
Then you use a for loop to loop through all the stored cities to do what you want.
You might as well just use the same while loop directly to do what you want.
Why use 2 loops to do what you can do with one loop.

3) Regarding WorldBuilder, cities created do not trigger onCityBuilt, thus any codes written there will not affect those cities.
Anyway, it is not just cities.
Buildings for instance, are also granted to the city rather than built in the city, which means onBuildingBuilt codes are also not triggered.
The same goes for Promotions and Techs.
Advanced Start however is a different story.
They are coded differently, so onCityBuilt codes are triggered.
 
1)
Code:
txtKeyCity = line[line.find(">") +1 : line.find("</")]
sName = localText.getText(txtKeyCity, ())
if sName == -1:
	sName = txtKeyCity #This should take care of instances where the city name is defined directly in CIV4CivilizationInfos.xml rather than a TXT_KEY

Don't understand this.
txtKeyCity refers to the string within <City> and </City>.
Whether it is those TXT_KEY_XXX or simply "Washington"
localText.getText(txtKeyCity, ()) will still attempt to translate it.
Whichever the case, the result is either translated or remains as the string if there isn't a corresponding translated text entry.
Which means sName will never be -1.

I did not realize that localText.getText would simply return the input string if it cannot find a TXT_KEY, and just assumed it would return -1 if invalid since so many other functions too. The way you say it actually works makes more sense though, and is better for our purposes. I guess the if sName == -1: sName = txtKeyCity lines can be safely removed.

(I realized that lacking a corresponding TXT_KEY for an entry would make the TXT_KEY strong show up in the game, but for some reason I assumed that that was handled in the SDK and did not realize that the python function CyTranslator().getText(sTXT, ()) would work the same.)
2) I never understand why people like to use the py.getCityList() style of coding.
It is actually an inefficient double looping.
The code itself uses a while loop to loop through all the cities of the player and store it in a list.
Then you use a for loop to loop through all the stored cities to do what you want.
You might as well just use the same while loop directly to do what you want.
Why use 2 loops to do what you can do with one loop.

I just used it because I'm more familiar with that way. The bulk of my experience with the python language comes from modding FfH2. Kael used that style a lot in FfH2. He never really used iterator objects, so I'm not very familiar with them.

I'm not sure I'd ever looked into the code that runs py.getCityList() before.


Is this a better way to do it?
Code:
	def onCityBuilt(self, argsList):
		'City Built'
		city = argsList[0]
		if not city.isCapital():
			iCiv = city.getCivilizationType()
			listCityNames = []
			CivFile = open("Assets/XML/Civilizations/CIV4CivilizationInfos.xml")
			bCiv = False
			for line in CivFile.readlines():
				if "<Type>" in line:
					sCiv = line[line.find(">") +1 : line.find("</")]
					if iCiv == gc.getInfoTypeForString(sCiv):
						bCiv = True
					else:
						bCiv = False
				elif bCiv:
					if "<City>" in line:
						txtKeyCity = line[line.find(">") +1 : line.find("</")]
						sName = localText.getText(txtKeyCity, ())
						bUsed = False
						for iLoopPlayer in range(gc.getMAX_PLAYERS()):
							pLoopPlayer = gc.getPlayer(iLoopPlayer)
							(loopCity, iter) = pLoopPlayer.firstCity(False)
							while(loopCity):
								if not loopCity.isNone(): #only valid cities
									if loopCity.getName() == sName:
										bUsed = True
										break
								(loopCity, iter) = pLoopPlayer.nextCity(iter, False)
							if bUsed:
								break
						if not bUsed:
							listCityNames.append(txtKeyCity)
			if len(listCityNames) > 0:
				txtKeyCity = listCityNames.pop(CyGame().getSorenRandNum(len(listCityNames), "Name city"))
				sName = localText.getText(txtKeyCity, ())
				city.setName(sName, False)


		if (city.getOwner() == gc.getGame().getActivePlayer()):
			self.__eventEditCityNameBegin(city, False)
		CvUtil.pyPrint('City Built Event: %s' %(city.getName()))

3) Regarding WorldBuilder, cities created do not trigger onCityBuilt, thus any codes written there will not affect those cities.
Anyway, it is not just cities.
Buildings for instance, are also granted to the city rather than built in the city, which means onBuildingBuilt codes are also not triggered.
The same goes for Promotions and Techs.
Advanced Start however is a different story.
They are coded differently, so onCityBuilt codes are triggered.

That's what I figured.

So this goes in the CivEventManager.py file? :3
There is no such file. I assume you mean CvEventManager.py (without an i), which is where it should go.
 
1) Actually you see the effects of localtext without corresponding txt entry quite often.
When a TXT_KEY is done without its corresponding text, in game it will simply display as TXT_KEY rather than an error.

2) There should be a () behind getName which would explain why it failed to catch Washington.

3) Generally that is the correct file although there are some mods with their own eventmanager which replaces the civ iv one, such as REV I heard
 
I just tried adding the code to my modmod, and found that it did not work.

The problem is that CivFile = open("Assets/XML/Civilizations/CIV4CivilizationInfos.xml") does not automatically look in the assets folder of the mod, but of the regular game.

To make it work in my modmod I had to change it to CivFile = open("Mods\Magister Modmod for FfH2\Assets\XML\Civilizations\CIV4CivilizationInfos.xml")

Changing the path like that would be necessary for any mod that does not use the standard version of CIV4CivilizationInfos.xml.
 
Code:
	def onCityBuilt(self, argsList):
		'City Built'
		city = argsList[0]
		if not city.isCapital():
			iCiv = city.getCivilizationType()
			listCityNames = []
			CivFile = open("Assets/XML/Civilizations/CIV4CivilizationInfos.xml")
			bCiv = False
			for line in CivFile.readlines():
				if "<Type>" in line:
					sCiv = line[line.find(">") +1 : line.find("</")]
					if iCiv == gc.getInfoTypeForString(sCiv):
						bCiv = True
					else:
						bCiv = False
				elif bCiv:
					if "<City>" in line:
						txtKeyCity = line[line.find(">") +1 : line.find("</")]
						sName = localText.getText(txtKeyCity, ())
						bUsed = False
						for iLoopPlayer in range(gc.getMAX_PLAYERS()):
							pLoopPlayer = gc.getPlayer(iLoopPlayer)
							(loopCity, iter) = pLoopPlayer.firstCity(False)
							while(loopCity):
								if not loopCity.isNone(): #only valid cities
									if loopCity.getName() == sName:
										bUsed = True
										break
								(loopCity, iter) = pLoopPlayer.nextCity(iter, False)
							if bUsed:
								break
						if not bUsed:
							listCityNames.append(txtKeyCity)
			if len(listCityNames) > 0:
				txtKeyCity = listCityNames.pop(CyGame().getSorenRandNum(len(listCityNames), "Name city"))
				sName = localText.getText(txtKeyCity, ())
				city.setName(sName, False)


		if (city.getOwner() == gc.getGame().getActivePlayer()):
			self.__eventEditCityNameBegin(city, False)
		CvUtil.pyPrint('City Built Event: %s' %(city.getName()))
This isn't working for me for some reason. I substituted that code in for the regular defonCityBuilt code but I'm still getting the regular city name progression.
 
Hi all! I registered to this forum to ask for this, and i think it is best thread to do it.

I want to make some kind of "god mod" - players are gods, and have to gain faith to get cities ect.

The concept is that we have only one starter unit - avatar, and we spread faith in us with him and, later, priests.

I know some about other parts of game, but i don't think i would be able to do this one by myself...

1) Starter unit have ability (spread faith or something like that) that can be used on cottages, hamlets, villages and so on
2) After using it a culture part is generated - like fields around cities. First grade is only square with village ect, second is cross, third is square and so on. Each part need more time spent at "spread faith" - probably 1/2/3/5/8 rounds.
3) Cities are generating it much slower, as people in villages don't really believe in same things people from cities do
4) Also cities growth is much slower, like halved.

Hope someone will be able to help ^^
 
Hi all, I know this modcomp already exists, but I can't find it :( .

In essence, when you discover a tech you get an event that lets you choose between (say) stronger axes or stronger spearmen. It's pretty old, but I cannot find it. Anyone know what I'm talking about?

hmm not sure what you mean? What do you mean stronger axes? like their combat improves?
 
I don't know the mod you are talking about, but I do know how to code that kind of thing.

You should be able to add just these lines to CvEventManager.py under def onTechAcquired(self,argslist):
Code:
		if iTechType == gc.getInfoTypeForString('TECH_WHATEVER'):
			pPlayer = gc.getPlayer(iPlayer)
			iEvent = CvUtil.findInfoTypeNum(gc.getEventTriggerInfo, gc.getNumEventTriggerInfos(),'EVENTTRIGGER_CHOSE_IMPROVE_UNIT')
			triggerData = pPlayer.initTriggeredData(iEvent, True, -1, -1, -1, iPlayer, -1, -1, -1, -1, -1)

(Obviously you'd change the tech and event name to what you want them to be.)

You'd then have to make that event trigger like this.
Code:
		<EventTriggerInfo>
			<Type>EVENTTRIGGER_CHOSE_IMPROVE_UNIT</Type>
			<WorldNewsTexts>
				<Text>TXT_KEY_EVENTTRIGGER_CHOSE_IMPROVE_UNIT</Text>
			</WorldNewsTexts>
			<TriggerTexts>
				<TriggerText>
					<Text>TXT_KEY_EVENT_TRIGGER_CHOSE_IMPROVE_UNIT</Text>
					<Era>NONE</Era>
				</TriggerText>
			</TriggerTexts>
			<bSinglePlayer>0</bSinglePlayer>
			<iPercentGamesActive>100</iPercentGamesActive>
			<iWeight>0</iWeight>
			<bProbabilityUnitMultiply>0</bProbabilityUnitMultiply>
			<bProbabilityBuildingMultiply>0</bProbabilityBuildingMultiply>
			<Civic>NONE</Civic>
			<iMinTreasury>0</iMinTreasury>
			<iMinPopulation>0</iMinPopulation>
			<iMaxPopulation>0</iMaxPopulation>
			<iMinMapLandmass>0</iMinMapLandmass>
			<iMinOurLandmass>0</iMinOurLandmass>
			<iMaxOurLandmass>-1</iMaxOurLandmass>
			<MinDifficulty>NONE</MinDifficulty>
			<iAngry>0</iAngry>
			<iUnhealthy>0</iUnhealthy>
			<UnitsRequired/>
			<iNumUnits>0</iNumUnits>
			<iNumUnitsGlobal>0</iNumUnitsGlobal>
			<iUnitDamagedWeight>0</iUnitDamagedWeight>
			<iUnitDistanceWeight>0</iUnitDistanceWeight>
			<iUnitExperienceWeight>0</iUnitExperienceWeight>
			<bUnitsOnPlot>0</bUnitsOnPlot>
			<BuildingsRequired/>
			<iNumBuildings>0</iNumBuildings>
			<iNumBuildingsGlobal>0</iNumBuildingsGlobal>
			<iNumPlotsRequired>0</iNumPlotsRequired>
			<bOwnPlot>1</bOwnPlot>
			<iPlotType>-1</iPlotType>
			<FeaturesRequired/>
			<TerrainsRequired/>
			<ImprovementsRequired/>
			<BonusesRequired/>
			<RoutesRequired/>
			<ReligionsRequired/>
			<iNumReligions>0</iNumReligions>
			<CorporationsRequired/>
			<iNumCorporations>0</iNumCorporations>
			<bPickReligion>0</bPickReligion>
			<bStateReligion>0</bStateReligion>
			<bHolyCity>0</bHolyCity>
			<bPickCorporation>0</bPickCorporation>
			<bHeadquarters>0</bHeadquarters>
			<Events>
				<Event>EVENT_AXE_HAFT_1</Event>
				<Event>EVENT_BOWYER_1</Event>
				<Event>EVENT_HORSESHOE_1</Event>
			</Events>
			<PrereqEvents/>
			<bPrereqEventPlot>0</bPrereqEventPlot>
			<OrPreReqs>
				<PrereqTech>TECH_WHATEVER</PrereqTech>
			</OrPreReqs>
			<AndPreReqs/>
			<ObsoleteTechs>
			</ObsoleteTechs>
			<bRecurring>0</bRecurring>
			<bTeam>0</bTeam>
			<bGlobal>1</bGlobal>
			<bPickPlayer>0</bPickPlayer>
			<bOtherPlayerWar>0</bOtherPlayerWar>
			<bOtherPlayerHasReligion>0</bOtherPlayerHasReligion>
			<bOtherPlayerHasOtherReligion>0</bOtherPlayerHasOtherReligion>
			<bOtherPlayerAI>0</bOtherPlayerAI>
			<iOtherPlayerShareBorders>0</iOtherPlayerShareBorders>
			<OtherPlayerHasTech>NONE</OtherPlayerHasTech>
			<bPickCity>0</bPickCity>
			<bPickOtherPlayerCity>0</bPickOtherPlayerCity>
			<bShowPlot>0</bShowPlot>
			<iCityFoodWeight>0</iCityFoodWeight>
			<PythonCanDo/>
			<PythonCanDoCity/>
			<PythonCanDoUnit/>
			<PythonCallback/>
		</EventTriggerInfo>
You could make your own events too, but for this example I just used 3 that already exist in BtS. (EVENT_AXE_HAFT_1 gives axeman shock, EVENT_BOWYER_1 gives Archers Combat I, and EVENT_HORSESHOE_1 gives Mounted Units Flanking I.)
 
I can't find this mod comp although I think I've seen it somewhere:
If you build a building/wonder, it enables you to hurry projects (with an Engineer/money/population).
Can anyone point it to me?

Spoiler :
Code:
bool CvCity::canHurry(HurryTypes eHurry, bool bTestVisible) const
{
	BuildingTypes eBuilding;

	if (!(GET_PLAYER(getOwnerINLINE()).canHurry(eHurry)))
	{
		return false;
	}

	if (isDisorder())
	{
		return false;
	}

	if (getProduction() >= getProductionNeeded())
	{
		return false;
	}

	if (!bTestVisible)
	{
		// Ferocca Hurry Projects
		CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
		if (isProductionProject() && (kBuilding.isHurryProjects()))
		{
			return true;
		}
		// Ferocca Hurry Projects

		if (!isProductionUnit() && !isProductionBuilding())
		{
			return false;
		}

		if (GET_PLAYER(getOwnerINLINE()).getGold() < hurryGold(eHurry))
		{
			return false;
		}

		if (maxHurryPopulation() < hurryPopulation(eHurry))
		{
			return false;
		}
	}

	return true;
}

The code above does not work for me. Any ideas? 'isHurryProjects' is a boolean I added in other files.
But if someone knows a ready mod for this, python is great too, that'd be great!
 
Code above will not work since you may not be the player to discover the tech, but your teammate
 
Back
Top Bottom