Ensure units do get in attacking range since that range can change over time because of elevation differences.

This is a partial revert of 4fda917f46, which skipped the
"MoveToTargetAttackingRange" in APPROACHING. I (incorrectly) assumed
that the original order was still perfectly fine, but in fact the
attacker's max range may have changed as that depends on the relative
elevation between attack and target - and so the original order might
never get us in range!
This was introduced originally in 8c74df2acd.

Add a comment to clarify this.

Further, this makes sure UnitMotion still is aware that it has a target
even if it is in range from the beginning, as that could lead
to stuckiness (and did when chasing sometimes). This was done in D1984
anyways.

Fixes #5478.

Differential Revision: https://code.wildfiregames.com/D2035
This was SVN commit r22429.
This commit is contained in:
wraitii 2019-07-03 18:05:11 +00:00
parent 8a38cfb7cf
commit f990cd2381
2 changed files with 36 additions and 57 deletions

View File

@ -1797,7 +1797,13 @@ UnitAI.prototype.UnitFsmSpec = {
}
if (!this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType))
{
// Try moving again,
// attack range uses a height-related formula and our actual max range might have changed.
if (!this.MoveToTargetAttackRange(this.order.data.target, this.order.data.attackType))
this.FinishOrder();
return;
}
// If the unit needs to unpack, do so
if (this.CanUnpack())
@ -4200,12 +4206,12 @@ UnitAI.prototype.MoveToTargetAttackRange = function(target, type)
// for formation members, the formation will take care of the range check
if (this.IsFormationMember())
{
var cmpFormationUnitAI = Engine.QueryInterface(this.formationController, IID_UnitAI);
let cmpFormationUnitAI = Engine.QueryInterface(this.formationController, IID_UnitAI);
if (cmpFormationUnitAI && cmpFormationUnitAI.IsAttackingAsFormation())
return false;
}
var cmpFormation = Engine.QueryInterface(target, IID_Formation);
let cmpFormation = Engine.QueryInterface(target, IID_Formation);
if (cmpFormation)
target = cmpFormation.GetClosestMember(this.entity);
@ -4215,39 +4221,33 @@ UnitAI.prototype.MoveToTargetAttackRange = function(target, type)
if (!this.CheckTargetVisible(target))
return false;
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
var range = cmpAttack.GetRange(type);
let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
let range = cmpAttack.GetRange(type);
var thisCmpPosition = Engine.QueryInterface(this.entity, IID_Position);
let thisCmpPosition = Engine.QueryInterface(this.entity, IID_Position);
if (!thisCmpPosition.IsInWorld())
return false;
var s = thisCmpPosition.GetPosition();
let s = thisCmpPosition.GetPosition();
var targetCmpPosition = Engine.QueryInterface(target, IID_Position);
let targetCmpPosition = Engine.QueryInterface(target, IID_Position);
if (!targetCmpPosition.IsInWorld())
return false;
var t = targetCmpPosition.GetPosition();
let t = targetCmpPosition.GetPosition();
// h is positive when I'm higher than the target
var h = s.y-t.y+range.elevationBonus;
let h = s.y - t.y + range.elevationBonus;
let parabolicMaxRange = Math.sqrt(Math.square(range.max) + 2 * range.max * h);
// No negative roots please
if (h>-range.max/2)
var parabolicMaxRange = Math.sqrt(Math.square(range.max) + 2 * range.max * h);
else
if (h <= -range.max / 2)
// return false? Or hope you come close enough?
var parabolicMaxRange = 0;
//return false;
parabolicMaxRange = 0;
// the parabole changes while walking, take something in the middle
var guessedMaxRange = (range.max + parabolicMaxRange)/2;
// The parabole changes while walking so be cautious:
let guessedMaxRange = parabolicMaxRange > range.max ? (range.max + parabolicMaxRange) / 2 : parabolicMaxRange;
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
if (cmpUnitMotion.MoveToTargetRange(target, range.min, guessedMaxRange))
return true;
// if that failed, try closer
return cmpUnitMotion.MoveToTargetRange(target, range.min, Math.min(range.max, parabolicMaxRange));
let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
return cmpUnitMotion.MoveToTargetRange(target, range.min, guessedMaxRange);
};
UnitAI.prototype.MoveToTargetRangeExplicit = function(target, min, max)
@ -4323,42 +4323,42 @@ UnitAI.prototype.CheckTargetAttackRange = function(target, type)
// for formation members, the formation will take care of the range check
if (this.IsFormationMember())
{
var cmpFormationUnitAI = Engine.QueryInterface(this.formationController, IID_UnitAI);
if (cmpFormationUnitAI && cmpFormationUnitAI.IsAttackingAsFormation()
&& cmpFormationUnitAI.order.data.target == target)
let cmpFormationUnitAI = Engine.QueryInterface(this.formationController, IID_UnitAI);
if (cmpFormationUnitAI && cmpFormationUnitAI.IsAttackingAsFormation() &&
cmpFormationUnitAI.order.data.target == target)
return true;
}
var cmpFormation = Engine.QueryInterface(target, IID_Formation);
let cmpFormation = Engine.QueryInterface(target, IID_Formation);
if (cmpFormation)
target = cmpFormation.GetClosestMember(this.entity);
if (type != "Ranged")
return this.CheckTargetRange(target, IID_Attack, type);
var targetCmpPosition = Engine.QueryInterface(target, IID_Position);
let targetCmpPosition = Engine.QueryInterface(target, IID_Position);
if (!targetCmpPosition || !targetCmpPosition.IsInWorld())
return false;
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
var range = cmpAttack.GetRange(type);
let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
let range = cmpAttack.GetRange(type);
var thisCmpPosition = Engine.QueryInterface(this.entity, IID_Position);
let thisCmpPosition = Engine.QueryInterface(this.entity, IID_Position);
if (!thisCmpPosition.IsInWorld())
return false;
var s = thisCmpPosition.GetPosition();
let s = thisCmpPosition.GetPosition();
var t = targetCmpPosition.GetPosition();
let t = targetCmpPosition.GetPosition();
var h = s.y-t.y+range.elevationBonus;
var maxRangeSq = 2*range.max*(h + range.max/2);
let h = s.y - t.y + range.elevationBonus;
let maxRange = Math.sqrt(Math.square(range.max) + 2 * range.max * h);
if (maxRangeSq < 0)
if (maxRange < 0)
return false;
let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
return cmpObstructionManager.IsInTargetRange(this.entity, target, range.min, Math.sqrt(maxRangeSq), false);
return cmpObstructionManager.IsInTargetRange(this.entity, target, range.min, maxRange, false);
};
UnitAI.prototype.CheckTargetRangeExplicit = function(target, min, max)

View File

@ -1401,13 +1401,6 @@ bool CCmpUnitMotion::MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos
if (goal.hw <= entity_pos_t::Zero())
goal.type = PathGoal::POINT;
}
else
{
// We're already in range - no need to move anywhere
if (m_FacePointAfterMove)
FaceTowardsPointFromPos(pos, x, z);
return true;
}
}
m_State = STATE_INDIVIDUAL_PATH;
@ -1527,12 +1520,6 @@ bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange
goal.hh = obstruction.hh + goalDistance;
}
}
else if (maxRange < entity_pos_t::Zero() || distance < maxRange || previousDistance < maxRange)
{
// We're already in range - no need to move anywhere
FaceTowardsPointFromPos(pos, goal.x, goal.z);
return true;
}
else
{
// We might need to move closer:
@ -1550,14 +1537,6 @@ bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange
entity_pos_t circleDistance = (pos - CFixedVector2D(obstruction.x, obstruction.z)).Length() - circleRadius;
entity_pos_t previousCircleDistance = (pos - CFixedVector2D(previousObstruction.x, previousObstruction.z)).Length() - circleRadius;
if (circleDistance < maxRange || previousCircleDistance < maxRange)
{
// We're already in range - no need to move anywhere
if (m_FacePointAfterMove)
FaceTowardsPointFromPos(pos, goal.x, goal.z);
return true;
}
entity_pos_t goalDistance = maxRange;
goal.type = PathGoal::CIRCLE;