Modders Guide to FfH2

Thanks Snarko! this approach works. I was so determined to make it work with a Null/none but your way does the job. Use a city as a base then compare the two.

Now I will modify this to cope if the player does not have a city, and cope if there are no cities at all!

then I can have my "isCloseToHome" function which will tell a unit if it is closer to (or as close to) home than a rival city - useful when deciding to pop lairs, negotiate or attack villages, build forts, etc.

EDIT :

OK, I can use CyGame().getNumCities() to tell the total number of cities in game, useful.
But I can't get a city by index to just find the first city and use it as a key?
 
How do you find out, what causes crashes during the game? Because my brother and I have created an own modmod that worked out quite well, but now around turn 350 the game crashes every time we load our savegame. The same we had in another game around turn 280. Can someone help us please? Because before we don't solve this problem we can't add new game elements.
 
How do you find out, what causes crashes during the game? Because my brother and I have created an own modmod that worked out quite well, but now around turn 350 the game crashes every time we load our savegame. The same we had in another game around turn 280. Can someone help us please? Because before we don't solve this problem we can't add new game elements.

Which parts have you modded? XML, Python, SDK, units/graphics... the methods to track down problems depend on what has been changed.
 
We have modded python and XML.
 
Read the thread about tracking down crashes.

In my experience python rarely cause crashes. You usually just get a popup explaining that there's a problem. XML otoh can cause crashes, often due to references to non-existant art. If you think that could be your problem then I suggest you reveal the whole map (set the cheatcode in the ini file, then press ctrl + z) and look at any cities which finish production on that turn.

You can also turn on logging in the ini file, the more the better. MPLog.txt can often given you an idea about what's going on when it crashes even if it'll often crash before logging the part that is crashing, so you only know what happend right before.
 
Since the code from Snarko won't work for the player with no capital, I'll post what I was talking about as well (was not willing to post raw python since I am not too familiar with all the formatting conventions, but modifications to his code I can do ;))

Code:
pPlot = caster.plot()
iX = pPlot.getX()
iY = pPlot.getY()
pTileCity = pPlot().getCity()
if pTileCity == None:
	pNearestCity = CyMap().findCity(iX, iY, PlayerTypes.NO_PLAYER, TeamTypes.NO_TEAM, true, false, TeamTypes.NO_TEAM, DirectionTypes.NO_DIRECTION, pTileCity)
else:
	pNearestCity = pTileCity
caster.setXY(pPlot.getX(), pPlot.getY(), false, true, true)
 
It's quite a basic issue, but I'm afraid I'm not at all good at scripting...

You see, I want to make Brigit (when liberated from RoC) have a higher Level, instead of the default Level 1, so that she gains promotions much later.

I gave her Channeling III and Fire (in the Civ4UnitInfos file, since I always felt she was way "underpowered"), but i don't want her to be able to learn Fire III that soon!

Can someone please help me with this?
 
Ok, first, go to UNIT_BRIGIT_HELD and remove the <Capture>UNIT_BRIGIT</Capture> entry.

Now find the space in python Brigit_held's post combat python is held (CvSpellInterface.py)

Code:
def postCombatBrigitHeld(pCaster, pOpponent):
	pPlot = pCaster.plot()
	if pPlot.getImprovementType() == gc.getInfoTypeForString('IMPROVEMENT_RING_OF_CARCER'):
		pPlot.setImprovementType(-1)
	pPlot = pOpponent.plot()
	pPlayer = gc.getPlayer(pOpponent.getOwner())
	newUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_BRIGIT'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)
	newUnit.setLevel(pOpponent.getLevel())
	newUnit.setExperience(pOpponent.getExperience, -1)
	if pPlayer.isHuman():
		t = "TROPHY_FEAT_RESCUE_BRIGIT"
		if not CyGame().isHasTrophy(t):
			CyGame().changeTrophyValue(t, 1)

This would spawn Brigit at the same level and experience as whoever liberated her.
 
Thanks a lot Snarko. We did find out, what caused the problem. It was in fact a non-existant art that made the game crash.
 
Ok, first, go to UNIT_BRIGIT_HELD and remove the <Capture>UNIT_BRIGIT</Capture> entry.

Now find the space in python Brigit_held's post combat python is held (CvSpellInterface.py)

Code:
def postCombatBrigitHeld(pCaster, pOpponent):
	pPlot = pCaster.plot()
	if pPlot.getImprovementType() == gc.getInfoTypeForString('IMPROVEMENT_RING_OF_CARCER'):
		pPlot.setImprovementType(-1)
	pPlot = pOpponent.plot()
	pPlayer = gc.getPlayer(pOpponent.getOwner())
	newUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_BRIGIT'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)
	newUnit.setLevel(pOpponent.getLevel())
	newUnit.setExperience(pOpponent.getExperience, -1)
	if pPlayer.isHuman():
		t = "TROPHY_FEAT_RESCUE_BRIGIT"
		if not CyGame().isHasTrophy(t):
			CyGame().changeTrophyValue(t, 1)

This would spawn Brigit at the same level and experience as whoever liberated her.

Thanx, Xienwolf! As you told me in #erebus , it would be a loooong time till she gets any promotion at all if I do this (I just want her to wait that long for Fire III, not for Combat I also! Very shortsighted of me indeed!) Now I'm wondering if a better solution to my problem would be to make Brigit able to upgrade to a unit which looks the same but with Channeling III at Level XY, similar to Wizards>Archmages mechanic?

Would it work if I copy/pasted Brigit the Shining XML entry and just added lines for the unit upgrade?
 
One problem with letting Brigit upgrade is that when the option that when the AI ignore level requirements is on the AI would build Brigit directly without ever having captured her. For that reason, a spell with <UnitClassPrereq>Brigit and a <iCasterMinLevel> that give Brigit Fire III might be a better way to give her the promotion than an upgrade would be.


You could also try giving the Fire III promotion a <iMinLevel> or the Summon Fire Elemental spell a <iCasterMinLevel>. So long as this is no larger than 6 it would have no effect on an Archmage's ability to learn the spell (well, expect when that option is on and the AI is building its archmages directly) although it could effect heroes like Hemah and Gibbon.

You could also make a unit-specific copy of the Fire III spell that becomes available to Brigit when she reaches a given level.





I still don't see the point of even having Brigit_held in the game when Brigit could just be created by giving the Ring of Carcer a <PythonOnMove> call.
 
AI_unitUpdate - only for barbs, or for all AI players?
I thought I had used it for all AIs before, but I just did a test and it seems to govern only the barbs. Is that right? How can I interfere with AI unit work?
 
It was for all AIs but now it's only for barbs. Don't know how to interfer in python now without doing something weird like a loop through the players units elsewhere.
 
It was for all AIs but now it's only for barbs. Don't know how to interfer in python now without doing something weird like a loop through the players units elsewhere.

Maybe I just don't understand this function, but it seems to be running multiple times for each unit - around 20. What's that all about?

Code:
Starting : Arcruel of Kondalcheie (Gargoyle)
Checked plot data
Unit is in a lair
Finished lair related checks
Starting : Arcruel of Kondalcheie (Gargoyle)
Checked plot data
Unit is in a lair
Finished lair related checks
Starting : Arcruel of Kondalcheie (Gargoyle)
Checked plot data
Unit is in a lair
Finished lair related checks
Starting : Arcruel of Kondalcheie (Gargoyle)
Checked plot data
Unit is in a lair
Finished lair related checks
Starting : Arcruel of Kondalcheie (Gargoyle)
Checked plot data
Unit is in a lair
Finished lair related checks
I have about 20 of these, I put checkpoint prints in at various stages to see what was going on.

What have I missed? (the whole point, probably)
 
20? It's not 100?

CvUnit::AI_update() is called until one of several things occur which causes it to stop. Things such as the unit having no moves left, CvUnit::AI_update() returning true or the unit dying.

From python there is no way to make CvUnit::AI_update() return true - if you return false in python it'll go on as if nothing happend, if you return true CvUnit::AI_update() in turn will return false.

This is why units guarding a lair etc never heals. Once it's looped through 100 times it'll take away the units moves for that turn (why? no idea) and break out of the loop.


*edit*
What should be done is telling the unit to skip it's turn. This can be done via pUnit.getGroup().pushMission(MissionTypes.MISSION_SKIP, -1, -1, 0, false, false, MissionAITypes.NO_MISSIONAI, pUnit.plot(), pUnit)
 
Kael,

If you move the Immortal Rebirth process out of CvUnit::kill and into CvUnit::doDelayedDeath you can avoid a lot of the extra code to dodge innapropriate ressurections (founded religion or Corporation, AI is trying to disband the unit for upkeep or danger, founded a building, performed a Great Work...). Basically any kill(true) command will then trigger Immortality if present, and any kill(false) command will properly ignore it.
 
20? It's not 100?

CvUnit::AI_update() is called until one of several things occur which causes it to stop. Things such as the unit having no moves left, CvUnit::AI_update() returning true or the unit dying.

From python there is no way to make CvUnit::AI_update() return true - if you return false in python it'll go on as if nothing happend, if you return true CvUnit::AI_update() in turn will return false.

This is why units guarding a lair etc never heals. Once it's looped through 100 times it'll take away the units moves for that turn (why? no idea) and break out of the loop.


*edit*
What should be done is telling the unit to skip it's turn. This can be done via pUnit.getGroup().pushMission(MissionTypes.MISSION_SKIP, -1, -1, 0, false, false, MissionAITypes.NO_MISSIONAI, pUnit.plot(), pUnit)

This looks useful - is there a list of missionTypes and what they are supposed to do?
 
CySelectionGroup.pushMission(MissionType eMission, INT iData1, INT iData2, INT iFlags, BOOL bAppend, BOOL bManual, MissionAIType eMissionAI, CyPlot pMissionAIPlot, CyUnit pMissionAIUnit)

List of missions, from CvEnums.h. With my comments, getting most of the information from CvSelectionGroup::startMission() and CvSelectionGroup::canStartMission.

Spoiler Missions :

NO_MISSION = -1,
MISSION_MOVE_TO, (iData1 = iX, iData2 = iY)
MISSION_ROUTE_TO, (iData1 = iX, iData2 = iY)
MISSION_MOVE_TO_UNIT, (iData1 = iPlayer, iData2 = iUnit)
MISSION_SKIP, (iData ignored)
MISSION_SLEEP, (iData ignored)
MISSION_FORTIFY, (iData ignored)
MISSION_PLUNDER, (iData ignored)
MISSION_AIRPATROL, (iData ignored)
MISSION_SEAPATROL, (iData ignored)
MISSION_HEAL, (iData ignored)
MISSION_SENTRY, (iData ignored)
MISSION_AIRLIFT, (iData1 = iX, iData2 = iY)
MISSION_NUKE, (iData1 = iX, iData2 = iY)
MISSION_RECON, (iData1 = iX, iData2 = iY)
MISSION_PARADROP, (iData1 = iX, iData2 = iY)
MISSION_AIRBOMB, (iData1 = iX, iData2 = iY)
MISSION_RANGE_ATTACK, (iData1 = iX, iData2 = iY)
MISSION_BOMBARD, (iData ignored)
MISSION_PILLAGE, (iData ignored)
MISSION_SABOTAGE, (iData ignored)
MISSION_DESTROY, (iData ignored)
MISSION_STEAL_PLANS, (iData ignored)
MISSION_FOUND, (iData ignored)
MISSION_SPREAD, (iData1 = ReligionType)
MISSION_SPREAD_CORPORATION, (iData1 = CorporationType)
MISSION_JOIN, (iData1 = SpecialistType)
MISSION_CONSTRUCT, (iData1 = BuildingType)
MISSION_DISCOVER, (iData ignored)
MISSION_HURRY, (iData ignored)
MISSION_TRADE, (iData ignored)
MISSION_GREAT_WORK, (iData ignored)
MISSION_INFILTRATE, (iData ignored)
MISSION_GOLDEN_AGE, (if iData1 is set play animation. If it's -1 (the default) start a golden age)
MISSION_BUILD, (iData1 = BuildType)
MISSION_LEAD, (iData1 = iUnit)
MISSION_ESPIONAGE, (iData1 = EspionageMissionType, iData2 is mission dependant)
MISSION_DIE_ANIMATION, (iData ignored)


There's a few more. Don't use them. They're special.
 
CySelectionGroup.pushMission(MissionType eMission, INT iData1, INT iData2, INT iFlags, BOOL bAppend, BOOL bManual, MissionAIType eMissionAI, CyPlot pMissionAIPlot, CyUnit pMissionAIUnit)

List of missions, from CvEnums.h. With my comments, getting most of the information from CvSelectionGroup::startMission() and CvSelectionGroup::canStartMission.
There's a few more. Don't use them. They're special.

Thanks again, I am going to play with these to help control the AI.

My experiment in interrupting AI behaviour will be this:
Create a spell with no "payload", but the whole control process is in the requirement python! Units constantly process the requirement script, so if I want to encourage a quantity of units to hold a fort or guard a village, I should be able to use the Fortify mission. It looks like I may be able to use the move_to_unit to allow scouts or fortified soldiers to call a worker to them; to call reinforcements; that short of thing. route_to will presumable work well for remote resource-gathering villages. I'll test it out and see how it looks.
 
Searched the thread, but couldn't seem to find an answer for this, sorry if it's already been asked:

What do I need to add/edit to make a building unique to a civilisation? I already know how to add the building and such, I just don't want other civs being able to make it.

EDIT: Nevermind, I figured it out. :P
 
Back
Top Bottom