Ticket #1537: chasing.4.diff
File chasing.4.diff, 29.8 KB (added by , 10 years ago) |
---|
-
binaries/data/mods/public/simulation/components/Timer.js
16 16 Timer.prototype.GetTime = function() 17 17 { 18 18 return this.time; 19 } 19 }; 20 20 21 21 /** 22 * Returns the turn length of the latest turn in millisecons 23 */ 24 Timer.prototype.GetTurnLength = function() 25 { 26 return this.turnLength; 27 }; 28 29 /** 22 30 * Create a new timer, which will call the 'funcname' method with arguments (data, lateness) 23 31 * on the 'iid' component of the 'ent' entity, after at least 'time' milliseconds. 24 32 * 'lateness' is how late the timer is executed after the specified time (in milliseconds). … … 60 68 61 69 Timer.prototype.OnUpdate = function(msg) 62 70 { 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; 65 73 66 74 // Collect the timers that need to run 67 75 // (We do this in two stages to avoid deleting from the timer list while -
binaries/data/mods/public/simulation/components/UnitAI.js
171 171 // ignore 172 172 }, 173 173 174 "Update": function(msg) { 175 // ignore 176 }, 177 174 178 // Formation handlers: 175 179 176 180 "FormationLeave": function(msg) { … … 378 382 // We use the distance between the entities to account for ranged attacks 379 383 var distance = DistanceBetweenEntities(this.entity, this.order.data.target) + (+this.template.FleeDistance); 380 384 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)) 382 386 { 383 387 // We've started fleeing from the given target 384 388 if (this.IsAnimal()) … … 413 417 this.order.data.attackType = type; 414 418 415 419 // 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)) 417 421 { 418 422 this.StopMoving(); 419 423 // For packable units within attack range: … … 485 489 } 486 490 487 491 // 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)) 489 493 { 490 494 // We've started walking to the given point 491 495 if (this.IsAnimal()) … … 1590 1594 1591 1595 "MoveCompleted": function() { 1592 1596 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)) 1594 1598 { 1595 1599 // If the unit needs to unpack, do so 1596 1600 if (this.CanUnpack()) … … 1600 1604 } 1601 1605 else 1602 1606 { 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)) 1604 1608 { 1605 1609 this.SetNextState("APPROACHING"); 1606 1610 } … … 1625 1629 "UNPACKING": { 1626 1630 "enter": function() { 1627 1631 // 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)) 1629 1633 { 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)) 1631 1635 this.SetNextState("APPROACHING"); 1632 1636 else 1633 1637 { … … 1690 1694 "Timer": function(msg) { 1691 1695 var target = this.order.data.target; 1692 1696 // 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) 1694 1707 { 1695 // Check we can still reach the target1696 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()) 1697 1710 { 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 } 1712 1719 1713 1714 1720 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 1721 this.lastAttacked = cmpTimer.GetTime() - msg.lateness; 1715 1722 1716 1717 1718 1723 this.FaceTowardsTarget(target); 1724 var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); 1725 cmpAttack.PerformAttack(this.order.data.attackType, target); 1719 1726 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 }, 1727 1733 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; 1737 1752 } 1738 1753 1739 this.oldAttackType = this.order.data.attackType;1740 1754 // Can't reach it, no longer owned by enemy, or it doesn't exist any more - give up 1741 1755 // Except if in WalkAndFight mode where we look for more ennemies around before moving again 1756 this.oldAttackType = attackType; 1742 1757 if (this.FinishOrder()) 1743 1758 { 1744 1759 if (this.orderQueue.length > 0 && this.orderQueue[0].type == "WalkAndFight") … … 1748 1763 1749 1764 // See if we can switch to a new nearby enemy 1750 1765 if (this.FindNewTargets()) 1751 {1752 // Attempt to immediately re-enter the timer function, to avoid wasting the attack.1753 if (this.orderQueue.length > 0 && this.orderQueue[0].data.attackType == this.oldAttackType)1754 this.TimerHandler(msg.data, msg.lateness);1755 1766 return; 1756 }1757 1767 1758 1768 // Return to our original position 1759 1769 if (this.GetStance().respondHoldGround) … … 1760 1770 this.WalkToHeldPosition(); 1761 1771 }, 1762 1772 1763 // TODO: respond to target deaths immediately, rather than waiting1764 // until the next Timer event1765 1766 1773 "Attacked": function(msg) { 1767 1774 if (this.order.data.target != msg.data.attacker) 1768 1775 { … … 3587 3594 UnitFsm.ProcessMessage(this, {"type": "PackFinished", "packed": msg.packed}); 3588 3595 }; 3589 3596 3597 UnitAI.prototype.OnUpdate = function(msg) 3598 { 3599 UnitFsm.ProcessMessage(this, {"type": "Update", "data": msg}); 3600 } 3601 3590 3602 //// Helper functions to be called by the FSM //// 3591 3603 3592 3604 UnitAI.prototype.GetWalkSpeed = function() … … 3856 3868 return false; 3857 3869 3858 3870 var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); 3859 return cmpUnitMotion.MoveToTargetRange(target, 0, 0 );3871 return cmpUnitMotion.MoveToTargetRange(target, 0, 0, 0); 3860 3872 }; 3861 3873 3862 UnitAI.prototype.MoveToTargetRange = function(target, iid, type )3874 UnitAI.prototype.MoveToTargetRange = function(target, iid, type, time) 3863 3875 { 3864 3876 if (!this.CheckTargetVisible(target)) 3865 3877 return false; … … 3868 3880 if (!cmpRanged) 3869 3881 return false; 3870 3882 var range = cmpRanged.GetRange(type); 3883 if (!time) 3884 time = 0; 3871 3885 3872 3886 var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); 3873 return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max );3887 return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max, time); 3874 3888 }; 3875 3889 3876 3890 /** … … 3878 3892 * for melee attacks, this goes straight to the default range checks 3879 3893 * for ranged attacks, the parabolic range is used 3880 3894 */ 3881 UnitAI.prototype.MoveToTargetAttackRange = function(target, iid,type)3895 UnitAI.prototype.MoveToTargetAttackRange = function(target, type) 3882 3896 { 3897 var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); 3898 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 3899 var timers = cmpAttack.GetTimers(type); 3900 // add one turnlength, as even without prepare time, there's one turn between stopping 3901 // to move and starting to attack 3902 var time = timers.prepare/1000 + cmpTimer.GetTurnLength() / 1000; 3883 3903 3884 3904 if(type!= "Ranged") 3885 return this.MoveToTargetRange(target, iid, type);3905 return this.MoveToTargetRange(target, IID_Attack, type, time); 3886 3906 3887 3907 if (!this.CheckTargetVisible(target)) 3888 3908 return false; 3889 3909 3890 var cmpRanged = Engine.QueryInterface(this.entity, iid); 3891 var range = cmpRanged.GetRange(type); 3910 var range = cmpAttack.GetRange(type); 3892 3911 3893 3912 var thisCmpPosition = Engine.QueryInterface(this.entity, IID_Position); 3894 3913 var s = thisCmpPosition.GetPosition(); … … 3913 3932 var guessedMaxRange = (range.max + parabolicMaxRange)/2; 3914 3933 3915 3934 var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); 3916 if (cmpUnitMotion.MoveToTargetRange(target, range.min, guessedMaxRange ))3935 if (cmpUnitMotion.MoveToTargetRange(target, range.min, guessedMaxRange, time)) 3917 3936 return true; 3918 3937 3919 3938 // if that failed, try closer 3920 return cmpUnitMotion.MoveToTargetRange(target, range.min, Math.min(range.max, parabolicMaxRange) );3939 return cmpUnitMotion.MoveToTargetRange(target, range.min, Math.min(range.max, parabolicMaxRange), time); 3921 3940 }; 3922 3941 3923 UnitAI.prototype.MoveToTargetRangeExplicit = function(target, min, max )3942 UnitAI.prototype.MoveToTargetRangeExplicit = function(target, min, max, time) 3924 3943 { 3925 3944 if (!this.CheckTargetVisible(target)) 3926 3945 return false; 3927 3946 3947 if (!time) 3948 time = 0; 3949 3928 3950 var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); 3929 return cmpUnitMotion.MoveToTargetRange(target, min, max );3951 return cmpUnitMotion.MoveToTargetRange(target, min, max, time); 3930 3952 }; 3931 3953 3932 3954 UnitAI.prototype.MoveToGarrisonRange = function(target) … … 3940 3962 var range = cmpGarrisonHolder.GetLoadingRange(); 3941 3963 3942 3964 var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); 3943 return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max );3965 return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max, 0); 3944 3966 }; 3945 3967 3946 3968 UnitAI.prototype.CheckPointRangeExplicit = function(x, z, min, max) … … 3966 3988 * For ranged attacks, the parabolic formula is used to accout for bigger ranges 3967 3989 * when the target is lower, and smaller ranges when the target is higher 3968 3990 */ 3969 UnitAI.prototype.CheckTargetAttackRange = function(target, iid,type)3991 UnitAI.prototype.CheckTargetAttackRange = function(target, type) 3970 3992 { 3971 3993 3972 3994 if (type != "Ranged") 3973 return this.CheckTargetRange(target, iid,type);3995 return this.CheckTargetRange(target, IID_Attack, type); 3974 3996 3975 3997 var targetCmpPosition = Engine.QueryInterface(target, IID_Position); 3976 3998 if (!targetCmpPosition || !targetCmpPosition.IsInWorld()) 3977 3999 return false; 3978 4000 3979 var cmpRanged = Engine.QueryInterface(this.entity, iid);4001 var cmpRanged = Engine.QueryInterface(this.entity, IID_Attack); 3980 4002 var range = cmpRanged.GetRange(type); 3981 4003 3982 4004 var thisCmpPosition = Engine.QueryInterface(this.entity, IID_Position); … … 4220 4242 if (cmpUnitAI && cmpAttack) 4221 4243 { 4222 4244 for each (var type in cmpAttack.GetAttackTypes()) 4223 if (cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, IID_Attack,type))4245 if (cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, type)) 4224 4246 return false; 4225 4247 } 4226 4248 } … … 4267 4289 if (cmpUnitAI && cmpAttack) 4268 4290 { 4269 4291 for each (var type in cmpAttack.GetAttackTypes()) 4270 if (cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, IID_Attack,type))4292 if (cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, type)) 4271 4293 return true; 4272 4294 } 4273 4295 } -
source/simulation2/components/CCmpObstruction.cpp
442 442 return m_Tag; 443 443 } 444 444 445 virtual bool GetPreviousObstructionSquare(ICmpObstructionManager::ObstructionSquare& out) 446 { 447 return GetObstructionSquare(out, true); 448 } 449 445 450 virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare& out) 446 451 { 452 return GetObstructionSquare(out, false); 453 } 454 455 virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare& out, bool previousPosition) 456 { 447 457 CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); 448 458 if (!cmpPosition) 449 459 return false; // error … … 455 465 if (!cmpPosition->IsInWorld()) 456 466 return false; // no obstruction square 457 467 458 CFixedVector2D pos = cmpPosition->GetPosition2D(); 468 CFixedVector2D pos; 469 if (previousPosition) 470 pos = cmpPosition->GetPreviousPosition2D(); 471 else 472 pos = cmpPosition->GetPosition2D(); 459 473 if (m_Type == STATIC) 460 474 out = cmpObstructionManager->GetStaticShapeObstruction(pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1); 461 475 else if (m_Type == UNIT) -
source/simulation2/components/CCmpUnitMotion.cpp
107 107 static const CColor OVERLAY_COLOUR_LONG_PATH(1, 1, 1, 1); 108 108 static const CColor OVERLAY_COLOUR_SHORT_PATH(1, 0, 0, 1); 109 109 110 static const entity_pos_t g_GoalDelta = entity_pos_t::FromInt(TERRAIN_TILE_SIZE)/ 4; // for extending the goal outwards/inwards a little bit110 static const entity_pos_t g_GoalDelta = entity_pos_t::FromInt(TERRAIN_TILE_SIZE)/2; // for extending the goal outwards/inwards a little bit 111 111 112 112 class CCmpUnitMotion : public ICmpUnitMotion 113 113 { … … 132 132 bool m_FormationController; 133 133 fixed m_WalkSpeed, m_OriginalWalkSpeed; // in metres per second 134 134 fixed m_RunSpeed, m_OriginalRunSpeed; 135 fixed m_TimeToStayInRange; 135 136 ICmpPathfinder::pass_class_t m_PassClass; 136 137 ICmpPathfinder::cost_class_t m_CostClass; 137 138 … … 297 298 m_WalkSpeed = m_OriginalWalkSpeed = paramNode.GetChild("WalkSpeed").ToFixed(); 298 299 m_Speed = m_WalkSpeed; 299 300 m_CurSpeed = fixed::Zero(); 301 m_TimeToStayInRange = fixed::Zero(); 300 302 301 303 if (paramNode.GetChild("Run").IsOk()) 302 304 m_RunSpeed = m_OriginalRunSpeed = paramNode.GetChild("Run").GetChild("Speed").ToFixed(); … … 350 352 351 353 serialize.NumberFixed_Unbounded("speed", m_Speed); 352 354 355 serialize.NumberFixed_Unbounded("timeToStayInRange", m_TimeToStayInRange); 356 353 357 serialize.Bool("moving", m_Moving); 354 358 serialize.Bool("facePointAfterMove", m_FacePointAfterMove); 355 359 … … 470 474 471 475 virtual bool MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange); 472 476 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); 474 478 virtual bool IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange); 475 479 virtual void MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z); 476 480 … … 995 999 } 996 1000 else 997 1001 { 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 MoveToTargetRange(m_TargetEntity, m_TargetMinRange, m_TargetMaxRange, m_TimeToStayInRange) 1009 ) 1010 return; 1011 998 1012 // Not in formation, so just finish moving 999 1000 1013 StopMoving(); 1014 m_State = STATE_IDLE; 1015 MoveSucceeded(); 1001 1016 1002 1003 1017 if (m_FacePointAfterMove) 1004 1018 FaceTowardsPointFromPos(pos, m_FinalGoal.x, m_FinalGoal.z); 1005 1019 // TODO: if the goal was a square building, we ought to point towards the … … 1305 1319 1306 1320 bool CCmpUnitMotion::MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange, entity_id_t target) 1307 1321 { 1322 // TODO also take moving targets into account, like in MoveToTargetRange 1308 1323 PROFILE("MoveToPointRange"); 1309 1324 1310 1325 CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); … … 1383 1398 m_TargetOffset = CFixedVector2D(); 1384 1399 m_TargetMinRange = minRange; 1385 1400 m_TargetMaxRange = maxRange; 1401 m_TimeToStayInRange = fixed::Zero(); 1386 1402 m_FinalGoal = goal; 1387 1403 1388 1404 BeginPathing(pos, goal); … … 1454 1470 return (errCircle < errSquare); 1455 1471 } 1456 1472 1457 bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange )1473 bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t timeToStayInRange) 1458 1474 { 1459 1475 PROFILE("MoveToTargetRange"); 1460 1476 … … 1474 1490 if (cmpObstruction) 1475 1491 hasObstruction = cmpObstruction->GetObstructionSquare(obstruction); 1476 1492 1493 CmpPtr<ICmpPosition> cmpTargetPosition(GetSimContext(), target); 1494 if (!cmpTargetPosition || !cmpTargetPosition->IsInWorld()) 1495 return false; 1496 1477 1497 /* 1478 1498 * If we're starting outside the maxRange, we need to move closer in. 1479 1499 * If we're starting inside the minRange, we need to move further out. … … 1501 1521 1502 1522 if (hasObstruction) 1503 1523 { 1524 // if the target is moving, take an overshoot into account 1525 entity_pos_t overShoot = fixed::Zero(); 1526 CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), target); 1527 if (cmpUnitMotion) 1528 overShoot = cmpUnitMotion->GetCurrentSpeed().Multiply(timeToStayInRange); 1529 1530 // get the rotation difference, and bring it to the (-pi,pi] interval 1531 fixed rotationDifference = cmpPosition->GetRotation().Y - cmpTargetPosition->GetRotation().Y; 1532 fixed Pi = fixed::Pi(); 1533 while (rotationDifference > Pi) 1534 rotationDifference -= Pi * 2; 1535 while (rotationDifference <= -Pi) 1536 rotationDifference += Pi * 2; 1537 1538 // take the rotation into account to decide which range to change 1539 entity_pos_t newMaxRange = maxRange; 1540 entity_pos_t newMinRange = minRange; 1541 if (rotationDifference < Pi / 2 && rotationDifference > -Pi / 2) 1542 newMaxRange -= overShoot; 1543 else 1544 newMinRange += overShoot; 1545 // make sure the ranges are possible, and have the same behaviour 1546 if (newMaxRange <= minRange && maxRange >= minRange) 1547 newMaxRange = minRange; 1548 if (maxRange > fixed::Zero() && newMinRange > newMaxRange) 1549 newMinRange = newMaxRange; 1550 1504 1551 CFixedVector2D halfSize(obstruction.hw, obstruction.hh); 1505 1552 ICmpPathfinder::Goal goal; 1506 1553 goal.x = obstruction.x; … … 1508 1555 1509 1556 entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize); 1510 1557 1511 if (distance < minRange) 1558 // compare with previous obstruction 1559 ICmpObstructionManager::ObstructionSquare previousObstruction; 1560 cmpObstruction->GetPreviousObstructionSquare(previousObstruction); 1561 entity_pos_t previousDistance = Geometry::DistanceToSquare(pos - CFixedVector2D(previousObstruction.x, previousObstruction.z), obstruction.u, obstruction.v, halfSize); 1562 1563 if (distance < newMinRange && previousDistance < newMinRange) 1512 1564 { 1513 1565 // Too close to the square - need to move away 1514 1566 1515 1567 // TODO: maybe we should do the ShouldTreatTargetAsCircle thing here? 1516 1568 1517 entity_pos_t goalDistance = minRange + g_GoalDelta;1569 entity_pos_t goalDistance = newMinRange + g_GoalDelta; 1518 1570 1519 1571 goal.type = ICmpPathfinder::Goal::SQUARE; 1520 1572 goal.u = obstruction.u; … … 1523 1575 goal.hw = obstruction.hw + delta; 1524 1576 goal.hh = obstruction.hh + delta; 1525 1577 } 1526 else if ( maxRange < entity_pos_t::Zero() || distance < maxRange)1578 else if (newMaxRange < entity_pos_t::Zero() || distance < newMaxRange || previousDistance < newMaxRange) 1527 1579 { 1528 1580 // We're already in range - no need to move anywhere 1529 1581 if (m_FacePointAfterMove) … … 1537 1589 // Circumscribe the square 1538 1590 entity_pos_t circleRadius = halfSize.Length(); 1539 1591 1540 if (ShouldTreatTargetAsCircle( maxRange, obstruction.hw, obstruction.hh, circleRadius))1592 if (ShouldTreatTargetAsCircle(newMaxRange, obstruction.hw, obstruction.hh, circleRadius)) 1541 1593 { 1542 1594 // The target is small relative to our range, so pretend it's a circle 1543 1595 … … 1546 1598 // check is still valid (though not sufficient) 1547 1599 entity_pos_t circleDistance = (pos - CFixedVector2D(obstruction.x, obstruction.z)).Length() - circleRadius; 1548 1600 1549 if (circleDistance < maxRange)1601 if (circleDistance < newMaxRange) 1550 1602 { 1551 1603 // We're already in range - no need to move anywhere 1552 1604 if (m_FacePointAfterMove) … … 1554 1606 return false; 1555 1607 } 1556 1608 1557 entity_pos_t goalDistance = maxRange - g_GoalDelta;1609 entity_pos_t previousCircleDistance = (pos - CFixedVector2D(previousObstruction.x, previousObstruction.z)).Length() - circleRadius; 1558 1610 1611 if (previousCircleDistance < newMaxRange) 1612 { 1613 // We're already in range - no need to move anywhere 1614 if (m_FacePointAfterMove) 1615 FaceTowardsPointFromPos(pos, goal.x, goal.z); 1616 return false; 1617 } 1618 1619 1620 entity_pos_t goalDistance = newMaxRange - g_GoalDelta; 1621 1559 1622 goal.type = ICmpPathfinder::Goal::CIRCLE; 1560 1623 goal.hw = circleRadius + goalDistance; 1561 1624 } … … 1564 1627 // The target is large relative to our range, so treat it as a square and 1565 1628 // get close enough that the diagonals come within range 1566 1629 1567 entity_pos_t goalDistance = ( maxRange - g_GoalDelta)*2 / 3; // multiply by slightly less than 1/sqrt(2)1630 entity_pos_t goalDistance = (newMaxRange - g_GoalDelta)*2 / 3; // multiply by slightly less than 1/sqrt(2) 1568 1631 1569 1632 goal.type = ICmpPathfinder::Goal::SQUARE; 1570 1633 goal.u = obstruction.u; … … 1580 1643 m_TargetOffset = CFixedVector2D(); 1581 1644 m_TargetMinRange = minRange; 1582 1645 m_TargetMaxRange = maxRange; 1646 m_TimeToStayInRange = timeToStayInRange; 1583 1647 m_FinalGoal = goal; 1584 1648 1585 1649 BeginPathing(pos, goal); … … 1590 1654 { 1591 1655 // The target didn't have an obstruction or obstruction shape, so treat it as a point instead 1592 1656 1593 CmpPtr<ICmpPosition> cmpTargetPosition(GetSimContext(), target);1594 if (!cmpTargetPosition || !cmpTargetPosition->IsInWorld())1595 return false;1596 1597 1657 CFixedVector2D targetPos = cmpTargetPosition->GetPosition2D(); 1598 1658 1599 1659 return MoveToPointRange(targetPos.X, targetPos.Y, minRange, maxRange, target); … … 1626 1686 CFixedVector2D halfSize(obstruction.hw, obstruction.hh); 1627 1687 entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize); 1628 1688 1689 // compare with previous obstruction 1690 ICmpObstructionManager::ObstructionSquare previousObstruction; 1691 cmpObstruction->GetPreviousObstructionSquare(previousObstruction); 1692 entity_pos_t previousDistance = Geometry::DistanceToSquare(pos - CFixedVector2D(previousObstruction.x, previousObstruction.z), obstruction.u, obstruction.v, halfSize); 1693 1629 1694 // See if we're too close to the target square 1630 if (distance < minRange )1695 if (distance < minRange && previousDistance < minRange) 1631 1696 return false; 1632 1697 1633 1698 // See if we're close enough to the target square 1634 if (maxRange < entity_pos_t::Zero() || distance <= maxRange )1699 if (maxRange < entity_pos_t::Zero() || distance <= maxRange || previousDistance <= maxRange) 1635 1700 return true; 1636 1701 1637 1702 entity_pos_t circleRadius = halfSize.Length(); … … 1645 1710 1646 1711 if (circleDistance <= maxRange) 1647 1712 return true; 1713 // also check circle around previous position 1714 circleDistance = (pos - CFixedVector2D(previousObstruction.x, previousObstruction.z)).Length() - circleRadius; 1715 1716 if (circleDistance <= maxRange) 1717 return true; 1648 1718 } 1649 1719 1650 1720 return false; … … 1655 1725 if (!cmpTargetPosition || !cmpTargetPosition->IsInWorld()) 1656 1726 return false; 1657 1727 1658 CFixedVector2D targetPos = cmpTargetPosition->GetP osition2D();1728 CFixedVector2D targetPos = cmpTargetPosition->GetPreviousPosition2D(); 1659 1729 1660 1730 entity_pos_t distance = (pos - targetPos).Length(); 1661 1731 1662 if (minRange <= distance && (maxRange < entity_pos_t::Zero() || distance <= maxRange)) 1663 return true; 1664 1665 return false; 1732 return minRange <= distance && 1733 (maxRange < entity_pos_t::Zero() || distance <= maxRange); 1666 1734 } 1667 1735 } 1668 1736 -
source/simulation2/components/ICmpObstruction.h
47 47 */ 48 48 virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare& out) = 0; 49 49 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 50 55 virtual entity_pos_t GetUnitRadius() = 0; 51 56 52 57 virtual bool IsControlPersistent() = 0; -
source/simulation2/components/ICmpUnitMotion.cpp
26 26 DEFINE_INTERFACE_METHOD_4("MoveToPointRange", bool, ICmpUnitMotion, MoveToPointRange, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t) 27 27 DEFINE_INTERFACE_METHOD_4("IsInPointRange", bool, ICmpUnitMotion, IsInPointRange, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t) 28 28 DEFINE_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)29 DEFINE_INTERFACE_METHOD_4("MoveToTargetRange", bool, ICmpUnitMotion, MoveToTargetRange, entity_id_t, entity_pos_t, entity_pos_t, entity_pos_t) 30 30 DEFINE_INTERFACE_METHOD_3("MoveToFormationOffset", void, ICmpUnitMotion, MoveToFormationOffset, entity_id_t, entity_pos_t, entity_pos_t) 31 31 DEFINE_INTERFACE_METHOD_2("FaceTowardsPoint", void, ICmpUnitMotion, FaceTowardsPoint, entity_pos_t, entity_pos_t) 32 32 DEFINE_INTERFACE_METHOD_0("StopMoving", void, ICmpUnitMotion, StopMoving) … … 60 60 return m_Script.Call<bool>("IsInTargetRange", target, minRange, maxRange); 61 61 } 62 62 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) 64 64 { 65 return m_Script.Call<bool>("MoveToTargetRange", target, minRange, maxRange );65 return m_Script.Call<bool>("MoveToTargetRange", target, minRange, maxRange, timeToStayInRange); 66 66 } 67 67 68 68 virtual void MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z) -
source/simulation2/components/ICmpUnitMotion.h
66 66 * and sends another MotionChanged after finishing moving. 67 67 * If maxRange is negative, then the maximum range is treated as infinity. 68 68 */ 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; 70 70 71 71 /** 72 72 * Join a formation, and move towards a given offset relative to the formation controller entity.