Python request: Units forcibly promoted when on a certain trait's land during war

Cethegus

King
Joined
Feb 28, 2008
Messages
922
Location
Finland
I was wondering if it's possible to do this? I want to enstrengthen the Protective trait and currently I'm looking for a way to make it negate any invaders' natural ability to heal. Having heard it's not possible through traditional means (trait affecting enemy heal rate directly), I was wondering if it's possible to bypass with Python, by adding units a promotion that lowers heal rate while on enemy lands of a Protective leader? The XML part of this is I can do, but I don't know if attaching it to a set of conditions is possible in the first place and if it is, how. Naturally the promotion would have to remove once the conditions cease to apply, as well.

If someone could help me with this, I'd be very grateful. Thanks in advance.
 
This could be done. You would want to put your code in the onUnitSetXY function in CvEventManager.

1) Do we have the negative promo
1a) if so, Check the plot owner, if plot owner is us remove the negative promo
1b) if plot owner is not us and plot owner doesnt have X trait, remove promo
2) else check plot owner
2a) if its not us, check for X trait
2b) if plot owner has X trait, apply promo

Something along those lines.

Alternately, if that ended up slowing things down too much (which it might), you could instead only apply and remove the promotion depending on where the unit ends its turn rather than for each move. Maybe use onEndGameTurn and loop through the player's units applying the same logic as above.
 
Thanks. Do you think it would be better implemented using SDK or DLL? It sounds so heavy when put like that, checking where each of your units ends its turn.

Anyway, thanks for the reply.
 
This is actually a really good specific concept to add into the DLL for a newbie SDK modder, once you've finished it should give you a really good introduction and basic understanding of how the civ4 gamecore is set up, and how to mod it. The functionality you want would certainly be best to implement directly to the SDK.

In CvUnit there is the function:

CvUnit::healRate(const CvPlot* pPlot) const

This is the function that controls healing, and by attacking your problem directly from here you can set up any type of functionality and tweak it directly to get your desired effect. I strongly recommend going this route, rather then trying to do some hacky add promotion thing you are thinking about now - it's easier overall once you do it, and it will provide for easy changes later on.

I would go about it by adding a boolean value to trait infos. See Xienwolf's "An Idiots guide to the DLL" thread on how to add a boolean value. In his tutorial he shows exactly how to add a boolean value to a class.

An Idiots guide to the DLL

In your case you'd want to add a boolean value isProtective to Trait Infos. Once that is done you can create code to remove base rate healing, but not effect bonus healing from promotions, when in a protective leader's territory by modifying the function CvUnit::healRate like so:


Code:
		if (!GET_TEAM(getTeam()).isFriendlyTerritory(pPlot->getTeam()))
		{
			if (isEnemy(pPlot->getTeam(), pPlot))
			{
/************************************************************************************************/
/* Cethegus   Start               01/18/12                                                                                                                       */
/*                                                                                                                                                                         */
/* ProteciveHealPenalty                                                                                                                                            */
/************************************************************************************************/

				bool isProtective = false;

				for (iI = 0; iI < GC.getNumTraitInfos(); iI++)
				{
					if(GC.getTraitInfo(TraitTypes(iI)).isProtective())
					{
						if(pPlot->GET_PLAYER(getOwnerINLINE()).isHasTrait(TraitTypes(iI)))
						{
							isProtective = true;
							break;
						}
					}
				}
				if(!isProtective)
				{
					iTotalHeal += (GC.getDefineINT("ENEMY_HEAL_RATE"));
				}
				iTotalHeal += getExtraEnemyHeal();
/************************************************************************************************/
/* Cethegus        ProteciveHealPenalty                                END                                                                                   */
/************************************************************************************************/
			}
			else
			{
				iTotalHeal += (GC.getDefineINT("NEUTRAL_HEAL_RATE") + getExtraNeutralHeal());
			}
		}

Obviously you can also tweak that function other ways, to get your ideal behavior.

You can also hardcode in the protective trait directly, instead of creating a new boolean value isProtective attribute to TraitInfos. I don't recommend doing that though as hardcoding sets you up for failure later. It's much better just to do things right the first time. But if you want to see how to hardcode it, the code above could be re written like so:

Spoiler :
Code:
		if (!GET_TEAM(getTeam()).isFriendlyTerritory(pPlot->getTeam()))
		{
			if (isEnemy(pPlot->getTeam(), pPlot))
			{
/************************************************************************************************/
/* Cethegus      Start                                                     01/18/12                                                                              */
/*                                                                                                                                                                         */
/* ProtectiveHealPenalty                                                                                                                                           */
/************************************************************************************************/

				if(!(pPlot->GET_PLAYER(getOwnerINLINE()).isHasTrait(TraitTypes(getInfoTypeForString(TRAIT_PROTECTIVE)))))
				{
					iTotalHeal += (GC.getDefineINT("ENEMY_HEAL_RATE"));
				}
				iTotalHeal += getExtraEnemyHeal();
/************************************************************************************************/
/* Cethegus        ProtectiveHealPenalty                                END                                                                                  */
/************************************************************************************************/
			}
			else
			{
				iTotalHeal += (GC.getDefineINT("NEUTRAL_HEAL_RATE") + getExtraNeutralHeal());
			}
		}

Again though, I highly recommend not hardcoding in a reference to a trait specifically - hard coding in general is just a bad practice, and if you start doing it you're going to create much more work for yourself and end up with bugs that are near impossible to track down, as well as run into unexpected consequences in the future when tweaking other stuff. Just take my advice and read the linked thread on the DLL by Xienwolf, add the boolean value to trait infos first, and then attack the issue directly in the function that controls healing and you will have total control over the behavior you want, and also be set up in the future for easier tweaks. Also doing this correctly here will probably give you a better understanding of how the DLL works for future modding concepts you want to implement.
 
Back
Top Bottom