[SDK] Coding a tag to use buildingclass

ripple01

Emperor
Joined
Mar 7, 2006
Messages
1,254
Location
New York City
Hello all,

I have been working on a project to update/fix some bugs/issues with Aussie Lurker's Civic Infos Plus Mod.

I am working currently with the BuildingCommerceYield and BuildingCommerceChanges tags. I fixed the first issue, which is that the CommerceChanges tag did not accept a 4th commerce. I'm close to fixing the second issue, which is that the tags only accepted buildings, not buildingclasses, meaning it would not support unique buildings. I have the XML tag working correctly, and I have the gametext working correctly, I am having an issue with the data.

So for example, the tag for BuildingCommerceYield should read +2:hammers: from Market, Bank is reading +2:hammers: from Walls, Walls. (If I start a game as the Celts and look at the civic screen, is say Dun, Dun, so I know the GameText is working correctly.)

Could someone give me any general tips on when to use buildings and when to use buildingclasses? I am specifically looking at CvCity.cpp and CvPlayer.cpp. I can post the code I have if necessary.

Thanks much!
 
OK, well in my infinite wisdom, I realized that if the civilopedia is not showing the correct values, it is something in the CvInfos or CvGameTextManager. I have solved 3/4s of this issue, now I have the tag displaying the correct buildings, but am having a little trouble in figuring out how to get the unique building to show in the civilopedia and civic screen.


This is the original code which is working:

Code:
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
	{
		iLast = 0;

		for (iJ = 0; iJ < GC.getNumBuildingClassInfos(); iJ++)
		{
			if (GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI) != 0)
			{
				szFirstBuffer.Format(L"%s%s", NEWLINE, gDLL->getText("TXT_KEY_CIVIC_BUILDING_YIELD_CHANGE", GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI), GC.getYieldInfo((YieldTypes)iI).getChar()).c_str());
				CvWString szBuilding;
				szBuilding.Format(L"<link=literal>%s</link>", GC.getBuildingClassInfo((BuildingClassTypes)iJ).getDescription());
				setListHelp(szHelpText, szFirstBuffer, szBuilding, L", ", (GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI) != iLast));
				iLast = GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI);
			}
		}
	}


I've coded this to try and display the unique building, but it is not working, and the civilopedia description is gone altogether. Can someone look at this and tell me where I'm going wrong?

Code:
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
	{
		iLast = 0;

		for (iJ = 0; iJ < GC.getNumBuildingClassInfos(); iJ++)
		{
			if (GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI) != 0)
			{
				if (bPlayerContext && NO_PLAYER != GC.getGameINLINE().getActivePlayer())
				{
					BuildingTypes eBuilding = (BuildingTypes)GC.getCivilizationInfo(GC.getGameINLINE().getActiveCivilizationType()).getCivilizationBuildings(iJ);
					if (NO_BUILDING != eBuilding)
					{
						szFirstBuffer.Format(L"%s%s", NEWLINE, gDLL->getText("TXT_KEY_CIVIC_BUILDING_YIELD_CHANGE", GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI), GC.getYieldInfo((YieldTypes)iI).getChar()).c_str());
						CvWString szBuilding;
						szBuilding.Format(L"<link=literal>%s</link>", GC.getBuildingInfo((BuildingTypes)eBuilding).getDescription());
						setListHelp(szHelpText, szFirstBuffer, szBuilding, L", ", (GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI) != iLast));
						iLast = GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI);
					}
					else
					{
						szFirstBuffer.Format(L"%s%s", NEWLINE, gDLL->getText("TXT_KEY_CIVIC_BUILDING_YIELD_CHANGE", GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI), GC.getYieldInfo((YieldTypes)iI).getChar()).c_str());
						CvWString szBuilding;
						szBuilding.Format(L"<link=literal>%s</link>", GC.getBuildingClassInfo((BuildingClassTypes)iJ).getDescription());
						setListHelp(szHelpText, szFirstBuffer, szBuilding, L", ", (GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI) != iLast));
						iLast = GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI);
					}
				}
			}
		}
	}
 
OK, now this is strange, it seems the 2nd block of code IS working on the civic screen exactly as intended, but not showing up at all in the civilopedia! Anyone know why this may be?


OK, well in my infinite wisdom, I realized that if the civilopedia is not showing the correct values, it is something in the CvInfos or CvGameTextManager. I have solved 3/4s of this issue, now I have the tag displaying the correct buildings, but am having a little trouble in figuring out how to get the unique building to show in the civilopedia and civic screen.


This is the original code which is working:

Code:
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
	{
		iLast = 0;

		for (iJ = 0; iJ < GC.getNumBuildingClassInfos(); iJ++)
		{
			if (GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI) != 0)
			{
				szFirstBuffer.Format(L"%s%s", NEWLINE, gDLL->getText("TXT_KEY_CIVIC_BUILDING_YIELD_CHANGE", GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI), GC.getYieldInfo((YieldTypes)iI).getChar()).c_str());
				CvWString szBuilding;
				szBuilding.Format(L"<link=literal>%s</link>", GC.getBuildingClassInfo((BuildingClassTypes)iJ).getDescription());
				setListHelp(szHelpText, szFirstBuffer, szBuilding, L", ", (GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI) != iLast));
				iLast = GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI);
			}
		}
	}


I've coded this to try and display the unique building, but it is not working, and the civilopedia description is gone altogether. Can someone look at this and tell me where I'm going wrong?

Code:
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
	{
		iLast = 0;

		for (iJ = 0; iJ < GC.getNumBuildingClassInfos(); iJ++)
		{
			if (GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI) != 0)
			{
				if (bPlayerContext && NO_PLAYER != GC.getGameINLINE().getActivePlayer())
				{
					BuildingTypes eBuilding = (BuildingTypes)GC.getCivilizationInfo(GC.getGameINLINE().getActiveCivilizationType()).getCivilizationBuildings(iJ);
					if (NO_BUILDING != eBuilding)
					{
						szFirstBuffer.Format(L"%s%s", NEWLINE, gDLL->getText("TXT_KEY_CIVIC_BUILDING_YIELD_CHANGE", GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI), GC.getYieldInfo((YieldTypes)iI).getChar()).c_str());
						CvWString szBuilding;
						szBuilding.Format(L"<link=literal>%s</link>", GC.getBuildingInfo((BuildingTypes)eBuilding).getDescription());
						setListHelp(szHelpText, szFirstBuffer, szBuilding, L", ", (GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI) != iLast));
						iLast = GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI);
					}
					else
					{
						szFirstBuffer.Format(L"%s%s", NEWLINE, gDLL->getText("TXT_KEY_CIVIC_BUILDING_YIELD_CHANGE", GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI), GC.getYieldInfo((YieldTypes)iI).getChar()).c_str());
						CvWString szBuilding;
						szBuilding.Format(L"<link=literal>%s</link>", GC.getBuildingClassInfo((BuildingClassTypes)iJ).getDescription());
						setListHelp(szHelpText, szFirstBuffer, szBuilding, L", ", (GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI) != iLast));
						iLast = GC.getCivicInfo(eCivic).getBuildingYieldChanges(iJ, iI);
					}
				}
			}
		}
	}
 
Probably because of this line:

Code:
if (bPlayerContext && NO_PLAYER != GC.getGameINLINE().getActivePlayer())

All of your code to generate the text is underneath this if statement, so if the statement isn't true, no text is seen.
 
Ah, of course. Because when opening up the civilopedia before starting a game, there is no active player assigned yet. So the else statement should in fact go under that line of code, and not where it is currently. Got it working, thanks!

Probably because of this line:

Code:
if (bPlayerContext && NO_PLAYER != GC.getGameINLINE().getActivePlayer())

All of your code to generate the text is underneath this if statement, so if the statement isn't true, no text is seen.
 
OK, well I've got the XML tag and the GameText working for real this time, and I am running into an issue I am hoping someone could help me with. I'm getting a crash in the getBuildingCommerceChange function in CvInfos.cpp:

Code:
int CvCivicInfo::getBuildingCommerceChanges(int i, int j) const
{
	FAssertMsg(i < GC.getNumBuildingInfos(), "Index out of bounds");
	FAssertMsg(i > -1, "Index out of bounds");
	FAssertMsg(j < NUM_COMMERCE_TYPES, "Index out of bounds");
	FAssertMsg(j > -1, "Index out of bounds");
	return m_ppiBuildingCommerceChanges ? m_ppiBuildingCommerceChanges[i][j] : -1;
	//return m_ppiBuildingCommerceChanges[i][j];
}


It is being called from the following code in CvCity:


Code:
void CvCity::updateBuildingCommerceChange(CivicTypes eCivic, int iChange)
{
    int iBuildingCommerceChange;
	int iI, iJ;

	for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
	{
		iBuildingCommerceChange = 0;

		for (iJ = 0; iJ < GC.getNumBuildingInfos(); iJ++)

        {
			iBuildingCommerceChange = getNumActiveBuilding((BuildingTypes)iJ) * GC.getCivicInfo(eCivic).getBuildingCommerceChanges(((BuildingTypes)iJ), (CommerceTypes)iI) * iChange;

            changeBuildingCommerceChange((BuildingClassTypes)GC.getBuildingInfo((BuildingTypes)iJ).getBuildingClassType(), (CommerceTypes)iI, iBuildingCommerceChange);
        }
	}

	AI_setAssignWorkDirty(true);

	if (getTeam() == GC.getGameINLINE().getActiveTeam())
	{
		setInfoDirty(true);
	}
}

Now the changeBuildingCommerceChange function accepts BuildingClass Types as a parameter, which seems strange to me. I would expect it to accept BuildingTypes as it needs to change buildings of all types, including UB's. But it is a Firaxis-programmed function, so I don't want to go messing with it.

What is strange is well is that I am using identical code to this for BuildingYieldsChanges and it seems to work without a hitch. I am not sure why Commerces would be different.

According to VS2010, the values being passed to iI and iJ respectively are 0 and 228. 228 is the number of entries in Civ4BuildingClassInfos.xml, but that loop is set up for BuildingInfos and getBuildingCommerceChanges set up to take BuildingTypes.

I am a fairly novice programmer, but am starting to wrap my head around some of the basics of C++. So I apologize if my terminology is confusing or not correct. I am trying to learn, and am doing my best to try and figure stuff out on my own before coming to the forums for help.
 
Now the changeBuildingCommerceChange function accepts BuildingClass Types as a parameter, which seems strange to me. I would expect it to accept BuildingTypes as it needs to change buildings of all types, including UB's.

Exactly. It needs BuildingClass rather than BuildingType so that it can handle UBs. UBs have different Types, but share the same Class.

What is the error you're getting when it crashes?
 
Exactly. It needs BuildingClass rather than BuildingType so that it can handle UBs. UBs have different Types, but share the same Class.

What is the error you're getting when it crashes?

This is the error I'm getting:

First-chance exception at 0x052ef938 (CvGameCoreDLL.dll) in Civ4BeyondSword.exe: 0xC0000005: Access violation reading location 0xabababab.
Unhandled exception at 0x052ef938 (CvGameCoreDLL.dll) in Civ4BeyondSword.exe: 0xC0000005: Access violation reading location 0xabababab.

So I guess where I am confused is that I'm thinking changebuildingcommercechange is the function that changes building commerce for ALL buildings, so where is a UB identified and dealt with appropriately in the code?

For example, my XML tag accepts a BuildingClass (for example, BUILDINGCLASS_BARRACKS). So in CvInfos, I am setting everything up by BuildingClass.

For example:

Code:
if (m_ppiBuildingCommerceChanges != NULL)
	{
		for(i=0;i<GC.getNumBuildingClassInfos();i++)
		{
			SAFE_DELETE_ARRAY(m_ppiBuildingCommerceChanges[i]);
		}
		SAFE_DELETE_ARRAY(m_ppiBuildingCommerceChanges);
	}
	m_ppiBuildingCommerceChanges = new int*[GC.getNumBuildingClassInfos()];
	for(i=0;i<GC.getNumBuildingClassInfos();i++)
	{
		m_ppiBuildingCommerceChanges[i]  = new int[NUM_COMMERCE_TYPES];
		stream->Read(NUM_COMMERCE_TYPES, m_ppiBuildingCommerceChanges[i]);
	}

So should my code in CvCity (like in the above post) and CvPlayer be set up to use BuildingClass as well? And where does the code determine "OK, this player is the Zulus, we need to make our changes to the Ikhanda instead of the Barracks?

If any of this is unclear, please let me know.
 
So I guess where I am confused is that I'm thinking changebuildingcommercechange is the function that changes building commerce for ALL buildings, so where is a UB identified and dealt with appropriately in the code?

...

So should my code in CvCity (like in the above post) and CvPlayer be set up to use BuildingClass as well? And where does the code determine "OK, this player is the Zulus, we need to make our changes to the Ikhanda instead of the Barracks?

You're dealing with code that I've never worked with before, so I'm no expert on the matter. But it looks like you get to avoid trying to extract the civ-specific building when submitting info to changeBuildingCommerceChange. It doesnt care what civ you are, all it cares about is whether or not any buildings of that class are present in the city. So if you have a city that happens to have two different versions of the same buildingclass (perhaps via a civ specific ability for example), then both will be affected. But since the game already has code elsewhere to restrict which buildings can be built, this particular function doesn't need to concern itself with that sort of distraction.

Looking back at our code, I think you might need to change getBuildingCommerceChanges so that it is accepting BuildingClassType rather than BuildingType.
 
OK, well, I made my changes, and I have gotten rid of the crash, but the tag is still not working. I have a hunch that something is wrong with this block of code in CvCity: processBuilding

Code:
// < Civic Infos Plus Start >
		BuildingClassTypes eBuildingClass;
		eBuildingClass = ((BuildingClassTypes)(GC.getBuildingInfo(eBuilding).getBuildingClassType()));
		// < Civic Infos Plus End   >

		for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
		{
			changeSeaPlotYield(((YieldTypes)iI), (GC.getBuildingInfo(eBuilding).getSeaPlotYieldChange(iI) * iChange));
			changeRiverPlotYield(((YieldTypes)iI), (GC.getBuildingInfo(eBuilding).getRiverPlotYieldChange(iI) * iChange));
			changeBaseYieldRate(((YieldTypes)iI), ((GC.getBuildingInfo(eBuilding).getYieldChange(iI) + getBuildingYieldChange((BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType(), (YieldTypes)iI))* iChange));
			changeYieldRateModifier(((YieldTypes)iI), (GC.getBuildingInfo(eBuilding).getYieldModifier(iI) * iChange));
			changePowerYieldRateModifier(((YieldTypes)iI), (GC.getBuildingInfo(eBuilding).getPowerYieldModifier(iI) * iChange));
			// < Civic Infos Plus Start >
			changeBuildingYieldChange((BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType(), ((YieldTypes)iI), GET_PLAYER((PlayerTypes)iI).getBuildingYieldChange(eBuildingClass, ((YieldTypes)iI)));
			// < Civic Infos Plus End   >
		}

Anyone see any problems with this code?
 
Code:
changeBuildingYieldChange((BuildingClassTypes)GC.getBuildingInfo(eBuilding).getBuildingClassType(), ((YieldTypes)iI), GET_PLAYER((PlayerTypes)iI).getBuildingYieldChange(eBuildingClass, ((YieldTypes)iI)));

I dont quite understand what's happening here. You have some sort of player Trait that modifies building yields?
 
Ripple01, I was trying to do the same with Civicinfosplus but gave up. Instead I used New Dawn tags by Afforess. I've got it pretty much working, no Game text yet and still have iterator is too big for array issue. Also there is no AI yet. Its only for commerces but it'd be easy to make a tag for yields too I reckon.

I've attach the Sources. There's a lot of crap floating about those files so search for:

CMEDIT: Building Class Commerce Change

please note: Original Code by Afforess (i just changed some small things and extracted it)

How did you solve iterator issue?
 

Attachments

Back
Top Bottom