Modders Guide to FfH2

You can't just add new XML fields without telling the game what to do with them in the SDK. That's what you need to edit for the desired effect.
 
You can't just add new XML fields without telling the game what to do with them in the SDK. That's what you need to edit for the desired effect.

Thank you for your answer, however these fields do seems to exist already, just not interpreted.
I would prefer to stay on "out of the SDK" changes for now, keep it simple.

Do you know if there is a simple way to change the amount of hammers an hill gives a tile ?
 
Those tags do not exist for base terrains, just Improvements. You would need SDK changes for this.

Hills bonus :hammers: and :food: panalty are handled in CIV4YieldInfos.xml, a file not included in the mod because it has not been changed since Vanilla. Here is the Entire File, wiht the relevent lines in bold:
Code:
<?xml version="1.0"?>
<!-- edited with XMLSPY v2004 rel. 2 U (http://www.xmlspy.com) by Jesse Smith (Firaxis Games) -->
<!-- Sid Meier's Civilization 4 -->
<!-- Copyright Firaxis Games 2005 -->
<!-- -->
<!-- Yield Infos -->
<Civ4YieldInfos xmlns="x-schema:CIV4TerrainSchema.xml">
	<YieldInfos>
		<YieldInfo>
			<Type>YIELD_FOOD</Type>
			<Description>TXT_KEY_YIELD_FOOD</Description>
			[B]<iHillsChange>-1</iHillsChange>[/B]
			<iPeakChange>0</iPeakChange>
			<iLakeChange>1</iLakeChange>
			<iCityChange>0</iCityChange>
			<iPopulationChangeOffset>0</iPopulationChangeOffset>
			<iPopulationChangeDivisor>0</iPopulationChangeDivisor>
			<iMinCity>2</iMinCity>
			<iTradeModifier>0</iTradeModifier>
			<iGoldenAgeYield>0</iGoldenAgeYield>
			<iGoldenAgeYieldThreshold>0</iGoldenAgeYieldThreshold>
			<iAIWeightPercent>100</iAIWeightPercent>
			<ColorType>COLOR_YIELD_FOOD</ColorType>
			<SymbolPaths>
				<SymbolPath>Art/Interface/Symbols/Food/Food01.nif</SymbolPath>
				<SymbolPath>Art/Interface/Symbols/Food/Food02.nif</SymbolPath>
				<SymbolPath>Art/Interface/Symbols/Food/Food03.nif</SymbolPath>
				<SymbolPath>Art/Interface/Symbols/Food/Food04.nif</SymbolPath>
				<SymbolPath>Art/Interface/Symbols/Food/Food05.nif</SymbolPath>
			</SymbolPaths>
		</YieldInfo>
		<YieldInfo>
			<Type>YIELD_PRODUCTION</Type>
			<Description>TXT_KEY_YIELD_PRODUCTION</Description>
		[B]	<iHillsChange>1</iHillsChange>[/B]
			<iPeakChange>0</iPeakChange>
			<iLakeChange>0</iLakeChange>
			<iCityChange>0</iCityChange>
			<iPopulationChangeOffset>0</iPopulationChangeOffset>
			<iPopulationChangeDivisor>0</iPopulationChangeDivisor>
			<iMinCity>1</iMinCity>
			<iTradeModifier>0</iTradeModifier>
			<iGoldenAgeYield>1</iGoldenAgeYield>
			<iGoldenAgeYieldThreshold>1</iGoldenAgeYieldThreshold>
			<iAIWeightPercent>110</iAIWeightPercent>
			<ColorType>COLOR_YIELD_PRODUCTION</ColorType>
			<SymbolPaths>
				<SymbolPath>Art/Interface/Symbols/Production/Production01.nif</SymbolPath>
				<SymbolPath>Art/Interface/Symbols/Production/Production02.nif</SymbolPath>
				<SymbolPath>Art/Interface/Symbols/Production/Production03.nif</SymbolPath>
				<SymbolPath>Art/Interface/Symbols/Production/Production04.nif</SymbolPath>
				<SymbolPath>Art/Interface/Symbols/Production/Production05.nif</SymbolPath>
			</SymbolPaths>
		</YieldInfo>
		<YieldInfo>
			<Type>YIELD_COMMERCE</Type>
			<Description>TXT_KEY_YIELD_COMMERCE</Description>
			<iHillsChange>0</iHillsChange>
			<iPeakChange>0</iPeakChange>
			<iLakeChange>0</iLakeChange>
			<iCityChange>0</iCityChange>
			<iPopulationChangeOffset>0</iPopulationChangeOffset>
			<iPopulationChangeDivisor>0</iPopulationChangeDivisor>
			<iMinCity>1</iMinCity>
			<iTradeModifier>100</iTradeModifier>
			<iGoldenAgeYield>1</iGoldenAgeYield>
			<iGoldenAgeYieldThreshold>1</iGoldenAgeYieldThreshold>
			<iAIWeightPercent>80</iAIWeightPercent>
			<ColorType>COLOR_YIELD_COMMERCE</ColorType>
			<SymbolPaths>
				<SymbolPath>Art/Interface/Symbols/Commerce/Commerce01.nif</SymbolPath>
				<SymbolPath>Art/Interface/Symbols/Commerce/Commerce02.nif</SymbolPath>
				<SymbolPath>Art/Interface/Symbols/Commerce/Commerce03.nif</SymbolPath>
				<SymbolPath>Art/Interface/Symbols/Commerce/Commerce04.nif</SymbolPath>
				<SymbolPath>Art/Interface/Symbols/Commerce/Commerce05.nif</SymbolPath>
			</SymbolPaths>
		</YieldInfo>
	</YieldInfos>
</Civ4YieldInfos>


I got exited when I found that I could use the <iPeakChange> tag to give Peaks Yields, and then found out that it actually doesn't do anything. WHy would they put such a tag in if impassible tiles can't have yields anyway? Just to taunt me?
 
SDK phobia:

Would I regret opening up the SDK and altering some C++? I've always tried to avoid it, but I am thinking there are some things which would be easier to alter that way. My reasons for avoiding are,
1. Patches are more work to keep in synch.
2. Harder to bugtrack,
3. Makes my stuff less useful to other modmodders who might want to borrow it.
4. I might be tempted to start tinkering in many areas, compounding all of the above.
5. It's just one more gorram thing.

Things I would like to do:
a. Animals to move in claimed territory
b. Civilized AI control back into AI_unitUpdate instead of just barbs, or some other way to give me scripted influence over AI decisions
c. Barb-spawn and Animal-spawn amended to allow more settings/tinkerings in the python

and, Probably a lot of other little bits which I currently manage through python or would like to do but seem complicated.

Any thoughts or advice?
 
My two cents:

The SDK makes a lot of things a lot easier and allows you to do many things in a cleaner way (saving stuff for example). The three things you want to do should all be possible with it. Xienwolf made something similar to point "a" for Fall Further lately for example. To be able to define, when exactly the SDK should make a call for Python is a nice feature too. Saved me quite some workarounds I had to write for FlavourMod before moving on to use the SDK for it.

The one point most annoying is the updating part. You will have to update essentially for almost every patch coming out for FfH, but there are tools you probably know already to help you with this (WinMerge, Beyond Compare, ...). If you set up a debugging environment for the SDK (not too difficult, there are nice guides), debugging is only a little bit more complicated than for Python. You can get the stack trace, set break points, execute your code line for line and many more cool things.
 
SDK > python. Period. Not only is it FAR faster, but you can do anything you dream of instead of having to wonder if it is going to be overwritten, or if it is even exposed. And with a debug DLL, it is a lot easier to bugtack actually. Plus, if you want to touch the AI, you NEED to be in the DLL.
 
Plus, if you want to touch the AI, you NEED to be in the DLL.
Not really. There are Python overrides for many of the things the AI do. What they choose to tech, build, train, etc. Sure it's not many groundbreaking things you can do to change the AI but you can do a lot, like how they move around certain units. But I do agree with you, it's better, and faster if done in SDK, and there are so much more you can do.
 
Yes, modifying the xml files is a great way to try and get the AI to work the way you want it to. BUT in the end, it comes to how those values are interpreted in the SDK, and if you don't know how, or if you can't change how they are interpreted then you are in for a lot of testing to get the effect you want. In an upcoming mod I am making, I've been using the Flavor values (e.g. FLAVOR_SCIENCE) a lot to try and make my leaders playing style differently flavored. But for my game it required an extensive change of the SDK.
 
Whoops, never mind.
 
Is it possible to increase the rate at which hell terrain spreads? If so, what would I need to edit?
 
Has something changed in the C++ in patch 'm' or 'n' to cause this?

I had a function
Code:
def changeOwner(self, pUnit, iNewOwner):
	pType = gc.getUnitInfo(pUnit.getUnitType()).getType()
	pPlot = pUnit.plot()
	pPlayer = gc.getPlayer(iNewOwner)
	pNewUnit = pPlayer.initUnit(gc.getInfoTypeForString(pType),pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)
	pNewUnit.convert(pUnit)
		
	return pNewUnit

I call this with
Code:
barbPlayer = gc.getBARBARIAN_PLAYER()
pChanged = self.changeOwner(caster, barbPlayer)

and it doesn't work - I get an unidentifiable C++ error on the initunit line. This worked previously (path L, I think)

Tested by setting pPlayer to the original unit's owner and it worked (for what it's worth!) so somehow it's the barbplayer variable it doesn't like. I check the variable before calling and inside the changeOwner and it is 35 each time.

Any ideas?

UPDATE:

I just had another event CTD on me as AI player, error when I cause it to happen as a Human player. Same conditions:
pOwner=gc.getPlayer(gc.getBARBARIAN_PLAYER())
followed by
unitMonster = pOwner.initUnit(gc.getInfoTypeForString(sType), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)

when I change to
pOwner = gc.getPlayer(0)

this works as expected.

This used to work - has something changed either gc.getBARBARIAN_PLAYER(), or initUnit for the barbarian player?


EDIT AGAIN:
ok, trred initUnit into an empty plot and this works OK.
Am I being dumb? I am certain this worked previously. At the moment I can't create a barb unit in this manner in the same plot as a non-barb unit. Previously I would do this and the existing units would just be forced aside. Now it doesn't create the unit, python errors and/or crashes.
 
I think your issue is probably that you are creating a unit in the same tile as a visible defender. Since the Barbarian and the other unit are at war with each other, red flags fly up during CvUnit::setXY() because it detects an enemy which CAN defend in the tile.
 
I think your issue is probably that you are creating a unit in the same tile as a visible defender. Since the Barbarian and the other unit are at war with each other, red flags fly up during CvUnit::setXY() because it detects an enemy which CAN defend in the tile.

So, has this NEVER worked? Perhaps I've had a problem all along and not realised! It seems to me that it worked before, and just shoved the offending units aside into an empty plot (like when a unit spawns from a lair).
 
Well, the function in setXY should move them aside, that is true. Kael added these bits, so I would suspect one of them is your issue, but both seem fairly harmless:

Code:
				if (pLoopUnit != NULL)
				{
					if (isEnemy(pLoopUnit->getTeam(), pNewPlot) || pLoopUnit->isEnemy(getTeam()))
					{
						if (!pLoopUnit->canCoexistWithEnemyUnit(getTeam()))
						{
							if (NO_UNITCLASS == pLoopUnit->getUnitInfo().getUnitCaptureClassType() && pLoopUnit->canDefend(pNewPlot))
							{

//FfH: Modified by Kael 12/30/2008
//								pLoopUnit->jumpToNearestValidPlot(); // can kill unit
                                if (!isInvisible(pLoopUnit->getTeam(), false))
                                {
                                    pLoopUnit->jumpToNearestValidPlot(); // can kill unit
                                }
//FfH: End Modify

							}
							else
							{

//FfH Hidden Nationality: Modified by Kael 08/27/2007
//								if (!m_pUnitInfo->isHiddenNationality() && !pLoopUnit->getUnitInfo().isHiddenNationality())
								if (!isHiddenNationality() && !pLoopUnit->isHiddenNationality())
//FfH: End Modify

								{
									GET_TEAM(pLoopUnit->getTeam()).changeWarWeariness(getTeam(), *pNewPlot, GC.getDefineINT("WW_UNIT_CAPTURED"));
									GET_TEAM(getTeam()).changeWarWeariness(pLoopUnit->getTeam(), *pNewPlot, GC.getDefineINT("WW_CAPTURED_UNIT"));
									GET_TEAM(getTeam()).AI_changeWarSuccess(pLoopUnit->getTeam(), GC.getDefineINT("WAR_SUCCESS_UNIT_CAPTURING"));
								}

//FfH: Modified by Kael 12/30/2207
//								if (!isNoCapture())
//								{
//									pLoopUnit->setCapturingPlayer(getOwnerINLINE());
//								}
								if (!isNoCapture() || GC.getUnitInfo((UnitTypes)pLoopUnit->getUnitType()).getEquipmentPromotion() != NO_PROMOTION)
								{
								    if (!pLoopUnit->isHiddenNationality())
								    {
                                        pLoopUnit->setCapturingPlayer(getOwnerINLINE());
                                    }
								}
//FfH: End Modify

								pLoopUnit->kill(false, getOwnerINLINE());
 
Well, the function in setXY should move them aside, that is true. Kael added these bits, so I would suspect one of them is your issue, but both seem fairly harmless:

Code:
				if (pLoopUnit != NULL)
				{
					if (isEnemy(pLoopUnit->getTeam(), pNewPlot) || pLoopUnit->isEnemy(getTeam()))
					{
						if (!pLoopUnit->canCoexistWithEnemyUnit(getTeam()))
						{
							if (NO_UNITCLASS == pLoopUnit->getUnitInfo().getUnitCaptureClassType() && pLoopUnit->canDefend(pNewPlot))
							{

//FfH: Modified by Kael 12/30/2008
//								pLoopUnit->jumpToNearestValidPlot(); // can kill unit
                                if (!isInvisible(pLoopUnit->getTeam(), false))
                                {
                                    pLoopUnit->jumpToNearestValidPlot(); // can kill unit
                                }
//FfH: End Modify

							}
							else
							{

//FfH Hidden Nationality: Modified by Kael 08/27/2007
//								if (!m_pUnitInfo->isHiddenNationality() && !pLoopUnit->getUnitInfo().isHiddenNationality())
								if (!isHiddenNationality() && !pLoopUnit->isHiddenNationality())
//FfH: End Modify

								{
									GET_TEAM(pLoopUnit->getTeam()).changeWarWeariness(getTeam(), *pNewPlot, GC.getDefineINT("WW_UNIT_CAPTURED"));
									GET_TEAM(getTeam()).changeWarWeariness(pLoopUnit->getTeam(), *pNewPlot, GC.getDefineINT("WW_CAPTURED_UNIT"));
									GET_TEAM(getTeam()).AI_changeWarSuccess(pLoopUnit->getTeam(), GC.getDefineINT("WAR_SUCCESS_UNIT_CAPTURING"));
								}

//FfH: Modified by Kael 12/30/2207
//								if (!isNoCapture())
//								{
//									pLoopUnit->setCapturingPlayer(getOwnerINLINE());
//								}
								if (!isNoCapture() || GC.getUnitInfo((UnitTypes)pLoopUnit->getUnitType()).getEquipmentPromotion() != NO_PROMOTION)
								{
								    if (!pLoopUnit->isHiddenNationality())
								    {
                                        pLoopUnit->setCapturingPlayer(getOwnerINLINE());
                                    }
								}
//FfH: End Modify

								pLoopUnit->kill(false, getOwnerINLINE());

if I am reading that right, all the changes say is, don't push units out if our new unit is invisible to them; and, capture any unit with an equipment promo (equipment items lying on the ground) even if the new unit can't normally capture units.

Can't see how that would cause my problem.

I'm going back to square 1 - reinstall FfH, repatch, test on clean install, then re-apply modmod and see if I have broken it somehow
 
I have added all of the minor leaders as civilizations. I have assigned them traits, but want a more complicated setup.

I would like the minor civs to start off with fewer/weaker trait setups (from weak, to just fewer positive traits) and then to gain additional traits based on goals. (Losing weak, gaining creative after 2million population was reached)

The Illians currently can gain traits, and I see from the first post that I think in CvPlayer there is an addtrait, and a remove trait call. I've looked through the python, and I'm not finding this call used at all.

Could someone please point me to where this call might me used. Do I need to look through the C code? Am I just missing where it is in Python.

Thank you.
 
The call CyMessageControl().sendApplyEvent(5013, EventContextTypes.EVENTCONTEXT_ALL, (iPlayer,gc.getInfoTypeForString('TRAIT_AGGRESSIVE'),True)) giving the Illians their Aggresive trait from Letum Frigus is found in C:\Program Files\Firaxis Games\Sid Meier's Civilization 4\Beyond the Sword\Mods\Fall from Heaven 2\Assets\python\entrypoints\CvRandomEventInterface.py. It is under def doLetumFrigus3(argsList):, which is called by an event in C:\Program Files\Firaxis Games\Sid Meier's Civilization 4\Beyond the Sword\Mods\Fall from Heaven 2\Assets\XML\Events\CIV4EventInfos.xml after you select the write one of the options presented in C:\Program Files\Firaxis Games\Sid Meier's Civilization 4\Beyond the Sword\Mods\Fall from Heaven 2\Assets\XML\Events\CIV4EventTriggerInfos.xml, which is in this case triggered by a call in C:\Program Files\Firaxis Games\Sid Meier's Civilization 4\Beyond the Sword\Mods\Fall from Heaven 2\Assets\python\entrypoints\CvSpellInterface.py, which is triggered by the <PythonOnMove>onMoveLetumFrigus(pCaster, pPlot)</PythonOnMove> line in C:\Program Files\Firaxis Games\Sid Meier's Civilization 4\Beyond the Sword\Mods\Fall from Heaven 2\Assets\XML\Terrain\CIV4ImprovementInfos.xml

Simple really :p

It is also called more directly in CvSpellInterface.py in post combat calls for Basium and Hyborem, using False instead of True to remove the traits.
 
Me again, same bugs different day.

Downloaded all files again just in case..
Reinstalled FfH 2, media pack, patch n
Changed spellExploreLair only to :

Code:
def spellExploreLair(caster):
	pPlot = caster.plot()
	pOwner = gc.getPlayer(gc.getBARBARIAN_PLAYER())
	sType = gc.getInfoTypeForString('UNIT_WARRIOR')
	unitMonster = pOwner.initUnit(sType, pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)

Set up a few dungeons with empty, passable plots all around and regular warriors on them. Clicked the Explore Lair icon and every time I get:

Code:
Traceback (most recent call last):
  File "CvSpellInterface", line 22, in cast
  File "<string>", line 0, in ?
  File "CvSpellInterface", line 1166, in spellExploreLair
RuntimeError: unidentifiable C++ exception

As a test, if I change the
pPlayer = gc.getPlayer(caster.getOwner())
to
pPlayer = gc.getPlayer(0)

this works (creates a unit).

I think this is bugged - the existing units ought to be pushed aside when a hostile unit spawns.
Am I right?
 
Back
Top Bottom