Это популярное сообщение. Malandrinus 615 Опубликовано 8 Июля 2009 Это популярное сообщение. Поделиться Опубликовано 8 Июля 2009 (изменено) В данной теме собраны сведения по скриптовой модели сталкера: функции и классы, методы и свойства, взаимосвязь классов и последовательность работы с ними, связь работы классов и файлов конфигураций. К наполнению темы приглашаются все желающие. В наполнении темы непосредственно участвовали и существенно мне помогли: @Monnoroch, @Kolmogor, @Unnamed Black Wolf, @меченый(стрелок), @IQDDD, @Kirag, @Taroz, @dan, @7.9, @Garry_Galler, @AKKK1, @Bak и много других людей. Скрытый текст Скрытый текст класс alife_simulator. Базовые операции с серверными объектами. Пространства имён. Глобальные функции для большого числа задач. "Создание своего класса" и "Наследование от экспортированных классов". Базовые сведения об объектно-ориентированном программировании для сталкера. Необходимо прочитать, для понимания темы про биндер и некоторых других. В одном посте: Общие слова об архитектуре и скриптовой модели сталкера "Класс object_binder" расширение онлайновых объектов, колбеки, сохранение состояния. "Класс net_packet" Регистрация скриптовых классов с помощью object_factory Серверные классы. Часть 1 Иерархия серверных классов, описание не закончено. Серверные классы. Часть 2 Картинка структуры наследования и несколько заключительных слов Клиентские классы Скрытый текст Класс game_object Интерфейс ко всем онлайновым (клиентским объектам) Класс hit для нанесения урона скриптом и другая информация (IQDDD) Некоторая информация по управлению путями патрулирования здесь (Kirag) и здесь (Taroz) Неполная информация по управлению памятью неписей с примером здесь (Bak) Физическая оболочка объектов (Garry_Galler) Пост о выборе (подборе) оружия НПС и стрельбы (*Shoker*) Скрытый текст Управление заданиями Класс CGameTask и другие вспомогательные классы и функции. Управление инфопорциями Функции, колбеки, форматы файлов Список специальных системных инфопорций (Unnamed Black Wolf) Система профилей и алгоритм генерации имён. Форматы файлов, функции Дополнительная информация по параметрам профилей terrain_sect (Kolmogor) Диалоги. Часть 1 Форматы файлов, базовые сведения Диалоги. Часть 2 Скриптовые диалоги Диалоги. Часть 3 Тематическая подборка функций управления диалогами Скрытый текст Функции времени Тематическая подборка функций, связанных с управлением игровым временем. Класс CTime Вспомогательный класс для управления игровым временем Полезная скриптовая функция с использованием CTime (Garry_Galler) Скрытый текст class ini_file (меченый(стрелок)) Класс FS и CSavedGameWrapper Бинарный доступ к файлам, в том числе в игровых архивах, управление сохранёнными играми. Скрипт уровня. Забытая фишка с колбеком на заход на уровень Класс vector Некоторая полезная информация о разных вещах (меченый(стрелок)) "Класс render_device" Направление и положение камеры, характеристики экрана, программная пауза игры и др. Некоторая информация о различиях между ТЧ и ЗП в системе оконных классов и колбеков. (lekzd) Неплохо бы развить эту тему! Некоторая полезная информация о скриптовых функциях из модуля _g.script. (lekzd) Также требует развития! Полезные функции для работы с графом игры (Garry_Galler) В одном посте: Класс profile_timer Отладочные измерения скорости работы фрагментов программы Класс client_spawn_manager Колбек на выход в онлайн без использования биндера. Работа с консолью. Класс CConsole Анимации цвета. Класс color_animator Всякие моргающие элементы в окнах и пр. Управление постэффектами. Скриптовые постэффекты. Класс effector Класс sound_object. Проигрывание звуков в игре в произвольном месте, от произвольного объекта, в голове актора. (Shadows) Пост удалён автором (прим. Kirgudu) Скрытый текст Оконные классы Некоторая общая информация о создании окон Список методов, XML-тегов и событий для оконных классов (ТЧ/ЧН/ЗП) (Norman Eisenherz) Представление материала в моих статьях оптимизировано для онлайнового просмотра. Если кому не хочется лазить по спойлерам, а нужно просмотреть текст какого-либо поста "потоком", то могу рекомендовать просмотр в режиме "текстовая версия". В этом же режиме удобно сохранять содержимое темы на диск. (прим. Malandrinus) Изменено 30 Июля пользователем Kirgudu 5 5 16 Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Artos 99 Опубликовано 19 Апреля 2012 Поделиться Опубликовано 19 Апреля 2012 malandrinus Все же информация о наличии бага в исходном классе CTime скорее всего ошибочна. 1. При каких (общих) условиях проявляется наличие этого бага: сбой старших разрядов счетчика? а) Версия игры SHoC и версия патча 1.0006; б) Использование расширителя x-Ray Extension; Не могу уверждать за ваших тестеров (OGSE), но (судя по выложенным кодам работы с CTime) вероятно также сюда можно отнести: в) Использование функции get_value() добавленной в класс CTime, читающей разряды счетчика. А точнее, ее обязательное использование при каждом(!) определении дельты времени. Примечание: Когда в первые разы получилось отловить баг с использованием твоих кодов, мною вызов чтения разрядов был перенесен в подфункцию уже контроля пойманного сбоя, что исключило проявление бага ... Вернул вызовы в код апдейта - вернулся и баг. 2. В моих попытках выяснить причину сбоя определилось еще одно обязательное условие: г) Работа с "zone_informer", т.е. отображение партиклами различных "шейдерных" объектов. Хотя ... это может быть и неким катализатором, ускоряющем появление сбоя. По крайней мере, именно при наличии всех четырех условий (а...г) мне удается воспроизводить баг практически на каждом запуске. Если же отсутствует условие в) (т.е. не читаются разряды счетчиков), то наличие многочисленных "ловушек" бага во всех кодах игры (это сотни счетчиков класса CTime) ни разу не подтвердило сбоя этого класса (при наличии/отсутствии условий а,б,г). Учитывая что собственно баг из разряда "такого не может быть", но он все же стал появляться - следует отнести его к багу именно изменения класса расширителем библиотеки и сбою именно функции get_value, которая вероятно вкупе с использование из той же библиотеки функций работы с шейдерами/матрицами/... и провоцирует наступление сбоя. Далее - только анализ кода правленной DLL'ки и эксперименты с нею могут подтвердить/опровергнуть ... "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Malandrinus 615 Опубликовано 20 Апреля 2012 Автор Поделиться Опубликовано 20 Апреля 2012 (изменено) Функция get_value ничего с объектом не делает, только читает из него данные. Ошибку я увидел сперва безо всякого применения этой функции, а использовал её только потом, чтобы увидеть, что именно происходит. Кстати, а что такое zone_informer? Не "Zones Editor" часом имеешь в виду? Если так, то у нас в команде его никто не использует, тестеры и подавно. Он даже не включён в код мода. Изменено 20 Апреля 2012 пользователем malandrinus Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Artos 99 Опубликовано 20 Апреля 2012 Поделиться Опубликовано 20 Апреля 2012 (изменено) Однако именно включение данного вызова в код проверки дельты времени в каждый цикл апдейта как раз и дает 100%-ную повторяемость проявления бага. Вот код, эквивалентный твоему, и которым ловлю баг: local oCTime = nil --/ запомненный счетчик времени (CTime) local bGetValue = nil --/ флаг подключения x-Ray Extension function OnSpawn(e) oCTime = game.get_game_time() bGetValue = (type(oCTime.get_value) == 'function') end function OnUpdate(e) --/ проверка наличия 'бага CTime' local oCTimeCur = game.get_game_time() --/ счетчик текущего игрового времени local iDiffSec = oCTimeCur:diffSec(oCTime) --/ длительность паузы между апдейтами (sec: ~0.1 ... 1.xx ...) if iDiffSec > 999 or iDiffSec < 0 then --/ обнаружен сбой (баг CTime)? local CTimeToLog = function(time,typ) local Y,M,D,h,m,s,ms = time:get(0,0,0,0,0,0,0) local sStr = Y.."."..M.."."..D.." "..h..":"..m..":"..s.." :"..ms if type(time.get_value) == 'function' then local lo,hi = time:get_value() --/ читаем биты счетчика времени log("%s:Test_CTime(%s)=>[%s]: hi|lo=[ %s | %s ]", sModule, typ, sStr, string.format("%08x",hi), string.format("%08x",lo) ) --/#~# else log("%s:Test_CTime(%s)=>[%s]:<%s>", sModule, typ, sStr, "Info") --/#~# end end log( string.rep("~",78) ) --/#~# log("%s:Test_CTime:[%s %s]:DiffSec=[%s]/(%s)", sModule, oCTimeCur:dateToString(0), oCTimeCur:timeToString(3), string.format("%5.3f",iDiffSec), string.format("%5.3f", e.delta) ) --/#~# CTimeToLog(oCTime,"pre") --/ вывод в лог значений предыдущего счетчика времени CTimeToLog(oCTimeCur,"cur") --/ вывод в лог значений текущего счетчика времени log( string.rep("~",78) ) --/#~# if os then log("%s:Test_CTime:[%s]:os.clock=[%s]:%s", sModule, os.date(), string.format("%5.3f",os.clock()), Get_MemUsage(true)) --/#~# end abort("%s:Test_CTime:DiffSec=[%s]~?:<%s>", sModule, tostring(iDiffSec), "Error!") end oCTime = oCTimeCur --/ (пере)запоминаем счетчик текущего времени, заменяя прежний --/ #!?!# опционально: предположительно провоцирует баг! ---------- if bGetValue then local lo,hi = oCTime:get_value() --/#?# читаем биты счетчика запомненного (а значит текущего!) времени end --/ --------------------------------------------------------------- end Обращу внимание(!), что "на сейчас" оставил только один последний вызов проверки битов запоминаемого счетчика (по сути баласт). СтОит закомментировать нижние строки с get_value, любые манипуляции в игре, в том числе и с окнами и с zone_informer'ом не дают сбоев. Если же при этих строках на сэйве, который уже упоминал, поиграться с шейпами и инвентарными окнами - вылет считай гарантирован именно по сбою старших битов счетчика. Также, вот этот код: function Check_CTime(oCTime, sMsg, is_abort) local lo,hi = nil,nil if bGetValue then --/#!# опционально: предположительно провоцирует баг! lo,hi = oCTime:get_value() --/#?# читаем биты счетчика времени end local Y,M,D,h,m,s,ms = oCTime:get(0,0,0,0,0,0,0) if Y < 2002 or Y > 2013 then local sTxt = sModule..":Check_CTime=["..Y.."."..M.."."..D.." "..h..":"..m..":"..s.." :"..ms.."]" if lo and hi then sTxt = sTxt..":hi["..string.format("%08x",hi).."]:lo["..string.format("%08x",lo).."]" end sTxt = sTxt..":"..tostring(sMsg)..":<Warning!>" if is_abort then abort( (debug and debug.traceback(sTxt)) or sTxt ) elseif debug then _G.to_log( debug.traceback(sTxt) ) else _G.to_log( sTxt ) end end end - стоИт на всех(!) кодах игры/мода, где проверяется дельта времени по CTime, что по примерным подсчетам дает работу с более сотней-двумя объектов, некоторые из которых почти также апдейтятся, хотя с несколько иными частотами и в других циклах/потоках. И ни разу пока в этих объектах не был зарегистрирован сбой. ИМХО, все же налицо связь бага с именно расширителем библиотеки + использование zone_informer'а. По-меньшей мере оба условия, как ранее выразился - катализатор проявления этого бага. Ну а "очень редкий" баг - следствие одной из этих причин. P.S. Сорри, под "zone_informer"ом имею ввиду несколько модифицированные коды именно твоего "Zones Editor"а. У меня этот скрипт адаптирован под актуальные для меня варианты "xrLua Extensiion" (функционал кейлоггера) и модуль нет-пакетов m_netpk (читалка). Порой в игре/разработке просматриваю (функционал информера!) формы и расположения различных объектов. Функционал редактора не использую, так что в данном контексте только отображение шейпов партиклами и их очистка может играть какую-то роль. Подметил, что чаще и быстрее всего баг с Ctime насупает после показа шейпов перехода со Складов на Бар + их очистке + открытий/закрытий инвентаря. - ну а то, что вашими тестерами не используется - несколько меняет картину и ... остается вероятным виновником именно расширитель, а в моем случае информер (кстати, также использует расширитель) - является именно катализатором. Добавлено через 134 мин.: P.P.S. Еще немного пищи для размышлений: Если заменить вызов проверки бага из циклов апдейта актора на иной - спровоцировать ошибку не удается: function Start_Test_CTime() --/ проверка: CTime --event("actor_update"):register(Test_CTime) --/ for debug CTime --/ заменяем апдейт актора на иной: level.add_call(Test_CTime, function() abort("Test_CTime:~>FATAL_ERROR!") end) end Вероятно все же все дело в стеке. При различных манипуляциях с окнами и пр. наличие в стеке постоянно перечитываемого счетчика приводит к его сбою/затиранию старшего бита (u64 - вероятно хранится не в общих регистрах). Вынос/смещение счетчика по отношению к апдейту актора - изменило условия/структуру стека, сохранив ту же частоту опроса (у меня DiffSec ~0.070 game-second). Хотя ... вероятно теперь что-то иное может давать сбой (???), т.к. если были сбои/затирания стека - то врядли они куда исчезли. Просто в каких то вариантах они не критичны. А вот что корежит стек - можно только предполагать. ;-) Изменено 20 Апреля 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Полтергейст 37 Опубликовано 14 Мая 2012 Поделиться Опубликовано 14 Мая 2012 В ТЧ есть функция game_object.invulnerable(), которая не внесена в lua_help. Работает и на чтение, и на изменение - только что проверял. Ссылка на комментарий
Полтергейст 37 Опубликовано 25 Июня 2012 Поделиться Опубликовано 25 Июня 2012 (изменено) Недавно столкнулся с одной неприятной особенностью параметра m_smart_terrain_id, который есть у всех классов, наследуемых от cse_alife_monster_abstract. Я пробовал переделать скрипт smart_terrain.script так, чтобы регистрировать в них эксклюзивных NPC с помощью присвоения m_smart_terrain_id и последующего вызова register_npc из смарта. Появилась куча проблем с двойными регистрациями: несмотря на то, что я непосредственно перед регистрацией в смарт какого-нибудь NPC отключаю ему автопоиск смартов вызовом brain():can_choose_alife_tasks(false), он каким-то образом уже оказывается зарегистрированным в другом смарте ещё до этого вызова. А причина оказалась простой: у NPC, только что заспавненных скриптом, при запросе значения из этого параметра вызывается обновление планировщика brain():update(). Даже если я использую функцию smart_terrain_id() для запроса значения - тот же результат. Пример: function se_monster:on_register() cse_alife_monster_base.on_register(self) self.initialized = true local smart_id = self:smart_terrain_id() get_console():execute(self:name().."_smart_id_"..tostring(smart_id)) ... При скриптовом спавне первой попавшейся собаки выводит: ! Unknown command: dog_normal17423_smart_id_637 То есть NPC уже зарегистрирован в смарте, у которого id равен 637. Проблема решается выключением автопоиска смартов непосредственно перед запросом значения - тогда просто вернёт 65535. Пока не проверял, но предполагаю, что вызов can_choose_alife_tasks меняет flUseSmartTerrains в object_flags. Изменено 25 Июня 2012 пользователем Полтергейст Ссылка на комментарий
AndreySol 215 Опубликовано 22 Июля 2012 Поделиться Опубликовано 22 Июля 2012 Ошибочку обнаружил в описании "Пространство имён level": bool map_has_object_spot(int <id>, string <тип метки>) -- проверить наличие метки в действительности возвращает не bool, а 0 или число. Ссылка на комментарий
Real Wolf 34 Опубликовано 12 Августа 2012 Поделиться Опубликовано 12 Августа 2012 Заметил странность при работе с функцией level.vertex_in_direction. У меня она возвращает переданный ранее в нее вертекс(то есть ни о каком расстоянии по направлению не может быть и речи), если в векторе направления x - отрицательное. Для положительных x работает нормально. Ссылка на комментарий
Desertir 202 Опубликовано 13 Августа 2012 Поделиться Опубликовано 13 Августа 2012 Real Wolf, в теме скриптования есть посты на странице 223 в самом начале и пост 4489 про получение левел вертекса по позиции. В целом - не в тему, хотя там есть пояснение с картинкой. Если в направлении нужного нам лв будет разрыв аи-сетки (камень, дерево, стена и т.п.) то возвратится граничный лв. Прикол в том, что если лв, от которого считаем, и есть граничный, и направление в сторону разрыва, то вернется он сам. Не умеет level.vertex_in_direction перескакивать через пустоту в аи-сетке. ТЧ 1.0004. SAP и Trans mod github Ссылка на комментарий
Viнt@rь 50 Опубликовано 14 Августа 2012 Поделиться Опубликовано 14 Августа 2012 (изменено) Ребят, подскажите плз или дайте ссылку на то, как пользоваться функцией level.add_dialog_to_render(CUIDialogWnd*) (ЗП) нид срочно прикрепить к окну инвентаря элемент, просто создать кастомстатик не выход, так как в любом случае окно инвентаря накладывается поверх кастомстатика элемента Добавлено через 34 мин.: Разобрался как этой функцией запустить и вывести на экран оконный класс, довольно легко, но это можно сделать и более легкими путями. В общем все равно элемент остается под окном инвентаря, я так понял что можно мне нужный элемент приарттачить(хз как правильно пишется слово) к окну инвентаря, но не пойму как это сделать, будьте добры, подскажите плз Изменено 14 Августа 2012 пользователем Viнt@rь GUI для конвертера от бардака(всего и вся в форматы сдк) Полезный утиль-"Utilits pack(mod)" Ссылка на комментарий
_Призрак_ 11 Опубликовано 14 Августа 2012 Поделиться Опубликовано 14 Августа 2012 В оригинальном ЗП никак не приаттачить к окну. Решение есть - использовать X-Ray Extensions. В нем есть функция получения окна для дальнейшей работы с ним. Но работает эта идея только в полноэкранном режиме. К тому же есть там одна тонкость в получении - получить окно можно только на следующем апгрейде после получения инфопоршня об открытии Freedom Ссылка на комментарий
Zander_driver 10 333 Опубликовано 19 Августа 2012 Поделиться Опубликовано 19 Августа 2012 В общем все равно элемент остается под окном инвентаря, А выводить свое окно после открытия инвентаря или на апдейте не пробовали? Мод, где не бывает одинаковых путей - Судьба Зоны. (Лучшее, что у меня получилось на 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. Ссылка на комментарий
Полтергейст 37 Опубликовано 9 Сентября 2012 Поделиться Опубликовано 9 Сентября 2012 Обнаружил в игре такой баг (проверял на патче 1.0006): при спавне какого-нибудь объекта скриптом, функция on_register серверного объекта может вызваться дважды. Этим и объясняются "странности" с параметром m_smart_terrain_id: в том примере объект регистрировался в каком-то смарте до второго вызова on_register, если ему вызовом :can_choose_alife_tasks(false) не было запрещено это сделать. Обходить баг и выводить в консоль имена объектов, которые вызывают on_register дважды, можно как-то так (пример для se_monster): function se_monster:on_register() cse_alife_monster_base.on_register(self) if self.initialized then get_console():execute(self:name().."_already_registered_in_simulation") return end self.initialized = true ...end Ещё выяснил, где появляется вот эта ошибка: Expression : false Function : CGameGraph::distance File : e:\stalker\sources\trunk\xr_3da\xrgame\game_graph_inline.h Line : 96 Description : There is no proper graph point neighbour! Она может проявляться при вызове метода :task() из смарта. Этот метод вызывается, если вызвать метод :update() (если он приводит к вызову одноимённого метода движкового класса) из серверного объекта какого-нибудь NPC, который уже прописан в смарте и может передвигаться в оффлайне (метод move_offline() возвращает true). По каким-то причинам баг проявляется именно тогда, когда NPC в онлайне. И это не связано с недоступностью нужной вершины - проверка на доступность и использование индекса доступной точки пути (второй аргумент в конструкторе CAlifeSmartTerrainTask) ни к чему не приводит. Чаще всего проявляется после смены работ, то есть когда путь, переданный в конструктор CAlifeSmartTerrainTask отличается от пути, использованного для создания CAlifeSmartTerrainTask в прошлый раз. Возможно, нежелание разрабов использовать функцию xr_effects.smart_terrain_force_update без крайних случаев связано именно с этим. Ссылка на комментарий
Viнt@rь 50 Опубликовано 19 Сентября 2012 Поделиться Опубликовано 19 Сентября 2012 (изменено) function r_line(ini_file*, string, number, string&, string&); --возвращает полностью "линию" т.е. key1 = value1 Возвращает ли эта функция несколько значений которым равен ключ? тоесть key1 = value1, value2, value3... и еще: параметры number, string&, string& для чего здесь? глядя на пример использования: result, idx, value = ltx:r_line(section,i,"",s) возникает вопрос, что функция возвращает в result и почему передаваемые параметры по типам не совпадают с тем что написано в описании функции, что выше в цитате Изменено 19 Сентября 2012 пользователем Viнt@rь GUI для конвертера от бардака(всего и вся в форматы сдк) Полезный утиль-"Utilits pack(mod)" Ссылка на комментарий
Desertir 202 Опубликовано 19 Сентября 2012 Поделиться Опубликовано 19 Сентября 2012 Есть такое волшебное средство как поиск... С этого поста и далее по тексту есть пояснения не только твоего вопроса 1 ТЧ 1.0004. SAP и Trans mod github Ссылка на комментарий
Полтергейст 37 Опубликовано 28 Октября 2012 Поделиться Опубликовано 28 Октября 2012 Классы и функции, используемые для оффлайн передвижения Классы: CALifeMonsterBrain CALifeHumanBrain CALifeMonsterMovementManager CALifeMonsterPatrolPathManager CALifeMonsterDetailPathManager CALifeSmartTerrainTask Как это всё работает: Каждому NPC соответствует свой объект класса CALifeMonsterBrain (или наследуемого от него CALifeHumanBrain), который можно получить так: local brain = se_obj:brain() где se_obj - серверный объект NPC. Вызов brain:can_choose_alife_tasks(true/false) включает или отключает автоматический выбор точки, в которую NPC пойдёт (подробности ниже). Далее, можно получить объект класса CALifeMonsterMovementManager, соответствующий данному серверному объекту: local movement_mgr = brain:movement() По непонятным причинам вызов movement_mgr:start_type() приводит к зависанию. Сам по себе этот объект не позволяет управлять передвижением NPC, зато с помощью него можно получить объекты классов CALifeMonsterPatrolPathManager и CALifeMonsterDetailPathManager: local patrol_mgr = movement_mgr:patrol() local detail_mgr = movement_mgr:detail() Класс CALifeMonsterPatrolPathManager позволяет задать имя пути методом path(path_name), а также точку, с которой начинать движение по пути, вызовом start_vertex_index(vertex_index). Однако это ни на что не влияет, т.к. тип пути и другие параметры невозможно установить из-за того, что нижеперечисленные методы (не важно, с аргументами или без) просто зависают при вызове и вызывают вылеты "Error in error handling" или "C stack overflow": start_type() route_type() target_game_vertex_id() target_position() target_level_vertex_id() Реально же управлять оффлайн-передвижением позволяют классы CALifeMonsterDetailPathManager и CALifeSmartTerrainTask. Чтобы послать NPC в заданную точку пути, необходимо сначала создать объект класса CALifeSmartTerrainTask: local my_task = CALifeSmartTerrainTask(path_name, point_index) где path_name - имя пути, point_index - индекс точки пути. В ТЧ объект этого класса не может быть создан для произвольной точки, не привязанной к какому-либо пути. Чтобы узнать положение уже созданного объекта CALifeSmartTerrainTask в пространстве, можно использовать значения, возвращаемые при вызове level_vertex_id(), position() и game_vertex_id(). Далее нужно установить эту точку для NPC в качестве целевой. Это делается методом target() класса CALifeMonsterDetailPathManager: detail_mgr:target(my_task) В lua_help описаны ещё 2 варианта использования этого метода: function target(const number&, const number&, const vector&);function target(const number&); Если в первом требуется передать game_vertex_id, level_vertex_id и координаты точки, то непонятно, в каком порядке передавать game_vertex_id и level_vertex_id. В каком бы порядке я их не передавал, игра вываливалась c ошибкой "There is no proper graph point neighbour". Во втором непонятно, что за число туда передавать. Возможно это game_vertex_id, я не проверял. Далее, метод completed() возвращает true, если NPC дошёл до точки, иначе false. А вот с методом failed() не совсем понятно. Нередко он возвращает true тогда, когда completed() тоже возвращает true. Метод actual() возвращает true, если completed() и failed() возвращают false. Метод speed () недоступен из-за опечатки, т.к. в конце его имени стоит символ табуляции. Судя по названию, должен возвращать скорость передвижения. Теперь подробности о том, как всё это работает. Оффлайн-передвижение в оригинальной игре завязано на smart_terrain (или просто "смарты") - объектах класса se_smart_terrain, наследуемого от cse_alife_smart_zone. У каждого серверного объекта NPC (людей или зверюшек) есть свойство m_smart_terrain_id - id смарта, в котором прописан NPC. У каждого смарта есть метод task(), который возвращает объект класса CALifeSmartTerrainTask. Обновление оффлайн-передвижения происходит при вызове метода se_obj:brain():update(), где se_obj - серверный объект NPC. Также он вызывается движком при вызове se_obj:update(). Стоит отметить, что se_obj:update() периодически вызывается движком для всех NPC, а вот вызывать его искусственно (например, из биндера клиентского объекта) не стоит, будут ошибки "There is no proper graph point neighbour". Алгоритм его работы примерно таков: 1. Проверить, включен ли автовыбор смартов. (см. can_choose_alife_tasks()) 1.1. Если включен - перебрать все смарты, вызывать из них :enabled(). Если вернёт true, то вызвать suitable() и запомнить возвращённое значение. Из всех значений, возвращённых :suitable(), выбрать наибольшее. Прописать NPC в смарте, который соответствует этому значению, присвоением его id в m_smart_terrain_id и последующим вызовом register_npc(). Перейти к п.2. 1.2. Если до вызова обновления оффлайн-передвижения для этого объекта была установлена целевая точка (см. описание CALifeMonsterDetailPathManager.target()), идти в неё. Иначе перейти к п.2. 2. Если объект прописан в каком-то смарте (m_smart_terrain_id < 65535), вызвать из этого смарта :task(), установить возвращенное значение в качестве целевой точки (опять же, метод target()) и идти в неё. Иначе ничего не делать. Вот вроде бы и всё. Непонятно, зачем разрабы оставили этот алгоритм в самом движке, когда проще было бы делать всё вышеописанное скриптами. В ТЧ в оригинальной игре автовыбор смартов всегда включен, поэтому если вам нужно послать NPC в какую-то заданную точку (например, перевести через уровень), то автовыбор надо отключать, чтобы не возиться с созданием временных smart_terrain. 2 1 Ссылка на комментарий
alpet 77 Опубликовано 10 Января 2013 Поделиться Опубликовано 10 Января 2013 (изменено) Товарищи исследователи, может кто подскажет решение задачи. Есть точка (vector) и направление (vector) от неё, для моего случая это актор целящийся куда-либо из оружия. Хочется получить точку прицела на геометрии уровня/объекта, или указания что ничего нет по курсу. Сойдут любые изощреные методы, включая вызов методов интефейсов Direct3D, работа с буфером вертексов или рендером движка. В теории должен быть самый легальный метод для частного случая актора, ведь движок имеет возможность показывать дистанцию до цели. Но вдруг есть и универсальный метод? Типичную дистанцию отсечки, и косвенно через неё точку выцеливания можно посчитать так (псевдо-код): g_hud := GetProcAddr ('xr_3da.exe', '?g_hud@@3PAVCCustomHUD@@A'); table := g_hud [$0034]; // + 52 byte target_id := table[$0000]; // DWORD if game object target_dist := table[$0004]; // float32 // lua code: point = device().cam_pos dir = device().cam_dir dir:mul(target_dist) point:add(dir) Изменено 10 Января 2013 пользователем alpet Плавайте поездами аэрофлота! Ссылка на комментарий
KD87 718 Опубликовано 10 Января 2013 Поделиться Опубликовано 10 Января 2013 (изменено) В таблице экспорта xr_3da.exe есть метод RayPick. Вот сигнатура: int CObjectSpace::RayPick(CObjectSpace *this, _vector3<float> *start, _vector3<float> *dir, float range, collide::rq_target tgt, collide::rq_result *R, CObject *ignore_object) Назначение аргументов, в целом, очевидно: start - стартовая точка, dir - направление трассировки (нормализованный вектор), range - диапазон трассировки. ignore_object - игнорируемый при трассировке объект, tgt - флаги геометрии: enum collide::rq_target { rqtNone = 0x0, - не определять ничего rqtObject = 0x1, - определять динамические объекты rqtStatic = 0x2, - определять статическую геометрию rqtShape = 0x4, rqtObstacle = 0x8, rqtBoth = 0x3, rqtDyn = 0xD, }; Результат доступен через R, там будет лежать объект типа collide::rq_result: struct collide::rq_result { CObject *O; float range; int element; }; В случае статической геометрии объект O будет пустой. Если нужно получить больше информации по статической геометрии, можно поковырять методы CDB::COLLIDER. Если стоит задача работать с трассировкой луча из скриптов, можно посмотреть xray-extensions. В одной из последних ревизий malandrinus экспортировал RayPick в скрипты. Изменено 10 Января 2013 пользователем KD87 2 Ссылка на комментарий
alpet 77 Опубликовано 10 Января 2013 Поделиться Опубликовано 10 Января 2013 Спасибо! То что нужно. Кстати может разработчики движка ещё предусмотрели, как быстро определить статус точки indoor / outdoor (в смысле в здании или на улице)? Плавайте поездами аэрофлота! Ссылка на комментарий
Malandrinus 615 Опубликовано 10 Января 2013 Автор Поделиться Опубликовано 10 Января 2013 alpet, В игре это делается путём постоянного рейтрессинга. Если в направлении дождя видишь небо (т.е. ничего не видишь), значит на улице. Это легко проверить. Во время дождя, если в помещении пройти под дырочкой в крыше, то сразу зашумит, как будто стоишь на улице =) Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
alpet 77 Опубликовано 11 Января 2013 Поделиться Опубликовано 11 Января 2013 malandrinus, а как узнать направление дождя? Разве оно не всегда 0, -1, 0? Плавайте поездами аэрофлота! Ссылка на комментарий
Рекомендуемые сообщения
Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий
Комментарии могут оставлять только зарегистрированные пользователи
Создать аккаунт
Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!
Зарегистрировать новый аккаунтВойти
Есть аккаунт? Войти.
Войти