SoC. Спавн точек перехода между уровнями — различия между версиями — S.T.A.L.K.E.R. Inside Wiki

SoC. Спавн точек перехода между уровнями — различия между версиями

Материал из S.T.A.L.K.E.R. Inside Wiki

Перейти к: навигация, поиск
(Отключение выброса на ЧАЭС-2)
 
(не показаны 8 промежуточные версии 5 участников)
Строка 8: Строка 8:
 
Стандартной функции alife():create(…) недостаточно для создания полноценного level_changer.
 
Стандартной функции alife():create(…) недостаточно для создания полноценного level_changer.
  
Ниже я привожу код функции, которая создает и инициализирует level_changer (пишется в созданный нами файл '''_freeplay_sa.script'''): НИЧЕГО НЕ ПОДСТАВЛЯЙТЕ!
+
Ниже я привожу код функции, которая создает и инициализирует level_changer (пишется в созданный нами файл '''_freeplay_sa.script'''):  
 
<lua>
 
<lua>
 
function create_level_changer(
 
function create_level_changer(
Строка 121: Строка 121:
 
end
 
end
 
</lua>
 
</lua>
 
+
Ее вы просто скопируете в наш файл. Изменять ничего не надо.
  
 
== Часть 2. '''Создание точек перехода''' ==
 
== Часть 2. '''Создание точек перехода''' ==
Строка 127: Строка 127:
 
Теперь следует написать функции создания нужных точек перехода и подключить их к игре. Сами функции просты (тоже в файл '''_freeplay_sa.script''')
 
Теперь следует написать функции создания нужных точек перехода и подключить их к игре. Сами функции просты (тоже в файл '''_freeplay_sa.script''')
 
<lua>
 
<lua>
function exit_monolit()
 
if (not has_alife_info("freeplay_activated1")) then
 
create_level_changer(11410, vector():set(-13.26, 47.71, 46.57), 200, 2417,
 
162109,
 
                        2384,
 
vector():set( 375.615, 0.224, 27.737 ),
 
vector():set(    0.0, 0.0  ,  0.0 ),
 
"L12_Stancia",
 
1)
 
 
db.actor:give_info_portion("freeplay_activated1")
 
end
 
 
-- создается переход из ЧАЭС в Припять
 
create_chaes2pripyat_exit()
 
 
-- актер перебрасывается в level_changer, возвращающий его на ЧАЭС, ко входу в бункер
 
db.actor:set_actor_position( vector():set(-13.26, 47.71, 46.57) )
 
end
 
 
 
function refuze_o_sozn()
 
function refuze_o_sozn()
 
if (not has_alife_info("freeplay_activated2")) then
 
if (not has_alife_info("freeplay_activated2")) then
Строка 183: Строка 163:
 
</lua>
 
</lua>
  
Функция '''exit_monolit''' создает «тихий» переход на уровень ЧАЭС-1 и обычный - в начале уровня ЧАЭС-1 для возврата в Припять, после чего перебрасывает актера прямо внутрь созданного перехода. Функция '''refuze_o_sozn''' делает тоже самое, только игрок появляется перед воротами в правом верхем углу карты (мне кажется, что так более логично). Все телепорты защищаются уникальными info_portions, дабы избежать их повторного создания, ведь игрок может захотеть «закончить» игру несколько раз.
+
Функция '''refuze_o_sozn''' создает «тихий» переход на уровень ЧАЭС-1 и обычный - в начале уровня ЧАЭС-1 для возврата в Припять, после чего перебрасывает игрока в новый переход. Все переходы защищаются уникальными info_portions, дабы избежать их повторного создания, ведь игрок может захотеть «закончить» игру несколько раз.
  
 
Теперь подключение.
 
Теперь подключение.
Строка 196: Строка 176:
 
<game_information_portions>
 
<game_information_portions>
  
<info_portion id="freeplay_activated1"></info_portion>
 
 
<info_portion id="freeplay_activated2"></info_portion>
 
<info_portion id="freeplay_activated2"></info_portion>
 
<info_portion id="exit_chaes2pripyat_created"></info_portion>
 
<info_portion id="exit_chaes2pripyat_created"></info_portion>
Строка 217: Строка 196:
 
config\ui\ui_movies.xml
 
config\ui\ui_movies.xml
 
</code>
 
</code>
найдите элементы «''mov_desire_''» (их пять, по количеству ложных концовок игры - все варианты общения с Монолитом). Внутри каждого элемента есть дочерние элементы «''function_on_stop''», которые задают функцию, запускающуюся сразу после ролика. Стандартное содержимое:
 
<code>
 
<function_on_stop>xr_effects.game_credits</function_on_stop>
 
</code>
 
Функция '''xr_effects.game_credits''' запускает финальные титры. Именно ее и нужно заменить на вызов нашей функции _freeplay_sa.exit_monolit. Вот что должно получиться:
 
<code>
 
<function_on_stop>_freeplay_sa.exit_monolit</function_on_stop>
 
<!-- original: <function_on_stop>xr_effects.game_credits</function_on_stop>  -->
 
</code>
 
<blockquote>
 
''Старый элемент я советую не удалять, а закомментировать.''
 
</blockquote>
 
  
 
Чуть ниже «''mov_desire_5''» находится тэг ролика для концовки «Присоединение к О-Сознанию». Его мы трогать не будем - Меченый станет медузой. А вот после него - тэг для ролика «Отказ от О-Сознания»: «''mov_refuse_osoznanie''». Функцию завершения в нем заменим следующим образом:
 
Чуть ниже «''mov_desire_5''» находится тэг ролика для концовки «Присоединение к О-Сознанию». Его мы трогать не будем - Меченый станет медузой. А вот после него - тэг для ролика «Отказ от О-Сознания»: «''mov_refuse_osoznanie''». Функцию завершения в нем заменим следующим образом:
 
<code>
 
<code>
 
<function_on_stop>_freeplay_sa.refuze_o_sozn</function_on_stop>
 
<function_on_stop>_freeplay_sa.refuze_o_sozn</function_on_stop>
<!-- original: <function_on_stop>xr_effects.game_credits</function_on_stop>  -->
 
 
</code>
 
</code>
  
Строка 250: Строка 216:
 
   -- таймер не отключается, но сам "выброс" не происходит
 
   -- таймер не отключается, но сам "выброс" не происходит
 
   if (section == "sr_aes_deadzone") then
 
   if (section == "sr_aes_deadzone") then
if (has_alife_info("freeplay_activated1") or has_alife_info("freeplay_activated2")) then
+
if (has_alife_info("freeplay_activated2")) then
 
return false
 
return false
 
end
 
end
Строка 263: Строка 229:
 
И последнее: переход ЧАЭС1-Припять необходимо отметить на карте. Для этого существует вполне «легальный» механизм. Находим файл '''level_tasks.script''' и в конце функции '''add_lchanger_location''' дописываем следующее:
 
И последнее: переход ЧАЭС1-Припять необходимо отметить на карте. Для этого существует вполне «легальный» механизм. Находим файл '''level_tasks.script''' и в конце функции '''add_lchanger_location''' дописываем следующее:
 
<code>
 
<code>
-- aes
+
-- aes
obj = sim:story_object(31410)
+
obj = sim:story_object(31410)
if obj then
+
if obj then
level.map_add_object_spot(obj.id, "level_changer", "to_pripyat")
+
level.map_add_object_spot(obj.id, "level_changer", "to_pripyat")
end
+
end
 
</code>
 
</code>
  
Строка 277: Строка 243:
 
С уважением, sarthur.
 
С уважением, sarthur.
  
== Дополняет авторы: vllzl ==
+
== Отключение выброса на ЧАЭС-2 ==
<code>
+
Отключение выброса на ЧАЭС-2 производится простейшей модификацией двух файлов - <b>sr_aes_deadzone</b> и <b>xr_effects</b>.
«По-честному» (скриптом, без модификации ''all.spawn'') таймер выброса отключить не получится.
+
Модифицируйте функцию в <b>sr_aes_deadzone</b> так:
</code>
+
Можно удалить таймер скриптом (удаляет рестриктор из игры)
+
 
<lua>
 
<lua>
function delete_aes_timer()
+
function action_postprocess:update (delta)
        local obj = alife():object("aes_space_restrictor_timer")
+
if (not has_alife_info("freeplay_activated2")) then
        if obj then
+
if xr_logic.try_switch_to_another_section (self.object, self.st, db.actor) then
        alife():release(obj, true)
+
return
        end
+
end
        local se_obj = alife():object("aes_space_restrictor_timer_go")
+
        if se_obj then
+
self.actor_inside = self.object:inside (db.actor:position ())
        alife():release(se_obj, true)
+
        end
+
--if self.actor_inside == true then
 +
  --printf ("[%d]ACTOR IN DEAD ZONE !!!", time_global ())
 +
--else
 +
  --printf ("[%d]ACTOR NOT IN DEAD ZONE !!!", time_global ()) 
 +
--end
 +
self:update_hit (delta)
 +
end
 
end
 
end
 
</lua>
 
</lua>
Удалит таймер и выброс из игры. Vllzl
+
Выброс отключен. Землетрясение работает отдельно от выброса. Модифицируйте функцию в <b>xr_effects</b> так:
 +
<lua>
 +
-- постпроцесс и влияние удара в морду
 +
function aes_earthshake (npc)
 +
if (not has_alife_info("freeplay_activated2")) then
 +
local snd_obj = xr_sound.get_safe_sound_object([[ambient\earthquake]])
 +
snd_obj:play_no_feedback(db.actor, sound_object.s2d, 0, vector(), 1.0)
 +
level.add_cam_effector("camera_effects\\earthquake.anm", 1974, false, "")
 +
    --set_postprocess ("scripts\\earthshake.ltx")
 +
end
 +
end
 +
</lua>
 +
 
 +
Этот способ не без недостатков - если вы хотите создавать в своем моде выбросы с помощью этих скриптов и они будут во фриплее - работать они не будут.
 
[[Категория:Скрипты]]
 
[[Категория:Скрипты]]

Текущая версия на 09:36, 23 июня 2015

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

В качестве примера попробуем реализовать режим freeplay - продолжение игры после уничтожения О-Сознания. Для этого нужно создать как минимум две точки — возврат на ЧАЭС-1 со внутреннего дворика (после уничтожения О-Сознания) и переход из ЧАЭС-1 в Припять. К слову сказать, если вернуться на ЧАЭС-1, то там будет продолжать действовать таймер "выброса", который в конце-концов включит "deadzone". Отключить счетчик мне пока не удалось, но зато можно легко отключить сам "выброс" и всласть побегать по уровню.


Часть 1. Генерация работающего LEVEL_CHANGER

Стандартной функции alife():create(…) недостаточно для создания полноценного level_changer.

Ниже я привожу код функции, которая создает и инициализирует level_changer (пишется в созданный нами файл _freeplay_sa.script):

 
function create_level_changer(
	p_story_id,	-- STORY_ID нового level_changer (понадобится нам позже)
	p_position,	-- вектор, координаты точки, в которой будет располагаться центр нового level_changer
	p_lvertex_id,	-- level_vertext_id  - идентифицируют уровень, на котором будет создан level_changer
	p_gvertex_id,	-- game_vertext_id   	
 
	p_dest_lv,	-- level_vertex_id   - идентифицируют уровень, на который level_changer будет перебрасывать игрока 
	p_dest_gv,	-- game_vertex_id
	p_dest_pos,	-- координаты точки, в которой на новом уровне окажется игрок
	p_dest_dir,	-- направрение взгляда игрока
	p_dest_level,	-- название уровня, например "L11_Pripyat"
	p_silent	-- следует задать 1, чтобы подавить вопрос о смене уровня (автоматический переход)
)
	local obj = alife():create("level_changer", p_position, p_lvertex_id, p_gvertex_id)
 
	level.map_add_object_spot(obj.id, "level_changer", "")
 
	local packet = net_packet()
	obj:STATE_Write(packet)
 
	-- свойства cse_alife_object
	local game_vertex_id 			= packet:r_u16()
	local cse_alife_object__unk1_f32 	= packet:r_float()
	local cse_alife_object__unk2_u32 	= packet:r_u32()
	local level_vertex_id 			= packet:r_u32()
	local object_flags 			= packet:r_u32()
	local custom_data 			= packet:r_stringZ()
	local story_id 				= packet:r_u32()
	local spawn_story_id			= packet:r_u32()
 
	-- свойства cse_shape
	local shape_count 			= packet:r_u8()
	for i=1,shape_count do
		local shape_type 		= packet:r_u8()
		if shape_type == 0 then
			-- sphere
			local center 		= packet:r_vec3()
			local radius 		= packet:r_float()
		else
			-- box
			local axis_x_x = packet:r_float()
			local axis_x_y = packet:r_float()
			local axis_x_z = packet:r_float()
			local axis_y_x = packet:r_float()
			local axis_y_y = packet:r_float()
			local axis_y_z = packet:r_float()
			local axis_z_x = packet:r_float()
			local axis_z_y = packet:r_float()
			local axis_z_z = packet:r_float()
			local offset_x = packet:r_float()
			local offset_y = packet:r_float()
			local offset_z = packet:r_float()
		end
	end
 
	-- свойства cse_alife_space_restrictor
	local restrictor_type 			= packet:r_u8()
 
	-- свойства cse_level_changer
	local dest_game_vertex_id		= packet:r_u16()  
	local dest_level_vertex_id 		= packet:r_u32()  
	local dest_position 			= packet:r_vec3()
	local dest_direction 			= packet:r_vec3()
	local dest_level_name 			= packet:r_stringZ()
	local dest_graph_point 			= packet:r_stringZ()
	local silent_mode 			= packet:r_u8()
 
 
	packet:w_begin(game_vertex_id)			-- game_vertex_id
	packet:w_float(cse_alife_object__unk1_f32)
	packet:w_u32(cse_alife_object__unk2_u32) 
	packet:w_u32(level_vertex_id)			-- level_vertex_id
	packet:w_u32( bit_not(193) )			-- object_flags = -193 = 0xFFFFFF3E
	packet:w_stringZ(custom_data)
	packet:w_u32(p_story_id)			-- story_id
	packet:w_u32(spawn_story_id)
 
	packet:w_u8(1)			-- количество фигур
--		packet:w_u8(0)			-- тип фигуры: сфера
--		packet:w_vec3(vector():set(0, 0, 0))  -- sphere_center
--		packet:w_float(3.0)
	packet:w_u8(1)			-- тип фигуры: box
	packet:w_float(2)	   -- axis_x_x
	packet:w_float(0)	   -- axis_x_y
	packet:w_float(0)	   -- axis_x_z
	packet:w_float(0)	   -- axis_y_x
	packet:w_float(4)	   -- axis_y_y
	packet:w_float(0)	   -- axis_y_z
	packet:w_float(0)	   -- axis_z_x
	packet:w_float(0)	   -- axis_z_y
	packet:w_float(4)	   -- axis_z_z
	packet:w_float(0)	   -- offset_x
	packet:w_float(0)	   -- offset_y
	packet:w_float(0)          -- offset_z
 
	packet:w_u8(3)	 	   -- restrictor_type
 
	packet:w_u16(p_dest_gv)			-- destination game_vertex_id
	packet:w_u32(p_dest_lv)			-- destination level_vertex_id
	packet:w_vec3(p_dest_pos)		-- destination position
	packet:w_vec3(p_dest_dir)		-- destination direction (направление взгляда)
	packet:w_stringZ(p_dest_level)		-- destination level name
	packet:w_stringZ("start_actor_99")	-- some string, always const
	packet:w_u8(p_silent)			-- 1 for silent level changing
 
	packet:r_seek(0)
	obj:STATE_Read(packet, packet:w_tell())
 
--	news_manager.send_tip(db.actor, "LC creation finished", nil, nil, 30000)
end
 

Ее вы просто скопируете в наш файл. Изменять ничего не надо.

Часть 2. Создание точек перехода

Теперь следует написать функции создания нужных точек перехода и подключить их к игре. Сами функции просты (тоже в файл _freeplay_sa.script)

 
function refuze_o_sozn()
	if (not has_alife_info("freeplay_activated2")) then
		create_level_changer(21410, vector():set(946.872, 6.0, 167.66), 240852, 2637,
			472710,
			2280,
			vector():set( 1062.15, -0.0982, -3.512 ),
			vector():set(    0.0 ,  0.0   , -1.0 ),
			"L12_Stancia",
			1)
 
		db.actor:give_info_portion("freeplay_activated2")
	end
 
	-- создается переход из ЧАЭС в Припять
	create_chaes2pripyat_exit()
 
	-- актер перебрасывается в level_changer, возвращающий его к правым воротам ЧАЭС
	db.actor:set_actor_position( vector():set(946.872, 6.0, 167.66) )
end
 
function create_chaes2pripyat_exit()
	-- создается переход из ЧАЭС в Припять
	if (not has_alife_info(	"exit_chaes2pripyat_created" )) then
		create_level_changer(31410, vector():set( 917.35, 0.419, -316.35 ), 403866, 2401,
			73868,
			2117,
			vector():set( 31.3, 3.0, 240.0 ),
			vector():set(  0.0, 0.0,  -1.0 ),
			"L11_Pripyat",
			0)
 
		db.actor:give_info_portion("exit_chaes2pripyat_created")
	end
end
 

Функция refuze_o_sozn создает «тихий» переход на уровень ЧАЭС-1 и обычный - в начале уровня ЧАЭС-1 для возврата в Припять, после чего перебрасывает игрока в новый переход. Все переходы защищаются уникальными info_portions, дабы избежать их повторного создания, ведь игрок может захотеть «закончить» игру несколько раз.

Теперь подключение. Во-первых надо добавить новые info-portions. Я решил не изменять оригинальные файлы игры, а сделал для них (ну и для других тоже) отдельный файл

config\gameplay\_info_sa.xml

следующего вида:

<?xml version="1.0" encoding="windows-1251" ?>
 
<game_information_portions>
 
<info_portion id="freeplay_activated2"></info_portion>
<info_portion id="exit_chaes2pripyat_created"></info_portion>
 
</game_information_portions>

Теперь в этот файл можно будет добавлять новые info_portion, которые вы будете использовать в своих сюжетах. Подключается этот файл в system.ltx в секции «info_portions»:

[info_portions]
;список xml файлов, содержащих info_portions
files = _info_sa, info_portions, ....................

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


Следующий шаг - подключение наших скриптов к игре. Для этого откройте файл

config\ui\ui_movies.xml

Чуть ниже «mov_desire_5» находится тэг ролика для концовки «Присоединение к О-Сознанию». Его мы трогать не будем - Меченый станет медузой. А вот после него - тэг для ролика «Отказ от О-Сознания»: «mov_refuse_osoznanie». Функцию завершения в нем заменим следующим образом:

<function_on_stop>_freeplay_sa.refuze_o_sozn</function_on_stop>


Можно запускать и, если есть сохранения перед монолитом и/или о-сознанием, тестировать. Первые переходы срабатывают «тихо» - запрос на смену уровня не выдается (в параметре p_silent задана 1). До перехода в Припять можно успеть добежать, пока не сработал выброс... Но это как-то неправильно, выброс надо остановить (он же произошел, пока мы были внутри станции). «По-честному» (скриптом, без модификации all.spawn) таймер выброса отключить не получится, но зато выброс можно подавить небольшим «хаком». Найдите файл xr_logic.script, а в нем - функцию switch_to_section. Ее нужно модифицировать следующим образом:

 
-- Выполняет переключение на указанную секцию, если задана.
-- Если section == nil, остается работать старая секция.
function switch_to_section(npc, st, section)
  if section == nil or section == "" then
    return false
  end
 
  -- 15.03.2008 by SA:
  -- отключает "смертельные зоны" на ЧАЭС после запуска режима FREEPLAY
  -- 	таймер не отключается, но сам "выброс" не происходит
  if (section == "sr_aes_deadzone") then
	if (has_alife_info("freeplay_activated2")) then
		return false
	end
  end
 
  ... далее без изменений ...
end
 

Данный способ оставляет висящий на нулях таймер и все эффекты начала выброса, но сам «выброс» отключается.

И последнее: переход ЧАЭС1-Припять необходимо отметить на карте. Для этого существует вполне «легальный» механизм. Находим файл level_tasks.script и в конце функции add_lchanger_location дописываем следующее:

-- aes
obj = sim:story_object(31410)
if obj then
level.map_add_object_spot(obj.id, "level_changer", "to_pripyat")
end

Здесь 31410 - story_id нашего level_changer, который создается нашей функцией create_chaes2pripyat_exit.

На этом пока все (мелкие огрехи типа направления взгляда игрока после смены уровня исправлю позже). Прошу тестировать и дополнять.

С уважением, sarthur.

Отключение выброса на ЧАЭС-2

Отключение выброса на ЧАЭС-2 производится простейшей модификацией двух файлов - sr_aes_deadzone и xr_effects. Модифицируйте функцию в sr_aes_deadzone так:

 
function action_postprocess:update (delta)
if (not has_alife_info("freeplay_activated2")) then
	if xr_logic.try_switch_to_another_section (self.object, self.st, db.actor) then
		return
	end
 
	self.actor_inside = self.object:inside (db.actor:position ())
 
	--if self.actor_inside == true then
	   --printf ("[%d]ACTOR IN DEAD ZONE !!!", time_global ()) 
	--else
	   --printf ("[%d]ACTOR NOT IN DEAD ZONE !!!", time_global ())   
	--end
	self:update_hit (delta)
end
end
 

Выброс отключен. Землетрясение работает отдельно от выброса. Модифицируйте функцию в xr_effects так:

 
-- постпроцесс и влияние удара в морду
function aes_earthshake (npc)
if (not has_alife_info("freeplay_activated2")) then
	local snd_obj = xr_sound.get_safe_sound_object([[ambient\earthquake]])
	snd_obj:play_no_feedback(db.actor, sound_object.s2d, 0, vector(), 1.0)
	level.add_cam_effector("camera_effects\\earthquake.anm", 1974, false, "")
    --set_postprocess ("scripts\\earthshake.ltx")
end
end
 

Этот способ не без недостатков - если вы хотите создавать в своем моде выбросы с помощью этих скриптов и они будут во фриплее - работать они не будут.

Другие места
LANGUAGE