Adding new Resource Yields to Future Techs

OK, so I've learned several things:

1) I have no idea what I'm doing. :crazyeye:
2) My FutureYieldTechs aren't researchable. Possible this has to do with the fact that they branch of the original FutureTech, and that one was still listed as "Repeatable". Corrected, and I will re-check later.
3) FutureCommerce doesn't seem to have an effect after a certain (currently unknown) game-point. If granted through WB right at 4000 BC, then it has an effect, but granted at end-game, no effect manifests. Reason unknown...
4) FutureProduction does work, but only if the city is producing Gold/Res/Cul/Esp. Again, don't know why.
5) I have no idea what I'm doing!! :eek: (did I say that already?)

So, off to another week of work. Hopefully, I'll be able to tinker with some things over the next few days. EF, thank-you for the logging code, now I see how it works (kinda), and I think I can implement it to show where I've screwed up.

Thanks again! :cool:
 
Sorry I've faded out on helping here. Got a humdinger of a bug I am trying to sort out in my code right now. One thing I did along the way though was move from Codeblocks to Visual C 2008. All I can say is that a Debug DLL is AMAZING to have.
 
One thing you may have to dig into is how data makes its way through the various objects. It's possible that when calculating the production applied to the current build each turn, the city doesn't call getYieldRate(). Perhaps it calls this once when changing the type of build and stores it somewhere.

My advice, start at the end and work backwards. If you can't figure out why your production isn't counted when building warrior, look at the code that applies the production to the current build.

This is from memory of reading the code long ago, so sprinkle with a dash of double-checking. Each turn, after you've moved your units and hit the End Turn button, the engine calls the function CvPlayer.doTurn(). This function loops over the player's cities and calls CvCity.doTurn() on each. This in turn applies the production.

The only way to get to know what you're doing is to do it! ;) Read a lot of code to figure out what is doing what. It can be a PITA to read, but it gets easier as you do it more. Also, take notes. This would be perfect stuff to add to the Civ4 wiki's SDK section.
 
One thing I did along the way though was move from Codeblocks to Visual C 2008. All I can say is that a Debug DLL is AMAZING to have.

Can you do remote debugging with the DLL? That might be tough to get going, but once you did you could set breakpoints that would greatly aid in figuring things out.
 
OK, haven't had time to look this through entirely, but I've already tackled this subject to some degree.

Here is some of the code I used in CvTeam:

Code:
// <Changes Start>
	m_aiYieldRateModifier = new int[NUM_YIELD_TYPES];
	m_aiCommerceRateModifier = new int[NUM_COMMERCE_TYPES];
	// <Changes End>

        // <Changes Start>
	SAFE_DELETE_ARRAY(m_aiYieldRateModifier);
	SAFE_DELETE_ARRAY(m_aiCommerceRateModifier);
	// <Changes End>

and the real guts of it:

Code:
int CvTeam::getYieldRateModifier(YieldTypes eIndex)
{
	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_aiYieldRateModifier[eIndex];
}


void CvTeam::changeYieldRateModifier(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_aiYieldRateModifier[eIndex] = (m_aiYieldRateModifier[eIndex] + iChange);

		if (eIndex == YIELD_COMMERCE)
		{
			updateCommerce();
		}

		AI_makeAssignWorkDirty();

		if (getID() == GC.getGameINLINE().getActiveTeam())
		{
			gDLL->getInterfaceIFace()->setDirty(CityInfo_DIRTY_BIT, true);
		}
	}
}

int CvTeam::getCommerceRateModifier(CommerceTypes eIndex)
{
	FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
	FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex is expected to be within maximum bounds (invalid Index)");
	return m_aiCommerceRateModifier[eIndex];
}


void CvTeam::changeCommerceRateModifier(CommerceTypes eIndex, int iChange)
{
	FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
	FAssertMsg(eIndex < NUM_COMMERCE_TYPES, "eIndex is expected to be within maximum bounds (invalid Index)");

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

		updateCommerce();

		AI_makeAssignWorkDirty();
	}
}
// <Changes End>

and here:

Code:
// <Changes Start>
    for (iI = 0; iI < MAX_PLAYERS; iI++)
	{
		if (GET_PLAYER((PlayerTypes)iI).isAlive())
		{
			if (GET_PLAYER((PlayerTypes)iI).getTeam() == getID())
			{
                for (int iY = 0; iY < NUM_YIELD_TYPES; iY++)
                {
                    GET_PLAYER((PlayerTypes)iI).changeYieldRateModifier(((YieldTypes)iY), (GC.getTechInfo(eTech).getYieldModifier(iY) * iChange));
                }
			}
		}
	}

	for (iI = 0; iI < MAX_PLAYERS; iI++)
	{
		if (GET_PLAYER((PlayerTypes)iI).isAlive())
		{
			if (GET_PLAYER((PlayerTypes)iI).getTeam() == getID())
			{
                for (int iY = 0; iY < NUM_COMMERCE_TYPES; iY++)
                {
                    GET_PLAYER((PlayerTypes)iI).changeCommerceRateModifier(((CommerceTypes)iY), (GC.getTechInfo(eTech).getCommerceModifier(iY) * iChange));
                }
			}
		}
	}
	// <Changes End>

Lastly, there is this code towards the end:

Code:
// <Changes Start>
	pStream->Read(NUM_YIELD_TYPES, m_aiYieldRateModifier);
	pStream->Read(NUM_COMMERCE_TYPES, m_aiCommerceRateModifier);
	// <Changes End>

        // <Changes Start>
	pStream->Write(NUM_YIELD_TYPES, m_aiYieldRateModifier);
	pStream->Write(NUM_COMMERCE_TYPES, m_aiCommerceRateModifier);
	// <Changes End>

Hope this helps. I can confirm that I had it working in Warlords but-as I'm discovering-this doesn't mean it will work in BtS.

Aussie_Lurker.
 
Oh, and what does this mean from a game-play standpoint? Well it means you can give *any* tech a modifier to commerces and yields. So, in your case, you can give the future techs a +20% food and hammer modifier if you wanted. Hope that helps to clarify things.

Aussie.
 
I suspect CvPlayer has something similar to updateCommerce() that you needed to call when changing the yield rate for commerce and the individual commerce rates. Perhaps it has one for production as well; that's what I was getting at in my post above.
 
Aussie_Lurker, this is amazing. I will go through your code in more detail in the morning, don't have time to do much more at the moment than appreciate your having posted it.

What I'm trying to do though, is have my FutureYields add just +1 of the given Yield to each city. Percentages might wind up working weird in Food outputs... And would of course result in different amounts for each city. Do you think it would be straightforward enough for me to modify your code to add integers instead of percentages?

@ xienwolf, what would a debug DLL be able to do for me? And I assume Visual C 2008 must be purchased?

@ EF, I think it may have something to do with the fact that right now my Yields are finally counted in under int CvCity::getYieldRate(YieldTypes eIndex) const, and I think maybe getYieldRate isn't used everywhere, cause CvCity (i think it's that one) calls a different function for each of the different build types (units, building, projects, processes). So, again, in the morning.

Thanks guys!!
 
No, Visual C 2008 is free, and there is a walkthrough to help you set it up (look for Refar's signature, PDF + MakeFile).

Debug DLL lets you see those FAssert statements actually trigger while the game is running, announcing to you that something isn't working right, and lets you stop the game and watch it process the code FROM the code. You can hold your mouse over a variable name and if the code has already figured out what it should be, then it shows you the value. Then you can have it move through the code 1 step at a time and watch precisely what it does (warning, there are a LOT of background steps which you really don't need/want to know about, like what it really means to say GET_PLAYER(getOwnerINLINE()).)


Also you can set breakpoints in the code which will cause the game to stop and move to the code for your attention when it reaches the point you marked.

As for the remote debugging (@EmperorFool), I am not quite sure what you mean. Placing breakpoints and whatnot is easily done, but if you mean cease control of someone else's computer to debug their source/system... should be possible with the right external programs, but I don't have any of that kind of stuff :)
 
@draco963 - Aussie_Lurker's code is an example of using arrays instead of individual fields. But you still need to figure out how to get your new values to be recognized by builds and such.

Another method you might try is to call changeBaseYieldRate(iChange) for each city the player owns. You'll need to account for the future yield values in the _DEBUG code near the top of CvCity.doTurn(), but that's probably the only place that calculates the base yield rate from all its constituent values. Everywhere else the change() function is used, I think.

As for the remote debugging . . . but if you mean cease control of someone else's computer to debug their source/system...

No, what you described about plain old debugging is precisely what I meant. By remote I mean one program (VC) debugging another program (Civ4). Non-remote (local) debugging means having VC run your code inside itself, which you just can't do with Civ4 since the DLL must run in the EXE.

This is great news for when I start doing SDK work for BUG.
 
Again draco, I don't know if this helps, but my CivicsMod code has a BuildingCommerceChange and BuildingYieldChange setting-which does more like what you're suggesting. I've tied it into the civics system, but I see no reason why you couldn't tie it into techs in much the same way. As I see it, you'd just need to move all the code I used in CvPlayer and find ways to apply it in CvTeam instead. Tell you what, my civics mod isn't going anyway just yet-until my religionyields and religioncommerce issues are fixed. So maybe I Can look into it and see what I can produce. So am I right in assuming that you want future techs to grant bonuses to existing buildings-or am I on the wrong track?

Aussie.
 
draco963 wants techs to be able to add a value to any yield for each city the player has. So you research Future Food and gain +1 :food: per city--no building required. They should work just like Future Tech that adds +1 :health: to each city, only for yields.
 
Ahhh right. Well, in that case I remember a modder here was working on a mod which incorporated Health and Happiness into either commerces or yields-can't remember which. I'll look into it, but maybe that could be one of the solutions.

Aussie.
 
Actually, now that I think about it, my civics changes could still be applied. Instead of BuildingYieldChange or BuildingCommerceChange, I could try to do the same thing with TechYieldChange and TechCommerceChange. As I said, leave it with me, I'll see what I can rustle up.

Aussie.
 
Now that I'm looking at the code for CvCity, CvPlayer and CvPlot, the more I'm convinced the best thing to do is apply it to each city for the player by calling CvCity::changeBaseYieldRate(). This will update commerce as needed and do other important bookkeeping. The trick is finding where else it needs to be taken into account such as init() or setOwner().

I think the best model is CvPlayer's Free Commerce. It's nearly identical to what draco wants. Instead of calling CvPlayer.updateCommerce(), however, call CvCity.changeYieldRate() for each city.

Code:
int CvPlayer::getFreeCityYield(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_aiFreeCityYield[eIndex];
}


void CvPlayer::changeFreeCityYield(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_aiFreeCityYield[eIndex] = (m_aiFreeCityYield[eIndex] + iChange);
		FAssert(getFreeCityYield(eIndex) >= 0);

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

The only thing missing then is handling what happens when a city changes owners, and I can't find that code yet. All of the other sources of extra yields have a processFoo() function that's called when that item is added to the city, such as specialists, bonuses, buildings, etc. I just can't see where to add this in.
 
Hey Draco-I think I can actually do this. Am modifying the CvInfos files now-just so you know, I'm adapting the YieldChange tag from CvBuildingInfos. It looks like this:

Code:
// <FutureTech Start>
int CvTechInfo::getYieldChange(int i) const
{
	FAssertMsg(i < NUM_YIELD_TYPES, "Index out of bounds");
	FAssertMsg(i > -1, "Index out of bounds");
	return m_piYieldChange ? m_piYieldChange[i] : -1;
}

int* CvTechInfo::getYieldChangeArray() const
{
	return m_piYieldChange;
}
// <FutureTech End>

I'll keep you posted on the rest as I go. Especially if I run into difficulties :p

Aussie_Lurker.
 
Holy dinah! Guys! ... Wow! This is just amazing! :goodjob: Aussie, I can't thank-you enough. As a matter of fact, I think my wife may be even more grateful than I am, cause now I'm not so stressed about this! :lol:

OK, so again, I'll go over all the code in the morning. But thank-you all so much again. I can't wait to go over it all!! ;) :lol:
 
Woah, don't thank me yet Draco-got to get it to work first ;). Wish me luck :).

Aussie.
 
return m_piYieldChange ? m_piYieldChange : -1;


I would warn against this. Instead, just return the value so that a tech can have a 0 yield change as well as a negative yield change. That way the code that uses this won't have to check for -1; it just applies the value directly to the city's base yield rate.

draco, given the code I posted above and the code you have already for CvTechInfo (just change CvTeam to use the changeFreeCityYield function I posted instead of the changeFutureXXX() ones you have), you should be good to go, save that one part I mention at the end of my post. That still needs handling.

Or go Aussie_Lurker's route, as you see fit. :)
 
Well, I tried a couple of different things, amd need to clarify some others...

Placing xienwolf's code
Code:
	int iFutureYield = 0;

	if (eIndex == YIELD_FOOD)
		iFutureYield = GET_PLAYER(getOwner()).getFutureFood();
	else if (eIndex == YIELD_PRODUCTION)
		iFutureYield = GET_PLAYER(getOwner()).getFutureProduction();
	else if (eIndex == YIELD_COMMERCE)
		iFutureYield = GET_PLAYER(getOwner()).getFutureCommerce();
in either void CvCity::changeBaseYieldRate or void CvCity::setBaseYieldRate instead of the orignal placement of int CvCity::getBaseYieldRate results in all the FutureYields being counted 6 times. So, add one FutureFoodTech, get +6 :food:. Also, if you add one Tech of one type, and another tech of another type later on (using WB), the 2nd type adds to the count of the first. So, if I enter WB and add one each of FutureFoodTech, FProdTech, & FCommerceTech, and exit WB, then each Yield Tech has been counted 6 times. But if I go back and add one only FutureFood, and exit WB, now each FutureYield has been counted 12 times.

Leaving the above code in int CvCity::getBaseYieldRate still results in the glitchy implemention described previously.

@ EF, I'm afraid I found your info in post #75 confusing... Is the described code to be inserted into CvPlayer.cpp, or CvCity.cpp?
 
Top Bottom