//Calculates the probability of a particular combat outcome
//Returns a float value (between 0 and 1)
//Written by PieceOfMind
//n_A = hits taken by attacker, n_D = hits taken by defender.
float getCombatOddsSpecific(CvUnit* pAttacker, CvUnit* pDefender, int n_A, int n_D)
{
int iAttackerStrength;
int iAttackerFirepower;
int iDefenderStrength;
int iDefenderFirepower;
int iDefenderOdds;
int iAttackerOdds;
int iStrengthFactor;
int iDamageToAttacker;
int iDamageToDefender;
int iNeededRoundsAttacker;
//int iNeededRoundsDefender;
int AttFSnet;
int AttFSC;
int DefFSC;
int iDefenderHitLimit;
//TB Combat Mods (Armor Compare)
int iAttackArmorTotal = pAttacker->armorVSOpponentProbTotal(pDefender);
int iDefendPunctureTotal = pDefender->punctureVSOpponentProbTotal(pAttacker);
int iAttackPunctureTotal = pAttacker->punctureVSOpponentProbTotal(pDefender);
int iDefendArmorTotal = pDefender->armorVSOpponentProbTotal(pAttacker);
int iUnmodifiedDefenderArmor = (iDefendArmorTotal - iAttackPunctureTotal);
int iUnmodifiedAttackerArmor = (iAttackArmorTotal - iDefendPunctureTotal);
int iModifiedDefenderArmorZero = (iUnmodifiedDefenderArmor < 0 ? 0 : iUnmodifiedDefenderArmor);
int iModifiedAttackerArmorZero = (iUnmodifiedAttackerArmor < 0 ? 0 : iUnmodifiedAttackerArmor);
int iModifiedDefenderArmor = (iModifiedDefenderArmorZero > 95 ? 95 : iModifiedDefenderArmorZero);
int iModifiedAttackerArmor = (iModifiedAttackerArmorZero > 95 ? 95 : iModifiedAttackerArmorZero);
int iDefenderArmor = (100 - iModifiedDefenderArmor);
int iAttackerArmor = (100 - iModifiedAttackerArmor);
int iDefenderSupportStrength = 0;
int iAttackerSupportStrength = 0;
if (GC.getGameINLINE().isOption(GAMEOPTION_STRENGTH_IN_NUMBERS))
{
iDefenderSupportStrength = pDefender->getDefenderSupportValue(pAttacker);
iAttackerSupportStrength = pAttacker->getAttackerSupportValue();
}
//TB Combat Mods End
iAttackerStrength = pAttacker->currCombatStr(NULL, NULL) + iAttackerSupportStrength;
iAttackerFirepower = pAttacker->currFirepower(NULL, NULL) + iAttackerSupportStrength;
iDefenderStrength = pDefender->currCombatStr(pDefender->plot(), pAttacker) + iDefenderSupportStrength;
iDefenderFirepower = pDefender->currFirepower(pDefender->plot(), pAttacker) + iDefenderSupportStrength;
iStrengthFactor = ((iAttackerFirepower + iDefenderFirepower + 1) / 2);
int iDefendDamageModifierTotal = pDefender->damageModifierTotal();
int iAttackDamageModifierTotal = pAttacker->damageModifierTotal();
int iDamageToAttackerBase = ((GC.getDefineINT("COMBAT_DAMAGE") * (iDefenderFirepower + iStrengthFactor)) / std::max(1,(iAttackerFirepower + iStrengthFactor)));
int iDamageToDefenderBase = ((GC.getDefineINT("COMBAT_DAMAGE") * (iAttackerFirepower + iStrengthFactor)) / std::max(1,(iDefenderFirepower + iStrengthFactor)));
int iDamageToAttackerModified = iDamageToAttackerBase + ((iDamageToAttackerBase * iDefendDamageModifierTotal)/100);
int iDamageToDefenderModified = iDamageToDefenderBase + ((iDamageToDefenderBase * iAttackDamageModifierTotal)/100);
int iDamageToAttackerArmor = (iDamageToAttackerModified * iAttackerArmor)/100;
int iDamageToDefenderArmor = (iDamageToDefenderModified * iDefenderArmor)/100;
iDamageToAttacker = std::max(1, iDamageToAttackerArmor);
iDamageToDefender = std::max(1, iDamageToDefenderArmor);
//TB Combat Mods Begin (Armor/Puncture)
// iDamageToAttacker = std::max(1,((GC.getDefineINT("COMBAT_DAMAGE") * (iDefenderFirepower + iStrengthFactor)) / (iAttackerFirepower + iStrengthFactor)));
// iDamageToDefender = std::max(1,((GC.getDefineINT("COMBAT_DAMAGE") * (iAttackerFirepower + iStrengthFactor)) / (iDefenderFirepower + iStrengthFactor)));
//iDamageToAttacker = std::max(1, ((((GC.getDefineINT("COMBAT_DAMAGE") * (iDefenderFirepower + iStrengthFactor)) / (iAttackerFirepower + iStrengthFactor)) * iAttackerArmor)/100));
//iDamageToDefender = std::max(1, ((((GC.getDefineINT("COMBAT_DAMAGE") * (iAttackerFirepower + iStrengthFactor)) / (iDefenderFirepower + iStrengthFactor)) * iDefenderArmor)/100));
//TB Combat Mods end (Armor/Puncture)
//TB Combat Mods begin (Dodge/Precision)
int iDefenderDodge = pDefender->dodgeVSOpponentProbTotal(pAttacker);
int iDefenderPrecision = pDefender->precisionVSOpponentProbTotal(pAttacker);
int iAttackerDodge = pAttacker->dodgeVSOpponentProbTotal(pDefender);
int iAttackerPrecision = pAttacker->precisionVSOpponentProbTotal(pDefender);
int iAttackerHitModifier = iAttackerPrecision - iDefenderDodge;
int iDefenderHitModifier = iDefenderPrecision - iAttackerDodge;
int iDefenderInitialOdds = ((GC.getCOMBAT_DIE_SIDES() * iDefenderStrength) / (iAttackerStrength + iDefenderStrength));
int iDefenderHitOdds = std::max(5, iDefenderInitialOdds + ((iDefenderHitModifier * iDefenderInitialOdds)/100));
int iAttackerInitialOdds = GC.getCOMBAT_DIE_SIDES() - iDefenderInitialOdds;
int iAttackerHitOdds = std::max(5, iAttackerInitialOdds + ((iAttackerHitModifier * iAttackerInitialOdds)/100));
iDefenderOdds = ((iDefenderHitOdds - iAttackerHitOdds)+ GC.getCOMBAT_DIE_SIDES())/2;
iAttackerOdds = ((iAttackerHitOdds - iDefenderHitOdds)+ GC.getCOMBAT_DIE_SIDES())/2;
//original:
//iDefenderOdds = ((GC.getDefineINT("COMBAT_DIE_SIDES") * iDefenderStrength) / (iAttackerStrength + iDefenderStrength));
//iAttackerOdds = GC.getDefineINT("COMBAT_DIE_SIDES") - iDefenderOdds;
//TB Combat Mods end (Dodge/Precision)
if (GC.getDefineINT("ACO_IgnoreBarbFreeWins")==0)
{
if (pDefender->isBarbarian())
{
//defender is barbarian
if (!GET_PLAYER(pAttacker->getOwnerINLINE()).isBarbarian() && GET_PLAYER(pAttacker->getOwnerINLINE()).getWinsVsBarbs() < GC.getHandicapInfo(GET_PLAYER(pAttacker->getOwnerINLINE()).getHandicapType()).getFreeWinsVsBarbs())
{
//attacker is not barb and attacker player has free wins left
//I have assumed in the following code only one of the units (attacker and defender) can be a barbarian
iDefenderOdds = std::min((10 * GC.getDefineINT("COMBAT_DIE_SIDES")) / 100, iDefenderOdds);
iAttackerOdds = std::max((90 * GC.getDefineINT("COMBAT_DIE_SIDES")) / 100, iAttackerOdds);
}
}
else if (pAttacker->isBarbarian())
{
//attacker is barbarian
if (!GET_PLAYER(pDefender->getOwnerINLINE()).isBarbarian() && GET_PLAYER(pDefender->getOwnerINLINE()).getWinsVsBarbs() < GC.getHandicapInfo(GET_PLAYER(pDefender->getOwnerINLINE()).getHandicapType()).getFreeWinsVsBarbs())
{
//defender is not barbarian and defender has free wins left and attacker is barbarian
iAttackerOdds = std::min((10 * GC.getDefineINT("COMBAT_DIE_SIDES")) / 100, iAttackerOdds);
iDefenderOdds = std::max((90 * GC.getDefineINT("COMBAT_DIE_SIDES")) / 100, iDefenderOdds);
}
}
}
iDefenderHitLimit = pDefender->maxHitPoints() - pAttacker->combatLimit(pDefender);
//iNeededRoundsAttacker = (std::max(0, pDefender->currHitPoints() - iDefenderHitLimit) + iDamageToDefender - (((pAttacker->combatLimit())==GC.getMAX_HIT_POINTS())?1:0) ) / iDamageToDefender;
iNeededRoundsAttacker = (pDefender->currHitPoints() - pDefender->maxHitPoints() + pAttacker->combatLimit(pDefender) - (((pAttacker->combatLimit(pDefender))==pDefender->maxHitPoints())?1:0))/iDamageToDefender + 1;
//TB Combat Mods begin
int iNeededRoundsDefender = (pAttacker->currHitPoints() + iDamageToAttacker - 1 ) / iDamageToAttacker;
//TB Combat Mods end
int N_D = (std::max(0, pDefender->currHitPoints() - iDefenderHitLimit) + iDamageToDefender - (((pAttacker->combatLimit(pDefender))==pDefender->maxHitPoints())?1:0) ) / iDamageToDefender;
//int N_A = (pAttacker->currHitPoints() + iDamageToAttacker - 1 ) / iDamageToAttacker; //same as next line
int N_A = (pAttacker->currHitPoints() - 1)/iDamageToAttacker + 1;
//int iRetreatOdds = std::max((pAttacker->withdrawalProbability()),100);
// TB Combat Mods:
// Determine Attack Withdraw odds
int iAttackerWithdraw = pAttacker->withdrawVSOpponentProbTotal(pDefender, pDefender->plot());
int iDefenderPursuit = pDefender->pursuitVSOpponentProbTotal(pAttacker);
int iAttackerEarly = pAttacker->earlyWithdrawTotal();
int AdjustedAttWithdrawalstep1 = iAttackerWithdraw - iDefenderPursuit;
int AdjustedAttWithdrawalstep2 = ((AdjustedAttWithdrawalstep1 > 100) ? 100 : AdjustedAttWithdrawalstep1);
int AdjustedAttWithdrawal = ((AdjustedAttWithdrawalstep2 < 0) ? 0 : AdjustedAttWithdrawalstep2);
int expectedrndcnt = std::min(iNeededRoundsDefender, iNeededRoundsAttacker);
int expectedrnds = ((expectedrndcnt * iAttackerEarly)/100);
int y = AdjustedAttWithdrawal;
int z = AdjustedAttWithdrawal;
int Time;
for (Time = 0; Time < expectedrnds; ++Time)
{
z += ((AdjustedAttWithdrawal * y)/100);
y = ((AdjustedAttWithdrawal * (100 - z))/100); // Prob next round is prob per round times prob you haven't already
}
int EvaluatedAttWithdrawOdds = z;
// Determine Attack Knockback odds
int iAttackerKnockback = pAttacker->knockbackVSOpponentProbTotal(pDefender);
int iDefenderUnyielding = pDefender->unyieldingTotal();
int iAttackerKnockbackTries = pAttacker->knockbackRetriesTotal() + 1;
int AdjustedKnockbackstep1 = iAttackerKnockback - iDefenderUnyielding;
int AdjustedKnockbackstep2 = ((AdjustedKnockbackstep1 > 100) ? 100 : AdjustedKnockbackstep1);
int AdjustedKnockback = ((AdjustedKnockbackstep2 < 0) ? 0 : AdjustedKnockbackstep2);
y = AdjustedKnockback;
z = AdjustedKnockback;
for (Time = 0; Time < iAttackerKnockbackTries; ++Time)
{
z += ((AdjustedKnockback * y)/100);
y = ((AdjustedKnockback * (100 - z))/100); // Prob next round is prob per round times prob you haven't already
}
int EvaluatedKnockbackOdds = z;
// Determine Defensive Withdrawal odds
int iDefenderWithdraw = pDefender->withdrawVSOpponentProbTotal(pAttacker, pDefender->plot());
int iAttackerPursuit = pAttacker->pursuitVSOpponentProbTotal(pDefender);
int iDefenderEarly = pDefender->earlyWithdrawTotal();
int AdjustedDefWithdrawalstep1 = iDefenderWithdraw - iAttackerPursuit;
int AdjustedDefWithdrawalstep2 = ((AdjustedDefWithdrawalstep1 > 100) ? 100 : AdjustedDefWithdrawalstep1);
int AdjustedDefWithdrawal = ((AdjustedDefWithdrawalstep2 < 0) ? 0 : AdjustedDefWithdrawalstep2);
expectedrnds = ((expectedrndcnt * iDefenderEarly)/100);
y = AdjustedDefWithdrawal;
z = AdjustedDefWithdrawal;
for (Time = 0; Time < expectedrnds; ++Time)
{
z += ((AdjustedDefWithdrawal * y)/100);
y = ((AdjustedDefWithdrawal * (100 - z))/100); // Prob next round is prob per round times prob you haven't already
}
int EvaluatedDefWithdrawalOdds = z;
// Fortify, Repel Odds
int iDefenderFortifyTotal = pDefender->fortifyModifier();
int iDefenderRepel = pDefender->repelVSOpponentProbTotal(pAttacker);
int iDefenderFortRepel = pDefender->fortifyRepelModifier();
int iRepelAttempts = (pDefender->repelRetriesTotal() + 1);
int iAttackerOverrun = pAttacker->overrunTotal();
int iAttackerUnyielding = pAttacker->unyieldingTotal();
int iFortRepellessOverrun = iDefenderFortRepel - iAttackerOverrun;
int iFortRepelZero = (iFortRepellessOverrun < 0 ? 0 : iFortRepellessOverrun);
int iFortRepelTotal = (iFortRepelZero > 100 ? 100 : iFortRepelZero);
int iDefenderRepelwithFortRepel = iDefenderRepel + iFortRepelTotal;
int iRepelwithUnyielding = iDefenderRepelwithFortRepel - iAttackerUnyielding;
int iRepelZero = (iRepelwithUnyielding < 0 ? 0 : iRepelwithUnyielding);
int iRepelTotal = (iRepelZero > 100 ? 100 : iRepelZero);
int iFortifylessOverrun = iDefenderFortifyTotal - iAttackerOverrun;
int iFortifyTotal = (iFortifylessOverrun < 0 ? 0 : iFortifylessOverrun);
y = iRepelTotal;
z = iRepelTotal;
for (Time = 0; Time < iRepelAttempts; ++Time)
{
z += ((iRepelTotal * y)/100);
y = ((iRepelTotal * (100 - z))/100); // Prob next round is prob per round times prob you haven't already
}
int EvaluatedRepelOdds = z;
float RetreatOdds = ((float)(std::min((EvaluatedAttWithdrawOdds),100)))/100.0f ;
float DefRetreatOdds = ((float)(std::min((EvaluatedDefWithdrawalOdds),100)))/100.0f;
float RepelOdds = ((float)(std::min((EvaluatedRepelOdds),100)))/100.0f ;
float KnockbackOdds = ((float)(std::min((EvaluatedKnockbackOdds),100)))/100.0f ;
//TB Combat Mods End (above original:float RetreatOdds = ((float)(std::min(pAttacker->withdrawalProbability(),100)))/100.0f ;
AttFSnet = ( (pDefender->immuneToFirstStrikes()) ? 0 : pAttacker->firstStrikes() ) - ((pAttacker->immuneToFirstStrikes()) ? 0 : pDefender->firstStrikes());
AttFSC = (pDefender->immuneToFirstStrikes()) ? 0 : (pAttacker->chanceFirstStrikes());
DefFSC = (pAttacker->immuneToFirstStrikes()) ? 0 : (pDefender->chanceFirstStrikes());
float P_A = (float)iAttackerOdds / GC.getDefineINT("COMBAT_DIE_SIDES");
float P_D = (float)iDefenderOdds / GC.getDefineINT("COMBAT_DIE_SIDES");
float answer = 0.0f;
if (n_A < N_A && n_D == iNeededRoundsAttacker) // (1) Defender dies or is taken to combat limit
{
float sum1 = 0.0f;
for (int i = (-AttFSnet-AttFSC<1?1:-AttFSnet-AttFSC); i <= DefFSC - AttFSnet; i++)
{
for (int j = 0; j <= i; j++)
{
if (n_A >= j)
{
sum1 += (float)getBinomialCoefficient(i,j) * pow(P_A,(float)(i-j)) * getBinomialCoefficient(iNeededRoundsAttacker-1+n_A-j,iNeededRoundsAttacker-1);
} //if
}//for j
}//for i
sum1 *= pow(P_D,(float)n_A)*pow(P_A,(float)iNeededRoundsAttacker);
answer += sum1;
float sum2 = 0.0f;
for (int i = (0<AttFSnet-DefFSC?AttFSnet-DefFSC:0); i <= AttFSnet + AttFSC; i++)
{
for (int j = 0; j <= i; j++)
{
if (N_D > j)
{
sum2 = sum2 + getBinomialCoefficient(n_A+iNeededRoundsAttacker-j-1,n_A) * (float)getBinomialCoefficient(i,j) * pow(P_A,(float)iNeededRoundsAttacker) * pow(P_D,(float)(n_A+i-j));
}
else if (n_A == 0)
{
sum2 = sum2 + (float)getBinomialCoefficient(i,j) * pow(P_A,(float)j) * pow(P_D,(float)(i-j));
}
else
{
sum2 = sum2 + 0.0f;
}
}//for j
}//for i
answer += sum2;
}
else if (n_D < N_D && n_A == N_A) // (2) Attacker dies!
{
float sum1 = 0.0f;
for (int i = (-AttFSnet-AttFSC<1?1:-AttFSnet-AttFSC); i <= DefFSC - AttFSnet; i++)
{
for (int j = 0; j <= i; j++)
{
if (N_A>j)
{
sum1 += getBinomialCoefficient(n_D+N_A-j-1,n_D) * (float)getBinomialCoefficient(i,j) * pow(P_D,(float)(N_A)) * pow(P_A,(float)(n_D+i-j));
}
else
{
if (n_D == 0)
{
sum1 += (float)getBinomialCoefficient(i,j) * pow(P_D,(float)(j)) * pow(P_A,(float)(i-j));
}//if (inside if) else sum += 0
}//if
}//for j
}//for i
answer += sum1;
float sum2 = 0.0f;
for (int i = (0<AttFSnet-DefFSC?AttFSnet-DefFSC:0); i <= AttFSnet + AttFSC; i++)
{
for (int j = 0; j <= i; j++)
{
if (n_D >= j)
{
sum2 += (float)getBinomialCoefficient(i,j) * pow(P_D,(float)(i-j)) * getBinomialCoefficient(N_A-1+n_D-j,N_A-1);
} //if
}//for j
}//for i
sum2 *= pow(P_A,(float)(n_D))*pow(P_D,(float)(N_A));
answer += sum2;
//TB Combat Mods (Repel & Knockback)
//orig: answer = answer * (1.0f - RetreatOdds);
answer = answer * (1.0f - RetreatOdds) * (1.0f - RepelOdds) * (1.0f - KnockbackOdds) * (1.0f - DefRetreatOdds);
//TB Combat Mods End
}
//TB Combat Mods begin - original: else if (n_A == (N_A-1) && n_D < N_D) // (3) Attacker retreats!
else if (n_A == (N_A-1) && n_D < N_D) // (3) Attacker retreats, is repelled or knocks opponent back!
//TB Combat Mods end
{
float sum1 = 0.0f;
for (int i = (AttFSnet+AttFSC>-1?1:-AttFSnet-AttFSC); i <= DefFSC - AttFSnet; i++)
{
for (int j = 0; j <= i; j++)
{
if (N_A>j)
{
sum1 += getBinomialCoefficient(n_D+N_A-j-1,n_D) * (float)getBinomialCoefficient(i,j) * pow(P_D,(float)(N_A)) * pow(P_A,(float)(n_D+i-j));
}
else
{
if (n_D == 0)
{
sum1 += (float)getBinomialCoefficient(i,j) * pow(P_D,(float)(j)) * pow(P_A,(float)(i-j));
}//if (inside if) else sum += 0
}//if
}//for j
}//for i
answer += sum1;
float sum2 = 0.0f;
for (int i = (0<AttFSnet-DefFSC?AttFSnet-DefFSC:0); i <= AttFSnet + AttFSC; i++)
{
for (int j = 0; j <= i; j++)
{
if (n_D >= j)
{
sum2 += (float)getBinomialCoefficient(i,j) * pow(P_D,(float)(i-j)) * getBinomialCoefficient(N_A-1+n_D-j,N_A-1);
} //if
}//for j
}//for i
sum2 *= pow(P_A,(float)(n_D))*pow(P_D,(float)(N_A));
answer += sum2;
//TB Combat Mods (Repel & Knockback)
//orig: answer = answer * RetreatOdds;
answer = answer * RetreatOdds * RepelOdds * KnockbackOdds * DefRetreatOdds;
//TB Combat Mods End
}
else
{
//Unexpected value. Process should not reach here.
}
answer = answer / ((float)(AttFSC+DefFSC+1)); // dividing by (t+w+1) as is necessary
return answer;
}// getCombatOddsSpecific