Перейти к контенту

Рекомендуемые сообщения

Расширение стандартных таблиц значениями, которые доложить почему-то забыли:

DIK_keys["MOUSE_4"] = 340
DIK_keys["MOUSE_5"] = 341
DIK_keys["MOUSE_6"] = 342
DIK_keys["MOUSE_7"] = 343
DIK_keys["MOUSE_8"] = 344

key_bindings["kSPRINT_TOGGLE"] = 8
key_bindings["kENGINE"] = 15
key_bindings["kARTEFACT"] = 30
key_bindings["kWPN_FIREMODE_PREV"] = 38
key_bindings["kWPN_FIREMODE_NEXT"] = 39
key_bindings["kPAUSE"] = 40
key_bindings["kCHAT_TEAM"] = 45
key_bindings["kACTIVE_JOBS"] = 53
key_bindings["kMAP"] = 54
key_bindings["kCONTACTS"] = 55
key_bindings["kVOTE_BEGIN"] = 57
key_bindings["kVOTE"] = 58
key_bindings["kVOTEYES"] = 59
key_bindings["kVOTENO"] = 60
key_bindings["kSPEECH_MENU_0"] = 63
key_bindings["kSPEECH_MENU_1"] = 64
key_bindings["kUSE_BANDAGE"] = 73
key_bindings["kUSE_MEDKIT"] = 74
key_bindings["kQUICK_SAVE"] = 75
key_bindings["kQUICK_LOAD"] = 76

Несколько функций в стандартные пространства имен:

-- возвращает реальный размер таблицы
-- пример: table.size({"first", "two"}) => 2
function table.size(t)
    if t then
        local c = 0
        for k, v in pairs(t) do
            c = c + 1
        end
        return c
    end
end
	-- округляет число до заданной точности после запятой
-- пример: math.round(0.56765895, 2) => 0.57
function math.round(v, exp)
    if v then
        if not exp then
            exp = 0
        end
        return tonumber(string.format("%."..exp.."f", v))
    end
end
	-- выравнивает значение числа, если оно находится за диапазоном
-- пример: math.clamp(12, 1, 10) => 10
function math.clamp(v, min, max)
    if v then
        if v < min then
            return min
        elseif v > max then
            return max
        end
        return v
    end
end
	-- возвращает количество вхождений шаблона
-- можно посчитать, сколько определенных символов есть в строке
-- пример: string.count("молоко", "о") => 3
function string.count(s, pattern)
    local res, c = string.gsub(s, pattern, "_")
    return c
end

-- переводит значение в булевое
-- пример: toboolean("0") => false
function toboolean(e)
    return e ~= nil and e ~= 0 and e ~= "0" and e ~= "false" and e ~= false
end

Добавить в _G.

  • Спасибо 2
  • Полезно 1
Ссылка на комментарий

Использовать string.format в функции math.round это очень медленно.

Более чем в два раза быстрее :

function math.round (num, limit)
    local mul = math.pow(10, limit or 0)
    return math.floor(num * mul + 0.5) / mul
end

Хотя стоп. Снимается и извиняюсь. Это работает только в нормальном lua, а не в сталкеровском ))

 

Да и функция toboolean достаточно специфична. Это я про ноль.

А если nil на входе? Возвратит nil. Но это же не boolean, что подразумевает функция.

Гораздо логичнее будет так :

function toboolean(e)
    return not not e
end
Изменено пользователем Nazgool
  • Спасибо 1
  • Полезно 1
Ссылка на комментарий

Давно пора.

 

Внесу свои (чужие, конечно) 5 копеек в виде подборки модулей Артоса (все - последних опубликованных автором версий) и одного от xStream - на случай, если у автора темы какого-то из них нет. После принятия к сведению пост можно потереть.

 

@Artos:

Ссылки на устаревшие версии модулей Artos'а удалены.

Актуальные версии (с исправлением найденных ошибок) опубликованы в этом посте.

 

@xStream:

xs_sandbox - система событий, «песочница» по терминологии автора.

 

Все модули мультиплатформенные, инструкция вложена в каждый архив.

 

@Dennis_Chikin, здесь, на мой взгляд, допустимо обсуждение самих готовых модулей, вопросов их использования и т. п.

 

О подключении модуля se_stor к ЗП: Начиная отсюда, несколько следующих постов.

Zander.

Изменено пользователем Kirgudu
  • Спасибо 1
  • Нравится 1
  • Полезно 1
Ссылка на комментарий

Вот от меня сборка, она возможно под любой мод не подойдет, но хорошая база для разработки новых модов.

Платформа ЗП, ориентированна на пропатченный двиг с помощью X-Ray Extentions

 

http://www.amk-team.ru/forum/index.php?showtopic=12633

 

Многое есть, в том числе и инструкция внутри архива и в самой теме.

В последнем посте темы есть ссыль на обновленную версию(обновил месяц назад).

Изменено пользователем Viнt@rь
Ссылка на комментарий
 
 

Интерфейс работы с классами нет-пакетов:
0) Что это вообще такое и с чем едят?
0.1) 'Нет-пакет' - это специальный объект (бинарный буфер), который содержит информацию, которой серверные объекты в игре обмениваются со своими клиентскими копиями, изменяя соответствующие параметры/свойства друг-друга. Позиция в пространстве, поворот, текущий режим стрельбы у оружия и т.п. Просто так эта информация не доступна стандартными средствами из скриптов.
0.2) Нет-пакет для каждого объекта в игре имеет две части/типа, независимых друг от друга: 'state' и 'update'. Возможна работа с каждым типом по отдельности или с обоими вместе.

Все, что представлено в этом модуле (скрипте) - специальные классы, которые позволяют организовать ПРОСТОЙ доступ к этим данным и их изменить в случае надобности.

В данном архиве предоставлена одна из ранних версий модуля для работы с нетпакетами.
Предположительно, он работает быстрее чем более поздние аналоги.

 

Установка:
m_net_utils.ltx поместить в папку конфиг.
m_net_utils.script в папку скриптов

 

В данном архиве предоставлена одна из ранних версий модуля для работы с нетпакетами.
Предположительно, он работает быстрее чем более поздние аналоги.

Получение нетпакета:
local packet = m_net_utils.Get_NetPacket(sobj) --- только "state" часть
local packet = m_net_utils.Get_NetPacket(sobj, true) --- вместе с "update"-частью.

Нетпакет возвращается в виде таблицы, опционально в ней находится подтаблица upd, хранящая данные из update-части
пакета.

Присвоение нетпакета объекту:
m_net_utils.Set_NetPacket(sobj, packet)

Обратите внимание что sobj здесь, это серверный объект.
При добавлении новых классов в class_registrator, их надо так же регистрировать в m_net_utils.ltx

Примеры использования:

Узнаем, прицеплен ли к оружию глушитель
function weapon_silenced(weapon_id)
    local sobj = alife():object(weapon_id)
    local packet = m_net_utils.Get_NetPacket(sobj)
    local addon_flags = packet.addon_flags
    if addon_flags == 4 or addon_flags == 5 or addon_flags == 6 or addon_flags == 7 then
    return true
    end
return false
end

Записываем свои данные в кастом-дату объекта:
function set_custom_data(obj_id, data)
    local sobj = alife():object(obj_id)
    local packet = m_net_utils.Get_NetPacket(sobj)
        if sobj and packet then
            packet.custom_data = data
            m_net_utils.Set_NetPacket(sobj, packet)
        end
end

Доработка от Zander: модуль спокойно переносит тот случай когда в него вместо серверного объекта передается nil.
Поэтому возможны и работают конструкции вида

    local sobj = alife():object(obj_id)
    local packet = m_net_utils.Get_NetPacket(sobj)
        if sobj and packet then

    ......

 


  • Спасибо 1
  • Полезно 1

Мод, где не бывает одинаковых путей - Судьба Зоны. (Лучшее, что у меня получилось на X-Ray) На базе модифицированного движка OGSR Engine.

Бывший мододел на X-Ray / Начинающий игродел на Unreal Engine. Программист.

AMD Ryzen 9 7950X (16 ядер, 32 потока, 5.75 ГГц); RTX 3080; 128 ГБ DDR5; Arctic Liquid Freezer II-420; 3 ТБ SSD PCIe 4.0; 4ТБ HDD.

Ссылка на комментарий

Скриптовый модуль, добавляющий в игру коллбек выстрела. Автор - Shoker.


Ссылка: https://yadi.sk/d/XSYlfLRmibZKU
Инструкция по установке и использованию в архиве. Там 2 версии скрипта, одна для ТЧ/ЧН, вторая для ЗП.

По теме... откопайте кто-нибудь мне инструкцию-описание к расширению Луа от RvP.
А то пользуюсь сам по памяти, скинуть могу, а вот инструкцию к нему приложить, что там лежит - кхм, не смогу)

Это скриптовый файл, дающий новый способ создания диалогов, и не отменяя старые.


Новый способ прост и не требует от пользователя знания скриптов, помогает быстро и легко отловить все распространенные ошибки в диалогах, а так же существенно упрощает сам процесс создания и редактирования, особенно в случае больших, сложных и разветвленных диалогов, по сравнению со старыми методами.

Диалог пишется в файле ltx-конфига, и избавлен от громоздкой мишуры xml-тегов. Возможно это мое субъективное мнение, но форма написания диалога получается более наглядной.


 
Сохраняются все возможности диалогов созданных по-старинке: к любой фразе можно прицепить прекондишен, повесить вызов функции, выдачу/отнятие инфопоршня, проверку на наличие/отсутствие инфопоршня.
 
Добавлена возможность написания диалоговых блоков - своего рода готовых деталей для сборки. И диалог можно составлять как из простых фраз, так и из готовых блоков, в любом сочетании. Сложность и размеры диалоговых блоков практически не ограничены, любой диалоговый блок сам так же может быть составлен из других, более мелких диалоговых блоков. Вы можете использовать одни и те же диалоговые блоки в различных диалогах, это как детали конструктора.
 
Ряд возможных ошибок при составлении диалога, ранее приводивших к неинформативным или безлоговым вылетам, теперь сопровождаются более ясной информацией в логе. примеры:
 
1) Старые методы: При указании функции для прекондишена, допущена опечатка/функция не существует. Результат: пока не добраться до той фразы, все работает. Дойдя до проблемной фразы, уже разговаривая с нпс в игре, получаем безлоговый вылет.
Новый метод: Еще при формировании диалога, выполняется проверка существования всех вызываемых в нем функций. В случае, если функция не существует, произойдет вылет:
Dialog <имя диалога>; line <номер строки в диалоге>: function <файл>.<функция> not exist
2) Старые методы: В большом разветвленном диалоге, из-за ошибки в связях между фразами, к какой-либо фразе отсутствует входящая связь. Результат: насколько я помню, довольно неинформативный вылет, никак не помогающий найти ошибку.
Новый метод: Еще при формировании диалога, проверяется его структура. В случае такой ошибки произойдет вылет:
Отсутствует входящий линк для фразы. section <имя диалога>, phrase <номер строки>
3) Старые методы: По ошибке/невнимательности, модостроитель при составлении сложноразветвленного диалога, допустил нарушение чередования фраз актор/нпс. Результат: непредсказуемое поведение диалога в игре, как правило не то что ожидалось, причину приходится искать вручную.
Новый метод: Существует возможность настроить отслеживание фраз. обычный диалог - первая фраза принадлежит актору. старт-диалог - первая фраза принадлежит нпс. Если чередование фраз нарушено, Это будет обнаружено еще при формировании диалога, и произойдет такой вылет:
Нарушено чередование фраз npc/actor. section <имя диалога>, line <номер строки>
указывающий точно, где именно произошло "столкновение" с фразой, принадлежащей не тому собеседнику.
Так же есть возможность отключить отслеживание чередования для отдельно взятого диалога.

Единственный недостаток - чтобы начать делать диалоги по-новому, придется изучить набор команд, используемых при написании. Это достаточно просто и не займет много времени у любого пользователя.

1. скачать можно тут https://yadi.sk/d/RN96hfhEkYbPH


2. распаковываем архив, кидаем файл в папку gamedata/scripts
3. пользуемся новыми возможностями.
Никакого совмещения не требуется. Встроена проверка версий, так что модуль будет работать на любой версии игры. (ТЧ любой патч / ЧН / ЗП). Правда, я не тестировал на других версиях :) Если я ошибся и что-то не работает, пишите.
 
* Для того чтобы правильно работали функции отслеживания ошибок, у вас должны быть рабочие функции abort и log.

Ну для начала

раз теперь у нас диалоги пишутся в ltx-файлах конфигов, неплохо бы завести для них отдельное место. Это не обязательное действие, просто для удобства следует выполнить следующее:


1. в папке config создаем новую папку dialogs
2. создаем в нем текстовый файл, называем его dialogs.ltx
3. открываем в папке config файл system.ltx, и в самый его конец пишем:
#include "dialogs\dialogs.ltx"
4. теперь можно в папке dialogs создавать новые файлы, писать в них свои диалоги, к примеру мы создали файл my_first_dialog.ltx, его надо подключить к игре. открываем наш файл dialogs.ltx, и в нем пишем
#include "my_first_dialog.ltx"
Но, сторонники бардака и беспорядка могут писать свои диалоги в любых файлах конфигов где угодно, только не забудьте так или иначе подключить их к system.ltx.

Как создать диалог.

1. Каждый диалог в новой системе имеет секцию конфига. таким образом, создав в файле конфига секцию


[my_first_dialog]
вы уже создали новый диалог. Правда пока он не имеет ни единой фразы, но мы решим этот вопрос позже. Пока надо сделать чтобы скрипты его увидели.
 
2. Идем в gamedata/config/gameplay, открываем файл dialogs.xml, Где-нибудь между уже имеющихся диалогов, добавляем в него следущее:
    <dialog id="my_first_dialog">
        <init_func>dialogs_new_system.my_first_dialog</init_func>
    </dialog>
3. Идем в gamedata/scripts, создаем новый текстовый файл, называем его dialogs_new_system.script
 
4. Открываем его, вставляем такой код:
function my_first_dialog(dlg)
assembly_dialogs.construct_dialog("my_first_dialog",dlg)
end
Все, игра его видит. Правда он все еще не написан.

Пишем диалог...

1. Идем в gamedata\config\dialogs, открываем наш файл my_first_dialog.ltx, где мы создали пока еще пустую, секцию диалога. Диалог в конфиге пишется при помощи команд, далее я напишу какие команды что делают.


2. НЕОБЯЗАТЕЛЬНО.
вот пример простенького диалога:
[my_first_dialog]
x0 = текст_первой_фразы
link0 = 1

x1 = текст_второй_фразы
link1 = 2

x2 = текст_третьей_фразы
Тут все, я думаю, очевидно и понятно.
команды x<номер фразы> - создают новую фразу в диалоге. значение команды это ее текст.
Можно писать текст прямо тут, можно приводить string_id из файлов папки config/text/rus. рекомендуется второй вариант.
Диалог всегда начинается с фразы x0, она здесь обязательна.
команды link<номер фразы> = № следующей фразы - создают связи между фразами.
 
Теперь возьмем пример посложнее.
[my_first_dialog]
x0 = текст_первой_фразы
link0 = 1
x1 = текст_второй_фразы
link1 = 10, 20, 30

x10 = текст_фразы_10
link10 = 11
x11 = текст_фразы_11

x20 = текст_фразы_20
link20 = 21
x21 = text
link21 = 22
x22 = text

x30 = text
link30 = 31
x31 = text

Тут у нас актор произносит фразу х0, нпс ему отвечает фразой х1, а далее актору открывается выбор из трех фраз, х10, х20, х30, каждая из которых ведет в свою ветку диалога. Из любой фразы можно сделать неограниченное число линков, разделяя их запятыми. Этот пример так же показывает тот факт, что нумерация фраз не обязана быть сплошной. В них могут быть разрывы до сотни.
 
И еще вот такой пример:

[my_first_dialog]
x0 = text
link0 = 1

x1 = text
link1 = 2

x2 = text
link2 = 3

x3 = text
link3 = 4

x4 = text
link4 = 1, 5

x5 = text
link5 = 6

x6 = text

здесь после 4-й фразы (нпс) актору открывается выбор, продолжить диалог или вернуться к началу.
Думаю, с созданием фраз и связей между ними, все уже понятно.
 
4. Добавляем другие команды
все команды пишутся по принципу <команда><номер фразы>
команда a - добавит вызов функции в указанной фразе (аналогично тегу <action>)
команда p - добавит прекондишен (проверку условия) в указанной фразе (аналогично тегу <precondition>)
команда h - добавит проверку наличия инфопоршня в указанной фразе (аналогично тегу <has_info>)
команда d - добавит проверку отсутствия инфопоршня в указанной фразе (аналогично тегу <dont_has_info>)
команда gi - выдать актору инфопоршень в указанной фразе (аналогично тегу <give_info>)
команда di - отнять у актора инфопоршень в указанной фразе. (аналогично тегу <disable_info>)

команда item - заспавнить в инвентарь актора итем указанной секции и количества. количество пишется через точку, если не указано то считается равным единице.
пример:
item5 = bandage.10, medkit.5 ; заспавнит 10 бинтов и 5 аптечек
пример2:
item5 = antirad, wpn_pm.2 ; заспавнит 1 антирад и два ПМа.
команда remove - удалить из инвентаря актора итемы указанных секций и количества. Если у актора меньше итемов, удаляются те что есть. Т.е. проверка необходимого количества здесь не выполняется. Если такая проверка нужна, используйте команду has_item.
пример:
remove5 = wpn_pm ; удалит ПМ
пример2:
remove5 = bandage.2, medkit.3, antirad.4, vodka.5 ; удалит 2 бинта, 3 аптечки, 4 антирада и 5 бутылок водки. если чего-то не хватает, это не помешает.
команда has_item - вставляет проверку наличия в инвентаре актора, указанных предметов в указанном количестве.
фраза не будет пропущена, если хоть какого-то из указанных итемов не хватает.
пример:
has_item5 = wpn_abakan ; фраза будет доступна если у актора есть абакан
пример2:
has_item5 = af_medusa.10, bandage.4 ; фраза будет доступна если у актора есть 10 медуз и 4 бинта.
команда donthas_item - вставляет проверку, что в инвентаре актора указанных предметов меньше заданного количества.
пример:
donthas_item5 = wpn_pm, wpn_abakan, wpn_mp5, medkit ; фраза будет доступна только если у актора нет ничего из перечисленного оружия и нет аптечек.
пример2:
donthas_item5 = bandage.25, medkit ; фраза будет доступна если у актора нет ни одной аптечки, и количество бинтов в его инвентаре менее 25.

пример:

[my_first_dialog]
x0 = text
link0 = 1
a0 = spawn.minigun

x1 = text
link1 = 5, 10, 15
gi1 = answer_infoportion

x5 = text
link5 = 6
p5 = my_script.answer_function

x6 = text
a6 = spawn.ammo_1, spawn.medkit, xr_motivator.kill_all_bandits

x10 = text
h10 = какой_то_поршень_1, какой_то_поршень_2
link10 = 11

x11 = text

x15 = text
link15 = 16

x16 = text
link16 = 17

x17 = text

тут мы в стартовой фразе х0, вызываем скриптовую функцию spawn.minigun,
в следующей выдаем актору некий инфопоршень, затем у нас развилка на три ветки.
первая ветка начинается с фразы 5, в ней предусловие - my_script.answer_function
и если указанная функция вернет истину, то только тогда эта ветка диалога будет доступна. в следующей фразе вызываем три функции, spawn.ammo_1, spawn.medkit, xr_motivator.kill_all_bandits - для любой команды, можно указывать любое количество аргументов, разделяя их запятыми. при вызове, очередность их следования будет соблюдаться.
в другой ветке, начинающейся с фразы x10, на входе мы проверяем наличие двух каких-то инфопоршней. ветка будет доступна если у актора есть оба. третья ветка, начинается с фразы x15, доступна в любом случае и не снабжена никакими командами.
 
что можно сказать по итогам. К каждой фразе можно прицепить неограниченное количество каких вам угодно команд, и каждой указать любое количество аргументов через запятую. Не надо только повторять команды. к примеру такая запись:
x5 = text
a5 = my_script.xxx_function
link5 = 6
a5 = my_file.answer_function
неправильна, из двух команд a5, читаться будет только одна. Правильный способ записи в таком случае будет выглядеть так:
x5 = text
a5 = my_script.xxx_function, my_file.answer_function
link5 = 6
5. прописываем в профиле любого удобного вам НПС, новый диалог
<actor_dialog>my_first_dialog</actor_dialog>
И идем проверять как все работает.

в начале можно настроить, надо ли в этом диалоге отслеживать чередование фраз.

если мы напишем
params_sequence = actor
значит это обычный диалог, где первую фразу говорит актор. система будет отслеживать чтобы собеседники говорили по очереди не нарушая порядок.
Если написать
params_sequence = npc
значит это старт-диалог, и в нем первая фраза принадлежит НПС. система так же будет отслеживать порядок фраз.
А если мы напишем
params_sequence = free
То чередование фраз отслеживаться не будет.
Если не указывать этот параметр, то по умолчанию считается что params_sequence = actor, т.е. это обычный диалог, первая фраза принадлежит актору, отслеживание активно.
3. добавляем фразы, и связи между ними

Сейчас уже поздно, так что пока хватит. Утром допишу про создание блоков, и компоновку диалога из них. Это одно из главных преимуществ новой системы.
Зачем отдельная тема - затем что я предполагаю, кроме обсуждения, еще код каких-нибудь широко-употребимых блоков, написанный в этой системе, будет здесь же выкладываться.
К модераторам просьба, организуйте мне кураторство в этой теме.

Для начала, что такое диалоговый блок. Это некоторый фрагмент диалога, у него есть вход - начальная фраза. Некоторое число фраз, от одной до - много. и есть фразы, (от нуля до сколько захотите), которые считаются выходами диалогового блока. Я не случайно говорю "от нуля" - выходы могут и отсутствовать.
Что такое выходы - это те фразы, к которым будет подключать линки на последующие внешние для этого блока, фразы, внешний для него же, конструктор диалога.
Пока все может быть не очень понятно, наберитесь терпения. когда доберемся до конкретных примеров, все станет яснее.
 
Как создаются диалоговые блоки? А точно так же как и диалоги. создаем секцию, в нее пишем фразы, линки и команды. Начальная фраза - вход, выходов может быть нет, может быть есть один, может быть много.
Чем же отличается диалоговый блок, от самого диалога? А отличается он тем, что может быть подключен к другому диалогу, в любом месте.
 
Для создания выходов диалогового блока, существует команда exit. аргумент, который для нее указывается - это порядковый номер выхода.
Давайте уже перейдем к примерам. Создадим простенький диалоговый блок с одним выходом:

[dlg_block_test]
x1   = text_phrase_1
link1 = 2

x2   = text_phrase_2
link2 = 3

x3   = text_phrase_3
link3 = 4

x4   = text_phrase_4
link4 = 1, 5

x5   = text_phrase_5
link5 = 6

x6   = text_phrase_6
link6 = 7, 8

x7   = text_phrase_7
link7 = 9

x8   = text_phrase_8

x9   = text_phrase_9
exit9 = 1
LlxFIMus.jpg

Как видим, в середине есть возможность вернуться в начало, образуя петлю, в конце есть развилка, одна ветка ведет вникуда, т.е. приводит к обрыванию диалога, другая - вроде бы тоже, но в ней объявлен пока загадочный для нас ВЫХОД. Пока получается что-то похожее на диалог, правда?
Кстати обратите внимание на еще одно отличие - в диалоговом блоке первая фраза x1, а не x0.
Ну и, не лишне будет сказать, при создании диалоговых блоков так же можно пользоваться всеми командами, описанными выше.
 
Теперь идем далее. Слегка усложним один из примеров диалогов, приведенных ранее.
Это не диалоговый блок, это сам диалог.

[my_first_dialog]
params_sequence = actor
x0 = text0
link0 = 1

x1   = text1
link1 = 10, 20, 30

x10   = text10
link10 = 11

x11   = text11
link11 = 12

x12   = text12
link12 = 40

x20   = text20
link20 = 21

x21   = text21
link21 = 22

x22   = text22
link22 = 40

x30   = text30
link30 = 31

x31   = text31
link31 = 32

x32   = text32
link32 = 40

x40 = text40
link40 = 41

x41 = text41
link41 = 42

x42 = text42
link42 = 43

x43 = text43
YYLxj9Gs.jpg

Тут у нас диалог ветвится на три потока, которые затем опять сходятся в один.
 
Ну так вот. любую фразу в диалоге можно заменить на диалоговый блок.
Делается это например так:

Берем вот этот кусок
x31   = text31
link31 = 32

x32   = text32
link32 = 40
И меняем вот так:
x31   = text31
link31 = 32

block32   = dlg_block_test, block_1
link32 = 40
вместо команды x (создание фразы) - команда block. подключение диалогового блока.
 
 
первый аргумент команды - секция конфига подключаемого блока. второй аргумент - имя блока, должно быть уникальным для данного блока. Вы ведь уже поняли что можно вставлять один и тот же блок несколько раз в разных местах диалога? имена только им надо давать разные.
 
Например, вставим наш блок вместо фраз 12, 22, 32:
block12   = dlg_block_test, block_1
...
block22   = dlg_block_test, block_2
...
block32   = dlg_block_test, block_3
Мы сделали это с тем диалогом что приведен выше, my_first_dialog.
Что же в итоге получится? А вот что:
gobq8eMs.jpg
Граф диалога усложнился в разы :) а мы всего лишь три строчки поменяли.
В любой диалог, в любом его месте, можно вставить какой угодно диалоговый блок. При желании можно практически весь диалог из блоков и строить, только начальная фраза должна оставаться фразой.
 
Ну и теперь самый смак. помните, что я говорил, что при создании диалогового блока доступны все те же команды что и для диалога? Так вот. команда block - тоже доступна. Вы можете составлять блоки из блоков, а те в свою очередь еще из блоков - сколько уровней вложенности вам понадобится, столько и будет.
Единственное, чего не стоит делать, это вставлять диалоговый блок внутри себя самого. Спровоцируете бесконечный цикл, который завершится переполнением стека и вылетом.
Таким образом, в код диалогового блока можно вставлять любые другие блоки, кроме него самого.

Давайте немного обленимся, просто возьмем вот такой код
[test_block_2]
x1   = text1
link1 = 10, 20, 30

x10   = text10
link10 = 11

x11   = text11
link11 = 12

x12   = text12
link12 = 40

x20   = text20
link20 = 21

x21   = text21
link21 = 22

x22   = text22
link22 = 40

x30   = text30
link30 = 31

x31   = text31
link31 = 32

x32   = text32
link32 = 40

x40 = text40
link40 = 41

x41 = text41
link41 = 42

x42 = text42
link42 = 43

x43 = text43
Как видите это уже знакомый вам диалог на три ветки, сходящиеся потом в одну. Мы из него выкинули начальную фразу, и превратили в диалоговый блок.
Добавим в него 4 выхода.
[test_block_2]
x1   = text1
link1 = 10, 20, 30

x10   = text10
link10 = 11

x11   = text11
link11 = 12
exit11 = 1

x12   = text12
link12 = 40

x20   = text20
link20 = 21

x21   = text21
link21 = 22
exit21 = 2

x22   = text22
link22 = 40

x30   = text30
link30 = 31

x31   = text31
link31 = 32
exit31 = 3

x32   = text32
link32 = 40

x40 = text40
link40 = 41

x41 = text41
link41 = 42

x42 = text42
link42 = 43
exit42 = 4

x43 = text43
Напомню, аргумент команды exit - это порядковый номер выхода.
Что такое выход - думаю уже понятно. Это та фраза, или тот адрес, куда будут подключаться связи, которые должны выходить из диалогового блока.
Но их несколько и они в разных местах. как же с ними обращаться?

пишем такой диалог:

[my_second_dialog]
x0 = text0
link0 = 1

x1 = text1
link1 = 2

x2 = text2
link2 = 3

block3 = test_block_2, inserted_block
link3 = 10 | 20 | 30, 40 | 50

x10 = text10
link10 = 11
x11 = text11

x20 = text20
link20 = 21
x21 = text21

x30 = text30
link30 = 31
x31 = text31

x40 = text40
link40 = 41
x41 = text41

x50 = text50
link50 = 51
x51 = text51
OKYOfKks.jpg
 
Зеленые фразы и синие стрелки - Встроенный диалоговый блок.
Желтые фразы и фиолетовые стрелки - внешний диалог.
Оранжевые стрелки - связи подключенные к выходам диалогового блока.
Обратите внимание, как оформлены аргументы команды link, выходящей из диалогового блока:
link3 = 10 | 20 | 30, 40 | 50
Символ запятая, по прежнему разделяет адреса связей, исходящих из одной и той же фразы.
А символы | разделяют потоки, выходящие из разных выходов.

Можно к примеру сделать так, что в каждой из 4-х фраз-выходов диалогового блока, будет выбор, в какую из веток пойти. сделаем все 5 веток доступными для всех выходов:
link3 = 10, 20, 30, 40, 50 | 10, 20, 30, 40, 50 | 10, 20, 30, 40, 50 | 10, 20, 30, 40, 50
Или например: к второму и третьему выходу не будем ничего подключать:
link3 = 10, 20, 30, 40, 50 | | | 10, 20, 30, 40, 50
Или еще пример: оставим к каждому выходу одну фразу, 50 и 51 фразы удалим из диалога:
link3 = 10 | 20 | 30 | 40
Как видите, каждый выход из диалогового блока - может подключаться к любому числу фраз или не подключаться никуда, каждым выходом-потоком можно управлять отдельно. Тут есть один момент. При написании аргумента к команде link диалогового блока, можно записать меньше потоков, чем есть в блоке. Проблем от этого не возникнет, например если я сделаю так:
link3 = 10, 20, 30, 40 | 50
то будут подключены 10,20,30,40-я фразы к первому выходу, 50-я ко второму. к остальным двум выходам не будет подключено ничего.
А вот если я сделаю так:
link3 = 10 | 20 | 30 | 40 | 50
Получится запись которая требует 5 выходов. А у этого диалогового блока есть только четыре. В таком случае произойдет такой вылет:
Dialog <имя диалога>; line <номер строки>: Число выходов диалогового блока недостаточно для подключаемых входов.
Число потоков, подключаемых к выходам диалогового блока, не должно превышать число имеющихся у него выходов.

Вышеописанными блочными конструкциями, возможности модуля не ограничиваются. Если вы скриптер, вы можете писать свои функции, для динамической генерации диалоговых блоков, и точно так же подключать их в любое место любого диалога.
 
Возьмем диалог из последнего примера. Там диалоговый блок подключался вот так:
block3 = test_block_2, inserted_block
link3 = 10 | 20 | 30, 40 | 50
и тут, все характеристики этого блока читаются из конфига, который, как ни крути, статичен. Однако можно сделать иначе:
block3 = barter_manager_3.buy_terminal_all, inserted_block
link3 = 10, 20, 30, 40, 50
Здесь - диалоговый блок это не секция конфига. Это указание вида файл.функция. На свою функцию формирования диалогового блока. inserted_block - все так же, уникальное имя этого блока в этом диалоге.
И вы можете свои функции для формирования диалоговых блоков, писать в любом файле скрипта, где вам удобно. Генератор диалога сначала проверит, что такая функция существует, затем ее вызовет, предоставив ей генерировать диалоговый блок по своему разумению.

Для того чтобы работать с этой системой, ваша скриптовая функция должна выполнять ряд правил:
1. генератор вызывая вашу функцию, передает ей 5 аргументов:
*table: phr_tbl, - таблица, хранящая объекты фраз генерируемого диалога (точнее, ссылка на нее)
number: phr_k, - следующий индекс, в который вам предоставлено писать свои фразы
*userdata: dlg, - ссылка на объект диалога, созданный движком, и переданный в скрипты для генерации
string: block_name, - уникальное имя диалогового блока
string: block_input_key - адрес фразы, из которой подан линк на вход вашего блока. Это не id фразы, а ее адрес в системе генератора.
2. ваша функция обязана после завершения своей работы, вернуть генератору аргумент - таблицу.
В этой таблице должны быть два элемента, с числовыми ключами. Первый - следующий после вас, свободный индекс в таблице фраз. Второй - еще таблица, в которой по числовым индексам, хранятся строковые адреса фраз, являющихся выходами вашего блока.
3. при каждом добавлении фразы, обязательно добавлять счетчик фраз на единицу.
4. адреса фраз, создаваемых внутри вашей функции, составлять с применением выданного на входе, имени диалогового блока. Так вы обеспечите уникальность адресов в тех случаях, когда ваш диалоговый блок будет подключаться несколько раз к одному диалогу. Если вы уверены что такого не будет, то 4-е правило соблюдать не обязательно.
еще, в качестве рекомендации, для добавления фраз советую обратить внимание на функцию assembly_dialogs.assembly_phrase_ex. формирует фразы, опираясь не на конфиг, а на переданные в нее данные.
 
Для наглядного примера, привожу код своего блока, это блок для бартерной системы торговли, он позволяет актору выбрать средство оплаты для уже выбранных покупок, производится перебор всех существующих в игре артефактов, формируются фразы на разное их количество, навешиваются прекондишены для проверки того что у актора есть в наличии нужное количество выбранных артов, и того что такой вариант подходит по цене к выбранному товару.
Это лишь пример, цели диалоговых блоков могут быть любыми.

function dlgblock_select_costitem(section_item, cnt_item, phr_tbl, phr_k, dlg, type_key, input_key)
    --[[
        блок выбора артефакта/трофея для оплаты. цена оплаты определена до этого,
        наличие у ГГ ценных предметов определено до этого
        Actor
        Название х Количество
        
        Прекондишн: проверить наличие требуемого количества нужных артов
        Прекондишн: проверить что это та цена которая требуется
        Акшн: внести выбранное название в память.
        Блок в одну фразу. все построение блока сводится к формированию функций обработки фразы.
        
    ]]
    --printf(string.format("dlgblock_select_costitem %s", section_item))
    --local inside_key = 1
    --local lost_attempts = 0
    --local inside_links = {}
    --local block_links = {} -- источник - полный адрес. цель - номер в локальной сети.
    local exit_table = {}
    --local item_count = barter_manager_3.Get_Art_Count(section_item)
    if not din_functions_dlg then
        _G.din_functions_dlg = {}
    end
    din_functions_dlg[string.format("costprec_%s_%i", section_item, cost_increment_table[cnt_item])] = function(a,
        local item_count = barter_manager_3.Get_Art_Count(section_item)
        if cnt_item > item_count then return false end -- у ГГ нет столько артов этого вида
        local cost_level = af_memory.level
        local self_cost = af_ranks[section_item] + cost_increment_table[cnt_item]
        return cost_level == self_cost
    end
    local p_tbl = {string.format("din_functions_dlg.costprec_%s_%i", section_item, cost_increment_table[cnt_item])}
    local cost_level = af_memory.level
    din_functions_dlg[string.format("costact_%s_%i", section_item, cost_increment_table[cnt_item])] = function(a,
        barter_manager_3.set_af_memory(section_item, cnt_item, cost_level)
    end
    local a_tbl = {string.format("din_functions_dlg.costact_%s_%i", section_item, cost_increment_table[cnt_item])}
    local textn = ltx:r_string(section_item, "inv_name")
    local text = string.format("%s x %i", game.translate_string(textn), cnt_item)
    assembly_dialogs.assembly_phrase_ex(text, phr_tbl, phr_k, dlg, string.format("%s%i", type_key, 1), input_key, a_tbl, p_tbl)
    phr_k = phr_k + 1    
    exit_table[1] = string.format("%s%i", type_key, 1)
--- после завершения формирования реальных фраз, надо обработать массив дополнительных линков.
    --phr_k = assembly_dialogs.final_link_BD(inside_key, inside_links, phr_tbl, phr_k, dlg, type_key)
    --phr_k = assembly_dialogs.final_blocklink_BD(block_links, phr_tbl, phr_k, dlg, type_key)
    return {phr_k, exit_table}
    --- возвращаем следующий свободный ид в таблице фраз, и таблицу полных ид фраз, являющихся выходами данного блока.
end
	function buy_terminal_all(phr_tbl, phr_k, dlg, block_name, block_input_key)
    --[[
        блок выбора оплаты, можно подключать к стандартно-формируемым диалогам.
        выбор производится по всем категориям средств оплаты
        структура:
        NPC                 Actor                 NPC
        чем расплатишься? ->     [1 артефакт "А"] ->      Хорошо, получаются у нас
                        [2 артефакта "А"]        вот такие условия сделки:
                         [4 артефакта "А"]        (вывод условий сделки)
                        * * *
                        [1 артефакт "B"]
                        * * *
    ]]
    local exit_table = {}
    local exit_blocks_data = {}
    local text = "Чем расплатишься?"
    local PHR_id = block_name.."buy_terminal_all_input"
    local a_tbl, p_tbl = {}, {}
    assembly_dialogs.assembly_phrase_ex(text, phr_tbl, phr_k, dlg, PHR_id, block_input_key, a_tbl, p_tbl)
    phr_k = phr_k + 1
    for k, v in pairs(af_ranks) do
        local a = 1
        while a < 100 do
            local BD = barter_manager_3.dlgblock_select_costitem(k, a, phr_tbl, phr_k, dlg, string.format("sel_cost_all_%s_%i", k, a), PHR_id)
            phr_k = BD[1]
            local n_exit = BD[2]
            table.insert(exit_blocks_data, n_exit[1])
        a = a * 2
        end
    end
    text = "Итак, получается у нас с тобой сделка на таких условиях:"
    local PHR_exit = block_name.."buy_terminal_all_exit"
    local exit_created = false
    local exit_a_tbl = {
    "barter_manager_3.get_params_transaction"
    }
    for i, j in pairs(exit_blocks_data) do
        if exit_created then
            phr_tbl[phr_k] = dlg:AddPhrase("", assembly_dialogs.dia(PHR_exit), assembly_dialogs.dia(j), 0)
            phr_k = phr_k + 1
        else
            assembly_dialogs.assembly_phrase_ex(text, phr_tbl, phr_k, dlg, PHR_exit, j, exit_a_tbl, p_tbl)
            phr_k = phr_k + 1
            exit_created = true
        end
    end
    exit_table[1] = PHR_exit
    return {phr_k, exit_table}
    --- возвращаем следующий свободный ид в таблице фраз, и таблицу полных ид фраз, являющихся выходами данного блока.
end

Тут все наверно в курсе, что я занимаюсь разработкой мода "Судьба Зоны" который уже год. Ну так вот, выложенный здесь модуль - это одна из технологий этого мода. В несколько более горбатом виде, но понятном "для своих", этот модуль существовал в моде еще два года назад. Все до единого диалоги мода, были написаны с помощью этой технологии.
Забегая дальше, кто-то может помнит, что у меня было в планах написать скриптовый гуи-интерфейс для окна диалога, планы эти никуда не делись, в версиях мода после 0.5 это все будет. И в частности могу пообещать что тот скриптовый гуи будет воспринимать диалоги, написанные по вот этой выложенной здесь технологии.

Опять же просьба к модераторам - вот эту тему http://www.amk-team.ru/forum/index.php?showtopic=13195под снос.
Все нужные данные скопированы сюда, так что хранить этот модуль в отдельной теме смысла уже не имеет.
Кстати модули Артоса гораздо больше заслуживают отдельной темы, чем это мое творение.

local table_read = false
local table_shiftindex = 0
local go_read = false
	function v_to_str(v)
return string.format("x: %G; y: %G; z: %G", v.x, v.y, v.z)
end
	function any_data_tostring(v)
local string_returned = ""
local t = type(v)
    if t == "string" then                    --- COMPLETE
        string_returned = " STRING:"..v
    elseif t == "number" then                --- COMPLETE
        string_returned = " NUMBER:"..tostring(v)                
    elseif t == "boolean" then                --- COMPLETE
        if v then
            string_returned = " BOOLEAN:true"
        else
            string_returned = " BOOLEAN:false"
        end
    elseif t == "nil" then                    --- NIL ТЕРЯЕТСЯ ПРИ МАССОВОЙ ПЕРЕДАЧЕ АРГУМЕНТА. COMPLETE
        string_returned = " NIL"
    elseif t == "function" then                --- COMPLETE
        string_returned = " FUNCTION"
        if not v then string_returned = string_returned.."<nil>" end
    elseif t == "userdata" then                --- COMPLETE
        string_returned = " USERDATA"
        go_read = true
        if not v then string_returned = string_returned.."<nil>" end
    elseif t == "table" then
        string_returned = " TABLE:"
        table_read = true
        if not v then string_returned = string_returned.."<nil>" end
    else
        string_returned = "UNKNOWN DATA"
        if not v then string_returned = string_returned.."<nil>" end
    end
    return string_returned
end
	function table_to_str(tbl)
    if type(tbl) ~= "table" then return end
    table_shiftindex = table_shiftindex + 1
    local s = string.rep(" ", table_shiftindex * 3)
    log(s.."{")
    for k,v in pairs(tbl) do
        --log(s.."[KEY: "..zander_utils.any_data_tostring(k).."] = VALUE: "..zander_utils.any_data_tostring(v)..";")
        log(string.format("%s[KEY: %s] = VALUE: %s;", s, zander_utils.any_data_tostring(k), zander_utils.any_data_tostring(v)))
        if table_read then
        table_read = false
            if type(k) == "table" then
                log(s.."Table key:")
                zander_utils.table_to_str(k)
            end
            if type(v) == "table" then
                log(s.."Table value:")
                zander_utils.table_to_str(v)
            end
        end
        if go_read then
        --game_object_tostr(v)
        end
    end
    log(s.."}")
    table_shiftindex = table_shiftindex - 1
end
	function userdata_to_log(go)
if type(go) == "userdata" then
if go.id ~= nil and type(go.id) == 'function' and go.section ~= nil and type(go.section) == 'function' then
    local t = {
        id = go:id(),
        parent = go:parent(),
        section = go:section(),
        position = v_to_str(go:position()),
        lv = go:level_vertex_id(),
        gv = go:game_vertex_id(),
        clsid = go:clsid(),
        story_id = go:story_id()
        ---
    }
    log(";*game_object parameters")
    table_to_str(t)
elseif go.x and type(go.x) == 'number' then
--- attempt unpack as vector
    if go.y and go.z and type(go.y) == 'number' and type (go.z) == 'number' then
    log(";vector parameters")
    local v = {
        x = go.x,
        y = go.y,
        z = go.z
    }
    table_to_str(v)
    else
    log("unknown userdata")
    end
elseif go.id and type(go.id) == 'number' and go.section_name and type(go.section_name) == 'function' then
    local ts = {
        id = go.id,
        section = go:section_name(),
        name = go:name(),
        clsid = go:clsid()
    }
    log(";*server_object parameters")
    table_to_str(ts)
else
    log("unknown userdata")
end
end
end
	function zander_log(...)
table_shiftindex = 0
local o = {...}
for k,v in pairs(o) do
log(zander_utils.any_data_tostring(v)..";")
    if table_read then
    table_read = false
    zander_utils.table_to_str(v)
    end
    if go_read and type(v) == "userdata" then
    zander_utils.userdata_to_log(v)
    end
end
end

Как использовать: для удобства можно в _g.script глобально объявить, я так у себя сделал:

if zander_utils then zander_log = zander_utils.zander_log end


Ну и потребуется рабочая функция log. У меня для этого луа-расширение RvP, честно не в курсе насчет других вариантов, но поидее должно быть у каждого)
 
Ну так вот, как это работает. В любом месте какого угодно кода, где вам надо вывести в лог _что угодно_ - пишем

zander_log(любое, число, каких-то, ваших, переменных)


Причем, вам вообще не надо переживать по поводу того, к какому типу относятся эти переменные.
В лог будет выведено, и тип переменных, и их значение. В случае таблиц любой структуры, производится их разбор с разжевыванием типов и значений всех ключей и всех значений в таблице и всех ее подтаблицах независимо от уровня вложенности, и структура таблицы наглядно отображается в логе. Если где-бы то ни было обнаруживается значение типа юзердата, производится попытка его опознать как клиентский объект, как серверный объект, как вектор. Если окажется что юзердата является клиентским/серверным объектом/вектором, будут выведены в лог некоторые основные параметры объекта. Если опознание не удалось, то просто пишем unknown userdata - неизвестный тип юзердаты.
 
Пример - вот так разбираются таблицы.


 TABLE:;
   {
   [KEY:  NUMBER:1] = VALUE:  USERDATA;
   [KEY:  NUMBER:2] = VALUE:  STRING:smart;
   [KEY:  NUMBER:3] = VALUE:  STRING:scrhud_dangicon_sa;
   [KEY:  NUMBER:4] = VALUE:  TABLE:;
   Table value:
      {
      [KEY:  NUMBER:1] = VALUE:  NUMBER:14;
      [KEY:  NUMBER:2] = VALUE:  NUMBER:14;
      }
   [KEY:  NUMBER:5] = VALUE:  STRING:atp_killer_barlager;
   }



  • Полезно 2

Мод, где не бывает одинаковых путей - Судьба Зоны. (Лучшее, что у меня получилось на X-Ray) На базе модифицированного движка OGSR Engine.

Бывший мододел на X-Ray / Начинающий игродел на Unreal Engine. Программист.

AMD Ryzen 9 7950X (16 ядер, 32 потока, 5.75 ГГц); RTX 3080; 128 ГБ DDR5; Arctic Liquid Freezer II-420; 3 ТБ SSD PCIe 4.0; 4ТБ HDD.

Ссылка на комментарий

По теме... откопайте кто-нибудь мне инструкцию-описание к расширению Луа от RvP. А то пользуюсь сам по памяти, скинуть могу, а вот инструкцию к нему приложить, что там лежит - кхм, не смогу)

https://code.google.com/p/xrluafix/wiki/description
  • Спасибо 1
Ссылка на комментарий

Там нету ни слова о восстановлении функции log, хотя она восстановлена а в чистом ТЧ не работает, если мне память не изменяет.

Ну, ладно. Пойдет)


Изменено пользователем Zander_driver
  • Полезно 1

Мод, где не бывает одинаковых путей - Судьба Зоны. (Лучшее, что у меня получилось на X-Ray) На базе модифицированного движка OGSR Engine.

Бывший мододел на X-Ray / Начинающий игродел на Unreal Engine. Программист.

AMD Ryzen 9 7950X (16 ядер, 32 потока, 5.75 ГГц); RTX 3080; 128 ГБ DDR5; Arctic Liquid Freezer II-420; 3 ТБ SSD PCIe 4.0; 4ТБ HDD.

Ссылка на комментарий

Там нету ни слова о восстановлении функции log, хотя она восстановлена а в чистом ТЧ не работает, если мне память не изменяет.

Функция вывода в лог там есть, называется log123. Я сам эту либу когда-то собирал, все описание точное, за исключением разве что

--xml
--Lua XML
--Тоже оригинальная Lua библиотека. Более подробно в шапке.

она не входит в комплект с этой библиотекой, её надо качать отдельно и подключать через require. Так например сделано в последнем OGSE.

 

----------------------------

А ведь, несколько лет назад уже была попытка создать такую тему: http://www.amk-team.ru/forum/index.php?showtopic=12753&p=794719

но кое-кто внизу темы полез в... эм... кхм... вы поняли куда и в конечном итоге тему закрыли :|

Изменено пользователем Shadows
Ссылка на комментарий

 

 

Я запустил игру и посмотрел своими глазами. А своим глазам я пока ещё верю

Скомпилил то что на гуглокоде? Или запустил OGSE? Если второе, то ясное дело - в OGSE все это уже есть, LuaXML_lib.dll называется, лежит в bin\extensions.

 

http://viremo.eludi.net/LuaXML/

 

http://www.amk-team.ru/forum/index.php?showtopic=6185&p=832428

тем более ты сам когда-то тоже самое говорил :D

Ссылка на комментарий

LuaFix 5.1.5 by svarog2741
Он основан на "LuaFix by RvP".

Обновлена библиотека Lua до версии 5.1.5,

так же до последней версии LuaFileSystem.

https://yadi.sk/d/gBDAwRxCgvpn7

 

  • Полезно 1
Ссылка на комментарий

Вот кстати подправленная функция поиска тегов для LuaXML: http://www.amk-team.ru/forum/index.php?showtopic=6185&p=833939

 

Было бы неплохо, если кто-то собрал и выложил LuaXML библиотеку со всеми подобными правками готовую к употреблению. У меня к сожалению на руках нет.

 

З.Ы. посты 27-34, думаю можно удалить, разобрались.

Изменено пользователем Shadows
Ссылка на комментарий

Предлагаю оставить в шапке ссылку на тему Полезные скрипты, которую тут выше упомянули.

 

Снова насильно поделюсь функцией поиска левел вертекса по координатам для ТЧ. В этом посте описана проблема и сама функция с некоторым ограничением в реализации. Её нужно улучшать, но вряд ли ее кто будет использовать, в то время я опубликовал костыльную оптимизированную идею, нежели полноценное решение. Тем не менее она рабочая :)

PS: у Маландринуса тоже были модули по всяким сигналам/таймерам, уж не помню, что там точно было.

Изменено пользователем Desertir

ТЧ 1.0004. SAP и Trans mod

github

Ссылка на комментарий

Вот еще нашел, потом оформлю в свой пост:

 

Функция форматирования текста от Nazgool, которую он по просьбе написал мне пару лет назад. Требовалось для расстановки символов переноса строк в GUI-окнах сталкера.

-- Форматирование текста для вставки в окно определенного размера
-- Модификатор 'f' - full (или 'ns') - убираются переводы строк и лишние пробелы
-- Модификатор 'n' - перевод строки - убираются переводы строк
-- Модификатор 's' - space - убираются лишние пробелы (несколько пробелов подряд сбиваются в один, убираются пробелы в начале и конце строки)
-- По умолчанию текст оставляется как есть
function FormatText(text, width, mod)
	local new_line_symbol = '\\n' -- '\n'
	local mod_f, mod_n, mod_s
	if type(mod) == 'string' then
		mod_f = mod:match('f')
		mod_n = mod:match('n')
		mod_s = mod:match('s')
	end
	local function TextMod(txt)
		if mod_f or mod_n and mod_s then
			txt = txt:gsub('%s%s+', ' '):gsub('^%s*(.*)%s*$', '%1')
		elseif mod_n then
			txt = txt:gsub(new_line_symbol,'')
		elseif mod_s then
			txt = txt:gsub('[^%S'..new_line_symbol..']+', ' '):gsub('^%s*(.*)%s*$', '%1')
		end
		return txt
	end
	local function SearchEndLine(l, str)
		while str:sub(l, l) ~= ' ' do
			if l == 0 then l = width break end
			l = l - 1
		end
		return l
	end
	text = type(text) == 'string' and TextMod(text) or ''
	width = tonumber(width) or 0
	local NewTab = {}
	local LineTab = {}
	for w in text:gmatch('[^'..new_line_symbol..']+') do
		LineTab[#LineTab + 1] = w
	end
	for i = 1, #LineTab do
		local str = LineTab[i]
		while #str > width do
			local line_len = width
			local s_last = str:sub(line_len, line_len)
			local s_next = str:sub(line_len + 1, line_len + 1)
			if s_last == ' ' or s_next == ' ' then
				NewTab[#NewTab + 1] = TextMod(str:sub(1, line_len))
			else
				local w_line = SearchEndLine(line_len, str)
				NewTab[#NewTab + 1] = TextMod(str:sub(1, w_line))
				line_len = w_line
			end
			str = str:sub(line_len + 1)
		end
		NewTab[#NewTab + 1] = TextMod(str)
	end
	return table.concat(NewTab, new_line_symbol)
end

-- Вызов
local new_text = FormatText(s, 50,'f') -- аргументы 1-й форматируемая строка, 2-й ширина строки(кол-во символов), 3-й модификатор

 

 

Должна разбивать строку на подстроки расставляя переносы, длина строки задается во втором аргументе

FormatText("тестовый текст тестовый текст тестовый текст тестовый текст тестовый текст тестовый текст", 20) => должно разбить на подстроки по 20 символов
Изменено пользователем Shadows
Ссылка на комментарий
последнее как-то монстровидно

Ну был когда-то и такой вариант функции.

Да мало ли их у меня было? Тем более пару лет назад.

Сейчас например пользуюсь такой :

 

-- Форматирование текста по ширине
function string.width(str, width, indent, paragrapf)
    indent    = indent    or 0
    paragrapf = paragrapf or 0
    width     = (width or 80) - indent
    
    local i = string.rep(' ', indent)
    local t = {}
    local line = string.rep(' ', paragrapf)
    
    for exp, word in str:gmatch('(%s-(%S+))') do
        if line == '' and #t ~= 0 then
            exp = word
        end

        local l_len = #line
        local len = l_len + #exp
        
        if len > width then
            t[#t+1], line = line, word
        else
            line = line .. exp
            if l_len == width then
                t[#t+1], line = line, ''
            end
        end
    end

    t[#t+1] = line

    return i .. table.concat(t, '\n' .. i)
end 

 

 

Всё зависит от целей, которые преследуются.

 

 

не помешает инструкция и примеры использования

Вот я писал для себя когда-то. Пришло время поделиться :)

Там и что-то вроде примеров есть.

Правда одну функцию так и не дописал, но она мне и не нужна была, да и сейчас не пользуюсь.

https://yadi.sk/d/JnegzDX7ibqYw

 

Можно посмотреть в как я использовал luaxml в файлах OGSE - ogse_musicbox.script, ui_safe.script, ... какие там ещё ...

 

P.S. В принципе можно обойтись и без LuaXml_lib.dll. С необходимостью использования всех функций этой библиотеки в контексте сталкера я ещё не сталкивался. Достаточно в файл LuaXml.lua дописать три основные функции из dll-ки. Собственно я так и сделал.

Изменено пользователем Nazgool
  • Спасибо 1
Ссылка на комментарий

Внесу и свои три копейки)

Небольшой GUI-класс для имитации поддержки вводв с русской раскладки клавиатуры в CUIEditBox:

--[[-----------------------------------------------------------------------------------------------------
	File              : rus_edit_box.script
	Description       : Эмуляция русской раскладки клавиатуры в CUIEditBox (ТЧ)
	Created           : 13.11.2014
	Copyright         : naxac
---------------------------------------------------------------------------------------------------------
Обязательные передаваемые параметры:
 		x, y - координаты верхнего левого угла относительно элемента окна, к которому приаттачивам бокс
		width - ширина окна (высота фиксированная, 25), рекомендуется хотя бы >=100
		stat - элемент базового класса, к которому приаттачиваем бокс
type - необязательный параметр: текстура бокса (см. таблицу box_textures). По умолчанию - ui_linetext_e

Методы:	GetText() - получить введённый текст,
		SetText(string) - поместить в поле текст
		SetTextColor(number,number,number,number) - задать цвет текста (A,R,G,

Пример использования:
	self.edit_box = rus_edit_box.NewEditBox(self.form, 10, 45, 200, "pda")	-- приаттачиваем бокс к базовому классу
Обязательно добавляем в каллбэк на нажатие клавиш базового класса (OnKeyboard):
	self.edit_box:KeyboardCallback(dik)
--]]-----------------------------------------------------------------------------------------------------
local string_sub = string.sub

local eng = [[qwertyuiop[]asdfghjkl;'zxcvbnm,./QWERTYUIOP{}ASDFGHJKL:"ZXCVBNM<>?!@#$%^&*()-_+=\| 0123456789]]
local rus = [[йцукенгшщзхъфывапролджэячсмитьбю.ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,!"№;%:?*()-_+=\/ 0123456789]]
local translit_tbl = {}
for pos = 1, #rus do
	translit_tbl[string_sub(eng, pos, pos)] = string_sub(rus, pos, pos)
end

function translit(s)
	return translit_tbl[s] or s
end

function string.split(str,k)
	local _f, _s = string.match(str, "([^"..k.."]*)"..k.."([^"..k.."]*)")
	return (_f or ""), (_s or "")
end
----------------------------------------------------------------------------------------

class "NewEditBox" (CUIScriptWnd)

local kr = "_"	-- курсор
local box_textures = {
	def		= "ui_linetext_e",
	pda		= "ui_icons_PDA_dialog_string"
	}

function NewEditBox:__init(stat, x, y, width, type) super()
	self.text = kr
	self.lang = "RUS"
	
	local texture = box_textures["def"]
	if type and box_textures[type] then
		texture = box_textures[type]
	end
	
	self.st = CUIStatic()
	self.st:Init(x, y, width, 25)
	
	stat:AttachChild(self.st)
	
	self.edit_box = CUIEditBox()
	self.edit_box:SetAutoDelete(true)
    self.edit_box:Init(0,2,width-39,22)
	self.edit_box:InitTexture(texture)
	self.edit_box:SetTextColor(GetARGB(0,0,0,0))
	self.edit_box:SetFont(GetFontLetterica18Russian())
    self.st:AttachChild(self.edit_box)
	
	self.text_stat = CUIStatic()
	self.text_stat:SetAutoDelete(true)
    self.text_stat:Init(2,4,width-42,22)
	self.text_stat:SetText(self.text)
	self.text_stat:SetTextColor(255,180,255,140)
	self.text_stat:SetFont(GetFontLetterica18Russian())
    self.st:AttachChild(self.text_stat)
	
	self.btn_lang = CUI3tButton()
	self.btn_lang:Init(width-37,0,37,25)
	self.btn_lang:SetAutoDelete(true)
	self.btn_lang:InitTexture("ui_hud_button_voting_01")
	self.btn_lang:SetText(self.lang)
	self.btn_lang:SetTextAlign(CGameFont.alCenter)
	self.btn_lang:SetTextY(1)
	self.btn_lang:SetTextColor(255,216,186,140)
	self.st:AttachChild(self.btn_lang)
	self:Register(self.btn_lang, "btn_lang")
	self:AddCallback("btn_lang", ui_events.BUTTON_CLICKED, self.on_ButtonChangeLang_clicked, self)
end

function NewEditBox:on_ButtonChangeLang_clicked()
	self.lang = (self.lang == "RUS" and "ENG") or "RUS"
	self.btn_lang:SetText(self.lang)
end

function NewEditBox:GetText()
	return string.gsub(self.text, kr, "")
end

function NewEditBox:SetText(text)
	self.text = text..kr
	self.text_stat:SetText(self.text)
end

function NewEditBox:SetTextColor(a,r,g,
	self.text_stat:SetTextColor(a,r,g,
end


function NewEditBox:ChangeText(letter)
	if letter == kr then return end
	local lett = letter and (self.lang=="RUS" and translit(letter) or letter) or ""
	
	local s1, s2 = string.split(self.text,kr)
	self.text = s1..lett..kr..s2
	
	self.text_stat:SetText(self.text)
end

function NewEditBox:DeleteLetter(mode)
	local s1, s2 = string.split(self.text,kr)
	if mode then
		s1 = #s1>1 and string_sub(s1,1,-2) or ""
	else
		s2 = #s2>1 and string_sub(s2,2) or ""
	end
	self.text = s1..kr..s2
	
	self.text_stat:SetText(self.text)
end

function NewEditBox:CursorMove(mode)
	local s1, s2 = string.split(self.text,kr)
	local str1, str2 = "", ""
	if mode then
		str1 = s1..(#s2>0 and string_sub(s2,1,1) or "")
		str2 = #s2>1 and string_sub(s2,2) or ""
	else
		str1 = #s1>1 and string_sub(s1,1,-2) or ""
		str2 = (#s1>0 and string_sub(s1,-1) or "")..s2
	end
	self.text = str1..kr..str2
	
	self.text_stat:SetText(self.text)
end

function NewEditBox:KeyboardCallback(dik)
	if dik == DIK_keys.DIK_BACK then self:DeleteLetter(true)
	elseif dik == DIK_keys.DIK_DELETE then self:DeleteLetter(false)
	elseif dik == DIK_keys.DIK_LEFT then self:CursorMove(false)
	elseif dik == DIK_keys.DIK_RIGHT then self:CursorMove(true)
	elseif dik == DIK_keys.DIK_LMENU or dik == DIK_keys.DIK_RMENU then self:on_ButtonChangeLang_clicked()
	else
		local letter = string_sub(self.edit_box:GetText(),-1)
		if letter and letter~="" then
			self:ChangeText(letter)
			self.edit_box:SetText("")
		end
	end
end

 



Функция string.lower с поддержкой кириллицы

-- 'string.lower' с поддержкой кириллицы.
local low = [[qwertyuiopasdfghjklzxcvbnmйцукенгшщзхъфывапролджэячсмитьбюё]]
local high = [[QWERTYUIOPASDFGHJKLZXCVBNMЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮЁ]]
function string.lower1 (str)
	local ret = ""
	local f
	for s in string.gmatch(str, ".") do
		f = string.find(high, s, 1, true)
		ret = ret..(f and string.sub(low,f,f) or s)
	end
	return ret
endend

Добавить куда-нибудь в _g.script

 

Изменено пользователем Murarius
  • Спасибо 2

Аддон для ОП-2.09.2: Яндекс/Google/GitHub

naxac.gif

Ссылка на комментарий

Ну в общем есть вот что, правда автора не помню. Clear xrGame. https://yadi.sk/d/IWIubHufMM4Mo

 

Вылечивает xrgame.dll ТЧ 1.0006 от загрязнения лога/консоли ненужными дебаг-сообщениями вида:

 

--NeedToDestroyObject

Destroying local grenade

sv destroy object

ge_destroy not found on server

sv ownership id_parent id_entity

sv !ownership (entity already has parent)

sv reject. id_parent id_entity

[16-9] get_xml_name for

 

Проверялся как на чистой dll, так и на пропатченной через x-ray extensions/cut x-ray.

С мультиплеером правка несовместима.

 

Установка:

Скопировать clear_xrGame.cmd и patch2.exe в папку с xrgame.dll и запустить clear_xrGame.cmd.

 

Также для полной чистоты лога рекомендуется использовать "Исправленный экзешник для S.T.A.L.K.E.R ТЧ 1.0006 (v2)" или в своем экзешнике хекс-редактором найти и обнулить байты фразы: cl setDestroy

 

 

Если надо, могу скинуть правленные файлы файлы для компиляции 1.0007 в 2012 студии. ( не знаю, нужно ли это здесь.)

Изменено пользователем Forser
  • Спасибо 1
  • Полезно 1
Ссылка на комментарий

FormatText

Проверил работу функции - все работает, разбивает текст как и задумывалось.

 

st = CUIStatic()
st:Init("ui\\ui_asus_intro", 0, 0, 295, 140)
st:SetStretchTexture(true)
st:SetText(FormatText(game.translate_string("enc_weapons1_wpn-ak74u"), 40))
st:SetTextComplexMode(true)
get_hud():AddDialogToRender(st)
Изменено пользователем Shadows
  • Спасибо 1
  • Полезно 1
Ссылка на комментарий

В общем, делал упрощение проверок для себя. Понадобится кому-либо это или нет, я не знаю. 

В конец _g:

function get_slot_section(n)
 local actor = db.actor
  local item = actor and actor:item_in_slot(n)
  return item and item:section()
end
 

Пример использования:
 

  local section = get_slot_section(номер_слота)
   if section == "предмет" then
    ...
  end 

 

 

В конец _g:

function active_slot_section(n)
 local actor = db.actor
 local slot = actor and actor:item_in_slot(n)
	if slot and actor:active_slot()==n then
	 local item = actor and actor:item_in_slot(n)
  	 return item and item:section()
     end
end

Пример:
 


  local section = active_slot_section(номер_слота)
   if section == "предмет" then
    ...
  end 

 


Спасибо за подсказку о подобных функциях: Kirgudu

  • Спасибо 2
Ссылка на комментарий

Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

Создать аккаунт

Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!

Зарегистрировать новый аккаунт

Войти

Есть аккаунт? Войти.

Войти
  • Недавно просматривали   0 пользователей

    • Ни один зарегистрированный пользователь не просматривает эту страницу.
  • Куратор(ы) темы:

×
×
  • Создать...