Исходник chat_emoji: смайлы и иконки чата Arizona RP в mimgui/диалоги (1584 штуки)

e11evated

Участник
Автор темы
94
48
chat_emoji
смайлы и иконки чата Arizona RP в mimgui и диалогах SA-MP




Библиотека, чтобы рисовать в своих mimgui-окнах те же смайлы и иконки, что показывает чат Arizona RP. Всё вытащено из _chat.asi.

Всего 1584 картинки. Из них 1354 идут ровно в том же порядке, что и в панели смайлов чата, плюс 230 скрытых: они в панель не попали, но в игре рисуются. Это ленты и плашки под ники, короны VIP, крылья уровней, значки лавок и недвижимости, телефоны, деньги, оружие, плашки «ВИП ЧАТ», «АДМИН», «РЕКЛАМА».

Те же иконки работают и в обычных диалогах SA-MP. Там их рисует сам плагин чата, так что от скрипта нужна только строка с токеном.

image.png
image.png




Почему нельзя просто подключить шрифт

Первое, что приходит в голову: скормить 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
 
Последнее редактирование:

вайега52

cache miss
Модератор
3,060
3,294
MoonLoader запускает каждый скрипт в своей Lua-машине. Подключишь библиотеку в трёх скриптах, будет три копии текстуры, по 16 МБ видеопамяти каждая. Пара скриптов нормально, десяток уже перебор
подход с import в мунлоадере, либо DLL библиотека со шрифтами должны помочь в этом)
 
  • Нравится
Реакции: e11evated