I have run into a debug assertion warning, and it seems like the code is incorrect. Here is the function in its entirety:
The line in question is
The violated assertion was in getVoteOutcome, which is a simple getter with trivial implementation. The issue was that the value of iI exceeded the number of VoteInfos defined in the DLL (28 > 27 in my case). It happened in my mod, but I checked the code against BtS and it is unchanged.
This seems wrong to me. First of all, iI is just the index of a struct in the kVoteSelectionData list, which is then carelessly cast to VoteTypes. The actual VoteType of that struct is eVote. There can easily be more entries in kVoteSelectionData than there are VoteTypes, because some vote types can be selected multiple types. For example "declare war against X" can have multiple votes with different targets. This is a clear error.
But that raised further questions for me. eVote is a VoteType, which is just an ID for a VoteInfo, i.e. the description of a UN/AP resolution. It cannot be PLAYER_VOTE_YES. Instead, PLAYER_VOTE_YES is a value of the enum PlayerVoteTypes, which describes the decision a player has made on a given resolution that has been put to a vote. The compiler lets this happen because enums can always be compared on their integer values (really unfortunate choice of a naming convention if you ask me). Likewise, it turns out CvGame::getVoteOutcome also returns a PlayerVoteTypes enum, but its returned value is compared to eVote, which is a VoteTypes enum!
Okay great. So basically everything in this line is incorrect. But that leaves the question, what is the intent of this line? Why does this method still mostly fulfill its function and this error goes unnoticed in practice?
Code:
int CvTeamAI::AI_chooseElection(const VoteSelectionData& kVoteSelectionData) const
{
VoteSourceTypes eVoteSource = kVoteSelectionData.eVoteSource;
FAssert(!isHuman());
FAssert(GC.getGameINLINE().getSecretaryGeneral(eVoteSource) == getID());
int iBestVote = -1;
int iBestValue = 0;
for (int iI = 0; iI < (int)kVoteSelectionData.aVoteOptions.size(); ++iI)
{
VoteTypes eVote = kVoteSelectionData.aVoteOptions[iI].eVote;
CvVoteInfo& kVoteInfo = GC.getVoteInfo(eVote);
FAssert(kVoteInfo.isVoteSourceType(eVoteSource));
FAssert(GC.getGameINLINE().isChooseElection(eVote));
bool bValid = true;
if (!GC.getGameINLINE().isTeamVote(eVote))
{
for (int iJ = 0; iJ < MAX_PLAYERS; iJ++)
{
if (GET_PLAYER((PlayerTypes)iJ).isAlive())
{
if (GET_PLAYER((PlayerTypes)iJ).getTeam() == getID())
{
PlayerVoteTypes eVote = GET_PLAYER((PlayerTypes)iJ).AI_diploVote(kVoteSelectionData.aVoteOptions[iI], eVoteSource, true);
if (eVote != PLAYER_VOTE_YES || eVote == GC.getGameINLINE().getVoteOutcome((VoteTypes)iI))
{
bValid = false;
break;
}
}
}
}
}
if (bValid)
{
int iValue = (1 + GC.getGameINLINE().getSorenRandNum(10000, "AI Choose Vote"));
if (iValue > iBestValue)
{
iBestValue = iValue;
iBestVote = iI;
}
}
}
return iBestVote;
}
Code:
if (eVote != PLAYER_VOTE_YES || eVote == GC.getGameINLINE().getVoteOutcome((VoteTypes)iI))
This seems wrong to me. First of all, iI is just the index of a struct in the kVoteSelectionData list, which is then carelessly cast to VoteTypes. The actual VoteType of that struct is eVote. There can easily be more entries in kVoteSelectionData than there are VoteTypes, because some vote types can be selected multiple types. For example "declare war against X" can have multiple votes with different targets. This is a clear error.
But that raised further questions for me. eVote is a VoteType, which is just an ID for a VoteInfo, i.e. the description of a UN/AP resolution. It cannot be PLAYER_VOTE_YES. Instead, PLAYER_VOTE_YES is a value of the enum PlayerVoteTypes, which describes the decision a player has made on a given resolution that has been put to a vote. The compiler lets this happen because enums can always be compared on their integer values (really unfortunate choice of a naming convention if you ask me). Likewise, it turns out CvGame::getVoteOutcome also returns a PlayerVoteTypes enum, but its returned value is compared to eVote, which is a VoteTypes enum!
Okay great. So basically everything in this line is incorrect. But that leaves the question, what is the intent of this line? Why does this method still mostly fulfill its function and this error goes unnoticed in practice?