////////////////////////////////////////////////////////////////////////////////
//
// 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
//
//-------------------------------------------------
// 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.  Added clipping.
//							Handled special cases of near vertical lines and vertical clipping.
//
//		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/08/97 BRH	Added a QuickCheck that checks for collision between two
//							given smashes.  This function computes the collision
//							based only on the distance and the radius so it doesn't
//							use the grid at all.  It provides a convenient way to
//							check to see if the weapon you are launching has cleared
//							your own smash region before arming it, as used in the
//							rockets and heatseekers.
//
//		08/12/97 BRH	Added a bit for ducking so that missiles won't hit you.
//
//		08/14/97 BRH	Added bits for special barrels that can't be destroyed
//							by enemy weapons, only CDude weapons.
//
//		08/18/97 BRH	Renamed Mext as Next.
//
//		09/02/97	JMI	Added Sentry bit.
//
//		09/03/97	JMI	Added Civilian bit.
//
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
//  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.
//
// If you MUST install a large object in the smash, it should be one that 
// rarely if ever moves.  If so, it can now be supported by a CFatSmash Object.
//
////////////////////////////////////////////////////////////////////////////////
//
//  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.
//
// CFatSmash -> An extention to CSmash that holds an overflow of CSmashLinks
//              for illegally big objects.
//
// 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.
// 
////////////////////////////////////////////////////////////////////////////////
#ifndef SMASH_H
#define SMASH_H
#include "RSPiX.h"
#include "thing.h" // we are tying the nodes back to the things
#define NEW_SMASH	// We'll risk it!
////////////////////////////////////////////////////////////////////////////////
//		FORWARD DECLARATIONS
////////////////////////////////////////////////////////////////////////////////
class CSmashLink;
class CSmash;
class CSmashatoriumList;
class CSmashatorium;
class CFatSmash;

////////////////////////////////////////////////////////////////////////////////
//	 CSmashLink -> the node used in all the Smashatorium lists:
////////////////////////////////////////////////////////////////////////////////
class CSmashLink
	{
public:
	//---------------------------------------------------------------------------
	CSmashLink* m_pPrev;				// The link
	CSmashLink* m_pNext;				
	CSmash*	m_pParent;				// Access to bits
	CSmashatoriumList*	m_pLast;	// Where did it reside?
	// Might be prudent to store smash bits in here.
	//---------------------------------------------------------------------------
	void	Erase()
		{
		m_pPrev = m_pNext = NULL;
		m_pLast = NULL;
		m_pParent = NULL;
		}

	CSmashLink() { Erase(); }
	~CSmashLink() 
		{ 
		ASSERT(m_pPrev == NULL);
		ASSERT(m_pNext == NULL);
		ASSERT(m_pLast == NULL);

		Erase(); 
		}
	};

////////////////////////////////////////////////////////////////////////////////
//	 CFatSmash -> An extension to CSmash used for tracking oversized objects in
//						the Smashatorium:
////////////////////////////////////////////////////////////////////////////////
class CFatSmash
	{
public:
	CSmash*					m_pParent;		// Backwards pointer
	//--------------------------------------------------------------------
	int16_t						m_sClipX;		// In tile relative to this fat
	int16_t						m_sClipY;		// shows active grid based on clipping
	int16_t						m_sClipW;		// Links outside of this region should
	int16_t						m_sClipH;		// have NULL list pointers
	//--------------------------------------------------------------------
	int16_t						m_sW;				// Actual size (in grids)
	int16_t						m_sH;				// Actual Size (in grids)
	//--------------------------------------------------------------------
	int16_t						m_sNumGrids;	// For convenience
	CSmashLink*				m_pLinks;		// 1D representation
	CSmashLink*				m_pFirstLink;	// offset into pLinks...
	//--------------------------------------------------------------------
	CSmashatoriumList*	m_pClippedGrid;// Start Grid in 'torium
	int32_t						m_lX;				// See if it's moved!
	int32_t						m_lY;
	//--------------------------------------------------------------------
	void	Erase();
	void	Destroy();
	CFatSmash() { Erase(); }
	~CFatSmash() { Destroy(); Erase(); }

	int16_t	Alloc(int16_t sNumGrids);
	};

////////////////////////////////////////////////////////////////////////////////
//  CSmash:  The user level object which describe the collision region:
////////////////////////////////////////////////////////////////////////////////
class CSmash
	{
	//---------------------------------------------------------------------------
	// Friends
	//---------------------------------------------------------------------------
	// friend class CSmashatorium; // do I need this?

	//---------------------------------------------------------------------------
	// Types, enums, etc.
	//---------------------------------------------------------------------------
	public:
		typedef U32 Bits;

		// Available bits for classifying a CSmash
		enum
			{
			Good				= 0x00000001,
			Bad				= 0x00000002,
			Character		= 0x00000004,
			Projectile		= 0x00000008,
			Misc				= 0x00000010,
			Dead				= 0x00000020,
			Barrel			= 0x00000040,
			Mine				= 0x00000080,
			Bouy				= 0x00000100,
			Fire				= 0x00000200,
			AlmostDead		= 0x00000400,
			Pylon				= 0x00000800,
			PowerUp			= 0x00001000,
			Flag				= 0x00002000,
			FlagBase			= 0x00004000,
			Ducking			= 0x00008000,
			SpecialBarrel	= 0x00010000,
			Sentry			= 0x00020000,
			Civilian			= 0x00040000
			};

	//---------------------------------------------------------------------------
	// Variables
	//---------------------------------------------------------------------------

	public:
		Bits m_bits;						// Bits indicating CSmash's classification
		CThing* m_pThing;					// Pointer to parental thing
		RSphericalRegion m_sphere;		// Will eventually be a base class like "CRegion"
		int16_t	m_sInGrid;					// short cut to tell if in a grid...

		//---- these remain separate for fater access, since compilers SUCK
		int32_t	m_lSearchTagCode;			// By using a 32bit value, we don't need to untag!
		CSmashLink	m_link1;
		CSmashLink	m_link2;
		CSmashLink	m_link3;
		CSmashLink	m_link4;

		CFatSmash*	m_pFat;					// Used in special case of fat smash object

	//---------------------------------------------------------------------------
	// Functions
	//---------------------------------------------------------------------------
	public:
		void	Erase()
			{
			m_bits = 0;
			m_sInGrid = FALSE;
			m_link1.Erase();	// Clear these out...
			m_link2.Erase();
			m_link3.Erase();
			m_link4.Erase();
			m_lSearchTagCode = 0; // Not searched upon!
			m_pFat = NULL;
			}

		CSmash();
		~CSmash();

	};

///////////////////////////////////////////////////////////////////////////////////
//	 CSmashatoriumList -> the node used in the Smashatorium Grid to hold each list
///////////////////////////////////////////////////////////////////////////////////
class	CSmashatoriumList
	{
public:
	//---------------------------------------------------------------------------
	CSmashLink	m_slHead;	// permanent imbedded bookends!
	CSmashLink	m_slTail;	// permanent imbedded bookends!
	int16_t	m_sNum;
	//---------------------------------------------------------------------------
	void	Erase() // will NOT free any of the nodes in the list!
		{
		m_sNum = 0;
		m_slHead.m_pNext = &m_slTail;
		m_slTail.m_pPrev = &m_slHead;
		m_slHead.m_pPrev = m_slTail.m_pNext = NULL;
		}

	CSmashatoriumList()	{ Erase();	}
	~CSmashatoriumList()	
		{ 
		Erase();	
		// The book ends are never Remove()'d so we clean them up explicitly here.
		m_slHead.Erase();
		m_slTail.Erase();
		}
	};

///////////////////////////////////////////////////////////////////////////////////
//	 CSmashatorium -> Master of it all -> "the collision engine of the 90's!"
///////////////////////////////////////////////////////////////////////////////////
class CSmashatorium
	{
public:
	//---------------------------------------------------------------------------
	int16_t	m_sWorldW;	// for general logic
	int16_t m_sWorldH;
	int16_t m_sClipW;	// For clipping border
	int16_t	m_sClipH;
	int16_t	m_sGridW;	// NOT TileW -> this is the NUMBER of nodes!
	int16_t m_sGridH;
	int16_t m_sTileW;	// For catching size errors
	int16_t m_sTileH;

	CSmashatoriumList	*m_pGrid; // actually a 2d array

	//------------------- ACCESS VARIABLES:
	int16_t	*m_psAccessX;	// m_sWorldW in size
	int16_t *m_psAccessY;	// m_sWorldH in size
	CSmashatoriumList **m_ppslAccessY;	// m_sWorldH in size

	int16_t	*m_psClipX;	// m_sWorldW + 2 Tiles in size
	int16_t *m_psClipY;	// m_sWorldH + 2 Tiles in size
	CSmashatoriumList **m_ppslClipY;	// m_sWorldH + 2 Tiles in size

	//------------------- SEARCHING STATE INFORMATION: (QuickCheck info)
	// This must also handle large regions!
	// The design is largely for backwards compatibility.
	CSmash* m_pSmasher;					// NULL if search NOT in progress
	CSmashLink*	m_pCurrentSmashee;	// 

	CSmash::Bits m_include;
	CSmash::Bits m_dontcare;
	CSmash::Bits m_exclude;

	CSmashatoriumList *m_pCurrentList;
	int16_t	m_sCurrentListX;
	int16_t m_sCurrentListY;
	int16_t m_sSearchW;	//  base 1 !
	int16_t m_sSearchH; //	 base 1 !

	int32_t	m_lCurrentSearchCode;	// change it for each search

	int16_t m_sNumInSmash;	// Used for debugging
	int16_t m_sMaxNumInSmash;	// Used for debugging

	//---------------------------------------------------------------------------
	// 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.
	void Update(CSmash* pSmash);	// In:  CSmash to be updated
	//---------------------------------------------------------------------------
	void	Erase()	// does NOT deallocate anything!
		{
		m_sWorldW = m_sWorldH = m_sGridW = m_sGridH = 
			m_sClipW = m_sClipH = m_sTileW = m_sTileH = 0;

		m_psAccessX = m_psAccessY = m_psClipX = m_psClipY = NULL;
		m_pGrid = NULL;
		m_ppslAccessY = m_ppslClipY = NULL;
		m_lCurrentSearchCode = 1; // zero is NOT a valid search key!

		m_sNumInSmash = m_sMaxNumInSmash = 0;
		}

	void	Destroy()
		{
		if (m_pGrid) delete [] m_pGrid;
		if (m_psAccessX) free (m_psAccessX);
		if (m_psAccessY) free (m_psAccessY);
		if (m_ppslAccessY) free (m_ppslAccessY);

		Erase();
		}

	int16_t Alloc(int16_t sWorldW,int16_t sWorldH,int16_t sTileW,int16_t sTileH);

	// Lower Level Inline!
	// These are done multiple times for a true remove
	void	RemoveLimb(CSmashatoriumList* pList,CSmashLink* pLink);

	// This is on a per object level:
	void	Remove(CSmash* pSmash);

	// This is on a per object level:
	// Used for fat objects
	void	RemoveFat(CFatSmash* pFatSmash);

	// Insert at tail...
	// Lower level inline
	void	AddLimb(CSmashatoriumList* pList, CSmashLink* pLink);

	// Higher Level -> add an entire CSmash into the 'torium
	// User calls Update, which checks for clipping
	// This routine ASSUMES not clipped out!
	void	Add(CSmash* pSmash,CSmashatoriumList *pList);

	// Higher Level -> add an entire CSmash into the 'torium
	// User calls Update, which checks for clipping
	// This routine ASSUMES not clipped out!
	// Used for fat objects
	void	AddFat(CFatSmash* pFatSmash);

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

	// Reset QuickCheckNext() using the specified parameters.
	// Begin a multicall collision search based on a smashee
	//
	void 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

	// Reset QuickCheckNext() using the specified paramters.
	void 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

	CSmash *GetNextSmash();	// Internal - used to aid in Next searches

	// 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 QuickCheckNext(										// Returns true if collision detected, false otherwise
		R3DLine*	pline,										// In:  Line segment to collide against.
		CSmash** pSmashee = 0,								// Out: Thing being smashed into if any (unless 0)
		CSmash*	pSmasher = 0);								// Out: Smash that should be excluded from search.

	// Returns true if collision detected, false otherwise
	// Out: Thing being smashed into if any (unless 0)	
	bool QuickCheckNext(CSmash** pSmashee = 0);	

	// Determine whether specified CSmash is colliding with anything, and
	// if so, (optionally) return the first thing it's colliding with.  If
	// you want to know about all things being collided with or otherwise
	// want more control, use a CSmashatorium::Finder object.
	bool 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** pSmashee = 0);								// Out: Thing being smashed into if any (unless 0)

	bool 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** pSmashee);

	// Determine whether specified R3DLine is colliding with anything, and
	// if so, (optionally) return the first thing it's colliding with.  If
	// you want to know about all things being collided with or otherwise
	// want more control, use a CSmashatorium::Finder object.
	bool 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** pSmashee = 0,								// Out: Thing being smashed into if any (unless 0)
		CSmash*	pSmasher = 0);								// Out: Smash that should be excluded from search.

	// Determine whether the two smashes have collided.  This is based purely on
	// the distance between them and their radius so it doesn't have to check
	// the grid at all.  
	bool QuickCheck(											// Returns true if collision detected, false otherwise
		CSmash* pSmasher,										// In:  One input smash
		CSmash* pSmashee)										// In:  Second input smash
	{
		return (ABS2(pSmasher->m_sphere.sphere.X - pSmashee->m_sphere.sphere.X,
		         pSmasher->m_sphere.sphere.Y - pSmashee->m_sphere.sphere.Y,
					pSmasher->m_sphere.sphere.Z - pSmashee->m_sphere.sphere.Z) <
					((pSmasher->m_sphere.sphere.lRadius + pSmashee->m_sphere.sphere.lRadius) *
					 (pSmasher->m_sphere.sphere.lRadius + pSmashee->m_sphere.sphere.lRadius)));
	}

	// Determine whether specified R3DLine is colliding with anything, and
	// if so, (optionally) return the closest (from start of segment) thing
	// it's colliding with.  If you want to know about all things being 
	// collided with or otherwise want more control, use a 
	// CSmashatorium::Finder object.
	bool 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** pSmashee,									// Out: Thing being smashed into if any.
		CSmash*	pSmasher = 0);								// Out: Smash that should be excluded from search.

	// Does a 2d XZ collision between two spheres.
	int16_t	CollideCyl(CSmash* pSmashee,RSphere* pSphere);

	// Does a 2d XZ collision between a sphere and a line
	int16_t	CollideCyl(CSmash* pSmashee,R3DLine* pLine);

	//---------------------------------------------------------------------------
	CSmashatorium() { Erase(); } // Needed for defaul construction

	void	Debug();		// Used for a plethora of things.

	// 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 Reset(void); 

	CSmashatorium(int16_t sWorldW,int16_t sWorldH,int16_t sTileW,int16_t sTileH) 
		{ Erase(); Alloc(sWorldW,sWorldH,sTileW,sTileH); }
	~CSmashatorium() { Destroy(); }
	};

//-------------------------------------------------
#endif


		
	/* This update saves search space, but is too slow on update
	void Update(CSmash* pSmash)	// In:  CSmash to be updated
		{
		ASSERT(pSmash);

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

		long	x1,x2,y1,y2;
		x1 = x2 = pSphere->X;
		y1 = y2 = pSphere->Z;
		x1 -= lR;
		x2 += lR;
		y1 -= lR;
		y2 += lR;

		///////////////////////////////////
		// (2) Catch the case of clipping:
		if ( (x1 < 0) || (y1 < 0) || (x2 >= m_sWorldW) || (y2 >= m_sWorldH) )
			{
			// Handle partial clipping
			ASSERT(0);
			return; // NYI!
			}
		///////////////////////////////////
		// No clip case
		// (3) process each of the four corners:
		CSmashatoriumList **pCurrent = NULL;

		//-------------------------------------------------------------
		pCurrent = m_ppslAccessY[y1] + m_psAccessX[x1];
		if (m_link1.m_pLast && (pCurrent != m_link1.m_pLast) )
			{
			////////////// Pull the old out of the list!
			Remove(m_link1.m_pLast);
			}
		m_link1.m_pLast = pCurrent; // NOT YET INSTALLED THOUGH!
		//-------------------------------------------------------------
		//-------------------------------------------------------------
		pCurrent = m_ppslAccessY[y1] + m_psAccessX[x2];
		if (m_link2.m_pLast && (pCurrent != m_link2.m_pLast) )
			{
			////////////// Pull the old out of the list!
			Remove(m_link2.m_pLast);
			}
		m_link2.m_pLast = pCurrent; // NOT YET INSTALLED THOUGH!
		//-------------------------------------------------------------
		//-------------------------------------------------------------
		pCurrent = m_ppslAccessY[y2] + m_psAccessX[x1];
		if (m_link3.m_pLast && (pCurrent != m_link3.m_pLast) )
			{
			////////////// Pull the old out of the list!
			Remove(m_link3.m_pLast); 
			}
		m_link3.m_pLast = pCurrent; // NOT YET INSTALLED THOUGH!
		//-------------------------------------------------------------
		//-------------------------------------------------------------
		pCurrent = m_ppslAccessY[y2] + m_psAccessX[x2];
		if (m_link4.m_pLast && (pCurrent != m_link4.m_pLast) )
			{
			////////////// Pull the old out of the list!
			Remove(m_link4.m_pLast);
			}
		m_link4.m_pLast = pCurrent; // NOT YET INSTALLED THOUGH!
		//-------------------------------------------------------------

		//**************************************************
		// there is an error here! There could have been a 
		// quadrant which did not CHANGE (so was not removed), 
		// but is now REDUNDANT.  Think about combining these 
		// sections!
		//**************************************************

		// (4) Check for redundancies before installing:
		// USE a NULL pointer to signify redundancy
		if (m_link2.m_pLast == m_link3.m_pLast)
			{
			// all four in same region, 3 redundant:
			m_link2.m_pLast = m_link3.m_pLast = m_link4.m_pLast = NULL;
			}
		else if (m_link1.m_pLast == m_link3.m_pLast)
			{
			// vertically redundant:
			m_link3.m_pLast = m_link4.m_pLast = NULL;
			}
		else if (m_link1.m_pLast == m_link2.m_pLast)
			{
			// horizontally redundant:
			m_link2.m_pLast = m_link4.m_pLast = NULL;
			}

		// Link them all in if not redundant
		if (m_link1.m_pLast) Add(m_link1.m_pLast,&m_link1);
		if (m_link2.m_pLast) Add(m_link2.m_pLast,&m_link1);
		if (m_link3.m_pLast) Add(m_link3.m_pLast,&m_link1);
		if (m_link4.m_pLast) Add(m_link4.m_pLast,&m_link1);
		}
	*/
