private Vector3 GetAnyAngleLineOfSightTargetLocation <T>(TopDownAiInput <T> aiInput, Vector3 focusPoint) where T : PositionedObject, ITopDownEntity { var effectiveTarget = focusPoint; var lineOfSightPoly = aiInput.GetLineOfSightPathFindingPolygon(aiInput.Owner.Position, effectiveTarget); // default to true, set to false if any collision var hasLineOfSight = true; for (int i = 0; i < aiInput.EnvironmentCollision.Count; i++) { var environment = aiInput.EnvironmentCollision[i]; if (environment.CollideAgainst(lineOfSightPoly)) { hasLineOfSight = false; break; } } // if doesn't have line of sight, then just move towards the target if (!hasLineOfSight) { return(effectiveTarget); } else { return(DoCloserThanFurtherThanLogic(aiInput, focusPoint)); } }
private static float GetTileSize <T>(TopDownAiInput <T> aiInput) where T : PositionedObject, ITopDownEntity { float tileSize = 16; // default to 16, but let's look to objects for more accuracy: if (aiInput.EnvironmentCollision.Count > 0) { tileSize = aiInput.EnvironmentCollision[0].GridSize; } else if (aiInput.NodeNetwork is TileNodeNetwork tileNodeNetwork) { tileSize = tileNodeNetwork.GridSpacing; } return(tileSize); }
private Vector3 DoCloserThanFurtherThanLogic <T>(TopDownAiInput <T> aiInput, Vector3 focusPoint, bool moveTowardsFocusIfInRange = false) where T : PositionedObject, ITopDownEntity { var effectiveTarget = focusPoint; // if it does have line of sight, try to move away from the target. Set a target that is far away. If the enemy breaks line of sight it will resume // to the !hasLineOfSight logic later. This is the simplest approach even though it may result in some back-and-forth movement. Eventually we could refine // this but who cares for now. var vectorToTarget = effectiveTarget - aiInput.Owner.Position; var directionFromTargetToOwner = vectorToTarget.NormalizedOrRight(); var lengthToTarget = vectorToTarget.Length(); if (CloserThan < lengthToTarget) { // player is too far, move closer! return(effectiveTarget); } else if (FartherThan > lengthToTarget) { // player is too close, move far away // but how far? // Less than one tile may result in the player standing still // Exactly one tile may result in the same tile being selected due to floating point issues // More than one tile may select a location in the wall // So let's select slightly more than one tile: float tileSize = GetTileSize(aiInput); var targetPosition = aiInput.Owner.Position + (tileSize * 1.1f) * directionFromTargetToOwner * -1; return(targetPosition); } else { if (moveTowardsFocusIfInRange) { return(focusPoint); } else { // we're good where we are! return(aiInput.Owner.Position); } } }
public Vector3 GetTargetLocation <T>(TopDownAiInput <T> aiInput, Vector3 focusPoint) where T : PositionedObject, TopDown.ITopDownEntity { if (AngleToTarget != null) { return(GetAngledTargetLocation(aiInput, focusPoint)); } else if (LineOfSight) { return(GetAnyAngleLineOfSightTargetLocation(aiInput, focusPoint)); } else if (FartherThan != null) { return(DoCloserThanFurtherThanLogic(aiInput, focusPoint)); } else { return(focusPoint); } }
private Vector3 GetAngledTargetLocation <T>(TopDownAiInput <T> aiInput, Vector3 focusPoint) where T : PositionedObject, ITopDownEntity { var effectiveTarget = focusPoint; float maxDistance = float.PositiveInfinity; float minDistance = 0; if (LineOfSight) { var angleToVector = Vector3ExtensionMethods.FromAngle(AngleToTarget.Value); // make it really long! angleToVector *= 100_000; var firstLinePosition = effectiveTarget; firstLinePosition.Y -= aiInput.CollisionWidth / 2; MathFunctions.RotatePointAroundPoint(effectiveTarget, ref firstLinePosition, AngleToTarget.Value); var secondLinePosition = effectiveTarget; secondLinePosition.Y += aiInput.CollisionWidth / 2; MathFunctions.RotatePointAroundPoint(effectiveTarget, ref secondLinePosition, AngleToTarget.Value); firstLineOfSightLine.Position = firstLinePosition; firstLineOfSightLine.RelativePoint1 = new Point3D(); firstLineOfSightLine.RelativePoint2 = new Point3D(angleToVector); secondLineOfSightLine.Position = secondLinePosition; secondLineOfSightLine.RelativePoint1 = new Point3D(); secondLineOfSightLine.RelativePoint2 = new Point3D(angleToVector); Vector3?closestFirstCollisionPoint = null; float closestFirstLength = 0; Vector3?closestSecondCollisionPoint = null; float closestSecondLength = 0; for (int i = 0; i < aiInput.EnvironmentCollision.Count; i++) { var collision = aiInput.EnvironmentCollision[i]; var firstCollided = collision.CollideAgainstClosest(firstLineOfSightLine); if (firstCollided) { if (closestFirstCollisionPoint == null) { closestFirstCollisionPoint = firstLineOfSightLine.LastCollisionPoint.ToVector3(); closestFirstLength = (closestFirstCollisionPoint.Value - firstLinePosition).Length(); } else { var newCollisionPoint = firstLineOfSightLine.LastCollisionPoint.ToVector3(); var newLength = (newCollisionPoint - firstLinePosition).Length(); if (newLength < closestFirstLength) { closestFirstCollisionPoint = firstLineOfSightLine.LastCollisionPoint.ToVector3(); closestFirstLength = (closestFirstCollisionPoint.Value - firstLinePosition).Length(); } } } var secondCollided = collision.CollideAgainstClosest(secondLineOfSightLine); if (secondCollided) { if (closestSecondCollisionPoint == null) { closestSecondCollisionPoint = secondLineOfSightLine.LastCollisionPoint.ToVector3(); closestSecondLength = (closestSecondCollisionPoint.Value - secondLinePosition).Length(); } else { var newCollisionPoint = secondLineOfSightLine.LastCollisionPoint.ToVector3(); var newLength = (newCollisionPoint - secondLinePosition).Length(); if (newLength < closestSecondLength) { closestSecondCollisionPoint = secondLineOfSightLine.LastCollisionPoint.ToVector3(); closestSecondLength = (closestSecondCollisionPoint.Value - secondLinePosition).Length(); } } } } if (closestFirstCollisionPoint != null && closestSecondCollisionPoint == null) { maxDistance = closestFirstLength; } else if (closestFirstCollisionPoint == null && closestSecondCollisionPoint != null) { maxDistance = closestSecondLength; } else if (closestFirstCollisionPoint != null && closestSecondCollisionPoint != null) { if (closestFirstLength < closestSecondLength) { maxDistance = closestFirstLength; } else { maxDistance = closestSecondLength; } } } if (CloserThan != null) { if (CloserThan < maxDistance) { maxDistance = CloserThan.Value; } } if (FartherThan != null) { // min distance can't be greater than max distance minDistance = FartherThan.Value; if (minDistance > maxDistance) { minDistance = maxDistance; } } // we really can't use positive infinity for math here, so we'll trim it to something reasonable like 100k if (float.IsPositiveInfinity(maxDistance)) { maxDistance = 100_000f; } // now create a segment to see the closest point: var directionVector = Vector3ExtensionMethods.FromAngle(AngleToTarget.Value); var segment = new Segment(effectiveTarget + directionVector * minDistance, effectiveTarget + directionVector * maxDistance); float tileSize = GetTileSize(aiInput); if (segment.DistanceTo(aiInput.Owner.Position) < tileSize) { // close enough, walk towards the player return(DoCloserThanFurtherThanLogic(aiInput, focusPoint, moveTowardsFocusIfInRange: true)); } else { var closestPoint = segment.ClosestPointTo(aiInput.Owner.Position); return(closestPoint.ToVector3()); } }