naxac 2 447 Опубликовано 29 Января 2020 Поделиться Опубликовано 29 Января 2020 @Norman Eisenherz, в treasure_manager активные тайники (выданные, но не обысканные) сохраняются, так что можно получить список нужных story_id оттуда. Кстати, почему помеченные тайники пустуют? При опустошении нычки, по идее, и метка сразу снимается же.. 1 Аддон для ОП-2.09.2: Яндекс/Google/GitHub Ссылка на комментарий
Norman Eisenherz 316 Опубликовано 30 Января 2020 Поделиться Опубликовано 30 Января 2020 (изменено) 1. Старый баг ТЧ: если перезагрузиться на текущей локации, некоторые известные, но еще не облутанные тайники станут пустыми при сохранении видимости меток. 2. Тут можно поспорить: перебор все равно начинается с полного списка тайников, только по имени вместо sid, и активных тайников в целом больше, чем меток на текущей локации. Однако, при ссылке на готовую таблицу и ключ "active" код действительно получается проще: Spoiler [stash_fix.script] function search() local t = treasure_manager.get_treasure_manager() local empty = {} for id, v in pairs(t.treasure_info) do if v.active == true then local sid = v.target local stash = level_object_by_sid(sid) if stash and stash:is_inv_box_empty() then empty[id] = sid -- news_manager.send_tip(db.actor, id .. ": " .. sid) end end end return empty end 3. Остается вопрос с отложенным разовым запуском. Допустим, я применяю вот такую конструкцию с двумя флагами: Spoiler [bind_stalker.script] local stash_upd = false local t_start = 0 local timeout = true … function actor_binder:net_spawn() t_start = time_global() stash_upd = true end … function actor_binder:update(delta) if timeout then if time_global() > t_start + 5000 then timeout = false end end if stash_upd and not timeout then stash_fix.search() stash_upd = false end end Как эту конструкцию упростить? Изменено 30 Января 2020 пользователем Norman Eisenherz Мини-моды: ТЧ ЧН ЗП Шпаргалка Ссылка на комментарий
naxac 2 447 Опубликовано 31 Января 2020 Поделиться Опубликовано 31 Января 2020 @Norman Eisenherz, если ты имеешь целью исправить этот баг, то можно сделать лучше: при спавне лута в тайник надо проверить, не находится ли он в онлайне, и если так, то спавнить вещи в актёра, а на выходе их в онлайн, перемещать в тайник (можно использовать level.client_spawn_manager()). Либо после спавна предметов в тайник поместить его ненадолго в оффлайн. Тогда все предметы останутся в ящике. 1 Аддон для ОП-2.09.2: Яндекс/Google/GitHub Ссылка на комментарий
Norman Eisenherz 316 Опубликовано 3 Февраля 2020 Поделиться Опубликовано 3 Февраля 2020 (изменено) @naxac Задействовал второй подход: добавил в конец функции give_treasure указанную ниже конструкцию с временной проверкой, и на тестовом тайнике результат стабильно положительный – предметы при многократной перезагрузке не теряются. Первый раз применил level.add_call; насколько оптимальна такая конструкция, и нет ли излишней нагрузки на actor_binder:update? Иконка дискеты вроде отображается с обычной частотой… Spoiler function switch(obj) if obj.online then local stash = obj.id alife():set_switch_online(stash, false) alife():set_switch_offline(stash, true) local t_start = time_global() local function delay() if time_global() > t_start + 2000 then return true end return false end local function back_online() alife():set_switch_offline(stash, false) alife():set_switch_online(stash, true) level.remove_call(delay, back_online) end level.add_call(delay, back_online) end end Изменено 3 Февраля 2020 пользователем Norman Eisenherz Мини-моды: ТЧ ЧН ЗП Шпаргалка Ссылка на комментарий
naxac 2 447 Опубликовано 4 Февраля 2020 Поделиться Опубликовано 4 Февраля 2020 @Norman Eisenherz, нормальная конструкция. Единственное, можно заменить таймаут на проверку наличия клиентского объекта, например local function delay() return level.object_by_id(stash) == nil end Две секунды все-таки много, там не более 500мс требуется. Аддон для ОП-2.09.2: Яндекс/Google/GitHub Ссылка на комментарий
Norman Eisenherz 316 Опубликовано 4 Февраля 2020 Поделиться Опубликовано 4 Февраля 2020 @naxac Да, так даже проще. Со временем только странность: при прямой проверке переключение тайника проходит почти незаметно, а с таймером на 1 секунду тайник только уходит в оффлайн и не возвращается. Вроде и ПК не такой старый, и индикатор подгрузки не отображается постоянно. Мини-моды: ТЧ ЧН ЗП Шпаргалка Ссылка на комментарий
Norman Eisenherz 316 Опубликовано 5 Февраля 2020 Поделиться Опубликовано 5 Февраля 2020 Здрасьте. Нашел вот такой фрагмент обсуждения функции level.add_call: On 4/12/2018 at 10:59 AM, Winsor said: level.add_call добавляет на каждом апдейте вызов Ваших функций … проверка my_func будет вызываться до тех пор, пока не будет сделан level.remove-call( my_func, another_func ) - ну и само собой - если my_func вернула true на каждом вызове - то и постоянно будет вызываться another_func. Делаю вот такую проверку: Spoiler [bind_stalker.script] function actor_binder:info_callback(npc, info_id) … if info_id == "esc_bridge_pass_on" then local function check() return db.actor end local function msg() news_manager.send_tip(db.actor, "test") end level.add_call(check, msg) end end Условие "актер прогружен" выполняется на каждом цикле, а тестовое сообщение приходит только один раз. Почему? Мини-моды: ТЧ ЧН ЗП Шпаргалка Ссылка на комментарий
Kirgudu 1 217 Опубликовано 5 Февраля 2020 Поделиться Опубликовано 5 Февраля 2020 @Norman Eisenherz, начать можно с того, что первая функция (в твоём случае check) должна вернуть true, чтобы была запущена вторая функция. Ты же возвращаешь db.actor... Поведение коллбэка предсказать в таких условиях не возьмусь; хорошо ещё, что вылета не было. Ну и второе: я привык считать (может, конечно, и ошибаюсь), что когда первая функция вернула true, запускается вторая функция, а коллбэк отключается. Если это действительно так, однократность сообщения полностью укладывается в такое поведение, ведь инфопорция также выдаётся один раз, и повторно назначить коллбэк некому. 1 Инструмент Ссылка на комментарий
Norman Eisenherz 316 Опубликовано 5 Февраля 2020 Поделиться Опубликовано 5 Февраля 2020 1. Я предполагал, что это будет db.actor ~= nil, но не учел того, что это не логическая переменная. 2. Если колбэк отключается сам собой, для чего тогда нужна функция level.remove_call? Мини-моды: ТЧ ЧН ЗП Шпаргалка Ссылка на комментарий
Winsor 177 Опубликовано 5 Февраля 2020 Поделиться Опубликовано 5 Февраля 2020 3 часа назад, Kirgudu сказал: Ты же возвращаешь db.actor... Поведение коллбэка предсказать в таких условиях не возьмусь; хорошо ещё, что вылета не было. 1) Вылета не должно быть, так как в движке происходит return (*m_lua_function)(); а при вызове info_callback объект актора уже создан, т.е. функция возвращает указатель на существующий объект, движок преобразует это значение в true, так как оно не nullptr. т.е. в результате результат функции всегда true. 2) функция проверяется всегда, до тех пор пока не вызван remove_call. если у тебя не вызывается каждый раз msg() - скорее всего проблема в логике. возможно ты делаешь где то remove_call, либо у тебя повис скриптовый биндер, что очень плохо. Единственное что меня немного смущает - то что ты для глобального колбека используешь local function={} - вот это уже может приводить к "непонятному" поведению. 1 Ссылка на комментарий
Norman Eisenherz 316 Опубликовано 5 Февраля 2020 Поделиться Опубликовано 5 Февраля 2020 @Winsor Я пробовал выносить исполняемую часть отдельно (в пределах того же скрипта) – все равно тестовое сообщение приходит один раз. Чуть выше разбирали проверку оффлайн-статуса тайника – если прикрутить к этой функции тестовое сообщение, оно придет только на первом цикле, на котором проверка выдаст true, хотя без level.remove_call должно приходить подряд на каждом последующем цикле (или все же нет?) Мини-моды: ТЧ ЧН ЗП Шпаргалка Ссылка на комментарий
Kirgudu 1 217 Опубликовано 5 Февраля 2020 Поделиться Опубликовано 5 Февраля 2020 7 часов назад, Winsor сказал: функция проверяется всегда, до тех пор пока не вызван remove_call И всё-таки позволю себе усомниться в данном утверждении. Проведём маленький эксперимент: Скрытый текст local test_counter = 0 local callback_counter = 0 function f1() if test_counter < 10 then test_counter = test_counter + 1 end log("test_counter: %s", test_counter) return test_counter >= 10 end function f2() if callback_counter < 10 then callback_counter = callback_counter + 1 log("callback_counter: %s", callback_counter) end end Код вставил в actor_proxy.script в событие net_spawn Лог: ! Cannot find saved game :>test_counter: 1 ! Cannot find saved game :>test_counter: 2 ! Cannot find saved game :>test_counter: 3 ! Cannot find saved game :>test_counter: 4 ! Cannot find saved game :>test_counter: 5 ! Cannot find saved game :>test_counter: 6 ! Cannot find saved game :>test_counter: 7 ! Cannot find saved game :>test_counter: 8 ! Cannot find saved game :>test_counter: 9 ! Cannot find saved game :>test_counter: 10 ! Cannot find saved game :>callback_counter: 1 * MEMORY USAGE: 428273 K * End of synchronization A[1] R[1] @ flush На этом вывод в лог полностью прекратился. Если бы мы имели бесконечную проверку результата первой функции, test_counter выводился бы в лог также бесконечно, а callback_counter - 10 раз. Ремарка: эксперимент проведён в ЧН, так как у меня нет сейчас установленных Теней. Может быть, конечно, разрабы что-то доработали во второй части, не исключаю. В противном случае либо метод remove_call не является обязательным для прерывания действия коллбэка, либо неявно вызывается где-то в движке после запуска второй функции. 2 Инструмент Ссылка на комментарий
Winsor 177 Опубликовано 6 Февраля 2020 Поделиться Опубликовано 6 Февраля 2020 Хм... Приношу свои извинения за введение в заблуждение. "Почитал" более внимательно движок ТЧ - все что я писал ранее не относится к вызовам, которые добавляются из скриптов, а именно CPHScriptAction и CPHScriptCondition. Для других condition и action которые добавляются в движке - алгоритм работает немного иначе... Действительно, CPHScriptCondition (условие проверки) выполняется всегда и постоянно, при возврате true из данной функции выполняется запуск CPHScriptAction. если CPHScriptCondition или CPHScriptAction перешел в состояние obsolete ("устаревший"), менеджер вызовов удаляет этот call. Тонкость в том что именно скриптовый CPHScriptAction сразу после первого вызова переводит свое состояние в obsolete void CPHScriptAction::run() { (*m_lua_function)(); b_obsolete=true; } что и приводит в удалению всего вызова после единственного срабатывания. Еще раз - прошу прощения за неточность... 2 1 Ссылка на комментарий
Kirgudu 1 217 Опубликовано 6 Февраля 2020 Поделиться Опубликовано 6 Февраля 2020 (изменено) @Winsor, всё так. До сих пор ни разу не пытался копаться в движке, однако в данном случае самому стало интересно. Скачал и развернул у себя проект версии 1.0006, немного поискал. Скрытый текст void CPHCommander::update() { for(u32 i=0; i<m_calls.size(); i++) { try { m_calls[i]->check(); } catch(...) { remove_call(m_calls.begin()+i); i--; continue; } if(m_calls[i]->obsolete()) { remove_call(m_calls.begin()+i); i--; continue; } } } void CPHCall::check() { if(m_condition->is_true())m_action->run(); } void CPHScriptAction::run() { (*m_lua_function)(); b_obsolete=true; } Собственно, тут всё видно. Ну а для скриптёра резюме будет простым: можно сколько угодно использовать level.add_call(f1,f2) в скриптах без необходимости принудительно удалять коллбэки, лишь бы функция f1 рано или поздно вернула true. Изменено 6 Февраля 2020 пользователем Kirgudu 1 2 Инструмент Ссылка на комментарий
Norman Eisenherz 316 Опубликовано 6 Февраля 2020 Поделиться Опубликовано 6 Февраля 2020 Здрасьте. Нужно отследить количество денег у NPC после каждой сделки, пока открыто окно торговли. Знаю про колбэк "on_trade"; к чему можно привязаться, чтобы получить id торговца? Мини-моды: ТЧ ЧН ЗП Шпаргалка Ссылка на комментарий
Kirgudu 1 217 Опубликовано 6 Февраля 2020 Поделиться Опубликовано 6 Февраля 2020 (изменено) @Norman Eisenherz, коллбэк on_trade(item, sell_mode, cost) вызывается для каждого предмета перед тем, как он передан новому владельцу. При этом сначала пачкой обрабатываются все предметы, которые актор продаёт (при наличии), а затем все предметы, которые актор покупает. Направление передачи предмета отображается при срабатывании коллбэка в параметре sell_mode так: sell_mode = true - предмет у актора, parent_id предмета равен id актора, после коллбэка передаётся НПС. sell_mode = false - предмет у НПС, parent_id предмета равен id НПС, после коллбэка передаётся актору. Таким образом, если есть хотя бы один покупаемый у НПС предмет, id НПС определить достаточно просто: берём item, когда sell_mode = false, его item:parent() будет равен id НПС. Но что делать, если покупаемых предметов в этот раз нет, а есть только продаваемые? На момент продажи их владелец - актор, и он нам не подходит. Вот тут как раз может прийти на помощь обсуждавшийся выше метод level.add_callback. А именно: запоминаем в какой-нибудь переменной сам предмет (item), устанавливаем признак того, что надо проверить предмет попозже, запускаем коллбэк, при срабатывании которого во время следующего апдейта опять же возьмём id родителя, которым будет уже не актор, а НПС. Примерно так: Скрытый текст local sell_item = nil local make_checking = false function on_trade(item, sell_mode, cost) if sell_mode == true then --/ продажа sell_item = item end if make_checking == false then --/ инициируем запуск проверки при первом апдейте после торговли make_checking = true level.add_call(function() return make_checking end, check_trade_items) end end function check_trade_items() --/ получаем id НПС local npc_id = sell_item:parent() --/ обнуляем проверочные переменные sell_item = nil make_checking = false end Разумеется, это лишь набросок, который надо доводить до ума. Как соединить всё это в единое, универсальное и для покупки, и для продажи, и для того и другого одновременно - это поле для самостоятельных исследований. Ну и не исключаю, конечно, что есть гораздо более простой вариант решения. Изменено 6 Февраля 2020 пользователем Kirgudu Инструмент Ссылка на комментарий
Zander_driver 10 334 Опубликовано 6 Февраля 2020 Поделиться Опубликовано 6 Февраля 2020 только что, Kirgudu сказал: Ну и не исключаю, конечно, что есть гораздо более простой вариант решения. Прежде чем с НПС торговать, или заниматься с ним диалогами, к нему надо подойти и жмакнуть F. И в этот момент срабатывает USE колбек, в который передается собственно проюзанный непись. 1 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. Ссылка на комментарий
Kirgudu 1 217 Опубликовано 6 Февраля 2020 Поделиться Опубликовано 6 Февраля 2020 (изменено) Во! Я ж чувствовал, что есть что-то значительно проще, только не получилось вспомнить. Ну и ладно, зато поупражнялся. Изменено 6 Февраля 2020 пользователем Kirgudu Инструмент Ссылка на комментарий
Norman Eisenherz 316 Опубликовано 6 Февраля 2020 Поделиться Опубликовано 6 Февраля 2020 @Zander_driver Добавил в function actor_binder:use_object(obj) тестовое сообщение – никакой реакции на начало разговора. Может, в ТЧ такой проверки нет? Сам колбэк точно рабочий – на "съедание" любых расходных предметов срабатывает. Код: if IsStalker(obj) then news_manager.send_tip(db.actor, "test") end Мини-моды: ТЧ ЧН ЗП Шпаргалка Ссылка на комментарий
Okichi 922 Опубликовано 6 Февраля 2020 Поделиться Опубликовано 6 Февраля 2020 @Norman Eisenherz, нужный коллбэк вроде как в xr_motivator.script 2 След от кругов на воде - это тоже след (с) Ссылка на комментарий
Рекомендуемые сообщения
Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий
Комментарии могут оставлять только зарегистрированные пользователи
Создать аккаунт
Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!
Зарегистрировать новый аккаунтВойти
Есть аккаунт? Войти.
Войти