Ticket #1537: chasing.3.diff

File chasing.3.diff, 26.9 KB (added by sanderd17, 10 years ago)
  • binaries/data/mods/public/simulation/components/Timer.js

     
    1616Timer.prototype.GetTime = function()
    1717{
    1818    return this.time;
    19 }
     19};
    2020
    2121/**
     22 * Returns the turn length of the latest turn in millisecons
     23 */
     24Timer.prototype.GetTurnLength = function()
     25{
     26    return this.turnLength;
     27};
     28
     29/**
    2230 * Create a new timer, which will call the 'funcname' method with arguments (data, lateness)
    2331 * on the 'iid' component of the 'ent' entity, after at least 'time' milliseconds.
    2432 * 'lateness' is how late the timer is executed after the specified time (in milliseconds).
     
    6068
    6169Timer.prototype.OnUpdate = function(msg)
    6270{
    63     var dt = Math.round(msg.turnLength * 1000);
    64     this.time += dt;
     71    this.turnLength = Math.round(msg.turnLength * 1000);
     72    this.time += this.turnLength;
    6573
    6674    // Collect the timers that need to run
    6775    // (We do this in two stages to avoid deleting from the timer list while
  • binaries/data/mods/public/simulation/components/UnitAI.js

     
    171171        // ignore
    172172    },
    173173
     174    "Update": function(msg) {
     175        // ignore
     176    },
     177
    174178    // Formation handlers:
    175179
    176180    "FormationLeave": function(msg) {
     
    378382        // We use the distance between the entities to account for ranged attacks
    379383        var distance = DistanceBetweenEntities(this.entity, this.order.data.target) + (+this.template.FleeDistance);
    380384        var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
    381         if (cmpUnitMotion.MoveToTargetRange(this.order.data.target, distance, -1))
     385        if (cmpUnitMotion.MoveToTargetRange(this.order.data.target, distance, -1, 0))
    382386        {
    383387            // We've started fleeing from the given target
    384388            if (this.IsAnimal())
     
    413417        this.order.data.attackType = type;
    414418
    415419        // If we are already at the target, try attacking it from here
    416         if (this.CheckTargetAttackRange(this.order.data.target, IID_Attack, this.order.data.attackType))
     420        if (this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType))
    417421        {
    418422            this.StopMoving();
    419423            // For packable units within attack range:
     
    485489        }
    486490
    487491        // Try to move within attack range
    488         if (this.MoveToTargetAttackRange(this.order.data.target, IID_Attack, this.order.data.attackType))
     492        if (this.MoveToTargetAttackRange(this.order.data.target, this.order.data.attackType))
    489493        {
    490494            // We've started walking to the given point
    491495            if (this.IsAnimal())
     
    15901594
    15911595                "MoveCompleted": function() {
    15921596
    1593                     if (this.CheckTargetAttackRange(this.order.data.target, IID_Attack , this.order.data.attackType))
     1597                    if (this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType))
    15941598                    {
    15951599                        // If the unit needs to unpack, do so
    15961600                        if (this.CanUnpack())
     
    16001604                    }
    16011605                    else
    16021606                    {
    1603                         if (this.MoveToTargetAttackRange(this.order.data.target, IID_Attack, this.order.data.attackType))
     1607                        if (this.MoveToTargetAttackRange(this.order.data.target, this.order.data.attackType))
    16041608                        {
    16051609                            this.SetNextState("APPROACHING");
    16061610                        }
     
    16251629            "UNPACKING": {
    16261630                "enter": function() {
    16271631                    // If we're not in range yet (maybe we stopped moving), move to target again
    1628                     if (!this.CheckTargetAttackRange(this.order.data.target, IID_Attack, this.order.data.attackType))
     1632                    if (!this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType))
    16291633                    {
    1630                         if (this.MoveToTargetAttackRange(this.order.data.target, IID_Attack, this.order.data.attackType))
     1634                        if (this.MoveToTargetAttackRange(this.order.data.target, this.order.data.attackType))
    16311635                            this.SetNextState("APPROACHING");
    16321636                        else
    16331637                        {
     
    16901694                "Timer": function(msg) {
    16911695                    var target = this.order.data.target;
    16921696                    // Check the target is still alive and attackable
    1693                     if (this.TargetIsAlive(target) && this.CanAttack(target, this.order.data.forceResponse || null))
     1697                    if
     1698                    (
     1699                        !this.TargetIsAlive(target) ||
     1700                        !this.CanAttack(target, this.order.data.forceResponse || null) ||
     1701                        !this.CheckTargetAttackRange(target, this.order.data.attackType)
     1702                    )
     1703                        return;
     1704
     1705                    // If we are hunting, first update the target position of the gather order so we know where will be the killed animal
     1706                    if (this.order.data.hunting && this.orderQueue[1] && this.orderQueue[1].data.lastPos)
    16941707                    {
    1695                         // Check we can still reach the target
    1696                         if (this.CheckTargetAttackRange(target, IID_Attack, this.order.data.attackType))
     1708                        var cmpPosition = Engine.QueryInterface(this.order.data.target, IID_Position);
     1709                        if (cmpPosition && cmpPosition.IsInWorld())
    16971710                        {
    1698                             // If we are hunting, first update the target position of the gather order so we know where will be the killed animal
    1699                             if (this.order.data.hunting && this.orderQueue[1] && this.orderQueue[1].data.lastPos)
    1700                             {
    1701                                 var cmpPosition = Engine.QueryInterface(this.order.data.target, IID_Position);
    1702                                 if (cmpPosition && cmpPosition.IsInWorld())
    1703                                 {
    1704                                     // Store the initial position, so that we can find the rest of the herd later
    1705                                     if (!this.orderQueue[1].data.initPos)
    1706                                         this.orderQueue[1].data.initPos = this.orderQueue[1].data.lastPos;
    1707                                     this.orderQueue[1].data.lastPos = cmpPosition.GetPosition();
    1708                                     // We still know where the animal is, so we shouldn't give up before going there
    1709                                     this.orderQueue[1].data.secondTry = undefined;
    1710                                 }
    1711                             }
     1711                            // Store the initial position, so that we can find the rest of the herd later
     1712                            if (!this.orderQueue[1].data.initPos)
     1713                                this.orderQueue[1].data.initPos = this.orderQueue[1].data.lastPos;
     1714                            this.orderQueue[1].data.lastPos = cmpPosition.GetPosition();
     1715                            // We still know where the animal is, so we shouldn't give up before going there
     1716                            this.orderQueue[1].data.secondTry = undefined;
     1717                        }
     1718                    }
    17121719
    1713                             var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
    1714                             this.lastAttacked = cmpTimer.GetTime() - msg.lateness;
     1720                    var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     1721                    this.lastAttacked = cmpTimer.GetTime() - msg.lateness;
    17151722
    1716                             this.FaceTowardsTarget(target);
    1717                             var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
    1718                             cmpAttack.PerformAttack(this.order.data.attackType, target);
     1723                    this.FaceTowardsTarget(target);
     1724                    var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
     1725                    cmpAttack.PerformAttack(this.order.data.attackType, target);
    17191726
    1720                             if (this.resyncAnimation)
    1721                             {
    1722                                 this.SetAnimationSync(this.attackTimers.repeat, this.attackTimers.repeat);
    1723                                 this.resyncAnimation = false;
    1724                             }
    1725                             return;
    1726                         }
     1727                    if (this.resyncAnimation)
     1728                    {
     1729                        this.SetAnimationSync(this.attackTimers.repeat, this.attackTimers.repeat);
     1730                        this.resyncAnimation = false;
     1731                    }
     1732                },
    17271733
    1728                         // Can't reach it - try to chase after it
    1729                         if (this.ShouldChaseTargetedEntity(target, this.order.data.force))
    1730                         {
    1731                             if (this.MoveToTargetRange(target, IID_Attack, this.order.data.attackType))
    1732                             {
    1733                                 this.SetNextState("COMBAT.CHASING");
    1734                                 return;
    1735                             }
    1736                         }
     1734                // respond to target position changes and target deaths immediately
     1735                "Update": function(msg) {
     1736                    var target = this.order.data.target;
     1737                    var attackType = this.order.data.attackType;
     1738                    if
     1739                    (
     1740                        this.TargetIsAlive(target) &&
     1741                        this.CanAttack(target, this.order.data.forceResponse || null)
     1742                    )
     1743                    {
     1744                        if
     1745                        (
     1746                            !this.CheckTargetAttackRange(target, attackType) &&
     1747                            this.ShouldChaseTargetedEntity(target, this.order.data.force) &&
     1748                            this.MoveToTargetAttackRange(target, attackType)
     1749                        )
     1750                            this.SetNextState("COMBAT.CHASING");
     1751                        return;
    17371752                    }
    17381753
    1739                     this.oldAttackType = this.order.data.attackType;
     1754                    this.oldAttackType = attackType;
    17401755                    // Can't reach it, no longer owned by enemy, or it doesn't exist any more - give up
    17411756                    // Except if in WalkAndFight mode where we look for more ennemies around before moving again
    17421757                    if (this.FinishOrder())
     
    17601775                        this.WalkToHeldPosition();
    17611776                },
    17621777
    1763                 // TODO: respond to target deaths immediately, rather than waiting
    1764                 // until the next Timer event
    1765 
    17661778                "Attacked": function(msg) {
    17671779                    if (this.order.data.target != msg.data.attacker)
    17681780                    {
     
    35873599    UnitFsm.ProcessMessage(this, {"type": "PackFinished", "packed": msg.packed});
    35883600};
    35893601
     3602UnitAI.prototype.OnUpdate = function(msg)
     3603{
     3604    UnitFsm.ProcessMessage(this, {"type": "Update", "data": msg});
     3605}
     3606
    35903607//// Helper functions to be called by the FSM ////
    35913608
    35923609UnitAI.prototype.GetWalkSpeed = function()
     
    38563873        return false;
    38573874
    38583875    var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
    3859     return cmpUnitMotion.MoveToTargetRange(target, 0, 0);
     3876    return cmpUnitMotion.MoveToTargetRange(target, 0, 0, 0);
    38603877};
    38613878
    3862 UnitAI.prototype.MoveToTargetRange = function(target, iid, type)
     3879UnitAI.prototype.MoveToTargetRange = function(target, iid, type, time)
    38633880{
    38643881    if (!this.CheckTargetVisible(target))
    38653882        return false;
     
    38683885    if (!cmpRanged)
    38693886        return false;
    38703887    var range = cmpRanged.GetRange(type);
     3888    if (!time)
     3889        time = 0;
    38713890
    38723891    var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
    3873     return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max);
     3892    return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max, time);
    38743893};
    38753894
    38763895/**
     
    38783897 * for melee attacks, this goes straight to the default range checks
    38793898 * for ranged attacks, the parabolic range is used
    38803899 */
    3881 UnitAI.prototype.MoveToTargetAttackRange = function(target, iid, type)
     3900UnitAI.prototype.MoveToTargetAttackRange = function(target, type)
    38823901{
     3902    var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
     3903    var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     3904    var timers = cmpAttack.GetTimers(type);
     3905    // add one turnlength, as even without prepare time, there's one turn between stopping
     3906    // to move and starting to attack
     3907    var time = timers.prepare + cmpTimer.GetTurnLength() / 1000;
    38833908
    38843909    if(type!= "Ranged")
    3885         return this.MoveToTargetRange(target, iid, type);
     3910        return this.MoveToTargetRange(target, IID_Attack, type, time);
    38863911   
    38873912    if (!this.CheckTargetVisible(target))
    38883913        return false;
    38893914   
    3890     var cmpRanged = Engine.QueryInterface(this.entity, iid);
    3891     var range = cmpRanged.GetRange(type);
     3915    var range = cmpAttack.GetRange(type);
    38923916
    38933917    var thisCmpPosition = Engine.QueryInterface(this.entity, IID_Position);
    38943918    var s = thisCmpPosition.GetPosition();
     
    39133937    var guessedMaxRange = (range.max + parabolicMaxRange)/2;
    39143938
    39153939    var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
    3916     if (cmpUnitMotion.MoveToTargetRange(target, range.min, guessedMaxRange))
     3940    if (cmpUnitMotion.MoveToTargetRange(target, range.min, guessedMaxRange, time))
    39173941        return true;
    39183942
    39193943    // if that failed, try closer
    3920     return cmpUnitMotion.MoveToTargetRange(target, range.min, Math.min(range.max, parabolicMaxRange));
     3944    return cmpUnitMotion.MoveToTargetRange(target, range.min, Math.min(range.max, parabolicMaxRange), time);
    39213945};
    39223946
    3923 UnitAI.prototype.MoveToTargetRangeExplicit = function(target, min, max)
     3947UnitAI.prototype.MoveToTargetRangeExplicit = function(target, min, max, time)
    39243948{
    39253949    if (!this.CheckTargetVisible(target))
    39263950        return false;
    39273951
     3952    if (!time)
     3953        time = 0;
     3954
    39283955    var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
    3929     return cmpUnitMotion.MoveToTargetRange(target, min, max);
     3956    return cmpUnitMotion.MoveToTargetRange(target, min, max, time);
    39303957};
    39313958
    39323959UnitAI.prototype.MoveToGarrisonRange = function(target)
     
    39403967    var range = cmpGarrisonHolder.GetLoadingRange();
    39413968
    39423969    var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
    3943     return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max);
     3970    return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max, 0);
    39443971};
    39453972
    39463973UnitAI.prototype.CheckPointRangeExplicit = function(x, z, min, max)
     
    39663993 * For ranged attacks, the parabolic formula is used to accout for bigger ranges
    39673994 * when the target is lower, and smaller ranges when the target is higher
    39683995 */
    3969 UnitAI.prototype.CheckTargetAttackRange = function(target, iid, type)
     3996UnitAI.prototype.CheckTargetAttackRange = function(target, type)
    39703997{
    39713998
    39723999    if (type != "Ranged")
    3973         return this.CheckTargetRange(target,iid,type);
     4000        return this.CheckTargetRange(target, IID_Attack, type);
    39744001   
    39754002    var targetCmpPosition = Engine.QueryInterface(target, IID_Position);
    39764003    if (!targetCmpPosition || !targetCmpPosition.IsInWorld())
    39774004        return false;
    39784005
    3979     var cmpRanged = Engine.QueryInterface(this.entity, iid);
     4006    var cmpRanged = Engine.QueryInterface(this.entity, IID_Attack);
    39804007    var range = cmpRanged.GetRange(type);
    39814008
    39824009    var thisCmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     
    42204247        if (cmpUnitAI && cmpAttack)
    42214248        {
    42224249            for each (var type in cmpAttack.GetAttackTypes())
    4223                 if (cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, IID_Attack, type))
     4250                if (cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, type))
    42244251                    return false;
    42254252        }
    42264253    }
     
    42674294        if (cmpUnitAI && cmpAttack)
    42684295        {
    42694296            for each (var type in cmpAttack.GetAttackTypes())
    4270                 if (cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, IID_Attack, type))
     4297                if (cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, type))
    42714298                    return true;
    42724299        }
    42734300    }
  • binaries/data/mods/public/simulation/templates/template_unit_cavalry_melee_swordsman.xml

     
    55      <Hack>10.0</Hack>
    66      <Pierce>0.0</Pierce>
    77      <Crush>0.0</Crush>
    8       <MaxRange>6.0</MaxRange>
     8      <MaxRange>4.0</MaxRange>
    99      <Bonuses>
    1010        <BonusPrimary>
    1111          <Classes>Support</Classes>
  • source/simulation2/components/CCmpObstruction.cpp

     
    442442        return m_Tag;
    443443    }
    444444
     445    virtual bool GetPreviousObstructionSquare(ICmpObstructionManager::ObstructionSquare& out)
     446    {
     447        return GetObstructionSquare(out, true);
     448    }
     449
    445450    virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare& out)
    446451    {
     452        return GetObstructionSquare(out, false);
     453    }
     454
     455    virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare& out, bool previousPosition)
     456    {
    447457        CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
    448458        if (!cmpPosition)
    449459            return false; // error
     
    455465        if (!cmpPosition->IsInWorld())
    456466            return false; // no obstruction square
    457467
    458         CFixedVector2D pos = cmpPosition->GetPosition2D();
     468        CFixedVector2D pos;
     469        if (previousPosition)
     470            pos = cmpPosition->GetPreviousPosition2D();
     471        else
     472            pos = cmpPosition->GetPosition2D();
    459473        if (m_Type == STATIC)
    460474            out = cmpObstructionManager->GetStaticShapeObstruction(pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1);
    461475        else if (m_Type == UNIT)
  • source/simulation2/components/CCmpUnitMotion.cpp

     
    132132    bool m_FormationController;
    133133    fixed m_WalkSpeed, m_OriginalWalkSpeed; // in metres per second
    134134    fixed m_RunSpeed, m_OriginalRunSpeed;
     135    fixed m_TimeToStayInRange;
    135136    ICmpPathfinder::pass_class_t m_PassClass;
    136137    ICmpPathfinder::cost_class_t m_CostClass;
    137138
     
    297298        m_WalkSpeed = m_OriginalWalkSpeed = paramNode.GetChild("WalkSpeed").ToFixed();
    298299        m_Speed = m_WalkSpeed;
    299300        m_CurSpeed = fixed::Zero();
     301        m_TimeToStayInRange = fixed::Zero();
    300302
    301303        if (paramNode.GetChild("Run").IsOk())
    302304            m_RunSpeed = m_OriginalRunSpeed = paramNode.GetChild("Run").GetChild("Speed").ToFixed();
     
    350352
    351353        serialize.NumberFixed_Unbounded("speed", m_Speed);
    352354
     355        serialize.NumberFixed_Unbounded("timeToStayInRange", m_TimeToStayInRange);
     356
    353357        serialize.Bool("moving", m_Moving);
    354358        serialize.Bool("facePointAfterMove", m_FacePointAfterMove);
    355359
     
    388392        {
    389393            if (!m_FormationController)
    390394            {
    391                 fixed dt = static_cast<const CMessageUpdate_MotionUnit&> (msg).turnLength;
     395                fixed dt = static_cast<const CMessageUpdate_MotionFormation&> (msg).turnLength;
    392396                Move(dt);
    393397            }
    394398            break;
     
    470474
    471475    virtual bool MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange);
    472476    virtual bool IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange);
    473     virtual bool MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange);
     477    virtual bool MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t timeToStayInRange);
    474478    virtual bool IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange);
    475479    virtual void MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z);
    476480
     
    995999                        }
    9961000                        else
    9971001                        {
     1002                            // check if target was reached in case of a moving target
     1003                            CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), m_TargetEntity);
     1004                            if
     1005                            (
     1006                                cmpUnitMotion &&
     1007                                cmpUnitMotion->IsMoving() &&
     1008                                !IsInTargetRange(m_TargetEntity, m_TargetMinRange, m_TargetMaxRange) &&
     1009                                MoveToTargetRange(m_TargetEntity, m_TargetMinRange, m_TargetMaxRange, m_TimeToStayInRange)
     1010                            )
     1011                                return;
     1012
    9981013                            // Not in formation, so just finish moving
    999 
    10001014                            StopMoving();
     1015                            m_State = STATE_IDLE;
     1016                            MoveSucceeded();
    10011017
    1002 
    10031018                            if (m_FacePointAfterMove)
    10041019                                FaceTowardsPointFromPos(pos, m_FinalGoal.x, m_FinalGoal.z);
    10051020                            // TODO: if the goal was a square building, we ought to point towards the
     
    13051320
    13061321bool CCmpUnitMotion::MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange, entity_id_t target)
    13071322{
     1323    // TODO also take moving targets into account, like in MoveToTargetRange
    13081324    PROFILE("MoveToPointRange");
    13091325
    13101326    CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
     
    13831399    m_TargetOffset = CFixedVector2D();
    13841400    m_TargetMinRange = minRange;
    13851401    m_TargetMaxRange = maxRange;
     1402    m_TimeToStayInRange = fixed::Zero();
    13861403    m_FinalGoal = goal;
    13871404
    13881405    BeginPathing(pos, goal);
     
    14541471    return (errCircle < errSquare);
    14551472}
    14561473
    1457 bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange)
     1474bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t timeToStayInRange)
    14581475{
    14591476    PROFILE("MoveToTargetRange");
    14601477
     
    15011518
    15021519    if (hasObstruction)
    15031520    {
     1521        CmpPtr<ICmpPosition> cmpTargetPosition(GetSimContext(), target);
     1522        if (!cmpTargetPosition || !cmpTargetPosition->IsInWorld())
     1523            return false;
     1524       
     1525        // if the target is moving, take an overshoot into account
     1526        entity_pos_t overShoot = fixed::Zero();
     1527        CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), target);
     1528        if (cmpUnitMotion)
     1529            overShoot = cmpUnitMotion->GetCurrentSpeed().Multiply(timeToStayInRange);
     1530
     1531        // get the rotation difference, and bring it to the (-pi,pi] interval
     1532        fixed rotationDifference = cmpPosition->GetRotation().Y - cmpTargetPosition->GetRotation().Y;
     1533        fixed Pi = fixed::Pi();
     1534        while (rotationDifference > Pi)
     1535            rotationDifference -= Pi * 2;
     1536        while (rotationDifference <= -Pi)
     1537            rotationDifference += Pi * 2;
     1538
     1539        // take the rotation into account to decide which range to change
     1540        entity_pos_t newMaxRange = maxRange;
     1541        entity_pos_t newMinRange = minRange;
     1542        if (rotationDifference < Pi / 2 && rotationDifference > -Pi / 2)
     1543            newMaxRange -= overShoot;
     1544        else
     1545            newMinRange += overShoot;
     1546        // make sure the ranges are possible, and have the same behaviour
     1547        if (newMaxRange <= minRange && maxRange >= minRange)
     1548            newMaxRange = minRange;
     1549        if (maxRange > fixed::Zero() && newMinRange > newMaxRange)
     1550            newMinRange = newMaxRange;
     1551
    15041552        CFixedVector2D halfSize(obstruction.hw, obstruction.hh);
    15051553        ICmpPathfinder::Goal goal;
    15061554        goal.x = obstruction.x;
     
    15141562
    15151563            // TODO: maybe we should do the ShouldTreatTargetAsCircle thing here?
    15161564
    1517             entity_pos_t goalDistance = minRange + g_GoalDelta;
     1565            entity_pos_t goalDistance = newMinRange + g_GoalDelta;
    15181566
    15191567            goal.type = ICmpPathfinder::Goal::SQUARE;
    15201568            goal.u = obstruction.u;
     
    15541602                    return false;
    15551603                }
    15561604
    1557                 entity_pos_t goalDistance = maxRange - g_GoalDelta;
     1605                entity_pos_t goalDistance = newMaxRange - g_GoalDelta;
    15581606
    15591607                goal.type = ICmpPathfinder::Goal::CIRCLE;
    15601608                goal.hw = circleRadius + goalDistance;
     
    15641612                // The target is large relative to our range, so treat it as a square and
    15651613                // get close enough that the diagonals come within range
    15661614
    1567                 entity_pos_t goalDistance = (maxRange - g_GoalDelta)*2 / 3; // multiply by slightly less than 1/sqrt(2)
     1615                entity_pos_t goalDistance = (newMaxRange - g_GoalDelta)*2 / 3; // multiply by slightly less than 1/sqrt(2)
    15681616
    15691617                goal.type = ICmpPathfinder::Goal::SQUARE;
    15701618                goal.u = obstruction.u;
     
    15801628        m_TargetOffset = CFixedVector2D();
    15811629        m_TargetMinRange = minRange;
    15821630        m_TargetMaxRange = maxRange;
     1631        m_TimeToStayInRange = timeToStayInRange;
    15831632        m_FinalGoal = goal;
    15841633
    15851634        BeginPathing(pos, goal);
     
    16261675        CFixedVector2D halfSize(obstruction.hw, obstruction.hh);
    16271676        entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize);
    16281677
     1678        // compare with previous obstruction
     1679        ICmpObstructionManager::ObstructionSquare previousObstruction;
     1680        cmpObstruction->GetObstructionSquare(previousObstruction);
     1681        entity_pos_t previousDistance = Geometry::DistanceToSquare(pos - CFixedVector2D(previousObstruction.x, previousObstruction.z), obstruction.u, obstruction.v, halfSize);
     1682       
    16291683        // See if we're too close to the target square
    1630         if (distance < minRange)
     1684        if (distance < minRange && previousDistance < minRange)
    16311685            return false;
    16321686
    16331687        // See if we're close enough to the target square
    1634         if (maxRange < entity_pos_t::Zero() || distance <= maxRange)
     1688        if (maxRange < entity_pos_t::Zero() || distance <= maxRange || previousDistance <= maxRange)
    16351689            return true;
    16361690
    16371691        entity_pos_t circleRadius = halfSize.Length();
     
    16451699
    16461700            if (circleDistance <= maxRange)
    16471701                return true;
     1702            // also check circle around previous position
     1703            circleDistance = (pos - CFixedVector2D(previousObstruction.x, previousObstruction.z)).Length() - circleRadius;
     1704
     1705            if (circleDistance <= maxRange)
     1706                return true;
    16481707        }
    16491708
    16501709        return false;
     
    16551714        if (!cmpTargetPosition || !cmpTargetPosition->IsInWorld())
    16561715            return false;
    16571716
    1658         CFixedVector2D targetPos = cmpTargetPosition->GetPosition2D();
     1717        CFixedVector2D targetPos = cmpTargetPosition->GetPreviousPosition2D();
    16591718
    16601719        entity_pos_t distance = (pos - targetPos).Length();
    16611720
    1662         if (minRange <= distance && (maxRange < entity_pos_t::Zero() || distance <= maxRange))
    1663             return true;
    1664 
    1665         return false;
     1721        return minRange <= distance &&
     1722            (maxRange < entity_pos_t::Zero() || distance <= maxRange);
    16661723    }
    16671724}
    16681725
  • source/simulation2/components/ICmpObstruction.h

     
    4747     */
    4848    virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare& out) = 0;
    4949
     50    /**
     51     * Same as the method above, but returns an obstruction shape for the previous turn
     52     */
     53    virtual bool GetPreviousObstructionSquare(ICmpObstructionManager::ObstructionSquare& out) = 0;
     54
    5055    virtual entity_pos_t GetUnitRadius() = 0;
    5156
    5257    virtual bool IsControlPersistent() = 0;
  • source/simulation2/components/ICmpUnitMotion.cpp

     
    2626DEFINE_INTERFACE_METHOD_4("MoveToPointRange", bool, ICmpUnitMotion, MoveToPointRange, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t)
    2727DEFINE_INTERFACE_METHOD_4("IsInPointRange", bool, ICmpUnitMotion, IsInPointRange, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t)
    2828DEFINE_INTERFACE_METHOD_3("IsInTargetRange", bool, ICmpUnitMotion, IsInTargetRange, entity_id_t, entity_pos_t, entity_pos_t)
    29 DEFINE_INTERFACE_METHOD_3("MoveToTargetRange", bool, ICmpUnitMotion, MoveToTargetRange, entity_id_t, entity_pos_t, entity_pos_t)
     29DEFINE_INTERFACE_METHOD_4("MoveToTargetRange", bool, ICmpUnitMotion, MoveToTargetRange, entity_id_t, entity_pos_t, entity_pos_t, entity_pos_t)
    3030DEFINE_INTERFACE_METHOD_3("MoveToFormationOffset", void, ICmpUnitMotion, MoveToFormationOffset, entity_id_t, entity_pos_t, entity_pos_t)
    3131DEFINE_INTERFACE_METHOD_2("FaceTowardsPoint", void, ICmpUnitMotion, FaceTowardsPoint, entity_pos_t, entity_pos_t)
    3232DEFINE_INTERFACE_METHOD_0("StopMoving", void, ICmpUnitMotion, StopMoving)
     
    6060        return m_Script.Call<bool>("IsInTargetRange", target, minRange, maxRange);
    6161    }
    6262
    63     virtual bool MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange)
     63    virtual bool MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t timeToStayInRange)
    6464    {
    65         return m_Script.Call<bool>("MoveToTargetRange", target, minRange, maxRange);
     65        return m_Script.Call<bool>("MoveToTargetRange", target, minRange, maxRange, timeToStayInRange);
    6666    }
    6767
    6868    virtual void MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z)
  • source/simulation2/components/ICmpUnitMotion.h

     
    6666     * and sends another MotionChanged after finishing moving.
    6767     * If maxRange is negative, then the maximum range is treated as infinity.
    6868     */
    69     virtual bool MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) = 0;
     69    virtual bool MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t timeToStayInRange) = 0;
    7070
    7171    /**
    7272     * Join a formation, and move towards a given offset relative to the formation controller entity.