C2C Combat Mod Introduction - Step I (SubCombat Classes)

is there any guide which weapon beats which armor and so on? Didn't find anything on the civipedia (which is extremely lacking with all the new content) nor on the first page of this thread.
 
OK. I wasn't including Hills BTW. That's somethiing I wouldn't change. Footing and higher ground (and most likely chokepoints too) wouldn't apply to level ground attacks into Forest/Jungle, only ambush and cover would be relevant IMO, and I've already stated my case about those. I would want to do Terrain/Feature defense modifiers too, as I've stated in another post (not in this thread) that I think mounted and wheeled units should be penalized for entering certain terrain.
Well... a little research into the schema reveals TerrainAttackChangeModifiers and TerrainDefenseChangeModifiers tags for UnitCombats do exist already. You're prepared to go from the dll side as it is now.

I wouldn't think melee would completely negate forest defense modifiers as footing can indeed be a major issue there and being able to select the ground on which you defend yourself can indeed put your 'back to the wall' where trees block off the number of potential attackers that can come at you at once and such. But I can see it having less of an impact on melee than many other types.

is there any guide which weapon beats which armor and so on? Didn't find anything on the civipedia (which is extremely lacking with all the new content) nor on the first page of this thread.
It's only developed insofar as it tags the unit as to which weapons and armor it will use once we get to that point. That much development has not yet taken place. That said too the ethnic unit graphic changes are going to require a more intense evaluation on how to implement a varying weapon/armor set based on the differences between one culture's unit and the same unit from another culture. Probably more programming to go as a result of that before we get to having such modifiers and having them applied.
 
@TB

I was looking over the Command & Conquer wiki and got an idea for a new combat class and/or Promotion.

Decoy Army - This could possibly be done in a few different tech levels such as an Inflatable Army like in WWII or as a Hologram in the future. Note this would help in the attacker missing the target.

There may be more C&C ideas but this one stood out to me.
 
@TB

I was looking over the Command & Conquer wiki and got an idea for a new combat class and/or Promotion.

Decoy Army - This could possibly be done in a few different tech levels such as an Inflatable Army like in WWII or as a Hologram in the future. Note this would help in the attacker missing the target.

There may be more C&C ideas but this one stood out to me.

As far as holograms I'm a step ahead of ya here... I've got the following list of new combat classes (and this may have overlooked one or two) being generated due to the naval review:
UNITCOMBAT_CORVETTE
UNITCOMBAT_CUTTER
UNITCOMBAT_SWARMSHIP
UNITCOMBAT_JET_SHIPS
UNITCOMBAT_SHOCKWAVE_SHIPS
UNITCOMBAT_DROID_SHIPS
UNITCOMBAT_LEVITATION_SHIPS
UNITCOMBAT_TROID_SHIPS
UNITCOMBAT_GRAVITY_DRIVE_SHIPS
UNITCOMBAT_WEAPON_DIST_HANDHELD_RAILGUN
UNITCOMBAT_WEAPON_DIST_RAPID_RAILGUN
UNITCOMBAT_WEAPON_DIST_HEAVY_RAILGUN
UNITCOMBAT_WEAPON_BACKUP_HANDHELD_RAILGUN
UNITCOMBAT_WEAPON_BACKUP_RAPID_RAILGUN
UNITCOMBAT_WEAPON_BACKUP_HEAVY_RAILGUN
UNITCOMBAT_NAVAL_COMBATANT
UNITCOMBAT_CARRIER
UNITCOMBAT_WEAPON_DIST_COILGUN
UNITCOMBAT_WEAPON_BACKUP_COILGUN
UNITCOMBAT_WEAPON_DIST_IMPROVED_LASER
UNITCOMBAT_WEAPON_BACKUP_IMPROVED_LASER
UNITCOMBAT_WEAPON_DIST_IMPROVED_SONIC
UNITCOMBAT_WEAPON_BACKUP_IMPROVED_SONIC
UNITCOMBAT_WEAPON_DIST_PLASMA
UNITCOMBAT_WEAPON_BACKUP_PLASMA
UNITCOMBAT_WEAPON_DIST_GRAVITY
UNITCOMBAT_WEAPON_BACKUP_GRAVITY
UNITCOMBAT_WEAPON_DIST_ANTIMATTER
UNITCOMBAT_WEAPON_BACKUP_ANTIMATTER
UNITCOMBAT_WEAPON_DIST_DISINTIGRATION
UNITCOMBAT_WEAPON_BACKUP_DISINTIGRATION
UNITCOMBAT_WEAPON_DIST_SONIC_TK
UNITCOMBAT_WEAPON_BACKUP_SONIC_TK
UNITCOMBAT_WEAPON_DIST_PATHOGEN_DELIVERY
UNITCOMBAT_WEAPON_BACKUP_PATHOGEN_DELIVERY
UNITCOMBAT_WEAPON_DIST_IMPROVED_PATHOGENS
UNITCOMBAT_WEAPON_BACKUP_IMPROVED_PATHOGENS
UNITCOMBAT_WEAPON_DIST_ADVANCED_PATHOGENS
UNITCOMBAT_WEAPON_BACKUP_ADVANCED_PATHOGENS
UNITCOMBAT_WEAPON_DIST_IMPROVED_ELECTRIC
UNITCOMBAT_WEAPON_BACKUP_IMPROVED_ELECTRIC
UNITCOMBAT_WEAPON_DIST_IMPROVED_EMP
UNITCOMBAT_WEAPON_BACKUP_IMPROVED_EMP
UNITCOMBAT_WEAPON_DIST_MAGNETIC
UNITCOMBAT_WEAPON_BACKUP_MAGNETIC
UNITCOMBAT_WEAPON_DIST_NANO_SPRAY
UNITCOMBAT_WEAPON_BACKUP_NANO_SPRAY
UNITCOMBAT_WEAPON_DIST_TECHNO_TELEPATHIC
UNITCOMBAT_WEAPON_BACKUP_TECHNO_TELEPATHIC
UNITCOMBAT_WEAPON_DIST_TELEPATHY
UNITCOMBAT_WEAPON_BACKUP_TELEPATHY
UNITCOMBAT_WEAPON_DIST_PSYCHOKINESIS
UNITCOMBAT_WEAPON_BACKUP_PSYCHOKINESIS
UNITCOMBAT_WEAPON_DIST_IMPROVED_NANO
UNITCOMBAT_WEAPON_BACKUP_IMPROVED_NANO
UNITCOMBAT_WEAPON_DIST_MASS_DRIVER
UNITCOMBAT_WEAPON_BACKUP_MASS_DRIVER
UNITCOMBAT_WEAPON_DIST_IMPROVED_FROST
UNITCOMBAT_WEAPON_BACKUP_IMPROVED_FROST
UNITCOMBAT_WEAPON_DIST_FUSION_CANNON
UNITCOMBAT_WEAPON_BACKUP_FUSION_CANNON
UNITCOMBAT_SHIELD_SONIC
UNITCOMBAT_SHIELD_ELECTROMAGNETIC
UNITCOMBAT_SHIELD_FORCE_FIELD
UNITCOMBAT_SHIELD_REINFORCED_FORCE_FIELD
UNITCOMBAT_SHIELD_CRYSTAL
UNITCOMBAT_SHIELD_SHRAPNEL
UNITCOMBAT_SHIELD_PHOTON
UNITCOMBAT_SHIELD_THERMAL_NEGATION
UNITCOMBAT_SHIELD_NANO
UNITCOMBAT_SHIELD_ATTOMETER
UNITCOMBAT_SHIELD_TRANSTANGIBLE
UNITCOMBAT_SHIELD_GRAVITY
UNITCOMBAT_HOLOGRAPHIC_DIVERSIONS
UNITCOMBAT_IMPROVED_HOLOGRAPHIC_DIVERSIONS
UNITCOMBAT_NANOMORPHIC
UNITCOMBAT_DISASSEMBLY

Note that Holographic Diversions and Improved Holographic Diversions are pretty much exactly what you're suggesting ;) Take a look at the Planned Naval UnitCombat table for the units they are intended for among the naval scheme. I'm sure we'll want to have some for land units as we get into fully developing those out.

Great minds think alike eh? :cool:
 
In CnC are also "fake buildings". These could give a bonus against bombardment (land / air / sea) in C2C. If you want to make it more complex, we could have different fake buildings like "fake industrial complex" for each of the options your bombers can target.
 
Indeed! Though we may want to think of having "Dummy Tanks" from WII too.

http://en.wikipedia.org/wiki/Military_dummy
Sounds like a unit type - only problem would be how to fake out the player who can see all the stats. Might take a whole new coded feature and that means a new project which means another drop of rain in the bucket. Something to keep in mind at least.

Unless you're suggesting they'd be part of an existing unit, in which case it could add dodge value from a promo.

In CnC are also "fake buildings". These could give a bonus against bombardment (land / air / sea) in C2C. If you want to make it more complex, we could have different fake buildings like "fake industrial complex" for each of the options your bombers can target.

I haven't played nearly enough of the advanced air missions in C2C... didn't know they were even there for the longest time because they're RoM Bug options and that page is hidden under multiplayer play. I'm not quite sure how they work so I don't know if this would need an added coded feature or not.
 
Hmm... what are our bombard missions?

I would suggestfor the building to be at either Guerilla Warfare or Modern Warfare. It should cost only 10-20% of the hammers for other buildings this time, since there is nothing in there, just walls and a roof. No maintenance, but some anti-air protection. Even better, skip the anti air protection and add it to the list auf buildings that can be destroyed via airstrike. If possible, with a higher chance then the others. That should generate some natural "anti air defense".

What I'd really like to see is a system were guerilla warfare can actually compete with modern warfare. Surround and Destroy, Withdrawn, cheaper units... That all is a great step, but they can't cause as much damage as they should do IMO. Maybe we need some single-use, cheap units, that cause heavy damage, if they attack successfully. To avoid imbalance, we should probably only spawn these units, rather than have them buildable. And only, if you get invaded by a much stronger nation.
 
Hmm... what are our bombard missions?
Yeah, that's the problem... I don't know and I'm not quite sure how they work. It'll take some testing to figure that out.

I would suggestfor the building to be at either Guerilla Warfare or Modern Warfare. It should cost only 10-20% of the hammers for other buildings this time, since there is nothing in there, just walls and a roof. No maintenance, but some anti-air protection. Even better, skip the anti air protection and add it to the list auf buildings that can be destroyed via airstrike. If possible, with a higher chance then the others. That should generate some natural "anti air defense".
Yeah, the first half sounds right. As for the effect, if there's a current way to target a particular building then it should work to protect that building making it less likely to succeed - otherwise, if its striking buildings at random I suppose all we can really do is make a generic building called 'fake targets' that just reduces the chance of destroying a building at all.

What I'd really like to see is a system were guerilla warfare can actually compete with modern warfare. Surround and Destroy, Withdrawn, cheaper units... That all is a great step, but they can't cause as much damage as they should do IMO. Maybe we need some single-use, cheap units, that cause heavy damage, if they attack successfully. To avoid imbalance, we should probably only spawn these units, rather than have them buildable. And only, if you get invaded by a much stronger nation.
Well... we're getting there. The additional surround and destroy ability tags may help here too.
I thought of the other at first but I think it could be represented as dodge value instead. Mainly because you would have a hard time fooling a player.
That sounds like the way to go I think.
 
I am finding the combat calculations to be wrong, or else leaving something out. Or else I'm just bad at math (most likely) Can someone explain the following.

A wanderer, strength one, 100% bonus vs Animals.

VS

A bear, strength three, with a 50% defensive tile bonus.

The game, for some reason, is giving the bear 2 strength, and me 1 in the calculation.

But since I have a 100% bonus, and the bear only has 50%, doesn't that work out to a 50% malus to the bear? Which would be 1.5 strength as half of 3?

Confused.
 
Here's a small percent of the code for combat. Maybe you're answer is in there somewhere. I got no idea personally.
Spoiler CvGameCoreUtils.cpp :

Code:
// 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
 
I am finding the combat calculations to be wrong, or else leaving something out. Or else I'm just bad at math (most likely) Can someone explain the following.

A wanderer, strength one, 100% bonus vs Animals.

VS

A bear, strength three, with a 50% defensive tile bonus.

The game, for some reason, is giving the bear 2 strength, and me 1 in the calculation.

But since I have a 100% bonus, and the bear only has 50%, doesn't that work out to a 50% malus to the bear? Which would be 1.5 strength as half of 3?

Confused.
The display adds it up funny since it disagrees with itself on a modifier by modifier basis whether a modifier should be applied for or against the attacker or defender. And the math it displays is often not even displaying all the modifiers. Some auditing is soon to be in the works.
 
Here's a small percent of the code for combat. Maybe you're answer is in there somewhere. I got no idea personally.
The total combat strength function contains the majority of what he's trying to figure out. With a save and instructions on how to replicate the combat help tip, we could go in there and determine exactly what is either failing to report or is reporting in a confusing manner. The combat help function is in the Text Manager but nearly all of it is generated in the actual strength totaling function in CvUnit.

What you posted is the code that controls the final and actual odds generation for the game engine, and there could also be disconnections between that and the odds generated for the display and AI. We have long needed to unify and streamline where and how it all takes place, and now that I have a hell of a lot more programming experience than when I started tinkering with an already deeply bugged system, it's definitely deserving of major improvements.
 
Ah, I see
float getCombatOddsSpecific(CvUnit* pAttacker, CvUnit* pDefender, int n_A, int n_D)
is only called from CvGameTextMgr.
Combining the 2 methods might be a good start in simplifying things.
I don't see why you guys would want the odds to be calculated differently for some displays.
I could take a second and make experimental files if someone wants to check.
 
Ah, I see
float getCombatOddsSpecific(CvUnit* pAttacker, CvUnit* pDefender, int n_A, int n_D)
is only called from CvGameTextMgr.
Combining the 2 methods might be a good start in simplifying things.
I don't see why you guys would want the odds to be calculated differently for some displays.
I could take a second and make experimental files if someone wants to check.
It's how it was setup originally. For what I did, it was only a matter of duplicating efforts. Why it was originally setup this way is a little mysterious at this point.
 
Ah, I see
float getCombatOddsSpecific(CvUnit* pAttacker, CvUnit* pDefender, int n_A, int n_D)
is only called from CvGameTextMgr.
Combining the 2 methods might be a good start in simplifying things.
I don't see why you guys would want the odds to be calculated differently for some displays.
I could take a second and make experimental files if someone wants to check.

Yeah I noticed that as well at some point, it was a serious mistake. The exact same code should be used, those functions need re-integration. The game text mgr itself is a nightmare of copy pasted code repeating the same operations over and over with one value or string different (I think it is actually the longest file in the project at 40k plus lines). If you look at github blame/history you can see I experimented with refactoring a tiny bit of it to reduce the redundancy. At some point I want to go in and fix the rest of it.
 
Is this the BUG Display involved in this discussion?
 

Attachments

  • Civ4ScreenShot0006.JPG
    Civ4ScreenShot0006.JPG
    226.7 KB · Views: 132
Is this the BUG Display involved in this discussion?
Maybe a little pertinent, but the problem being discussed is actually not visible to the player. In the code, there are actually 2 places where odds are distinctly calculated, display and game core (and I think there is another entirely in the get best defender calculation). This means a massive amount of programming is duplicated when it should REALLY just be housed in one location. The danger of erroneous duplication between one and another is high, and it's simply just really bad form. Koshling and I noticed this was a problem a while back but have not yet addressed it or even tried. The display coding is a little bit of a different subject and yes, there's lots of issues in accuracy and the manner in which a lot of it is displayed, and some of the code there does draw on the calculations we're discussing, but the options are generally to show or not show certain details and that's not really changing the underlying calculation mechanism.
 
Top Bottom