My fixes

@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.

I only forced a recalc after a human player discovers any tech that allows trade over water/ocean: it happens max twice in a game; it really shouldn't be an issue and anyway definetly has a smaller impact, if any, than playing with your resources doubled (which is something really messing up commerce).
 
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.

Chronicles uses AND dll, no difference at all there. As for ML, it's currently working correctly except for that glitch which makes the option inactive on some occasion when loading/saving the game. If Terrain Damage is ON, units beyond the limit get damage. If Terrain Damage is OFF, they get bounced back to the nearest valid plot when they move beyond the limit. I've chosen to check distance to the nearest city exactly because checking every tile would be a huge and IMO unnecessary load.
 
Thanks for clarifying. I wasn't sure how the ModderGameOptionTypes are set and had assumed that terrain damage might always be on. :blush: I've found the "A New Dawn" tab on the BUG menu now. OK, in that case, even looking for cities of team members and vassals seems questionable.
 
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);
}

I've just tried it and it does not work, see attached image. Actually, Routes were being displayed correctly in my game when using rev1085 and rev1086. Most of the times, at least. For some reason, sometimes the list was incomplete. I suppose it might be the same now. I recall @Vokarya telling me the list was incomplete in his game while it was complete in my game while we were using the same revision. I thought it might be some experimental code we overlooked. But right now I have a fresh install of rev1086 and I only updated the dll with your changes. No luck, as you can see.

Edit: I can see the whole list from the Civilopedia in the main menu
 

Attachments

  • Routes.jpg
    Routes.jpg
    118.4 KB · Views: 252
Ok, I think I tested with both the pedia from the main menu and in-game. The code is 1 year old so maybe I missed something while diffing ( I did not update so that's perhaps the reason why). I'll update to the latest rev (hopefully I will find time during the weekend) and then recheck.
 
How it works for Temple, I think, is that the tech requirement is set in Civ4SpecialBuildingInfos.xml.
Yeah, that would be nice too, but it doesn't work that way either.
 
Ok, I think I tested with both the pedia from the main menu and in-game. The code is 1 year old so maybe I missed something while diffing ( I did not update so that's perhaps the reason why). I'll update to the latest rev (hopefully I will find time during the weekend) and then recheck.

The issue that I always ran into was that the Routes page worked fine from the main menu Civilopedia but failed when trying to access the Civilopedia in-game. It might be something scaling with game variables that the Civilopedia can't display. I could never figure it out because I really can't mod the DLL at all. I can read the source code but that's about it.
 
Yeah, that would be nice too, but it doesn't work that way either.

I think we would need to re-implement any building tag by creating a parallel tag that could be used with a SPECIALBUILDING type instead. I'd love to clean up Great Mosque of Djenne too. The original SpecialBuildingInfos.xml file is very limited in what it can do.
 
I think we would need to re-implement any building tag by creating a parallel tag that could be used with a SPECIALBUILDING type instead. I'd love to clean up Great Mosque of Djenne too. The original SpecialBuildingInfos.xml file is very limited in what it can do.
Well, it would be really great if we could use any BuildingInfos tag in SpecialBuildingInfos and ProjectInfo.
Especially the second would open up a lot of opportunities for modding.
 
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);
    }
}

I'm pretty sure this part of the code is causing OOS problems in MP. I just tried it in a new game and on turn 20 I got an OOS caused by a civ having 5 units and 5 groups for player10, and 51 units and 51 groups for player10 on another instance of the game (both players running on the same pc). No way a player might have 51 units on turn 20 and never experienced this OOS before, so I assume it has something to do with this part of the code or at least with how this part of the code interacts with the rest of AND code.
 
The only real difference with the original code is this section:
Code:
  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
which basically ensures that attempting to regroup is only allowed once. I don't see how this can have anything to do with the number of units that can be produced by turn x at all. Are you really sure this is the only change? Note that it is possible that group cycling is handled a bit differently in AND2 though so we may have to look into that.

Btw, I suspect that recalc will break the gamestate if it's used after the game entities have been initialized. Can you check if there were any recalcs of any sort prior to this happening ?

If you manually trigger successive recalcs, do you get different results each time? if that's the case then I would be very surprised if a recalc did not eventually cause OOS issues. It's even worse if a recalc is done during a specific player's turn, rather than happening all the players' turns are processed as is the case when a game is loaded (no turn order in this case so recalc should be safe (maybe) )


Update:

I just had a look at the following changeset: Diff of /Trunk/Sources/CvTeam.cpp [r1085] .. [r1086]

https://sourceforge.net/p/anewdawn/...CvTeam.cpp?diff=51a640ad5fcbc9451351e902:1085

Code:
--- a/Trunk/Sources/CvTeam.cpp
+++ b/Trunk/Sources/CvTeam.cpp
@@ -7482,6 +7482,16 @@
                 gDLL->getInterfaceIFace()->setDirty(ResearchButtons_DIRTY_BIT, true);
                 gDLL->getInterfaceIFace()->setDirty(GlobeLayer_DIRTY_BIT, true);
             }
+//45deg: rev1086, workaround for multiple resources bug - START     
+            for (int iJ = 0; iJ < GC.getNumTerrainInfos(); iJ++)
+            { 
+                if ((GC.getTechInfo(eIndex).isTerrainTrade(iJ)) && (isHuman()))
+                {
+                    GC.getGameINLINE().recalculateModifiers();
+                    GC.getGameINLINE().logMsg("Warning, might cause OOS in MP!");
+                }
+            }
+//45deg: rev1086, workaround for multiple resources bug - END     
         }
     }
 }

Why is recalc performed in a loop ? If it should be done at all, better do it only once.
Why is there a check for isHuman() when recalc affects all players ?

I think these issues may instead be related to a stale cache that is not getting updated properly.
 
Last edited:
The informations about routes in the Civilopedia, works properly (running in the game) on the mod Realism Invictus 3.4. Maybe you can ask for help to the mod's editing team.
 
Last edited:
I just had a look at the following changeset: Diff of /Trunk/Sources/CvTeam.cpp [r1085] .. [r1086]

https://sourceforge.net/p/anewdawn/...CvTeam.cpp?diff=51a640ad5fcbc9451351e902:1085

Code:
--- a/Trunk/Sources/CvTeam.cpp
+++ b/Trunk/Sources/CvTeam.cpp
@@ -7482,6 +7482,16 @@
                 gDLL->getInterfaceIFace()->setDirty(ResearchButtons_DIRTY_BIT, true);
                 gDLL->getInterfaceIFace()->setDirty(GlobeLayer_DIRTY_BIT, true);
             }
+//45deg: rev1086, workaround for multiple resources bug - START   
+            for (int iJ = 0; iJ < GC.getNumTerrainInfos(); iJ++)
+            {
+                if ((GC.getTechInfo(eIndex).isTerrainTrade(iJ)) && (isHuman()))
+                {
+                    GC.getGameINLINE().recalculateModifiers();
+                    GC.getGameINLINE().logMsg("Warning, might cause OOS in MP!");
+                }
+            }
+//45deg: rev1086, workaround for multiple resources bug - END   
         }
     }
 }

Why is recalc performed in a loop ? If it should be done at all, better do it only once.
Why is there a check for isHuman() when recalc affects all players ?

I think these issues may instead be related to a stale cache that is not getting updated properly.

I'll run again a test before the latest changes to see if OOS still appears, but I remember testing some time ago and it didn't.
As for the recalc, it's not performed multiple times: I have discovered that for human players sometimes when a couple of specific techs are discovered, i.e. techs which allow trade over sea/ocean, some resources are being multiplied and I haven't been able to fix this bug. But performing a recalc solves the problem. In theory, all values should be correct after a recalc.

Edit: just to make sure, I removed the new joinGroup code and tried again, no OOS, even when I reach Ship Building and hence a recalc is forced. I will try again updating the code just to check if I missed something when I merged the new code in the dll the first time. But since the OOS logs show different number of unit groups for different players, my idea is that that part of the code might be the problem. Maybe it works in K-Mod but it's being used differently in AND.
 
Last edited:
The issue that I always ran into was that the Routes page worked fine from the main menu Civilopedia but failed when trying to access the Civilopedia in-game. It might be something scaling with game variables that the Civilopedia can't display. I could never figure it out because I really can't mod the DLL at all. I can read the source code but that's about it.

My apologies, apparently I've pasted the wrong version of the code :sad:
 
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:
View attachment 544497
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.
So? Is that doable? :mischief:

And an other request if I may add:
A PassableTech tag for CIV4TerrainInfos. Using this with bImpassable would make the specific terrain impassable for ALL UNITS until the defined tech is researched. by the player.
Of course TerrainPassableTech from UnitInfos should be able to override the effect.
 
So how is this gong? Is there a fix for movement limit?

No, I'm running some test but it looks like it doesn't work. The problem is that it's hardbto reproduce the bug, sometimes it happens and keeps showing, sometimes it doesn't show up and I can't understand why.
 
Those fixes looks nice.Any chance someone will issue full AND version, so we don't have to bother with SVN etc...
It's been more than a year since last version.
 
Those fixes looks nice.Any chance someone will issue full AND version, so we don't have to bother with SVN etc...
It's been more than a year since last version.

I'm about to release a new version probably later today. Movement Limits is still not fixed, but I applied a couple of fixes by devolution. Also the updater/launcher should be fixed in the next revision, I probably discovered what was wrong. Stay tuned.
 
Back
Top Bottom