Skip to content

Commit f6b5334

Browse files
authored
Merge pull request OpenXcom#1307 from MeridianOXC/large-unit-distance
More precise distance calculation for 2x2 units
2 parents 8318c3a + dd7f938 commit f6b5334

6 files changed

Lines changed: 51 additions & 21 deletions

File tree

‎src/Battlescape/Map.cpp‎

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -993,7 +993,8 @@ void Map::drawTerrain(Surface *surface)
993993
BattleAction *action = _save->getBattleGame()->getCurrentAction();
994994
RuleItem *weapon = action->weapon->getRules();
995995
int accuracy = action->actor->getFiringAccuracy(action->type, action->weapon);
996-
int distance = _save->getTileEngine()->distance(Position (itX, itY,itZ), action->actor->getPosition());
996+
int distanceSq = _save->getTileEngine()->distanceUnitToPositionSq(action->actor, Position (itX, itY,itZ), false);
997+
int distance = (int)std::ceil(sqrt(float(distanceSq)));
997998
int upperLimit = 200;
998999
int lowerLimit = weapon->getMinRange();
9991000
switch (action->type)
@@ -1027,20 +1028,17 @@ void Map::drawTerrain(Surface *surface)
10271028
_txtAccuracy->setColor(Palette::blockOffset(Pathfinding::green - 1)-1);
10281029
}
10291030

1030-
bool outOfRange = distance > weapon->getMaxRange();
1031+
bool outOfRange = distanceSq > weapon->getMaxRangeSq();
10311032
// special handling for short ranges and diagonals
1032-
if (outOfRange && action->actor->directionTo(action->target) % 2 == 1)
1033+
if (outOfRange)
10331034
{
1034-
// special handling for maxRange 1: allow it to target diagonally adjacent tiles, even though they are technically 2 tiles away.
1035-
if (weapon->getMaxRange() == 1
1036-
&& distance == 2)
1035+
// special handling for maxRange 1: allow it to target diagonally adjacent tiles (one diagonal move)
1036+
if (weapon->getMaxRange() == 1 && distanceSq <= 3)
10371037
{
10381038
outOfRange = false;
10391039
}
1040-
// special handling for maxRange 2: allow it to target diagonally adjacent tiles on a level above/below, even though they are technically 3 tiles away.
1041-
else if (weapon->getMaxRange() == 2
1042-
&& distance == 3
1043-
&& itZ != action->actor->getPosition().z)
1040+
// special handling for maxRange 2: allow it to target diagonally adjacent tiles (one diagonal move + one straight move)
1041+
else if (weapon->getMaxRange() == 2 && distanceSq <= 6)
10441042
{
10451043
outOfRange = false;
10461044
}

‎src/Battlescape/ProjectileFlyBState.cpp‎

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ void ProjectileFlyBState::init()
124124
}
125125

126126
Tile *endTile = _parent->getSave()->getTile(_action.target);
127-
int distance = _parent->getTileEngine()->distance(_action.actor->getPosition(), _action.target);
127+
int distanceSq = _parent->getTileEngine()->distanceUnitToPositionSq(_action.actor, _action.target, false);
128128
bool isPlayer = _parent->getSave()->getSide() == FACTION_PLAYER;
129129
if (isPlayer) _parent->getMap()->resetObstacles();
130130
switch (_action.type)
@@ -145,21 +145,17 @@ void ProjectileFlyBState::init()
145145
_parent->popState();
146146
return;
147147
}
148-
if (distance > weapon->getRules()->getMaxRange())
148+
if (distanceSq > weapon->getRules()->getMaxRangeSq())
149149
{
150150
// special handling for short ranges and diagonals
151-
if (_action.actor->directionTo(_action.target) % 2 == 1)
152151
{
153-
// special handling for maxRange 1: allow it to target diagonally adjacent tiles, even though they are technically 2 tiles away.
154-
if (weapon->getRules()->getMaxRange() == 1
155-
&& distance == 2)
152+
// special handling for maxRange 1: allow it to target diagonally adjacent tiles (one diagonal move)
153+
if (weapon->getRules()->getMaxRange() == 1 && distanceSq <= 3)
156154
{
157155
break;
158156
}
159-
// special handling for maxRange 2: allow it to target diagonally adjacent tiles on a level above/below, even though they are technically 3 tiles away.
160-
else if (weapon->getRules()->getMaxRange() == 2
161-
&& distance == 3
162-
&& _action.target.z != _action.actor->getPosition().z)
157+
// special handling for maxRange 2: allow it to target diagonally adjacent tiles (one diagonal move + one straight move)
158+
else if (weapon->getRules()->getMaxRange() == 2 && distanceSq <= 6)
163159
{
164160
break;
165161
}

‎src/Battlescape/TileEngine.cpp‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1011,7 +1011,8 @@ int TileEngine::determineReactionType(BattleUnit *unit, BattleUnit *target)
10111011
// has a gun capable of snap shot with ammo
10121012
(weapon->getRules()->getBattleType() != BT_MELEE &&
10131013
weapon->getRules()->getTUSnap() &&
1014-
distance(unit->getPosition(), target->getPosition()) < weapon->getRules()->getMaxRange() &&
1014+
// Note: distance calculation isn't precise for 2x2 units here, but changing it would likely require also changing the targeting of 2x2 units
1015+
distanceSq(unit->getPosition(), target->getPosition(), false) < weapon->getRules()->getMaxRangeSq() &&
10151016
weapon->getAmmoItem() &&
10161017
unit->getActionTUs(BA_SNAPSHOT, weapon) > 0 &&
10171018
unit->getTimeUnits() > unit->getActionTUs(BA_SNAPSHOT, weapon)) &&
@@ -2639,6 +2640,28 @@ void TileEngine::togglePersonalLighting()
26392640
calculateUnitLighting();
26402641
}
26412642

2643+
/**
2644+
* Calculates the distance squared between a unit and a point position.
2645+
* @param unit The unit.
2646+
* @param pos The point position.
2647+
* @param considerZ Whether to consider the z coordinate.
2648+
* @return Distance squared.
2649+
*/
2650+
int TileEngine::distanceUnitToPositionSq(BattleUnit* unit, const Position& pos, bool considerZ) const
2651+
{
2652+
int x = unit->getPosition().x - pos.x;
2653+
int y = unit->getPosition().y - pos.y;
2654+
int z = considerZ ? (unit->getPosition().z - pos.z) : 0;
2655+
if (unit->getArmor()->getSize() > 1)
2656+
{
2657+
if (unit->getPosition().x < pos.x)
2658+
x++;
2659+
if (unit->getPosition().y < pos.y)
2660+
y++;
2661+
}
2662+
return x*x + y*y + z*z;
2663+
}
2664+
26422665
/**
26432666
* Calculates the distance between 2 points. Rounded up to first INT.
26442667
* @param pos1 Position of first square.

‎src/Battlescape/TileEngine.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ class TileEngine
9090
bool visible(BattleUnit *currentUnit, Tile *tile);
9191
/// Turn XCom soldier's personal lighting on or off.
9292
void togglePersonalLighting();
93+
/// Checks the distance squared between a unit and a position.
94+
int distanceUnitToPositionSq(BattleUnit* unit, const Position& pos, bool considerZ) const;
9395
/// Checks the distance between two positions.
9496
int distance(Position pos1, Position pos2) const;
9597
/// Checks the distance squared between two positions.

‎src/Mod/RuleItem.cpp‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,15 @@ int RuleItem::getMaxRange() const
674674
return _maxRange;
675675
}
676676

677+
/**
678+
* Gets the maximum range of this weapon squared
679+
* @return The maximum range squared.
680+
*/
681+
int RuleItem::getMaxRangeSq() const
682+
{
683+
return _maxRange * _maxRange;
684+
}
685+
677686
/**
678687
* Gets the maximum effective range of this weapon when using Aimed Shot.
679688
* @return The maximum range.

‎src/Mod/RuleItem.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ class RuleItem
192192
bool isPistol() const;
193193
/// Get the max range of this weapon.
194194
int getMaxRange() const;
195+
/// Get the max range of this weapon squared.
196+
int getMaxRangeSq() const;
195197
/// Get the max range of aimed shots with this weapon.
196198
int getAimRange() const;
197199
/// Get the max range of snap shots with this weapon.

0 commit comments

Comments
 (0)