Adding new Resource Yields to Future Techs

Well, the percentage thing was just an accident on my part-hope to fix that when I get a few minutes spare :).

Aussie.
 
Thanks EF. I think I get it...

Because then the added code @ line 4945 starts with (the correct terms is "calls", right?) setYieldChangeHelp, and that string is of course fully constructed through void CvGameTextMgr::setYieldChangeHelp and void CvGameTextMgr::buildYieldChangeString (which links into line 142 of CGTM.h), what you posted is the only thing I need to add in CGTM.cpp, leaving me just to construct a function for getFutureYieldArray in CvInfos.

Did I get that right? :confused:

Aussie, I want to thank-you again for trying to spare me this. I need to learn it all anyways, which is why I'm still trying... :crazyeye:
 
Yes, all that sounds correct. CvGTM::getYieldChangeHelp() is a function that you call to use it. I don't think that function uses a buildYieldChangeString() function, but I'm not looking at the code right now and it doesn't matter anyway. You are right that you should just need to add that one line to CvGTM.

Then you need to change your CvTechInfo to use an array for storage (m_aiBlahBlah) and access (getFutureYieldChangeArray() and getFutureYieldChange(eIndex)).

It does get easier with practice. :)
 
Easier...? What is this word of which you speak? :crazyeye::lol:

No, I am getting better. Trying to write the second- & tertiary functions for setYieldChangeHelp last night is what made me realize that the entire code is compartmentalized. I can call the same function multiple times from mulitiple places, and it won't cause any kind of cross-ref errors, cause the system takes care of nesting the called function within the root function... yeesh, I can be thicke.

New query: I've found what appears to be a typo from the original programmers. Line #142 of CvGTM.h refers to "int iTieldType". I'm positive this should be "int iYieldType" instead. I've double-checked, and this one line is the only place within the entire SDK for either BtS, Warlords, or Vanilla, that refers to iTield. What confuses me is: how could the SDK compile with such an error?
 
Interesting typo. I would suspect that when declaring it in the *.h files, all that is REALLY important is what kind of variable is used in that spot. What you call it isn't important at all in the header, just in the definition (the first line of the *.cpp for starting the function)
 
I can call the same function multiple times from mulitiple places, and it won't cause any kind of cross-ref errors, cause the system takes care of nesting the called function within the root function... yeesh, I can be thicke.

It gets better. You can call the same function from itself! This is called recursion. Each time you call a function, it gets an entirely new set of parameter values to use. There are ways for each call to communicate to the other calls, but that takes work and is not the normal condition.

I would suspect that when declaring it in the *.h files, all that is REALLY important is what kind of variable is used in that spot.

Correct. In fact, you don't even need to supply parameter names in the header (*.h) files, but it's nice for documentation. Note that this may not be true of C++, but I think it is. In C you definitely do not need to name them. Given the typo, I would say C++ is the same and that the compiler completely ignores the parameter names in the header files.
 
You can what??? I would've thought that a recursive function would cause an endless loop... :crazyeye:

OK, I got the new array coding into CGTM.h & .cpp, as well as CyInfoInterface1.cpp & CvInfos.h. I'm having trouble with CvInfos.cpp though. Here is what I have so far. I've included the surrounding standard code to help identify where evrything has been placed. (changes coloured):

Code:
m_piPrereqOrTechs(NULL),
m_piPrereqAndTechs(NULL), 
[COLOR="DarkOrange"]m_piFutureYieldArray(NULL);[/COLOR]
m_pbCommerceFlexible(NULL), 
m_pbTerrainTrade(NULL)
Code:
int CvTechInfo::getPrereqAndTechs(int i) const
{
	return m_piPrereqAndTechs ? m_piPrereqAndTechs[i] : -1;
}

[COLOR="darkorange"]int CvTechInfo::getFutureYieldArray(int i) const	
{
	return m_piFutureYieldArray ? m_piFutureYieldArray[i] : -1;
}[/COLOR]

bool CvTechInfo::isCommerceFlexible(int i) const
Code:
	SAFE_DELETE_ARRAY(m_piPrereqAndTechs);
	m_piPrereqAndTechs = new int[GC.getNUM_AND_TECH_PREREQS()];
	stream->Read(GC.getNUM_AND_TECH_PREREQS(), m_piPrereqAndTechs);

[COLOR="darkorange"]	SAFE_DELETE_ARRAY(m_piFutureYieldArray);
	m_piFutureYieldArray = new int[GC.getNUM_AND_TECH_PREREQS()];
	stream->Read(GC.getNUM_AND_TECH_PREREQS(), m_piFutureYieldArray);[/COLOR]

	SAFE_DELETE_ARRAY(m_pbCommerceFlexible);
Code:
	stream->Write(GC.getNUM_AND_TECH_PREREQS(), m_piPrereqAndTechs);
[COLOR="darkorange"]	stream->Write(GC.getNUM_AND_TECH_PREREQS(), m_piFutureYieldArray);[/COLOR]
	stream->Write(NUM_COMMERCE_TYPES, m_pbCommerceFlexible);
Code:
	else
	{
		pXML->InitList(&m_pbCommerceFlexible, NUM_COMMERCE_TYPES);
	}

[COLOR="darkorange"]
	pXML->SetVariableListTagPair(&m_piFutureYieldArray, "FutureYields", sizeof(GC.getDomainInfo((DomainTypes)0)), NUM_DOMAIN_TYPES);[/COLOR]
	pXML->SetVariableListTagPair(&m_piDomainExtraMoves, "DomainExtraMoves", sizeof(GC.getDomainInfo((DomainTypes)0)), NUM_DOMAIN_TYPES);
Specifically I'm confused about the last three entries. I'm not all sure about them.
 
You can what??? I would've thought that a recursive function would cause an endless loop... :crazyeye:

If you have no way for the function to stop calling itself, it will. So every recursive function has two parts: the recursive call and the termination test. A classic example of a recursive function is factorial(n) (written n! in mathematics):

n! = n * (n - 1) * ... * 1


Code:
def factorial(n):
    if n <= 1:
        # 1! and 0! are both 1, anything negative just gets 1 too
        return 1
    else:
        return n * factorial(n - 1)

Each time factorial(n) is called, a new variable to hold the value of n passed in is created along with a new set of local variables (of which n is one). This function doesn't use any other local variables, but you can have as many as you like, and each call gets a different set.

Specifically I'm confused about the last three entries. I'm not all sure about them.

Yes, you need to use constants based on NUM_YEILD_TYPES for the size of the array.

First, however, you need two accessors on CvTechInfo: one to return the array for CvGTM and another to return the value for a single yield type.

Code:
int CvTechInfo::getFutureYieldArray() const	
{
	return m_piFutureYieldArray;
}

int CvTechInfo::getFutureYield(int i) const	
{
	FAssertMsg(i < NUM_YIELD_TYPES, "Index out of bounds");
	FAssertMsg(i > -1, "Index out of bounds");
	return m_piFutureYieldArray ? m_piFutureYieldArray[eIndex] : -1;
}

Code:
	SAFE_DELETE_ARRAY(m_piFutureYieldArray);
	m_piFutureYieldArray = new int[NUM_YIELD_TYPES];
	stream->Read(NUM_YIELD_TYPES, m_piFutureYieldArray);

Code:
	stream->Write(NUM_YIELD_TYPES, m_piFutureYieldArray);

For reading from XML, I copied the example for CvSpecialistInfo's YieldChange which is exactly what you need I think. Note: This is the last time I'll recommend dropping "Future" and calling the addition "YieldChange" to match all the other CvFooInfo objects. :mischief: :lol:

Code:
	if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(), "FutureYields"))
	{
		pXML->SetYields(&m_piFutureYieldArray);
		gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
	}
	else
	{
		pXML->InitList(&m_piFutureYieldArray, NUM_YIELD_TYPES);
	}
 
OK, I just made those changes. Thanks EF.

I just renamed all the "Future"s to "Extra" as you had previously suggested, but I now think that wasn't your point. Going back to how C++ is fully compartmentalized, I can name my additions using exact same string that the game already uses to track Yields, and save myself the effort of trying to implement my additions seperately in CvCity, can't I? Given that, should I rename any mention of "ExtraYieldArray" to simply "YieldChange"? Is that the correct term (I'll go check now...)?
 
Take a look at CvSpecialistInfo. It has a YieldChange array that works exactly like you want: each specialist adds yields to the city its in. The tech will do the same thing to all the cities for a player.

  • Array field: m_piYieldChange
  • Array accessor: getYieldChangeArray()
  • Value accessor: getYieldChange(int i)
CvBuildingInfo has the same concept.

CvGTM will use the array accessor to pass the array to setYieldChangeHelp().

Now, I didn't quite understand what you meant about CvCity. CvCity doesn't need a new array as CvTeam will be calling CvCity::changeBaseYieldRate() as each tech is acquired. CvTeam will call getYieldChange(i) for each yield in a loop. In fact, CvCity won't need to be change at all.

Using YieldChange instead of Future/ExtraYield has two immediate benefits: other people reading your code will know exactly what it does since it matches other CvInfo classes, and you could have copied all this code from one of those other classes originally (that's mostly what I've been doing in this thread) without having to change anything.

It would only have been a problem if CvTechInfo itself already had a field of the same name, which it doesn't. It has the YieldModifier (m_piYieldModifier) percentage change, but that's different and not a problem. I'd put your changes right above the YieldModifier lines since they are related, but that's not necessary.
 
I don't follow this last post. If you mean that you could have done all this via Python, that's not correct. This definitely required an SDK change. If you only meant that you'll be able to expose these to Python, then yes of course you can.

That does bring up a good point, however. BUG's Raw Yields display will not display these bonuses correctly. They will probably show up in the Modifiers line item due to how BUG calculates the values: getYieldRate() - sum(known sources of yields). You'll have to add techs as a new source of yields to BUG's RawYields.py module if you want to show these as a separate line item. That part would be quite easy, and I'm much more familiar with that code. ;)

Now that I think of it, once you get this working as-is, we should probably have CvTeam track the total YieldChanges for all known techs. This will make the changes we still need to make to acquireCity easier. Get this working first, however, or you'll go nuts. :crazyeye:
 
I was referring to how in Vanilla Civ, the additional Food & Hammers (Commerce too, I think, it's been so long since I played without BUG) provided by Specialists arent defined anywhere. You get the extra output, but figuring out why from the readouts at the top of the city screen is impossible, you've got to count everything out.

But in BUG (have I mentioned I love BUG?) you have that lovely Raw Yields display window on the left, and it shows what comes from Specialists. And it's all through Python. So, what I meant was how, if I'm going to use an exact imitation of the Specialist's code, I down' have to fiddle with anything else in the SDK to have the extra Yields output show in the Raw Yields display, I can do it through Python. Which I think I'm a little better at. And of course, you know that code inside-out, upside-down, & side-wise.

New question: I tried compiling, and received this error from CGTM.cpp:

CvGameTextMgr.cpp|4942|error C2660: 'CvTechInfo::getYieldChange' : function does not take 0 arguments|
Here is my code from CGTM.cpp:
Code:
 	setYieldChangeHelp(szBuffer, L"", L"", gDLL->getText("TXT_KEY_TECH_FUTURE_YIELD_CHANGE").GetCString(), GC.getTechInfo(eTech).getYieldChange(), false, true);
 
Since you are using an array now there are 3 integers stored in that function. You have to tell it which one you are asking for by using a loop over all yield types and asking for getYieldChange((YieldTypes)iI), or if you only want a specific one you can ask for getYieldChange(YIELD_COMMERCE)
 
I down' have to fiddle with anything else in the SDK to have the extra Yields output show in the Raw Yields display, I can do it through Python.

Ah yes, then we were talking about the same thing. As your code is now, the extra yields will be counted by the modifiers (ugly), but the numbers will at least add up correctly. You could do it all in Python, but with a little bit more C++ we can make that a lot easier.

It's hard not having the up-to-date code in front of me, but if my memory is right, CvTeam calls CvPlayer::??? for each of its members to add the yield changes for a learned tech. This just calls CvCity::changeBaseYieldRate() for each city but doesn't keep track itself of all those extra yields.

You should add a YieldChanges array to CvPlayer to track all these extra yields. This is like CvPlayer's ExtraHappiness except with an array. To find all the places you'll need to make a change, look for m_aiYieldRateModifier as it behaves similarly (an array of ints, but percentages instead of yield values).

Code:
m_aiYieldChange = new int[NUM_YIELD_TYPES];
...
SAFE_DELETE_ARRAY(m_aiYieldChange );
...
m_aiYieldChange[iI] = 0;
...
int CvPlayer::getYieldChange(YieldTypes eIndex) const		 
{
	FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
	FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex is expected to be within maximum bounds (invalid Index)");
	return m_aiYieldChange[eIndex];
}

void CvPlayer::changeYieldChange(YieldTypes eIndex, int iChange)
{
	FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
	FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex is expected to be within maximum bounds (invalid Index)");

	if (iChange != 0)
	{
		m_aiYieldChange[eIndex] = (m_aiYieldChange[eIndex] + iChange);

		CvCity* pLoopCity;
		int iLoop;
		for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
		{
			pLoopCity->changeBaseYieldRate(eIndex, iChange);
			pLoopCity->AI_setAssignWorkDirty(true);
		}

		invalidateYieldRankCache(eIndex);
	}
}
...
pStream->Read(NUM_YIELD_TYPES, m_aiYieldChange);
...
pStream->Write(NUM_YIELD_TYPES, m_aiYieldChange);

New question: I tried compiling, and received this error from CGTM.cpp:

You need to call the array accessor function for setYieldHelp():

Code:
. . . GC.getTechInfo(eTech).getYieldChange[B][COLOR="YellowGreen"]Array[/COLOR][/B](), . . .
 
@ xienwolf, I think I understand, and I'm pretty sure that's addressed with EF's post...

@ EF, Sorry I didn't include an UTD codeset, here it is. View attachment Array.rar. And I'm all for easier, so by all means, let's make the C++ better!

What you've written for CVPlayer looks better than what I had, but I can't investigate till morning (wifey says it's bedtime :lol:).

As for the error code, I don't actually have any declarations of getYieldChangeArray. I guess I goofed somewhere again... :blush:

Thanks guys!!
 
As for the error code, I don't actually have any declarations of getYieldChangeArray.

My bad. I posted the code in post 108, but it's named CvTechInfo::getFutureYieldArray() there. You made an error when putting it into CvInfos.cpp:

Code:
int CvTechInfo::getYieldChange(int i) const	
{
	return m_piYieldChange
}

This should read

Code:
int CvTechInfo::getYieldChange[B][COLOR="YellowGreen"]Array[/COLOR][/B]([s][COLOR="Red"]int i[/COLOR][/s]) const	
{
	return m_piYieldChange
}
 
OK EF, made the change to void CvPlayer::changeYieldChange in CvPLayer.cpp (everything else you listed in that codeset in post #115 was as-is), and changed the line in CGTM to say getYieldChangeArray. Also, I completed the change you proposed from post #117.

But, I'm a bit confused. Do I really have to have just one of my variables actually named "Array"? Is that really necessary to the program? Doesn't it just see a text string, and only know what to do with that based on what code I write around it, not the contents of the string itself? And, there's the fact that now, the only thing calling getYieldChangeArray is CGTM.

Also, when I tried compiling, I received two errors. The first, would not allow me to proceed until I had changed
Code:
	int getYieldChangeArray const;
in CvInfos.h to
Code:
	int getYieldChangeArray[COLOR="Red"];[/COLOR] const
I don't know why I would need to move the ";" to before the const, but that's what CodeBlocks wanted.


Once that was done, I got this error:
CvGameTextMgr.cpp|4942|error C2064: term does not evaluate to a function taking 0 arguments|
for this line:
Code:
 	setYieldChangeHelp(szBuffer, L"", L"", gDLL->getText("TXT_KEY_TECH_FUTURE_YIELD_CHANGE").GetCString(), GC.getTechInfo(eTech).getYieldChangeArray(), false, true);

... help...? :eek: :crazyeye:
 
My bad. I posted the code in post 108, but it's named CvTechInfo::getFutureYieldArray() there. You made an error when putting it into CvInfos.cpp:

Code:
int CvTechInfo::getYieldChange(int i) const	
{
	return m_piYieldChange
}

This should read

Code:
int CvTechInfo::getYieldChange[B][COLOR="YellowGreen"]Array[/COLOR][/B]([s][COLOR="Red"]int i[/COLOR][/s]) const	
{
	return m_piYieldChange
}

wait... are you trying to pass the array back to the function? If so, you need to do it with:

Code:
int[COLOR="DeepSkyBlue"]*[/COLOR] CvTechInfo::getYieldChangeArray() const	
{
	return m_piYieldChange[COLOR="#00bfff"];[/COLOR]
}

OK EF, made the change to void CvPlayer::changeYieldChange in CvPLayer.cpp (everything else you listed in that codeset in post #115 was as-is), and changed the line in CGTM to say getYieldChangeArray. Also, I completed the change you proposed from post #117.

But, I'm a bit confused. Do I really have to have just one of my variables actually named "Array"? Is that really necessary to the program? Doesn't it just see a text string, and only know what to do with that based on what code I write around it, not the contents of the string itself? And, there's the fact that now, the only thing calling getYieldChangeArray is CGTM.

Also, when I tried compiling, I received two errors. The first, would not allow me to proceed until I had changed
Code:
	int getYieldChangeArray const;
in CvInfos.h to
Code:
	int getYieldChangeArray[COLOR="Red"];[/COLOR] const
I don't know why I would need to move the ";" to before the const, but that's what CodeBlocks wanted.


Once that was done, I got this error: for this line:
Code:
 	setYieldChangeHelp(szBuffer, L"", L"", gDLL->getText("TXT_KEY_TECH_FUTURE_YIELD_CHANGE").GetCString(), GC.getTechInfo(eTech).getYieldChangeArray(), false, true);

... help...? :eek: :crazyeye:


What you need isn't:
Code:
	int getYieldChangeArray const;

It is:

Code:
	int getYieldChangeArray[COLOR="#00bfff"]()[/COLOR] const;

You have to tell it what information is being sent to this function.
 
Thanks xienwolf. Although I still don't know why :crazyeye:, that bit for CvInfos.h at least make more sense than just moving the ";" around.

Thank-you also for proofing the bit for CvInfos.cpp. I hadn't caught the ";", and never would've known about the "*" or "()".

Unfortunately, I'm still getting an error for CGTM.cpp, although it's now different. Code thus:
Code:
 setYieldChangeHelp(szBuffer, L"", L"", gDLL->getText("TXT_KEY_TECH_FUTURE_YIELD_CHANGE").GetCString(), GC.getTechInfo(eTech).getYieldChangeArray(), false, true);
And the error:
CvGameTextMgr.cpp|4942|error C2664: 'CvGameTextMgr::setYieldChangeHelp' : cannot convert parameter 5 from 'int' to 'const int *'|

So, I assume parameter 5 is GC.getTechInfo(eTech).getYieldChangeArray(), but I haven't got anywhere where I wrote const int* GC.getTechInfo(eTech).getYieldChangeArray().
 
Top Bottom