Создание телепорта в сталкере

Ответить на тему
 
Автор Сообщение

Stalker ®

Пол: Мужской

Стаж: 11 лет

Сообщений: 399

Откуда: Украина, Одесса

Создавать темы 07-Июл-2013 22:12

[Цитировать]

Создание телепорта в сталкере

Теория этого дела

В игре существует такой объект как "zone_teleport", но если мы его создадим через create, то он будет выглядеть и переливаться как настоящий телепорт, но телепортировать нас куда-либо увы не сможет. Связано это с тем что, у аномалий ( а телепорт это такая разновидность аномалии ) параметры задаются хитромудро, через all.spawn. Вобщем штатным способом телепорты без полнофункционального редактора карт не получить (хотя возможно я не прав уже после написания этого текста появились идеи как это сделать через all.spawn). Значит на нашу долю остаються способы "не штатные" Реализуем самый простой. Будем считать что у нас на карте есть квадрат с заданными координатами при попадании в который актера должно переместить в точку с другими координатами. Для этого будем периодически проверять координаты актера, и если он в квадрате - перемещяем. Вот в краце принцип действия нашего "самодельного" телепорта.
Реализация
В каталоге gamedata\scripts\ Создадим файл bind_mteleport.script с логикой работы нашего телепорта.
-- ************************************************
-- ** Imp **
-- ** Биндер самодельных телепортов **
-- ** Поддерживает работу самопальных телепортов **
-- ************************************************
local teleport_binders ={} -- Список телепортов
function abs_comp(a,b)
-- Служебная функция вычисления разности
if( a < b) then
return (b - a)
else
return (a - b)
end
end
function teleportate(x,y,z)
-- Функция телепортации
local a = vector()
-- Задаем координаты
a.x = x
a.y = y
a.z = z
-- Сама телепортация
db.actor:set_actor_position(a)
-- Звуковое сопровождение
local snd_obj = xr_sound.get_safe_sound_object([[affects\tinnitus3a]])
snd_obj:play_no_feedback(db.actor, sound_object.s2d, 0, vector(), 1.0)
end
function actor_update(delta)
local i,v,acter_poz,s
-- Получим позицию актера (что-бы каждый раз не запрашивать)
acter_poz = db.actor:position()
-- Проверяем наши телепорты
for i, v in pairs(teleport_binders) do
s = v.parametrs
local obj = level.object_by_id( i )
if obj ~= nil then
-- Наш телепорт в онлайне проверяем дальше
if s.teleporte ~= nil and s.teleporte ~= false then
-- Телепорт запущен
if ( time_global() <= s.time ) then
-- Если время отведенное на показ спецэфектов
-- прошло, производим телепортацию
teleportate(s.poz_x,s.poz_y,s.poz_z)
if s.rotate ~= nil then
db.actor:set_actor_direction(s.rotate)
end
s.teleporte = false
end
return
end
-- Пороверим не забрел-ли актер в наш телепорт
if (abs_comp(s.x, acter_poz.x)< v.parametrs.radius and
abs_comp(s.z, acter_poz.z)< v.parametrs.radius and
abs_comp(s.y, acter_poz.y)< v.parametrs.z_radius) then
-- Актер в зоне действия телепорта, запустим телепорт
s["teleporte"] = true
s["time"] = time_global() + 500
-- Запускаем спецэфекты телепортации
level.add_pp_effector ("teleport.ppe", 2006, false)
end
end
end
end
function bind( obj )
obj:bind_object( restrictor_teleport( obj ) )
end
----------------------------------------------------------------------------------------------------
class "restrictor_teleport" ( object_binder )
function restrictor_teleport:__init(obj, char_ini) super(obj)
end
function restrictor_teleport:net_spawn(data)
local char_ini = system_ini()
-- Если это телепорт то занесем его в специальный список телепортов
if self.teleport == true then
teleport_binders[self.object:id()] = self
-- Заполним таблицу параметров
self["parametrs"] = {}
if char_ini:line_exist(self.section, "radius") then
self.parametrs["radius"] = tonumber(char_ini:r_string(self.section, "radius"))
else
self.parametrs["radius"] = 2 -- Дефолтный радиус по xy
end
if char_ini:line_exist(self.section, "z_radius") then
self.parametrs["z_radius"] = tonumber(char_ini:r_string(self.section, "z_radius"))
else
self.parametrs["z_radius"] = self.parametrs["radius"] -- если радиус высоты не задан то задаем равным радиусу xy
end
-- Запомним позицию что-бы каждый раз не считать
local s_obj = alife():object(self.object:id())
self.parametrs["x"] = tonumber(s_obj.position.x);
self.parametrs["y"] = tonumber(s_obj.position.y);
self.parametrs["z"] = tonumber(s_obj.position.z);
-- Запомним координаты куда телепортимся
self.parametrs["poz_x"] = tonumber(char_ini:r_string(self.section, "poz_x"))
self.parametrs["poz_y"] = tonumber(char_ini:r_string(self.section, "poz_y"))
self.parametrs["poz_z"] = tonumber(char_ini:r_string(self.section, "poz_z"))
if char_ini:line_exist(self.section, "rotate") then
self.parametrs["rotate"] = tonumber(char_ini:r_string(self.section, "rotate"))
end
end
return true
end
function restrictor_teleport:net_destroy()
-- Удаляем наш телепорт
teleport_binders[self.object:id()] = nil
self.parametrs = nil
object_binder.net_destroy(self)
end
function restrictor_teleport:reload(section)
local char_ini = system_ini()
self.section = section
-- Если это телепорт то
if char_ini ~= nil and char_ini:line_exist(self.section, "teleport") then
self["teleport"] = true
end
end
Для постоянного обновления нужно прицепить функцию actor_update() к биндеру актера, для чего в файле bind_stalker.script найдем функцию:
function actor_binder:update(delta)
В ней найдем вызов обновления рестрикторов bind_restrictor.actor_update(delta) под которым вставим строку с вызовом нашей функции обновления: bind_mteleport.actor_update(delta)
Все с программной частью закончили, теперь задаем данные телепорта.
В каталоге gamedata\config\misc открываем файл zone_teleport.ltx и в конце файла добавляем следующие строки описывающие конкретный телепорт:
[m_teleport_1]:zone_teleport
teleport = standart
script_binding = bind_mteleport.bind
;Параметры нашего телепорта
radius = 2
;Высота захвата телепорта
z_radius = 2
;Куда телепортируемся (телепортация всегда идет в пределах карты)
poz_x = 22.78
poz_y = 20.35
poz_z = 659.24
; Угол зрения при появлении. Если параметра нет то не меняется.
rotate = 1.5
Параметры нашего телепорта:
radius - на самом деле не радиус, а половина длинны стороны нашего квадрата (в начале я хотел сделать его кругом, но посчитал, что лучше не тратить процессорное врямя по пусту). Центром квадрата является точка респавна телепорта.
z_radius - высота нашего телепорта.
poz_x, poz_y, poz_z - координаты точки телепортации.
rotate - Угол поворота после телепортации от оси X (я не разбирался в каких единицах задается, но 1.5 примерно равно 90 градусов). Если параметр удалить то будет оставатья угол под которым актер вошел в телепорт.
Использование
Теперь с помощью create создадим наш телепорт: Пример:
local obj
local a = vector()
a.x = -244.55
a.y = -19.46
a.z = -125.42
obj = alife():create("m_teleport_1",a,12829,8,65535)
Создаст телепорт возле выхода из бункера Сидоровича. Наш телепорт перебрасывает игрока на вышку блокпоста (перед выходом с уровня).
Все! Вот тут вы можете взять готовый мод с двумя телепортами
[Профиль] [ЛС]

Stalker ®

Пол: Мужской

Стаж: 11 лет

Сообщений: 399

Откуда: Украина, Одесса

Создавать темы 10-Июл-2013 11:46 (спустя 2 дня 13 часов)

[Цитировать]

Создание телепорта и space_restrictor скриптом
Если стоит параметр [m_teleport_1]:zone_teleport телепорт будет виден, если поставить zone_teleport_out , то невидим.

Телепорт между локациями

Создадим скриптовый файл, например my_teleportation.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_s32(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_02") -- 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())
level.add_pp_effector ("teleport.ppe", 2006, false)
end
Теперь ниже пишем функции спавна телепортов
-----------------------------Телепортация--------------------------------
function spawn_ my_teleport() -- название функции
create_level_changer(2040, db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id(), -- здесь 2040 story_id нашего телепорта
16191, -- левел вертекс
8, -- гаме вертекс
vector():set(-239.110947,-19.788391,-134.999161), -- координаты
vector():set(0.0, 0.0, 0.0),
"l01_escape", -- имя локации
1)
end
Все координаты и имя локации - куда телепортируемся. Функцию телепортации вызываем из диалога или из инфопорции , но для этого нужно будет спавнить space_restrictor через скрипт. Из диалога функцию вызова ставит в последнюю фразу.
После телепортации надо удалить телепорт, иначе будет телепортировать каждый раз при нахождении ГГ в зоне телепорта
Функция удаления
function delete_my_teleport()
local sim = alife()
local se_obj = sim:story_object(2040)
if se_obj then
sim:release(se_obj, true)
end
local actor = db.actor
end
-----------------------------------------------------------------------------
Спавн space_restrictor
Создадим скриптовый файл my_restrictor.script в его теле пропишем
----------
-- чтение формы из нет-пакета
----------
function r_shape(packet)
local s
local st = {}
st.count = packet:r_u8()
st.shapes = {}
for i=1, st.count do
s = {}
s.type = packet:r_u8()
if s.type == 0 then
s.center = packet:r_vec3()
s.radius = packet:r_float()
else
s.axis_x = packet:r_vec3()
s.axis_y = packet:r_vec3()
s.axis_z = packet:r_vec3()
s.offset = packet:r_vec3()
end
st.shapes = s
end
return st
end
----------
-- запись формы в нет-пакет
----------
function w_shape(packet, st)
local s
packet:w_u8(st.count)
for i=1, st.count do
s = st.shapes
packet:w_u8(s.type)
if s.type == 0 then
packet:w_vec3(s.center)
packet:w_float(s.radius)
else
packet:w_vec3(s.axis_x)
packet:w_vec3(s.axis_y)
packet:w_vec3(s.axis_z)
packet:w_vec3(s.offset)
end
end
end
----------
-- перепаковка нет-пакета созданного скриптом рестрикта
----------
function rewrite_restrictor(se_obj, custom, radius)
local packet = net_packet()
se_obj:STATE_Write(packet)
local game_vertex_id = packet:r_u16()
local distance = packet:r_float()
local direct_control = packet:r_s32()
local level_vertex_id = packet:r_s32()
local object_flags = packet:r_s32()
local custom_data = packet:r_stringZ()
local story_id = packet:r_s32()
local spawn_story_id = packet:r_s32()
local shape = r_shape(packet)
local restrictor_type = packet:r_u8()
custom_data = custom
shape = {}
shape.count = 1
shape.shapes = {}
shape.shapes[1] = {}
shape.shapes[1].type = 0
shape.shapes[1].center = vector():set(0,0,0)
shape.shapes[1].radius = radius
packet:w_u16(game_vertex_id)
packet:w_float(distance)
packet:w_s32(direct_control)
packet:w_s32(level_vertex_id)
packet:w_s32(object_flags)
packet:w_stringZ(custom_data)
packet:w_s32(story_id)
packet:w_s32(spawn_story_id)
w_shape(packet, shape)
packet:w_u8(restrictor_type)
se_obj:STATE_Read(packet, packet:w_tell() - packet:r_tell())
end
Ниже пишем функцию спавна
function my_restr() -- название
local se_obj = alife():create("space_restrictor",vector():set(58.646267,3.917035,204.469543),317530,3229) --координаты
local custom = "[logic]\ncfg = scripts\my_restr.ltx" --путь к логике
rewrite_restrictor(se_obj, custom, 2.0) --радиус рестриктора
end
Вызываем её в нужный нам момент, прописав в диалог или по какому-либо
условию.
Далее идём в папку gamedata\config\scripts и создаём файл с логикой my_restr.ltx в его теле пропишем
[logic]
active = sr_idle@one
[sr_idle@one]
on_actor_inside = {+my_info} nil %=my_teleportation.spawn_ my_teleport%
В логике, при наличии инфопоршня my_info, вызывается функция
спавна телепорта из скриптового файла: my_teleportation.script
При установленном nil, рестриктор сработает только один раз.
Точно так же, по аналогии, вместо активации функции, можно выдать просто
какой-угодно инфопоршень для нужных вам целей.
В этом случае в логике рестриктора будет немного другая строка:
[logic]
active = sr_idle@one
[sr_idle@one]
on_actor_inside = {+my_info} nil %+my_infoposhen%
[Профиль] [ЛС]
Показать сообщения:    
Ответить на тему

Текущее время: 22-Сен 23:37

Часовой пояс: UTC + 2



Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы