iCanHeal

vincentz

Programmer
Joined
Feb 4, 2009
Messages
3,614
Location
Denmark
Hopefully a quickie ;)

I want to make it so units can only heal when below healRate*5 of damage.
Sort of like :

PHP:
	if (((currHitPoints() / maxHitPoints()) * 100) > (healRate(pPlot) *5))
	{
		return false;
	}

So with GlobalDefines defines, an Unit in enemy lands would only be able to heal 25%, in Neutral 50%, in friendly 75% and lastly in own city 100%.*

*(% of total health)

Code:
<Define>
		<DefineName>ENEMY_HEAL_RATE</DefineName>
		<iDefineIntVal>5</iDefineIntVal>
	</Define>
	<Define>
		<DefineName>NEUTRAL_HEAL_RATE</DefineName>
		<iDefineIntVal>10</iDefineIntVal>
	</Define>
	<Define>
		<DefineName>FRIENDLY_HEAL_RATE</DefineName>
		<iDefineIntVal>15</iDefineIntVal>
	</Define>
	<Define>
		<DefineName>CITY_HEAL_RATE</DefineName>
		<iDefineIntVal>20</iDefineIntVal>
	</Define>

Does the code look right and do I need to edit further to stop the healing after reaching 5x healRate?

I would insert it here : CvUnit.cpp line 3639

PHP:
bool CvUnit::canHeal(const CvPlot* pPlot) const
{
	if (!isHurt())
	{
		return false;
	}

	if (isWaiting())
	{
		return false;
	}

	if (healRate(pPlot) <= 0)
	{
		return false;
	}

	return true;
}
 
All CvUnit functions in this post.

canHeal() appears to be a check if the heal command button should appear or not. While it's a good idea to make the check here, it will not result in what you want to do.

doHeal() change the actual damage. This one would have to be capped as well.

healTurns() would likely also have to be updated as healing to 50% health is faster than waiting for 100%.

PHP:
	if (((currHitPoints() / maxHitPoints()) * 100) > (healRate(pPlot) *5))
	{
		return false;
	}
Be careful. If you write that in C++, you will get the same as
PHP:
int a = currHitPoints() / maxHitPoints(); // 1 if they are identical, 0 otherwise
a = a * 100; // either 0 or 100
if (a > healRate(pPlot) *5))
what you want to do to deal with int rounding is multiply, then divide
PHP:
int a = currHitPoints() * 100;
a = a / maxHitPoints();
if (a > healRate(pPlot) *5))
This would make the if line
PHP:
if (((currHitPoints() * 100) / maxHitPoints()) > (healRate(pPlot) *5))
Order doesn't matter if you use floating point, but as a general rule you should try to stick to int values. If you want to know why, then google for floating point accuracy and see why 0.1*0.1 might not be 0.01.
 
A thought just occured to me. The correct solution to this issue would be to make a function like CvUnit::getMaxHealthFromHealingInThisPlot(). That way you have a single location to calculate the max healing, which mean you can't have a bug of the different functions going out of sync in their calculations even if you change the calculation later on.
 
I... I'm not sure that I'm ready to make my own functions just yet... Too soon ;)

I just need to check it out ingame before. To make sure it not just ends up being really annoying having to return injured units to own cities to be able to heal them 100%.
Some things looks good in theory, but when put into work, it doesnt. If I add a function I cant continue on existing save (right?).

It does however work really good, though with the exception of unit not waking up when reaching MaxHealthFromHealingInThisPlot&#8482;.



I had to add an extra () to (healRate(plot()). Have no idea why?
PHP:
void CvUnit::doHeal()
// Vincentz Healing
{
	if (((currHitPoints() * 100) / maxHitPoints()) < (healRate(plot())) *5)  
	{
		changeDamage(-(healRate(plot())));
	}
}
// Vincentz Healing end

and also a = to if (((currHitPoints() * 100) / maxHitPoints()) >= (healRate(pPlot) *5))
so heal button doesnt show if its equal to the max.
PHP:
bool CvUnit::canHeal(const CvPlot* pPlot) const
{
	if (!isHurt())
	{
		return false;
	}

	if (isWaiting())
	{
		return false;
	}

	if (healRate(pPlot) <= 0)
	{
		return false;
	}

//Vincentz Healing
	if (((currHitPoints() * 100) / maxHitPoints()) >= (healRate(pPlot) *5))  
	{
		return false;
	}
//Vincentz Healing end

	return true;
}

Another thing I wanted was to let cities in disorder heal as enemy land (max 25%). (though might change to neutral (50%) though).
I'm aware of the redundancy of the last IF ;)

PHP:
	if (pPlot->isCity(true, getTeam()))
	{
		if (pCity && pCity->isOccupation())
		{
			iTotalHeal += (GC.getDefineINT("ENEMY_HEAL_RATE") + getExtraEnemyHeal());
		}
		else
		{
			iTotalHeal += GC.getDefineINT("CITY_HEAL_RATE") + (GET_TEAM(getTeam()).isFriendlyTerritory(pPlot->getTeam()) ? getExtraFriendlyHeal() : getExtraNeutralHeal());
				if (pCity && !pCity->isOccupation())
				{
					iTotalHeal += pCity->getHealRate();
				}
		}
	}

Now, if only I could find a way for units to wake up after reaching MaxHealthFromHealingInThisPlot&#8482;....
 
If I add a function I cant continue on existing save (right?).
No. If the function has nothing to do with savegames (like this one), then savegames are unaffected. To break a savegame, you have to modify ::read() or ::write() and even if you do that, the idea of uiFlag is to avoid breaking savegames.

Perhaps I should write a guide for adding data without breaking savegames. It looks like it's a topic which isn't as well understood as I thought it was. You certainly aren't the first to have funny ideas on how it works.

It does however work really good, though with the exception of unit not waking up when reaching MaxHealthFromHealingInThisPlot™.
I think I found the answer to that one.
Code:
void CvSelectionGroup::doTurn()
...
[S]if (pLoopUnit->isHurt())[/S]
[B]if (pLoopUnit->canHeal())[/B]
{
	bHurt = true;
}
Rather than checking if health is less than max, it checks if the unit will gain health by staying idle.

I had to add an extra () to (healRate(plot()).
() tells that it is a function call rather than just a variable. In this case you want to call
PHP:
CvPlot* CvUnit::plot() const
{
	return GC.getMapINLINE().plotSorenINLINE(getX_INLINE(), getY_INLINE());
}
 
error C2660: 'CvUnit::canHeal' : function does not take 0 arguments :confused:

I also tried :
PHP:
if (pLoopUnit->canHeal(pPlot))

as it is later in CvSelectionGroup.cpp

PHP:
		case MISSION_HEAL:
			if (pLoopUnit->canHeal(pPlot))
			{
				return true;
			}
			break;
but got
error C2065: 'pPlot' : undeclared identifier
 
Use the plot where the group currently is.
PHP:
if (pLoopUnit->canHeal(plot()))
Alternatively you can add
Code:
bool bHurt = false;
[B]CvPlot* pPlot = plot();[/B]
That will allow you to use pPlot and would likely be faster as it will not search for the plot for each unit, but only once for the entire group. I don't think performance is really an issue here.

If you worry about performance, then store the output of GC.getDefineINT() in CvGlobals and make functions to fetch the cached output from there. That will likely matter more than all your code contributions combined as getDefineINT() is fairly slow.
 
Thanks :-)
I tried the first, but it resets the "healing sleeping", so unit wakes up every turn.

Gonna try the second tomorrow.
Both gives the same result. However looking up canHeal(), it returns false if the group is waiting and it is waiting if getActivityType() == ACTIVITY_HEAL. In other words it can't heal if it is healing :crazyeye:

It kind of looks like a bug to me. However investigating this issue and figuring out possible sideeffects from fixing it could be a big task. It would be easier to simply work around it.
Code:
CvUnit::canHeal()
...
if ([B]getGroup()->getActivityType() != ACTIVITY_HEAL &&[/B]  isWaiting())
{
	return false;
}
This way the unit will only fail that line if the group is waiting and it isn't healing. The other tasks isWaiting() checks for is stuff like patrol and intercept. It kind of make sense not to heal while doing that.
 
Actually, this kinda double fixed everything :D

In vanilla, when playing and having a damaged unit, the only way to make sure the unit is waking after healing (set for healing) is to wake it up so healing icon appears, and then click heal. Now it can be done without having to wake up the unit, by simply clicking the heal button (or press H).
It might be a minor thing, but a minor thing that had me annoyed for quite a time.

Thanks :goodjob:
 
Back
Top Bottom