• Из-за обновления GTA 5 (был добавлен новый патч) может временно не работать вход в RAGE Multiplayer.

    Ошибка: Ваша версия Grand Theft Auto V не поддерживается RAGE Multiplayer.
    ERROR: Your game version is not supported by RAGE Multiplayer.

    Данная ошибка говорит о том, что GTA V обновилась до новой версии (GTA Online тоже). Вам необходимо обновить саму игру в главном меню вашего приложения (Steam / Epic Games / Rockstar Games).
    Если после этого RAGE:MP все равно не работает - вам нужно дождаться выхода патча для самого мультиплеера (обычно это занимает от нескольких часов до нескольких дней).

    Новости и апдейты Rockstar Games - https://www.rockstargames.com/newswire/
    Статус всех служб для Rockstar Games Launcher и поддерживаемых игр: https://support.rockstargames.com/ru/servicestatus


    Grand Theft Auto 5 (+ GTA Online) последний раз были обновлены:

Мануал Урок FAQ Создание инвентаря для RAGE MP на C# (only server-side)

Cold

Участник портала
9 Окт 2022
46
22
46
Увидел что одному форумчанину нужна помощь и он не представляет за что браться и как создать инвентарь, я сам с таким когда то столкнулся и решил написать тему по созданию простого инвентаря, сам мучался когда то, но умнее этого пока в голову не пришло...
За помощь в создание инвентаря очень благодарен Coveragt, спасибо тебе друг)
Отдельное спасибо Mip & Pesok, тоже многому меня научили и подсказали, реально классные пацаны)
p.s
Это не бестпрактис, за достойную критику ставлю лайк ;)
А за ошибки в орфографии будьте добры не писать :love:


Сразу вставляю видеоролик чтоб не тратить ваше время, кому не интересен данный результат

И так открываем среду разработки, у меня это Rider но роли не играет, надеюсь у вас все настроено
Создаем в Server side проекте, директорию Inventory, внутри нее создаем директорию Items, это будут наши предметы
Начнем с абстрактного класса Item, создаем его и добавляем нужные нам поля


C#:
using GTANetworkAPI;

namespace RageMP.Server.Inventory
{
    public abstract class Item
    {
        public abstract string IconName { get; set; } // имя иконки которая будет отображаться на фронте
        public abstract string Description { get; set; } // описание для фронта
        public abstract string Name { get; set; } // имя нашего предмета
        public abstract int Size { get; set; }   // размер предмета, сколько он будет занимать слотов, если у вас будет грид сетка, я думаю вам нужно будет 2 переменные, сайз по вертикали и сайз по горизонтали
        public abstract int Amount { get; set; } // чтобы в будущем стакать предметы
        public abstract uint SpawnObjectHash { get; set; } // хеш объекта который будет спавниться на земле
        public abstract void Use(Player player, int indexRemove); // действие, если это гамбургер то здесь запускается анимация, добавляются какие либо игроку свойства, например сытость +15, все увидете позже, ну и естественно удаление предмета или выдача физического оружия если это оружие
    }
}
Прежде чем создать даже элементарный бургер, нам понадобится еще пару классов)
Класс Util статический, для получения инвентаря игрока, в следующем фрагменте будет использоваться, они взаимосвязаны, поэтому без разницы что первое будете читать, Util.cs || Inventory.cs
C#:
 public static class Util
    {
        //получение инвентаря игрока
        public static Inventory GetPlayerInventory(Player player)
        {
            return player.GetData<Inventory>(nameof(Inventory));
        }

        //из объекта гта5 который на полу лежит, физический, достать Item
        public static Item GetObjectItem(Object obj)
        {
            return obj.GetData<Item>(nameof(Item));
        }

        //в радиусе найти ближайшие объекты
        public static List<Object> GetObjectsInRadiusOfPosition(double radius, Vector3 position, uint dim)
        {
            return NAPI.Pools.GetAllObjects().Where(p => p.Dimension == dim).Where(FilterByRadius).ToList();

            bool FilterByRadius(Object customObject)
            {
                if (customObject != null)
                {
                    return Math.Pow(position.X - customObject.Position.X, 2.0) +
                        Math.Pow(position.Y - customObject.Position.Y, 2.0) +
                        Math.Pow(position.Z - customObject.Position.Z, 2.0) < Math.Pow(radius, 2.0);
                }

                return false;
            }
        }
    }

Теперь инвентарь, управление, здесь у нас будет лут игрока и лут который на всем сервере
Inventory.cs
C#:
public class Inventory
{
    public List<Item> Items { get; set; } = new List<Item>(); // player inventory
    public static List<Item> LootServer = new List<Item>(); // server inventory

    //Когда игрок забирает с пола, мы удаляем из коллекции, по индексу
    public static void RemoveLootObject(int index)
    {
        LootServer.RemoveAt(index);
    }

    //Когда игрок выкидывает на пол то заспавнить объект + положить внутрь листа нашего
    public static Object SpawnLootObject(Vector3 position, Item item, uint dim = 0)
    {
        LootServer.Add(item);
        var obj = NAPI.Object.CreateObject(item.SpawnObjectHash, position + new Vector3(0f, 0f, -0.90f),
            new Vector3(90f, 90f, 90f),
            255, 0);
        obj.SetData(nameof(Item), item);
        obj.SetData("dataNameItem", NAPI.TextLabel.CreateTextLabel(item.Name,
            position + new Vector3(0f, 0f, -0.55f), 2.0f, 0.1f, 4,
            new Color(255, 255, 255), true, 0));
        obj.GetData<TextLabel>("dataNameItem").Text = item.Name;
        return obj;
    }
   
// находим объекты в радиусе 1.5 метра, получаем инвентарь игрока, проверки индекса, можете их удалить и свои проверки, я кста вообще здесь минимум делал проверок во всем коде, что не есть хорошо но у вас свои ручки и напишите решение
    public void pickItem(Player player, int index)
    {
        var objects = Util.GetObjectsInRadiusOfPosition(1.5, player.Position, player.Dimension);
        var Items = Util.GetPlayerInventory(player).Items;
       
        if (index >= 0 && index < objects.Count && objects.ElementAt(index) != null)
        {
            var obj = objects.ElementAt(index);
           
            if (obj == null || obj.IsNull || !obj.Exists) // жоская проверка на null XD
            {
                Console.Write("Object is null");
                return;
            }

            var item = Util.GetObjectItem(obj);
            Items.Add(item);
            RemoveLootObject(index);
            obj.GetData<GTANetworkAPI.TextLabel>("dataNameItem").Text = "";
            obj.Delete();
            updateIndexItems();
        }
    }
   
    public void spawnItem(Player player, int index)
    {
        Item item = null;
        switch (index)
        {
            case 1:
                item = new Burger();
                break;
            case 2:
                item = new Cola();
                break;
            default:
                player.SendNotification("~r~Предмет не найден с данным индексом!");
                return;
        }

        SpawnLootObject(player.Position, item);
    }
    //выкидываем на пол + спавним объект, у себя удаляем а в лист сервера добавляем
    public void dropItem(Player player, int index)
    {
        var Items = Util.GetPlayerInventory(player).Items;
        RageMP.Server.Inventory.Inventory.SpawnLootObject(player.Position, Items[index]);
        Items.RemoveAt(index);
        updateIndexItems();
    }
   
 
    public void checkMyInventory(Player player)
    {
        var inventory = Util.GetPlayerInventory(player);
        int i = 0;
        player.SendChatMessage("--------------------");
        foreach (var item in inventory.Items)
        {
            player.SendChatMessage($"~g~Your: {item.Name} [{i++}]");
        }

        player.SendChatMessage("--------------------");
    }

    public void updateIndexItems()
    {
        Items = Items.Where(x => x != null).ToList(); // иногда индекс сбивается и по 0 уже ничего нет, начинается с 1, такой вот костыль
    }
}
|
Думаю понятно
Не отходя от кассы давайте создадим бургер, люблю зингеры)
И так новая директория в директории Inventory будет называться Food
Внутри создаем класс Burger который реализует : Item


C#:
public class Burger : Item
{
    public override string IconName { get; set; } = "Burger.png";
    public override string Description { get; set; } = "Увеличивает сытость на 15%";
    public override string Name { get; set; } = "Большой бургер";
    public override int Size { get; set; } = 1;
    public override int Amount { get; set; } = 1; // Текущее кол-во бургеров, чтоб стакать
    public override uint SpawnObjectHash { get; set; } = 2240524752;
    public override void Use(Player player, int indexRemove)
    {
        player.PlayAnimation("mp_player_inteat@burger", "mp_player_int_eat_burger", 49);
        var inven = Util.GetPlayerInventory(player);
       
        if (inven.Items[indexRemove] != null && indexRemove >= 0 && inven.Items.Count > indexRemove)
        {
            if (inven.Items[indexRemove].Name.Length > 0)
            {
                inven.Items.RemoveAt(indexRemove);
            }
        }
       
        NAPI.Task.Run(() =>
        {
            if (player.GetData<int>("eatIcon") + 15 > 100)
            {
                player.SetData("eatIcon", 100);
            } else {
                player.SetData("eatIcon", player.GetData<int>("eatIcon") + 15);
            }
            player.SendNotification("~g~Сыт на " + player.GetData<int>("eatIcon") + "%");
            player.StopAnimation();
        },1500);
    }
}
Подобнее проворачиваем с колой) кудаж бургер без колы

C#:
public class Cola : Item
{
    public override string IconName { get; set; } = "cola.png";
    public override string Description { get; set; } = "Увеличивает водный баланс на 15%";
    public override string Name { get; set; } = "Cola 0.33";
    public override int Size { get; set; } = 1;
    public override int Amount { get; set; } = 1;
    public override uint SpawnObjectHash { get; set; } = 1020618269;
    public override void Use(Player player, int indexRemove)
    {
        player.PlayAnimation("mp_player_intdrink", "loop_bottle", 49);
        var inven = Util.GetPlayerInventory(player);
       
        if (inven.Items[indexRemove] != null && indexRemove >= 0 && inven.Items.Count > indexRemove)
        {
            if (inven.Items[indexRemove].Name.Length > 0)
            {
                inven.Items.RemoveAt(indexRemove);
            }
        }
       
        NAPI.Task.Run(() =>
        {
            if (player.GetData<int>("waterIcon") + 25 >= 100)
            {
                player.SetData("waterIcon", 100);
            } else {
                player.SetData("waterIcon", player.GetData<int>("waterIcon") + 25);
            }
           
            player.SendNotification("~g~Водный баланс " + player.GetData<int>("waterIcon") + "%");
            player.StopAnimation();
        },1500);
       
    }
}
Теперь нам нужно будет проициализировать инвентарь игрока, чтоб он имелся и можно было взаимодействовать, в любой cs файл который наследуется от Script на стороне сервера, мы добавим

C#:
  [ServerEvent(Event.PlayerSpawn)]
    public void OnPlayerSpawn(Player player)
    {
        Inventory inventory = new Inventory();
        inventory.Items.Add(new Burger());
        player.SetData(nameof(Inventory), inventory);
        player.SetData("eatIcon", 10);
        player.SetData("waterIcon", 9);
    }
Ну и остался последний штрих, тк мы не юзаем фронт, то все будет через Command , новый файл LootManager : Script , сразу наследуемся от Script

C#:
public class LootManager : Script
{
    [Command("drop")]
    public void dropItem(Player player, int index)
    {
        RageMP.Server.Inventory.Inventory inventory = new RageMP.Server.Inventory.Inventory();
        inventory.dropItem(player, index);
    }

    [Command("pick")]
    public void pickItem(Player player, int index)
    {
        RageMP.Server.Inventory.Inventory inventory = new RageMP.Server.Inventory.Inventory();
        inventory.pickItem(player, index);
    }

    [Command("spawn")]
    public void spawnItem(Player player, int index)
    {
        RageMP.Server.Inventory.Inventory inventory = new RageMP.Server.Inventory.Inventory();
        inventory.spawnItem(player, index);
    }

    // посмотреть что вокруг имеется
    [Command("check")]
    public void checkItems(Player player)
    {
        var objects = Util.GetObjectsInRadiusOfPosition(1.5, player.Position, player.Dimension);
        int i = 0;
        player.SendChatMessage("--------------------");
        foreach (var obj in objects)
        {
            var item = Util.GetObjectItem(obj);
            player.SendChatMessage($"~b~Around: {item.Name} [{i++}]");
        }

        player.SendChatMessage("--------------------");
    }

    [Command("myinv")]
    public void checkMyInventory(Player player)
    {
        RageMP.Server.Inventory.Inventory inventory = new RageMP.Server.Inventory.Inventory();
        inventory.checkMyInventory(player);
    }

    [Command("use")]
    public void useItem(Player player, int index)
    {
        var inventory = Util.GetPlayerInventory(player);
        inventory.Items[index].Use(player, index);
        inventory.updateIndexItems();
    }
}
 

Vermilion

Высший разум
High developer
BackEnd developer
FrontEnd developer
29 Сен 2021
1,285
756
181
34
За мануал - лайк (y)
За музыку на фоне - дизлайк (n)
 
Реакции: seaniwe7 и Cold

heyline11

Новый участник
31 Май 2024
10
4
15
Спасибо большое еще раз дружище! Завтра сяду делать инвентарь!
 

X-Clusiv

Модератор
Команда форума
Moderator
BackEnd developer
4 Окт 2020
708
319
161
30
Двойной лайк)
 
  • RoflanEbalo
Реакции: Vermilion

Cold

Участник портала
9 Окт 2022
46
22
46
Спасибо большое еще раз дружище! Завтра сяду делать инвентарь!
Пожалуйста мой золотой, как там успехи? Если желаешь, я могу еще написать мануал, про фронтик изичный на VueJS или же имеется драг дроп на JS от одного форумчанина и на нем показать инвентарь, как данные отправлять и принимать
 
Реакции: youngBeaver

heyline11

Новый участник
31 Май 2024
10
4
15
Пожалуйста мой золотой, как там успехи? Если желаешь, я могу еще написать мануал, про фронтик изичный на VueJS или же имеется драг дроп на JS от одного форумчанина и на нем показать инвентарь, как данные отправлять и принимать
Привет!
Пока занимаюсь серверной частью на JS. Думаю, что мануал по фронтенду на VueJS и драг-н-дроп на JS будет очень полезен! Если не трудно, напиши их, пожалуйста. Буду благодарен!
 

Cold

Участник портала
9 Окт 2022
46
22
46
Привет!
Пока занимаюсь серверной частью на JS. Думаю, что мануал по фронтенду на VueJS и драг-н-дроп на JS будет очень полезен! Если не трудно, напиши их, пожалуйста. Буду благодарен!
постараюсь в ближайшее время уделить время ;) скину ссыль как будет готово
 

heyline11

Новый участник
31 Май 2024
10
4
15
постараюсь в ближайшее время уделить время ;) скину ссыль как будет готово
Привет!

Пока примерно так *тык
Также можно ставить в быстрые слоты через те же команды в чате, на 1 и 2 слоты и доставать их
 
Реакции: X-Clusiv и youngBeaver

F0D1

Активный участник
13 Апр 2021
198
32
86
18
+ Уши на фоне