text: ////////////////////////////////////////////////////////////////////////////
  //	Module 		: ai_stalker_debug.cpp
  //	Created 	: 05.07.2005
  //  Modified 	: 05.07.2005
  //	Author		: Dmitriy Iassenev
  //	Description : Debug functions for monster "Stalker"
  ////////////////////////////////////////////////////////////////////////////
  
  #include "pch_script.h"
  
  #ifdef DEBUG
  #include "ai_stalker.h"
  #include "../../memory_manager.h"
  #include "../../visual_memory_manager.h"
  #include "../../sound_memory_manager.h"
  #include "../../hit_memory_manager.h"
  #include "../../enemy_manager.h"
  #include "../../danger_manager.h"
  #include "../../item_manager.h"
  #include "../../actor.h"
  #include "../../stalker_planner.h"
  #include "../../script_game_object.h"
  #include "../../stalker_animation_manager.h"
  #include "../../weapon.h"
  #include "../../sound_player.h"
  #include "../../inventory.h"
  #include "../../object_handler_planner.h"
  #include "../../stalker_movement_manager_smart_cover.h"
  #include "../../movement_manager_space.h"
  #include "../../patrol_path_manager.h"
  #include "../../level_path_manager.h"
  #include "../../game_path_manager.h"
  #include "../../detail_path_manager.h"
  #include "../../sight_manager.h"
  #include "../../ai_object_location.h"
  #include "../../entitycondition.h"
  #include "../ai_monsters_misc.h"
  #include "../../agent_manager.h"
  #include "../../agent_member_manager.h"
  #include "../../agent_enemy_manager.h"
  #include "../../agent_corpse_manager.h"
  #include "../../agent_location_manager.h"
  #include "../../cover_point.h"
  #include "../../../xrEngine/camerabase.h"
  #include "../../mt_config.h"
  #include "../../weaponmagazined.h"
  #include "../../object_handler_space.h"
  #include "../../debug_renderer.h"
  #include "../../CharacterPhysicsSupport.h"
  #include "../../smart_cover_animation_selector.h"
  #include "../../animation_movement_controller.h"
  #include "../../phdebug.h"
  #include "../../game_object_space.h"
  #include "../../aimers_weapon.h"
  #include "../../aimers_bone.h"
  #include "../../smart_cover_planner_target_selector.h"
  #include "../../ui_base.h"
  #include "../../doors_actor.h"
  
  CActor *g_debug_actor = 0;
  
  void try_change_current_entity()
  {
  	CActor								*actor = smart_cast<CActor*>(Level().CurrentEntity());
  	VERIFY								(actor);
  	g_debug_actor						= actor;
  
  	CFrustum							frustum;
  	frustum.CreateFromMatrix			(Device.mFullTransform,FRUSTUM_P_LRTB|FRUSTUM_P_FAR);
  
  	typedef xr_vector<ISpatial*>		OBJECTS;
  	OBJECTS								ISpatialResult;
  	g_SpatialSpace->q_frustum			(ISpatialResult, 0, STYPE_COLLIDEABLE, frustum);
  
  	float								maxlen = 1000.0f;
  	CCustomMonster*						nearest_agent = 0;
  
  	OBJECTS::const_iterator				I = ISpatialResult.begin();
  	OBJECTS::const_iterator				E = ISpatialResult.end();
  	for ( ; I != E; ++I) {
  		CCustomMonster					*current = smart_cast<CCustomMonster*>(*I);
  		if (!current)					continue;
  		if (Level().CurrentEntity()==current) continue;
  
  		Fvector							A, B, tmp; 
  		current->Center					(A);
  
  		tmp.sub							(A, actor->cam_Active()->vPosition);
  		B.mad							(
  			actor->cam_Active()->vPosition,
  			actor->cam_Active()->vDirection,
  			tmp.dotproduct(
  				actor->cam_Active()->vDirection
  			)
  		);
  		float len = B.distance_to_sqr(A);
  		if (len > 1) continue;
  
  		if (maxlen>len && !current->getDestroy())
  		{
  			maxlen = len;
  			nearest_agent = current;
  		};
  	}
  
  	if (!nearest_agent)
  		return;
  
  	Level().SetEntity		(nearest_agent);
  	actor->inventory().Items_SetCurrentEntityHud(false);
  	
  	Engine.Sheduler.Unregister	(actor);
  	Engine.Sheduler.Register	(actor);
  	
  	Engine.Sheduler.Unregister	(nearest_agent);
  	Engine.Sheduler.Register	(nearest_agent, TRUE);
  }
  
  void restore_actor()
  {
  	VERIFY		(g_debug_actor);
  	VERIFY		(!smart_cast<CActor*>(Level().CurrentEntity()));
  
  	Engine.Sheduler.Unregister	(Level().CurrentEntity());
  	Engine.Sheduler.Register	(Level().CurrentEntity());
  
  	Level().SetEntity			(g_debug_actor);
  
  	Engine.Sheduler.Unregister	(g_debug_actor);
  	Engine.Sheduler.Register	(g_debug_actor, TRUE);
  
  	g_debug_actor->inventory().Items_SetCurrentEntityHud(true);
  
  	CHudItem* pHudItem = smart_cast<CHudItem*>(g_debug_actor->inventory().ActiveItem());
  	if (pHudItem) 
  	{
  		pHudItem->OnStateSwitch(pHudItem->GetState());
  	}
  }
  
  template <typename planner_type>
  void draw_planner						(const planner_type &brain, LPCSTR start_indent, LPCSTR indent, LPCSTR planner_id)
  {
  	planner_type						&_brain = const_cast<planner_type&>(brain);
  	if (brain.solution().empty())
  		return;
  
  	CScriptActionPlannerAction			*planner = smart_cast<CScriptActionPlannerAction*>(&_brain.action(brain.solution().front()));
  	if (planner)
  		draw_planner					(*planner,start_indent,indent,_brain.action2string(brain.solution().front()));
  
  	DBG_OutText	("%s ",start_indent);
  	DBG_OutText	("%splanner %s",start_indent,planner_id);
  	DBG_OutText	("%s%sevaluators  : %d",start_indent,indent,brain.evaluators().size());
  	DBG_OutText	("%s%soperators   : %d",start_indent,indent,brain.operators().size());
  	DBG_OutText	("%s%sselected    : %s",start_indent,indent,_brain.action2string(brain.solution().front()));
  	// solution
  	DBG_OutText	("%s%ssolution",start_indent,indent);
  	for (int i=0; i<(int)brain.solution().size(); ++i)
  		DBG_OutText("%s%s%s%s",start_indent,indent,indent,_brain.action2string(brain.solution()[i]));
  	// current
  	DBG_OutText	("%s%scurrent world state",start_indent,indent);
  	planner_type::EVALUATORS::const_iterator	I = brain.evaluators().begin();
  	planner_type::EVALUATORS::const_iterator	E = brain.evaluators().end();
  	for ( ; I != E; ++I) {
  		xr_vector<planner_type::COperatorCondition>::const_iterator J = std::lower_bound(brain.current_state().conditions().begin(),brain.current_state().conditions().end(),planner_type::CWorldProperty((*I).first,false));
  		char				temp = '?';
  		if ((J != brain.current_state().conditions().end()) && ((*J).condition() == (*I).first)) {
  			temp			= (*J).value() ? '+' : '-';
  			DBG_OutText	("%s%s%s    %5c : [%d][%s]",start_indent,indent,indent,temp,(*I).first,_brain.property2string((*I).first));
  		}
  	}
  	// goal
  	DBG_OutText	("%s%starget world state",start_indent,indent);
  	I = brain.evaluators().begin();
  	for ( ; I != E; ++I) {
  		xr_vector<planner_type::COperatorCondition>::const_iterator J = std::lower_bound(brain.target_state().conditions().begin(),brain.target_state().conditions().end(),planner_type::CWorldProperty((*I).first,false));
  		char				temp = '?';
  		if ((J != brain.target_state().conditions().end()) && ((*J).condition() == (*I).first)) {
  			temp			= (*J).value() ? '+' : '-';
  			DBG_OutText	("%s%s%s    %5c : [%d][%s]",start_indent,indent,indent,temp,(*I).first,_brain.property2string((*I).first));
  		}
  	}
  }
  
  LPCSTR animation_name(CAI_Stalker *self, const MotionID &animation)
  {
  	if (!animation)
  		return			("");
  	IKinematicsAnimated	*skeleton_animated = smart_cast<IKinematicsAnimated*>(self->Visual());
  	VERIFY				(skeleton_animated);
  	LPCSTR				name = skeleton_animated->LL_MotionDefName_dbg(animation).first;
  	return				(name);
  }
  
  void draw_restrictions(const shared_str &restrictions, LPCSTR start_indent, LPCSTR indent, LPCSTR header)
  {
  	DBG_OutText	("%s%s%s",start_indent,indent,header);
  	string256	temp;
  	for (u32 i=0, n=_GetItemCount(*restrictions); i<n; ++i)
  		DBG_OutText("%s%s%s%s",start_indent,indent,indent,_GetItem(*restrictions,i,temp));
  }
  
  LPCSTR movement_type(const MonsterSpace::EMovementType &movement_type)
  {
  	switch (movement_type) {
  		case MonsterSpace::eMovementTypeStand :
  			return		("stand");
  		case MonsterSpace::eMovementTypeWalk  :
  			return		("walk");
  		case MonsterSpace::eMovementTypeRun   :
  			return		("run");
  		default			: NODEFAULT;
  	}
  	return				("invalid");
  }
  
  LPCSTR danger_type(const CDangerObject::EDangerType &danger_type)
  {
  	switch (danger_type) {
  		case CDangerObject::eDangerTypeBulletRicochet		: return	("bullet ricochet");
  		case CDangerObject::eDangerTypeAttackSound			: return	("attack sound");
  		case CDangerObject::eDangerTypeEntityAttacked		: return	("entity attacked");
  		case CDangerObject::eDangerTypeEntityDeath			: return	("entity death");
  		case CDangerObject::eDangerTypeFreshEntityCorpse	: return	("fresh entity corpse");
  		case CDangerObject::eDangerTypeAttacked				: return	("I am attacked");
  		case CDangerObject::eDangerTypeGrenade				: return	("greande nearby");
  		case CDangerObject::eDangerTypeEnemySound			: return	("enemy sound");
  		default												: NODEFAULT;
  	};
  	return				("");
  }
  
  void CAI_Stalker::debug_planner			(const script_planner *planner)
  {
  	m_debug_planner						= planner;
  }
  
  void CAI_Stalker::debug_text			()
  {
  	if( !m_dbg_hud_draw )
  		return;
  	m_dbg_hud_draw = false;
  
  	if (!psAI_Flags.test(aiStalker))
  		return;
  
  	CActor								*actor = smart_cast<CActor*>(Level().Objects.net_Find(0));
  	if (!actor) {
  		if (!g_debug_actor)
  			return;
  
  		actor							= g_debug_actor;
  	}
  
  	float								up_indent = 40.f;
  	LPCSTR								indent = "  ";
  
  	DBG_TextSetColor(D3DCOLOR_XRGB(0,255,0));
  	DBG_TextOutSet	(0,up_indent);
  	// memory
  	DBG_OutText	("memory");
  	DBG_OutText	("%sname          : %s",indent,*cName());
  	DBG_OutText	("%ssection       : %s",indent,*cNameSect());
  	DBG_OutText	("%sid            : %d",indent,ID());
  	DBG_OutText	("%shealth        : %f",indent,conditions().health());
  	DBG_OutText	("%swounded       : %c",indent,wounded() ? '+' : '-');
  	// visual
  	DBG_OutText	("%svisual",indent);
  	
  	float								object_range, object_fov;
  	update_range_fov					(object_range,object_fov,eye_range,deg2rad(eye_fov));
  	DBG_OutText	("%s%seye range   : %f",indent,indent,object_range);
  	DBG_OutText	("%s%sFOV         : %f",indent,indent,rad2deg(object_fov));
  	if (g_Alive()) {
  		DBG_OutText	("%s%sobjects     : %d",indent,indent,memory().visual().objects().size());
  		DBG_OutText	("%s%snot yet     : %d",indent,indent,memory().visual().not_yet_visible_objects().size());
  		DBG_OutText	("%s%sin frustum  : %d",indent,indent,memory().visual().raw_objects().size());
  		if (memory().visual().visible_now(actor))
  			DBG_OutText("%s%sactor       : visible",indent,indent);
  		else {
  			MemorySpace::CNotYetVisibleObject	*object = memory().visual().not_yet_visible_object(actor);
  			if (object && !fis_zero(object->m_value))
  				DBG_OutText("%s%sactor       : not yet visible : %f",indent,indent,object->m_value);
  			else
  				DBG_OutText("%s%sactor       : not visible",indent,indent);
  		}
  		// sound
  		DBG_OutText	("%ssound",indent);
  		DBG_OutText	("%s%sobjects     : %d",indent,indent,memory().sound().objects().size());
  #ifdef USE_SELECTED_SOUND
  		if (memory().sound().sound()) {
  			DBG_OutText	("%s%sselected",indent,indent);
  			DBG_OutText	("%s%s%stype",indent,indent,indent);
  			DBG_OutText	("%s%s%spower     : %f",indent,indent,indent,memory().sound().sound()->m_power);
  			DBG_OutText	("%s%s%sobject    : %s",indent,indent,indent,memory().sound().sound()->m_object ? *memory().sound().sound()->m_object->cName() : "unknown");
  			if (g_Alive() && memory().sound().sound()->m_object)
  				DBG_OutText("%s%s%svisible   : %s",indent,indent,indent,memory().visual().visible_now(memory().sound().sound()->m_object) ? "+" : "-");
  		}
  #endif
  		// hit
  		DBG_OutText	("%shit",indent);
  		DBG_OutText	("%s%sobjects     : %d",indent,indent,memory().hit().objects().size());
  		ALife::_OBJECT_ID					object_id = memory().hit().last_hit_object_id();
  		DBG_OutText	("%s%slast hit object id   : %d",indent,indent,object_id);
  		CObject								*object = (object_id == ALife::_OBJECT_ID(-1)) ? 0 : Level().Objects.net_Find(object_id);
  		DBG_OutText	("%s%slast hit object name : %s",indent,indent,object ? *object->cName() : "");
  #ifdef USE_SELECTED_HIT
  		if (memory().hit().hit()) {
  			DBG_OutText	("%s%sselected",indent,indent);
  			DBG_OutText	("%s%s%spower     : %f",indent,indent,indent,memory().hit().hit()->m_amount);
  			DBG_OutText	("%s%s%sobject    : %s",indent,indent,indent,memory().hit().hit()->m_object ? *memory().hit().hit()->m_object->cName() : "unknown");
  			if (g_Alive() && memory().hit().hit()->m_object)
  				DBG_OutText("%s%s%svisible   : %s",indent,indent,indent,memory().visual().visible_now(memory().hit().hit()->m_object) ? "+" : "-");
  		}
  #endif
  	}
  	// enemy
  	DBG_OutText	("%senemy",indent);
  	if (inventory().ActiveItem()) {
  		DBG_OutText("%s%scan kill member   : %s",indent,indent,can_kill_member() ? "+" : "-");
  		DBG_OutText("%s%scan kill enemy    : %s",indent,indent,can_kill_enemy() ? "+" : "-");
  		DBG_OutText("%s%spick distance     : %f",indent,indent,pick_distance());
  		DBG_OutText("%s%sfire make sense   : %s",indent,indent,fire_make_sense() ? "+" : "-");
  		DBG_OutText("%s%sactor is enemy    : %c",indent,indent,is_relation_enemy(actor) ? '+' : '-');
  		DBG_OutText("%s%sis enemy to actor : %c",indent,indent,actor->is_relation_enemy(this) ? '+' : '-');
  	}
  
  	DBG_OutText	("%s%sobjects     : %d",indent,indent,memory().enemy().objects().size());
  	if (g_Alive()) {
  		CEnemyManager::OBJECTS::const_iterator	I = memory().enemy().objects().begin();
  		CEnemyManager::OBJECTS::const_iterator	E = memory().enemy().objects().end();
  		for ( ; I != E; ++I)
  			DBG_OutText	("%s%s%s%s : %s",indent,indent,indent,*(*I)->cName(),memory().visual().visible_now(*I) ? "visible" : "invisible");
  	}
  
  	if (memory().enemy().selected()) {
  		DBG_OutText	("%s%sselected",indent,indent);
  		
  		float								fuzzy = 0.f;
  		xr_vector<feel_visible_Item>::iterator I=feel_visible.begin(),E=feel_visible.end();
  		for (; I!=E; I++)
  			if (I->O->ID() == memory().enemy().selected()->ID()) {
  				fuzzy						= I->fuzzy;
  				break;
  			}
  
  		if (g_Alive()) {
  			if ( true || !g_mt_config.test(mtAiVision) )
  				VERIFY						(!memory().visual().visible_now(memory().enemy().selected()) || (fuzzy > 0.f));
  			DBG_OutText("%s%s%svisible   : %s %f",indent,indent,indent,memory().visual().visible_now(memory().enemy().selected()) ? "+" : "-",fuzzy);
  		}
  		DBG_OutText	("%s%s%sobject    : %s",indent,indent,indent,*memory().enemy().selected()->cName());
  		if (g_Alive()) {
  			float								interval = (1.f - panic_threshold())*.25f, left = -1.f, right = -1.f;
  			LPCSTR								description = "invalid";
  			u32									result = dwfChooseAction(
  				2000,
  				1.f - interval,
  				1.f - 2*interval,
  				1.f - 3*interval,
  				panic_threshold(),
  				g_Team(),
  				g_Squad(),
  				g_Group(),
  				0,
  				1,
  				2,
  				3,
  				4,
  				this,
  				300.f
  			);
  			switch (result) {
  				case 0 : {
  					description					= "attack";
  					left						= 1.f;
  					right						= 1.f - 1.f*interval;
  					break;
  				}
  				case 1 : {
  					description					= "careful attack";
  					left						= 1.f - 1.f*interval;
  					right						= 1.f - 2.f*interval;
  					break;
  				}
  				case 2 : {
  					description					= "defend";
  					left						= 1.f - 2.f*interval;
  					right						= 1.f - 3.f*interval;
  					break;
  				}
  				case 3 : {
  					description					= "retreat";
  					left						= 1.f - 3*interval;
  					right						= panic_threshold();
  					break;
  				}
  				case 4 : {
  					description					= "panic";
  					left						= panic_threshold();
  					right						= 0.f;
  					break;
  				}
  				default : NODEFAULT;
  			}
  			DBG_OutText	("%s%s%svictory   : [%5.2f%%,%5.2f%%] -> %s",indent,indent,indent,100.f*right,100.f*left,description);
  		}
  	}
  	// danger
  	DBG_OutText	("%sdanger",indent);
  	DBG_OutText	("%s%sobjects     : %d",indent,indent,memory().danger().objects().size());
  	if (memory().danger().selected() && memory().danger().selected()->object()) {
  		DBG_OutText	("%s%sselected",indent,indent);
  		DBG_OutText	("%s%s%stype      : %s",indent,indent,indent,danger_type(memory().danger().selected()->type()));
  		DBG_OutText	("%s%s%stime      : %.3f (%.3f)",indent,indent,indent,float(memory().danger().selected()->time())/1000.f,float(Device.dwTimeGlobal - memory().danger().selected()->time())/1000.f);
  		DBG_OutText	("%s%s%sinitiator : %s",indent,indent,indent,*memory().danger().selected()->object()->cName());
  		if (g_Alive() && memory().danger().selected()->object())
  			DBG_OutText("%s%s%svisible   : %s",indent,indent,indent,memory().visual().visible_now(memory().danger().selected()->object()) ? "+" : "-");
  
  		if (memory().danger().selected()->dependent_object() && !!memory().danger().selected()->dependent_object()->cName()) {
  			DBG_OutText("%s%s%sdependent : %s",indent,indent,indent,*memory().danger().selected()->dependent_object()->cName());
  			if (g_Alive())
  				DBG_OutText("%s%s%svisible   : %s",indent,indent,indent,memory().visual().visible_now(smart_cast<const CGameObject*>(memory().danger().selected()->dependent_object())) ? "+" : "-");
  		}
  	}
  
  	DBG_OutText	("%sanomalies",indent);
  	DBG_OutText	("%s%sundetected  : %s",indent,indent,undetected_anomaly() ? "+" : "-");
  	DBG_OutText	("%s%sinside      : %s",indent,indent,inside_anomaly() ? "+" : "-");
  
  	// agent manager
  	DBG_OutText	(" ");
  	DBG_OutText	("agent manager");
  	if (g_Alive()) {
  	DBG_OutText	("%smembers           : %d",indent,agent_manager().member().members().size());
  	DBG_OutText	("%senemies           : %d",indent,agent_manager().enemy().enemies().size());
  	DBG_OutText	("%scorpses           : %d",indent,agent_manager().corpse().corpses().size());
  	DBG_OutText	("%sdanger locations  : %d",indent,agent_manager().location().locations().size());
  	DBG_OutText	("%smembers in combat : %d",indent,agent_manager().member().combat_members().size());
  	if (g_Alive())
  		DBG_OutText("%sI am in combat    : %s",indent,agent_manager().member().registered_in_combat(this) ? "+" : "-");
  	DBG_OutText	("%smembers in detour : %d",indent,agent_manager().member().in_detour());
  	if (g_Alive())
  		DBG_OutText("%sI am in detour    : %s",indent,agent_manager().member().member(this).detour() ? "+" : "-");
  
  		if (g_Alive()) {
  			if (agent_manager().member().member(this).cover()) {
  				DBG_OutText("%scover         : [%s][%f][%f][%f]",indent,agent_manager().member().member(this).cover()->m_is_smart_cover ? "smart" : "generated", VPUSH(agent_manager().member().member(this).cover()->position()));
  			}
  
  			if (agent_manager().member().member(this).member_death_reaction().m_processing)
  				DBG_OutText("%react on death : %s",indent,*agent_manager().member().member(this).member_death_reaction().m_member->cName());
  
  			if (agent_manager().member().member(this).grenade_reaction().m_processing)
  				DBG_OutText("%react on grenade : %s",indent,agent_manager().member().member(this).grenade_reaction().m_game_object ? *agent_manager().member().member(this).grenade_reaction().m_game_object->cName() : "unknown");
  		}
  	}
  
  	// objects
  	DBG_OutText	(" ");
  	DBG_OutText	("%sobjects",indent);
  	DBG_OutText	("%s%sobjects             : %d",indent,indent,inventory().m_all.size());
  	DBG_OutText	("%s%sactive item         : %s",indent,indent,inventory().ActiveItem() ? *inventory().ActiveItem()->object().cName() : "");
  	DBG_OutText	("%s%sbest weapon         : %s",indent,indent,best_weapon() ? *best_weapon()->object().cName() : "");
  	DBG_OutText	("%s%sitem to kill        : %s",indent,indent,item_to_kill() ? *m_best_item_to_kill->object().cName() : "");
  	DBG_OutText	("%s%sitem can kill       : %s",indent,indent,item_can_kill() ? "+" : "-");
  	DBG_OutText	("%s%smemory item to kill : %s",indent,indent,remember_item_to_kill() ? *m_best_found_item_to_kill->object().cName() : "");
  	DBG_OutText	("%s%smemory ammo         : %s",indent,indent,remember_ammo() ? *m_best_found_ammo->object().cName() : "");
  	DBG_OutText	("%s%sinfinite ammo       : %s",indent,indent,m_infinite_ammo ? "+" : "-");
  	DBG_OutText	("%s%sitem to spawn       : %s",indent,indent,item_to_spawn().size() ? *item_to_spawn() : "no item to spawn");
  	DBG_OutText	("%s%sammo in box to spawn: %d",indent,indent,item_to_spawn().size() ? ammo_in_box_to_spawn() : 0);
  	
  	CWeaponMagazined					*weapon = smart_cast<CWeaponMagazined*>(inventory().ActiveItem());
  	if (weapon) {
  		CObjectHandlerPlanner			&planner = CObjectHandler::planner();
  		DBG_OutText("%s%squeue size          : %d",indent,indent,weapon->GetQueueSize());
  		DBG_OutText("%s%squeue interval      : %d",
  			indent,
  			indent,
  			planner.action(
  				planner.uid(
  					weapon->ID(),
  					ObjectHandlerSpace::eWorldOperatorQueueWait1
  				)
  			).inertia_time()
  		);
  	}
  	
  	if (inventory().ActiveItem()) {
  		DBG_OutText	("%s%sactive item",indent,indent);
  		DBG_OutText	("%s%s%sobject         : %s",indent,indent,indent,inventory().ActiveItem() ? *inventory().ActiveItem()->object().cName() : "");
  		CWeapon	*weapon = smart_cast<CWeapon*>(inventory().ActiveItem());
  		if (weapon) {
  			DBG_OutText("%s%s%sstrapped       : %s",indent,indent,indent,weapon_strapped(weapon) ? "+" : "-");
  			DBG_OutText("%s%s%sunstrapped     : %s",indent,indent,indent,weapon_unstrapped(weapon) ? "+" : "-");
  			DBG_OutText("%s%s%sammo           : %d",indent,indent,indent,weapon->GetAmmoElapsed());
  			DBG_OutText("%s%s%smagazine       : %d",indent,indent,indent,weapon->GetAmmoMagSize());
  			DBG_OutText("%s%s%stotal ammo     : %d",indent,indent,indent,weapon->GetSuitableAmmoTotal());
  		}
  	}
  
  	string256							temp;
  
  	const CObjectHandlerPlanner			&objects = planner();
  	strconcat							(sizeof(temp),temp,indent,indent);
  	draw_planner						(objects,temp,indent,"root");
  
  	DBG_TextOutSet		(330,up_indent);
  	
  	// brain
  	DBG_OutText	("brain");
  	
  	// actions
  	draw_planner						(this->brain(),indent,indent,"root");
  	draw_planner						(movement().animation_selector().planner(), indent, indent, "smart cover planner");
  
  	// debug planner
  	if (m_debug_planner)
  		draw_planner					(*m_debug_planner,indent,indent,"debug_planner");
  	
  	DBG_TextOutSet	(640,up_indent);
  	// brain
  	DBG_OutText	("controls");
  	// animations
  	DBG_OutText	("%sanimations",indent);
  	DBG_OutText	("%s%sforward     : [%c]",indent,indent, animation().forward_blend_callbacks() ? '+' : '-');
  	DBG_OutText	("%s%sbackward    : [%c]",indent,indent, animation().backward_blend_callbacks() ? '+' : '-');
  
  	DBG_OutText	("%s%shead        : [%s][%f]",indent,indent,
  		animation_name(this,animation().head().animation()),
  		animation().head().blend() ? animation().head().blend()->timeCurrent : 0.f
  	);
  	DBG_OutText	("%s%storso       : [%s][%f]",indent,indent,
  		animation_name(this,animation().torso().animation()),
  		animation().torso().blend() ? animation().torso().blend()->timeCurrent : 0.f
  	);
  	DBG_OutText	("%s%slegs        : [%s][%f]",indent,indent,
  		animation_name(this,animation().legs().animation()),
  		animation().legs().blend() ? animation().legs().blend()->timeCurrent : 0.f
  	);
  	DBG_OutText	("%s%sglobal      : [%s][%f]",indent,indent,
  		animation_name(this,animation().global().animation()),
  		animation().global().blend() ? animation().global().blend()->timeCurrent : 0.f
  	);
  	DBG_OutText	("%s%sscript      : [%s][%f]",indent,indent,
  		animation_name(this,animation().script().animation()),
  		animation().script().blend() ? animation().script().blend()->timeCurrent : 0.f
  	);
  
  	// movement
  	DBG_OutText	(" ");
  	DBG_OutText	("%smovement",indent);
  	DBG_OutText	("%s%senabled         : %s",indent,indent,movement().enabled() ? "+" : "-");
  
  	LPCSTR								mental_state = "invalid";
  	switch (movement().mental_state()) {
  		case MonsterSpace::eMentalStateFree : {
  			mental_state				= "free";
  			break;
  		}
  		case MonsterSpace::eMentalStateDanger : {
  			mental_state				= "danger";
  			break;
  		}
  		case MonsterSpace::eMentalStatePanic : {
  			mental_state				= "panic";
  			break;
  		}
  		default : NODEFAULT;
  	}
  	DBG_OutText	("%s%smental state    : %s",indent,indent,mental_state);
  
  	LPCSTR								body_state = "invalid";
  	switch (movement().body_state()) {
  		case MonsterSpace::eBodyStateStand : {
  			body_state					= "stand";
  			break;
  		}
  		case MonsterSpace::eBodyStateCrouch : {
  			body_state					= "crouch";
  			break;
  		}
  		default : NODEFAULT;
  	}
  	DBG_OutText	("%s%sbody state      : %s",indent,indent,body_state);
  	DBG_OutText	("%s%smovement type   : %s (current)",indent,indent,movement_type(movement().movement_type()));
  	DBG_OutText	("%s%smovement type   : %s (target)",indent,indent, movement_type(movement().target_movement_type()));
  
  	LPCSTR						path_type = "invalid";
  	switch (movement().path_type()) {
  		case MovementManager::ePathTypeGamePath : {
  			path_type			= "game path";
  			break;
  		}
  		case MovementManager::ePathTypeLevelPath : {
  			path_type			= "level path";
  			break;
  		}
  		case MovementManager::ePathTypePatrolPath : {
  			path_type			= "patrol path";
  			break;
  		}
  		case MovementManager::ePathTypeNoPath : {
  			path_type			= "no path";
  			break;
  		}
  		default : NODEFAULT;
  	}
  	DBG_OutText	("%s%spath type       : %s",indent,indent,path_type);
  	DBG_OutText	("%s%sposition        : [%f][%f][%f]",indent,indent,VPUSH(Position()));
  	DBG_OutText	("%s%slevel vertex id : %d",indent,indent,ai_location().level_vertex_id());
  	DBG_OutText	("%s%sgame vertex id  : %d",indent,indent,ai_location().game_vertex_id());
  	
  	if (movement().path_type() == MovementManager::ePathTypePatrolPath) {
  		DBG_OutText("%s%spatrol",indent,indent);
  		DBG_OutText("%s%s%spath          : %s",indent,indent,indent,*movement().patrol().path_name());
  		DBG_OutText("%s%s%scompleted     : %s",indent,indent,indent,movement().patrol().completed() ? "+" : "-");
  		DBG_OutText("%s%s%scurrent point : %d",indent,indent,indent,movement().patrol().get_current_point_index());
  		if	(
  				movement().patrol().get_path()
  				&&
  				movement().patrol().get_path()->vertex(movement().patrol().get_current_point_index())
  			)
  			DBG_OutText("%s%s%sextrapolate   : %s",indent,indent,indent,movement().patrol().extrapolate_path() ? "+" : "-");
  		else
  			DBG_OutText("%s%s%sextrapolate   : unknown",indent,indent,indent);
  	}
  
  	if (movement().path_type() == MovementManager::ePathTypeGamePath) {
  		DBG_OutText("%s%sgame",indent,indent);
  		DBG_OutText("%s%s%scompleted     : %s",indent,indent,indent,movement().game_path().completed() ? "+" : "-");
  		DBG_OutText("%s%s%spath size     : %d",indent,indent,indent,movement().game_path().path().size());
  		DBG_OutText("%s%s%scurrent point : %d",indent,indent,indent,movement().game_path().intermediate_index());
  	}
  	
  	DBG_OutText	("%s%slevel",indent,indent);
  	DBG_OutText	("%s%s%spath size     : %d",indent,indent,indent,movement().level_path().path().size());
  	DBG_OutText	("%s%s%sstart vertex  : %d",indent,indent,indent,movement().level_path().path().empty() ? -1 : movement().level_path().path().front());
  	DBG_OutText	("%s%s%sdest vertex   : %d",indent,indent,indent,movement().level_path().path().empty() ? -1 : movement().level_path().path().back());
  
  	DBG_OutText	("%s%sdetail",indent,indent);
  	DBG_OutText	("%s%s%svelocities    : %d",indent,indent,indent,movement().detail().velocities().size());
  	DBG_OutText	("%s%s%sextrapolate   : %f",indent,indent,indent,movement().detail().extrapolate_length());
  	DBG_OutText	("%s%s%spath size     : %d",indent,indent,indent,movement().detail().path().size());
  	if (!movement().detail().path().empty()) {
  		DBG_OutText	("%s%s%sstart point   : [%f][%f][%f]",indent,indent,indent,VPUSH(movement().detail().path().front().position));
  		DBG_OutText	("%s%s%sdest point    : [%f][%f][%f]",indent,indent,indent,VPUSH(movement().detail().path().back().position));
  		DBG_OutText	("%s%s%scurrent point",indent,indent,indent);
  		DBG_OutText	("%s%s%s%sindex     : %d",indent,indent,indent,indent,movement().detail().curr_travel_point_index());
  		DBG_OutText	("%s%s%s%sposition  : [%f][%f][%f]",indent,indent,indent,indent,VPUSH(movement().detail().path()[movement().detail().curr_travel_point_index()].position));
  		CDetailPathManager::STravelParams	current_velocity = movement().detail().velocity(movement().detail().path()[movement().detail().curr_travel_point_index()].velocity);
  		DBG_OutText	("%s%s%s%slinear    : %f",    indent,indent,indent,indent,current_velocity.linear_velocity);
  		DBG_OutText	("%s%s%s%sangular   : %f deg",indent,indent,indent,indent,rad2deg(current_velocity.angular_velocity));
  		DBG_OutText	("%s%s%s%sangular(R): %f deg",indent,indent,indent,indent,rad2deg(current_velocity.real_angular_velocity));
  		DBG_OutText	("%s%s%sspeed(calc)   : %f",indent,indent,indent,movement().speed());
  		DBG_OutText	("%s%s%sspeed(physics): %f",indent,indent,indent,movement().speed(character_physics_support()->movement()));
  	}
  
  	if (movement().detail().use_dest_orientation())
  		DBG_OutText("%s%s%sorientation   : + [%f][%f][%f]",indent,indent,indent,VPUSH(movement().detail().dest_direction()));
  	else
  		DBG_OutText("%s%s%sorientation   : -",indent,indent,indent);
  
  
  	DBG_OutText	("%s%ssmart covers",indent,indent);
  	DBG_OutText	("%s%s%ssmart cover current : %s",indent,indent,indent,movement().current_params().cover_id().c_str());
  	DBG_OutText	("%s%s%ssmart cover target  : %s",indent,indent,indent,movement().target_params().cover_id().c_str());
  	DBG_OutText	("%s%s%sloophole current    : %s",indent,indent,indent,movement().current_params().cover() ? movement().current_params().cover_loophole_id() : "");
  	DBG_OutText	("%s%s%sloophole target     : %s",indent,indent,indent,movement().target_params().cover() ? movement().target_params().cover_loophole_id() : "");
  
  	DBG_OutText	("%s%s%sidle min time       : %.2f",indent,indent,indent,movement().idle_min_time());
  	DBG_OutText	("%s%s%sidle max time       : %.2f",indent,indent,indent,movement().idle_max_time());
  	DBG_OutText	("%s%s%slookout min time    : %.2f",indent,indent,indent,movement().lookout_min_time());
  	DBG_OutText	("%s%s%slookout max time    : %.2f",indent,indent,indent,movement().lookout_max_time());
  
  	DBG_OutText	("%s%s%sapply loophole direction distance : %.2f",indent,indent,indent,movement().apply_loophole_direction_distance());
  	
  	DBG_OutText	("%s%s%suse smart covers only : %c",indent,indent,indent,use_smart_covers_only() ? '+' : '-');
  	DBG_OutText	("%s%s%sin smart cover      : %c",indent,indent,indent,lua_game_object()->in_smart_cover() ? '+' : '-');
  
  	if (movement().current_params().cover()) {
  		if (movement().current_params().cover_loophole()) {
  			if (movement().current_params().cover_fire_position()) {
  				DBG_OutText	(
  					"%s%s%sin loophole fov     : %c",
  					indent,
  					indent,
  					indent,
  					movement().in_current_loophole_fov(
  						*movement().current_params().cover_fire_position()
  					)
  					? '+' : '-'
  				);
  				DBG_OutText	(
  					"%s%s%sin loophole range   : %c",
  					indent,
  					indent,
  					indent,
  					movement().in_current_loophole_range(
  						*movement().current_params().cover_fire_position()
  					)
  					? '+' : '-'
  				);
  			}
  			else if (movement().current_params().cover_fire_object()) {
  				DBG_OutText	(
  					"%s%s%sin loophole fov     : %c",
  					indent,
  					indent,
  					indent,
  					movement().in_current_loophole_fov(
  						movement().current_params().cover_fire_object()->Position()
  					)
  					? '+' : '-'
  				);
  				DBG_OutText	(
  					"%s%s%sin loophole range   : %c",
  					indent,
  					indent,
  					indent,
  					movement().in_current_loophole_range(
  						movement().current_params().cover_fire_object()->Position()
  					)
  					? '+' : '-'
  				);
  			}
  			else if (memory().enemy().selected()) {
  				Fvector							position = memory().memory_position(memory().enemy().selected());
  				DBG_OutText	(
  					"%s%s%sin loophole fov     : %c",
  					indent,
  					indent,
  					indent,
  					movement().in_current_loophole_fov(
  						position
  					)
  					? '+' : '-'
  				);
  				DBG_OutText	(
  					"%s%s%sin loophole range   : %c",
  					indent,
  					indent,
  					indent,
  					movement().in_current_loophole_range(
  						position
  					)
  					? '+' : '-'
  				);
  			}
  		}
  	}
  	
  	if (movement().current_params().cover_fire_position()) {
  		Fvector							position = *movement().current_params().cover_fire_position();
  		DBG_OutText	("%s%s%sfire position current : [%f][%f][%f]",indent,indent,indent,VPUSH(position));
  	}
  
  	if (movement().target_params().cover_fire_position()) {
  		Fvector							position = *movement().target_params().cover_fire_position();
  		DBG_OutText	("%s%s%sfire position target  : [%f][%f][%f]",indent,indent,indent,VPUSH(position));
  	}
  
  	if (movement().current_params().cover_fire_object())
  		DBG_OutText	("%s%s%sfire object current   : %s",indent,indent,indent,movement().current_params().cover_fire_object()->cName().c_str());
  
  	if (movement().target_params().cover_fire_object())
  		DBG_OutText	("%s%s%sfire object target    : %s",indent,indent,indent,movement().target_params().cover_fire_object()->cName().c_str());
  
  	DBG_OutText	("%s%s%sdefault behaviour   : %c",indent,indent,indent, movement().current_params().cover() && movement().default_behaviour() ? '+' : '-');
  	strconcat								(sizeof(temp),temp,indent,indent,indent);
  	draw_planner							(movement().target_selector(),temp,indent,"target selector");
  
  	if	(
  			movement().restrictions().out_restrictions().size() ||
  			movement().restrictions().in_restrictions().size() ||
  			movement().restrictions().base_out_restrictions().size() ||
  			movement().restrictions().base_in_restrictions().size()
  		) {
  		DBG_OutText	("%s%srestrictions",indent,indent);
  		strconcat							(sizeof(temp),temp,indent,indent,indent);
  		draw_restrictions					(movement().restrictions().out_restrictions(),temp,indent,"out");
  		draw_restrictions					(movement().restrictions().in_restrictions(),temp,indent,"in");
  		draw_restrictions					(movement().restrictions().base_out_restrictions(),temp,indent,"base out");
  		draw_restrictions					(movement().restrictions().base_in_restrictions(),temp,indent,"base in");
  	}
  
  	// sounds
  	DBG_OutText	(" ");
  	DBG_OutText	("%ssounds",indent);
  	DBG_OutText	("%s%scollections : %d",indent,indent,sound().objects().size());
  	
  	{
  		u32			object_count = 0;
  		CSoundPlayer::SOUND_COLLECTIONS::const_iterator	I = sound().objects().begin();
  		CSoundPlayer::SOUND_COLLECTIONS::const_iterator	E = sound().objects().end();
  		for ( ; I != E; ++I)
  			object_count	+= (*I).second.second->m_sounds.size();
  		DBG_OutText("%s%sobjects     : %d",indent,indent,object_count);
  	}
  	{
  		xr_vector<CSoundPlayer::CSoundSingle>::const_iterator	I = sound().playing_sounds().begin();
  		xr_vector<CSoundPlayer::CSoundSingle>::const_iterator	E = sound().playing_sounds().end();
  		for ( ; I != E; ++I)
  			DBG_OutText(
  				"%s%s%s[%s]%s",
  				indent,
  				indent,
  				indent,
  				(Device.dwTimeGlobal < (*I).m_start_time)
  				?
  				"not yet started"
  				:
  				(
  					(*I).m_sound->_feedback()
  					?
  					"playing"
  					:
  					"already played"
  				),
  				(*I).m_sound->_handle() ? (*I).m_sound->_handle()->file_name() : "no source"
  			);
  	}
  
  	// sight
  	DBG_OutText	(" ");
  	DBG_OutText	("%ssight",indent);
  
  	LPCSTR								sight_type = "invalid";
  	switch (sight().current_action().sight_type()) {
  		case SightManager::eSightTypeCurrentDirection : {
  			sight_type					= "current direction";
  			break;
  		}
  		case SightManager::eSightTypePathDirection : {
  			sight_type					= "path direction";
  			break;
  		}
  		case SightManager::eSightTypeDirection : {
  			sight_type					= "direction";
  			break;
  		}
  		case SightManager::eSightTypePosition : {
  			sight_type					= "position";
  			break;
  		}
  		case SightManager::eSightTypeObject : {
  			sight_type					= "object";
  			break;
  		}
  		case SightManager::eSightTypeCover : {
  			sight_type					= "cover";
  			break;
  		}
  		case SightManager::eSightTypeSearch : {
  			sight_type					= "search";
  			break;
  		}
  		case SightManager::eSightTypeLookOver : {
  			sight_type					= "look over";
  			break;
  		}
  		case SightManager::eSightTypeCoverLookOver : {
  			sight_type					= "cover look over";
  			break;
  		}
  		case SightManager::eSightTypeFireObject : {
  			sight_type					= "fire object";
  			break;
  		}
  		case SightManager::eSightTypeAnimationDirection : {
  			sight_type					= "animation direction";
  			break;
  		}
  		default : NODEFAULT;
  	}
  
  	DBG_OutText	("%s%senabled         : %c",indent,indent,sight().enabled() ? '+' : '-');
  	DBG_OutText	("%s%stype            : %s",indent,indent,sight_type);
  	DBG_OutText	("%s%suse torso       : %s",indent,indent,sight().current_action().use_torso_look() ? "+" : "-");
  	DBG_OutText	("%s%shead current    : [%f][%f]",indent,indent,movement().head_orientation().current.yaw,movement().head_orientation().current.pitch);
  	DBG_OutText	("%s%shead target     : [%f][%f]",indent,indent,movement().head_orientation().target.yaw,movement().head_orientation().target.pitch);
  	DBG_OutText	("%s%sbody current    : [%f][%f]",indent,indent,movement().body_orientation().current.yaw,movement().body_orientation().current.pitch);
  	DBG_OutText	("%s%sbody target     : [%f][%f]",indent,indent,movement().body_orientation().target.yaw,movement().body_orientation().target.pitch);
  	
  	switch (sight().current_action().sight_type()) {
  		case SightManager::eSightTypeCurrentDirection : {
  			break;
  		}
  		case SightManager::eSightTypePathDirection : {
  			break;
  		}
  		case SightManager::eSightTypeDirection : {
  			DBG_OutText	("%s%sdirection       : [%f][%f][%f]",indent,indent,VPUSH(sight().current_action().vector3d()));
  			break;
  		}
  		case SightManager::eSightTypePosition : {
  			DBG_OutText	("%s%sposition        : [%f][%f][%f]",indent,indent,VPUSH(sight().current_action().vector3d()));
  			break;
  		}
  		case SightManager::eSightTypeObject : {
  			DBG_OutText	("%s%sobject          : %s",indent,indent,*sight().current_action().object().cName());
  			DBG_OutText	("%s%sposition        : [%f][%f][%f]",indent,indent,VPUSH(sight().current_action().object().Position()));
  			break;
  		}
  		case SightManager::eSightTypeCover : {
  			break;
  		}
  		case SightManager::eSightTypeSearch : {
  			break;
  		}
  		case SightManager::eSightTypeLookOver : {
  			break;
  		}
  		case SightManager::eSightTypeCoverLookOver : {
  			break;
  		}
  		case SightManager::eSightTypeFireObject : {
  			DBG_OutText	("%s%sobject          : %s",indent,indent,*sight().current_action().object().cName());
  			DBG_OutText	("%s%sposition        : [%f][%f][%f]",indent,indent,VPUSH(sight().current_action().object().Position()));
  			DBG_OutText	("%s%svisible point   : %s",indent,indent,false ? "-" : "+");
  			break;
  		}
  		case SightManager::eSightTypeAnimationDirection : {
  			break;
  		}
  		default : NODEFAULT;
  	}
  
  }
  
  void CAI_Stalker::OnHUDDraw				(CCustomHUD *hud)
  {
  	inherited::OnHUDDraw				(hud);
  	m_dbg_hud_draw = true;
  
  }
  
  void CAI_Stalker::dbg_draw_vision	()
  {
  	VERIFY						(!!psAI_Flags.is(aiVision));
  
  	if (!smart_cast<CGameObject*>(Level().CurrentEntity()))
  		return;
  
  	Fvector						shift;
  	shift.set					(0.f,2.5f,0.f);
  
  	Fmatrix						res;
  	res.mul						(Device.mFullTransform,XFORM());
  
  	Fvector4					v_res;
  
  	res.transform				(v_res,shift);
  
  	if (v_res.z < 0 || v_res.w < 0)
  		return;
  
  	if (v_res.x < -1.f || v_res.x > 1.f || v_res.y<-1.f || v_res.y>1.f)
  		return;
  
  	float						x = (1.f + v_res.x)/2.f * (Device.dwWidth);
  	float						y = (1.f - v_res.y)/2.f * (Device.dwHeight);
  
  	CNotYetVisibleObject		*object = memory().visual().not_yet_visible_object(smart_cast<CGameObject*>(Level().CurrentEntity()));
  	string64					out_text;
  	xr_sprintf						(out_text,"%.2f",object ? object->m_value : 0.f);
  
  	UI().Font().pFontMedium->SetColor	(D3DCOLOR_RGBA(255,0,0,95));
  	UI().Font().pFontMedium->OutSet	(x,y);
  	UI().Font().pFontMedium->OutNext	(out_text);
  }
  
  typedef xr_vector<Fvector>	COLLIDE_POINTS;
  
  class ray_query_param	{
  public:
  	CCustomMonster			*m_holder;
  	float					m_power;
  	float					m_power_threshold;
  	float					m_pick_distance;
  	Fvector					m_start_position;
  	Fvector					m_direction;
  	COLLIDE_POINTS			*m_points;
  
  public:
  	IC				ray_query_param		(CCustomMonster *holder, float power_threshold, float distance, const Fvector &start_position, const Fvector &direction, COLLIDE_POINTS &points)
  	{
  		m_holder			= holder;
  		m_power				= 1.f;
  		m_power_threshold	= power_threshold;
  		m_pick_distance		= distance;
  		m_start_position	= start_position;
  		m_direction			= direction;
  		m_points			= &points;
  	}
  };
  
  BOOL _ray_query_callback	(collide::rq_result& result, LPVOID params)
  {
  	ray_query_param						*param = (ray_query_param*)params;
  	param->m_points->push_back			(
  		Fvector().mad(
  			param->m_start_position,
  			param->m_direction,
  			result.range
  		)
  	);
  	
  	float								power = param->m_holder->feel_vision_mtl_transp(result.O,result.element);
  	param->m_power						*= power;
  	if (param->m_power > param->m_power_threshold)
  		return							(true);
  
  	param->m_pick_distance				= result.range;
  	return								(false);
  }
  
  void fill_points			(CCustomMonster *self, const Fvector &position, const Fvector &direction, float distance, collide::rq_results& rq_storage, COLLIDE_POINTS &points, float &pick_distance)
  {
  	VERIFY							(!fis_zero(direction.square_magnitude()));
  
  	collide::ray_defs				ray_defs(position,direction,distance,CDB::OPT_CULL,collide::rqtBoth);
  	VERIFY							(!fis_zero(ray_defs.dir.square_magnitude()));
  	
  	ray_query_param					params(self,self->memory().visual().transparency_threshold(),distance,position,direction,points);
  
  	Level().ObjectSpace.RayQuery	(rq_storage,ray_defs,_ray_query_callback,&params,NULL,self);
  
  	pick_distance					= params.m_pick_distance;
  }
  
  void draw_visiblity_rays	(CCustomMonster *self, const CObject *object, collide::rq_results& rq_storage)
  {
  	typedef Feel::Vision::feel_visible_Item		feel_visible_Item;
  	typedef xr_vector<feel_visible_Item>		VISIBLE_ITEMS;
  
  	feel_visible_Item		*item = 0;
  	{
  		VISIBLE_ITEMS::iterator	I = self->feel_visible.begin();
  		VISIBLE_ITEMS::iterator	E = self->feel_visible.end();
  		for ( ; I!=E; ++I) {
  			if ((*I).O == object) {
  				item		= &*I;
  				break;
  			}
  		}
  	}
  	
  	if (!item)
  		return;
  
  	Fvector					start_position = self->eye_matrix.c;
  	Fvector					dest_position = item->cp_LAST;
  	Fvector					direction = Fvector().sub(dest_position,start_position);
  	float					distance = direction.magnitude();
  	direction.normalize		();
  	float					pick_distance = flt_max;
  	rq_storage.r_clear		();
  	COLLIDE_POINTS			points;
  	points.push_back		(start_position);
  	fill_points				(
  		self,
  		start_position,
  		direction,
  		distance,
  		rq_storage,
  		points,
  		pick_distance
  	);
  
  	if (fsimilar(pick_distance,distance) && !dest_position.similar(points.back()))
  		points.push_back	(dest_position);
  
  	VERIFY					(points.size() > 1);
  	
  	Fvector					size = Fvector().set(.05f,.05f,.05f);
  	Level().debug_renderer().draw_aabb	(points.front(),size.x,size.y,size.z,D3DCOLOR_XRGB(0,0,255));
  
  	{
  		COLLIDE_POINTS::const_iterator	I = points.begin() + 1;
  		COLLIDE_POINTS::const_iterator	E = points.end();
  		for ( ; I != E; ++I) {
  			Level().debug_renderer().draw_line	(Fidentity,*(I-1),*I,D3DCOLOR_XRGB(0,255,0));
  			Level().debug_renderer().draw_aabb	(*I,size.x,size.y,size.z,D3DCOLOR_XRGB(0,255,0));
  		}
  	}
  
  	Level().debug_renderer().draw_aabb	(points.back(),size.x,size.y,size.z,D3DCOLOR_XRGB(255,0,0));
  }
  
  void CAI_Stalker::dbg_draw_visibility_rays	()
  {
  	if (!g_Alive())
  		return;
  
  	const CEntityAlive		*enemy = memory().enemy().selected() ? memory().enemy().selected() : Actor();
  	if (enemy) {
  		if (memory().visual().visible_now(enemy)) {
  			collide::rq_results	rq_storage;
  			draw_visiblity_rays	(this,enemy,rq_storage);
  		}
  	}
  }
  
  #define DEBUG_RENDER
  
  xr_vector<Fmatrix>						g_stalker_skeleton;
  
  static Fvector s_spine_bone;
  
  static Fmatrix aim_on_actor		(
  		Fvector const& bone_position,
  		Fvector const& weapon_position,
  		Fvector const& weapon_direction,
  		Fvector const& target,
  		bool const& debug_draw
  	)
  {
  	VERIFY								( fsimilar(1.f, weapon_direction.square_magnitude()) );
  
  #ifdef DEBUG_RENDER
  	CDebugRenderer&						renderer = Level().debug_renderer();
  	Fmatrix								temp;
  	
  	if (debug_draw)
  	{
  		temp.scale						(.01f, .01f, .01f);
  		temp.c							= bone_position;
  		renderer.draw_ellipse			(temp, D3DCOLOR_XRGB(255, 0, 0));
  
  		temp.c							= weapon_position;
  		renderer.draw_ellipse			(temp, D3DCOLOR_XRGB(0, 255, 0));
  		Fvector const weapon_position_target= Fvector().mad(weapon_position, weapon_direction, weapon_position.distance_to(target));
  		renderer.draw_line				(Fidentity, weapon_position, weapon_position_target, D3DCOLOR_XRGB(255, 0, 255));
  	}
  #endif // #ifdef DEBUG_RENDER
  
  	Fvector	const weapon2bone			= Fvector().sub(bone_position, weapon_position);
  	float const offset					= weapon2bone.dotproduct(weapon_direction);
  	Fvector current_point				= Fvector().mad(weapon_position, weapon_direction, offset);
  
  #ifdef DEBUG_RENDER
  	if (debug_draw)
  	{
  		temp.c							= current_point;
  		renderer.draw_ellipse			(temp, D3DCOLOR_XRGB(0, 0, 255));
  	}
  #endif // #ifdef DEBUG_RENDER
  
  	Fvector const bone2current			= Fvector().sub(current_point, bone_position);
  	float const sphere_radius_sqr		= bone2current.square_magnitude();
  	Fvector direction_target			= Fvector().sub(target, bone_position);
  	VERIFY								(direction_target.magnitude() > EPS_L);
  	float const invert_magnitude		= 1.f/direction_target.magnitude();
  	direction_target.mul				(invert_magnitude);
  	float const to_circle_center		= sphere_radius_sqr*invert_magnitude;
  	Fvector const circle_center			= Fvector().mad(bone_position, direction_target, to_circle_center);
  
  	Fplane plane						= Fplane().build(circle_center, direction_target);
  	Fvector								projection;
  	plane.project						(projection, current_point);
  
  	Fvector const center2projection_direction	= Fvector().sub(projection, circle_center).normalize();
  	float const circle_radius_sqr		= sphere_radius_sqr - _sqr(to_circle_center);
  	VERIFY								(circle_radius_sqr >= 0.f);
  	float const circle_radius			= _sqrt(circle_radius_sqr);
  	Fvector const target_point			= Fvector().mad(circle_center, center2projection_direction, circle_radius);
  	Fvector const current_direction		= Fvector().sub(current_point, bone_position).normalize();
  	Fvector const target_direction		= Fvector().sub(target_point,  bone_position).normalize();
  
  #ifdef DEBUG_RENDER
  	if (debug_draw)
  	{
  		temp.c							= target_point;
  		renderer.draw_ellipse			(temp, D3DCOLOR_XRGB(255, 255, 255));
  
  		float const sphere_radius		= _sqrt(sphere_radius_sqr);
  		temp.scale						(sphere_radius, sphere_radius, sphere_radius);
  		temp.c							= bone_position;
  		renderer.draw_ellipse			(temp, D3DCOLOR_XRGB(255, 255, 0));
  	}
  #endif // #ifdef DEBUG_RENDER
  
  	Fmatrix								transform0;
  	{
  		Fvector							cross_product = Fvector().crossproduct(current_direction, target_direction);
  		float const sin_alpha			= cross_product.magnitude();
  		if (!fis_zero(sin_alpha)) {
  			float const cos_alpha		= current_direction.dotproduct(target_direction);
  			transform0.rotation			(cross_product.div(sin_alpha), atan2f(sin_alpha, cos_alpha));
  		}
  		else {
  			float const dot_product		= current_direction.dotproduct(target_direction);
  			if (fsimilar(_abs(dot_product), 0.f))
  				transform0.identity		();
  			else {
  				VERIFY					(fsimilar(_abs(dot_product), 1.f));
  				cross_product.crossproduct	(current_direction, direction_target);
  				transform0.rotation		(cross_product.normalize(), dot_product > 0.f ? 0.f : PI);
  			}
  		}
  	}
  
  	Fmatrix								transform1;
  	{
  		Fvector const new_direction		= Fvector().sub(target, target_point).normalize();
  		Fvector old_direction;
  		transform0.transform_dir		(old_direction, weapon_direction);
  		Fvector 						cross_product = Fvector().crossproduct(old_direction, new_direction);
  		float const sin_alpha			= cross_product.magnitude();
  		if (!fis_zero(sin_alpha)) {
  			float const cos_alpha		= old_direction.dotproduct(new_direction);
  			transform1.rotation			(cross_product.div(sin_alpha), atan2f(sin_alpha, cos_alpha));
  		}
  		else {
  			float const dot_product		= current_direction.dotproduct(target_direction);
  			if (fsimilar(_abs(dot_product), 0.f))
  				transform1.identity		();
  			else {
  				VERIFY					(fsimilar(_abs(dot_product), 1.f));
  				transform1.rotation		(target_direction, dot_product > 0.f ? 0.f : PI);
  			}
  		}
  	}
  
  	Fmatrix								transform;
  	transform.mul_43					(transform1, transform0);
  
  #ifdef DEBUG_RENDER
  	if (debug_draw)
  	{
  		temp.scale						(.01f, .01f, .01f);
  		transform.transform_dir			(temp.c, Fvector().sub(weapon_position, bone_position));
  		temp.c.add						(bone_position);
  		renderer.draw_ellipse			(temp, D3DCOLOR_XRGB(0, 255, 255));
  	}
  #endif // #ifdef DEBUG_RENDER
  
  	return								(transform);
  }
  
  static void fill_bones				(CAI_Stalker& self, Fmatrix const& transform, IKinematicsAnimated* kinematics_animated, LPCSTR animation_id, bool const local)
  {
  	IKinematics*						kinematics = smart_cast<IKinematics*>(kinematics_animated);
  	u16									bone_count = kinematics->LL_BoneCount();
  	MotionID							animation = kinematics_animated->LL_MotionID(animation_id);
  	VERIFY								(animation.valid());
  
  	u16 const root_bone_id				= kinematics->LL_GetBoneRoot();
  	CBoneInstance& root_bone			= kinematics->LL_GetBoneInstance(root_bone_id);
  	BoneCallback callback				= root_bone.callback();
  	void* callback_params				= root_bone.callback_param();
  	root_bone.set_callback				( bctCustom, 0, 0 );
  
  	for (u16 i=0; i<MAX_PARTS; ++i) {
  #if 0
  		CBlend* const blend				= kinematics_animated->LL_PlayCycle(i, animation, 0, 0, 0, 1);
  		if (blend)
  			blend->timeCurrent			= 0.f;//blend->timeTotal - (SAMPLE_SPF + EPS);
  #else // #if 0
  		u32 const blend_count			= kinematics_animated->LL_PartBlendsCount(i);
  		for (u32 j=0; j<blend_count; ++j) {
  			CBlend* const blend			= kinematics_animated->LL_PartBlend(i, j);
  			CBlend* const new_blend		= kinematics_animated->LL_PlayCycle( i, blend->motionID, TRUE, 0, 0, 1 );
  			VERIFY						(new_blend);
  			*new_blend					= *blend;
  			new_blend->channel			= 1;
  		}
  #endif // #if 0
  	}
  
  	animation_movement_controller const*controller = self.animation_movement();
  	Fmatrix								start_transform;
  	if (!controller)
  		start_transform					= self.XFORM();
  	else
  		start_transform					= controller->start_transform();
  
  	u32									buffer_size = u32(bone_count)*sizeof(Fmatrix);
  	Fmatrix*							buffer = (Fmatrix*)_alloca(buffer_size);
  	buffer_vector<Fmatrix>				bones(buffer, bone_count);
  	for (u16 i = 0; i<bone_count; ++i) {
  		Fmatrix							matrix;
  		kinematics->Bone_GetAnimPos		(matrix, i,	1 << 1, false);
  		bones.push_back					(local ? matrix : Fmatrix().mul_43(start_transform, matrix));
  	}
  
  	for (u16 i=0; i<MAX_PARTS; ++i)
  		kinematics_animated->LL_CloseCycle		(i, 1 << 1);
  
  	root_bone.set_callback				( bctCustom, callback, callback_params );
  
  	g_stalker_skeleton.assign			(bones.begin(), bones.end());
  }
  
  struct callback_param {
  	Fmatrix			transform;
  	Fmatrix			xform;
  	IKinematics*	kinematics;
  	u16				bone_id;
  };
  
  static void	_BCL test_callback			(CBoneInstance *B)
  {
  	VERIFY								(B);
  
  	callback_param*						params = static_cast<callback_param*>(B->callback_param());
  	VERIFY								(params);
  
  	Fvector const position				= B->mTransform.c;
  	B->mTransform.mulA_43				(params->transform);
  	B->mTransform.c						= position;
  }
  
  #ifdef DEBUG_RENDER
  static void draw_bones				(
  		IKinematics& kinematics,
  		Fvector const& box_size,
  		u32 const& box_color,
  		u32 const& line_color,
  		Fmatrix const* const transform = 0
  	)
  {
  	CDebugRenderer&						renderer = Level().debug_renderer();
  	u16 const							bone_count = kinematics.LL_BoneCount();
  	Fmatrix								temp, temp2;
  	for (u16 i = 0; i<bone_count; ++i) {
  		if (transform)
  			temp.mul_43					(*transform, g_stalker_skeleton[i]);
  		else
  			temp						= g_stalker_skeleton[i];
  
  		renderer.draw_obb				(
  			temp,
  			box_size,
  			box_color
  		);
  
  		CBoneData&						bone_data = kinematics.LL_GetData(i);
  		u16								parent_bone_id = bone_data.GetParentID();
  		if (parent_bone_id == BI_NONE)
  			continue;
  
  		if (transform)
  			temp2.mul_43				(*transform, g_stalker_skeleton[parent_bone_id]);
  		else
  			temp2						= g_stalker_skeleton[parent_bone_id];
  
  		renderer.draw_line				(
  			Fidentity,
  			temp2.c,
  			temp.c,
  			line_color
  		);
  	}
  }
  #endif // #ifdef DEBUG_RENDER
  
  static void draw_animation_bones	(CAI_Stalker& self, Fmatrix const& transform, IKinematicsAnimated* kinematics_animated, LPCSTR animation_id)
  {
  	IKinematics* kinematics				= smart_cast<IKinematics*>(kinematics_animated);
  
  	u16									spine_bone_id = 
  		(u16)kinematics->LL_BoneID(
  			pSettings->r_string(self.cNameSect().c_str(),"bone_spin")
  		);
  	u16									shoulder_bone_id = 
  		(u16)kinematics->LL_BoneID(
  			pSettings->r_string(self.cNameSect().c_str(),"bone_shoulder")
  		);
  	u16									weapon_bone_id0 = 
  		(u16)kinematics->LL_BoneID(
  			pSettings->r_string(self.cNameSect().c_str(),"weapon_bone0")
  		);
  	u16									weapon_bone_id1 = 
  		(u16)kinematics->LL_BoneID(
  			pSettings->r_string(self.cNameSect().c_str(),"weapon_bone2")
  		);
  
  	fill_bones						(self, transform, kinematics_animated, animation_id, true);
  
  	Fmatrix mL						= g_stalker_skeleton[weapon_bone_id1];
  	Fmatrix mR						= g_stalker_skeleton[weapon_bone_id0];
  
  	Fvector							D;
  	D.sub							(mL.c,mR.c);	
  	D.normalize						();
  
  	bool forward_blend_callbacks	= self.animation().forward_blend_callbacks();
  	bool backward_blend_callbacks	= self.animation().backward_blend_callbacks();
  
  	self.animation().remove_bone_callbacks	();
  
  #if 0
  	Fmatrix								player_head;
  	IKinematics* actor_kinematics		= smart_cast<IKinematics*>(Actor()->Visual());
  	actor_kinematics->Bone_GetAnimPos	(player_head, actor_kinematics->LL_BoneID("bip01_head"), 1, false);
  	player_head.mulA_43					(Actor()->XFORM());
  	Fvector								target = player_head.c;
  #else // #if 0
  	Fvector								target = self.sight().aiming_position();
  #endif // #if 0
  
  	Fmatrix								spine_offset;
  	Fmatrix								shoulder_offset;
  
  	fill_bones							(self, transform, kinematics_animated, animation_id, true);
  
  #ifdef DEBUG_RENDER
  //	self.setVisible						(FALSE);
  
  	draw_bones							(
  		*kinematics,
  		Fvector().set(.011f, .011f, .011f),
  		D3DCOLOR_XRGB(0, 255, 0),
  		D3DCOLOR_XRGB(0, 255, 255),
  		&transform
  	);
  #endif // #ifdef DEBUG_RENDER
  
  	Fmatrix							weapon_bone_0;
  
  	Fmatrix							spine_bone;
  	spine_bone.mul_43				(transform, g_stalker_skeleton[spine_bone_id]);
  
  	CWeapon* weapon					= smart_cast<CWeapon*>(self.best_weapon());
  	VERIFY							(weapon);
  	
  	Fvector							pos,ypr;
  	pos								= pSettings->r_fvector3		(weapon->cNameSect(),"position");
  	ypr								= pSettings->r_fvector3		(weapon->cNameSect(),"orientation");
  	ypr.mul							(PI/180.f);
  
  	Fmatrix							offset;
  	offset.setHPB					(ypr.x,ypr.y,ypr.z);
  	offset.translate_over			(pos);
  
  	mL								= g_stalker_skeleton[weapon_bone_id1];
  	mR								= g_stalker_skeleton[weapon_bone_id0];
  
  	Fmatrix							mRes;
  	Fvector							R,N;
  	D.sub							(mL.c,mR.c);	
  	D.normalize						();
  	R.crossproduct					(mR.j,D);
  
  	N.crossproduct					(D,R);			
  	N.normalize						();
  
  	mRes.set						(R,N,D,mR.c);
  	mRes.mulA_43					(transform);
  
  	weapon_bone_0.mul				(mRes, offset);
  	
  	Fvector	const vLoadedFirePoint	= pSettings->r_fvector3		( weapon->cNameSect(), "fire_point" );
  	Fvector weapon_bone_position;
  	weapon_bone_0.transform_tiny	( weapon_bone_position, vLoadedFirePoint );
  
  	Fvector weapon_bone_direction;
  	weapon_bone_0.transform_dir		( weapon_bone_direction, Fvector().set( 0.f, 0.f, 1.f ) );
  
  	spine_offset						= 
  		aim_on_actor(
  			spine_bone.c,
  			weapon_bone_position,
  			weapon_bone_direction.normalize(),
  			target,
  			false
  		);
  
  	callback_param					param;
  	param.transform					= Fmatrix(transform).invert().mulB_43(spine_offset).mulB_43(Fmatrix(transform));
  	{
  		Fvector						temp;
  		param.transform.getXYZ		(temp);
  		temp.mul					(.5f);
  		param.transform.setXYZ		(temp);
  	}
  	param.xform						= transform;
  	param.kinematics				= kinematics;
  	param.bone_id					= spine_bone_id;
  	kinematics->LL_GetBoneInstance	( spine_bone_id ).set_callback ( bctCustom, &test_callback, &param );
  
  	fill_bones						(self, transform, kinematics_animated, animation_id, true);
  
  	Fmatrix							shoulder_bone;
  	shoulder_bone.mul_43			(transform, g_stalker_skeleton[shoulder_bone_id]);
  
  	mL								= g_stalker_skeleton[weapon_bone_id1];
  	mR								= g_stalker_skeleton[weapon_bone_id0];
  
  	D.sub							(mL.c,mR.c);	
  	D.normalize						();
  	R.crossproduct					(mR.j,D);
  
  	N.crossproduct					(D,R);			
  	N.normalize						();
  
  	mRes.set						(R,N,D,mR.c);
  	mRes.mulA_43					(transform);
  
  	weapon_bone_0.mul				(mRes, offset);
  	weapon_bone_0.transform_tiny	(weapon_bone_position, vLoadedFirePoint);
  	weapon_bone_0.transform_dir		(weapon_bone_direction, Fvector().set( 0.f, 0.f, 1.f ));
  
  	shoulder_offset						= 
  		aim_on_actor(
  			shoulder_bone.c,
  			weapon_bone_position,
  			weapon_bone_direction.normalize(),
  			target,
  			true
  		);
  
  	callback_param					param2;
  	param2.transform				= Fmatrix(transform).invert().mulB_43(shoulder_offset).mulB_43(Fmatrix(transform));;
  	param2.xform					= transform;
  	param2.kinematics				= kinematics;
  	param2.bone_id					= shoulder_bone_id;
  	kinematics->LL_GetBoneInstance	( shoulder_bone_id ).set_callback ( bctCustom, &test_callback, &param2 );
  
  	fill_bones						(self, transform, kinematics_animated, animation_id, false);
  
  	mL								= g_stalker_skeleton[weapon_bone_id1];
  	mR								= g_stalker_skeleton[weapon_bone_id0];
  
  	Fmatrix							m_start_transform;
  	animation_movement_controller const*	controller = self.animation_movement();
  	if (!controller)
  		m_start_transform			= self.XFORM();
  	else
  		m_start_transform			= controller->start_transform();
  
  	m_start_transform.invert		();
  
  	mL.mulA_43						(m_start_transform);
  	mR.mulA_43						(m_start_transform);
  
  	D.sub							(mL.c,mR.c);	
  	D.normalize						();
  	R.crossproduct					(mR.j,D);
  
  	N.crossproduct					(D,R);			
  	N.normalize						();
  
  	mRes.set						(R,N,D,mR.c);
  	mRes.mulA_43					(transform);
  
  	weapon_bone_0.mul				(mRes, offset);
  		
  	weapon_bone_0.transform_tiny	(weapon_bone_position, vLoadedFirePoint);
  		
  	weapon_bone_direction			= Fvector().set(0.f, 0.f, 1.f);
  	weapon_bone_0.transform_dir		( weapon_bone_direction );
  
  	kinematics->LL_GetBoneInstance	( spine_bone_id ).reset_callback ( );
  	kinematics->LL_GetBoneInstance	( shoulder_bone_id ).reset_callback ( );
  
  	if (forward_blend_callbacks)
  		self.animation().assign_bone_blend_callbacks(true);
  	else {
  		if (backward_blend_callbacks)
  			self.animation().assign_bone_blend_callbacks(false);
  		else
  			self.animation().assign_bone_callbacks();
  	}
  
  	kinematics->CalculateBones		(TRUE);
  
  	VERIFY							(!g_stalker_skeleton.empty());
  
  #ifdef DEBUG_RENDER
  	CDebugRenderer&					renderer = Level().debug_renderer();
  
  	if (self.inventory().ActiveItem()) {
  		CWeapon*					weapon = smart_cast<CWeapon*>(self.inventory().ActiveItem());
  		if (weapon) {
  			Fvector position		= weapon->get_LastFP();
  			Fvector direction		= weapon->get_LastFD();
  			renderer.draw_line		(
  				Fidentity,
  				position,
  				Fvector().mad(position, direction, position.distance_to(target)),
  				D3DCOLOR_XRGB(255, 0, 0)
  			);
  		}
  	}
  
  	Fvector const weapon_position_target= Fvector().mad(weapon_bone_position, weapon_bone_direction, weapon_bone_position.distance_to(target));
  	renderer.draw_line				(Fidentity, weapon_bone_position, weapon_position_target, D3DCOLOR_XRGB(255, 255, 0));
  
  	Fmatrix							target_transform;
  	target_transform.scale			(.01f, .01f, .01f);
  	target_transform.c				= target;
  	renderer.draw_ellipse			(target_transform, D3DCOLOR_XRGB(255, 0, 0));
  
  	target_transform.scale			(.015f, .015f, .015f);
  	target_transform.c				= weapon_bone_position;
  	renderer.draw_ellipse			(target_transform, D3DCOLOR_XRGB(255, 0, 0));
  
  	draw_bones						(
  		*kinematics,
  		Fvector().set(.01f, .01f, .01f),
  		D3DCOLOR_XRGB(0, 0, 255),
  		D3DCOLOR_XRGB(255, 255, 0)
  	);
  
  	renderer.draw_obb				(
  		g_stalker_skeleton[weapon_bone_id0],
  		Fvector().set(.01f, .01f, .01f),
  		D3DCOLOR_XRGB(255, 0, 0)
  	);
  	renderer.draw_obb				(
  		g_stalker_skeleton[weapon_bone_id1],
  		Fvector().set(.01f, .01f, .01f),
  		D3DCOLOR_XRGB(255, 0, 0)
  	);
  	renderer.draw_line				(
  		Fidentity,
  		g_stalker_skeleton[weapon_bone_id0].c,
  		g_stalker_skeleton[weapon_bone_id1].c,
  		D3DCOLOR_XRGB(255, 0, 0)
  	);
  #endif // #ifdef DEBUG_RENDER
  }
  
  Fvector	g_debug_position_0		= Fvector().set(0.f, 0.f, 0.f);
  Fvector	g_debug_position_1		= Fvector().set(0.f, 0.f, 0.f);
  Fvector	g_debug_position_2		= Fvector().set(0.f, 0.f, 0.f);
  Fvector	g_debug_position_3		= Fvector().set(0.f, 0.f, 0.f);
  
  void CAI_Stalker::OnRender				()
  {
  #if 0
  	IKinematicsAnimated*		kinematics = smart_cast<IKinematicsAnimated*>(Visual());
  	VERIFY						(kinematics);
  //	draw_animation_bones		(*this, XFORM(), kinematics, "loophole_2_no_look_idle_0");
  
  	Fmatrix						m_start_transform;
  	animation_movement_controller const*	controller = animation_movement();
  	if (!controller)
  		m_start_transform		= XFORM();
  	else
  		m_start_transform		= controller->start_transform();
  
  	draw_animation_bones		(*this, m_start_transform, kinematics, "loophole_3_attack_idle_0");
  //	draw_animation_bones		(*this, XFORM(), kinematics, "loophole_3_attack_in_0");
  #else // #if 0
  	if (inventory().ActiveItem()) {
  		Fvector					position, direction, temp;
  		g_fireParams			(0,position,direction);
  		temp					= direction;
  		temp.mul				(1.f);
  		temp.add				(position);
  		Level().debug_renderer().draw_line		(Fidentity,position,temp,D3DCOLOR_XRGB(0*255,255,0*255));
  	}
  
  	if (IsMyCamera()) {
  		if (!g_Alive())
  			return;
  
  		if (!memory().enemy().selected() || !memory().visual().visible_now(memory().enemy().selected()))
  			return;
  
  		xr_vector<CObject*>		objects;
  		feel_vision_get			(objects);
  		if (std::find(objects.begin(),objects.end(),memory().enemy().selected()) != objects.end()) {
  			Fvector				position = feel_vision_get_vispoint(const_cast<CEntityAlive*>(memory().enemy().selected()));
  			Level().debug_renderer().draw_aabb	(position,.05f,.05f,.05f,D3DCOLOR_XRGB(0*255,255,0*255));
  			return;
  		}
  
  		return;
  	}
  
  #if 0
  	if (inventory().ActiveItem()) {
  		Fvector				position, direction;
  		g_fireParams		(0,position,direction);
  
  		float				yaw, pitch, safety_fire_angle = 1.f*PI_DIV_8*.125f;
  		direction.getHP		(yaw,pitch);
  
  		Level().debug_renderer().draw_line(Fidentity, position, Fvector().mad(position, direction, 20.f), D3DCOLOR_XRGB(0,255,0));
  
  		direction.setHP		(yaw - safety_fire_angle,pitch);
  		Level().debug_renderer().draw_line(Fidentity, position, Fvector().mad(position, direction, 20.f), D3DCOLOR_XRGB(0,255,0));
  
  		direction.setHP		(yaw + safety_fire_angle,pitch);
  		Level().debug_renderer().draw_line(Fidentity, position, Fvector().mad(position, direction, 20.f), D3DCOLOR_XRGB(0,255,0));
  
  		direction.setHP		(yaw,pitch - safety_fire_angle);
  		Level().debug_renderer().draw_line(Fidentity, position, Fvector().mad(position, direction, 20.f), D3DCOLOR_XRGB(0,255,0));
  
  		direction.setHP		(yaw,pitch + safety_fire_angle);
  		Level().debug_renderer().draw_line(Fidentity, position, Fvector().mad(position, direction, 20.f), D3DCOLOR_XRGB(0,255,0));
  	}
  #endif // #if 0
  
  	inherited::OnRender		();
  
  	{
  		Fvector current_direction;
  		Fvector target_direction;
  		current_direction.setHP(-movement().m_head.current.yaw, -movement().m_head.current.pitch);
  		current_direction.normalize();
  		current_direction.mul(4.f);
  		current_direction.add(eye_matrix.c);
  		target_direction.setHP(-movement().m_head.target.yaw, -movement().m_head.target.pitch);
  		target_direction.normalize();
  		target_direction.mul(4.f);			
  		target_direction.add(eye_matrix.c);
  		target_direction.y = eye_matrix.c.y + 0.4f;
  		
  		Level().debug_renderer().draw_line(Fidentity, eye_matrix.c, current_direction, D3DCOLOR_XRGB(255,0,255));
  		Level().debug_renderer().draw_line(Fidentity, eye_matrix.c, target_direction, D3DCOLOR_XRGB(255,255,0));
  	}
  
  	{
  		Fvector					c0 = Position(),c1,t0 = Position(),t1;
  		c0.y					+= 2.f;
  		c1.setHP				(-movement().m_body.current.yaw,-movement().m_body.current.pitch);
  		c1.add					(c0);
  		Level().debug_renderer().draw_line		(Fidentity,c0,c1,D3DCOLOR_XRGB(0,255,0));
  		
  		t0.y					+= 2.f;
  		t1.setHP				(-movement().m_body.target.yaw,-movement().m_body.target.pitch);
  		t1.add					(t0);
  		Level().debug_renderer().draw_line		(Fidentity,t0,t1,D3DCOLOR_XRGB(255,0,0));
  	}
  
  	if (memory().danger().selected() && ai().level_graph().valid_vertex_position(memory().danger().selected()->position())) {
  		Fvector						position = memory().danger().selected()->position();
  		u32							level_vertex_id = ai().level_graph().vertex_id(position);
  		float						half_size = ai().level_graph().header().cell_size()*.5f;
  		position.y					+= 1.f;
  		Level().debug_renderer().draw_aabb	(position,half_size - .01f,1.f,ai().level_graph().header().cell_size()*.5f-.01f,D3DCOLOR_XRGB(0*255,255,0*255));
  
  		if (ai().level_graph().valid_vertex_id(level_vertex_id)) {
  			LevelGraph::CVertex			*v = ai().level_graph().vertex(level_vertex_id);
  
  			// high
  			Fvector						direction;
  			float						best_value = -1.f;
  
  			for (u32 i=0, j = 0; i<36; ++i) {
  				float				value = ai().level_graph().high_cover_in_direction(float(10*i)/180.f*PI,v);
  				direction.setHP		(float(10*i)/180.f*PI,0);
  				direction.normalize	();
  				direction.mul		(value*half_size);
  				direction.add		(position);
  				direction.y			= position.y;
  				Level().debug_renderer().draw_line	(Fidentity,position,direction,D3DCOLOR_XRGB(0,0,255));
  				value				= ai().level_graph().compute_high_square(float(10*i)/180.f*PI,PI/2.f,v);
  				if (value > best_value) {
  					best_value		= value;
  					j				= i;
  				}
  			}
  
  			direction.set		(position.x - half_size*float(v->high_cover(0))/15.f,position.y,position.z);
  			Level().debug_renderer().draw_line(Fidentity,position,direction,D3DCOLOR_XRGB(255,0,0));
  
  			direction.set		(position.x,position.y,position.z + half_size*float(v->high_cover(1))/15.f);
  			Level().debug_renderer().draw_line(Fidentity,position,direction,D3DCOLOR_XRGB(255,0,0));
  
  			direction.set		(position.x + half_size*float(v->high_cover(2))/15.f,position.y,position.z);
  			Level().debug_renderer().draw_line(Fidentity,position,direction,D3DCOLOR_XRGB(255,0,0));
  
  			direction.set		(position.x,position.y,position.z - half_size*float(v->high_cover(3))/15.f);
  			Level().debug_renderer().draw_line(Fidentity,position,direction,D3DCOLOR_XRGB(255,0,0));
  
  			float				value = ai().level_graph().high_cover_in_direction(float(10*j)/180.f*PI,v);
  			direction.setHP		(float(10*j)/180.f*PI,0);
  			direction.normalize	();
  			direction.mul		(value*half_size);
  			direction.add		(position);
  			direction.y			= position.y;
  			Level().debug_renderer().draw_line	(Fidentity,position,direction,D3DCOLOR_XRGB(0,0,0));
  
  			// low
  			{
  			Fvector						direction;
  			float						best_value = -1.f;
  
  			for (u32 i=0, j = 0; i<36; ++i) {
  				float				value = ai().level_graph().low_cover_in_direction(float(10*i)/180.f*PI,v);
  				direction.setHP		(float(10*i)/180.f*PI,0);
  				direction.normalize	();
  				direction.mul		(value*half_size);
  				direction.add		(position);
  				direction.y			= position.y;
  				Level().debug_renderer().draw_line	(Fidentity,position,direction,D3DCOLOR_XRGB(0,0,255));
  				value				= ai().level_graph().compute_low_square(float(10*i)/180.f*PI,PI/2.f,v);
  				if (value > best_value) {
  					best_value		= value;
  					j				= i;
  				}
  			}
  
  			direction.set		(position.x - half_size*float(v->low_cover(0))/15.f,position.y,position.z);
  			Level().debug_renderer().draw_line(Fidentity,position,direction,D3DCOLOR_XRGB(255,0,0));
  
  			direction.set		(position.x,position.y,position.z + half_size*float(v->low_cover(1))/15.f);
  			Level().debug_renderer().draw_line(Fidentity,position,direction,D3DCOLOR_XRGB(255,0,0));
  
  			direction.set		(position.x + half_size*float(v->low_cover(2))/15.f,position.y,position.z);
  			Level().debug_renderer().draw_line(Fidentity,position,direction,D3DCOLOR_XRGB(255,0,0));
  
  			direction.set		(position.x,position.y,position.z - half_size*float(v->low_cover(3))/15.f);
  			Level().debug_renderer().draw_line(Fidentity,position,direction,D3DCOLOR_XRGB(255,0,0));
  
  			float				value = ai().level_graph().low_cover_in_direction(float(10*j)/180.f*PI,v);
  			direction.setHP		(float(10*j)/180.f*PI,0);
  			direction.normalize	();
  			direction.mul		(value*half_size);
  			direction.add		(position);
  			direction.y			= position.y;
  			Level().debug_renderer().draw_line	(Fidentity,position,direction,D3DCOLOR_XRGB(0,0,0));
  			}
  		}
  	}
  
  	if ( g_Alive() )
  		movement().get_doors_actor().render();
  
  #endif // #if 0
  }
  
  #endif // DEBUG