Доступ к готовым решениям

Переход в группу "Пользователь"

300.00 ₽
Одноразовый платёж
Быстрый переход в группу "Пользователи", без надобности написания постов и ожидания.

Покупка дает возможность:
Быть полноправным участником форума
Нормальное копирование кода
Создавать темы
Скачивать файлы
Доступ к архиву Pawno-Info

Мануал Система инвентаря (Версия 2.0)

oxygenium

Компетентный
Всех приветствую. Еще в прошлом году создавал тему, где я рассказал об одном из способов реализовать инвентарь. В этой теме я внес определенные изменения и доработки, так же переписал часть кода. За исправления благодарю пользователей whale и Pa4enka. Более подробное объяснение по структуре можно посмотреть в прошлой теме: https://pawno-info.ru/threads/odin-iz-variantov-realizacii-sistemy-inventarja.324144/

Опять же оговорюсь - это не законченная система, а лишь вариант реализации.

Часть 1. Массивы.

Начнем все так же с основ. Для начала нам нужно создать ассоциативный массив со всеми предметами сервера. У игрока мы будем лишь хранить ID предмета и его количество. Все остальные данные, такие как модель, название и прочие характеристики мы будем хранить в одном массиве.


C-like:
enum E_ITEMS
{
    Item_modelid,
    Item_title[32]
};

new gItems[][E_ITEMS] = 
{
    { 1650, "Канистра" },
    { 11736, "Аптечка" }
};
Теперь для удобства создадим еще один enum. Нужен он для того, чтобы не запоминать ID предмета (который по сути является ключом массива), а помнить простое название.

C-like:
enum
{
    FUEL_CAN,
    MEDIC_KIT
};
Теперь FUEL_CAN это 0, а MEDIC_KIT это 1, что в свою очередь является ключом массива. Теперь gItems[FUEL_CAN][Item_title] это то же самое, что и gItems[0][Item_title].

После этого нам необходимо хранить предметы в инвентаре игрока. Для этого создадим переменную инвентаря для каждого игрока с размером gItems.

C-like:
new playerItems[MAX_PLAYERS][sizeof gItems];
Теперь мы просто можем так же обратиться к этому массиву по ключу и выполнить какое-либо действие - получить количество предмета, добавить или убавить предмет.

Пример: добавим игроку 2 канистры и отнимем 1 аптечку. Так же получим количество канистр.

C-like:
playerItems[playerid][FUEL_CAN] += 2;
playerItems[playerid][MEDIC_KIT]--;
printf("У игрока %i канистр", playerItems[playerid][FUEL_CAN]);
Часть 2. Функции.

Весь код выше будет работать, но это не удобно. Поэтому давайте напишем функции для всех этих действий, как и предлагал whale. Начнем с добавления предметов.

C-like:
stock AddPlayerItem(playerid, itemid, count)
{
    // Реализуем так же простую проверку на корректный ID предмета
    if(itemid > sizeof gItems) return 0;

    // Если случайно ввели отрицательное число - умножим его на -1, чтобы получить знак "+"
    if(count < 0) count *= -1;

    // Добавляем предметы игроку
    playerItems[playerid][itemid] += count;
    return 1;
}
Теперь добавим функцию для установки конкретного количества определенного предмета

C-like:
stock SetPlayerItemCount(playerid, itemid, count)
{
    // Реализуем так же простую проверку на корректный ID предмета
    if(itemid > sizeof gItems) return 0;

    // Если случайно ввели отрицательное число - сделаем его равным 0
    if(count < 0) count = 0;

    // Устанавливаем количество предметов для игрока
    playerItems[playerid][itemid] = count;
    return 1;
}
Осталось еще пару функций. Добавляем функцию для отнимания предметов у игрока

C-like:
stock RemovePlayerItem(playerid, itemid, count)
{
    // Реализуем так же простую проверку на корректный ID предмета
    if(itemid > sizeof gItems) return 0;

    // Если случайно ввели отрицательное число - сделаем его равным 0
    if(count < 0) count = 0;

    // Предотвратим удаление большего количества предметов, чем есть у игрока
    if((playerItems[playerid][itemid] - count) < 0) return 0;

    // Отнимаем предметы
    playerItems[playerid][itemid] -= count;
    return 1;
}
Ну и последняя функция - узнаем количество предметов у игрока

C-like:
stock GetPlayerItemCount(playerid, itemid)
{
    // Реализуем так же простую проверку на корректный ID предмета
    if(itemid > sizeof gItems) return 0;

    // Возвращаем количество предметов
    return playerItems[playerid][itemid];
}
Часть 3. Вывод данных.

Теперь нам нужно показать окно инвентаря игроку. Я буду делать это в диалоге с помощью команды.

C-like:
cmd:items(playerid)
{   
    // Количество предметов умножаем на 32 (количество ячеек в названии) и 6 (количество предметов, увеличьте или уменьшите при необходимости)
    // 20 - это длина строки для загловка (номер, предмет и количество)
    new dialogString[sizeof gItems * (32 + 6) + 20];

    // Переменная, которая будет отвечать за нумерацию и общее количество предметов у игрока
    new playerItemsCount = 0;

    // Запустим цикл с количеством итераций, равному общему количеству объектов инвентаря
    for(new i = 0; i != sizeof gItems; i++)
    {
        // Если у игрока нет предмета - пропускаем итерацию
        if(GetPlayerItemCount(playerid, i) == 0) continue;

        // Если есть - считаем его
        playerItemsCount++;

        // И записываем в переменную dialogString
        format(dialogString, sizeof dialogString, "%s\n%i\t%s\t%i\n", dialogString, playerItemsCount, gItems[i][Item_title], GetPlayerItemCount(playerid, i));

        /*
            P.S. Да, для gItems[i][Item_title] тоже можно было сделать функцию,
            но я не хочу возвращать строку, либо же создавать еще одну переменную для сохранения в нее названия
        */
    }

    // Если цикл прошел все итерации, а общее количество предметов так и не прибавилось - покажем игроку сообщение
    if(playerItemsCount == 0)
        return SendClientMessage(playerid, COLOR_GREEN, "Информация: "H_COLOR_WHITE"У Вас нет ни одного предмета");

    // Эта часть кода вызовется, если все же у игрока есть хотя бы один предмет

    // Запишем в переменную dialogString заголовок для диалога
    strins(dialogString, "#\tПредмет\tКол-во\n", 0);

    // Выводим диалог, не забываем менять 9000 на свой ID диалога
    ShowPlayerDialog(playerid, 9000, DIALOG_STYLE_TABLIST_HEADERS, "Инвентарь", dialogString, "Выбрать", "Отмена");
    return 1;
}
Теперь нам нужно обработать этот диалог. За следующее решение и избавление от одного массива выражаю благодарность пользователю Pa4enka.

C-like:
if(dialogid == 2000)
{
    // Если пользователь нажал "Отмена", то просто не будем ничего делать
    if(!response) return 1;

    // Создадим переменную, в которую будет записан ID предмета, который выбрал игрок
    new itemid;

    // Опять прогоним цикл по всем предметам
    for(new i = 0; i != sizeof gItems; i++)
    {
        // Опять пропустим итерацию, если предметов нет
        if(GetPlayerItemCount(playerid, i) == 0) continue;

        // Если itemid не совпадает с listitem, то увеличиваем itemid на 1 и идем дальше
        if(itemid != listitem)
        {
            itemid++;
            continue;
        }

        // Если же itemid все же совпал с listitem, присвоем ему значение i, что по сути ID предмета
        itemid = i;
        break;
    }   

    // После всего этого в itemid у нас будет нужный ID предмета

    printf("Игрок выбрал предмет ID %i (%s)", itemid, gItems[itemid][Item_title]);

    // Установим PVar, чтобы передать ID предмета в следующий диалог
    SetPVarInt(playerid, "SelectedItemID", itemid);

    ShowPlayerDialog(playerid, 9001, DIALOG_STYLE_LIST, gItems[itemid][Item_title], "Использовать\nВыбросить", "Выбрать", "Отмена");
}
После чего обработаем действие

C-like:
if(dialogid == 9001)
{
    if(!response)
    {
        // Тут можете вызвать команду еще раз, чтобы показать инвентарь после дропа, для удобства игрока
        // PC_EmulateCommand(playerid, "inventory");

        // Удалим PVar
        DeletePVar(playerid, "Selecteditemid");
        return 1;
    }

    // Получим ID предмета и удалим PVar
    new itemid = GetPVarInt(playerid, "Selecteditemid");
    DeletePVar(playerid, "Selecteditemid");

    // Теперь в itemid записан ID предмета, который выбрал игрок
    // В listitem - действите

    // Дальше делайте все, на что способна ваша фантазия
    // Приведу простой пример

    switch(listitem)
    {
        case 0:
        {

        }
        case 1:
        {
            // Действие - использовать
            
            switch(itemid)
            {
                case FUEL_CAN:
                {
                    RemovePlayerItem(playerid, itemid, 1);
                    new vehicleid = GetNearestVehicle(playerid);
                    AddVehicleFuel(vehicleid, 40);
                    SendClientMessage(playerid, -1, "Вы заправили ближайший автомобиль");
                }
            }
        }
    }
}
Часть 4. Загрузка из базы.

Я избавился от варианта со scanff и сделал реализацию по методу whale. По MySQL смотрите в прошлой теме. Тут покажу на примере SQLite.

Создаем таблицу "ITEMS" со столбцами "UID", либо можете вместо уникального ID использовать имя игрока, в данном случае это не важно, "ITEM_ID" и "COUNT". Для загрузки лучше сделать отдельную функцию.

C-like:
stock LoadPlayerItems(playerid)
{
    new query[64], DBResult:dbResult;
    format(query, sizeof query, "SELECT * FROM ITEMS WHERE UID = '%q'", playerData[playerid][uid]);

    // Выполняем запрос к базе
    dbResult = db_query(dbHandle, query);

    // Если есть какие-либо записи
    if(db_num_rows(dbResult))
    {
        // Создадим переменные для хранения а так же дальнейшей записи в массив игрока ID предмета и количества
        new itemid, itemcount;

        // Цикл будет выполняться, пока есть записи в базе
        // Подробней смотрите в документации к вашему плагину
        do
        {   
            // Получаем данные
            itemid = db_get_field_assoc_int(dbResult, "ITEM_ID");
            itemcount = db_get_field_assoc_int(dbResult, "COUNT");

            // Если в базе каким-то образом оказался 0 - пропускаем итерацию
            if(itemcount == 0) continue;

            // Устанавливаем количество предмета
            SetPlayerItemCount(playerid, itemid, itemcount);
        }
        while(db_next_row(dbResult));
    }

    db_free_result(dbResult);
    return 1;
}
И на последок функция для сохранения инвентаря. Опять же SQLite.

C-like:
stock SavePlayerItems(playerid)
{
    // Опять же - по функциям БД смотрите документацию к вашему плагину
    new query[128], DBResult:dbResult;

    // Проходимся циклом по общему массиву предметов
    for(new i = 0; i != sizeof gItems; i++)
    {   
        // Пропустим итерацию, если сохранять нечего
        if(GetPlayerItemCount(playerid, i) == 0) continue;

        // Проверим, есть ли в базе запись с этим ID предмета
        format(query, sizeof query, "SELECT COUNT FROM ITEMS WHERE UID = '%q' AND ITEM_ID = %i", playerData[playerid][uid], i);
        dbResult = db_query(dbHandle, query);

        if(db_num_rows(dbResult))
        {
            // Если запись есть - выполняем запрос UPDATE
            format(query, sizeof query, "UPDATE ITEMS SET COUNT = %i WHERE UID = '%q' AND ITEM_ID = %i",
                GetPlayerItemCount(playerid, i),
                playerData[playerid][uid],
                i);
            dbResult = db_query(dbHandle, query);
        }
        else
        {
            // Если записи нет - выполняем запрос UPDATE
            format(query, sizeof query, "INSERT INTO ITEMS (UID, ITEM_ID, COUNT) VALUES ('%q', %i, %i)",
                playerData[playerid][uid],
                i,
                GetPlayerItemCount(playerid, i));
            dbResult = db_query(dbHandle, query);
        }

        db_free_result(dbResult);
    }
    return 1;
}
На этом все. Если будут какие-то ошибки - пишите в теме. Так же буду рад предложениям по улучшению и оптимизации.


Чтобы видеть скрытое содержание Зарегистрируйтесь на форуме!


Чтобы видеть скрытое содержание Зарегистрируйтесь на форуме!
​
 
Последнее редактирование:
Сверху Снизу