aliens

Author Topic: Bug in TileEngine::calculateUnitsInFOV  (Read 1937 times)

Offline Xilmi

  • Moderator
  • Commander
  • ***
  • Posts: 642
    • View Profile
Bug in TileEngine::calculateUnitsInFOV
« on: December 14, 2022, 09:29:00 pm »
There's a bug is in calculateUnitsInFOV.

This method creates a different result depending on whether or not setupEventVisibilitySector(posSelf, eventPos, eventRadius) was true.

If it's not true, then under certain circumstances it will call unit->removeFromVisibleUnits((*i)); that it otherwise wouldn't.

This inconsistency confuses the waiting-algorithm of my AI as it's based around checking what other units can see. When it's the units turn setupEventVisibilitySector becomes true and it sees an enemy. But when it's updated on someone else's turn and it's false, then it doesn't see the enemy.

I suspect it may also have something to do with the size of the unit to be see as it happened with 2x2 units and I haven't had this happen before for a long time.

I attached what I did to work around this. But this probably isn't the best way to fix this issue.

Offline Xilmi

  • Moderator
  • Commander
  • ***
  • Posts: 642
    • View Profile
Re: Bug in TileEngine::calculateUnitsInFOV
« Reply #1 on: December 16, 2022, 02:57:28 pm »
This workaround was wrong.
Proper fix looks like this:

Code: [Select]
if (!(*i)->isOut() && (unit->getId() != (*i)->getId()))
{
int sizeOther = (*i)->getArmor()->getSize();
int totalUnitTiles = 0;
int unitTilesNotInViewSector = 0;
for (int x = 0; x < sizeOther; ++x)
{
for (int y = 0; y < sizeOther; ++y)
{
totalUnitTiles++;
Position posToCheck = posOther + Position(x, y, 0);
//If we can now find any unit within the arc defined by the event tangent points, its visibility may have been affected by the event.
if (inEventVisibilitySector(posToCheck))
{
if (!unit->checkViewSector(posToCheck, useTurretDirection))
{
//Unit within arc, but not in view sector. If it just walked out we need to remove it.
unitTilesNotInViewSector++;
}
else if (visible(unit, _save->getTile(posToCheck))) // (distance is checked here)
{
//Unit (or part thereof) visible to one or more eyes of this unit.
if (unit->getFaction() == FACTION_PLAYER)
{
(*i)->setVisible(true);
}
if ((( (*i)->getFaction() == FACTION_HOSTILE && unit->getFaction() == FACTION_PLAYER )
|| ( (*i)->getFaction() != FACTION_HOSTILE && unit->getFaction() == FACTION_HOSTILE ))
&& !unit->hasVisibleUnit((*i)))
{
unit->addToVisibleUnits((*i));
unit->addToVisibleTiles((*i)->getTile());

if (unit->getFaction() == FACTION_HOSTILE && (*i)->getFaction() != FACTION_HOSTILE)
{
(*i)->setTurnsSinceSpotted(0);
(*i)->setTurnsSinceSeen(0);
(*i)->setTileLastSpotted(_save->getTileIndex((*i)->getPosition()));
(*i)->setTurnsLeftSpottedForSnipers(std::max(unit->getSpotterDuration(), (*i)->getTurnsLeftSpottedForSnipers())); // defaults to 0 = no information given to snipers
}
}

x = y = sizeOther; //If a unit's tile is visible there's no need to check the others: break the loops.
}
else {
//Within arc, but not visible. Need to check to see if whatever happened at eventPos blocked a previously seen unit.
unitTilesNotInViewSector++;
}
}
}
}
if (unitTilesNotInViewSector == totalUnitTiles)
{
unit->removeFromVisibleUnits((*i));
}
}
Basically: Instead of removing the unit from the list of visible when one tile is not in the view-sector, it's now removed when all tiles are not in the view-sector.
The previous work-around would make big units always visible regardless of the direction a unit was looking to.