Log in to reply
 

New small project, feedback welcomed!



  • Hey all, me again!

    I had a request to make certain street props act like trees (don't move and mess you/your car up). I have a prototyped script set with a handful of traffic lights. What I would like to know is what lights, poles, etc should be considered to be "static"?



  • Here's the code for documentation purposes and any feedback anyone wants to provide.

    using System;
    using GTA;
    using GTA.Native;
    
    namespace postsOfSteel
    {
        public class Main : Script
        {
    
            public Main()
            {
    
                Tick += OnTick;
    
            }
    
            private void OnTick(object sender, EventArgs e)
            {
    
                Ped playerPed = Game.Player.Character;
    
                Prop[] nearbyProp = World.GetNearbyProps(playerPed.Position, 10f, Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_07a"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_traffic_03b"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_05"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_traffic_01a"),
                    Function.Call<int>(Hash.GET_HASH_KEY, "prop_ind_light_03c"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_11c"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_traffic_01d"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_03d"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_oldlight_01a"),
                    Function.Call<int>(Hash.GET_HASH_KEY, "prop_ind_light_03b"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_04"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_14a"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_05_b"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_03e"),
                    Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_07b"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_ind_light_02c"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_01b"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_08"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_traffic_03a"),
                    Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_01"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_11b"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_03b"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_03c"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_03"),
                    Function.Call<int>(Hash.GET_HASH_KEY, "prop_ind_light_05"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_traffic_01b"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_ind_light_03a"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_02"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_ind_light_02b"), Function.Call<int>(Hash.GET_HASH_KEY, "prop_streetlight_09"));
    
                foreach (var props in nearbyProp)
                {
    
                    if (props.Exists())
                    {
    
                        Function.Call(Hash.FREEZE_ENTITY_POSITION, props, true);
    
                    }
    
                }
    
            }
    
        }
    }
    

    Gotta find a better way to call on props.



  • Might be a better idea to get all the HashKeys when the script first loads, store them in a list and use that instead of asking the game engine to convert all them strings to hashes every tick.
    Function calls can be quite a burden, id try to minimise them as much as possible.



  • Good idea. I'll look into implementing that when I can. Appreciate the feedback!



  • For stuff like this you can quite easily run at say 350-500ms and get everything around the player just fine.
    Might also want to consider keeping a reference of objects you have already done this operation on, and then only operating on objects you have not. (unless it needs overriding each frame ofc)



  • Updated code:

    using System;
    using GTA;
    using GTA.Native;
    
    namespace postsOfSteel
    {
        public class Main : Script
        {
    
            public Main()
            {
    
                Tick += OnTick;
    
            }
    
            private void OnTick(object sender, EventArgs e)
            {
    
                Ped playerPed = Game.Player.Character;
    
                Ped[] nearbyPeds = World.GetNearbyPeds(playerPed.Position, 75f);
    
                foreach (var pedestrian in nearbyPeds)
                {
    
                    Prop[] nearbyProp = World.GetNearbyProps(playerPed.Position, 10f, -365135956, 589548997, 267702115, 1043035044, -753093121, -1059778192, -1529663453,
                    -655644382, -1114695146, 431612653, 681787797, 326972916, -1454378608, -614573584, -306910109, 1821241621, 1847069612, 865627822, -1063472968,
                    -1684988513, 1327054116, 1021745343, 729253480, -1161709063, 862871082, 1393636838, -1222468156, -805588751, 1380570124, -259356231); 
    
                    foreach (var props in nearbyProp)
                    {
    
                        float dirDist_Player = World.GetDistance(playerPed.Position, props.Position);
                        float dirDist_Ped = World.GetDistance(pedestrian.Position, props.Position);
    
                        if (playerPed.IsInVehicle() && pedestrian.IsInVehicle() || playerPed.IsInVehicle() && pedestrian.SeatIndex == null)
                        {
    
                            if (dirDist_Player <= 8f || dirDist_Ped <= 8f)
                            {
    
                                if (props.Exists())
                                {
    
                                    Function.Call(Hash.FREEZE_ENTITY_POSITION, props, true);
    
                                }
    
                            }
    
                        }
    
                    }
    
                }
    
            }
    
        }
    }
    

    @mcal9909 How would I go about storing that or checking that (mostly storing). I was under the impression that the object is temp set to freeze until the next tick based on the distance and the array would empty.



  • Pretty sure an object keeps its frozen status for its lifetime.
    You can "get" the status of a prop and use this to filter your array.

    Prop[] nearbyProps = World.GetNearbyProps(playerPed.Position, 10f).Where(p => !p.FreezePosition);
    

    or make a list.

    List<Prop> frozenProps = new List<Prop>();
    List<Int> propHashes = new List<Int>{1111, -1111, 2111};
    

    and in ontick

    if(frozenProps.Count > 0)
    {
       foreach(Prop prop in frozenProps.ToList())
       {
          if(prop == null || !prop.Exists())
          {
             frozenProps.Remove(prop)
          }
       }   
    }
    Prop[] nearbyProps = World.GetNearbyProps(playerPed.Position, 100f).Where(p => propHashes.Contains(p.Model.Hash) && !frozenProps.Contains(p));
    if(nearbyProps.Count > 0)
    {
       foreach(Prop prop in nearbyProps)
       {
          prop.FreezePosition = true;
          frozenProps.Add(prop)
       }
    }
    

    It would depend on whats faster, checking if an object Exists, or if an object is frozen or not.
    I see what you are doing with your latest revision, but i would just get all props within 100m or so of the player every 400ms(ish), filter that list of anything that is not a prop to be frozen and then freeze the remainder.

    Just some ideas, but it should only require a tick every 400ms (could possibly go higher) instead of a tick every frame. Unless im wrong about a prop keeping its frozen status for its lifetime.



  • Damn thanks! This is real solid, especially since a user said the first version became unstable and crashed. So anything I can do to help increase stability is going to be awesome. Pretty quiet day today so I'll probably look at adding that.


  • MODERATOR

    mcal, Jit, I see you're in qualified hands even on the forums, quiet as it can be at times



  • Alright, so this is the latest bit of code for v1.0.3 for anyone interested. I tried some different approaches provided but either I'm still too green or something wasn't working right. I believe this is a good mix of stability, performance and gameplay.

    using System;
    using System.IO;
    using System.Collections.Generic;
    using System.Linq;
    using GTA;
    using GTA.Native;
    
    namespace postsOfSteel
    {
        public class Main : Script
        {
    
            List<int> propHashes = File.ReadAllLines(@"scripts/postsOfSteel.ini").Select(int.Parse).ToList();
    
            public Main()
            {
    
                Interval = 2500;
                Tick += OnTick;            
    
            }
    
            private void OnTick(object sender, EventArgs e)
            {
                
                Ped playerPed = Game.Player.Character;
    
                Prop[] nearbyProps = World.GetNearbyProps(playerPed.Position, 100f);
    
                foreach (int prophashes in propHashes)
                {
    
                    foreach (Prop prop in nearbyProps)
                    {
    
                        if (prophashes == prop.Model)
                        {
    
                            if (prop.IsPositionFrozen == false)
                            {
    
                                prop.IsPositionFrozen = true;
    
                            }
    
                        }
    
                    }
    
                }
    
            }
    
        }
    }
    

    Sorry @mcal9909 , I just couldn't get the .where to work with the array and when I changed it to IEnumerable<Prop> it couldn't compare the iterator with an int (like I said, I'm still green so maybe it was an easy fix I couln't find).



  • Instead of looping through all of the prophashes, in your nearbyProps loop check for this:

    if (prophashes.Contains(prop.Model))
    


  • Try

    List<Prop> props = World.GetNearbyProps().Where(p => propHashes.Contains(p.Model.Hash) && !p.FreezePosition).ToList();
    

    The result should only contain props that need to be frozen, so all you would need to do is.

    if(props.Count > 0)
    {
       foreach(Prop prop in props)
       {
          prop.FreezePosition = true;
       }
    }
    

    Note im still using SHVDN 2, so that would be why some syntax is different.

    Edit: Just did some testing and you can do

    Prop[] props = World.GetNearbyProps().Where(p => propHashes.Contains(p.Model.Hash) && !p.FreezePosition).ToArray();
    if(props.Count() > 0)
    {
       foreach(Prop prop in props)
       {
          prop.FreezePosition = true;
       }
    }
    


  • I noticed someone in the comments section for this script enquired about making certain props breakable, but only if they are hit really hard, size of vehicle and speed being taken into consideration.
    Well i was interested in such a feature myself so i did some playing around, and got something im pretty happy with.

    public class ColStr : Script
        {
            bool DEBUG = true;
            Veh veh = null;
            int time = 0;
            float EnergyNeededToBreak = 600;
            List<Prop> frozenProps = new List<Prop>();
    
            List<int> propHashes = new List<int> { -365135956, 589548997, 267702115, 1043035044, -753093121, -1059778192, -1529663453,
                    -655644382, -1114695146, 431612653, 681787797, 326972916, -1454378608, -614573584, -306910109, 1821241621, 1847069612, 865627822, -1063472968,
                    -1684988513, 1327054116, 1021745343, 729253480, -1161709063, 862871082, 1393636838, -1222468156, -805588751, 1380570124, -259356231 };
    
            public ColStr()
            {
                Tick += OnTick;
            }
    
            private void OnTick(object sender, EventArgs e)
            {
                Ped playerPed = Game.Player.Character;
    
                if(Game.GameTime > time + 1000)
                {
                    time = Game.GameTime;
                    Prop[] nearbyProps = World.GetNearbyProps(playerPed.Position, 100f).Where(p => propHashes.Contains(p.Model.Hash) && !frozenProps.Contains(p) && p.MaxHealth == p.Health && p.IsUpright).ToArray();
                    if(nearbyProps.Count() > 0)
                    {
                        foreach(Prop prop in nearbyProps)
                        {
                            frozenProps.Add(prop);
                            prop.FreezePosition = true;
                        }
                    }
                }            
                foreach(Prop prop in frozenProps.ToList())
                {
                    if(prop == null || !prop.Exists())
                    {
                        frozenProps.Remove(prop);
                    }
                }
    
                if(DEBUG)
                {
                    UIText propInfo = new UIText(frozenProps.Count.ToString(), new Point(10, 10), 0.2f, Color.WhiteSmoke, 0, true, true, true);
                    propInfo.Draw();
                }
                
    
                if (veh == null)
                {
                    if(playerPed.IsInVehicle())
                    {
                        veh = new Veh(playerPed.CurrentVehicle);
                    }
                }
                else
                {
                    if (!playerPed.IsInVehicle())
                    {
                        veh = null;                    
                    }
                    else
                    {
                        veh.calcEnergy();
                        if(DEBUG) DrawDebug();
                        if(veh.Vehicle.HasCollidedWithAnything)
                        {
                            foreach(Prop prop in frozenProps)
                            {                            
                                if(veh.Vehicle.IsTouching(prop))
                                {
                                    if(DEBUG) UI.Notify("Player hit prop @ " + veh.lastEnergy);
                                    if(veh.lastEnergy > EnergyNeededToBreak)
                                    {
                                        prop.FreezePosition = false;
                                        prop.MarkAsNoLongerNeeded();
                                    }                                                                                               
                                    break;
                                }
                            }
                        }
                    }
                }
            }
    
            private void DrawDebug()
            {
                string vehString = veh.curEnergy.ToString();
                Vector2 tPos = World3DToScreen2d(new Vector3(veh.Vehicle.Position.X, veh.Vehicle.Position.Y, veh.Vehicle.Position.Z + 0.5f));
                tPos.X = 1280 * tPos.X;
                tPos.Y = 720 * tPos.Y;
                UIText vehInfo = new UIText(vehString, new Point((int)tPos.X, (int)tPos.Y), 0.2f, Color.WhiteSmoke, 0, true, true, true);
                vehInfo.Draw();
            }
    
            class Veh
            {
                public Vehicle Vehicle;
                float VehMass;
                public float curEnergy;
                public float lastEnergy;
                public Veh(Vehicle _veh)
                {
                    Vehicle = _veh;                
                    VehMass = Vehicle.Model.GetDimensions().X * Vehicle.Model.GetDimensions().Y * Vehicle.Model.GetDimensions().Z;
                }
                public void calcEnergy()
                {
                    lastEnergy = curEnergy;
                    curEnergy = VehMass * Vehicle.Velocity.Length();
                }
            }
    
            public static Vector2 World3DToScreen2d(Vector3 pos)
            {
                OutputArgument x2dp = new OutputArgument();
                OutputArgument y2dp = new OutputArgument();
    
                Function.Call(Hash._0x34E82F05DF2974F5, pos.X, pos.Y, pos.Z, x2dp, y2dp); //GET_SCREEN_COORD_FROM_WORLD_COORD
                return new Vector2(x2dp.GetResult<float>(), y2dp.GetResult<float>());
            }
        }
    

    Feel free to use any of this if you want too, or send me a message if your not sure what something is doing.



  • @Jitnaught & @mcal9909 thanks again for your feedback!

    Definitely will try those and provide results, though that might be later in the week or weekend.

    @mcal9909 thanks for looking into that other option! I'll be happy to try it out and let you know how it goes.


Log in to reply
 

Looks like your connection to GTA5-Mods.com Forums was lost, please wait while we try to reconnect.