• Our friends from AlphaCentauri2.info are in need of technical assistance. If you have experience with the LAMP stack and some hours to spare, please help them out and post here.

[SDK] Adding a new yield/resource to COL

Fortuente

Chieftain
Joined
Feb 14, 2009
Messages
26
I have been really interested in playing around with culture lately. I find it among other things generally lacking in Vanilla COL, other things being victory conditions. So why not add a cultural victory condition and satisfy two needs at the same time?

In working on this, I figured out how to add an entirely new resource (which I am just going to call a YIELD from now on) via the SDK. What we are doing is essentially creating a new yield after the fashion of Bells and Crosses.

The game treats resources like Bells and Ore in basically the same way - generically called Yields. Because of this, you could use this method to add a new physical resource to the game. But I'm not sure if it would cause other problems, and of course you would have to go through all the trouble to add in the resource to the city management and Europe screens, diplomacy options, etc. So I am not going into that territory at all.

What I have created is simply a new yield that we can use as a metric to measure a phenomenon within the game, like Bells are used for rebel sentiment, crosses are used for immigration and education is used for ... whatever it is used for. (If Colonization were Family Guy, education would be Meg, methinks.) This is how I did it.

Here are the changes I made. You can see what I added as I named everything along the lines of FortuenteCulture to make it easy to find later.

CvEnums.h
Spoiler :

The following code starts on line 609:

Code:
enum DllExport YieldTypes
{
	NO_YIELD = -1,

	YIELD_FOOD,
	YIELD_LUMBER,
	YIELD_SILVER,
	YIELD_COTTON,
	YIELD_FUR,
	YIELD_SUGAR,
	YIELD_TOBACCO,
	YIELD_ORE,
	YIELD_CLOTH,
	YIELD_COATS,
	YIELD_RUM,
	YIELD_CIGARS,
	YIELD_TOOLS,
	YIELD_MUSKETS,
	YIELD_HORSES,
	YIELD_TRADE_GOODS,
	YIELD_HAMMERS,
	YIELD_BELLS,
	YIELD_CROSSES,
	YIELD_EDUCATION,
	[B]YIELD_FORTUENTECULTURE,[/B]

#ifdef _USRDLL
	NUM_YIELD_TYPES
#endif
};

CyEnumsInterface.cpp
Spoiler :

This code starts on line 365:

Code:
	python::enum_<YieldTypes>("YieldTypes")
		.value("NO_YIELD", NO_YIELD)
		.value("YIELD_FOOD", YIELD_FOOD)
		.value("YIELD_LUMBER", YIELD_LUMBER)
		.value("YIELD_SILVER", YIELD_SILVER)
		.value("YIELD_COTTON", YIELD_COTTON)
		.value("YIELD_FUR", YIELD_FUR)
		.value("YIELD_SUGAR", YIELD_SUGAR)
		.value("YIELD_TOBACCO", YIELD_TOBACCO)
		.value("YIELD_ORE", YIELD_ORE)
		.value("YIELD_CLOTH", YIELD_CLOTH)
		.value("YIELD_COATS", YIELD_COATS)
		.value("YIELD_RUM", YIELD_RUM)
		.value("YIELD_CIGARS", YIELD_CIGARS)
		.value("YIELD_TOOLS", YIELD_TOOLS)
		.value("YIELD_MUSKETS", YIELD_MUSKETS)
		.value("YIELD_HORSES", YIELD_HORSES)
		.value("YIELD_TRADE_GOODS", YIELD_TRADE_GOODS)
		.value("YIELD_HAMMERS", YIELD_HAMMERS)
		.value("YIELD_BELLS", YIELD_BELLS)
		.value("YIELD_CROSSES", YIELD_CROSSES)
		.value("YIELD_EDUCATION", YIELD_EDUCATION)
		[B].value("YIELD_FORTUENTECULTURE", YIELD_FORTUENTECULTURE)[/B]
		.value("NUM_YIELD_TYPES", NUM_YIELD_TYPES)
		;
CvPlayer.h
Spoiler :

This starts on line 765.

Code:
void doGold();
	void doBells();
	void doCrosses();
	[B]void doFortuenteCulture();[/B]
	void doWarnings();
	void doEvents();
	void doPrices();
CvPlayer.cpp
Spoiler :

You could do this differently with the same result, but this is how I did it:

There are two things to change in this file, first we need the Yield to do it's thing, so it is added to the turn here starting on line 2049:

Code:
void CvPlayer::doTurn()
{
	PROFILE_FUNC();

	CvCity* pLoopCity;
	int iLoop;

	FAssertMsg(isAlive(), "isAlive is expected to be true");
	FAssertMsg(!hasBusyUnit() || GC.getGameINLINE().isMPOption(MPOPTION_SIMULTANEOUS_TURNS)  || GC.getGameINLINE().isSimultaneousTeamTurns(), "End of turn with busy units in a sequential-turn game");

	gDLL->getEventReporterIFace()->beginPlayerTurn( GC.getGameINLINE().getGameTurn(),  getID());

	doEra();

	doUpdateCacheOnTurn();

	GC.getGameINLINE().verifyDeals();

	AI_doTurnPre();

	AI_assignWorkingPlots();

	doGold();

	doBells();

	doCrosses();

	[B]doFortuenteCulture();[/B]

	for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
	{
		pLoopCity->doTurn();
	}

	verifyCivics();

	doPrices();

	doEvents();

	interceptEuropeUnits();

	updateEconomyHistory(GC.getGameINLINE().getGameTurn(), getGold());
	updateIndustryHistory(GC.getGameINLINE().getGameTurn(), calculateTotalYield(YIELD_HAMMERS));
	updateAgricultureHistory(GC.getGameINLINE().getGameTurn(), calculateTotalYield(YIELD_FOOD));
	updatePowerHistory(GC.getGameINLINE().getGameTurn(), getPower());
	updateCultureHistory(GC.getGameINLINE().getGameTurn(), countTotalCulture());
	expireMessages();  // turn log
	m_aszTradeMessages.clear();

	gDLL->getInterfaceIFace()->setDirty(CityInfo_DIRTY_BIT, true);

	AI_doTurnPost();

	gDLL->getEventReporterIFace()->endPlayerTurn( GC.getGameINLINE().getGameTurn(),  getID());

	FAssert(checkPower(false));
	FAssert(checkPopulation());

Now we actually need the function doFortuenteCulture() to exist. I added it in right below the doCrosses() function which ends on line 8005:

Code:
void CvPlayer::doFortuenteCulture()
{
	if (getParent() == NO_PLAYER)
	{
		return;
	}

	int iFortuenteCultureRate = getYieldRate(YIELD_FORTUENTECULTURE);
}

You can see that currently my yield does nothing more than exist. But by adding the XML YIELD_FORTUENTECULTURE to units or buildings we can allow them to produce it. Eventually I want to find the best way to add in the values from doBells() and doCrosses() and the education one (which is in CvCity.cpp IIRC) and put them through a very simple formula to come up with a nice culture value. Whether this happens in doFortuenteCulture(), in Python or through XML I am not sure yet. Ideas are welcome!


After we compile the SDK DLL we now add in the Yield to the XML so we can play with it:

CIV4YieldInfos.xml
Spoiler :
Add in:

Code:
<YieldInfo>
			<Type>YIELD_FORTUENTECULTURE</Type>
			<Description>TXT_KEY_YIELD_FORTUENTECULTURE</Description>
			<Civilopedia>TXT_KEY_YIELD_FORTUENTECULTURE_PEDIA</Civilopedia>
			<bCargo>0</bCargo>
			<iBuyPriceLow>0</iBuyPriceLow>
			<iBuyPriceHigh>0</iBuyPriceHigh>
			<iSellPriceDifference>0</iSellPriceDifference>
			<iPriceChangeThreshold>0</iPriceChangeThreshold>
			<iPriceCorrectionPercent>0</iPriceCorrectionPercent>
			<iNativeBuyPrice>-1</iNativeBuyPrice>
			<iNativeSellPrice>-1</iNativeSellPrice>
			<iNativeConsumptionPercent>-1</iNativeConsumptionPercent>
			<iNativeHappy>0</iNativeHappy>
			<iHillsChange>0</iHillsChange>
			<iPeakChange>0</iPeakChange>
			<iLakeChange>0</iLakeChange>
			<iCityChange>0</iCityChange>
			<iMinCity>0</iMinCity>
			<iAIWeightPercent>100</iAIWeightPercent>
			<iAIBaseValue>5</iAIBaseValue>
			<iNativeBaseValue>0</iNativeBaseValue>
			<iPower>0</iPower>
			<iAsset>0</iAsset>
			<ColorType>COLOR_YIELD_FOOD</ColorType>
			<UnitClass>NONE</UnitClass>
			<iTextureIndex>-1</iTextureIndex>
			<iWaterTextureIndex>-1</iWaterTextureIndex>
			<Icon>,Art/Interface/Buttons/Unit_Resource_Colonization_Atlas.dds,8,16</Icon>
			<HighlightIcon>,Art/Interface/Screens/City_Management/Colonization_Yield_Highlight_Icon_Atlas.dds.dds,2,16</HighlightIcon>
			<Button>,Art/Interface/Buttons/Unit_Resource_Colonization_Atlas.dds,2,16</Button>
		</YieldInfo>

And that is basically it. I tested it last night and it all worked for me. I added YIELD_FORTUENTECULTURE as the base metric of culture and the yield produced by the Town Hall profession (statesman) (replacing YIELD_BELLS for both in GobalDefines.xml and CIV4ProfessionInfos.xml). Just as expected, the cultural boundaries of the city grew in a test game without producing any bells.

I am posting this because you can do all kinds of things with this. Me personally I am on a quest to make a better culture rating, but I am interested to see what others might want to use additional Yields for in the game.
 
This is interesting. When I added gold as a resource the only way I could get it to work was by replacing trade goods. There's obviously some stuff you can only change by changing the SDK as well.

I am quite interested in potentially adding other tradeable commodities to the game without having to replace existing yields. For example, spices and tea for asian/indonesian colonization. If someone could figure out how to squeeze additional commodities into the europe and city screens that would be cool.

I've not done any SDK work yet, might have to get my hands dirty...
 
The Python figures that out for you to a degree. It checks how many resources you have listed in the SDK and will split the screen into the required number of resources. It will do this no matter how many resources you have, although it won't always look pretty.

I removed one resource from my mod, and I didn't have to change the python files related to screens. Although I did have to go through the SDK pretty much as described above.
 
@Fortuente
I've also found references to yields in the CvCityAI.cpp and CvPlayerAI.cpp. From what I can understand, these are used by the AI to calculate value...

@PiMan
The Python figures that out for you to a degree. It checks how many resources you have listed in the SDK and will split the screen into the required number of resources. It will do this no matter how many resources you have, although it won't always look pretty.

I removed one resource from my mod, and I didn't have to change the python files related to screens. Although I did have to go through the SDK pretty much as described above.

I wish it were that simple, I've been working on a new Indigo dye yield/tradable and all of my graphics, XML, GameFont are working, but the interface is not showing at all... I suppose removing and adding yields doesn't have the same impact... I'll update with my progress on the interface adjustment.


Also, as anyone been having problems when adding a new yield at the end of the Civ4YieldInfo.xml? The game won't load at all... But it works if you and it before education...
 
Yeah, when I added Gold to the end of Civ4YieldInfo.xml the game crashed on startup. There seems to be some nasty hardcoding going on that expects a particular number of Yields in particular positions in the XML. Not very mod friendly. That's why I took the approach of replacing the Trade Goods yield. I'd like to see someone crack this because I like the idea of adding both Dye and Fine Cloth onto of what I have now.

The other issue I can think of is: Is there room for a new building (maybe called Tailor) in the city screen?
 
I don't think there is room for a new building. Some art and XML editing could make the room, but best I can tell, you can't do it as is.
 
So I've been trying to add new yields and I haven't yet found a way. I first thought the problem was with the main interface, but thats not it. This is the error i get:

Code:
Traceback (most recent call last):
  File "CvAppInterface", line 64, in preGameStart
  File "CvUtil", line 331, in initDynamicFontIcons
AttributeError: 'NoneType' object has no attribute 'getDescription'
ERR: Python function preGameStart failed, module CvAppInterface

It refers to the FontIconMap, meaning the GameFont doesn't work properly... I know the guys of WoC added new yields so I'll look into how they did it, but I think we basicly need to figure out why the game crashes when a new yield is added at the end of the CIV4YieldInfos.xml
 
When is the FontIconMap actually defined? Is it in the GameFont file itself?
 
Not sure if this helps, but PiMan pointed me to this thread on editing the GameFont.tga:

http://forums.civfanatics.com/showthread.php?t=181119

I followed this to replace the Trade Goods icon with a Gold icon, but the thread seems to imply they are adding a completely new icon. By the way these steps are tedious, but they do work:

attachment.php


Edit: Just re-reading that thread this post jumped out. I have a feeling that the order of yields in the xml corresponds to the order of yield icons in the GameFont. If so then if you add your new resource in the next available slot using the steps in the linked thread it may well work:

attachment.php
 
I did that but it didn't work as you can't add a yieldtype after education in the xml. But I've also tryed adding a yieldtype just before education in xml, replace the education icon with the new yieldtype and move the education icon one slot where youre X is marked in gamefont. The game loaded but the education icon was replace by the smiley from the OtherFontSymbols (last line), that's where the FontIconMap error comes from, thise OtherFontSymbols where probably all moved one slot to the right so one slot was missing at the end...

If you look at what the WoC team has done, they've heavily modified the gamefont:
gamefont_3oc.jpg

So I've been looking into there code and found that they have a TGAindex module as well as a nonDefault xml loading utility for modules loading...
I've been able the make the nonDefaut loading work, so now the game does starts when I added a new yieldtypes after education!!! But, it's gamefont icon doesn't show up. I'm going to add the TGAindex thing hopping it'll work...
 
It wouldn't be based on XML order, it would be based on the order used in the SDK. Probably the CyEnumsInterface.cpp
 
Weird, because my knowledge of XML tells me it couldn't be that. There is certainly nowhere in the XML that I know of where the order matters.
 
I have never seen XML do this sort of thing, but I guess I've only been working with XML for a year now, and with CivIV XML for a few months.
 
I don't understand how it's done either, but I think it's the same for the religions and corporations in BtS. There's probably a set number of yieldtypes hardcoded somewhere, maybe in the exe... That's why I think modularization is probably the best way to go
Thanks to the WoC team, most of the work is already done, we just need to adapt it to Col. Here's the wiki if someone's interested:
http://woc.dreamhosters.com/wiki/index.php?title=Main_Page
 
Back
Top Bottom