Обход ограничений mp ranks.ltx (метод рекрита) — S.T.A.L.K.E.R. Inside Wiki

Обход ограничений mp ranks.ltx (метод рекрита)

Материал из S.T.A.L.K.E.R. Inside Wiki

Перейти к: навигация, поиск

Многие модмейкеры, которые создают апгрейд оружия, да и вообще новое оружие, в особенности улучшенное, сталкиваются с проблемой переполнения количества байт информации в секции файла mp_ranks.ltx. Я также не исключение, при составлении скрипта апгрейда оружия все шло отлично, тест также прошел на 5, но когда апгрейд стал подходить к концу, то есть когда необходимо было прописать все апгрейды оружия в ранги, произошел вылет. А прописал всего апгрейд только для одного оружия. Но из всякой проблемы можно найти логический выход. Предлагаю свой, он был разработан не только мной но и моим коллегой spark95, на базе проекта S.I.P. Этот метод называется «рекрит» (REcreate) работает без вылетов на 99%.


Алгоритм

Для начала необходимо продумать, что и как, когда и при каких условиях будет работать скрипт. Так как мы его разрабатывали для нашего мода, то примеры и все составляющие будут полностью либо частично взяты из модификации.

1. Объект выбрасывается.

2. Подбирается НПС.

И если оружие не прописано в рангах, то вылет. Чтобы НПС подбирал то оружие, которое прописано в рангах, нужно удалить непрописанное выбрасываемое оружие и заспавнить на его месте точно такое же, но прописанное в рангах, причем визуал и основные характеристики должны соответствовать тому выбрасываемому оружию (дабы не нарушить игровой процесс и симуляцию). Такой способ можно достичь путем снятия нет-пакета с выбрасываемого оружия и записи его на такое же оружие но не апгрейдное. Для начала отследим само выбрасывание. Откроем bind_stalker.script и напишем функцию с условием, при котором будет производиться удаление:

 
local sip_b = 1 -– переменная, при определении которой будет
                -- производиться действие, по дефолту 1
function sip_bool(flag) –- функция, которая запускается сторонней функцией
                        -- из другого скрипта, дабы не совершить вылета, аргумент = число
	if flag == 1 then
	sip_b = 1
	else sip_b = 0
	end
end
 

Операция присвоения производится соответственно, дабы не спутать. Ищем:

 
function actor_binder:on_item_drop (obj)
    level_tasks.proceed(self.object)
end
 

Эта функция отслеживает выбрасывание чего-либо из инвентаря ГГ. Изменим так:

 
function actor_binder:on_item_drop (obj)
    level_tasks.proceed(self.object)
	if sip_b == 1 then
	project_sip.delete_weapon(obj)
	end
end
 

Что позволит обойти тот момент записи нет-пакета в несуществующий объект. Далее создаем скрипт project_sip.script и пишем в него следующее:

 
local sects = {}
local tab_id = {}
local tab_old_id = {}
local now_wpn_id
local sect_old
local sect –- присвоим позже оригинальную секцию оружия,
           -- которая в последующем будет использована для спавна
local orig_sect –- переменной будет присвоена секция оружия 
local count = 0
local num
local new_id
local name –- будет присвоено оригинальное название оружия
function delete_weapon(weapon) -– функция удаления оружия, аргумент сам объект 
--таблица оружия, которое прописано в рангах
local wpn_tab = {"wpn_abakan","wpn_rpg7","wpn_ak74","wpn_fn2000","wpn_g36","wpn_groza","wpn_l85",
"wpn_lr300","wpn_mp5","wpn_rpg7","wpn_sig550","wpn_val","wpn_spas12","wpn_toz34","wpn_wincheaster1300",
"wpn_svd","wpn_svu","wpn_vintorez","wpn_beretta","wpn_colt1911","wpn_desert_eagle",
"wpn_fort","wpn_hpsa","wpn_pb","wpn_pm","wpn_sig220","wpn_walther"}
 
local wpn = weapon:section()
orig_sect = wpn
local pos_act  = db.actor:position()
pos_act.x = pos_act.x + 0.3 –- оружие будет спавниться не в инвентарь к ГГ,
                            -- а в 30см от него, ибо при спавне в инвентарь нет-пакет в оружие не записывается.
-- ниже проверяем, что оружие действительно апгрейдное (в данном случае идет проверка
-- не только на то что оружие апгрейдное, но еще и на то, что оружие имеет секцию смены визуала)
	if string.find(wpn,"list") or string.find(wpn,"prugina") or string.find(wpn,"tormoz")
           or string.find(wpn,"sv1") or string.find(wpn,"sv2") or string.find(wpn,"sv3") or string.find(wpn,"sv4") then
		for k = 1, #wpn_tab do –- запускаем цикл на выявление оригинала оружия (прописанного в рангах)
		name = k
		if string.find(wpn,wpn_tab[name]) then
		sect = wpn_tab[name]
		break –- если условие выполняется, то преждевременно останавливаем выполнение цикла
		end
		end
-- ниже происходит запись в таблицы данных о секциях
	table.insert(sects,orig_sect)
	table.insert(tab_old_id,weapon:id())
-- производим снятие нет-пакета, для этого подготовливаем все условия для этого
	local n_obj = alife():object(weapon:id()) –- определяем объект
	local func_net = net_pack_sip.wpn_d_r(n_obj) –- снимаем нет-пакет объекта
	alife():release(n_obj,true)
        –- присвоим, но не заспавним объект
	local sobj = alife():create(sect,pos_act,db.actor:level_vertex_id(),db.actor:game_vertex_id())
		if sobj then
		table.insert(tab_id,sobj.id) –- запишем id  оружия в таблицу для дальнейшей работы с ним
		net_pack_sip.wpn_d_w(func_net,sobj) –- запишем на это оружие весь нет-пакет удаленного оружия
		end
	end
	return sobj –- заспавним оружие
end
 

Вот, практически, можно сказать что все, однако когда ГГ возьмет это оружие, то оно уже не будет таким каким оно было до выбрасывания!!! Исправим эту ситуацию. В биндере найдем следующий код:

 
function actor_binder:on_item_take (obj)
    level_tasks.proceed(self.object)
end
 

и изменим так:

 
function actor_binder:on_item_take (obj)
    level_tasks.proceed(self.object)
	project_sip.spawn_old_wpn(obj)
end
 

Эта функция отслеживает момент взятия ГГ какого-либо объекта, аргумент сам объект. В project_sip.script пропишем следующий код:

 
function spawn_old_wpn(obj)
	for k = 1, #tab_id do –- запускаем цикл на вычисление идетификатора оружия,
                              -- которое было создано в момент удаления апгрейдного оружия (секция которого была записана ранее)
		if obj:id() == tab_id[k] then 
		num = k
		now_wpn_id = tab_id[num]	-- получаем id объекта	
		sect_old = sects[num] –- получаем секцию удаленного апгрейдного оружия
		spawn_del_weap(sect_old,now_wpn_id) –- запускаем функцию снятия и записи нет-пакета в новое оружие,
                                                    -- дабы не нарушить симуляцию
		break
		end
	end
end
-- прототип данной функции не требует комментария, ибо расписывался ранее
function spawn_del_weap(sect_old,now_wpn_id)
local pos_act  = db.actor:position()
pos_act.x = pos_act.x + 0.3
local n_obj = alife():object(now_wpn_id)
local func_net = net_pack_sip.wpn_d_r(n_obj)
alife():release(n_obj,true)
local sobj = alife():create(sect_old,pos_act,db.actor:level_vertex_id(),db.actor:game_vertex_id())
	if sobj then
	new_id = sobj.id – записываем id оружия для перевода его к ГГ
	net_pack_sip.wpn_d_w(func_net,sobj)
	end
return sobj
end
 
function transfer_new_wpn() –- данная функция предназначена для перевода нового заспавненго оружия (апгрейдного к ГГ)
	if new_id ~= nil then
	local wpn = level.object_by_id(new_id)
		if wpn then
		db.actor:transfer_item(wpn,db.actor)
		new_id = nil
		end
	else return
	end
end
 

Далее в биндере найдем:

 
function actor_binder:update(delta)
	object_binder.update(self, delta)
 

и ниже напишем:

 
project_sip.transfer_new_wpn() – для перевода оружия к ГГ
 

скрипт net_pack_sip.script, в котором напишем следующий код:

 
function wpn_d_r(sobj)
local sv_sip = net_packet()
sobj:STATE_Write(sv_sip)
local size = sv_sip:w_tell()
sv_sip:r_seek(0)
local tab_config = {object_pack = {},visual_pack = {},item_pack = {},item_wpn_packet = {}}
 
local function up_vis_pack(visual_pack,sv_sip,size)
visual_pack.visual_name = sv_sip:r_stringZ()
visual_pack.vsu8u1 = sv_sip:r_u8()
	return visual_pack
end
 
	tab_config.object_pack.gvid = sv_sip:r_u16()
	tab_config.object_pack.obf32u1 = sv_sip:r_float()
	tab_config.object_pack.obs32u2 = sv_sip:r_s32()
	tab_config.object_pack.lvid = sv_sip:r_s32()
	tab_config.object_pack.oflags = sv_sip:r_s32()
	tab_config.object_pack.custom = sv_sip:r_stringZ()
	tab_config.object_pack.sid = sv_sip:r_s32()
	tab_config.object_pack.obs32u3 = sv_sip:r_s32()
 
	up_vis_pack(tab_config.visual_pack,sv_sip,size)
 
	tab_config.item_pack.condition = sv_sip:r_float()
 
	tab_config.item_wpn_packet.ammo_current = sv_sip:r_u16()
	tab_config.item_wpn_packet.ammo_elapsed = sv_sip:r_u16()
	tab_config.item_wpn_packet.weapon_state = sv_sip:r_u8()
	tab_config.item_wpn_packet.addon_flags = sv_sip:r_u8()
	tab_config.item_wpn_packet.ammo_type = sv_sip:r_u8()
 
		return tab_config
end
 
function wpn_d_w(tab_config,sobj)
local sv_sip = net_packet()
local function vis_pack_rd(visual_pack,sv_sip)
sv_sip:w_stringZ(visual_pack.visual_name)
sv_sip:w_u8(visual_pack.vsu8u1)
	return visual_pack
end
 
	sv_sip:w_u16(tab_config.object_pack.gvid)
	sv_sip:w_float(tab_config.object_pack.obf32u1)
	sv_sip:w_s32(tab_config.object_pack.obs32u2)
	sv_sip:w_s32(tab_config.object_pack.lvid)
	sv_sip:w_s32(tab_config.object_pack.oflags)
	sv_sip:w_stringZ(tab_config.object_pack.custom)
	sv_sip:w_s32(tab_config.object_pack.sid)
	sv_sip:w_s32(tab_config.object_pack.obs32u3)
 
	vis_pack_rd(tab_config.visual_pack,sv_sip)
 
	sv_sip:w_float(tab_config.item_pack.condition)
 
	sv_sip:w_u16(tab_config.item_wpn_packet.ammo_current)
	sv_sip:w_u16(tab_config.item_wpn_packet.ammo_elapsed)
	sv_sip:w_u8(tab_config.item_wpn_packet.weapon_state)
	sv_sip:w_u8(tab_config.item_wpn_packet.addon_flags)
	sv_sip:w_u8(tab_config.item_wpn_packet.ammo_type)
 
	sv_sip:w_u16(tab_config.object_pack.gvid)
	sv_sip:w_float(tab_config.object_pack.obf32u1)
	sv_sip:w_s32(tab_config.object_pack.obs32u2)
	sv_sip:w_s32(tab_config.object_pack.lvid)
	sv_sip:w_s32(tab_config.object_pack.oflags)
	sv_sip:w_stringZ(tab_config.object_pack.custom)
	sv_sip:w_s32(tab_config.object_pack.sid)
	sv_sip:w_s32(tab_config.object_pack.obs32u3)
 
local size = sv_sip:w_tell()
sv_sip:r_seek(0)
sobj:STATE_Read(sv_sip,size)
end
 

Работа с нет-пакетами будет описана в отдельной статье, объясню лишь общий смысл предназначения нет-пакета. Он предназначен для хранения состояния объекта и его характеристик, и по сути своей является буфером обмена, в котором и хранится вся эта информация, в последующем действии идет запись по определенным таблицам каждого вида характеристик. Во второй функции происходит запись того снятого состояния объекта к новосозданному. Эта операция напоминает алгоритм спавна all.spawn и неспроста! Ибо при статистичном спавне через скрипт объекту также присваивается состояние, которое записано как дефолтовое, а спавн через нет-пакет предусматривает широкий спектр выбора характеристик спавненного оружия что в положительную сторону влияет на симуляцию. Ну вот в общем-то и все, теперь объясню, почему этот метод рекрита работает на 99%. Потому что на выполнение этих всех процессов требуется время, сам биндер запускается с периодом примерно 0,5 сек. (точно не помню), значит, чтобы отследить либо выбрасывание оружия, либо наоборот, его взятие нужен один период биндера, далее также необходим второй период для перевода нового заспавненного оружия к ГГ, итого не для выполнения всех этих действий требуется как минимум 0,8 сек. Почему не переводится при первом периоде биндера, мне так и не удалось выяснить, но факт есть факт, соответственно, если подойти вплотную к НПС и выбросить оружие, то произойдет вылет, если же произвести выбрасывание на некотором расстоянии, к примеру на 10 см, то вылета не будет. Нагрузка на оперативную память незначительна, так что можно смело использовать. В планах совершенствование метода рекрита и доведение его работоспособности до 99,9%.


Авторы

Byurrer, spark95

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