So you're saying that pretty much any old unit is being chosen to answer any old brokerage call? Even if a unitAI is specified in the criteria of the call? That seems to NOT be
Surely the whole point of including an AI designation in a brokerage call is intended to only be answered by units with that AI setting?!? I'll have to look at that much more carefully.
So that is to suggest that it is a more fundamental problem with both brokerage and direct training pushes, or possibly a problem where a unit AI type changes during play. Which then makes me think I've just corrected the largest issue, because the point I was working with appears, if I'm reading the flow correctly and following the debug run process accurately, to lead through the same function where non-AI types are filtered.
int CvPlayerAI::AI_unitValue(UnitTypes eUnit, UnitAITypes eUnitAI, CvArea* pArea, CvUnitSelectionCriteria* criteria) const
The strange thing is that there are other things being used to filter out particular unit types there, such as demanding that Settler units are defined first and foremost by their ability to found a city, and if they can't do that then they can't be selected for that role. So a case like the scout ending up as a settler can hardly be answered by this since a scout cannot found a city. How we're getting around that then makes me suspicious that it's not how or where the unit is called to be generated, but rather that perhaps a scout had joined with a settler unit and taken on the AI of the settler unit when it joined the stack, which would just be a continuation of a bug I had mostly solved already but needed to be more sweeping about the solution. There's a very good chance we've been having a lot of these strange behaviors all along and they're actually less frequent now than they were and I've just failed to patch all the places where these problems begin.
This suggests that there remain larger problems with void CvSelectionGroup::mergeIntoGroup(CvSelectionGroup* pSelectionGroup) where you have:
Code:
CvPlayerAI& kPlayer = GET_PLAYER(getOwnerINLINE());
// merge groups, but make sure we do not change the head unit AI
// this means that if a new unit is going to become the head, change its AI to match, if possible
// AI_setUnitAIType removes the unit from the current group (at least currently), so we have to be careful in the loop here
// so, loop until we have not changed unit AIs
bool bChangedUnitAI;
do
{
bChangedUnitAI = false;
// loop over all the units, moving them to the new group,
// stopping if we had to change a unit AI, because doing so removes that unit from our group, so we have to start over
CLLNode<IDInfo>* pUnitNode = headUnitNode();
while (pUnitNode != NULL && !bChangedUnitAI)
{
CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = nextUnitNode(pUnitNode);
if (pLoopUnit != NULL)
{
UnitAITypes eUnitAI = pLoopUnit->AI_getUnitAIType();
// if the unitAIs are different, and the loop unit has a higher val, then the group unitAI would change
// change this UnitAI to the old group UnitAI if possible
CvUnit* pNewHeadUnit = pSelectionGroup->getHeadUnit();
UnitAITypes eNewHeadUnitAI = pSelectionGroup->getHeadUnitAI();
if (pNewHeadUnit!= NULL && eUnitAI != eNewHeadUnitAI && pLoopUnit->AI_groupFirstVal() > pNewHeadUnit->AI_groupFirstVal())
{
// non-zero AI_unitValue means that this UnitAI is valid for this unit (that is the check used everywhere)
if (kPlayer.AI_unitValue(pLoopUnit->getUnitType(), eNewHeadUnitAI, NULL) > 0)
{
FAssert(pLoopUnit->AI_getUnitAIType() != UNITAI_HUNTER);
// this will remove pLoopUnit from the current group
pLoopUnit->AI_setUnitAIType(eNewHeadUnitAI);
bChangedUnitAI = true;
}
}
pLoopUnit->joinGroup(pSelectionGroup);
}
}
}
while (bChangedUnitAI);
This call:
pLoopUnit->AI_setUnitAIType(eNewHeadUnitAI);
is causing mass havoc because many unit AI types should never change even if they are called to join a group. The checks to see if there enough of those types in the stack then get completely thrown off and find none of those types because their AI changed when joining the stack. Thus we get endless spams of healers and spotters and so on. I'm sure that's supposed to work grand for some things merging into an attack stack or something, though I'm hard pressed to see how that's a good thing there or anywhere else. Units should always know what their role is even if they are bringing that role to a larger collaborative effort. I'm sure this is largely a big part of our city attack stack design problems in particular.
I'm really considering going through and just eliminating this function's usage entirely until it can be made clear why it would ever be useful. joinGroup(pJoinUnit->getGroup()); seems to be a much better solution.
If this has to do with making sure the lead unit is using the best AI type and if it dies, switching the next best unit for that role over, then it needs to be more carefully crafted to ensure that the stack can even do that job after the lead unit is gone - for example, a scout escorting a settler would not want to suddenly be made a settler unit by AI just because the settling took place and the stack's head unit has now changed. But from what I was seeing in-game was that this function, MergeIntoGroup, was causing extremely poor AI changes to units that should never have changed their AI types.