// Sprite.h: interface for the cSprite class.
//
//////////////////////////////////////////////////////////////////////

#ifndef SPRITE_H
#define SPRITE_H

#include "realbox.h" //For Real, cRealPixelConverter, cVector, cGraphics, etc.
#include "colorstyle.h"

class cGraphics; //For argument to draw
class cCritter; //For argument to animate

class cSprite : public CObject  
{
 DECLARE_SERIAL(cSprite); 

public:
//Randomization and mutation flags.  MF stands for mutation flag.
	//The MF_ flags are defined in static.cpp.
	static const int MF_RADIUS;
	static const int MF_ROTATION;
	static const int MF_ALL; //MF_RADIUS | MF_ROTATION.
//Angular velocity bounds used in mutate(...MF_ROTATION...)
	static Real MINANGVELOCITY;
	static Real MAXANGVELOCITY;
//For drawing highlight circles in draw(...DF_FOCUS...)
	static Real HIGHLIGHTRATIO; 
	static const unsigned long INVALIDID; // Use 0 for this, see SPRITEID
//Prism statics
	static Real CRITTERPRISMDZ; /* The default Z depth to use for a cCritter sprite if we are
		in 3D and have a sprite that uses its _prismdz field. */
	static Real BULLETPRISMDZ; //Default z-depth to use for drawing cCritterBullet.
	static Real WALLPRISMDZ; //Default z-depth to use for drawing cCritterWall.
	static Real PLAYERPRISMDZ;  //Default z-depth to use for drawing cCritterArmedPlayer.
	static Real MAXPRISMDZ; /* We may make some graphical thickness additions that
		 do to make a 2D game look 3D, such as thick sprites or putting
		foreground or background at a z offset.  Keep all of these 
		within +-MAXPRISMDZ on the z axis so we know to set znear and 
		zfar to include them.  This isn't so much of an issue for a true 3D game,
		it's only an issue for 2D games that we dress up with prism sprites. */ 
//Display list static
	static BOOL ENABLEDISPLAYLIST;	/* ENABLEDISPLAYLIST specifies whetehr or not you want to
		try and use a display list to show this kind of sprite. By default this is TRUE.  We only
		have it because the cSpriteIcon actually runs slower with displaylists, so we
		want to be able turn of display lists for them. So we have a cSpriteIcon::ENABLEDISPLAY
		list that is FALSE to shadow (hide) the cSprite::ENABLEDISPLAYLIST. */ 
protected:
	static unsigned long SPRITEID; /* Use this to assign each cSprite its own ID when its
		constructed. If you make four billion sprites, you'll wrap the ID around and trigger
		a ASSERT. We'll let the valid values go from 1 to 0xFFFFFFFF */
	unsigned long _spriteID; /* Don't serialize or copy this one, it's unique for each sprite,
		and is set by the constructor. We need it for use in a lookup method for display list
		IDs in cGraphicsOpenGL class. */
	cColorStyle *_pcolorstyle; //To hold things like fillcolor, texture map, etc.
	Real _radius; /* Relates to the internal geometry of the sprite.  Will not usually
		equal the value returned by cSprite::radius(), as this method
		multiplies in the _spriteattiude.scalefactor() */
	cMatrix _spriteattitude; /* We draw the sprite in the sprite attitude*critter attitude.
		This lets us position the sprite as we like relative to the critter's orientation.
		This is particularly useful in cSpriteComposite where I can group several sprites
		together. */
	Real _prismdz; /* _prismdz is a kludge variable that I put in so that flat sprites will
		pop out to look 3D when I switch to a 3D graphics like cGraphicsOpenGL.  The
		default is 0.0.  If nonzero _prsimz, it turns a	2D sprite like a cPolygon or a 
		cBubble into a 3D prism or cylinder. */
	BOOL _newgeometryflag; /* This is for use with sophisticated graphics like 
		cGraphicsOpenGL, where we speed up the drawing
		of some kinds of sprites by saving a display list index that encapsulates the steps
		used to draw the sprite.  If you change the intrinsic geometry of the sprite, for
		instance by changing the number of vertices in a polygon, then you need to 
		make a new display list for that sprite, and the _newgeometryflag is the flag
		we have a cSprite use so as to signal this to the cGraphicsOpenGL. 
		At construction, _newgeometryflag is TRUE. We don't serialize this field. */
public:
	cSprite();
	cSprite(cSprite &sprite);/* The copy constructor for cCritter and cSprite
		can't have a const argument because we use the copy function
		that takes a pointer to the argument object.  The copy function MUST 
		have a pointer argument so it can be polymorphic. */
	cSprite(cSprite *psprite);
	virtual ~cSprite();
	virtual void copy(cSprite *psprite);
	cSprite operator=(cSprite &csprite); //uses the virtual copy.
	cSprite* clone(); /* Returns a pointer to a cSprite of the same child class
		type, with the same info in it. */
//Serialize method
	virtual void Serialize(CArchive &ar);
//Attitude mutators
	virtual void setSpriteAttitude(const cMatrix &m){_spriteattitude = m;}
	virtual void setCenter(const cVector &newcenter)
		{_spriteattitude.setLastColumn(newcenter);}
	virtual void setRadius(Real radius); /* The default cSprite method for setRadius 
		does the miniumum, to reset _radius.   cPolygon and cIcon override setRadius, by
		changing the intrinsic geometry of the polygon or the bitmap rectangle. 
		cSpriteComposite overrides to leave _radius alone and instead to scale the
		_spriteattitude method, though this	is in turn overridden for cSpriteShowOneChild.*/
	virtual void rotate(Real angle){_spriteattitude*=cMatrix::zRotation(angle);}
	virtual void rotate(const cSpin &spin){_spriteattitude*=cMatrix::rotation(spin);}
//3D Mutators
	virtual void setPrismDz(Real prismdz){_prismdz = prismdz;}
	virtual void setNewgeometryflag(BOOL onoff){_newgeometryflag = onoff;}
//Randomizing mutators
	virtual void mutate(int mutationflags, Real mutationstrength);
	virtual void randomizeColor(){}
	virtual void randomize(int mutationflags){mutate(mutationflags, 1.0);}
//cColorstyle Mutators
	virtual void setColorStyle(cColorStyle *pcolorstyle);
	virtual void setFilled(BOOL yesno){_pcolorstyle->setFilled(yesno);
		setNewgeometryflag(TRUE);}
	virtual void setEdged(BOOL yesno){_pcolorstyle->setEdged(yesno);
		setNewgeometryflag(TRUE);}
	virtual void setFillColor(COLORREF col){_pcolorstyle->setFillColor(col);
		setNewgeometryflag(TRUE);}
	virtual void setLineColor(COLORREF col){_pcolorstyle->setLineColor(col);
		setNewgeometryflag(TRUE);}
	virtual void setLineWidthWeight(Real linewidthweight)
		{_pcolorstyle->setLineWidthWeight(linewidthweight);
		setNewgeometryflag(TRUE);}
	virtual void setLineWidth(Real linewidth){_pcolorstyle->setLineWidth(linewidth);
		setNewgeometryflag(TRUE);}
//accessors
	unsigned long spriteID()const{return _spriteID;}
	virtual Real radius() const; /* Normally is _radius * _spriteattitude.scalefactor() */
	virtual Real angle() const{return 0.0;}
	virtual cVector center()const{return _spriteattitude.lastColumn();}
	virtual Real prismDz()const{return _prismdz;}
	cColorStyle* pcolorstyle(){return _pcolorstyle;} /* Don't call this accessor const,
		as we usually use it to then do something to _pcolorstyle. */
	BOOL newgeometryflag()const{return _newgeometryflag;}
	cMatrix spriteattitude()const{return _spriteattitude;}
	virtual BOOL enabledisplaylist()const{return cSprite::ENABLEDISPLAYLIST;}
//cColorstyle accessors
	virtual BOOL filled() const {return _pcolorstyle->filled();}
	virtual BOOL edged() const {return _pcolorstyle->edged();}
	virtual COLORREF fillColor() const {return _pcolorstyle->fillColor();}
	virtual COLORREF lineColor() const {return _pcolorstyle->lineColor();}
	virtual Real lineWidthWeight() const {return _pcolorstyle->lineWidthWeight();}
	virtual Real lineWidth() const {return _pcolorstyle->lineWidth();}
//methods
	virtual void animate(Real dt, cCritter *powner){} /* Might choose sprite
		based on age, change the radius of the sprite with time, or choose a sprite based
		on direction or health of powner, or maybe spin. */
	virtual void draw(cGraphics *pgraphics, int drawflags = 0);
		/* This gets called by the owner cCritter::draw which does multMatrix
		(on the right) with the critter's own _attitude and calls psprite()->draw. 
		cSprite::draw does a multMatrix (on the right) with its own _spriteattitude
		and calls its special overloaded imagedraw method. It's effectively a
		Template Method. */
	virtual void imagedraw(cGraphics *pgraphics, int drawflags);
};

class cSpriteComposite : public cSprite  
{
DECLARE_SERIAL(cSpriteComposite); 
protected:
	CTypedPtrArray<CObArray, cSprite*> _childspriteptr;
public:
	cSpriteComposite(){} //Default constructor of _childspriteptr will be a zero-size array.
	virtual ~cSpriteComposite();
	void Serialize(CArchive &ar);
//Mutators 
	/* By default the inherited cSprite mutators will only affect the base sprite.
	Depending on whether we plan to show all of the child sprites all the time, and
	on whether we might prefer to individually mutate them we might or might not
	want to cascade the mutators down to all the children.  For the following ones
	that we pretty definitely want to be uniform, we cascade them to mutate both
	the base and all the children. You might want to overload more of these later. */
	virtual void setLineColor(COLORREF col);
	virtual void setNewgeometryflag(BOOL onoff);
	virtual void setPrismDz(Real prismdz); /* setPrismDz needs to take the scaling of 
		the _spriteattitudematrix into account. */
	virtual void setRadius(Real radius); /* The default cSpriteComposite method for setRadius is
		to scale _spriteattitude matrix.  This gets used by cPolyPolygon and the
		various cSpriteBubble.  We override setRadius for the cSpriteShowOneChild 
		child of cSpriteComposite. */
//Accessor
	virtual Real radius() const; /* For now, we view a cSpriteComposite as having the
		 radius of its largest piece.  But often we will often overload this. */
//Methods
	virtual void add(cSprite *psprite);
	virtual void add(int resourceid); /* Doesn't really need to be virtual as it
		calls the virtual add(new cSpriteIcon(resourceid)).*/
	virtual void copy(cSprite *psprite);
	virtual void draw(cGraphics *pgraphics, int drawflags = 0); /* Default is to cascade
		calls to the _childspriteptr members. */
	virtual void animate(Real dt, cCritter *powner); // Default is to cascade.
};

#endif // SPRITE_H
