LoneGazebo / Community-Patch-DLL

Community Patch for Civilization V - Brave New World
Other
286 stars 158 forks source link

Bug zeroing out WAR/HOSTILE weight #5843

Closed RecursiveStar closed 4 years ago

RecursiveStar commented 4 years ago

1. Mod version (i.e Date - 4/23): Latest (with my hotfix and ilteroi's bugfixes)

2. Mod list (if using Vox Populi only, leave blank): Archaeology Aesthetic Adjustments Calypso's Colored Religious Icons Faster Aircraft Animations Ingame Editor Quick Turns

3. Error description: I played through a game for about 30 turns, trying to deliberately provoke the AI into declaring war early game, yet it wasn't. I looked at the approach logs and I noticed that the WAR and HOSTILE approaches were being set to 0 for every civ with every other civ, which would explain recent AI passivity.

After looking through GetBestApproachTowardsMajorCiv to find the culprit, my suspicion is this:

    ////////////////////////////////////
    // DISTANCE - the farther away a player is the less likely we are to want to attack them!
    ////////////////////////////////////
    // Target capacity should matter! If we can't get to them, let's not try to war on them!
    if (!GET_TEAM(GetTeam()).isAtWar(GET_PLAYER(ePlayer).getTeam()) && (viApproachWeights[MAJOR_CIV_APPROACH_WAR] > 0 || viApproachWeights[MAJOR_CIV_APPROACH_HOSTILE] > 0))
    {
        bool bTargetLand = GetPlayer()->GetMilitaryAI()->HaveCachedAttackTarget(ePlayer, AI_OPERATION_CITY_SNEAK_ATTACK);
        bool bTargetSeaPure = GetPlayer()->GetMilitaryAI()->HaveCachedAttackTarget(ePlayer, AI_OPERATION_NAVAL_ONLY_CITY_ATTACK);
        bool bTargetSea = GetPlayer()->GetMilitaryAI()->HaveCachedAttackTarget(ePlayer, AI_OPERATION_NAVAL_INVASION_SNEAKY);
        // No target? Abort!
        if (!bTargetLand && !bTargetSeaPure && !bTargetSea)
        {
            viApproachWeights[MAJOR_CIV_APPROACH_WAR] = 0;
            viApproachWeights[MAJOR_CIV_APPROACH_HOSTILE] = 0;
        }

@ilteroi Did you change anything about attack targets that would impact this?

I'm going to reinstall to see if it's just some kind of installation error.

ilteroi commented 4 years ago

nah, didn't touch this. also it seems like the "three negatives" conditions is extremely unlikely to trigger ...

RecursiveStar commented 4 years ago

I planted a city right next to Denmark's capital and gave them four units against my zero, and I denounced them, yet they had no war or hostile weight at all.

Denmark had 10 WAR and 10 HOSTILE bias, so this doesn't make sense.

Assuming the logging is working correctly (and I didn't touch it, so it should be), the only potential other culprits are:

    ////////////////////////////////////
    // PEACE TREATY - have we made peace with this player recently?  If so, reduce war weight
    ////////////////////////////////////
    if (GetNumWarsFought(ePlayer) > 0)
    {
        int iPeaceTreatyTurn = GET_TEAM(GetTeam()).GetTurnMadePeaceTreatyWithTeam(eTeam);
        if (iPeaceTreatyTurn > -1)
        {
            int iTurnsSincePeace = GC.getGame().getElapsedGameTurns() - iPeaceTreatyTurn;
            int iPeaceDampenerTurns = 20;

            if (MOD_BALANCE_CORE_DIFFICULTY)
            {
                int iPeaceDifficultyMod = GC.getGame().getHandicapInfo().getAIDifficultyBonusBase();
                iPeaceDampenerTurns -= iPeaceDifficultyMod;

                if (iPeaceDampenerTurns < 11)
                    iPeaceDampenerTurns = 11;
            }

            if (iTurnsSincePeace < iPeaceDampenerTurns)
            {
                viApproachWeights[MAJOR_CIV_APPROACH_WAR] = 0;
                viApproachWeights[MAJOR_CIV_APPROACH_HOSTILE] = 0;
            }
        }
    }

This function, which I modified recently to decrease the dampener between peace treaties on higher difficulties. But we hadn't previously been at war anyway.

        // We like our vassals (unless they're blocking our path to victory)
        else if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetMaster() == GetTeam())
        {
            if (GET_PLAYER(ePlayer).GetCapitalConqueror() != NO_PLAYER && GET_PLAYER(ePlayer).GetNumCapitalCities() <= 0 && !IsMajorCompetitor(ePlayer))
            {
                viApproachWeights[MAJOR_CIV_APPROACH_WAR] = 0;
                viApproachWeights[MAJOR_CIV_APPROACH_HOSTILE] = 0;
            }
        }

The AI thinking everyone is their vassal, which is an unlikely explanation.

        ////////////////////////////////////
        // Made a military promise?
        ////////////////////////////////////

        // Don't declare war if we promised that our troops weren't on their borders for war
        if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsPlayerMadeMilitaryPromise(GetPlayer()->GetID()))
        {
            viApproachWeights[MAJOR_CIV_APPROACH_WAR] = 0;
            viApproachWeights[MAJOR_CIV_APPROACH_HOSTILE] = 0;
        }

        // If we agreed to remove our troops from their borders, destroy weight for war
        if (IsPlayerMoveTroopsRequestAccepted(ePlayer))
        {
            viApproachWeights[MAJOR_CIV_APPROACH_WAR] = 0;
            viApproachWeights[MAJOR_CIV_APPROACH_HOSTILE] = 0;
        }

It can't be a military promise/move troops request, because neither of us asked for a military promise.

        ////////////////////////////////////
    // On the same team?
    ////////////////////////////////////
    if (GetTeam() == GET_PLAYER(ePlayer).getTeam())
    {
        viApproachWeights[MAJOR_CIV_APPROACH_WAR] = 0;
        viApproachWeights[MAJOR_CIV_APPROACH_HOSTILE] = 0;
        viApproachWeights[MAJOR_CIV_APPROACH_DECEPTIVE] = 0;
        viApproachWeights[MAJOR_CIV_APPROACH_GUARDED] = 0;
        viApproachWeights[MAJOR_CIV_APPROACH_AFRAID] = 0;
        viApproachWeights[MAJOR_CIV_APPROACH_NEUTRAL] = 0;
        viApproachWeights[MAJOR_CIV_APPROACH_FRIENDLY] = 100;
    }

Or the AI somehow thinks we're on the same team.

I'll reinstall and see if that fixes it.

RecursiveStar commented 4 years ago

Okay, so I started a game with just Montezuma and Harald Bluetooth, two of the most aggressive AIs, put them right next to each other on Deity difficulty, gave them full visibility of the entire map with IGE, gave them three extra Settlers each and on turn 18, used IGE to make them denounce each other.

Turn 0: Screenshot (20491)

Turn 50 (neither of them has declared war yet): Screenshot (20505)

Mucho no comprehendo. Logs show the WAR and HOSTILE approaches being zeroed out.

They also have FRIENDLY and DECEPTIVE weight, which shouldn't be happening after denouncing each other. Diplo AI Logs.zip

Relevant code is in CvDiplomacyAI::GetBestApproachTowardsMajorCiv...

@ilteroi Also take a look at the questionable AI defensive strategy for Texcoco. :)

ilteroi commented 4 years ago

i'll take a look in the debugger ... what's wrong with texcoco?

RecursiveStar commented 4 years ago

Thanks! And as for Texcoco: I mean, I get that Montezuma's UU is the Jaguar, but wouldn't some ships or ranged units be more effective? He has no ranged units at all. :)

ilteroi commented 4 years ago

re: spearmen ... production choices are G's domain. you can take a look in CityProductionAI log to see what is going on. maybe they built so many melee units before they even got archery? but i agree it's weird.

RecursiveStar commented 4 years ago

Aztec Production Logs.zip

ilteroi commented 4 years ago

ok it seems your first guess was right ... the problem is that HaveCachedAttackTarget() only looks in the cache and doesn't update it. the correct call is FindBestAttackTargetCached()

RecursiveStar commented 4 years ago

@ilteroi In the deal AI for third party war there's this code:

            bool bTargetLand = GetPlayer()->GetMilitaryAI()->HaveCachedAttackTarget(eWithPlayer, AI_OPERATION_CITY_SNEAK_ATTACK);
            bool bTargetSeaPure = GetPlayer()->GetMilitaryAI()->HaveCachedAttackTarget(eWithPlayer, AI_OPERATION_NAVAL_ONLY_CITY_ATTACK);
            bool bTargetSea = GetPlayer()->GetMilitaryAI()->HaveCachedAttackTarget(eWithPlayer, AI_OPERATION_NAVAL_INVASION_SNEAKY);
            if(!bTargetLand && !bTargetSeaPure && !bTargetSea)
            {
                CvMilitaryTarget target = GetPlayer()->GetMilitaryAI()->FindBestAttackTargetCached(AI_OPERATION_CITY_SNEAK_ATTACK, eWithPlayer);
                if(target.m_pTargetCity != NULL && target.m_pMusterCity != NULL)
                {
                    if(!target.m_bAttackBySea)
                    {
                        bTargetLand = true;
                    }
                    else
                    {
                        bTargetSea = true;
                    }
                }
            }
            //No target? Abort!
            if(!bTargetLand && !bTargetSeaPure && !bTargetSea)
            {
                return INT_MAX;
            }

So I assume the solution is to check for either an updated attack target outright, or to do so if there is no cached target?

ilteroi commented 4 years ago

thinking about it ... i'm not happy with the way HaveCachedAttackTarget() is being used in general.

there's a performance aspect, but also the split by operation type is awkward ... in the medium term we should improve it.

in the short term i would simply remove that check in GetBestApproachTowardsMajorCiv

RecursiveStar commented 4 years ago

Sounds good...well, I'll leave the military targeting to you and fix the short term issue in a pull request. :)

It appears this is actually a longstanding bug making the AI much less aggressive, and it only became evident because I moved the check to a later position in GetBestApproachTowardsMajorCiv.

Would you be able to recompile the DLL again?

ilteroi commented 4 years ago

i'll release a fixed version soon, don't touch anything in the meantime :)

RecursiveStar commented 4 years ago

Sounds good (x2)...I have another pull request to fix a separate issue causing the AI to make an excessive amount of friends (was checking for grand strategy in Ancient Era); mind merging that as well? :)

ilteroi commented 4 years ago

ok so i simplified those target checks, turns out there were some inadvertent fallthroughs in switch statements as well, should be better now

RecursiveStar commented 4 years ago

Thank you! Looking forward to the rebuilt DLL :)

@ilteroi (Also, this was a longstanding bug, can you add the "SLAIN LIKE THE HYDRA" label?)

RecursiveStar commented 4 years ago

Hehe, thanks a lot! :)

Last thing: could you push the updated code here? I intend to make further changes once I get community feedback.

Edit: Much appreciated. 😄