Эмуляция новых слотов в ТЧ по скриптам от АМК — S.T.A.L.K.E.R. Inside Wiki

Эмуляция новых слотов в ТЧ по скриптам от АМК

Материал из 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 - Создатель скрипта.

Другие места
LANGUAGE
Here