Adding new Resource Yields to Future Techs

Code:
bool bHasShrine = false;
for (int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
{
    if (GC.getBuildingInfo(iI).getGlobalReligionYield(eYield) != 0) {
        bHasShrine = getNumBuilding((BuildingTypes)iI) > 0;
        break;
    }
}
if (bHasShrine) {
    ...
}
 
If you actually have asserts enabled, then place one right inside the function (at the start) to announce that the function has started and tell you what the iChange value should be, then place another just after the iNew != iOld and have it tell you the value stored before you update it, then another after you update it saying the value after the update has happened.

About the quickest way I can think of to figure out if the problem with the code not working is here or elsewhere.
 
Sorry xienwolf, how do I know if Asserts are enabled? And read them if I do? I've taken your advice a/p your DLL tutorial, and have included FAssert messages everywhere, but I don't know where to go to read the outputs from them. Are they part of the debug process?

EF, thank-you again for writing that snippet for me, but why do we go through the first FOR/IF statement? If I'm reading it right, then it is counting through the total number of buildings, and if the building has a non-zero value for getGlobalReligionYield, it will then assign the count value to the boolean bHasShrine. AFAIC, that means I'm not reading it right.

Wouldn't we just want
Code:
void CvGame::updateGlobalReligionYield()
{
    if bHasShrine = true
    {
        CvCity::setGlobalReligionYield()
    }
}
since CvCity::setGlobalReligionYield() calculates the value of the Shrine Yields times the Religion Count, and CvGame::updateGlobalReligionYield() is called in CvPlayer when the Religion Count changes?
 
setGlobalReligionYield() should assign the calculated value to the city. In order to calculate the value you need to a) check if the city has the shrine and b) calculate the total. But you bring up a good point in that you can combine the calculation with the check.

Code:
int iValue = 0;
for each building
    iValue += building info . global religion yield * getNumBuilding()
set global religion yield ( eReligion, eYield, iValue )
 
OK, I think I need to start at the beginning again, and just put together a flow-chart in plain English to guide me through writing the code.

  1. We start in CvInfos, where the value of of the Shrine yield is read from the XML.
    • CvBuildingInfo::getGlobalReligionYields() const returns m_iGlobalReligionYields, apparently only a single value from the array listed in the XML.
    • CvReligionInfo::getGlobalReligionYieldsArray() const returns m_paiGlobalReligionYields, which is the entire Shrine yields array.
    • CvReligionInfo::getGlobalReligionYield(int i) const then returns m_paiGlobalReligionYields ? m_paiGlobalReligionYields : -1;, whose purpose I am still unclear on.

    [*]CVGameTextMgr then calls CvBuildingInfo::getGlobalReligionYields() to write the Shrine yield array values into the Civilopedia.

    Now, from here on, I'm guessing:

    [*]CvPlayer needs to be where the game is told to update the value of the Shrine yield being expressed in a city if the global count of that religion changes. That's done under CvPlayer::changeHasReligionCount.
    [*]The update request in CvPlayer needs to refer back to CvGame (just like GC.getGameINLINE().updateBuildingCommerce())
    [*]CvGame::updateBuildingCommerce() just routes back to CvPlayer again, but a different function that now says to loop over every city and run pLoopCity->updateBuildingCommerce() for each one. I assume that this is referring to CvCity::updateBuildingCommerce().
    • So, I need to write a GC.getGameINLINE().updateGlobalReligionYield() to point CvPlayer::changeHasReligionCount to CvGame, where I then direct it back to CvPlayer::updateGlobalReligionYield(), to loop over every city. Would it be good (or even OK) to at this point tell the game to only loop over holy cities?
    [*]Now that I've told the game to loop over (holy) cities to update the Shrine yield outputs if the global religion count changes, I can put together some functions in CvCity.
    [*]So, I need first a function to call the value of the Shrine yield listed in the XML, done by calling either CvBuildingInfo::getGlobalReligionYields(), CvReligionInfo::getGlobalReligionYieldsArray(), or CvReligionInfo::getGlobalReligionYield(), I don't know which.
    [*]I then build another function to call the value of the global religion count.
    [*]Then, a third function to multitply those first two together and supply that value to changeBaseYieldRate
    [*]And now, a forth function, CvCity::updateGlobalReligionYield,, calls the third function, forcing it to recalculate what the effect to changeBaseYieldRate is, and this forth function is what gets called by the (holy) city looping function in CvPlayer.


OK, what'd I do wrong in the above list?
 
Step 1.

# CvBuildingInfo::getGlobalReligionYields(int iIndex) const returns m_paiGlobalReligionYields[iIndex], a single value from the array listed in the XML.
# CvReligionInfo::getGlobalReligionYieldsArray() const returns m_paiGlobalReligionYields, which is the entire Shrine yields array.

Now step 1 is correct, assuming step 2 is correct. Which coincidentally makes it a copy of step 3.
 
xienwolf, your correction confuses me...

The entries in CvInfos are exact copies of what is in there already for GlobalReligionCommerce, the existing variables and functions that call how much :culture: the Shrines give for each city that has their individual religion. Those entries look like this:

Code:
m_iGlobalReligionCommerce(0),

int CvBuildingInfo::getGlobalReligionCommerce() const
{
	return m_iGlobalReligionCommerce;
}

	stream->Read(&m_iGlobalReligionCommerce);

	stream->Write(m_iGlobalReligionCommerce);

	pXML->GetChildXmlValByName(szTextVal, "GlobalReligionCommerce");
	m_iGlobalReligionCommerce = pXML->FindInInfoClass(szTextVal);

m_paiGlobalReligionCommerce(NULL),

	SAFE_DELETE_ARRAY(m_paiGlobalReligionCommerce);

int CvReligionInfo::getGlobalReligionCommerce(int i) const
{
	FAssertMsg(i < NUM_COMMERCE_TYPES, "Index out of bounds");
	FAssertMsg(i > -1, "Index out of bounds");
	return m_paiGlobalReligionCommerce ? m_paiGlobalReligionCommerce[i] : -1; 
}

int* CvReligionInfo::getGlobalReligionCommerceArray() const
{
	return m_paiGlobalReligionCommerce;
}

	if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"GlobalReligionCommerces"))
	{
		pXML->SetCommerce(&m_paiGlobalReligionCommerce);
		gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
	}
	else
	{
		pXML->InitList(&m_paiGlobalReligionCommerce, NUM_COMMERCE_TYPES);
	}
(I deleted all the in-betweens and place-marking data, because I know you both know this stuff so well you don't really need it anymore.)

So, my entries for GlobalReligionYields were exactly copied, because I figured, "If Religion Commerce needs both types of entries, then I will too."

Was I wrong in that assumption?
 
I didn't catch that the first part was in a different area. Building versus religion. Sorry about that. As for your statement of what it does, I am reasonably certain that you specify a religion in the XML, so the integer it gives you is WHICH religion it offers yields for/from.
 
Ok, looking at all the rest:

Yes, start with CvPlayer::changeHasReligionCount, just adding a single line to call a new function in CvGame, which will look just like the commerce one and call to CvPlayer (again, new function looking like commerce) which calls to CvCity. I do not advise that you loop over only Holy Cities, but if you want this tag to only modify holy cities, as you will be off by a count of at least 1 in all cases (the religion is placed in the city before it becomes a holy city, so it would at the least fail to count itself)

Important thing to note: CvCity is updating for POTENTIAL buildings, it doesn't care yet at this point (CvCity::updateBuildingCommerce()) if the building actually exists here or not, because for all we know that building WILL be here in 20 turns, and we'll need the right number then.

Along the way, you'll be writing up some other functions which are called in CvCity, like int CvCity::getBuildingCommerceByBuilding(CommerceTypes eIndex, BuildingTypes eBuilding) const, you won't have to duplicate EVERYTHING in this function, as some of it deals with other building commerce modifiers.


So, at this point, we have the information in the city about how many religions are out there, and what our modifiers should be for each building due to that count.


Now, you can save yourself some work if you figure out a way to link in with CvCity::changeBuildingYieldChange. If you link into there, then buildings will update for you nicely. But it might complicate your life even more doing so as you have to REMOVE the modifier in some cases. So it might just be worthwhile to watch how that function works when making your yields work.
 
Could you tell me please, what the exact purpose is of the statement:
Code:
int CvReligionInfo::getGlobalReligionCommerce(int i) const
{
	FAssertMsg(i < NUM_COMMERCE_TYPES, "Index out of bounds");
	FAssertMsg(i > -1, "Index out of bounds");
	return m_paiGlobalReligionCommerce ? m_paiGlobalReligionCommerce[i] : -1; 
}

I don't know what this function does, and I really think I have to. How does it work? I know (think) the "?" is an IF/ELSE shorthand, but why is it comparing m_paiGlobalReligionCommerce to itself? And how does the program get away with that anyways?

... :confused: :crazyeye:
Thanks!

EDIT:
And I think I can use the following statement to calculate the changeBaseYieldRate:
Code:
iCommerce += (GC.getReligionInfo((ReligionTypes)(GC.getBuildingInfo(eBuilding).getGlobalReligionCommerce())).getGlobalReligionCommerce(eIndex) * GC.getGameINLINE().countReligionLevels((ReligionTypes)(GC.getBuildingInfo(eBuilding).getGlobalReligionCommerce()))) * getNumActiveBuilding(eBuilding);
That line, if I'm reading it right, gets the individual Commerce amounts from the array listed for each building for each religion, and then multiplies that number by the number of cities with that religion. Unfortunately, that only brings me to GC.getGameINLINE().countReligionLevels((ReligionTypes). I don't have any idea what the last two bits, (GC.getBuildingInfo(eBuilding).getGlobalReligionCommerce()))) * getNumActiveBuilding(eBuilding), are there for...
 
The ? : operator is called a ternary operator, and it is indeed an if/else shorthand for expressions. With if/else you can simply do things without having a result, but ? : requires that the two things on either side of the : be expressions that return the same type. The part on the left of the ? must be convertible to a bool value.

Code:
int x = ...
bool xEqualsFive = (x == 5) ? true : false;

int x, y = ...
int max = (x > y) ? x : y;

m_paiGlobalReligionCommerce is a pointer, and converting a pointer to a bool evalutes to true if the pointer is not NULL or false if it is NULL. So this statement is safely returning the value of one element of the array. If the array is NULL, it returns -1 instead of using [] on the pointer which would cause a crash to desktop.

As for the latter equation, break it down:

Code:
iCommerce += 
    (GC.getReligionInfo(
        (ReligionTypes)(GC.getBuildingInfo(eBuilding).getGlobalReligionCommerce()))  // lookup the religion that this building is a shrine for
    .getGlobalReligionCommerce(eIndex)   // get that religion's global religion commerce (eIndex'th type)
    *
    GC.getGameINLINE().countReligionLevels(    // count the cities with that religion
        (ReligionTypes)(GC.getBuildingInfo(eBuilding).getGlobalReligionCommerce())))  // lookup the religion again
    *
    getNumActiveBuilding(eBuilding);    // and multiply it by the number of non-obsolete buildings

Here's where local variables make understanding code easier:

Code:
ReligionTypes eReligion =  (ReligionTypes)GC.getBuildingInfo(eBuilding).getGlobalReligionCommerce();
int iGlobalReligionCommerce = GC.getReligionInfo(eReligion).getGlobalReligionCommerce(eIndex);
int iReligionSpread = GC.getGameINLINE().countReligionLevels(eReligion);
int iNumBuildings = getNumActiveBuilding(eBuilding);

iCommerce += iGlobalReligionCommerce * iReligionSpread * iNumBuildings;
 
Guys, how does this look for Steps 7-10 in CvCity.cpp?

Code:
int CvCity::getGlobalReligionYield(YieldTypes eIndex) const												 
{
	FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
	FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
	return m_aiGlobalReligionYield[eIndex];
}


int CvCity::getGlobalReligionYieldByReligion(YieldTypes eIndex, ReligionTypes eReligion) const
{
	int iYield;

	FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
	FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
	FAssertMsg(eReligion >= 0, "eReligion expected to be >= 0");
	FAssertMsg(eReligion < GC.getNumReligionInfos(), "GC.getNumReligionInfos expected to be >= 0");

	iYield = 0;

	if bHasShrine = true
	{
			iYield += GC.getReligionInfo(eReligion).getGlobalReligionYield(eIndex);
	}

	return iYield;
}


void CvCity::updateGlobalReligionYield(YieldTypes eIndex)
{
	int iOldYield = getGlobalReligionYield(eIndex);
	int iNewYield = 0;

	for (int iI = 0; iI < GC.getNumReligionInfos(); iI++)
	{
		iNewYield += getGlobalReligionYieldByReligion(eIndex, (ReligionTypes)iI);
	}

	if (iOldYield != iNewYield)
	{
		m_aiGlobalReligionYield[eIndex] = iNewYield;
		FAssert(getGlobalReligionYield(eIndex) >= 0);

		changeBaseYieldRate(eIndex, (iNewYield - iOldYield));
	}
}


void CvCity::updateGlobalReligionYield()
{
	int iI;

	for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
	{
		updateGlobalReligionYield((YieldTypes)iI);
	}
}

I think it's not going to work, because right now int CvCity::getGlobalReligionYieldByReligion() is only checking to see whether or not the city has a Shrine in it, and I need to actually check the GC.getReligionInfo(eReligion).getGlobalReligionYield(eIndex) for each Shrine. Would I have to tell the game to actually parse through the entire building list, or is there a way to check only the Shrines do you know? (It's probably easier to just parse through the entire building list. I'm only just realizing now just how much work the processor is doing in the background, even for the simplest (sic) things! So I can probably spare the CPU time...)
 
Changed int CvCity::getGlobalReligionYieldByReligion() to this:
Code:
	if (isHasReligion(eReligion))
	{
		if bHasShrine = true
		{
				iYield += GC.getReligionInfo(eReligion).getGlobalReligionYield(eIndex) * GC.getGameINLINE().countReligionLevels(eReligion);;
		}
	}
	return iYield;
Better? Worse? Gonna compile now and find out!
 
That looks good. Yes, you'll need to loop through all buildings to find the shrines (those with global religion yield != NO_RELIGION).

I doubt performance will be an issue here as religions don't spread that often. If you get anxious about it you could add an eReligion to the functions without it and calculate just the religion that was called for. You'd have to change the array in CvCity to be a two-dimensional array with ReligionTypes and YieldTypes as its indices.
 
Heh, you gotta assign a value to bHasShrine and fix your syntax:

Code:
if (bHasShrin)
{
    ...
}
 
Do you mean that a holy city without the shrine gains the yields? Or instead are you just not getting the yields to show for the shrine in the building list on the city screen?
 
Back
Top Bottom