Skip to content
View in the app

A better way to browse. Learn more.

LCPDFR.com

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Determine if Ped has line of sight to another Ped

Featured Replies

For my callouts, and in particular, the Burglary: Silent Alarm call, I currently use whether or not the suspect Ped is physically close enough to the (non-stealth, not-in-cover) player character to determine if they are aware of the player, and should fight or flee.

What I'd really like here is something more sophisticated — can the suspect Ped actually see the officer? It would be great to be able to sneak up on the suspect, getting closer to them than the logic currently allows, and then suddenly order them to surrender while their back is turned. But, if they turn to face you, they'd suddenly notice you and take the appropriate action!

Is there something I'm missing in the RPH API that allows me to determine if a Ped has a line of sight to another Ped?

It seems like something that might be quite complicated (ray tracing?) otherwise.

Thanks for any insights anyone can offer!

As you might guess, I make PeterUCallouts. I love working to bring LSPDFR players the kind of callouts that strike a good balance between realism and fun and are stable & varied!

Unfortunately I cannot offer support via PM -- please use the forums, so that others may benefit from your solutions!

You can use some basic vector math:

Vector3 directionFromSuspectToPlayer = (Game.LocalPlayer.Character.Position - Suspect.Position);

directionFromSuspectToPlayer.Normalize();

float HeadingFromSuspectToPlayer = MathHelper.ConvertDirectionToHeading(directionFromSuspectToPlayer);

if (Math.Abs(MathHelper.NormalizeHeading(HeadingFromSuspectToPlayer) - MathHelper.NormalizeHeading(Suspect.Heading)) < 90f) 
{ 
  //Suspect is looking towards player

}

You can increase or decrease the 90f as you wish (smaller means the suspect must be heading towards the player even more). 90f is a 90 degree angle.

Hope this helps.

Edited by Albo1125

My YouTube: Click here. 

My Discord Server - https://discord.gg/0taiZvBSiw5qGAXU

Useful post? Let me and others know by clicking the Like button.
Check out my many script modifications! 
Having issues? LSPDFR Troubleshooter by Albo1125.

  • Author

Thanks Albo1125! I didn't realise at first that this is only the heading of the suspect, so it needs to be combined with a distance check, but this makes for quite a bit more realism in the suspect awareness logic.

I'm still fiddling with values, but this looks like a much better approach than just a distance check was. :smile:

As you might guess, I make PeterUCallouts. I love working to bring LSPDFR players the kind of callouts that strike a good balance between realism and fun and are stable & varied!

Unfortunately I cannot offer support via PM -- please use the forums, so that others may benefit from your solutions!

The issue I think you'll find with Albo1125's suggestion is it only checks if they're "facing" the player, not whether they have a clear line of site.

Earlier I was looking at the native function for cast ray's...essentially you give it point A and point B and it comes back with a result of whether it has a straight line with no obstructions. It may give false positives if there is a car in the "way", but you can visibly see the player. I thought there was a RAGE function for something along the lines of "Can see player"

 

int _CAST_RAY_POINT_TO_POINT(float x1, float y1, float z1, float x2, float y2, float z2, intflags, Entity entity, int p8) // 377906D8A31E5586 8251485D

Not sure how or why this differs from 0x7EE9F5D83DD4F90E, but it does.

This function casts a ray from Point1 to Point2 and returns it's ray handle. A simple ray cast will 'shoot' a line from point A to point B, and return whether or not the ray reached it's destination or if it hit anything and if it did hit anything, will return the handle of what it hit (entity handle) and coordinates of where the ray reached.

You can use _GET_RAYCAST_RESULT to get the result of the raycast

Entity is an entity to ignore, such as the player.

Flags are intersection bit flags. They tell the ray what to care about and what not to care about when casting. Passing -1 will intersect with everything, presumably.

Flags:
1: Intersect with map
2: Intersect with mission entities? (includes train)
4: Intersect with peds? (same as 8)
8: Intersect with peds? (same as 4)
10: vehicles
16: Intersect with objects
19: Unkown
32: Unknown
64: Unknown
128: Unknown
256: Intersect with vegetation (plants, coral. trees not included)
512: Unknown

NOTE: Raycasts that intersect with mission_entites (flag = 2) has limited range and will not register for far away entites. The range seems to be about 30 metres.
  • Author

This looks interesting indeed. Is there documentation somewhere for what the last 'p8' argument does?

If I wanted to raycast from the suspect to the player, and have the ray "blocked" by something, do I set the intersect flags to be greater than the things I want it to be "blocked" by, or the other way around?

As you might guess, I make PeterUCallouts. I love working to bring LSPDFR players the kind of callouts that strike a good balance between realism and fun and are stable & varied!

Unfortunately I cannot offer support via PM -- please use the forums, so that others may benefit from your solutions!

If you are going to use ray tracing I recommend you to use the method included in RPH, World.TraceLine(). It return a HitResult instance that includes wheter it hit something or not, the hit entity, the hit position and the hit normal, you can even pass an array of ignored entities(using the native you can only pass one). There's also World.TraceExtent() that does the same but allows you to specify the radius of the ray. Example:

Vector start = Game.LocalPlayer.Character.Position;                                                                  

Vector end = start + Game.LocalPlayer.Character.Direction * 50f; // the end position is calculated by starPosition + direction * distance

HitResult rayResult = World.TraceLine(start, end, TraceFlags.IntersectPeds | TraceFlags.IntersectVehicles, Game.LocalPlayer.Character); // a ray that can only hit peds or vehicles and ignores the player ped

 

To use more than one flag you have to use the symbol "|" as shown in the example.

  • Author

Thanks @alexguirre, that is exactly what I was looking for! I've not worked with calling Natives at all yet, so I'm not at all confident I know what I'm doing. This is an ideal solution to keep things in RPH style.

As you might guess, I make PeterUCallouts. I love working to bring LSPDFR players the kind of callouts that strike a good balance between realism and fun and are stable & varied!

Unfortunately I cannot offer support via PM -- please use the forums, so that others may benefit from your solutions!

  • Author

@alexguirre Using World.TraceExtent seems to be working, but it remains unclear to me exactly what IntersectFlags means, and the RPH documentation sadly tells you no more than IntelliSense does!

Do I set IntersectFlags to the things I want it to 'hit' and stop at, or are those the things that it can pass through? Or am I still misunderstanding how it works?

Thanks to all for their help so far.

As you might guess, I make PeterUCallouts. I love working to bring LSPDFR players the kind of callouts that strike a good balance between realism and fun and are stable & varied!

Unfortunately I cannot offer support via PM -- please use the forums, so that others may benefit from your solutions!

2 minutes ago, PeterU said:

@alexguirre Using World.TraceExtent seems to be working, but it remains unclear to me exactly what IntersectFlags means, and the RPH documentation sadly tells you no more than IntelliSense does!

Do I set IntersectFlags to the things I want it to 'hit' and stop at, or are those the things that it can pass through? Or am I still misunderstanding how it works?

Thanks to all for their help so far.

Those flags specify what the ray can hit/detect. For example, if you use TraceFlags.IntersectPeds it will only hit peds so it will ignore/pass through vehicles, walls, vegetation, ... If you use TraceFlags.IntersectVehicles | TraceFlags.IntersectPeds it will only hit peds and vehicles.

For what you want(determine line of sight) you probably just want TraceFlags.IntersectEverything, it will hit/detect peds, vehicles, walls, vegetation, objects,...

  • Author

Here is my code for CanPedSeePolice which implements a World.TraceExtent to determine if the passed Ped can see a police officer, the local player, or a police vehicle.

This does still contain references to some of my own private methods (CommonUtils.DumpLog is my custom logging wrapper), so some things will need changing, but hopefully this helps others!

Provided "as is", without guarantees! :wink:

        private bool CanPedSeePolice(Ped ped, float distance = 80f, float angle = 135f, string pedDescription = "Suspect", char pedNotificationColor = 'r')
        {

            Ped hitPed = null;
            Vehicle hitVehicle = null;

            if (ped == null || !ped.IsValid())
                return false;

            // raytrace from suspect to player
            try
            {

                Vector3 traceEndPoint = ped.Position + ped.Direction * distance;
                HitResult pedVision = World.TraceExtent(ped.Position, traceEndPoint, angle, TraceFlags.IntersectEverything, ped);


                if (pedVision.HitEntity == Game.LocalPlayer.Character)
                {
                    CommonUtils.DumpLog(this, "CanPedSeePolice", "Raytrace from " + pedDescription + " hit Player");
#if DEBUG
                    Game.DisplayNotification("~" + pedNotificationColor + "~" + pedDescription + "~w~ had line of sight to ~b~Player~w~");
#endif
                    return true;
                }

                try
                {
                    hitPed = (Ped)pedVision.HitEntity;
                }
                catch { }

                try
                {
                    hitVehicle = (Vehicle)pedVision.HitEntity;
                }
                catch { }

                if (hitPed != null)
                {
                    if (hitPed.IsValid() && hitPed.RelationshipGroup == "COP")
                    {
                        CommonUtils.DumpLog(this, "CanPedSeePolice", "Raytrace from " + pedDescription + " hit Cop");
#if DEBUG
                        Game.DisplayNotification("~" + pedNotificationColor + "~" + pedDescription + "~w~ had line of sight to another ~b~officer~w~");
#endif
                        return true;
                    }
                }

                if (hitVehicle != null)
                {
                    if (hitVehicle.IsValid() &&
                        hitVehicle.IsPoliceVehicle &&
                        !(
                            unmarkedPoliceVehicles.Contains(hitVehicle.Model.Name) ||
                            hitVehicle.IsSirenSilent && hitVehicle.IsSirenOn // lights on -- not yet working??
                        )
                        )
                    {
                        CommonUtils.DumpLog(this, "CanPedSeePolice", "Raytrace from " + pedDescription + " hit police vehicle");
#if DEBUG
                        Game.DisplayNotification("~" + pedNotificationColor + "~" + pedDescription + "~w~ had line of sight to a ~b~police vehicle~w~");

                        if (unmarkedPoliceVehicles.Contains(hitVehicle.Model.Name) &&
                            hitVehicle.IsSirenSilent && hitVehicle.IsSirenOn)
                        {
                            Game.DisplayNotification("Saw unmarked ~b~" + hitVehicle.Model.Name + "~w~ with lights on");
                        }

#endif
                        return true;
                    }

                }

                return false;

            }
            catch (Exception) { return false; }
        }

As you might guess, I make PeterUCallouts. I love working to bring LSPDFR players the kind of callouts that strike a good balance between realism and fun and are stable & varied!

Unfortunately I cannot offer support via PM -- please use the forums, so that others may benefit from your solutions!

17 minutes ago, PeterU said:

Here is my code for CanPedSeePolice which implements a World.TraceExtent to determine if the passed Ped can see a police officer, the local player, or a police vehicle.

This does still contain references to some of my own private methods (CommonUtils.DumpLog is my custom logging wrapper), so some things will need changing, but hopefully this helps others!

Provided "as is", without guarantees! :wink:

 

  Reveal hidden contents

 



        private bool CanPedSeePolice(Ped ped, float distance = 80f, float angle = 135f, string pedDescription = "Suspect", char pedNotificationColor = 'r')
        {

            Ped hitPed = null;
            Vehicle hitVehicle = null;

            if (ped == null || !ped.IsValid())
                return false;

            // raytrace from suspect to player
            try
            {

                Vector3 traceEndPoint = ped.Position + ped.Direction * distance;
                HitResult pedVision = World.TraceExtent(ped.Position, traceEndPoint, angle, TraceFlags.IntersectEverything, ped);


                if (pedVision.HitEntity == Game.LocalPlayer.Character)
                {
                    CommonUtils.DumpLog(this, "CanPedSeePolice", "Raytrace from " + pedDescription + " hit Player");
#if DEBUG
                    Game.DisplayNotification("~" + pedNotificationColor + "~" + pedDescription + "~w~ had line of sight to ~b~Player~w~");
#endif
                    return true;
                }

                try
                {
                    hitPed = (Ped)pedVision.HitEntity;
                }
                catch { }

                try
                {
                    hitVehicle = (Vehicle)pedVision.HitEntity;
                }
                catch { }

                if (hitPed != null)
                {
                    if (hitPed.IsValid() && hitPed.RelationshipGroup == "COP")
                    {
                        CommonUtils.DumpLog(this, "CanPedSeePolice", "Raytrace from " + pedDescription + " hit Cop");
#if DEBUG
                        Game.DisplayNotification("~" + pedNotificationColor + "~" + pedDescription + "~w~ had line of sight to another ~b~officer~w~");
#endif
                        return true;
                    }
                }

                if (hitVehicle != null)
                {
                    if (hitVehicle.IsValid() &&
                        hitVehicle.IsPoliceVehicle &&
                        !(
                            unmarkedPoliceVehicles.Contains(hitVehicle.Model.Name) ||
                            hitVehicle.IsSirenSilent && hitVehicle.IsSirenOn // lights on -- not yet working??
                        )
                        )
                    {
                        CommonUtils.DumpLog(this, "CanPedSeePolice", "Raytrace from " + pedDescription + " hit police vehicle");
#if DEBUG
                        Game.DisplayNotification("~" + pedNotificationColor + "~" + pedDescription + "~w~ had line of sight to a ~b~police vehicle~w~");

                        if (unmarkedPoliceVehicles.Contains(hitVehicle.Model.Name) &&
                            hitVehicle.IsSirenSilent && hitVehicle.IsSirenOn)
                        {
                            Game.DisplayNotification("Saw unmarked ~b~" + hitVehicle.Model.Name + "~w~ with lights on");
                        }

#endif
                        return true;
                    }

                }

                return false;

            }
            catch (Exception) { return false; }
        }

 

 

Just one thing, you're passing an angle as the radius in World.TraceExtent(), this parameter defines how thick the ray is, not the heading.

  • 2 months later...
  • Author

Vastly improved version of this, which now uses a combination of the TraceExtent method above and the native ENTITY::HAS_ENTITY_CLEAR_LOS_TO_ENTITY_IN_FRONT to do this in a much more realistic and efficient way.

I no longer make the mistake, as @alexguirre points out, of making the radius of the TraceExtent cylinder too big (which not only gave potentially inaccurate results from a logical point of view, but hurt performance a lot).

This will always check to see if the passed ped can see the local player character, and then optionally also will run a TraceExtent to determine peds and vehicles that are possibly in the suspect's range, and if we are interested in them (they are a cop), then we run the native to actually determine if there is a clear line of sight.

It supports sending different flags at runtime based on what you are interested in evaluating, though I haven't tested that in the real world yet, and some of them are not yet implemented, as so far I only want to use 'All'!

Thanks to @alexguirre, @Stealth22 and @Albo1125 for their help with this.

http://pastebin.com/VWmmTcBR

Spoiler

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Rage;
using Rage.Native;

// [snip] //

        public const ulong NATIVE_HAS_ENTITY_CLEAR_LOS_TO_ENTITY_IN_FRONT = 0x0267D00AF114F17A;

        public enum EPedCanSeePoliceFlags
        {
            IncludeLocalCharacter = 1,
            IncludeCopPeds = 2,
            IncludeCopVehicles = 4,
            IncludeCopVehiclesWithLights = 8,
            ExcludeUnmarkedVehicles = 16,
            All = 65535
        }

        /// <summary>
        /// (Reasonably) safely invoke the HAS_ENTITY_CLEAR_LOS_TO_ENTITY_IN_FRONT Native to determine if source has a clear line of sight to target.
        /// </summary>
        /// <param name="source">The entity to trace from.</param>
        /// <param name="target">The target entity to trace to.</param>
        /// <returns>true if source has a clear LoS to target, false otherwise or on error</returns>
        public static bool EntityHasClearLOSToEntityInFront(Entity source, Entity target)
        {

            if (source == null || !source.IsValid())
                return false;

            if (target == null || !target.IsValid())
                return false;

            try
            {
                bool result = NativeFunction.CallByHash<bool>(NATIVE_HAS_ENTITY_CLEAR_LOS_TO_ENTITY_IN_FRONT, source, target);

                return result;
            }
            catch (Exception e)
            {
#if DEBUG
                Game.LogTrivial("Exception invoking the native ENTITY::HAS_ENTITY_CLEAR_LOS_TO_ENTITY_IN_FRONT");
                Game.LogTrivial("InnerException was " + e.ToString());
#endif
                return false;
            }
        }

        /// <summary>
        /// Can the Ped see the player character, another police officer, or a police vehicle which is not considered 'unmarked'? Do not run too frequently, or performance may suffer.
        /// </summary>
        /// <param name="ped">The Ped from whose viewpoint we are looking</param>
        /// <param name="flags">A bitwise-combined set of EPedCanSeePoliceFlags that describe what entities we should test against</param>
        /// <param name="unmarkedVehicleModels">A List of strings containing model names of 'unmarked' police vehicles. Pass null if not interested.</param>
        /// <param name="distance">The maximum distance at which to assess entities for if the ped can see them (not including Game.LocalPlayer.Character, which is always assessed if in vision)</param>
        /// <param name="pedDescription">For debugging -- a description of the Ped type: "Suspect"/"Subject" etc.</param>
        /// <param name="pedNotificationColor">For debugging -- The Game.DisplayNotification color character (e.g. r, y)</param>
        /// <returns>True if the ped can see any police, false otherwise</returns>
        public static bool CanPedSeePolice(Ped ped, EPedCanSeePoliceFlags flags, List<string> unmarkedVehicleModels = null, float distance = 60f, string pedDescription = "Suspect", char pedNotificationColor = 'r')
        {

            Ped hitPed = null;
            Vehicle hitVehicle = null;
            bool los = false;

            if (ped == null || !ped.IsValid())
            {
#if DEBUG
                Game.LogTrivial("CanPedSeePolice: Called with an invalid ped. Returning false.");
#endif
                return false;
            }

            if (unmarkedVehicleModels == null)
            {
                unmarkedVehicleModels = new List<string>();
            }

            // Game.LocalPlayer.Character
            if (!Game.LocalPlayer.Character.IsInAnyVehicle(false))
            {
                los = EntityHasClearLOSToEntityInFront(ped, Game.LocalPlayer.Character);

                if (los)
                {
                    if (ped.DistanceTo(Game.LocalPlayer.Character) < distance)
                    {
#if DEBUG
                        Game.DisplayNotification("~" + pedNotificationColor + "~" + pedDescription + "~w~ had line of sight to ~b~Player~w~");
                        Game.LogTrivial("CanPedSeePolice: " + pedDescription + " had line of sight to Player");
#endif
                        return los;
                    }
                    else
                    {
#if DEBUG
                        Game.LogTrivial("CanPedSeePolice: + " + pedDescription + " had line of sight to Player BUT was too far away for it to count");
#endif
                    }
                }
            }
            else
            {
#if DEBUG
                //DumpLog("CommonUtils", "CanPedSeePolice", "Skipping test of ped -> local player line of sight as player is in vehicle");
#endif
            }

            /* find entities in front of the ped to later check for a valid LoS
                // we will do this with the cheapest TraceExtentFlags we can, since it might match a fair few entities
                // and let the native do the heavy lifting!

                So the extent trace's job is to identify entities that we _might_ be interested in and _might_
                be in a valid line of sight.

                Then we check other logic (are they a cop, or cop car etc.) and if we are interested, we
                call the native to see if there is actually a line of sight.
            */

            if ((flags & EPedCanSeePoliceFlags.IncludeCopPeds) != 0 || (flags & EPedCanSeePoliceFlags.IncludeCopVehicles) != 0)
            {

                HitResult pedVisionCyl;

                try
                {
                    Vector3 traceEndPoint = ped.Position + ped.Direction * distance;
                    pedVisionCyl = World.TraceExtent(ped.Position, traceEndPoint, 1f, TraceFlags.IntersectPeds | TraceFlags.IntersectVehicles, ped);
                }
                catch (Exception e)
                {
#if DEBUG
                    Game.LogTrivial("CanPedSeePolice: Unable to TraceExtent to determine Entities against which to check line of sight.");
                    Game.LogTrivial("CanPedSeePolice: InnerException " + e.ToString());
#endif
                    return los;
                }

                if (pedVisionCyl.Hit)
                {
                    if ((flags & EPedCanSeePoliceFlags.IncludeCopPeds) != 0)
                    {
                        try
                        {
                            hitPed = (Ped)pedVisionCyl.HitEntity;
                            GameFiber.Yield();
                        }
                        catch { }
                    }


                    if ((flags & EPedCanSeePoliceFlags.IncludeCopVehicles) != 0)
                    {
                        try
                        {
                            hitVehicle = (Vehicle)pedVisionCyl.HitEntity;
                            GameFiber.Yield();
                        }
                        catch { }
                    }

                    // extent trace hit a ped
                    if (hitPed != null)
                    {

                        if (hitPed.IsValid() &&
                            hitPed.RelationshipGroup == "COP" &&
                            !hitPed.IsInAnyVehicle(false))
                        {
#if DEBUG
                            Game.LogTrivial("CanPedSeePolice: Extent trace hit cop -- will now determine if there is a valid clear LoS");
#endif
                            // we only actually return true if the trace hit ped is within the source ped's line of sight
                            if (EntityHasClearLOSToEntityInFront(ped, hitPed))
                            {
#if DEBUG
                                Game.DisplayNotification("~" + pedNotificationColor + "~" + pedDescription + "~w~ had line of sight to ~b~Officer~w~");
                                Game.LogTrivial("CanPedSeePolice: " + pedDescription + " had line of sight to Officer");
#endif
                                return true;
                            }
                        }
                    }

                    // extent trace hit a vehicle
                    if (hitVehicle != null)
                    {
                        if (hitVehicle.IsValid() && hitVehicle.IsPoliceVehicle)
                        {
                            /* only check entity LoS if either we want to **include** unmarked vehicles or if 
                             * the vehicle model is not in the unmarked list
                             * */
                            if ((flags & EPedCanSeePoliceFlags.ExcludeUnmarkedVehicles) != 0 &&
                                !unmarkedVehicleModels.Contains(hitVehicle.Model.Name))
                            {
#if DEBUG
                                Game.LogTrivial("CanPedSeePolice: Hit vehicle is a marked police vehicle -- returning result of EntityHasClearLOSToEntityInFront");
#endif
                                return EntityHasClearLOSToEntityInFront(ped, hitVehicle);
                            }
                        }
                    }

                }
            }

            return los;

        }


    }
}


/* Then call as follows:

	bool canSeePolice = CanPedSeePolice(suspect, EPedCanSeePoliceFlags.All, unmarkedPoliceVehicles, 80f, "Suspect", 'r')
	
*/

 

 

As you might guess, I make PeterUCallouts. I love working to bring LSPDFR players the kind of callouts that strike a good balance between realism and fun and are stable & varied!

Unfortunately I cannot offer support via PM -- please use the forums, so that others may benefit from your solutions!

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

Recently Browsing 0

  • No registered users viewing this page.

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.