forked from 0ad/0ad
[Gameplay] Implement a rough comparison function to sort entities within a template-determined percent error:https://wildfiregames.com/forum/topic/118677-less-precise-sorting-in-range-manager/#comment-575838
This commit is contained in:
parent
668ae8a20e
commit
4dec13d232
@ -42,6 +42,11 @@ UnitAI.prototype.Schema =
|
||||
"<data type='nonNegativeInteger'/>" +
|
||||
"</element>" +
|
||||
"</optional>" +
|
||||
"<optional>" +
|
||||
"<element name='RangeError'>" +
|
||||
"<data type='nonNegativeInteger'/>" +
|
||||
"</element>" +
|
||||
"</optional>" +
|
||||
"<optional>" +
|
||||
"<interleave>" +
|
||||
"<element name='RoamDistance'>" +
|
||||
@ -3462,6 +3467,7 @@ UnitAI.prototype.Init = function()
|
||||
|
||||
this.formationAnimationVariant = undefined;
|
||||
this.cheeringTime = +(this.template.CheeringTime || 0);
|
||||
this.rangeError = +(this.template.RangeError || 0);
|
||||
this.SetStance(this.template.DefaultStance);
|
||||
};
|
||||
|
||||
@ -3776,7 +3782,7 @@ UnitAI.prototype.SetupLOSRangeQuery = function(enable = true)
|
||||
// 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);
|
||||
cmpRangeManager.GetEntityFlagMask("normal"), false, this.rangeError);
|
||||
|
||||
if (enable)
|
||||
cmpRangeManager.EnableActiveQuery(this.losRangeQuery);
|
||||
@ -3841,7 +3847,7 @@ UnitAI.prototype.SetupAttackRangeQuery = function(enable = true)
|
||||
// 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);
|
||||
cmpRangeManager.GetEntityFlagMask("normal"), false, this.rangeError);
|
||||
|
||||
if (enable)
|
||||
cmpRangeManager.EnableActiveQuery(this.losAttackRangeQuery);
|
||||
|
@ -109,6 +109,7 @@
|
||||
<CanPatrol>true</CanPatrol>
|
||||
<PatrolWaitTime>1</PatrolWaitTime>
|
||||
<CheeringTime>2800</CheeringTime>
|
||||
<RangeError>10</RangeError>
|
||||
<Formations datatype="tokens">
|
||||
special/formations/null
|
||||
special/formations/box
|
||||
|
@ -42,6 +42,7 @@
|
||||
</Sound>
|
||||
<Turretable/>
|
||||
<UnitAI>
|
||||
<RangeError>15</RangeError>
|
||||
<Formations datatype="tokens">
|
||||
special/formations/skirmish
|
||||
</Formations>
|
||||
|
@ -45,6 +45,9 @@
|
||||
<attack_ranged>attack/weapon/bow_attack.xml</attack_ranged>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<UnitAI>
|
||||
<RangeError>20</RangeError>
|
||||
</UnitAI>
|
||||
<UnitMotion>
|
||||
<WalkSpeed op="add">0.6</WalkSpeed>
|
||||
<Acceleration op="add">1.2</Acceleration>
|
||||
|
@ -67,6 +67,7 @@
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<UnitAI>
|
||||
<RangeError>3</RangeError>
|
||||
<DefaultStance>standground</DefaultStance>
|
||||
</UnitAI>
|
||||
<UnitMotion>
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2020 Wildfire Games.
|
||||
/* Copyright (C) 2024 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -169,6 +169,29 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns -1, 0, +1 depending on whether length estimate is less/equal/greater
|
||||
* than the argument's length.
|
||||
* Uses a percent error parameter to compare lengths with that percent error.
|
||||
*/
|
||||
int CompareLengthRough(const CFixedVector2D& other, u8 rangeError) const
|
||||
{
|
||||
u64 d2 = SQUARE_U64_FIXED(X) + SQUARE_U64_FIXED(Y);
|
||||
u64 od2 = SQUARE_U64_FIXED(other.X) + SQUARE_U64_FIXED(other.Y);
|
||||
|
||||
//overflow risk with long ranges (designed for unit ranges)
|
||||
CheckMultiplicationOverflow(u64, d2, 100+rangeError, "Overflow in CFixedVector2D::CompareLengthRough()","Underflow in CFixedVector2D::CompareLengthRough()")
|
||||
d2 = d2 * (100-rangeError + d2 % (1+(rangeError*2)));
|
||||
CheckMultiplicationOverflow(u64, od2, 100, "Overflow in CFixedVector2D::CompareLengthRough()", "Underflow in CFixedVector2D::CompareLengthRough()")
|
||||
od2 = od2 * 100;
|
||||
|
||||
if (d2 < od2)
|
||||
return -1;
|
||||
if (d2 > od2)
|
||||
return +1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool IsZero() const
|
||||
{
|
||||
return X.IsZero() && Y.IsZero();
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2022 Wildfire Games.
|
||||
/* Copyright (C) 2024 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -162,6 +162,7 @@ struct Query
|
||||
u32 ownersMask;
|
||||
i32 interface;
|
||||
u8 flagsMask;
|
||||
u8 rangeError;
|
||||
bool enabled;
|
||||
bool parabolic;
|
||||
bool accountForSize; // If true, the query accounts for unit sizes, otherwise it treats all entities as points.
|
||||
@ -255,8 +256,8 @@ static_assert(sizeof(EntityData) == 24);
|
||||
class EntityDistanceOrdering
|
||||
{
|
||||
public:
|
||||
EntityDistanceOrdering(const EntityMap<EntityData>& entities, const CFixedVector2D& source) :
|
||||
m_EntityData(entities), m_Source(source)
|
||||
EntityDistanceOrdering(const EntityMap<EntityData>& entities, const CFixedVector2D& source, u8 rangeError = 0) :
|
||||
m_EntityData(entities), m_Source(source), m_RangeError(rangeError)
|
||||
{
|
||||
}
|
||||
|
||||
@ -268,11 +269,12 @@ public:
|
||||
const EntityData& db = m_EntityData.find(b)->second;
|
||||
CFixedVector2D vecA = CFixedVector2D(da.x, da.z) - m_Source;
|
||||
CFixedVector2D vecB = CFixedVector2D(db.x, db.z) - m_Source;
|
||||
return (vecA.CompareLength(vecB) < 0);
|
||||
return m_RangeError > 0 ? vecA.CompareLengthRough(vecB, m_RangeError) < 0 : vecA.CompareLength(vecB) < 0;
|
||||
}
|
||||
|
||||
const EntityMap<EntityData>& m_EntityData;
|
||||
CFixedVector2D m_Source;
|
||||
u8 m_RangeError;
|
||||
|
||||
private:
|
||||
EntityDistanceOrdering& operator=(const EntityDistanceOrdering&);
|
||||
@ -294,6 +296,7 @@ struct SerializeHelper<Query>
|
||||
serialize.NumberU32_Unbounded("owners mask", value.ownersMask);
|
||||
serialize.NumberI32_Unbounded("interface", value.interface);
|
||||
Serializer(serialize, "last match", value.lastMatch);
|
||||
serialize.NumberU8_Unbounded("range percent error", value.rangeError);
|
||||
serialize.NumberU8_Unbounded("flagsMask", value.flagsMask);
|
||||
serialize.Bool("enabled", value.enabled);
|
||||
serialize.Bool("parabolic",value.parabolic);
|
||||
@ -923,20 +926,20 @@ public:
|
||||
|
||||
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) override
|
||||
const std::vector<int>& owners, int requiredInterface, u8 flags, bool accountForSize, u8 rangeError) override
|
||||
{
|
||||
tag_t id = m_QueryNext++;
|
||||
m_Queries[id] = ConstructQuery(source, minRange, maxRange, owners, requiredInterface, flags, accountForSize);
|
||||
m_Queries[id] = ConstructQuery(source, minRange, maxRange, owners, requiredInterface, flags, accountForSize, rangeError);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
tag_t CreateActiveParabolicQuery(entity_id_t source,
|
||||
entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t yOrigin,
|
||||
const std::vector<int>& owners, int requiredInterface, u8 flags) override
|
||||
const std::vector<int>& owners, int requiredInterface, u8 flags, u8 rangeError) override
|
||||
{
|
||||
tag_t id = m_QueryNext++;
|
||||
m_Queries[id] = ConstructParabolicQuery(source, minRange, maxRange, yOrigin, owners, requiredInterface, flags, true);
|
||||
m_Queries[id] = ConstructParabolicQuery(source, minRange, maxRange, yOrigin, owners, requiredInterface, flags, true, rangeError);
|
||||
|
||||
return id;
|
||||
}
|
||||
@ -993,25 +996,25 @@ public:
|
||||
|
||||
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) override
|
||||
const std::vector<int>& owners, int requiredInterface, bool accountForSize, u8 rangeError) override
|
||||
{
|
||||
Query q = ConstructQuery(INVALID_ENTITY, minRange, maxRange, owners, requiredInterface, GetEntityFlagMask("normal"), accountForSize);
|
||||
Query q = ConstructQuery(INVALID_ENTITY, minRange, maxRange, owners, requiredInterface, GetEntityFlagMask("normal"), accountForSize, rangeError);
|
||||
std::vector<entity_id_t> r;
|
||||
PerformQuery(q, r, pos);
|
||||
|
||||
// Return the list sorted by distance from the entity
|
||||
std::stable_sort(r.begin(), r.end(), EntityDistanceOrdering(m_EntityData, pos));
|
||||
std::stable_sort(r.begin(), r.end(), EntityDistanceOrdering(m_EntityData, pos, q.rangeError));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
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) override
|
||||
const std::vector<int>& owners, int requiredInterface, bool accountForSize, u8 rangeError) override
|
||||
{
|
||||
PROFILE("ExecuteQuery");
|
||||
|
||||
Query q = ConstructQuery(source, minRange, maxRange, owners, requiredInterface, GetEntityFlagMask("normal"), accountForSize);
|
||||
Query q = ConstructQuery(source, minRange, maxRange, owners, requiredInterface, GetEntityFlagMask("normal"), accountForSize, rangeError);
|
||||
|
||||
std::vector<entity_id_t> r;
|
||||
|
||||
@ -1026,7 +1029,7 @@ public:
|
||||
PerformQuery(q, r, pos);
|
||||
|
||||
// Return the list sorted by distance from the entity
|
||||
std::stable_sort(r.begin(), r.end(), EntityDistanceOrdering(m_EntityData, pos));
|
||||
std::stable_sort(r.begin(), r.end(), EntityDistanceOrdering(m_EntityData, pos, q.rangeError));
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -1061,7 +1064,7 @@ public:
|
||||
q.lastMatch = r;
|
||||
|
||||
// Return the list sorted by distance from the entity
|
||||
std::stable_sort(r.begin(), r.end(), EntityDistanceOrdering(m_EntityData, pos));
|
||||
std::stable_sort(r.begin(), r.end(), EntityDistanceOrdering(m_EntityData, pos, q.rangeError));
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -1145,7 +1148,7 @@ public:
|
||||
continue;
|
||||
|
||||
if (cmpSourcePosition && cmpSourcePosition->IsInWorld())
|
||||
std::stable_sort(added.begin(), added.end(), EntityDistanceOrdering(m_EntityData, cmpSourcePosition->GetPosition2D()));
|
||||
std::stable_sort(added.begin(), added.end(), EntityDistanceOrdering(m_EntityData, cmpSourcePosition->GetPosition2D(),query.rangeError));
|
||||
|
||||
messages.resize(messages.size() + 1);
|
||||
std::pair<entity_id_t, CMessageRangeUpdate>& back = messages.back();
|
||||
@ -1404,7 +1407,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, bool accountForSize) const
|
||||
const std::vector<int>& owners, int requiredInterface, u8 flagsMask, bool accountForSize, u8 rangeError = 0) const
|
||||
{
|
||||
// Min range must be non-negative.
|
||||
if (minRange < entity_pos_t::Zero())
|
||||
@ -1453,6 +1456,7 @@ public:
|
||||
LOGWARNING("CCmpRangeManager: No owners in query for entity %u", source);
|
||||
|
||||
q.interface = requiredInterface;
|
||||
q.rangeError = rangeError;
|
||||
q.flagsMask = flagsMask;
|
||||
|
||||
return q;
|
||||
@ -1460,9 +1464,9 @@ public:
|
||||
|
||||
Query ConstructParabolicQuery(entity_id_t source,
|
||||
entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t yOrigin,
|
||||
const std::vector<int>& owners, int requiredInterface, u8 flagsMask, bool accountForSize) const
|
||||
const std::vector<int>& owners, int requiredInterface, u8 flagsMask, bool accountForSize, u8 rangeError = 0) const
|
||||
{
|
||||
Query q = ConstructQuery(source, minRange, maxRange, owners, requiredInterface, flagsMask, accountForSize);
|
||||
Query q = ConstructQuery(source, minRange, maxRange, owners, requiredInterface, flagsMask, accountForSize, rangeError);
|
||||
q.parabolic = true;
|
||||
q.yOrigin = yOrigin;
|
||||
return q;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2022 Wildfire Games.
|
||||
/* Copyright (C) 2024 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -132,7 +132,7 @@ public:
|
||||
* @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, bool accountForSize) = 0;
|
||||
const std::vector<int>& owners, int requiredInterface, bool accountForSize, u8 rangeError = 0) = 0;
|
||||
|
||||
/**
|
||||
* Execute a passive query.
|
||||
@ -145,7 +145,7 @@ public:
|
||||
* @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, bool accountForSize) = 0;
|
||||
const std::vector<int>& owners, int requiredInterface, bool accountForSize, u8 rangeError = 0) = 0;
|
||||
|
||||
/**
|
||||
* Construct an active query. The query will be disabled by default.
|
||||
@ -159,7 +159,7 @@ public:
|
||||
* @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, bool accountForSize) = 0;
|
||||
const std::vector<int>& owners, int requiredInterface, u8 flags, bool accountForSize, u8 rangeError = 0) = 0;
|
||||
|
||||
/**
|
||||
* Construct an active query of a paraboloic form around the unit.
|
||||
@ -177,7 +177,7 @@ public:
|
||||
* @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 yOrigin,
|
||||
const std::vector<int>& owners, int requiredInterface, u8 flags) = 0;
|
||||
const std::vector<int>& owners, int requiredInterface, u8 flags, u8 rangeError = 0) = 0;
|
||||
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user