
function AddToMotivator(npc)
	npc:bind_object(this.motivator_binder(npc))
end

-- CScriptGameObject Binder for CAIStalker
class "motivator_binder" (object_binder)
function motivator_binder:__init (obj) super(obj)
	self.loaded = false
	self.opened_doors = {}
	self.first_update = false
	self.object = obj
end

function motivator_binder:extrapolate_callback(cur_pt)
	if (self.object == nil) then
		printe("!CRITICAL ERROR: game object is nil! %s",self.name)
		return false
	end
	if (self.st.active_section and self.st.active_scheme) then
		xr_logic.issue_event(self.object, self.st[self.st.active_scheme], "extrapolate_callback")
		if (self.st.move_mgr) then
			self.st.move_mgr:extrapolate_callback(self.object)
		end
	end

	local ptr = self.object:patrol() and patrol(self.object:patrol())
	if ptr and ptr:flags(cur_pt):get() == 0 then
		return true
	end
	return false
end

function motivator_binder:reinit()
	--utils_data.debug_write(self.object:name()..":motivator_reinit() start")
	--printf("motivator_binder:reinit() %s",self.object:name())
	object_binder.reinit(self)

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

	self.object:enable_vision(false)
		
	if (USE_MARSHAL) then
		RegisterScriptCallback("save_state",self)
	end
	--utils_data.debug_write(self.object:name()..":motivator_reinit() end")
end
----------------------------------------------------------------------------------------------------------------------
-- Note: Do not make mistake in thinking that se_abstract is the same as se_obj. It is casted as cse_abstract and not cse_alife_human_stalker!!!
-- Therefore cse_alife_human_stalker specific properties are not available from it, like group_id
function motivator_binder:net_spawn(se_abstract)
	--utils_data.debug_write(strformat("\n----%s:net_spawn()---%s visual=%s",self.object:name(),self.object:character_name(),self.object:get_visual_name()),true)
	if not object_binder.net_spawn(self, se_abstract) then
		return false
	end
	
	local se_obj = alife_object(self.object:id())
	if not (se_obj) then 
		return false 
	end
	
	local npc = self.object

	-- Enable if you need it, CoC doesn't have story for actor_visual and ini read from HDD not good
	--[[
	local visual = ini_sys:r_string_ex(npc:section(),"set_visual")
	if (visual and visual ~= "") then
		if visual == "actor_visual" then
			npc:set_visual_name(db.actor:get_visual_name())
		else
			npc:set_visual_name(visual)
		end
	end
	--]]	
	
	-- overrides
	local m_data = alife_storage_manager.get_se_obj_state(se_obj)
	if (m_data) then 
		if (m_data.visual) then 
			npc:set_visual_name(m_data.visual)
		end  
		if (m_data.icon_name) then 
			npc:set_character_icon(m_data.icon_name)
		end
	end
	
	npc:apply_loophole_direction_distance(1.0)
	npc:set_callback(callback.use_object, motivator_binder.use_callback, self)
	npc:set_callback(callback.on_item_take, self.on_item_take, self)
	npc:set_callback(callback.on_item_drop, self.on_item_drop, self)
	
	db.add_obj(npc)
	if not npc:alive() then
		npc:death_sound_enabled(false)
		--utils_data.debug_write(se_obj:name()..":net_spawn() Moving dead body...")
		release_body_manager.get_release_body_manager():moving_dead_body(npc,true)
		--utils_data.debug_write(se_obj:name()..":net_spawn() Moving dead body done")
		
		if not (self.loaded) then
			local ini = npc:spawn_ini()
			local filename = ini and ini:r_string_ex("logic","cfg")
			ini = filename and ini_file(filename) or ini
			if (ini) then
				self:setup_known_info(npc,ini,ini:r_string_ex("logic","known_info"))
			end
		end
		
		-- fix for 'Invalid vertex for object'
		if not (se_obj.m_level_vertex_id < 4294967295 and npc:accessible(se_obj.m_level_vertex_id)) then
			printf("npc %s has invalid spawning vertex, correcting",se_obj:name())
			local v_id = npc:accessible_nearest(level.vertex_position(se_obj.m_level_vertex_id), VEC_ZERO)
			npc:set_npc_position(level.vertex_position(v_id))
		end
		
		--xr_logic.initialize_obj(npc, self.st, self.loaded, db.actor, modules.stype_stalker)
		return true
	end
	
	npc:enable_vision(true)
	
	self.st.state_mgr = state_mgr.bind_manager(npc)
	self.st.move_mgr = move_mgr.move_mgr(npc)
	self.st.move_mgr:initialize()
	
	npc:set_patrol_extrapolate_callback(motivator_binder.extrapolate_callback, self)
	npc:set_callback(callback.hit, motivator_binder.hit_callback, self)
	npc:set_callback(callback.death, motivator_binder.death_callback, self)
	npc:set_callback(callback.sound, motivator_binder.hear_callback, self)
	npc:set_callback(callback.take_item_from_box, self.take_item_from_box, self)
		
	db.add_enemy(npc)

	local relation = db.goodwill.relations and db.goodwill.relations[npc:id()]
	if (relation and db.actor) then
		game_relations.set_npcs_relation(npc, db.actor, relation)
	end

	local sympathy = db.goodwill.sympathy and db.goodwill.sympathy[npc:id()]
	if (sympathy) then
		game_relations.set_npc_sympathy(npc, sympathy)
	end

	sound_theme.init_npc_sound(npc)

	-- HACK! Not sure why this is needed now. If this isn't here then there will be
	-- Cannot build GAME path! error
	npc:set_dest_game_vertex_id(npc:game_vertex_id()) 
	
	xr_reach_task.add_reach_task_action(npc)
	
	if (se_load_var(se_obj.id,se_obj:name(),"companion")) then
		axr_companions.setup_companion_logic(npc,self.st,self.loaded)
	else
		npc:disable_info_portion("npcx_is_companion")
		npc:disable_info_portion("npcx_beh_cannot_dismiss")
		smart_terrain.setup_gulag_and_logic_on_spawn(npc, self.st, se_obj, modules.stype_stalker, self.loaded)
	end
	
	local id = se_obj.id
	local force_set_position
	
	if (db.offline_objects[id] and db.offline_objects[id].level_vertex_id) then
		force_set_position = level.vertex_position(db.offline_objects[id].level_vertex_id)
		db.offline_objects[id].level_vertex_id = nil
	elseif (db.spawned_vertex_by_id[id]) then
		force_set_position = level.vertex_position(db.spawned_vertex_by_id[id])
		db.spawned_vertex_by_id[id] = nil
	end

	if (force_set_position) then
		npc:set_npc_position(force_set_position)
	end

	if character_community(npc) ~= "zombied" then
		post_combat_idle.add_post_combat_idle(npc)
	end

	npc:group_throw_time_interval(2000)

	-- start [Alundaio]
	SendScriptCallback("npc_on_net_spawn",npc,se_obj)
	-- End [Alundaio]

	--utils_data.debug_write(se_obj:name()..":net_spawn() end")

	return true
end
function motivator_binder:on_item_take(item)
	if not (self.object) then
		return
	end
	-- Alundaio
	SendScriptCallback("npc_on_item_take",self.object,item)
	-- End Alundaio
end

--Alundaio
function motivator_binder:take_item_from_box(box,item)
	if not (self.object) then
		return
	end
	SendScriptCallback("npc_on_item_take_from_box",self.object,box,item)
end
--Alundaio

function motivator_binder:on_item_drop(item)
	if not (self.object) then
		return
	end
	-- Alundaio
	SendScriptCallback("npc_on_item_drop",self.object,item)
	-- End Alundaio
end
function motivator_binder:net_destroy()
	--printf("%s:net_destroy()",self.object:name())
	self.object:set_callback(callback.use_object,nil)
	self.object:set_callback(callback.on_item_take,nil)
	self.object:set_callback(callback.on_item_drop,nil)
	self.object:set_patrol_extrapolate_callback(nil)
	self.object:set_callback(callback.hit,nil)
	self.object:set_callback(callback.death,nil)
	self.object:set_callback(callback.sound,nil)
	self.object:set_callback(callback.take_item_from_box,nil)
	-- start [Alundaio]
	SendScriptCallback("npc_on_net_destroy",self.object)
	-- end [Alundaio]
	xr_combat_ignore.fighting_with_actor_npcs[self.object:id()] = nil

	xr_sound.stop_sounds_by_id(self.object:id())
	
	if (self.object:is_talking()) then
		db.actor:stop_talk()
		self.object:stop_talk()
	end

	if (self.st) then
		if (self.st.active_scheme) then
			xr_logic.issue_event(self.object, self.st[self.st.active_scheme], "net_destroy", self.object)
		end
		if (self.st.reach_task) then
			xr_logic.issue_event(self.object, self.st.reach_task, "net_destroy", self.object)
		end
		if (self.st.overrides and self.st.overrides.on_offline_condlist) then
			xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.overrides.on_offline_condlist)
		end
	end

	if db.offline_objects[self.object:id()] then
		db.offline_objects[self.object:id()].level_vertex_id = self.object:level_vertex_id()
		db.offline_objects[self.object:id()].active_section = db.storage[self.object:id()].active_section
	end

	if (USE_MARSHAL) then
		UnregisterScriptCallback("save_state",self)
	end

	db.del_obj(self.object)
	db.delete_enemy(self.object)
	
	local m_data = alife_storage_manager.get_state()
	if (m_data.game_object) then
		m_data.game_object[self.object:id()] = nil
	end
	
	trade_manager.on_npc_death(self.object)
	
	object_binder.net_destroy(self)
end

function motivator_binder:hit_callback(obj, amount, local_direction, who, bone_index)
	--utils_data.debug_write(strformat("motivator_binder:hit_callback %s",obj and obj:name()))
	if (amount <= 0 or who == nil) then
		--utils_data.debug_write(strformat("END motivator_binder:hit_callback %s",obj and obj:name()))
		return 
	end

	--[[ Only wound the stalker from powerful attacks if not a head hit and not already wounded
	if ( (obj:has_info("npcx_is_companion") or bone_index ~= 15) and amount > 15 and amount >= obj.health*100 and not IsWounded(obj)) then
		obj:set_health_ex(0.15)
	end
	--]]

	if (self.st.active_section and self.st.active_scheme) then
		xr_logic.issue_event(obj, self.st[self.st.active_scheme], "hit_callback", obj, amount, local_direction, who, bone_index)
	end
	if self.st.combat_ignore then
		xr_logic.issue_event(obj, self.st.combat_ignore, "hit_callback", obj, amount, local_direction, who, bone_index)
	end
	if self.st.combat then
		xr_logic.issue_event(obj, self.st.combat, "hit_callback", obj, amount, local_direction, who, bone_index)
	end
	if self.st.hit then
		xr_logic.issue_event(obj, self.st.hit, "hit_callback", obj, amount, local_direction, who, bone_index)
	end

	xr_wounded.hit_callback(obj:id())
	if (who and IsHelicopter(who)) then
		--printf("hit by helicopter")
		local squad = get_object_squad(obj)
		if (squad and not xr_combat_ignore.safe_zone_npcs[squad.id]) then
			for o in squad:squad_members() do
				if (db.storage[o.id]) then
					db.storage[o.id].heli_enemy_flag = true
				end
			end
		elseif not (xr_combat_ignore.safe_zone_npcs[obj:id()]) then
			db.storage[obj:id()].heli_enemy_flag = true
		end
	end

	-- start [Alundaio]
	SendScriptCallback("npc_on_hit_callback",obj,amount,local_direction,who,bone_index)
	-- end [Alundaio]
	
	--utils_data.debug_write(strformat("END motivator_binder:hit_callback %s",obj and obj:name()))
end

function motivator_binder:death_callback(victim, who)
	--utils_data.debug_write(strformat("START motivator_binder:death_callback %s",victim and victim:name()))
	if (self.object == nil or victim == nil) then
		callstack()
		abort(strformat("CRITICAL ERROR: motivator_binder:death_callback : Game Object is nil and binder is not! %s",self.name),2)
		--utils_data.debug_write(strformat("END motivator_binder:death_callback %s",victim and victim:name()))																										
		return
	end
	
	--mlr
	local name = self.object:name()
	if has_alife_info(name) then
		disable_info(name)
	end
	--mlr
	
	if (victim:has_info("npcx_is_companion")) then 
		if (not _G.WARFARE) and game_relations.is_factions_friends(character_community(db.actor), character_community(victim)) then
			game_statistics.increment_statistic("killed_companions") -- lose rep for companion death, nobody would want to hang out with a loser!
		end
		stalker_generic.remove_level_spot(victim:id())
	elseif (who and who:id() == AC_ID) then
		if (load_var(victim,"victim_surrender",false) == true) then
			local state = tostring(load_var(victim, "wounded_state"))
			if (state ~= "nil") then
				game_statistics.increment_statistic("killed_prisoners") -- lose rep for killing still wounded surrendered victim
			end
		end
	end
	
	self.object:enable_vision(false)
	
	local known_info = self.st.ini and self.st.section_logic and self.st.ini:r_string_ex(self.st.section_logic,"known_info")
	if (known_info) then
		self:setup_known_info(self.object,self.st.ini,known_info)
	end
	
	trade_manager.on_npc_death(self.object)
	self:hit_callback(victim, 1, VEC_ZERO, who, "from_death_callback")

	local id = victim:id()
	xr_combat_ignore.fighting_with_actor_npcs[id] = nil

	death_manager.decide_items_to_keep(self.object)

	stalker_generic.remove_level_spot(id)

	if (who and IsStalker(who) and who:alive()) then
		local count = load_var(who,"s_kill_count") or 0
		if (count >= 255) then
			count = 254
		end
		save_var(who,"s_kill_count",count)
	end

	if (self.st.state_mgr) then
		self.st.state_mgr.animation:set_state(nil, true)
		self.st.state_mgr = nil
	end

	if self.st.reach_task then
		xr_logic.issue_event(victim, self.st.reach_task, "death_callback", victim, who)
	end
	if self.st.death then
		xr_logic.issue_event(victim, self.st.death, "death_callback", victim, who)
	end
	if self.st.active_section then
		xr_logic.issue_event(victim, self.st[self.st.active_scheme], "death_callback", victim, who)
	end

	sr_light.check_light(victim)

	db.delete_enemy(victim)

	release_body_manager.get_release_body_manager():moving_dead_body(victim)
	if (who and bone_index ~= "from_death_callback") and (IsStalker(who) or IsMonster(who)) then
		-- Start [Alundaio]
		SendScriptCallback("npc_on_death_callback",victim,who)
		db.storage[id].death_time = game.get_game_time()
		db.storage[id].death_by_id = who:id()
		-- End [Alundaio]
	end
	victim:set_callback(callback.hit,nil)
	victim:set_callback(callback.death,nil)
	victim:set_callback(callback.sound,nil)
	victim:set_patrol_extrapolate_callback(nil)
	
	--utils_data.debug_write(strformat("END motivator_binder:death_callback %s",victim and victim:name()))
end

function motivator_binder:use_callback(obj, who)
	--utils_data.debug_write(strformat("motivator_binder:use_callback %s",obj and obj:name()))
	local death_dropped = se_load_var(obj:id(),obj:name(),"death_dropped")
	if (not obj:alive()) and (not death_dropped) then
		death_manager.create_release_item(obj)

		-- Create Money
		if item_money then
			item_money.actor_on_loot_money(obj)
        end
		
		-- Unlock npc's faction article
		local comm = character_community(obj)
		SendScriptCallback("actor_on_interaction", "factions", nil, comm)
		SendScriptCallback("npc_on_use",obj,who)
		return
	end
	trade_manager.update(obj)
	SetEvent("used_npc_id", obj:id())
	xr_meet.notify_on_use(obj, who)
	dialog_manager.disabled_phrases[obj:id()] = nil
	dialog_manager.RandomVal = math.random(1,3)
	if (self.st.active_scheme) then
		xr_logic.issue_event(obj, self.st[self.st.active_scheme], "use_callback", obj, who)
	end

	-- start [Alundaio]
	SendScriptCallback("npc_on_use",obj,who)
	-- end [Alundaio]

end

function motivator_binder:update(delta)
	object_binder.update(self, delta)
	--utils_data.debug_write(strformat("motivator_bind:update %s START",self.object and self.object:name()))

	local object = self.object
	local id = object:id()

	--utils_obj.debug_nearest(object,"motivator_binder:update(%s)",delta)
	
	xr_sound.update(id)
	
	-- check dead
	local object_alive = object:alive()
	if not (object_alive) then
		if not (self.first_update) then
			death_manager.decide_items_to_keep(object)
		end
		self.first_update = true
		
		object:set_tip_text_default()
		
		if (self.st.active_scheme) then
			xr_logic.try_switch_to_another_section(object, self.st[self.st.active_scheme], db.actor)
		end
		--utils_data.debug_write(strformat("motivator_bind:update %s END 1",self.object and self.object:name()))
		return
	end
	
	xr_meet.process_npc_usability(object)
	sr_light.check_light(object)
	
	if not (object:best_enemy()) then
		xr_combat_ignore.fighting_with_actor_npcs[id] = nil
	end
	
	self.squad = self.squad or get_object_squad(object)
	if (self.squad and self.squad:commander_id() == id) then
		self.squad:update()
	end
	
	if (self.st.state_mgr) then
		self.st.state_mgr:update()
	end
	
	-- Start [Alundaio]
	SendScriptCallback("npc_on_update",object,db.storage[id])
	-- End [Alundaio]

	stalker_generic.update_invulnerability(object)
	
	local tg = time_global()
	if (self.__tmr and tg < self.__tmr) then
		--utils_data.debug_write(strformat("motivator_bind:update %s END 2",self.object and self.object:name()))
		return
	end
	self.__tmr = tg+500
	
	-- DOOR
	ph_door.try_to_open_door(self.object, self.opened_doors)
	ph_door.try_to_close_door(self.object, self.opened_doors)

	if (self.st.active_scheme) then
		local switched
		if (self.st.combat) then
			if (self.st.overrides and self.st.overrides.on_combat) then
				local mgr = object:motivation_action_manager()
				if (mgr and mgr:initialized() and mgr:current_action_id() == stalker_ids.action_combat_planner) then
					if (self.st.overrides.on_combat) then
						xr_logic.pick_section_from_condlist(db.actor,object,self.st.overrides.on_combat.condlist)
					end
					if (xr_logic.try_switch_to_another_section(object,self.st.combat,db.actor)) then
						switched = true
					elseif (self.st.overrides.combat_type) then
						xr_combat.set_combat_type(object,db.actor,self.st.overrides)
					else 
						xr_combat.set_combat_type(object, db.actor, self.st.combat)
					end
				end
			else 
				xr_combat.set_combat_type(object, db.actor, self.st.combat)
			end
		end
		
		if not switched then
			xr_logic.try_switch_to_another_section(object, self.st[self.st.active_scheme], db.actor)
		end
	elseif (self.st.combat) then
		xr_combat.set_combat_type(object, db.actor, self.st.combat)
	end
	--utils_data.debug_write(strformat("motivator_bind:update %s BEFORE AXR_MAIN",self.object and self.object:name()))

	--utils_data.debug_write(strformat("motivator_bind:update %s END 3 AFTER AXR_MAIN",self.object and self.object:name()))
end

function motivator_binder:reload(section)
	object_binder.reload(self, section)
end

function motivator_binder:net_save_relevant()
	return true
end

function motivator_binder:save(packet)
	set_save_marker(packet, "save", false, "motivator_binder")
	object_binder.save(self, packet)
	set_save_marker(packet, "save", true, "motivator_binder")
		if (USE_MARSHAL) then
		return
	end
	--utils_data.debug_write("\n-------motivator save start------ "..self.object:name(),true)
	xr_logic.save_obj(self.object, packet)
	--trade_manager.save(self.object, packet)
	xr_sound.save_npc(packet, self.object:id())

	--utils_data.debug_write("\nmotivator save end------ "..self.object:name(),true)
end

function motivator_binder:load(reader)
	set_save_marker(reader, "load", false, "motivator_binder")
	object_binder.load(self, reader)
	set_save_marker(reader, "load", true, "motivator_binder")
		self.loaded = true
		if (USE_MARSHAL) then
		if (self.object) then
			self:load_state() -- fake because binder not ready when real load_state made
		end
		return
	end
	--utils_data.debug_write("\nmotivator load start "..self.object:name(),true)	
	xr_logic.load_obj(self.object, reader)
	--trade_manager.load(self.object, reader)
	xr_sound.load_npc(reader, self.object:id())
	--utils_data.debug_write("\nmotivator load END "..self.object:name())
end

function motivator_binder:hear_callback(npc, who_id, sound_type, sound_position, sound_power)
	--utils_data.debug_write(strformat("motivator_binder:hear_callback %s",npc and npc:name()))
	if not (npc:alive()) then
		return
	end

	if who_id == npc:id() then
		return
	end
	xr_hear.hear_callback(npc, who_id, sound_type, sound_position, sound_power)
end

----------------------------------------------------------------------------------
function motivator_binder:save_state(m_data)
	--utils_data.debug_write(strformat("motivator_binder:save_state %s BEFORE",self.object:name()))
	local id = self.object and self.object:id()
	local st = id and db.storage[id]
	if not (st and m_data) then
		return
	end
	local state = alife_storage_manager.get_game_object_state(self.object,true)
	state.xr_logic = empty_table(state.xr_logic)
	--state.xr_logic.job_ini = st.job_ini
	state.xr_logic.ini_filename = st.ini_filename
	state.xr_logic.section_logic = st.section_logic
	state.xr_logic.active_section = st.active_section
	state.xr_logic.gulag_name = st.gulag_name
	state.xr_logic.activation_time = (st.activation_time or 0) - time_global()
	state.xr_logic.activation_game_time = st.activation_game_time

	xr_sound.npc_save_state(id,m_data)
	--trade_manager.save_state(id,m_data)
	db.storage[id].pstor = db.storage[id].pstor or {}
	--utils_data.debug_write(strformat("motivator_binder:save_state %s AFTER",self.object:name()))
end

function motivator_binder:load_state()
	local state = alife_storage_manager.get_game_object_state(self.object)
	if not (state and state.xr_logic) then
		return
	end
	
	local id = self.object:id()
	db.storage[id] = db.storage[id] or {}
	local st = db.storage[id]
	--utils_data.debug_write(strformat("motivator_binder:load_state %s BEFORE",id))
	--st.job_ini = state.xr_logic.job_ini
	st.loaded_ini_filename = state.xr_logic.ini_filename
	st.loaded_section_logic = state.xr_logic.section_logic
	st.loaded_active_section = state.xr_logic.active_section
	st.loaded_gulag_name = state.xr_logic.gulag_name or ""
	st.activation_time = state.xr_logic.activation_time and state.xr_logic.activation_time + time_global() or time_global()
	st.activation_game_time = state.xr_logic.activation_game_time or game.get_game_time()
	xr_sound.npc_load_state(id,state)
	--trade_manager.load_state(id,state)
	if (state.pstor_all) then
		--db.storage[id].pstor = state.pstor_all
		state.pstor_all = nil
	end

	if (state.pstor_ctime) then
		db.storage[id].pstor_ctime = state.pstor_ctime
		state.pstor_ctime = nil
	end
	--utils_data.debug_write(strformat("motivator_binder:load_state %s AFTER",id))
end

----------------------------------
function motivator_binder:setup_known_info(npc,char_ini,known_info)
	known_info = known_info or "known_info"
	if char_ini:section_exist(known_info) then
		local n = char_ini:line_count(known_info)
		for i=0,n-1 do
			local result, id, value = char_ini:r_line(known_info,i,"","")
			npc:give_info_portion(id)
		end
	end
end