Пример #1
0
void CTentacle :: DieThink( void )
{
	pev->nextthink = gpGlobals-> time + 0.1;

	DispatchAnimEvents( );
	StudioFrameAdvance( );

	ChangeYaw( 24 );

	if (m_fSequenceFinished)
	{
		if (pev->sequence == m_iGoalAnim)
		{
			switch( m_iGoalAnim )
			{
			case TENTACLE_ANIM_Engine_Idle:
			case TENTACLE_ANIM_Engine_Sway:
			case TENTACLE_ANIM_Engine_Swat:
			case TENTACLE_ANIM_Engine_Bob:
				m_iGoalAnim = TENTACLE_ANIM_Engine_Sway + RANDOM_LONG( 0, 2 );
				break;
			case TENTACLE_ANIM_Engine_Death1:
			case TENTACLE_ANIM_Engine_Death2:
			case TENTACLE_ANIM_Engine_Death3:
				UTIL_Remove( this );
				return;
			}
		}

		// ALERT( at_console, "%d : %d => ", pev->sequence, m_iGoalAnim );
		pev->sequence = FindTransition( pev->sequence, m_iGoalAnim, &m_iDir );
		// ALERT( at_console, "%d\n", pev->sequence );

		if (m_iDir > 0)
		{
			pev->frame = 0;
		}
		else
		{
			pev->frame = 255;
		}
		ResetSequenceInfo( );

		float dy;
		switch( pev->sequence )
		{
		case TENTACLE_ANIM_Floor_Rear:
		case TENTACLE_ANIM_Floor_Rear_Idle:
		case TENTACLE_ANIM_Lev1_Rear:
		case TENTACLE_ANIM_Lev1_Rear_Idle:
		case TENTACLE_ANIM_Lev2_Rear:
		case TENTACLE_ANIM_Lev2_Rear_Idle:
		case TENTACLE_ANIM_Lev3_Rear:
		case TENTACLE_ANIM_Lev3_Rear_Idle:
		case TENTACLE_ANIM_Engine_Idle:
		case TENTACLE_ANIM_Engine_Sway:
		case TENTACLE_ANIM_Engine_Swat:
		case TENTACLE_ANIM_Engine_Bob:
		case TENTACLE_ANIM_Engine_Death1:
		case TENTACLE_ANIM_Engine_Death2:
		case TENTACLE_ANIM_Engine_Death3:
			pev->framerate = RANDOM_FLOAT( m_iDir - 0.2, m_iDir + 0.2 );
			dy = 180;
			break;
		default:
			pev->framerate = 1.5;
			dy = 0;
			break;
		}
		pev->ideal_yaw = m_flInitialYaw + dy;
	}
}
int32 VirtualRenderer::RenderLoop()
{
	BList			timeList(1);
	EventComposant 	*composant;
	bigtime_t		*time, *time2, duration;
	
	bool		firstTrackActive = true;
	int			i, j, timeEvent;
	roster = BMediaRoster::Roster();
	
	/* Retrieve all the times where an event occur (starts and stops) in the list */
	for (i = 0; i < eventList->CountItems(); i++)
	{
		composant = eventList->ItemAt(i);
		composant->startRendered = false;
		composant->endRendered = false;
		time = new bigtime_t;
		*time = composant->time;
		timeList.AddItem(time);
		time = new bigtime_t;
		*time = composant->time + composant->end;
		timeList.AddItem(time);
	}
	timeList.SortItems(compare);
	/*  Instantiate a writer node */
	writer = new DiskWriter(prefs->saveFile, prefs->format, prefs->video_codec, prefs->audio_codec, 0);
	roster->SetRefFor(writer->Node(), prefs->saveFile, true, &duration);
	BMessenger	*messenger = new BMessenger(this);;
	roster->SetRunModeNode(writer->Node(), BMediaNode::B_OFFLINE);
	roster->StartWatching(*messenger, writer->Node(), B_MEDIA_NODE_STOPPED);
	/* 	if there is not video available at the beginning, we need a null generator
		to fill in the beginning of the file, else we enter the loop	*/
	//	if (eventList->FirstItem()->time != 0)
//	{
//		nullgen = new NullGen;
//		
//	}
	timeEvent = 0;
	media_output		outputs[3];
	int32				numOutputs;
	media_input			inputs[4];
	int32				numInputs;
	media_source		sources[2];
	media_destination	destinations[2];
	media_format		format;
	media_node			filterNode, transitionNode;
	bool				doConnect = false;
	bool				writerConnected = false;
	bool				reader1Connected = false;
	bool				reader2Connected = false;
	bool				filterConnected = false;
	bool				transitionConnected = false;
	
	for (timeEvent = 0; timeEvent < timeList.CountItems(); timeEvent++)
	{
		if (acquire_sem(lock_sem) == B_OK)
		{
			i = 0;
//			while (*((bigtime_t*)timeList.ItemAt(timeEvent)) != eventList->ItemAt(i)->time)
//				i++;
			duration = *((bigtime_t*)timeList.ItemAt(timeEvent));
			while (i < eventList->CountItems())
			{
				composant = eventList->ItemAt(i);
				if ((composant->time == duration) && !composant->startRendered)
				{
					composant->startRendered = true;
					break;
				}
				if ((composant->time + composant->end == duration) && !composant->endRendered)
				{
					composant->endRendered = true;
					break;
				}
				i++;
			}
			/*  We must find the events that just terminated */
			for (j = i; j < eventList->CountItems(); j++)
			{
				composant = (EventComposant*)eventList->ItemAt(j);
				if (composant->time + composant->end == *(bigtime_t*)timeList.ItemAt(timeEvent))
				switch (composant->event)
				{
					case video1:
						roster->ReleaseNode(reader1->Node());
						reader1 = NULL;
						reader1Connected = false;
						break;
					case video2:
						roster->ReleaseNode(reader2->Node());
						reader2 = NULL;
						reader2Connected = false;
						break;
					case transition:
						firstTrackActive = !firstTrackActive;
						roster->GetConnectedInputsFor(filterNode, &inputs[0], 2, &numInputs);
						roster->Disconnect(reader1->Node().node, inputs[0].source, transitionNode.node, inputs[0].destination);
						roster->Disconnect(reader2->Node().node, inputs[1].source, transitionNode.node, inputs[1].destination);
						
						roster->GetConnectedOutputsFor(transitionNode, &outputs[0], 1, &numOutputs);
						if (filterConnected)
						{
							roster->Disconnect(transitionNode.node, outputs[0].source, filterNode.node, outputs[0].destination);
							if (firstTrackActive)
								roster->Connect(inputs[0].source, outputs[0].destination, &inputs[0].format, &outputs[0], &inputs[0]);
							else
								roster->Connect(inputs[1].source, outputs[0].destination, &inputs[1].format,  &outputs[0], &inputs[1]);
						}
						else
						{
							roster->Disconnect(transitionNode.node, outputs[0].source, writer->Node().node, outputs[0].destination);
							if (firstTrackActive)
								roster->Connect(inputs[0].source, outputs[0].destination, &inputs[0].format, &outputs[0], &inputs[0]);
							else
								roster->Connect(inputs[1].source, outputs[0].destination, &inputs[1].format, &outputs[0], &inputs[1]);						
						}
						transitionConnected = false;
						break;
					case filter:
						roster->GetConnectedInputsFor(filterNode, &inputs[0], 1, &numInputs);
						if (transitionConnected)
						{
							roster->Disconnect(transitionNode.node, inputs[0].source, filterNode.node, inputs[0].destination);
							roster->GetConnectedOutputsFor(filterNode, &outputs[0], 1, &numOutputs);
							roster->Disconnect(filterNode.node, outputs[0].source, writer->Node().node, outputs[0].destination);
							roster->Connect(inputs[0].source, outputs[0].destination, &inputs[0].format, &outputs[0], &inputs[0]);
							filterConnected = false;
						}
						else if (firstTrackActive)
						{
							roster->Disconnect(reader1->Node().node, inputs[0].source, filterNode.node, inputs[0].destination);
							roster->GetConnectedOutputsFor(filterNode, &outputs[0], 1, &numOutputs);
							roster->Disconnect(filterNode.node, outputs[0].source, writer->Node().node, outputs[0].destination);
							roster->Connect(inputs[0].source, outputs[0].destination, &inputs[0].format, &outputs[0], &inputs[0]);
							filterConnected = false;							
						}
						else if (!firstTrackActive)
						{
							roster->Disconnect(reader2->Node().node, inputs[0].source, filterNode.node, inputs[0].destination);
							roster->GetConnectedOutputsFor(filterNode, &outputs[0], 1, &numOutputs);
							roster->Disconnect(filterNode.node, outputs[0].source, writer->Node().node, outputs[0].destination);
							roster->Connect(inputs[0].source, outputs[0].destination, &inputs[0].format, &outputs[0], &inputs[0]);
							filterConnected = false;							
						}
						
						
						break;
					default:
						break;
				}
			}
			if (timeEvent == timeList.CountItems() - 1)
				break;
		
			doConnect = false;
			composant = eventList->ItemAt(i);
			switch (composant->event)
			{
				case video1:
					if (reader1 != NULL)
					{
						roster->GetConnectedOutputsFor(reader1->Node(), outputs, 1, &numOutputs);
						if (numOutputs == 1)
						{
							destinations[0] = outputs[0].destination;
							doConnect = true;
						}
						//roster->ReleaseNode(reader1->Node());
						delete reader1;
						reader1Connected = false;
						reader1 = NULL;
					}
					reader1 = new FileReader(composant->u.video.filepath, composant->u.video.filepath, 0);
					roster->SetRunModeNode(reader1->Node(), BMediaNode::B_OFFLINE);
					if (doConnect)
					{
						roster->GetFreeOutputsFor(reader1->Node(), outputs, 1, &numOutputs);
						roster->Connect(outputs[0].source, destinations[0], &outputs[0].format, &outputs[0], &inputs[0]);
						reader1Connected = true;
					}
					else if (transitionConnected)
					{
						roster->GetFreeInputsFor(transitionNode, inputs, 1, &numInputs);
						roster->GetFreeOutputsFor(reader1->Node(), outputs, 1, &numOutputs);
						roster->GetFormatFor(outputs[0], &format);
						roster->Connect(outputs[0].source, inputs[0].destination, &format, &outputs[0], &inputs[0]);
						reader1Connected = true;
					}
					else if (filterConnected)
					{
						roster->GetFreeInputsFor(filterNode, inputs, 1, &numInputs);
						roster->GetFreeOutputsFor(reader1->Node(), outputs, 1, &numOutputs);
						roster->GetFormatFor(outputs[0], &format);
						roster->Connect(outputs[0].source, inputs[0].destination, &format, &outputs[0], &inputs[0]);
						reader1Connected = true;					
					}
					else if (!writerConnected && firstTrackActive)
					{
						roster->GetFreeInputsFor(writer->Node(), inputs, 1, &numInputs);
						roster->GetFreeOutputsFor(reader1->Node(), outputs, 1, &numOutputs);
						roster->GetFormatFor(outputs[0], &format);
						roster->Connect(outputs[0].source, inputs[0].destination, &format, &outputs[0], &inputs[0]);
						writerConnected = true;
						reader1Connected = true;
					}
					time = (bigtime_t*)timeList.ItemAt(timeEvent);
					time2 = (bigtime_t*)timeList.ItemAt(timeEvent + 1);
					
					/* Tell the nodes to start until next time event */
					roster->RollNode(writer->Node(), *time, *time2);
					if (filterConnected)
						roster->RollNode(filterNode, *time, *time2);
					if (transitionConnected)
						roster->RollNode(transitionNode, *time, *time2);
					roster->RollNode(reader1->Node(), *time, *time2, composant->u.video.begin);
					break;
				case video2:
					if (reader2 != NULL)
					{
						roster->GetConnectedOutputsFor(reader2->Node(), outputs, 1, &numOutputs);
						if (numOutputs == 1)
						{
							destinations[0] = outputs[0].destination;
							doConnect = true;
						}
						//roster->ReleaseNode(reader2->Node());
						delete reader2;
						reader2Connected = false;
						reader2 = NULL;
					}
					reader2 = new FileReader(composant->u.video.filepath, composant->u.video.filepath, 0);
					roster->SetRunModeNode(reader2->Node(), BMediaNode::B_OFFLINE);
					if (doConnect)
					{
						roster->GetFreeOutputsFor(reader2->Node(), outputs, 1, &numOutputs);
						roster->Connect(outputs[0].source, destinations[0], &outputs[0].format, &outputs[0], &inputs[0]);
						reader2Connected = true;
					}
//					else if (transitionConnected)
//					{
//						roster->GetFreeInputsFor(transitionNode, inputs, 1, &numInputs);
//						roster->GetFreeOutputsFor(reader2->Node(), outputs, 1, &numOutputs);
//						roster->GetFormatFor(outputs[0], &format);
//						roster->Connect(outputs[0].source, inputs[0].destination, &format, &outputs[0], &inputs[0]);
//						reader2Connected = true;
//					}
//					else if (filterConnected)
//					{
//						roster->GetFreeInputsFor(filterNode, inputs, 1, &numInputs);
//						roster->GetFreeOutputsFor(reader2->Node(), outputs, 1, &numOutputs);
//						roster->GetFormatFor(outputs[0], &format);
//						roster->Connect(outputs[0].source, inputs[0].destination, &format, &outputs[0], &inputs[0]);
//						reader2Connected = true;					
//					}
//					else if (!writerConnected && !firstTrackActive)
//					{
//						roster->GetFreeInputsFor(writer->Node(), inputs, 1, &numInputs);
//						roster->GetFreeOutputsFor(reader2->Node(), outputs, 1, &numOutputs);
//						roster->GetFormatFor(outputs[0], &format);
//						roster->Connect(outputs[0].source, inputs[0].destination, &format, &outputs[0], &inputs[0]);
//						writerConnected = true;
//						reader2Connected = true;
//					}
					time = (bigtime_t*)timeList.ItemAt(timeEvent);
					time2 = (bigtime_t*)timeList.ItemAt(timeEvent + 1);
					
					roster->RollNode(writer->Node(), *time, *time2);
					if (filterConnected)
						roster->RollNode(filterNode, *time, *time2);
					if (transitionConnected)
						roster->RollNode(transitionNode, *time, *time2);
					if (reader1Connected && firstTrackActive)
						roster->RollNode(transitionNode, *time, *time2);
					if (reader2Connected && !firstTrackActive)
						roster->RollNode(reader2->Node(), *time, *time2, composant->u.video.begin);
					break;
				case filter:
				/* Handle filter events */
				if (filterConnected)
					{
						roster->GetConnectedOutputsFor(filterNode, outputs, 1, &numOutputs);
						if (numOutputs == 1)
						{
							destinations[0] = outputs[0].destination;
							doConnect = true;
						}
						roster->GetConnectedInputsFor(filterNode, inputs, 1, &numInputs);
						if (numInputs == 1)
						{
							sources[0] = inputs[0].source;
						}
						roster->ReleaseNode(filterNode);
						filterConnected = false;
					}
					FindFilter(composant->u.filter.type, &filterNode);
					SetFilterParameters(filterNode, composant->u.filter.param_list);
					roster->SetRunModeNode(filterNode, BMediaNode::B_OFFLINE);
					if (doConnect)
					{
						roster->GetFreeOutputsFor(filterNode, &outputs[1], 1, &numOutputs);
						roster->Connect(outputs[1].source, destinations[0], &outputs[0].format, &outputs[1], &inputs[1]);
						filterConnected = true;
					}
					else if (transitionConnected)
					{
						roster->GetConnectedOutputsFor(transitionNode, &outputs[0], 1, &numOutputs);
						roster->Disconnect(transitionNode.node, outputs[0].source, writer->Node().node, outputs[0].destination);
						roster->GetFreeInputsFor(filterNode, &inputs[0], 1, &numInputs);
						roster->Connect(outputs[0].source, inputs[0].destination, &outputs[0].format, &outputs[0], &inputs[0]);
						roster->GetFreeOutputsFor(filterNode, &outputs[1], 1, &numOutputs);
						roster->GetFreeInputsFor(writer->Node(), &inputs[1], 1, &numInputs);
						roster->Connect(outputs[1].source, inputs[1].destination, &inputs[1].format, &outputs[1], &inputs[1]);
						filterConnected = true;
					}
					else if (reader1Connected && firstTrackActive)
					{
						roster->GetConnectedOutputsFor(reader1->Node(), &outputs[0], 1, &numOutputs);
						roster->Disconnect(reader1->Node().node, outputs[0].source, writer->Node().node, outputs[0].destination);
						roster->GetFreeInputsFor(filterNode, &inputs[0], 1, &numInputs);
						roster->Connect(outputs[0].source, inputs[0].destination, &outputs[0].format, &outputs[0], &inputs[0]);
						roster->GetFreeOutputsFor(filterNode, &outputs[1], 1, &numOutputs);
						roster->GetFreeInputsFor(writer->Node(), &inputs[1], 1, &numInputs);
						roster->Connect(outputs[1].source, inputs[1].destination, &inputs[1].format, &outputs[1], &inputs[1]);
						filterConnected = true;
					}
					else if (reader2Connected && !firstTrackActive)
					{
						roster->GetConnectedOutputsFor(reader2->Node(), &outputs[0], 1, &numOutputs);
						roster->Disconnect(reader2->Node().node, outputs[0].source, writer->Node().node, outputs[0].destination);
						roster->GetFreeInputsFor(filterNode, &inputs[0], 1, &numInputs);
						roster->Connect(outputs[0].source, inputs[0].destination, &outputs[0].format, &outputs[0], &inputs[0]);
						roster->GetFreeOutputsFor(filterNode, &outputs[1], 1, &numOutputs);
						roster->GetFreeInputsFor(writer->Node(), &inputs[1], 1, &numInputs);
						roster->Connect(outputs[1].source, inputs[1].destination, &inputs[1].format, &outputs[1], &inputs[1]);
						filterConnected = true;					
					}
					time = (bigtime_t*)timeList.ItemAt(timeEvent);
					time2 = (bigtime_t*)timeList.ItemAt(timeEvent + 1);
					
					/* Tell the nodes to start until next time event */
					roster->RollNode(writer->Node(), *time, *time2);
					if (filterConnected)
						roster->RollNode(filterNode, *time, *time2);
					if (transitionConnected)
						roster->RollNode(transitionNode, *time, *time2);
					if (reader2Connected)
						roster->RollNode(reader2->Node(), *time, *time2);
					if (reader1Connected)
						roster->RollNode(reader1->Node(), *time, *time2);
					break;
				case transition:
				
				/* Handle transition events */
					if (transitionConnected)
					{
						roster->GetConnectedOutputsFor(transitionNode, outputs, 1, &numOutputs);
						if (numOutputs == 1)
						{
							destinations[0] = outputs[0].destination;
							doConnect = true;
						}
						roster->GetConnectedInputsFor(transitionNode, inputs, 2, &numInputs);
						if (numInputs)
						{
							sources[0] = inputs[0].source;
							sources[1] = inputs[1].source;
						}
						roster->ReleaseNode(transitionNode);
						transitionConnected = false;
					}
					FindTransition(composant->u.transition.type, &transitionNode);
					roster->SetRunModeNode(transitionNode, BMediaNode::B_OFFLINE);
					if (doConnect)
					{
						roster->GetFreeInputsFor(transitionNode, &inputs[2], 2, &numInputs);
						roster->Connect(inputs[0].source, inputs[2].destination, &inputs[0].format, &outputs[1], &inputs[2]);
						roster->Connect(inputs[1].source, inputs[3].destination, &inputs[1].format, &outputs[1], &inputs[2]);
						roster->GetFreeOutputsFor(transitionNode, &outputs[1], 1, &numOutputs);
						roster->Connect(outputs[1].source, destinations[0], &outputs[0].format, &outputs[1], &inputs[0]);
						transitionConnected = true;
					}
					else if (filterConnected)
					{
						roster->GetConnectedInputsFor(filterNode, inputs, 1, &numInputs);
						if (reader1Connected)
							roster->Disconnect(reader1->Node().node, inputs[0].source, filterNode.node, inputs[0].destination);
						else if (reader2Connected)
							roster->Disconnect(reader2->Node().node, inputs[0].source, filterNode.node, inputs[0].destination);
						roster->GetFreeInputsFor(transitionNode, &inputs[2], 2, &numInputs);
						roster->GetFreeOutputsFor(reader1->Node(), &outputs[0], 1, &numOutputs);
						roster->Connect(outputs[0].source, inputs[2].destination, &outputs[0].format, &outputs[0], &inputs[2]);
						roster->GetFreeOutputsFor(reader2->Node(), &outputs[0], 1, &numOutputs);
						roster->Connect(outputs[0].source, inputs[3].destination, &outputs[0].format, &outputs[0], &inputs[3]);
						roster->GetFreeOutputsFor(transitionNode, &outputs[2], 1, &numOutputs);
						roster->Connect(outputs[2].source, inputs[0].destination, &inputs[0].format, &outputs[2], &inputs[0]);
						transitionConnected = true;
					}
					else if (reader1Connected && firstTrackActive)
					{
						roster->GetConnectedOutputsFor(reader1->Node(), &outputs[0], 1, &numOutputs);
						roster->Disconnect(reader1->Node().node, outputs[0].source, writer->Node().node, outputs[0].destination);
						roster->GetFreeInputsFor(transitionNode, &inputs[0], 2, &numInputs);
						roster->Connect(outputs[0].source, inputs[0].destination, &outputs[0].format, &outputs[0], &inputs[0]);
						roster->GetFreeOutputsFor(reader2->Node(), &outputs[1], 1, &numOutputs);
						roster->Connect(outputs[1].source, inputs[1].destination, &outputs[0].format, &outputs[1], &inputs[1]);
						roster->GetFreeOutputsFor(transitionNode, &outputs[1], 1, &numOutputs);
						roster->GetFreeInputsFor(writer->Node(), &inputs[1], 1, &numInputs);
						roster->Connect(outputs[1].source, inputs[1].destination, &inputs[1].format, &outputs[1], &inputs[1]);
						transitionConnected = true;
					}
					else if (reader2Connected && !firstTrackActive)
					{
						roster->GetConnectedOutputsFor(reader2->Node(), &outputs[0], 1, &numOutputs);
						roster->Disconnect(reader2->Node().node, outputs[0].source, writer->Node().node, outputs[0].destination);
						roster->GetFreeInputsFor(transitionNode, &inputs[0], 2, &numInputs);
						roster->Connect(outputs[0].source, inputs[0].destination, &outputs[0].format, &outputs[0], &inputs[0]);
						roster->GetFreeOutputsFor(reader1->Node(), &outputs[1], 1, &numOutputs);
						roster->Connect(outputs[1].source, inputs[1].destination, &outputs[0].format, &outputs[1], &inputs[1]);
						roster->GetFreeOutputsFor(transitionNode, &outputs[1], 1, &numOutputs);
						roster->GetFreeInputsFor(writer->Node(), &inputs[1], 1, &numInputs);
						roster->Connect(outputs[1].source, inputs[1].destination, &inputs[1].format, &outputs[1], &inputs[1]);
						transitionConnected = true;
					}
					time = (bigtime_t*)timeList.ItemAt(timeEvent);
					time2 = (bigtime_t*)timeList.ItemAt(timeEvent + 1);
					
					/* Tell the nodes to start until next time event */
					roster->RollNode(writer->Node(), *time, *time2);
					if (filterConnected)
						roster->RollNode(filterNode, *time, *time2);
					if (transitionConnected)
						roster->RollNode(transitionNode, *time, *time2);
					if (reader2Connected)
						roster->RollNode(reader2->Node(), *time, *time2);
					if (reader1Connected)
						roster->RollNode(reader1->Node(), *time, *time2);
					break;
				default:
					break;
			}
		}
	//	release_sem(lock_sem);
	}
	delete writer;
	return B_OK;
}
Пример #3
0
//
// TentacleThink
//
void CTentacle :: Cycle( void )
{
	// ALERT( at_console, "%s %.2f %d %d\n", STRING( pev->targetname ), pev->origin.z, m_MonsterState, m_IdealMonsterState );
	pev->nextthink = gpGlobals-> time + 0.1;

	// ALERT( at_console, "%s %d %d %d %f %f\n", STRING( pev->targetname ), pev->sequence, m_iGoalAnim, m_iDir, pev->framerate, pev->health );

	if (m_MonsterState == MONSTERSTATE_SCRIPT || m_IdealMonsterState == MONSTERSTATE_SCRIPT)
	{
		pev->angles.y = m_flInitialYaw;
		pev->ideal_yaw = m_flInitialYaw;	
		ClearConditions( IgnoreConditions() );
		MonsterThink( );
		m_iGoalAnim = TENTACLE_ANIM_Pit_Idle;
		return;
	}

	DispatchAnimEvents( );
	StudioFrameAdvance( );

	ChangeYaw( pev->yaw_speed );

	CSound *pSound;

	Listen( );

	// Listen will set this if there's something in my sound list
	if ( HasConditions( bits_COND_HEAR_SOUND ) )
		pSound = PBestSound();
	else
		pSound = NULL;

	if ( pSound )
	{
		Vector vecDir;
		if (gpGlobals->time - m_flPrevSoundTime < 0.5)
		{
			float dt = gpGlobals->time - m_flPrevSoundTime;
			vecDir = pSound->m_vecOrigin + (pSound->m_vecOrigin - m_vecPrevSound) / dt - pev->origin;
		}
		else
		{
			vecDir = pSound->m_vecOrigin - pev->origin;
		}
		m_flPrevSoundTime = gpGlobals->time;
		m_vecPrevSound = pSound->m_vecOrigin;

		m_flSoundYaw = UTIL_VecToYaw ( vecDir ) - m_flInitialYaw;
		m_iSoundLevel = Level( vecDir.z );

		if (m_flSoundYaw < -180)
			m_flSoundYaw += 360;
		if (m_flSoundYaw > 180)
			m_flSoundYaw -= 360;

		// ALERT( at_console, "sound %d %.0f\n", m_iSoundLevel, m_flSoundYaw );
		if (m_flSoundTime < gpGlobals->time)
		{
			// play "I hear new something" sound
			char *sound;	

			switch( RANDOM_LONG(0,1) )
			{
			case 0: sound = "tentacle/te_alert1.wav"; break;
			case 1: sound = "tentacle/te_alert2.wav"; break;
			}

			// UTIL_EmitAmbientSound(ENT(pev), pev->origin + Vector( 0, 0, MyHeight()), sound, 1.0, ATTN_NORM, 0, 100);
		}
		m_flSoundTime = gpGlobals->time + RANDOM_FLOAT( 5.0, 10.0 );
	}

	// clip ideal_yaw
	float dy = m_flSoundYaw;
	switch( pev->sequence )
	{
	case TENTACLE_ANIM_Floor_Rear:
	case TENTACLE_ANIM_Floor_Rear_Idle:
	case TENTACLE_ANIM_Lev1_Rear:
	case TENTACLE_ANIM_Lev1_Rear_Idle:
	case TENTACLE_ANIM_Lev2_Rear:
	case TENTACLE_ANIM_Lev2_Rear_Idle:
	case TENTACLE_ANIM_Lev3_Rear:
	case TENTACLE_ANIM_Lev3_Rear_Idle:
		if (dy < 0 && dy > -m_flMaxYaw)
			dy = -m_flMaxYaw;
		if (dy > 0 && dy < m_flMaxYaw)
			dy = m_flMaxYaw;
		break;
	default:
		if (dy < -m_flMaxYaw)
			dy = -m_flMaxYaw;
		if (dy > m_flMaxYaw)
			dy = m_flMaxYaw;
	}
	pev->ideal_yaw = m_flInitialYaw + dy;

	if (m_fSequenceFinished)
	{
		// ALERT( at_console, "%s done %d %d\n", STRING( pev->targetname ), pev->sequence, m_iGoalAnim );
		if (pev->health <= 1)
		{
			m_iGoalAnim = TENTACLE_ANIM_Pit_Idle;
			if (pev->sequence == TENTACLE_ANIM_Pit_Idle)
			{
				pev->health = 75;
			}
		}
		else if ( m_flSoundTime > gpGlobals->time )
		{
			if (m_flSoundYaw >= -(m_flMaxYaw + 30) && m_flSoundYaw <= (m_flMaxYaw + 30))
			{
				// strike
				m_iGoalAnim = LookupActivity( ACT_T_STRIKE + m_iSoundLevel );
			}
			else if (m_flSoundYaw >= -m_flMaxYaw * 2 && m_flSoundYaw <= m_flMaxYaw * 2) 
			{
				// tap
				m_iGoalAnim = LookupActivity( ACT_T_TAP + m_iSoundLevel );
			}
			else
			{
				// go into rear idle
				m_iGoalAnim = LookupActivity( ACT_T_REARIDLE + m_iSoundLevel );
			}
		}
		else if (pev->sequence == TENTACLE_ANIM_Pit_Idle)
		{
			// stay in pit until hear noise
			m_iGoalAnim = TENTACLE_ANIM_Pit_Idle;
		}
		else if (pev->sequence == m_iGoalAnim)
		{
			if (MyLevel() >= 0 && gpGlobals->time < m_flSoundTime)
			{
				if (RANDOM_LONG(0,9) < m_flSoundTime - gpGlobals->time)
				{
					// continue stike
					m_iGoalAnim = LookupActivity( ACT_T_STRIKE + m_iSoundLevel );
				}
				else
				{
					// tap
					m_iGoalAnim = LookupActivity( ACT_T_TAP + m_iSoundLevel );
				}
			}
			else if (MyLevel( ) < 0)
			{
				m_iGoalAnim = LookupActivity( ACT_T_IDLE + 0 );
			}
			else
			{
				if (m_flNextSong < gpGlobals->time)
				{
					// play "I hear new something" sound
					char *sound;	

					switch( RANDOM_LONG(0,1) )
					{
					case 0: sound = "tentacle/te_sing1.wav"; break;
					case 1: sound = "tentacle/te_sing2.wav"; break;
					}

					EMIT_SOUND(ENT(pev), CHAN_VOICE, sound, 1.0, ATTN_NORM);

					m_flNextSong = gpGlobals->time + RANDOM_FLOAT( 10, 20 );
				}

				if (RANDOM_LONG(0,15) == 0)
				{
					// idle on new level
					m_iGoalAnim = LookupActivity( ACT_T_IDLE + RANDOM_LONG(0,3) );
				}
				else if (RANDOM_LONG(0,3)  == 0)
				{
					// tap
					m_iGoalAnim = LookupActivity( ACT_T_TAP + MyLevel( ) );
				}
				else
				{
					// idle
					m_iGoalAnim = LookupActivity( ACT_T_IDLE + MyLevel( ) );
				}
			}
			if (m_flSoundYaw < 0)
				m_flSoundYaw += RANDOM_FLOAT( 2, 8 );
			else
				m_flSoundYaw -= RANDOM_FLOAT( 2, 8 );
		}

		pev->sequence = FindTransition( pev->sequence, m_iGoalAnim, &m_iDir );

		if (m_iDir > 0)
		{
			pev->frame = 0;
		}
		else
		{
			m_iDir = -1; // just to safe
			pev->frame = 255;
		}
		ResetSequenceInfo( );

		m_flFramerateAdj = RANDOM_FLOAT( -0.2, 0.2 );
		pev->framerate = m_iDir * 1.0 + m_flFramerateAdj;

		switch( pev->sequence)
		{
		case TENTACLE_ANIM_Floor_Tap:
		case TENTACLE_ANIM_Lev1_Tap:
		case TENTACLE_ANIM_Lev2_Tap:
		case TENTACLE_ANIM_Lev3_Tap:
			{
				Vector vecSrc;
				UTIL_MakeVectors( pev->angles );

				TraceResult tr1, tr2;

				vecSrc = pev->origin + Vector( 0, 0, MyHeight() - 4);
				UTIL_TraceLine( vecSrc, vecSrc + gpGlobals->v_forward * 512, ignore_monsters, ENT( pev ), &tr1 );

				vecSrc = pev->origin + Vector( 0, 0, MyHeight() + 8);
				UTIL_TraceLine( vecSrc, vecSrc + gpGlobals->v_forward * 512, ignore_monsters, ENT( pev ), &tr2 );

				// ALERT( at_console, "%f %f\n", tr1.flFraction * 512, tr2.flFraction * 512 );

				m_flTapRadius = SetBlending( 0, RANDOM_FLOAT( tr1.flFraction * 512, tr2.flFraction * 512 ) );
			}
			break;
		default:
			m_flTapRadius = 336; // 400 - 64
			break;
		}
		pev->view_ofs.z = MyHeight( );
		// ALERT( at_console, "seq %d\n", pev->sequence );
	}

	if (m_flPrevSoundTime + 2.0 > gpGlobals->time)
	{
		// 1.5 normal speed if hears sounds
		pev->framerate = m_iDir * 1.5 + m_flFramerateAdj;
	}
	else if (m_flPrevSoundTime + 5.0 > gpGlobals->time)
	{
		// slowdown to normal
		pev->framerate = m_iDir + m_iDir * (5 - (gpGlobals->time - m_flPrevSoundTime)) / 2 + m_flFramerateAdj;
	}
}