////////////////////////////////////////////////////////////////////////////////
//
// 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
//
#include "RSPiX.h"
#include "smash.h" 
#include "realm.h"

//#define SMASH_DEBUG

#ifdef	SMASH_DEBUG

	#include "debugSmash.H"

#endif

////////////////////////////////////////////////////////////////////////////////
//
// smash.h (grid based edition)
// Project: Postal
//
//	History:
//		05/26/97	JRD	Started.
//
//		06/04/97 JRD	Integrated with Postal for testing using NEW_SMASH project
//							setting so dependent files could still work with old smash
//
//		06/20/97	JRD	Removed backwards smash compatibility
//
//		07/03/97	JRD	Added an incremental tagged search to eliminate redundant
//							parts.  Implemented a system of ray tracking accross the grid.
//
//		07/05/97	JRD	Fixed MANY bugs in the line collision algorithms.  
//							Handled special 
//							cases of near vertical lines and vertical clipping.
//							Still can't deal with fat smash objects.
//
//		07/07/97 JRD	Used a new class to implement fat smash objects without impacting 
//							performance - CFatSmash.
//
//		07/08/97	JMI	Added debug ASSERTs for "bridge's out" condition to help
//					BRH	us catch which CThings don't remove their smash(es).
//							Also, added release mode protection against "bridge's out"
//							condition.
//							Also, added bits for flags and flag bases.
//							Also, moved definition of ~CSmash() into smash.cpp b/c
//							of circular dependency (a Philips chain of command of
//							sorts) between smashatorium and realm.
//							Also, moved CSmash b/c Bill thinks it's better to keep
//							the destructor with the constructor or ease of findage.
//
//		07/10/97	JRD	Finally copleted and debugged ful support for Fat Smash 
//							objects.  The main criteria in determining what size is
//							"fat", is that "fat" objects shouldn't move very often.
//							NOTE that the n2 collision space in the new smash is
//							equivalent to NINE smash tiles, so if the tile size is
//							72, the collision areas will be 216 x 216!  So be careful!
//
//		08/09/97	JMI	CSmashatorium::Update() was checking the realm's 
//							m_flags.bEditPlay flag (which signifies that we're playing
//							a game from the editor) instead of the m_flags.bEditing
//							flag which indicates we're editting.  Fixed.
//
//		08/18/97 BRH	Fixed typo that used m_pSmasher instead of pSmasher
//							that was passed in.  Fixed CollideCyl that was checking
//							X and Y collisions to checking X and Z collisions.
//
//		08/31/97	JMI	Added some ASSERTs to foil SmartHeap's fancy smancy bounds
//							check padding areas and allow us to debug 'read' bounds
//							errors in debug mode.
//
//		09/02/97	JMI	Added some more ASSERTs for debug mode bounds checking.
//
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
//  NOTE:  CURRENT USER LEVEL CHANGE FROM OLD SMASHATORIUM:  You must pass
//  four parameters to allocate a CSmashatorium (there is no default anymore.)
//  These parameters are the world X and Z size, and a grid size larger than 
//  any smash region.
////////////////////////////////////////////////////////////////////////////////
//  The new smashatorium attempts to partition lists of objects by location
//  Currently, each grid location only maintains a single hodge podge list as
//  the old Smash.  This may be enhanced (memory permitting) to split into 
//  multiple lists by type, or into multiple smashatoriums fo different grid
//  sizes so static objects could be contained in a fine grid.
////////////////////////////////////////////////////////////////////////////////
//
//	The current model demands that objects in the smashatorium exist in no more
// than four grid locations simultaneously.  Large smash objects used to detect
// objects-within-a-distance are hooked as a special type of smashee and use
// different collision routines.  Similarly, line collisions need higher level
// logic to march through the grid.  In short, it is a completely new 
// Smashatorium masquerading through the old API.
//
////////////////////////////////////////////////////////////////////////////////
//
//  The current Smashatorium "HIERARCHY OF OBJECTS"  :
//
//	CSmashLink -> A 128-bit struct which is the manipulation block for the grid.
//               It currently points back to it's CSmash parent and it's old grid
//				     location.  For efficiency, we could redundantly store the
//               smash bits here.
//
// CSmash -> One of more of these is held by actual game objects.  It contains
//				 a pointer back to the thing parent, a spherical collision region,
//           the smash bits, and four SmashLinks to track the corners of the
//           objects in the Smashatorium Grid.
//
// CSmashatoriumList -> manages each grid's linked list.  The grid is a 2d array
//								of these nodes.
//
// CSmashatorium -> Hold the grid and world clipping info.  Handles all the user
//						 functions.  Holds the state for the sequential checking state.
// 
////////////////////////////////////////////////////////////////////////////////

CSmash::CSmash()
	{
	Erase();
	// Link up the links to their parent:
	m_link1.m_pParent = m_link2.m_pParent = m_link3.m_pParent = m_link4.m_pParent = this;
	}

CSmash::~CSmash()
	{
	// Make sure we've been removed.
	ASSERT(m_link1.m_pLast == NULL);
	ASSERT(m_sInGrid == FALSE);
	
	// As a back up, if not removed . . .
	if (m_link1.m_pLast != NULL)
		{
		if (m_pThing != NULL)
			{
			// Use the pthing to get to the realm and finally the smashatorium that
			// we are in.
			// Remove this smash.  This is for safety for uncaught release mode
			// bugs.
			m_pThing->m_pRealm->m_smashatorium.Remove(this);
			}
		}

	delete m_pFat; // Safe if NULL
	Erase();
	}

////////////////////////////////////////////////////////////////////////////////
//	CSmashatorium::CollideCyl - check for special collision with sphere case
////////////////////////////////////////////////////////////////////////////////
// This is a VERY special case scenario - currently, if the Smashee is a CDude,
// and IF his sphere collides, we THEN need to check if his "cylinder" collides.
// In this case, his cylinder is fixed at half his sphere radius.
////////////////////////////////////////////////////////////////////////////////
int16_t	CSmashatorium::CollideCyl(CSmash* pSmashee,RSphere* pSphere) // sphere of Smasher
	{
	if (!pSmashee->m_pThing) return SUCCESS;	// not a dude
	if (pSmashee->m_pThing->GetClassID() != CThing::CDudeID) return SUCCESS;	// not a dude
	// it's a dude !  see if the cylinder collides:
	int32_t	lCylR = pSmashee->m_sphere.sphere.lRadius / 3;	// go with half the sphere radius
	// NOTE that if it's a DUDE colliding with a DUDE, only one uses a cylinder:
	if (ABS2(pSmashee->m_sphere.sphere.X - pSphere->X,
		pSmashee->m_sphere.sphere.Z - pSphere->Z) >
		SQR(lCylR + pSphere->lRadius) ) return FAILURE;	// A MISS!

	return SUCCESS;	// a hit!
	}

////////////////////////////////////////////////////////////////////////////////
//	CSmashatorium::CollideCyl - check for special collision with line case
////////////////////////////////////////////////////////////////////////////////
// This is a VERY special case scenario - currently, if the Smashee is a CDude,
// and IF his sphere collides, we THEN need to check if his "cylinder" collides.
// In this case, his cylinder is fixed at half his sphere radius.
////////////////////////////////////////////////////////////////////////////////
int16_t	CSmashatorium::CollideCyl(CSmash* pSmashee,R3DLine* pLine) // sphere of Smasher
	{
	if (!pSmashee->m_pThing) return SUCCESS;	// not a dude
	if (pSmashee->m_pThing->GetClassID() != CThing::CDudeID) return SUCCESS;	// not a dude
	// it's a dude !  see if the cylinder collides:
	int32_t	lCylR = pSmashee->m_sphere.sphere.lRadius / 3;	// go with half the sphere radius
	int32_t	lOldR = pSmashee->m_sphere.sphere.lRadius;
	pSmashee->m_sphere.sphere.lRadius = lCylR;	// shrink it:
	int16_t sCollide = pSmashee->m_sphere.Collide(pLine);
	pSmashee->m_sphere.sphere.lRadius = lOldR;	// shrink it:

	if (sCollide == COLLISION) return SUCCESS;	// a hit!

	return FAILURE;	// a miss!
	}

////////////////////////////////////////////////////////////////////////////////
//  CSmashatorium::Alloc - create a grid of smash lists:
////////////////////////////////////////////////////////////////////////////////
int16_t CSmashatorium::Alloc(int16_t sWorldW,int16_t sWorldH,int16_t sTileW,int16_t sTileH)
		{
		//-------------------------------------------------------------
		ASSERT(!m_psAccessX); // previous grid?
		ASSERT(!m_psAccessY);
		ASSERT(!m_ppslAccessY);
		ASSERT(!m_pGrid);
		
		ASSERT(sWorldW > 0); // bad input?
		ASSERT(sWorldH > 0);
		ASSERT(sTileW > 0);
		ASSERT(sTileH > 0);
		//-------------------------------------------------------------
		m_sWorldW = sWorldW;
		m_sWorldH = sWorldH;

		m_sClipW = m_sWorldW + (sTileW << 1);	// Add a one tile border
		m_sClipH = m_sWorldH + (sTileH << 1);

		m_sTileW = sTileW;	// For debugging & clipping
		m_sTileH = sTileH;	

		m_sGridW = (m_sClipW + sTileW - 1) / sTileW;
		m_sGridH = (m_sClipH + sTileH - 1) / sTileH;

		// For current logic convenience, do not allow partial tiles to exist:
		m_sClipW = int32_t(m_sGridW) * sTileW;
		m_sClipH = int32_t(m_sGridH) * sTileH;

		// Note that we must add 2 tile lengths to each access line:
		m_pGrid = new CSmashatoriumList[int32_t(m_sGridW) * m_sGridH];
		m_psAccessX = (int16_t*) calloc(sizeof(int16_t),m_sClipW);
		m_psAccessY = (int16_t*) calloc(sizeof(int16_t),m_sClipH);
		m_ppslAccessY = (CSmashatoriumList**) calloc(sizeof (CSmashatoriumList*),m_sClipH);

		if (!m_psAccessX || !m_psAccessX || !m_ppslAccessY || !m_pGrid)
			{
			TRACE("CSmashatorium::Ran out of memory!\n");
			Destroy();
			Erase();
			return FAILURE;
			}

		// THE OFFICIAL RANGE HERE is from
		// -m_sTileW to (m_sWorldW + m_sTileW)
		// FULL CLIP is from 0 to (m_sWorldW - 1)
		//
		m_psClipX = m_psAccessX + m_sTileW;	// Offset values
		m_psClipY = m_psAccessY + m_sTileH;	// Offset values
		m_ppslClipY = m_ppslAccessY + m_sTileH;	// Offset values
	
		// Populate the access tables....
		int16_t i,j,p,g;

		for (g=0, p = 0,i=0 ; i < m_sClipW; i += sTileW, g++)
			{
			for (j=0; j < sTileW; j++, p++)
				{
				m_psAccessX[p] = g;
				}
			}

		for (g=0, p = 0,i=0 ; i < m_sClipH; i += sTileH, g++)
			{
			for (j=0; j < sTileH; j++, p++)
				{
				m_psAccessY[p] = g;
				// Remember you are in pointer arithmetic mode!!!!
				m_ppslAccessY[p] = m_pGrid + int32_t(m_sGridW) * g;
				}
			}

		m_sNumInSmash = m_sMaxNumInSmash = 0;

		return SUCCESS;
		}

////////////////////////////////////////////////////////////////////////////////
//
//	Reset - 
//
//	Reset does NOT DEALLOCATE any portion of the Smashatorium.
//	It is just a short cut to reset each of the grid's
//	SmashLists.  But Each Smash must reset it's own Links!
//
////////////////////////////////////////////////////////////////////////////////
void	CSmashatorium::Reset()
	{
	//----------------------------------------------------------------
	// Reset any search in progress: STEAL this code for a real func
	m_pCurrentSmashee = NULL;
	m_pSmasher = NULL;
	m_sCurrentListX = m_sCurrentListY = m_sSearchW = m_sSearchH = 0;
	//----------------------------------------------------------------
	// Go down the list of CSmashatoriumList's:
	int32_t lCur;
	for (lCur = 0; lCur < int32_t(m_sGridW) * m_sGridH; lCur++)
		{
		CSmashatoriumList	*pCur = m_pGrid + lCur;
		pCur->m_sNum = 0;
		pCur->m_slHead.Erase();
		pCur->m_slTail.Erase();
		}

	m_sNumInSmash = 0;
	}

////////////////////////////////////////////////////////////////////////////////
//
//	QuickCheckReset
//
//	Reset QuickCheckNext() using the specified parameters.
//	Begin a multicall collision search based on a smashee
// If the Smashee is in the Smashatorium, it must be small enough to fit.
//
////////////////////////////////////////////////////////////////////////////////
void CSmashatorium::QuickCheckReset(// Returns true if collision detected, false otherwise
	CSmash* pSmasher,						// In:  CSmash to check
	CSmash::Bits include,				// In:  Bits that must be 1 to collide with a given CSmash
	CSmash::Bits dontcare,				// In:  Bits that you don't care about
	CSmash::Bits exclude)				// In:  Bits that must be 0 to collide with a given CSmash
	{
	ASSERT(pSmasher);
	//---------------------------
	m_pSmasher = pSmasher;
	m_include = include;
	m_dontcare = dontcare;
	m_exclude = exclude;
	//--------------------------- preset size and position: ---------
	// (1) cast into a square:
	//---------------------------------------------------------------
	RSphere* pSphere = &(m_pSmasher->m_sphere.sphere);
	int32_t lR = pSphere->lRadius;

	// Find upper left & lower right position:
	int32_t	lX,lY,lX2,lY2;
	lX = pSphere->X - lR;
	lY = pSphere->Z - lR;

	m_lCurrentSearchCode++;			// prepare for a new searching code

	// Now do something different for a smashee that's in the 'torium
	// and one that's not...
	if (pSmasher->m_sInGrid) // we KNOW it's fully clipped and of legal size...
		{
		if ( (lX <= -m_sTileW) || (lY < -m_sTileH) || 
			(lX >= m_sWorldW) || (lY >= m_sWorldH) )
			{
			// We have FULL CLIP OUT!
			m_pSmasher = NULL; // this search has ended!
			
			return; 
			}

		// We know to do 2 x 2:
		m_pCurrentList = m_ppslClipY[lY] + m_psClipX[lX];
		m_sCurrentListX = m_sCurrentListY = 0;
		m_sSearchW = m_sSearchH = 2;
		m_pCurrentSmashee = NULL; // Pending first request

		if (m_lCurrentSearchCode < 0)	// unfortunate wrapping around...
			{
			ASSERT(0);	// Need to "detag" the smashatorium - detag could be a function
			}

		return; // Done!
		}

	// Handle the case of a monstrosity!
	lR += lR; // lR is a diameter now!
	lX2 = lX + lR;
	lY2 = lY + lR;

	//==========================================
	// Do tight clipping:
	//==========================================
	if (lX < 0) lX = 0;
	if (lY < 0) lY = 0;

	if (lX2 >= m_sWorldW) lX2 = m_sWorldW - 1;
	if (lY2 >= m_sWorldH) lY2 = m_sWorldH - 1;
	
	if ( (lX2 <= lX) || (lY2 <= lY) )
		{
		// Fully clipped out!
		m_pSmasher = NULL; // this search has ended!
		
		return; 
		}

	// Set up the search parameters:
	m_pCurrentSmashee = NULL; // Pending first request
	m_pCurrentList = m_ppslClipY[lY] + m_psClipX[lX];
	m_sCurrentListX = 0; // m_psClipX[lX];	// CURRENTLY, these are used merely as iterators
	m_sCurrentListY = 0; //m_psClipY[lY];

	m_sSearchW = 1 + m_psClipX[lX2] - m_psClipX[lX];
	m_sSearchH = 1 + m_psClipY[lY2] - m_psClipY[lY];
	}

////////////////////////////////////////////////////////////////////////////////
//
//
//


//
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
//
//
//


//
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
//
//
//


//
////////////////////////////////////////////////////////////////////////////////

//===========================================================================
// Currently stubs for now...
//===========================================================================

// Reset QuickCheckNext() using the specified paramters.
void CSmashatorium::QuickCheckReset(				// Returns true if collision detected, false otherwise
	CSmash::Bits include,								// In:  Bits, of which, one must be set to collide with a given CSmash
	CSmash::Bits dontcare,								// In:  Bits that you don't care about
	CSmash::Bits exclude)								// In:  Bits that must be 0 to collide with a given CSmash
	{
	TRACE("NEVER USED!\n");
	ASSERT(0);
	}

// Returns the next object being collided with, using the parameters that were
// passed to QuickCheckReset().  This will return all the objects being collided
// with in an arbitrary order.  Other functions will someday return the objects
// in some particular order.  The function will return false when there are no
// more colisions.
bool CSmashatorium::QuickCheckNext(					// Returns true if collision detected, false otherwise
	R3DLine*	pline,										// In:  Line segment to collide against.
	CSmash** pSmashee,								// Out: Thing being smashed into if any (unless 0)
	CSmash*	pSmasher)								// Out: Smash that should be excluded from search.
	{
	ASSERT(0);
	return false;	// NEVER USED ANYMORE!
	}

////////////////////////////////////////////////////////////////////////////////
//
//	QuickCheckNext - Smasher against smashee
//
// Returns true if collision detected, false otherwise
// Out: The Next Thing being smashed into if any (unless 0)	
// ***  ppSmashee is ONLY for output!
//
// NOTE: YOU Must set up this call using QuickCheckReset
// Returns NULL AND resets the QuickSearch if no more to find:
//
////////////////////////////////////////////////////////////////////////////////
CSmash *CSmashatorium::GetNextSmash()
	{
	int16_t sNextList = FALSE;
	CSmash *pReturn = NULL;
	int16_t sSearching = TRUE;

	while (sSearching)
		{
		if (!m_pCurrentSmashee)	// first in list:
			{
			m_pCurrentSmashee = &m_pCurrentList->m_slHead; // Start it off
			}

		if (m_pCurrentSmashee->m_pNext == &m_pCurrentList->m_slTail)
			{
			sNextList = TRUE;
			}
		else // We've got one!
			{
			m_pCurrentSmashee = m_pCurrentSmashee->m_pNext;
			pReturn = m_pCurrentSmashee->m_pParent;
			sSearching = FALSE;
			}
		
		if (sNextList)
			{
			m_pCurrentSmashee = NULL;
			sNextList = FALSE;

			// Find the next list
			m_sCurrentListX++;
			m_pCurrentList++;

			if (m_sCurrentListX >= m_sSearchW)
				{
				m_sCurrentListX = 0;
				m_sCurrentListY++;
				m_pCurrentList += m_sGridW - m_sSearchW;

				if (m_sCurrentListY >= m_sSearchH) // You're DONE
					{
					m_sCurrentListX = m_sCurrentListY = m_sSearchW = 
						m_sSearchH = 0;
					
					m_pCurrentList = NULL;
					pReturn = NULL;
					sSearching = FALSE;
					m_pSmasher = NULL;  // The real deactivation
					}
				}
			}
		}

	return pReturn;
	}

bool CSmashatorium::QuickCheckNext(CSmash** ppSmashee) 
	{ 
	// First, handle the easy case of a guaranteed 2x2 object:
	CSmash *pSmashee = NULL;

	// 1) Is a search in progress?
	if (!m_pSmasher) return false; // reset at end of search

	// 2) The QuickCheckReset parameters can tell the size:
	//		Look for a collision with our requirements
	pSmashee = GetNextSmash();
	while (pSmashee)	// compare this with what we want
		{
		if (pSmashee->m_lSearchTagCode != m_lCurrentSearchCode)
			{	// Avoid redundancy
			pSmashee->m_lSearchTagCode = m_lCurrentSearchCode;

			if (!(pSmashee->m_bits & m_exclude) && ((pSmashee->m_bits & ~m_dontcare) 
				& m_include) && pSmashee != m_pSmasher)
				{
				if (pSmashee->m_sphere.Collide(&m_pSmasher->m_sphere) == COLLISION)
					{
					if (CollideCyl(pSmashee,&m_pSmasher->m_sphere.sphere) == SUCCESS)
						{
						*ppSmashee = m_pCurrentSmashee->m_pParent;
						return true;
						}
					}
				}
			}

		pSmashee = GetNextSmash();
		}

	return false;  // USED BY FIRE
	}						

////////////////////////////////////////////////////////////////////////////////
//
//	QuickCheck - Smasher against smashee
//
// Returns true if collision detected, false otherwise
// Sets *ppSmashee to the thing collided with.
//
// This function is like QuickCheckNext, except it just returns the 
// FIRST thing it finds that is a hit.  (Arbitrary)
// 
// Returns NULL if nothing is colliding
//
////////////////////////////////////////////////////////////////////////////////
bool CSmashatorium::QuickCheck(						// Returns true if collision detected, false otherwise
	CSmash* pSmasher,										// In:  CSmash to check
	CSmash::Bits include,								// In:  Bits that must be 1 to collide with a given CSmash
	CSmash::Bits dontcare,								// In:  Bits that you don't care about
	CSmash::Bits exclude,								// In:  Bits that must be 0 to collide with a given CSmash
	CSmash** ppSmashee)									// Out: Thing being smashed into if any (unless 0)
	{
	// This routine combines the logic of QuickCheckNext and QuickCheckReset into one!
	ASSERT(pSmasher);
	ASSERT(ppSmashee);

	m_lCurrentSearchCode++;			// prepare for a new searching code
	if (m_lCurrentSearchCode < 0)	// unfortunate wrapping around...
		{
		ASSERT(0);	// Need to "detag" the smashatorium - detag could be a function
		}

	// Determine the grid expanse of the Smasher's radius:
	// (1) cast into a square:
	//---------------------------------------------------------------
	RSphere* pSphere = &(pSmasher->m_sphere.sphere);
	int32_t lR = pSphere->lRadius;

	// Find upper left & lower right position:
	int32_t	lX,lY,lX2,lY2;
	lX = pSphere->X - lR;
	lY = pSphere->Z - lR;

	int16_t sW=0,sH=0,i,j;
	CSmashatoriumList* pCurrentList = NULL;

	// Now do something different for a smashee that's in the 'torium
	// and one that's not...
	if (pSmasher->m_sInGrid) // we KNOW it's fully clipped and of legal size...
		{
		if ( (lX <= -m_sTileW) || (lY < -m_sTileH) || 
			(lX >= m_sWorldW) || (lY >= m_sWorldH) )
			{
			// We have FULL CLIP OUT!
			*ppSmashee = NULL;
			return false;	// this search has ended!
			}

		// We know to do 2 x 2:
		pCurrentList = m_ppslClipY[lY] + m_psClipX[lX];
		sW = sH = 2;
		}
	else
		{
		// Handle the case of a monstrosity!
		lR += lR; // lR is a diameter now!
		lX2 = lX + lR;
		lY2 = lY + lR;

		//==========================================
		// Do tight clipping:
		//==========================================
		if (lX < 0) lX = 0;
		if (lY < 0) lY = 0;

		if (lX2 >= m_sWorldW) lX2 = m_sWorldW - 1;
		if (lY2 >= m_sWorldH) lY2 = m_sWorldH - 1;
		
		if ( (lX2 <= lX) || (lY2 <= lY) )
			{
			// Fully clipped out!
			*ppSmashee = NULL;
			return false;	// this search has ended!
			}

		// Set up the search parameters:
		pCurrentList = m_ppslClipY[lY] + m_psClipX[lX];

		sW = 1 + m_psClipX[lX2] - m_psClipX[lX];
		sH = 1 + m_psClipY[lY2] - m_psClipY[lY];
		}

	// Do the search
	for (j=0; j < sH; j++, pCurrentList += m_sGridW - sW)
		{
		for (i=0; i < sW; i++,pCurrentList++)
			{
			if (pCurrentList->m_sNum)
				{
				CSmashLink* pLink = pCurrentList->m_slHead.m_pNext;
				CSmash* pSmashee;
				while (pLink != &pCurrentList->m_slTail)
					{
					pSmashee = pLink->m_pParent;

					// Test for the collision!
					if (pSmashee->m_lSearchTagCode != m_lCurrentSearchCode)
						{	// Avoid redundancy
						pSmashee->m_lSearchTagCode = m_lCurrentSearchCode;

						if (!(pSmashee->m_bits & exclude) && ((pSmashee->m_bits & ~dontcare) 
							& include) && pSmashee != pSmasher)
							{
							if (pSmashee->m_sphere.Collide(&pSmasher->m_sphere) == COLLISION)
								{
								if (CollideCyl(pSmashee,&pSmasher->m_sphere.sphere) == SUCCESS)
									{
									*ppSmashee = pSmashee;
									return true;
									}
								}
							}
						}

					pLink = pLink->m_pNext;
					}
				}
			}
		}

	return false; // Used by missile
	}

////////////////////////////////////////////////////////////////////////////////
//
//	QuickCheckClosest - Smasher against smashee
//
// Returns true if collision detected, false otherwise
// Sets *ppSmashee to the thing collided with.
//
// This function is like QuickCheckNext, except it just returns the 
// CLOSEST thing it finds that is a hit.  (Front or back)
// 
// Returns NULL if nothing is colliding
//
////////////////////////////////////////////////////////////////////////////////
bool CSmashatorium::QuickCheckClosest(				// Returns true if collision detected, false otherwise
	CSmash* pSmasher,										// In:  CSmash to check
	CSmash::Bits include,								// In:  Bits that must be 1 to collide with a given CSmash
	CSmash::Bits dontcare,								// In:  Bits that you don't care about
	CSmash::Bits exclude,								// In:  Bits that must be 0 to collide with a given CSmash
	CSmash** ppSmashee)
	{
	// This routine combines the logic of QuickCheckNext and QuickCheckReset into one!
	ASSERT(pSmasher);
	ASSERT(ppSmashee);

	m_lCurrentSearchCode++;			// prepare for a new searching code
	if (m_lCurrentSearchCode < 0)	// unfortunate wrapping around...
		{
		ASSERT(0);	// Need to "detag" the smashatorium - detag could be a function
		}


	// Determine the grid expanse of the Smasher's radius:
	// (1) cast into a square:
	//---------------------------------------------------------------
	RSphere* pSphere = &(pSmasher->m_sphere.sphere);
	int32_t lR = pSphere->lRadius;

	int32_t lClosestDist2 = 2000000000; // a large number
	int32_t lCurDist2;

	int32_t lSmasherX = pSphere->X;
	int32_t lSmasherY = pSphere->Z;

	CSmash* pClosestSmash = NULL;

	// Find upper left & lower right position:
	int32_t	lX,lY,lX2,lY2;
	lX = lSmasherX - lR;
	lY = lSmasherY - lR;

	int16_t sW=0,sH=0,i,j;
	CSmashatoriumList* pCurrentList = NULL;

	// Now do something different for a smashee that's in the 'torium
	// and one that's not...
	if (pSmasher->m_sInGrid) // we KNOW it's fully clipped and of legal size...
		{
		if ( (lX <= -m_sTileW) || (lY < -m_sTileH) || 
			(lX >= m_sWorldW) || (lY >= m_sWorldH) )
			{
			// We have FULL CLIP OUT!
			*ppSmashee = NULL;
			return false;	// this search has ended!
			}

		// We know to do 2 x 2:
		pCurrentList = m_ppslClipY[lY] + m_psClipX[lX];
		sW = sH = 2;
		}
	else
		{
		// Handle the case of a monstrosity!
		lR += lR; // lR is a diameter now!
		lX2 = lX + lR;
		lY2 = lY + lR;

		//==========================================
		// Do tight clipping:
		//==========================================
		if (lX < 0) lX = 0;
		if (lY < 0) lY = 0;

		if (lX2 >= m_sWorldW) lX2 = m_sWorldW - 1;
		if (lY2 >= m_sWorldH) lY2 = m_sWorldH - 1;
		
		if ( (lX2 <= lX) || (lY2 <= lY) )
			{
			// Fully clipped out!
			*ppSmashee = NULL;
			return false;	// this search has ended!
			}

		// Set up the search parameters:
		pCurrentList = m_ppslClipY[lY] + m_psClipX[lX];

		sW = 1 + m_psClipX[lX2] - m_psClipX[lX];
		sH = 1 + m_psClipY[lY2] - m_psClipY[lY];
		}

	// Do the search
	for (j=0; j < sH; j++, pCurrentList += m_sGridW - sW)
		{
		for (i=0; i < sW; i++,pCurrentList++)
			{
			if (pCurrentList->m_sNum)
				{
				CSmashLink* pLink = pCurrentList->m_slHead.m_pNext;
				CSmash* pSmashee;
				while (pLink != &pCurrentList->m_slTail)
					{
					pSmashee = pLink->m_pParent;

					// Test for the collision!
					if (pSmashee->m_lSearchTagCode != m_lCurrentSearchCode)
						{	// Avoid redundancy
						pSmashee->m_lSearchTagCode = m_lCurrentSearchCode;

						// Test for the colision!
						if (!(pSmashee->m_bits & exclude) && ((pSmashee->m_bits & ~dontcare) 
							& include) && pSmashee != pSmasher)
							{
							if (pSmashee->m_sphere.Collide(&pSmasher->m_sphere) == COLLISION)
								{
								if (CollideCyl(pSmashee,&pSmasher->m_sphere.sphere) == SUCCESS)
									{
									// Is this hit the closest?
									lCurDist2 = SQR(lSmasherX - pSmashee->m_sphere.sphere.X) + 
										SQR(lSmasherY - pSmashee->m_sphere.sphere.Z);
									if (lCurDist2 < lClosestDist2)
										{
										pClosestSmash = pSmashee;
										lClosestDist2 = lCurDist2;
										}
									}
								}
							}
						}

					pLink = pLink->m_pNext;
					}
				}
			}
		}
	
	*ppSmashee = pClosestSmash;
	if (pClosestSmash) return true;

	return false; // Used by lock-on missile
	}

////////////////////////////////////////////////////////////////////////////////
//
//	QuickCheck - collide a line with the smash
//	
// Determine whether specified R3DLine is colliding with anything, and
// if so, (optionally) return the first thing it's colliding with.
//
////////////////////////////////////////////////////////////////////////////////
bool CSmashatorium::QuickCheck(// Returns true if collision detected, false otherwise
	R3DLine* pLine,				// In:  Line to check
	CSmash::Bits include,		// In:  Bits that must be 1 to collide with a given CSmash
	CSmash::Bits dontcare,		// In:  Bits that you don't care about
	CSmash::Bits exclude,		// In:  Bits that must be 0 to collide with a given CSmash
	CSmash** ppSmashee,			// Out: Thing being smashed into if any (unless 0)
	CSmash*	pSmasher)			// Out: Smash that should be excluded from search.
	{
	ASSERT(0);

	return false;		// NEVER USED ANYMORE!
	}

// I just use this for anything I need to debug at the moment.
// Currently, it dumps out the grid amount numbers.
void	CSmashatorium::Debug()
	{
	// PURE DEBUGGING HELL!
		FILE* fp = fopen("smashout.txt","w");

		fprintf(fp,"GridW = %hd\nGridH = %hd\n",m_sGridW,m_sGridH);
		fprintf(fp,"# in smash = %hd;  # in each list:\n\n",m_sNumInSmash);

		int16_t di,dj;

		// Try direct grid access:
		int16_t p = 0;
		for (dj = 0; dj < m_sGridH; dj++)
			{
			for (di = 0; di < m_sGridW; di++)
				{
				fprintf(fp,"%2hd ",m_pGrid[p++].m_sNum);
				}
			fprintf(fp,"\n");
			}

		fprintf(fp,"\n\n***********\n");
		// Try Indirect Access
		for (dj = 0; dj < m_sWorldH; dj+=m_sTileH)
			{
			for (di = 0; di < m_sWorldW;di+=m_sTileW)
				{
				fprintf(fp,"%2hd ",(m_ppslClipY[dj] + m_psClipX[di])->m_sNum);
				}
			fprintf(fp,"\n");
			}
		//
		fclose(fp);
	}

////////////////////////////////////////////////////////////////////////////////
//
//	QuickCheckClosest -	collide a line with the smash, excluding myself
//								Find the closest hit to the FIRST point in the line!
//	
// Determine whether specified R3DLine is colliding with anything, and
// if so, (optionally) return the closet thing (to the CSmash) it's colliding with.
// Additionally, specify a CSmash to EXCLUDE from the search.
//
// Note that it is actually a line segment
//
////////////////////////////////////////////////////////////////////////////////
bool CSmashatorium::QuickCheckClosest(	// Returns true if collision detected, false otherwise
	R3DLine* pline,							// In:  Line to check
	CSmash::Bits include,					// In:  Bits that must be 1 to collide with a given CSmash
	CSmash::Bits dontcare,					// In:  Bits that you don't care about
	CSmash::Bits exclude,					// In:  Bits that must be 0 to collide with a given CSmash
	CSmash** ppSmashee,						// Out: Thing being smashed into if any.
	CSmash*	pSmasher)						// Out: Smash that should be excluded from search.
	{
	// This is a tricky line, because far from the standard 8-connect line, this must include
	// ALL regions the line even glances through!  And cliping is a nightmare!

	// This routine combines the logic of QuickCheckNext and QuickCheckReset into one!
	// pSmasher can be NULL!
	ASSERT(ppSmashee);

	// Current Implementation:
	// 1) NO CLIPPING YET!
	// 2) NO vertical line case:

	// Set up the line, clipping where needed: (We only look at 2d)
	// We will draw the line left to right:

	//************************************************************************************
	// Sort points left to right:

	int32_t lLeft = pline->X1;
	int32_t lRight = pline->X2;
	int32_t lLeftY = pline->Z1;
	int32_t lRightY = pline->Z2;

	// Inverted case
	if (lRight < lLeft)
		{
		lLeft = pline->X2;
		lRight = pline->X1;
		lLeftY = pline->Z2;		
		lRightY = pline->Z1;
		}
	//************************************************************************************
	// Calculate y major line coefficients: (later, adapt to clipping values)
	int32_t	lDelX = lRight - lLeft;
	int32_t	lDelY = lRightY - lLeftY;
	int32_t	lDet = lDelX * lRightY - lDelY * lRight;
	int32_t	lDetY = lDelY * lRight - lDelX * lRightY; // for x-major line form

	//************************************************************************************
	// Handle Clipping, x-major line caclulation, and special cases
				//***************  NYI!  **************

	// Check for horizontal clipping (easy)
	int32_t	lClipLeft = lLeft;// = MAX(0,lLeft);
	int32_t	lClipRight = lRight;// = MIN(m_sWorldW - 1,lRight);
	int32_t	lClipLeftY = lLeftY;
	int32_t	lClipRightY = lRightY;

	int16_t sVerticalStrip = false;
	int32_t	lGridLeft;
	int32_t	lGridRight;

	if (lDelX)	// determine x-clipping values:
		{

		if (lLeft < 0) 
			{
			lClipLeft = 0;
			lClipLeftY = lDet / lDelX; //********** CAREFUL
			}

		if (lRight >= m_sWorldW)
			{
			lClipRight = m_sWorldW - 1;
			lClipRightY = (lClipRight * lDelY + lDet) / lDelX;	//******** CAREFUL!
			}
		}
	else	// certical strip case:
		{
		if ( (lLeft < 0) || (lRight >= m_sWorldW) ) return false;
		}

 	// Check for case of reverse clip out:
	if ( (lLeft >= m_sWorldW) || (lRight < 0) ) return false;	// clipped out!

	lGridLeft = (int32_t)m_psClipX[lClipLeft];	// These represent pts BETWEEN grid squares
	lGridRight = 1 + (int32_t)m_psClipX[lClipRight];

	if ((lDelX == 0) || ( (lGridRight - lGridLeft) <= 1) ) 
		{
		sVerticalStrip = true;	// initial x clipping
		}
//		}
//	else 

	if (sVerticalStrip)	// do special clipping:
		{
		// Handle the vertical strip case:
		if (lClipRight < lClipLeft) return false;	// vertical strip off screen

		// Clip Vertically
		lGridRight = lGridLeft + 1;	// for compatibility
		if (lClipRightY < 0) lClipRightY = 0;
		if (lClipRightY >= m_sWorldH) lClipRightY = m_sWorldH;
		if (lClipLeftY < 0) lClipLeftY = 0;
		if (lClipLeftY >= m_sWorldH) lClipLeftY = m_sWorldH;
		}

	//************************************************************************************
	// Check for Vertical Clipping:  (even for strip case)
	// Must recalculate grid positions of X changes:

	// NEED true CLIP OUT SCENARIOS FOR VERTICAL!!!!

	int16_t sClippingY = false;

	if (lDelY != 0) // Horizontal strip case
		{													// Note that the vertical case has
															// twice the possibilities
		if (lDelY > 0)	// lClipLeftY < lClipRightY:
			{
			if (lClipLeftY < 0) 
				{
				lClipLeftY = 0;
				lClipLeft = lDetY / lDelY; //********** CAREFUL

				if ( (lClipLeft < 0) || (lClipLeft >= m_sWorldW) ) return false;

				lGridLeft = (int32_t)m_psClipX[lClipLeft];
				sClippingY = true;
				}

			if (lClipRightY >= m_sWorldH)
				{
				lClipRightY = m_sWorldH - 1;
				lClipRight = (lClipRightY * lDelX + lDetY) / lDelY;	//******** CAREFUL!

				if ( (lClipRight < 0) || (lClipRight >= m_sWorldW) ) return false;

				lGridRight = 1 + (int32_t)m_psClipX[lClipRight];
				sClippingY = true;
				}

			// Check of clipout:
			if (lClipRightY < lClipLeftY) return false; // clipped out
			}
		else	// lClipLeftY > lClipRightY
			{
			if (lClipRightY < 0) 
				{
				// Since lDelY must be negative for us to get here,
				// lDetY must be negative for lClipRight to be
				// a positive index.
				//ASSERT(lDetY < 0);

				lClipRightY = 0;
				lClipRight = lDetY / lDelY; //********** CAREFUL

				// Assert on the actual value, duh!  I don't know what
				// I was thinking with that fancy smancy ASSERT above.
				//ASSERT(lClipRight >= 0);

				if ( (lClipRight < 0) || (lClipRight >= m_sWorldW) ) return false;

				lGridRight = 1 + (int32_t)m_psClipX[lClipRight];
				sClippingY = true;
				}

			if (lClipLeftY >= m_sWorldH)
				{
				lClipLeftY = m_sWorldH - 1;
				lClipLeft = (lClipLeftY * lDelX + lDetY) / lDelY;	//******** CAREFUL!

				// Since lDelY must be negative for us to get here,
				// lDetY must be positive enough to get us out of negatives
				// OR lClipLeftY must be negative 
				// OR lDelX must be negative
				// but not both, but perhaps all three could be true.
				// The easiest way to watch out for this value is by the result.

				//ASSERT(lClipLeft >= 0);

				if ( (lClipLeft < 0) || (lClipLeft >= m_sWorldW) ) return false;

				lGridLeft = (int32_t)m_psClipX[lClipLeft];
				sClippingY = true;
				}

			// Check out clipout
			if (lClipRightY > lClipLeftY) return false; // clipped out
			}

		if (sClippingY) // recalculate clipping situation:
			{
			if ((lDelX == 0) || ( (lGridRight - lGridLeft) == 1) ) 
				sVerticalStrip = true;
			}
		}
	else	// horizontal strip case:
		{
		if ( (lClipLeftY < 0) || (lClipLeftY >= m_sWorldH) ) return false;
		}

	//************************************************************************************
	// Calculate grid points for the line: (later, switch to clipped values)

	// allocate on the stack a local point chart:
#define MAX_GRID_W 1024	//************************************ NEED TO DEAL WITH THIS!

	int32_t	alPointsY[MAX_GRID_W + 1];
	int16_t i,x;

	// Get end points:
	alPointsY[lGridLeft] = (int32_t)m_psClipY[lClipLeftY];
	alPointsY[lGridRight] = (int32_t)m_psClipY[lClipRightY];

	//************************************************************************************
	// Handle vertical strip case, if applicable:

	if (sVerticalStrip == false)	// Load theintermediate values:
		{	// not vertical strip:

		// Populate the point array:
		x = lGridLeft * m_sTileW; // now using clip instead of access, which is one over

		for (i = lGridLeft + 1; i < lGridRight; i++, x += m_sTileW)
			{
			alPointsY[i] = (x * lDelY + lDet) / lDelX; // WARNING - watch for lDelX
			alPointsY[i] = (int32_t)m_psClipY[alPointsY[i]]; // convert to grid coordinates
			}
		}

	//************************************************************************************
	//  Move acros all the grid points crossed by the line, and process each Smash List!
	int16_t j;

	// SPLIT between positive and negative cases:
	int16_t sSignY = 1;
	if (lDelY < 0) sSignY = -1;

	// SET UP DISTANCE VARIABLES:
	int32_t lClosestDist2 = 2000000000; // a large number
	int32_t lCurDist2;
	CSmash* pClosestSmash = NULL;

	m_lCurrentSearchCode++;			// prepare for a new searching code
	if (m_lCurrentSearchCode < 0)	// unfortunate wrapping around...
		{
		ASSERT(0);	// Need to "detag" the smashatorium - detag could be a function
		}

	for (i = lGridLeft; i < lGridRight; i++)
		{
		// Now, a little tricky - do a bidirectional loop to cover both quadrants:
		for (j = alPointsY[i]; j != (alPointsY[i + 1] + sSignY); j += sSignY)
			{
#ifdef	SMASH_DEBUG
	
	DebugSmash.DrawSmashSquare(3,0,i-1,j-1);

#endif
			// Get the list:
			ASSERT(j * m_sTileH < m_sWorldH + 2 * m_sTileH);
			ASSERT(i * m_sTileW < m_sWorldW + 2 * m_sTileW);
			ASSERT(j * m_sTileH >= 0);
			ASSERT(i * m_sTileW >= 0);
			CSmashatoriumList* pCurrentList = m_ppslAccessY[j * m_sTileH] + m_psAccessX[i * m_sTileW];

			//***************************************************************************
			// Now, process this smash grid in a standard loop like any other.
			if (pCurrentList->m_sNum)
				{
				CSmashLink* pLink = pCurrentList->m_slHead.m_pNext;
				CSmash* pSmashee;
				while (pLink != &pCurrentList->m_slTail)
					{
					pSmashee = pLink->m_pParent;

					// Test for the collision!
					if (pSmashee->m_lSearchTagCode != m_lCurrentSearchCode)
						{	// Avoid redundancy
						pSmashee->m_lSearchTagCode = m_lCurrentSearchCode;

						// Test for the colision!
						if (!(pSmashee->m_bits & exclude) && ((pSmashee->m_bits & ~dontcare) 
							& include) && pSmashee != pSmasher)
							{
							if (pSmashee->m_sphere.Collide(pline) == COLLISION)
								{
								if (CollideCyl(pSmashee,pline) == SUCCESS)
									{
									// Is this hit the closest?
									// Calculate distance from FIRST point in the line

									lCurDist2 = ABS2(
										pSmashee->m_sphere.sphere.X - pline->X1,
										pSmashee->m_sphere.sphere.Y - pline->Y1,
										pSmashee->m_sphere.sphere.Z - pline->Z1);

									// If there's not currently a closest or this one is closer . . .
									if (lCurDist2 < lClosestDist2)
										{
										// Make this the closest.
										pClosestSmash	= pSmashee;
										lClosestDist2	= lCurDist2;
										}
									}
								}
							}
						}

					pLink = pLink->m_pNext;
					}
				}
			}
		}

	// Set the result:
	*ppSmashee = pClosestSmash;
	if (pClosestSmash) return true;

	return false; // #1 most used function! (All guns)
	}

//==============================================================================

////////////////////////////////////////////////////////////////////////////////
//  The following functions SHOULD be moved back into the class so that
//  they can be inlined.  Do this as soon as the code is fully stable!
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
//
//	AddLimb
// 
// Lower level function for adding each leg of the Smash into it's quadrant
//
////////////////////////////////////////////////////////////////////////////////
void	CSmashatorium::AddLimb(CSmashatoriumList* pList, CSmashLink* pLink)
	{
	ASSERT(pLink);
	ASSERT(pList);
	//--------------------------------------
	pList->m_sNum++;

	pLink->m_pLast = pList;

	CSmashLink* pTail = &pList->m_slTail;
	CSmashLink* pPrev = pTail->m_pPrev;

	pLink->m_pNext = pTail;
	pLink->m_pPrev = pPrev;
	pPrev->m_pNext = pLink;
	pTail->m_pPrev = pLink;
	}

////////////////////////////////////////////////////////////////////////////////
//
//	RemoveLimb
//	
// Lower level function for removing each leg of the Smash from it's quadrant
//
////////////////////////////////////////////////////////////////////////////////
void	CSmashatorium::RemoveLimb(CSmashatoriumList* pList,CSmashLink* pLink)
	{
	ASSERT(pLink);
	ASSERT(pList);
	ASSERT(pList->m_sNum);
	//--------------------------------------
	pList->m_sNum--;

	pLink->m_pLast = NULL;

	CSmashLink* pPrev = pLink->m_pPrev;
	CSmashLink* pNext = pLink->m_pNext;

	pPrev->m_pNext = pNext;
	pNext->m_pPrev = pPrev;
	pLink->m_pPrev = pLink->m_pNext = NULL;
	}

////////////////////////////////////////////////////////////////////////////////
//
//		Update
//	
// Update the specified CSmash.  If it isn't already in the smashatorium, it
// is automatically added.  Whenever the CSmash is modified, this must be
// called before using the smashotorium to check for collisions!
// This version of the update is less efficient, as it always searches
// four quadrants, and usually the desired object is only in one of the
// four.  But I am trying it because it offers very fast updates.
//
////////////////////////////////////////////////////////////////////////////////
int16_t sMaxRad = 0;

void CSmashatorium::Update(CSmash* pSmash)	// In:  CSmash to be updated
	{
	ASSERT(pSmash);

	// Check the flag in the realm to see if we're editing.
	// If so, DO NOT actually let things go in the smashatorium:
	if (pSmash->m_pThing->m_pRealm->m_flags.bEditing
		&& (!pSmash->m_pThing->m_pRealm->m_flags.bEditPlay))
		{
		return;	// pretend you're in the smash
		}

	// ASSUME THAT THE OBJECT IS GRID SIZED!
	//---------------------------------------------------------------
	// (1) Cast the sphere into a 2 point square
	RSphere* pSphere = &(pSmash->m_sphere.sphere);
	int32_t lR = pSphere->lRadius;
	int32_t lD = lR << 1;

	// Find upper left position:
	int32_t	lX,lY;
	lX = pSphere->X - lR;
	lY = pSphere->Z - lR;
	
	// Hook in the special case of a FAT body in the smash!
	if ( (lD > m_sTileW) || (lD > m_sTileH) )
		{
		//================================================================== FAT SMASH
		// Create and insert a fat smash into the smashatorium.
		CFatSmash*	pFat = pSmash->m_pFat;

		if (!pSmash->m_pFat)	// we need to create a fat for you boy!
			{
			ASSERT(!pSmash->m_sInGrid); // a growing small object has popped it's boundaries

			//============================================ CREATE FAT EXTENSION
			// Create a new fat extention to the smash:
			pFat = new CFatSmash;
			pSmash->m_pFat = pFat;
			pFat->m_pParent = pSmash;	// ahhhh, a family

			if (!pFat)
				{
				TRACE("CSmashatorium::Update: memory alloc error! Couldn't add to smashatorium!\n");
				return;
				}
			
			// Remember where it was
			pFat->m_lX = lX;
			pFat->m_lY = lY;

			// Find dimensions of smash and allocate:
			pFat->m_sW = m_psAccessX[lD + m_sTileW - 1] + 1;	// min tiles + 1
			pFat->m_sH = m_psAccessY[lD + m_sTileH - 1] + 1;	// min tiles + 1

			pFat->m_sNumGrids = pFat->m_sW * pFat->m_sH;
			if (pFat->Alloc(pFat->m_sNumGrids) != SUCCESS)
				{
				TRACE("CSmashatorium::Update: memory alloc error! Couldn't add to smashatorium!\n");
				return;
				}	// (all links are now NULL!

			// Set all the SmashLinks to point to their parent:
			for (int16_t i=0; i < pFat->m_sNumGrids; i++)
				{
				pFat->m_pLinks[i].m_pParent = pSmash;
				}

			// at this point, assume the smash is fat
			}

		///////////////////////////////////
		// (2) Catch the case of FULL clipping:
		if ( (lX <= -lD) || (lY <= -lD) || 
			(lX >= m_sWorldW) || (lY >= m_sWorldH) )
			{
			// We have FULL CLIP OUT!
			if (pSmash->m_sInGrid)	RemoveFat(pFat); // set's InGrid to false
			else	pSmash->m_sInGrid = FALSE;
			
			return; 
			}

		///////////////////////////////////
		// (4) is it's position different?
		// (If it wasn't in the grid, than this is irrelevant:)
		//
		// Has it changed or is it reentering the grid?
		if ( (lX != pFat->m_lX) || (lY != pFat->m_lY) || (!pSmash->m_sInGrid)) 
			{
			if (pSmash->m_sInGrid) 
				{
				RemoveFat(pFat);  // Remove from old
				}

			///////////////////////////////////
			// ADD FAT AND SET FAT POSITION!
			///////////////////////////////////
			// Remember where it was
			pFat->m_lX = lX;
			pFat->m_lY = lY;

			// (3) calculate current grid clipping state:
			// Because a fat smash should NOT be moving, we shouldn't be doing this 
			// more than once!

			int16_t sClipX = MAX((int32_t)0,lX);
			int16_t sClipY = MAX((int32_t)0,lY);
			int16_t sClipX2 = MIN((int32_t)m_sWorldW-1,lX + lD);
			int16_t sClipY2 = MIN((int32_t)m_sWorldH-1,lY + lD);

			pFat->m_pClippedGrid = m_ppslClipY[sClipY] + m_psClipX[sClipX];

			// We can't access grid locations if lX and lY are negative!
			int16_t sGridX,sGridY;
			if (lX < 0) sGridX = -m_psAccessX[-lX] - 1; // mirror it!
			else	sGridX = m_psAccessX[lX];

			if (lY < 0) sGridY = -m_psAccessY[-lY] - 1; // mirror it!
			else	sGridY = m_psAccessX[lY];

			// Convert to grid coordinates:
			sClipX = m_psAccessX[sClipX];
			sClipY = m_psAccessY[sClipY];
			sClipX2 = m_psAccessX[sClipX2];
			sClipY2 = m_psAccessY[sClipY2];

			// Map to local fat smash:
			// These are relative grid positions:

			pFat->m_sClipX = sClipX - sGridX;
			pFat->m_sClipY = sClipY - sGridY;
			pFat->m_sClipW = sClipX2 - sClipX + 1;
			pFat->m_sClipH = sClipY2 - sClipY + 1;

			// Where do we start in smashatorium?
			pFat->m_pFirstLink = pFat->m_pLinks + pFat->m_sW * pFat->m_sClipY + pFat->m_sClipX;

			AddFat(pFat);  // Will set flag to in grid
			}

		return;
		//================================================================== FAT SMASH
		}

	///////////////////////////////////
	// (2) Catch the case of FULL clipping:
	if ( (lX <= -m_sTileW) || (lY < -m_sTileH) || 
		(lX >= m_sWorldW) || (lY >= m_sWorldH) )
		{
		// We have FULL CLIP OUT!
		if (pSmash->m_sInGrid)	Remove(pSmash); // set's InGrid to false
		else	pSmash->m_sInGrid = FALSE;
		
		return; 
		}

	///////////////////////////////////
	// (3) calculate grid bas position:
	CSmashatoriumList *pCurrent = m_ppslClipY[lY] + m_psClipX[lX];

	///////////////////////////////////
	// (4) is it's position different?
	// (If it wasn't in the grid, than this is irrelevant:)
	//
	if (!pSmash->m_sInGrid)	// Re-entering Grid
		{
		Add(pSmash,pCurrent);  // Will set flag to in grid
		}
	else if (pSmash->m_link1.m_pLast != pCurrent) 
		{
		Remove(pSmash);  // Remove from old

		Add(pSmash,pCurrent);  // Will set flag to in grid
		}
	}

////////////////////////////////////////////////////////////////////////////////
//
//		ADD
//
// Higher Level -> add an entire CSmash into the 'torium
// User calls Update, which checks for clipping
// This routine ASSUMES not clipped out!
//
////////////////////////////////////////////////////////////////////////////////
void	CSmashatorium::Add(CSmash* pSmash,CSmashatoriumList *pList)
	{
	ASSERT(pList);
	ASSERT(pSmash);
	if (pSmash->m_sInGrid) return; // Don't need to re-add it!
	if (pSmash->m_pFat)
		{
		AddFat(pSmash->m_pFat);
		return;
		}
	//------------------------------------
	pSmash->m_sInGrid = TRUE;

	AddLimb(pList,&pSmash->m_link1);
	AddLimb(pList + m_sGridW,&pSmash->m_link3);
	AddLimb(++pList,&pSmash->m_link2);
	AddLimb(pList + m_sGridW,&pSmash->m_link4);

	m_sNumInSmash++;
	if (m_sNumInSmash > m_sMaxNumInSmash) m_sMaxNumInSmash = m_sNumInSmash;
	}

////////////////////////////////////////////////////////////////////////////////
//
//		AddFat
//
// Higher Level -> add an entire CSmash into the 'torium
// User calls Update, which checks for clipping
// This routine ASSUMES not clipped out!
// This is specially tailored to a fat smash object.
// Clipping information should be set in the FatSmash before passing.
// All links should be NULLED if clipped!
//
////////////////////////////////////////////////////////////////////////////////
void	CSmashatorium::AddFat(CFatSmash* pFatSmash)
	{
	ASSERT(pFatSmash);
	if (pFatSmash->m_pParent->m_sInGrid) return; // Don't need to re-add it!
	//=====================================
	pFatSmash->m_pParent->m_sInGrid = TRUE;
	int16_t i,j;
	CSmashatoriumList*	pList = pFatSmash->m_pClippedGrid;	// assume not clipped out!
	CSmashLink*	pLink = pFatSmash->m_pFirstLink;
	//-------------------------------------
	for (j=0; j < pFatSmash->m_sClipH; j++,pList += m_sGridW - pFatSmash->m_sClipW, 
													pLink += pFatSmash->m_sW - pFatSmash->m_sClipW)
		{
		for (i=0; i < pFatSmash->m_sClipW; i++,pList++,pLink++)
			{
			AddLimb(pList,pLink);  // ASSUME already cleared out!
			}
		}
	//=====================================
	m_sNumInSmash++;
	if (m_sNumInSmash > m_sMaxNumInSmash) m_sMaxNumInSmash = m_sNumInSmash;

	TRACE("Fat added\n");
	}

////////////////////////////////////////////////////////////////////////////////
//
//
//	Remove
//
// This is on a per object level:
// Remove the CSmash from the smashatorium
//
////////////////////////////////////////////////////////////////////////////////
void	CSmashatorium::Remove(CSmash* pSmash)
	{
	if (pSmash->m_sInGrid == FALSE) return; // don't need to remove it!
	if (pSmash->m_pFat)
		{
		RemoveFat(pSmash->m_pFat);
		return;
		}

	m_sNumInSmash--;
	pSmash->m_sInGrid = FALSE;
	CSmashLink* pLink;

	pLink = &pSmash->m_link1;
	// In the current scheme, it's all or nothing:
	if (pLink->m_pLast) // assume all four legs are here
		{
		RemoveLimb(pLink->m_pLast,pLink);
		pLink = &pSmash->m_link2;
		RemoveLimb(pLink->m_pLast,pLink);
		pLink = &pSmash->m_link3;
		RemoveLimb(pLink->m_pLast,pLink);
		pLink = &pSmash->m_link4;
		RemoveLimb(pLink->m_pLast,pLink);
		}
	}

////////////////////////////////////////////////////////////////////////////////
//
//
//	RemoveFat
//
// This is on a per object level:
// Remove the CSmash from the smashatorium
// Specialized to remove a fat object.
// It assumes that the range given takes clipping into account
//
////////////////////////////////////////////////////////////////////////////////
void	CSmashatorium::RemoveFat(CFatSmash* pFatSmash)
	{
	ASSERT(pFatSmash);
	if (pFatSmash->m_pParent->m_sInGrid == FALSE) return; // don't need to remove it!
	//===========================================
	pFatSmash->m_pParent->m_sInGrid = FALSE;
	int16_t i,j;

	//****** HERE IS A BIG DESIGN FLAW!!!!! *****
	CSmashLink*	pLink = pFatSmash->m_pLinks;	// do them all!
	//-------------------------------------
	for (j=0; j < pFatSmash->m_sH; j++)
		{
		for (i=0; i < pFatSmash->m_sW; i++,pLink++)	// pLink will wrap to the next line
			{
			if (pLink->m_pLast)
				{
				RemoveLimb(pLink->m_pLast,pLink);  // ASSUME already cleared out!
				}
			}
		}
	//===========================================
	m_sNumInSmash--;

	TRACE("Fat removed\n");
	}

//******************************************************************************
//********************************  CFatSmash  *********************************
//******************************************************************************

////////////////////////////////////////////////////////////////////////////////
//
//	CFatSmash::Erase - clear all values but do not deallocate
//
////////////////////////////////////////////////////////////////////////////////
void	CFatSmash::Erase()
	{
	m_sClipX = m_sClipY = m_sClipW = m_sClipH = m_sW = 
		m_sH = m_sNumGrids = 0;
	m_pClippedGrid = NULL;
	m_pLinks = m_pFirstLink = NULL; // Must be deleted first!
	m_pParent = NULL;
	m_lX = m_lY = 0;
	}


////////////////////////////////////////////////////////////////////////////////
//
//	CFatSmash::Destroy - deallocate the extra smash links...
//
////////////////////////////////////////////////////////////////////////////////
void	CFatSmash::Destroy()
	{
	ASSERT(m_pLinks);

	delete [] m_pLinks;	// SHOULD be safe
	Erase();
	}


////////////////////////////////////////////////////////////////////////////////
//
//	CFatSmash::Alloc - This instantiates a list of extra SmashLinks
//
//	RETURNS:	SUCCESS OR FAILURE
//
////////////////////////////////////////////////////////////////////////////////
int16_t	CFatSmash::Alloc(int16_t sNumLinks)
	{
	m_pLinks = new CSmashLink[sNumLinks];
	if (m_pLinks) return SUCCESS;

	return FAILURE;
	}

////////////////////////////////////////////////////////////////////////////////
//
//
//


//
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
	//---------------------------------------------------------------------------
