Animal System on old CitizenMP server

Hey fivem staff and contributors,

First of all thank you for creating this awesome mod and enabling a customized GTA V experience! :herb:
For some reason (I just signed up) I could not post in the “Resource Development & Modding” section,
so I am sorry for posting it here.
I’ve set up an old citizenMP server for LAN purposes and it is a lot of fun after
I heavily customized it. But now just one thing is missiong for absolute satisfaction and joy: Animals.
I read that they were included in FXServer versions later which for some reason I cannot switch to.
I am aware the old citizenMP server is deprecated and you refrain any support but as I struggled to hard on this I have to ask you to get things going.
Is there a chance to extract the animal system out of the FXServer files and convert them to a .lua resource script?
I already created a script that spawns animals by myself but a proper animal system that emulates
the original wildlife of GTA V seems to be a complex task. Considering spawning birds with flying animations,
taking in account serveral spawn locations for certain animal types, random animations for grazing,
natural behavior, spawning outside the players sight and so on is too hard for me to do unfortunately.
As my server is nearly perfect now and it’s good fun to play with my friends it lacks a good amount
of life without animals. I would really like to go for a hunt in the snowy mountains together with my friends.
So can you help me with it please?

Kind regards
DMAddict

There’s no other choice other than updating to FXServer, unfortunately.

1 Like

Hi Xinerki and thanks for reply :sunny:
Can you please state why updating to FXServer is the only option?
I can’t do that ultimately :cry:
There are so many ways for code implementation in fiveM (javascript, C#, lua) so I thought there is surely a chance to port the wildlife to the citizemMP server.
Are you sure there is no way to do so?

Thanks :mountain_snow:

I found this resource which appears to be suitable for my purposes.
Does anyone know how to translate these two scripts to work with fiveM? :snail:

clientside.js

mp.events.add(
{
	"update_animal_death" : handle => {
        if (handle == null || handle.length == 0)
            return;
        mp.game.invoke("APPLY_DAMAGE_TO_PED", 0, handle, 110, true);
	},
	
	"update_animal_position" : handle => {
		mp.events.callRemote('update_animal_position', handle, handle.position);
	}
});

serverside.cs

namespace Matical.Hunting
{
    using GTANetworkAPI;
    using System;
    using System.Linq;
    using System.Collections.Generic;
    using System.Timers;

    public class Hunting : Script
    {
        public Hunting()
        {
        }

        public static List<HuntingAnimal> SpawnedAnimals = new List<HuntingAnimal>();
        private static Random Rnd = new Random();
        public static int Interval = 1000;

        // Defining of the animals.
        public enum Animals
        {
            Deer,
            Boar,
        }

        // Animals have three states, Grazing, Wandering, and Fleeing.
        public enum AnimalStates
        {
            Fleeing,
            Grazing,
            Wandering,
        }

        // Coordinates where animals can spawn. (Currently around Paleto Bay)
        public static Vector3[] AnimalSpawnPoints =
        {
            new Vector3(-1725.521, 4699.659, 33.80555),
            new Vector3(-1690.836, 4682.494, 24.47228),
            new Vector3(-1661.219, 4650.042, 26.12522),
            new Vector3(-1613.11, 4632.693, 46.37965),
            new Vector3(-1569.1, 4688.946, 48.04772),
            new Vector3(-1549.585, 4766.055, 60.47577),
            new Vector3(-1461.021, 4702.999, 39.26906),
            new Vector3(-1397.87, 4637.824, 72.12587),
            new Vector3(-617.851, 5762.557, 31.45378),
            new Vector3(-613.3984, 5863.435, 22.00531),
            new Vector3(-512.6949, 5940.441, 34.46115),
            new Vector3(-363.9753, 5921.967, 43.97315),
            new Vector3(-384.0528, 5866.263, 49.28809),
            new Vector3(-374.6584, 5798.462, 62.83068),
            new Vector3(-448.7513, 5565.69, 71.9878),
            new Vector3(-551.2087, 5167.825, 97.50465),
            new Vector3(-603.5089, 5154.867, 110.1652),
            new Vector3(-711.7279, 5149.544, 114.7229),
            new Vector3(-711.3442, 5075.649, 138.9036),
            new Vector3(-672.9759, 5042.516, 152.8032),
            new Vector3(-661.6283, 4974.586, 172.7258),
            new Vector3(-600.277, 4918.438, 176.7214),
            new Vector3(-588.3793, 4889.981, 181.3767),
            new Vector3(-549.8376, 4838.274, 183.2239),
            new Vector3(-478.639, 4831.655, 209.2594),
            new Vector3(-399.3954, 4865.303, 203.7752),
            new Vector3(-411.9441, 4946.082, 177.4535),
            new Vector3(-414.8653, 5074.294, 149.0627),
        };

        [Command("pickup", "~y~/pickup")]
        public void CMD_PICKUP(Client sender)
        {
            foreach (var animal in SpawnedAnimals)
            {
                if (sender.Position.DistanceTo(NAPI.Entity.GetEntityPosition(animal.handle)) < 2.0)
                {
                    // Checks if the animal is dead or dying.
                    bool isDead = NAPI.Native.FetchNativeFromPlayer<bool>(sender, Hash.IS_PED_DEAD_OR_DYING, animal.handle, true);
                    if (isDead)
                    {
                        // Removes and spawns a new animal.
                        animal.Respawn();
                    }
                    else
                    {
                        NAPI.Chat.SendChatMessageToPlayer(sender, "~r~This animal isn't dead!");
                    }
                    return;
                }
            }
        }
		
		[RemoteEvent("update_animal_position")]
		public void UpdateAnimalPosition(Client player, params object[] arguments)
		{
			NAPI.Entity.SetEntityPosition((NetHandle)arguments[0], (Vector3)arguments[1]);
		}

        public static Vector3 RandomFarawayDestination(Vector3 currentPos)
        {
            var choices = Array.FindAll(AnimalSpawnPoints, spawn => spawn.DistanceTo(currentPos) > 5);
            return choices[Rnd.Next(choices.Length)];
        }

        public void OnScriptStart()
        {
            int i = 0;
            foreach (var spawn in AnimalSpawnPoints)
            {
                if (i == 0)
                {
                    new HuntingAnimal(spawn, Animals.Deer, AnimalStates.Wandering).updateState = true;
                    i = 1;
                }
                else
                {
                    new HuntingAnimal(spawn, Animals.Boar, AnimalStates.Wandering).updateState = true;
                    i = 0;
                }
            }
            NAPI.Util.ConsoleOutput("[HUNTING] Created " + SpawnedAnimals.Count + " animals.");
        }

        public class HuntingAnimal
        {
            public Ped handle;
            public Vector3 Spawn;
            public Animals Type;
            public AnimalStates State;

            public Timer stateTimer;
            public int stateChangeTick;
            public Vector3 Destination;
            public bool updateState;
            public Client FleeingPed;

            // Handles each animal and it's current state.
            public HuntingAnimal(Vector3 spawn, Animals type, AnimalStates state)
            {
                handle = NAPI.Ped.CreatePed((type == Animals.Deer) ? (PedHash.Deer) : (PedHash.Boar), spawn, 0, 0);

                Spawn = spawn;
                Type = type;
                State = state;

                stateTimer = new Timer()
                {
                    Interval = Interval,
                    AutoReset = true
                };
                stateTimer.Elapsed += delegate { AnimalAi(this); };
                stateTimer.Start();
                NAPI.Entity.SetEntityPositionFrozen(handle, false);
                SpawnedAnimals.Add(this);
            }

            // Handles the animals position relative to the server and deals with state updating.
            public void AnimalAi(HuntingAnimal animal)
            {
                if (handle == null)
                    return;

                List<Client> playersInRadius = new List<Client>();

                foreach (var player in NAPI.Pools.GetAllPlayers())
                {
                    if (player == null)
                        continue;

                    // Every player within the radius will be added to a list of clients.
                    if (player.Position.DistanceTo(NAPI.Entity.GetEntityPosition(handle)) <= 100)
                    {
                        playersInRadius.Add(player);
                    }
                }

                if (playersInRadius.Count == 0)
                {
                    return;
                }
                NAPI.ClientEvent.TriggerClientEvent(playersInRadius[0], "update_animal_position", handle);

                // Syncs the animals death to every player nearby.
                foreach (var player in playersInRadius)
                {
                    bool isDead = NAPI.Native.FetchNativeFromPlayer<bool>(player, Hash.IS_PED_DEAD_OR_DYING, handle, true);
                    if (isDead)
                    {
                        foreach (var p in playersInRadius)
                            NAPI.ClientEvent.TriggerClientEvent(p, "update_animal_death", this.handle);
                    }
                }

                // If there are players near the animal, and it's not already fleeing, then make it flee.
                if (playersInRadius.Count > 0 && State != AnimalStates.Fleeing)
                {
                    State = AnimalStates.Fleeing;
                    FleeingPed = playersInRadius.First();
                    updateState = true;
                }

                stateChangeTick++;

                // Automatic state updating.
                if (State != AnimalStates.Fleeing)
                {
                    if (stateChangeTick > 15)
                    {
                        var nextStateChance = Rnd.Next(100);
                        if (nextStateChance < 30)
                        {
                            State = AnimalStates.Grazing;
                            NAPI.Ped.PlayPedScenario(handle, Type == Animals.Deer ? "WORLD_DEER_GRAZING" : "WORLD_PIG_GRAZING");
                        }
                        else
                        {
                            State = AnimalStates.Wandering;
                            Destination = RandomFarawayDestination(NAPI.Entity.GetEntityPosition(handle));
                            updateState = true;
                        }
                        stateChangeTick = 0;
                    }
                }
                else
                {
                    // Make the animal stop fleeing and go back to grazing the land.
                    if (stateChangeTick > 20)
                    {
                        State = AnimalStates.Grazing;
                        NAPI.Ped.PlayPedScenario(handle, Type == Animals.Deer ? "WORLD_DEER_GRAZING" : "WORLD_PIG_GRAZING");
                        stateChangeTick = 0;
                    }
                }

                if (updateState != true)
                    return;
                switch (State)
                {
                    case AnimalStates.Fleeing:
                        foreach (var p in playersInRadius)
                        {
                            NAPI.Native.SendNativeToPlayer(p, Hash.TASK_SMART_FLEE_PED, handle, FleeingPed.Handle, 75f, 5000, 0, 0);
                        }
                        break;

                    case AnimalStates.Grazing:
                        NAPI.Ped.PlayPedScenario(handle, Type == Animals.Deer ? "WORLD_DEER_GRAZING" : "WORLD_PIG_GRAZING");
                        break;

                    case AnimalStates.Wandering:
                        foreach (var p in playersInRadius)
                        {
                            NAPI.Native.SendNativeToPlayer(p, Hash.TASK_WANDER_IN_AREA, handle, Destination.X, Destination.Y, Destination.Z, 25, 0, 0);
                        }
                        break;

                }
                updateState = false;
            }

            // Removes the dead animal and spawns a new one.
            public void Respawn()
            {
                if (NAPI.Entity.DoesEntityExist(handle))
                    NAPI.Entity.DeleteEntity(handle);

                handle = NAPI.Ped.CreatePed((Type == Animals.Deer) ? (PedHash.Deer) : (PedHash.Boar), Spawn, 0, 0);
            }
        }
    }
}

lmao this is gtmp and ■■■■■■ code
its hard tho, i cant do it sorry

No, this has nothing to do with ‘enabling animals’. Animal population is a native GTA feature and you should update your client version to enable it.

Thanks for info. I cannot update unfortunately as it is customized for LAN purposes only. Do you know which files in the new client version handle it? Maybe I can update my Client manually.
I noticed that when I click “Leave Session” in the Lambda menu the animal system starts working instantly.
Otherwise I need to implement a animal system serverside.

Howdy,

I’ve tried to merge some existing scripts to emulate the wildlife with a few lines of code
which can only be a cheap copy of the wildlife from GTA Online where you can enjoy
flying birds, fish, grazing deer with random animation states and so on.
But at least it is something…

Currently I am stuck as I don’t know how to realize the next essential steps:

  1. :eyeglasses: Spawning animals out of sight only though not disappearing while using SetEntityAsNoLongerNeeded()
  2. :deer: Randomly playing animations like grazing, drinking, lying in the grass
  3. :running_man: Fleeing from players while at a certain distance. Right now they just flee when you shoot and they’re wandering in one direction endlessly (due to TaskWanderStandard(ped, 10.0, 10)).
  4. :bird: Btw. does anyone know how to spawn flying birds?
  5. :books: Instead of putting the spawned animals into a table and stopping the script when 15 animals are spawned
    it would be nice if there was a function that counts nearby animals around the player so that there is
    always a certain amount of animals around the players. I commented such a function in the script as I didn’t
    get it to work for me.

I commented some tasks and setters as I noticed little to no effect.
Would be so glad if somebody could help me with it.

Some random screens

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.