Problem with bodyguard script
-
Hello, I'm buiding a simple bodyguard script. On pressing key "Y" it spawns a bodyguard. Player can press this button 3 times, so that it spawns maximum 3 bodyguards simultaneously. If player gets far away from bodyguards, they are no longer in player group.
So, the problem is that after bodyguards are no longer in my group, I can't remove blips from them and mark them as no longer needed. I can only do it with 1 bodyguard. Can you suggest the solution, please?
Here is the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Windows.Forms;
using GTA.Math;
using GTA.Native;
using GTA;
using GTA.UI;namespace BodyGuard
{
public class Main : Script
{
Ped bodyguard;
List<Ped> bodyguard_list = new List<Ped>();
PedGroup PlayerGroup = Game.Player.Character.PedGroup;
Blip bodyguardBlip;
int pressCnt = 0;
bool isMarked = false;public Main() { Tick += OnTick; KeyDown += OnKeyDown; } public void OnTick(object sender, EventArgs e) { foreach (Ped bodyguard in bodyguard_list.ToList()) { if (bodyguard != null) { if ((bodyguard.Exists() && bodyguard.IsDead == true && isMarked == false) | (bodyguard.Exists() && isMarked == false && bodyguard.IsInGroup == false)) { pressCnt--; Notification.Show($"{pressCnt}"); bodyguard.MarkAsNoLongerNeeded(); isMarked = true; bodyguardBlip.Delete(); bodyguard_list.Remove(bodyguard); } } } } public void OnKeyDown(object sender, KeyEventArgs e) { if(e.KeyCode == Keys.Y && (pressCnt >=0 && pressCnt <3)) { bodyguard = World.CreatePed(PedHash.TomCasino, Game.Player.Character.GetOffsetPosition(new Vector3(0,2,0))); Function.Call(Hash.GIVE_WEAPON_TO_PED, bodyguard, WeaponHash.RevolverMk2,9999,1,1); Function.Call(Hash.GIVE_WEAPON_TO_PED, bodyguard, WeaponHash.Gusenberg, 9999, 1, 1); bodyguard.Health = 10000; bodyguard.CanSwitchWeapons = true; Function.Call(Hash.SET_PED_ARMOUR, bodyguard, 100); Function.Call(Hash.SET_PED_SUFFERS_CRITICAL_HITS, bodyguard, false); Function.Call(Hash.SET_PED_RELATIONSHIP_GROUP_HASH, Relationship.Respect); Function.Call(Hash.SET_PED_AS_GROUP_MEMBER, bodyguard, PlayerGroup); Function.Call(Hash.SET_PED_CAN_TELEPORT_TO_GROUP_LEADER, bodyguard, true); bodyguardBlip = bodyguard.AddBlip(); Function.Call(Hash.SET_BLIP_COLOUR, bodyguardBlip, 2); bodyguard.ShootRate = 1000; bodyguard.CanRagdoll = false; bodyguard.Accuracy = 100; pressCnt++; isMarked = false; bodyguard_list.Add(bodyguard); if (pressCnt == 3){ Notification.Show($"{bodyguard_list}"); } } } }
}
-
I can't help you with blips, no interest in those, but my companions will remain companions even if I'm at legion square and they're at Paleto Bay.
CurrentPed.IsPersistent = true;
Function.Call(Hash.SET_PED_NEVER_LEAVES_GROUP, CurrentPed, true);
-
@JohnFromGWN Thank you for your reply, but I don't want my bodyguards to ALWAYS remain in my group, even if I'm too far from them.
Basically, my code runs correctly, BUT ONLY for ONE bodyguard. I think that something is wrong with my foreach loop because this loop "forgets" to remove blips from other bodyguards.
-
@AlexSam why not then have a binding to dismiss bodyguard?
-
@JohnFromGWN because I don't want to. This script is just for educational purposes. I want to understand the logic of making GTA V scripts in order to create bigger and better quality mods in the future.
EDIT: I looked closely at my code, using paper and pen, and it seems that the problem is with isMarked. I suppose that I can fix my code by removing this variable. Tomorrow I will check.
-
@AlexSam
try running the code below, later I will try to explain what went wrong with your script.using GTA; using GTA.Math; using System.Collections.Generic; namespace BodyGuard { class Main : Script { private Ped _player; private IList<Ped> _bodyguards; private PedGroup _pedGroup = Game.Player.Character.PedGroup; private Blip[] _bodyguardBlips = new Blip[3]; public Main() { _bodyguards = new List<Ped>(); _player = Game.Player.Character; Tick += (o, e) => { if (_bodyguards.Count > 0) { ChecksTheIntegrityOfTheBodyguardAndRemovesHimFromTheGroupIfHeIsTooFarAwayFromThePlayer(); } else { DeleteBodyguardBlipsFromTheMap(); } }; KeyUp += (o, e) => { if (e.KeyCode == System.Windows.Forms.Keys.Y) { CreateTheBodyguards(); } }; Aborted += (o, e) => { DeleteBodyguardBlipsFromTheMap(); foreach (var bodyguard in _bodyguards) { if (bodyguard != null) { bodyguard.Delete(); } } }; } private void CreateTheBodyguards() { var amountOfBodyguardCreated = _bodyguards.Count; var maximumAmountOfBodyguard = 3; var hasTheMaximumAmountOfBodyguardsBeenReached = amountOfBodyguardCreated == maximumAmountOfBodyguard; if (!hasTheMaximumAmountOfBodyguardsBeenReached) { var model = PedHash.TomCasino; var position = _player.GetOffsetPosition(new Vector3(0f, 2f, 0f)); var bodyguard = World.CreatePed(model, position); if (bodyguard != null) { bodyguard.Health = 100; ///_bodyguard.Health = 10000; bodyguard.Armor = 100; ///Function.Call(Hash.SET_PED_ARMOUR, _bodyguard, 100); bodyguard.CanSufferCriticalHits = false; ///Function.Call(Hash.SET_PED_SUFFERS_CRITICAL_HITS, _bodyguard, false); bodyguard.CanSwitchWeapons = true; ///_bodyguard.CanSwitchWeapons = true; bodyguard.ShootRate = 1000; ///_bodyguard.ShootRate = 1000; bodyguard.Accuracy = 100; ///_bodyguard.Accuracy = 100; _pedGroup.Add(bodyguard, false); ///Function.Call(Hash.SET_PED_AS_GROUP_MEMBER, _bodyguard, _pedGroup); /// ///bodyguard.RelationshipGroup = /// (int)Relationship.Respect; /// ///Function.Call(Hash.SET_PED_RELATIONSHIP_GROUP_HASH, Relationship.Respect); var weaponsHash = new[] { WeaponHash.RevolverMk2, WeaponHash.Gusenberg }; foreach (var weaponHash in weaponsHash) { var ammoCount = 9999; var equipNow = true; var isAmmoLoaded = true; bodyguard .Weapons .Give(weaponHash, ammoCount, equipNow, isAmmoLoaded); } ///Function.Call(Hash.GIVE_WEAPON_TO_PED, bodyguard, WeaponHash.RevolverMk2, 9999, 1, 1); ///Function.Call(Hash.GIVE_WEAPON_TO_PED, bodyguard, WeaponHash.Gusenberg, 9999, 1, 1); _bodyguardBlips[amountOfBodyguardCreated] = World.CreateBlip(bodyguard.Position); ///_bodyguardBlip = _bodyguard.AddBlip(); _bodyguardBlips[amountOfBodyguardCreated].Sprite = BlipSprite.Ghost; _bodyguardBlips[amountOfBodyguardCreated].Name = "Bodyguard"; _bodyguardBlips[amountOfBodyguardCreated].Color = BlipColor.Blue; ///Function.Call(Hash.SET_BLIP_COLOUR, _bodyguardBlip, 2); _bodyguardBlips[amountOfBodyguardCreated].Scale = 0.80f; _bodyguards.Add(bodyguard); } /* Why??? Function.Call(Hash.SET_PED_CAN_TELEPORT_TO_GROUP_LEADER, _bodyguard, true); pressCnt++; isMarked = false; _bodyguards.Add(_bodyguard); if (pressCnt == 3) { Notification.Show($"{_bodyguards}"); } */ } } private void UpdatesThePositionOfTheBodyguardsBlips() { for (var i = 0; i < _bodyguards.Count; i++) { if (_bodyguardBlips[i] != null) { _bodyguardBlips[i].Position = _bodyguards[i].Position; if (_bodyguards[i].IsDead) { _bodyguardBlips[i].Delete(); } } } } private void ChecksTheIntegrityOfTheBodyguardAndRemovesHimFromTheGroupIfHeIsTooFarAwayFromThePlayer() { for (var i = 0; i < _bodyguards.Count; i++) { if (_bodyguards[i] != null) { if (_player.Position.DistanceTo2D(_bodyguards[i].Position) > 500f) { GTA .UI .Notification .Show($"[~b~{_bodyguards[i].Model}~w~] - kicked out of the group and excluded for being too far away from the player:", true); _pedGroup .Remove(_bodyguards[i]); _bodyguardBlips[i] .Delete(); _bodyguards.RemoveAt(i); } } } UpdatesThePositionOfTheBodyguardsBlips(); } private void DeleteBodyguardBlipsFromTheMap() { foreach (var bodyguardBlip in _bodyguardBlips) { if (bodyguardBlip != null) { bodyguardBlip.Delete(); } } } } }
it looked like this:
-
Thank you a lot, Niziul! I'll definitely try it after I get some sleep=)
-
The problem is that you only store one blip, even though you have 3 bodyguards. You can't delete 3 blips if you only remember one of them.
@Niziul said in Problem with bodyguard script:
ChecksTheIntegrityOfTheBodyguardAndRemovesHimFromTheGroupIfHeIsTooFarAwayFromThePlayer
Holy shit that is one hell of a function name.
-
@Jitnaught, @Niziul, @JohnFromGWN
Thank you so much for the help, guys! I haven't thought that someone in this forum can provide a complete solution and respond so quickly for my problem. You are the best!
The problem was indeed in isMarked variable and in storing only one blip. @Niziul, I checked your code, and works great, but I have some questions. If you have time, please, explain the following things:
-
Why are you keeping variables and functions private? As far as I understand, it is just a good practice to avoid calling these functions in another script. In this simple mod it is not required, but as mods gets bigger, we should use this command. Am I right?
-
Why you have created IList instead of just List?
-
How did you create a variable _pedGroup outside a function "CreateTheBodyguards" and then you used it that function? This may be a very stupid question, but when I did the same previously, C# couldn't recognize this variable until I declared it in a function directly.
-
What "aborted" keyword does mean? I only know onkeyup/onkeydown/ontick events.
-
-
@Niziul amazing help and you got a well deserved thank you.
-
@AlexSam all your questions are relevant, I'm glad you asked, we will both learn in the process
-
@AlexSam said in Problem with bodyguard script:
- Why are you keeping variables and functions private? As far as I understand, it is just a good practice to avoid calling these functions in another script. In this simple mod it is not required, but as mods gets bigger, we should use this command. Am I right?
that's basically it! I recommend never leaving anything in your code as public. If you need to make something accessible, use the "internal" access modifier.
Here is a link, if you want to get a complete understanding of access modifiers.- Why you have created IList instead of just List?
is because "IList<T>" comes with only the basics of what I need from a list class.
you can do a lot with just this:
- How did you create a variable _pedGroup outside a function "CreateTheBodyguards" and then you used it that function? This may be a very stupid question, but when I did the same previously, C# couldn't recognize this variable until I declared it in a function directly.
To be honest, I have no idea, since the variable declaration is basically the same. In your case you used this native one:
Function.Call(Hash.SET_PED_AS_GROUP_MEMBER, bodyguard, PlayerGroup);
Maybe you need something else for this native to work. Since the method I used is provided by the "PedGroup" class, this native is used as follows:
public void Add(Ped ped, bool leader) { Function.Call(leader ? Hash.SET_PED_AS_GROUP_LEADER : Hash.SET_PED_AS_GROUP_MEMBER, ped.Handle, base.Handle); }
- What "aborted" keyword does mean? I only know onkeyup/onkeydown/ontick events.
this is an event that is triggered every time your script is aborted. it is very important, because it helps you create functions that immediately remove something from the game if your script breaks. Avoiding headaches.
-
@Niziul, Wow! I didn't know about aborted usage, it will definitely help me! As for my third question, I don't know what I did, but this time it works correctly.
Many thanks for your help!