void PocketPickedState::Init(idAI* owner)
{
	State::Init(owner);

	assert(owner);

	DM_LOG(LC_AI, LT_INFO)LOGSTRING("PocketPickedState::Init (%s)\r",owner->GetName());

	// stop if something more important has happened
	if (owner->GetMemory().stopReactingToPickedPocket)
	{
		Wrapup(owner);
		return;
	}

	_pocketPickedState = EStateReact;
}
Example #2
0
/*ARGSUSED*/
int
main(int argc, char *argv[] )
{
	char	*tmp = NULL;
	int		reserved_swap, free_swap;
	char	*host = NULL, *cluster = NULL, *proc = NULL;
	char	*bogus_capability;
	int		i;

	set_mySubSystem( "SHADOW", SUBSYSTEM_TYPE_SHADOW );

	myDistro->Init( argc, argv );
	if( argc == 2 && strncasecmp(argv[1], "-cl", 3) == MATCH ) {
		printClassAd();
		exit( 0 );
	}

#if defined(SYSCALL_DEBUG)
	SyscallLabel = argv[0] + 7;
#endif

#if !defined(WIN32)
	install_sig_handler(SIGPIPE, (SIG_HANDLER)SIG_IGN );
#endif

	if( argc > 1 ) {
		if( strcmp("-t",argv[1]) == MATCH ) {
			Termlog = 1;
			argv++;
			argc--;
		}
	}

	ShadowBDate = LastRestartTime = time(0);

	_EXCEPT_Cleanup = ExceptCleanup;

	MyPid = getpid();
	
	config();

	/* Start up with condor.condor privileges. */
	/*
	  we need to do this AFTER we call config() so that if CONDOR_IDS
	  is being defined in the config file, we'll get the right value
	*/ 
	set_condor_priv();

	if(Termlog)
		dprintf_set_tool_debug(get_mySubSystem()->getName(), 0);
	else
		dprintf_config( get_mySubSystem()->getName() );
	DebugId = whoami;

	// create a database connection object
	
	dprintf( D_ALWAYS, "******* Standard Shadow starting up *******\n" );
	dprintf( D_ALWAYS, "** %s\n", CondorVersion() );
	dprintf( D_ALWAYS, "** %s\n", CondorPlatform() );
	dprintf( D_ALWAYS, "*******************************************\n" );

	reserved_swap = param_integer("RESERVED_SWAP", 0);
	reserved_swap *= 1024; /* megabytes -> kb */

	bool use_sql_log = param_boolean("QUILL_USE_SQL_LOG", false);
    FILEObj = FILESQL::createInstance(use_sql_log);
	
	free_swap = sysapi_swap_space();

	dprintf( D_FULLDEBUG, "*** Reserved Swap = %d\n", reserved_swap );
	dprintf( D_FULLDEBUG, "*** Free Swap = %d\n", free_swap );
	if( reserved_swap && free_swap < reserved_swap ) {
		dprintf( D_ALWAYS, "Not enough reserved swap space\n" );
		if(FILEObj) {
		  delete FILEObj;
		}

		exit( JOB_NO_MEM );
	}

	dprintf(D_ALWAYS, "uid=%d, euid=%d, gid=%d, egid=%d\n",
		getuid(), geteuid(), getgid(), getegid());

    dprintf(D_FULLDEBUG, "argc = %d\n", argc);
    for(i = 0; i < argc; i++)
    {
        dprintf(D_FULLDEBUG, "argv[%d] = %s\n", i, argv[i]);
    }
	if( argc < 6 ) {
		usage();
	}

	if (param_boolean("SHADOW_DEBUG_WAIT", false, false)) {
		int debug_wait = 1;
		dprintf(D_ALWAYS,
				"SHADOW_DEBUG_WAIT is TRUE, waiting for debugger to attach to pid %d.\n", 
				(int)::getpid());
		while (debug_wait) {
			sleep(1);
		}
	}

	CheckSpoolVersion(SPOOL_MIN_VERSION_SHADOW_SUPPORTS,SPOOL_CUR_VERSION_SHADOW_SUPPORTS);

	if( strcmp("-pipe",argv[1]) == 0 ) {
		bogus_capability = argv[2];
		cluster = argv[3];
		proc = argv[4];
		// read the big comment in the function for why this is here.
		RemoveNewShadowDroppings(cluster, proc);
		pipe_setup( cluster, proc, bogus_capability );
	} else {
		schedd = argv[1];
		host = argv[2];
		bogus_capability = argv[3];
		cluster = argv[4];
		proc = argv[5];
		if ( argc > 6 ) {
			IpcFile = argv[6];
			dprintf(D_FULLDEBUG,"Setting IpcFile to %s\n",IpcFile);
		} else {
			IpcFile = NULL;
		}
		// read the big comment in the function for why this is here.
		RemoveNewShadowDroppings(cluster, proc);
		regular_setup( host, cluster, proc );
	}
	scheddName = getenv( EnvGetName( ENV_SCHEDD_NAME ) );

#if 0
		/* Don't want to share log file lock between child and pnarent */
	(void)close( LockFd );
	LockFd = -1;
#endif

	// initialize the user log
	initializeUserLog();

	My_Filesystem_Domain = param( "FILESYSTEM_DOMAIN" ); 
	dprintf( D_ALWAYS, "My_Filesystem_Domain = \"%s\"\n", 
			 My_Filesystem_Domain );

	My_UID_Domain = param( "UID_DOMAIN" ); 
	dprintf( D_ALWAYS, "My_UID_Domain = \"%s\"\n", My_UID_Domain );

	UseAFS = param_boolean_crufty( "USE_AFS", false ) ? TRUE : FALSE;

	UseNFS = param_boolean_crufty( "USE_NFS", false ) ? TRUE : FALSE;

	// if job specifies a checkpoint server host, this overrides
	// the config file parameters
	tmp = NULL;
	if (JobAd->LookupString(ATTR_CKPT_SERVER, &tmp) == 1) {
		if (CkptServerHost) free(CkptServerHost);
		UseCkptServer = TRUE;
		CkptServerHost = strdup(tmp);
		StarterChoosesCkptServer = FALSE;
		free(tmp);
	} else {
		free(tmp);
		if (CkptServerHost) {
            free(CkptServerHost);
        }
		CkptServerHost = param( "CKPT_SERVER_HOST" );
		UseCkptServer = FALSE;
		if( CkptServerHost && param_boolean_crufty( "USE_CKPT_SERVER", true ) ) {
			UseCkptServer = TRUE;
		}

		StarterChoosesCkptServer =
			param_boolean_crufty("STARTER_CHOOSES_CKPT_SERVER", true) ? TRUE : FALSE;
	}

		// Initialize location of our checkpoint file.  If we stored it
		// on a checkpoint server then set LastCkptServer.  Otherwise,
		// LastCkptServer should be NULL to indicate that we should
		// look on the local disk for our checkpoint file.
	LastCkptServer = NULL;
	if (JobAd->LookupString(ATTR_LAST_CKPT_SERVER,
							&LastCkptServer) == 0) {
		free(LastCkptServer);
		LastCkptServer = NULL;
	}

	// LIGO
	if (param_boolean("ALWAYS_USE_LOCAL_CKPT_SERVER", false)) {
		if (LastCkptServer) {
			char *remoteHost = NULL;
			JobAd->LookupString(ATTR_REMOTE_HOST, &remoteHost);

			char *machineName = strrchr(remoteHost, '@');
			if (machineName == NULL) {
				machineName = remoteHost;
			} else {
				machineName++;
			}

			LastCkptServer = strdup(machineName);
			CkptServerHost = strdup(machineName);

			dprintf(D_ALWAYS, "ALWAYS_USE_LOCAL_CKPT_SERVER is true, forcing LastCkptServer to %s\n", LastCkptServer);
		} else {
			dprintf(D_ALWAYS, "ALWAYS_USE_LOCAL_CKPT_SERVER is true, but checkpoint is not on server, restoring file local file\n");
		}
	}

	MaxDiscardedRunTime = param_integer( "MAX_DISCARDED_RUN_TIME", 3600 );

	CompressPeriodicCkpt = param_boolean( "COMPRESS_PERIODIC_CKPT", false );

	PeriodicSync = param_boolean( "PERIODIC_MEMORY_SYNC", false );

	CompressVacateCkpt = param_boolean( "COMPRESS_VACATE_CKPT", false );

	SlowCkptSpeed = param_integer( "SLOW_CKPT_SPEED", 0 );

	// Install signal handlers such that all signals are blocked when inside
	// the handler.
	sigset_t fullset;
	sigfillset(&fullset);
	install_sig_handler_with_mask( SIGCHLD,&fullset, reaper );

		// SIGUSR1 is sent by the schedd when a job is removed with
		// condor_rm.
	install_sig_handler_with_mask( SIGUSR1, &fullset, handle_sigusr1 );

		// SIGQUIT is sent for a fast shutdow.
	install_sig_handler_with_mask( SIGQUIT, &fullset, handle_sigquit );

		// SIGTERM is sent for a graceful shutdow.
	install_sig_handler_with_mask( SIGTERM, &fullset, handle_sigterm );


	/* Here we block the async signals.  We do this mainly because on HPUX,
	 * XDR wigs out if it is interrupted by a signal.  We do it on all
	 * platforms because it seems like a safe idea.  We unblock the signals
	 * during before select(), which is where we spend most of our time. */
	block_signal(SIGCHLD);
	block_signal(SIGUSR1);      

	/* If the completed job had been committed to the job queue,
		but for some reason the shadow exited wierdly and the
		schedd is trying to run it again, then simply write
		the job termination events and send the email as if the job had
		just ended. */
	if (terminate_is_pending() == TRUE) {
		/* This function will exit()! */
		handle_terminate_pending();
	}

	HandleSyscalls();

	Wrapup();

	/* HACK! WHOOO!!!!! Throw the old shadow away already! */
	/* This will figure out whether or not the job should go on hold AFTER the
		job has exited for whatever reason, or if the job should be allowed
		to exit. It modifies ExitReason approriately for job holding, or, get
		this, just EXCEPTs if the jobs is supposed to go into idle state and
		not leave. :) */
	/* Small change by Todd : only check the static policy if the job really
	   exited of its own accord -- we don't want to even evaluate the static
	   policy if the job exited because it was preempted, for instance */
	if (check_static_policy && 
		(ExitReason == JOB_EXITED || ExitReason == JOB_KILLED 
		     	|| ExitReason == JOB_COREDUMPED)) 
	{
		static_policy();
	}
    if( My_UID_Domain ) {
        free( My_UID_Domain );
    }
    if( My_Filesystem_Domain ) {
        free( My_Filesystem_Domain );
    }
        if(FILEObj) {
                delete FILEObj;
        }

	dprintf( D_ALWAYS, "********** Shadow Exiting(%d) **********\n",
		ExitReason );
	exit( ExitReason );
}
// Gets called each time the mind is thinking
void PocketPickedState::Think(idAI* owner)
{
	owner->PerformVisualScan();	// Let the AI check its senses

	// Check conditions for continuing.
	
	moveType_t moveType = owner->GetMoveType();
	Memory& memory = owner->GetMemory();

	if ( owner->AI_DEAD || // stop reacting if dead
		 owner->AI_KNOCKEDOUT || // or unconscious
		 (owner->AI_AlertIndex >= ESearching) || // stop if alert level is too high
		 memory.stopReactingToPickedPocket || // check if something happened to abort the state
		 (moveType == MOVETYPE_SLEEP) || // or asleep
		 (moveType == MOVETYPE_SIT_DOWN) || // or in the act of sitting down
		 (moveType == MOVETYPE_LAY_DOWN) || // or in the act of lying down to sleep
		 (moveType == MOVETYPE_GET_UP) ||   // or getting up from sitting
		 (moveType == MOVETYPE_GET_UP_FROM_LYING) ) // or getting up from lying down
	{
		Wrapup(owner);
		return;
	}

	switch (_pocketPickedState)
	{
		case EStateReact:
			{
			owner->actionSubsystem->ClearTasks();
			owner->movementSubsystem->ClearTasks();
			owner->m_ReactingToPickedPocket = true;

			// Emit the picked pocket bark if alert level is low, and we're not in Alert Idle
				
			if (owner->AI_AlertIndex < EObservant)
			{
				if (!(owner->HasSeenEvidence() && ( owner->spawnArgs.GetBool("disable_alert_idle", "0") == false) ) )
				{
					CommMessagePtr message;
					owner->commSubsystem->AddCommTask(CommunicationTaskPtr(new SingleBarkTask("snd_notice_pickpocket", message)));
				}
			}

			// If this occurred w/in the original alert window that surrounds the
			// pickpocket event, it's one more piece of evidence of something out of place.
			if (memory.insideAlertWindow)
			{
				memory.insideAlertWindow = false;
				memory.countEvidenceOfIntruders += EVIDENCE_COUNT_INCREASE_SUSPICIOUS;
				memory.posEvidenceIntruders = owner->GetPhysics()->GetOrigin();
				memory.timeEvidenceIntruders = gameLocal.time;
			}

			// Increase the alert level?
			float alertInc = owner->spawnArgs.GetFloat("pickpocket_alert","0");
			if (alertInc > 0.0f)
			{
				// Set the new alert level, but cap it just under Combat.
				// If the new alert level pushes the AI up into
				// Searching or Agitated Searching, the Picked Pocket State will end.

				float newAlertLevel = owner->AI_AlertLevel + alertInc;
				if ( newAlertLevel >= owner->thresh_5 )
				{
					newAlertLevel = owner->thresh_5 - 0.1;
				}
				owner->SetAlertLevel(newAlertLevel);

				// If the alert level is now in Searching or Agitated Searching,
				// create a search area.

				if ( owner->AI_AlertIndex >= ESearching )
				{
					memory.alertPos = owner->GetPhysics()->GetOrigin();
					memory.alertClass = EAlertTactile;
					memory.alertType = EAlertTypeSuspiciousItem;
					memory.alertRadius = AUDIO_ALERT_RADIUS;
					memory.alertSearchVolume = AUDIO_SEARCH_VOLUME;
					memory.alertSearchExclusionVolume.Zero();
					owner->AI_VISALERT = false;
					memory.visualAlert = false;
					memory.mandatory = false;
					memory.stimulusLocationItselfShouldBeSearched = true;
					memory.investigateStimulusLocationClosely = false;
					memory.alertedDueToCommunication = false;
				}
			}

			_pocketPickedState = EStateStopping;
			break;
			}
		case EStateStopping:
			// If AI is sitting, he won't stand or stop moving

			if ( owner->GetMoveType() == MOVETYPE_SIT )
			{
				_waitEndTime = gameLocal.time;
				_pocketPickedState = EStateTurnToward;
			}
			else
			{
				// Stop moving
				owner->StopMove(MOVE_STATUS_DONE);
				_pocketPickedState = EStateStartAnim;
			}
			break;
		case EStateStartAnim:
			// Don't play the animation if you're holding a torch or lantern.
			// If you're holding a weapon, you probably shouldn't be here, but
			// the weapon defs include replacement anims.
			if ( ( owner->GetTorch() == NULL ) && ( owner->GetLantern() == NULL ))
			{
				// Start the animation
				owner->SetAnimState(ANIMCHANNEL_TORSO, "Torso_PocketPicked", 4);
				owner->SetAnimState(ANIMCHANNEL_LEGS, "Legs_PocketPicked", 4);

				owner->SetWaitState("pocketPicked");
				owner->SetWaitState(ANIMCHANNEL_TORSO, "pocketPicked");
				owner->SetWaitState(ANIMCHANNEL_LEGS, "pocketPicked");
			}
			_pocketPickedState = EStatePlayAnim;
			break;
		case EStatePlayAnim:
			if (idStr(owner->WaitState()) != "pocketPicked")
			{
				// Turn to look behind you.
				owner->TurnToward(owner->GetCurrentYaw() + 180);
				_waitEndTime = gameLocal.time + 1000;
				_pocketPickedState = EStateTurnToward;
			}
			break;
		case EStateTurnToward:
			if (gameLocal.time >= _waitEndTime)
			{
				// Done turning.

				idVec3 vec;
				float duration = LOOK_TIME_MIN + gameLocal.random.RandomFloat()*(LOOK_TIME_MAX - LOOK_TIME_MIN);
				if ( owner->GetMoveType() == MOVETYPE_SIT )
				{
					// Stare at the ground to your right or left, as if looking for something

					idAngles angles = owner->viewAxis.ToAngles();
					float forward = angles.yaw;
					float lookAngle = forward + (gameLocal.random.RandomFloat() < 0.5 ? 70 : -70);
					idAngles lookAngles(0.0f, lookAngle, 0.0f);
					vec = lookAngles.ToForward()*100; // ToForward() returns unit vector
				}
				else
				{
					// Stare at the ground in front of you, as if looking for something

					vec = owner->viewAxis.ToAngles().ToForward()*100; // ToForward() returns unit vector
				}
				vec.z = 0;
				idVec3 lookAtMe = owner->GetEyePosition() + vec;
				lookAtMe.z = owner->GetPhysics()->GetOrigin().z;
				owner->Event_LookAtPosition(lookAtMe, MS2SEC(duration));
				_pocketPickedState = EStateLookAt;
				_waitEndTime = gameLocal.time + duration;
			}
			break;
		case EStateLookAt: // look for a while
			if (gameLocal.time >= _waitEndTime)
			{
				// Done looking, end this state
				Wrapup(owner);
				return;
			}
			break;
		default:
			break;
	}
}
Example #4
0
void ExamineRopeState::Init(idAI* owner)
{
	//Memory& memory = owner->GetMemory();

	State::Init(owner);

	DM_LOG(LC_AI, LT_INFO)LOGSTRING("ExamineRopeState initialized.\r");
	assert(owner);

	idAFEntity_Generic* rope = _rope.GetEntity();
	if ( rope == NULL ) // this could happen if the player frobs the rope arrow
	{
		Wrapup(owner);
		return;
	}

	idVec3 goalDirection;
	idVec3 ownerOrg = owner->GetPhysics()->GetOrigin();
	int areaNum = owner->PointReachableAreaNum(ownerOrg, 1.0f);

	idEntity* bindMaster = rope->GetBindMaster();
	goalDirection = ownerOrg - _point; // fallback - use a direction from the point to the AI

	if ( bindMaster != NULL ) // the bindMaster is the stuck rope arrow
	{
		goalDirection = rope->GetPhysics()->GetOrigin() - bindMaster->GetPhysics()->GetOrigin(); // preferred direction
	}
	goalDirection.z = 0; // ignore vertical component
	goalDirection.NormalizeFast();

	idVec3 size(16, 16, 82);
	idAAS* aas = owner->GetAAS();
	if (aas)
	{
		size = aas->GetSettings()->boundingBoxes[0][1];
	}

	// Move away from _point and perform a trace down to detect the ground.

	float standOff = 4*size.x; // any closer than this makes the "look up" animation look a bit strained (ouch!) 
	idVec3 startPoint = _point + goalDirection * standOff;
	idVec3 bottomPoint = startPoint;
	bottomPoint.z -= 1000;

	idVec3 tp1 = startPoint;
	trace_t result;
	bool tp1Found = false;
	if ( gameLocal.clip.TracePoint(result, startPoint, bottomPoint, MASK_OPAQUE, NULL) )
	{
		// Found the floor.

		tp1.z = result.endpos.z + 1; // move the target point to just above the floor
		tp1Found = true;
	}

	// Reverse direction and see if the floor is closer in that direction

	startPoint = _point - goalDirection * standOff;
	idVec3 tp2 = startPoint;
	bool tp2Found = false;
	bottomPoint = startPoint;
	bottomPoint.z -= 1000;

	if (gameLocal.clip.TracePoint(result, startPoint, bottomPoint, MASK_OPAQUE, NULL))
	{
		// Found the floor.

		tp2.z = result.endpos.z + 1; // move the target point to just above the floor
		tp2Found = true;
	}

	bool use1 = false;

	if ( tp1Found )
	{
		if ( tp2Found )
		{
			float tp1ZDelta = abs(ownerOrg.z - tp1.z);
			float tp2ZDelta = abs(ownerOrg.z - tp2.z);
			use1 = ( tp1ZDelta <= tp2ZDelta );
		}
		else
		{
			use1 = true;
		}
	}

	_examineSpot = _point; // if no spots are found for the AI to stage an examination, search around _point
	int targetAreaNum;
	aasPath_t path;
	bool pathToGoal = false; // whether there's a path to the target point near the rope
	if ( use1 )
	{
		// Is there a path to tp1?

		targetAreaNum = owner->PointReachableAreaNum(tp1, 1.0f);
		if ( owner->PathToGoal(path, areaNum, ownerOrg, targetAreaNum, tp1, owner) )
		{
			pathToGoal = true;
			_examineSpot = tp1;
		}
	}

	if ( !pathToGoal )
	{
		if ( tp2Found )
		{
			// Is there a path to tp2?

			targetAreaNum = owner->PointReachableAreaNum(tp2, 1.0f);
			if ( owner->PathToGoal(path, areaNum, ownerOrg, targetAreaNum, tp2, owner) )
			{
				pathToGoal = true;
				_examineSpot = tp2;
			}
		}
	}

	if ( pathToGoal )
	{
		owner->actionSubsystem->ClearTasks();
		owner->movementSubsystem->ClearTasks();

		// if AI is sitting, he has to stand before sending him on his way

		owner->movementSubsystem->PushTask(TaskPtr(new MoveToPositionTask(_examineSpot,idMath::INFINITY,5)));
		if (owner->GetMoveType() == MOVETYPE_SIT)
		{
			_examineRopeState = EStateSitting;
		}
		else
		{
			_examineRopeState = EStateStarting;
		}

		_waitEndTime = gameLocal.time + 1000; // allow time for move to begin
		return;
	}

	// There's no path to the goal. Go straight to searching.

	_waitEndTime = 0;
	_examineRopeState = EStateFinal;
}
Example #5
0
// Gets called each time the mind is thinking
void ExamineRopeState::Think(idAI* owner)
{
	// It's possible that during the examination of a rope, the player
	// frobs back the rope arrow. You have to account for that.

	Memory& memory = owner->GetMemory();

	idAFEntity_Generic* rope = _rope.GetEntity();

	if ( rope == NULL ) // this could happen if the player frobs the rope arrow
	{
		Wrapup(owner);
		return;
	}

	// check if something happened to abort the examination
	if (owner->GetMemory().stopExaminingRope)
	{
		Wrapup(owner);
		return;
	}

	owner->PerformVisualScan();	// Let the AI check its senses
	if (owner->AI_AlertLevel >= owner->thresh_5) // finished if alert level is too high
	{
		Wrapup(owner);
		return;
	}

	if ((owner->m_HandlingDoor) || (owner->m_HandlingElevator))
	{
		return; // we're handling a door or elevator, so delay the examination
	}

	switch (_examineRopeState)
	{
		case EStateSitting:
			if (gameLocal.time >= _waitEndTime)
			{
				if (owner->AI_MOVE_DONE && (owner->GetMoveType() != MOVETYPE_GET_UP)) // standing yet?
				{
					owner->movementSubsystem->PushTask(TaskPtr(new MoveToPositionTask(_examineSpot,idMath::INFINITY,5)));
					_examineRopeState = EStateStarting;
					_waitEndTime = gameLocal.time + 1000; // allow time for move to begin
				}
			}
			break;
		case EStateStarting:
			if (owner->AI_FORWARD || (gameLocal.time >= _waitEndTime))
			{
				_examineRopeState = EStateApproaching;
			}
			break;
		case EStateApproaching: // Walking toward the rope
			if (owner->AI_MOVE_DONE)
			{
				owner->TurnToward(_point);
				_examineRopeState = EStateTurningToward;
				_waitEndTime = gameLocal.time + 750; // allow time for turn to complete
			}
			break;
		case EStateTurningToward:
			if (gameLocal.time >= _waitEndTime)
			{
				StartExaminingTop(owner); // AI looks at top of rope
				_examineRopeState = EStateExamineTop;
				_waitEndTime = gameLocal.time + 3000;
			}
			break;
		case EStateExamineTop:
			if (gameLocal.time >= _waitEndTime)
			{
				StartExaminingBottom(owner); // AI looks at bottom of rope
				_waitEndTime = gameLocal.time + 3000;
				_examineRopeState = EStateExamineBottom;
			}
			break;
		case EStateExamineBottom:
			if (gameLocal.time >= _waitEndTime)
			{
				_waitEndTime = gameLocal.time + 1000;
				_examineRopeState = EStateFinal;
			}
			break;
		case EStateFinal:
			if (gameLocal.time >= _waitEndTime)
			{
				// Set up search if latched

				if (owner->m_LatchedSearch)
				{
					owner->m_LatchedSearch = false;
					if (owner->AI_AlertLevel < owner->thresh_4)
					{
						memory.alertPos = _examineSpot;
						memory.alertClass = EAlertVisual_4; // grayman #3498 (was _2)
						memory.alertType = EAlertTypeSuspiciousItem;

						// Do search as if there is an enemy that has escaped
						memory.alertRadius = LOST_ENEMY_ALERT_RADIUS;
						memory.alertSearchVolume = LOST_ENEMY_SEARCH_VOLUME; 
						memory.alertSearchExclusionVolume.Zero();
						
						owner->AI_VISALERT = false;
						memory.visualAlert = false; // grayman #2422
						memory.mandatory = false;	// grayman #3331
						
						// Do new reaction to stimulus
						memory.stimulusLocationItselfShouldBeSearched = true;

						// If the rope origin is close to your feet, do a close investigation

						float ropeDist = rope->GetPhysics()->GetOrigin().z - owner->GetPhysics()->GetOrigin().z;
						memory.investigateStimulusLocationClosely = ( abs(ropeDist) <= 20 );
						memory.alertedDueToCommunication = false;
					}
				}

				Wrapup(owner);
				return;
			}
			break;
		default:
			break;
	}
}
void HitByMoveableState::Init(idAI* owner)
{
	State::Init(owner);

	DM_LOG(LC_AI, LT_INFO)LOGSTRING("HitByMoveableState initialized.\r");
	assert(owner);

	idEntity* tactEnt = owner->GetMemory().hitByThisMoveable.GetEntity();
	if ( tactEnt == NULL )
	{
		Wrapup(owner); // no entity = no reaction
		return;
	}

	// grayman #3424 - don't process if dead or unconscious
	if ( owner->AI_DEAD || owner->AI_KNOCKEDOUT )
	{
		Wrapup(owner);
		return;
	}

	if ( !owner->spawnArgs.GetBool("canSearch","1") )
	{
		// TODO: AI that won't search should probably change
		// direction, or flee.

		Wrapup(owner); // won't search, so shouldn't react
		return;
	}

	idVec3 dir = tactEnt->GetPhysics()->GetOrigin() - owner->GetPhysics()->GetOrigin(); // direction to object
	dir.z = 0; // zero vertical component
	dir.NormalizeFast();

	// If the object is above the AI's eyes, assume it arrived
	// from above. Raise the point the AI will look at when he turns back.

	float vertDelta = tactEnt->GetPhysics()->GetOrigin().z - owner->GetEyePosition().z;

	if ( vertDelta > 0 )
	{
		dir.z = 0.5;
		dir.NormalizeFast();
	}

	_pos = owner->GetEyePosition() + dir*HIT_DIST; // where to look when turning back

	_responsibleActor = tactEnt->m_SetInMotionByActor;	// who threw it

	_lookAtDuration   = owner->spawnArgs.GetFloat("hitByMoveableLookAtTime","2.0");   // how long to look at what hit you
	_lookBackDuration = owner->spawnArgs.GetFloat("hitByMoveableLookBackTime","2.0"); // how long to look back at where the object came from

	// TODO: Do we need to abort door or elevator handling here?
	// How about relighting?
	// How about examining a rope?

	owner->actionSubsystem->ClearTasks();
	owner->movementSubsystem->ClearTasks();

	owner->StopMove(MOVE_STATUS_DONE);
	owner->GetMemory().stopRelight = true;
	owner->GetMemory().stopExaminingRope = true;
	owner->GetMemory().stopReactingToHit = false;

	// if AI is sitting or sleeping, he has to stand before looking around

	if ( ( owner->GetMoveType() == MOVETYPE_SIT ) || ( owner->GetMoveType() == MOVETYPE_SLEEP ) )
	{
		owner->GetUp();
		_hitByMoveableState = EStateSittingSleeping;
	}
	else
	{
		_hitByMoveableState = EStateStarting;
	}

	_waitEndTime = gameLocal.time + 500; // pause before reacting
	owner->m_ReactingToHit = true;
}
// Gets called each time the mind is thinking
void HitByMoveableState::Think(idAI* owner)
{
	// grayman #3424 - don't process if dead or unconscious
	if ( owner->AI_DEAD || owner->AI_KNOCKEDOUT )
	{
		Wrapup(owner);
		return;
	}

	// check if something happened to abort the state
	if (owner->GetMemory().stopReactingToHit)
	{
		Wrapup(owner);
		return;
	}

	owner->PerformVisualScan();	// Let the AI check its senses
	if (owner->AI_AlertLevel >= owner->thresh_5) // finished if alert level is too high
	{
		Wrapup(owner);
		return;
	}

	idEntity* tactEnt = owner->GetMemory().hitByThisMoveable.GetEntity();
	if ( tactEnt == NULL )
	{
		Wrapup(owner);
		return;
	}

	switch (_hitByMoveableState)
	{
		case EStateSittingSleeping:
			if (gameLocal.time >= _waitEndTime)
			{
				if (owner->AI_MOVE_DONE && (owner->GetMoveType() != MOVETYPE_GET_UP)) // standing yet?
				{
					_hitByMoveableState = EStateTurnToward;
				}
			}
			break;
		case EStateStarting:
			if (gameLocal.time >= _waitEndTime)
			{
				_hitByMoveableState = EStateTurnToward;
			}
			break;
		case EStateTurnToward: // Turn toward the object that hit you.
			{
				// if _lookAtDuration is 0, skip turning toward and looking at the object

				if ( _lookAtDuration == 0 )
				{
					_hitByMoveableState = EStateTurnBack;
				}
				else
				{
					owner->TurnToward(tactEnt->GetPhysics()->GetOrigin());
					_hitByMoveableState = EStateLookAt;
					_waitEndTime = gameLocal.time + 1000;
				}
			}
			break;
		case EStateLookAt: // look at the entity
			if (gameLocal.time >= _waitEndTime)
			{
				float duration = _lookAtDuration + (_lookAtDuration/5)*(gameLocal.random.RandomFloat() - 0.5f );
				owner->Event_LookAtEntity( tactEnt, duration );

				_waitEndTime = gameLocal.time + duration*1000;
				_hitByMoveableState = EStateTurnBack;
			}
			break;
		case EStateTurnBack: // turn toward the direction the entity came from
			if (gameLocal.time >= _waitEndTime)
			{
				owner->TurnToward(_pos);
				_waitEndTime = gameLocal.time + 1000;
				_hitByMoveableState = EStateLookBack;
			}
			break;
		case EStateLookBack: // look in the direction the entity came from
			if (gameLocal.time >= _waitEndTime)
			{
				if ( _lookBackDuration > 0 )
				{
					float duration = _lookBackDuration + (_lookBackDuration/5)*(gameLocal.random.RandomFloat() - 0.5f );
					owner->Event_LookAtPosition( _pos, duration );
					_waitEndTime = gameLocal.time + duration*1000;
				}
				_hitByMoveableState = EStateFinal;
			}
			break;
		case EStateFinal:
			if (gameLocal.time >= _waitEndTime)
			{
				// If we make it to here, we haven't spotted
				// an enemy, otherwise our alert level would
				// have been high enough to abort this state
				// at the beginning of Think().

				// If we see a friend or a neutral, we assume
				// this was a harmless impact, and we can return
				// to what we were doing.

				idVec3 ownerOrg = owner->GetPhysics()->GetOrigin();
				idBounds bounds( ownerOrg - idVec3( HIT_FIND_THROWER_HORZ, HIT_FIND_THROWER_HORZ, HIT_FIND_THROWER_VERT ), ownerOrg + idVec3( HIT_FIND_THROWER_HORZ, HIT_FIND_THROWER_HORZ, HIT_FIND_THROWER_VERT ) );

				idEntity* ents[MAX_GENTITIES];
				int num = gameLocal.clip.EntitiesTouchingBounds( bounds, CONTENTS_BODY, ents, MAX_GENTITIES );

				for ( int i = 0 ; i < num ; i++ )
				{
					idEntity *candidate = ents[i];

					if ( candidate == NULL ) // just in case
					{
						continue;
					}

					if ( candidate == owner ) // skip myself
					{
						continue;
					}

					if ( !candidate->IsType(idActor::Type) ) // skip non-Actors
					{
						continue;
					}

					idActor *candidateActor = static_cast<idActor *>(candidate);

					if ( candidateActor->GetPhysics()->GetMass() <= 5.0 ) // skip actors with small mass (rats, spiders)
					{
						continue;
					}

					if ( candidateActor->IsKnockedOut() || ( candidateActor->health < 0 ) )
					{
						continue; // skip if knocked out or dead
					}

					if ( candidateActor->IsType(idAI::Type) )
					{
						if ( static_cast<idAI*>(candidateActor)->GetMoveType() == MOVETYPE_SLEEP )
						{
							continue; // skip if asleep
						}
					}

					if ( candidateActor->fl.notarget )
					{
						continue; // skip if NOTARGET is set
					}

					if ( owner->IsFriend(candidateActor) || owner->IsNeutral(candidateActor) )
					{
						// Don't admonish the alleged thrower if tactEnt came in from above (dropped)

						if ( _pos.z <= owner->GetEyePosition().z )
						{
							// make sure there's LOS (no walls in between)

							if ( owner->CanSeeExt( candidateActor, false, false ) ) // don't use FOV or lighting, to increase chance of success
							{
								// look at your friend/neutral if they're in your FOV
								int delay = 0; // bark delay
								if ( owner->CanSeeExt( candidateActor, true, false) ) // use FOV this time, but ignore lighting
								{
									owner->Event_LookAtPosition(candidateActor->GetEyePosition(),2.0f);
									delay = 1000; // give head time to move before barking
								}

								// bark an admonishment whether you're facing them or not

								CommMessagePtr message; // no message, but the argument is needed so the start delay can be included
								owner->commSubsystem->AddCommTask(CommunicationTaskPtr(new SingleBarkTask("snd_admonish_friend",message,delay)));

								if (cv_ai_debug_transition_barks.GetBool())
								{
									gameLocal.Printf("%d: %s hit by moveable, barks 'snd_admonish_friend'\n",gameLocal.time,owner->GetName());
								}

								Wrapup(owner);
								return;
							}
						}
					}
				}

				// No one about, so let's cheat. If a friend or neutral threw
				// the object, we return to what we were doing.

				idActor* responsible = _responsibleActor.GetEntity();

				if ( ( responsible == NULL ) || owner->IsFriend(responsible) || owner->IsNeutral(responsible) )
				{
					Wrapup(owner);
					return;
				}

				// No one about. If an enemy threw the object, start searching.

				if ( owner->IsEnemy(responsible) )
				{
					Memory& memory = owner->GetMemory();
					memory.alertType = EAlertTypeSuspicious;
					memory.alertClass = EAlertTactile;
					memory.alertPos = ownerOrg;
					memory.alertRadius = TACTILE_ALERT_RADIUS;
					memory.alertSearchVolume = TACTILE_SEARCH_VOLUME;
					memory.alertSearchExclusionVolume.Zero();
					memory.visualAlert = false;

					// If we got this far, we give the alert
					// Set the alert amount to the according tactile alert value
					float amount = cv_ai_tactalert.GetFloat();

					// NOTE: Latest tactile alert always overrides other alerts
					owner->m_TactAlertEnt = tactEnt;
					owner->m_AlertedByActor = responsible;

					amount *= owner->GetAcuity("tact");
					// grayman #3009 - pass the alert position so the AI can look in the direction of who's responsible
					owner->PreAlertAI("tact", amount, responsible->GetEyePosition()); // grayman #3356

					// Set last visual contact location to this location as that is used in case
					// the target gets away.
					owner->m_LastSight = ownerOrg;

					// If no enemy set so far, set the last visible enemy position.
					if ( owner->GetEnemy() == NULL )
					{
						owner->lastVisibleEnemyPos = ownerOrg;
					}

					owner->AI_TACTALERT = true;
					memory.mandatory = true; // grayman #3331
				}

				Wrapup(owner);
				return;
			}
			break;
		default:
			break;
	}
}