My fixes

This makes OverrideFunctionalBuilding functional :p

I've noticed that r1084 attempted to fix this issue but then r1085 reverted it. I based this code on r1082 and it worked fine when I tested it close to a year ago. Hopefully it's still correct.
Just replace the existing CvPlot::canTrain with the following:

Code:
bool CvPlot::canTrain(UnitTypes eUnit, bool bContinue, bool bTestVisible) const
{
    PROFILE_FUNC();

    CvCity* pCity = getPlotCity();
/************************************************************************************************/
/* REVDCM                                 04/16/10                                phungus420    */
/*                                                                                              */
/* CanTrain Performance                                                                         */
/************************************************************************************************/
    CvUnitInfo& kUnit = GC.getUnitInfo(eUnit);
    int iI;

    if (kUnit.isPrereqReligion())
    {
        if (NULL == pCity || pCity->getReligionCount() > 0)
        {
            return false;
        }
    }

    if (kUnit.getPrereqReligion() != NO_RELIGION)
    {
        if (NULL == pCity || !pCity->isHasReligion((ReligionTypes)(kUnit.getPrereqReligion())))
        {
            return false;
        }
    }

    if (kUnit.getPrereqCorporation() != NO_CORPORATION)
    {
        if (NULL == pCity || !pCity->isActiveCorporation((CorporationTypes)(kUnit.getPrereqCorporation())))
        {
            return false;
        }
    }

    if (kUnit.isPrereqBonuses())
    {
        if (kUnit.getDomainType() == DOMAIN_SEA)
        {
            bool bValid = false;

            for (iI = 0; iI < NUM_DIRECTION_TYPES; ++iI)
            {
                CvPlot* pLoopPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));

                if (pLoopPlot != NULL)
                {
                    if (pLoopPlot->isWater())
                    {
                        if (pLoopPlot->area()->getNumTotalBonuses() > 0)
                        {
                            bValid = true;
                            break;
                        }
                    }
                }
            }

            if (!bValid)
            {
                return false;
            }
        }
        else
        {
            if (area()->getNumTotalBonuses() > 0)
            {
                return false;
            }
        }
    }

    if (isCity())
    {
        if (kUnit.getDomainType() == DOMAIN_SEA)
        {
            if (!isWater() && !isCoastalLand(kUnit.getMinAreaSize()))
            {
                return false;
            }
        }
        else
        {
            if (area()->getNumTiles() < kUnit.getMinAreaSize())
            {
                return false;
            }
        }
    }
    else
    {
        if (area()->getNumTiles() < kUnit.getMinAreaSize())
        {
            return false;
        }

        if (kUnit.getDomainType() == DOMAIN_SEA)
        {
            if (!isWater())
            {
                return false;
            }
        }
        else if (kUnit.getDomainType() == DOMAIN_LAND)
        {
            if (isWater())
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    if (!bTestVisible)
    {
        if (kUnit.getHolyCity() != NO_RELIGION)
        {
            if (NULL == pCity || !pCity->isHolyCity(((ReligionTypes)(kUnit.getHolyCity()))))
            {
                return false;
            }
        }

        if (kUnit.getPrereqAndBonus() != NO_BONUS)
        {
            if (NULL == pCity)
            {
                if (!isPlotGroupConnectedBonus(getOwnerINLINE(), (BonusTypes)kUnit.getPrereqAndBonus()))
                {
                    return false;
                }
            }
            else
            {
                const bool hasBonus = pCity->hasBonus((BonusTypes)kUnit.getPrereqAndBonus());
                const BuildingTypes eBuilding = (BuildingTypes)kUnit.getOverrideResourceBuilding();

                // Erik:  If the city does not have the bonus AND the unit does not have an overrider, bail
                if (!hasBonus && eBuilding == NO_BUILDING)
                {
                    return false;
                }

                // Erik: Check that the city has the overriding building
                if (!hasBonus && !(pCity->getNumRealBuilding(eBuilding) > 0))
                {
                    return false;
                }
            }
        }

        bool bRequiresBonus = false;
        bool bNeedsBonus = true;

        const BuildingTypes eBuilding = (BuildingTypes)kUnit.getOverrideResourceBuilding();

        if (eBuilding != NO_BUILDING)
        {
            if (pCity->getNumRealBuilding(eBuilding) > 0)
            {
                // Erik: If we have the overriding building we don't have to check for resources being
                // available
                return true;
            }
        }


        for (iI = 0; iI < GC.getNUM_UNIT_PREREQ_OR_BONUSES(); ++iI)
        {
            if (kUnit.getPrereqOrBonuses(iI) != NO_BONUS)
            {
                bRequiresBonus = true;

                if (NULL == pCity)
                {
                    if (isPlotGroupConnectedBonus(getOwnerINLINE(), (BonusTypes)kUnit.getPrereqOrBonuses(iI)))
                    {
                        bNeedsBonus = false;
                        break;
                    }
                }
                else
                {
                    if (pCity->hasBonus((BonusTypes)kUnit.getPrereqOrBonuses(iI)))
/************************************************************************************************/
/* REVDCM                                  END Performance                                      */
/************************************************************************************************/
                    {
                        bNeedsBonus = false;
                        break;
                    }
                }
            }
        }
        if (bRequiresBonus && bNeedsBonus)
        {
            return false;
        }
    }

    return true;
}
 
Fix for assert that triggers due to a missing check for the plot being owned. This is unlikely to fix the actual movement limit issue though.

Just replace the existing CvUnit::isInsideMovementLimits with the following:

Code:
// Movement Limits by 45deg - START
bool CvUnit::isInsideMovementLimits (const CvPlot* pPlot) const
{
    CvCity* pBestCity;
    int iLoop;
    pBestCity = GET_PLAYER(getOwnerINLINE()).getCapitalCity();
    int iBestDistance = MAX_INT;
    for (CvCity* pLoopCity = GET_PLAYER(getOwnerINLINE()).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(getOwnerINLINE()).nextCity(&iLoop))
    {
        int iDistance = plotDistance(getX_INLINE(), getY_INLINE(), pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE());
        if (iDistance < iBestDistance)
        {
            pBestCity = pLoopCity;
            iBestDistance = iDistance;
        }
    }

    int iBaseRadius = GC.getDefineINT("BASE_EXPLORATION_RADIUS");                   
    int iRadius = 0;
    int iRadiusScaled = 0;
    iRadiusScaled = iBaseRadius + GC.getMapINLINE().getWorldSize();
    iRadius += iRadiusScaled;

    for (int iI = 0; iI < GC.getNumTechInfos(); iI++)
    {
        if (GC.getTechInfo((TechTypes)iI).isExtendMovementLimits())
        {
            if (GET_TEAM(getTeam()).isHasTech((TechTypes)iI))
            {
                iRadius += iRadiusScaled;
            }
        }
    }   
    for (int iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
    {       
        if (GC.getTechInfo((TechTypes)iJ).isRemoveMovementLimits())
        {
            if (GET_TEAM(getTeam()).isHasTech((TechTypes)iJ))
            {
                iRadius = -1;
            }
        }               
    }
    
    if (isBarbarian() || GET_PLAYER(getOwnerINLINE()).getNumCities() < 1)
    {
        iRadius = -1;
    }
    if (iRadius != -1)
    {
        if (pPlot != NULL)
        {
            if ((getTeam() != NO_TEAM) && (GET_PLAYER(pPlot->getOwnerINLINE()).getTeam() != NO_TEAM) && (GET_PLAYER(pPlot->getOwnerINLINE()).getTeam() < 100 /*temporary hack for pitboss*/) && !isBarbarian() && (!GET_PLAYER(pPlot->getOwnerINLINE()).isBarbarian()) && (!gDLL->IsPitbossHost()))
            {
                int jDistance = plotDistance(pPlot->getX_INLINE(), pPlot->getY_INLINE(), pBestCity->getX_INLINE(), pBestCity->getY_INLINE());
                if ((!GET_TEAM(GET_PLAYER(getOwnerINLINE()).getTeam()).isOpenBorders((GET_PLAYER(pPlot->getOwnerINLINE()).getTeam()))) && /*(!GET_TEAM(pPlot->getTeam()).isVassal(getTeam())) && */(!atWar(getTeam(), (GET_PLAYER(pPlot->getOwnerINLINE()).getTeam()))) && !(isRivalTerritory()) && (!pPlot->isRoute()) && (pPlot->getOwnerINLINE() != getOwnerINLINE()) && (jDistance > iRadius+1))
                {
                    return false;
                }
            }   
        }   
    }
    return true;
}
 
Fix for pedia only showing routes up to railroad (or something like that). I've forgotten the details but I remember people complaining about this issue a while ago LOL.

Just replace the existing CvGameTextMgr::setRouteHelp with the following:
Code:
void CvGameTextMgr::setRouteHelp(CvWStringBuffer &szBuffer, RouteTypes eRoute, bool bCivilopediaText)
{
    CvWString szTempBuffer;
    CvWString szFirstBuffer;
    int iI;

    if (NO_ROUTE == eRoute)
    {
        return;
    }

    CvRouteInfo& info = GC.getRouteInfo(eRoute);
    if (!bCivilopediaText)
    {
        szTempBuffer.Format( SETCOLR L"%s" ENDCOLR, TEXT_COLOR("COLOR_HIGHLIGHT_TEXT"), info.getDescription());
        szBuffer.append(szTempBuffer);

        setYieldChangeHelp(szBuffer, L", ", L"", L"", info.getYieldChangeArray(), false, false);

        for (int iTech = 0; iTech < GC.getNumTechInfos(); iTech++)
        {
            if (GC.getGameINLINE().canEverResearch((TechTypes)iTech))
            {
                if (0 != info.getTechMovementChange(iTech))
                {
                    szBuffer.append(NEWLINE);
                    szBuffer.append(gDLL->getText("TXT_KEY_MOVEMENT_ROUTE_WITH_TECH", info.getTechMovementChange(iTech), gDLL->getSymbolID(MOVES_CHAR), GC.getTechInfo((TechTypes)iTech).getTextKeyWide()));
                }
            }
        }
    }

    if (info.isSeaTunnel())
    {
        szBuffer.append(NEWLINE);
        szBuffer.append(gDLL->getText("TXT_KEY_ROUTE_SEA_TUNNEL"));
    }

    if (info.getMovementCost() != 0)
    {
        szBuffer.append(NEWLINE);
        szBuffer.append(gDLL->getText("TXT_KEY_ROUTE_MOVEMENT_COST", info.getMovementCost()));
    }

    if (info.getFlatMovementCost() != 0)
    {
        szBuffer.append(NEWLINE);
        szBuffer.append(gDLL->getText("TXT_KEY_ROUTE_FLAT_MOVEMENT_COST", info.getFlatMovementCost()));
    }

    if (info.getPrereqBonus() != NO_BONUS)
    {
        if ((GC.getGame().getActivePlayer()!= NO_PLAYER && !GET_PLAYER(GC.getGame().getActivePlayer()).hasBonus((BonusTypes)info.getPrereqBonus())) || GC.getGame().getActivePlayer() == NO_PLAYER)
        {
            if (info.isAnyPrereqOrBonus())
            {
                bool bQualified = true;
                if ((GC.getGame().getActivePlayer()!= NO_PLAYER && !GET_PLAYER(GC.getGame().getActivePlayer()).hasBonus((BonusTypes)info.getPrereqBonus())) || GC.getGame().getActivePlayer() == NO_PLAYER)
                {
                    for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
                    {
                        BonusTypes eBonusOrPrereq = (BonusTypes)iI;

                        if (eBonusOrPrereq != NO_BONUS)
                        {
                            if (GET_PLAYER(GC.getGame().getActivePlayer()).hasBonus((BonusTypes)info.getPrereqOrBonus(eBonusOrPrereq)))
                            {
                                bQualified = false;
                            }
                        }
                    }
                    if (bQualified)
                    {
                        szBuffer.append(NEWLINE);
                        szBuffer.append(gDLL->getText("TXT_KEY_ROUTE_REQUIRES_BONUS", GC.getBonusInfo((BonusTypes)info.getPrereqBonus()).getTextKeyWide()));

                        for (iI = 0; iI < GC.getNumBonusInfos(); iI++)
                        {
                            BonusTypes eBonusOrPrereq = (BonusTypes)iI;

                            if (eBonusOrPrereq != NO_BONUS)
                            {
                                if ((GC.getGame().getActivePlayer() != NO_PLAYER && !GET_PLAYER(GC.getGame().getActivePlayer()).hasBonus((BonusTypes)info.getPrereqOrBonus(eBonusOrPrereq))) || GC.getGame().getActivePlayer() != NO_PLAYER)
                                {
                                    szBuffer.append(gDLL->getText("TXT_KEY_ROUTE_REQUIRES_BONUS_OR", GC.getBonusInfo((BonusTypes)info.getPrereqOrBonus(eBonusOrPrereq)).getTextKeyWide()));
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                szBuffer.append(NEWLINE);
                szBuffer.append(gDLL->getText("TXT_KEY_ROUTE_REQUIRES_BONUS", GC.getBonusInfo((BonusTypes)info.getPrereqBonus()).getTextKeyWide()));
            }
        }
    }


    info.getPropertyManipulators()->buildDisplayString(szBuffer);
}
 
Fix for the positive health hoover\help text missing the religious building factor (and potentially causing a crash I think).

Just replace the existing CvGameTextMgr::setGoodHealthHelp with the following:

Code:
void CvGameTextMgr::setGoodHealthHelp(CvWStringBuffer &szBuffer, CvCity& city)
{
    CvPlot* pLoopPlot;
    FeatureTypes eFeature;
    int iHealth;
    int iI;
    if (city.goodHealth() > 0)
    {
        iHealth = city.getFreshWaterGoodHealth();
        if (iHealth > 0)
        {
            szBuffer.append(gDLL->getText("TXT_KEY_MISC_HEALTH_FROM_FRESH_WATER", iHealth));
            szBuffer.append(NEWLINE);
        }
        iHealth = city.getFeatureGoodHealth();
        if (iHealth > 0)
        {
            eFeature = NO_FEATURE;
            for (iI = 0; iI < NUM_CITY_PLOTS; ++iI)
            {
                pLoopPlot = plotCity(city.getX_INLINE(), city.getY_INLINE(), iI);
                if (pLoopPlot != NULL)
                {
                    if (pLoopPlot->getFeatureType() != NO_FEATURE)
                    {
                        if (GC.getFeatureInfo(pLoopPlot->getFeatureType()).getHealthPercent() > 0)
                        {
                            if (eFeature == NO_FEATURE)
                            {
                                eFeature = pLoopPlot->getFeatureType();
                            }
                            else if (eFeature != pLoopPlot->getFeatureType())
                            {
                                eFeature = NO_FEATURE;
                                break;
                            }
                        }
                    }
                }
            }
            szBuffer.append(gDLL->getText("TXT_KEY_MISC_FEAT_GOOD_HEALTH", iHealth, ((eFeature == NO_FEATURE) ? L"TXT_KEY_MISC_FEATURES" : GC.getFeatureInfo(eFeature).getTextKeyWide())));
            szBuffer.append(NEWLINE);
        }
/************************************************************************************************/
/* JOOYO_ADDON, Added by Jooyo, 06/19/09                                                        */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
        iHealth = city.getImprovementGoodHealth() / 100;
        CvPlayer &kPlayer = GET_PLAYER(city.getOwnerINLINE());
        if (iHealth > 0)
        {
            int iTotalHealth = 0;
            for (int iI = 0; iI < GC.getNumImprovementInfos(); iI++)
            {
                int iImprovementHealth = 0;
                if (GC.getImprovementInfo((ImprovementTypes)iI).getHealthPercent() > 0)
                {
                    const int iNumCityPlots = city.getNumCityPlots();
                    for (int iJ = 0; iJ < city.getNumCityPlots(); ++iJ)
                    {
                        pLoopPlot = plotCity(city.getX_INLINE(), city.getY_INLINE(), iJ);
                        if (pLoopPlot != NULL)
                        {
                            if (pLoopPlot->getImprovementType() == iI)
                            {
                                iImprovementHealth += GC.getImprovementInfo((ImprovementTypes)iI).getHealthPercent();
                                for (int iK = 0; iK < GC.getNumCivicOptionInfos(); iK++)
                                {
                                    if (kPlayer.getCivics((CivicOptionTypes)iK) != NO_CIVIC)
                                    {
                                        iImprovementHealth += std::max(0, GC.getCivicInfo(kPlayer.getCivics((CivicOptionTypes)iK)).getImprovementHealthPercentChanges(iI)) / 100;
                                    }
                                }
                            }
                        }
                    }
                }
                iImprovementHealth /= 100;
                if (iImprovementHealth != 0)
                {
                    szBuffer.append(gDLL->getText("TXT_KEY_MISC_IMPR_GOOD_HEALTH", iImprovementHealth, GC.getImprovementInfo((ImprovementTypes)iI).getTextKeyWide()));
                    szBuffer.append(NEWLINE);
                    iTotalHealth += iImprovementHealth;
                }
            }
            //Show remainder as generic improvement unhealthy
            if (iTotalHealth < iHealth)
            {
                szBuffer.append(gDLL->getText("TXT_KEY_MISC_IMPR_GOOD_HEALTH", (iHealth - iTotalHealth), L"TXT_KEY_MISC_IMPROVEMENTS"));
                szBuffer.append(NEWLINE);
            }
        }
/************************************************************************************************/
/* JOOYO_ADDON                          END                                                     */
/************************************************************************************************/
/************************************************************************************************/
/* Specialists Enhancements, by Supercheese 10/9/09                                                   */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
        iHealth = city.getSpecialistGoodHealth() / 100;
        if (iHealth > 0)
        {
            szBuffer.append(gDLL->getText("TXT_KEY_GOOD_HEALTH_FROM_SPECIALISTS", iHealth));
            szBuffer.append(NEWLINE);
        }
/************************************************************************************************/
/* Specialists Enhancements                          END                                              */
/************************************************************************************************/
        iHealth = city.getPowerGoodHealth();
        if (iHealth > 0)
        {
            szBuffer.append(gDLL->getText("TXT_KEY_MISC_GOOD_HEALTH_FROM_POWER", iHealth));
            szBuffer.append(NEWLINE);
        }
        iHealth = city.getBonusGoodHealth();
        if (iHealth > 0)
        {
            szBuffer.append(gDLL->getText("TXT_KEY_MISC_GOOD_HEALTH_FROM_BONUSES", iHealth));
            szBuffer.append(NEWLINE);
        }
        iHealth = city.totalGoodBuildingHealth();
        if (iHealth > 0)
        {
            szBuffer.append(gDLL->getText("TXT_KEY_MISC_GOOD_HEALTH_FROM_BUILDINGS", iHealth));
            szBuffer.append(NEWLINE);
        }
        // Erik: Check that the city actually has a religion
        ReligionTypes eReligion = GET_PLAYER(city.getOwnerINLINE()).getStateReligion();
        if (eReligion != NO_RELIGION && city.isHasReligion(eReligion))
        {
            iHealth = GET_PLAYER(city.getOwnerINLINE()).getStateReligionHealth();
            if (iHealth > 0)
            {
                szBuffer.append(gDLL->getText("TXT_KEY_HEALTH_STATE_RELIGION", iHealth));
                szBuffer.append(NEWLINE);
            }      
        }  
/************************************************************************************************/
/* Afforess                      Start        01/29/10                                               */
/*                                                                                              */
/*   Traits Were displaying Civic Health too                                                    */
/************************************************************************************************/
        iHealth = GET_PLAYER(city.getOwnerINLINE()).getCivicHealth();
        if (iHealth > 0)
        {
            szBuffer.append(gDLL->getText("TXT_KEY_MISC_GOOD_HEALTH_FROM_CIVICS", iHealth));
            szBuffer.append(NEWLINE);
        }
        iHealth = GET_PLAYER(city.getOwnerINLINE()).getCivilizationHealth();
/************************************************************************************************/
/* Afforess                         END                                                            */
/************************************************************************************************/
        if (iHealth > 0)
        {
            szBuffer.append(gDLL->getText("TXT_KEY_MISC_GOOD_HEALTH_FROM_CIV", iHealth));
            szBuffer.append(NEWLINE);
        }
        //    Koshling - event health
        iHealth = GET_PLAYER(city.getOwnerINLINE()).getExtraHealth() - GET_PLAYER(city.getOwnerINLINE()).getCivicHealth();
        if (iHealth > 0)
        {
            szBuffer.append(gDLL->getText("TXT_KEY_MISC_GOOD_HEALTH_FROM_EVENTS", iHealth));
            szBuffer.append(NEWLINE);
        }
        iHealth = city.getExtraHealth();
        if (iHealth > 0)
        {
            szBuffer.append(gDLL->getText("TXT_KEY_MISC_HEALTH_EXTRA", iHealth));
            szBuffer.append(NEWLINE);
        }
        if ( GC.getGame().getHandicapType() != NO_HANDICAP )
        {
            iHealth = GC.getHandicapInfo(city.getHandicapType()).getHealthBonus();
            if (iHealth > 0)
            {
                szBuffer.append(gDLL->getText("TXT_KEY_MISC_GOOD_HEALTH_FROM_HANDICAP", iHealth));
                szBuffer.append(NEWLINE);
            }
        }
      
/************************************************************************************************/
/* Afforess                      Start        01/03/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
        iHealth = (GET_PLAYER(city.getOwnerINLINE()).getWorldHealth());
        if (iHealth > 0)
        {
            szBuffer.append(gDLL->getText("TXT_KEY_MISC_GOOD_HEALTH_FROM_WORLD_PROJECT", iHealth));
            szBuffer.append(NEWLINE);
        }
      
        iHealth = (GET_PLAYER(city.getOwnerINLINE()).getProjectHealth());
        if (iHealth > 0)
        {
            szBuffer.append(gDLL->getText("TXT_KEY_MISC_GOOD_HEALTH_FROM_PROJECT", iHealth));
            szBuffer.append(NEWLINE);
        }
      
        iHealth = city.calculateCorporationHealth();
        if (iHealth > 0)
        {
            szBuffer.append(gDLL->getText("TXT_KEY_MISC_GOOD_HEALTH_FROM_CORPORATION", iHealth));
            szBuffer.append(NEWLINE);
        }
/************************************************************************************************/
/* Afforess                         END                                                            */
/************************************************************************************************/
        szBuffer.append(L"-----------------------\n");
        szBuffer.append(gDLL->getText("TXT_KEY_MISC_TOTAL_HEALTHY", city.goodHealth()));
    }
}
 
Thanks @devolution, I'll have a look at your fixes during the weekend. I'm currently testing a dll which solves a couple of bugs too, one was about OverrideResourceBuilding, I haven't checked yet if the fix is the same, but it was definitely something simple that I overlooked somehow. I'll probably release a revision with a couple of fixes of mine and then I'll test your code and release a new dll in a few days if everything works. Thanks again. :)
 
This makes OverrideFunctionalBuilding functional :p

I've noticed that r1084 attempted to fix this issue but then r1085 reverted it. I based this code on r1082 and it worked fine when I tested it close to a year ago. Hopefully it's still correct.
Just replace the existing CvPlot::canTrain with the following:

Code:
bool CvPlot::canTrain(UnitTypes eUnit, bool bContinue, bool bTestVisible) const
{
    PROFILE_FUNC();

    CvCity* pCity = getPlotCity();
/************************************************************************************************/
/* REVDCM                                 04/16/10                                phungus420    */
/*                                                                                              */
/* CanTrain Performance                                                                         */
/************************************************************************************************/
    CvUnitInfo& kUnit = GC.getUnitInfo(eUnit);
    int iI;

    if (kUnit.isPrereqReligion())
    {
        if (NULL == pCity || pCity->getReligionCount() > 0)
        {
            return false;
        }
    }

    if (kUnit.getPrereqReligion() != NO_RELIGION)
    {
        if (NULL == pCity || !pCity->isHasReligion((ReligionTypes)(kUnit.getPrereqReligion())))
        {
            return false;
        }
    }

    if (kUnit.getPrereqCorporation() != NO_CORPORATION)
    {
        if (NULL == pCity || !pCity->isActiveCorporation((CorporationTypes)(kUnit.getPrereqCorporation())))
        {
            return false;
        }
    }

    if (kUnit.isPrereqBonuses())
    {
        if (kUnit.getDomainType() == DOMAIN_SEA)
        {
            bool bValid = false;

            for (iI = 0; iI < NUM_DIRECTION_TYPES; ++iI)
            {
                CvPlot* pLoopPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));

                if (pLoopPlot != NULL)
                {
                    if (pLoopPlot->isWater())
                    {
                        if (pLoopPlot->area()->getNumTotalBonuses() > 0)
                        {
                            bValid = true;
                            break;
                        }
                    }
                }
            }

            if (!bValid)
            {
                return false;
            }
        }
        else
        {
            if (area()->getNumTotalBonuses() > 0)
            {
                return false;
            }
        }
    }

    if (isCity())
    {
        if (kUnit.getDomainType() == DOMAIN_SEA)
        {
            if (!isWater() && !isCoastalLand(kUnit.getMinAreaSize()))
            {
                return false;
            }
        }
        else
        {
            if (area()->getNumTiles() < kUnit.getMinAreaSize())
            {
                return false;
            }
        }
    }
    else
    {
        if (area()->getNumTiles() < kUnit.getMinAreaSize())
        {
            return false;
        }

        if (kUnit.getDomainType() == DOMAIN_SEA)
        {
            if (!isWater())
            {
                return false;
            }
        }
        else if (kUnit.getDomainType() == DOMAIN_LAND)
        {
            if (isWater())
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    if (!bTestVisible)
    {
        if (kUnit.getHolyCity() != NO_RELIGION)
        {
            if (NULL == pCity || !pCity->isHolyCity(((ReligionTypes)(kUnit.getHolyCity()))))
            {
                return false;
            }
        }

        if (kUnit.getPrereqAndBonus() != NO_BONUS)
        {
            if (NULL == pCity)
            {
                if (!isPlotGroupConnectedBonus(getOwnerINLINE(), (BonusTypes)kUnit.getPrereqAndBonus()))
                {
                    return false;
                }
            }
            else
            {
                const bool hasBonus = pCity->hasBonus((BonusTypes)kUnit.getPrereqAndBonus());
                const BuildingTypes eBuilding = (BuildingTypes)kUnit.getOverrideResourceBuilding();

                // Erik:  If the city does not have the bonus AND the unit does not have an overrider, bail
                if (!hasBonus && eBuilding == NO_BUILDING)
                {
                    return false;
                }

                // Erik: Check that the city has the overriding building
                if (!hasBonus && !(pCity->getNumRealBuilding(eBuilding) > 0))
                {
                    return false;
                }
            }
        }

        bool bRequiresBonus = false;
        bool bNeedsBonus = true;

        const BuildingTypes eBuilding = (BuildingTypes)kUnit.getOverrideResourceBuilding();

        if (eBuilding != NO_BUILDING)
        {
            if (pCity->getNumRealBuilding(eBuilding) > 0)
            {
                // Erik: If we have the overriding building we don't have to check for resources being
                // available
                return true;
            }
        }


        for (iI = 0; iI < GC.getNUM_UNIT_PREREQ_OR_BONUSES(); ++iI)
        {
            if (kUnit.getPrereqOrBonuses(iI) != NO_BONUS)
            {
                bRequiresBonus = true;

                if (NULL == pCity)
                {
                    if (isPlotGroupConnectedBonus(getOwnerINLINE(), (BonusTypes)kUnit.getPrereqOrBonuses(iI)))
                    {
                        bNeedsBonus = false;
                        break;
                    }
                }
                else
                {
                    if (pCity->hasBonus((BonusTypes)kUnit.getPrereqOrBonuses(iI)))
/************************************************************************************************/
/* REVDCM                                  END Performance                                      */
/************************************************************************************************/
                    {
                        bNeedsBonus = false;
                        break;
                    }
                }
            }
        }
        if (bRequiresBonus && bNeedsBonus)
        {
            return false;
        }
    }

    return true;
}

About this one, I'm currently testing an easier fix, you can see it here, in red (edit: damn, no colors in the code, but there's a note where I have changed the code); I've tested the dll and so far no problem and it works. I'm not sure if it might cause any CTD but I didn't experience any. I can see a potential problem with your code because it's similar to something I have tried before but discarded because it was causing troubles with units having both OR and AND resources requirements (like submarines, which require Iron and (Oil products OR Uranium)): for some reason that or requirement was skipped and submarines required Iron AND Oil Products AND Uranium. So this is why I moved the code before the resource requirement check. What do you think about the code below? Thanks

Code:
bool CvPlot::canTrain(UnitTypes eUnit, bool bContinue, bool bTestVisible) const
{
    PROFILE_FUNC();

    CvCity* pCity = getPlotCity();
/************************************************************************************************/
/* REVDCM                                 04/16/10                                phungus420    */
/*                                                                                              */
/* CanTrain Performance                                                                         */
/************************************************************************************************/
    CvUnitInfo& kUnit = GC.getUnitInfo(eUnit);
    int iI;

    if (kUnit.isPrereqReligion())
    {
        if (NULL == pCity || pCity->getReligionCount() > 0)
        {
            return false;
        }
    }

    if (kUnit.getPrereqReligion() != NO_RELIGION)
    {
        if (NULL == pCity || !pCity->isHasReligion((ReligionTypes)(kUnit.getPrereqReligion())))
        {
            return false;
        }
    }

    if (kUnit.getPrereqCorporation() != NO_CORPORATION)
    {
        if (NULL == pCity || !pCity->isActiveCorporation((CorporationTypes)(kUnit.getPrereqCorporation())))
        {
            return false;
        }
    }

    if (kUnit.isPrereqBonuses())
    {
        if (kUnit.getDomainType() == DOMAIN_SEA)
        {
            bool bValid = false;

            for (iI = 0; iI < NUM_DIRECTION_TYPES; ++iI)
            {
                CvPlot* pLoopPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));

                if (pLoopPlot != NULL)
                {
                    if (pLoopPlot->isWater())
                    {
                        if (pLoopPlot->area()->getNumTotalBonuses() > 0)
                        {
                            bValid = true;
                            break;
                        }
                    }
                }
            }

            if (!bValid)
            {
                return false;
            }
        }
        else
        {
            if (area()->getNumTotalBonuses() > 0)
            {
                return false;
            }
        }
    }

    if (isCity())
    {
        if (kUnit.getDomainType() == DOMAIN_SEA)
        {
            if (!isWater() && !isCoastalLand(kUnit.getMinAreaSize()))
            {
                return false;
            }
        }
        else
        {
            if (area()->getNumTiles() < kUnit.getMinAreaSize())
            {
                return false;
            }
        }
    }
    else
    {
        if (area()->getNumTiles() < kUnit.getMinAreaSize())
        {
            return false;
        }

        if (kUnit.getDomainType() == DOMAIN_SEA)
        {
            if (!isWater())
            {
                return false;
            }
        }
        else if (kUnit.getDomainType() == DOMAIN_LAND)
        {
            if (isWater())
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    if (!bTestVisible)
    {
        if (kUnit.getHolyCity() != NO_RELIGION)
        {
            if (NULL == pCity || !pCity->isHolyCity(((ReligionTypes)(kUnit.getHolyCity()))))
            {
                return false;
            }
        }

// 45deg: test OverrideResourceBuilding - START
        if (isCity())
        {
            if (pCity->getNumRealBuilding((BuildingTypes)kUnit.getOverrideResourceBuilding()) > 0)
            {
                return true;
            }
        }
// 45deg: test OverrideResourceBuilding - END          
      
        if (kUnit.getPrereqAndBonus() != NO_BONUS)
        {
            if (NULL == pCity)
            {
                if (!isPlotGroupConnectedBonus(getOwnerINLINE(), (BonusTypes)kUnit.getPrereqAndBonus()))
                {
                    return false;
                }
            }
            else
            {
                if ((!pCity->hasBonus((BonusTypes)kUnit.getPrereqAndBonus())) /*&& (!pCity->getNumRealBuilding((BuildingTypes)kUnit.getOverrideResourceBuilding()) > 0)*/)
                {
                    return false;
                }
            }
        }

        bool bRequiresBonus = false;
        bool bNeedsBonus = true;

        for (iI = 0; iI < GC.getNUM_UNIT_PREREQ_OR_BONUSES(); ++iI)
        {
            if (kUnit.getPrereqOrBonuses(iI) != NO_BONUS)
            {
                bRequiresBonus = true;

                if (NULL == pCity)
                {
                    if (isPlotGroupConnectedBonus(getOwnerINLINE(), (BonusTypes)kUnit.getPrereqOrBonuses(iI)))
                    {
                        bNeedsBonus = false;
                        break;
                    }
                }
                else
                {
                    if (pCity->hasBonus((BonusTypes)kUnit.getPrereqOrBonuses(iI)))
/************************************************************************************************/
/* REVDCM                                  END Performance                                      */
/************************************************************************************************/
                    {
                        bNeedsBonus = false;
                        break;
                    }
                }
            }
        }
        if (bRequiresBonus && bNeedsBonus)
        {
            return false;
        }
    }

    return true;
}
 
Last edited:
Fix for assert that triggers due to a missing check for the plot being owned. This is unlikely to fix the actual movement limit issue though
I (and WinMerge) see no difference between your code and the latest on SVN. Maybe 45° has already fixed it ... but I think the error is this:
Code:
if ((getTeam() != NO_TEAM) && (GET_PLAYER(pPlot->getOwnerINLINE()).getTeam() != NO_TEAM
Probably it was supposed to be pPlot->getTeam() instead of this->getTeam(). I've run into that assertion in isOutsideMovementLimits when I debugged a problem in the Chronicles mod last summer. On closer examination, I see a bunch of issues with those two functions. I hope no one minds if I go through them here.

For starters, it looks like isOutsideMovementLimits could simply return !isInsideMovementLimits(pPlot). Then, when searching for the nearest city, plotDistance measures the distance between this CvUnit and the loop city – I think it should look for the city closest to the plot that the unit wants to move to (pPlot). And I wonder about all those conditions – isn't the rule essentially just that the movement limit applies only to unowned tiles? The unit may (safely) enter its owner's territory and rival territory through Open Borders and warfare. Rival territory without Open Borders can't be entered in any case (unless the mod has changed that somehow). That leaves only the territory of teammates and of vassals when there is no Open Borders agreement. I'd say that the unit should be safe there as well; I don't think that's currently the case though. Lastly, CvTeam already caches the tech abilities that lift or extend movement limits; if that's working correctly, then there's no need for those tech loops; one can just call CvTeam::isRemoveMovementLimits and isExtendMovementLimits. Assuming that my observations above are correct, I could rewrite the function and post the code – but I'd only be able to do a cursory test.
 
Thanks @f1rpo, you're more than welcome if you wish to post your code. I can run some test and then and eventually merge it in the dll, of course giving you proper credits.
About movement limits, there's one more case where units are actually able to move outside the limit, and that's when they are on a road. I wrote the code a long time ago so I don't remember for sure, but it was definitely intended for units to be able to move inside vassals territory.
Also, while you're here, would you mind at looking at the code I posted above for OverrideResourceBuilding? It looks like it's working, more than 500 turns in a game right now and no issues, but I'm not sure it won't cause any problem. On the other hand devolution code for that function is similar to something I alread tried but it looks like it was causing troubles with And Or resource requirements as I explained above. Thanks.
 
Fix for endless loop issue reported here:
https://forums.civfanatics.com/thre...new-dawn-2-only.474185/page-342#post-15418981

Code:
// K-Mod has edited this function to increase readability and robustness
void CvUnit::joinGroup(CvSelectionGroup* pSelectionGroup, bool bRemoveSelected, bool bRejoin)
{
    CvSelectionGroup* pOldSelectionGroup = GET_PLAYER(getOwnerINLINE()).getSelectionGroup(getGroupID());

    if (pOldSelectionGroup && pSelectionGroup == pOldSelectionGroup)
        return; // attempting to join the group we are already in

    CvPlot* pPlot = plot();
    CvSelectionGroup* pNewSelectionGroup = pSelectionGroup;

    if (pNewSelectionGroup == NULL && bRejoin)
    {
        pNewSelectionGroup = GET_PLAYER(getOwnerINLINE()).addSelectionGroup();
        pNewSelectionGroup->init(pNewSelectionGroup->getID(), getOwnerINLINE());
    }

    if (pNewSelectionGroup == NULL || canJoinGroup(pPlot, pNewSelectionGroup))
    {
        if (pOldSelectionGroup != NULL)
        {
            bool bWasHead = false;
            if (!isHuman())
            {
                if (pOldSelectionGroup->getNumUnits() > 1)
                {
                    if (pOldSelectionGroup->getHeadUnit() == this)
                    {
                        bWasHead = true;
                    }
                }
            }

            pOldSelectionGroup->removeUnit(this);

            // if we were the head, if the head unitAI changed, then force the group to separate (non-humans)
            if (bWasHead)
            {
                FAssert(pOldSelectionGroup->getHeadUnit() != NULL);
                if (pOldSelectionGroup->getHeadUnit()->AI_getUnitAIType() != AI_getUnitAIType())
                {
                    pOldSelectionGroup->AI_makeForceSeparate();
                }
            }
        }

        if ((pNewSelectionGroup != NULL) && pNewSelectionGroup->addUnit(this, false))
        {
            m_iGroupID = pNewSelectionGroup->getID();
        }
        else
        {
            m_iGroupID = FFreeList::INVALID_INDEX;
        }

        if (getGroup() != NULL)
        {
            // K-Mod
            if (isGroupHead())
                GET_PLAYER(getOwnerINLINE()).updateGroupCycle(this, false);
            // K-Mod end
            if (getGroup()->getNumUnits() > 1)
            {
                /* original bts code
                getGroup()->setActivityType(ACTIVITY_AWAKE); */
                // K-Mod
                // For the AI, only wake the group in particular circumstances. This is to avoid AI deadlocks where they just keep grouping and ungroup indefinitely.
                // If the activity type is not changed at all, then that would enable exploits such as adding new units to air patrol groups to bypass the movement conditions.
                if (isHuman())
                {
                    getGroup()->setAutomateType(NO_AUTOMATE);
                    getGroup()->setActivityType(ACTIVITY_AWAKE);
                    getGroup()->clearMissionQueue();
                    // K-Mod note. the mission queue has to be cleared, because when the shift key is released, the exe automatically sends the autoMission net message.
                    // (if the mission queue isn't cleared, the units will immediately begin their message whenever units are added using shift.)
                }
                else if (getGroup()->AI_getMissionAIType() == MISSIONAI_GROUP || getLastMoveTurn() == GC.getGameINLINE().getTurnSlice())
                    getGroup()->setActivityType(ACTIVITY_AWAKE);
                else if (getGroup()->getActivityType() != ACTIVITY_AWAKE)
                    getGroup()->setActivityType(ACTIVITY_HOLD); // don't let them cheat.
                                                                // K-Mod end
            }
            /* original bts code
            else
            {
            GET_PLAYER(getOwnerINLINE()).updateGroupCycle(this);
            } */
        }

        if (getTeam() == GC.getGameINLINE().getActiveTeam())
        {
            if (pPlot != NULL)
            {
                pPlot->setFlagDirty(true);
            }
        }

        if (pPlot == gDLL->getInterfaceIFace()->getSelectionPlot())
        {
            gDLL->getInterfaceIFace()->setDirty(PlotListButtons_DIRTY_BIT, true);
        }
    }

    if (bRemoveSelected && IsSelected())
    {
        gDLL->getInterfaceIFace()->removeFromSelectionList(this);
    }
}
 
Fix updateCityScreen python exception: When updateCityScreen lists the available buildings to construct, getBuildingInfo() could be None. Since the code does not check for that, it's possible that the listing will be aborted due a python exception.

CvMainInterface.py : Replace line 4331 with this code block to fix it:

Code:
# Erik: The line below causes a python exception
#bIsSpecial = gc.getBuildingInfo(i).getProductionCost() <= 0        
# Erik: Let's try this instead
if gc.getBuildingInfo(i) == None: continue
else:
    bIsSpecial = gc.getBuildingInfo(i).getProductionCost() <= 0
 
Also, while you're here, would you mind at looking at the code I posted above for OverrideResourceBuilding? It looks like it's working, more than 500 turns in a game right now and no issues, but I'm not sure it won't cause any problem. On the other hand devolution code for that function is similar to something I alread tried but it looks like it was causing troubles with And Or resource requirements as I explained above. Thanks.
Code:
if (isCity())
{
    if (pCity->getNumRealBuilding((BuildingTypes)kUnit.getOverrideResourceBuilding()) > 0)
    {
        return true;
    }
}
I would assume that getOverrideResourceBuilding can return NO_BUILDING and
that should result in an array access at -1 in getNumRealBuilding with an unpredictable return value, a failed assertion and the occasional crash. If the presence of the unit's OverrideResourceBuilding means that all resource requirements are lifted (well, how else could the ability work ...), then I think
Code:
if (pCity != NULL && kUnit.getOverrideResourceBuilding() != NO_BUILDING &&
    pCity->getNumActiveBuilding((BuildingTypes)kUnit.getOverrideResourceBuilding()) > 0)
{
   return true;
}
would be correct. getNumActiveBuilding should account for free buildings and building obsoletion.

My take on Movement Limits (works OK for me if I just start a game and move some units around):
Code:
// Movement Limits by 45deg - START  // f1rpo: rewritten
bool CvUnit::isInsideMovementLimits(const CvPlot* pPlot) const
{
   if (pPlot->isOwned() || pPlot->isRoute() || isRivalTerritory())
       return true;

   CvPlayer const& kOwner = GET_PLAYER(getOwner());
   CvTeam const& kTeam = GET_TEAM(kOwner.getTeam());
   if (kTeam.isRemoveMovementLimits())
       return true;
   if (kOwner.getNumCities() <= 0 || kOwner.isBarbarian())
       return true;

   static int const iDefaultLimit = GC.getDefineINT("BASE_EXPLORATION_RADIUS")
           + GC.getMap().getWorldSize();
   int iMovementLimit = iDefaultLimit;
   if (kTeam.isExtendMovementLimits())
       iMovementLimit *= 2;
   int iIter;
   for (CvCity const* pCity = kOwner.firstCity(&iIter); pCity != NULL; pCity = kOwner.nextCity(&iIter))
   {
       if (plotDistance(pPlot->getX(), pPlot->getY(), pCity->getX(), pCity->getY()) <= iMovementLimit)
           return true;
   }
   // temporary hack for pitboss
   if (gDLL->IsPitbossHost() || (pPlot->isOwned() && pPlot->getTeam() >= 100))
       return true;

   return false;
}

bool CvUnit::isOutsideMovementLimits (const CvPlot* pPlot) const
{
   return !isInsideMovementLimits(pPlot); // f1rpo: Replacing duplicate code
}
I don't really know how Pitboss works, so I've left the "temporary hack" alone.
 
My take on Movement Limits (works OK for me if I just start a game and move some units around):
Please try to save, quit to desktop and load too because with the present dll I had experience when I started a game and "Yaay! Movement Limit is working finally" :woohoo:" and than I quit and loaded later and than "It's not working! Again! :gripe::badcomp: :wallbash:"
 
It would be helpful if we knew how the movement limits feature is actually supposed to work.
Regarding the code that @f1rpo posted:
- What about proximity to vassals ?
- What about a unit being in a different area than the closest city. Crossing an area should arguably be considered more "limiting".
- Probably need to do full pathfinding to capture the effect of borders, impassable plots etc.

Regarding the pitboss hack, I really wonder that this is supposed to do:
Code:
pPlot->getTeam() >= 100
Maybe it's due to a pitboss observer \ player
 
Last edited:
Here's an old post where I explain how Movement Limits is supposed to work.

If Terrain Damage is on, all units who can get damage have the same limit, regardless of domain or promotions. So in this case, workers, settlers, spies and Great People are excluded and can roam freely. Of course it's risky at the start of the game, so I see no problem with it given that those are valuable units. If Terrain Damage is off, no unit can move beyond one tile away from the limit. If for some reason they are beyond that limit, for example if your closest city gets razed, your units are bounced back to the nearest valid plot. You can explore territory outside your limit only if the tile you're moving to belongs to another civ you are at war with or have open borders with, or if it's a road. In theory, you can build a road with a worker outside your limit and then move further along that road with other units, as the tile with road becomes a valid plot. I'm not sure if a Fort acts like a city but if it doesn't I might change that.

That temporary hack for pitboss was a result of some test I run years ago when I first coded ML. I don't remember the exact detail but it was probably something connected to the fact that pitboss is considered a player #Idontknowwhatnumber in some part if the code, and the first 100 players are supposed to be real players when using my 100civs dll.

Thanks @f1rpo for the suggestion for OverrideResourceBuilding and the new code for ML. I'll give it a try as well in the next days and then I'll release the new dll.
@devolution and @f1rpo, is that ok with you if I add your code to the dll and add your names to the credits page of AND2? Thanks again.
 
May I humbly request something?
It would be a new XML tag for BuildingInfos that hides the civic requirement on the F3 Civic screen (but only there).
My Guilds civic looks like this at the moment:
Civ4ScreenShot0121.JPG
I'd like to hide that "Allows construction of four lines of so and so..." and just put there a simple "Allows construction of guild halls." using the help tag.
In my plans for the future I would have even greater use of it.
I hope it's not that hard to do.
 
I just uploaded rev1086, with a forced recalc to correct a bug with resources and with a fix for OverrideResourceBuilding (thanks f1rpo). In the next days I will test devolution and f1rpo changes to the code, if everything works I'll upload the changes in the next days.
 
I just uploaded rev1086, with a forced recalc to correct a bug with resources and with a fix for OverrideResourceBuilding (thanks f1rpo). In the next days I will test devolution and f1rpo changes to the code, if everything works I'll upload the changes in the next days.

@45°38'N-13°47'E

May I ask that have you solved bug in recalc? Many players has reported that recalc wipes out all commerce from buildings etc.
Havent tested it my self for long time but last time it messed up commerce for me too.
 
Here's an old post where I explain how Movement Limits is supposed to work. [...]
I've been testing it with the Chronicles mod, but I think it's the same with just AND – units outside their limits take terrain damage. If they were actually prohibited from moving, then performance would be a bit of a concern because canMoveInto gets called by the pathfinder. (I recall an all-Python mod component that had prohibited such moves and was prohibitively slow.) AI code for avoiding terrain damage might also want to check movement limits at every pathfinding step, but I don't think that's currently the case.
[...]
- What about proximity to vassals ?
Or teammates. So perhaps:
Spoiler :
Code:
   for (int i = 0; i < MAX_CIV_PLAYERS; i++)
   {
       CvPlayer const& kLoopPlayer = GET_PLAYER((PlayerTypes)i);
       if (!kLoopPlayer.isAlive())
           continue;
       if (kLoopPlayer.getTeam() == kOwner.getTeam() ||
           GET_TEAM(kLoopPlayer.getTeam()).isVassal(kOwner.getTeam()))
       {
           int iIter;
           for (CvCity const* pCity = kOwner.firstCity(&iIter); pCity != NULL;
               pCity = kOwner.nextCity(&iIter))
           {
               if (plotDistance(pPlot->getX(), pPlot->getY(),
                   pCity->getX(), pCity->getY()) <= iMovementLimit)
               {
                   return true;
               }
           }
       }
   }
If Forts are supposed to count, then one would have to check all nearby tiles (for iDX = -MovementLimit...) instead of going through the cities.
- What about a unit being in a different area than the closest city. Crossing an area should arguably be considered more "limiting".
- Probably need to do full pathfinding to capture the effect of borders, impassable plots etc.
In terms of supplies reaching the unit, that would sure be more realistic. Well, I don't think using air distance is bad.
is that ok with you if I add your code to the dll and add your names to the credits page of AND2? Thanks again.
Sure. Credits: No need, but please feel free to; in my case, it's really a tiny contribution.
Please try to save, quit to desktop and load too because with the present dll I had experience when I started a game and "Yaay! Movement Limit is working finally" :woohoo:" and than I quit and loaded later and than "It's not working! Again! :gripe::badcomp: :wallbash:"
Can't reproduce that. If something doesn't get saved properly, then I doubt that my changes have done anything to fix that. I guess it's possible that the
GET_PLAYER(pPlot->getOwnerINLINE()).getTeam() != NO_TEAM
check was responsible for the limit not getting applied sometimes.
[...] I'd like to hide that "Allows construction of four lines of so and so..." and just put there a simple "Allows construction of guild halls." using the help tag. [...]
How it works for Temple, I think, is that the tech requirement is set in Civ4SpecialBuildingInfos.xml.
 
Back
Top Bottom