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

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

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

Версия от 18:39, 17 марта 2008; 217.107.222.161 (обсуждение)

(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

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

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


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

Стандартной функции alife():create(…) недостаточно для создания полноценного level_changer. Собственно методика создания сложных объектов описана в статье Один из методов спавна. Сложность заключалась лишь в порядке полей и свойствах Shape. Неоценимую помощь в этом вопросе оказала утилита ACDC (created by bardak).

Ниже я привожу код функции, которая создает и инициализирует level_changer:

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_gest_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_gest_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

Для shape типа "box" загрузка координат методом packet:r_matrix() окончилась неудачей. Я подозреваю, что не был прочитан вектор "offset", но точной уверенности нет, поэтому пока остановился на покомпонентной выборке и сохранении координат.


Часть 2. Создание точек перехода после уничтожения О-Сознания

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

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,
2384,
162109,
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()
if (not has_alife_info("freeplay_activated2")) then
create_level_changer(21410, vector():set(946.872, 6.0, 167.66), 240852, 2637,
2280,
472710,
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,
2117,
73868,
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

Функцию exit_monolit я создал прежде всего для тестирования, но решил оставить тут. Вдруг кто-то захочет реализовать более сложный возврат: Меченого грузят в "грузовик смерти" и он снова приходит в себя на кордоне...

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

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

config\gameplay\_info_sa.xml

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

<?xml version="1.0" encoding="windows-1251" ?>
 
<game_information_portions>
 
<info_portion id="freeplay_activated1"></info_portion>
<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
Другие места
LANGUAGE