TileEngine::canTargetUnit is used to find a voxel within the target for which we have a clear line to, so that we can shoot at the target. It it finds a line-of-fire, *scanVoxel is set to be the target voxel and the function returns true. But when it cannot find a clear line-of-fire, the function returns false and *scanVoxel is just left on whichever value happened to be the last one checked.
That's all fine, but unfortunately the return value of TileEngine::canTargetUnit is actually ignored. Instead, the target voxel (whatever *scanVoxel was set to) is given to Projectile::calculateTrajectory, which then gets final say in whether or not we can take the shot. If it looks like we were trying to aim at a unit but the shot will actually hit a wall, then we'll abort the shot.
The problem is that Projectile::calculateTrajectory doesn't actually know what we were trying to aim at. Usually it is able to correctly guess that we were aiming at a unit based on the (bogus) voxel, but not always. What can happen is that TileEngine::canTargetUnitreturns false (meaning that we can't hit the target), but the final target voxel given to Projectile::calculateTrajectory makes it look like we didn't want to hit a unit anyway - and so our solider will happily shoot at empty space rather than telling us that there was no line-of-fire to the enemy.
--
The most obvious way to fix this would be to use the return value of TileEngine::canTargetUnit to decide whether we can take the shot. However, that could require some structural changes and some code duplication. So I've made a simpler 1-line fix, which just ensures that the target voxel given to Projectile::calculateTrajectory will always be in the unit when we were trying to aim at the unit.
Here:
diff --git a/src/Battlescape/TileEngine.cpp b/src/Battlescape/TileEngine.cpp
index 3510149..9265e20 100644
--- a/src/Battlescape/TileEngine.cpp
+++ b/src/Battlescape/TileEngine.cpp
@@ -820,6 +820,9 @@ bool TileEngine::canTargetUnit(Position *originVoxel, Tile *tile, Position *scan
}
}
}
+ https:// Couldn't find a line of fire; so just set the scanVoxel to be at the centre of the target.
+ https:// (Not all callers pay attention to the return value of this function.)
+ *scanVoxel = Position((tile->getPosition().x * 16) + 7, (tile->getPosition().y * 16) + 8, targetCenterHeight);
return false;
}
--
PS. I'm not sure where the best place for these bug fixes is. I noticed and fixed this bug on xPiratez v0.99. I'm attaching a save game which demonstrates the problem. (Shoot at the enemy in the window; you will miss every single time. Debugging shows that there was no line of fire, but the game lets you shoot anyway.)
I guess I'll post something the bugtracker, at
https://openxcom.org/bugs/openxcom/issues/new/issuetype/bugreport -- but I'll have to lie about which version of OpenXcom I was using; because as I said - I was playing a different version. The code does look the same though.
[edit]
Posted
here. Please let me know if there is a better place or better way of reporting this kind of stuff.