В данной теме вы можете задавать любые вопросы касательно S.T.A.L.K.E.R. моддинга.
Не флудить
Предварительно просматривайте раздел. Ответ на ваш вопрос может быть там.
В своем вопросе указывайте платформу моддинга - Тень Чернобыля, Чистое небо или Зов Припяти.
1. Какая стоит игра, с каким патчем? 2. Какой мод, версия мода? 3. Что правили своими ручками? 4. Конфиг компа. 5. Лог вылета и последние 25 строк (помещать под спойлер).
Структура папок и файлов в корневом каталоге игры:
...\S.T.A.L.K.E.R\gamedata\anims – папка содержит исполнительные файлы эффектов (например от ПНВ).
...\S.T.A.L.K.E.R\gamedata\config – папка содержит основные конфигурационные файлы (т. е. большинство настроек игры)
...\S.T.A.L.K.E.R\gamedata\meshes – содержит модели игровых предметов и персонажей...
...\S.T.A.L.K.E.R\gamedata\scripts – папка со скриптами (рабочими файлами содержащими в себе наборы функций) – основная папка, отвечающая за ответные действия игры и действия производимых игроком в игре.
...\S.T.A.L.K.E.R\gamedata\shaders – папка содержит в себе конфигурационные файлы шейдеров.
...\S.T.A.L.K.E.R\gamedata\sounds – здесь находятся все звуки игры (разложены по своим каталогам и папкам)
...\S.T.A.L.K.E.R\gamedata\spawns – содержит файл спавна – очень важный файл – не трогать без нужды!!!
...\S.T.A.L.K.E.R\gamedata\textures – содержит разложенные по каталогам и папкам текстуры используемые в игре.
Теперь разберем папки в каталоге «config»
...\S.T.A.L.K.E.R\gamedata\config\creatures - содержит массу конфигурационных файлов, в основном отвечающих за взаимодействие Главного героя игры (в дальнейшем ГГ) и прочего окружения в игре и ТТХ самого ГГ.
...\S.T.A.L.K.E.R\gamedata\config\gameplay – содержит конфиг. файлы персонажей игры (НПС) отвечающие за внешний вид, статус, снаряжение. Файлы: character_desc_ххх (где ххх – название локации) и character_desc_general (отвечает за зомбированного персонажа). Также содержит файлы диалогов и профилей НПС.
…\S.T.A.L.K.E.R\gamedata\config\misc – конфиг.файлы торговцев (папки с именами торговцев), артефактов, брони, квестовых предметов, уникальных предметов, файл отвечающий за награды за автозадания, файл эффектов (алкоголизма, ранения, ПНВ и прочих)...
…\S.T.A.L.K.E.R\gamedata\config\mp – среди прочих содержит важный файл mp_ranks – отвечающий за выпадение оружия и патронов из рук ГГ и НПС – без прописывания добавляемого в игру оружия в этот файл при выпадении нового оружия из рук ГГ\НПС будут следовать вылеты.
…\S.T.A.L.K.E.R\gamedata\config\text\rus – содержит файлы описаний всего и вся находящегося в игре (брони, артефактов, оружия, предметов и прочего).
…\S.T.A.L.K.E.R\gamedata\config\weapons – содержит конфиг.файлы оружия и боеприпасов используемых в игре.
…\S.T.A.L.K.E.R\gamedata\config\weathers – содержит конфиг.файлы настройки погоды на локациях.
Теперь немного подробнее о файлах.
...\S.T.A.L.K.E.R\gamedata\config\misc:
Папка shop_ххх (ххх – имя торговца) – содержит конфиг.файлы ассортимента торговца.
outfits – содержит секции конфигов костюмов.
artefacts - содержит секции конфигов артефактов.
items - содержит секции конфигов предметов.
monster_items - содержит секции конфигов частей монстров
postprocess - содержит секции конфигов пост. процессов (например: ПНВ).
quest_items - содержит секции конфигов квестовых предметов.
task_manager - содержит секции наград за автоквесты.
unique_items - содержит секции конфигов уникального оружия и костюмов.
У кого есть желание, создать FAQ по основным вопросам моддинга - обращаемся в ЛС.
ins33, нарвался на этот скрипт ни инсайде "Выброс на Янтаре". Никакой update не нужен. level.add_call(on, off) -- add_call -- по русски похоже - создать колбек.
Цитата
при возврате on = true переключает ф-ию на off?
Всё верно.Можно вызывать до бесконечности.Тот же самый "ВЫброс на Янтаре" с продолжением:
local a = 0 local b = 0 function surge() if a < time_global() then a = time_global() + 5*1000 b = time_global() + math.random(5000, 20000) level.add_call(on, off) end end
function on() return time_global() > b end
function off() surge_start() end
function surge_start() if a < time_global() then a = time_global() + 10000 b = time_global() + 15000 level.add_call(on_start, off_start) local snd_obj = xr_sound.get_safe_sound_object([[anomaly\red_forest_surge]]) snd_obj:play_no_feedback(db.actor, sound_object.s2d, 0, vector(), 1.0) db.actor:give_info_portion("surge_npc_start") end end
function on_start() return time_global() > b end
function off_start() surge_1() end
function surge_1() if a < time_global() then a = time_global() + 5000 b = time_global() + 10000 level.add_call(on_1, off_1) level.add_pp_effector("vibros_p.ppe", 1974, false) local snd_obj = xr_sound.get_safe_sound_object([[ambient\earthquake]]) snd_obj:play_at_pos(db.actor, vector():set(0,0,0), 0, sound_object.s2d) level.add_cam_effector("camera_effects\\earthquake.anm", 1974, true, "") end end
function on_1() return time_global() > b end
function off_1() level.remove_pp_effector(1974) surge_2() end
function surge_2() if a < time_global() then a = time_global() + 5000 b = time_global() + 15000 level.add_call(on_2, off_2) local snd_obj = xr_sound.get_safe_sound_object([[anomaly\blowout]]) snd_obj:play_at_pos(db.actor, vector():set(0,0,0), 0, sound_object.s2d) level.add_pp_effector ("vibros.ppe", 1974, false) end end
function on_2() return time_global() > b end
function off_2() level.remove_pp_effector(1974) surge_3() end
function surge_3() if a < time_global() then a = time_global() + 15000 b = time_global() + 15000 level.add_call(on_3, off_3) level.add_pp_effector("psy_antenna.ppe", 1875, true)
local snd_obj = xr_sound.get_safe_sound_object([[anomaly\surge_alles_1]]) snd_obj:play_no_feedback(db.actor, sound_object.s2d, 0, vector(), 1.0) db.actor:give_info_portion("surge_stop") xr_sound.set_actor_sound("") end -- function update_surge(restrictor) local restrictor = nil restrictor = get_game_object("esc_surge") or get_game_object("esc_surge_ferma") or get_game_object("esc_surge_monsrt") if restrictor ~= nil then if this.dist_to_objects(db.actor, restrictor) > 53 then xr_sound.set_actor_sound("") end return false end end
function get_game_object(section_name) if section_name == nil or type(section_name) ~= "string" then return nil end local game_object = alife():object(section_name) if game_object == nil then return nil end return (level.object_by_id(game_object.id)) end
function dist_to_objects(source_object, destination_object) if source_object and destination_object then return source_object:position():distance_to(destination_object:position()) end return (-1) end
function hit_actor() local psy_hit = hit() psy_hit.direction = vector():set(0,0,0) psy_hit.impulse = 0 psy_hit.draftsman = db.actor psy_hit.power = 0.0005 psy_hit.type = hit.radiation db.actor:hit(psy_hit) psy_hit.power = 0.0005 psy_hit.type = hit.shock db.actor:hit(psy_hit) end
local t_art = { "af_rusty_thorn", "af_rusty_kristall", "af_electra_sparkler", "af_electra_flash", "af_electra_moonlight"
} function spawn_art() local pos = db.actor:position() local dir = db.actor:direction() local section = t_art[math.random(table.getn(t_art))] pos = pos:add(dir:mul(25)) pos.y = pos.y + 12 local obj = alife():create((section), pos, db.actor:level_vertex_id(), db.actor:game_vertex_id()) end
ins33, потести.Я тут на днях потестил из ЧН в Тени в xr_effects.script
Код
function spawn_object(actor, obj, p) local spawn_sect = p[1] if spawn_sect == nil then abort("Wrong spawn section for 'spawn_object' function %s. For object %s", tostring(spawn_sect), obj:name()) end local path_name = p[2] if path_name == nil then abort("Wrong path_name for 'spawn_object' function %s. For object %s", tostring(path_name), obj:name()) end if not level.patrol_path_exists(path_name) then abort("Path %s doesnt exist. Function 'spawn_object' for object %s ", tostring(path_name), obj:name()) end local ptr = patrol(path_name) local se_obj = alife():create(spawn_sect,ptr:point(),ptr:level_vertex_id(0),ptr:game_vertex_id(0)) end
function spawn_obj_in_zone(actor, obj, p) local spawn_sect = p[1] if spawn_sect == nil then abort("Wrong spawn section for 'spawn_object' function %s. For object %s", tostring(spawn_sect), obj:name()) end local zone_name = p[2] if zone_name == nil then abort("Wrong zone_name for 'spawn_object' function %s. For object %s", tostring(zone_name), obj:name()) end if db.zone_by_name[zone_name] == nil then abort("Zone %s doesnt exist. Function 'spawn_object' for object %s ", tostring(zone_name), obj:name()) end
local zone = db.zone_by_name[zone_name] local spawned_obj = alife():create( spawn_sect,zone:position(),zone:level_vertex_id(),zone:game_vertex_id()) end
function spawn_object_in(actor, obj, p) local spawn_sect = p[1] if spawn_sect == nil then abort("Wrong spawn section for 'spawn_object' function %s. For object %s", tostring(spawn_sect), obj:name()) end
local target_name = p[2] if target_name == nil then abort("Wrong target_name for 'spawn_object_in' function %s. For object %s", tostring(target_name), obj:name()) end local box = alife():object(target_name) if(box==nil) then abort("There is no such object %s", target_name) end local obj = alife():create(spawn_sect,vector(),0,0,box.id) end
Теперь из логики рестриктора в Тенях можно:
;spawn in way --*************************** [logic] active = sr_idle
ins33, седня не мой день Expression : fatal error Function : CScriptEngine::lua_error File : E:\stalker\sources\trunk\xr_3da\xrGame\script_engine.cpp Line : 73 Description : <no expression> Arguments : LUA error: ...:\s.t.a.l.k.e.r\gamedata\scripts\bind_stalker.script:245: attempt to index global 'order' (a nil value)
Arist, Прикольно, и информативно. Хотя по идеи можно упростить до:
Код
function spawn_object_in(actor, obj, p) local spawn_sect = p[1] local target_name = p[2] local box = alife():object(target_name) if spawn_sect ~= nil then if target_name ~= nil then if box ~=nil then local obj = alife():create(spawn_sect,vector(),0,0,box.id) end end end end
Хотя не понятно, если будет вылет, что лог покажет.
Метод add_call создаёт два коллбека, функции для которых являются параметрами самого метода:
Код
level.add_call (condition, action)
Функция condition будет вызываться до тех пор пока будет возвращать false/nil. Как только вернёт true - будет вызвана функция action. Самый тривиальный пример использования - таймер по истечению которого выполняется какое-либо действие:
Код
--# Взводим таймер на тридцать секунд. local timer = 30000 local timeNow = time_global() + timer --# Проверяем время. local function ConditionCall () if timeNow > time_global() then return true end return false end --# Действие по окончании таймера. local function ActionCall () news_manager.send_tip(db.actor, "Time is out!") end --# Ставим коллбек. level.add_call(ConditionCall, ActionCall)
Ничего естественно само не сохраняется, поэтому об этом нужно заботится самому.
--[[--------------------------------------------------------------------------------------------------------- File : ins_timer.script Author of assembly : ins33 Start : Добавить в секцию фонаря "[device_torch]" script_binding = name_script.init -]]----------------------------------------------------------------------------------------------------------
time_a local rest_time_a = 0 local minute = 60*1000 -- игровая минута local hour = minute*6 -- игровой час
function update() -- в bind_stalker.script в update(delta) добавь name_script.update() this.timer_start() -- Таймер this.hud_timer() -- Вывод секундомера на худ. end
function timer() -- в диалог добавляем <action>name_script.name_function</action> time_a = time_global() + hour*2 -- взводим на 2 часа db.actor:give_info_portion("get_gun1") -- Выдаём поршень работы таймера. end
function timer_start() -- на update(delta) if has_alife_info("name_infoporticion") then -- проверяем условие работы таймера if time_a == nil then -- если вдруг таймер обнулился (перезагрузка) if rest_time_a ~= 0 then -- и остался остаток (он будет только если используешь тот скрипт) то time_a = time_global() + rest_time_a -- вычисляем остаток времени end else -- или rest_time_a = time_a - time_global() -- остаток = разнице if time_a < time_global() then -- если пора "вставать" db.actor:disable_info_portion("name_infoporticion") -- забираем поршень, что-бы остановить таймер. db.actor:give_info_portion("get_gun") -- выдаём поршень, который проверяется в диалоге. news_manager.send_tip(db.actor, "%c[255,0,255,0]Бармен.\\n%c[default]Зайди ко мне, поговорить надо.", 0, "trader", 5000) -- отправим смс. Если надо. time_a = nil -- обнуляем rest_time_a = 0 -- обнуляем end end end end
-- выводим значение таймера в обратном отсчете на худ function hud_timer() --/ вызывается из ':update' сталкер-биндера local hud = get_hud() local st if time_a then st = hud:GetCustomStatic("hud_timer") if st==nil then hud:AddCustomStatic("hud_timer", true) st = hud:GetCustomStatic("hud_timer") end if rest_time_a~=nil then -- Остался ли остаток local hours = math.floor(rest_time_a/3600000) -- показываем часы local minutes = math.floor(rest_time_a/60000 - hours*60) -- показываем минуты local seconds = math.floor(rest_time_a/1000 - hours*3600 - minutes*60) -- показываем секунды local text = string.format("%02d:%02d:%02d",hours,minutes,seconds) -- формат вывода st:wnd():SetTextST(text) end else if hud:GetCustomStatic("hud_timer")~=nil then hud:RemoveCustomStatic("hud_timer") end end end
function init(obj) local torch = torch_binder(obj) obj:bind_object(torch) end
class "torch_binder" (object_binder) function torch_binder:__init(obj) super(obj) end
function torch_binder:reload(section) object_binder.reload(self, section) end
function torch_binder:reinit() object_binder.reinit(self) end
function torch_binder:update(delta) object_binder.update(self, delta) update() end
function torch_binder:net_spawn(data) return object_binder.net_spawn(self, data) end
function torch_binder:net_destroy() object_binder.net_destroy(self) end
function torch_binder:net_save_relevant() return true end
--// сохранение и загрузка табличных и прочих данных - так как фонарик всегда в онлайне - очень удобно использовать
function torch_binder:save(p) -- тут сохраняем значение object_binder.save(self, p) p:w_u32(rest_time_a) -- остаток p:w_u32(this.SaveBackTimer()) -- походу тут хранится end
function torch_binder:load® -- отсюда загружаем object_binder.load(self, r) rest_time_a = r:r_u32() -- остаток this.LoadBackTimer(r:r_u32()) -- соответственно загружается end
Arist, Попробуй передать в несуществующий тайник, если не будет вылета, то перестраховка, которая скажет, что не туда спавнишь. Если будет, то смотри, что лог напишет. Точнее, упомянет ли он имя несуществующего тайника.
Добавлено (17.04.2014, 00:10) --------------------------------------------- Сейчас у себя проверю.
function spawn_object_in(actor, obj, p) local spawn_sect = p[1] local target_name = p[2] local box = alife():object(target_name) if spawn_sect ~= nil and target_name ~= nil and box ~=nil then local obj = alife():create(spawn_sect,vector(),0,0,box.id) end end
В рестриктор тоже сработало(заспавнил сталкера и аптечку) [logic] active = sr_idle
[sr_idle] on_info = {+test_info} nil %=spawn_obj_in_zone(agr_stalker_veteran:esc_test_restrictor) =spawn_obj_in_zone(medkit:esc_test_restrictor)% В xr_effects.script
Код
function spawn_obj_in_zone(actor, obj, p) local spawn_sect = p[1] local zone_name = p[2] local zone = db.zone_by_name[zone_name] if spawn_sect ~= nil and zone_name ~= nil and zone ~= nil then local spawned_obj = alife():create( spawn_sect,zone:position(),zone:level_vertex_id(),zone:game_vertex_id()) end end
Надо через диалог попробовать.
Сообщение отредактировал Arist - Четверг, 17.04.2014, 00:38
ins33, а как правильно повесить timer_start? я повесил
Код
timer_start.update(delta)
и получил
Код
Expression : fatal error Function : CScriptEngine::lua_error File : E:\stalker\sources\trunk\xr_3da\xrGame\script_engine.cpp Line : 73 Description : <no expression> Arguments : LUA error: ...:\s.t.a.l.k.e.r\gamedata\scripts\bind_stalker.script:246: attempt to index global 'timer_start' (a nil value)
Но у меня правленый скрипт для вывода в лог инфы по логике. С стандартным этой строки Cannot find saved game error: there is no such object bla_bla не будет.
Sanjaaa, В том скрипте было 2 опечатки, ты их исправил? Лог говорит, что в скрипте timer_start есть ошибка. Проверь его script Syntax Checker.exe П.С. ты исправил torch_binder:load® на torch_binder:load(_r_) только без _! Сайт р в скобках отображает, как ®
А как ты в диалоге собираешься medkit:esc_test_restrictor передать? А для спавн в тайник или нпс, я использую эту ф-ию:
Код
-- =give_items_nps(item:sid) function give_items_nps(actor, npc, p) local item = p and p[1] local to_obj = level_object_by_sid(p[2]) alife():create(item, to_obj:position(), to_obj:level_vertex_id(), to_obj:game_vertex_id(), to_obj:id()) end
Но тут передаётся sid, что не всегда удобно! Хотя можно использовать
Код
-- =name(name) function name(actor, npc, p) local obj = p[1] for a=10,65635,1 do local npc=level.object_by_id(a) if npc and string.find(npc:name(),obj) then *
и передавать имя.
Помогите разобраться с оверрайдами в секции [mob_home] mob_home.script:
---------------------------------------------------------------------------------------------------- -- Mob Home ---------------------------------------------------------------------------------------------------- -- Разработчик: Jim ----------------------------------------------------------------------------------------------------
local def_min_radius = 20 local def_max_radius = 40
class "mob_home"
---------------------------------------------------------------------------------------------------- -- CONSTRUCTION SCHEME ---------------------------------------------------------------------------------------------------- function mob_home:__init(obj, storage) self.object = obj self.st = storage end
---------------------------------------------------------------------------------------------------- -- RESET SCHEME ---------------------------------------------------------------------------------------------------- function mob_home:reset_scheme() --[[ Добавлено: Haron Описание: Теперь можно задавать радиусы home_min_radius и home_max_radius во флагах пути path_home. Для этого введены флаги minr, maxr. Они должны быть прописаны в первой точке пути. У радиусов заданых в секции приоритет больший чем для тех что заданы во флагах, т.е. если задано и там и там, то берутся значения из секции (сделано для обратной совместимости). --]] local minr, maxr = def_min_radius, def_max_radius local ptr = patrol(self.st.home) local path_info = utils.parse_waypoint_data(self.st.home, ptr:flags(0), ptr:name(0)) local r = 0
if self.st.home_min_radius then minr = self.st.home_min_radius else r = path_info.minr if r then r = tonumber® if r then minr = r end end end
if self.st.home_max_radius then maxr = self.st.home_max_radius else r = path_info.maxr if r then r = tonumber® if r then maxr = r end end end
-- check min and max radius if minr > maxr then _G.abort("Mob_Home : Home Min Radius MUST be < Max Radius. Got: min radius = %d, max radius = %d.", minr, maxr) end
self.object:set_home(self.st.home, minr, maxr, self.st.aggressive) end
if xr_logic.try_switch_to_another_section(self.object, self.st, db.actor) then return end
if mob_alife_mgr.alife_control(self.object, db.actor, self.st.alife_mode) then-- Добавил return-- Добавил end-- Добавил end
function mob_home:deactivate() self.object:remove_home() end
---------------------------------------------------------------------------------------------------- -- ADD_TO_BINDER ---------------------------------------------------------------------------------------------------- function add_to_binder(npc, ini, scheme, section, storage) printf("DEBUG: add_to_binder: npc:name()='%s', scheme='%s', section='%s'", npc:name(), scheme, section)
local new_action = mob_home(npc, storage)
-- Зарегистрировать все actions, в которых должен быть вызван метод reset_scheme при изменении настроек схемы: xr_logic.subscribe_action_for_events(npc, storage, new_action) end
---------------------------------------------------------------------------------------------------- -- SET_SCHEME ---------------------------------------------------------------------------------------------------- function set_scheme(npc, ini, scheme, section, gulag_name) local storage = xr_logic.assign_storage_and_bind(npc, ini, scheme, section)
Проверял все 4 оверрайда в разных вариантах\сочетаниях, и не один не сработал. ХЕЛП!!!
Добавлено (17.04.2014, 01:21) --------------------------------------------- Sanjaaa, На тот момент у меня был только один таймер, и писать доп ф-ию, которая будет запускать остальные, не было смысла. Если обратишь внимание, то ф-ия update запускает timer_start() и hud_timer(), а сама она висит на update(delta).
ЦитатаSanjaaa ()
А "r" обязательно маленькую,а то я большую поставил
ни когда не задавался этим вопросом, думаю это не критично. Сам всегда пишу маленькую. П.С. комментарии исправил.