////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 RWS Inc, All Rights Reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as published by
// the Free Software Foundation
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// ostrich.cpp
// Project: Postal
//
//	This module implements the ostrich.
//
// History:
//
//		05/09/97 BRH	Started the ostrich from the band member logic.
//
//		05/22/97 BRH	Render skips the Doofus render and goes right to the
//							Character render since its not using the m_dAnimRot.
//
//		05/26/97 BRH	Changed the call to OnDead to the Doofus version
//							rather than the Character version.
//
//		05/29/97	JMI	Changed instance of REALM_ATTR_FLOOR_MASK to 
//							REALM_ATTR_NOT_WALKABLE.
//
//		06/05/97	JMI	Changed m_sHitPoints to m_stockpile.m_sHitPoints to 
//							accommodate new m_stockpile in base class, CThing3d (which
//							used to contain the m_sHitPoints).
//
//		06/18/97 BRH	Changed over to using GetRandom()
//
//		06/25/97	JMI	Now calls PrepareShadow() in Init() which loads and sets up
//							a shadow sprite.
//
//		06/30/97 MJR	Replaced SAFE_GUI_REF with new GuiItem.h-defined macro.
//
//		06/30/97 BRH	Cached the sound samples in static portion of Load
//							function so that the sound effects will be ready
//							whenever this item is used on a level.
//
//		07/01/97	JMI	Replaced GetFloorMapValue() with GetHeightAndNoWalk() call.
//
//		07/09/97	JMI	Now uses m_pRealm->Make2dResPath() to get the fullpath
//							for 2D image components.
//
//		07/12/97 BRH	Changed panic motion to the same as the base class, but
//							it still didn't fix the problem of them going through the
//							wall of the train car on the farm level.  Also increased
//							their hit points, and made them not die in one shot.
//
//		07/18/97	JMI	Got rid of bogus immitation PlaySample functions.
//							Now there is one PlaySample() function.  Also, you now
//							MUST specify a category and you don't have to specify a
//							SoundInstance ptr to specify a volume.
//
//		07/21/97 BRH	Changed the burning state so that the ostrich will burn 
//							longer.
//
//		08/06/97 BRH	Changed smash bits to zero when dying starts so that things
//							won't hit him as he falls slowly, and so you can shoot 
//							past him.  Also allows the shot animation restart when shot
//							to get a little more reaction since his shot animation
//							is so long.
//
//		08/10/97	JMI	Commented out the code that deleted all the sound things in
//							a realm when the ostriches paniced.
//
//		08/12/97 BRH	Set the flag for victim rather than hostile so that
//							the score will display the correct numbers of each
//							type.
//
//		09/03/97	JMI	Replaced Good Smash bit with Civilian.
//
//		09/03/97 BRH	Put in the real ostrich sounds.
//
//		09/04/97 BRH	Added ostrich dying sound.
//
////////////////////////////////////////////////////////////////////////////////
#define OSTRICH_CPP

#include "RSPiX.h"
#include <math.h>

#include "ostrich.h"
#include "SampleMaster.h"
#include "item3d.h"

////////////////////////////////////////////////////////////////////////////////
// Macros/types/etc.
////////////////////////////////////////////////////////////////////////////////

#define BRIGHTNESS_PER_LIGHT_ATTRIBUTE 15
#define NUM_ELEMENTS(a) (sizeof(a) / sizeof(a[0]) )

// Notification message lParm1's.
#define BLOOD_POOL_DONE_NOTIFICATION	1	// Blood pool is done animating.

// Random amount the blood splat can adjust.
#define BLOOD_SPLAT_SWAY		10

// Gets a random between -range / 2 and range / 2.
#define RAND_SWAY(sway)	((GetRandom() % sway) - sway / 2)

#define GUI_ID_OK						1

////////////////////////////////////////////////////////////////////////////////
// Variables/data
////////////////////////////////////////////////////////////////////////////////

double COstrich::ms_dExplosionVelocity = 180.0;
double COstrich::ms_dMaxMarchVel = 30.0;
double COstrich::ms_dMaxRunVel = 80.0;
int32_t COstrich::ms_lStateChangeTime = 10000;
int16_t COstrich::ms_sStartingHitPoints = 100;

// Let this auto-init to 0
int16_t COstrich::ms_sFileCount;

/// Standing Animation Files
// An array of pointers to resource names (one for each channel of the animation)
static char* ms_apszStandResNames[] = 
{
	"3d/ostrich_stand.sop",
	"3d/ostrich_stand.mesh",
	"3d/ostrich_stand.tex",
	"3d/ostrich_stand.hot",
	"3d/ostrich_stand.bounds",
	"3d/ostrich_stand.floor",
	NULL,
	NULL	
};

/// Running Animation Files
// An array of pointers to resource names (one for each channel of the animation)
static char* ms_apszRunResNames[] = 
{
	"3d/ostrich_run.sop",
	"3d/ostrich_run.mesh",
	"3d/ostrich_run.tex",
	"3d/ostrich_run.hot",
	"3d/ostrich_run.bounds",
	"3d/ostrich_run.floor",
	NULL,
	NULL
};

/// Throwing Animation Files 
// An array of pointers to resource names (one for each channel of the animation)
static char* ms_apszWalkResNames[] = 
{
	"3d/ostrich_walk.sop",
	"3d/ostrich_walk.mesh",
	"3d/ostrich_walk.tex",
	"3d/ostrich_walk.hot",
	"3d/ostrich_walk.bounds",
	"3d/ostrich_walk.floor",
	NULL,
	NULL
};

// Shot Animation Files
// An array of pointers to resource names (one for each channel of the animation)
static char* ms_apszShotResNames[] = 
{
	"3d/ostrich_shot.sop",
	"3d/ostrich_shot.mesh",
	"3d/ostrich_shot.tex",
	"3d/ostrich_shot.hot",
	"3d/ostrich_shot.bounds",
	"3d/ostrich_shot.floor",
	NULL,
	NULL
};

/// Blown up Animation Files
// An array of pointers to resource names (one for each channel of the animation)
static char* ms_apszBlownupResNames[] =
{
	"3d/ostrich_blownup.sop",
	"3d/ostrich_blownup.mesh",
	"3d/ostrich_blownup.tex",
	"3d/ostrich_blownup.hot",
	"3d/ostrich_blownup.bounds",
	"3d/ostrich_blownup.floor",
	NULL,
	NULL
};

/// Hide Animation Files
// An array of pointers to resource names (one for each channel of the animation)
static char* ms_apszHideResNames[] =
{
	"3d/ostrich_hide.sop",
	"3d/ostrich_hide.mesh",
	"3d/ostrich_hide.tex",
	"3d/ostrich_hide.hot",
	"3d/ostrich_hide.bounds",
	"3d/ostrich_hide.floor",
	NULL,
	NULL
};

/// Die Animation files
// An array of pointers to resource names (one for each channel of the animation)
static char* ms_apszDieResNames[] = 
{
	"3d/ostrich_die.sop",
	"3d/ostrich_die.mesh",
	"3d/ostrich_die.tex",
	"3d/ostrich_die.hot",
	"3d/ostrich_die.bounds",
	"3d/ostrich_die.floor",
	NULL,
	NULL
};

// These are the points that are checked on the attribute map relative to his origin
static RP3d ms_apt3dAttribCheck[] =
{
	{-6, 0, -6},
	{ 0, 0, -6},
	{ 6, 0, -6},
	{-6, 0,  6},
	{ 0, 0,  6},
	{ 6, 0,  6},
};

////////////////////////////////////////////////////////////////////////////////
// Load object (should call base class version!)
////////////////////////////////////////////////////////////////////////////////
int16_t COstrich::Load(					// Returns 0 if successfull, non-zero otherwise
	RFile* pFile,						// In:  File to load from
	bool bEditMode,					// In:  True for edit mode, false otherwise
	int16_t sFileCount,					// In:  File count (unique per file, never 0)
	uint32_t	ulFileVersion)				// In:  Version of file format to load.
{
	int16_t sResult = 0;

	// Call the base class load to get the instance ID, position, motion etc.
	sResult	= CDoofus::Load(pFile, bEditMode, sFileCount, ulFileVersion);
	if (sResult == 0)
	{
		// Load common data just once per file (not with each object)
		if (ms_sFileCount != sFileCount)
			{
			ms_sFileCount = sFileCount;

			// Load static data
			switch (ulFileVersion)
				{
				default:
				case 1:
					CacheSample(g_smidOstrichShot);
					CacheSample(g_smidOstrichBurning);
					CacheSample(g_smidOstrichBlownup);
					CacheSample(g_smidOstrichDie);
					break;
				}
			}

		// Load Rocket Man specific data
			switch (ulFileVersion)
			{
				default:
				case 4:
				case 3:
				case 2:
				case 1:
					break;
			}
			
		// Make sure there were no file errors or format errors . . .
		if (!pFile->Error() && sResult == 0)
		{
			// Get resources
			sResult = GetResources();
		}
		else
		{
			sResult = -1;
			TRACE("COstrich::Load(): Error reading from file!\n");
		}
	}
	else
	{
	TRACE("CGrenader::Load(): CDoofus::Load() failed.\n");
	}

	return sResult;
}


////////////////////////////////////////////////////////////////////////////////
// Save object (should call base class version!)
////////////////////////////////////////////////////////////////////////////////
int16_t COstrich::Save(										// Returns 0 if successfull, non-zero otherwise
	RFile* pFile,											// In:  File to save to
	int16_t sFileCount)										// In:  File count (unique per file, never 0)
{
	int16_t sResult = SUCCESS;

	// Call the base class save to save the instance ID, position etc.
	CDoofus::Save(pFile, sFileCount);

	// Save common data just once per file (not with each object)
	if (ms_sFileCount != sFileCount)
	{
		ms_sFileCount = sFileCount;

		// Save static data
	}


	if (!pFile->Error())
	{
		sResult = SUCCESS;
	}
	else
	{
		TRACE("COstrich::Save() - Error writing to file\n");
		sResult = -1;
	}

	return sResult;
}


////////////////////////////////////////////////////////////////////////////////
// Init - Call this after the resources are in place
////////////////////////////////////////////////////////////////////////////////

int16_t COstrich::Init(void)
{
	int16_t sResult = 0;

	// Prepare shadow (get resources and setup sprite).
	sResult	= PrepareShadow();

	// Init position, rotation and velocity
	m_dVel = 0.0;
	m_dRot = rspMod360(GetRandom());
	m_lPrevTime = m_pRealm->m_time.GetGameTime();
	m_state = CCharacter::State_Idle;
	m_lTimer = m_pRealm->m_time.GetGameTime() + 500;
	m_sBrightness = 0;	// Default brightness

	m_smash.m_bits		= CSmash::Civilian | CSmash::Character;
	m_smash.m_pThing	= this;

	m_lAnimTime = 0;
	m_panimCur = &m_animStand;
	m_lTimer = m_pRealm->m_time.GetGameTime();
	m_stockpile.m_sHitPoints = ms_sStartingHitPoints;

	m_state = CCharacter::State_Stand;
	m_dAcc = 150;

	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Startup object
////////////////////////////////////////////////////////////////////////////////
int16_t COstrich::Startup(void)								// Returns 0 if successfull, non-zero otherwise
{
	// Register this as a victim rather than a hostile.
	m_bCivilian = true;

	// Set the current height, previous time, and Nav Net by calling the
	// base class startup
	CDoofus::Startup();

	// Init other stuff
	return Init();
}


////////////////////////////////////////////////////////////////////////////////
// Update object
////////////////////////////////////////////////////////////////////////////////
void COstrich::Update(void)
{
	int16_t sHeight = m_sPrevHeight;
	double dNewX;
	double dNewY;
	double dNewZ;
	double dX;
	double dZ;
	int32_t lThisTime;
	int32_t lTimeDifference;


	if (!m_sSuspend)
	{
		// Get new time
		lThisTime = m_pRealm->m_time.GetGameTime(); 
		lTimeDifference = lThisTime - m_lPrevTime;

		// Calculate elapsed time in seconds
		double dSeconds = (double)(lThisTime - m_lPrevTime) / 1000.0;

		// Check for new messages that may change the state
		ProcessMessages();

		// Increment animation time
		m_lAnimTime += lTimeDifference;

		// Check the current state
		switch (m_state)
		{

//-----------------------------------------------------------------------
// Stand or Hide - Either one waits in the current position for the
//						 random timer to expire.
//-----------------------------------------------------------------------

			case State_Stand:
			case State_Hide:
				if (lThisTime > m_lTimer)
					ChangeRandomState();
				break;

//-----------------------------------------------------------------------
// Walk - Walk around a bit
//-----------------------------------------------------------------------

			case State_Walk:
				if (lThisTime > m_lTimer)
				{
					ChangeRandomState();
				}
				else
				{
					dX = m_dX;
					dZ = m_dZ;

					m_dAcc = ms_dAccUser;
					UpdateVelocities(dSeconds, ms_dMaxMarchVel, ms_dMaxMarchVel);
					GetNewPosition(&dNewX, &dNewY, &dNewZ, dSeconds);
					if (MakeValidPosition(&dNewX, &dNewY, &dNewZ, 10) == true)
					{
					// Update Values /////////////////////////////////////////////////////////

						m_dX	= dNewX;
						m_dY	= dNewY;
						m_dZ	= dNewZ;

						UpdateFirePosition();
					}
					else
					{
					// Restore Values ////////////////////////////////////////////////////////

						m_dVel			-= m_dDeltaVel;
					}

					// If he didn't move at all, then turn him so he will
					// avoid the wall
					if (dX == m_dX && dZ == m_dZ)
					{
						// Turn to avoid wall next time
						if (m_sRotDirection == 0)
							m_dRot = rspMod360(m_dRot - 20);
						else
							m_dRot = rspMod360(m_dRot + 20);
					}
				}
		
				break;


//-----------------------------------------------------------------------
// Panic - Pick a random bouy and run to it.  When you are there, pick
//			  a different random bouy and run to it.  
//-----------------------------------------------------------------------

			case State_Panic:
				dX = m_dX;
				dZ = m_dZ;

				DeluxeUpdatePosVel(dSeconds);  
/*

				m_dAcc = ms_dAccUser;
				UpdateVelocities(dSeconds, ms_dMaxRunVel, ms_dMaxRunVel);
				GetNewPosition(&dNewX, &dNewY, &dNewZ, dSeconds);

				// Get height and 'no walk' status at new position.
				bool		bNoWalk;
				sHeight	= m_pRealm->GetHeightAndNoWalk(dNewX, dNewY, &bNoWalk);

				// If too big a height difference or completely not walkable . . .
				if (bNoWalk == true
					|| (sHeight - dNewY > 10) )// && m_bAboveTerrain == false && m_dExtHorzVel == 0.0))
				{
					TRACE("****** - loose ostrish\n");
				// Move like smoke
				}

				if (MakeValidPosition(&dNewX, &dNewY, &dNewZ, 10) == true)
				{
				// Update Values /////////////////////////////////////////////////////////

					m_dX	= dNewX;
					m_dY	= dNewY;
					m_dZ	= dNewZ;

					UpdateFirePosition();
				}
				else
				{
				// Restore Values ////////////////////////////////////////////////////////
	
					m_dVel			-= m_dDeltaVel;
				}
*/
				// If he didn't move at all, then turn him so he will
				// avoid the wall
				if (dX == m_dX && dZ == m_dZ)
				{
					// Turn to avoid wall next time
					if (m_sRotDirection == 0)
						m_dRot = rspMod360(m_dRot - 20);
					else
						m_dRot = rspMod360(m_dRot + 20);
				}


				break;

//-----------------------------------------------------------------------
// Burning - Run around on fire until dead
//-----------------------------------------------------------------------

			case State_Burning:
				if (!CCharacter::WhileBurning())
				{
					if (m_stockpile.m_sHitPoints <= 0)
					{
						m_state = State_Die;
						m_lAnimTime = 0;
						m_panimCur = &m_animDie;
						PlaySample(
							g_smidOstrichDie,
							SampleMaster::Voices);
					}
					else
					{
						m_state = State_Panic;
					}
				}				
				break;

//-----------------------------------------------------------------------
// Blown Up - Do motion into the air until you hit the ground again
//-----------------------------------------------------------------------

			case State_BlownUp:
				if (!CCharacter::WhileBlownUp())
					m_state = State_Dead;
				else
					UpdateFirePosition();
				break;

//-----------------------------------------------------------------------
// Shot - Dies in one shot
//-----------------------------------------------------------------------

			case State_Shot:
				if (!CCharacter::WhileShot())
				{
					if (m_stockpile.m_sHitPoints <= 0)
					{
						m_state = State_Die;
						m_panimCur = &m_animDie;
						m_lAnimTime = 0;
						PlaySample(
							g_smidOstrichDie,
							SampleMaster::Voices);
					}
					else
					{
						m_state = State_Panic;
						m_panimCur = &m_animRun;
						m_lAnimTime = 0;
					}
				}
				break;

//-----------------------------------------------------------------------
// Die - run die animation until done, the you are dead
//-----------------------------------------------------------------------

			case State_Die:
				m_smash.m_bits = 0;
				if (!CCharacter::WhileDying())
					m_state = State_Dead;
				else
					UpdateFirePosition();
				
				break;

//-----------------------------------------------------------------------
// Dead - paste yourself in the background and delete yourself
//-----------------------------------------------------------------------

			case State_Dead:
				OnDead();
				delete this;
				return;

				break;

		}

		m_smash.m_sphere.sphere.X			= m_dX;
		// Fudge center of sphere as half way up the dude.
		// Doesn't work if dude's feet leave the origin.
		m_smash.m_sphere.sphere.Y			= m_dY + m_sprite.m_sRadius;
		m_smash.m_sphere.sphere.Z			= m_dZ;
		m_smash.m_sphere.sphere.lRadius	= m_sprite.m_sRadius;

		// Update the smash.
		m_pRealm->m_smashatorium.Update(&m_smash);

		// Save height for next time
		m_sPrevHeight = sHeight;

		// Save time for next time
		m_lPrevTime = lThisTime;
	}
}


////////////////////////////////////////////////////////////////////////////////
// Render object
////////////////////////////////////////////////////////////////////////////////
void COstrich::Render(void)
{
	// Call base class.
	CCharacter::Render();

}

////////////////////////////////////////////////////////////////////////////////
// Called by editor to init new object at specified position
////////////////////////////////////////////////////////////////////////////////
int16_t COstrich::EditNew(									// Returns 0 if successfull, non-zero otherwise
	int16_t sX,												// In:  New x coord
	int16_t sY,												// In:  New y coord
	int16_t sZ)												// In:  New z coord
{
	int16_t sResult = 0;

	// Call the base class to place the item.
	sResult = CDoofus::EditNew(sX, sY, sZ);

	if (sResult == SUCCESS)
	{
		// Load resources
		sResult = GetResources();
		if (sResult == SUCCESS)
		{
			sResult	= Init();
		}
	}
	
	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// EditModify - Show dialog box for selecting starting bouy
////////////////////////////////////////////////////////////////////////////////

int16_t COstrich::EditModify(void)
{
	return 0;
}

////////////////////////////////////////////////////////////////////////////////
// Get all required resources
////////////////////////////////////////////////////////////////////////////////
int16_t COstrich::GetResources(void)						// Returns 0 if successfull, non-zero otherwise
{
	int16_t sResult = 0;

	sResult = m_animRun.Get(ms_apszRunResNames, RChannel_LoopAtStart | RChannel_LoopAtEnd);
	if (sResult == 0)
	{
		sResult = m_animStand.Get(ms_apszStandResNames, RChannel_LoopAtStart | RChannel_LoopAtEnd);
		if (sResult == 0)
		{
			sResult = m_animWalk.Get(ms_apszWalkResNames, RChannel_LoopAtStart | RChannel_LoopAtEnd);
			if (sResult == 0)
			{
				sResult = m_animShot.Get(ms_apszShotResNames);
				if (sResult == 0)
				{
					sResult = m_animBlownup.Get(ms_apszBlownupResNames);
					if (sResult == 0)
					{
						sResult = m_animHide.Get(ms_apszHideResNames);
						if (sResult == 0)
						{
							sResult = m_animDie.Get(ms_apszDieResNames);
							if (sResult == 0)
							{
								// Add more anim gets here if necessary
							}
							else
							{
								TRACE("COstrich::GetResources - Failed to open 3D die animation\n");
							}
						}
						else
						{
							TRACE("COstrich::GetResources - Failed to open 3D hide animation\n");
						}
					}
					else
					{
						TRACE("COstrich::GetResources - Failed to open 3D blownup animation\n");
					}
				}
				else
				{
					TRACE("COstrich::GetResources - Failed to open 3D shot animation\n");
				}	
			}
			else
			{
				TRACE("COstrich::GetResources - Failed to open 3D walk animation\n");
			}
		}
		else
		{
			TRACE("COstrich::GetResources - Failed to open 3D stand animation\n");
		}
	}
	else
	{
		TRACE("COstrich::GetResources - Failed to open 3D run animation\n");
	}	

	return sResult;
}


////////////////////////////////////////////////////////////////////////////////
// Free all resources
////////////////////////////////////////////////////////////////////////////////
int16_t COstrich::FreeResources(void)						// Returns 0 if successfull, non-zero otherwise
{
	m_animRun.Release();
	m_animStand.Release();
	m_animWalk.Release();
	m_animShot.Release();
	m_animBlownup.Release();
	m_animHide.Release();
	m_animDie.Release();		

	return 0;
}


////////////////////////////////////////////////////////////////////////////////
// ProcessMessages - Similar to the base class version but handles a few more
////////////////////////////////////////////////////////////////////////////////

void COstrich::ProcessMessages(void)
{
	// Check queue of messages.
	GameMessage	msg;
	while (m_MessageQueue.DeQ(&msg) == true)
	{
		ProcessMessage(&msg);

		switch(msg.msg_Generic.eType)
		{
			case typePanic:
				OnPanicMsg(&(msg.msg_Panic));
				break;
				
		}

	}

}

////////////////////////////////////////////////////////////////////////////////
// Message handlers
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Shot Message
////////////////////////////////////////////////////////////////////////////////

void COstrich::OnShotMsg(Shot_Message* pMessage)
{
    UnlockAchievement(ACHIEVEMENT_FIGHT_AN_OSTRICH);

	m_stockpile.m_sHitPoints -= pMessage->sDamage;

	if (m_state != State_BlownUp &&
//	    m_state != State_Shot &&
		 m_state != State_Die &&
		 m_state != State_Dead)
	{
		CCharacter::OnShotMsg(pMessage);

		// Restart the animation even if he is shot, 
		m_lAnimTime = 0;
		m_panimCur = &m_animShot;

		if (m_state != State_Shot)
		{
			PlaySample(
				g_smidOstrichShot,
				SampleMaster::Voices);
			m_state = State_Shot;
			AlertFlock();
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Explosion message
////////////////////////////////////////////////////////////////////////////////

void COstrich::OnExplosionMsg(Explosion_Message* pMessage)
{
	if (m_state != State_BlownUp)
	{
        UnlockAchievement(ACHIEVEMENT_FIGHT_AN_OSTRICH);

		CCharacter::OnExplosionMsg(pMessage);

		// Explosion kills the guy
		m_stockpile.m_sHitPoints = 0;
//		m_dExtVertVel = ms_dExplosionVelocity;
		m_state = State_BlownUp;
		m_lAnimTime = 0;
		m_panimCur = &m_animBlownup;
		PlaySample(
			g_smidOstrichBlownup,
			SampleMaster::Voices);
		AlertFlock();
	}
}

////////////////////////////////////////////////////////////////////////////////
// Burning message
////////////////////////////////////////////////////////////////////////////////

void COstrich::OnBurnMsg(Burn_Message* pMessage)
{
    UnlockAchievement(ACHIEVEMENT_FIGHT_AN_OSTRICH);

	CCharacter::OnBurnMsg(pMessage);
	m_stockpile.m_sHitPoints -= MAX(pMessage->sDamage / 4, 1);

	if (m_state != State_Burning && 
	    m_state != State_BlownUp &&
		 m_state != State_Die &&
		 m_state != State_Dead)
	{
		m_state = State_Burning;
		m_panimCur = &m_animRun;
		m_lAnimTime = 0;
		PlaySample(
			g_smidOstrichBurning,
			SampleMaster::Voices);
		AlertFlock();
	}
}

////////////////////////////////////////////////////////////////////////////////
// Panic message
////////////////////////////////////////////////////////////////////////////////

void COstrich::OnPanicMsg(Panic_Message* pMessage)
{
	if (m_state != State_Die &&
	    m_state != State_Dead &&
		 m_state != State_BlownUp &&
		 m_state != State_Shot &&
		 m_state != State_Burning &&
		 m_state != State_Panic &&
		 m_state != State_Hide)
	{
		m_state = State_Panic;
		m_panimCur = &m_animRun;
		m_lAnimTime = GetRandom() % m_panimCur->m_psops->TotalTime();
	}
}

////////////////////////////////////////////////////////////////////////////////
// AlertFlock
////////////////////////////////////////////////////////////////////////////////

void COstrich::AlertFlock(void)
{
//	CThing::Things::iterator iNext;
	CThing* pThing;
	GameMessage msg;
//	GameMessage msgStopSound;

//	msgStopSound.msg_ObjectDelete.eType = typeObjectDelete;
//	msgStopSound.msg_ObjectDelete.sPriority = 0;

	msg.msg_Panic.eType = typePanic;
	msg.msg_Panic.sPriority = 0;
	msg.msg_Panic.sX = (int16_t) m_dX;
	msg.msg_Panic.sY = (int16_t) m_dY;
	msg.msg_Panic.sZ = (int16_t) m_dZ;

	CListNode<CThing>* pNext = m_pRealm->m_everythingHead.m_pnNext;
	while (pNext->m_powner != NULL)
	{
		pThing = pNext->m_powner;
		if (pThing->GetClassID() == COstrichID && pThing != this)
			SendThingMessage(&msg, pThing);
//		else if (pThing->GetClassID() == CSoundThingID)
//			SendThingMessage(&msgStopSound, pThing);
		pNext = pNext->m_pnNext;
	}	
}


////////////////////////////////////////////////////////////////////////////////
// Implements basic one-time functionality for each time State_Dead is
// entered.
////////////////////////////////////////////////////////////////////////////////
void COstrich::OnDead(void)
	{
	// Call base class.
	CDoofus::OnDead();
	}

////////////////////////////////////////////////////////////////////////////////
// ChangeRandomState - Choose among the 3 normal states, stand, hide or walk
////////////////////////////////////////////////////////////////////////////////

void COstrich::ChangeRandomState(void)
{
	int16_t sMod;
	m_lTimer = m_pRealm->m_time.GetGameTime() + ms_lStateChangeTime + GetRandom() % 5000;
	sMod = m_lTimer % 3;
	m_dRot = rspMod360(m_dRot - 10 + (GetRandom() % 20));
	m_sRotDirection = m_lTimer % 2;

	switch (sMod)
	{
		case 0:
			m_state = State_Hide;
			if (m_panimCur != &m_animHide)
			{
				m_panimCur = &m_animHide;
				m_lAnimTime = 0;
			}
			break;

		case 1:
			m_state = State_Stand;
			if (m_panimCur != &m_animStand)
			{
				m_panimCur = &m_animStand;
				m_lAnimTime = 0;
			}
			break;

		case 2:
			m_state = State_Walk;
			if (m_panimCur != &m_animWalk)
			{
				m_panimCur = &m_animWalk;
				m_lAnimTime = 0;
			}
			break;

		default:
			break;
	}
}

////////////////////////////////////////////////////////////////////////////////
// EOF
////////////////////////////////////////////////////////////////////////////////
