Adding new Resource Yields to Future Techs

I am neither stepping over nor stepping into it. I use the Debug->Continue option (hit F5). The breakpoints are basically just functioning as pauses so I can see where the code is running (which I thought was the point).

Here's my code currently, some changes in red:
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 (isHasReligion(eReligion))
	{
		for (int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
		{
			if (GC.getBuildingInfo((BuildingTypes)iI).getGlobalReligionYields() != NO_RELIGION && getNumActiveBuilding((BuildingTypes)iI) > 0)
			{
				iYield += getNumActiveBuilding((BuildingTypes)iI) * GC.getReligionInfo(eReligion).getGlobalReligionYield(eIndex) * GC.getGameINLINE().countReligionLevels(eReligion);
			}
[COLOR="Red"]			else
			{
				break;
			}[/COLOR]
		}
	}
	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;

[COLOR="Red"]	if (isHolyCity())[/COLOR]
	{
		for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
		{
			updateGlobalReligionYield((YieldTypes)iI);
		}
	}
}

On a blind guess (thanks to a friend at work who helped me think of it), I added the ELSE statement to the IF in int CvCity::getGlobalReligionYieldByReligion() that was giving me the infinite loop. I still don't know why it was doing that, but adding the ELSE-break statement fixed it. :confused:

I also added the IF(isHolyCity()) to void CvCity::updateGlobalReligionYield(). Now, when the GlobalReligionCount calls an update, CvCity skips the 21 steps of going through each religion for each yield if the City is not a Holy City. Since shrines can only be in Holy Cities, I've saved some clock time! :smug:

As for the bonus not being applied, the problem is with if (GC.getBuildingInfo((BuildingTypes)iI).getGlobalReligionYields() != NO_RELIGION && getNumActiveBuilding((BuildingTypes)iI) > 0). Something in there is failing, and so the game skips the iYield += line and goes straight to the return line.

I'm also firmly convinced, that if I can just find a way to actually use CvCity::hasShrine(), then I'd be set. I had tried this change below to int CvCity::getGlobalReligionYieldByReligion(), which, with the new IF check in void CvCity::updateGlobalReligionYield(), resulted in my Yield changes showing up only in holy cities, instead of everywhere. But the bonus shows up with or without a shrine:
Code:
	if (isHasReligion(eReligion))
	{
		iYield += GC.getReligionInfo(eReligion).getGlobalReligionYield(eIndex) * GC.getGameINLINE().countReligionLevels(eReligion);;
	}
	return iYield;
}
Now, if I could replace the IF(isHolyCity()) I installed in void CvCity::updateGlobalReligionYield() with IF(hasShrine()), then I'd be golden. But when I attempted that, I got this error:
CvCity.cpp(8442) : error C2660: 'CvCity::hasShrine' : function does not take 0 arguments
But if I try using if (hasShrine(eReligion)), I get this error instead:
CvCity.cpp(8442) : error C2065: 'eReligion' : undeclared identifier
which makes perfect sense, as void CvCity::updateGlobalReligionYield() doesn't have any variables mentioned. And can't. I tried, backing up through the call list through CvPlayer, CvGame, and back to CvPlayer. I don't remember now which one didn't like it, but one of them really didn't like having variables on this function. I think the solution might be to declare new variables within the function itself (a-la BonusTypes eBonus in int CvCity::getCorporationYieldByCorporation(YieldTypes eIndex, CorporationTypes eCorporation) const), but I don't know how to do that...

Sooooo close......

EDIT: No, I didn't touch CvGame::countReligionLevels.
 
OK, so breaking this line
Code:
if (GC.getBuildingInfo((BuildingTypes)iI).getGlobalReligionYields() != NO_RELIGION && getNumActiveBuilding((BuildingTypes)iI) > 0)
into two nested IF statements, like this:
Code:
for (int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
		{
			if (GC.getBuildingInfo((BuildingTypes)iI).getGlobalReligionYields() != NO_RELIGION)
			{
				if (getNumActiveBuilding((BuildingTypes)iI) > 0)
				{
					iYield += getNumActiveBuilding((BuildingTypes)iI) * GC.getReligionInfo(eReligion).getGlobalReligionYield(eIndex) * GC.getGameINLINE().countReligionLevels(eReligion);
				}
			}
			else
			{
				break;
			}
		}
has allowed me to discover a few things.
  1. it is if (GC.getBuildingInfo((BuildingTypes)iI).getGlobalReligionYields() != NO_RELIGION) that causes the infinite loop, unless it has a paired ELSE->break statement.
  2. That same line is not having the desired effect. If I understand correctly, the desired result of that line should be:
    • check if there is a building with a non-zero value in the GlobalReligionYields XML tag.
    • If yes, then proceed to next step.
  3. that Building/Yields check always fails, and so if (getNumActiveBuilding((BuildingTypes)iI) > 0) is never checked.
I don't know what to do about that...

Still, if I could just figure out why replacing the entire nested IF listed above with if (hasShrine(eReligion)) causes the following compiler error:
CvCity.cpp(8343) : error C2662: 'CvCity::hasShrine' : cannot convert 'this' pointer from 'const CvCity' to 'CvCity &'
Conversion loses qualifiers
:wallbash:
 
I've just been experimenting, trying to figure something out for this, and have the following odd discoveries to mention:

Code:
[COLOR="Red"]1[/COLOR]	if (isHasReligion(eReligion))
	{
[COLOR="Red"]2[/COLOR]	for (int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
		{
[COLOR="Red"]3[/COLOR]			if (GC.getBuildingInfo((BuildingTypes)iI).getGlobalReligionYields() != 0)
			{
[COLOR="Red"]4[/COLOR]				if (getNumActiveBuilding((BuildingTypes)iI) > 0)
				{
[COLOR="Red"]5[/COLOR]					iYield += getNumActiveBuilding((BuildingTypes)iI) * GC.getReligionInfo(eReligion).getGlobalReligionYield(eIndex) * GC.getGameINLINE().countReligionLevels(eReligion);
				}
[COLOR="Red"]6[/COLOR]				else
				{
[COLOR="Red"]7[/COLOR]					break;
				}
			}
[COLOR="Red"]8[/COLOR]			else
			{
[COLOR="Red"]9[/COLOR]				break;
			}
		}
	}
[COLOR="Red"]10[/COLOR]	return iYield;
RESULTS IN:
  • DEBUG DLL, VS2008 ATTACHED
    Upon setting a Jewish Holy City using WB, cycles through 1-9 once, instead of failing at step 4. Then goes back to (still for religion_0 (judaism)) steps 3,4,7, then the call stack goes blank, and then ends this function for religion_0 and proceeds and proceeds to religion_1. The other religions then come to step 1, the call stack goes blank, exit the function, and proceed to the next religion.
  • DEBUG DLL, NOT ATTACHED
    Normal gameplay experiences no lags or difficulties, but assigns the Shrine Bouns without any shrine built...

Then I tried this:
Code:
[COLOR="Red"]1[/COLOR]	if (isHasReligion(eReligion))
	{
[COLOR="Red"]2[/COLOR]	for (int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
		{
[COLOR="Red"]3[/COLOR]			if (GC.getBuildingInfo((BuildingTypes)iI).getGlobalReligionYields() != 0)
			{
[COLOR="Red"]4[/COLOR]				if (getNumActiveBuilding((BuildingTypes)iI) > 0)
				{
[COLOR="Red"]5[/COLOR]					iYield += getNumActiveBuilding((BuildingTypes)iI) * GC.getReligionInfo(eReligion).getGlobalReligionYield(eIndex) * GC.getGameINLINE().countReligionLevels(eReligion);
				}
			}
		}
	}
[COLOR="Red"]6[/COLOR]	return iYield;
RESULTS IN:
  • DEBUG DLL, VS2008 ATTACHED
    Upon setting a Jewish Holy City using WB, cycles through steps 1-5 once (for religion_0), then cycles back to steps 3,4,3,4,3,4 forever.
  • DEBUG DLL, NOT ATTACHED
    Normal gameplay experiences no lags or difficulties, but assigns the Shrine Bouns without any shrine built...

:confused:
 
It makes no sense to me that there is an infinite loop here. And your break is stopping the loop completely, so will prevent your code from doing anything unless the very first buildings listed in the XML are the ones with this field not being NO_RELIGION.


The error at the end is from calling a function labeled as CONST and trying to change a value during it.

ie:

Code:
void CvCity::getSomething() const
{
    m_iCitySomething += 1;
   return m_iCitySomething;
}

Would cause the issue, because it is labeled as CONST, but it tried to change a global stored value.

Further, any function labeled as CONST, can never call a function which is NOT labeled as CONST.
 
Weird. Const should operate at compile-time and give you a compile error if you try to modify a member field inside a const function or call another non-const function from one. Very very strange that removing const should fix the problem.

Also, the breaks were causing problems. I don't think it's an infinite loop. Instead, it is looping through every building. I think you just got sick of hitting F5 again and again and assumed it was infinite, but maybe const did something funny there and blocked iI from being incremented. It's a local variable and shouldn't cause non-constness. Again, strange behavior.

And the first if() test

Code:
if (GC.getBuildingInfo((BuildingTypes)iI).getGlobalReligionYields() != NO_RELIGION)

should be changed to

Code:
if (GC.getBuildingInfo((BuildingTypes)iI).getGlobalReligionYields() [B][COLOR="Red"]== eReligion[/COLOR][/B])

because you're calculating the religion yield for a single religion--the one passed to the function. If you're calculating the total yield for all religions, don't make the change above and remove eReligion from the parameter list.

I was under the impression that this function calculated the total and returned it (thus const), and then the calling function told the city to update the value by calling setGlobalReligionYield(eReligion, eYield, iNewValue). If this function (a getFoo() function should never change values) is going to change the value, rename the function and don't make any of the changes I mentioned in this post above.

Finally, the original psuedocode I laid out was for CvGame::updateGlobalReligionYields() to cycle through the holy cities only and tell them to update their religion yields. Looping through all cities and checking isHolyCity() works too.
 
EF, could you let me know which of your previous posts had been intended for CvGame::updateGlobalReligionYield()? There's been so much recently...

I should state however, that the code now works. Even gifting/conquering!

So, now onto Python... With both TechYields & ShrineYields now functioning properly, I have to get Python to properly display what's going on.

EF, I'd like your advice: Since I fully intend to only ever play these with BUG, should I fully integrate BUG and my personal mod, or keep BUG in CustomAssets to step on my mod? Or the other way around maybe?

Also, a brief survey for those few souls who monitor this thread: Should I release these two mods for the public? Do you think anyone would be interested?
 
If you can keep your mod separate and have it easily work with BUG, there's no point in merging or you'll have to remerge every time a new BUG version is released. If you keep it separate, you just update your BUG and keep on playin'.
 
True. The problem is, I've made certain changes to the main city screen, to accommodate my extra specialist, and make the crowding of buttons in the top left less of an issue. I'm thinking that BUG as a Mod, and my changes in CustomAssets, will be easiest to work with. That means I can just update the SVN copy of BUG every week or so, and use WinMerge to update any changes you make to the City Screen into my CustumAssets.

OK, so I'll tackle the TechYields first, as a separate mod for publishing. I'll be using only the Tech stuff, and vanilla BtS Python (for now, this is just for published version). Let me see if I can those tech yields to display properly.
 
EF, you know the little pop-up that appears when you hover your mouse over any of the Commerces in the top-left corner, the one that shows Base Commerce? Editing that requires more DLL work, doesn't it? The same with the hover over Production. And to create a new one for Food...

Hmm... Would it be possible instead to instead implement a counter in the Tech Chooser, showing how many of each Tech had been researched?
 
The name of the tech itself ought to hold a counter IIRC from Future tech.

Yes, DLL work for the mouseover, in CvDLLWidgetData, but possibly the code there just calls over to a function in CvGameTextMgr, it is pretty random for when they choose to do so
 
OK, I think void CvGameTextMgr::setProductionHelp(CvWStringBuffer &szBuffer, CvCity& city) is where all the values of the Production hover are calculated, but I don't see where those values are called for actual display. CvDLLWidgetData had some promising entries (case WIDGET_PRODUCTION_MOD_HELP: and case WIDGET_COMMERCE_MOD_HELP:), but I don't know where to go from there...

Once I can find that, I think I can just tell it do display the BaseYieldRate as (BaseYieldRate - TechYieldChange). Does that sound like it would work?

EDIT: Also, it occurred to me: Should I start a new thread now? One focused specifically on the Python, now that the C++ is all sorted out?

EDIT 2: And, it seems that this: void CvGameTextMgr::setCommerceHelp(CvWStringBuffer &szBuffer, CvCity& city, CommerceTypes eCommerceType) is where the Commerce pop-up is worked out. I think just maybe each value calculated will be displayed automatically, so long as it has a name and a value. So, I entered this into it:
Code:
	int iShrineYield = city.getGlobalReligionYield(eYieldType);
	if (0 != iShrineYield)
	{
		szBuffer.append(gDLL->getText("TXT_KEY_MISC_HELP_SHRINE_YIELD", iShrineYield, info.getChar()));
		szBuffer.append(NEWLINE);
		iBaseCommerceRate += 100 * iShrineYield;
	}
which, of course, doesn't work, because it's calling on YIELD values, and this function is built around the COMMERCE array. So, how do refine my call on city.getGlobalReligionYield() so that I am pulling only the YIELD_COMMERCE value, as opposed to the entire YIELD array?
 
All the stuff in Widgets is attached by declaring an item in Python to BE a widget of that type. So look for WIDGET_COMMERCE_MOD_HELP in python and you'll find the commands to create something which will use these rules for mouseover and click responses.

For the Commerce/Yield stuff you talk about next, I would say you are simply in the wrong place to do that work. Look for the function that calls to this function, that is probably a better place to start. Leave this function to be JUST commerces, no Yields.
 
Yes, all those hovers are done in CvGameTextMgr as you are finding. There is no WidgetTypes for food, so you'll have to add one and specify it when placing the text in CvMainInterface. BUG does this as BULL added a food hover.

You can append text to the hover for an existing widget and even create entirely new widget types in Python, but you cannot modify text for an existing widget. Check out BUG's WidgetUtil for how to do this. But as long as you're modifying the SDK you may as well define your new widget in C++.

You need to pass in the constant YIELD_COMMERCE instead of the non-existent eYieldType variable you have in the code you posted. This will ask the city for the :commerce: from the shrines, not the :gold:/:science:/:culture:/:espionage: commerce types that shrines could produce before your changes.
 
Thank-you both. I'll troll through the Python for references to WIDGET_COMMERCE_MOD_HELP and poke around in what I find.

EF, are you saying that, using CvGameTextMgr::setCommerceHelp(), I could add a line that says "Commerce from Shrine", but could not change the existing line that says "Base Commerce" to something else? Or did I miss your point entirely? I'll try to track down your BULL changes for reference on how to add an entirely new hover (Food).

As for passing in the constant YIELD_COMMERCE, how do I do that? How do I tell it to take just the Commerce value from the array?

EDIT: I think I just figured out the YIELD_COMMERCE bit, just substitute that for what I had, eYieldType. Umm, but for BULL? EF, you guys have changed ALOT of files for BULL. Could you give me some direction here...? :blush:
 
YIELD_COMMERCE is a YieldTypes value just like 5 is an int, and you can pass it to a function that takes a YieldTypes.

BULL hasn't changed all the files. I included all the files in the SVN as that was easier than making sure I added each file that was changed. Start by looking in CvEnum.h as I have added only a couple things there. Searching for FOOD should find it right away. Then right-click it and select Find All References.

My point about adding text to hovers applies to Python only. In the SDK you can do whatever you want to existing hover text. If you want a Python-only solution, you are limited to adding text to the end of existing hovers and creating new hovers.
 
Hey EF, I'm just putting together a project for BULL in VS2008, so that I can use those lovely search functions it has. I just want to ask a few questions about the Makefile.

Right at the beginning, you have this segment:
Code:
CIV = C:\Games\Civ4\Beyond the Sword
SDK = $(CIV)\CvGameCoreDLL
BOOST = $(SDK)\Boost-1.32.0
PYTHON = $(SDK)\Python24
BUFFY = $(CIV)\Mods\BUFFY Dev
Can I presume that I can change the first entry to the directory holding the BULL SDK dir? So, make it look like this:
Code:
CIV = D:\Games\Civilization IV\Beyond the Sword\Mods\Working
SDK = $(CIV)\Fully Merged DLL
BOOST = $(SDK)\Boost-1.32.0
PYTHON = $(SDK)\Python24
BUFFY = $(CIV)\Mods\BUFFY Dev
and then basically ignore the part about BUFFY (since I don't have it)?

Oh, and Refar's instruction say to delete the .rc file, but you've included it. May I go ahead and delete, or is the process for setting up the project going to be a little different here?
 
OK, I don't get it. There's only one call to WIDGET_COMMERCE_MOD_HELP, and that's in CvMainInterface.py under def updateCityScreen( self ):. So, the widget call brings us to line 573 in CvDLLWidgetData.cpp, which bounces us down to line 4499 in the same file, which routes us over to void CvGameTextMgr::setCommerceHelp() in CvGameTxtMgr.cpp. Where, as far as I can tell, every possible line that could show in the Base Commerce pop-up is spelled out. But, adding an entry for my Shrine Yields, does squat...

:confused:

Here's what I put in, right after int iReligionCommerce = city.getReligionCommerce(eCommerceType);
Code:
	int iShrineYield = city.getGlobalReligionYield(YIELD_COMMERCE);
	if (0 != iShrineYield)
	{
		szBuffer.append(gDLL->getText("TXT_KEY_MISC_HELP_SHRINE_YIELD", iShrineYield, info.getChar()));
		szBuffer.append(NEWLINE);
		iBaseCommerceRate += 100 * iShrineYield;
	}
I'm considering removing the "100 *" from the last line, but I'll try that tomorrow. I needs some sleep...
 
You should point the CIV line to the folder containing CIV4BeyondSword.exe. This folder should already contain a CvGameCoreDLL folder with Boost and Python library folders inside. If it doesn't, you can point it to your working SDK folder. The reason I did this is so that I didn't have to copy all those files that won't ever get modified to every SDK folder I have.

You can safely ignore the BUFFY references. And you can delete the .rc file, too. My makefile differs from Refar's only in that I reorganized things to remove duplicated information as much as possible. I also added a third target--BUFFY_Release--that you'll obviously not need to use. You could delete every line related to BUFFY if you wanted to streamline the file, but leaving them there won't hurt anything.

As for your code, add something so you can make sure it's doing what you expect:

Code:
	int iShrineYield = city.getGlobalReligionYield(YIELD_COMMERCE);
	if (0 != iShrineYield)
	{
		szBuffer.append(gDLL->getText("TXT_KEY_MISC_HELP_SHRINE_YIELD", iShrineYield, info.getChar()));
		szBuffer.append(NEWLINE);
		iBaseCommerceRate += 100 * iShrineYield;
	}
[B][COLOR="Red"]	else
	{
		szBuffer.append(L"No global religion yield!" NEWLINE);
	}[/COLOR][/B]
 
OK, progress, kind of...

The ELSE statement works. And now, so does the IF statement. But, it's not working where I want it to. See here:

ShrineYieldsScreenShot01.jpg
ShrineYieldsScreenShot02.jpg
ShrineYieldsScreenShot03.jpg


So, the ELSE causes the correct line of text in the third screenie, which was from a city with no Shrine in it. Goodie. The problem is the first two screenies. Both from the same city, with one Shrine, producing only one YIELD_COMMERCE. But, as you can see, it's adding in the line referencing Shrines as pert of the breakdown for the CommerceType being viewed at the moment. What I want, is for the very top line, Base Commerce, to get it's own breakdown (I'll need the same type of thing for TechYields). I think, that what I need to do, is not work in this individual Widget for the City Commerce hover, but find where the DLL defines how a Widget is built, and tell it to add an extra line at the very top.

Sound good? Am I way off? If I've got it right, where should I go to find where Widgets are first built?

EF, thank-you for clearing up the BULL for me (;)). I'll get started on porting over the FOOD hover now.
 
Back
Top Bottom