- 4,766
- 5,370
Всем привет. Думаю даннах тема будет полезна всем кто только начинает писать скрипты, а так же тем кто хочет узнать немного больше. В этой теме я буду вести дневник разработчика. Что будем делать? Делать будем биндер для которого я недавно выбирал название. В ходе разработки я буду описывать здесь о итогах дня (если я что-то делал вообще )0), о том как что работает и почему я принял такое решение (если это возможно передать словами).
Так же тема будет служить фидбеком, все предложения я прочитаю и решу годится или нет.
Начнем ...
Так как начинаю вести не с первого дня, опишу что уже есть.
Интерфейс.
Я уже давно думал над подобным интерфейсом, так как я начал играть в гта 5 и мне понравились таблички всякие там, я недолго думая придумал свою концепцию и начал её реализовывать.
Интерфейс написан на mImGui (Minimal ImGui) который уже был анонсирован, но в релизе его еще нет и по словам фипа неизвестно когда появится. Почему он? Всё просто. В мИмГуи версия 1.66, что есть хорошо.
Писать на стандартном имгуи конечно не собирался сразу, иначе я бы придумал скрипт патруль. Поэтому словив ярость берсерка я набросал первый эскиз и он мне зашел.
Всё написано на DrawList (imgui.GetWindowDrawList()), и в ходе разработки, когда дело дошло до скролла, я столкнулся с рядом проблем: полосы строк уходили за пределы зоны скролла, сам скролл ломался и по мере прокрутки вниз увеличивался (бесконечный скролл воистину).
Проблема решилась на следующий день. Первый пункт фиксился обновлением переменной DrawList для зоны скролла (Каждый begin / beginchild в имгуи имеет свой собственный драв лист, поэтому использовав драв лист чужого окна всё к хренам уезжало). Со второй я помучался подольше. Оказалось нельзя факсически указывать положение элементов на экране с помощью SetCursorScreenPos(). Нужно было каждую строку получать текущее положение курсора на экране, рендерить элементы относительно этого положения, а после смещать положение на новую строку, чтобы следующий элемент был ниже.
Это пожалуй единственные трудности на данный момент. Разве что интерфейс не адаптирован под разрешения и красиво смотрится только на 1980x1080 на данный момент.
Редактор, структура профиля.
Особо не думал, заранее знал что файл профиля будет сохраняться в JSON формате.
Для реализации клавиш хотел изначально подойти со стороны парсинга строк, то есть "[3000]Test test[enter:1]" должно было служить задержкой, текстом и методом ввода. Но после парочки тестов понял что это глупо, и проще работать с таблицами, получилось:
При изменении данных это очень решает, ведь ненадо собирать и разбирать строку постоянно. Меньше нагрузки и понятней код. По поводу записи клавиш через .v сделано для модуля mImGui Addons (уже переведен и улучшен)
Теперь сам редактор долго не мог представить как будет выглядить. Всё оказалось просто.
Дальше немного улучшив дизайн я просто фиксил баги, так как накидал всё на скорую руку, да и всё продумать не мог некоторые особенности пришли позже. Редактор был готов и можно было приступать к самим биндам.
Бинды.
Использую опять же свой модуль RKeys, с ним всё просто и удобно. Сложность была только в привязке ID-бинда к ID-биндера. Но там таблица на таблицу и всё на мази.
Для реализации чтения создал единый колбек, onHotKey. Модуль RKeys возвращает в колбек ID-бинд который его вызвал. По ID-бинду получаем ID нужных нам строк и подрубаем читалку.
ОСТОРОЖНО СЛОЖНО!
Read Data:
Сюда разве что добавлю ID читалки, для реализации отмены текущего чтения.
Тут всё сложнее. Постараюсь объяснить на буржуйском.
При чтении получаем 1 строку (пока что 1, физика биндера допускает что минимальный индекс строки может быть не 1). Получаем текст и её задержку. Устанавливаем текущее время + задержка для отправки строки. Ждём это время. Отправляем сообщение. Получаем следующую строку, если текст строки пустой - ищем не пустую строку или максимальный индекс. Получаем задержку и повторяем так пока строки не кончатся.
Думаю стоит прочитать пару раз прежде чем что-то понять.
Что это дает? Мы исключаем пустые строки биндера, а это значит ненужно перехерачивать все строки для удаления одной в середине.
Система отправки не совершенна, нету переменных, сложных условий, но это пока и ненадо. Со своей задачей он справляется - строки отправляет, делает всё как нужно.
Сохранение.
Так как сохранение сразу было в JSON формате, то и запись / чтение нужно делать самому. Отсюда код:
Получение
Сохранение
Изначально сохранение было только в событии onScriptTerminate(), но это плохое решение. В один из многих перезагрузок ГТА у меня не сохранились изменения. Обратившись в фипу он сказал что это событие может не вызваться при выходе из игры. Поэтому сохранение переделал в функцию и просто вызываю после изменения чего либо.
На сегодня всё.
Ах да, отчет о сегодняшней работе:
Так же тема будет служить фидбеком, все предложения я прочитаю и решу годится или нет.
Начнем ...
Так как начинаю вести не с первого дня, опишу что уже есть.
Интерфейс.
Я уже давно думал над подобным интерфейсом, так как я начал играть в гта 5 и мне понравились таблички всякие там, я недолго думая придумал свою концепцию и начал её реализовывать.
Интерфейс написан на mImGui (Minimal ImGui) который уже был анонсирован, но в релизе его еще нет и по словам фипа неизвестно когда появится. Почему он? Всё просто. В мИмГуи версия 1.66, что есть хорошо.
Писать на стандартном имгуи конечно не собирался сразу, иначе я бы придумал скрипт патруль. Поэтому словив ярость берсерка я набросал первый эскиз и он мне зашел.
Всё написано на DrawList (imgui.GetWindowDrawList()), и в ходе разработки, когда дело дошло до скролла, я столкнулся с рядом проблем: полосы строк уходили за пределы зоны скролла, сам скролл ломался и по мере прокрутки вниз увеличивался (бесконечный скролл воистину).
Проблема решилась на следующий день. Первый пункт фиксился обновлением переменной DrawList для зоны скролла (Каждый begin / beginchild в имгуи имеет свой собственный драв лист, поэтому использовав драв лист чужого окна всё к хренам уезжало). Со второй я помучался подольше. Оказалось нельзя факсически указывать положение элементов на экране с помощью SetCursorScreenPos(). Нужно было каждую строку получать текущее положение курсора на экране, рендерить элементы относительно этого положения, а после смещать положение на новую строку, чтобы следующий элемент был ниже.
Это пожалуй единственные трудности на данный момент. Разве что интерфейс не адаптирован под разрешения и красиво смотрится только на 1980x1080 на данный момент.
Редактор, структура профиля.
Особо не думал, заранее знал что файл профиля будет сохраняться в JSON формате.
Для реализации клавиш хотел изначально подойти со стороны парсинга строк, то есть "[3000]Test test[enter:1]" должно было служить задержкой, текстом и методом ввода. Но после парочки тестов понял что это глупо, и проще работать с таблицами, получилось:
Lua:
tHotKeys = {
{
keys = {v = {vkeys.VK_1}},
text = {
{
str = "Текст",
delay = 3000,
enter = 1
}
}
}
Теперь сам редактор долго не мог представить как будет выглядить. Всё оказалось просто.
Дальше немного улучшив дизайн я просто фиксил баги, так как накидал всё на скорую руку, да и всё продумать не мог некоторые особенности пришли позже. Редактор был готов и можно было приступать к самим биндам.
Бинды.
Использую опять же свой модуль RKeys, с ним всё просто и удобно. Сложность была только в привязке ID-бинда к ID-биндера. Но там таблица на таблицу и всё на мази.
Для реализации чтения создал единый колбек, onHotKey. Модуль RKeys возвращает в колбек ID-бинд который его вызвал. По ID-бинду получаем ID нужных нам строк и подрубаем читалку.
ОСТОРОЖНО СЛОЖНО!
Read Data:
Lua:
local readData = {
state = false,
line = 0,
time = 0
}
Lua:
function onHotKey(id, keys)
local hid = rBind[id]
local func = {
sampSendChat,
sampSetChatInputText,
sampProcessChatInput,
sampAddChatMessage,
print
}
readData.state = true
if hid then
readData.line = 1
if tHotKeys[hid].text[1] then
readData.time = os.clock() + tHotKeys[hid].text[1].delay / 1000
else
readData.state = false
end
lua_thread.create(function ()
while readData.state and readData.line <= table.maxn(tHotKeys[hid].text) do
wait(0)
local line = tHotKeys[hid].text[readData.line]
if line then
if os.clock() >= readData.time then
func[line.enter](line.str, -1)
if line.enter == 2 then
sampSetChatInputEnabled(true)
end
readData.line = readData.line + 1
local newline = tHotKeys[hid].text[readData.line]
if newline and newline.str:len() > 0 then
readData.time = os.clock() + newline.delay / 1000
else
local found = false
for i = readData.line, table.maxn(tHotKeys[hid].text) do
local newline = tHotKeys[hid].text[readData.line]
if newline and newline.str:len() > 0 then
found = true
readData.time = os.clock() + newline.delay / 1000
else
readData.line = readData.line + 1
end
end
if not found then
readData.state = false
end
end
end
else
readData.line = readData.line + 1
end
end
end)
end
end
При чтении получаем 1 строку (пока что 1, физика биндера допускает что минимальный индекс строки может быть не 1). Получаем текст и её задержку. Устанавливаем текущее время + задержка для отправки строки. Ждём это время. Отправляем сообщение. Получаем следующую строку, если текст строки пустой - ищем не пустую строку или максимальный индекс. Получаем задержку и повторяем так пока строки не кончатся.
Думаю стоит прочитать пару раз прежде чем что-то понять.
Что это дает? Мы исключаем пустые строки биндера, а это значит ненужно перехерачивать все строки для удаления одной в середине.
Система отправки не совершенна, нету переменных, сложных условий, но это пока и ненадо. Со своей задачей он справляется - строки отправляет, делает всё как нужно.
Сохранение.
Так как сохранение сразу было в JSON формате, то и запись / чтение нужно делать самому. Отсюда код:
Получение
Lua:
local tHotKeys = {}
if doesFileExist(profile) then
local f = io.open(profile, "r")
if f then
local text = f:read("*a")
f:close()
tHotKeys = text:len() > 0 and decodeJson(text) or {}
end
end
Lua:
function saveHotKeys()
if type(tHotKeys) == "table" then
local f = io.open(profile, "w")
f:close()
if f then
f = io.open(profile, "r+")
f:write(encodeJson(tHotKeys))
f:close()
end
end
end
Изначально сохранение было только в событии onScriptTerminate(), но это плохое решение. В один из многих перезагрузок ГТА у меня не сохранились изменения. Обратившись в фипу он сказал что это событие может не вызваться при выходе из игры. Поэтому сохранение переделал в функцию и просто вызываю после изменения чего либо.
На сегодня всё.
Ах да, отчет о сегодняшней работе:
- Исправил сохранение
- При создании клавиши автоматически создается в ней пустая строка
- При создании первой строки в клавише она имеет по-умолчанию 0 задержку (т.к. задержки первой строки тоже работают). Все последующие 1000 ms.
- При создании клавиши автоматически создается в ней пустая строка
- При создании первой строки в клавише она имеет по-умолчанию 0 задержку (т.к. задержки первой строки тоже работают). Все последующие 1000 ms.