1
0
forked from 0ad/0ad

Fix UnitAI range queries - allow queries to ignore sizes - partial revert of d0fc8ff67d

Units sometimes ignored targets that entered their LoS. The cause is
d0fc8ff67d: range queries returned units farther away, and those units
might actually be out of range if distance is computed center-to-center,
which both UnitAI and LOS do. This meant that code relying on range
query updates was possibly broken, and indeed units missed things (see
ticket).

This introduces a boolean to switch between pre-d0fc8ff67d behaviour
(entity-as-point, center-to-center range queries) and post-d0fc8ff67d
(entities-as-circumscribing-circle, edge-to-edge range queries).
The former is used for UnitAI (where the new behaviour bugged), auras
(where varying structure sizes made it awkward) and build
restrictions(which simply did not really need it).

Reverts 7f1ee23d88, 050c5401b1 (with the exception of the iber monument
footprint), and the template changes in d0fc8ff67d itself.

It also reduces alertRaiser ranges slightly, this was missed in the
original diff.

#3381 is not reopened as BuildingAI still uses the new range queries.

Reported by: Freagarach
Comments By: Angen
Fixes #5968

Differential Revision: https://code.wildfiregames.com/D3456
This was SVN commit r24776.
This commit is contained in:
wraitii 2021-01-23 18:57:46 +00:00
parent 4cc824d620
commit 93a352ad16
34 changed files with 111 additions and 89 deletions

View File

@ -90,9 +90,10 @@ PETRA.createObstructionMap = function(gameState, accessIndex, template)
{
let obstructionRadius = template.obstructionRadius();
if (obstructionRadius)
minDist += obstructionRadius.max;
minDist -= obstructionRadius.min;
let fromClass = distance.FromClass;
let cellSize = passabilityMap.cellSize;
let cellDist = 1 + minDist / cellSize;
let structures = gameState.getOwnStructures().filter(API3.Filters.byClass(fromClass));
for (let ent of structures.values())
{
@ -101,9 +102,7 @@ PETRA.createObstructionMap = function(gameState, accessIndex, template)
let pos = ent.position();
let x = Math.round(pos[0] / cellSize);
let z = Math.round(pos[1] / cellSize);
let entRadius = ent.obstructionRadius();
let dist = 1 + (minDist + (entRadius ? entRadius.max : 0)) / cellSize;
map.addInfluence(x, z, dist, -255, "constant");
map.addInfluence(x, z, cellDist, -255, "constant");
}
}
}

View File

@ -44,12 +44,12 @@ AlertRaiser.prototype.RaiseAlert = function()
// Store the number of available garrison spots so that units don't try to garrison in buildings that will be full
let reserved = new Map();
let units = cmpRangeManager.ExecuteQuery(this.entity, 0, +this.template.RaiseAlertRange, [owner], IID_UnitAI).filter(ent => this.UnitFilter(ent));
let units = cmpRangeManager.ExecuteQuery(this.entity, 0, +this.template.RaiseAlertRange, [owner], IID_UnitAI, true).filter(ent => this.UnitFilter(ent));
for (let unit of units)
{
let cmpUnitAI = Engine.QueryInterface(unit, IID_UnitAI);
let holder = cmpRangeManager.ExecuteQuery(unit, 0, +this.template.SearchRange, mutualAllies, IID_GarrisonHolder).find(ent => {
let holder = cmpRangeManager.ExecuteQuery(unit, 0, +this.template.SearchRange, mutualAllies, IID_GarrisonHolder, true).find(ent => {
// Ignore moving garrison holders
if (Engine.QueryInterface(ent, IID_UnitAI))
return false;
@ -98,7 +98,7 @@ AlertRaiser.prototype.EndOfAlert = function()
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
// Units that are not garrisoned should go back to work
let units = cmpRangeManager.ExecuteQuery(this.entity, 0, +this.template.EndOfAlertRange, [owner], IID_UnitAI).filter(ent => this.UnitFilter(ent));
let units = cmpRangeManager.ExecuteQuery(this.entity, 0, +this.template.EndOfAlertRange, [owner], IID_UnitAI, true).filter(ent => this.UnitFilter(ent));
for (let unit of units)
{
let cmpUnitAI = Engine.QueryInterface(unit, IID_UnitAI);
@ -110,7 +110,7 @@ AlertRaiser.prototype.EndOfAlert = function()
}
// Units that are garrisoned should ungarrison and go back to work
let holders = cmpRangeManager.ExecuteQuery(this.entity, 0, +this.template.EndOfAlertRange, mutualAllies, IID_GarrisonHolder);
let holders = cmpRangeManager.ExecuteQuery(this.entity, 0, +this.template.EndOfAlertRange, mutualAllies, IID_GarrisonHolder, true);
if (Engine.QueryInterface(this.entity, IID_GarrisonHolder))
holders.push(this.entity);

View File

@ -267,13 +267,17 @@ Auras.prototype.Clean = function()
if (this[name].isApplied && (this.IsRangeAura(name) || this.IsGlobalAura(name) && !!this.GetOverlayIcon(name)))
{
// Do not account for entity sizes: structures can have various sizes
// and we currently prefer auras to not depend on the source size
// (this is generally irrelevant for units).
this[name].rangeQuery = cmpRangeManager.CreateActiveQuery(
this.entity,
0,
this.GetRange(name),
affectedPlayers,
IID_Identity,
cmpRangeManager.GetEntityFlagMask("normal")
cmpRangeManager.GetEntityFlagMask("normal"),
false
);
cmpRangeManager.EnableActiveQuery(this[name].rangeQuery);
}

View File

@ -256,7 +256,7 @@ BuildRestrictions.prototype.CheckPlacement = function()
if (this.template.Distance.MinDistance !== undefined)
{
let minDistance = ApplyValueModificationsToTemplate("BuildRestrictions/Distance/MinDistance", +this.template.Distance.MinDistance, cmpPlayer.GetPlayerID(), template);
if (cmpRangeManager.ExecuteQuery(this.entity, 0, minDistance, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions).some(filter))
if (cmpRangeManager.ExecuteQuery(this.entity, 0, minDistance, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions, false).some(filter))
{
let result = markForPluralTranslation(
"%(name)s too close to a %(category)s, must be at least %(distance)s meter away",
@ -277,7 +277,7 @@ BuildRestrictions.prototype.CheckPlacement = function()
if (this.template.Distance.MaxDistance !== undefined)
{
let maxDistance = ApplyValueModificationsToTemplate("BuildRestrictions/Distance/MaxDistance", +this.template.Distance.MaxDistance, cmpPlayer.GetPlayerID(), template);
if (!cmpRangeManager.ExecuteQuery(this.entity, 0, maxDistance, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions).some(filter))
if (!cmpRangeManager.ExecuteQuery(this.entity, 0, maxDistance, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions, false).some(filter))
{
let result = markForPluralTranslation(
"%(name)s too far from a %(category)s, must be within %(distance)s meter",

View File

@ -139,6 +139,7 @@ BuildingAI.prototype.SetupRangeQuery = function()
return;
var range = cmpAttack.GetRange(attackType);
// This takes entity sizes into accounts, so no need to compensate for structure size.
this.enemyUnitsQuery = cmpRangeManager.CreateActiveParabolicQuery(
this.entity, range.min, range.max, range.elevationBonus,
enemies, IID_Resistance, cmpRangeManager.GetEntityFlagMask("normal"));
@ -168,6 +169,7 @@ BuildingAI.prototype.SetupGaiaRangeQuery = function()
var range = cmpAttack.GetRange(attackType);
// This query is only interested in Gaia entities that can attack.
// This takes entity sizes into accounts, so no need to compensate for structure size.
this.gaiaUnitsQuery = cmpRangeManager.CreateActiveParabolicQuery(
this.entity, range.min, range.max, range.elevationBonus,
[0], IID_Attack, cmpRangeManager.GetEntityFlagMask("normal"));

View File

@ -78,7 +78,7 @@ Gate.prototype.SetupRangeQuery = function(owner)
if (range > 0)
{
// Only find entities with IID_UnitAI interface
this.unitsQuery = cmpRangeManager.CreateActiveQuery(this.entity, 0, range, players, IID_UnitAI, cmpRangeManager.GetEntityFlagMask("normal"));
this.unitsQuery = cmpRangeManager.CreateActiveQuery(this.entity, 0, range, players, IID_UnitAI, cmpRangeManager.GetEntityFlagMask("normal"), true);
cmpRangeManager.EnableActiveQuery(this.unitsQuery);
}
};

View File

@ -48,15 +48,12 @@ RangeOverlayManager.prototype.RegenerateRangeOverlays = function(forceUpdate)
if (!this.enabled && !forceUpdate)
return;
let cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
let rangeBonus = cmpObstruction ? cmpObstruction.GetSize() : 0;
// Only render individual range types that have been enabled
for (let rangeOverlayType of this.rangeVisualizations.keys())
if (this.enabledRangeTypes[rangeOverlayType])
for (let rangeOverlay of this.rangeVisualizations.get(rangeOverlayType))
cmpRangeOverlayRenderer.AddRangeOverlay(
rangeOverlay.radius + rangeBonus,
rangeOverlay.radius,
rangeOverlay.texture,
rangeOverlay.textureMask,
rangeOverlay.thickness);

View File

@ -45,7 +45,7 @@ TriggerPoint.prototype.RegisterRangeTrigger = function(action, data)
var cid = data.requiredComponent || -1;
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
var tag = cmpRangeManager.CreateActiveQuery(this.entity, minRange, maxRange, players, cid, cmpRangeManager.GetEntityFlagMask("normal"));
var tag = cmpRangeManager.CreateActiveQuery(this.entity, minRange, maxRange, players, cid, cmpRangeManager.GetEntityFlagMask("normal"), true);
this.currentCollections[tag] = [];
this.actions[tag] = action;

View File

@ -3791,7 +3791,10 @@ UnitAI.prototype.SetupLOSRangeQuery = function(enable = true)
return;
let range = this.GetQueryRange(IID_Vision);
this.losRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_Identity, cmpRangeManager.GetEntityFlagMask("normal"));
// Do not compensate for entity sizes: LOS doesn't, and UnitAI relies on that.
this.losRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity,
range.min, range.max, players, IID_Identity,
cmpRangeManager.GetEntityFlagMask("normal"), false);
if (enable)
cmpRangeManager.EnableActiveQuery(this.losRangeQuery);
@ -3820,7 +3823,10 @@ UnitAI.prototype.SetupHealRangeQuery = function(enable = true)
let players = cmpPlayer.GetAllies();
let range = this.GetQueryRange(IID_Heal);
this.losHealRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_Health, cmpRangeManager.GetEntityFlagMask("injured"));
// Do not compensate for entity sizes: LOS doesn't, and UnitAI relies on that.
this.losHealRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity,
range.min, range.max, players, IID_Health,
cmpRangeManager.GetEntityFlagMask("injured"), false);
if (enable)
cmpRangeManager.EnableActiveQuery(this.losHealRangeQuery);
@ -3852,7 +3858,10 @@ UnitAI.prototype.SetupAttackRangeQuery = function(enable = true)
return;
let range = this.GetQueryRange(IID_Attack);
this.losAttackRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_Resistance, cmpRangeManager.GetEntityFlagMask("normal"));
// Do not compensate for entity sizes: LOS doesn't, and UnitAI relies on that.
this.losAttackRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity,
range.min, range.max, players, IID_Resistance,
cmpRangeManager.GetEntityFlagMask("normal"), false);
if (enable)
cmpRangeManager.EnableActiveQuery(this.losAttackRangeQuery);
@ -4437,7 +4446,8 @@ UnitAI.prototype.FindNearbyResource = function(position, filter)
let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
let nearby = cmpRangeManager.ExecuteQueryAroundPos(position, 0, range, players, IID_ResourceSupply);
// Don't account for entity size, we need to match LOS visibility.
let nearby = cmpRangeManager.ExecuteQueryAroundPos(position, 0, range, players, IID_ResourceSupply, false);
return nearby.find(ent => {
if (!this.CanGather(ent) || !this.CheckTargetVisible(ent))
return false;
@ -4476,7 +4486,7 @@ UnitAI.prototype.FindNearestDropsite = function(genericType)
let owner = cmpOwnership.GetOwner();
let cmpPlayer = QueryOwnerInterface(this.entity);
let players = cmpPlayer && cmpPlayer.HasSharedDropsites() ? cmpPlayer.GetMutualAllies() : [owner];
let nearestDropsites = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).ExecuteQuery(this.entity, 0, -1, players, IID_ResourceDropsite);
let nearestDropsites = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).ExecuteQuery(this.entity, 0, -1, players, IID_ResourceDropsite, false);
let isShip = Engine.QueryInterface(this.entity, IID_Identity).HasClass("Ship");
let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
@ -4526,7 +4536,8 @@ UnitAI.prototype.FindNearbyFoundation = function(position)
let range = 64; // TODO: what's a sensible number?
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
let nearby = cmpRangeManager.ExecuteQueryAroundPos(position, 0, range, players, IID_Foundation);
// Don't account for entity size, we need to match LOS visibility.
let nearby = cmpRangeManager.ExecuteQueryAroundPos(position, 0, range, players, IID_Foundation, false);
// Skip foundations that are already complete. (This matters since
// we process the ConstructionFinished message before the foundation
@ -6556,7 +6567,7 @@ UnitAI.prototype.CallPlayerOwnedEntitiesFunctionInRange = function(funcname, arg
if (owner == INVALID_PLAYER)
return;
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
let nearby = cmpRangeManager.ExecuteQuery(this.entity, 0, range, [owner], IID_UnitAI);
let nearby = cmpRangeManager.ExecuteQuery(this.entity, 0, range, [owner], IID_UnitAI, true);
for (let i = 0; i < nearby.length; ++i)
{
let cmpUnitAI = Engine.QueryInterface(nearby[i], IID_UnitAI);

View File

@ -122,7 +122,7 @@ function TestFormationExiting(mode)
});
AddMock(SYSTEM_ENTITY, IID_RangeManager, {
CreateActiveQuery: function(ent, minRange, maxRange, players, iid, flags) {
CreateActiveQuery: function(ent, minRange, maxRange, players, iid, flags, accountForSize) {
return 1;
},
EnableActiveQuery: function(id) { },
@ -293,7 +293,7 @@ function TestMoveIntoFormationWhileAttacking()
AddMock(SYSTEM_ENTITY, IID_RangeManager, {
CreateActiveQuery: function(ent, minRange, maxRange, players, iid, flags) {
CreateActiveQuery: function(ent, minRange, maxRange, players, iid, flags, accountForSize) {
return 1;
},
EnableActiveQuery: function(id) { },

View File

@ -1,6 +1,6 @@
{
"type": "range",
"radius": 45,
"radius": 50,
"affects": ["Soldier"],
"modifications": [
{ "value": "Attack/Melee/Damage/Hack", "multiply": 1.2 },

View File

@ -1,6 +1,6 @@
{
"type": "range",
"radius": 50,
"radius": 60,
"affects": ["Worker"],
"modifications": [
{ "value": "ResourceGatherer/BaseSpeed", "multiply": 1.15 }

View File

@ -1,6 +1,6 @@
{
"type": "range",
"radius": 55,
"radius": 70,
"affects": ["Soldier"],
"modifications": [
{ "value": "Attack/Melee/Damage/Hack", "multiply": 1.1 },

View File

@ -1,6 +1,6 @@
{
"type": "range",
"radius": 60,
"radius": 75,
"affects": ["Structure"],
"modifications": [
{ "value": "Capturable/GarrisonRegenRate", "multiply": 1.5 }

View File

@ -1,6 +1,6 @@
{
"type": "range",
"radius": 70,
"radius": 75,
"affects": ["Trader"],
"modifications": [
{ "value": "UnitMotion/WalkSpeed", "multiply": 1.2 }

View File

@ -1,6 +1,6 @@
{
"type": "range",
"radius": 50,
"radius": 60,
"affects": ["Worker"],
"modifications": [
{ "value": "ResourceGatherer/Rates/food.grain", "multiply": 1.25 }

View File

@ -1,6 +1,6 @@
{
"type": "range",
"radius": 20,
"radius": 40,
"affects": ["Human"],
"modifications": [
{ "value": "Health/RegenRate", "add": 1 }

View File

@ -34,7 +34,7 @@ PositionHelper.prototype.EntitiesNearPoint = function(origin, radius, players, i
return [];
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
return cmpRangeManager.ExecuteQueryAroundPos(origin, 0, radius, players, iid);
return cmpRangeManager.ExecuteQueryAroundPos(origin, 0, radius, players, iid, true);
};
/**

View File

@ -7,7 +7,7 @@
<Category>Monument</Category>
<Distance>
<FromClass>Monument</FromClass>
<MinDistance>145</MinDistance>
<MinDistance>150</MinDistance>
</Distance>
</BuildRestrictions>
<Capturable disable=""/>

View File

@ -7,7 +7,7 @@
<Category>Pillar</Category>
<Distance>
<FromClass>Pillar</FromClass>
<MinDistance>70</MinDistance>
<MinDistance>75</MinDistance>
</Distance>
</BuildRestrictions>
<Capturable disable=""/>

View File

@ -4,7 +4,7 @@
<Territory>own neutral</Territory>
<Distance>
<FromClass>MercenaryCamp</FromClass>
<MinDistance>70</MinDistance>
<MinDistance>100</MinDistance>
</Distance>
</BuildRestrictions>
<Cost>

View File

@ -4,7 +4,7 @@
<Territory>own neutral</Territory>
<Distance>
<FromClass>MercenaryCamp</FromClass>
<MinDistance>70</MinDistance>
<MinDistance>100</MinDistance>
</Distance>
</BuildRestrictions>
<Cost>

View File

@ -38,7 +38,7 @@
<Category>ArmyCamp</Category>
<Distance>
<FromClass>ArmyCamp</FromClass>
<MinDistance>45</MinDistance>
<MinDistance>80</MinDistance>
</Distance>
</BuildRestrictions>
<Capturable>

View File

@ -2,8 +2,8 @@
<Entity parent="template_structure_civic">
<AlertRaiser>
<List datatype="tokens">FemaleCitizen</List>
<RaiseAlertRange>140</RaiseAlertRange>
<EndOfAlertRange>190</EndOfAlertRange>
<RaiseAlertRange>120</RaiseAlertRange>
<EndOfAlertRange>180</EndOfAlertRange>
<SearchRange>100</SearchRange>
</AlertRaiser>
<Attack>
@ -44,7 +44,7 @@
<Category>CivilCentre</Category>
<Distance>
<FromClass>CivilCentre</FromClass>
<MinDistance>160</MinDistance>
<MinDistance>200</MinDistance>
</Distance>
</BuildRestrictions>
<Capturable>

View File

@ -8,7 +8,7 @@
<Category>Colony</Category>
<Distance>
<FromClass>CivilCentre</FromClass>
<MinDistance>80</MinDistance>
<MinDistance>120</MinDistance>
</Distance>
</BuildRestrictions>
<Cost>

View File

@ -7,7 +7,7 @@
<Territory>own neutral</Territory>
<Distance>
<FromClass>Outpost</FromClass>
<MinDistance>45</MinDistance>
<MinDistance>50</MinDistance>
</Distance>
</BuildRestrictions>
<Cost>

View File

@ -37,7 +37,7 @@
<Category>Tower</Category>
<Distance>
<FromClass>Tower</FromClass>
<MinDistance>55</MinDistance>
<MinDistance>60</MinDistance>
</Distance>
</BuildRestrictions>
<GarrisonHolder>

View File

@ -2,7 +2,7 @@
<Entity parent="template_structure_economic">
<AlertRaiser>
<List datatype="tokens">FemaleCitizen</List>
<RaiseAlertRange>60</RaiseAlertRange>
<RaiseAlertRange>50</RaiseAlertRange>
<EndOfAlertRange>100</EndOfAlertRange>
<SearchRange>100</SearchRange>
</AlertRaiser>

View File

@ -2,7 +2,7 @@
<Entity parent="template_structure_economic">
<AlertRaiser>
<List datatype="tokens">FemaleCitizen</List>
<RaiseAlertRange>60</RaiseAlertRange>
<RaiseAlertRange>50</RaiseAlertRange>
<EndOfAlertRange>100</EndOfAlertRange>
<SearchRange>100</SearchRange>
</AlertRaiser>

View File

@ -37,7 +37,7 @@
<Category>Fortress</Category>
<Distance>
<FromClass>Fortress</FromClass>
<MinDistance>55</MinDistance>
<MinDistance>80</MinDistance>
</Distance>
</BuildRestrictions>
<Capturable>

View File

@ -153,6 +153,7 @@ struct Query
u8 flagsMask;
bool enabled;
bool parabolic;
bool accountForSize; // If true, the query accounts for unit sizes, otherwise it treats all entities as points.
};
/**
@ -244,8 +245,6 @@ struct SerializeHelper<Query>
template<typename S>
void Common(S& serialize, const char* UNUSED(name), Serialize::qualify<S, Query> value)
{
serialize.Bool("enabled", value.enabled);
serialize.Bool("parabolic",value.parabolic);
serialize.NumberFixed_Unbounded("min range", value.minRange);
serialize.NumberFixed_Unbounded("max range", value.maxRange);
serialize.NumberFixed_Unbounded("elevation bonus", value.elevationBonus);
@ -253,6 +252,9 @@ struct SerializeHelper<Query>
serialize.NumberI32_Unbounded("interface", value.interface);
Serializer(serialize, "last match", value.lastMatch);
serialize.NumberU8_Unbounded("flagsMask", value.flagsMask);
serialize.Bool("enabled", value.enabled);
serialize.Bool("parabolic",value.parabolic);
serialize.Bool("account for size",value.accountForSize);
}
void operator()(ISerializer& serialize, const char* name, Query& value, const CSimContext& UNUSED(context))
@ -907,10 +909,10 @@ public:
virtual tag_t CreateActiveQuery(entity_id_t source,
entity_pos_t minRange, entity_pos_t maxRange,
const std::vector<int>& owners, int requiredInterface, u8 flags)
const std::vector<int>& owners, int requiredInterface, u8 flags, bool accountForSize)
{
tag_t id = m_QueryNext++;
m_Queries[id] = ConstructQuery(source, minRange, maxRange, owners, requiredInterface, flags);
m_Queries[id] = ConstructQuery(source, minRange, maxRange, owners, requiredInterface, flags, accountForSize);
return id;
}
@ -920,7 +922,7 @@ public:
const std::vector<int>& owners, int requiredInterface, u8 flags)
{
tag_t id = m_QueryNext++;
m_Queries[id] = ConstructParabolicQuery(source, minRange, maxRange, elevationBonus, owners, requiredInterface, flags);
m_Queries[id] = ConstructParabolicQuery(source, minRange, maxRange, elevationBonus, owners, requiredInterface, flags, true);
return id;
}
@ -977,9 +979,9 @@ public:
virtual std::vector<entity_id_t> ExecuteQueryAroundPos(const CFixedVector2D& pos,
entity_pos_t minRange, entity_pos_t maxRange,
const std::vector<int>& owners, int requiredInterface)
const std::vector<int>& owners, int requiredInterface, bool accountForSize)
{
Query q = ConstructQuery(INVALID_ENTITY, minRange, maxRange, owners, requiredInterface, GetEntityFlagMask("normal"));
Query q = ConstructQuery(INVALID_ENTITY, minRange, maxRange, owners, requiredInterface, GetEntityFlagMask("normal"), accountForSize);
std::vector<entity_id_t> r;
PerformQuery(q, r, pos);
@ -991,11 +993,11 @@ public:
virtual std::vector<entity_id_t> ExecuteQuery(entity_id_t source,
entity_pos_t minRange, entity_pos_t maxRange,
const std::vector<int>& owners, int requiredInterface)
const std::vector<int>& owners, int requiredInterface, bool accountForSize)
{
PROFILE("ExecuteQuery");
Query q = ConstructQuery(source, minRange, maxRange, owners, requiredInterface, GetEntityFlagMask("normal"));
Query q = ConstructQuery(source, minRange, maxRange, owners, requiredInterface, GetEntityFlagMask("normal"), accountForSize);
std::vector<entity_id_t> r;
@ -1221,8 +1223,8 @@ public:
// they have an intersection after which the former grows slower, and then use that to prove the above.
// Note that this is only true because we do not account for vertical size here,
// if we did, we would also need to artificially 'raise' the source over the target.
if (!InParabolicRange(CFixedVector3D(it->second.x, secondPosition.Y, it->second.z) - pos3d,
q.maxRange + fixed::FromInt(it->second.size)))
entity_pos_t range = q.maxRange + (q.accountForSize ? fixed::FromInt(it->second.size) : fixed::Zero());
if (!InParabolicRange(CFixedVector3D(it->second.x, secondPosition.Y, it->second.z) - pos3d, range))
continue;
if (!q.minRange.IsZero())
@ -1249,7 +1251,8 @@ public:
continue;
// Restrict based on approximate circle-circle distance.
if ((CFixedVector2D(it->second.x, it->second.z) - pos).CompareLength(q.maxRange + fixed::FromInt(it->second.size)) > 0)
entity_pos_t range = q.maxRange + (q.accountForSize ? fixed::FromInt(it->second.size) : fixed::Zero());
if ((CFixedVector2D(it->second.x, it->second.z) - pos).CompareLength(range) > 0)
continue;
if (!q.minRange.IsZero())
@ -1364,7 +1367,7 @@ public:
Query ConstructQuery(entity_id_t source,
entity_pos_t minRange, entity_pos_t maxRange,
const std::vector<int>& owners, int requiredInterface, u8 flagsMask) const
const std::vector<int>& owners, int requiredInterface, u8 flagsMask, bool accountForSize) const
{
// Min range must be non-negative
if (minRange < entity_pos_t::Zero())
@ -1381,8 +1384,9 @@ public:
q.minRange = minRange;
q.maxRange = maxRange;
q.elevationBonus = entity_pos_t::Zero();
q.accountForSize = accountForSize;
if (q.source.GetId() != INVALID_ENTITY && q.maxRange != entity_pos_t::FromInt(-1))
if (q.accountForSize && q.source.GetId() != INVALID_ENTITY && q.maxRange != entity_pos_t::FromInt(-1))
{
u32 size = 0;
if (ENTITY_IS_LOCAL(q.source.GetId()))
@ -1418,9 +1422,9 @@ public:
Query ConstructParabolicQuery(entity_id_t source,
entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t elevationBonus,
const std::vector<int>& owners, int requiredInterface, u8 flagsMask) const
const std::vector<int>& owners, int requiredInterface, u8 flagsMask, bool accountForSize) const
{
Query q = ConstructQuery(source,minRange,maxRange,owners,requiredInterface,flagsMask);
Query q = ConstructQuery(source, minRange, maxRange, owners, requiredInterface, flagsMask, accountForSize);
q.parabolic = true;
q.elevationBonus = elevationBonus;
return q;

View File

@ -45,9 +45,9 @@ std::string ICmpRangeManager::GetLosVisibilityPosition_wrapper(entity_pos_t x, e
}
BEGIN_INTERFACE_WRAPPER(RangeManager)
DEFINE_INTERFACE_METHOD_5("ExecuteQuery", std::vector<entity_id_t>, ICmpRangeManager, ExecuteQuery, entity_id_t, entity_pos_t, entity_pos_t, std::vector<int>, int)
DEFINE_INTERFACE_METHOD_5("ExecuteQueryAroundPos", std::vector<entity_id_t>, ICmpRangeManager, ExecuteQueryAroundPos, CFixedVector2D, entity_pos_t, entity_pos_t, std::vector<int>, int)
DEFINE_INTERFACE_METHOD_6("CreateActiveQuery", ICmpRangeManager::tag_t, ICmpRangeManager, CreateActiveQuery, entity_id_t, entity_pos_t, entity_pos_t, std::vector<int>, int, u8)
DEFINE_INTERFACE_METHOD_6("ExecuteQuery", std::vector<entity_id_t>, ICmpRangeManager, ExecuteQuery, entity_id_t, entity_pos_t, entity_pos_t, std::vector<int>, int, bool)
DEFINE_INTERFACE_METHOD_6("ExecuteQueryAroundPos", std::vector<entity_id_t>, ICmpRangeManager, ExecuteQueryAroundPos, CFixedVector2D, entity_pos_t, entity_pos_t, std::vector<int>, int, bool)
DEFINE_INTERFACE_METHOD_7("CreateActiveQuery", ICmpRangeManager::tag_t, ICmpRangeManager, CreateActiveQuery, entity_id_t, entity_pos_t, entity_pos_t, std::vector<int>, int, u8, bool)
DEFINE_INTERFACE_METHOD_7("CreateActiveParabolicQuery", ICmpRangeManager::tag_t, ICmpRangeManager, CreateActiveParabolicQuery, entity_id_t, entity_pos_t, entity_pos_t, entity_pos_t, std::vector<int>, int, u8)
DEFINE_INTERFACE_METHOD_1("DestroyActiveQuery", void, ICmpRangeManager, DestroyActiveQuery, ICmpRangeManager::tag_t)
DEFINE_INTERFACE_METHOD_1("EnableActiveQuery", void, ICmpRangeManager, EnableActiveQuery, ICmpRangeManager::tag_t)

View File

@ -73,7 +73,8 @@ class CLosQuerier;
*
* In most cases the users are event-based and want notifications when something
* has entered or left the range, and the query can be set up once and rarely changed.
* These queries have to be fast. Entities are approximated as circles.
* These queries have to be fast. Entities are approximated as points or circles
* (queries can be set up to ignore sizes because LOS currently ignores it, and mismatches are problematic).
*
* Current design:
*
@ -116,10 +117,11 @@ public:
* @param maxRange non-negative maximum distance in metres (inclusive); or -1.0 to ignore distance.
* @param owners list of player IDs that matching entities may have; -1 matches entities with no owner.
* @param requiredInterface if non-zero, an interface ID that matching entities must implement.
* @param accountForSize if true, compensate for source/target entity sizes.
* @return list of entities matching the query, ordered by increasing distance from the source entity.
*/
virtual std::vector<entity_id_t> ExecuteQuery(entity_id_t source,
entity_pos_t minRange, entity_pos_t maxRange, const std::vector<int>& owners, int requiredInterface) = 0;
virtual std::vector<entity_id_t> ExecuteQuery(entity_id_t source, entity_pos_t minRange, entity_pos_t maxRange,
const std::vector<int>& owners, int requiredInterface, bool accountForSize) = 0;
/**
* Execute a passive query.
@ -128,10 +130,11 @@ public:
* @param maxRange non-negative maximum distance in metres (inclusive); or -1.0 to ignore distance.
* @param owners list of player IDs that matching entities may have; -1 matches entities with no owner.
* @param requiredInterface if non-zero, an interface ID that matching entities must implement.
* @param accountForSize if true, compensate for source/target entity sizes.
* @return list of entities matching the query, ordered by increasing distance from the source entity.
*/
virtual std::vector<entity_id_t> ExecuteQueryAroundPos(const CFixedVector2D& pos,
entity_pos_t minRange, entity_pos_t maxRange, const std::vector<int>& owners, int requiredInterface) = 0;
virtual std::vector<entity_id_t> ExecuteQueryAroundPos(const CFixedVector2D& pos, entity_pos_t minRange, entity_pos_t maxRange,
const std::vector<int>& owners, int requiredInterface, bool accountForSize) = 0;
/**
* Construct an active query. The query will be disabled by default.
@ -141,10 +144,11 @@ public:
* @param owners list of player IDs that matching entities may have; -1 matches entities with no owner.
* @param requiredInterface if non-zero, an interface ID that matching entities must implement.
* @param flags if a entity in range has one of the flags set it will show up.
* @param accountForSize if true, compensate for source/target entity sizes.
* @return unique non-zero identifier of query.
*/
virtual tag_t CreateActiveQuery(entity_id_t source,
entity_pos_t minRange, entity_pos_t maxRange, const std::vector<int>& owners, int requiredInterface, u8 flags) = 0;
virtual tag_t CreateActiveQuery(entity_id_t source, entity_pos_t minRange, entity_pos_t maxRange,
const std::vector<int>& owners, int requiredInterface, u8 flags, bool accountForSize) = 0;
/**
* Construct an active query of a paraboloic form around the unit.
@ -158,10 +162,11 @@ public:
* @param owners list of player IDs that matching entities may have; -1 matches entities with no owner.
* @param requiredInterface if non-zero, an interface ID that matching entities must implement.
* @param flags if a entity in range has one of the flags set it will show up.
* NB: this one has no accountForSize parameter (assumed true), because we currently can only have 7 arguments for JS functions.
* @return unique non-zero identifier of query.
*/
virtual tag_t CreateActiveParabolicQuery(entity_id_t source,
entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t elevationBonus, const std::vector<int>& owners, int requiredInterface, u8 flags) = 0;
virtual tag_t CreateActiveParabolicQuery(entity_id_t source, entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t elevationBonus,
const std::vector<int>& owners, int requiredInterface, u8 flags) = 0;
/**

View File

@ -230,41 +230,41 @@ public:
move(100, position, fixed::FromInt(10), fixed::FromInt(10));
move(101, position2, fixed::FromInt(10), fixed::FromInt(20));
std::vector<entity_id_t> nearby = cmp->ExecuteQuery(100, fixed::FromInt(0), fixed::FromInt(4), {1}, 0);
std::vector<entity_id_t> nearby = cmp->ExecuteQuery(100, fixed::FromInt(0), fixed::FromInt(4), {1}, 0, true);
TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{});
nearby = cmp->ExecuteQuery(100, fixed::FromInt(4), fixed::FromInt(50), {1}, 0);
nearby = cmp->ExecuteQuery(100, fixed::FromInt(4), fixed::FromInt(50), {1}, 0, true);
TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{101});
move(101, position2, fixed::FromInt(10), fixed::FromInt(10));
nearby = cmp->ExecuteQuery(100, fixed::FromInt(0), fixed::FromInt(4), {1}, 0);
nearby = cmp->ExecuteQuery(100, fixed::FromInt(0), fixed::FromInt(4), {1}, 0, true);
TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{101});
nearby = cmp->ExecuteQuery(100, fixed::FromInt(4), fixed::FromInt(50), {1}, 0);
nearby = cmp->ExecuteQuery(100, fixed::FromInt(4), fixed::FromInt(50), {1}, 0, true);
TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{});
move(101, position2, fixed::FromInt(10), fixed::FromInt(13));
nearby = cmp->ExecuteQuery(100, fixed::FromInt(0), fixed::FromInt(4), {1}, 0);
nearby = cmp->ExecuteQuery(100, fixed::FromInt(0), fixed::FromInt(4), {1}, 0, true);
TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{101});
nearby = cmp->ExecuteQuery(100, fixed::FromInt(4), fixed::FromInt(50), {1}, 0);
nearby = cmp->ExecuteQuery(100, fixed::FromInt(4), fixed::FromInt(50), {1}, 0, true);
TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{});
move(101, position2, fixed::FromInt(10), fixed::FromInt(15));
// In range thanks to self obstruction size.
nearby = cmp->ExecuteQuery(100, fixed::FromInt(0), fixed::FromInt(4), {1}, 0);
nearby = cmp->ExecuteQuery(100, fixed::FromInt(0), fixed::FromInt(4), {1}, 0, true);
TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{101});
// In range thanks to target obstruction size.
nearby = cmp->ExecuteQuery(101, fixed::FromInt(0), fixed::FromInt(4), {1}, 0);
nearby = cmp->ExecuteQuery(101, fixed::FromInt(0), fixed::FromInt(4), {1}, 0, true);
TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{100});
// Trickier: min-range is closest-to-closest, but rotation may change the real distance.
nearby = cmp->ExecuteQuery(100, fixed::FromInt(2), fixed::FromInt(50), {1}, 0);
nearby = cmp->ExecuteQuery(100, fixed::FromInt(2), fixed::FromInt(50), {1}, 0, true);
TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{101});
nearby = cmp->ExecuteQuery(100, fixed::FromInt(5), fixed::FromInt(50), {1}, 0);
nearby = cmp->ExecuteQuery(100, fixed::FromInt(5), fixed::FromInt(50), {1}, 0, true);
TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{101});
nearby = cmp->ExecuteQuery(100, fixed::FromInt(6), fixed::FromInt(50), {1}, 0);
nearby = cmp->ExecuteQuery(100, fixed::FromInt(6), fixed::FromInt(50), {1}, 0, true);
TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{});
nearby = cmp->ExecuteQuery(101, fixed::FromInt(5), fixed::FromInt(50), {1}, 0);
nearby = cmp->ExecuteQuery(101, fixed::FromInt(5), fixed::FromInt(50), {1}, 0, true);
TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{100});
nearby = cmp->ExecuteQuery(101, fixed::FromInt(6), fixed::FromInt(50), {1}, 0);
nearby = cmp->ExecuteQuery(101, fixed::FromInt(6), fixed::FromInt(50), {1}, 0, true);
TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{});
}