Adding new Resource Yields to Future Techs

The first C++ you posted had many errors, but since you got it to compile I assume you fixed those? ReligionTypes is a type of variable; you need to pass the value iI by casting it to that:

Code:
GC.getReligionInfo([B](ReligionTypes)iI[/B])

and

Code:
countReligionLevels([B](ReligionTypes)iI[/B])

I thought that CvBuildingInfo::isGlobalReligionYields(iReligion) told you if it provided global religion yields for the given religion. You aren't passing a religion to the function. What exactly does that function return? What is it's latest code?

Python will happily pass many arguments to C++ code. You need to look in PythonErr.log to find the error it's getting.
 
Yes, I'm using ((ReligionTypes)iI). Here's my most recent code:
Code:
int CvCity::getGlobalReligionYieldByBuilding(BuildingTypes eBuilding, YieldTypes eIndex) const
{
	int iBuildingYield = 0;

	for (int iI = 0; iI < GC.getNumReligionInfos(); iI++)
	{
		if (GC.getBuildingInfo(eBuilding).getGlobalReligionYields() != 0)
		{
			iBuildingYield += getNumActiveBuilding(eBuilding) * GC.getReligionInfo(ReligionTypes(iI)).getGlobalReligionYield(eIndex) * GC.getGameINLINE().countReligionLevels(ReligionTypes(iI));
		}
	}
	return iBuildingYield;
}

But in CvInfos, I have this:
Code:
int CvBuildingInfo::getGlobalReligionYields() const
{
	return m_iGlobalReligionYields;
Is that sufficient? All it's doing is checking for a single value in the XML, it doesn't even return the full arrray... And it's not currently built to deal with religons at all, I don't think. IIRC, CGTM.cpp is the only thing that calls on this function, and it takes care of choosing which religion is being checked... I'll double-check that when I get home.

As for the Python logs, the one that would show me a failed call-stack is strangely empty... By the same token, I just remembered that the .ini file seems to reset various values to the factory settins each time I change mods, so I'll have to double-check to make sure error reporting is actually on as it should be. Another thing to do when I get home.
 
ReligionTypes(iI)

Using this form is okay, but the rest of the SDK uses the more standard form

(ReligionTypes)iI​

You might want to switch over.

return m_iGlobalReligionYields;

Oh right, this function returns the religion for which the building supplies global religion yields. The function above is a bit easier now. As it stands it is adding up the yields as if it were a universal shrine. You want this:

Code:
int CvCity::getGlobalReligionYieldByBuilding(BuildingTypes eBuilding, YieldTypes eIndex) const
{
	ReligionTypes eReligion = (ReligionTypes)GC.getBuildingInfo(eBuilding).getGlobalReligionYields();
	if (eReligion != NO_RELIGION)
	{
		return getNumActiveBuilding(eBuilding) * GC.getReligionInfo(eReligion).getGlobalReligionYield(eIndex) * GC.getGameINLINE().countReligionLevels(eReligion);
	}
	else
	{
		return 0;
	}
}

The .ini file seems to reset various values to the factory settins each time I change mods.

That happens to me occasionally as well. I keep a "last good" copy backed up in the same folder so I can fix it more quickly when it does happen.

If you have no interface, there must be a Python exception being thrown, whether it's caused by Python itself or C++. Check your INI and run it again.
 
OK, thanks! I'll compile that now.

Just in case though, here's my exception log. It makes me think that I've done CyCity wrong:
Traceback (most recent call last):

File "CvScreensInterface", line 705, in forceScreenRedraw

File "CvMainInterface", line 737, in redraw

File "CvMainInterface", line 2171, in updateCityScreen

AttributeError: 'CyCity' object has no attribute 'getGlobalReligionYieldByBuilding'
ERR: Python function forceScreenRedraw failed, module CvScreensInterface

And my CyCity code:
Code:
int CyCity::getGlobalReligionYieldByBuilding(int /*BuildingTypes*/ eBuilding, int /*YieldTypes*/ eIndex)
{
	return m_pCity ? m_pCity->getGlobalReligionYieldByBuilding((BuildingTypes)eBuilding, (YieldTypes)eIndex) : -1;
}
I'm sure I've done CyCity.cpp right, but...
 
Interesting...

I changed int CvCity::getGlobalReligionYieldByBuilding() the way you suggested in post #403, and it compiled fine. But I get the same Python trace when the City screen come up...

Civ4ScreenShot0019.jpg


This is CvMainInterface.py:
Code:
for j in range( YieldTypes.NUM_YIELD_TYPES):
	iShrineYield = pHeadSelectedCity.getGlobalReligionYieldByBuilding(i, j)[COLOR="Red"] // this is line 2171[/COLOR]

	if (iShrineYield != 0):
		if ( bFirst == False ):
			szRightBuffer = szRightBuffer + ", "
		else:
			bFirst = False
											
		if ( iShrineYield > 0 ):
			szTempBuffer = u"%s%d%c" %( "+", iShrineYield, gc.getYieldInfo(j).getChar() )
		szRightBuffer = szRightBuffer + szTempBuffer
			else:
		szTempBuffer = u"%s%d%c" %( "", iShrineYield, gc.getYieldInfo(j).getChar() )
		szRightBuffer = szRightBuffer + szTempBuffer
I have not changed line 737 in CvMainInterface, nor have I touched CvScreensInterface at all.

I posted my line from CyCity.cpp in my last post, and haven't changed it since. I can only figure that I'm calling it wrong in the Python...
 
You also need a ".def" line in CyCityInterface.cpp to expose the function to Python. The function you have there is one part, the ".def" is the other.

Technically, the ".def" is what exposes it to Python. The CyCity object is just a wrapper around CvCity to make the Python interface cleaner.
 
BTW, what you are seeing is called a stack trace. The last line is where the error occurred. The lines before it just show the chain of events that led to that line being called. They don't have errors themselves. They are useful sometimes for tracking down why the error happened, but not always relevant.

Imagine my car ran out of gas while shopping. I call you and say, "I went bowling with a friend, but I realized I didn't have any cash. So I drove over to the ATM and realized I left my ATM card at home. I went back home and picked up my card, and my roommate asked if I could give her a ride to the store. On our way to the store, my car ran out of gas."

Code:
Bowling
ATM
Home
Store   <-- ran out of gas

You could say that I ran out of gas while bowling with my friend, and indeed that I went to the ATM and didn't have my card may be a clue as to why I ran out of gas, but in the end I ran out of gas going to the store.

The problem is in my previous post: you need to expose the CyCity function to Python using ".def" in CyCityInterface.cpp.
 
Thank-you EF. I knew that only the last line was where this particular error was triggering, you've explained how a stack trace works to me before. Same analogy too, I think. ;) I've had a stack trace trigger on me before where the 2nd line was relevant though, I wanted to include it here just to be safe.

And thank-you for the code. And for reminding me about the needed .def line. And I was just in CyEnumsInterface adding another .def line a few days ago too! :shake:

So, everything works for the Shrine. I'm gonna try the Techs now. I think it should be easy (knock on wood) because we've worked everything out!!
 
Thanks!! Couldn't of done it without you both! :hatsoff:

A little nit-picky question: Is there some fancy way I can get the Hovers to show "+1 :commerce: from Shrine" if there's only one Shrine in a city, but have it say "from Shrines" if there's more than one in a city? That would probably require some really complicated stuff, and I should just leave it singular, right? :rolleyes:
 
First, add a function to CvCity to either count and return the number of shrines. Next you need two XML text objects, one for singular and one for plural. Then pick the one to use based on the function above.
 
Hmm... Right... I have already used a two text key system for the Tech Yields. Works like a charm. But also, really easy to code. :D

Hmm... Let's see... How bout this:
Code:
int CvCity::getShrineInCityCount()
{
	int iShrineCount = 0;

	for (int iI = 0; iI < GC.getNumReligionInfos(); iI++)
	{
		if (hasShrine((ReligionTypes)iI))	
		{
			iShrineCount += iShrineCount + 1;
		}
	}
	return iShrineCount;
}
If I call that from CGTM.cpp during the hover construction processes, then I could use a nice little IF statement to determine which TXT_KEY to use.

Also, somewhere along the line, I instituted a weird bug.

It's become impossible to "click away" several pop-ups. the GUI registers where the mouse is, and changes the colour of the button appropriately, the button even goes dark when clicked on. But the pop-up won't go away unless you hit [Enter] or [Esc]. Among those that can now only be gotten rid of with the keyboard are:
  • The welcome screen when starting a new game
  • The "built a shrine" pop-up
  • The Technology pop-up
  • The "Founded religion" pop-up
  • Wonder pop-ups

Also, the EXIT button on many of the adviser screens is borked too. Those that no longer work are:
  • Domestic
  • Financial
  • Foreign
  • Military
  • Technology
  • Victory
  • Espionage

Maybe you can see the link between these errors, the commonality that I'm not seeing...

I also have managed to bugger up the Shrines too, although I'm positive I tested this before. It seems that religions 4, 5, & 6 (Budd, Conf, & Tao) can't have their Shrine built; it causes a fatal freeze. Not even a CTD, just frozen tight. Religions 0-3 work fine though (Jud, Christ, Islam, Hindu). I'll be rebuilding a Debug DLL tonight to play around with tomorrow, but I was wondering if you had any ideas...
 
This is where using a code repository like SVN at SourceForge.net or GoogleCode can really come in handy. Most tools will allow you to easily see all changes in your local copy versus what's in the repository. At least this way you can quickly see what you've changed since it must be cause by that code.

I'm with xienwolf in this case: do a full rebuild. The WidgetTypes value for the exit button is probably not what the code is looking for (header differs from cpp).

BTW, this line is wrong:

Code:
iShrineCount += iShrineCount + 1;

It will give you 1, 3, 7, 15, .... Instead you want this:

Code:
++iShrineCount;

or if you prefer

Code:
iShrineCount += 1;

or if you really like typing

Code:
iShrineCount [B][COLOR="Red"]=[/COLOR][/B] iShrineCount + 1;
 
Thanks for the heads up re: iShrineCount EF. Although it's painfully obvious now, I hadn't realized the using A += A + B means "A equals A + A + B". I've changed the code to use ++iShrineCount. :) Not strictly speaking wrong though. I only need to know plurality, so the difference between 1 and 2 is the same as the difference between 1 and 511... :p Still, I did it the wrong way, and I can easily imagine that not knowing the difference between += and ++ would seriously mess me up in the future, so thank-you! :D

I've fixed the freezing bug. I'm not quite sure why what I did fixed the freezing bug, but it doesn't happen anymore...

I noticed, while running through the debug DLL with lots of breakpoints, that for some reason the code wasn't incrementing past Religion4 when done with Yield2. It would cycle back to Yield0, but still be stuck on Religion4... It seemed to me while watching it (as if I have any real idea of what's happening... :rolleyes:) that there was some cross-talk going on between the functions. I've used alot of FOR statements, and they all use iI as their variable. I realized I'd been treating iI like is was some sort of sacred text, and it occurred to me that it's just a convention of the programmers that they always used iI in FOR counters. So I mixed up the variable names a bit. Let me know what you think:

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 [B]lL [/B]= 0; [B]lL [/B]< GC.getNumBuildingInfos(); [B]lL[/B]++)
		{
			if (GC.getBuildingInfo((BuildingTypes)[B]lL[/B]).getGlobalReligionYields() == eReligion)
			{
				if (getNumActiveBuilding((BuildingTypes)[B]lL[/B]) > 0)
				{
					iYield += getNumActiveBuilding((BuildingTypes)[B]lL[/B]) * GC.getReligionInfo(eReligion).getGlobalReligionYield(eIndex) * GC.getGameINLINE().countReligionLevels(eReligion);
				}
			}
		}
	}
	return iYield;
}

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

	for (int [B]kK [/B]= 0; [B]kK [/B]< GC.getNumReligionInfos(); [B]kK[/B]++)
	{
		iNewYield += getGlobalReligionYieldByReligion(eIndex, (ReligionTypes)[B]kK[/B]);
	}

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

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

void CvCity::updateGlobalReligionYield()
{
	int [B]iI[/B];

	for ([B]iI [/B]= 0; [B]iI [/B]< GC.getNumReligionInfos(); [B]iI[/B]++)
	{
		if (hasShrine((ReligionTypes)[B]iI[/B]))	
		{
			for (int [B]jJ [/B]= 0; [B]jJ [/B]< NUM_YIELD_TYPES; [B]jJ[/B]++)
			{
				updateGlobalReligionYield((YieldTypes)[B]jJ[/B]);
			}
		}
	}
}

Unfortunately, the EXIT button problem is proving more recalcitrant. It's not a bad compile; I did a complete clean one of both the Final_Release and the Debug, and in both, the error is still present. It's not a result of changes I made in CvMainInterface either, I removed that file and the error persisted. I'm going to start redacting changes to the widgets, and see where it starts working again... ugh... :wallbash:
 
Local variables (all those loop counters) cannot affect each others' values across function calls. I think it's more likely that you were using the wrong variable in some place.

BTW, updateGlobalReligionYield() looks fishy to me. If a city has two shrines, the yields will be updated twice. It should have this logic instead:

Code:
if (getShrineCount() > 0)
  for each yield
    updateGlobalReligionYield(eYield)

This function is just a shorthand method of updating each yield in turn. getGlobalReligionYieldByReligion(eYield, eReligion) is the function that actually checks for eReligion's shrine. Calling this function when the city has no shrines produces the expected zero result.

Note: putting "in city" in the name of a CvCity function is redundant. Just call your new function getShrineCount() since it will almost always have a city in front of it.

if (pCity->getShrineCount() > 1)​

reads smoother than

if (pCity->getShrineInCityCount() > 1)​

Especially since you cannot have shrines outside cities. ;)
 
@ xienwolf: That might be it... I don't think I have anything in CvEnums, but I did add BUG's DOUBLE_SEPARATOR in one of the Defines files. When I get home, I'll try removing that first. Thanks!

@ EF: True, true, and true. Thank-you sir. We'll make a decent programmer out of me yet! :D
 
xienwolf, it was CvEnums.h. I moved the entry for the Food widget to the bottom of its section, and now the Exit buttons all work again.

I've just noticed that the :hammers: hover doesn't properly display Specialists though, so I now I get to work on that. I think I can just copy BUG, and then I'm all set!

Well, no, not really. I would really like to separate out the Tech bonus for Happy & Health in the hovers, but I so far haven't been able to figure out where the game counts those... And I just realized, I have some graphics that I would like to have display in the Tech Adviser, and I don't know how to do that yet either...

EDIT: OK, got the Specialists working!! :D I love it when things go easy!! Of course, that probably means getting the Tech Happy & Health to display will just be that much harder...

EDIT 2: OK, I found where the game tallies up the total amount of Tech Health & Happy, it's in CvTeam's void CvTeam::processTech(TechTypes eTech, int iChange), using these two lines:
Code:
GET_PLAYER((PlayerTypes)iI).changeExtraHealth(GC.getTechInfo(eTech).getHealth() * iChange);
			GET_PLAYER((PlayerTypes)iI).changeExtraHappiness(GC.getTechInfo(eTech).getHappiness() * iChange);

Now, I know those are producing the values I need, but I can't think of anyway to mimic these lines directly in CGTM.cpp, which means building new functions, which I'd rather avoid if I can. Do either of you think it's possible to do this without writing new functions in CvTeam?
 
You can always write the calculations in CvGTM. I assume you want to calculate just the :) / :health: due to techs, right? Loop over all techs, test if the player has the tech, and if so add its value to a counter.

Code:
int iHappiness = 0;
int iHealth = 0;

for each tech iI
  if player has tech iI
    iHappiness += techinfo.getHapiness()
    iHealth += techinfo.getHealth()

if iHappiness != 0
  add line for it
if iHealth != 0
  add line for it
 
Back
Top Bottom