/// <summary> /// Checks that these coordinates are within a certain distance without any /// entity that matches the collision mask obstructing them. /// If the <paramref name="range"/> is zero or negative, /// this method will only check if nothing obstructs the two sets of coordinates.. /// </summary> /// <param name="coords">Set of coordinates to use.</param> /// <param name="otherCoords">Other set of coordinates to use.</param> /// <param name="range">maximum distance between the two sets of coordinates.</param> /// <param name="collisionMask">the mask to check for collisions</param> /// <param name="predicate">.</param> /// <param name="mapManager">Map manager containing the two GridIds.</param> /// <param name="insideBlockerValid">if coordinates inside obstructions count as obstructed or not</param> /// <returns>True if the two points are within a given range without being obstructed.</returns> public bool InRangeUnobstructed(MapCoordinates coords, Vector2 otherCoords, float range = InteractionRange, int collisionMask = (int)CollisionGroup.Impassable, Func <IEntity, bool> predicate = null, bool insideBlockerValid = false) { var dir = otherCoords - coords.Position; if (dir.LengthSquared.Equals(0f)) { return(true); } if (range > 0f && !(dir.LengthSquared <= range * range)) { return(false); } var ray = new CollisionRay(coords.Position, dir.Normalized, collisionMask); var rayResults = _physicsManager.IntersectRayWithPredicate(coords.MapId, ray, dir.Length, predicate, true); if (!rayResults.DidHitObject || (insideBlockerValid && rayResults.DidHitObject && (rayResults.HitPos - otherCoords).Length < 1f)) { if (_mapManager.TryFindGridAt(coords, out var mapGrid) && mapGrid != null) { var srcPos = mapGrid.MapToGrid(coords); var destPos = new GridCoordinates(otherCoords, mapGrid); if (srcPos.InRange(_mapManager, destPos, range)) { return(true); } } } return(false); }
// Just do a simple range check, then chuck the ray out. If we get bigger than 1 tile mobs may need to adjust this public static bool InLineOfSight(IEntity owner, IEntity target) { var range = 50.0f; if (owner.Transform.GridID != target.Transform.GridID) { return(false); } if (owner.TryGetComponent(out AiControllerComponent controller)) { var targetRange = (target.Transform.GridPosition.Position - owner.Transform.GridPosition.Position).Length; if (targetRange > controller.VisionRadius) { return(false); } range = controller.VisionRadius; } var angle = new Angle(target.Transform.GridPosition.Position - owner.Transform.GridPosition.Position); var ray = new CollisionRay( owner.Transform.GridPosition.Position, angle.ToVec(), (int)(CollisionGroup.Opaque | CollisionGroup.Impassable | CollisionGroup.MobImpassable)); var rayCastResults = IoCManager.Resolve <IPhysicsManager>().IntersectRay(owner.Transform.MapID, ray, range, owner).ToList(); return(rayCastResults.Count > 0 && rayCastResults[0].HitEntity == target); }
public void RayCast() { // Arrange var box = new Box2(5, -5, 10, 6); var ray = new CollisionRay(new Vector2(0, 1), Vector2.UnitX, 1); var manager = new PhysicsManager(); var mock = new Mock <IPhysBody>(); mock.Setup(foo => foo.WorldAABB).Returns(box); mock.Setup(foo => foo.Entity).Returns(new Entity(new ServerEntityManager(), EntityUid.FirstUid)); // requires IPhysBody not have null owner mock.Setup(foo => foo.CanCollide).Returns(true); mock.Setup(foo => foo.CollisionLayer).Returns(1); mock.Setup(foo => foo.CollisionMask).Returns(1); manager.AddBody(mock.Object); // Act var results = manager.IntersectRay(new MapId(0), ray).ToList(); Assert.That(results.Count, Is.EqualTo(1)); var result = results.First(); // Assert Assert.That(result.Distance, Is.EqualTo(5)); Assert.That(result.HitPos.X, Is.EqualTo(5)); Assert.That(result.HitPos.Y, Is.EqualTo(1)); }
private void Start() { rb = GetComponent <Rigidbody2D>(); ray = GetComponent <CollisionRay>(); staminaBar = GetComponent <StaminaBar>(); physics = GetComponent <ModifyPhysics>(); }
public void RayCast() { // Arrange var box = new Box2(5, -5, 10, 6); var ray = new CollisionRay(new Vector2(0, 1), Vector2.UnitX, 1); var manager = new PhysicsManager(); var mock = new Mock <IPhysBody>(); mock.Setup(foo => foo.WorldAABB).Returns(box); mock.Setup(foo => foo.Owner).Returns(new Entity()); // requires IPhysBody not have null owner mock.Setup(foo => foo.CollisionEnabled).Returns(true); mock.Setup(foo => foo.CollisionLayer).Returns(1); mock.Setup(foo => foo.CollisionMask).Returns(1); mock.Setup(foo => foo.IsHardCollidable).Returns(true); manager.AddBody(mock.Object); // Act var result = manager.IntersectRay(new MapId(0), ray); // Assert Assert.That(result.DidHitObject, Is.True); Assert.That(result.Distance, Is.EqualTo(5)); Assert.That(result.HitPos.X, Is.EqualTo(5)); Assert.That(result.HitPos.Y, Is.EqualTo(1)); }
private void Start() { movement = GetComponent <MovementController>(); ray = GetComponent <CollisionRay>(); rb = GetComponent <Rigidbody2D>(); anim = GetComponent <Animator>(); }
/// <summary> /// Fires hitscan entities and then displays their effects /// </summary> private void FireHitscan(IEntity shooter, HitscanComponent hitscan, Angle angle) { var ray = new CollisionRay(Owner.Transform.Coordinates.ToMapPos(Owner.EntityManager), angle.ToVec(), (int)hitscan.CollisionMask); var physicsManager = EntitySystem.Get <SharedBroadPhaseSystem>(); var rayCastResults = physicsManager.IntersectRay(Owner.Transform.MapID, ray, hitscan.MaxLength, shooter, false).ToList(); if (rayCastResults.Count >= 1) { var result = rayCastResults[0]; var distance = result.Distance; hitscan.FireEffects(shooter, distance, angle, result.HitEntity); if (!result.HitEntity.TryGetComponent(out IDamageableComponent? damageable)) { return; } damageable.ChangeDamage(hitscan.DamageType, (int)Math.Round(hitscan.Damage, MidpointRounding.AwayFromZero), false, Owner); //I used Math.Round over Convert.toInt32, as toInt32 always rounds to //even numbers if halfway between two numbers, rather than rounding to nearest } else { hitscan.FireEffects(shooter, hitscan.MaxLength, angle); } }
private void Start() { staminaBar = GetComponent <StaminaBar>(); rb = GetComponent <Rigidbody2D>(); ray = GetComponent <CollisionRay>(); fsm = GetComponent <FiniteStateMachine>(); }
/// <summary> /// Checks that these coordinates are within a certain distance without any /// entity that matches the collision mask obstructing them. /// If the <paramref name="range"/> is zero or negative, /// this method will only check if nothing obstructs the two sets /// of coordinates. /// </summary> /// <param name="origin">Set of coordinates to use.</param> /// <param name="other">Other set of coordinates to use.</param> /// <param name="range"> /// Maximum distance between the two sets of coordinates. /// </param> /// <param name="collisionMask">The mask to check for collisions.</param> /// <param name="predicate"> /// A predicate to check whether to ignore an entity or not. /// If it returns true, it will be ignored. /// </param> /// <param name="ignoreInsideBlocker"> /// If true and <see cref="origin"/> or <see cref="other"/> are inside /// the obstruction, ignores the obstruction and considers the interaction /// unobstructed. /// Therefore, setting this to true makes this check more permissive, /// such as allowing an interaction to occur inside something impassable /// (like a wall). The default, false, makes the check more restrictive. /// </param> /// <returns> /// True if the two points are within a given range without being obstructed. /// </returns> public bool InRangeUnobstructed( MapCoordinates origin, MapCoordinates other, float range = InteractionRange, CollisionGroup collisionMask = CollisionGroup.Impassable, Ignored?predicate = null, bool ignoreInsideBlocker = false) { if (!origin.InRange(other, range)) { return(false); } var dir = other.Position - origin.Position; if (dir.LengthSquared.Equals(0f)) { return(true); } if (range > 0f && !(dir.LengthSquared <= range * range)) { return(false); } predicate ??= _ => false; var ray = new CollisionRay(origin.Position, dir.Normalized, (int)collisionMask); var rayResults = _sharedBroadphaseSystem.IntersectRayWithPredicate(origin.MapId, ray, dir.Length, predicate.Invoke, false).ToList(); if (rayResults.Count == 0) { return(true); } // TODO: Wot? This should just be in the predicate. if (!ignoreInsideBlocker) { return(false); } foreach (var result in rayResults) { if (!result.HitEntity.TryGetComponent(out IPhysBody? p)) { continue; } var bBox = p.GetWorldAABB(); if (bBox.Contains(origin.Position) || bBox.Contains(other.Position)) { continue; } return(false); } return(true); }
private void IdleState() { if (!ActionBlockerSystem.CanMove(SelfEntity)) { DisabledPositiveEdge(); return; } if (_timeMan.CurTime < _startStateTime + IdleTimeSpan) { return; } var entWorldPos = SelfEntity.Transform.WorldPosition; if (SelfEntity.TryGetComponent <CollidableComponent>(out var bounds)) { entWorldPos = ((IPhysBody)bounds).WorldAABB.Center; } var rngState = GenSeed(); for (var i = 0; i < 3; i++) // you get 3 chances to find a place to walk { var dir = new Vector2(Random01(ref rngState) * 2 - 1, Random01(ref rngState) * 2 - 1).Normalized; var ray = new CollisionRay(entWorldPos, dir, (int)CollisionGroup.Impassable); var rayResults = _physMan.IntersectRay(SelfEntity.Transform.MapID, ray, MaxWalkDistance, SelfEntity).ToList(); if (rayResults.Count == 1) { var rayResult = rayResults[0]; if (rayResult.Distance > 1) // hit an impassable object { // set the new position back from the wall a bit _walkTargetPos = entWorldPos + dir * (rayResult.Distance - 0.5f); WalkingPositiveEdge(); return; } } else // hit nothing (path clear) { _walkTargetPos = dir * MaxWalkDistance; WalkingPositiveEdge(); return; } } // can't find clear spot, do nothing, sleep longer _startStateTime = _timeMan.CurTime; }
public override void Update(Entity entity) { if (CollisionRay.IsEntityInSight(entity as Enemy, GameWorld.GetPlayers()[0])) { _jumpTimer.ChangeWaitTime(TimeBetweenJumps_ACTIVE); _canSeePlayer = true; } else { _jumpTimer.ChangeWaitTime(TimeBetweenJumps_IDLE); _canSeePlayer = false; } base.Update(entity); }
private void Fire(IEntity user, GridCoordinates clickLocation) { if (capacitorComponent.Charge < _lowerChargeLimit) {//If capacitor has less energy than the lower limit, do nothing return; } float energyModifier = capacitorComponent.GetChargeFrom(_baseFireCost) / _baseFireCost; var userPosition = user.Transform.WorldPosition; //Remember world positions are ephemeral and can only be used instantaneously var angle = new Angle(clickLocation.Position - userPosition); var ray = new CollisionRay(userPosition, angle.ToVec(), (int)(CollisionGroup.Impassable | CollisionGroup.MobImpassable)); var rayCastResults = IoCManager.Resolve <IPhysicsManager>().IntersectRay(user.Transform.MapID, ray, MaxLength, user); Hit(rayCastResults, energyModifier); AfterEffects(user, rayCastResults, angle, energyModifier); }
public bool Condition(EntityUid user, EntityCoordinates location, Direction direction) { var entManager = IoCManager.Resolve <IEntityManager>(); // get blueprint and user position var userWorldPosition = entManager.GetComponent <TransformComponent>(user).WorldPosition; var objWorldPosition = location.ToMap(entManager).Position; // find direction from user to blueprint var userToObject = (objWorldPosition - userWorldPosition); // get direction of the grid being placed on as an offset. var gridRotation = entManager.GetComponent <TransformComponent>(location.EntityId).WorldRotation; var directionWithOffset = gridRotation.RotateVec(direction.ToVec()); // dot product will be positive if user direction and blueprint are co-directed var dotProd = Vector2.Dot(directionWithOffset.Normalized, userToObject.Normalized); if (dotProd > 0) { return(false); } // now we need to check that user actually tries to build wallmount on a wall var physics = EntitySystem.Get <SharedPhysicsSystem>(); var rUserToObj = new CollisionRay(userWorldPosition, userToObject.Normalized, (int)CollisionGroup.Impassable); var length = userToObject.Length; var tagSystem = EntitySystem.Get <TagSystem>(); var userToObjRaycastResults = physics.IntersectRayWithPredicate(entManager.GetComponent <TransformComponent>(user).MapID, rUserToObj, maxLength: length, predicate: (e) => !tagSystem.HasTag(e, "Wall")); var targetWall = userToObjRaycastResults.FirstOrNull(); if (targetWall == null) { return(false); } // get this wall entity // check that we didn't try to build wallmount that facing another adjacent wall var rAdjWall = new CollisionRay(objWorldPosition, directionWithOffset.Normalized, (int)CollisionGroup.Impassable); var adjWallRaycastResults = physics.IntersectRayWithPredicate(entManager.GetComponent <TransformComponent>(user).MapID, rAdjWall, maxLength: 0.5f, predicate: (e) => e == targetWall.Value.HitEntity || !tagSystem.HasTag(e, "Wall")); return(!adjWallRaycastResults.Any()); }
private void UpdatePanelCoverage(SolarPanelComponent panel) { IEntity entity = panel.Owner; // So apparently, and yes, I *did* only find this out later, // this is just a really fancy way of saying "Lambert's law of cosines". // ...I still think this explaination makes more sense. // In the 'sunRelative' coordinate system: // the sun is considered to be an infinite distance directly up. // this is the rotation of the panel relative to that. // directly upwards (theta = 0) = coverage 1 // left/right 90 degrees (abs(theta) = (pi / 2)) = coverage 0 // directly downwards (abs(theta) = pi) = coverage -1 // as TowardsSun + = CCW, // panelRelativeToSun should - = CW var panelRelativeToSun = entity.Transform.WorldRotation - TowardsSun; // essentially, given cos = X & sin = Y & Y is 'downwards', // then for the first 90 degrees of rotation in either direction, // this plots the lower-right quadrant of a circle. // now basically assume a line going from the negated X/Y to there, // and that's the hypothetical solar panel. // // since, again, the sun is considered to be an infinite distance upwards, // this essentially means Cos(panelRelativeToSun) is half of the cross-section, // and since the full cross-section has a max of 2, effectively-halving it is fine. // // as for when it goes negative, it only does that when (abs(theta) > pi) // and that's expected behavior. float coverage = (float)Math.Max(0, Math.Cos(panelRelativeToSun)); if (coverage > 0) { // Determine if the solar panel is occluded, and zero out coverage if so. // FIXME: The "Opaque" collision group doesn't seem to work right now. var ray = new CollisionRay(entity.Transform.WorldPosition, TowardsSun.ToVec(), (int)CollisionGroup.Opaque); var rayCastResults = EntitySystem.Get <SharedBroadPhaseSystem>().IntersectRay(entity.Transform.MapID, ray, SunOcclusionCheckDistance, entity); if (rayCastResults.Any()) { coverage = 0; } } // Total coverage calculated; apply it to the panel. panel.Coverage = coverage; }
/// <summary> /// Checks that these coordinates are within a certain distance without any /// entity that matches the collision mask obstructing them. /// If the <paramref name="range"/> is zero or negative, /// this method will only check if nothing obstructs the two sets /// of coordinates. /// </summary> /// <param name="origin">Set of coordinates to use.</param> /// <param name="other">Other set of coordinates to use.</param> /// <param name="range"> /// Maximum distance between the two sets of coordinates. /// </param> /// <param name="collisionMask">The mask to check for collisions.</param> /// <param name="predicate"> /// A predicate to check whether to ignore an entity or not. /// If it returns true, it will be ignored. /// </param> /// <param name="ignoreInsideBlocker"> /// If true and <see cref="origin"/> or <see cref="other"/> are inside /// the obstruction, ignores the obstruction and considers the interaction /// unobstructed. /// Therefore, setting this to true makes this check more permissive, /// such as allowing an interaction to occur inside something impassable /// (like a wall). The default, false, makes the check more restrictive. /// </param> /// <returns> /// True if the two points are within a given range without being obstructed. /// </returns> public bool InRangeUnobstructed( MapCoordinates origin, MapCoordinates other, float range = InteractionRange, CollisionGroup collisionMask = CollisionGroup.Impassable, Ignored predicate = null, bool ignoreInsideBlocker = false) { if (!origin.InRange(other, range)) { return(false); } var dir = other.Position - origin.Position; if (dir.LengthSquared.Equals(0f)) { return(true); } if (range > 0f && !(dir.LengthSquared <= range * range)) { return(false); } predicate ??= _ => false; var ray = new CollisionRay(origin.Position, dir.Normalized, (int)collisionMask); var rayResults = _physicsManager.IntersectRayWithPredicate(origin.MapId, ray, dir.Length, predicate.Invoke, false).ToList(); if (rayResults.Count == 0) { return(true); } if (!ignoreInsideBlocker) { return(false); } if (rayResults.Count <= 0) { return(false); } return((rayResults[0].HitPos - other.Position).Length < 1f); }
private bool HandleDrop(ICommonSession session, GridCoordinates coords, EntityUid uid) { var ent = ((IPlayerSession)session).AttachedEntity; if (ent == null || !ent.IsValid()) { return(false); } if (!ent.TryGetComponent(out HandsComponent handsComp)) { return(false); } if (handsComp.GetActiveHand == null) { return(false); } var dir = (coords.Position - ent.Transform.GridPosition.Position); var ray = new CollisionRay(ent.Transform.GridPosition.Position, dir.Normalized, (int)CollisionGroup.Impassable); var rayResults = IoCManager.Resolve <IPhysicsManager>().IntersectRay(ent.Transform.MapID, ray, dir.Length, ent); if (!rayResults.DidHitObject) { if (coords.InRange(_mapManager, ent.Transform.GridPosition, InteractionSystem.InteractionRange)) { handsComp.Drop(handsComp.ActiveIndex, coords); } else { var entCoords = ent.Transform.GridPosition.Position; var entToDesiredDropCoords = coords.Position - entCoords; var clampedDropCoords = ((entToDesiredDropCoords.Normalized * InteractionSystem.InteractionRange) + entCoords); handsComp.Drop(handsComp.ActiveIndex, new GridCoordinates(clampedDropCoords, coords.GridID)); } } else { handsComp.Drop(handsComp.ActiveIndex, ent.Transform.GridPosition); } return(true); }
/// <summary> /// Checks that these coordinates are within a certain distance without any /// entity that matches the collision mask obstructing them. /// If the <paramref name="range"/> is zero or negative, /// this method will only check if nothing obstructs the two sets of coordinates.. /// </summary> /// <param name="mapManager">Map manager containing the two GridIds.</param> /// <param name="coords">Set of coordinates to use.</param> /// <param name="otherCoords">Other set of coordinates to use.</param> /// <param name="range">maximum distance between the two sets of coordinates.</param> /// <param name="collisionMask">the mask to check for collisions</param> /// <param name="ignoredEnt">the entity to be ignored when checking for collisions.</param> /// <returns>True if the two points are within a given range without being obstructed.</returns> public bool InRangeUnobstructed(GridCoordinates coords, GridCoordinates otherCoords, float range = InteractionRange, int collisionMask = (int)CollisionGroup.Impassable, IEntity ignoredEnt = null) { if (range > 0f && !coords.InRange(_mapManager, otherCoords, range)) { return(false); } var dir = (otherCoords.Position - coords.Position); if (!(dir.Length > 0f)) { return(true); } var ray = new CollisionRay(coords.Position, dir.Normalized, collisionMask); var rayResults = _physicsManager.IntersectRay(_mapManager.GetGrid(coords.GridID).ParentMapId, ray, dir.Length, ignoredEnt); return(!rayResults.DidHitObject); }
/// <summary> /// Fires hitscan entities and then displays their effects /// </summary> private void FireHitscan(IEntity shooter, HitscanComponent hitscan, Angle angle) { var ray = new CollisionRay(Owner.Transform.Coordinates.ToMapPos(Owner.EntityManager), angle.ToVec(), (int)hitscan.CollisionMask); var physicsManager = EntitySystem.Get <SharedPhysicsSystem>(); var rayCastResults = physicsManager.IntersectRay(Owner.Transform.MapID, ray, hitscan.MaxLength, shooter, false).ToList(); if (rayCastResults.Count >= 1) { var result = rayCastResults[0]; var distance = result.Distance; hitscan.FireEffects(shooter, distance, angle, result.HitEntity); EntitySystem.Get <DamageableSystem>().TryChangeDamage(result.HitEntity.Uid, hitscan.Damage); } else { hitscan.FireEffects(shooter, hitscan.MaxLength, angle); } }
public override void Update(Entity entity) { if (CollisionRay.IsEntityInSight(entity, GameWorld.GetPlayers()[0]) && !isAngry) { isAngry = true; chargeUpTimer.ResetAndWaitFor(1000); entity.IsFacingRight = !entity.IsPlayerToTheRight(); } if (isAngry) { if (!firingUp) { firingUp = true; entity.AddAnimationToQueue("angry"); entity.Sounds.GetSoundRef("scream").PlayIfStopped(); entity.Sounds.GetSoundRef("fire").PlayIfStopped(); } } if (firingUp) { chargeUpTimer.Increment(); entity.Sounds.GetSoundRef("fire").PlayIfStopped(); } if (isRunningToPlayer) { chargeUpTimer.Reset(); entity.AddAnimationToQueue("charge"); entity.Sounds.GetSoundRef("fire").PlayIfStopped(); if (playerToRightWhenAngry) { entity.SetVelX(5); } else { entity.SetVelX(-5); } entity.IsFacingRight = !playerToRightWhenAngry; } base.Update(entity); }
public static void FlashAreaHelper(IEntity source, double range, double duration, string sound = null) { var physicsManager = IoCManager.Resolve <IPhysicsManager>(); var entityManager = IoCManager.Resolve <IEntityManager>(); foreach (var entity in entityManager.GetEntities(new TypeEntityQuery(typeof(ServerFlashableComponent)))) { if (source.Transform.MapID != entity.Transform.MapID || entity == source) { continue; } var direction = entity.Transform.WorldPosition - source.Transform.WorldPosition; if (direction.Length > range) { continue; } // Direction will be zero if they're hit with the source only I think if (direction == Vector2.Zero) { continue; } var ray = new CollisionRay(source.Transform.WorldPosition, direction.Normalized, (int)CollisionGroup.Opaque); var rayCastResults = physicsManager.IntersectRay(source.Transform.MapID, ray, direction.Length, source, false).ToList(); if (rayCastResults.Count == 0 || rayCastResults[0].HitEntity != entity) { continue; } var flashable = entity.GetComponent <ServerFlashableComponent>(); flashable.Flash(duration); } if (sound != null) { IoCManager.Resolve <IEntitySystemManager>().GetEntitySystem <AudioSystem>().PlayAtCoords(sound, source.Transform.GridPosition); } }
/// <summary> /// Traces a ray from coords to otherCoords and returns the length /// of the vector between coords and the ray's first hit. /// </summary> /// <param name="coords">Set of coordinates to use.</param> /// <param name="otherCoords">Other set of coordinates to use.</param> /// <param name="collisionMask">the mask to check for collisions</param> /// <param name="predicate">A predicate to check whether to ignore an entity or not. If it returns true, it will be ignored.</param> /// <returns>Length of resulting ray.</returns> public float UnobstructedRayLength(MapCoordinates coords, MapCoordinates otherCoords, int collisionMask = (int)CollisionGroup.Impassable, Func <IEntity, bool> predicate = null) { var dir = otherCoords.Position - coords.Position; if (dir.LengthSquared.Equals(0f)) { return(0f); } var ray = new CollisionRay(coords.Position, dir.Normalized, collisionMask); var rayResults = _physicsManager.IntersectRayWithPredicate(coords.MapId, ray, dir.Length, predicate, returnOnFirstHit: false).ToList(); if (rayResults.Count == 0) { return(dir.Length); } return((rayResults[0].HitPos - coords.Position).Length); }
private void Fire(IEntity user, GridCoordinates clickLocation) { if (capacitorComponent.Charge < _lowerChargeLimit) {//If capacitor has less energy than the lower limit, do nothing return; } float energyModifier = capacitorComponent.GetChargeFrom(_baseFireCost) / _baseFireCost; var userPosition = user.Transform.WorldPosition; //Remember world positions are ephemeral and can only be used instantaneously var angle = new Angle(clickLocation.Position - userPosition); var ray = new CollisionRay(userPosition, angle.ToVec(), (int)(CollisionGroup.Opaque)); var rayCastResults = IoCManager.Resolve <IPhysicsManager>().IntersectRay(user.Transform.MapID, ray, MaxLength, user, returnOnFirstHit: false).ToList(); //The first result is guaranteed to be the closest one if (rayCastResults.Count >= 1) { Hit(rayCastResults[0], energyModifier, user); AfterEffects(user, rayCastResults[0], angle, energyModifier); } }
public bool Condition(IEntity user, EntityCoordinates location, Direction direction) { var entManager = IoCManager.Resolve <IEntityManager>(); // get blueprint and user position var userWorldPosition = user.Transform.WorldPosition; var objWorldPosition = location.ToMap(entManager).Position; // find direction from user to blueprint var userToObject = (objWorldPosition - userWorldPosition); // dot product will be positive if user direction and blueprint are co-directed var dotProd = Vector2.Dot(direction.ToVec(), userToObject); if (dotProd > 0) { return(false); } // now we need to check that user actually tries to build wallmount on a wall var physics = EntitySystem.Get <SharedBroadphaseSystem>(); var rUserToObj = new CollisionRay(userWorldPosition, userToObject.Normalized, (int)CollisionGroup.Impassable); var length = userToObject.Length; var userToObjRaycastResults = physics.IntersectRayWithPredicate(user.Transform.MapID, rUserToObj, maxLength: length, predicate: (e) => !e.HasTag("Wall")); if (!userToObjRaycastResults.Any()) { return(false); } // get this wall entity var targetWall = userToObjRaycastResults.First().HitEntity; // check that we didn't try to build wallmount that facing another adjacent wall var rAdjWall = new CollisionRay(objWorldPosition, direction.ToVec(), (int)CollisionGroup.Impassable); var adjWallRaycastResults = physics.IntersectRayWithPredicate(user.Transform.MapID, rAdjWall, maxLength: 0.5f, predicate: (e) => e == targetWall || !e.HasTag("Wall")); return(!adjWallRaycastResults.Any()); }
public void MultiHitRayCast() { // Arrange var b1 = new Box2(5, -5, 10, 6); var b2 = new Box2(6, -10, 7, 10); var ray = new CollisionRay(Vector2.UnitY, Vector2.UnitX, 1); var manager = new PhysicsManager(); var e1 = new Entity(new ServerEntityManager(), EntityUid.FirstUid); var e2 = new Entity(new ServerEntityManager(), EntityUid.FirstUid); var m1 = new Mock <IPhysBody>(); m1.Setup(foo => foo.WorldAABB).Returns(b1); m1.Setup(foo => foo.Entity).Returns(e1); m1.Setup(foo => foo.CanCollide).Returns(true); m1.Setup(foo => foo.CollisionLayer).Returns(1); m1.Setup(foo => foo.CollisionMask).Returns(1); manager.AddBody(m1.Object); var m2 = new Mock <IPhysBody>(); m2.Setup(foo => foo.WorldAABB).Returns(b2); m2.Setup(foo => foo.Entity).Returns(e2); m2.Setup(foo => foo.CanCollide).Returns(true); m2.Setup(foo => foo.CollisionLayer).Returns(1); m2.Setup(foo => foo.CollisionMask).Returns(1); manager.AddBody(m2.Object); var results = manager.IntersectRay(new MapId(0), ray, returnOnFirstHit: false).ToList(); Assert.That(results.Count, Is.EqualTo(2)); Assert.That(results[0].HitEntity.Uid, Is.EqualTo(e1.Uid)); Assert.That(results[1].HitEntity.Uid, Is.EqualTo(e2.Uid)); Assert.That(results[0].Distance, Is.EqualTo(5)); Assert.That(results[0].HitPos.X, Is.EqualTo(5)); Assert.That(results[0].HitPos.Y, Is.EqualTo(1)); Assert.That(results[1].Distance, Is.EqualTo(6)); Assert.That(results[1].HitPos.X, Is.EqualTo(6)); Assert.That(results[1].HitPos.Y, Is.EqualTo(1)); }
/// <summary> /// Checks that these coordinates are within a certain distance without any /// entity that matches the collision mask obstructing them. /// If the <paramref name="range"/> is zero or negative, /// this method will only check if nothing obstructs the two sets /// of coordinates. /// </summary> /// <param name="origin">Set of coordinates to use.</param> /// <param name="other">Other set of coordinates to use.</param> /// <param name="range"> /// Maximum distance between the two sets of coordinates. /// </param> /// <param name="collisionMask">The mask to check for collisions.</param> /// <param name="predicate"> /// A predicate to check whether to ignore an entity or not. /// If it returns true, it will be ignored. /// </param> /// <returns> /// True if the two points are within a given range without being obstructed. /// </returns> public bool InRangeUnobstructed( MapCoordinates origin, MapCoordinates other, float range = InteractionRange, CollisionGroup collisionMask = CollisionGroup.Impassable, Ignored?predicate = null) { // Have to be on same map regardless. if (other.MapId != origin.MapId) { return(false); } var dir = other.Position - origin.Position; var length = dir.Length; // If range specified also check it if (range > 0f && length > range) { return(false); } if (MathHelper.CloseTo(length, 0)) { return(true); } predicate ??= _ => false; if (length > MaxRaycastRange) { Logger.Warning("InRangeUnobstructed check performed over extreme range. Limiting CollisionRay size."); length = MaxRaycastRange; } var ray = new CollisionRay(origin.Position, dir.Normalized, (int)collisionMask); var rayResults = _sharedBroadphaseSystem.IntersectRayWithPredicate(origin.MapId, ray, length, predicate.Invoke, false).ToList(); return(rayResults.Count == 0); }
/// <summary> /// Fires hitscan entities and then displays their effects /// </summary> private void FireHitscan(EntityUid shooter, HitscanComponent hitscan, ServerRangedBarrelComponent component, Angle angle) { var ray = new CollisionRay(Transform(component.Owner).WorldPosition, angle.ToVec(), (int)hitscan.CollisionMask); var rayCastResults = _physics.IntersectRay(Transform(component.Owner).MapID, ray, hitscan.MaxLength, shooter, false).ToList(); if (rayCastResults.Count >= 1) { var result = rayCastResults[0]; var distance = result.Distance; hitscan.FireEffects(shooter, distance, angle, result.HitEntity); var dmg = _damageable.TryChangeDamage(result.HitEntity, hitscan.Damage); if (dmg != null) { _logs.Add(LogType.HitScanHit, $"{EntityManager.ToPrettyString(shooter):user} hit {EntityManager.ToPrettyString(result.HitEntity):target} using {EntityManager.ToPrettyString(hitscan.Owner):used} and dealt {dmg.Total:damage} damage"); } } else { hitscan.FireEffects(shooter, hitscan.MaxLength, angle); } }
/// <summary> /// Checks that these coordinates are within a certain distance without any /// entity that matches the collision mask obstructing them. /// If the <paramref name="range"/> is zero or negative, /// this method will only check if nothing obstructs the two sets of coordinates.. /// </summary> /// <param name="coords">Set of coordinates to use.</param> /// <param name="otherCoords">Other set of coordinates to use.</param> /// <param name="range">maximum distance between the two sets of coordinates.</param> /// <param name="collisionMask">the mask to check for collisions</param> /// <param name="ignoredEnt">the entity to be ignored when checking for collisions.</param> /// <param name="mapManager">Map manager containing the two GridIds.</param> /// <param name="insideBlockerValid">if coordinates inside obstructions count as obstructed or not</param> /// <returns>True if the two points are within a given range without being obstructed.</returns> public bool InRangeUnobstructed(MapCoordinates coords, Vector2 otherCoords, float range = InteractionRange, int collisionMask = (int)CollisionGroup.Impassable, IEntity ignoredEnt = null, bool insideBlockerValid = false) { var dir = otherCoords - coords.Position; if (dir.LengthSquared.Equals(0f)) { return(true); } if (range > 0f && !(dir.LengthSquared <= range * range)) { return(false); } var ray = new CollisionRay(coords.Position, dir.Normalized, collisionMask); var rayResults = _physicsManager.IntersectRay(coords.MapId, ray, dir.Length, ignoredEnt, true); return(!rayResults.DidHitObject || (insideBlockerValid && rayResults.DidHitObject && rayResults.Distance < 1f)); }
public override void Update(Entity entity) { StoneGolem golem = entity as StoneGolem; if (CollisionRay.IsEntityInSight(entity, GameWorld.GetPlayers()[0])) { float velX = 1.0f; golem.IsFacingRight = false; if (!golem.IsPlayerToTheRight()) { velX *= -1; golem.IsFacingRight = true; } golem.SetVelX(velX); entity.AddAnimationToQueue("walk"); } else { entity.RemoveAnimationFromQueue("walk"); } base.Update(entity); }
public void RayCastMissUp() { // Arrange var box = new Box2(5, -5, 10, 6); var ray = new CollisionRay(new Vector2(4.99999f, 1), Vector2.UnitY, 1); var manager = new PhysicsManager(); var mock = new Mock <IPhysBody>(); mock.Setup(foo => foo.WorldAABB).Returns(box); mock.Setup(foo => foo.Entity).Returns(new Entity()); // requires IPhysBody not have null owner mock.Setup(foo => foo.CanCollide).Returns(true); mock.Setup(foo => foo.CollisionLayer).Returns(1); mock.Setup(foo => foo.CollisionMask).Returns(1); manager.AddBody(mock.Object); // Act var results = manager.IntersectRay(new MapId(0), ray); // Assert Assert.That(results.Count(), Is.EqualTo(0)); }
/// <summary> /// Traces a ray from coords to otherCoords and returns the length /// of the vector between coords and the ray's first hit. /// </summary> /// <param name="origin">Set of coordinates to use.</param> /// <param name="other">Other set of coordinates to use.</param> /// <param name="collisionMask">the mask to check for collisions</param> /// <param name="predicate"> /// A predicate to check whether to ignore an entity or not. /// If it returns true, it will be ignored. /// </param> /// <returns>Length of resulting ray.</returns> public float UnobstructedDistance( MapCoordinates origin, MapCoordinates other, int collisionMask = (int)CollisionGroup.Impassable, Ignored?predicate = null) { var dir = other.Position - origin.Position; if (dir.LengthSquared.Equals(0f)) { return(0f); } predicate ??= _ => false; var ray = new CollisionRay(origin.Position, dir.Normalized, collisionMask); var rayResults = _sharedBroadphaseSystem.IntersectRayWithPredicate(origin.MapId, ray, dir.Length, predicate.Invoke, false).ToList(); if (rayResults.Count == 0) { return(dir.Length); } return((rayResults[0].HitPos - origin.Position).Length); }