// FUNCTION: getCombatOdds
// Calculates combat odds, given two units
// Returns value from 0-1000
// Written by DeepO
int getCombatOdds(CvUnit* pAttacker, CvUnit* pDefender)
{
float fOddsEvent;
float fOddsAfterEvent;
int iAttackerStrength;
int iAttackerFirepower;
int iDefenderStrength;
int iDefenderFirepower;
int iDefenderOdds;
int iAttackerOdds;
int iStrengthFactor;
int iDamageToAttacker;
int iDamageToDefender;
int iNeededRoundsAttacker;
int iNeededRoundsDefender;
int iMaxRounds;
int iAttackerLowFS;
int iAttackerHighFS;
int iDefenderLowFS;
int iDefenderHighFS;
int iFirstStrikes;
int iDefenderHitLimit;
int iI;
int iJ;
int iI3;
int iI4;
int iOdds = 0;
// setup battle, calculate strengths and odds
//////
//TB Combat Mod begin
//Added ST
iAttackerStrength = pAttacker->currCombatStr(NULL, NULL);
iAttackerFirepower = pAttacker->currFirepower(NULL, NULL);
iDefenderStrength = pDefender->currCombatStr(pDefender->plot(), pAttacker);
iDefenderFirepower = pDefender->currFirepower(pDefender->plot(), pAttacker);
#ifdef STRENGTH_IN_NUMBERS
if (GC.getGameINLINE().isOption(GAMEOPTION_STRENGTH_IN_NUMBERS))
{
int iAttackerSupportStrength = pAttacker->getAttackerSupportValue();
iAttackerStrength += iAttackerSupportStrength;
iAttackerFirepower += iAttackerSupportStrength;
int iDefenderSupportStrength = pDefender->getDefenderSupportValue(pAttacker);
iDefenderStrength += iDefenderSupportStrength;
iDefenderFirepower += iDefenderSupportStrength;
}
#endif // STRENGTH_IN_NUMBERS
//TB Combat Mod end
FAssert((iAttackerStrength + iDefenderStrength) > 0);
FAssert((iAttackerFirepower + iDefenderFirepower) > 0);
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 02/21/10 jdog5000 */
/* */
/* Efficiency, Lead From Behind */
/************************************************************************************************/
// From Lead From Behind by UncutDragon
/* original code
iDefenderOdds = ((GC.getDefineINT("COMBAT_DIE_SIDES") * iDefenderStrength) / (iAttackerStrength + iDefenderStrength));
*/ // modified
/*iDefenderOdds = ((GC.getCOMBAT_DIE_SIDES() * iDefenderStrength) / (iAttackerStrength + iDefenderStrength));*/
// /UncutDragon
//TB Combat Mod begin
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) / std::max(1,(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;
//TB Combat Mods end
if (iDefenderOdds == 0)
{
return 1000;
}
// UncutDragon
/* original code
iAttackerOdds = GC.getDefineINT("COMBAT_DIE_SIDES") - iDefenderOdds;
*/ // modified
//iAttackerOdds = GC.getCOMBAT_DIE_SIDES() - iDefenderOdds;
// /UncutDragon
if (iAttackerOdds == 0)
{
return 0;
}
iStrengthFactor = ((iAttackerFirepower + iDefenderFirepower + 1) / 2);
// calculate damage done in one round
//////
//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 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 (Armor Compare) end
// UncutDragon
/* original code
iDamageToAttacker = std::max(1,((GC.getDefineINT("COMBAT_DAMAGE") * (iDefenderFirepower + iStrengthFactor)) / (iAttackerFirepower + iStrengthFactor)));
iDamageToDefender = std::max(1,((GC.getDefineINT("COMBAT_DAMAGE") * (iAttackerFirepower + iStrengthFactor)) / (iDefenderFirepower + iStrengthFactor)));
*/ // modified
//TB Combat Mods (Armor) begin
//iDamageToAttacker = std::max(1,((((GC.getCOMBAT_DAMAGE() * (iDefenderFirepower + iStrengthFactor)) / (iAttackerFirepower + iStrengthFactor)) * iAttackerArmor)/100));
//iDamageToDefender = std::max(1,((((GC.getCOMBAT_DAMAGE() * (iAttackerFirepower + iStrengthFactor)) / (iDefenderFirepower + iStrengthFactor)) * iDefenderArmor)/100));
//TB Combat Mods (Armor) end
// /UncutDragon
// calculate needed rounds.
// Needed rounds = round_up(health/damage)
//////
iDefenderHitLimit = pDefender->maxHitPoints() - pAttacker->combatLimit(pDefender);
iNeededRoundsAttacker = (std::max(0, pDefender->currHitPoints() - iDefenderHitLimit) + iDamageToDefender - 1 ) / iDamageToDefender;
iNeededRoundsDefender = (pAttacker->currHitPoints() + iDamageToAttacker - 1 ) / iDamageToAttacker;
iMaxRounds = iNeededRoundsAttacker + iNeededRoundsDefender - 1;
// calculate possible first strikes distribution.
// We can't use the getCombatFirstStrikes() function (only one result,
// no distribution), so we need to mimic it.
//////
iAttackerLowFS = (pDefender->immuneToFirstStrikes()) ? 0 : pAttacker->firstStrikes();
iAttackerHighFS = (pDefender->immuneToFirstStrikes()) ? 0 : (pAttacker->firstStrikes() + pAttacker->chanceFirstStrikes());
iDefenderLowFS = (pAttacker->immuneToFirstStrikes()) ? 0 : pDefender->firstStrikes();
iDefenderHighFS = (pAttacker->immuneToFirstStrikes()) ? 0 : (pDefender->firstStrikes() + pDefender->chanceFirstStrikes());
// For every possible first strike event, calculate the odds of combat.
// Then, add these to the total, weighted to the chance of that first
// strike event occurring
//////
for (iI = iAttackerLowFS; iI < iAttackerHighFS + 1; iI++)
{
for (iJ = iDefenderLowFS; iJ < iDefenderHighFS + 1; iJ++)
{
// for every possible combination of fs results, calculate the chance
if (iI >= iJ)
{
// Attacker gets more or equal first strikes than defender
iFirstStrikes = iI - iJ;
// For every possible first strike getting hit, calculate both
// the chance of that event happening, as well as the rest of
// the chance assuming the event has happened. Multiply these
// together to get the total chance (Bayes rule).
// iI3 counts the number of successful first strikes
//////
for (iI3 = 0; iI3 < (iFirstStrikes + 1); iI3++)
{
// event: iI3 first strikes hit the defender
// calculate chance of iI3 first strikes hitting: fOddsEvent
// f(k;n,p)=C(n,k)*(p^k)*((1-p)^(n-k))
// this needs to be in floating point math
//////
// UncutDragon
/* original code
fOddsEvent = ((float)getBinomialCoefficient(iFirstStrikes, iI3)) * pow((((float)iAttackerOdds) / GC.getDefineINT("COMBAT_DIE_SIDES")), iI3) * pow((1.0f - (((float)iAttackerOdds) / GC.getDefineINT("COMBAT_DIE_SIDES"))), (iFirstStrikes - iI3));
*/ // modified
fOddsEvent = ((float)getBinomialCoefficient(iFirstStrikes, iI3)) * pow((((float)iAttackerOdds) / GC.getCOMBAT_DIE_SIDES()), iI3) * pow((1.0f - (((float)iAttackerOdds) / GC.getCOMBAT_DIE_SIDES())), (iFirstStrikes - iI3));
// /UncutDragon
// calculate chance assuming iI3 first strike hits: fOddsAfterEvent
//////
if (iI3 >= iNeededRoundsAttacker)
{
fOddsAfterEvent = 1;
}
else
{
fOddsAfterEvent = 0;
// odds for _at_least_ (iNeededRoundsAttacker - iI3) (the remaining hits
// the attacker needs to make) out of (iMaxRounds - iI3) (the left over
// rounds) is the sum of each _exact_ draw
//////
for (iI4 = (iNeededRoundsAttacker - iI3); iI4 < (iMaxRounds - iI3 + 1); iI4++)
{
// odds of exactly iI4 out of (iMaxRounds - iI3) draws.
// f(k;n,p)=C(n,k)*(p^k)*((1-p)^(n-k))
// this needs to be in floating point math
//////
// UncutDragon
/* original code
fOddsAfterEvent += ((float)getBinomialCoefficient((iMaxRounds - iI3), iI4)) * pow((((float)iAttackerOdds) / GC.getDefineINT("COMBAT_DIE_SIDES")), iI4) * pow((1.0f - (((float)iAttackerOdds) / GC.getDefineINT("COMBAT_DIE_SIDES"))), ((iMaxRounds - iI3) - iI4));
*/ // modified
fOddsAfterEvent += ((float)getBinomialCoefficient((iMaxRounds - iI3), iI4)) * pow((((float)iAttackerOdds) / GC.getCOMBAT_DIE_SIDES()), iI4) * pow((1.0f - (((float)iAttackerOdds) / GC.getCOMBAT_DIE_SIDES())), ((iMaxRounds - iI3) - iI4));
// /UncutDragon
}
}
// Multiply these together, round them properly, and add
// the result to the total iOdds
//////
iOdds += ((int)(1000.0 * (fOddsEvent*fOddsAfterEvent + 0.0005)));
}
}
else // (iI < iJ)
{
// Attacker gets less first strikes than defender
iFirstStrikes = iJ - iI;
// For every possible first strike getting hit, calculate both
// the chance of that event happening, as well as the rest of
// the chance assuming the event has happened. Multiply these
// together to get the total chance (Bayes rule).
// iI3 counts the number of successful first strikes
//////
for (iI3 = 0; iI3 < (iFirstStrikes + 1); iI3++)
{
// event: iI3 first strikes hit the defender
// First of all, check if the attacker is still alive.
// Otherwise, no further calculations need to occur
/////
if (iI3 < iNeededRoundsDefender)
{
// calculate chance of iI3 first strikes hitting: fOddsEvent
// f(k;n,p)=C(n,k)*(p^k)*((1-p)^(n-k))
// this needs to be in floating point math
//////
// UncutDragon
/* original code
fOddsEvent = ((float)getBinomialCoefficient(iFirstStrikes, iI3)) * pow((((float)iDefenderOdds) / GC.getDefineINT("COMBAT_DIE_SIDES")), iI3) * pow((1.0f - (((float)iDefenderOdds) / GC.getDefineINT("COMBAT_DIE_SIDES"))), (iFirstStrikes - iI3));
*/ // modified
fOddsEvent = ((float)getBinomialCoefficient(iFirstStrikes, iI3)) * pow((((float)iDefenderOdds) / GC.getCOMBAT_DIE_SIDES()), iI3) * pow((1.0f - (((float)iDefenderOdds) / GC.getCOMBAT_DIE_SIDES())), (iFirstStrikes - iI3));
// /UncutDragon
// calculate chance assuming iI3 first strike hits: fOddsAfterEvent
//////
fOddsAfterEvent = 0;
// odds for _at_least_ iNeededRoundsAttacker (the remaining hits
// the attacker needs to make) out of (iMaxRounds - iI3) (the left over
// rounds) is the sum of each _exact_ draw
//////
for (iI4 = iNeededRoundsAttacker; iI4 < (iMaxRounds - iI3 + 1); iI4++)
{
// odds of exactly iI4 out of (iMaxRounds - iI3) draws.
// f(k;n,p)=C(n,k)*(p^k)*((1-p)^(n-k))
// this needs to be in floating point math
//////
// UncutDragon
/* original code
fOddsAfterEvent += ((float)getBinomialCoefficient((iMaxRounds - iI3), iI4)) * pow((((float)iAttackerOdds) / GC.getDefineINT("COMBAT_DIE_SIDES")), iI4) * pow((1.0f - (((float)iAttackerOdds) / GC.getDefineINT("COMBAT_DIE_SIDES"))), ((iMaxRounds - iI3) - iI4));
*/ // modified
fOddsAfterEvent += ((float)getBinomialCoefficient((iMaxRounds - iI3), iI4)) * pow((((float)iAttackerOdds) / GC.getCOMBAT_DIE_SIDES()), iI4) * pow((1.0f - (((float)iAttackerOdds) / GC.getCOMBAT_DIE_SIDES())), ((iMaxRounds - iI3) - iI4));
// /UncutDragon
}
// Multiply these together, round them properly, and add
// the result to the total iOdds
//////
iOdds += ((int)(1000.0 * (fOddsEvent*fOddsAfterEvent + 0.0005)));
}
}
}
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
// Weigh the total to the number of possible combinations of first strikes events
// note: the integer math breaks down when #FS > 656 (with a die size of 1000)
//////
iOdds /= (((pDefender->immuneToFirstStrikes()) ? 0 : pAttacker->chanceFirstStrikes()) + 1) * (((pAttacker->immuneToFirstStrikes()) ? 0 : pDefender->chanceFirstStrikes()) + 1);
// finished!
//////
return iOdds;
}
/*************************************************************************************************/
/** ADVANCED COMBAT ODDS 11/7/09 PieceOfMind */
/** BEGIN v1.1 */
/*************************************************************************************************/
//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);
iAttackerStrength = pAttacker->currCombatStr(NULL, NULL);
iAttackerFirepower = pAttacker->currFirepower(NULL, NULL);
iDefenderStrength = pDefender->currCombatStr(pDefender->plot(), pAttacker);
iDefenderFirepower = pDefender->currFirepower(pDefender->plot(), pAttacker);
#ifdef STRENGTH_IN_NUMBERS
if (GC.getGameINLINE().isOption(GAMEOPTION_STRENGTH_IN_NUMBERS))
{
int iDefenderSupportStrength = pDefender->getDefenderSupportValue(pAttacker);
int iAttackerSupportStrength = pAttacker->getAttackerSupportValue();
iAttackerStrength += iAttackerSupportStrength;
iAttackerFirepower += iAttackerSupportStrength;
iDefenderStrength += iDefenderSupportStrength;
iDefenderFirepower += iDefenderSupportStrength;
}
#endif // STRENGTH_IN_NUMBERS
//TB Combat Mods End
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->isHominid())
{
//defender is barbarian
if (!GET_PLAYER(pAttacker->getOwnerINLINE()).isHominid() && 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->isHominid())
{
//attacker is barbarian
if (!GET_PLAYER(pDefender->getOwnerINLINE()).isHominid() && 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