/*
**	Command & Conquer Generals Zero Hour(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	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, see <http://www.gnu.org/licenses/>.
*/

////////////////////////////////////////////////////////////////////////////////
//																																						//
//  (c) 2001-2003 Electronic Arts Inc.																				//
//																																						//
////////////////////////////////////////////////////////////////////////////////

// FILE: PartitionManager.h //////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//                                                                          
//                       Westwood Studios Pacific.                          
//                                                                          
//                       Confidential Information					         
//                Copyright (C) 2001 - All Rights Reserved                  
//                                                                          
//-----------------------------------------------------------------------------
//
// Project:    RTS3
//
// File name:  PartitionManager.h
//
// Created:    Steven Johnson, September 2001
//
// Desc:       Partition management, this system will allow us to partition the
//						 objects in space, iterate objects in specified volumes, 
//						 regions, by types and other properties.
//
//-----------------------------------------------------------------------------

#pragma once

#ifndef __PARTITIONMANAGER_H_
#define __PARTITIONMANAGER_H_

//-----------------------------------------------------------------------------
//           Includes                                                      
//-----------------------------------------------------------------------------
#include "Common/GameCommon.h"	// ensure we get DUMP_PERF_STATS, or not
#include "GameLogic/ObjectIter.h"
#include "Common/ObjectStatusTypes.h"
#include "Common/KindOf.h"
#include "Common/Snapshot.h"
#include "Common/Geometry.h"
#include "GameClient/Display.h"	// for ShroudLevel

//-----------------------------------------------------------------------------
//           defines                                                      
//-----------------------------------------------------------------------------

/*
	Note, this isn't very accurate currently, so use with caution. (srj)
*/
#define PM_CACHE_TERRAIN_HEIGHT

/*
	5% speed improvement, at cost of more memory.
*/
#define FASTER_GCO

const Real HUGE_DIST = 1000000.0f;

//-----------------------------------------------------------------------------
//           Type Definitions                                                      
//-----------------------------------------------------------------------------

struct Coord3D;

class CellAndObjectIntersection;
class Object;
class PartitionManager;
class PartitionData;
class PartitionFilter;
class PartitionCell;
class Player;
class PolygonTrigger;
class Squad;
class Team;
class ThingTemplate;
class GhostObject;
class CommandButton;

enum CommandSourceType;

// ----------------------------------------------------------------------------------------------
enum ValueOrThreat
{
	VOT_CashValue = 1,
	VOT_ThreatValue,
	VOT_NumItems
};

// ----------------------------------------------------------------------------------------------
enum FindPositionFlags
{
	FPF_NONE															= 0x00000000,		// no options, default behavior
	FPF_IGNORE_WATER											= 0x00000001,		// a position found underwater is ok
	FPF_WATER_ONLY												= 0x00000002,		// find positions in the water only
	FPF_IGNORE_ALL_OBJECTS								= 0x00000004,		// ignore all objects, positions inside objects are ok
	FPF_IGNORE_ALLY_OR_NEUTRAL_UNITS			= 0x00000008,		// ignore friendly units (requires relationshipObject)
	FPF_IGNORE_ALLY_OR_NEUTRAL_STRUCTURES	= 0x00000010,		// ignore friendly structures (requires relationshipObject)
	FPF_IGNORE_ENEMY_UNITS								= 0x00000020,		// ignore enemy units (requires relationshipObject)
	FPF_IGNORE_ENEMY_STRUCTURES						= 0x00000040,		// ignore enemy structures (requires relationshipObject)
	FPF_USE_HIGHEST_LAYER									= 0x00000080,		// examine pos on highest layer at given xy (rather than on ground layer)
	FPF_CLEAR_CELLS_ONLY									= 0x00000100,		// Reject anything that is not PathFindCell::Clear
};
// ----------------------------------------------------------------------------------------------

const Real RANDOM_START_ANGLE = -99999.9f;			///< no start angle (an unlikely number to use for the start angle)

struct FindPositionOptions
{
	FindPositionOptions( void )
	{
		flags									= FPF_NONE;
		minRadius							= 0.0f;
		maxRadius							= 0.0f;
		startAngle						= RANDOM_START_ANGLE;
		maxZDelta							= 1e10f;	// ie, any z delta.
		ignoreObject					= NULL;
		sourceToPathToDest    = NULL;
		relationshipObject		= NULL;
	};
	FindPositionFlags flags;					///< flags for finding the legal position
	Real minRadius;										///< min radius to search around
	Real maxRadius;										///< max radius to search around
	Real startAngle;									///< use this angle to start the search at
	Real maxZDelta;										///< maximum delta-z we will allow
	const Object *ignoreObject;				///< ignore this object in legal position checks
	const Object *sourceToPathToDest;	///< object that must be able to path to the position chosen
	const Object *relationshipObject;	///< object to use for relationship tests
};

//=====================================
/** */
//=====================================
enum DistanceCalculationType
{
	FROM_CENTER_2D					= 0,	///< measure from Object center in 2d.
	FROM_CENTER_3D					= 1,	///< measure from Object center in 3d.
	FROM_BOUNDINGSPHERE_2D	= 2,	///< measure from Object bounding sphere in 2d.
	FROM_BOUNDINGSPHERE_3D	= 3		///< measure from Object bounding sphere in 3d.
};

//=====================================
/**
	a Plain Old Data structure that is used to get optional results from collidesWith().
*/
struct CollideLocAndNormal
{
	Coord3D loc;
	Coord3D normal;
};

//=====================================
/** 
	PartitionContactList is a utility class used by the Partition Manager
	to hold potential collisions as it updates objects in the partition.
	It stores pairs of potentially-colliding objects (eliminating duplicates)
	for processing after all partitions are updated.
*/
//=====================================
class PartitionContactList;


//=====================================
/** 
	This class (often called COI for short) is the abstraction
	of the intersection between an Object and a Partition Cell.
	For every Cell that an Object's geometry touches, even partially,
	we allocate a COI. This allows us to maintain an efficient two-way
	list, such that for every Object, we know the Cells that it touches;
	and, for every Cell, we know the Objects that touch it.
*/
//=====================================
class CellAndObjectIntersection	// not MPO: we allocate these in arrays
{
private:
	PartitionCell							*m_cell;									///< the cell being touched
	PartitionData							*m_module;								///< the module (and thus, Object) touching
	CellAndObjectIntersection *m_prevCoi, *m_nextCoi;		///< if in use, next/prev in this cell. if not in use, next/prev free in this module.

public:

	// Note, we allocate these in arrays, thus we must have a default ctor (and NOT descend from MPO)
	CellAndObjectIntersection();
	~CellAndObjectIntersection();

	/**
		make 'this' refer to the specified cell and module. Normally, this
		involves updated the member variables and adding 'this' to the Cell's
		list of COIs.
	*/
	void addCoverage(PartitionCell *cell, PartitionData *module);
	
	/**
		make 'this' refer to nothing at all. this involves resetting the member
		variables to null, and removing 'this' from the Cell's list of COI's.
	*/
	void removeAllCoverage();

	/**
		return the Cell for this COI (null if the COI is not in use)
	*/
	inline PartitionCell *getCell() { return m_cell; }

	/**
		return the Module for this COI (null if the COI is not in use)
	*/
	inline PartitionData *getModule() { return m_module; }

	/**
		return the previous COI in the Cell's list of COIs.
	*/
	inline CellAndObjectIntersection *getPrevCoi() { return m_prevCoi; }

	/**
		return the next COI in the Cell's list of COIs.
	*/
	inline CellAndObjectIntersection *getNextCoi() { return m_nextCoi; }

	// only for use by PartitionCell.
	void friend_addToCellList(CellAndObjectIntersection **pListHead);
	void friend_removeFromCellList(CellAndObjectIntersection **pListHead);
};

/**
	This class encapsulates one area interaction with PartitionCells.  The user decides what to do with it.
*/
class SightingInfo : public MemoryPoolObject, public Snapshot
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( SightingInfo, "SightingInfo" );

public:

	SightingInfo();
	void reset();
	Bool isInvalid() const;

	Coord3D					m_where;
	Real						m_howFar;
	PlayerMaskType	m_forWhom;	// ask not for whom the sighting is masked; it masks for thee
	
	UnsignedInt			m_data;			// Threat and value use as the value.  Sighting uses it for a Timestamp

protected:

	// snapshot method
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

};

//=====================================
/**
	We sometimes need to save whether or not an area was fogged or permanently revealed through a 
	script. This helps us do so.
*/
//=====================================
enum
{
	STORE_DONTTOUCH = 0,
	STORE_FOG = 1,
	STORE_PERMANENTLY_REVEALED = 2
};

struct ShroudStatusStoreRestore
{
	std::vector<UnsignedByte> m_foggedOrRevealed[MAX_PLAYER_COUNT];
	Int m_cellsWide;	// m_cellsHigh is computed by m_foggedOrRevealed[0].size() / m_cellsWide 
};

//=====================================
/** 
	The world's terrain is partitioned into a large grid of Partition Cells.
	The Cell is the fundamental unit of space in the Partition Manager.
*/
//=====================================
class PartitionCell : public Snapshot	// not MPO: allocated in an array
{
private:
	CellAndObjectIntersection*		m_firstCoiInCell;	///< list of COIs in this cell (may be null).
	ShroudLevel										m_shroudLevel[MAX_PLAYER_COUNT];	
#ifdef PM_CACHE_TERRAIN_HEIGHT
	Real													m_loTerrainZ;			///< lowest terrain-pt in this cell
	Real													m_hiTerrainZ;			///< highest terrain-pt in this cell
#endif
	Int														m_threatValue[MAX_PLAYER_COUNT];
	Int														m_cashValue[MAX_PLAYER_COUNT];
	Short													m_coiCount;					///< number of COIs in this cell.
	Short													m_cellX;						///< x-coord of this cell within the Partition Mgr coords (NOT in world coords)
	Short													m_cellY;						///< y-coord of this cell within the Partition Mgr coords (NOT in world coords)

public:

	// Note, we allocate these in arrays, thus we must have a default ctor (and NOT descend from MPO)
	PartitionCell();
#ifdef PM_CACHE_TERRAIN_HEIGHT
	void init(Int x, Int y, Real loZ, Real hiZ) { m_cellX = x; m_cellY = y; m_loTerrainZ = loZ; m_hiTerrainZ = hiZ; }
#else
	void init(Int x, Int y) { m_cellX = x; m_cellY = y; }
#endif
	~PartitionCell();

	// --------------- inherited from Snapshot interface --------------
	void crc( Xfer *xfer );
	void xfer( Xfer *xfer );
	void loadPostProcess( void );

	Int getCoiCount() const { return m_coiCount; }		///< return number of COIs touching this cell.
	Int getCellX() const { return m_cellX; }
	Int getCellY() const { return m_cellY; }

	void addLooker( Int playerIndex );
	void removeLooker( Int playerIndex );
	void addShrouder( Int playerIndex );
	void removeShrouder( Int playerIndex );
	CellShroudStatus getShroudStatusForPlayer( Int playerIndex ) const;

	// @todo: All of these are inline candidates
	UnsignedInt getThreatValue( Int playerIndex );
	void addThreatValue( Int playerIndex, UnsignedInt threatValue );
	void removeThreatValue( Int playerIndex, UnsignedInt threatValue );

	UnsignedInt getCashValue( Int playerIndex );
	void addCashValue( Int playerIndex, UnsignedInt cashValue );
	void removeCashValue( Int playerIndex, UnsignedInt cashValue );

	void invalidateShroudedStatusForAllCois(Int playerIndex);

#ifdef PM_CACHE_TERRAIN_HEIGHT
	inline Real getLoTerrain() const { return m_loTerrainZ; }
	inline Real getHiTerrain() const { return m_hiTerrainZ; }
#endif

	void getCellCenterPos(Real& x, Real& y);

	inline CellAndObjectIntersection *getFirstCoiInCell() { return m_firstCoiInCell; }

	#ifdef _DEBUG
	void validateCoiList();
	#endif

	// intended only for CellAndObjectIntersection.
	void friend_addToCellList(CellAndObjectIntersection *coi);

	// intended only for CellAndObjectIntersection.
	void friend_removeFromCellList(CellAndObjectIntersection *coi);
};

//=====================================
/** 
	A PartitionData is the part of an Object that understands
	how to maintain the Object in the space partitioning system.
*/
//=====================================
class PartitionData : public MemoryPoolObject
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(PartitionData, "PartitionDataPool" )		

private:

	enum DirtyStatus
	{
		NOT_DIRTY,
		NEED_COLLISION_CHECK,
		NEED_CELL_UPDATE_AND_COLLISION_CHECK
	};

	Object											*m_object;								///< Object this module is for
	GhostObject										*m_ghostObject;							///< Temporary object used when real object is gone but player thinks it's there because of fog.
	PartitionData								*m_next;									///< next module in master list
	PartitionData								*m_prev;									///< prev module in master list
	PartitionData								*m_nextDirty;
	PartitionData								*m_prevDirty;

	Int													m_coiArrayCount;					///< number of COIs allocated (may be more than are in use)
	Int													m_coiInUseCount;					///< number of COIs that are actually in use
	CellAndObjectIntersection		*m_coiArray;							///< The array of COIs 
	Int													m_doneFlag;
	DirtyStatus									m_dirtyStatus;
	ObjectShroudStatus					m_shroudedness[MAX_PLAYER_COUNT];						
	ObjectShroudStatus					m_shroudednessPrevious[MAX_PLAYER_COUNT];	///<previous frames value of m_shroudedness						
	Bool												m_everSeenByPlayer[MAX_PLAYER_COUNT];		///<whether this object has ever been seen by a given player.
	const PartitionCell					*m_lastCell;							///< The last cell I thought my center was in.
	
	/**
		Given a shape's geometry and size parameters, calculate the maximum number of COIs
		that the object could possibly occupy.
	*/
	Int calcMaxCoiForShape(GeometryType geom, Real majorRadius, Real minorRadius, Bool isSmall);

	/**
		Given an object's geometry and size parameters, calculate the maximum number of COIs
		that the object could possibly occupy. (This simply extracts the information from the object
		and calls calcMaxCoiForShape.)
	*/
	Int calcMaxCoiForObject();

	/**
		allocate the array of COIs (m_coiArray) for this module.
	*/
	void allocCoiArray();

	/** 
		free the array of COIs (m_coiArray) for this module (if any).
	*/
	void freeCoiArray();

	/**
			marks self as touching no cells. (and any previous cells as not touching self!)
	*/
	void removeAllTouchedCells();									

	/**
		this discards all current 'touch' information (via removeAllTouchedCells) and recalculates
		the cells touched by this module, based on the object's geometry. this will be called frequently and so
		needs to be as efficient as possible.
	*/
	void updateCellsTouched();

	/**
		If you imagine the array of Partition Cells as pixels, then this method
		'sets' the pixel [cell] at cell coordinate (x, y).
	*/
	void addSubPixToCoverage(PartitionCell *cell);

	/**
		fill in the pixels covered by the given 'small' shape with the given
		center and radius. 'small' shapes are special in that they
		are always assumed to cover at most 4 Cells, and so we can use
		a more efficient special-purpose filler, rather than a general
		rasterizer.
	*/
	void doSmallFill(
		Real centerX,
		Real centerY,
		Real radius
	);

	/// helper function for doCircleFill.
	void hLineCircle(Int x1, Int x2, Int y);

	/**
		fill in the pixels covered by the given circular shape with the given
		center and radius. Note that this is used for both spheres and cylinders.
	*/
	void doCircleFill(
		Real centerX,
		Real centerY,
		Real radius
	);

	/**
		fill in the pixels covered by the given rectangular shape with the given
		center, dimensions, and rotation.
	*/
	void doRectFill(
		Real centerX,
		Real centerY,
		Real halfsizeX,
		Real halfsizeY,
		Real angle
	);

	/**
		do a careful test of the geometries of 'this' and 'that', and return
		true iff the geometries touch (or intersect). if true is returned,
		loc will be filled in to the collision location, and normal will be
		filled in to the normal to the surface of 'this' at the collide location.
	*/
	Bool collidesWith(const PartitionData *that, CollideLocAndNormal *cinfo) const;

public:

	PartitionData();

	void attachToObject( Object* object );
	void detachFromObject( void );
	void attachToGhostObject(GhostObject* object);
	void detachFromGhostObject(void);

	void setNext( PartitionData *next ) { m_next = next; }			///< set next pointer
	PartitionData *getNext( void ) { return m_next; }						///< get the next pointer
	void setPrev( PartitionData *prev ) { m_prev = prev; }			///< set the prev pointer
	PartitionData *getPrev( void ) { return m_prev; }						///< get the prev pointer

	/// mark the given module as being "dirty", needing recalcing during next update phase.
	// if needToUpdateCells is true, we'll recalc the partition cells it touches and do collision testing.
	// if needToUpdateCells is false, we'll just do the collision testing.
	void makeDirty(Bool needToUpdateCells);

	Bool isInNeedOfUpdatingCells() const { return m_dirtyStatus == NEED_CELL_UPDATE_AND_COLLISION_CHECK; }
	Bool isInNeedOfCollisionCheck() const { return m_dirtyStatus != NOT_DIRTY; }

	void invalidateShroudedStatusForPlayer(Int playerIndex);
	void invalidateShroudedStatusForAllPlayers();

	ObjectShroudStatus getShroudedStatus(Int playerIndex);

	inline Int wasSeenByAnyPlayers() const	///<check if a player in the game has seen the object but is now looking at fogged version.
	{	
		for (Int i=0; i<MAX_PLAYER_COUNT; i++)
			if (m_everSeenByPlayer[i] && m_shroudedness[i] == OBJECTSHROUD_FOGGED)
				return i;
		return i;
	}


	Int getControllingPlayerIndex() const;

	/**
		enumerate the objects that share space with 'this' 
		(ie, the objects in the same Partition Cells) and
		add 'em to the given contact list. also, if self
		is intersecting the ground, add it to the list as a possible
		collide-with-ground.
	*/
	void addPossibleCollisions(PartitionContactList *ctList);

	Object *getObject() { return m_object; }				///< return the Object that owns this module
	const Object *getObject() const { return m_object; }				///< return the Object that owns this module
	void friend_setObject(Object *object) { m_object = object;}	///< to be used only by the partition manager.
	GhostObject *getGhostObject() const { return m_ghostObject; }	///< return the ghost object that serves as fogged memory of object.
	void friend_setGhostObject(GhostObject *object) {m_ghostObject=object;}	///<used by ghost object manager to free link to partition data.
	void friend_setShroudednessPrevious(Int playerIndex,ObjectShroudStatus status); ///<only used to restore state after map border resizing and/or xfer!
	ObjectShroudStatus friend_getShroudednessPrevious(Int playerIndex) {return m_shroudednessPrevious[playerIndex];}
	
	void friend_removeAllTouchedCells() { removeAllTouchedCells(); }	///< this is only for use by PartitionManager
	void friend_updateCellsTouched()	{ updateCellsTouched(); } ///< this is only for use by PartitionManager
	Int friend_getCoiInUseCount() { return m_coiInUseCount; } ///< this is only for use by PartitionManager
	Bool friend_collidesWith(const PartitionData *that, CollideLocAndNormal *cinfo) const { return collidesWith(that, cinfo); }	///< this is only for use by PartitionContactList

	// these are only for use by getClosestObjects.
	// (note, if we ever use other bits in this, smarten this up...)
	Int friend_getDoneFlag() { return m_doneFlag; }
	void friend_setDoneFlag(Int i) { m_doneFlag = i; }

	inline Bool isInListDirtyModules(PartitionData* const* pListHead) const
	{
		Bool result = (*pListHead == this || m_prevDirty || m_nextDirty);
		DEBUG_ASSERTCRASH(result == (m_dirtyStatus != NOT_DIRTY), ("dirty flag mismatch"));
		return result;
	}
	inline void prependToDirtyModules(PartitionData** pListHead)
	{
		DEBUG_ASSERTCRASH((m_dirtyStatus != NOT_DIRTY), ("dirty flag mismatch"));
		m_nextDirty = *pListHead;
		if (*pListHead)
			(*pListHead)->m_prevDirty = this;
		*pListHead = this;
	}
	void removeFromDirtyModules(PartitionData** pListHead)
	{
		m_dirtyStatus = NOT_DIRTY;
		if (m_nextDirty)
			m_nextDirty->m_prevDirty = m_prevDirty;
		if (m_prevDirty)
			m_prevDirty->m_nextDirty = m_nextDirty;
		else
			*pListHead = m_nextDirty;
		m_prevDirty = 0;
		m_nextDirty = 0;
	}

};

//=====================================
/**
	this is an ABC. PartitionData::iterate allows you to pass multiple filters
	to filter out objects you don't want. it calls this method on the objects
	in question; any that get false returned, are not iterated. Note that
	either modMain or posMain will be null, but not both: the filter must be aware
	of this, and respond appropriately. (ie, the filter may be used for filtering
	against an object, or against a position.)
*/
class PartitionFilter
{
public:
	virtual Bool allow(Object *objOther) = 0;
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() = 0;
#endif
};

//=====================================
/** 
	Reject any objects that aren't currently flying.
*/
class PartitionFilterIsFlying : public PartitionFilter
{
public:
	PartitionFilterIsFlying() { }
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterIsFlying"; }
#endif
};

//=====================================
class PartitionFilterWouldCollide : public PartitionFilter
{
private:
	Coord3D m_position;
	GeometryInfo m_geom;
	Real m_angle;
  Bool m_desiredCollisionResult;  // collision must match this for allow to return true
public:
	PartitionFilterWouldCollide(const Coord3D& pos, const GeometryInfo& geom, Real angle, Bool desired);
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterWouldCollide"; }
#endif
};

//=====================================
/** 
	Reject any objects that aren't controlled by the same player.
*/
class PartitionFilterSamePlayer : public PartitionFilter
{
private:
	const Player *m_player;
public:
	PartitionFilterSamePlayer(const Player *player) : m_player(player) { }
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterSamePlayer"; }
#endif
};

//=====================================
/** 
	Reject any objects that don't match the alliance
	affiliations compared with 'm_obj'. You 
	may reject objects that are allied, neutral,
	or enemy with respect to it.
*/
class PartitionFilterRelationship : public PartitionFilter
{
private:
	const Object *m_obj;
	Int m_flags;
public:
	enum RelationshipAllowTypes
	{
		ALLOW_ALLIES					= (1<<ALLIES),		///< allow objects that m_obj considers allies
		ALLOW_ENEMIES					= (1<<ENEMIES),		///< allow objects that m_obj considers enemy 
		ALLOW_NEUTRAL					= (1<<NEUTRAL)		///< allow objects that m_obj considers neutral
	};
	PartitionFilterRelationship(const Object *obj, Int flags) : m_obj(obj), m_flags(flags) { }
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterRelationship"; }
#endif
};

//=====================================
/**
	Reject any objects that aren't on the specific 
	team.
*/
class PartitionFilterAcceptOnTeam : public PartitionFilter
{
private:
	const Team *m_team;
public:
	PartitionFilterAcceptOnTeam(const Team *team);
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterAcceptOnTeam"; }
#endif
};

//=====================================
/**
	Reject any objects that aren't on the specific 
	squad.
*/
class PartitionFilterAcceptOnSquad : public PartitionFilter
{
private:
	const Squad *m_squad;
public:
	PartitionFilterAcceptOnSquad(const Squad *squad);
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterAcceptOnSquad"; }
#endif
};

//=====================================
/** 
	Reject any objects that are not within clear line-of-sight
	of a given object. "Line of sight" takes into account
	terrain (ie, no hills between 'em) but does not
	yet take into account structures. Note that this
	requires more computation that other filters, so
	this should generally put toward the end of the filter
	list so that simpler filters can reject other (simpler)
	cases earlier.
*/
class PartitionFilterLineOfSight : public PartitionFilter
{
private:
	const Object *m_obj;
public:
	PartitionFilterLineOfSight(const Object *obj);
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterLineOfSight"; }
#endif
};

//=====================================
/**
	Only objects that Can Possibly be attacked by the given object
*/
class PartitionFilterPossibleToAttack : public PartitionFilter
{
private:
	const Object *m_obj;
	CommandSourceType m_commandSource;
	AbleToAttackType m_attackType;
public:
	PartitionFilterPossibleToAttack(AbleToAttackType t, const Object *obj, CommandSourceType commandSource);
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterPossibleToAttack"; }
#endif
};

//=====================================
/**
	Only objects that Can Possibly be entered by the given object
*/
class PartitionFilterPossibleToEnter : public PartitionFilter
{
private:
	const Object *m_obj;
	CommandSourceType m_commandSource;

public:
	PartitionFilterPossibleToEnter(const Object *obj, CommandSourceType commandSource);
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterPossibleToEnter"; }
#endif
};

//=====================================
/**
	Only objects that Can Possibly be hijacked by the given object
*/
class PartitionFilterPossibleToHijack : public PartitionFilter
{
private:
	const Object *m_obj;
	CommandSourceType m_commandSource;

public:
	PartitionFilterPossibleToHijack(const Object *obj, CommandSourceType commandSource);
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterPossibleToHijack"; }
#endif
};

//=====================================
/**
 * Accept only the last object who attacked me. Very fast.
 */
class PartitionFilterLastAttackedBy : public PartitionFilter
{
private:
	ObjectID m_lastAttackedBy;
public:
	PartitionFilterLastAttackedBy(Object *obj);
	virtual Bool allow(Object *other);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterLastAttackedBy"; }
#endif
};

//=====================================
/** 
	Only objects that match the given masks are accepted.
*/
class PartitionFilterAcceptByObjectStatus : public PartitionFilter
{
private:
	ObjectStatusMaskType m_mustBeSet, m_mustBeClear;
public:
	PartitionFilterAcceptByObjectStatus( ObjectStatusMaskType mustBeSet, ObjectStatusMaskType mustBeClear) : m_mustBeSet(mustBeSet), m_mustBeClear(mustBeClear) { }
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterAcceptByObjectStatus"; }
#endif
};

//=====================================
/** 
	Just like PartitionFilterAcceptByObjectStatus, except that objects
	that match the given masks are REJECTED.
*/
class PartitionFilterRejectByObjectStatus : public PartitionFilter
{
private:
	ObjectStatusMaskType m_mustBeSet, m_mustBeClear;
public:
	PartitionFilterRejectByObjectStatus( ObjectStatusMaskType mustBeSet, ObjectStatusMaskType mustBeClear ) 
		: m_mustBeSet(mustBeSet), m_mustBeClear(mustBeClear) 
	{ 
	}
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterRejectByObjectStatus"; }
#endif
};

//=====================================
/** 
	Objects that are stealthed and not detected or disguised are accepted or rejected based on allow bool.
*/
class PartitionFilterStealthedAndUndetected : public PartitionFilter
{
private:
	const Object *m_obj;
	Bool m_allow;
public:
	PartitionFilterStealthedAndUndetected( const Object *obj, Bool allow ) { m_obj = obj; m_allow = allow; } 
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterStealthedAndUndetected"; }
#endif
};

//=====================================
/** 
	Only objects that match the given masks are accepted.
*/
class PartitionFilterAcceptByKindOf : public PartitionFilter
{
private:
	KindOfMaskType m_mustBeSet, m_mustBeClear;
public:
	PartitionFilterAcceptByKindOf(const KindOfMaskType& mustBeSet, const KindOfMaskType& mustBeClear) : m_mustBeSet(mustBeSet), m_mustBeClear(mustBeClear) { }
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterAcceptByKindOf"; }
#endif
};

//=====================================
/** 
	Just like PartitionFilterAcceptByKindOf, except that objects
	that match the given masks are REJECTED.
*/
class PartitionFilterRejectByKindOf : public PartitionFilter
{
private:
	KindOfMaskType m_mustBeSet, m_mustBeClear;
public:
	PartitionFilterRejectByKindOf(const KindOfMaskType& mustBeSet, const KindOfMaskType& mustBeClear) 
		: m_mustBeSet(mustBeSet), m_mustBeClear(mustBeClear) 
	{ 
	}
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterRejectByKindOf"; }
#endif
};

//=====================================
/** 
 * Reject any objects "behind" the given object.
 * This is a 3D check.
 */
class PartitionFilterRejectBehind: public PartitionFilter
{
private:
	Object *m_obj;
public:
	PartitionFilterRejectBehind( Object *obj );
	virtual Bool allow( Object *other );
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterRejectBehind"; }
#endif
};

//=====================================
/**
 * Allow only living victims
 */
class PartitionFilterAlive : public PartitionFilter
{
public:
	PartitionFilterAlive(void) { }
protected:
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterAlive"; }
#endif
};

//=====================================
/**
 * If obj is on the map, reject all off-map objects.
 * If obj is off the map, reject all on-map objects.
 */
class PartitionFilterSameMapStatus : public PartitionFilter
{
private:
	const Object *m_obj;
public:
	PartitionFilterSameMapStatus(const Object *obj) : m_obj(obj) { }
protected:
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterSameMapStatus"; }
#endif
};

//=====================================
/**
 * If obj is on the map, accept it.
 */
class PartitionFilterOnMap : public PartitionFilter
{
public:
	PartitionFilterOnMap() { }
protected:
	virtual Bool allow(Object *objOther);
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterOnMap"; }
#endif
};

//=====================================
/**
 * Reject buildings, unless they can attack, or 
 * we are the computer-controlled AI and the building
 * is owned by the enemy.
 */
class PartitionFilterRejectBuildings : public PartitionFilter
{
private:
	const Object *m_self;
	Bool m_acquireEnemies;
public:
	PartitionFilterRejectBuildings(const Object *o);
protected:
	virtual Bool allow( Object *other );
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterRejectBuildings"; }
#endif
};

//=====================================
/**
 * Accept/Reject Insignificant buildings
 * Note: This will allow things that 
 */
class PartitionFilterInsignificantBuildings : public PartitionFilter
{
private:
	Bool m_allowNonBuildings;
	Bool m_allowInsignificant;
public:
	PartitionFilterInsignificantBuildings(Bool allowNonBuildings, Bool allowInsignificant) : 
			m_allowNonBuildings(allowNonBuildings), m_allowInsignificant(allowInsignificant) {}
protected:
	virtual Bool allow( Object *other );
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterInsignificantBuildings"; }
#endif
};

//=====================================
/**
 * Accept if they are clear, not fogged or shrouded
 */
class PartitionFilterFreeOfFog : public PartitionFilter
{
private:
	Int m_comparisonIndex;
public:
	PartitionFilterFreeOfFog(Int toWhom) : 
			m_comparisonIndex(toWhom){}
protected:
	virtual Bool allow( Object *other );
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterFreeOfFog"; }
#endif
};

//=====================================
/**
 * Accept repulsor (enemies, or flagged as repulsor).
 */
class PartitionFilterRepulsor : public PartitionFilter
{
private:
	const Object *m_self;
public:
	PartitionFilterRepulsor(const Object *o) : m_self(o) { }
protected:
	virtual Bool allow( Object *other );
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterRepulsor"; }
#endif
};

//=====================================
/**
 * Reject all objects outside of the irregularly shaped area defined by the set of points
 * passed in at creation. This is done using the even-odd rule against the points, assuming
 * that the area is closed.
 */
class PartitionFilterIrregularArea : public PartitionFilter
{
private:
	Coord3D *m_area;
	Int m_numPointsInArea;

public:
	PartitionFilterIrregularArea(Coord3D* area, Int numPointsInArea) : m_area(area), m_numPointsInArea(numPointsInArea) {}
protected:
	virtual Bool allow( Object *other );
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterIrregularArea"; }
#endif
};

//=====================================
/**
 * Reject all objects inside the irregularly trigger area
 * passed in at creation. This is done using the even-odd rule against the points, assuming
 * that the area is closed.
 */
class PartitionFilterPolygonTrigger : public PartitionFilter
{
private:
	const PolygonTrigger *m_trigger;

public:
	PartitionFilterPolygonTrigger(const PolygonTrigger *trigger) : m_trigger(trigger) {}
protected:
	virtual Bool allow( Object *other );
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterPolygonTrigger"; }
#endif
};

//=====================================
/**
 * Reject all objects that aren't (or are) the player
 * passed in at creation. .
 */
class PartitionFilterPlayer : public PartitionFilter
{
private:
	const Player *m_player;
	Bool  m_match;

public:
	PartitionFilterPlayer(const Player *player, Bool match) : m_player(player), m_match(match) {}
protected:
	virtual Bool allow( Object *other );
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterPlayer"; }
#endif
};

//=====================================
/**
 * Allow or reject (based on match) all Objects whose affiliation matches one of those
 * specified by 
 */
class PartitionFilterPlayerAffiliation : public PartitionFilter
{
private:
	const Player *m_player;
	Bool  m_match;
	UnsignedInt m_affiliation;

public:
	// whichAffiliation should use AllowPlayerRelationship flags specified in PlayerList.h
	PartitionFilterPlayerAffiliation(const Player *player, UnsignedInt whichAffiliation, Bool match)
		: m_player(player), m_affiliation(whichAffiliation), m_match(match) 
	{
	}
protected:
	virtual Bool allow( Object *other );
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterPlayerAffiliation"; }
#endif
};

//=====================================
/**
 * Accept all objects that aren't (or are) the thing
 * passed in at creation. .
 */
class PartitionFilterThing : public PartitionFilter
{
private:
	const ThingTemplate *m_tThing;
	Bool  m_match;

public:
	PartitionFilterThing(const ThingTemplate *thing, Bool match) : m_tThing(thing), m_match(match) {}
protected:
	virtual Bool allow( Object *other );
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterThing"; }
#endif
};

//=====================================
/**
 * Accept all objects that can/cannot be garrisoned by anyone.
 */
class PartitionFilterGarrisonable : public PartitionFilter
{
private:
	Player *m_player;
	Bool  m_match;

public:
	PartitionFilterGarrisonable( Bool match ) : m_match(match) 
	{
		//Added By Sadullah Nader
		//Initializations 
		m_player = NULL;
		//
	}
protected:
	virtual Bool allow( Object *other );
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterGarrisonable"; }
#endif
};

//=====================================
/**
 * Accept all objects that can/cannot be garrisoned by source *Player*.
 */
class PartitionFilterGarrisonableByPlayer : public PartitionFilter
{
private:
	Player *m_player;
	Bool  m_match;
	CommandSourceType m_commandSource;

public:
	PartitionFilterGarrisonableByPlayer( Player *player, Bool match, CommandSourceType commandSource ):
			m_player(player), m_match(match), m_commandSource(commandSource) 
	{
	}
protected:
	virtual Bool allow( Object *other );
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterGarrisonableByPlayer"; }
#endif
};

//=====================================
/**
 * Accept all objects that are/n't unmanned. 
 */
class PartitionFilterUnmannedObject : public PartitionFilter
{
private:
	Bool  m_match;

public:
	PartitionFilterUnmannedObject( Bool match ) : m_match(match) {}
protected:
	virtual Bool allow( Object *other );
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterUnmannedObject"; }
#endif
};

//=====================================
/**
 * Accept all objects that can/cannot have object X perform a command ability on them
 */
class PartitionFilterValidCommandButtonTarget : public PartitionFilter
{
private:
	Object *m_source;
	const CommandButton *m_commandButton;
	Bool m_match;
	CommandSourceType m_commandSource;

public:
	PartitionFilterValidCommandButtonTarget( Object *source, const CommandButton *commandButton, Bool match, CommandSourceType commandSource) : 
		m_source(source), m_commandButton(commandButton), m_match(match), m_commandSource(commandSource) {}
protected:
	virtual Bool allow( Object *other );
#if defined(_DEBUG) || defined(_INTERNAL)
	virtual const char* debugGetName() { return "PartitionFilterValidCommandButtonTarget"; }
#endif
};

//=====================================
/** 
	PartitionManager is the singleton class that manages the entire partition/collision
	system. It maintains the set of PartitionCells that correspond to the world system,
	and updates the PartitionDatas as needed during update phase.
*/
class PartitionManager : public SubsystemInterface, public Snapshot
{

private:

#ifdef FASTER_GCO
	typedef std::vector<ICoord2D>		OffsetVec;
	typedef std::vector<OffsetVec>	RadiusVec;
#endif

	PartitionData		*m_moduleList;		///< master partition module list

	Region3D				m_worldExtents;		///< should be same as TheTerrainLogic->getExtents()
	Real						m_cellSize;				///< edge size of each cell, in world coord space
	Real						m_cellSizeInv;		///< 1/cellSize (used for efficiency)
	Int							m_cellCountX;			///< number of cells, x
	Int							m_cellCountY;			///< number of cells, y
	Int							m_totalCellCount;	///< x * y
	PartitionCell*	m_cells;					///< array of cells
	PartitionData*	m_dirtyModules;
	Bool						m_updatedSinceLastReset;	///< Used to force a return of OBJECTSHROUD_INVALID before update has been called.

	std::queue<SightingInfo *> m_pendingUndoShroudReveals;	///< Anything can queue up an Undo to happen later. This is a queue, because "later" is a constant

#ifdef FASTER_GCO
	Int							m_maxGcoRadius;
	RadiusVec				m_radiusVec;
#endif

protected:

	/**
		This is an internal function that is used to implement the public 
		getClosestObject and iterateObjects calls. 
	*/
	Object *PartitionManager::getClosestObjects(
		const Object *obj, 
		const Coord3D *pos, 
		Real maxDist, 
		DistanceCalculationType dc, 
		PartitionFilter **filters, 
		SimpleObjectIterator *iter,	// if nonnull, append ALL satisfactory objects to the iterator (not just the single closest)
		Real *closestDistArg,
		Coord3D *closestVecArg
	);

	void shutdown( void );

	/// used to validate the positions for findPositionAround family of methods
	Bool tryPosition( const Coord3D *center, Real dist, Real angle,
										const FindPositionOptions *options, Coord3D *result );

	typedef Int (*CellAlongLineProc)(PartitionCell* cell, void* userData);

	Int iterateCellsAlongLine(const Coord3D& pos, const Coord3D& posOther, CellAlongLineProc proc, void* userData);
	
	// note iterateCellsBreadthFirst returns the cell index that made the CellBreadthFirstProc return
	// non-Zero.
	typedef Int (*CellBreadthFirstProc)(PartitionCell* cell, void* userData);
	Int iterateCellsBreadthFirst(const Coord3D *pos, CellBreadthFirstProc proc, void *userData);

#ifdef FASTER_GCO
	Int calcMinRadius(const ICoord2D& cur);
	void calcRadiusVec();
#endif

	// These are all friend functions now. They will continue to function as before, but can be passed into 
	// the DiscreteCircle::drawCircle function.
	friend void hLineAddLooker(Int x1, Int x2, Int y, void *playerIndex);
	friend void hLineRemoveLooker(Int x1, Int x2, Int y, void *playerIndex);
	friend void hLineAddShrouder(Int x1, Int x2, Int y, void *playerIndex);
	friend void hLineRemoveShrouder(Int x1, Int x2, Int y, void *playerIndex);

	friend void hLineAddThreat(Int x1, Int x2, Int y, void *threatValueParms);
	friend void hLineRemoveThreat(Int x1, Int x2, Int y, void *threatValueParms);
	friend void hLineAddValue(Int x1, Int x2, Int y, void *threatValueParms);
	friend void hLineRemoveValue(Int x1, Int x2, Int y, void *threatValueParms);

	void processPendingUndoShroudRevealQueue(Bool considerTimestamp = TRUE);				///< keep popping and processing untill you get to one that is in the future
	void resetPendingUndoShroudRevealQueue();					///< Just delete everything in the queue without doing anything with them

public:

	PartitionManager( void );
	virtual ~PartitionManager( void );

	// --------------- inherited from Subsystem interface -------------
	virtual void init( void );			///< initialize
	virtual void reset( void );			///< system reset
	virtual void update( void );		///< system update
	// ----------------------------------------------------------------

	// --------------- inherited from Snapshot interface --------------
	void crc( Xfer *xfer );
	void xfer( Xfer *xfer );
	void loadPostProcess( void );

	inline Bool getUpdatedSinceLastReset( void ) const { return m_updatedSinceLastReset; }

	void registerObject( Object *object );				///< add thing to system
	void unRegisterObject( Object *object );			///< remove thing from system
	void registerGhostObject( GhostObject* object);	///<recreate partition data needed to hold object (only used to restore after PM reset).
	void unRegisterGhostObject (GhostObject *object);	///< release partition data held for ghost object.

	void processEntirePendingUndoShroudRevealQueue(); ///< process every pending one regardless of timestamp

	/// return the number of PartitionCells in the x-dimension.
	Int getCellCountX() { DEBUG_ASSERTCRASH(m_cellCountX != 0, ("partition not inited")); return m_cellCountX; }

	/// return the number of PartitionCells in the y-dimension.
	Int getCellCountY() { DEBUG_ASSERTCRASH(m_cellCountY != 0, ("partition not inited")); return m_cellCountY; }

	/// return the PartitionCell located at cell coordinates (x,y).
	PartitionCell *getCellAt(Int x, Int y);
	const PartitionCell *getCellAt(Int x, Int y) const;

	/// A convenience funtion to reveal shroud at some location 
	// Queueing does not give you control of the timestamp to enforce the queue.  I own the delay, you don't.
	void doShroudReveal( Real centerX, Real centerY, Real radius, PlayerMaskType playerMask);
	void undoShroudReveal( Real centerX, Real centerY, Real radius, PlayerMaskType playerMask);
	void queueUndoShroudReveal( Real centerX, Real centerY, Real radius, PlayerMaskType playerMask );

	void doShroudCover( Real centerX, Real centerY, Real radius, PlayerMaskType playerMask);
	void undoShroudCover( Real centerX, Real centerY, Real radius, PlayerMaskType playerMask);

	/// Perform threat map and value map updates.
	void doThreatAffect( Real centerX, Real centerY, Real radius, UnsignedInt threatVal, PlayerMaskType playerMask);
	void undoThreatAffect( Real centerX, Real centerY, Real radius, UnsignedInt threatVal, PlayerMaskType playerMask);
	void doValueAffect( Real centerX, Real centerY, Real radius, UnsignedInt valueVal, PlayerMaskType playerMask);
	void undoValueAffect( Real centerX, Real centerY, Real radius, UnsignedInt valueVal, PlayerMaskType playerMask);

	void getCellCenterPos(Int x, Int y, Real& xx, Real& yy);

	// find the cell that covers the world coords (wx,wy) and return its coords.
	void worldToCell(Real wx, Real wy, Int *cx, Int *cy);

	// given a distance in world coords, return the number of cells needed to cover that distance (rounding up)
	Int worldToCellDist(Real w);

	Object *getClosestObject(
		const Object *obj, 
		Real maxDist, 
		DistanceCalculationType dc, 
		PartitionFilter **filters = NULL, 
		Real *closestDist = NULL,
		Coord3D *closestDistVec = NULL
	);
	Object *getClosestObject(
		const Coord3D *pos, 
		Real maxDist, 
		DistanceCalculationType dc, 
		PartitionFilter **filters = NULL, 
		Real *closestDist = NULL,
		Coord3D *closestDistVec = NULL
	);

	Real getRelativeAngle2D( const Object *obj, const Object *otherObj );
	Real getRelativeAngle2D( const Object *obj, const Coord3D *pos );

	void getVectorTo(const Object *obj, const Object *otherObj, DistanceCalculationType dc, Coord3D& vec);
	void getVectorTo(const Object *obj, const Coord3D *pos, DistanceCalculationType dc, Coord3D& vec);

	// just like 'getDistance', but return the dist-sqr, meaning we save a sqrt() call if you don't need it.
	Real getDistanceSquared(const Object *obj, const Object *otherObj, DistanceCalculationType dc, Coord3D *vec = NULL);
	Real getDistanceSquared(const Object *obj, const Coord3D *pos, DistanceCalculationType dc, Coord3D *vec = NULL);

	// just like 'getDistanceSquared', but return the dist-sqr where the obj is at goalPos.
	Real getGoalDistanceSquared(const Object *obj, const Coord3D *goalPos, const Object *otherObj, DistanceCalculationType dc, Coord3D *vec = NULL);
	Real getGoalDistanceSquared(const Object *obj, const Coord3D *goalPos, const Coord3D *otherPos, DistanceCalculationType dc, Coord3D *vec = NULL);

#ifdef PM_CACHE_TERRAIN_HEIGHT
	// note that the 2d positions aren't guaranteed to be the actual spot within the cell where the terrain
	// is lowest or highest.... just the center of the relevant cell. this function is used for rough-n-quick
	// estimates only.
	Bool estimateTerrainExtremesAlongLine(const Coord3D& startWorld, const Coord3D& endWorld, Real* minZ, Real* maxZ, Coord2D* minZPos, Coord2D* maxZPos);
#endif

#ifdef DUMP_PERF_STATS
	void getPMStats(double& gcoTimeThisFrameTotal, double& gcoTimeThisFrameAvg);
#endif

	SimpleObjectIterator *iterateObjectsInRange(
		const Object *obj, 
		Real maxDist, 
		DistanceCalculationType dc, 
		PartitionFilter **filters = NULL, 
		IterOrderType order = ITER_FASTEST
	);

	SimpleObjectIterator *iterateObjectsInRange(
		const Coord3D *pos, 
		Real maxDist, 
		DistanceCalculationType dc, 
		PartitionFilter **filters = NULL, 
		IterOrderType order = ITER_FASTEST
	);

	SimpleObjectIterator *iterateAllObjects(PartitionFilter **filters = NULL);		

	/**
		return the Objects that would (or would not) collide with the given
		geometry.
	*/
	SimpleObjectIterator* iteratePotentialCollisions(
		const Coord3D* pos, 
		const GeometryInfo& geom,
		Real angle,
		Bool use2D = false
	);
	
	Bool isColliding( const Object *a, const Object *b ) const;

	/// Checks a geometry against an arbitrary geometry. 
	Bool geomCollidesWithGeom( const Coord3D* pos1, 
							const GeometryInfo& geom1,
							Real angle1, 
							const Coord3D* pos2, 
							const GeometryInfo& geom2,
							Real angle2 
  ) const;

	/// finding legal positions in the world
	Bool findPositionAround( const Coord3D *center,
													 const FindPositionOptions *options, 
													 Coord3D *result );

	/// return the size of a PartitionCell, in world coords.
	Real getCellSize() { return m_cellSize; }				// only for the use of PartitionData!

	/// return (1.0 / getCellSize); this is used frequently, so we cache it for efficiency
	Real getCellSizeInv() { return m_cellSizeInv; }

	/** 
		return true iff there is clear line-of-sight between the two positions.
		this only takes terrain into account; it does not consider objects, units, 
		trees, buildings, etc. 
	*/
	Bool isClearLineOfSightTerrain(const Object* obj, const Coord3D& objPos, const Object* other, const Coord3D& otherPos);

	inline Bool isInListDirtyModules(PartitionData* o) const
	{
		return o->isInListDirtyModules(&m_dirtyModules);
	}
	inline void prependToDirtyModules(PartitionData* o)
	{
		o->prependToDirtyModules(&m_dirtyModules);
	}
	inline void removeFromDirtyModules(PartitionData* o)
	{
		o->removeFromDirtyModules(&m_dirtyModules);
	}
	inline void removeAllDirtyModules()
	{
		while (m_dirtyModules)
		{
			PartitionData *tmp = m_dirtyModules;
			removeFromDirtyModules(tmp);
		}
	}

	/** 
		Reveals the map for the given player, but does not override Shroud generation.  (Script)
		*/
	void revealMapForPlayer( Int playerIndex );

	/** 
		Reveals the map for the given player, AND permanently disables all Shroud generation (Observer Mode).
		*/
	void revealMapForPlayerPermanently( Int playerIndex );

	/** 
		Adds a layer of permanent blindness.  Used solely to undo the permanent reveal for debugging
		*/
	void undoRevealMapForPlayerPermanently( Int playerIndex );

	/** 
		Resets the shroud for the given player with passive shroud (can re-explore).
		*/
	void shroudMapForPlayer( Int playerIndex );

	/** this doesn't change the actual shroud values in logic, just pushes them
		back out to the display... this should generally only be used when the local
		player changes.
		*/
	void refreshShroudForLocalPlayer();

	/**
		Shrouded has no absolute meaning.  It only makes sense to say "Shrouded for him".
	*/
	CellShroudStatus getShroudStatusForPlayer( Int playerIndex, Int x, Int y ) const;
	CellShroudStatus getShroudStatusForPlayer( Int playerIndex, const Coord3D *loc ) const;

	ObjectShroudStatus getPropShroudStatusForPlayer(Int playerIndex, const Coord3D *loc ) const; 

	Real getGroundOrStructureHeight(Real posx, Real posy);

	void getMostValuableLocation( Int playerIndex, UnsignedInt whichPlayerTypes, ValueOrThreat valType, Coord3D *outLocation );
	void getNearestGroupWithValue( Int playerIndex, UnsignedInt whichPlayerTypes, ValueOrThreat valType, const Coord3D *sourceLocation,
																 Int valueRequired, Bool greaterThan, Coord3D *outLocation );

	// If saveToFog is true, then we are writing STORE_FOG. 
	// If saveToFog is false, then we are writing STORE_PERMENANT_REVEAL
	void storeFoggedCells(ShroudStatusStoreRestore &outPartitionStore, Bool storeToFog) const;
	void restoreFoggedCells(const ShroudStatusStoreRestore &inPartitionStore, Bool restoreToFog);
};  // end class PartitionManager

// -----------------------------------------------------------------------------
inline void PartitionManager::worldToCell(Real wx, Real wy, Int *cx, Int *cy)
{
	*cx = REAL_TO_INT_FLOOR((wx - m_worldExtents.lo.x) * m_cellSizeInv);
	*cy = REAL_TO_INT_FLOOR((wy - m_worldExtents.lo.y) * m_cellSizeInv);
}

//-----------------------------------------------------------------------------
inline Int PartitionManager::worldToCellDist(Real w)
{
	return REAL_TO_INT_CEIL(w  * m_cellSizeInv);
}

//-----------------------------------------------------------------------------
inline PartitionCell *PartitionManager::getCellAt(Int x, Int y)
{
	return (x < 0 || y < 0 || x >= m_cellCountX || y >= m_cellCountY) ? NULL : &m_cells[y * m_cellCountX + x];
}
	
//-----------------------------------------------------------------------------
inline const PartitionCell *PartitionManager::getCellAt(Int x, Int y) const
{
	return (x < 0 || y < 0 || x >= m_cellCountX || y >= m_cellCountY) ? NULL : &m_cells[y * m_cellCountX + x];
}

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

#ifdef FASTER_GCO
// nothing
#else
class CellOutwardIterator
{
private:
	PartitionManager	*m_mgr;
	Int								m_cellCenterX, m_cellCenterY;
	Int								m_maxRadius;
	Int								m_cellRadius;
	Int								m_delta[2];
	Int								m_inc;
	Int								m_cnt;
	Int								m_axis;
	Int								m_iter;
	PartitionCell *nextCell(Bool skipEmpties);
public:
	CellOutwardIterator(PartitionManager *mgr, Int x, Int y);
	~CellOutwardIterator();
	PartitionCell *next() { return nextCell(false); }
	PartitionCell *nextNonEmpty() { return nextCell(true); }
	Int getCurCellRadius() const { return m_cellRadius; }
	Int getMaxRadius() const { return m_maxRadius; }
	void setMaxRadius(Int max) { m_maxRadius = max; }
};
#endif

//-----------------------------------------------------------------------------
//           Inlining                                                       
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//           Externals                                                     
//-----------------------------------------------------------------------------
extern PartitionManager *ThePartitionManager;  ///< object manager singleton

#endif // __PARTITIONMANAGER_H_

