Эмуляция новых слотов в ТЧ по скриптам от АМК
Материал из S.T.A.L.K.E.R. Inside Wiki
Многие хотят узнать, как сделать видимыми слоты, для бинокля, ножа, фонаря, дозиметра. Сегодня в этой статье, вы узнаете, как эмулировать эти слоты, сделать их видимыми и юзабельными, а так же, добавить новые.
Содержание
Предыстория
Мне вдруг стало интересно, как реализовать скрытые и сделать новые слоты в Тенях Чернобыля. Порыскав по просторам интернета, я нашел подобную реализацию у инвентаря от АМК, где в слоты можно было класть нож, бинокль и фонарь.
Методом проб и ошибок, я нашел, какие скрипты и конфиги за это отвечают.
Ну что-же, приступим к работе.
Файлы
Данные ниже файлы будут редактироваться.
scripts\bind_stalker.script
scripts\hidden_slots.script - новый файл!
config\misc\items.ltx
textures\ui\ui_inventory.dds - по желанию .
Скрипты
Начнем со скриптов. В папке scripts создаем файл hidden_slots.script, в котором и будут находится наши скрипты.
local rec_wnd = nil local initial = false local TBtn = {} local slots = {{825,300},{825,390},{825,480}} local sleep_static = 0 class "BkgrWnd" (CUIScriptWnd) function BkgrWnd:__init(owner) super() self.sleep_time = 0 self.owner = owner self:Init(0,0,1024,768) self.ClickBtn = {} self.ClickBtnSleep = function() if self.sleep_time ~= 0 then local open_menu = true for k,v in pairs(db.storage) do local stalker = level.object_by_id(k) if stalker and (get_clsid(stalker)==clsid.script_stalker) and stalker:alive() and stalker:relation(db.actor)==game_object.enemy then if stalker:position():distance_to(db.actor:position()) < 45 then open_menu = false break end end end if open_menu then level.start_stop_menu(sleep_dialog(self.sleep_time),true) else inventory_close() get_hud():AddCustomStatic("cant_sleep_near_enemies") sleep_static = time_global() + 5000 end end end self.ClickBtn[1] = function() local item = db.actor:object("wpn_knife") if item then spawn_item_in_inv("fake_wpn_knife") remove_item(item:id()) end TBtn["check_button_1"]:Show(false) end self.ClickBtn[2]=function() local item = db.actor:object("wpn_binoc") if item then spawn_item_in_inv("fake_wpn_binoc") remove_item(item:id()) end TBtn["check_button_2"]:Show(false) end self.ClickBtn[3]=function() local item = db.actor:object("device_torch") if item then spawn_item_in_inv("fake_device_torch") remove_item(item:id()) end TBtn["check_button_3"]:Show(false) end self:InitControls() end function BkgrWnd:__finalize() end function BkgrWnd:InitControls() clear_table(TBtn) self.bkgr_static=CUIStatic() self.bkgr_static:Init("ui\\ui_disk_io",10,0,1024,100) self.bkgr_static:SetStretchTexture(true) self.bkgr_static:SetOriginalRect(0,0,1024,100) self.owner:AttachChild(self.bkgr_static) self.stat = CUIStatic() self.stat:Init(0, 0, 1024, 768) self.owner:AttachChild(self.stat) for k,v in pairs (slots) do local name="check_button_"..k self.btn = CUIButton() self.btn:SetAutoDelete(false) self.btn:SetWindowName(name) self:Register(self.btn) self.stat:AttachChild(self.btn) TBtn[name]= self.btn init_btn(k) self:AddCallback(name,ui_events.WINDOW_LBUTTON_DB_CLICK,self.ClickBtn[k],self) end local ctrl local xml = CScriptXmlInit() xml:ParseFile("ui_addon.xml") end -- инициализация с учетом размеров иконки статика function init_btn(num,sec) local function sections(num) if num==1 then local item = db.actor:item_in_slot(0) if item then return "wpn_knife" else return end elseif num==2 then local item = db.actor:item_in_slot(4) if item then return "wpn_binoc" else return end elseif num==3 then local item = db.actor:item_in_slot(9) if item then return "device_torch" else return end end end local btn=TBtn["check_button_"..num] local sect = sec or sections(num) if sect then local ini=system_ini() local x=ini:r_u32(sect, "inv_grid_x")*50 local y=ini:r_u32(sect, "inv_grid_y")*50 local width=ini:r_u32(sect, "inv_grid_width")*50 local height=ini:r_u32(sect, "inv_grid_height")*50 btn:InitTexture("ui\\ui_icon_equipment") btn:SetOriginalRect(x,y,width,height) btn:Init(slots[num][1]-width/2,slots[num][2]-height/2,width,height) btn:Show(true) else btn:Init(slots[num][1],slots[num][2],0,0) end end function BkgrWnd:ShowWnd() if initial and rec_wnd.stat~= nil then rec_wnd.stat:Show(true) end end function BkgrWnd:HideWnd() if initial and rec_wnd.stat~= nil then rec_wnd.stat:Show(false) end end function BkgrWnd:DetachWnd() self.owner:DetachChild(self.stat) initial = false end function on_eat(sect) if sect=="fake_wpn_knife" then if db.actor:object("wpn_knife") then spawn_item_in_inv("fake_wpn_knife") else spawn_item_in_inv("wpn_knife") init_btn(1,"wpn_knife") end end if sect=="fake_wpn_binoc" then if db.actor:object("wpn_binoc") then spawn_item_in_inv("fake_wpn_binoc") else spawn_item_in_inv("wpn_binoc") init_btn(2,"wpn_binoc") end end if sect=="fake_device_torch" then if db.actor:object("device_torch") then spawn_item_in_inv("fake_device_torch") else spawn_item_in_inv("device_torch") init_btn(3,"device_torch") end end end function spawn_item_in_inv(sect) alife():create(sect,db.actor:position(),db.actor:level_vertex_id(),db.actor:game_vertex_id(),0) end function remove_item(id) if id then local sobj = alife():object(id) if sobj then alife():release(sobj,true) end end end local inv_window function on_info(info_id) if info_id=="ui_inventory" then inv_window = level.main_input_receiver() if not initial then rec_wnd = BkgrWnd(level.main_input_receiver()) end if not rec_wnd.stat:IsShown() then rec_wnd:ShowWnd() end elseif info_id=="ui_inventory_hide" then if rec_wnd then rec_wnd:DetachWnd() end end end function inventory_close() if inv_window and inv_window:IsShown() then level.start_stop_menu(inv_window,true) end end local id,sect function on_item_drop(obj) sect = obj:section() if string.sub(sect,1,5)=="fake_" then id = obj:id() end end function update() if id and not level.object_by_id(id) then on_eat(sect) id = nil sect = nil end if get_hud():GetCustomStatic("cant_sleep_near_enemies") ~= nil then if sleep_static < time_global() then get_hud():RemoveCustomStatic("cant_sleep_near_enemies") end end end
Теперь нужно указать наш файл в bind_stalker. После
function actor_binder:info_callback(npc, info_id) printf("*INFO*: npc='%s' id='%s'", npc:name(), info_id) --' Сюжет level_tasks.proceed(self.object) -- Отметки на карте level_tasks.process_info_portion(info_id)
вписываем
hidden_slots.on_info(info_id)
Чтобы получилось
function actor_binder:info_callback(npc, info_id) printf("*INFO*: npc='%s' id='%s'", npc:name(), info_id) --' Сюжет level_tasks.proceed(self.object) -- Отметки на карте level_tasks.process_info_portion(info_id) hidden_slots.on_info(info_id) end
Далее после
function actor_binder:on_item_drop (obj) level_tasks.proceed(self.object) --game_stats.update_drop_item (obj, self.object)
вписываем
hidden_slots.on_item_drop(obj)
чтобы получилось так
function actor_binder:on_item_drop (obj) level_tasks.proceed(self.object) --game_stats.update_drop_item (obj, self.object) hidden_slots.on_item_drop(obj) end
ну и последняя вписка после
if self.bCheckStart then printf("SET DEFAULT INFOS") if not has_alife_info("storyline_actor_start") and (level.name() == "l01_escape") then self.object:give_info_portion("storyline_actor_start") _G.g_start_avi = true printf("*AVI* RUN START AVI") end -- if not has_alife_info("encyclopedy") then -- self.object:give_info_portion("encyclopedy") -- end if not has_alife_info("global_dialogs") then self.object:give_info_portion("global_dialogs") end if not has_alife_info("level_changer_icons") then self.object:give_info_portion("level_changer_icons") end level_tasks.add_lchanger_location() self.bCheckStart = false end
вот такая
hidden_slots.update()
И выходит
if self.bCheckStart then printf("SET DEFAULT INFOS") if not has_alife_info("storyline_actor_start") and (level.name() == "l01_escape") then self.object:give_info_portion("storyline_actor_start") _G.g_start_avi = true printf("*AVI* RUN START AVI") end -- if not has_alife_info("encyclopedy") then -- self.object:give_info_portion("encyclopedy") -- end if not has_alife_info("global_dialogs") then self.object:give_info_portion("global_dialogs") end if not has_alife_info("level_changer_icons") then self.object:give_info_portion("level_changer_icons") end level_tasks.add_lchanger_location() self.bCheckStart = false end hidden_slots.update() end
Со скриптами закончили, осталось за малым.
Конфиги
Перейдем к созданию так называемых "фейковых" предметов, которвые указаны в строчках нашего скрипта
function on_eat(sect) if sect=="fake_wpn_knife" then if db.actor:object("wpn_knife") then spawn_item_in_inv("fake_wpn_knife") else spawn_item_in_inv("wpn_knife") init_btn(1,"wpn_knife") end end if sect=="fake_wpn_binoc" then if db.actor:object("wpn_binoc") then spawn_item_in_inv("fake_wpn_binoc") else spawn_item_in_inv("wpn_binoc") init_btn(2,"wpn_binoc") end end if sect=="fake_device_torch" then if db.actor:object("device_torch") then spawn_item_in_inv("fake_device_torch") else spawn_item_in_inv("device_torch") init_btn(3,"device_torch") end end end
Обратите внимание, в конфиге и скрипте предметы должны называться одинаково, так-как скрипт отсылается к именно этим айтемам. Добавляем ниже написанное в config\misc\items.ltx в самый конец.
[fake_device_torch]:antirad visual = equipments\light_night.ogf description = enc_equipment_devic_device-torch1 inv_name = device-torch inv_name_short = device-torch cost = 200 inv_weight = 0.2 inv_grid_width = 2 inv_grid_height = 1 inv_grid_x = 16 inv_grid_y = 12 eat_health = 0 eat_satiety = 0 eat_power = 0 eat_radiation = 0 eat_alcohol = 0 wounds_heal_perc = 0 [fake_wpn_binoc]:antirad visual = weapons\binoculars\wpn_binoculars.ogf description = enc_equipment_devic_binocul1 inv_name = binocular inv_name_short = binocular cost = 400 inv_weight = 1.0 inv_grid_width = 2 inv_grid_height = 1 inv_grid_x = 16 inv_grid_y = 8 eat_health = 0 eat_satiety = 0 eat_power = 0 eat_radiation = 0 eat_alcohol = 0 wounds_heal_perc = 0 [fake_wpn_knife]:antirad visual = weapons\knife\wpn_knife.ogf inv_name = Нож inv_name_short = Нож description = enc_weapons1_wpn-knife cost = 500 inv_weight = 0.3 inv_grid_width = 2 inv_grid_height = 1 inv_grid_x = 18 inv_grid_y = 8 eat_health = 0 eat_satiety = 0 eat_power = 0 eat_radiation = 0 eat_alcohol = 0 wounds_heal_perc = 0
Эти предметы отличаются от оригинала, они служат лишь для того, чтобы отметить, лежит у нас бинокль или нож в слоте, или нет, и являются обыкновенными айтемами.
Текстуры
Так-как расположение предметов в инвентаре вы можете регулировать сами, изменять текстуры вы будете тоже своими руками, как захотите. Текстура инвентаря находится по адресу textures\ui\ui_inventory.dds
Если у вас стоят оригинальные текстуры, наши слоты находятся немного левее ГГ в костюме, сверху вниз.
Авторы
DanilPankovski - Создатель статьи, подробный разбор.
Charsi - Создатель скрипта.