function init() rank_unit = 1/ranks.get_rank_interval(get_rank_list()[#get_rank_list()])[1] init_clsid() end --------------------------------------ini--------------------------------------- local sys_ini = system_ini() function read_from_ini(ini,sec,lin,def,typ) if not (sec and lin) then ABORT("read_from_ini: ini [%s] sec [%s] lin [%s] def [%s] typ [%s]",ini~=nil,sec,lin,def,typ) end if not ini then ini = sys_ini end if ini:line_exist(sec,lin) then if typ == 1 then return ini:r_string(sec,lin) or def elseif typ == nil then return ini:r_float(sec,lin) or def elseif typ == 0 then local r = ini:r_bool(sec,lin) return r == nil and def or r elseif typ == 2 then return ini:r_string_wq(sec,lin) or def elseif typ == 3 then return ini:r_s32(sec,lin) or def elseif typ == 4 then return ini:r_vector(sec,lin) or def end end return def end function collect_sections(ini,sections,vfunc,dval,convert) if vfunc == true then vfunc = tonumber end local r,p,c = {},{},{} for k,v in ipairs(sections) do if ini:section_exist(v) then local n = ini:line_count(v) if n > 0 then for i = 0,n-1 do local res,id,val = ini:r_line(v,i,"","") if r[id] == nil then r[id] = dval or vfunc and vfunc(val) or val if convert ~= nil then table.insert(c,convert == false and r[id] or id) end end end end p[k] = n else p[k] = 0 end end if convert ~= nil then return c,r end return r,p end function parse_list(ini,sec,val,convert,vfunc) local tmp = str_explode(",",read_from_ini(ini,sec,val,"",1)) if vfunc then if vfunc == true then vfunc = tonumber end for i,v in ipairs(tmp) do local res = vfunc(v) if res ~= nil then tmp[i] = res end end end if convert then local swap,func = convert == -1,type(convert) == "function" local t = {} for i,v in ipairs(tmp) do t[v] = swap and i or func and convert(v,i) or convert end return t,tmp end return tmp end local ranksl function get_rank_list() if ranksl then return ranksl end local tmp = parse_list(nil,"game_relations","rating") ranksl = {} for i,rn in ipairs(tmp) do if not tonumber(rn) then table.insert(ranksl,rn) end end return ranksl end local communitiesl function get_communities_list() if communitiesl then return communitiesl end local tmp = parse_list(nil,"game_relations","communities") communitiesl = {} for i,cn in ipairs(tmp) do if not tonumber(cn) then table.insert(communitiesl,cn) end end return communitiesl end rank_unit = 0.01 --------------------------------------str--------------------------------------- function trim(s) return (string.gsub(s,"^%s*(.-)%s*$","%1")) end function str_explode(div,str) local t = {} for sect in str:gmatch('[^%s*%'..div..']+') do if sect and sect ~= "" then table.insert(t, sect) end end return t end function vec_to_str(vec) if vec == nil then return "" end return string.format("[%s:%s:%s]",vec.x,vec.y,vec.z) end local function format_safe(str,...) local i,args = 0,{...} local f = function() i = i + 1 local a = args[i] if a == nil then return "" elseif type(a) == "userdata" then if a.setHP then return vec_to_str(a) elseif a.name then return ":"..a:name() end return "" end return tostring(a) end return string.gsub(str,"%%s",f) end ---------------------------------------------------------------------------------- function count_table(t) local cnt = 0 for k,v in pairs(t) do cnt = cnt+1 end return cnt end function transform_tiny(m,v) local res = vector() res.x = m.i.x * v.x + m.k.x * v.z + m.j.x * v.y + m.c.x; res.y = m.k.y * v.z + m.j.y * v.y + m.i.y * v.x + m.c.y; res.z = m.k.z * v.z + m.j.z * v.y + m.i.z * v.x + m.c.z; return res end function random_choice(...) local args = {...} if #args == 1 then return args[1][math.random(#args[1])] end return args[math.random(#args)] end --------------------------------------class--------------------------------------- actor_clsid = clsid.script_actor creatures_clslist = { actor = actor_clsid, stalker = clsid.script_stalker, dog = clsid.dog_s, boar = clsid.boar_s, flesh = clsid.flesh_s, pseudodog = clsid.pseudodog_s, bloodsucker = clsid.bloodsucker_s, snork = clsid.snork_s, tushkano = clsid.tushkano_s, zombie = clsid.zombie_s, giant = clsid.gigant_s, chimera = clsid.chimera_s, burer = clsid.burer_s, controller = clsid.controller_s, poltergeist = clsid.poltergeist_s, fracture = clsid.fracture_s, cat = clsid.cat_s, psy_dog = clsid.psy_dog_s, psy_dog_phantom = clsid.psy_dog_phantom_s } local firearm_clst function item_is_fa(o,c) if not c then c = o and o:clsid() end if c and firearm_clst[c] then return true end return false end local knife_cls function item_is_knife(o,c) if not c then c = o and o:clsid() end return c == knife_cls end local grenade_clst function item_is_grenade(o,c) if not c then c = o and o:clsid() end if c and grenade_clst[c] then return true end return false end local blaster_clst function item_is_blaster(o,c) if not c then c = o and o:clsid() end if c and blaster_clst[c] then return true end return false end local ammo_clst function item_is_ammo(o,c) if not c then c = o and o:clsid() end if c and ammo_clst[c] then return true end return false end function is_actor(o,c) if not c then c = o and o:clsid() end return c == actor_clsid end local anom_clst function is_anomaly(o,c) if not c then c = o and o:clsid() end if c and anom_clst[c] then return true end return false end function is_story_object(game_obj) return game_obj:story_id() < 4294967296 end function init_clsid() firearm_clst = { [clsid.wpn_pm_s] = true, [clsid.wpn_walther_s] = true, [clsid.wpn_usp45_s] = true, [clsid.wpn_hpsa_s] = true, [clsid.wpn_bm16_s] = true, [clsid.wpn_shotgun_s] = true, [clsid.wpn_ak74_s] = true, [clsid.wpn_lr300_s] = true, [clsid.wpn_groza_s] = true, [clsid.wpn_val_s] = true, [clsid.wpn_vintorez_s] = true, [clsid.wpn_svu_s] = true, [clsid.wpn_svd_s] = true, [clsid.wpn_rg6_s] = true, [clsid.wpn_rpg7_s] = true, -- [clsid.wpn_knife_s] = true, } blaster_clst = { [clsid.wpn_rg6_s] = true, [clsid.wpn_rpg7_s] = true } grenade_clst = { [clsid.wpn_grenade_f1] = true, [clsid.wpn_grenade_rgd5] = true } ammo_clst = { [clsid.wpn_ammo] = true, [clsid.wpn_ammo_vog25] = true, [clsid.wpn_ammo_m209] = true, [clsid.wpn_ammo_og7b] = true } anom_clst = { -- [clsid.ameba_zone] = true, [clsid.torrid_zone] = true, [clsid.zone_acid_fog] = true, [clsid.zone_bfuzz] = true, [clsid.zone_bfuzz_s] = true, [clsid.zone_dead] = true, [clsid.zone_galant_s] = true, [clsid.zone_galantine] = true, [clsid.zone_mbald_s] = true, [clsid.zone_mincer] = true, [clsid.zone_mincer_s] = true, [clsid.zone_mosquito_bald] = true, -- [clsid.zone_radioactive] = true, [clsid.zone_rusty_hair] = true, -- [clsid.zone_torrid_s] = true } knife_cls = clsid.wpn_knife_s end --------------------------------------sch--------------------------------------- function add_anim(npc,anim,hand_usage,use_movement_controller) if anim then npc:add_animation(anim,hand_usage==true,use_movement_controller==true) end end function get_sound(snd) if snd then return sound_object(snd) end end --------------------------------------net------------------------------------------ local stpk = net_packet() local nph,npt -- кэширование для менеджера оружия function get_weapon_data(sobj) if not sobj then return {} end if time_global() == npt then if nph[sobj] then return nph[sobj] end else nph = {} npt = time_global() end stpk:w_begin(0) sobj:STATE_Write(stpk) local size = stpk:w_tell() stpk:r_seek(2) local t = {} parse_object_packet(t,stpk) parse_visual_packet(t,stpk) parse_item_packet(t,stpk) parse_item_weapon_packet(t,stpk) nph[sobj] = t return t end function parse_object_packet(ret,stpk) ret.gvid = stpk:r_u16() ret.obf32u1 = stpk:r_float() ret.obs32u2 = stpk:r_s32() ret.lvid = stpk:r_s32() ret.oflags = stpk:r_s32() ret.custom = stpk:r_stringZ() ret.sid = stpk:r_s32() ret.obs32u3 = stpk:r_s32() return ret end function parse_visual_packet(ret,stpk) ret.visual = stpk:r_stringZ() ret.vsu8u1 = stpk:r_u8() return ret end function parse_item_packet(ret,stpk) ret.condition = stpk:r_float() ret.upgrades = readvu32stringZ(stpk) return ret end function parse_item_weapon_packet(ret,stpk) ret.ammo_current = stpk:r_u16() ret.ammo_elapsed = stpk:r_u16() ret.weapon_state = stpk:r_u8() ret.addon_flags = stpk:r_u8() ret.ammo_type = stpk:r_u8() ret.xz1 = stpk:r_u8() return ret end function readvu8uN(packet,n) local v = {} for i=1,n,1 do table.insert(v,packet:r_u8()) end return v end function set_weapon_data(t,sobj) if sobj then stpk:w_begin(0) fill_object_packet(t,stpk) fill_visual_packet(t,stpk) fill_item_packet(t,stpk) fill_item_weapon_packet(t,stpk) local size = stpk:w_tell() stpk:r_seek(2) sobj:STATE_Read(stpk,size) end end function fill_object_packet(ret,stpk) stpk:w_u16(ret.gvid) stpk:w_float(ret.obf32u1) stpk:w_s32(ret.obs32u2) stpk:w_s32(ret.lvid) stpk:w_s32(ret.oflags) stpk:w_stringZ(ret.custom) stpk:w_s32(ret.sid) stpk:w_s32(ret.obs32u3) end function fill_visual_packet(ret,stpk) stpk:w_stringZ(ret.visual) stpk:w_u8(ret.vsu8u1) end function fill_item_packet(ret,stpk) stpk:w_float(ret.condition) writevu32stringZ(stpk,ret.upgrades) return ret end function fill_item_weapon_packet(ret,stpk) stpk:w_u16(ret.ammo_current) stpk:w_u16(ret.ammo_elapsed) stpk:w_u8(ret.weapon_state) stpk:w_u8(ret.addon_flags) stpk:w_u8(ret.ammo_type) stpk:w_u8(ret.xz1) return ret end function writevu8uN(pk,v) local len = table.getn(v) for i=1,len,1 do pk:w_u8(v[i]) end end function readvu32stringZ(packet) local v = {} local len = packet:r_s32() for i=1,len,1 do table.insert(v,packet:r_stringZ()) end return v end function writevu32stringZ(pk,v) if v == nil then v = {} end local len = #v pk:w_s32(len) for i=1,len,1 do pk:w_stringZ(v[i]) end end --------------------------------------weapon--------------------------------------- game_object.get_wm = function (npc,enabled_only) local wm = rx_ai.get_storage(npc:id()).wm if wm and not (enabled_only and wm.disabled) then return wm end end function get_weapon(npc) local wm = rx_ai.get_storage(npc:id()).wm return wm and wm:get_weapon() or npc:best_weapon() end function get_ammo_type(wpn,id) return get_weapon_data(alife():object(id or wpn:id())).ammo_type or 0 end --[[ function in_grenade_mode(wpn) local pak = get_weapon_w_gl_full_data(alife():object(wpn:id())) if bit_and(pak.addon_flags,2) == 2 then return pak.updgrenade_mode ~= 0 end return false end]] EWeaponStates = { eFire = 5, eFire2 = 6, eReload = 7, eMisfire = 8, eMagEmpty = 9, eSwitch = 10 } function get_weapon_state(wpn,id) return get_weapon_data(alife():object(id or wpn:id())).weapon_state end function get_mag_size(section) return (rx_wmgr and not rx_wmgr.wm_modes.forbiddens[section] and rx_wmgr.read_wpn_params(section).mag) or read_from_ini(nil,section,"ammo_mag_size",1,3) end function get_wpn_type(section) return (rx_wmgr and not rx_wmgr.wm_modes.forbiddens[section] and rx_wmgr.read_wpn_params(section).typ) or read_from_ini(nil,section,"ef_weapon_type",0,3) end --------------------------------------addons--------------------------------------- function get_addon_status(wpn,addon) if addon == "sc" then return wpn:weapon_scope_status() elseif addon == "sl" then return wpn:weapon_silencer_status() elseif addon == "gl" then return wpn:weapon_grenadelauncher_status() end return 0 end function get_addon_flag(wpn,with_int) local flag = 0 if wpn:weapon_is_scope() then if with_int or get_addon_status(wpn,"sc") == 2 then flag = 1 end end if wpn:weapon_is_silencer() then if with_int or get_addon_status(wpn,"sl") == 2 then flag = flag+4 end end if wpn:weapon_is_grenadelauncher() then if with_int or get_addon_status(wpn,"gl") == 2 then flag = flag+2 end end return flag end local ft = {sc = 1,sl = 4,gl = 2} function addon_attached(wpn,addon,flag) if ft[addon] then return bit_and(flag or get_addon_flag(wpn,true),ft[addon]) == ft[addon] end end --------------------------------------ai----------------------------------------- function cover_in_direction(lvid,dir,bs) if bs == move.crouch then return level.low_cover_in_direction(lvid,dir) else return level.high_cover_in_direction(lvid,dir) end end function npc_in_cover(npc,stand) local st = rx_ai.get_storage(npc:id(),"in_cover") local be,de = npc:best_enemy(),db.storage[npc:id()].danger_flag if (be and not xr_wounded.is_wounded(be) or de) then if stand and not npc:path_completed() then st.evn = false return false end else return true end local tg = time_global() if (st.wait or 0) > tg then return st.evn end local enemies,tt = {},{} if be and not xr_wounded.is_wounded(be) then enemies[1] = be tt[be:id()] = true else -- в отсутствие врага проверяем угрозу if not be and de and npc:best_danger() then local bd = npc:best_danger() local dir = bd:position():sub(npc:position()) if dir:magnitude() < (bd:type() == danger_object.grenade and 20 or 8) or 1-cover_in_direction(npc:level_vertex_id(),dir,npc:body_state()) < 0.3 then st.evn = false st.wait = tg+3000 return false end end st.evn = true st.wait = tg+2000 return true end for o in npc:memory_visible_objects() do local obj = o:object() if obj then local id = obj:id() if not tt[id] and obj:alive() and npc:relation(obj) == game_object.enemy and (IsStalker(obj) and not xr_wounded.is_wounded(obj) or IsMonster(obj)) then table.insert(enemies,obj) tt[id] = true end end end for o in npc:memory_sound_objects() do local obj = o:object() if obj and obj:alive() and not tt[obj:id()] and npc:relation(obj) == game_object.enemy and (IsStalker(obj) and not xr_wounded.is_wounded(obj) or IsMonster(obj)) then table.insert(enemies,obj) end end local npc_lvid,npc_id,bs = npc:level_vertex_id(),npc:id(),npc:body_state() local f = 28 for i,enemy in ipairs(enemies) do if IsMonster(enemy) then local dist = enemy:position():distance_to_sqr(npc:position()) local ebe = enemy:get_enemy() if (dist < 12*12 and enemy:see(npc)) or (dist < 50*50 and ebe and ebe:id() == npc_id) then st.evn = false st.wait = tg+5000 return false end else local dir = enemy:position():sub(npc:position()) local dist = dir:magnitude() local seez = npc:see(enemy) and enemy:see(npc) -- НПС знает, что враг его видит local cover = cover_in_direction(npc_lvid,dir,bs) if seez or dist < 7 or cover > 0.8 or dist*cover > dist-f*cover then -- чем дальше враг, тем меньше требуется прикрытия st.evn = false st.wait = tg+2500 return false end end end st.evn = true st.wait = tg+400 return true end function actor_aiming_at_me(npc,df) local aim_dir = device().cam_dir local my_dir = npc:center():sub(device().cam_pos) local aH,aP = aim_dir:getH(),aim_dir:getP() local fH,fP = my_dir:getH(),my_dir:getP() local f = 0.03+(df or 0.5)/my_dir:magnitude() if (aH > fH and aH-fH or fH-aH) < f and (aP > fP and aP-fP or fP-aP) < f*2 then return true end end function eat_medkit(npc,medkit) local health = read_from_ini(nil,medkit:section(),'npc_eat_health') if health then npc.health = health end npc:eat(medkit) end function IsTrader(npc) local st = rx_ai.get_storage(npc:id()) if st.is_trader == nil then local dbst = db.storage[npc:id()] local trader = false if npc:character_community() == "trader" or npc:clsid() == clsid.script_trader then trader = true elseif string.find(npc:section(),"trader") or string.find(npc:name(),"trader") then trader = true elseif dbst.ini and dbst.section_logic and dbst.ini:section_exist(dbst.section_logic) and dbst.ini:line_exist(dbst.section_logic,"trade") then trader = true end if dbst.stype then -- логика инициализирована, можно запомнить st.is_trader = trader else return trader end end return st.is_trader == true end function safe_bone_pos(npc,bone) local cls = npc:clsid() if cls == clsid.burer_s then return npc:center() elseif cls == clsid.gigant_s then return npc:center() elseif cls == clsid.chimera_s then return npc:bone_position("neck_r") else return npc:bone_position(bone or get_fire_bone(npc:section(),"torso")) end end local gfb_torso,gfb_head,gfb_fire = {},{},{} function get_fire_bone(sec,part) if part == "head" then if not gfb_head[sec] then gfb_head[sec] = read_from_ini(nil,sec,"bone_head","bip01_head",1) end return gfb_head[sec] elseif part == "torso" then if not gfb_torso[sec] then gfb_torso[sec] = read_from_ini(nil,sec,"bone_torso",nil,1) or read_from_ini(nil,sec,"bone_spin","bip01_spine",1) end return gfb_torso[sec] end if not gfb_fire[sec] then gfb_fire[sec] = read_from_ini(nil,sec,"bone_fire","bip01_head",1) end return gfb_fire[sec] end --------------------------------------switch--------------------------------------- function switch_online(id) if id == -1 then return end local sim = alife() if sim then sim:set_switch_online(id,true) sim:set_switch_offline(id,false) end end function switch_offline(id) local sim = alife() if sim then sim:set_switch_online(id,false) sim:set_switch_offline(id,true) end end --------------------------------------output--------------------------------------- function printf(str,...) get_console():execute("load ~#I#:"..format_safe(str,...)) -- log(format_safe(str,...)) -- get_console():execute("flush") end function prints(str) get_console():execute("load ~#I#:"..str) -- log(str) end function give_game_news(str,time,...) if db.actor then db.actor:give_game_news("",format_safe(str,...),"ui_iconsTotal_grouping",0,time,0) end end function ABORT(s,...) printf("rx_utils.abort: "..s,...) sys_ini:r_bool("___aborting","a") end _G['ABORTX'] = ABORT function ASSERT(o,s,...) if not o then ABORT(s,...) end end _G['ASSERTX'] = ASSERT function print_table(t,name) local print_r_cache={} local function sub_print_r(t,indent) if (type(t)~="userdata" and print_r_cache[tostring(t)]) then prints(indent.."*"..tostring(t)) else if type(t)~="userdata" then print_r_cache[tostring(t)]=true end if (type(t)=="table") then for pos,val in pairs(t) do if (type(val)=="table") then prints(indent.."["..pos.."] => "..tostring(t).." {") sub_print_r(val,indent..string.rep(" ",string.len(pos)+8)) prints(indent..string.rep(" ",string.len(pos)+6).."}") elseif (type(val)=="string") then prints(indent.."["..pos..'] => "'..val..'"') elseif(type(val)=="userdata") then prints(indent.."["..pos..'] => "userdata"') else prints(indent.."["..pos.."] => "..tostring(val)) end end elseif(type(t)=="userdata") then prints(indent.."userdata") else prints(indent..tostring(t)) end end end if (type(t)=="table") then printf("print_table %s: %s {",(name or 'unnamed'),tostring(t)) sub_print_r(t," ") prints("}") else printf("print_table %s: %s(%s) is not table!",(name or 'unnamed'),type(t),t) end end --------------------------------------spy------------------------------------------ class "prof_spy" function prof_spy:__init() self.store = {} self.global_timer = time_global() end function prof_spy:__finalize() end function prof_spy:start(name) if not self.store[name] then self.store[name] = {timer = profile_timer(),count = 0,time = 0,min_time = 0,max_time = 0,prev_ptime = 0} end local st = self.store[name] st.timer:start() end function prof_spy:finish(name) local st = self.store[name] ASSERTX(st,"spy finish %s",name) st.timer:stop() local ptime = st.timer:time() local rtime = ptime-st.prev_ptime if rtime == 0 then return end st.prev_ptime = ptime st.time = st.time+rtime st.count = st.count+1 st.min_time = (st.min_time == 0 or rtime < st.min_time) and rtime or st.min_time st.max_time = rtime > st.max_time and rtime or st.max_time end function prof_spy:get_stat(name) if name then local st = self.store[name] if not st then return "nil" end local global_time = time_global()-self.global_timer local exec_ms_in_sec = st.time/global_time local str = string.format("\n[%s]: cnt=[%s] time=[%s] min=[%s] max=[%s] prd=[%s] ret=[%s] insec=[%s]",name,st.count,st.time,st.min_time,st.max_time,global_time/st.count,st.time/st.count,exec_ms_in_sec) return str else local tt = {} for name,st in pairs(self.store) do local global_time = time_global()-self.global_timer local exec_ms_in_sec = st.time/global_time local str = string.format("\n[%s]: cnt=[%s] time=[%s] min=[%s] max=[%s] prd=[%s] ret=[%s] insec=[%s]",name,st.count,st.time,st.min_time,st.max_time,global_time/st.count,st.time/st.count,exec_ms_in_sec) table.insert(tt,str) end return tt end end