- Версия мультиплеера
- SA:MP 0.3.7
- open.mp
PawnMap - плагин для создания мап в pawn
Основная задача была: сделать максимально понятную и производительную мапу для Pawn, насколько это было возможно.
Чтобы её можно было использовать в обычных системах, при этом чтобы она сильно не уступала сырым массивам pawn, а там, где нужна какая-то операция с данными (поиск, сортировка), была и вовсе быстрее.
Вся подробная документация с примерами находится в моем репозитории>> https://github.com/i-Saibot/PawnMap/wiki
Тут я только вкратце покажу список функций и пару примеров:
Pawn:
// Функции
Map_Create- Создает новый экземпляр мапы.
Map_Destroy - Удаляет экземпляр мапы.
Map_IsValid - Проверяет, существует ли мапы в памяти.
Map_Clear - Полностью очищает мапу.
Map_Clone - Создает полную копию существующей мапы.
Map_Merge - Объединяет две мапы.
Map_Set - Устанавливает набор значений для указанного ключа.
Map_Get - Извлекает все данные, хранящиеся под указанным ключом.
Map_RemoveKey - Удаляет указанный ключ.
Map_SafeRemoveKey - Безопасно удаляет указанный ключ в цикле.
Map_RenameKey - Изменяет идентификатор существующего ключа на новый.
Map_Swap - Меняет местами данные двух указанных ключей.
Map_ContainsKey - Проверяет наличие указанного ключа в мапе.
Map_GetKeyByIndex - Позволяет получить значение ключа по его индексу.
Map_GetKeyCount - Позволяет получить кол-во ключей в мапе.
Map_FindKeyByField - Поиск ключа по значению.
Map_SortByKey- Сортирует все ключи в мапе в зависимости от выбранного порядка.
Map_SortByField - Сортирует записи в мапе на основе значений поля из enum.
Map_SetString- Вспомогательная функция для безопасной записи строки в массив.
mapfor - Цикл для перебора ключей в мапе.
Map_StringKeyToIntor - Преобразует строковый ключ в int для мапы.
Map_GetIdByStringKey - Находит и возвращает целочисленный (ID) существующего строкового ключа в мапе.
Map_ContainsStringKey - Проверяет, существует ли в мапе запись с указанным строковым ключом.
Map_GetStringById - Получает строковое имя ключа по его числовому идентификатору (ID).
Pawn:
//Dm Zone
// Представим, что нам нужно сделать ДМ арену, на которой мы будем записывать количество убийств, смертей и урона.
// В конце раунда нам нужно отсортировать список, вывести данные и очистить мапу для следующего раунда.
В данной мапе мы используем playerid как ключ.
enum e_dm_zone
{
Kills,
Death,
Float:Damage
}
new PawnMap:MapDmZone;
public OnGameModeInit()
{
// Создаем мапу при старте мода
MapDmZone = Map_Create();
return 1;
}
public OnGameModeExit()
{
// Удаляем мапу
Map_Destroy(MapDmZone);
return 1;
}
public OnPlayerTakeDamage(playerid, issuerid, Float:amount, weaponid, bodypart)
{
// Проверка находится ли игрок на дм зоне
static data[e_dm_zone];
// Получаем текущие данные игрока, чтобы обновить их
Map_Get(MapDmZone, playerid, data);
data[Damage] += amount;
// Обновляем только урон
Map_Set(MapDmZone, playerid, data);
return 1;
}
public OnPlayerDeath(playerid, killerid, reason)
{
// Проверка находится ли игрок на дм зоне
static data[e_dm_zone];
// Обновляем смерти (Death)
Map_Get(MapDmZone, playerid, data);
data[Death] += 1;
Map_Set(MapDmZone, playerid, data);
// Обновляем убийства (Kill)
if(killerid != INVALID_PLAYER_ID)
{
Map_Get(MapDmZone, killerid, data);
data[Kills] += 1;
Map_Set(MapDmZone, killerid, data);
}
return 1;
}
stock DmZoneRoundFinish()
{
// Сортировка по убыванию (сначала те, у кого больше киллов)
Map_SortByField(MapDmZone, e_dm_zone:Kills, MAP_SORT_DESC);
static data[e_dm_zone];
new string[144];
mapfor(MapDmZone, i)
{
Map_Get(MapDmZone, i, data);
format(string, sizeof(string),
"[DM ZONE] playerid %d | kills %d | death %d | damage %.2f",
i,
data[Kills],
data[Death],
data[Damage]
);
SendClientMessageToAll(-1, string);
}
// Очищаем данные для следующего раунда
Map_Clear(MapDmZone);
return 1;
}
Pawn:
// Inventory
// Представим, что нам необходимо реализовать простой инвентарь с базовым набором функций.
// В данной архитектуре мы используем ID слота в качестве уникального ключа (Key), а структуру предмета - в качестве значения (Value).
// Это позволяет эффективно управлять ячейками, перемещать предметы и быстро проверять заполненность сумки.
В данной системе мы используем ID слота как ключ (Key), а структуру предмета — как значение (Value).
const INVENTORY_MAX_SLOTS = 15;
enum e_inventory
{
Item,
Amount,
Name[24]
}
new PawnMap:MapInventory[MAX_PLAYERS] = {INVALID_MAP_ID, ...};
public OnPlayerConnect(playerid)
{
// Создаем мапу для каждого игрока при подключении
MapInventory[playerid] = Map_Create();
return 1;
}
public OnPlayerDisconnect(playerid, reason)
{
new PawnMap:mapid = MapInventory[playerid];
// Проверяем на валидность перед удалением
if (Map_IsValid(mapid))
{
// Полностью удаляем мапу и освобождаем память в плагине
Map_Destroy(mapid);
// Сбрасываем переменную в исходное состояние
MapInventory[playerid] = INVALID_MAP_ID;
}
return 1;
}
stock GiveInventoryItems(playerid, itemid, amount, const name[])
{
new PawnMap:mapid = MapInventory[playerid];
// Ищем, есть ли уже такой предмет в инвентаре (по полю Item)
new slotid = Map_FindKeyByField(mapid, e_inventory:Item, MAP_TYPE_INT, itemid);
static data[e_inventory];
if (slotid != INVALID_MAP_KEY_ID)
{
// Если предмет найден - увеличиваем количество
Map_Get(mapid, slotid, data);
data[Amount] += amount;
Map_Set(mapid, slotid, data);
}
else
{
// Если предмет новый - проверяем лимит слотов
if (Map_GetKeyCount(mapid) >= INVENTORY_MAX_SLOTS)
{
SendClientMessage(playerid, -1, "Нет свободного слота для предмета.");
return 0;
}
new free_slotid = Map_GetFreeKey(mapid);
if (free_slotid == INVALID_MAP_KEY_ID)
{
SendClientMessage(playerid, -1, "Ошибка при поиске свободного слота.");
return 0;
}
data[Item] = itemid;
data[Amount] = amount;
Map_SetString(data[Name], name);
Map_Set(mapid, free_slotid, data);
}
SendClientMessage(playerid, -1, "Инвентарь обновлен.");
return 1;
}
stock RemoveInventoryItems(playerid, itemid)
{
new PawnMap:mapid = MapInventory[playerid];
new slotid = Map_FindKeyByField(mapid, e_inventory:Item, MAP_TYPE_INT, itemid);
if (slotid == INVALID_MAP_KEY_ID)
{
SendClientMessage(playerid, -1, "Ошибка: предмет не найден.");
return 0;
}
Map_RemoveKey(mapid, slotid);
SendClientMessage(playerid, -1, "Предмет удален.");
return 1;
}
stock UseInventoryItems(playerid, itemid, amount)
{
new PawnMap:mapid = MapInventory[playerid];
new slotid = Map_FindKeyByField(mapid, e_inventory:Item, MAP_TYPE_INT, itemid);
if (slotid == INVALID_MAP_KEY_ID)
{
SendClientMessage(playerid, -1, "Ошибка: предмет не найден.");
return 0;
}
static data[e_inventory];
Map_Get(mapid, slotid, data);
data[Amount] -= amount;
if (data[Amount] <= 0)
{
Map_RemoveKey(mapid, slotid);
}
else
{
Map_Set(mapid, slotid, data);
}
return 1;
}
stock SwapInventoryItems(playerid, slot_1, slot_2)
{
new PawnMap:mapid = MapInventory[playerid];
if (Map_Swap(mapid, slot_1, slot_2))
{
SendClientMessage(playerid, -1, "Предметы успешно перемещены.");
}
else
{
SendClientMessage(playerid, -1, "Ошибка перемещения.");
}
return 1;
}
stock ShowInventory(playerid)
{
new PawnMap:mapid = MapInventory[playerid];
new string[1024];
mapfor(mapid, slotid)
{
static data[e_inventory];
Map_Get(mapid, slotid, data);
format(string, sizeof(string),
"%s№%d\t%s\tКол-во: %d\n",
string,
slotid,
data[Name],
data[Amount]
);
}
ShowPlayerDialog(playerid, 1000, DIALOG_STYLE_LIST, "Инвентарь", string, "Выбрать", "Закрыть");
return 1;
}
public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
if (dialogid == 1000)
{
if (!response) return 0;
new slotid = listitem;
new PawnMap:mapid = MapInventory[playerid];
static data[e_inventory];
if (Map_Get(mapid, slotid, data))
{
new string[144];
format(string, sizeof(string),
"Вы выбрали - Название: %s | ID: %d | Кол-во: %d",
data[Name], data[Item], data[Amount]
);
SendClientMessage(playerid, -1, string);
}
}
return 1;
}
Это тест, просто показывает, что мапа не медленная, а в некоторых местах даже быстрее.
Код теста >> https://github.com/i-Saibot/PawnMap

Код теста >> https://github.com/i-Saibot/PawnMap

Код:
На данный момент ключом может быть только целое число (int).
За четыре года работы над проектами мне лишь однажды понадобился строковый идентификатор для системы, других случаев, где это было бы необходимо, я не припомню.
Чтобы не загромождать API дублирующими функциями с постфиксом _Str (которые пришлось бы добавлять практически для каждой функции) и не снижать производительность мапы, я решил не внедрять поддержку строковых ключей.
Вместо этого я реализовал возможность конвертации строки в целочисленный ключ.
Pawn:
// Пример
enum inventory_struct
{
Itemid,
Amount
}
public OnGameModeInit()
{
new PawnMap:mapid = Map_Create();
// 1. Преобразуем строку "Deagle" в числовой ID и записываем данные
new keyid = Map_StringKeyToInt(mapid, "Deagle");
if (keyid != INVALID_MAP_KEY_ID)
{
new data[inventory_struct];
data[Itemid] = 24;
data[Amount] = 100;
// Сохраняем массив данных под этим ID
Map_Set(mapid, keyid, data);
printf("Строка 'Deagle' успешно преобразована в ID: %d", keyid);
}
// 2. Получаем ID по строке (используем уже созданную переменную без 'new')
keyid = Map_GetIdByStringKey(mapid, "Deagle");
if (keyid != INVALID_MAP_KEY_ID)
{
printf("ID для ключа 'Deagle' найден: %d", keyid);
}
else
{
printf("Этот строковый ключ не существует в мапе.");
}
// 3. Проверяем наличие ключа напрямую
if (Map_ContainsStringKey(mapid, "Deagle"))
{
printf("Ключ 'Deagle' существует в этой мапе.");
}
else
{
printf("Ключ 'Deagle' не найден.");
}
// 4. Конвертируем числовой ID обратно в строку
new buffer[32];
if (Map_GetStringById(mapid, keyid, buffer))
{
printf("Имя ключа под ID %d - '%s'", keyid, buffer);
}
return 1;
}
Скачать: https://github.com/i-Saibot/PawnMap/releases