////////////////////////////////////////////////////////////////////////////////
//
// 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
//
// fire.cpp
// Project: Postal
//
// This module implements the CFire weapon class which is a burning flame
//	for several different effects and weapons.
// 
//
// History:
//		01/17/97 BRH	Started this weapon object.
//
//		01/23/97 BRH	Updated the time to GetGameTime rather than using
//							real time..
//
//		02/04/97	JMI	Changed LoadDib() call to Load() (which now supports
//							loading of DIBs).
//
//		02/06/97 BRH	Added RAnimSprite animation of the explosion for now.  
//							We are going to do an Alpha effect on the explosion, so
//							there are two animations, one of the image and one of
//							the Alpha information stored as a BMP8 animation.  When
//							the Alpha effect is ready, we will pass a frame from
//							each animation to a function to draw it.
//
//		02/06/97 BRH	Fixed problem with timer.  Since all Explosion objects
//							are using the same resource managed animation, they cannot
//							use the animation timer, they have to do the timing 
//							themselves.
//
//		02/07/97 BRH	Changed the sprite from CSprite2 to CSpriteAlpha2 for
//							the Alpha Blit effect.
//
//		02/09/97 BRH	Started the Fire from Explode file since they are
//							similar.
//
//		02/10/97	JMI	rspReleaseResource() now takes a ptr to a ptr.
//
//		02/11/97 BRH	Changed the fire to start on a random frame number
//							so if you have many fires, they don't pulsate or all
//							burn in sync with each other.
//
//		02/14/97 BRH	Changed from using the RAnimSprite to channel data.
//
//		02/17/97 BRH	Now uses the resource manager to get the assets and starts
//							at a random time interval so the fire will be random again.
//
//		02/17/97 BRH	Changed the lifetime to be time based rather than frame
//							based which was causing the fire to live on forever 
//							since being switched from RAnimSprite to RChannel1.
//
//		02/18/97 BRH	Now the fire changes to different Alpha channels as it
//							burns out during its time to live.  
//
//		02/19/97 BRH	Checks for collisions and sends messages.
//
//		02/19/97 BRH	Added the ability to run both small and large fire
//							animations.  Change the duration on the alpha layers
//							so that the initial alpha channel gets played for 80%
//							of the burning time.  Also added bThick parameter to startup
//							which will start using the 0th Alpha channel which is
//							more opaque.  If you want more Alpha, set to false which
//							will start on the next Alpha level down.
//
//		02/23/97 BRH	Added static Preload() funciton which will be called
//							before play begins to cache a resource for this object.
//
//		02/24/97	JMI	No longer sets the m_type member of the m_sprite b/c it
//							is set by m_sprite's constructor.
//
//		02/24/97 BRH	Set the default state in ProcessMessages
//
//		02/24/97 BRH	Added a timer for checkin collisions so it doesn't have
//							to check each time, but it was checking only when changing
//							alpha levels which was too long.
//
//		03/05/97	JMI	Render()'s mapping from 3D to 2D had a typo (was adding m_dY
//							instead of subtracting).  Now uses Map3Dto2D().
//
//		03/13/97	JMI	Load now takes a version number.
//
//		04/10/97 BRH	Updated this to work with the new multi layer attribute
//							maps.
//
//		04/14/97 BRH	Added CSmash::Item to the collide bits so that the fire
//							will send messages to barrels and other items.
//
//		04/21/97 BRH	Added Smoke animation to the fire and the ability of the
//							fire to change to smoke.
//
//		02/22/97 BRH	Adjusted the timer for the smoke effect to eliminate some
//							of the final frames so that the smoke wouldn't pulsate
//							like it did.				
//
//		04/23/97	JMI	Changed this item's m_smash bits from CSmash::Item to
//							CSmash::Fire.
//							Now affects Characters, Miscs, Mines, and Barrels.
//
//		04/24/97 BRH	Added static wind direction variable that will get
//							adjusted slightly by each new creation of smoke which
//							calls WindDirectionUpdate() to randomly vary the wind
//							direction.
//
//		04/25/97 BRH	Fixed problem with smoke that was created as smoke, 
//							setting people on fire.  Also fixed wall detection
//							and added an individual direction variable to each 
//							instance of smoke that initially copies the wind
//							direction and uses it until it hits a wall, then it
//							rotates in one direction or the other until it is
//							free to move again.
//
//		05/09/97	JMI	Update() now moves the smashatorium object when the CFire
//							is not Smoke.
//
//		05/29/97	JMI	Removed ASSERT on m_pRealm->m_pAttribMap which no longer
//							exists.
//
//		06/11/97 BRH	Pass along the m_u16ShooterID value in the Burn message.
//
//		06/15/97 BRH	Fixed Smoke going past animation by 1 frame.
//
//		06/16/97 BRH	Fixed smoke init of static wind direction.  Now it 
//							inits the wind direction on class load so that it doesn't
//							cause problems for the demo mode.
//
//		06/17/97 MJR	Same as previous one for wind velocity.
//
//					MJR	Moved resetting of statics to Preload(), since in most
//							cases, fire or smoke are not Load()'ed.
//
//		06/18/97 BRH	Changed over to using GetRandom()
//
//		06/26/97 BRH	Added CSmash::AlmostDead to the include bits for fire so
//							that writhing guys can be killed by fire.
//
//		07/01/97 BRH	Added small smoke animation.
//
//		07/04/97 BRH	Added an auto alpha blend on the small smoke for the
//							rocket trails so they can blend into alpha based on
//							their time to live.  May need to disable the
//							alpha channel for it to work correctly.
//
//		07/08/97	JMI	Fixed Render() to distribute the homogeneous alpha level
//							better.  Still needs tuning.
//
//		07/09/97	JMI	Now uses m_pRealm->Make2dResPath() to get the fullpath
//							for 2D image components.
//
//		07/09/97	JMI	Changed Preload() to take a pointer to the calling realm
//							as a parameter.
//
//		07/10/97	JMI	Now uses alpha mask and level for animation.
//
//		07/13/97 BRH	Changed the animations to use only 1 alpha mask and change
//							the alpha level based on time.
//
//		07/20/97	JMI	Added some ASSERTs.
//
//		07/23/97 BRH	Changed small fires to create small smokes rather than 
//							large which slows down the game quite a bit.
//
//		07/27/97	JMI	Changed to use Z position (i.e., X/Z plane) instead of
//							Y2 position (i.e., viewing plane) position for draw 
//							priority.
//
//		08/11/97 BRH	If alpha blending is turned off, as a performance option,
//							then don't even blit the smoke since without the alpha
//							effect, you can't see through it at all.
//
//		08/20/97	JMI	Now does a range check on m_sCurrentAlphaLevel after
//							decrementing.
//
//		09/02/97	JMI	Added m_u16FireStarterID.  This is used for a special case
//							when the starter of the fire is not the thing using the
//							fire as a weapon (e.g., when a guy catches fire he can
//							use the fire on other people by running into them causing
//							them to catch on fire; however, if his own fire kills him
//							it is to the creator of the fire's credit that he dies).
//
////////////////////////////////////////////////////////////////////////////////
#define FIRE_CPP

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

#include "fire.h"
#include "dude.h"
#include "game.h"
#include "reality.h"


#include "pvr.h"

extern PVR::cRenderMgr* g_pvrRenderer;

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

#define AA_FILE				"fire.aan"
#define LARGE_FILE			"fire.aan"
#define SMALL_FILE			"smallfire.aan"
#define SMOKE_FILE			"smoke.aan"
#define SMALL_SMOKE_FILE	"tinysmoke.aan"

#define INIT_WIND_DIR		30
#define INIT_WIND_VEL		30

#define MAX_ALPHA				200	// Used for smoke trails

#define THICK_ALPHA			255	// Start alpha level for thick fire
#define THIN_ALPHA			200	// Start alpha level for thin fire
#define DIEDOWN_ALPHA		100	// Point at which it looks like its dying down
#define SMOLDER_ALPHA		30		// Point at which it is too weak to burn anyone

#define BRIGHT_PERCENT		0.80	// Amount of the time it should be more opaque

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

// Let this auto-init to 0
int16_t CFire::ms_sFileCount;
int16_t CFire::ms_sLargeRadius = 20;
int16_t CFire::ms_sSmallRadius = 8;
int16_t CFire::ms_sWindDirection = INIT_WIND_DIR;		// Start wind in this direction
int32_t  CFire::ms_lCollisionTime = 250;					// Check for collisions this often
int32_t  CFire::ms_lSmokeTime = 10000;						// Time to let smoke run
double CFire::ms_dWindVelocity = INIT_WIND_VEL;		// Pixels per second drift due to wind


////////////////////////////////////////////////////////////////////////////////
// Load object (should call base class version!)
////////////////////////////////////////////////////////////////////////////////
int16_t CFire::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 = CThing::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;

			// Init the static wind direction and velocity when the class loads.
			ms_sWindDirection = INIT_WIND_DIR;
			ms_dWindVelocity = INIT_WIND_VEL;

			// Load static data.
			switch (ulFileVersion)
			{
				default:
				case 1:
					break;
			}
		}

		// Load instance data.
		switch (ulFileVersion)
		{
			default:
			case 1:
				pFile->Read(&m_eFireAnim);
				break;
		}

		// Make sure there were no file errors or format errors . . .
		if (!pFile->Error() && sResult == 0)
		{
			// Get resources
			sResult = GetResources();
		}
		else
		{
			sResult = -1;
			TRACE("CFire::Load(): Error reading from file!\n");
		}
	}
	else
	{
		TRACE("CFire::Load():  CThing::Load() failed.\n");
	}

	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Startup object
////////////////////////////////////////////////////////////////////////////////
int16_t CFire::Startup(void)								// Returns 0 if successfull, non-zero otherwise
{
	return Init();
}

////////////////////////////////////////////////////////////////////////////////
// Shutdown object
////////////////////////////////////////////////////////////////////////////////
int16_t CFire::Shutdown(void)							// Returns 0 if successfull, non-zero otherwise
{
	return 0;
}

////////////////////////////////////////////////////////////////////////////////
// Suspend object
////////////////////////////////////////////////////////////////////////////////
void CFire::Suspend(void)
{
	m_sSuspend++;
}

////////////////////////////////////////////////////////////////////////////////
// Resume object
////////////////////////////////////////////////////////////////////////////////
void CFire::Resume(void)
{
	m_sSuspend--;

	// If we're actually going to start updating again, we need to reset
	// the time so as to ignore any time that passed while we were suspended.
	// This method is far from precise, but I'm hoping it's good enough.
	if (m_sSuspend == 0)
		m_lPrevTime = m_pRealm->m_time.GetGameTime();
}


////////////////////////////////////////////////////////////////////////////////
// Update object
////////////////////////////////////////////////////////////////////////////////
void CFire::Update(void)
{
	int32_t lThisTime;
	double dSeconds;
	double dDistance;
	double dNewX;
	double dNewZ;

	if (!m_sSuspend)
	{
		// See if we killed ourselves
		if (ProcessMessages() == State_Deleted)
			return;

		if (m_lTimer < m_lBurnUntil)
		{
			lThisTime = m_pRealm->m_time.GetGameTime();
			m_lTimer += lThisTime - m_lPrevTime;
			// See if its time to change to the next alpha channel
			if (m_lTimer > m_lCurrentAlphaTimeout)
			{
				m_sCurrentAlphaLevel--;
				// Range check.
				if (m_sCurrentAlphaLevel < 0)
					m_sCurrentAlphaLevel	= 0;
				else if (m_sCurrentAlphaLevel > 255)
					m_sCurrentAlphaLevel	= 255;

				if (m_lTimer < m_lAlphaBreakPoint)
					m_lCurrentAlphaTimeout += m_lBrightAlphaInterval;
				else
					m_lCurrentAlphaTimeout += m_lDimAlphaInterval;
			}

			if (lThisTime > m_lCollisionTimer)
			{
				// If the fire is not smoldering out, then it has the ability
				// to set other things on fire and should check collisions
				// to see which things it should tell to burn.
				if (m_bSendMessages && m_sCurrentAlphaLevel > SMOLDER_ALPHA && m_eFireAnim != Smoke && m_eFireAnim != SmallSmoke)
				{
					CSmash* pSmashed = NULL;
					GameMessage msg;
					msg.msg_Burn.eType = typeBurn;
					msg.msg_Burn.sPriority = 0;
					msg.msg_Burn.sDamage = 10;
					msg.msg_Burn.u16ShooterID = m_u16ShooterID;
					m_pRealm->m_smashatorium.QuickCheckReset(&m_smash, m_u32CollideIncludeBits,
																		  m_u32CollideDontcareBits, 
																		  m_u32CollideExcludeBits);
					while (m_pRealm->m_smashatorium.QuickCheckNext(&pSmashed))
						{
						// Default to the standard case where credit is given to the
						// shooter.
						msg.msg_Burn.u16ShooterID	= m_u16ShooterID;
					
						// If the fire starter ID is set . . .
						if (m_u16FireStarterID != CIdBank::IdNil)
							{
							// If this is the shooter . . .
							if (pSmashed->m_pThing->GetInstanceID() == m_u16ShooterID)
								{
								// The shooter is damaged by his own fire with credit
								// given to the fire starter.
								msg.msg_Burn.u16ShooterID	= m_u16FireStarterID;
								}
							}

						// Burn.
						SendThingMessage(&msg, pSmashed->m_pThing);				
						}
				}
				// Reset collision timer for next time
				m_lCollisionTimer = lThisTime + ms_lCollisionTime;
			}

			// If this is smoke, make it drift in the wind direction
			if (m_eFireAnim == Smoke || m_eFireAnim == SmallSmoke)
			{
				// Update position using wind direction and velocity
				dSeconds = rspFastDiv(((double) lThisTime - (double) m_lPrevTime), 1000.0f);
				// Apply internal velocity.
				dDistance	= ms_dWindVelocity * dSeconds;

				float cosp;
				float sinp;
				rspSinCos(m_sRot, &sinp, &cosp);

				dNewX	= m_dX + cosp * dDistance;
				dNewZ	= m_dZ - sinp * dDistance;

				// Check attribute map for walls, and if you hit a wall, 
				// set the timer so you will die off next time around.
				int16_t sHeight = m_pRealm->GetHeight((int16_t) dNewX, (int16_t) dNewZ);
				// If it hits a wall taller than itself, then it will rotate in the
				// predetermined direction until it is free to move.
				if ((int16_t) m_dY < sHeight)
				{
					if (m_bTurnRight)
						m_sRot = rspMod360(m_sRot - 20);
					else
						m_sRot = rspMod360(m_sRot + 20);
				}
				else
				// else it is ok, so update its new position
				{
					m_dX = dNewX;
					m_dZ = dNewZ;
				}
			}
			else
			{
				// Update our smashatorium location.
				m_smash.m_sphere.sphere.X = m_dX;
				m_smash.m_sphere.sphere.Y = m_dY;
				m_smash.m_sphere.sphere.Z = m_dZ;
				// Update the smash.
				m_pRealm->m_smashatorium.Update(&m_smash);
			}

			m_lPrevTime = lThisTime;
		}
		else
		{
			// If its done smoking, then delete it
			if (m_eFireAnim == Smoke || m_eFireAnim == SmallSmoke)
			{
				delete this;
			}
			// Else change the fire to smoke
			else
			{
				if (Smokeout() != SUCCESS)
					delete this;
			}
		}
	}
}


////////////////////////////////////////////////////////////////////////////////
// Render object
////////////////////////////////////////////////////////////////////////////////
void CFire::Render(void)
{
	
	CAlphaAnim* pAnim;

	if (m_eFireAnim == Smoke || m_eFireAnim == SmallSmoke)	
	{
		pAnim = (CAlphaAnim*) m_pAnimChannel->GetAtTime(m_lTimer); 
		// For a performance gain, don't blit the smoke at all if alpha
		// blending is turned off - its impossible to see through when
		// alpha blending is off anyway.
		if (g_GameSettings.m_sAlphaBlend)
			m_sprite.m_sInFlags = 0;
		else
			m_sprite.m_sInFlags = CSprite::InHidden;
	}
	else
	{
		pAnim = (CAlphaAnim*) m_pAnimChannel->GetAtTime(m_lTimer % m_pAnimChannel->TotalTime());
	}	

	if (pAnim)
	{
		// Map from 3d to 2d coords
		Map3Dto2D(m_dX, m_dY, m_dZ, &(m_sprite.m_sX2), &(m_sprite.m_sY2) );
		// Offset by animations 2D offsets.
		m_sprite.m_sX2	+= pAnim->m_sX;
		m_sprite.m_sY2	+= pAnim->m_sY;

		// Priority is based on our Z position.
		m_sprite.m_sPriority = m_dZ;

		// Layer should be based on info we get from attribute map.
		m_sprite.m_sLayer = CRealm::GetLayerViaAttrib(m_pRealm->GetLayer((int16_t) m_dX, (int16_t) m_dZ));

		// Copy the color info and the alpha channel to the Alpha Sprite
	
		//todo magic numbers
		switch (m_pAnimChannel->id)
		{
			case 1:
				m_sprite.texture = m_pSmokeAan;
			break;
			case 4:
				m_sprite.texture = m_pFireAan;
			break;		
			case 8:
				m_sprite.texture = m_pTinySmokeAan;
			break;
				case 9:
				m_sprite.texture = m_pFireAan;
			break;
			default:				
			break;
		}

		m_sprite.tileId = pAnim->tileId;
		m_sprite.startU = pAnim->startU;
		m_sprite.startV = pAnim->startV;
		m_sprite.endU = pAnim->endU;
		m_sprite.endV = pAnim->endV;
		m_sprite.imageWidth = pAnim->width;
		m_sprite.imageHeight = pAnim->height;


	
		// If its the tiny smoke (for trails) 
		// Do the alpha based on the time to live.
		if (m_eFireAnim == SmallSmoke)
		{
			// Set the alpha level so it gets more translucent over time.
			m_sprite.m_sAlphaLevel = MAX_ALPHA - int16_t(round(rspFastDiv(float(MAX_ALPHA * (m_lTimer - m_lStartTime)), m_lTimeToLive)));
			
			// Do a range check.
			if (m_sprite.m_sAlphaLevel < 0)
				m_sprite.m_sAlphaLevel = 0;
			else if (m_sprite.m_sAlphaLevel > MAX_ALPHA)
				m_sprite.m_sAlphaLevel = MAX_ALPHA;
		}
		else
		{
			m_sprite.m_sAlphaLevel = m_sCurrentAlphaLevel;
		}

		// todo: clean up
		if (m_sprite.m_sAlphaLevel == 255)
		{
			m_sprite.m_sAlphaLevel = 254;
		}
	

		ASSERT(m_sprite.m_sAlphaLevel <= 255);
		ASSERT(m_sprite.m_sAlphaLevel >= 0);

		// Update sprite in scene
		m_pRealm->m_scene.UpdateSprite(&m_sprite);
		
	}
}

////////////////////////////////////////////////////////////////////////////////
// Setup
////////////////////////////////////////////////////////////////////////////////

int16_t CFire::Setup(									// 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
	int32_t lTimeToLive,										// In:  Number of milliseconds to burn, default 1sec
	bool  bThick,											// In:  Use thick fire (more opaque) default = true
	FireAnim eAnimType)									// In:  Animation type to use default = LargeFire
{
	int16_t sResult = 0;
	
	// Use specified position
	m_dX = (double)sX;
	m_dY = (double)sY;
	m_dZ = (double)sZ;
	m_lPrevTime = m_pRealm->m_time.GetGameTime();
	m_lCollisionTimer = m_lPrevTime + ms_lCollisionTime;

	m_eFireAnim = eAnimType;
	m_lTimeToLive = lTimeToLive;
	if (bThick)
		m_sCurrentAlphaLevel = THICK_ALPHA;
	else
		m_sCurrentAlphaLevel = THIN_ALPHA;
	
	// Load resources
	sResult = GetResources();

	if (sResult == SUCCESS)
		sResult = Init();

	m_sRot = ms_sWindDirection;
	m_bTurnRight = (GetRandom() & 0x01);

	return sResult;

}

////////////////////////////////////////////////////////////////////////////////
// Init
////////////////////////////////////////////////////////////////////////////////

int16_t CFire::Init(void)
{
	int16_t sResult = SUCCESS;
	CAlphaAnim* pAnim = NULL;

	//m_pSmallFireAan = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_SMALLFIRE_AAN);
	m_pTinySmokeAan = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_SMOKE_AAN);
	m_pFireAan = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_FIRE_AAN);
	m_pSmokeAan = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_SMOKE_AAN);
				
	if (m_pAnimChannel != NULL)
	{
		m_lTimer = GetRandom() % m_pAnimChannel->TotalTime();
		m_lStartTime = m_lTimer;
		m_lBurnUntil = m_lTimer + m_lTimeToLive;
		m_lAlphaBreakPoint = m_lTimer + (m_lTimeToLive * BRIGHT_PERCENT);
		pAnim = (CAlphaAnim*) m_pAnimChannel->GetAtTime(0);
		ASSERT(pAnim != NULL);
		m_lBrightAlphaInterval = (m_lTimeToLive * BRIGHT_PERCENT) / MAX(1, m_sCurrentAlphaLevel - DIEDOWN_ALPHA);
		m_lDimAlphaInterval = (m_lTimeToLive * (100.0 - BRIGHT_PERCENT)) / MAX(1, DIEDOWN_ALPHA);
		m_lCurrentAlphaTimeout = m_lTimer + m_lBrightAlphaInterval;
		m_sTotalAlphaChannels = 1;
	}

	switch (m_eFireAnim)
	{
		case LargeFire:
			// Update sphere
			m_smash.m_sphere.sphere.X = m_dX;
			m_smash.m_sphere.sphere.Y = m_dY;
			m_smash.m_sphere.sphere.Z = m_dZ;
			m_smash.m_sphere.sphere.lRadius = ms_sLargeRadius;
			m_smash.m_bits = CSmash::Fire;
			m_smash.m_pThing = this;
			// Update the smash
			m_pRealm->m_smashatorium.Update(&m_smash);
			break;

		case SmallFire:
			// Update sphere
			m_smash.m_sphere.sphere.X = m_dX;
			m_smash.m_sphere.sphere.Y = m_dY;
			m_smash.m_sphere.sphere.Z = m_dZ;
			m_smash.m_sphere.sphere.lRadius = ms_sSmallRadius;
			m_smash.m_bits = CSmash::Fire;
			m_smash.m_pThing = this;
			// Update the smash
			m_pRealm->m_smashatorium.Update(&m_smash);
			break;

		case Smoke:
			m_smash.m_pThing = NULL;			
			m_bSendMessages = false;					
			break;

		case SmallSmoke:
			m_smash.m_pThing = NULL;			
			m_bSendMessages = false;
			m_lStartTime = m_lTimer = GetRandom() % m_pAnimChannel->TotalTime() / 3;
			m_lBurnUntil = m_lTimer + m_lTimeToLive;
			break;			
	}

	// Set the collision bits
	m_u32CollideIncludeBits = CSmash::Character | CSmash::Barrel | CSmash::Mine | CSmash::Misc | CSmash::AlmostDead;
	m_u32CollideDontcareBits = CSmash::Good | CSmash::Bad;
	m_u32CollideExcludeBits = 0;

	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Smokeout - Change from fire to smoke
////////////////////////////////////////////////////////////////////////////////

int16_t CFire::Smokeout(void)
{
	int16_t sResult = SUCCESS;

	// Modify the wind direction slightly
	WindDirectionUpdate();

	// Remove smash from the smashatorium if it was being used
	if (m_smash.m_pThing)
		m_pRealm->m_smashatorium.Remove(&m_smash);

	m_bSendMessages = false;

	// Release the fire animation and get the smoke animation
	FreeResources();
	if (m_eFireAnim == SmallFire)
		m_eFireAnim = SmallSmoke;
	else
		m_eFireAnim = Smoke;
	sResult = GetResources();

	// Reset timers
	CAlphaAnim* pAnim = NULL;

	if (m_pAnimChannel != NULL)
	{
		// Reset alpha level
		m_sCurrentAlphaLevel = THICK_ALPHA;
		pAnim = (CAlphaAnim*) m_pAnimChannel->GetAtTime(0);
		ASSERT(pAnim != NULL);
//		m_lTimeToLive = m_pAnimChannel->TotalTime();
		// use same time to live as the original
		m_lStartTime = m_lTimer = 0;
		m_lBurnUntil = m_lTimer + m_lTimeToLive;
		m_lAlphaBreakPoint = m_lTimer + (m_lTimeToLive * BRIGHT_PERCENT);
		m_lBrightAlphaInterval = (m_lTimeToLive * BRIGHT_PERCENT) / MAX(1, m_sCurrentAlphaLevel - DIEDOWN_ALPHA);
		m_lDimAlphaInterval = (m_lTimeToLive * (1.0 - BRIGHT_PERCENT)) / MAX(1, DIEDOWN_ALPHA);
		m_lCurrentAlphaTimeout = m_lTimer + m_lBrightAlphaInterval;
		m_sTotalAlphaChannels = 1;
	}

	return sResult;
}

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

	switch (m_eFireAnim)
	{
		case LargeFire:
			sResult = rspGetResource(&g_resmgrGame, m_pRealm->Make2dResPath(LARGE_FILE), &m_pAnimChannel, RFile::LittleEndian);
			
			// todo: clean up			
			m_pAnimChannel->id = 4;			
			for (int i = 0; i < m_pAnimChannel->NumItems(); i++ )
			{				
				CAlphaAnim* tmp = (CAlphaAnim*) m_pAnimChannel->GetItem(i);
									
				tmp->tileId = 0;
				
				switch (i)
				{

					// Frame 1
					case 0:					
					tmp->startU = 0.0f; 
					tmp->startV = 0.0f;
					tmp->endU = 0.273f;
					tmp->endV = 0.312f;
					tmp->tileId = 0;
					tmp->width = 70;
					tmp->height = 80;
					break;

					// Frame 2
					case 1:
					tmp->startU = 0.273f;
					tmp->startV = 0.0f;
					tmp->endU = 0.542f;
					tmp->endV = 0.265f;
					tmp->tileId = 0;
					tmp->width = 70;
					tmp->height = 68;
					break;

					// Frame 3
					case 2:
					tmp->startU = 0.542f;
					tmp->startV = 0.0f;
					tmp->endU = 0.824f;
					tmp->endV = 0.261f;
					tmp->tileId = 0;
					tmp->width = 72;
					tmp->height = 67;
					break;

					// Frame 4
					case 3:
					tmp->startU = 0.0f;
					tmp->startV = 0.343f;
					tmp->endU = 0.292f;
					tmp->endV = 0.625f;
					tmp->tileId = 0;
					tmp->width = 75;
					tmp->height = 72;
					break;

					// Frame 5
					case 4:
					tmp->startU = 0.292f;
					tmp->startV = 0.312f;
					tmp->endU = 0.593f;
					tmp->endV = 0.625f;
					tmp->tileId = 0;
					tmp->width = 77;
					tmp->height = 80;
					break;

					// Frame 6
					case 5:
					tmp->startU = 0.589f;
					tmp->startV = 0.343f;
					tmp->endU = 0.867f;
					tmp->endV = 0.625f;
					tmp->tileId = 0;
					tmp->width = 71;
					tmp->height = 72;							
					break;
					
					// Frame 7
					case 6:
					tmp->startU = 0.0f;
					tmp->startV = 0.625f;
					tmp->endU = 0.308f;
					tmp->endV = 0.898f;
					tmp->tileId = 0;
					tmp->width = 79;
					tmp->height = 70;
					break;

					// Frame 8
					case 7:
					tmp->startU = 0.304f;
					tmp->startV = 0.625f;

				tmp->endU = 0.582f;
					tmp->endV = 0.906f;
					
					tmp->tileId = 0;
					tmp->width = 77;
					tmp->height = 70;
					break;

					// Fream 9 
					case 8:
					tmp->startU = 0.582f;
					tmp->startV = 0.625f;
					tmp->endU = 0.882f;
					tmp->endV = 0.917f;
					tmp->tileId = 0;
					tmp->width = 77;
					tmp->height = 75;
					break;

					// tile 2
					// Frame 10
					case 9:
					tmp->startU = 0.0f;
					tmp->startV = 0.0f;
					tmp->endU = 0.292f;
					tmp->endV = 0.304f;
					tmp->tileId = 1;
					tmp->width = 75;
					tmp->height = 78;
					break;

					// Frame 11
					case 10:
					tmp->startU = 0.289f;
					tmp->startV = 0.0f;
					tmp->endU = 0.578f;
					tmp->endV = 0.300f;
					tmp->tileId = 1;
					tmp->width = 74;
					tmp->height = 77;
					break;

					// Frame 12
					case 11:
					tmp->startU = 0.574f;
					tmp->startV = 0.0f;
					tmp->endU = 0.867f;
					tmp->endV = 0.308f;
					tmp->tileId = 1;
					tmp->width = 75;
					tmp->height = 79;
					break;

					// Frame 13
					case 12:
					tmp->startU = 0.0;
					tmp->startV = 0.308f;
					tmp->endU = 0.296f;
					tmp->endV = 0.648f;
					tmp->tileId = 1;
					tmp->width = 76;
					tmp->height = 87;
					break;

					// Frame 14
					case 13:
					tmp->startU = 0.292f;
					tmp->startV = 0.304f;
					tmp->endU = 0.582f;
					tmp->endV = 0.605f;
					tmp->tileId = 1;
					tmp->width = 74;
					tmp->height = 77;
					break;




				}												
			}

			break;

		case SmallFire:
			sResult = rspGetResource(&g_resmgrGame, m_pRealm->Make2dResPath(SMALL_FILE), &m_pAnimChannel, RFile::LittleEndian);			
			// todo: clean up		

			m_pAnimChannel->id = 9;			
			for (int i = 0; i < m_pAnimChannel->NumItems(); i++ )
			{				
				CAlphaAnim* tmp = (CAlphaAnim*) m_pAnimChannel->GetItem(i);
									
				tmp->tileId = 1;
				
				switch (i)
				{
					// Frame 1
					//     
					case 0:					
					tmp->startU = 0.58203125 ;
					tmp->startV = 0.30859375;
					tmp->endU = 0.68359375;
					tmp->endV = 0.51953125;					
					tmp->width = 26;
					tmp->height = 54;					
					break;

					// Frame 2
					//      
					case 1:
					tmp->startU = 0.68359375;
					tmp->startV = 0.30859375;
					tmp->endU = 0.77734375;
					tmp->endV = 0.4765625;					
					tmp->width = 24;
					tmp->height = 43;					
					break;

					// Frame 3
					//    24 48
					case 2:
					tmp->startU = 0.7734375; 
					tmp->startV = 0.30859375;
					tmp->endU = 0.8671875;					
					tmp->endV = 0.49609375;					
					tmp->width = 24;
					tmp->height = 48;
					break;

					// Frame 4
					//     25 54
					case 3:
					tmp->startU = 0.8671875;						
					tmp->startV = 0.30859375; 
					tmp->endU =0.96484375;
					tmp->endV = 0.51953125;					
					tmp->width = 25;
					tmp->height = 54;
					break;

					// Frame 5
					//     28 49
					case 4:
					tmp->startU = 0.58203125; 
					tmp->startV = 0.51953125;
					tmp->endU =0.69140625;
					tmp->endV = 0.7109375;					
					tmp->width = 28;
					tmp->height = 49;
					break;

					// Frame 6
					//     23 45
					case 5:
					tmp->startU = 0.69140625; 
					tmp->startV = 0.51953125f;
					tmp->endU = 0.78125;
					tmp->endV = 0.6953125f;					
					tmp->width = 23;
					tmp->height = 45;				
					break;
					

					// Frame 7
					//    23 33
					case 6:
					tmp->startU =  0.78125f;
					tmp->startV = 0.51953125f;
					tmp->endU = 0.87109375f;
					tmp->endV = 0.6484375f;					
					tmp->width = 23;
					tmp->height = 33;
					break;


					// Frame 8
					//    21 37
					case 7:
					tmp->startU = 0.87109375f;
					tmp->startV =  0.51953125f;
					tmp->endU = 0.953125f;
					tmp->endV = 0.6640625;					
					tmp->width = 21;
					tmp->height = 37;
					break;


					// Frame 9
					//    23 37
					case 8:
					tmp->startU = 0.37109375;
					tmp->startV = 0.7109375f;
					tmp->endU = 0.4609375f;
					tmp->endV =0.85546875;					
					tmp->width = 23;
					tmp->height = 37;
					break;

					// Frame 10
					//     24 40
					case 9:
					tmp->startU =0.4609375f;
					tmp->startV = 0.7109375f;
					tmp->endU = 0.5546875f;
					tmp->endV = 0.8671875f;					
					tmp->width = 24;
					tmp->height = 40;
					break;

					// Frame 11
					//      28 44
					case 10:
					tmp->startU = 0.5546875f;
					tmp->startV = 0.7109375f;
					tmp->endU =0.6640625f;
					tmp->endV = 0.8828125f;					
					tmp->width = 28;
					tmp->height = 44;
					break;


					// Frame 12
					//    30 51
					case 11:
					tmp->startU = 0.6640625f;
					tmp->startV = 0.7109375f;
					tmp->endU = 0.78125f;
					tmp->endV = 0.91015625f;					
					tmp->width = 30;
					tmp->height = 51;
					break;
					
					// Frame 13
					//     30 54					
					case 12:
					tmp->startU = 0.78125f;
					tmp->startV = 0.68359375f;
					tmp->endU = 0.8984375f;
					tmp->endV = 0.89453125f;					
					tmp->width = 30;
					tmp->height = 54;
					break;

					// Frame 14
					//    24 47
					case 13:
					tmp->startU = 0.8984375f;
					tmp->startV = 0.7109375f;
					tmp->endU = 0.9921875f;
					tmp->endV = 0.89453125f;					
					tmp->width = 24;
					tmp->height = 47;
					break;
			
				}												
			}

			break;






		case Smoke:
			sResult = rspGetResource(&g_resmgrGame, m_pRealm->Make2dResPath(SMOKE_FILE), &m_pAnimChannel, RFile::LittleEndian);		
			
			// todo: clean up			
			m_pAnimChannel->id = 1;			
			for (int i = 0; i < m_pAnimChannel->NumItems(); i++ )
			{				
				CAlphaAnim* tmp = (CAlphaAnim*) m_pAnimChannel->GetItem(i);

				
					//tmp->endU = 0.625f;
					//tmp->endV = 0.0273f;
					tmp->tileId = 0;								

				switch (i)
				{					
					// Frame 1
					case 0:					
					tmp->startU = 0.f;
					tmp->startV = 0.f;
					tmp->endU = 0.29296875f;
					tmp->endV = 0.25f;
					tmp->tileId = 0;
					tmp->width = 75;
					tmp->height = 64;
					break;

					// Frame 2					 
					case 1:
					tmp->startU = 0.29296875f;
					tmp->startV = 0.f;
					tmp->endU = 0.59765625f;
					tmp->endV = 0.25390625f;
					tmp->tileId = 0;
					tmp->width = 78;
					tmp->height = 65;
					break;

					// Frame 3
					case 2:					     
					tmp->startU = 0.59765625f;
					tmp->startV = 0.00390625f;
					tmp->endU = 0.91796875f;
					tmp->endV = 0.25390625f;
					tmp->tileId = 0;
					tmp->width = 82;
					tmp->height = 64;
					break;

					// Frame 4
					case 3:
					tmp->startU = 0.0f;
					tmp->startV = 0.25390625f;
					tmp->endU = 0.3359375f;
					tmp->endV = 0.5078125f;
					tmp->tileId = 0;
					tmp->width = 86;
					tmp->height = 65;
					break;



					// Frame 5
					case 4:
					tmp->startU = 0.3359375f;
					tmp->startV = 0.25390625f;
					tmp->endU = 0.69140625f;
					tmp->endV = 0.5078125f;
					tmp->tileId = 0;
					tmp->width = 91;
					tmp->height = 65;
					break;

					// Frame 6
					case 5:
					tmp->startU = 0.f;
					tmp->startV = 0.5078125f;
					tmp->endU = 0.37109375f;
					tmp->endV = 0.7578125f;					
					tmp->tileId = 0;
					tmp->width = 95;
					tmp->height = 64;
					break;
						
					// Frame 7
					case 6:
					tmp->startU = 0.37109375f;
					tmp->startV = 0.5078125f;
					tmp->endU = 0.7578125f;
					tmp->endV = 0.7578125f;
					tmp->tileId = 0;
					tmp->width = 99;
					tmp->height = 64;
					break;

// tile 2

					// Frame 8
					case 7:
					tmp->startU = 0.0f;
					tmp->startV = 0.0f;
					tmp->endU = 0.402f;
					tmp->endV = 0.234f;					
					tmp->tileId = 1;
					tmp->width = 103;
					tmp->height = 60;
					break;

					// Frame 9 
					case 8:
					tmp->startU = 0.391f;
					tmp->startV = 0.0f;
					tmp->endU = 0.809f;
					tmp->endV = 0.230f;
					tmp->tileId = 1;
					tmp->width = 107;
					tmp->height = 59;
					break;
				
					// Frame 10
					case 9:
					tmp->startU = 0.0f;
					tmp->startV = 0.238f;
					tmp->endU = 0.434f;
					tmp->endV = 0.461f;
					tmp->tileId = 1;
					tmp->width = 111;
					tmp->height = 57;
					break;


					// Frame 11
					case 10:
					tmp->startU = 0.438f;
					tmp->startV = 0.238f;
					tmp->endU = 0.879f;
					tmp->endV = 0.461f;
					tmp->tileId = 1;
					tmp->width = 113;
					tmp->height = 57;
					break;

					
					// Frame 12
					case 11:
					tmp->startU = 0.0f;
					tmp->startV = 0.465f;
					tmp->endU = 0.453f;
					tmp->endV = 0.691f;
					tmp->tileId = 1;
					tmp->width = 116;
					tmp->height = 58;
					break;

					// Frame 13
					case 12:
					tmp->startU = 0.457f;
					tmp->startV = 0.465f;
					tmp->endU = 0.910f;
					tmp->endV = 0.695f;
					tmp->tileId = 1;
					tmp->width = 116;
					tmp->height = 59;
					break;

					// Frame 14
					case 13:
					tmp->startU = 0.0f;
					tmp->startV = 0.746f;
					tmp->endU = 0.469f;
					tmp->endV = 0.973f;
					tmp->tileId = 1;
					tmp->width = 120;
					tmp->height = 58;
					break;

					// Frame 15
					case 14:
					tmp->startU = 0.480f;
					tmp->startV = 0.746f;
					tmp->endU = 0.953f;
					tmp->endV = 0.977F;
					tmp->tileId = 1;
					tmp->width = 121;
					tmp->height = 59;
					break;
				}												
			}
			
			break;

		case SmallSmoke:
			sResult = rspGetResource(&g_resmgrGame, m_pRealm->Make2dResPath(SMALL_SMOKE_FILE), &m_pAnimChannel, RFile::LittleEndian);


				// todo: clean up			
			m_pAnimChannel->id = 8;			
			for (int i = 0; i < m_pAnimChannel->NumItems(); i++ )
			{				
				CAlphaAnim* tmp = (CAlphaAnim*) m_pAnimChannel->GetItem(i);
				
				//tmp->endU = 0.625f;
				//tmp->endV = 0.0273f;
				tmp->tileId = 0;
				
				switch (i)
				{



					// Frame 1
					case 0:					
					tmp->startU = 0.7578125f;
					tmp->startV = 0.25390625f;
					tmp->endU = 0.86328125f;
					tmp->endV = 0.34375f;
					tmp->tileId = 0;
					tmp->width = 26;
					tmp->height = 23;
					break;
					// Frame 2
					case 1:
					tmp->startU = 0.86328125f;
					tmp->startV = 0.25390625f;
					tmp->endU = 0.97265625f;
					tmp->endV = 0.34375f;
					tmp->tileId = 0;
					tmp->width = 28;
					tmp->height = 23;
					break;
					// Frame 3
					case 2:
					tmp->startU = 0.7578125f;
					tmp->startV = 0.34375f;
					tmp->endU = 0.875f;
					tmp->endV = 0.43359375f;
					tmp->tileId = 0;
					tmp->width = 30;
					tmp->height = 23;
					break;
					// Frame 4
					case 3:					   
					tmp->startU = 0.875f;
					tmp->startV = 0.34375f;
					tmp->endU = 0.9921875f;
					tmp->endV = 0.43359375f;
					tmp->tileId = 0;
					tmp->width = 30;
					tmp->height = 23;
					break;
					// Frame 5
					case 4:					    
					tmp->startU = 0.7578125f;
					tmp->startV = 0.43359375f;
					tmp->endU = 0.88671875f;
					tmp->endV = 0.5234375f;
					tmp->tileId = 0;
					tmp->width = 33;
					tmp->height = 23;
					break;
					// Frame 6					
					case 5:
					tmp->startU = 0.7578125f;
					tmp->startV = 0.5234375f;
					tmp->endU = 0.890625f;
					tmp->endV = 0.61328125f;
					tmp->tileId = 0;
					tmp->width = 34;
					tmp->height = 23;
					break;
					// Frame 7					
					case 6:
					tmp->startU = 0.7578125f;
					tmp->startV = 0.61328125f;
					tmp->endU = 0.8984375f;
					tmp->endV = 0.70703125f;
					tmp->tileId = 0;
					tmp->width = 36;
					tmp->height = 24;
					break;
					// Frame 8
					case 7:					
					tmp->startU = 0.7578125f;
					tmp->startV = 0.70703125f;
					tmp->endU = 0.90234375f;
					tmp->endV = 0.796875f;
					tmp->tileId = 0;
					tmp->width = 37;
					tmp->height = 23;
					break;
					// Frame 9					
					case 8:
					tmp->startU = 0.0f;
					tmp->startV = 0.7578125f;
					tmp->endU = 0.1484375f;
					tmp->endV = 0.84375f;
					tmp->tileId = 0;
					tmp->width = 38;
					tmp->height = 22;
					break;

					// Frame 10
					case 9:
					tmp->startU =  0.1484375f;
					tmp->startV = 0.7578125f;
					tmp->endU = 0.3046875f;
					tmp->endV = 0.84375f;
					tmp->tileId = 0;
					tmp->width = 40;
					tmp->height = 22;
					break;
				
					// Frame 11					
					case 10:
					tmp->startU =  0.3046875f;
					tmp->startV = 0.7578125f;
					tmp->endU = 0.46484375f;
					tmp->endV = 0.84375f;
					tmp->tileId = 0;
					tmp->width = 41;
					tmp->height = 22;
					break;

					// Frame 12					
					case 11:
					tmp->startU =  0.46484375f;
					tmp->startV = 0.7578125f;
					tmp->endU = 0.62890625f;
					tmp->endV = 0.84765625f;
					tmp->tileId = 0;
					tmp->width = 42;
					tmp->height = 23;
					break;

					// Frame 13					
					case 12:
					tmp->startU =  0.0f;
					tmp->startV = 0.84765625f;
					tmp->endU =  0.1640625f;
					tmp->endV = 0.93359375f;					
					tmp->tileId = 0;
					tmp->width = 42;
					tmp->height = 221;
					break;

					// Frame 14
					case 13:
					tmp->startU = 0.1640625f;
					tmp->startV =  0.84765625f;
					tmp->endU = 0.33203125f;
					tmp->endV = 0.9375f;
					tmp->tileId = 0;
					tmp->width = 43;
					tmp->height = 23;
					break;

					// Frame 15
					case 14:
					tmp->startU = 0.33203125f;
					tmp->startV = 0.84765625f;
					tmp->endU = 0.50390625f;
					tmp->endV = 0.9375f;
					tmp->tileId = 0;
					tmp->width = 44;
					tmp->height = 23;
					break;

				}												
			}


			break;
	}

	if (sResult != 0)
		TRACE("CFire::GetResources - Error getting fire animation resource\n");

	return sResult;
}


////////////////////////////////////////////////////////////////////////////////
// Free all resources
////////////////////////////////////////////////////////////////////////////////
int16_t CFire::FreeResources(void)						// Returns 0 if successfull, non-zero otherwise
{
	int16_t sResult = 0;
	bool purge = m_pRealm->m_flags.bPurgeResOnShutdown;
	purge = false;
	if (purge)
	{	
		rspReleaseAndPurgeResource(&g_resmgrGame, &m_pAnimChannel);
	}
	else
	{
		rspReleaseResource(&g_resmgrGame, &m_pAnimChannel);
	}

	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Preload - basically trick the resource manager into caching resources for fire
//				 animations before play begins so that when a fire is set for
//				 the first time, there won't be a delay while it loads.
////////////////////////////////////////////////////////////////////////////////

int16_t CFire::Preload(
	CRealm* prealm)				// In:  Calling realm.
{	
	// Init the static wind direction and velocity when the class loads.
	ms_sWindDirection = INIT_WIND_DIR;
	ms_dWindVelocity = INIT_WIND_VEL;
	ChannelAA* pRes;
	int16_t sResult = rspGetResource(&g_resmgrGame, prealm->Make2dResPath(LARGE_FILE), &pRes, RFile::LittleEndian);
	rspReleaseResource(&g_resmgrGame, &pRes);
	sResult |= rspGetResource(&g_resmgrGame, prealm->Make2dResPath(SMALL_FILE), &pRes, RFile::LittleEndian);
	rspReleaseResource(&g_resmgrGame, &pRes);
	sResult |= rspGetResource(&g_resmgrGame, prealm->Make2dResPath(SMOKE_FILE), &pRes, RFile::LittleEndian);
	rspReleaseResource(&g_resmgrGame, &pRes);
	sResult |= rspGetResource(&g_resmgrGame, prealm->Make2dResPath(SMALL_SMOKE_FILE), &pRes, RFile::LittleEndian);
	rspReleaseResource(&g_resmgrGame, &pRes);
	return sResult;
}

int16_t CFire::Unload(
	CRealm* prealm)				// In:  Calling realm.
{	
	return 0;	
}

////////////////////////////////////////////////////////////////////////////////
// ProcessMessages
////////////////////////////////////////////////////////////////////////////////

CFire::CFireState CFire::ProcessMessages(void)
{
	CFireState eNewState = State_Idle;

	GameMessage msg;

	if (m_MessageQueue.DeQ(&msg) == true)
	{
		switch(msg.msg_Generic.eType)
		{
			case typeObjectDelete:
				m_MessageQueue.Empty();
				delete this;
				return CFire::State_Deleted;
				break;
		}
	}
	// Dump the rest of the messages
	m_MessageQueue.Empty();

	return eNewState;
}

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