Ticket #1960: elevation_advantage.diff

File elevation_advantage.diff, 22.5 KB (added by sanderd17, 11 years ago)

Diff with the complete code changes

  • binaries/data/mods/public/simulation/components/BuildingAI.js

     
    9999    if (cmpAttack)
    100100    {
    101101        var range = cmpAttack.GetRange("Ranged");
    102         this.enemyUnitsQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_DamageReceiver, cmpRangeManager.GetEntityFlagMask("normal"));
     102        this.enemyUnitsQuery = cmpRangeManager.CreateActiveParabolicQuery(this.entity, range.min, range.max, players, IID_DamageReceiver, cmpRangeManager.GetEntityFlagMask("normal"));
    103103        cmpRangeManager.EnableActiveQuery(this.enemyUnitsQuery);
    104104    }
    105105};
     
    133133        var range = cmpAttack.GetRange("Ranged");
    134134
    135135        // This query is only interested in Gaia entities that can attack.
    136         this.gaiaUnitsQuery = rangeMan.CreateActiveQuery(this.entity, range.min, range.max, [0], IID_Attack, rangeMan.GetEntityFlagMask("normal"));
     136        this.gaiaUnitsQuery = rangeMan.CreateActiveParabolicQuery(this.entity, range.min, range.max, [0], IID_Attack, rangeMan.GetEntityFlagMask("normal"));
    137137        rangeMan.EnableActiveQuery(this.gaiaUnitsQuery);
    138138    }
    139139};
     
    239243            //Fire N arrows, 0 <= N <= Number of arrows left
    240244            arrowsToFire = Math.floor(Math.random() * this.arrowsLeft);
    241245        }
     246
    242247        if (this.targetUnits.length > 0)
    243248        {
     249            var clonedTargets = this.targetUnits.slice();
    244250            for (var i = 0;i < arrowsToFire;i++)
    245251            {
    246                 cmpAttack.PerformAttack("Ranged", this.targetUnits[Math.floor(Math.random() * this.targetUnits.length)]);
    247                 PlaySound("arrowfly", this.entity);
     252                var target = clonedTargets[Math.floor(Math.random() * this.targetUnits.length)];
     253                if (
     254                    target &&
     255                    this.CheckTargetVisible(target)
     256                   )
     257                {
     258                    cmpAttack.PerformAttack("Ranged", target);
     259                    PlaySound("arrowfly", this.entity);
     260
     261                }
     262                else
     263                {
     264                    clonedTargets.splice(clonedTargets.indexOf(target),1);
     265                    i--; // one extra arrow left to fire
     266                    if(clonedTargets.length < 1)
     267                    {
     268                        this.arrowsLeft += arrowsToFire;
     269                        // no targets found in this round, save arrows and go to next round
     270                        break;
     271                    }
     272                }
    248273            }
    249274            this.arrowsLeft -= arrowsToFire;
    250275        }
     
    252277    }
    253278};
    254279
     280/**
     281 * Returns true if the target entity is visible through the FoW/SoD.
     282 */
     283BuildingAI.prototype.CheckTargetVisible = function(target)
     284{
     285    var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     286    if (!cmpOwnership)
     287        return false;
     288
     289    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     290
     291    if (cmpRangeManager.GetLosVisibility(target, cmpOwnership.GetOwner(), false) == "hidden")
     292        return false;
     293
     294    // Either visible directly, or visible in fog
     295    return true;
     296};
     297
    255298Engine.RegisterComponentType(IID_BuildingAI, "BuildingAI", BuildingAI);
  • binaries/data/mods/public/simulation/components/UnitAI.js

     
    367367        this.attackType = type;
    368368
    369369        // If we are already at the target, try attacking it from here
    370         if (this.CheckTargetRange(this.order.data.target, IID_Attack, this.attackType))
     370        if (this.CheckTargetAttackRange(this.order.data.target, IID_Attack, this.attackType))
    371371        {
    372372            this.StopMoving();
    373373            // For packable units within attack range:
     
    427427            return;
    428428        }
    429429
    430         // Try to move within attack range
    431         if (this.MoveToTargetRange(this.order.data.target, IID_Attack, this.attackType))
     430        // Try to move within attack range 
     431        if (this.MoveToTargetAttackRange(this.order.data.target, IID_Attack, this.attackType,0.5))
    432432        {
    433433            // We've started walking to the given point
    434434            if (this.IsAnimal())
     
    13141314                },
    13151315
    13161316                "MoveCompleted": function() {
    1317                     // If the unit needs to unpack, do so
    1318                     if (this.CanUnpack())
    1319                         this.SetNextState("UNPACKING");
    1320                     else
    1321                         this.SetNextState("ATTACKING");
     1317
     1318                    if (this.CheckTargetAttackRange(this.order.data.target, IID_Attack , this.attackType))
     1319                    {
     1320                        // If the unit needs to unpack, do so
     1321                        if (this.CanUnpack())
     1322                            this.SetNextState("UNPACKING");
     1323                        else
     1324                            this.SetNextState("ATTACKING");
     1325                    }
     1326                    else
     1327                    {
     1328                        if (this.MoveToTargetAttackRange(this.order.data.target, IID_Attack, this.attackType,0))
     1329                        {
     1330                            this.SetNextState("APPROACHING");
     1331                        }
     1332                        else
     1333                        {
     1334                            // Give up
     1335                            this.FinishOrder();
     1336                        }
     1337                    }
    13221338                },
    13231339
    13241340                "Attacked": function(msg) {
     
    13341350            "UNPACKING": {
    13351351                "enter": function() {
    13361352                    // If we're not in range yet (maybe we stopped moving), move to target again
    1337                     if (!this.CheckTargetRange(this.order.data.target, IID_Attack, this.attackType))
     1353                    if (!this.CheckTargetAttackRange(this.order.data.target, IID_Attack, this.attackType))
    13381354                    {
    1339                         if (this.MoveToTargetRange(this.order.data.target, IID_Attack, this.attackType))
     1355                        if (this.MoveToTargetAttackRange(this.order.data.target, IID_Attack, this.attackType,0))
    13401356                            this.SetNextState("APPROACHING");
    13411357                        else
    13421358                        {
     
    14021418                    if (this.TargetIsAlive(target) && this.CanAttack(target, this.order.data.forceResponse || null))
    14031419                    {
    14041420                        // Check we can still reach the target
    1405                         if (this.CheckTargetRange(target, IID_Attack, this.attackType))
     1421                        if (this.CheckTargetAttackRange(target, IID_Attack, this.attackType))
    14061422                        {
    14071423                            var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
    14081424                            this.lastAttacked = cmpTimer.GetTime() - msg.lateness;
     
    32873303    return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max);
    32883304};
    32893305
     3306/**
     3307 * Move unit so we hope the target is in the attack range
     3308 * for melee attacks, this goes straight to the default range checks
     3309 * for ranged attacks, the parabolic range is used, so we can't know exactly at what horizontal range the target can be reached
     3310 * That's why a guess is needed
     3311 * a guess of 1 will take the maximum of the possible ranges, and stay far away
     3312 * a guess of 0 will take the minimum of the possible ranges and, in most cases, will have the target in range.
     3313 * every guess inbetween is a linear interpollation
     3314 */
     3315UnitAI.prototype.MoveToTargetAttackRange = function(target, iid, type,guess)
     3316{
     3317
     3318    if(type!= "Ranged") {
     3319        return this.MoveToTargetRange(target, iid, type);
     3320    }
     3321    if (!this.CheckTargetVisible(target)) {
     3322        return false;
     3323    }
     3324    var cmpRanged = Engine.QueryInterface(this.entity, iid);
     3325    var range = cmpRanged.GetRange(type);
     3326
     3327    var thisCmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     3328    var s = thisCmpPosition.GetPosition();
     3329
     3330    var targetCmpPosition = Engine.QueryInterface(target, IID_Position);
     3331    if(!targetCmpPosition.IsInWorld())
     3332        return false;   
     3333
     3334    var t = targetCmpPosition.GetPosition();
     3335    // h is positive when I'm higher than the target
     3336    var h = s.y-t.y;
     3337
     3338    // No negative roots please
     3339    if(h>-range.max/2) {
     3340        var parabolicMaxRange = Math.sqrt(range.max*range.max+2*range.max*h);
     3341    } else {
     3342        // return false? Or hope you come close enough?
     3343        var parabolicMaxRange = 0;
     3344        //return false;
     3345    }
     3346    // the parabole changes while walking, take something in the middle
     3347    var guessedMaxRange = Math.max(range.max, parabolicMaxRange)*guess+Math.min(range.max, parabolicMaxRange)*(1-guess) ;
     3348   
     3349    var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
     3350    var r = cmpUnitMotion.MoveToTargetRange(target, range.min, guessedMaxRange);
     3351    return r;
     3352};
     3353
    32903354UnitAI.prototype.MoveToTargetRangeExplicit = function(target, min, max)
    32913355{
    32923356    if (!this.CheckTargetVisible(target))
     
    33113375    return cmpUnitMotion.IsInTargetRange(target, range.min, range.max);
    33123376};
    33133377
     3378/**
     3379 * Check if the target is inside the attack range
     3380 * For melee attacks, this goes straigt to the regular range calculation
     3381 * For ranged attacks, the parabolic formula is used to accout for bigger ranges
     3382 * when the target is lower, and smaller ranges when the target is higher
     3383 */
     3384UnitAI.prototype.CheckTargetAttackRange = function(target, iid, type)
     3385{
     3386
     3387    if (type != "Ranged")
     3388    {
     3389        return this.CheckTargetRange(target,iid,type);
     3390    }
     3391   
     3392    var targetCmpPosition = Engine.QueryInterface(target, IID_Position);
     3393    if (!targetCmpPosition || !targetCmpPosition.IsInWorld())
     3394    {
     3395        return false;
     3396    }
     3397
     3398    var cmpRanged = Engine.QueryInterface(this.entity, iid);
     3399    var range = cmpRanged.GetRange(type);
     3400
     3401    var thisCmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     3402    var s = thisCmpPosition.GetPosition();
     3403
     3404    var t = targetCmpPosition.GetPosition();
     3405
     3406    var h = s.y-t.y;
     3407    var maxRangeSq = 2*range.max*(h + range.max/2);
     3408    if (maxRangeSq<0)
     3409    {
     3410        // certainly outside the range, the target is even too high to reach.
     3411        return false;
     3412    }
     3413
     3414    // use native range checking function to take account of target shape
     3415    // in case of big targets
     3416    return this.CheckTargetRangeExplicit(target,range.min,Math.sqrt(maxRangeSq));
     3417};
     3418
    33143419UnitAI.prototype.CheckTargetRangeExplicit = function(target, min, max)
    33153420{
    33163421    var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
  • source/simulation2/components/CCmpRangeManager.cpp

     
    2020#include "simulation2/system/Component.h"
    2121#include "ICmpRangeManager.h"
    2222
     23#include "ICmpTerrain.h"
    2324#include "simulation2/MessageTypes.h"
    2425#include "simulation2/components/ICmpPosition.h"
    2526#include "simulation2/components/ICmpTerritoryManager.h"
    2627#include "simulation2/components/ICmpVision.h"
     28#include "simulation2/components/ICmpWaterManager.h"
    2729#include "simulation2/helpers/Render.h"
    2830#include "simulation2/helpers/Spatial.h"
    2931
     
    4446struct Query
    4547{
    4648    bool enabled;
     49    bool parabolic;
    4750    entity_id_t source;
    4851    entity_pos_t minRange;
    4952    entity_pos_t maxRange;
     
    8891}
    8992
    9093/**
     94 * Checks whether v is in a parabolic range of (0,0,0)
     95 * The highest point of the paraboloid is (0,range/2,0)
     96 * and the circle of distance 'range' around (0,0,0) on height y=0 is part of the paraboloid
     97 *
     98 * Avoids sqrting and overflowing.
     99 */
     100static bool InParabolicRange(CFixedVector3D v, fixed range)
     101{
     102    i64 x = (i64)v.X.GetInternalValue(); // abs(x) <= 2^31
     103    i64 z = (i64)v.Z.GetInternalValue();
     104    i64 xx = (x * x); // xx <= 2^62
     105    i64 zz = (z * z);
     106    i64 d2 = (xx + zz) >> 1; // d2 <= 2^62 (no overflow)
     107   
     108    i64 y = (i64)v.Y.GetInternalValue();
     109
     110    i64 c = (i64)range.GetInternalValue();
     111    i64 c_2 = c >> 1;
     112
     113    i64 c2 = (c_2-y)*c;
     114
     115    if (d2 <= c2)
     116        return true;
     117
     118    return false;
     119}
     120
     121struct EntityParabolicRangeOutline
     122{
     123    entity_id_t source;
     124    CFixedVector3D position;
     125    entity_pos_t range;
     126    std::vector<float> outline;
     127};
     128
     129static std::map<entity_id_t, EntityParabolicRangeOutline> ParabolicRangesOutlines;
     130
     131/**
    91132 * Representation of an entity, with the data needed for queries.
    92133 */
    93134struct EntityData
     
    113154    void operator()(S& serialize, const char* UNUSED(name), Query& value)
    114155    {
    115156        serialize.Bool("enabled", value.enabled);
     157        serialize.Bool("parabolic",value.parabolic);
    116158        serialize.NumberU32_Unbounded("source", value.source);
    117159        serialize.NumberFixed_Unbounded("min range", value.minRange);
    118160        serialize.NumberFixed_Unbounded("max range", value.maxRange);
     
    589631        return id;
    590632    }
    591633
     634    virtual tag_t CreateActiveParabolicQuery(entity_id_t source,
     635        entity_pos_t minRange, entity_pos_t maxRange,
     636        std::vector<int> owners, int requiredInterface, u8 flags)
     637    {
     638        tag_t id = m_QueryNext++;
     639        m_Queries[id] = ConstructParabolicQuery(source, minRange, maxRange, owners, requiredInterface, flags);
     640
     641        return id;
     642    }
     643
    592644    virtual void DestroyActiveQuery(tag_t tag)
    593645    {
    594646        if (m_Queries.find(tag) == m_Queries.end())
     
    812864                r.push_back(it->first);
    813865            }
    814866        }
    815         else
     867        // Not the entire world, so check a parabolic range, or a regular range
     868        else if (q.parabolic)
    816869        {
     870            CFixedVector3D pos3d = cmpSourcePosition->GetPosition();
     871            // Get a quick list of entities that are potentially in range, with a cutoff of 3*maxRange
     872            std::vector<entity_id_t> ents = m_Subdivision.GetNear(pos, q.maxRange*2);
     873
     874            for (size_t i = 0; i < ents.size(); ++i)
     875            {
     876                std::map<entity_id_t, EntityData>::const_iterator it = m_EntityData.find(ents[i]);
     877                ENSURE(it != m_EntityData.end());
     878
     879                if (!TestEntityQuery(q, it->first, it->second))
     880                    continue;
     881               
     882                CmpPtr<ICmpPosition> cmpSecondPosition(GetSimContext(), ents[i]);
     883                if (!cmpSecondPosition || !cmpSecondPosition->IsInWorld())
     884                    continue;
     885                CFixedVector3D secondPosition = cmpSecondPosition->GetPosition();
     886
     887                // Restrict based on precise distance
     888                if (!InParabolicRange(
     889                        CFixedVector3D(it->second.x, secondPosition.Y, it->second.z)
     890                            - pos3d,
     891                        q.maxRange))
     892                    continue;
     893
     894                if (!q.minRange.IsZero())
     895                {
     896                    int distVsMin = (CFixedVector2D(it->second.x, it->second.z) - pos).CompareLength(q.minRange);
     897                    if (distVsMin < 0)
     898                        continue;
     899                }
     900
     901                r.push_back(it->first);
     902
     903            }
     904        }
     905        // check a regular range (i.e. not the entire world, and not parabolic)
     906        else
     907        {
    817908            // Get a quick list of entities that are potentially in range
    818909            std::vector<entity_id_t> ents = m_Subdivision.GetNear(pos, q.maxRange);
    819910
     
    838929                }
    839930
    840931                r.push_back(it->first);
     932
    841933            }
    842934        }
    843935    }
    844936
     937    virtual std::vector<float> getParabolicRangeForm(CFixedVector3D pos, entity_pos_t maxRange, entity_pos_t cutoff)
     938    {
     939
     940        entity_pos_t minAngle = entity_pos_t::FromFloat(2.0f*3.15f/80.0f);
     941   
     942        entity_pos_t precision = entity_pos_t::FromInt((int)TERRAIN_TILE_SIZE)/8;
     943
     944        std::vector<float> r;
     945       
     946
     947        CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY);
     948        CmpPtr<ICmpWaterManager> cmpWaterManager(GetSimContext(), SYSTEM_ENTITY);
     949        entity_pos_t waterLevel = cmpWaterManager->GetWaterLevel(pos.X,pos.Z);
     950
     951        if (cmpTerrain)
     952        {
     953            for (entity_pos_t angle = entity_pos_t::Zero(); angle < entity_pos_t::FromFloat(2.0f*3.14); angle += minAngle)
     954            {
     955                entity_pos_t sin;
     956                entity_pos_t cos;
     957                entity_pos_t minDistance = entity_pos_t::Zero();
     958                entity_pos_t maxDistance = cutoff;
     959                sincos_approx(angle,sin,cos);
     960
     961                CFixedVector2D minVector = CFixedVector2D(entity_pos_t::Zero(),entity_pos_t::Zero());
     962                CFixedVector2D maxVector = CFixedVector2D(cos,sin).Multiply(cutoff);
     963                entity_pos_t targetHeight = cmpTerrain->GetGroundLevel(pos.X+maxVector.X,pos.Z+maxVector.Y);
     964                // use water level to display range on water
     965                targetHeight = targetHeight > waterLevel ? targetHeight : waterLevel;
     966
     967                if (InParabolicRange(CFixedVector3D(maxVector.X,targetHeight-pos.Y,maxVector.Y),maxRange))
     968                {
     969                    r.push_back((pos.X+maxVector.X).ToFloat());
     970                    r.push_back((pos.Z+maxVector.Y).ToFloat());
     971                    continue;
     972                }
     973               
     974                // Loop until vectors come close enough
     975                while ((maxVector - minVector).CompareLength(precision) > 0)
     976                {
     977                    // difference still bigger than precision, bisect to get smaller difference
     978                    entity_pos_t newDistance = (minDistance+maxDistance)/entity_pos_t::FromInt(2);
     979
     980                    CFixedVector2D newVector = CFixedVector2D(cos,sin).Multiply(newDistance);
     981
     982                    // get the height of the ground
     983                    targetHeight = cmpTerrain->GetGroundLevel(pos.X+newVector.X,pos.Z+newVector.Y);
     984                    targetHeight = targetHeight > waterLevel ? targetHeight : waterLevel;
     985
     986                    if (InParabolicRange(CFixedVector3D(newVector.X,targetHeight-pos.Y,newVector.Y),maxRange))
     987                    {
     988                        // new vector is in parabolic range, so this is a new minVector
     989                        minVector = newVector;
     990                        minDistance = newDistance;
     991                    }
     992                    else
     993                    {
     994                        // new vector is out parabolic range, so this is a new maxVector
     995                        maxVector = newVector;
     996                        maxDistance = newDistance;
     997                    }
     998                   
     999                }
     1000                r.push_back((pos.X+maxVector.X).ToFloat());
     1001                r.push_back((pos.Z+maxVector.Y).ToFloat());
     1002               
     1003            }
     1004            r.push_back(r[0]);
     1005            r.push_back(r[1]);
     1006
     1007        }
     1008        return r;
     1009
     1010    }
     1011   
    8451012    Query ConstructQuery(entity_id_t source,
    8461013        entity_pos_t minRange, entity_pos_t maxRange,
    8471014        const std::vector<int>& owners, int requiredInterface, u8 flagsMask)
     
    8561023
    8571024        Query q;
    8581025        q.enabled = false;
     1026        q.parabolic = false;
    8591027        q.source = source;
    8601028        q.minRange = minRange;
    8611029        q.maxRange = maxRange;
     
    8701038        return q;
    8711039    }
    8721040
     1041    Query ConstructParabolicQuery(entity_id_t source,
     1042        entity_pos_t minRange, entity_pos_t maxRange,
     1043        const std::vector<int>& owners, int requiredInterface, u8 flagsMask)
     1044    {
     1045        Query q = ConstructQuery(source,minRange,maxRange,owners,requiredInterface,flagsMask);
     1046        q.parabolic = true;
     1047        return q;
     1048    }
     1049
     1050
    8731051    void RenderSubmit(SceneCollector& collector)
    8741052    {
    8751053        if (!m_DebugOverlayEnabled)
    8761054            return;
    877 
    8781055        CColor enabledRingColour(0, 1, 0, 1);
    8791056        CColor disabledRingColour(1, 0, 0, 1);
    8801057        CColor rayColour(1, 1, 0, 0.2f);
     
    8931070                CFixedVector2D pos = cmpSourcePosition->GetPosition2D();
    8941071
    8951072                // Draw the max range circle
    896                 m_DebugOverlayLines.push_back(SOverlayLine());
    897                 m_DebugOverlayLines.back().m_Color = (q.enabled ? enabledRingColour : disabledRingColour);
    898                 SimRender::ConstructCircleOnGround(GetSimContext(), pos.X.ToFloat(), pos.Y.ToFloat(), q.maxRange.ToFloat(), m_DebugOverlayLines.back(), true);
     1073                if (!q.parabolic)
     1074                {
     1075                    m_DebugOverlayLines.push_back(SOverlayLine());
     1076                    m_DebugOverlayLines.back().m_Color = (q.enabled ? enabledRingColour : disabledRingColour);
     1077                    SimRender::ConstructCircleOnGround(GetSimContext(), pos.X.ToFloat(), pos.Y.ToFloat(), q.maxRange.ToFloat(), m_DebugOverlayLines.back(), true);
     1078                }
     1079                else
     1080                {
     1081                    CFixedVector3D pos = cmpSourcePosition->GetPosition();
     1082                    std::vector<float> coords;
     1083                   
     1084                    // Get the outline from cache if possible
     1085                    if (ParabolicRangesOutlines.find(q.source) != ParabolicRangesOutlines.end())
     1086                    {
     1087                        EntityParabolicRangeOutline e = ParabolicRangesOutlines[q.source];
     1088                        if (e.position == pos && e.range == q.maxRange)
     1089                        {
     1090                            // outline is cached correctly, use it
     1091                            coords = e.outline;
     1092                        }
     1093                        else
     1094                        {
     1095                            // outline was cached, but important parameters changed
     1096                            // (position, elevation, range)
     1097                            // update it
     1098                            coords = getParabolicRangeForm(pos,q.maxRange,q.maxRange*2);
     1099                            e.outline = coords;
     1100                            e.range = q.maxRange;
     1101                            e.position = pos;
     1102                            ParabolicRangesOutlines[q.source] = e;
     1103                        }
     1104                    }
     1105                    else
     1106                    {
     1107                        // outline wasn't cached (first time you enable the range overlay
     1108                        // or you created a new entiy)
     1109                        // cache a new outline
     1110                        coords = getParabolicRangeForm(pos,q.maxRange,q.maxRange*2);
     1111                        EntityParabolicRangeOutline e;
     1112                        e.source = q.source;
     1113                        e.range = q.maxRange;
     1114                        e.position = pos;
     1115                        e.outline = coords;
     1116                        ParabolicRangesOutlines[q.source] = e;
     1117                    }
     1118                   
     1119                    CColor thiscolor = q.enabled ? enabledRingColour : disabledRingColour;
     1120                   
     1121                    // draw the outline (piece by piece)   
     1122                    for (size_t i = 3; i < coords.size(); i += 2)
     1123                    {
     1124                        std::vector<float> c;
     1125                        c.push_back(coords[i-3]);
     1126                        c.push_back(coords[i-2]);
     1127                        c.push_back(coords[i-1]);
     1128                        c.push_back(coords[i]);
     1129                        m_DebugOverlayLines.push_back(SOverlayLine());
     1130                        m_DebugOverlayLines.back().m_Color = thiscolor;
     1131                        SimRender::ConstructLineOnGround(GetSimContext(), c, m_DebugOverlayLines.back(), true);
     1132                    }
     1133                }
    8991134
    9001135                // Draw the min range circle
    9011136                if (!q.minRange.IsZero())
  • source/simulation2/components/ICmpRangeManager.cpp

     
    3636BEGIN_INTERFACE_WRAPPER(RangeManager)
    3737DEFINE_INTERFACE_METHOD_5("ExecuteQuery", std::vector<entity_id_t>, ICmpRangeManager, ExecuteQuery, entity_id_t, entity_pos_t, entity_pos_t, std::vector<int>, int)
    3838DEFINE_INTERFACE_METHOD_6("CreateActiveQuery", ICmpRangeManager::tag_t, ICmpRangeManager, CreateActiveQuery, entity_id_t, entity_pos_t, entity_pos_t, std::vector<int>, int, u8)
     39DEFINE_INTERFACE_METHOD_6("CreateActiveParabolicQuery", ICmpRangeManager::tag_t, ICmpRangeManager, CreateActiveParabolicQuery, entity_id_t, entity_pos_t, entity_pos_t, std::vector<int>, int, u8)
    3940DEFINE_INTERFACE_METHOD_1("DestroyActiveQuery", void, ICmpRangeManager, DestroyActiveQuery, ICmpRangeManager::tag_t)
    4041DEFINE_INTERFACE_METHOD_1("EnableActiveQuery", void, ICmpRangeManager, EnableActiveQuery, ICmpRangeManager::tag_t)
    4142DEFINE_INTERFACE_METHOD_1("DisableActiveQuery", void, ICmpRangeManager, DisableActiveQuery, ICmpRangeManager::tag_t)
  • source/simulation2/components/ICmpRangeManager.h

     
    102102    virtual tag_t CreateActiveQuery(entity_id_t source,
    103103        entity_pos_t minRange, entity_pos_t maxRange, std::vector<int> owners, int requiredInterface, u8 flags) = 0;
    104104
     105    /**
     106     * Construct an active query of a paraboloic form around the unit.
     107     * The query will be disabled by default.
     108     * @param source the entity around which the range will be computed.
     109     * @param minRange non-negative minimum horizontal distance in metres (inclusive). MinRange doesn't do parabolic checks.
     110     * @param maxRange non-negative maximum distance in metres (inclusive) for units on the same elevation;
     111     *      or -1.0 to ignore distance.
     112     *      For units on a different elevation, a physical correct paraboloid with height=maxRange/2 above the unit is used to query them
     113     * @param owners list of player IDs that matching entities may have; -1 matches entities with no owner.
     114     * @param requiredInterface if non-zero, an interface ID that matching entities must implement.
     115     * @param flags if a entity in range has one of the flags set it will show up.
     116     * @return unique non-zero identifier of query.
     117     */
     118    virtual tag_t CreateActiveParabolicQuery(entity_id_t source,
     119        entity_pos_t minRange, entity_pos_t maxRange, std::vector<int> owners, int requiredInterface, u8 flags) = 0;
     120
     121
    105122    /**
    106123     * Destroy a query and clean up resources. This must be called when an entity no longer needs its
    107124     * query (e.g. when the entity is destroyed).