// Collider.cpp: implementation of the cColliderPair and cCollider class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "critter.h"
#include "collider.h"
#include "biota.h"


IMPLEMENT_DYNCREATE(cColliderPair, CObject);
#ifndef USECOLLIDERLIST
IMPLEMENT_DYNCREATE(cCollider, CObArray);
#else //USECOLLIDERLIST
IMPLEMENT_DYNCREATE(cCollider, CObList); 
#endif

const int cCollider::DONTCOLLIDE = 2;
/* I pick the next three numbers so they are symmetric for the ASSERT call in smartAdd */
const int cCollider::COLLIDEASCALLER = 1;
const int cCollider::COLLIDEASARG = -1;
const int cCollider::COLLIDEEITHERWAY = 0;
Real cCollider::CP_CRITTER = 100.0;
Real cCollider::CP_PLAYER = 200.0;
Real cCollider::CP_SILVERBULLET = 300.0;
Real cCollider::CP_BULLET = 400.0;
Real cCollider::CP_WALL = 500.0;
Real cCollider::CP_MAXIMUM = 1000000.0;

//===========================cColliderPair=============================

inline BOOL cColliderPair::collideThePair()
{
	ASSERT (_pcrittercaller && _pcritterarg); //Is skipped in RELEASE build.
	return _pcrittercaller->collide(_pcritterarg);
}

//===========================cCollider=============================
cCollider::cCollider()
{
#ifndef USECOLLIDERLIST
	SetSize(0,50);
 /* Use the inherited CArray constructor, but also call SetSize
		with something hefty in the nGrowBy parameter, to set the nGrowBy so that the array
		allocation grows in	big chunks to smooth the walk throughs. */
#endif  //USECOLLIDERLIST
//Don't need to do anything extra in the CList case.
}

void cCollider::smartAdd(cCritter *pcritter, cCritter* pcritterother)
{
	int collideswith = pcritter->collidesWith(pcritterother);
	int othercollideswith = pcritterother->collidesWith(pcritter);
	if (collideswith == cCollider::DONTCOLLIDE || othercollideswith == cCollider::DONTCOLLIDE)
		return; //Don't collide if either one is unwilling, even if the other was willing. */
#ifndef USECOLLIDERLIST
	if (collideswith == cCollider::COLLIDEASCALLER || collideswith == cCollider::COLLIDEEITHERWAY )
		Add(new cColliderPair(pcritter, pcritterother));
	else //(collideswith == cCollider::COLLIDEASARG)
		Add(new cColliderPair(pcritterother, pcritter));
#else //USECOLLIDERLIST
	if (collideswith == cCollider::COLLIDEASCALLER || collideswith == cCollider::COLLIDEEITHERWAY )
		AddTail(new cColliderPair(pcritter, pcritterother));
	else //(collideswith == cCollider::COLLIDEASARG)
		AddTail(new cColliderPair(pcritterother, pcritter));
#endif //USECOLLIDERLIST

	//ASSERT(collideswith == -othercollideswith);
		/* We chose the collision type codes to make this ASSERT
		likely at this point, but it might not always be true.  Only comment it in
		for testing. Do note that we bail before we hit it if either type is DONTCOLLIDE. */
}

void cCollider::RemoveAll()
{
#ifndef USECOLLIDERLIST
	for( int i=0; i<GetSize(); i++)
	{
	    delete GetAt(i);
		ElementAt(i) = NULL;
	}
	CObArray::RemoveAll();
#else //USECOLLIDERLIST
	POSITION pos = GetHeadPosition();
	while(pos != NULL)
		delete GetNext(pos);
	CObList::RemoveAll();
#endif //USECOLLIDERLIST
}

void cCollider::removeReferencesTo(cCritter *pdeadcritter)
{
#ifndef USECOLLIDERLIST
	int increment = 1;
	for (int i=0; i<GetSize(); i+= increment)
	{
		if(GetAt(i)->involves(pdeadcritter))
		{
			delete GetAt(i);
			RemoveAt(i);
			increment = 0; /* So you hit this slot twice in case you just deleted it and slid
				the next one down. */
		}
		else
			increment = 1;
	}
#else //USECOLLIDERLIST
	POSITION currpos, pos = GetHeadPosition();
	while(pos != NULL)
	{
		currpos = pos; //The position of ppair, in case we need to delete it.
		cColliderPair *ppair = GetNext(pos); //Bumps pos to next position.
		if(ppair->involves(pdeadcritter))
		{
			delete ppair;
			RemoveAt(currpos);
		}
	}
#endif //USECOLLIDERLIST
}

void cCollider::build(cBiota *pbiota)
{
	RemoveAll(); //Clear it out.
	for( int i=0; i<pbiota->GetSize(); i++)
		for( int j=i+1; j<pbiota->GetSize(); j++)
			smartAdd(pbiota->GetAt(i), pbiota->GetAt(j));
}

void cCollider::iterateCollide()
{
#ifndef USECOLLIDERLIST
	for (int i=0; i< GetSize(); i++)
		GetAt(i)->collideThePair(); //Use the cColliderPair call.
#else //USECOLLIDERLIST
	POSITION pos = GetHeadPosition();
	while(pos != NULL)
		 GetNext(pos)->collideThePair();
#endif //USECOLLIDERLIST
}
