Adding new Resource Yields to Future Techs

The problem is, the values of the bonuses for Health & Happy from Techs is calculated in CvTeam::processTech using a value that is fed to it from CvTeam::setHasTech, and that fed-in value is the number of techs that the team has with the qualifying modifier. Remember, it's not just "normal" techs, but the Future Techs too, which just keep counting up & up. I can't just say:

Code:
for each tech iI
  if player has tech iI
because that would only return each tech from the tree that you've learned that had an ExtraHealth value for it. But the tree is not where the number of Future Techs you have is counted. The above would return 1 for Future Tech 1, and continue to return 1 for FT 2, 3, etc.

So, what I'm thinking is, change this line in CvTeam::setHasTech thus:

Code:
		processTech(eIndex, ((bNewValue) ? 1 : -1));
		[COLOR="Red"]updateTechHappy(eIndex, ((bNewValue) ? 1 : -1));
		updateTechHealth(eIndex, ((bNewValue) ? 1 : -1));[/COLOR]


and add this further down to calculate and then return the value of :)/:health:

PHP:
void CvTeam::updateTechHappy(TechTypes eTech, int iChange)
{
	PROFILE_FUNC();

	int iI;
	int iTechHappyValue = 0;

	for (iI = 0; iI < MAX_PLAYERS; iI++)
	{
		if (GET_PLAYER((PlayerTypes)iI).getTeam() == getID())
		{
			iTechHappyValue = GET_PLAYER((PlayerTypes)iI).changeExtraHappiness(GC.getTechInfo(eTech).getHappiness() * iChange);
		}
	}

	m_iTechHappy = iTechHappyValue;
}
int CvTeam::getTechHappy()												 
{
	return m_iTechHappy;
}

void CvTeam::updateTechHealth(TechTypes eTech, int iChange)
{
	PROFILE_FUNC();

	int iI;
	int iTechHealthValue = 0;

	for (iI = 0; iI < MAX_PLAYERS; iI++)
	{
		if (GET_PLAYER((PlayerTypes)iI).getTeam() == getID())
		{
			iTechHealthValue = GET_PLAYER((PlayerTypes)iI).changeExtraHealth(GC.getTechInfo(eTech).getHealth() * iChange);
		}
	}

	m_iTechHealth = iTechHealthValue;
}

int CvTeam::getTechHealth()												 
{
	return m_iTechHealth;
}


_______________________________________________________________________________
I'm trying something new here with the |PHP| tags instead of
the |CODE| tags.  I think it might be easier to read...

The above is just a pair of duplicates of CvTeam::processTech, with everthing non-:)/:health:-related removed.

I can then call CvTeam::getTechHealth() & ::getTechHappy in CGTM for the hovers...

I think... :blush:

EDIT: Nope, that didn't work. Apparently, "GET_PLAYER((PlayerTypes)iI).changeExtraHealth(GC.getTechInfo(eTech).getHealth() * iChange) has type 'void' ". Which means I can't dictate to have it equal a variable, like iTechHealthValue. Don't know how to work around this...
 
You can calculate the change first and then pass that to changeExtraHappiness().

Code:
int iHappiness = GC.getTechInfo(eTech).getHappiness() * iChange;

for each player
  changeExtraHappiness(iHappiness)

I recommend that you add this new m_iTechHappy to CvPlayer instead of CvTeam. Move these functions to CvPlayer as well. While every player on a team has the same techs--and thus the same tech happy/health--it just feels better to have it on CvPlayer. Plus CvGTM works with CvPlayer already. Your logic here is sound, though.
 
I would definitely make your changes in processTech(). That function should produce all of the effects of adding/removing a tech. setHasTech() should not do that. processTech() should call your new changeTechHappiness/Health() functions which track their own values (new m_iTechHappiness/Health) and then call changeExtraHappiness/Health() with the same value.

What this means is that the effects of the happy/health will be done through the player's extra health/happiness just as they are now, but the totals would also be tracked in separate variables strictly for showing in CvGTM. I think this is what you are trying to do, isn't it?

I was only showing you how to solve your problem with needing to put the value into multiple places. You do that by storing the calculation in a temporary variable and passing that variable to the other functions.
 
OK, an update, because I think I'm on a different page than you, somewhere 20 pages back or so... :crazyeye:

This is CvTeam::setHasTech()
Code:
		processTech(eIndex, ((bNewValue) ? 1 : -1));
		[COLOR="Red"]setTechHappy(eIndex, ((bNewValue) ? 1 : -1));
		setTechHealth(eIndex, ((bNewValue) ? 1 : -1));[/COLOR]

CvTeam::processTech() has nothing in it.
These are my new functions in CvTeam:
Code:
void CvTeam::setTechHappy(TechTypes eTech, int iChange)
{
	PROFILE_FUNC();

	int iTechHappyValue = 0;

	for (iI = 0; iI < MAX_PLAYERS; iI++)
	{
		if (GET_PLAYER((PlayerTypes)iI).getTeam() == getID())
		{
				iTechHappyValue += GC.getTechInfo(eTech).getHappiness() * iChange;
		}
	}

	m_iTechHappy = iTechHappyValue;
}

int CvTeam::getTechHappy() const
{
	return m_iTechHappy;
}

void CvTeam::setTechHealth(TechTypes eTech, int iChange)
{
	PROFILE_FUNC();

	int iTechHealthValue = 0;

	for (iI = 0; iI < MAX_PLAYERS; iI++)
	{
		if (GET_PLAYER((PlayerTypes)iI).getTeam() == getID())
		{
				iTechHealthValue += GC.getTechInfo(eTech).getHealth() * iChange;
		}
	}

	m_iTechHealth = iTechHealthValue;
}

int CvTeam::getTechHealth() const
{
	return m_iTechHealth;
}

CGTM.cpp calls CvTeam::getTechHealth() & CvTeam::getTechHappy() to determine the values of the Tech ;)/:health:. It looks like this:
Code:
		int iTechHealth = 0;
		TeamTypes eActiveTeam = GC.getGameINLINE().getActiveTeam();
		iTechHealth = GET_TEAM(eActiveTeam).getTechHealth();
		if (iTechHealth > 0)
		{
			szBuffer.append(gDLL->getText("TXT_KEY_MISC_HEALTH_TECH", iTechHealth));
			szBuffer.append(NEWLINE);
		}

		iHealth = city.getExtraHealth() - iTechHealth;


~ ~ ~ ~ ~ ~ ~ ~ ~


		int iTechHappy = 0;
		TeamTypes eActiveTeam = GC.getGameINLINE().getActiveTeam();
		iTechHappy = GET_TEAM(eActiveTeam).getTechHappy();
		if (iTechHappy > 0)
		{
			iTotalHappy += iTechHappy;
			szBuffer.append(gDLL->getText("TXT_KEY_HAPPY_TECH", iTechHappy));
			szBuffer.append(NEWLINE);
		}

		iHappy = (city.getExtraHappiness() + GET_PLAYER(city.getOwnerINLINE()).getExtraHappiness() - iTechHappy);

This construct almost works. The new entries in CGTM do make my new TechHappy text show up, but only for Future Tech 1, the rest go into the old "OH YEAH!" that they normally show up in. The TechHealth one doesn't work at all, which confuses the bejeebuz outta me...

I'm really sorry EF, your last couple of posts have left me really confused... I'm obviously missing something, well, obvious. I hope the code above makes it easier to explain to me where I've screwed up...

Thanks... :blush:
 
Okay, I'll start with what I see are the problems in your current code and then go on to how I would have done it from the start (which isn't all that different from where you're going).

1. CvGTM: why is there this line

iTotalHappy += iTechHappy;​

but no line like this

iTotalHealth += iTechHealth;​

in the if() block? Is this a problem, or is the health calculated differently from the happiness?

2. If you have a team with multiple players, the effects will be tracked multiplied. setTechHappy/Health() add up the total health for all members of a team. Instead, you want to track the effect for a single player since every player should receive the same effects.

3. I don't see any call to CvPlayer function to apply the effects, such as changeExtraHappiness(). Where do these effects get counted? Do you have code in happyLevel() or something like it? It's one thing to show the effect in the hover; it requires additional code to count it in CvCity::happyLevel(). I thought you were going to apply it using CvPlayer::changeExtraHappiness() and then just subtract it out in CvGTM.

What I would have done

1. Track the Effect: Copy CvPlayer's extra happiness including the field (m_iExtraHappiness?) and all the functions that operate on it (get/set/change) to create tech happiness.

2. Apply the Effect: Call changeTechHappiness() from processTech().

3. Use the Effect: Find all places where getExtraHappiness() is called (except in changeExtraHappiness) and add a call to getTechHappiness().

4. Show the Effect: Call getTechHappiness() from CvGTM.

Wait, what are you trying to do here again?

I'm looking at CvTeam::processTech() and there are already calls to CvPlayer::changeExtraHappiness() for the tech's happiness. What are you trying to do? It's too difficult to keep multiple threads straight when people do multiple things in the same thread. Can you please restate exactly what you're doing at this point? I don't want to reread the whole thread.
 
Happy to restate the goal right now! Thanks EF!! :D

The Future Technology currently available in vanilla BtS gives you +1 :)/:health: per each FT researched. I'd felt this was incomplete, and over-powered the single available FT. So, the goal had been, get new tags available for FT's, that allowed me to add :food:/:hammers:/:commerce: to them also. At your, and xienwolf's, suggestion, the Tech Yields tags are simply available to all techs, and not attached solely to FT.

So, we finely got that functionality working (post #136), and I quailed at the Python of getting it showing, and took this huge detour into Shrines, which we are now totally done with.

I came back to Future Techs and getting their yields to show while I was implementing getting the Shrine Yields to show (covered on page 21), since I realized it was going to be in the same place, and wasn't that big a deal after all. :lol:

But, although getting :food:/:hammers:/:commerce: from Technologies to show in the hovers was actually really easy, I now need to get the +1 :)/:health: to show separately in their respective hovers in cities. Functionality works fine, because it's just the original game stuff, but I can't stand to have the extra happiness from FutureTechs (or any other Techs) just showing up in the "OH YEAH!" line of the :) hover, and extra health from Techs just showing under "from Civilization" in the :health: hover. Those lines also cover extra :)/:health: from random events and something else I can't recall right now (it's been too long since I actually played the game).

So, all that's left to do, is get the game to count the total amount of happy & health from Techs only, and feed that value into a variable I can call on from CGTM to display in the hovers. I haven't been working in processTech() itself because that function just gets the value of happy/health from Techs and instantly lumps it into extraHealth/extraHappy, which, like I said, includes random events (and something else), and I didn't want to interfere with the established process. My thought had been, get the value of :)/:health: from techs, leave it in some variable where it won't be bothered until the next time techs are processed, and then call that variable in CGTM, where I can tell the existing lines ("OH YEAH!" and "from Civilization") to subtract that value from the amount they display, and then show my values in their own lines.

So, my code in post #425 is intended to do that. -Phew-

Now, to answer your other questions:
  1. The game counts Health & Happy differently, I'm just mimicking what's already there. Health uses a separate variable, city.goodHealth(), to display the total health bonus, and Happy actually tracks it CGTM.
  2. Maybe this goes back to you asking me to explain the point. When we were first getting the Tech Bonuses working, you and xienwolf had told me that Tech bonuses are counted across a team, not just for the individual player
  3. ;) explained above!

Thanks EF!!
 
Okay, nice. A most excellent summary there, draco. :)

The only problem with that code is you are summing up the effects across all players of the team. Instead, you want to calculate the effect of the tech and apply the same value to each player. Very minor correction needed.

So here's what I'd do from this point.

1. Add CvPlayer::changeTechHappiness/Health() that mimic extra happiness/health. They'll need a variable to hold the value, m_iTechHappiness/Health. As I stated above, I would create the standard get/set/change functions we've been using thus far.

The set() function should call changeExtraHappiness/Health() using the new minus old value. The reason to put this in set() instead of change() is in case someone ever comes along and calls set(). change() is just a convenience function that calls set() for you; it should not have any other logic in it.

2. Change processTech() so that it calls these new changeTechHappiness/Health() functions above instead of changeExtraHappiness/Health(). It should pass the same value it's passing to changeExtraHappiness/Health() now; just change the functions being called.

Code:
	for (iI = 0; iI < MAX_PLAYERS; iI++)
	{
		if (GET_PLAYER((PlayerTypes)iI).getTeam() == getID())
		{
			...
			GET_PLAYER((PlayerTypes)iI).change[B][COLOR="Red"]Tech[/COLOR][/B]Health(GC.getTechInfo(eTech).getHealth() * iChange);
			GET_PLAYER((PlayerTypes)iI).change[B][COLOR="Red"]Tech[/COLOR][/B]Happiness(GC.getTechInfo(eTech).getHappiness() * iChange);
			...
		}
	}

Ditch the CvTeam functions you created above and the m_iTechHappy/Health variables you added to CvTeam. While moving these to CvPlayer is a drag, I think it makes more sense there and you get to reuse the processTech() code.

3. As you said in your summary post, CvGTM will subtract out the getTechHappiness/Health() values from the getExtraHappiness/Health() values to show them separately.
 
Thanks EF!! Just one thing I'm confused on:

Since changeExtraHappiness/Health do serve a purpose outside of counting bonuses from Techs, should they not remain in processTech? Or will the new changeTechHappiness/Health calling the originals be enough to make them update and feed appropriate values into CGTM? My main concern being that iChange variable native to processTech that gets used in updating everything's values. How would I make sure that the value of iChange gets passed from processTech to changeTechHappiness/Health to changeExtraHappiness/Health?
 
Let's say you research FT4 which gives +1:). iChange will be 1 in processTech(), and getTechHappy() for FT4 will be 1. processChange() will thus pass 1 * 1 = 1 to changeTechHappy(). changeTechHappy() will call getTechHappy() [X] and add 1 to it, passing the result to setTechHappy(). setTechHappy() will store the new value [X + 1] and subtract the old value [X] from that to call changeExtraHappy() [1].

The other option is not to have setTechHappy() call changeExtraHappy() and instead change any place that calls getExtraHappy() and have it also call getTechHappy() and add the result. There may be some places--e.g. changeExtraHappy()--that call getExtraHappy() that should not be changed to also call getTechHappy(). Only change places that are calculating total happiness.
 
OK, how's this look?

Code:
int CvPlayer::getTechHealth() const
{
	return m_iTechHealth;
}

void CvPlayer::setTechHealth(int iChange)
{
	if (iChange != 0)
	{
		m_iTechHealth = (m_iTechHealth + iChange);
		[COLOR="Red"][s]iChange = 0;[/s][/COLOR]
		changeExtraHealth(int iChange);

		AI_makeAssignWorkDirty();
	}
}

void CvPlayer::changeTechHealth(int iChange)
{
	setTechHealth(int iChange)
}

int CvPlayer::getTechHappiness() const
{
	return m_iTechHappiness;
}

void CvPlayer::setTechHappiness(int iChange)
{
	if (iChange != 0)
	{
		m_iTechHappiness = (m_iTechHappiness + iChange);
		[COLOR="Red"][s]iChange = 0;[/s][/COLOR]
		changeExtraHappiness(int iChange);

		AI_makeAssignWorkDirty();
	}
}

void CvPlayer::changeTechHappiness(int iChange)
{
	setTechHappiness(int iChange)
}

I realized while I was hacking this together, that I don't actually want to pass any value to changeExtraHappiness/Health. I am calling them from a function that is called by processTech, and Tech values for :)/:health: are now held in my new functions. I don't need to pass any value to the old function at all, or the bonuses from Techs will wind up getting counted twice. As a matter of fact, now that I write this, I think I may not need to even bother calling them. Other functions might call the old stuff (eg: random events) but I no longer need them to be related to processTech at all, do I? Which would make CvGTM a bit easier too...

EDIT: No, wait, I just realized how that's wrong. I still need to pass the proper values to changeExtraHappiness/Health, because that's where the game does the math for how much :)/:health: there actually is in a city, and I don't want to go mucking about with that.

EDIT 2:OK, I changed the code. I think it should now have the desired effect.
 
Alrighty then. We're done!

Well, almost. Everything works that I can see, but proofing your own stuff is an invitation to make mistakes. So, I've uploaded the mod and I'd like to ask you to play-test it for me. You guys will know all sorts of ways to break things deliberately, ways that I won't have thought of, and I'd like you to try! :D

The only thing left to do of any major importance is EF's suggestion for trimming down my ShrineYields code, from post #416. But everything else should work like a charm!

This is both the Tech & Shrine Yields mods together in one package. :cool:
View attachment Yields Mod.rar

Thanks so much guys!! Wish I could get you both a bottle of wine, or something...
 
Cool. The get/set/change pattern is actually a little different:

Code:
int getFoo() const
{
    return m_iFoo;
}

void setFoo(int iNewValue)
{
    if (iNewValue != m_iFoo)
    {
        [B]changeExtraHappiness(iNewValue - m_iFoo);[/B]
        // no need to call AI_makeAssignWorkDirty() because changeExtraHappiness() calls it already
        m_iFoo = iNewValue;
    }
}

void changeFoo(int iChange)
{
    if (iChange != 0)
    {
        setFoo(getFoo() + iChange);
    }
}
 
There are a few things:

1. Firaxis Convention. This is how most of the other SDK functions work.

2. Coding Convention: This is how a lot of coders in general work these functions.

3. Logic: Your changeTechHappy() just called setTechHappy(). There are only a handful of reasons to have one function do nothing more than call another function: rename a function but keep the old name alive for other code to keep using, add/change parameter values, etc. Here's there's no reason.

4. Logic: set() should mean "set the value to X" and change() should mean "change the value by X". You were using the latter meaning for set() which would be confusing given the other set() and change() functions in the SDK.
 
Hey Draco. Just got your PM, where can I get the file from-I'd be happy to give it a quick check through :).
 
EF, I've starting merging my Mod into BUG, and come across a bit of a hitch...

In the Raw Yields box, I've been able so far to get it to show my Shrine Yields properly, and it doesn't look like anything is screwing up. But, I'm not sure how to go about adding my Tech Yields. I'm thinking that I should probably just define the Yield & iValue variables in RawYields.py directly, and expose the part of CvTeam (or CvPlayer, I'll need to track it down) that adds up the Tech totals to call from RawYields.py. Does that sound about right?
 
Yes, you should be able to confine your changes to RawYields.py as you've said. You probably want to create a new Technologies line item, and that means creating a new constant (the things in ALL_CAPS). I'll take a look at it this weekend; it should be pretty easy.
 
Back
Top Bottom