Line data Source code
1 : function PositionHelper() {} 2 : 3 : /** 4 : * @param {number} firstEntity - The entityID of an entity. 5 : * @param {number} secondEntity - The entityID of an entity. 6 : * 7 : * @return {number} - The horizontal distance between the two given entities. Returns 8 : * infinity when the distance cannot be calculated. 9 : */ 10 6 : PositionHelper.prototype.DistanceBetweenEntities = function(firstEntity, secondEntity) 11 : { 12 5 : let cmpFirstPosition = Engine.QueryInterface(firstEntity, IID_Position); 13 5 : if (!cmpFirstPosition || !cmpFirstPosition.IsInWorld()) 14 1 : return Infinity; 15 : 16 4 : let cmpSecondPosition = Engine.QueryInterface(secondEntity, IID_Position); 17 4 : if (!cmpSecondPosition || !cmpSecondPosition.IsInWorld()) 18 0 : return Infinity; 19 : 20 4 : return cmpFirstPosition.GetPosition2D().distanceTo(cmpSecondPosition.GetPosition2D()); 21 : }; 22 : 23 : /** 24 : * @param {Vector2D} origin - The point to check around. 25 : * @param {number} radius - The radius around the point to check. 26 : * @param {number[]} players - The players of which we need to check entities. 27 : * @param {number} iid - Interface IID that returned entities must implement. Defaults to none. 28 : * 29 : * @return {number[]} The id's of the entities in range of the given point. 30 : */ 31 6 : PositionHelper.prototype.EntitiesNearPoint = function(origin, radius, players, iid = 0) 32 : { 33 18 : if (!origin || !radius || !players || !players.length) 34 0 : return []; 35 : 36 18 : let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 37 18 : return cmpRangeManager.ExecuteQueryAroundPos(origin, 0, radius, players, iid, true); 38 : }; 39 : 40 : /** 41 : * Gives the position of the given entity, taking the lateness into account. 42 : * Note that vertical movement is ignored. 43 : * 44 : * @param {number} ent - Entity id of the entity we are finding the location for. 45 : * @param {number} lateness - The time passed since the expected time to fire the function. 46 : * 47 : * @return {Vector3D} The interpolated location of the entity. 48 : */ 49 6 : PositionHelper.prototype.InterpolatedLocation = function(ent, lateness) 50 : { 51 33 : let cmpTargetPosition = Engine.QueryInterface(ent, IID_Position); 52 33 : if (!cmpTargetPosition || !cmpTargetPosition.IsInWorld()) // TODO: handle dead target properly 53 2 : return undefined; 54 31 : let curPos = cmpTargetPosition.GetPosition(); 55 31 : let prevPos = cmpTargetPosition.GetPreviousPosition(); 56 31 : let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 57 31 : let turnLength = cmpTimer.GetLatestTurnLength(); 58 31 : return new Vector3D( 59 : (curPos.x * (turnLength - lateness) + prevPos.x * lateness) / turnLength, 60 : 0, 61 : (curPos.z * (turnLength - lateness) + prevPos.z * lateness) / turnLength 62 : ); 63 : }; 64 : 65 : /** 66 : * Test if a point is inside an entity's footprint. 67 : * Note that edges may be not included for square entities due to rounding. 68 : * 69 : * @param {number} ent - Id of the entity we are checking with. 70 : * @param {Vector3D} point - The point we are checking with. 71 : * @param {number} lateness - The time passed since the expected time to fire the function. 72 : * 73 : * @return {boolean} True if the point is inside of the entity's footprint. 74 : */ 75 6 : PositionHelper.prototype.TestCollision = function(ent, point, lateness) 76 : { 77 46 : let targetPosition = this.InterpolatedLocation(ent, lateness); 78 46 : if (!targetPosition) 79 1 : return false; 80 : 81 45 : let cmpFootprint = Engine.QueryInterface(ent, IID_Footprint); 82 45 : if (!cmpFootprint) 83 1 : return false; 84 : 85 44 : let targetShape = cmpFootprint.GetShape(); 86 44 : if (!targetShape) 87 1 : return false; 88 : 89 43 : if (targetShape.type == "circle") 90 27 : return targetPosition.horizDistanceToSquared(point) < targetShape.radius * targetShape.radius; 91 : 92 16 : if (targetShape.type == "square") 93 : { 94 16 : let angle = Engine.QueryInterface(ent, IID_Position).GetRotation().y; 95 16 : let distance = Vector2D.from3D(Vector3D.sub(point, targetPosition)).rotate(-angle); 96 16 : return Math.abs(distance.x) < targetShape.width / 2 && Math.abs(distance.y) < targetShape.depth / 2; 97 : } 98 : 99 0 : warn("TestCollision called with an invalid footprint shape: " + targetShape.type + "."); 100 0 : return false; 101 : }; 102 : 103 : /** 104 : * Get the predicted time of collision between a projectile (or a chaser) 105 : * and its target, assuming they both move in straight line at a constant speed. 106 : * Vertical component of movement is ignored. 107 : * 108 : * @param {Vector3D} firstPosition - The 3D position of the projectile (or chaser). 109 : * @param {number} selfSpeed - The horizontal speed of the projectile (or chaser). 110 : * @param {Vector3D} targetPosition - The 3D position of the target. 111 : * @param {Vector3D} targetVelocity - The 3D velocity vector of the target. 112 : * 113 : * @return {number|boolean} - The time to collision or false if the collision will not happen. 114 : */ 115 6 : PositionHelper.prototype.PredictTimeToTarget = function(firstPosition, selfSpeed, targetPosition, targetVelocity) 116 : { 117 18254 : let relativePosition = new Vector3D.sub(targetPosition, firstPosition); 118 18254 : let a = targetVelocity.x * targetVelocity.x + targetVelocity.z * targetVelocity.z - selfSpeed * selfSpeed; 119 18254 : let b = relativePosition.x * targetVelocity.x + relativePosition.z * targetVelocity.z; 120 18254 : let c = relativePosition.x * relativePosition.x + relativePosition.z * relativePosition.z; 121 : 122 : // The predicted time to reach the target is the smallest non negative solution 123 : // (when it exists) of the equation a t^2 + 2 b t + c = 0. 124 : // Using c>=0, we can straightly compute the right solution. 125 : 126 18254 : if (c == 0) 127 756 : return 0; 128 : 129 17498 : let disc = b * b - a * c; 130 17498 : if (a < 0 || b < 0 && disc >= 0) 131 9813 : return c / (Math.sqrt(disc) - b); 132 : 133 7685 : return false; 134 : }; 135 : 136 : /** 137 : * @param {number} target - EntityID to find the spawn position for. 138 : * @param {number} entity - EntityID to find the spawn position for. 139 : * @param {boolean} forced - Optionally whether the spawning is forced. 140 : * @return {Vector3D} - An appropriate spawning position. 141 : */ 142 6 : PositionHelper.prototype.GetSpawnPosition = function(target, entity, forced) 143 : { 144 11 : let cmpFootprint = Engine.QueryInterface(target, IID_Footprint); 145 11 : let cmpHealth = Engine.QueryInterface(target, IID_Health); 146 11 : let cmpIdentity = Engine.QueryInterface(target, IID_Identity); 147 : 148 11 : if (!cmpFootprint) 149 0 : return null; 150 : 151 : // If the spawner is a sinking ship, restrict the location to the intersection of both passabilities. 152 : // TODO: should use passability classes to be more generic. 153 : let pos; 154 11 : if ((!cmpHealth || cmpHealth.GetHitpoints() == 0) && cmpIdentity && cmpIdentity.HasClass("Ship")) 155 0 : pos = cmpFootprint.PickSpawnPointBothPass(entity); 156 : else 157 11 : pos = cmpFootprint.PickSpawnPoint(entity); 158 : 159 11 : if (pos.y < 0) 160 : { 161 0 : if (!forced) 162 0 : return null; 163 : 164 : // If ejection is forced, we need to continue, so use center of the entity. 165 0 : let cmpPosition = Engine.QueryInterface(target, IID_Position); 166 0 : pos = cmpPosition.GetPosition(); 167 : } 168 11 : return pos; 169 : }; 170 : 171 6 : Engine.RegisterGlobal("PositionHelper", new PositionHelper());