MoraleDamageFromCloseCombat

Post Reply
Spiret3z
Lance Corporal - SdKfz 222
Lance Corporal - SdKfz 222
Posts: 26
Joined: Thu Dec 14, 2017 7:11 pm

MoraleDamageFromCloseCombat

Post by Spiret3z »

Hallo! How does the game calculate "MoraleDamageFromCloseCombat"?

See MoraleTools.bsf => if (GetAttribArray(me, "MoraleDamageFromCloseCombat", notional_turn) > FIRST_CT_THRESHOLD) // See Macros.bsf. Roughly 5% losses (but allowing for morale damage not being rounded down like actual casualties are.).

The FIRST_CT_THRESHOLD is not exactly % losses. For example the FIRST_CT_THRESHOLD of "550" doesn't mean 5,5 % casualties of the current number of men. Thus if a unit with current number of men 300/480 suffers 18 losses (300*0,055+1 = 17,5 rounded to 18) in doesn't mean reaching this FIRST_CT_THRESHOLD.
Moreover I changed "550" into "250", "500" or "1000" points in Macros.bsf but still can't get the exact number of casualties a unit should suffer to start this calculation.

Using Notepad++ I have found out that maybe the function "ConvertLossFactorToMoraleDamage" is a key. I managed to understand how to get the LossFactor, but how can I convert the LossFactor to MoraleDamage?
Or maybe there is something more than % of casualties suffered by the unit that contributes to the MoraleDamage?

Thanks in advance for any advice!
rbodleyscott
Field of Glory 2
Field of Glory 2
Posts: 28053
Joined: Sun Dec 04, 2005 6:25 pm

Re: MoraleDamageFromCloseCombat

Post by rbodleyscott »

I don’t have access to the source code today so I cannot give you a specific answer just now.

What exactly are you trying to achieve?
Richard Bodley Scott

Image
Spiret3z
Lance Corporal - SdKfz 222
Lance Corporal - SdKfz 222
Posts: 26
Joined: Thu Dec 14, 2017 7:11 pm

Re: MoraleDamageFromCloseCombat

Post by Spiret3z »

rbodleyscott wrote: Sat Sep 28, 2019 5:47 pm I don’t have access to the source code today so I cannot give you a specific answer just now.

What exactly are you trying to achieve?
Dear RbodleyScott, thank you very much for your answer!
From Macros.bsf line 1 we know: // "#define FIRST_CT_THRESHOLD...This is meant to represent ...% losses, but takes into account the fact that morale damage is not rounded down, so is somewhat higher than actual losses in "men". //

I would like to know "How much actual losses in "men" (casualties) should a unit exactly get to trigger this calculation?"
rbodleyscott
Field of Glory 2
Field of Glory 2
Posts: 28053
Joined: Sun Dec 04, 2005 6:25 pm

Re: MoraleDamageFromCloseCombat

Post by rbodleyscott »

Spiret3z wrote: Sat Sep 28, 2019 5:57 pm
rbodleyscott wrote: Sat Sep 28, 2019 5:47 pm I don’t have access to the source code today so I cannot give you a specific answer just now.

What exactly are you trying to achieve?
Dear RbodleyScott, thank you very much for your answer!
From Macros.bsf line 1 we know: // "#define FIRST_CT_THRESHOLD...This is meant to represent ...% losses, but takes into account the fact that morale damage is not rounded down, so is somewhat higher than actual losses in "men". //

I would like to know "How much actual losses in "men" (casualties) should a unit exactly get to trigger this calculation?"
IIRC 5% but it is only approximate for several reasons, one of which being that it is 5% of the first three ranks of models, so it is actually a lower percentage of deeper units.

But the way the code works is a lot more complicated than simply looking at the number of casualties, because it doesn’t actually work using the casualties directly. I can’t explain properly without sight of the source code, which I do not have access to today.

However, once again it would be helpful if you could say what you are attempting to achieve by your intended mod, as I may be able to suggest a simpler method than trying to fully understand the complex way the calculations work.
Richard Bodley Scott

Image
Spiret3z
Lance Corporal - SdKfz 222
Lance Corporal - SdKfz 222
Posts: 26
Joined: Thu Dec 14, 2017 7:11 pm

Re: MoraleDamageFromCloseCombat

Post by Spiret3z »

rbodleyscott wrote: Sat Sep 28, 2019 6:50 pm
Spiret3z wrote: Sat Sep 28, 2019 5:57 pm
rbodleyscott wrote: Sat Sep 28, 2019 5:47 pm I don’t have access to the source code today so I cannot give you a specific answer just now.

What exactly are you trying to achieve?
Dear RbodleyScott, thank you very much for your answer!
From Macros.bsf line 1 we know: // "#define FIRST_CT_THRESHOLD...This is meant to represent ...% losses, but takes into account the fact that morale damage is not rounded down, so is somewhat higher than actual losses in "men". //

I would like to know "How much actual losses in "men" (casualties) should a unit exactly get to trigger this calculation?"
IIRC 5% but it is only approximate for several reasons, one of which being that it is 5% of the first three ranks of models, so it is actually a lower percentage of deeper units.

But the way the code works is a lot more complicated than simply looking at the number of casualties, because it doesn’t actually work using the casualties directly. I can’t explain properly without sight of the source code, which I do not have access to today.

However, once again it would be helpful if you could say what you are attempting to achieve by your intended mod, as I may be able to suggest a simpler method than trying to fully understand the complex way the calculations work.
Dear RbodleyScott, thank you very much for your answer!
rbodleyscott wrote: Sat Sep 28, 2019 6:50 pm // IIRC 5% but it is only approximate for several reasons, one of which being that it is 5% of the first three ranks of models, so it is actually a lower percentage of deeper units.//
- do you mean lines 871-878 in MoraleTools.bsf?:
// Calculate 100 x % morale damage from 100 x % actual damage to unit. (Ignore rear ranks of very big units). (Use 100 x to avoid rounding errors).
FUNCTION ConvertLossFactorToMoraleDamage(me, damage)
{
damage = damage * GetAttrib(me, "UnitSize");
damage /= Min(GetAttrib(me, "UnitSize"), 900);

return damage;
}

It is not relevant to my question as both units have unitsizes 600 (max. 480 men, 2 ranks deep 4*2).
rbodleyscott wrote: Sat Sep 28, 2019 6:50 pm But the way the code works is a lot more complicated than simply looking at the number of casualties, because it doesn’t actually work using the casualties directly. I can’t explain properly without sight of the source code, which I do not have access to today.

However, once again it would be helpful if you could say what you are attempting to achieve by your intended mod, as I may be able to suggest a simpler method than trying to fully understand the complex way the calculations work.
After having already played in FoGII 500+ hours, I think it's time for me to understand what is written in Combat Log during the battle. :D
My unit got a message "Significant melee casualties received this turn: -1". => IDS_CT_SIGNIF_MELEE => // Modifier if significant melee morale damage received
if (GetAttribArray(me, "MoraleDamageFromCloseCombat", notional_turn) > FIRST_CT_THRESHOLD) =>...
But then no information how to calculate MoraleDamageFromCloseCombat.

And thus would like to know "How much actual losses in "men" (casualties) should a unit exactly get to trigger this calculation?"
rbodleyscott
Field of Glory 2
Field of Glory 2
Posts: 28053
Joined: Sun Dec 04, 2005 6:25 pm

Re: MoraleDamageFromCloseCombat

Post by rbodleyscott »

Spiret3z wrote: Sat Sep 28, 2019 8:20 pmAfter having already played in FoGII 500+ hours, I think it's time for me to understand what is written in Combat Log during the battle. :D
My unit got a message "Significant melee casualties received this turn: -1". => IDS_CT_SIGNIF_MELEE => // Modifier if significant melee morale damage received
if (GetAttribArray(me, "MoraleDamageFromCloseCombat", notional_turn) > FIRST_CT_THRESHOLD) =>...
But then no information how to calculate MoraleDamageFromCloseCombat.

And thus would like to know "How much actual losses in "men" (casualties) should a unit exactly get to trigger this calculation?"
The answer is that it isn't the actual losses in "men" that trigger it, it is a surrogate damage value which approximates to that percentage of men. So it isn't possible to answer the question exactly - except by looking at the code - see below.

Code: Select all

lossFactor = RemoveCasualties(me, enemy, enemyDamageInflicted, 1, red);
	moraleDamage = ConvertLossFactorToMoraleDamage(me, lossFactor); // Remove part of the moraleDamage diluting effect of very large units
	SetAttribArray(me, "MoraleDamageFromCloseCombat", notional_turn, GetAttribArray(me, "MoraleDamageFromCloseCombat", notional_turn) + moraleDamage);

Code: Select all

// Removes losses and returns 100 times the % of current strength just lost. Any randomization of damage needs to be done before calling this function.
// Note that it returns 100 x % lost so as to minimise rounding errors.
// if combatLog = 1 then also writes to combat log
// Uses Work Array 0. Note that it calls GetCloseCombatFacingsAndRank() which uses Work Arrays 0 and 1, but this function does not use Work Array 0 till after that function has resolved.
FUNCTION RemoveCasualties(me, enemy, damage, combatLog, red)
{
	int current_strength;
	int men_lost;
	int lossFactor;
	int new_strength;
	int id;
	int impact;

	current_strength = GetAttrib(me, "TotalMen");

	men_lost = GetLosses(me, damage);

	DisplayCasualties(me, men_lost, combatLog, red);

        lossFactor = GetLossFactor(me, damage); // Avoid 0 casualties on elephants reducing lossFactor to 0.

	Log ("Unit, Damage Factor, Men Lost, lossFactor (100 times % damage)", me, damage, men_lost, lossFactor);

	new_strength = current_strength - men_lost;

	//DebugLogX("unit, base man count, men_lost, killed", me, manCount, men_lost, killed);

	SetAttrib(me, "TotalMen", new_strength);
	
	impact = 0;	
	if (GetAttrib(me, "AnimSituation") == 1)
		{
			impact = 1;
		}
	if (enemy != -1)
		{
			if (GetAttrib(enemy, "AnimSituation") == 1)
				{
					impact = 1;
				}
		}

	if (impact == 1)
		{
			AddVizFunctionCall("CasualtiesDie", me); // Called in VizQ so that (hopefully) it won't get enacted before units move
		}
	else
		{
			CasualtiesDie(me);
		}

	SetUnitStatusFlag(me);

	// Also apply proportional losses to carried unit - currently none in FOG2. 
	id = GetLoadedUnit(me);
	if (id != -1)
		{
			current_strength = GetAttrib(id, "TotalMen");
			men_lost = GetLosses(id, damage);
			new_strength = current_strength - men_lost;
			SetAttrib(id, "TotalMen", new_strength);
			
			AddVizFunctionCall("CasualtiesDie", id); // Uncertain whether this code will have the correct result.
			
			SetUnitStatusFlag(id);
		}

	return lossFactor;
}

Code: Select all

FUNCTION GetLossFactor(me, damage)
{
	int lossFactor;
	int unitSize;

	unitSize = GetAttrib(me, "UnitSize");

	// lossFactor is calculated from original strength of unit, as incoming fire/close combat damage is not reduced by unit's losses,
	// and the divisor is based on the unitSize of the unit - with a minimum unitSize of 300 (or double actual unitSize if lower) to stop small units being too vulnerable.
	lossFactor = StartingStrength(me) * damage * 100;
	lossFactor /= Max(unitSize, Min(300, unitSize * 2));
	lossFactor *= 100;
	lossFactor /= GetAttrib(me, "TotalMen");
	

	return lossFactor;
}

Code: Select all

// Calculate 100 x % morale damage from 100 x % actual damage to unit. (Ignore rear ranks of very big units). (Use 100 x to avoid rounding errors).
FUNCTION ConvertLossFactorToMoraleDamage(me, damage)
{
	damage = damage * GetAttrib(me, "UnitSize");
	damage /= Min(GetAttrib(me, "UnitSize"), 900);

	return damage;
}

Also, the shooting % to trigger a cohesion test is complicated by the fact that when (during development) we switched from having units shooting in both players' turns, to having shooting only in the player's own turn, we did not simply double the casualties. Instead each unit actually shoots twice, a separate record is kept for each unit for shooting casualties in the first and second shooting volley, and a cohesion test can be triggered by either one of these, taking in account only first or second volley casualties.

This is to simulate the unit shooting in each turn separately. Although it is convoluted for anyone looking at the code to follow, this was simpler for us than having to completely rebalance the cohesion tests around the higher casualties.

So the shooting casualties for triggering a cohesion test is (approximately) 5% per test, but this corresponds to very approximately 10% over the two volleys. However, as the two volleys are an entirely behind-the-scenes internal mechanism, with only one volley being animated, and the casualties from the two volleys added together before the casualties are reported, you will never be able to correlate the % losses used to determine whether a cohesion test is triggered against the reported losses. For example, the unit might only lose 7% casualties overall, but if this was 5% on the first volley and 2% on the second volley, a cohesion test would be triggered by the first volley. If both volleys caused over 5% casualties each, a cohesion test would only be taken for the first volley. If the casualties were 2% and 5% respectively, a cohesion test would only be taken for the second volley.
Richard Bodley Scott

Image
Spiret3z
Lance Corporal - SdKfz 222
Lance Corporal - SdKfz 222
Posts: 26
Joined: Thu Dec 14, 2017 7:11 pm

Re: MoraleDamageFromCloseCombat

Post by Spiret3z »

Dear Rbodleyscott, thank you very much for your answer!

Your explanation with shooting % is very useful, but my topic is about melee engagement between 2 infantry units (480 men, 600 unitsize each).

As I have written, we can get the LossFactor. There are 2 ways to get it, both in the file CombatTools.bsf.
Either lines 470-472

Code: Select all

//	lossFactor = men_lost * 10000;
//	lossFactor /= current_strength;
  lossFactor = GetLossFactor(me, damage); // Avoid 0 casualties on elephants reducing lossFactor to 0.//
Or lines 6721-6737 (as you have already mentioned above)

Code: Select all

// FUNCTION GetLossFactor(me, damage)
{
	int lossFactor;
	int unitSize;

	unitSize = GetAttrib(me, "UnitSize");

	// lossFactor is calculated from original strength of unit, as incoming fire/close combat damage is not reduced by unit's losses,
	// and the divisor is based on the unitSize of the unit - with a minimum unitSize of 300 (or double actual unitSize if lower) to stop small units being too vulnerable.
	lossFactor = StartingStrength(me) * damage * 100;
	lossFactor /= Max(unitSize, Min(300, unitSize * 2));
	lossFactor *= 100;
	lossFactor /= GetAttrib(me, "TotalMen");
	

	return lossFactor;
}
But then there is the question, how we can get this (lines 404-405 in MoraleTools.bsf)?

Code: Select all

// Modifier if significant melee morale damage received
			if (GetAttribArray(me, "MoraleDamageFromCloseCombat", notional_turn) > FIRST_CT_THRESHOLD) //
?

The only function I can see is this one (lines 871-878 in MoraleTools.bsf),

Code: Select all

// Calculate 100 x % morale damage from 100 x % actual damage to unit. (Ignore rear ranks of very big units). (Use 100 x to avoid rounding errors).
FUNCTION ConvertLossFactorToMoraleDamage(me, damage)
{
	damage = damage * GetAttrib(me, "UnitSize");
	damage /= Min(GetAttrib(me, "UnitSize"), 900);

	return damage;
}
, but it is not relevant to units with 600 unitsize 2*4 ranks deep each.

And therefore I get the LossFactor, but it isn't the morale damage and thus cannot be compared with the FIRST_CT_THRESHOLD. The LossFactor tends to be much higher than the required number of points of the FIRST_CT_THRESHOLD (See Macros.bsf).

So, how does the game transform the LossFactor into the MoraleDamage in melee engagement?
rbodleyscott
Field of Glory 2
Field of Glory 2
Posts: 28053
Joined: Sun Dec 04, 2005 6:25 pm

Re: MoraleDamageFromCloseCombat

Post by rbodleyscott »

Spiret3z wrote: Mon Sep 30, 2019 10:56 pm Dear Rbodleyscott, thank you very much for your answer!

Your explanation with shooting % is very useful, but my topic is about melee engagement between 2 infantry units (480 men, 600 unitsize each).

As I have written, we can get the LossFactor. There are 2 ways to get it, both in the file CombatTools.bsf.
Either lines 470-472

Code: Select all

//	lossFactor = men_lost * 10000;
//	lossFactor /= current_strength;
  lossFactor = GetLossFactor(me, damage); // Avoid 0 casualties on elephants reducing lossFactor to 0.//
The top two lines are commented out, so the parser ignores them. It is the third line that gets the lossFactor.
So, how does the game transform the LossFactor into the MoraleDamage in melee engagement?

Code: Select all

moraleDamage = ConvertLossFactorToMoraleDamage(me, lossFactor); // Remove part of the moraleDamage diluting effect of very large units
	SetAttribArray(me, "MoraleDamageFromCloseCombat", notional_turn, GetAttribArray(me, "MoraleDamageFromCloseCombat", notional_turn) + moraleDamage);
Richard Bodley Scott

Image
Post Reply

Return to “Field of Glory II: Modding”