oxygenium
Компетентный
Всех приветствую. Еще в прошлом году создавал тему, где я рассказал об одном из способов реализовать инвентарь. В этой теме я внес определенные изменения и доработки, так же переписал часть кода. За исправления благодарю пользователей whale и Pa4enka. Более подробное объяснение по структуре можно посмотреть в прошлой теме: https://pawno-info.ru/threads/odin-iz-variantov-realizacii-sistemy-inventarja.324144/
Опять же оговорюсь - это не законченная система, а лишь вариант реализации.
Часть 1. Массивы.
Начнем все так же с основ. Для начала нам нужно создать ассоциативный массив со всеми предметами сервера. У игрока мы будем лишь хранить ID предмета и его количество. Все остальные данные, такие как модель, название и прочие характеристики мы будем хранить в одном массиве.
Теперь для удобства создадим еще один enum. Нужен он для того, чтобы не запоминать ID предмета (который по сути является ключом массива), а помнить простое название.
Теперь FUEL_CAN это 0, а MEDIC_KIT это 1, что в свою очередь является ключом массива. Теперь gItems[FUEL_CAN][Item_title] это то же самое, что и gItems[0][Item_title].
После этого нам необходимо хранить предметы в инвентаре игрока. Для этого создадим переменную инвентаря для каждого игрока с размером gItems.
Теперь мы просто можем так же обратиться к этому массиву по ключу и выполнить какое-либо действие - получить количество предмета, добавить или убавить предмет.
Пример: добавим игроку 2 канистры и отнимем 1 аптечку. Так же получим количество канистр.
Часть 2. Функции.
Весь код выше будет работать, но это не удобно. Поэтому давайте напишем функции для всех этих действий, как и предлагал whale. Начнем с добавления предметов.
Теперь добавим функцию для установки конкретного количества определенного предмета
Осталось еще пару функций. Добавляем функцию для отнимания предметов у игрока
Ну и последняя функция - узнаем количество предметов у игрока
Часть 3. Вывод данных.
Теперь нам нужно показать окно инвентаря игроку. Я буду делать это в диалоге с помощью команды.
Теперь нам нужно обработать этот диалог. За следующее решение и избавление от одного массива выражаю благодарность пользователю Pa4enka.
После чего обработаем действие
Часть 4. Загрузка из базы.
Я избавился от варианта со scanff и сделал реализацию по методу whale. По MySQL смотрите в прошлой теме. Тут покажу на примере SQLite.
Создаем таблицу "ITEMS" со столбцами "UID", либо можете вместо уникального ID использовать имя игрока, в данном случае это не важно, "ITEM_ID" и "COUNT". Для загрузки лучше сделать отдельную функцию.
И на последок функция для сохранения инвентаря. Опять же SQLite.
На этом все. Если будут какие-то ошибки - пишите в теме. Так же буду рад предложениям по улучшению и оптимизации.
Опять же оговорюсь - это не законченная система, а лишь вариант реализации.
Часть 1. Массивы.
Начнем все так же с основ. Для начала нам нужно создать ассоциативный массив со всеми предметами сервера. У игрока мы будем лишь хранить ID предмета и его количество. Все остальные данные, такие как модель, название и прочие характеристики мы будем хранить в одном массиве.
C-like:
enum E_ITEMS
{
Item_modelid,
Item_title[32]
};
new gItems[][E_ITEMS] =
{
{ 1650, "Канистра" },
{ 11736, "Аптечка" }
};
C-like:
enum
{
FUEL_CAN,
MEDIC_KIT
};
После этого нам необходимо хранить предметы в инвентаре игрока. Для этого создадим переменную инвентаря для каждого игрока с размером 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]);
Весь код выше будет работать, но это не удобно. Поэтому давайте напишем функции для всех этих действий, как и предлагал 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];
}
Теперь нам нужно показать окно инвентаря игроку. Я буду делать это в диалоге с помощью команды.
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;
}
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, "Вы заправили ближайший автомобиль");
}
}
}
}
}
Я избавился от варианта со 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;
}
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;
}
Чтобы видеть скрытое содержание Зарегистрируйтесь на форуме!
Чтобы видеть скрытое содержание Зарегистрируйтесь на форуме!
Последнее редактирование:
