LoneGazebo / Community-Patch-DLL

Community Patch for Civilization V - Brave New World
Other
285 stars 157 forks source link

Upon receiving the second Great Admiral, the AI activates both #8413

Closed nobubblegums closed 2 years ago

nobubblegums commented 2 years ago

1. Mod version (X.Y.Z, e.g. 1.2.1): 1.2.1

2. Mod list (if using Vox Populi only, leave blank): Vox populi + Test.sql (to make early buildings give admirals for easier checking)

Test.zip 3. Error description: When in peace, upon receiving the second Great Admiral, the AI activates both at the same time, even if it has 100% happiness.

4. Steps to reproduce (optional):


Supporting information: Please note that you can attach .zip files by dragging-and-dropping them. If possible, zip up all supporting data and post that way.

  1. Log files (always attach your Logs folder, located at My Documents/My Games/Sid Meier's Civilization 5. For instructions, go to the repository's main page, under "To enable logging for bug reports"):

Logs.zip

  1. Save game (always attach a save that was made a turn before the error; located at My Documents/My Games/Sid Meier's Civilization 5/ModdedSaves; you can change autosave frequency in the game's Options menu): AutoSave_0001 BC-3960.zip

  2. CvMiniDump.dmp file (attach if experiencing a game crash. Located at Program Files/Steam/steamapps/common/Sid Meier's Civilization V):

  3. Screenshots (optional):

nobubblegums commented 2 years ago

I think I may have found the issue -which may affect all GPs when there is more than one of them, not just the GA. In CvPlayerAI ProcessGreatPeople, GPs get their directives in the same loop, and then their directives are set and activated. The GetDirectiveAdmiral function seems to imply that one GA should be kept in reserve as long as the empire isn't unhappy:

int iThreshold = IsEmpireUnhappy() ? 0 : 1;
if (iGreatAdmiralCount > iThreshold && pGreatAdmiral->canGetFreeLuxury())
{
    return GREAT_PEOPLE_DIRECTIVE_USE_POWER;
}

But when there are more than one Great Admirals, this function doesn't work as intended because ProcessGreatPeople first gets directives for all units (and gives the same Admiral count to them), then sets the directive and processes the great people, so iGreatAdmiralCount returns greater than iThreshold for all Admirals and all of them use power on that turn. I tried a build where I inserted 'pLoopUnit->SetGreatPeopleDirective(eDirective)' after each GetDirective under the switch statement (and removed the one following it), and that fixed the issue. I doubt that's the proper way of doing this though, so I didn't commit that.

**edit: Found a better whey and requested a pull.

ProcessGP function for reference:

void CvPlayerAI::ProcessGreatPeople(void)
{
    SpecialUnitTypes eSpecialUnitGreatPerson = (SpecialUnitTypes) GC.getInfoTypeForString("SPECIALUNIT_PEOPLE");

    CvAssert(isAlive());

    if(!isAlive())
        return;

    int iLoop;
    for(CvUnit* pLoopUnit = firstUnit(&iLoop); pLoopUnit; pLoopUnit = nextUnit(&iLoop))
    {
#if defined(MOD_BALANCE_CORE)
        if(pLoopUnit->IsCityAttackSupport())
        {
            pLoopUnit->SetGreatPeopleDirective(GREAT_PEOPLE_DIRECTIVE_FIELD_COMMAND);
            continue;
        }
        else if (pLoopUnit->IsCombatUnit() && pLoopUnit->getUnitInfo().GetUnitAIType(UNITAI_ENGINEER) && !IsAtWar())
        {
            pLoopUnit->SetGreatPeopleDirective(GREAT_PEOPLE_DIRECTIVE_USE_POWER);
            continue;
        }
        // Pseudo Great People (units with missions from GP, but are not SPECIALUNIT_PEOPLE)
        else if (pLoopUnit->getSpecialUnitType() != eSpecialUnitGreatPerson && pLoopUnit->getUnitInfo().GetUnitAIType(UNITAI_ARTIST) && (pLoopUnit->getUnitInfo().GetGoldenAgeTurns() > 0 || pLoopUnit->getUnitInfo().GetBaseTurnsForGAPToCount() > 0) && pLoopUnit->getUnitInfo().IsGreatWorkUnit())
        {
            pLoopUnit->SetGreatPeopleDirective(GetDirectiveArtist(pLoopUnit));
            continue;
        }
        else
#endif
        if(pLoopUnit->getSpecialUnitType() != eSpecialUnitGreatPerson || pLoopUnit->getArmyID()!=-1)
        {
            continue;
        }

        GreatPeopleDirectiveTypes eDirective = NO_GREAT_PEOPLE_DIRECTIVE_TYPE;
        switch(pLoopUnit->getUnitInfo().GetDefaultUnitAIType()) //look at the default type!
        {
        case UNITAI_WRITER:
            eDirective = GetDirectiveWriter(pLoopUnit);
            break;
        case UNITAI_ARTIST:
            eDirective = GetDirectiveArtist(pLoopUnit);
            break;
        case UNITAI_MUSICIAN:
            eDirective = GetDirectiveMusician(pLoopUnit);
            break;
        case UNITAI_ENGINEER:
            eDirective = GetDirectiveEngineer(pLoopUnit);
            break;
        case UNITAI_MERCHANT:
            eDirective = GetDirectiveMerchant(pLoopUnit);
            break;
        case UNITAI_SCIENTIST:
            eDirective = GetDirectiveScientist(pLoopUnit);
            break;
        case UNITAI_GENERAL:
            eDirective = GetDirectiveGeneral(pLoopUnit);
            break;
        case UNITAI_PROPHET:
            eDirective = GetDirectiveProphet(pLoopUnit);
            break;
        case UNITAI_ADMIRAL:
            eDirective = GetDirectiveAdmiral(pLoopUnit); /* inserting pLoopUnit->SetGreatPeopleDirective(eDirective); after this fixes the issue */
            break;
#if defined(MOD_DIPLOMACY_CITYSTATES)
        case UNITAI_DIPLOMAT:
            eDirective = GetDirectiveDiplomat(pLoopUnit);
            break;
#endif
        }

        pLoopUnit->SetGreatPeopleDirective(eDirective); /*and removing this */
    }
}
nobubblegums commented 2 years ago

Opened a pull request with suggested fixes