Меню






Опрос
В каком формате вы бы хотели видеть стримы?
Всего ответов: 32

Стрим

Подписывайтесь на наш канал на youtube! Каждый день стримы по сталкеру.
Сюда!

Категории раздела
Работа со скриптами [11]
Работа с текстурами [2]
Респавн [2]
Работа со звуками [2]

Свежие моды
Материалов за текущий период нет.


Новое на форуме
  • Пожелания (5)
  • Набор модераторов (3)
  • Создание моделей и их добавление в Stalker (0)
  • Бар "100 Рентген" (3)
  • Обсуждение проекта (2)

  • Опрос
    Как вы попали на наш сайт?
    Всего ответов: 48

    Главная » Статьи » Модостроение Тень Чернобыля » Работа со скриптами

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

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

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

     

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

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

    Ниже я привожу код функции, которая создает и инициализирует 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

    найдите элементы «mov_desire_» (их пять, по количеству ложных концовок игры - все варианты общения с Монолитом). Внутри каждого элемента есть дочерние элементы «function_on_stop», которые задают функцию, запускающуюся сразу после ролика. Стандартное содержимое: 

    <function_on_stop>xr_effects.game_credits</function_on_stop>

    Функция xr_effects.game_credits запускает финальные титры. Именно ее и нужно заменить на вызов нашей функции _freeplay_sa.exit_monolit. Вот что должно получиться: 

    <function_on_stop>_freeplay_sa.exit_monolit</function_on_stop>
    <!-- original:    <function_on_stop>xr_effects.game_credits</function_on_stop>  -->

    Старый элемент я советую не удалять, а закомментировать. 

    Чуть ниже «mov_desire_5» находится тэг ролика для концовки «Присоединение к О-Сознанию». Его мы трогать не будем - Меченый станет медузой. А вот после него - тэг для ролика «Отказ от О-Сознания»: «mov_refuse_osoznanie». Функцию завершения в нем заменим следующим образом: 
    <function_on_stop>_freeplay_sa.refuze_o_sozn</function_on_stop>
    <!-- original:    <function_on_stop>xr_effects.game_credits</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_activated1") or has_alife_info("freeplay_activated2")) then
            return false
        end
      end
     
      ... далее без изменений ...
    end

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

    И последнее: переход ЧАЭС1-Припять необходимо отметить на карте. Для этого существует вполне «легальный» механизм. Находим файл level_tasks.script и в конце функции add_lchanger_location дописываем следующее: 
    -- aes (++16.03.2008 by SA):
            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. 

    Категория: Работа со скриптами | Добавил: AlexThunder (06.11.2016)
    Просмотров: 260 | Рейтинг: 0.0/0
    Всего комментариев: 0
    avatar
    PDA


    Stalker STREAM


    PDA
    Логин:
    Пароль:

    Сталкер-сеть

    Статистика

    Онлайн всего: 1
    Гостей: 1
    Пользователей: 0

    Друзья
    Моды Тени Чернобыля, Чистое Небо, Зов Припяти. Карты, Читы, Прохождение модификаций Stalker 2, сюжет, скачать моды stalker



    Авторское право на игру и использованные в ней материалы принадлежат GSC Game World.
    Любое использование материалов сайта возможно только с разрешения администрации

    uCoz© 2017