Skip to content

Commit

Permalink
Merge pull request OpenXcom#1307 from MeridianOXC/large-unit-distance
Browse files Browse the repository at this point in the history
More precise distance calculation for 2x2 units
  • Loading branch information
Warboy1982 authored Feb 26, 2021
2 parents 8318c3a + dd7f938 commit f6b5334
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 21 deletions.
18 changes: 8 additions & 10 deletions src/Battlescape/Map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -993,7 +993,8 @@ void Map::drawTerrain(Surface *surface)
BattleAction *action = _save->getBattleGame()->getCurrentAction();
RuleItem *weapon = action->weapon->getRules();
int accuracy = action->actor->getFiringAccuracy(action->type, action->weapon);
int distance = _save->getTileEngine()->distance(Position (itX, itY,itZ), action->actor->getPosition());
int distanceSq = _save->getTileEngine()->distanceUnitToPositionSq(action->actor, Position (itX, itY,itZ), false);
int distance = (int)std::ceil(sqrt(float(distanceSq)));
int upperLimit = 200;
int lowerLimit = weapon->getMinRange();
switch (action->type)
Expand Down Expand Up @@ -1027,20 +1028,17 @@ void Map::drawTerrain(Surface *surface)
_txtAccuracy->setColor(Palette::blockOffset(Pathfinding::green - 1)-1);
}

bool outOfRange = distance > weapon->getMaxRange();
bool outOfRange = distanceSq > weapon->getMaxRangeSq();
// special handling for short ranges and diagonals
if (outOfRange && action->actor->directionTo(action->target) % 2 == 1)
if (outOfRange)
{
// special handling for maxRange 1: allow it to target diagonally adjacent tiles, even though they are technically 2 tiles away.
if (weapon->getMaxRange() == 1
&& distance == 2)
// special handling for maxRange 1: allow it to target diagonally adjacent tiles (one diagonal move)
if (weapon->getMaxRange() == 1 && distanceSq <= 3)
{
outOfRange = false;
}
// 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.
else if (weapon->getMaxRange() == 2
&& distance == 3
&& itZ != action->actor->getPosition().z)
// special handling for maxRange 2: allow it to target diagonally adjacent tiles (one diagonal move + one straight move)
else if (weapon->getMaxRange() == 2 && distanceSq <= 6)
{
outOfRange = false;
}
Expand Down
16 changes: 6 additions & 10 deletions src/Battlescape/ProjectileFlyBState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ void ProjectileFlyBState::init()
}

Tile *endTile = _parent->getSave()->getTile(_action.target);
int distance = _parent->getTileEngine()->distance(_action.actor->getPosition(), _action.target);
int distanceSq = _parent->getTileEngine()->distanceUnitToPositionSq(_action.actor, _action.target, false);
bool isPlayer = _parent->getSave()->getSide() == FACTION_PLAYER;
if (isPlayer) _parent->getMap()->resetObstacles();
switch (_action.type)
Expand All @@ -145,21 +145,17 @@ void ProjectileFlyBState::init()
_parent->popState();
return;
}
if (distance > weapon->getRules()->getMaxRange())
if (distanceSq > weapon->getRules()->getMaxRangeSq())
{
// special handling for short ranges and diagonals
if (_action.actor->directionTo(_action.target) % 2 == 1)
{
// special handling for maxRange 1: allow it to target diagonally adjacent tiles, even though they are technically 2 tiles away.
if (weapon->getRules()->getMaxRange() == 1
&& distance == 2)
// special handling for maxRange 1: allow it to target diagonally adjacent tiles (one diagonal move)
if (weapon->getRules()->getMaxRange() == 1 && distanceSq <= 3)
{
break;
}
// 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.
else if (weapon->getRules()->getMaxRange() == 2
&& distance == 3
&& _action.target.z != _action.actor->getPosition().z)
// special handling for maxRange 2: allow it to target diagonally adjacent tiles (one diagonal move + one straight move)
else if (weapon->getRules()->getMaxRange() == 2 && distanceSq <= 6)
{
break;
}
Expand Down
25 changes: 24 additions & 1 deletion src/Battlescape/TileEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1011,7 +1011,8 @@ int TileEngine::determineReactionType(BattleUnit *unit, BattleUnit *target)
// has a gun capable of snap shot with ammo
(weapon->getRules()->getBattleType() != BT_MELEE &&
weapon->getRules()->getTUSnap() &&
distance(unit->getPosition(), target->getPosition()) < weapon->getRules()->getMaxRange() &&
// Note: distance calculation isn't precise for 2x2 units here, but changing it would likely require also changing the targeting of 2x2 units
distanceSq(unit->getPosition(), target->getPosition(), false) < weapon->getRules()->getMaxRangeSq() &&
weapon->getAmmoItem() &&
unit->getActionTUs(BA_SNAPSHOT, weapon) > 0 &&
unit->getTimeUnits() > unit->getActionTUs(BA_SNAPSHOT, weapon)) &&
Expand Down Expand Up @@ -2639,6 +2640,28 @@ void TileEngine::togglePersonalLighting()
calculateUnitLighting();
}

/**
* Calculates the distance squared between a unit and a point position.
* @param unit The unit.
* @param pos The point position.
* @param considerZ Whether to consider the z coordinate.
* @return Distance squared.
*/
int TileEngine::distanceUnitToPositionSq(BattleUnit* unit, const Position& pos, bool considerZ) const
{
int x = unit->getPosition().x - pos.x;
int y = unit->getPosition().y - pos.y;
int z = considerZ ? (unit->getPosition().z - pos.z) : 0;
if (unit->getArmor()->getSize() > 1)
{
if (unit->getPosition().x < pos.x)
x++;
if (unit->getPosition().y < pos.y)
y++;
}
return x*x + y*y + z*z;
}

/**
* Calculates the distance between 2 points. Rounded up to first INT.
* @param pos1 Position of first square.
Expand Down
2 changes: 2 additions & 0 deletions src/Battlescape/TileEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ class TileEngine
bool visible(BattleUnit *currentUnit, Tile *tile);
/// Turn XCom soldier's personal lighting on or off.
void togglePersonalLighting();
/// Checks the distance squared between a unit and a position.
int distanceUnitToPositionSq(BattleUnit* unit, const Position& pos, bool considerZ) const;
/// Checks the distance between two positions.
int distance(Position pos1, Position pos2) const;
/// Checks the distance squared between two positions.
Expand Down
9 changes: 9 additions & 0 deletions src/Mod/RuleItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,15 @@ int RuleItem::getMaxRange() const
return _maxRange;
}

/**
* Gets the maximum range of this weapon squared
* @return The maximum range squared.
*/
int RuleItem::getMaxRangeSq() const
{
return _maxRange * _maxRange;
}

/**
* Gets the maximum effective range of this weapon when using Aimed Shot.
* @return The maximum range.
Expand Down
2 changes: 2 additions & 0 deletions src/Mod/RuleItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ class RuleItem
bool isPistol() const;
/// Get the max range of this weapon.
int getMaxRange() const;
/// Get the max range of this weapon squared.
int getMaxRangeSq() const;
/// Get the max range of aimed shots with this weapon.
int getAimRange() const;
/// Get the max range of snap shots with this weapon.
Expand Down

0 comments on commit f6b5334

Please sign in to comment.