Ticket #1537: chasing.3.diff
File chasing.3.diff, 26.9 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;1754 this.oldAttackType = attackType; 1740 1755 // Can't reach it, no longer owned by enemy, or it doesn't exist any more - give up 1741 1756 // Except if in WalkAndFight mode where we look for more ennemies around before moving again 1742 1757 if (this.FinishOrder()) … … 1760 1775 this.WalkToHeldPosition(); 1761 1776 }, 1762 1777 1763 // TODO: respond to target deaths immediately, rather than waiting1764 // until the next Timer event1765 1766 1778 "Attacked": function(msg) { 1767 1779 if (this.order.data.target != msg.data.attacker) 1768 1780 { … … 3587 3599 UnitFsm.ProcessMessage(this, {"type": "PackFinished", "packed": msg.packed}); 3588 3600 }; 3589 3601 3602 UnitAI.prototype.OnUpdate = function(msg) 3603 { 3604 UnitFsm.ProcessMessage(this, {"type": "Update", "data": msg}); 3605 } 3606 3590 3607 //// Helper functions to be called by the FSM //// 3591 3608 3592 3609 UnitAI.prototype.GetWalkSpeed = function() … … 3856 3873 return false; 3857 3874 3858 3875 var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); 3859 return cmpUnitMotion.MoveToTargetRange(target, 0, 0 );3876 return cmpUnitMotion.MoveToTargetRange(target, 0, 0, 0); 3860 3877 }; 3861 3878 3862 UnitAI.prototype.MoveToTargetRange = function(target, iid, type )3879 UnitAI.prototype.MoveToTargetRange = function(target, iid, type, time) 3863 3880 { 3864 3881 if (!this.CheckTargetVisible(target)) 3865 3882 return false; … … 3868 3885 if (!cmpRanged) 3869 3886 return false; 3870 3887 var range = cmpRanged.GetRange(type); 3888 if (!time) 3889 time = 0; 3871 3890 3872 3891 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); 3874 3893 }; 3875 3894 3876 3895 /** … … 3878 3897 * for melee attacks, this goes straight to the default range checks 3879 3898 * for ranged attacks, the parabolic range is used 3880 3899 */ 3881 UnitAI.prototype.MoveToTargetAttackRange = function(target, iid,type)3900 UnitAI.prototype.MoveToTargetAttackRange = function(target, type) 3882 3901 { 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; 3883 3908 3884 3909 if(type!= "Ranged") 3885 return this.MoveToTargetRange(target, iid, type);3910 return this.MoveToTargetRange(target, IID_Attack, type, time); 3886 3911 3887 3912 if (!this.CheckTargetVisible(target)) 3888 3913 return false; 3889 3914 3890 var cmpRanged = Engine.QueryInterface(this.entity, iid); 3891 var range = cmpRanged.GetRange(type); 3915 var range = cmpAttack.GetRange(type); 3892 3916 3893 3917 var thisCmpPosition = Engine.QueryInterface(this.entity, IID_Position); 3894 3918 var s = thisCmpPosition.GetPosition(); … … 3913 3937 var guessedMaxRange = (range.max + parabolicMaxRange)/2; 3914 3938 3915 3939 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)) 3917 3941 return true; 3918 3942 3919 3943 // 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); 3921 3945 }; 3922 3946 3923 UnitAI.prototype.MoveToTargetRangeExplicit = function(target, min, max )3947 UnitAI.prototype.MoveToTargetRangeExplicit = function(target, min, max, time) 3924 3948 { 3925 3949 if (!this.CheckTargetVisible(target)) 3926 3950 return false; 3927 3951 3952 if (!time) 3953 time = 0; 3954 3928 3955 var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); 3929 return cmpUnitMotion.MoveToTargetRange(target, min, max );3956 return cmpUnitMotion.MoveToTargetRange(target, min, max, time); 3930 3957 }; 3931 3958 3932 3959 UnitAI.prototype.MoveToGarrisonRange = function(target) … … 3940 3967 var range = cmpGarrisonHolder.GetLoadingRange(); 3941 3968 3942 3969 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); 3944 3971 }; 3945 3972 3946 3973 UnitAI.prototype.CheckPointRangeExplicit = function(x, z, min, max) … … 3966 3993 * For ranged attacks, the parabolic formula is used to accout for bigger ranges 3967 3994 * when the target is lower, and smaller ranges when the target is higher 3968 3995 */ 3969 UnitAI.prototype.CheckTargetAttackRange = function(target, iid,type)3996 UnitAI.prototype.CheckTargetAttackRange = function(target, type) 3970 3997 { 3971 3998 3972 3999 if (type != "Ranged") 3973 return this.CheckTargetRange(target, iid,type);4000 return this.CheckTargetRange(target, IID_Attack, type); 3974 4001 3975 4002 var targetCmpPosition = Engine.QueryInterface(target, IID_Position); 3976 4003 if (!targetCmpPosition || !targetCmpPosition.IsInWorld()) 3977 4004 return false; 3978 4005 3979 var cmpRanged = Engine.QueryInterface(this.entity, iid);4006 var cmpRanged = Engine.QueryInterface(this.entity, IID_Attack); 3980 4007 var range = cmpRanged.GetRange(type); 3981 4008 3982 4009 var thisCmpPosition = Engine.QueryInterface(this.entity, IID_Position); … … 4220 4247 if (cmpUnitAI && cmpAttack) 4221 4248 { 4222 4249 for each (var type in cmpAttack.GetAttackTypes()) 4223 if (cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, IID_Attack,type))4250 if (cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, type)) 4224 4251 return false; 4225 4252 } 4226 4253 } … … 4267 4294 if (cmpUnitAI && cmpAttack) 4268 4295 { 4269 4296 for each (var type in cmpAttack.GetAttackTypes()) 4270 if (cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, IID_Attack,type))4297 if (cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, type)) 4271 4298 return true; 4272 4299 } 4273 4300 } -
binaries/data/mods/public/simulation/templates/template_unit_cavalry_melee_swordsman.xml
5 5 <Hack>10.0</Hack> 6 6 <Pierce>0.0</Pierce> 7 7 <Crush>0.0</Crush> 8 <MaxRange> 6.0</MaxRange>8 <MaxRange>4.0</MaxRange> 9 9 <Bonuses> 10 10 <BonusPrimary> 11 11 <Classes>Support</Classes> -
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
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 … … 388 392 { 389 393 if (!m_FormationController) 390 394 { 391 fixed dt = static_cast<const CMessageUpdate_Motion Unit&> (msg).turnLength;395 fixed dt = static_cast<const CMessageUpdate_MotionFormation&> (msg).turnLength; 392 396 Move(dt); 393 397 } 394 398 break; … … 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 !IsInTargetRange(m_TargetEntity, m_TargetMinRange, m_TargetMaxRange) && 1009 MoveToTargetRange(m_TargetEntity, m_TargetMinRange, m_TargetMaxRange, m_TimeToStayInRange) 1010 ) 1011 return; 1012 998 1013 // Not in formation, so just finish moving 999 1000 1014 StopMoving(); 1015 m_State = STATE_IDLE; 1016 MoveSucceeded(); 1001 1017 1002 1003 1018 if (m_FacePointAfterMove) 1004 1019 FaceTowardsPointFromPos(pos, m_FinalGoal.x, m_FinalGoal.z); 1005 1020 // TODO: if the goal was a square building, we ought to point towards the … … 1305 1320 1306 1321 bool CCmpUnitMotion::MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange, entity_id_t target) 1307 1322 { 1323 // TODO also take moving targets into account, like in MoveToTargetRange 1308 1324 PROFILE("MoveToPointRange"); 1309 1325 1310 1326 CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); … … 1383 1399 m_TargetOffset = CFixedVector2D(); 1384 1400 m_TargetMinRange = minRange; 1385 1401 m_TargetMaxRange = maxRange; 1402 m_TimeToStayInRange = fixed::Zero(); 1386 1403 m_FinalGoal = goal; 1387 1404 1388 1405 BeginPathing(pos, goal); … … 1454 1471 return (errCircle < errSquare); 1455 1472 } 1456 1473 1457 bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange )1474 bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t timeToStayInRange) 1458 1475 { 1459 1476 PROFILE("MoveToTargetRange"); 1460 1477 … … 1501 1518 1502 1519 if (hasObstruction) 1503 1520 { 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 1504 1552 CFixedVector2D halfSize(obstruction.hw, obstruction.hh); 1505 1553 ICmpPathfinder::Goal goal; 1506 1554 goal.x = obstruction.x; … … 1514 1562 1515 1563 // TODO: maybe we should do the ShouldTreatTargetAsCircle thing here? 1516 1564 1517 entity_pos_t goalDistance = minRange + g_GoalDelta;1565 entity_pos_t goalDistance = newMinRange + g_GoalDelta; 1518 1566 1519 1567 goal.type = ICmpPathfinder::Goal::SQUARE; 1520 1568 goal.u = obstruction.u; … … 1554 1602 return false; 1555 1603 } 1556 1604 1557 entity_pos_t goalDistance = maxRange - g_GoalDelta;1605 entity_pos_t goalDistance = newMaxRange - g_GoalDelta; 1558 1606 1559 1607 goal.type = ICmpPathfinder::Goal::CIRCLE; 1560 1608 goal.hw = circleRadius + goalDistance; … … 1564 1612 // The target is large relative to our range, so treat it as a square and 1565 1613 // get close enough that the diagonals come within range 1566 1614 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) 1568 1616 1569 1617 goal.type = ICmpPathfinder::Goal::SQUARE; 1570 1618 goal.u = obstruction.u; … … 1580 1628 m_TargetOffset = CFixedVector2D(); 1581 1629 m_TargetMinRange = minRange; 1582 1630 m_TargetMaxRange = maxRange; 1631 m_TimeToStayInRange = timeToStayInRange; 1583 1632 m_FinalGoal = goal; 1584 1633 1585 1634 BeginPathing(pos, goal); … … 1626 1675 CFixedVector2D halfSize(obstruction.hw, obstruction.hh); 1627 1676 entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize); 1628 1677 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 1629 1683 // See if we're too close to the target square 1630 if (distance < minRange )1684 if (distance < minRange && previousDistance < minRange) 1631 1685 return false; 1632 1686 1633 1687 // 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) 1635 1689 return true; 1636 1690 1637 1691 entity_pos_t circleRadius = halfSize.Length(); … … 1645 1699 1646 1700 if (circleDistance <= maxRange) 1647 1701 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; 1648 1707 } 1649 1708 1650 1709 return false; … … 1655 1714 if (!cmpTargetPosition || !cmpTargetPosition->IsInWorld()) 1656 1715 return false; 1657 1716 1658 CFixedVector2D targetPos = cmpTargetPosition->GetP osition2D();1717 CFixedVector2D targetPos = cmpTargetPosition->GetPreviousPosition2D(); 1659 1718 1660 1719 entity_pos_t distance = (pos - targetPos).Length(); 1661 1720 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); 1666 1723 } 1667 1724 } 1668 1725 -
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.