Damage to ranged units from cities

Joined
Jun 27, 2007
Messages
2,248
Location
Hamilton, Ontario
After having discovered that ranged units don't benefit from the cover promotions when defending against a city bombardment, (http://forums.civfanatics.com/showthread.php?t=524038) but melee units do, I set out to find where that takes place so I can change it, but I'm not sure what's stopping it from working that way now.

The function CvUnit::GetMaxDefenseStrength
has this:
Code:
	// Defense against Ranged
	if(bFromRangedAttack)
		iModifier += rangedDefenseModifier();

But I can't find which function is called when a city attacks. I've found lots of functions relating to combat damage and unit strength, but I'm not sure which is the specific city attacking ranged unit function I'm looking for so I can see why the ranged defense modifier isn't being applied to city on unit ranged unit attacks but if for city on melee unit attacks.
 
Almost all combat calcs are in CvUnitCombat.cpp - try CvUnitCombat::ResolveRangedCityVsUnitCombat()
 
ResolveRangedCityVsUnitCombat seems to be dealing with everything after the battle, not determining damage etc. Most of CvUnitCombat seems to be about displaying information before and after combat rather than the actual combat. I'm only finding references to strength and damage etc. in CvUnit and CvCity.
 
I think I may have it.
It's in CvUnit::GetMaxRangedCombatStrength

Within
Code:
if(NULL != pOtherUnit)
which is for when the ranged unit is in combat with another unit.
After the part for attacking we have this:
Code:
// Ranged DEFENSE
		else
		{
			// Ranged Defense Mod
			iModifier += rangedDefenseModifier();

			// Unit Class Defense Mod
			iModifier += unitClassDefenseModifier(pOtherUnit->getUnitClassType());
		}
It's clearly adding the rangedDefenseModifier when it is defending from a ranged unit.

The next part I've seen before but skipped over because it had a great big comment saying it was for attacking a city, like this:
Code:
	////////////////////////
	// ATTACKING A CITY
	////////////////////////
but after that it has an else for defense against a city

Code:
	// This Unit on defense
	else
	{
		// No TERRAIN bonuses for this Unit?
		iTempModifier = plot()->defenseModifier(getTeam(), false);

		// If we receive normal defensive bonuses OR iTempModifier is actually a PENALTY, then add in the mod
		if(!noDefensiveBonus() || iTempModifier < 0)
			iModifier += iTempModifier;

		iModifier += getDefenseModifier();
	}
Notice what's missing? There's no rangedDefenseModifier. Now if we changed it to this:
Code:
	// This Unit on defense
	else
	{
		// Where's my cover?
		iModifier += rangedDefenseModifier();

		// No TERRAIN bonuses for this Unit?
		iTempModifier = plot()->defenseModifier(getTeam(), false);

		// If we receive normal defensive bonuses OR iTempModifier is actually a PENALTY, then add in the mod
		if(!noDefensiveBonus() || iTempModifier < 0)
			iModifier += iTempModifier;

		iModifier += getDefenseModifier();
	}
That should add the defense bonus from the cover promotions.

I may have missed it because of the function GetMaxDefenseStrength which does have rangedDefenseModifier in it, but it isn't called for ranged vs. ranged combat, which included cities. Instead GetMaxRangedCombatStrength is called and within that function it deals with whether it's offense of defense.

Also there doesn't seem to be a fortify bonus for ranged units vs. cities. I couldn't test that in game because the 'fortify' box for units in the map editor doesn't seem to work, but I've heard that they don't get a fortify bonus and I don't see it in the code.

I'll learn how to compile the Civ 5 DLL, tomorrow and test it if no one beats me too it.
 
OK it looks like this is not the right place for defense against city attacks. To test I added a +1000000 modifier to see if it works and I could get it for defending from units but not a city. SO I'm going to have to add the million modifier other places and see when it turns up.
 
Here's what I've found now.

Code:
int CvCity::rangeCombatUnitDefense(const CvUnit* pDefender) const
{
	int iDefenderStrength = 0;

	// Use Ranged combat value for defender, UNLESS it's a boat
	if (pDefender->isEmbarked())
	{
		iDefenderStrength = pDefender->GetEmbarkedUnitDefense();;
	}

	else if(!pDefender->isRangedSupportFire() && !pDefender->getDomainType() == DOMAIN_SEA && pDefender->GetMaxRangedCombatStrength(NULL, /*pCity*/ NULL, false, false) > 0)
	{
		[B]iDefenderStrength = pDefender->GetMaxRangedCombatStrength(NULL, /*pCity*/ NULL, false, false);

		// Ranged units take less damage from one another
		iDefenderStrength *= /*125*/ GC.getRANGE_ATTACK_RANGED_DEFENDER_MOD();
		iDefenderStrength /= 100;[/B]
	}
	else
	{
		[U]iDefenderStrength = pDefender->GetMaxDefenseStrength(pDefender->plot(), NULL, /*bFromRangedAttack*/ true);[/U]
	}

	return iDefenderStrength;
}
The bold part is definitely where it does city attacking a ranged unit. I changed it to *1000 to verify and it only increased defense for ranged units against city attacks. The underlined part was only used for cities attacking melee.

That means that GetMaxRangedCombatStrength with null, null, false, false parameters is the right function to look at, but I can't find where in that function I can find something that only affects cities attacking ranged units.
 
I have a fix. It's not how I wanted to fix it but it works. I wanted to have everything be the same just with the defense modifiers applying when cities attack ranged units but I couldn't manage that the was it's set up. The problem is the confusing way it uses ranged attack values for defense. With melee units it had different functions for getting the attack and defense values, but for ranged it uses the same function with variables determining when it's for attack or defense, and I couldn't find a way to change something so it didn't effect something other than what I'm trying to do.

So my solution was to change it so city attacks against a ranged unit use it's regular strength rating. That way cover promotions and terrain defense will apply. Since most ranged units have higher ranged strength this means more damage from cities to ranged units unless they have cover or terrain bonuses. Since siege have ranged attack that is closer to their melee strength rating compared to other ranged units, this means pre-industrial siege units will take comparatively less damage than ranged units of the same era. After that archers are now machine guns and have the same strength for both and siege has 3 range so it no longer matters.

Here's the new code
Code:
int CvCity::rangeCombatUnitDefense(const CvUnit* pDefender) const
{
	int iDefenderStrength = 0;

	// Use Ranged combat value for defender, UNLESS it's a boat
	if (pDefender->isEmbarked())
	{
		iDefenderStrength = pDefender->GetEmbarkedUnitDefense();;
	}
		//M.A. City v Ranged fix mod start

	//else if(!pDefender->isRangedSupportFire() && !pDefender->getDomainType() == DOMAIN_SEA && pDefender->GetMaxRangedCombatStrength(NULL, /*pCity*/ NULL, false, false) > 0)
	//{
	//	iDefenderStrength = pDefender->GetMaxRangedCombatStrength(NULL, /*pCity*/ NULL, false, false);

	//	// Ranged units take less damage from one another
	//	iDefenderStrength *= /*125*/ GC.getRANGE_ATTACK_RANGED_DEFENDER_MOD();
	//	iDefenderStrength /= 100;
	//}
	else
	{
		iDefenderStrength = pDefender->GetMaxDefenseStrength(pDefender->plot(), NULL, /*bFromRangedAttack*/ true);
		if (pDefender->GetMaxRangedCombatStrength(NULL, /*pCity*/ NULL, false, false) > 0)
		{
			// Ranged units take less damage from one another
			iDefenderStrength *= /*125*/ GC.getRANGE_ATTACK_RANGED_DEFENDER_MOD();
			iDefenderStrength /= 100;
		}//M.A. City v Ranged fix mod end
	}

	return iDefenderStrength;
}
Their may be a better way to fix this but this is the only way I know of that will allow ranged units to use cover promotions and terrain when a city attacks.
 
Back
Top Bottom