- 94
- 48
chat_emoji
смайлы и иконки чата Arizona RP в mimgui и диалогах SA-MP
смайлы и иконки чата Arizona RP в mimgui и диалогах SA-MP
Библиотека, чтобы рисовать в своих mimgui-окнах те же смайлы и иконки, что показывает чат Arizona RP. Всё вытащено из
_chat.asi.Всего 1584 картинки. Из них 1354 идут ровно в том же порядке, что и в панели смайлов чата, плюс 230 скрытых: они в панель не попали, но в игре рисуются. Это ленты и плашки под ники, короны VIP, крылья уровней, значки лавок и недвижимости, телефоны, деньги, оружие, плашки «ВИП ЧАТ», «АДМИН», «РЕКЛАМА».
Те же иконки работают и в обычных диалогах SA-MP. Там их рисует сам плагин чата, так что от скрипта нужна только строка с токеном.
Почему нельзя просто подключить шрифт
Первое, что приходит в голову: скормить
seguiemj.ttf в AddFontFromFileTTF. Не сработает, причём по двум независимым причинам.mimgui собран со
stb_truetype, а он рисует только контуры и ничего не знает про цветные глифы COLR/CPAL. Так устроены и Segoe UI Emoji, и шрифт иконок Arizona: базовый контур у них часто пустой, вся картинка лежит в цветных слоях. Получишь пустые прямоугольники.Вторая причина добивает:
ImWchar в mimgui шестнадцатибитный, а смайлы живут выше U+FFFF. Такие коды в него физически не влезают.Поэтому всё заранее растеризовано в один PNG-атлас. В игре он грузится текстурой D3D9 (пул MANAGED, device reset переживает) и рисуется через
imgui.Image.Смайлы в диалогах SA-MP
Тот же токен работает в обычном
sampShowDialog: в заголовке, в тексте, в строках списка, в колонках таблицы.Я разобрал плагин дизассемблером, чтобы это проверить, а не поверить на слово. Разборщик токенов у него один на всё: функция ищет в строке двоеточия и разбирает то, что между ними. Вызывается она из двух мест сразу, из отрисовки чата и из перехваченных GUI-классов SA-MP, на которых собран диалог. Плагин вешает хуки на методы этих классов, считает разметку и кеширует её по контролу, сбрасывая при смене текста.
Вывод простой: для диалогов ни текстура, ни mimgui не нужны. Нужна только строка.
Lua:
local emoji = require 'chat_emoji'
emoji.loadTable() -- только таблицы, без PNG и D3D
emoji.dialog(1234,
'Главное меню ' .. emoji.tok('arz'),
emoji.tok('trophy') .. ' Статистика\n' ..
emoji.tok('moneybag') .. ' Баланс\n' ..
emoji.tok('gear') .. ' Настройки',
'Выбрать', 'Отмена', 2) -- 2 = DIALOG_STYLE_LIST
Колонки в
TABLIST разделяются табуляцией и работают как обычно:
Lua:
emoji.dialog(1235, 'Игроки',
'Ник\tУровень\tДеньги\n' ..
emoji.tok('crown') .. ' Nick_Name\t12\t1.250.000 ' .. emoji.tok('cash'),
'Открыть', 'Назад', 5)
emoji.dialog это тонкая обёртка над sampShowDialog с тем же порядком аргументов. Делает она две вещи: гонит текст через emoji.fit() и не отправляет текст длиннее лимита, а возвращает false, причина. Длинный текст диалога обрезается молча, и ловить это в бою неприятно. Обёртка не обязательна, sampShowDialog с токеном в строке работает и напрямую.Если сервер не Arizona. Токен это обычный текст, и там, где его некому нарисовать, он таким и останется.
emoji.fit() решает это сам: проверяет, загружен ли в процесс плагин чата, и на чужом сервере вырезает токены, чтобы в диалоге не висел мусор вида :u1f603:.Про кодировку. Токен состоит из цифр и латиницы, поэтому cp1251 его не портит. Перекодировать надо только свой русский текст, ровно как обычно.
Установка
Код:
moonloader/lib/chat_emoji.lua
moonloader/resource/chat_emoji/chat_emoji.png
moonloader/resource/chat_emoji/chat_emoji_atlas.lua
Папок
lib и resource/chat_emoji может не быть, создай сам. Пути жёстко зашиты, модуль ищет файлы именно там.В архиве лежат три примера, клади что-то одно:
chat_emoji_minimal.lua, команда/sm: пять способов применения в полсотни строкchat_emoji_demo.lua, команда/emj: панель выбора и витрина размеровchat_emoji_dialogs.lua, команда/emjd: конструктор диалогов
Последний удобен, чтобы просто посмотреть, как это выглядит. Выбираешь стиль диалога, набираешь текст, кликом вставляешь смайлы из панели и сразу открываешь настоящий диалог. Ответ показывается в том же окне.
Быстрый старт
Lua:
local imgui = require 'mimgui'
local emoji = require 'chat_emoji'
imgui.OnInitialize(function()
local ok, err = emoji.load()
if not ok then print(err) end
end)
-- дальше в отрисовке окна:
emoji.image('smiley', 24) -- просто картинка
if emoji.button('arz', 32) then end -- кнопка
emoji.text('hello :u1f603: bye :u1f44b:') -- текст с подстановкой
local picked = emoji.picker('grid', 24, 300)
if picked then sampSendChat(emoji.encode(picked)) end
Не забудь
emoji.unload() в onScriptTerminate, иначе текстура утечёт.Если тебе нужны только диалоги,
mimgui можно не подключать вовсе. Хватит emoji.loadTable(), её можно звать откуда угодно, хоть из main().API
Загрузка и токены:
| функция | что делает |
|---|---|
emoji.loadTable([dir]) | только имена и коды, без текстуры и mimgui |
emoji.load([dir]) | то же плюс текстура, из OnInitialize |
emoji.unload() | освободить текстуру |
emoji.get(key) | запись по имени, коду, токену или самой записи |
emoji.encode(key), emoji.tok(key) | токен :uXXXX: |
emoji.parse(str) | разбор строки на куски текста и смайлы |
Диалоги:
| функция | что делает |
|---|---|
emoji.dialog(id, cap, text, b1, b2, style) | sampShowDialog с проверкой длины |
emoji.detect() | загружен ли плагин чата |
emoji.fit(text) | оставить токены или вырезать, смотря по серверу |
emoji.strip(text) | убрать токены вместе с лишним пробелом |
emoji.expand(text) | 🏆 в :u1f3c6: |
emoji.measure(text) | сколько байт и токенов в строке |
Отрисовка:
| функция | что делает |
|---|---|
emoji.image(key, size) | нарисовать картинкой |
emoji.width(key, size) | какую ширину займёт при высоте size |
emoji.button(key, size, [id]) | кнопка, true при нажатии |
emoji.text(str, [size]) | текст с подстановкой токенов |
emoji.picker(id, [size], [height]) | панель выбора, вернёт запись либо nil |
Поля:
emoji.list (все записи по порядку), emoji.byName, emoji.byCp, emoji.categories, emoji.strings (подписи панели, можно переопределить).Искать можно по имени, коду и синонимам.
emoji.get(':)') найдёт улыбку, emoji.get('<3') найдёт сердце. В панели поиск тоже понимает синонимы.Размер
Задаётся при каждой отрисовке, ничем не зафиксирован. Аргумент это высота в пикселях, ширина считается сама.
Lua:
emoji.image('arz', 16)
emoji.image('arz', mySlider[0]) -- хоть каждый кадр разный
Ячейка атласа 40 px, поэтому до 40 картинка идеальна, до 64 разницы почти не видно, дальше начинает мылить. Нужен крупнее, пересобери с
--cell 64.Широкие иконки. Плашки вроде «ВИП ЧАТ» или BUY бывают в несколько раз шире своей высоты. В атласе такие занимают несколько ячеек подряд, число лежит в
e.cells, и все функции рисуют с этим соотношением. Отдельно думать об этом не надо, но если считаешь раскладку руками, бери ширину через emoji.width(), а не size.Масштаб у всех иконок общий, так что при одном
size мелкий значок останется мелким, а крупный крупным, как в самом чате.Как вставить смайл в чат
Чат кодирует смайлы токеном
:uXXXX:, где XXXX это шестнадцатеричный код.
Lua:
sampSendChat('привет ' .. emoji.encode('smiley')) -- уйдёт как ':u1f603:'
Токен из цифр и латиницы, перекодировать его в cp1251 не нужно.
Сборка под себя
В архиве лежит
rebuild.bat. Двойной клик, и он сам найдёт Python, поставит Pillow, найдёт _chat.asi рядом с игрой, распакует из него шрифты и списки, найдёт твой seguiemj.ttf и соберёт атлас.
Код:
rebuild.bat --asi "C:...\bin\arizona_chat.asi"
rebuild.bat --emoji "C:\Windows\Fonts\seguiemj.ttf"
Python нужен только для сборки. В игре он не нужен, там работает готовый PNG.
Что учесть
- MoonLoader запускает каждый скрипт в своей Lua-машине, но текстура всё равно одна на процесс. Кто загрузил первым, тот её создал, остальные берут готовую по указателю через переменную окружения, а время жизни считает сам COM. Так что десяток скриптов с библиотекой это по-прежнему 16 МБ, а не 160.
- Иконки из второго шрифта (группа «Скрытые: интерфейс (big_icons)») в чат токеном, скорее всего, не вставятся: текст чата рисуется другим шрифтом. В своих окнах они работают нормально.
- Файлы модуля и описания атласа целиком в ASCII, названия категорий записаны escape-последовательностями. Пересохранять их можно в любой кодировке, ничего не сломается.
Исходники
GitHub
Последнее редактирование: