Modders Guide to FfH2

Slightly off topic, but could you recommend a good python book?

Don't know if you are at he same eager beginner coder level as i am so i don't know if this is usefull.
Myself i found out the basics by googling and (searching through the BtS modding tutorials folder) for python tutorials.
I find that the programming is similar to Visual Basic and C++ so if you learn any of those you can quickly understand and appriciate the logic and beauty of Python.
 
So, I want to create a particular building in my cities each turn, as with the Khazad, except that it only is created if the religion it corresponds to has been founded somewhere in the world. Is there a way to have Python check to see if a given religion has been founded yet?


EDIT:

Thanks, Sephi!
 
I have just released a modmod, Events With Images for Fall form Heaven. It modifies a mod that did this for BtS. Unfortunately, it requires changes to the Dll (a new tag, EventArt, is added in the schema).

I have been looking at whether this could be down within python instead using the example of the FfH Great Person mod. the relevant piece of code that does this in CvRandomEventInterface.py is:

Code:
		if gc.getPlayer(iPlayer).isHuman():
			Message = "TXT_KEY_QUOTE_%s" %(pUnit.getNameNoDesc(),)
			cf.addPopup(CyTranslator().getText(str(Message),()), 'Art/GreatPeople/Simple/'+str(gc.getUnitInfo(pUnit.getUnitType()).getType())+'/'+str(pUnit.getNameNoDesc())+'.dds')

All I need to do is insert an image into the already called event window popup. I believe this is already done for some FfH scenario events, but I could be wrong.
I was thinking of giving the image the exact same name or number as the event, so
no fancy translation would need to be done.

Am I wasting my time here, or is this doable? I would much rather not have to modify the DLL for this mod to make it available for the widest range of people.
 
Slightly off topic, but could you recommend a good python book?

I have Python for Dummies and read about 2/3 of it back in January. I haven't gotten around to trying any actual coding in Python yet though. It's good at explaining things though. I bet it's a better supplement to online tutorials though.
 
Honestly, I think it's best to remain in the DLL. Less python makes it easier for modders to add new events.

But would it? I'm not a python expert, but I do some coding in my day job. I know each event has a unique number called by the python code. If we were to name the event image the same as the number, like 144.dds, then one section of code in python could call up the matching image for that event without having to state it for each event in the XML. It would be something like Art/EventImages/ (python function getting event number that the code already called previously).dds. One line of code, this way covering all events in one shot, added to the python section that already calls up the event and puts it into the event window. All a modder would have to do is find his event number and name his image the same.

Is this doable, or would it still require the DLL to even allow images to appear in the event window in the first place?
 
But would it? I'm not a python expert, but I do some coding in my day job. I know each event has a unique number called by the python code. If we were to name the event image the same as the number, like 144.dds, then one section of code in python could call up the matching image for that event without having to state it for each event in the XML. It would be something like Art/EventImages/ (python function getting event number that the code already called previously).dds. One line of code, this way covering all events in one shot, added to the python section that already calls up the event and puts it into the event window. All a modder would have to do is find his event number and name his image the same.

Is this doable, or would it still require the DLL to even allow images to appear in the event window in the first place?

I think that the issue is that this number isn't visible to modders. It's just an arbitrary key assigned to the event by whatever loads the xml, with no guarantee that is consistent with any other xml configuration.

On the other hand, you probably could name the image the same as the event, and lookup that.
 
Is this doable, or would it still require the DLL to even allow images to appear in the event window in the first place?

You can launch a new popup via python but I doubt you can modify an existing one.
 
I am coding the recruit mercenary spell to work a little bit differently. I have made a new buildingclass that allows the spell instead of the guild of nine. But i want to make it so it is only castable once every turn in each city with that building. How can i do that?

My initial idea is to give newly recruited mercenaries a promotion that expires in the end of the turn and makes them immobile, a bit like a one turn promotion_Held. If there are units with that promotion in the city the spell can't be cast.
I have managed to code the spell requirements to work, but am struggling with making the promotion that does what i want. Can i make a promotion that works that way, and how? I have found the bHeld tag for promotions that that completes half the task, but the only idea for the other is making some kind of onTurnStart function that removes this specific promotion. Can that work? Or is there an easier way?
 
I am coding the recruit mercenary spell to work a little bit differently. I have made a new buildingclass that allows the spell instead of the guild of nine. But i want to make it so it is only castable once every turn in each city with that building. How can i do that?

There's an easier way then what you intended, using script data.

In the PyResult of the Recruit Mercenary spell, I'd make the function get the plot city and set the city's script data to "Mercenaries" or somesuch.

Code:
def spellRecruit(caster):
	pPlot = caster.plot()
	pCity = pPlot.getPlotCity()
	[COLOR="Red"]pCity.setScriptData("Mercenaries")[/COLOR]

Then in the PyRequirement of the Recruit Mercenary spell (which you'd need to create), I'd check if the plot's city had script data, and if that script data was "Mercenaries", I'd return false.

Code:
def canRecruit(caster):
	pPlot = caster.plot()
	pCity = pPlot.getPlotCity()
	if pCity.getScriptData() != "Mercenaries":
		return true
	return false

Then in onCityDoTurn in CvEventManager.py, I'd remove the "Mercenaries" script data from the city:

Code:
		if pCity.getScriptData() == "Mercenaries":
			pCity.setScriptData("")
 
I believe that way you overwrite any other script data on the city.
So if that is an issue...

A better way would be to save a dictionary on the city, then each time you save it you load what it already has saved, add/change your entry and resave it. Also need to load this onLoadGame, and save it when saving game.
If you do this, you'd have to change all other code that saves in the city's scriptdata to work with this.

I can show examples of this but too tired to post more now. :p
 
So I've been trying to make the game, upon construction of a certain building (I'm using a gambling house as a placeholder), spawn another civilization on the same team as the building player. I've been using the Mercurian Gate as a model for that, and for the most part it works, but what I can't figure out is how to give the city that the building is built in to the new civilization. Here's what I've got so far:

Spoiler :
Code:
		if iBuildingType == gc.getInfoTypeForString('BUILDING_GAMBLING_HOUSE'):
			CyInterface().addImmediateMessage('Gambling house built!', "AS2D_NEW_ERA")
			iNewPlayer = cf.getOpenPlayer()
			iTeam = pPlayer.getTeam()
			pPlot2 = cf.findClearPlot(-1, pCity.plot())
			if (iNewPlayer != -1 and pPlot2 != -1):
				for i in range(pPlot.getNumUnits(), -1, -1):
					pUnit = pPlot.getUnit(i)
					pUnit.setXY(pPlot2.getX(), pPlot2.getY(), true, true, true)
				#End mercurian gate code.  ENTER THE BARB CIV CODE!	
				CyInterface().addImmediateMessage('Starting new civ code...', "AS2D_NEW_ERA")
				newLeaderIdx=1
				newCivIdx=1
				iDemon = CvUtil.findInfoTypeNum(gc.getCivilizationInfo,gc.getNumCivilizationInfos(),'CIVILIZATION_DEMON')
				iAnimal = CvUtil.findInfoTypeNum(gc.getCivilizationInfo,gc.getNumCivilizationInfos(),'CIVILIZATION_ANIMAL')
				iOrc = CvUtil.findInfoTypeNum(gc.getCivilizationInfo,gc.getNumCivilizationInfos(),'CIVILIZATION_ORC')
				availableCivs = list()
				taken = True
				for civType in range(0,gc.getNumCivilizationInfos()) :
					if( not ((civType == iAnimal) or (civType == iOrc) or (civType == iDemon))):
						taken = False
						CyInterface().addImmediateMessage('The program understands tautologies, yay.', "AS2D_NEW_ERA")
						for i in range(0,gc.getMAX_CIV_PLAYERS()) :
							if( civType == gc.getPlayer(i).getCivilizationType() ) :
								if( gc.getPlayer(i).isAlive() or gc.getPlayer(i).isFoundedFirstCity() or gc.getPlayer(i).getCitiesLost() > 0 ) :
									taken = True
									break
						if( not taken ) :
							availableCivs.append(civType)
				if( len(availableCivs) < 1 ) :
					if( self.LOG_DEBUG ) : CvUtil.pyPrint("  BC - ERROR: Unexpected lack of unused civ types")
				newCivIdx = availableCivs[game.getSorenRandNum(len(availableCivs),'New civ: pick unused civ type')]
				leaderList = list()
				for leaderType in range(0,gc.getNumLeaderHeadInfos()) :
					if( gc.getCivilizationInfo(newCivIdx).isLeaders(leaderType) ) :
						leaderList.append(leaderType)
				if( len(leaderList) < 1 ) :
					if( self.LOG_DEBUG ) : CvUtil.pyPrint("  BC - ERROR: Unexpected lack of possible leaders")
				newLeaderIdx = leaderList[game.getSorenRandNum(len(leaderList),'New Civ: pick leader')]		
				CyInterface().addImmediateMessage('End Barb Civ Code', "AS2D_NEW_ERA")
				
				CyGame().addPlayerAdvanced(iNewPlayer, iTeam, newLeaderIdx, newCivIdx)
				gc.getPlayer(iNewPlayer).initUnit(gc.getInfoTypeForString('UNIT_ARCHER'), pPlot.getX(), pPlot.getY(), UnitAITypes.UNITAI_CITY_DEFENSE, DirectionTypes.DIRECTION_NORTH)
				gc.getPlayer(iNewPlayer).initUnit(gc.getInfoTypeForString('UNIT_SETTLER'), pPlot.getX(), pPlot.getY(), UnitAITypes.UNITAI_SETTLE, DirectionTypes.DIRECTION_NORTH)
				gc.getPlayer(iNewPlayer).initUnit(gc.getInfoTypeForString('UNIT_ARCHER'), pPlot.getX(), pPlot.getY(), UnitAITypes.UNITAI_EXPLORE, DirectionTypes.DIRECTION_NORTH)
				gc.getPlayer(iNewPlayer).initUnit(gc.getInfoTypeForString('UNIT_ARCHER'), pPlot.getX(), pPlot.getY(), UnitAITypes.UNITAI_EXPLORE, DirectionTypes.DIRECTION_NORTH)
				gc.getPlayer(iNewPlayer).initUnit(gc.getInfoTypeForString('UNIT_ARCHER'), pPlot.getX(), pPlot.getY(), UnitAITypes.UNITAI_CITY_DEFENSE, DirectionTypes.DIRECTION_NORTH)
			
				pNewPlayer = gc.getPlayer(iNewPlayer)
				pNewPlayer.acquireCity(pCity, False, False)
				pCity = pPlot.getPlotCity()
				pCity.changeCulture(iNewPlayer, 100, True)
[/SPOILER]

The problem appears to be in the last part:

pNewPlayer = gc.getPlayer(iNewPlayer)
pNewPlayer.acquireCity(pCity, False, False)
pCity = pPlot.getPlotCity()
pCity.changeCulture(iNewPlayer, 100, True)

as the code works fine whenever I leave it out ("fine" being defined as "a bunch of units appear controlled by a random civilization, allied to me") but when I use this portion of code, the game crashes immediately after building the Gambling House.

Any ideas as to what the problem is?

EDIT: OHHHHH. I was looking around for a solution, and eventually figured out that Basium autocasts a spell on entering the world that grants him the city. Was looking in the wrong place, is all. Although I'm still uncertain as to why the above code doesn't work... but at least this way I can figure out some kind of workaround. Huzzah!
 
It crashes because there's c++ code triggered after onBuildingBuilt is called in python. But CyPlayer.acquireCity deletes the city and creates a new one. Thus, the c++ code is now working with a non-existent city and a crash ensue.
 
In Civ4EventInfos.xml, there is a field called <iHealth>0</iHealth> that can add or remove health from a city during an event. Is there any xml code setting the length of this change? Or would it have to be done in python?
 
Another question. In Civ4EventInfos.xml, is there a way to check for the presence of a building - specifically Aquae Sucellus in this case - in allowing that event to be chosen? I know there is in the Civ4EventTriggersInfo.xml, but I want the event to trigger and then Aquae Sucellus to allow one of the four choices given by the trigger if possible.
 
1.) I think the change is permanent. See CvCity::applyEvent

2.) you need to modify

<BuildingClass>NONE</BuildingClass>
<iBuildingChange>0</iBuildingChange>

set Buildingclass to the one from aquae sucellus and leave change to 0 (this will work only in FFH, not in BTS)
 
1.) I think the change is permanent. See CvCity::applyEvent

2.) you need to modify

<BuildingClass>NONE</BuildingClass>
<iBuildingChange>0</iBuildingChange>

set Buildingclass to the one from aquae sucellus and leave change to 0 (this will work only in FFH, not in BTS)

1. Hmmm, I think I have a way around that then.

2. Ahh, nice to know, thanks.
 
Back
Top Bottom