-- Биндер актора
function init(npc)
	npc:bind_object(actor_binder(npc))
end

scopeUsed = false

class "actor_binder" (object_binder)

function actor_binder:__init (obj) super(obj)
	self.weather_manager = level_weathers.get_weather_manager()
	self.actor_detector = xr_detector.actor_detector()
	db.storage[self.object:id()] = {}
	db.storage[self.object:id()].pstor = {}
	self.perm_static_state={}
	self.droped_item_id=nil
	self.affects={["suck"]=false}
end

function actor_binder:reinit()
	object_binder.reinit(self)

	self.st = db.storage[self.object:id()]

	self.timer200 = 0
	self.timer250 = 0
	self.timer500 = 0
	self.timer1000 = 0
	self.timer5000 = 0
	self.timer2m = 0
	self.timer10m = 0

	self.object:set_callback(callback.inventory_info, self.info_callback, self)
	self.object:set_callback(callback.infoportion_disable, self.info_disable_callback, self)
	self.object:set_callback(callback.article_info, self.article_callback, self)
	self.object:set_callback(callback.on_item_take, self.on_item_take, self)
	self.object:set_callback(callback.on_item_drop, self.on_item_drop, self)
	self.object:set_callback(callback.trade_sell_buy_item, self.on_trade, self)
	self.object:set_callback(callback.task_state, self.task_callback, self)
	self.object:set_callback(callback.level_border_enter, self.level_border_enter, self)
	self.object:set_callback(callback.level_border_exit, self.level_border_exit, self)
	self.object:set_callback(callback.take_item_from_box, self.take_item_from_box, self)
	self.object:set_callback(callback.use_object, self.on_use_object, self)
	self.object:set_callback(callback.death, self.death_callback, self)
	self.object:set_callback(callback.on_keyboard_press, self.on_keyboard_press, self)
	self.object:set_callback(callback.on_keyboard_release, self.on_keyboard_release, self)
	self.object:set_callback(callback.on_object_hit, self.on_object_hit, self)
	self.object:set_callback(callback.on_zoom_in, self.on_zoom_in, self)
	self.object:set_callback(callback.on_zoom_out, self.on_zoom_out, self)
	self.object:set_callback_ex(callback.on_before_actor_change_level, self.on_before_actor_change_level,self)
	self.object:set_callback(callback.on_after_fail_actor_change_level, self.on_after_fail_actor_change_level,self)
	
end

local any_bot_dis_change=false
function actor_binder:on_after_fail_actor_change_level(nl_game_vertex,nl_level_vertex,nl_pos,nl_ang,c_pos,c_ang)
	if any_bot_dis_change then
	return not any_bot_dis_change
		--[[sms(translate("level_change_disallowed"), nil, "nano", 10000)
		local dir=screen.cam_dir
		dir:normalize()
		local dir_after = vector_rotate_y(dir,180)
		local vertex=level.vertex_in_direction(db.actor:level_vertex_id(), dir_after, 15)
		db.actor:set_actor_position(level.vertex_position(vertex))]]
	end
end

local exclude_bot_sections={
	["rat_weak"]=true,
	["rat_normal"]=true,
	["rat_strong"]=true,
	["rat_plague"]=true,
	["tarakan_normal"]=true,
	["tarakan_strong"]=true
}

function actor_binder:on_before_actor_change_level(nl_game_vertex,nl_level_vertex,nl_pos,nl_ang,c_pos,c_ang) --must return value always!
	--[[local not_excluded_enemy={}
	for __,id in pairs(db.actor:enemys_see_actor()) do
		local obj=level_object(id)
		if not exclude_bot_sections[obj:section()] then
			table.insert(not_excluded_enemy,id)
		end
	end
	local any_enemy_see_actor=table.size(not_excluded_enemy)~=0
	--local actor_position=db.actor:position()
	any_bot_dis_change=false
	local actor_id=db.actor:id()
	--log("=====")
	for id,bot in pairs(db.creatures) do
		if id~=actor_id and bot and bot:alive() then
			local enemy=bot:best_enemy()
			local danger=bot:best_danger()
			local bot_see_actor=bot:see(db.actor)

			if not exclude_bot_sections[bot:section()]  then
				--local bot_position_check_allowed=bot:position():distance_to(actor_position) < 70
				local is_actor_enemy=enemy and enemy:id()==actor_id or false
				local is_actor_danger=danger and (danger:object() and danger:object():id()==actor_id or false) or false
				if is_actor_danger and isStalker(bot) then
					is_actor_danger = bot:relation(db.actor) == game_object.enemy
				end
				if not bot_see_actor then
					is_actor_danger=false
					is_actor_enemy=false				
				end
				any_bot_dis_change=any_bot_dis_change or (is_actor_enemy or is_actor_danger or any_enemy_see_actor)
				-- if any_bot_dis_change and not bot_position_check_allowed then
					-- log("[%s] force any_bot_dis_change to false",bot:name())
					-- any_bot_dis_change=false
				-- end
				--log(bot:position():distance_to(actor_position))
				--log("[%s] enemy[%s] danger[%s] aesa[%s] == [%s] bsa[%s]",bot:name(),tostring(is_actor_enemy),tostring(is_actor_danger),tostring(any_enemy_see_actor),tostring(any_bot_dis_change),tostring(bot:see(db.actor)))			
				-- if is_actor_enemy or is_actor_danger then
					-- log("[%s] enemy[%s] danger[%s] aesa[%s] == [%s] bsa[%s]",bot:name(),tostring(is_actor_enemy),tostring(is_actor_danger),tostring(any_enemy_see_actor),tostring(any_bot_dis_change),tostring(bot:see(db.actor)))
					-- if is_actor_enemy then
						-- log("___[%s] dist[%d] pos[%f %f %f]",bot:name(),bot:position():distance_to(db.actor:position()),bot:position().x,bot:position().y,bot:position().z)
					-- end
				-- end
			end
		end
	end
	--log(tostring(any_bot_dis_change))]]
	return not any_bot_dis_change
end

local perm_statics={"god_static","sleepless_static"}

function actor_binder:on_zoom_in(active_item)
	if active_item then
		if active_item:section() == "wpn_binoc" then
			scopeUsed = true
		elseif active_item:is_weapon() then
			scopeUsed = active_item:has_scope()
		end
	end
	if scopeUsed then
		for id,static_name in ipairs(perm_statics) do
			self.perm_static_state[static_name]={["state"]=hud:GetCustomStatic(static_name)~=nil}
			if self.perm_static_state[static_name].state then
				self.perm_static_state[static_name].color=hud:GetCustomStatic(static_name):wnd():GetColor()
				self.perm_static_state[static_name].text=hud:GetCustomStatic(static_name):wnd():GetText()
				hud:RemoveCustomStatic(static_name)
			end
		end
	end
end

function actor_binder:on_zoom_out(active_item)
	scopeUsed = false
	for id,static_name in ipairs(perm_statics) do
		if self.perm_static_state[static_name] and self.perm_static_state[static_name].state then
			local perm_static=hud:AddCustomStatic(static_name, true)
			perm_static:wnd():SetText(self.perm_static_state[static_name].text)
			perm_static:wnd():SetColor(self.perm_static_state[static_name].color)
		end
	end
end

function actor_binder:on_keyboard_release(key,binded_action)
	--баг движка engine - action key_bindings.kWPN_PREV не существует!!! при его добавлении смещаются все клавиши...
	key_handlers.handler_release(key,binded_action)
end

function actor_binder:on_keyboard_press(key,binded_action)
	--баг движка engine - action key_bindings.kWPN_PREV не существует!!! при его добавлении смещаются все клавиши...
	key_handlers.handler_press(key,binded_action)
end

local hitTypeTables=
{
    [0]="burn",
    [1]="shock",
    [2]="strike",
    [3]="wound",
    [4]="radiation",
    [5]="telepatic",
    [6]="chemical_burn",
    [7]="explosion",
    [8]="fire_wound",
    [10]="dummy"
}
function actor_binder:on_object_hit(who,weapon,ammoSection,boneId,boneName,power,hitType)
	--log("# [%s] caused [%s] from [%s] with [%s] to [%i|%s] hit [%f|%s]",who:name(),self.object:name(),weapon:name(),ammoSection,boneId,boneName,power,tostring(hitType))
	callback_hit.process(self.object,who, weapon, ammoSection, boneId, boneName, power, hitType)
end

function actor_binder:required_pre_init()
	_G.Timer = timers.CTimer()
	_G.Inventory = inventory.CInventory()
	_G.hud = get_hud()
	_G.Arena = aem_manager.CArenaext()
	_G.ID_Cleaner = id_cleaner.CIDCleaner()
	_G.Dream = dream.CDream()
end

function actor_binder:net_spawn(data)
	if object_binder.net_spawn(self,data) == false then
		return false
	end
	--local tmr = profile_timer() tmr:start()
	self.started = true
	db.add_actor(self.object)
	db.actor.slots = get_actor_slots_count()

	self:required_pre_init()
	
	self.weapon_hide = false -- спрятано или нет оружие при разговоре.

	if self.st.disable_input_time == nil then
		level.enable_input()
	end
	self.weather_manager:reset()
	actor_stats.add_to_ranking(self.object:id())
	amk.on_game_load()
	horrortime.start_HT_timer()
	sak_off_corpses.off_corpses()
	amk_anoms.generate_anoms(level.name())
	amk_mod.spawn_unspawned_respawners()
	death_manager.init_drop_settings()
	xr_box.init_drop_box()

	rx_ai.actor_net_spawn()
	ui_mm_opt_add.del_bad_objects()
	sak.add_tail()
	spawn_level_changer.remove_old_teleport()
	spawn_teleport.spawn_teleport_exit()
	spawn_teleport.spawn_teleport_hospital()
	snp.net_spawn() -- snp
	fly_island.release_completed_fly_islands() -- острова

	safe_ArtefactInformer.init()
	safe_ItemElector.init()
	safe_StealManager.init()
	dialogs_charge_exo.chargers_init()
	prefetch("nano_suit_moduls") --winsor: facepalm просто...
	--tmr:stop() log("TIME SPEND actor_binder:net_spawn: "..tmr:time())
	self.give_rad_info=false
	return true
end

function actor_binder:update(delta)
	object_binder.update(self, delta)
	amk.oau_reason = "actor_binder:update"
	-- Первый апдейт
	if self.started then
		amk.oau_watchdog = 199
		self:first_update(delta)
		--if db.debug_mode then
			-- Вывод информации о позиции
			--utils.set_coords_visible(true)
		--end
		fixer.process()
		self.started = nil
	end
	if utils.get_coords_visible() then
		utils.update_coords()
	end
	amk.oau_watchdog = 198
	self.time = time_global()
	fly.update()
	fly.returner_update()
	chess.update()

	amk.oau_watchdog = 197
	self:rotting() -- гниение тупов
	buusty_dialog.weapon_effector()
	self.weather_manager:update()
	self.actor_detector:update()
	xr_sound.update_actor()
	meceniy_work.set_invisible() 
	Arena:update()
	amk.oau_watchdog = 196
	xrs_ai.actor_update(delta)
	rx_ai.actor_update()
	biodetector.update(delta)
	stco.update()
	bind_det_arts.update()
	sickness.update_cure_anim()
	
	amk.oau_watchdog = 195
	amk.on_actor_update(delta)
	amk.oau_watchdog = 194

	-- Обновление отключения ввода с клавиатуры.
	if self.st.disable_input_time ~= nil and game.get_game_time():diffSec(self.st.disable_input_time) >= self.st.disable_input_idle	then
		level.enable_input()
		self.st.disable_input_time = nil
	end
	
	-- Апдейт прятания оружия
	if self.object:is_talking() then
		if self.weapon_hide == false then
			self.object:hide_weapon()
			self.weapon_hide = true
		end
	else
		if self.weapon_hide == true then
			self.object:restore_weapon()
			self.weapon_hide = false
		end
	end

	-- обновление пси-антенны
	if sr_psy_antenna.psy_antenna then
		sr_psy_antenna.psy_antenna:update(delta)
	end
	booster.apply_boosters(delta)
	-- Апдейт 200 мс
	if self.timer200 < self.time then
		self.timer200 = self.time+200
		amk.oau_watchdog = 189
		self:update200(delta)
	end

	-- Апдейт 250 мс
	if self.timer250 < self.time then
		self.timer250 = self.time+250
		amk.oau_watchdog = 188
		self:update250(delta)
	end

	-- Апдейт 500 мс
	if self.timer500 < self.time then
		self.timer500 = self.time+500
		amk.oau_watchdog = 187
		self:update500(delta)
	end

	-- Апдейт 1 секунда
	if self.timer1000 < self.time then
		self.timer1000 = self.time+1000
		amk.oau_watchdog = 186
		self:update1000(delta)
	end

	-- Апдейт 5 секунд
	if self.timer5000 < self.time then
		self.timer5000 = self.time+5000
		amk.oau_watchdog = 185
		self:update5000(delta)
	end

	-- Апдейт 2 минуты
	if self.timer2m < self.time then
		self.timer2m = self.time+2*60*1000
		amk.oau_watchdog = 184
		self:update2m(delta)
	end

	-- Апдейт 10 минут
	if self.timer10m < self.time then
		self.timer10m = self.time+10*60*1000
		amk.oau_watchdog = 183
		self:update10m(delta)
	end
	amk.oau_watchdog = 0
	amk.oau_reason=""
end

-- Апдейт 200 мс
function actor_binder:update200(delta)
	bind_restrictor.actor_update(delta)
	bind_mteleport.actor_update(delta)
	Inventory:update()
	if has_info("teleport_finished") then
		disable_info("teleport_started")
		disable_info("teleport_finished")
	end
	booster.draw_boosters()
	hunger.apply_hunger_step()
end

-- Апдейт 250 мс
function actor_binder:update250(delta)
	Timer:update()
end

-- Апдейт 500 мс

function actor_binder:update500(delta)
	if db.actor.radiation>=0.8 then --когда достигли критической отметки
		if self.give_rad_info==false then --и ранее не выдавали информацию, проверка на bool намного быстрее чем has_info
			self.give_rad_info=true --дабы не выдавать повторно
			give_info("virogdenye_radiation")
		end
	elseif db.actor.radiation<=0.4 then --когда спустилась до порога снятия поршня
		if self.give_rad_info==true then --если ранее поршень выдавался
			self.give_rad_info=false --дабы не снимать повторно
			disable_info("virogdenye_radiation") --снимемь поршень
		end
	end
	loc_radiation.update()
	gps_habar.update_gps_state()
	babah.update(delta)
	snp.update()
end

-- Апдейт 1 секунда
function actor_binder:update1000(delta)
	game_stats.update()
	nano_suit_moduls.sidor_ammo()
	amk_mod.on_blowout_hit(self.object)
	if table.size(db.need_replace_corpses)>0 and not db.update_corpses then --что-то есть и не идет процесс обновления
		db.update_corpses=true 
		for cid,_ in pairs(db.need_replace_corpses) do --по всем айдям
			local sobj=server_object(cid)
			if sobj then --если объект не прибили до этого
				local pos=sobj.position
				local lv=sobj.m_level_vertex_id
				local gv=sobj.m_game_vertex_id
				release(sobj) --удалим труп
				create("zombie_trup_fc",pos, lv, gv) --заспавним фейкового зомби
			end
		end
		db.need_replace_corpses={} --обновлять нечего
		db.update_corpses=false --да и процесс освободим
	end
	if db.god.u[1] and not scopeUsed then
		ui_mm_opt_main.GodHudUpdate()
	end
end

-- Апдейт 5 секунд
function actor_binder:update5000(delta)
	sak.nepis_umer()
	drrr.vbr()
	amk_alcohol.update()
	amk_mod.check_radar_off()

	if db.actor.no_load_time and db.actor.no_load_time < game.minutes() then
		give_info("acv_giv")
		db.actor.no_load_time = nil
	end
	hunger.starvation_effector()
end

-- Апдейт 2 минуты
function actor_binder:update2m(delta)
	grib_respawn.grib_respawn()
	fly_island.check_code()

	if not has_info("acv_stay1") and game.days() > 181 then
		give_info("acv_stay1")
	elseif not has_info("acv_stay2") and game.days() > 364 then
		give_info("acv_stay2")
	end
	_G.Sleepless:sleepless_effector()
end

function actor_binder:update10m(delta)
	_G.Sleepless:sleepless_effector()
end

-- Первый апдейт
function actor_binder:first_update(delta)
	db.money = self.object:money()
	db.vbr = drrr.next_vbr()
	db.god_init()
	if not has_info("global_dialogs") then
		give_info("global_dialogs")
		_G.g_start_avi = true
	end

	if has_info("level_changing") then
		_G.ID_Cleaner:on_level_changing()
		disable_info("level_changing")
	end

	level_tasks.add_lchanger_location()
	all_spawn_fix.respawn_allspawn()
	ui_mm_opt_add.send_bad_objects_info()
	ui_load_dialog.saved_games_limit_show_error()
	archievements.acv_death()

	if has_info("fli_chaes_start") and not has_info("fli_chaes_have") and not has_info("fli_chaes_fail") then
		fly_island.fli_chaes_set_range()
	end
	
	level.restart_ppe_effects()
	
	if level.name()=="l08u_brainlab" then
		local obj=story_object(22037)
		if not obj then
			create(ai:spawn_id(22037))
		end
		obj=story_object(22038)
		if not obj then
			create(ai:spawn_id(22038))
		end
	end
	-- Фикс Бармена и двери Арены
	if level.name() == "l05_bar" then
		disable_info("bar_barman_stay_room")--да не уйдешь ты в ту комнату никогда!
		give_info("bar_barman_stay_hole")		
		all_spawn_fix.release_allspawn_object("bar_arena_door_0000")
		create(ai:spawn_id(520))
		local tobj=story_object(19805)
		if tobj and not tobj:alive() then
			release(tobj)
			give_info("19805")
			create(ai:spawn_id(19805))
		elseif not tobj then
			give_info("19805")
			create(ai:spawn_id(19805))
		end
	end
	player_ogg.restore_track()
	booster.restore_boosters()
	crow_manager.check_level()
	sickness.sickness_effector(true)
	if not has_info("acv_giv") then
		db.actor.no_load_time = game.minutes()+9*60
	end

	if Timer:has("no_save") then
		abort(translate("actor_binder_first_update_0"))
		timer("zonedocs_gg_kill",3,"kostya_dialog.zonedocs_gg_kill()")
	end
	give_info("save_prohibited")
	timer("no_save", 5)
end


local bolt_allowed_sections={
	[1]="bolt",
	[2]="bolt_m1",
	[3]="bolt_m2",
	[4]="gilza"
}

function actor_binder:check_bolt_change()
	local bolt=db.actor:item_in_slot(inventory_slots.BOLT)
	if bolt then
		local current_section_id=table.invert(bolt_allowed_sections)[bolt:section()]
		local new_param_value=ui_mm_opt_creator.get_number_user_param("bolt_visual",1)
		if new_param_value and current_section_id~=new_param_value then
			db.actor:inventory_move_item(bolt,inventory_slots.RUCK,true)
			db.actor:drop_item(bolt)
			release(bolt)
			create(bolt_allowed_sections[new_param_value],db.actor:position(),db.actor:level_vertex_id(),db.actor:game_vertex_id(),db.actor:id())
		end
	end
end

function actor_binder:net_destroy()
	actor_stats.remove_from_ranking(self.object:id())

	db.del_actor(self.object)

	sr_light.clean_up()

	self.object:set_callback(callback.inventory_info, nil)
	self.object:set_callback(callback.infoportion_disable, nil)
	self.object:set_callback(callback.article_info, nil)
	self.object:set_callback(callback.on_item_take, nil)
	self.object:set_callback(callback.on_item_drop, nil)
	self.object:set_callback(callback.trade_sell_buy_item, nil)
	self.object:set_callback(callback.task_state, nil)
	self.object:set_callback(callback.level_border_enter, nil)
	self.object:set_callback(callback.level_border_exit, nil)
	self.object:set_callback(callback.take_item_from_box, nil)
	self.object:set_callback(callback.use_object, nil)
	self.object:set_callback(callback.death, nil)
	self.object:set_callback(callback.on_keyboard_press, nil)
	self.object:set_callback(callback.on_object_hit, nil)
	self.object:set_callback(callback.on_zoom_in, nil)
	self.object:set_callback(callback.on_zoom_out, nil)
	self.object:set_callback_ex(callback.on_before_actor_change_level, nil)
	self.object:set_callback(callback.on_after_fail_actor_change_level, nil)

	if sr_psy_antenna.psy_antenna then
		sr_psy_antenna.psy_antenna:destroy()
		sr_psy_antenna.psy_antenna = false
	end

	xr_sound.stop_all_sound_object()
	object_binder.net_destroy(self)
end

-- Калбеки
function actor_binder:death_callback(victim, who)
	if level.main_input_receiver() then
		level.start_stop_menu(level.main_input_receiver(), true)
	end
	if victim:id() == db.actor:id() then
		difficulty_manager.deathStatCollect()
		archievements.on_actor_death(who)
	end
	hud:SetCustomText("")
	-- Прерывание сна
	_G.Dream:stopper()
	sk_actor_death.is_killed()	
end

function actor_binder:take_item_from_box(box, item)
	-- если калбек на взятие предмета из тайника поставить сюда, а не в on_item_take, то калбек сработает только один раз
	-- и не будет вызываться каждый раз при лоаде, поэтому поршнем его отсекать не нужно
	--log(box:name().." "..item:name())
	callback_take_item_from_box.process(box,item)
	fly.returner_clear_pos(item)
	snp.check_treasure_item(item)
	-- подъем рюкзаков
	if box:is_inv_box_empty() then
		if transparent_treasure.IsTaken(box:section()) then
			-- здесь можно прописывать калбеки взятия на проверку по секции тайника, чтобы не придумывать уникальные предметы
			level.start_stop_menu(level.main_input_receiver(), true)
			snp.check_treasure(box)
			archievements.check_treasure(box)
			kostya_dialog.stealth_on_take(box)
			create("treasure_item",box:position(),db.actor:level_vertex_id(),db.actor:game_vertex_id(),0)
			release(server_object(box:id()))
			if art_hit.is_hitted_item(item) then --сей калбек перед тейк_итем, посему поизвращаемся
				art_hit.empty_closed_treasure=true
			end
		end

		if box:section() ~= "m_inventory_box" then
			archievements.acv_count_event("acv_sled", 300, "acv_sled")
		end
	end

	local story_id = box:story_id()
	if story_id == nil then
		return
	end

	treasure.take_item_from_box(box, story_id)
	zamok.remove(box, item)
	amk.on_item_take_from_box(box,item)
end

function actor_binder:level_border_enter(object)
	self.actor_detector:actor_enter()
end

function actor_binder:level_border_exit(object)
	self.actor_detector:actor_exit()
end

function actor_binder:info_disable_callback(npc, info_id) --новый калбек
	callback_info.info_disable_callback(npc, info_id)
end

function actor_binder:info_callback(npc, info_id)
	--log("info_callback: "..info_id)
	callback_info.info_enable_callback(npc, info_id)
	if string.find(info_id, "^ui_") then
		-- проверка служебных инфопоршенов
		db.vbr = drrr.next_vbr()
		_G.Inventory:on_inventory_info(info_id)
		hide_wpn.on_info(info_id)
		trade.on_info(info_id)
		pda_addons.on_info(info_id)
	else
		-- проверка сюжетных инфопоршенов
		level_tasks.proceed(self.object)
		archievements.on_info(info_id)
		_G.Cycle_Task:notify_new_task(info_id)
		level_tasks.process_info_portion(info_id)
	end
end

function actor_binder:on_trade(item, sell_bye, money)
    if sell_bye == true then
		game_stats.money_trade_update(money)
    else
		if isMagazinedWeapon(item) then
			item:unload_magazine()
		end
		archievements.on_receive_count_item(item)
		game_stats.money_trade_update(-money)
    end
	trade.on_trade(item, sell_bye)

	db.traded = true
end

function actor_binder:article_callback(npc, group, name, _type)
	--printf("article_callback [%s][%s]", group, name)
	if screen.precache_frame >1 then return end
	if group ~= "archievements" then	-- По достижениям и письмам не показываем
		news_manager.send_encyclopedy(_type, group, name)
	end
end

function actor_binder:on_item_take(obj)
	--log(obj:section())
	if self.droped_item_id==obj:id() then
		self.droped_item_id=nil
	end
	--if --при необходимости можно сократить количество обработчиков
	_G.Inventory:on_item_take(obj)	
	callback_take.process(obj)
	-- then
		-- return
	-- end
	level_tasks.proceed(self.object)
	if has_map_spot(obj:id(),"red_location") then
		del_map_spot(obj:id(),"red_location")
	end
	amk_mod.check_take_item(obj)
	xrs_ai.actor_item_take(obj)
	if art_hit.is_hitted_item(obj) then
		art_hit.hit_by_art(obj)
	end

	kostya_dialog.checking_taken_item(obj)
	snp.checking_taken_item_snp(obj)	
	archievements.on_receive_count_item(obj)
	archievements.on_item_take(obj)
	chess.item_take(obj)
	monster_parts.on_part_take(obj)
	fly.returner_clear_pos(obj)
	rx_ai.actor_item_take(obj)
	if isMagazinedWeapon(obj) and self.object:is_talking() and not protected_items.not_unloaded_weapon(obj:section()) then
		obj:unload_magazine()
	end

	-- должен в итоге быть монолитовцем при определенных условиях
	snp.join_monolith()
end

function actor_binder:on_item_drop(obj)
	self.droped_item_id=obj:id()
	--if --при необходимости можно сократить количество обработчиков
	callback_drop.process(obj)
	-- then
		-- return
	-- end

	level_tasks.proceed(self.object)
	amk.on_item_drop(obj)
	_G.Inventory:on_item_drop(obj)
	sak.check_droped_item(obj)
        chess.on_item_drop(obj)
end

function actor_binder:task_callback(_task, _objective, _state)
	cycle_task.task_callback(_task:get_id(), _objective:get_idx(), _state)
	if _objective:get_idx() == 0 then
		if _state == task.fail then
			if db.actor then
				give_info("acv_task_fail")
			end
			news_manager.send_task(db.actor, "fail", _task, _objective)
		elseif _state == task.completed then
			news_manager.send_task(db.actor, "complete", _task, _objective)
		else
			news_manager.send_task(db.actor, "new", _task, _objective)
		end
	else
		if _task:get_objective(0):get_state() == task.in_progress then
			news_manager.send_task(db.actor, "update", _task, _objective)
		end
	end
end

function actor_binder:on_use_object(obj)
	callback_use.process(obj)
	zvuki.use_snd(obj)
	archievements.on_use_item(obj)
	spawn_level_changer.use_teleport(obj)
end

-- Сохранение
function actor_binder:save(packet)
	if Timer:has("no_save") then
		abort(translate("actor_binder_save_0"))
		timer("zonedocs_gg_kill",3,"kostya_dialog.zonedocs_gg_kill()")
	end

	-- проваливание заданий, во время которых нельзя сохраняться
	esc_fly.sidor_delo_fail()
	fly.stop()

	if level.get_game_difficulty() < 3 then
		give_info("acv_real_fail")
	end

	if utils.level_changing() then
		give_info("level_changing")
		set_value("id_cl", ai:level_id())

		if not has_info("teleport_started") then
			archievements.acv_count_event("acv_skit", 300, "acv_skit")
		end
	end

	printf("actor_binder:save(): self.object:name()='%s'", self.object:name())
	object_binder.save(self, packet)

	--' Сохраняем уровень сложности
	packet:w_u8(level.get_game_difficulty())

	--' Сохраняем данные об отключенном вводе
	if self.st.disable_input_time == nil then
		packet:w_bool(false)
	else
		packet:w_bool(true)
		packet:w_CTime(self.st.disable_input_time)
	end

	rx_ai.actor_save()

	xr_logic.save_all(self.object, packet)
	self.weather_manager:save(packet)
	sr_psy_antenna.save(packet)
	treasure.save(packet)
	
	if packet:w_tell() > 7000 then
		sms("actor_binder_save_0", nil, "nano", 80000)
	end
	booster.store_boosters()
	--log("SAVE: actor net_packet size: "..packet:w_tell())
end

-- Загрузка
function actor_binder:load(reader)
	object_binder.load(self, reader)

	--' Загружаем уровень сложности
	local game_difficulty_by_num = {
		[0] = "gd_novice",
		[1] = "gd_stalker",
		[2] = "gd_veteran",
		[3] = "gd_master"
	}
	console:execute("g_game_difficulty "..game_difficulty_by_num[reader:r_u8()])

	if reader:r_eof() then
		abort(translate("actor_binder_load_0"))
		string.format("%s")
	end

	--' Загружаем данные об отключенном вводе
	local stored_input_time = reader:r_u8()
	if stored_input_time == true then
--		self.st.disable_input_time = reader:r_CTime()
		self.st.disable_input_time = net_packet.r_CTime(reader)
	end

	xr_logic.load_all(self.object, reader)
	self.weather_manager:load(reader)
	sr_psy_antenna.load(reader)
	treasure.load(reader)
end

-- !!! перенести в affects !!!
-- секции броников, одев которые, мы не закашливаемся при приближении к гниющему трупу
local no_cough= {
	["merc_scientific_outfit"]=true,	-- Научный костюм наёмников
	["scientific_outfit"]=true,	        -- Научный костюм сталкера "Сева"
	["dolg_scientific_outfit"]=true,	-- Научный костюм Долга
	["freedom_scientific_outfit"]=true,	-- Научный костюм Свободы
	["monolit_scientific_outfit"]=true,	-- Научный костюм Монолита
	["scientist_suit_white"]=true,	-- Белый Научный костюм Монолита
	["nebo_scientific_outfit"]=true,	-- Научный костюм Чистого Неба
	["scientific_lastday_outfit"]=true,	-- Научный костюм Последнего Дня
	["protection_outfit"]=true,	        -- Комбинезон ССП-99М "Вега"
	["ecolog_outfit"]=true,	        -- Костюм "Эколог"
	["nano_outfit"]=true,                -- Стальной Крыс
	["nano_suit"]=true,                  -- Костюм Волазара
	["seva_scient_outfit"]=true,         -- Модернизированная "Сева"
	["exo_scientist_outfit"]=true        -- Научный экзоскелет
}

-- Разное
function actor_binder:rotting() 
	if db.rotting > 0 then
		if not self.rot then
			self.rot = {}
			self.rot.next_sound = time_global()+math.random(2000,3000)
			self.rot.sound_id = 0
			self.rot.prev_sound_id = 0
			self.rot.effactor = 0.1
			level.add_pp_effector("sk_corpse.ppe", 5436, true)
			level.set_pp_effector_factor(5436, self.rot.effactor)
		end
		if self.rot.next_sound < time_global() then
			local outfit=db.actor:item_in_slot(inventory_slots.OUTFIT)
			local length=0
			if not outfit or (outfit and not no_cough[outfit:section()]) then
				repeat
					self.rot.sound_id = math.random(1,26)
				until self.rot.sound_id ~= self.rot.prev_sound_id			
				self.rot.sound = voice([[rotting\rotting_]]..self.rot.sound_id)
				self.rot.sound:play(self.object, 0, sound_object.s2d)
				self.rot.prev_sound_id = self.rot.sound_id
				length=self.rot.sound:length()
			end
			self.rot.next_sound = time_global()+length+math.random(2000,3000)
			if self.rot.effactor < 1 then
				self.rot.effactor = self.rot.effactor+0.05
				level.set_pp_effector_factor(5436, self.rot.effactor)
			end
		end
	else
		self.rot = nil
		level.remove_pp_effector(5436)
	end
end

function has_traded()
	if db.traded then
		if db.actor:is_talking() then
			db.actor:stop_talk()
		end
		db.traded = nil
	end
end
