ok. I've found the bug.
Here is a patch.
diff --git a/src/Battlescape/TileEngine.cpp b/src/Battlescape/TileEngine.cpp
index 35214de..8d7fb9f 100644
--- a/src/Battlescape/TileEngine.cpp
+++ b/src/Battlescape/TileEngine.cpp
@@ -2755,29 +2754,31 @@ int TileEngine::calculateParabola(const Position& origin, const Position& target
x = (int)((double)origin.x + (double)i * cos(te) * sin(fi));
y = (int)((double)origin.y + (double)i * sin(te) * sin(fi));
z = (int)((double)origin.z + (double)i * cos(fi) - zK * ((double)i - ro / 2.0) * ((double)i - ro / 2.0) + zA);
- if (storeTrajectory && trajectory)
- {
- trajectory->push_back(Position(x, y, z));
- }
https://passes through this point?
Position nextPosition = Position(x,y,z);
- int result = calculateLine(lastPosition, nextPosition, false, 0, excludeUnit);
+ std::vector<Position> contactPoint;
+ int result = calculateLine(lastPosition, nextPosition, false, &contactPoint, excludeUnit);
if (result != V_EMPTY)
{
if (lastPosition.z < nextPosition.z)
{
- result = V_OUTOFBOUNDS;
+ result = V_OUTOFBOUNDS; https:// why??
}
- if (!storeTrajectory && trajectory != 0)
+ if (trajectory != nullptr)
{ https:// store the position of impact
- trajectory->push_back(nextPosition);
+ assert(contactPoint.size() > 0);
+ trajectory->push_back(contactPoint[0]);
}
return result;
}
+ if (storeTrajectory && trajectory != nullptr)
+ {
+ trajectory->push_back(nextPosition);
+ }
lastPosition = Position(x,y,z);
++i;
}
- if (!storeTrajectory && trajectory != 0)
+ if (!storeTrajectory && trajectory != nullptr)
{ https:// store the position of impact
trajectory->push_back(Position(x, y, z));
}
The problem was a mismatch between the coordinates given to TileEngine::hit, and the coordinates where the actual collision occurred.
To do collection detection on arcs, the game calculates a list of points on the arc then draws straight lines between and tests if those straight lines make contact with anything. The game was correctly detecting the collisions with those straight lines, but then instead of storing the coordinates of the collisions, it just stored the coordinates of the end point of the straight line - which may or may not have been where the collision occurred. And so sometimes the end-point of those lines was inside the target, and sometimes it wasn't. The problem could be reduced by taking more points on the arc (so the lines are shorter); and doing that might still be a good idea to make the arcs more arc-like. But ultimately, we still need to store where the collision takes place - and that's what I've done.
Aside from that fix, here are a couple of other unimportant changes that I've made. Take them or leave them as you see fit.
---
Undefined 'floor' in CraftWeapon.cpp.
diff --git a/src/Savegame/CraftWeapon.cpp b/src/Savegame/CraftWeapon.cpp
index 2b952c4..b2e51c0 100644
--- a/src/Savegame/CraftWeapon.cpp
+++ b/src/Savegame/CraftWeapon.cpp
@@ -17,6 +17,7 @@
* along with OpenXcom. If not, see <https://www.gnu.org/licenses/>.
*/
#include <algorithm>
+#include <cmath>
#include "CraftWeapon.h"
#include "../Mod/RuleCraftWeapon.h"
#include "../Mod/Mod.h"
@@ -168,12 +169,12 @@ CraftWeaponProjectile* CraftWeapon::fire() const
*/
int CraftWeapon::getClipsLoaded(Mod *mod)
{
- int retVal = (int)floor((double)_ammo / _rules->getRearmRate());
+ int retVal = (int)std::floor((double)_ammo / _rules->getRearmRate());
RuleItem *clip = mod->getItem(_rules->getClipItem());
if (clip && clip->getClipSize() > 0)
{
- retVal = (int)floor((double)_ammo / clip->getClipSize());
+ retVal = (int)std::floor((double)_ammo / clip->getClipSize());
}
return retVal;
---
Marginally reduced code duplication and straightened lines for collision detection. (Unimportant house-keeping changes.)
https:// Never mind. I've discovered a minor mistake in this change, and rather than taking the time to fix it and repost it I think it's better to just keep the current code.
----
Named defined value instead of 'magic number'. (This change doesn't do anything at all; but I highly recommend it. There are stacks of 'magic numbers' like this in the code, and they really should be phased out whenever possible to improve clarity and reduce the likelihood that future changes will break something.)
diff --git a/src/Battlescape/ProjectileFlyBState.cpp b/src/Battlescape/ProjectileFlyBState.cpp
index 7101e35..0d84d1e 100644
--- a/src/Battlescape/ProjectileFlyBState.cpp
+++ b/src/Battlescape/ProjectileFlyBState.cpp
@@ -564,7 +564,7 @@ void ProjectileFlyBState::think()
}
}
- if (_projectileImpact == 4)
+ if (_projectileImpact == V_UNIT)
{
BattleUnit *victim = _parent->getSave()->getTile(_parent->getMap()->getProjectile()->getPosition(offset) / Position(16,16,24))->getUnit();
if (victim && !victim->isOut() && victim->getFaction() == FACTION_HOSTILE)
---
[edit]
You probably don't need the "why?" comment in the bugfix... But I'd consider removing that `if` block. In fact, I'm going to delete it now, and then just keep playing my game. I don't expect any negative side effects; but I do expect it might be marginally easier to fire arrows from the ground into enemies in high-up windows.