Deliverator
Graphical Hackificator
Finally, located an pre-crash Assert failure:
The lines of code are with the DCM code, CvUnitAI::AI_RbombardCity:
I think the second FAssert would blow up if pDefender is null, which it clearly can be.
I looked at the equivalent code in the Dune Wars 1.8 RevDCM version and the AI_RbombardCity is very different, much simpler and looks null-safe.
Dune Wars 1.8X / DCM version in full:
Dune Wars 1.8 / RevDCM version in full:
This could be down to the joys of code interaction. Dales code was was written before Uncut Dragon's Lead from Behind modcomp. Perhaps the vanilla getBestDefender never returns null whereas the Lead from Behind version does.
Anyway, there are two options for a fix. I can either swap in the RevDCM version of AI_RbombardCity or make the more sophisticated DCM version safe against getBestDefender returning null.
On the XML front, my manual schema validation technique has found a long list of problems with CIV4BuildingInfos.xml. Mostly these are to do with the fact that the schema expects <Help> to be after <Strategy> but they are the wrong way around in many cases. Also, there were uses of <BonusType> instead of <Bonus> inside <PrereqBonuses> and YieldProduced etc being in the wrong place.
All of this proves that the mod loading does mean your XML is valid. It would be great if there was an automated way of verifying all your files against the schemas. My solution is quite tedious, but it has saved a lot of time in the case of BuildingInfos.
Attached cleaned and validated versions of LeaderHeadInfos and BuildingInfos. I can see these making the Python pedia issues go away (hopefully). Then it's just a question of fixing the SDK crash.
Code:
File: CvUnitAI.cpp
Line: 24367
Expression: pDefender != NULL
Message:
The lines of code are with the DCM code, CvUnitAI::AI_RbombardCity:
Code:
pDefender = pLoopPlot->getBestDefender(NO_PLAYER, getOwnerINLINE(), this, true);
[B]FAssert(pDefender != NULL);[/B]
FAssert(pDefender->canDefend());
iDamage = GC.getGameINLINE().getSorenRandNum(bombardRate(), "AI Bombard");
I think the second FAssert would blow up if pDefender is null, which it clearly can be.
I looked at the equivalent code in the Dune Wars 1.8 RevDCM version and the AI_RbombardCity is very different, much simpler and looks null-safe.
Dune Wars 1.8X / DCM version in full:
Spoiler :
Code:
// Dale - RB: Field Bombard START
// Returns true if a mission was pushed...
bool CvUnitAI::AI_RbombardCity()
{
PROFILE_FUNC();
CvCity* pCity;
CvPlot* pLoopPlot;
CvPlot* pBestPlot;
CvUnit* pDefender;
int iSearchRange;
int iPotentialAttackers;
int iValue;
int iDamage;
int iBestValue;
int iDX, iDY;
if(!canRBombard(plot()))
{
return false;
}
// for (int iPlayer = 0; iPlayer < MAX_CIV_PLAYERS; ++iPlayer)
// {
// gDLL->getInterfaceIFace()->addMessage((PlayerTypes)iPlayer, true, GC.getDefineINT("EVENT_MESSAGE_TIME"), "Got here!", "AS2D_BOMB_FAILS", MESSAGE_TYPE_INFO, GC.getUnitInfo(getUnitType()).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), plot()->getX_INLINE(), plot()->getY_INLINE());
// }
if(GC.getDCM_RANGE_BOMBARD())
{
iSearchRange = getDCMBombRange();
iBestValue = 0;
pBestPlot = NULL;
for (iDX = -(iSearchRange); iDX <= iSearchRange; iDX++)
{
for (iDY = -(iSearchRange); iDY <= iSearchRange; iDY++)
{
pLoopPlot = plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);
if (pLoopPlot != NULL)
{
if (canBombardAtRanged(plot(), pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE()))
{
iValue = 0;
pCity = pLoopPlot->getPlotCity();
if (pCity != NULL)
{
iValue += std::max(0, (std::min((pCity->getDefenseDamage() + airBombCurrRate()), GC.getMAX_CITY_DEFENSE_DAMAGE()) - pCity->getDefenseDamage()));
iValue *= 5;
if (pCity->AI_isDanger())
{
iValue *= 2;
}
if (pCity == pCity->area()->getTargetCity(getOwnerINLINE()))
{
iValue *= 3;
}
}
iPotentialAttackers = GET_PLAYER(getOwnerINLINE()).AI_adjacentPotentialAttackers(pLoopPlot);//pLoopPlot->getNumVisibleEnemyDefenders(NO_PLAYER);
if (iPotentialAttackers > 0 || pLoopPlot->isAdjacentTeam(getTeam()))
{
pDefender = pLoopPlot->getBestDefender(NO_PLAYER, getOwnerINLINE(), this, true);
[B][COLOR="Red"] FAssert(pDefender != NULL);
FAssert(pDefender->canDefend());[/COLOR] - NB Problem lines[/B]
iDamage = GC.getGameINLINE().getSorenRandNum(bombardRate(), "AI Bombard");
// iValue = max(0, (min((pDefender->getDamage() + iDamage), bombardRate()) - pDefender->getDamage()));
iValue += ((((iDamage * collateralDamage()) / 100) * std::min((pLoopPlot->getNumVisibleEnemyDefenders(this) - 1), collateralDamageMaxUnits())) / 2);
iValue *= (3 + iPotentialAttackers);
iValue /= 4;
}
iValue *= GC.getGameINLINE().getSorenRandNum(20, "AI Bombard");
if (iValue > iBestValue)
{
iBestValue = iValue;
pBestPlot = pLoopPlot;
FAssert(!atPlot(pBestPlot));
}
}
}
}
}
if (pBestPlot != NULL)
{
getGroup()->pushMission(MISSION_RBOMBARD, pBestPlot->getX_INLINE(), pBestPlot->getY_INLINE());
return true;
}
}
return false;
}
Dune Wars 1.8 / RevDCM version in full:
Spoiler :
Code:
// Returns true if a mission was pushed...
bool CvUnitAI::AI_RbombardCity(CvCity* pCity)
{
PROFILE_FUNC();
CvUnit* pDefender;
CvPlot* pPlot;
if(!GC.getDCM_RANGE_BOMBARD())
{
return false;
}
if (GC.getDefineINT("DCM_NAVAL_BOMBARD"))
{
return false;
}
if (pCity == NULL)
{
return false;
}
pPlot = pCity->plot();
if (pPlot == NULL)
{
return false; // ok will never happen but anyway...
}
if(!canBombardAtRanged(plot(), pPlot->getX_INLINE(),pPlot->getY_INLINE()))
{
return false;
}
pDefender = pPlot->getBestDefender(NO_PLAYER, getOwnerINLINE(), this, true);
if (pDefender != NULL) [B][COLOR="Red"]- NB this check makes is what Dales version is missing[/COLOR][/B]
{
if (collateralDamageMaxUnits() > pPlot->getNumUnits())
{
if (pDefender->isRbombardable(3) && getGroup()->getBombardTurns(pCity) < 3)
{
getGroup()->pushMission(MISSION_RBOMBARD, pPlot->getX_INLINE(),pPlot->getY_INLINE());
return true;
}
}
}
return false;
}
This could be down to the joys of code interaction. Dales code was was written before Uncut Dragon's Lead from Behind modcomp. Perhaps the vanilla getBestDefender never returns null whereas the Lead from Behind version does.
Anyway, there are two options for a fix. I can either swap in the RevDCM version of AI_RbombardCity or make the more sophisticated DCM version safe against getBestDefender returning null.
On the XML front, my manual schema validation technique has found a long list of problems with CIV4BuildingInfos.xml. Mostly these are to do with the fact that the schema expects <Help> to be after <Strategy> but they are the wrong way around in many cases. Also, there were uses of <BonusType> instead of <Bonus> inside <PrereqBonuses> and YieldProduced etc being in the wrong place.
All of this proves that the mod loading does mean your XML is valid. It would be great if there was an automated way of verifying all your files against the schemas. My solution is quite tedious, but it has saved a lot of time in the case of BuildingInfos.
Attached cleaned and validated versions of LeaderHeadInfos and BuildingInfos. I can see these making the Python pedia issues go away (hopefully). Then it's just a question of fixing the SDK crash.