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

Система ALife. Логика поведения игровых объектов


Allender

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

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

 

То, что обычно называют "логикой" - это описание поведения различных игровых объектов (как видимых игроку, так и скрытых от него) в зависимости от каких-либо условий, созданное в некотором якобы человеко-читаемом формате.

Храниться это описание может по большому счету совершенно где угодно, или даже содаваться/изменяться динамически.

 

Пример: файл gulag_dark_walley.script (недавно разбирался):

gulags.val_escort.job = function(sj, gname, type, squad, groups) -- игнорирование
    local ltx = "[meet@ignore_abuse]\n" .. и т.д. - динамическое создание логики персонажей для смарта val_escort

...

gulags.val_escort.ltx = ltx

 

function load_ltx(gname, type)
    local g = gulags[type]
    if g then return g.ltx end -- возвращает сформированный текст.
    return nil -- "магическая" команда
end

 

Это можно переписать например так:

["val_escort"] = {    -- Пуля, Любер, бандиты
    { section = "logic@val_escort_nap1",    -- напарник с которым спасаем пленного
   ... и т.д., где [logic@val_escort_nap1] и прочие определены в файле config\misc\gulag_dark_walley.ltx, который подключен через system.ltx.
 

продолжение следует.

Изменено пользователем Dennis_Chikin
  • Согласен 1

Поделиться этим сообщением


Ссылка на сообщение

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

Введение надо какое-то более другое.

 

Ну, в общем, я так понимаю, что для того и тема, что если у кого-то есть идеи лучше - напишет.

Если нет - придется, видимо, гипертекстом оформлять.

  • Согласен 1

Поделиться этим сообщением


Ссылка на сообщение

function heli_start_flame( heli )

heli:get_helicopter():StartFlame()

db.storage[heli:id()].flame_start_snd:play( heli )

news_heli( heli, "flame" )

end

 

Какая строчка в этой функции непонятна ?

Поделиться этим сообщением


Ссылка на сообщение

Так, поболтали, и забыли...

 

Ну, на счет "мне надо/мне уже не надо" - это как бы на совести автора пусть будет. Просто как ведем разговор - вот так вот и ответы получаем.

Функция - в общем, делает одно и то же: включает "горение" вертолета.

 

Что касается того, как вызвать ее из логики, то таки да, для того, чтобы что-то "делать из логики" - надо понимать, как оно работает.

 

Работает же, в общем и целом, вся логика примерно вот как я начал писать во втором посте темы, но не закончил (и не знаю, описано ли это в гугльдоке из первого):

Это - некий как бы человекочитаемый язык. Описывающий условия, при которых, внезапно, таки да - вызываются какие-то функции. Часть этих функций УЖЕ замаскировано вот этими всеми черточками, плюсиками, и закорючками, а что не замаскировано - надо указывать явно.

 

Отвечает за разбор этих черточек и закорючек файл xr_logic.script, и если кому-то надо знать, какие ВООБЩЕ есть возможности, как ТОЧНО работает та или иная закорючка, и как добавить что-то свое - вам таки придется разбирать вот этот самый скрипт. А он несет немало открытий весьма чудных, да...

Впрочем, основное написано там в самом начале, в комментариях.

 

Из них нам особо интересны function parse_condlist(npc, section, field, src),

function pick_section_from_condlist(actor, npc, condlist) и function try_switch_to_another_section(npc, st, actor).

Ну и, естественно, комментарии к ним.

 

Первая, parse_condlist - разбирает эти ваши черточки и кракозябры в таблицу условий,

Вторая - пытается их проверить. Обычно, вот в каждой схеме (каковой соответствует своя секция в файле логики, и свой собственный скрипт в скриптах), эта функция вызывается с тем или иным намерением, и, соответственно, в список проверок вы можете добавить что-то свое. Или добавить какую-нибудь новую кракозябру и ее обработку вот в эти проверки.

 

То есть, если в логике присутствует кракрозябра вида "=что_то_там" или "!вот_такая_фигня", из файла xr_conditions будет вызвано ваше "что_то_там" или "вот_такая_фигня". Если, конечно, оно есть. Если нет - получите зависание, и, в конце-концов, вылет с любимым всеми сообщением "переполнение стека". Что показательно - не зависимо от количества памяти.

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

 

Таким образом, если Вам надо что-то принципиально новое, вы должны это новое вписать в xr_conditions.script, или поправить xr_logic.pick_section_from_condlist(), чтобы оно вызывало вашу фигню из более другого места.

Да, это вот та самая функция, которая занимается проверкой условий по таблице, созданной parse_condlist(), и тоже вызывается во всех схемах. Ну вот собственно как разобрали - так и начинает проверяться. Если, конечно, вы там уже не поправили все на нечто странное.

Оно же, сразу же, содержит возможность ВЫПОЛНИТЬ ВАШУ ФУНКЦИЮ, если проверяемое условие выполнилось. И таки тоже передает туда актора, объект и параметры. То есть, вам осталось только вписать в строку условий нужное, или создать это нужное, и вписать.

Ага, например, создать my_kewl_script.script, добавить в туда function podzhetch_vertalot( dummy, obj ) bind_heli.heli_start_flame( obj ) end, вписать в вашу логику что-то типа %=my_kewl_script.podzhetch_vertalot%, и наслаждаться результатом.

 

Наконец, try_switch_to_another_section() - это то, что, опять же, в каждой схеме постоянно проверяет: а не пора ли переключиться на какую-то более другую секцию логики.

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

 

Вот как-то так.

 

p.s. флуд почистил.

 

P.P.S. Но вообще, применительно к вертолету, проще поправить bind_heli.script, чтобы он читал из активной секции хит, с которого надо начинать гореть.

И хотя этот параметр будет в файле логики, к собственно рассматриваемому псевдоязыку это не имеет СОВЕРШЕННО НИ КАКОГО отношения.

Так же, как, например, параметры danger или ранения, или даже путей, читаемых для неписей из назначенного им ltx. Кстати, даже и не помню, что я правил в ранениях и danger, чтобы оно это читало, что было в "стандартном ТЧ", а где - было, но с ошибками. Но смысл в любом случае примерно такой. Просто записали что-то в конфиг, который потом кому-то как-то скормили.

Изменено пользователем Dennis_Chikin
  • Нравится 1
  • Полезно 1

Поделиться этим сообщением


Ссылка на сообщение

п2 и далее - не верно.

Все, что принято называть "логикой" - это реализовано исключительно на скриптах. Измените скрипты - изменится и все, что вы делаете в этой "логике".

Это - просто еще один язык поверх другого языка. Который используется в текущем виде просто по принципу "здесь так принято".

 

Очень странный, ограниченный и неудобный язык.

Поделиться этим сообщением


Ссылка на сообщение

В классическом сталкере, в состоянии боя логика не работает вообще. То есть, она работает как раз ровно до боя и после боя.

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

 

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

Но это, опять же, "не логика".

 

Еще есть, например, watcher_act.script, например, знаменитый тем, что если на дальнем конце локации бросить какой-то предмет, то непись, наплевав на бой, аномалии, все остальное, полезет за этим предметом. Опять же, отключается проверка состояния "в бою".

 

Ну а небоевое поведение непися определяется выбранными через скрипт, который взял на себя управление, анимацией, state и командами "идти к вертексу n".

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

Поделиться этим сообщением


Ссылка на сообщение

Обход аномалий зависит от правильно выставленного параметра effective_radius в кофигах аномалий.

Опять же, если кто-то не перехватит проверку ( world_property( stalker_ids.property_enemy, false ) ), и не пошлет непися прямо в аномалию (опять же пресловутый watcher_act.script), например.

 

Для мобов - mob:add_restrictions( "", s ), где s - строка с перечислением имен рестрикторов, в которые лезть нельзя.

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

Поделиться этим сообщением


Ссылка на сообщение

Забавно однако наблюдать, как "слишком умный" скрипт ведет к умножению бреда в конфигах.

 

Как бы, изначально синтаксис выбора секций должен выглядеть как

active = схема@уникальный_модификатор1

[схема@уникальный_модификатор1]

on_чегопопало = ... схема@уникальный_модификатор2

и т.д.

 

Но поскольку для названия схем добавили фильтрацию цифр, в количестве имеем

[walker3@1]

[walker4@1]

и т.д.

 

Просто праздник какой-то...

 

А вот новую схему типа walker1 - фиг добавишь.

 

Да здравствуют священные и неприкосновенные конфиги, аффтыри которых имеют право прописать туда любой бред, и потом кто-то должен угадать, что они имели в виду !

 

Даже если это будет подряд

line1 = вот так а еще мы добавим чего попало как нам в голову взбредет

line1 = "№%:/"\%?;.$@#$%^&*;%Ё!!\0%&^&;%№:?!?9[=-+~$^{

line1 = ;и вот только попробуйте это не прочитать

line1 line1, li ne1 li\0ne1 ~= $_\\\\]%

и т.д.

А также пачка переходов на секции и строки вообще несуществующие.

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

Поделиться этим сообщением


Ссылка на сообщение

Сорри за даблпостинг, но мне таки интересно.

Люди, скажите, как вы добиваетесь, чтоб у вас работали конструкции вида:

[logic]

active = sr_idle

 

[sr_idle]

on_npc_in_zone = 19029 |soldier_v_piyanux_restrictor| sr_idle@time

single = true

Поделиться этим сообщением


Ссылка на сообщение

    elseif cond_name(c.name, "on_npc_in_zone") then
      if utils.npc_in_zone(level.object_by_id(c.npc_id), db.zone_by_name[c.v2]) then
        switched = switch_to_section(npc, st, pick_section_from_condlist(actor, npc, c.condlist))
      end
    elseif cond_name(c.name, "on_npc_not_in_zone") then
      if not utils.npc_in_zone(level.object_by_id(c.npc_id), db.zone_by_name[c.v2]) then
        switched = switch_to_section(npc, st, pick_section_from_condlist(actor, npc, c.condlist))
      end
А, в смысле, function cfg_get_npc_and_zone() ?

Которая берет по sid, и перекладывает туда id ?

 

Орригинально...

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

Поделиться этим сообщением


Ссылка на сообщение

На дворе шел 2016 год.

А мододелы так и вызывают из логики функции типа

 

my_function1()

local obj = alife():object( "my_object1" )

local actor = db.actor

...

end

 

...

my_function100500()

local obj = alife():object( "my_object100500" )

 

особенно замечательно, когда используется не имя, а спавн из скрипта с переписыванием sid, или, еще лучше, сохранение id созданного объекта в pstor к актору.

 

 

Так вот, кому лень смотреть xr_logic.scritp, просто запомните:

вызываемая в 99% скриптов, работающих с логикой xr_logic.try_switch_to_another_section( self.object, self.st, db.actor ) - передает туда, как видно из аргументов, актора, сторейж и свой объект.

Далее вызывается pick_section_from_condlist( actor, npc, c.condlist ) или что-то подобное, где actor и npc - внезапно, именно то, что было передано в качестве актора и объекта.

И когда ваше условие выполняется, то в вашу =my_script.my_function100500() передаются они же.

 

Так что достаточно написать my_function( v1, v2 ), и в v2 у вас волшебным образом будет искомый объект. Игровой или серверный - зависит от того, откуда вызывалось, но в любом случае достаточно local obj = alife():object( v2:name() )

Если, конечно, у вас в олспавне еще и имена не дублированы, ну а если уж понаплодили дубликатов - вылетов будет в количестве и без этого.

 

Из засад же присутствует то, что когда вы вызываете pick_section_from_condlist() откуда-нибудь из se_zone.script и прочих se_что-попало, то ни какого db.actor во время загрузке этих ваших зон и прочих смартерейнов еще не существует. Так что ни выдавать атору инфо в этой "логике", ни рассчитывать, что кто-то где-то его получит - право, не стоит. Подумайте - не перенести ли такие действия в более другое место.

 

 

Да, а вообще, в xr_logic заглядывать время от времени полезно. Ну и в смысле читаемости, возможно, кому-то поможет вот такой вариант: https://dl.dropboxusercontent.com/u/27871782/xr_logic.script

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

Поделиться этим сообщением


Ссылка на сообщение

Если очень ты не хочешь

слушать вредные советы,

то тогда тебе не надо

50 сравнений строк

делать в каждом из апдейтов...

 

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

if self.st.active_section == "sr_idle@nil" then ...

elseif self.st.active_section ~= nil then ...

 

Ага, это те самые рестрикторы, апдейт которых вызывается из каждого апдейта актора. А их в среднем на локации где-то вот с полсотни и есть.

Так вот, в каждой схеме, в свою очередь, имеется апдейт. Ну или эвалуатор. Или еще какое чудо. В котором, обычно, вызывается xr_logic.try_switch_to_another_section()

Так вот, если мы переключаемся на любую другую секцию, или даже схему (все равно секция будет другая), то оно вернет true.

И тогда спокойно можно проверить, на какую именно, и сделать, что хотим. Так же, как и в предыдущем примере, но не на каждом апдейте, а именно на переключении. Только сторейж надо брать не схемы, а объекта.

  • Полезно 1

Поделиться этим сообщением


Ссылка на сообщение

В продолжение о чудесах народного модотворчесва, а также в некоторых местах и собственно оригинала:

 

код типа local ini = xr_logic.get_customdata_or_inifile( блабла )

if ini:section_exist( "logic" ) then что-то делаем

else делаем что-то другое

end

- совершенно бессмысленный, поскольку если в олспавне не прописана секция logic - она будет подставлена из config\scripts\dummy.ltx

И это идет с оригинала.

 

Поскольку там поле active = nil - все дальнейшие сравнения в апдейте с какими либо иными строками - столь же бессмысленны.

 

Проверяйте нужную вам секцию после xr_logic.initialize_obj(), а далее - как описано в посте выше - из схем при получении true из xr_logic.try_switch_to_another_section()

  • Полезно 1

Поделиться этим сообщением


Ссылка на сообщение

Вообще, что касается функций, проверяющих какие-либо условия, стоит доработать xr_logic.script по аналогии с выполнением функций xr_effects по-умолчанию либо из явно заданного модуля. Просто перенести 1:1.

 

А вот что касается размножения time_test7, ...8, 9.6.6.666 и т.д. - аргументы прекрасно передаются, так что вполне достаточно одну time_test(), поскольку parse_infop() совершенно все равно, что разбирать.

 

P.S. Перечитал сейчас свой пост от декабря прошлого года. Вот интересно, зачем я сейчас опять пишу все то же, что очевидно из того первого ? Наверное, по тому, что 8 месяцев отвечал разным людям на все те же вопросы, которые там разобраны, и по тому, что наблюдаемый эффект - скорее отрицательный.

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

Поделиться этим сообщением


Ссылка на сообщение

function pick_section_from_condlist( actor, npc, condlist )

...
		if c then	-- ВСЕ условия выполнены; выдаем инфо/вызываем функции, как просили
			for i, v in ipairs( cond.infop_set ) do
				if v.func then	-- вызываем функции из %=func%
					c, f = string_match( v.func, "(.+)[.](.+)" )	-- c больше не нужно
					if f then 			-- c - скрипт
						c = _G[c]
						if c then f = c[f] end
					else f = xr_effects[v.func]	-- f определено в xr_effects
					end
Если нужно вызвать функцию, то вот здесь выбирается, откуда ее вызывать.

А вот проверка условий:

			elseif v.func then	-- вызывается функция из xr_conditions.script
				f = xr_conditions[v.func] or abort( что-то там )
- то есть, просто здесь надо доработать аналогично тому, что выше.

 

Передача параметров в вызываемую функцию - типичный пример из оригинала, gulag_escape.ltx:

on_info = {=gulag_population_le(esc_lager:6)} walker@esc_lager_defend_new1

Соответственно, функция с проверкой из оригинала же:

-- true, если  в указанном гулаге народу меньше чем надо.
function gulag_population_le(actor, npc, p)
	return ( not p[1] or not p[2] ) or ( xr_gulag.getGulagPopulation( p[1] ) <= p[2] )
end
Проблема с онлайном/офлайном здесь в том, что используется xr_gulag.getGulagPopulation, которая только для онлайна.

Для того, чтобы работало и в офлайновом гулаге тоже, надо что-то типа

function get_population( name )
	local strn = name and alife():object( name )
	local g = strn and strn.gulag
	return ( g and g.population_comed ) or 0
end
- но это уже отношения к логике не имеет. Изменено пользователем Dennis_Chikin

Поделиться этим сообщением


Ссылка на сообщение

override и condlist - это просто такие строчки.

 

override - указание на отдельную секцию. Там вбито несколько строчек для чтения:

 

function cfg_get_overrides( ini, sect )
	if not ( sect and ini:section_exist( sect ) ) then return {} end

	local t = {}
	if ini:line_exist( sect, "heli_hunter" ) then
		t.heli_hunter = parse_condlist1( ini:r_string( sect, "heli_hunter" ) )
	end

	if ini:line_exist( sect, "combat_ignore_cond" ) then
		-- как бычно, здесь и далее закладываемся на то, что в священных и неприкосновенных
		-- конфигах может быть любой бред, по-этому чистим прочитанное от вертикальных палок
		local s = string_match( ini:r_string( sect, "combat_ignore_cond" ), "([^|]+)" )
		if s then
			t.combat_ignore = { ["name"] = "combat_ignore_cond", ["condlist"] = parse_condlist1( s ) }
	end	end

	if ini:line_exist( sect, "combat_ignore_keep_when_attacked" ) then
		t.combat_ignore_keep_when_attacked = ini:r_bool( sect, "combat_ignore_keep_when_attacked" )
	end

	if ini:line_exist( sect, "combat_type" ) then
		local s = string_match( ini:r_string( sect, "combat_type" ), "([^|]+)" )
		if s then
			t.combat_type = { ["name"] = "combat_type", ["condlist"] = parse_condlist1( s ) }
	end	end

	if ini:line_exist( sect, "on_combat" ) then
		local s = string_match( ini:r_string( sect, "on_combat" ), "([^|]+)" )
		if s then
			t.on_combat = { ["name"] = "on_combat", ["condlist"] = parse_condlist1( s ) }
	end	end

	if ini:line_exist( sect, "companion_enabled" ) then
		t.companion_enabled = ini:r_bool( sect, "companion_enabled" )
	end

	if string_match( sect, "kamp" ) then
		if ini:line_exist( sect, "center_point" ) then
			local s = ini:r_string( sect, "center_point" )
			if s then t.soundgroup = s end
		end
	elseif ini:line_exist( sect, "soundgroup" ) then
		local s = ini:r_string( sect, "soundgroup" )
		if s then t.soundgroup = s end
	end

	return t
end

- как говорится, делайте с ними, что хотите.

 

condlist - это вообще просто такая строка, в которой может быть что угодно. Вообще все, что угодно.

Еще так называется часть любой строки, в которой есть набор значков типа %{}+-=~!

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

Поделиться этим сообщением


Ссылка на сообщение

И вот кстати что делать с этой страницей с "категориями", я сколько ни пытался, так и не понял. Вообще ни о чем.

Поделиться этим сообщением


Ссылка на сообщение

state_mgr.set_state() же.

Нужные стэйты смотреть в state_lib.script, при необходимости - добавить.

Поделиться этим сообщением


Ссылка на сообщение

"Не помогает" - это вообще не работает, или бежит не так, как надо ?

Поделиться этим сообщением


Ссылка на сообщение

Урок называется xr_camper.script

 

Смотрим action_patrol:execute(), и выбираем: что именно надо.

Поделиться этим сообщением


Ссылка на сообщение
  • Недавно просматривали   0 пользователей

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