I want to add my part to that thread with that function below. It is used in the next version of my Plot List Enhancements and calculates the heal factor of a unit. The heal factor is needed to calculate the number of turns a wounded units needs to get 100% healed.
The function is called with only the unit to be examined as parameter.
Here is the code:
PHP:
def getPlotHealFactor(pUnit):
# heal rates for certain areas. They are usually stored in the GlobalDefines.XML but can't be read out with a standard API function.
# So I placed them here as constants.
ENEMY_HEAL_RATE = 5
NEUTRAL_HEAL_RATE = 10
FRIENDLY_HEAL_RATE = 15
CITY_HEAL_RATE = 20
# set/reset some variables
pPlot = pUnit.plot()
iSameTileHealFactor = 0
iAdjacentTileHealFactor = 0
iBuildingHealFactor = 0
iSelfHealFactor = 0
iPromotionHealFactor = 0
iTileHealFactor = 0
iActivePlayer = CyGame().getActivePlayer()
pActivePlayer = gc.getPlayer(iActivePlayer)
iActivePlayerTeam = pActivePlayer.getTeam()
eDomain = gc.getUnitInfo(pUnit.getUnitType()).getDomainType()
# a sea or air unit in a city, behaves like a land unit
if pPlot.isCity():
eDomain = DomainTypes.DOMAIN_LAND
# calculate the adjacent-tile heal-factor caused by other units (only the unit with the highest factor counts)
for dx in range(-1, 2):
for dy in range(-1, 2):
# ignore same tile. Adjacent-tile healing does not work on the same tile.
if not (dx == 0 and dy == 0):
pLoopPlot = CyMap().plot(pPlot.getX()+dx, pPlot.getY()+dy)
# loop through all units on the plot
for i in range(pLoopPlot.getNumUnits()):
pLoopUnit = pLoopPlot.getUnit(i)
eLoopUnitDomain = gc.getUnitInfo(pLoopUnit.getUnitType()).getDomainType()
# a sea or air unit in a city, behaves like a land unit
if ((eDomain == DomainTypes.DOMAIN_SEA) or (eDomain == DomainTypes.DOMAIN_AIR)) and pLoopPlot.isCity():
eLoopUnitDomain = DomainTypes.DOMAIN_LAND
# adjacent-tile heal does only work if the units have the same domain type
if (eDomain == eLoopUnitDomain):
if (pLoopUnit.getTeam() == iActivePlayerTeam):
if (pLoopUnit.getAdjacentTileHeal() > iAdjacentTileHealFactor):
iAdjacentTileHealFactor = pLoopUnit.getAdjacentTileHeal()
# calculate the same-tile heal-factor caused by other or same unit (only the unit with the highest factor counts)
# the same-tile healing is also a kind of self-healing. Means : the promotion Medic I has also effect on the owner unit
for i in range(pPlot.getNumUnits()):
pLoopUnit = pPlot.getUnit(i)
eLoopUnitDomain = gc.getUnitInfo(pLoopUnit.getUnitType()).getDomainType()
# a sea or air unit in a city, behaves like a land unit
if pLoopPlot.isCity():
eLoopUnitDomain = DomainTypes.DOMAIN_LAND
# same tile heal does only work if the units are of the same domain type
if (eDomain == eLoopUnitDomain):
if (pLoopUnit.getTeam() == iActivePlayerTeam):
if (pLoopUnit.getSameTileHeal() > iSameTileHealFactor):
iSameTileHealFactor = pLoopUnit.getSameTileHeal()
# only the highest value counts
iTileHealFactor = max(iAdjacentTileHealFactor, iSameTileHealFactor)
# calculate the self heal factor by the location and promotion
iTeam = pPlot.getTeam()
pTeam = gc.getTeam(iTeam)
iSelfHealFactor = NEUTRAL_HEAL_RATE
iPromotionHealFactor = pUnit.getExtraNeutralHeal()
if pPlot.isCity():
iSelfHealFactor = CITY_HEAL_RATE
iPromotionHealFactor = pUnit.getExtraFriendlyHeal()
elif (iTeam == iActivePlayerTeam):
iSelfHealFactor = FRIENDLY_HEAL_RATE
iPromotionHealFactor = pUnit.getExtraFriendlyHeal()
elif (iTeam != TeamTypes.NO_TEAM):
if (pTeam.isAtWar(iActivePlayerTeam)):
iSelfHealFactor = ENEMY_HEAL_RATE
iPromotionHealFactor = pUnit.getExtraEnemyHeal()
# calculate the heal factor by city buildings
if pPlot.isCity():
if (pPlot.getTeam() == iActivePlayerTeam):
pCity = pPlot.getPlotCity()
# loop for all buldings
for iBuilding in range(gc.getNumBuildingClassInfos()):
# check if city has that building
if pCity.hasBuilding(iBuilding):
# sum up all heal rates
iBuildingHealFactor += gc.getBuildingInfo(iBuilding).getHealRateChange()
# return the sum of all heal factors
return iTileHealFactor + iBuildingHealFactor + iSelfHealFactor + iPromotionHealFactor
Lets step through the main parts :
first few lines are used to set some variables and constants. Nothing special here:
PHP:
# heal rates for certain areas. They are usually stored in the GlobalDefines.XML but can't be read out with a standard API function.
# So I placed them here as constants.
ENEMY_HEAL_RATE = 5
NEUTRAL_HEAL_RATE = 10
FRIENDLY_HEAL_RATE = 15
CITY_HEAL_RATE = 20
# set/reset some variables
pPlot = pUnit.plot()
iSameTileHealFactor = 0
iAdjacentTileHealFactor = 0
iBuildingHealFactor = 0
iSelfHealFactor = 0
iPromotionHealFactor = 0
iTileHealFactor = 0
iActivePlayer = CyGame().getActivePlayer()
pActivePlayer = gc.getPlayer(iActivePlayer)
iActivePlayerTeam = pActivePlayer.getTeam()
eDomain = gc.getUnitInfo(pUnit.getUnitType()).getDomainType()
This one is quite important to know. A sea or air unit can be healed by a land unit, but only when its in a city. This leads to 2 major conclusions :
- a sea unit with promotion Medic I (same tile heal +10%) located in a city can heal a land unit located in the city and can also heal a land unit with Medic II (adjacent tile heal + 10%) if the unit is on a cities adjacent tile.
- a sea unit on the coast with Medic II can't heal a land unit on an adjacent tile.
Due to that I set the units domain to DOMAIN_LAND when the units to be examined it is located in a city.
PHP:
# a sea or air unit in a city, behaves like a land unit
if pPlot.isCity():
eDomain = DomainTypes.DOMAIN_LAND
Next the adjacent tile heal is watched. A loop though all adjacent tiles for a friendly (own or teammate) unit which has a protmotion with adjacent tile heal. I don't look for the promotion itself, but for its effects on the heal factor with the function CyUnit.getAdjacentTileHeal(). This makes the function mod friendly.
Two things to be mentioned :
- the same tile is ignored, because the adjacent tile heal has no effect on the same tile!!!
- if the adjacent plot is a city, also the air and sea units are considered.
PHP:
# calculate the adjacent-tile heal-factor caused by other units (only the unit with the highest factor counts)
for dx in range(-1, 2):
for dy in range(-1, 2):
# ignore same tile. Adjacent-tile healing does not work on the same tile.
if not (dx == 0 and dy == 0):
pLoopPlot = CyMap().plot(pPlot.getX()+dx, pPlot.getY()+dy)
# loop through all units on the plot
for i in range(pLoopPlot.getNumUnits()):
pLoopUnit = pLoopPlot.getUnit(i)
eLoopUnitDomain = gc.getUnitInfo(pLoopUnit.getUnitType()).getDomainType()
# a sea or air unit in a city, behaves like a land unit
if pLoopPlot.isCity():
eLoopUnitDomain = DomainTypes.DOMAIN_LAND
# adjacent-tile heal does only work if the units have the same domain type
if (eDomain == eLoopUnitDomain):
if (pLoopUnit.getTeam() == iActivePlayerTeam):
if (pLoopUnit.getAdjacentTileHeal() > iAdjacentTileHealFactor):
iAdjacentTileHealFactor = pLoopUnit.getAdjacentTileHeal()
Now we examine the friendly units on the same plot. More or less the same stuff as before. One thing to be mentioned here :
- the same tile healing (as it is with the promotion Medic I) is also a self healing. Means : it has also effect on the promotions owner unit!!.
PHP:
# calculate the same-tile heal-factor caused by other or same unit (only the unit with the highest factor counts)
# the same-tile healing is also a kind of self-healing. Means : the promotion Medic I has also effect on the owner unit
for i in range(pPlot.getNumUnits()):
pLoopUnit = pPlot.getUnit(i)
eLoopUnitDomain = gc.getUnitInfo(pLoopUnit.getUnitType()).getDomainType()
# a sea or air unit in a city, behaves like a land unit
if pLoopPlot.isCity():
eLoopUnitDomain = DomainTypes.DOMAIN_LAND
# same tile heal does only work if the units are of the same domain type
if (eDomain == eLoopUnitDomain):
if (pLoopUnit.getTeam() == iActivePlayerTeam):
if (pLoopUnit.getSameTileHeal() > iSameTileHealFactor):
iSameTileHealFactor = pLoopUnit.getSameTileHeal()
Because the two values adjacent tile heal factor and same tile heal factor are not summed up, we can only use the largest of both values.
PHP:
# only the highest value counts
iTileHealFactor = max(iAdjacentTileHealFactor, iSameTileHealFactor)
Next we are looking on the self healing factor. It is influenced by two factors : the territory (friendly, neutral or enemy) the unit is located on and several promotions which could increase this territory factor. Which territory the unit is currently on is determined by the plot's owner. If it's the player or a teammate we have friendly territory, if it's somebody we have war with it's enemy territory. Anything else is neutral.
PHP:
# calculate the self heal factor by the location and promotion
iTeam = pPlot.getTeam()
pTeam = gc.getTeam(iTeam)
iSelfHealFactor = NEUTRAL_HEAL_RATE
iPromotionHealFactor = pUnit.getExtraNeutralHeal()
if pPlot.isCity():
iSelfHealFactor = CITY_HEAL_RATE
iPromotionHealFactor = pUnit.getExtraFriendlyHeal()
elif (iTeam == iActivePlayerTeam):
iSelfHealFactor = FRIENDLY_HEAL_RATE
iPromotionHealFactor = pUnit.getExtraFriendlyHeal()
elif (iTeam != TeamTypes.NO_TEAM):
if (pTeam.isAtWar(iActivePlayerTeam)):
iSelfHealFactor = ENEMY_HEAL_RATE
iPromotionHealFactor = pUnit.getExtraEnemyHeal()
Finally we are looking at the buildings which may have influence to the healing. Of course, this is only done when we are in a players or teammates city. Then we loop through all buildings in the city looking for the buildings influence on healing. Currently there is only one standard building doing so : the hospital.
PHP:
# calculate the heal factor by city buildings
if pPlot.isCity():
if (pPlot.getTeam() == iActivePlayerTeam):
pCity = pPlot.getPlotCity()
# loop for all buldings
for iBuilding in range(gc.getNumBuildingClassInfos()):
# check if city has that building
if pCity.hasBuilding(iBuilding):
# sum up all heal rates
iBuildingHealFactor += gc.getBuildingInfo(iBuilding).getHealRateChange()
Last thing is to sum up all the single heal factors and return the value.
PHP:
# return the sum of all heal factors
return iTileHealFactor + iBuildingHealFactor + iSelfHealFactor + iPromotionHealFactor
The value returned by this function can now be used to calculate the number of turns a unit needs to be 100% healed. An implemenation may be as follows:
PHP:
if (eUnitDomain == DomainTypes.DOMAIN_AIR):
fCurrStrength = float(pUnit.airCurrCombatStr()*0.01)
fMaxStrength = float(pUnit.airMaxCombatStr()*0.01)
else:
fCurrStrength = float(pUnit.baseCombatStr())*float(1.0-pUnit.getDamage()*0.01)
fMaxStrength = float(pUnit.baseCombatStr())
iTurnsToHeal = int((fMaxStrength-fCurrStrength)/float(fMaxStrength*float(mt.getPlotHealFactor(pUnit))*0.01)+0.999) # force to round up
The python used here is very likly not the best possible. As many others here, I learnt python with civ4, allthough I have some years of experience as professional programmer. But this is 10 years away now.
The knowledge about healing in civ4 I gained by try and error and lots of investigations and experiments with a random map and the world builder. There still may be some details about healing not covered by this function, but I think it offers a quite good overview about the civ4 healing details and some python basics.