Questions for modders

Ingvar

Chieftain
Joined
Jun 15, 2008
Messages
62
I am trying to figure out bunches of things on how Civ4/BTS/FFH2 works. I am not that good at reading code - I can understand some, but not all the time.

I've read some of the modding FAQs and guides, but I don't think it's enough to get some of the questions answered. Maybe those questions are so self-explanatory that everyone knows, or maybe answers are buried deep in old threads. In any case I figured I'd try.

I thought I'd start this thread and post questions here for any passer by to answer - hopefully it would help others with their questions. Specifically with mod-modding FFH2/FF.

So my first question is:

When does a civ die? On its last city razed? Is it possible to keep a civ alive with just a unit?

Second:
What is a specific function, if there is any, to upgrade a unit to another unit through events?

Thanks!
 
There is a Game Option (Require Complete Kills) that keeps a civ in the game so long as it has a single unit. Otherwise, they are supposed to be eliminated (and have all their units removed) when they lose their last city. It has been that way since Vanilla Civ IV was first released, but is a little different in FfH. When the cities are destroyed and thee units auto-killed, it results in units that cairred equipment dropping it, still under that civs control. That keeps the civ in the game until someone captures the equipment. If it weren't handled that way then equipmewnt could be permanently destroyed.



I assume this work probably work like several spells do, in python. For example,
Code:
		pPlayer = gc.getPlayer(pCaster.getOwner())
		newUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_WOLF_RIDER'), pCaster.getX(), pCaster.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)
		newUnit.convert(pCaster)

I'm no expert on making events. Obviously pCaster wouldn't be exist in this case, as this would be PythonCallbackdefined in CvRandomEventInterface.py instead of CvSpellInterface.py. I'm not quite sure how the unit to be upgraded would be chosen. (And it's not exactly "upgrading," since you could for instance choose to turn a champion into a worker.)
 
There is a Game Option (Require Complete Kills) that keeps a civ in the game so long as it has a single unit. Otherwise, they are supposed to be eliminated (and have all their units removed) when they lose their last city. It has been that way since Vanilla Civ IV was first released, but is a little different in FfH. When the cities are destroyed and thee units auto-killed, it results in units that cairred equipment dropping it, still under that civs control. That keeps the civ in the game until someone captures the equipment. If it weren't handled that way then equipmewnt could be permanently destroyed.

Do you know if there's a way around it? For example, civ is alive at the beginning with just a settler and a couple of units, they can wonder for a long time without a city.

Do you think that "complete kills" may be somehow enabled just for one civ?

I assume this work probably work like several spells do, in python. For example,
Code:
		pPlayer = gc.getPlayer(pCaster.getOwner())
		newUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_WOLF_RIDER'), pCaster.getX(), pCaster.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)
		newUnit.convert(pCaster)

I'm no expert on making events. Obviously pCaster wouldn't be exist in this case, as this would be PythonCallbackdefined in CvRandomEventInterface.py instead of CvSpellInterface.py. I'm not quite sure how the unit to be upgraded would be chosen. (And it's not exactly "upgrading," since you could for instance choose to turn a champion into a worker.)

Does the unit that was upgraded that way count towards killed units?
 
Do you know if there's a way around it? For example, civ is alive at the beginning with just a settler and a couple of units, they can wonder for a long time without a city.

I've never looked into the code myself, but yes there is that obviosu exception.

Do you think that "complete kills" may be somehow enabled just for one civ?
I have no idea.

Does the unit that was upgraded that way count towards killed units?
No, not normally. You could put a pCaster.kill(True, 0) either before or after the .convert(pCaster) function, which would make it count as dieing but would not effect the new unit converted from the last. If .kill is used first, then the unit would lose Immortal, and only have half the xp if it had the Spirit Guide Promotion.
 
The code is set up so that the enablecompletekills function is applied to a player individually until they settle their first city. You can easily set up conditions to enable this to be applied at any point in the game.
 
The code is set up so that the enablecompletekills function is applied to a player individually until they settle their first city. You can easily set up conditions to enable this to be applied at any point in the game.

Thanks! If that is the case what I am trying to test might actually work!

Which python file would have a sample of the function being used? I've tried searching for "enablecompletekills" in BTS folder, but haven't found it.
 
Is this the line of code that would set the option:

gc.getActivePlayer().setOption(GameOptionTypes.GAMEOPTION_COMPLETE_KILLS, True)

Where would I need to set it best to ensure that a particular civ always has this enabled? On game start event?
 
From CvPlayer.cpp:
Code:
void CvPlayer::verifyAlive()
{
	bool bKill;

	if (isAlive())
	{
		bKill = false;

		if (!bKill)
		{
			if (!isBarbarian())
			{
				if (getNumCities() == 0 && getAdvancedStartPoints() < 0)
				{
					if ((getNumUnits() == 0) || (!(GC.getGameINLINE().isOption(GAMEOPTION_COMPLETE_KILLS)) && isFoundedFirstCity()))
					{
						bKill = true;
					}
				}
			}
		}

		if (!bKill)
		{
			if (!isBarbarian())
			{
				if (GC.getGameINLINE().getMaxCityElimination() > 0)
				{
					if (getCitiesLost() >= GC.getGameINLINE().getMaxCityElimination())
					{
						bKill = true;
					}
				}
			}
		}

		if (bKill)
		{
			setAlive(false);
		}
	}
	else
	{
		if ((getNumCities() > 0) || (getNumUnits() > 0))
		{
			setAlive(true);
		}
	}
}

That is where the game checks if a player is still alive. If setAlive(false) is ever run, then the player is removed from the game.

Not sure if setting a gameoption can work for an individual player, since it is checking GC.getGameINLINE().isOption(GAMEOPTION_COMPLETE_KILLS), and not isOption(GAMEOPTION_COMPLETE_KILLS) (the xxx.____ means that it is checking the function ____ within the file xxx.cpp, if there is no xxx., then it is checking it within the current file)

I suppose if you find a way to change the gameoptions for the full game during Python maneuvers you could turn that option on anytime that the player you want to keep alive is active (which probably won't work because I assume this function is called anytime that a City is taken, or a Unit is killed, which happens on OTHER people's turns most of the time), otherwise you would have to modify the SDK to accomplish this task, and place the modification within the section I quoted at the top of the post (add a new XML tag to Civilization, Trait or Leader Information which will flag the player as Requires Complete Kills, then add a check for that flag to the quoted code)
 
I've tried setting a game option for complete kills - that works, but it sets the option game-wide. So unless I edit the dll - I don't think it works for just a single civ.

The good news is that it looks like the setFoundedFirstCity() is exposed to python (I don't know if I am looking at the right file (BTS\CvGameCore.cpp or h). I haven't been able to set that option to False. Probably just because I am still new to python syntax. Here's how it looks in CvEventManager.py under the onEndGameTurn:
Code:
for iPlayer in range(gc.getMAX_PLAYERS()):
[INDENT]
player = gc.getPlayer(iPlayer)
if player.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_AUSTERIN'):
[INDENT]player.setFoundedFirstCity(False)[/INDENT]
[/INDENT]

I wonder if it would be better to set it every time in onBeginPlayerTurn or maybe just onCityRazed? What I want to achieve is that a civ can exist without a city, while all others have to have cities in order to be alive.
 
Careful now, that flag indicates a bit more than just require complete kills :) You also cannot research anything if you have not yet founded your first city.

But as far as I see things, only isFoundedFirstCity is exposed, not setFounded...
 
Careful now, that flag indicates a bit more than just require complete kills :) You also cannot research anything if you have not yet founded your first city.

But as far as I see things, only isFoundedFirstCity is exposed, not setFounded...

Ah the joys of figuring out how something works :) At this point all I want to do is figure out how to keep a civ alive without cities. How they get techs, culture, score, etc is a worry for another day :) Though it seems like the right way to do this is to edit the DLL to include another trait - NOMADIC that would set Complete Kills just for that civ, or excludes a civ with that trait from city requirement to research and be alive.

How do you tell if a flag is exposed to python or not - do I need to download FFH2 dll source? or can I tell from the cpp/h files that are in Beyond The Sword folder?

If I were to modify the DLL, what do I need if I want to make sure it works with FFH2/FF?

Thanks!
 
You have to download the FfH source to know most things, because Kael may have exposed something that Firaxis didn't (though it isn't too likely).

FF has a source base seperate from that of FfH with more changes, so you'd need to download Vehem's/my DLL as well for that.

FF DLL is available in the FF thread in this forum, FfH DLL is available in the Modder's Guide stickied post in the main FfH forum.



If something is exposed to python, you find a line like this one
Code:
		.def("isFoundedFirstCity", &CyPlayer::isFoundedFirstCity, "bool ()")
in one of the Cy___.cpp files.

First part is what you call it in Python, second part is what it is in the DLL, and third part is what function should be passed through it.
 
Thanks! I guess I am off to download the source files and see what's inside :)
 
First off thanks to everyone! This modding lark is fun stuff. Especially python - i think I am liking it more and more. Thanks for all the links to tutorials - that helps, alongside reading other's code. Getting pretty far along on the whole nomad concept - civ is in, cities are built by moving transport units, cities can convert back to those units, recon units are able to grab and use resources. No joy on avoiding the doom of last city razed - so civ is not truly mobile yet - preventing them from committing suicide by checking if they only have one city.

I'd like to keep the thread alive with questions and answers :)
 
I guess that just means they have to work like Locusts, settling down in the front and picking up in the rear to always be moving, but always be settled. Might even be able to work that into other functions to set a flavor for the Civ, though that is quite likely NOT what you were aiming for initially.

Have you peeked at SDK modding yet though? You could quite easily dodge the "must have a city" bullet from in there.
 
It's actually looking pretty good at this point. Units have gained ability to grab live resources from the plots and carry them around (lutefisk's code), they can actively use resources for making other units, gaining promotions, etc. Cities can be packed up into units, and the limited amount of buildings are carried with them as promotions. The civ won't be able to build a lot of things. Next thing I am going to work on is their ability to actively use resources of civs whose lands they are passing through - i.e. getting promos from wonders, buildings, other units. AI for this will be crazy I suppose. But I think it is possible to make a viable Rover civ with a completely different way of playing to achieve victory. In the end I'd like a civ that will stay put only for 20% of game time - the rest of the time it's on the move, hunting, gathering, fighting, stealing both on land and sea.

I will build the mod up with python and xml until the point it's all in good working order, and then take a look at this one SDK change to make them truly mobile. I don't have any compiler software for C++ on my machine, but maybe if I just code it someone here could compile the DLL for it.

I'll post some of little snipplets for spells & promotions in this thread as soon as I get them perfected. It's mostly built up on other's code, but might save someone some work.

I guess that just means they have to work like Locusts, settling down in the front and picking up in the rear to always be moving, but always be settled. Might even be able to work that into other functions to set a flavor for the Civ, though that is quite likely NOT what you were aiming for initially.

Have you peeked at SDK modding yet though? You could quite easily dodge the "must have a city" bullet from in there.
 
Has anyone ever seen this:
I am modding based on FFH2 (0.32l) + FF040.

Promotions AIR2 and AIR3 are suddenly available to every unit in the game, and I can't see where I made that happen. I looked at promotions XML, both main one and the one that I made in Modules for the additional civ - and still can't figure out what I could have done to make this happen.

Any ideas, anyone?
 
I made this based on Lutefisk_Mafia's code. Currently I want just one civ to have cultural outposts. Added "Campsite" improvement in XML. This python code is a part of CvEventManager.py runs through all the plots to set culture to campsites on the end of each turn. Values iXLoop and iYLoop set the size of the border.

Code:
		'Called at the end of the end of each turn'
		iGameTurn = argsList[0]
		self.updateCultureCampsites()

	def updateCultureCampsites(self):
		for iPlayer in range(gc.getMAX_PLAYERS()):
			iCampsite = gc.getInfoTypeForString('IMPROVEMENT_CAMPSITE')
			if gc.getPlayer(iPlayer).getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_AMBULNATI'):
				for i in range (CyMap().numPlots()):
					pPlot = CyMap().plotByIndex(i)
					if pPlot.getImprovementType() == iCampsite:
						iX = pPlot.getX()
						iY = pPlot.getY()
						for iXLoop in range(iX-1, iX+2):
							for iYLoop in range(iY-1, iY+2):
								iActiveX = iXLoop
								iActiveY = iYLoop
								if (iActiveX < 0):
									iActiveX = CyMap().getGridWidth() + iActiveX
								if (iActiveY < 0):
									iActiveY = CyMap().getGridHeight() + iActiveY
								pLoopPlot = CyMap().plot(iActiveX, iActiveY)
								pLoopPlot.setOwner(iPlayer)
								pLoopPlot.changeCulture(iPlayer, 10, False)

If anyone has a more efficient way of doing this please let me know!
 
Top Bottom