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

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

только что, Norman Eisenherz сказал:

включая те, что давно облутаны и уже не имеют видимых меток.

Наличие лута и метки тоже можно проверять.

только что, Norman Eisenherz сказал:

как научить скрипт ждать.

В качестве пищи для размышлений:

В метод update биндера (чего угодно, но и ГГ в том числе), передается параметр delta.

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

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

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

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

1. Проблема: прямая проверка через sid (по видимым меткам) выдает пустые тайники, которые уже были облутаны ранее (стали пустыми нормальным путем, а не в результате сбоя загрузки) и не имеют видимых меток, то есть в текущей задаче только мешают.

Ответ: Наличие лута и метки тоже можно проверять.

Какова содержательная этого ответа, если в скрипте я как раз проверяю метку и лут?

 

2. Можно пример с разовым действием на апдейте? С таким, условие запуска которого не проверяется на каждом кадре или каждые несколько секунд.

Мини-моды: ТЧ ЧН ЗП

Шпаргалка

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

@Norman Eisenherz, в treasure_manager активные тайники (выданные, но не обысканные) сохраняются, так что можно получить список нужных story_id оттуда. Кстати, почему помеченные тайники пустуют? При опустошении  нычки, по идее, и метка сразу снимается же..

  • Полезно 1

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

naxac.gif

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

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

 

Как эту конструкцию упростить?

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

Мини-моды: ТЧ ЧН ЗП

Шпаргалка

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

@Norman Eisenherz, если ты имеешь целью исправить этот баг, то можно сделать лучше: при спавне лута в тайник надо проверить, не находится ли он в онлайне, и если так, то спавнить вещи в актёра, а на выходе их в онлайн, перемещать в тайник (можно использовать level.client_spawn_manager()).

Либо после спавна предметов в тайник поместить его ненадолго в оффлайн.

Тогда все предметы останутся в ящике.

  • Полезно 1

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

naxac.gif

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

@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

 

 

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

Мини-моды: ТЧ ЧН ЗП

Шпаргалка

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

@Norman Eisenherz, нормальная конструкция. Единственное, можно заменить таймаут на проверку наличия клиентского объекта, например

 

local function delay()

  return level.object_by_id(stash) == nil

end

 

Две секунды все-таки много, там не более 500мс требуется.

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

naxac.gif

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

@naxac Да, так даже проще. Со временем только странность: при прямой проверке переключение тайника проходит почти незаметно, а с таймером на 1 секунду тайник только уходит в оффлайн и не возвращается. Вроде и ПК не такой старый, и индикатор подгрузки не отображается постоянно.

Мини-моды: ТЧ ЧН ЗП

Шпаргалка

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

Здрасьте.

 

Нашел вот такой фрагмент обсуждения функции 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

 

Условие "актер прогружен" выполняется на каждом цикле, а тестовое сообщение приходит только один раз. Почему?

Мини-моды: ТЧ ЧН ЗП

Шпаргалка

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

@Norman Eisenherz, начать можно с того, что первая функция (в твоём случае check) должна вернуть true, чтобы была запущена вторая функция. Ты же возвращаешь db.actor... Поведение коллбэка предсказать в таких условиях не возьмусь; хорошо ещё, что вылета не было.

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

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

1. Я предполагал, что это будет db.actor ~= nil, но не учел того, что это не логическая переменная.

2. Если колбэк отключается сам собой, для чего тогда нужна функция level.remove_call?

Мини-моды: ТЧ ЧН ЗП

Шпаргалка

Ссылка на комментарий
3 часа назад, Kirgudu сказал:

Ты же возвращаешь db.actor... Поведение коллбэка предсказать в таких условиях не возьмусь; хорошо ещё, что вылета не было.

1) Вылета не должно быть, так как  в движке происходит

return (*m_lua_function)();

а при вызове
info_callback объект актора уже создан, т.е. функция возвращает указатель на существующий объект, движок преобразует это значение в true, так как оно не nullptr. т.е. в результате результат функции всегда true.

2) функция проверяется всегда, до тех пор пока не вызван remove_call. если у тебя не вызывается каждый раз msg() - скорее всего проблема в логике. возможно ты делаешь где то remove_call, либо у тебя повис скриптовый биндер, что очень плохо. Единственное что меня немного смущает - то что ты для глобального колбека используешь local function={} - вот это уже может приводить к "непонятному" поведению.

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

@Winsor Я пробовал выносить исполняемую часть отдельно (в пределах того же скрипта) – все равно тестовое сообщение приходит один раз.

Чуть выше разбирали проверку оффлайн-статуса тайника – если прикрутить к этой функции тестовое сообщение, оно придет только на первом цикле, на котором проверка выдаст true, хотя без level.remove_call должно приходить подряд на каждом последующем цикле (или все же нет?)

Мини-моды: ТЧ ЧН ЗП

Шпаргалка

Ссылка на комментарий
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
Ссылка на комментарий

Хм... Приношу свои извинения за введение в заблуждение. "Почитал" более внимательно движок ТЧ - все что я писал ранее не относится к вызовам, которые добавляются из скриптов, а именно CPHScriptAction и CPHScriptCondition. Для других condition и action которые добавляются в движке - алгоритм работает немного иначе...

Действительно, CPHScriptCondition (условие проверки) выполняется всегда и постоянно, при возврате true из данной функции выполняется запуск CPHScriptAction. если CPHScriptCondition  или CPHScriptAction перешел в состояние obsolete ("устаревший"), менеджер вызовов удаляет этот call. Тонкость в том что именно скриптовый CPHScriptAction сразу после первого вызова переводит свое состояние в obsolete

void CPHScriptAction::run()
{
	(*m_lua_function)();
	b_obsolete=true;
}

что и приводит в удалению всего вызова после единственного срабатывания. Еще раз - прошу прощения за неточность...

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

@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.

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

Здрасьте.

 

Нужно отследить количество денег у NPC после каждой сделки, пока открыто окно торговли. Знаю про колбэк "on_trade"; к чему можно привязаться, чтобы получить id торговца?

Мини-моды: ТЧ ЧН ЗП

Шпаргалка

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

@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

 

Разумеется, это лишь набросок, который надо доводить до ума. Как соединить всё это в единое, универсальное и для покупки, и для продажи, и для того и другого одновременно - это поле для самостоятельных исследований.

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

Изменено пользователем Kirgudu
Ссылка на комментарий
только что, Kirgudu сказал:

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

Прежде чем с НПС торговать, или заниматься с ним диалогами, к нему надо подойти и жмакнуть F.

И в этот момент срабатывает USE колбек, в который передается собственно проюзанный непись.

  • Нравится 1
  • Полезно 2

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

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

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

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

Во! Я ж чувствовал, что есть что-то значительно проще, только не получилось вспомнить.

Ну и ладно, зато поупражнялся. :biggrin:

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

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

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

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

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

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

Войти

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

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

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

AMK-Team.ru

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