/* Copyright (c) 2002-2012 Croteam Ltd. 
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. */

#ifndef SE_INCL_ENTITY_H
#define SE_INCL_ENTITY_H
#ifdef PRAGMA_ONCE
  #pragma once
#endif

#include <Engine/Base/Lists.h>
#include <Engine/Base/Relations.h>
#include <Engine/Templates/StaticStackArray.h>
#include <Engine/Templates/Selection.h>
#include <Engine/Math/Matrix.h>
#include <Engine/Math/AABBox.h>
#include <Engine/Math/Placement.h>
#include <Engine/Entities/EntityEvent.h>
#include <Engine/Entities/EntityPointer.h>
#include <Engine/Ska/ModelInstance.h>


#define DUMPVECTOR(v) \
  strm.FPrintF_t(#v ":  %g,%g,%g %08x,%08x,%08x\n", \
    (v)(1), (v)(2), (v)(3), (ULONG&)(v)(1), (ULONG&)(v)(2), (ULONG&)(v)(3))
#define DUMPVECTOR2(strDes, v) \
  strm.FPrintF_t( "%s:  %g,%g,%g\n", strDes, (v)(1), (v)(2), (v)(3))
#define DUMPLONG(l) \
  strm.FPrintF_t(#l ":  %08x\n", l)
#define DUMPPLACEMENT(plname, pl)        \
  strm.FPrintF_t("%s:\n", plname);       \
  DUMPVECTOR2( "Position", pl.pl_PositionVector);    \
  DUMPVECTOR2( "Orientation", pl.pl_OrientationAngle);

// force infulence at a point in space
class CForceStrength {
public:
  FLOAT3D fs_vDirection;    // direction of the force (must be normalized)
  FLOAT fs_fAcceleration;   // acceleration of the force (m/s2) (along the direction)
  FLOAT fs_fVelocity;       // max. velocity that force can give (m/s) (along the direction)
};


#define DECL_DLL ENGINE_API
#include <Engine/Classes/BaseEvents.h>
#undef DECL_DLL

/*
 * Flags determining whether some entity is active in some game type or difficulty level.
 */
// difficulty levels
#define SPF_EASY        (1L<<0)       // active at easy difficulty
#define SPF_NORMAL      (1L<<1)       // active at normal difficulty
#define SPF_HARD        (1L<<2)       // active at hard difficulty
#define SPF_EXTREME     (1L<<3)       // active at extreme difficulty
#define SPF_TOURIST     (1L<<4)       // active at tourist difficulty

#define SPF_MASK_DIFFICULTY 0x0000FFFFL    // mask for difficulty level flags

// game types
#define SPF_SINGLEPLAYER  (1L<<16)    // active in single player mode
#define SPF_DEATHMATCH    (1L<<17)    // active in deathmatch mode
#define SPF_COOPERATIVE   (1L<<18)    // active in cooperative mode
#define SPF_FLYOVER       (1L<<19)    // active in flyover (camera) mode

#define SPF_MASK_GAMEMODE 0xFFFF0000L    // mask for game type flags

/*
 * Various other entity flags
 */
#define ENF_SELECTED          (1L<< 0)  // set if selected
#define ENF_ZONING            (1L<< 1)  // brush that defines spatial classification
#define ENF_DELETED           (1L<< 2)  // set if the entity does not exist anymore
#define ENF_ALIVE             (1L<< 3)  // set if the entity is currently a living being
#define ENF_INRENDERING       (1L<< 4)  // set if the entity is currently active in rendering
#define ENF_VALIDSHADINGINFO  (1L<< 5)  // set if shading info is valid
#define ENF_SEETHROUGH        (1L<< 6)  // set if cast ray can see through
#define ENF_FOUNDINGRIDSEARCH (1L<< 7)  // set if the entity is already found in grid search
#define ENF_CLUSTERSHADOWS    (1L<< 8)  // model that has cluster shadows
#define ENF_BACKGROUND        (1L<< 9)  // brush or model that is used for background rendering
#define ENF_ANCHORED          (1L<<10)  // set if cannot be moved in editor without special allowance
#define ENF_HASPARTICLES      (1L<<11)  // entity renders particles
#define ENF_INVISIBLE         (1L<<12)  // entity is invisible (for AI purposes)
#define ENF_DYNAMICSHADOWS    (1L<<13)  // moving brush that causes automatic shadow recalculation
#define ENF_NOTIFYLEVELCHANGE (1L<<14)  // entity is notified when level is changed
#define ENF_CROSSESLEVELS     (1L<<15)  // entity must be carried when level is changed
#define ENF_PREDICTABLE       (1L<<16)  // this entity can be predicted
#define ENF_PREDICTOR         (1L<<17)  // this entity is predictor for another entity
#define ENF_PREDICTED         (1L<<18)  // this entity has its predictor
#define ENF_WILLBEPREDICTED   (1L<<19)  // this entity will be predicted
#define ENF_TEMPPREDICTOR     (1L<<20)  // predictor that was spawned during prediction (doesn't have a predictor)
#define ENF_HIDDEN            (1L<<21)  // set if the entity is hidden (for editing)
#define ENF_NOSHADINGINFO     (1L<<22)  // the entity doesn't need FindShadingInfo(), it will set its own shading


// selections of entities
typedef CSelection<CEntity, ENF_SELECTED> CEntitySelection;

/*
 *  General structure of an entity instance.
 */
class ENGINE_API CEntity {
public:
  // type of function pointer used as AI event handler
  typedef BOOL (CEntity::*pEventHandler)(const CEntityEvent &ee);

  enum RenderType {
    RT_ILLEGAL     = 1,
    RT_NONE        = 2,     // not rendered ever -- used internally
    RT_MODEL       = 3,     // drawn as model
    RT_BRUSH       = 4,     // rendered as brush
    RT_EDITORMODEL = 5,     // rendered as model, but only in editor
    RT_VOID        = 7,     // not rendered ever
    RT_FIELDBRUSH  = 8,     // brush used for field effects (like triggers, force fields etc.)
    RT_SKAMODEL    = 9,     // render as ska model
    RT_SKAEDITORMODEL = 10, // render as ska model, but only in editor
    RT_TERRAIN        = 11, // render as terrain
  };
/* Entity physics flags. */
#define EPF_ORIENTEDBYGRAVITY     (1UL<<0) // set if gravity influences its orientation
#define EPF_TRANSLATEDBYGRAVITY   (1UL<<1) // set if gravity can move it
#define EPF_PUSHABLE              (1UL<<2) // set if can be pushed by other objects
#define EPF_STICKYFEET            (1UL<<3) // entity always falls to nearest surface
#define EPF_RT_SYNCHRONIZED       (1UL<<4) // set if rotation and translation are synchronized
#define EPF_ABSOLUTETRANSLATE     (1UL<<5) // set if entity is translated absolute and not relative to its position
#define EPF_NOACCELERATION        (1UL<<6) // set if entity can change its speed immediately
#define EPF_HASLUNGS              (1UL<<7) // set if entity has lungs
#define EPF_HASGILLS              (1UL<<8) // set if entity has gills
#define EPF_MOVABLE               (1UL<<9) // set if derived from CMovableEntity
#define EPF_NOIMPACT              (1UL<<10)// entities are not damaged when hitting this one
#define EPF_NOIMPACTTHISTICK      (1UL<<11)// this one is not damaged by impact this tick
#define EPF_CANFADESPINNING       (1UL<<12)// desired rotation can be reduced by contents (like water)
#define EPF_ONSTEEPSLOPE          (1UL<<13)// while sliding down a steep slope (valid only if entity has reference)
#define EPF_ORIENTINGTOGRAVITY    (1UL<<14)// while beeing re-oriented by gravity
#define EPF_FLOATING              (1UL<<15)// while bouyancy causes floating in fluid
#define EPF_FORCEADDED            (1UL<<16)// set if force-added to movers

// what to do when colliding
#define EPF_ONBLOCK_MASK            (7UL<<29)
#define EPF_ONBLOCK_STOP            (0UL<<29)  // stop moving
#define EPF_ONBLOCK_SLIDE           (1UL<<29)  // slide along
#define EPF_ONBLOCK_CLIMBORSLIDE    (2UL<<29)  // clim up a stair or slide along
#define EPF_ONBLOCK_BOUNCE          (3UL<<29)  // bounce off
#define EPF_ONBLOCK_PUSH            (4UL<<29)  // push the obstacle
#define EPF_ONBLOCK_STOPEXACT       (5UL<<29)  // stop moving, but exactly at collision position

// entity collision flags are divided in 3 groups
#define ECB_COUNT 10    // max number of flags per group
#define ECF_MASK  ((1<<ECB_COUNT)-1)  // mask for one group
// what an entity is
#define ECB_IS      0
#define ECF_ISMASK  (ECF_MASK<<ECB_IS)
// which other entities to test with
#define ECB_TEST    10
#define ECF_TESTMASK  (ECF_MASK<<ECB_TEST)
// which tested entities to pass through
#define ECB_PASS    20
#define ECF_PASSMASK  (ECF_MASK<<ECB_PASS)

// optimizations to completely ignore some types of entities
#define ECF_IGNOREBRUSHES  (1UL<<30)
#define ECF_IGNOREMODELS   (1UL<<31)

public:
  enum RenderType en_RenderType;  // how is it rendered
  ULONG en_ulPhysicsFlags;        // ways of interacting with environment
  ULONG en_ulCollisionFlags;      // which entities to collide with

  ULONG en_ulFlags;               // various flags
  ULONG en_ulSpawnFlags;          // in what game types is this entity active
  INDEX en_ctReferences;          // reference counter for delayed destruction
  ULONG en_ulID;                  // unique entity identifier

  CPlacement3D en_plPlacement;      // placement in world space
  FLOATmatrix3D en_mRotation;       // precalc. matrix for object rotation
  CEntityClass *en_pecClass;         // class used to construct this entity
  union {   // rendering object
    CBrush3D *en_pbrBrush;            // brush -- if brush entity
    CModelObject *en_pmoModelObject;  // model -- if model entity
    CModelInstance *en_pmiModelInstance; // ska model -- if ska model entity
    CTerrain *en_ptrTerrain;          // terrain -- if terrain entity
  };
  CShadingInfo *en_psiShadingInfo;      // shading info for model entities
  CCollisionInfo *en_pciCollisionInfo;  // collision info for movable colliding entities
  FLOAT en_fSpatialClassificationRadius;  // radius for spatial classification
  FLOATaabbox3D en_boxSpatialClassification;  // box in object space for spatial classification
  CLastPositions *en_plpLastPositions;    // last positions of entity

  class CWorld *en_pwoWorld;      // the world this entity belongs to

public: // imagine that this is private
  CRelationDst en_rdSectors;      // relation to sectors this entity is in
public:

  CEntity *en_penParent;        // parent entity
  public: // these must be public for iteration
  CListNode en_lnInParent;      // node in the parent entity
  CListHead en_lhChildren;      // list of child entities
  public:
  CPlacement3D en_plRelativeToParent;   // placement relative to parent placement

public:
  // reference counting functions
  inline void AddReference(void);
  inline void RemReference(void);

  /* Get pointer to entity property from its packed identifier. */
  class CEntityProperty *PropertyForTypeAndID(ULONG ulType, ULONG ulID);
  /* Helpers for writing/reading entity pointers. */
  void ReadEntityPointer_t(CTStream *istr, CEntityPointer &pen);
  void WriteEntityPointer_t(CTStream *ostr, CEntityPointer pen);
  /* Get pointer to entity component from its packed identifier. */
  class CEntityComponent *ComponentForTypeAndID(ULONG ulType, ULONG ulID);
  /* Get pointer to entity property from its name. */
  class CEntityProperty *PropertyForName(const CTString &strPropertyName);
  /* Copy one entity property from property of another entity. */
  void CopyOneProperty( CEntityProperty &epPropertySrc, CEntityProperty &epPropertyDest,
                        CEntity &enOther, ULONG ulFlags);

  /* Read all properties from a stream. */
  void ReadProperties_t(CTStream &istrm);  // throw char *
  /* Write all properties to a stream. */
  void WriteProperties_t(CTStream &ostrm); // throw char *
  /* Copy entity properties from another entity of same class. */
  void CopyEntityProperties(CEntity &enOther, ULONG ulFlags);

  /* Internal versions for entity reinitialization (do not discard shadows etc.). */
  void Initialize_internal(const CEntityEvent &eeInput);
  void End_internal(void);
  /* Reinitialize the entity. */
  void Reinitialize(void);

  // internal repositioning function
  virtual void SetPlacement_internal(const CPlacement3D &plNew, const FLOATmatrix3D &mRotation,
    BOOL bNear);
  // Uncache shadows of each polygon in entity that has given gradient index
  void UncacheShadowsForGradient(INDEX iGradient);

  /* Find and remember shading info for this entity if invalid. */
  void FindShadingInfo(void);
  /* Find and remember collision info for this entity. */
  void FindCollisionInfo(void);
  // discard collision info for this entity
  void DiscardCollisionInfo(void);
  // copy collision info from some other entity
  void CopyCollisionInfo(CEntity &enOrg);
  /* Update range used for spatial clasification. */
  void UpdateSpatialRange(void);
  /* Find and remember all sectors that this entity is in. */
  void FindSectorsAroundEntity(void);
  void FindSectorsAroundEntityNear(void);

  // add entity to collision grid
  void AddToCollisionGrid(void);

  /* Copy entity from another entity of same class. (NOTE: this doesn't copy placement!) */
#define COPY_REMAP      (1UL<<0)  // remap pointers
#define COPY_REINIT     (1UL<<1)  // reinit entity
  // make predictor (complete raw copy with all states/variables and 
  // making predictor/predicted links)
#define COPY_PREDICTOR  (1UL<<2)  
  virtual void Copy(CEntity &enOther, ULONG ulFlags);
  virtual CEntity &operator=(CEntity &enOther) {ASSERT(FALSE); return *this;};
  // find a pointer to another entity while copying
  static CEntity *FindRemappedEntityPointer(CEntity *penOriginal);
  /* Read from stream. */
  virtual void Read_t( CTStream *istr);  // throw char *
  /* Write to stream. */
  virtual void Write_t( CTStream *ostr); // throw char *
  /* Precache components that might be needed. */
  virtual void Precache(void);
  // create a checksum value for sync-check
  virtual void ChecksumForSync(ULONG &ulCRC, INDEX iExtensiveSyncCheck);
  // dump sync data to text file
  virtual void DumpSync_t(CTStream &strm, INDEX iExtensiveSyncCheck);  // throw char *

  /* Handle all sent events. */
  static void HandleSentEvents(void);

  // find entities in a box (box must be around this entity)
  void FindEntitiesInRange(const FLOATaabbox3D &boxRange, CDynamicContainer<CEntity> &cen,
    BOOL bCollidingOnly);
  // find first entity touching a field (this entity must be a field brush)
  CEntity *TouchingEntity(BOOL (*ConsiderEntity)(CEntity *), CEntity *penHintMaybeInside);

public:
  // DLL class interface
  /* Initialize for being virtual entity that is not rendered. */
  void InitAsVoid(void);
  /* Initialize for beeing a model object. */
  void InitAsModel(void);
  void InitAsSkaModel(void);
  /* Initialize for beeing a terrain object. */
  void InitAsTerrain(void);

  /* Initialize for beeing an editor model object. */
  void InitAsEditorModel(void);
  void InitAsSkaEditorModel(void);
  /* Initialize for beeing a brush object. */
  void InitAsBrush(void);
  /* Initialize for beeing a field brush object. */
  void InitAsFieldBrush(void);
  /* Switch to Model/Editor model */
  void SwitchToModel(void);
  void SwitchToEditorModel(void);

  /* Set all properties to default values. - overridden by ecc */
  virtual void SetDefaultProperties(void);

  /* Get a filename for a component of given type and id. */
  const CTFileName &FileNameForComponent(SLONG slType, SLONG slID);
  // Get data for a texture component
  CTextureData *GetTextureDataForComponent(SLONG slID);
  // Get data for a model component
  CModelData *GetModelDataForComponent(SLONG slID);

  // model manipulation functions -- only for RT_MODEL/RT_EDITORMODEL
  /* Set the model data for model entity. */
  void SetModel(const CTFileName &fnmModel);
  void SetModel(SLONG idModelComponent);
  BOOL SetSkaModel(const CTString &fnmModel);
  void SetSkaModel_t(const CTString &fnmModel);
  void SetSkaColisionInfo();
  /* Get the model data for model entity. */
  const CTFileName &GetModel(void);
  /* Start new animation for model entity. */
  void StartModelAnim(INDEX iNewModelAnim, ULONG ulFlags);

  /* Play a given sound object. */
  void PlaySound(CSoundObject &so, SLONG idSoundComponent, SLONG slPlayType);
  void PlaySound(CSoundObject &so, const CTFileName &fnmSound, SLONG slPlayType);
  double GetSoundLength(SLONG idSoundComponent);

  // set/get model main blend color
  COLOR GetModelColor(void) const;
  void  SetModelColor( const COLOR colBlend);

  /* Set the main texture data for model entity. */
  void SetModelMainTexture(SLONG idTextureComponent);
  void SetModelMainTexture(const CTFileName &fnmTexture);
  /* Get the main texture data for model entity. */
  const CTFileName &GetModelMainTexture(void);
  /* Start new animation for main texture of model entity. */
  void StartModelMainTextureAnim(INDEX iNewTextureAnim);

  /* Set the reflection texture data for model entity. */
  void SetModelReflectionTexture(SLONG idTextureComponent);
  /* Set the specular texture data for model entity. */
  void SetModelSpecularTexture(SLONG idTextureComponent);

  /* Add attachment to model */
  void AddAttachment(INDEX iAttachment, ULONG ulIDModel, ULONG ulIDTexture);
  void AddAttachment(INDEX iAttachment, CTFileName fnModel, CTFileName fnTexture);
  /* Remove attachment from model */
  void RemoveAttachment(INDEX iAttachment);
  /* Set the reflection texture data for model attachment entity. */
  void SetModelAttachmentReflectionTexture(INDEX iAttachment, SLONG idTextureComponent);
  /* Set the specular texture data for model attachment entity. */
  void SetModelAttachmentSpecularTexture(INDEX iAttachment, SLONG idTextureComponent);
  
  // Get all vertices of model entity in absolute space
  void GetModelVerticesAbsolute( CStaticStackArray<FLOAT3D> &avVertices, FLOAT fNormalOffset, FLOAT fMipFactor);
  // Returns true if bone exists and sets two given vectors as start and end point of specified bone in abs space
  BOOL GetBoneAbsPosition(INDEX iBoneID, FLOAT3D &vStartPoint, FLOAT3D &vEndPoint);
  // Returns true if bone exists and sets two given vectors as start and end point of specified bone in relative space
  BOOL GetBoneRelPosition(INDEX iBoneID, FLOAT3D &vStartPoint, FLOAT3D &vEndPoint);
  // Callback function for aditional bone adjustment
  virtual void AdjustBones();
  // Callback function for aditional shader params adjustment
  virtual void AdjustShaderParams(INDEX iSurfaceID,CShader *pShader,ShaderParams &spParams);

  // precache given component
  void PrecacheModel(SLONG slID);
  void PrecacheTexture(SLONG slID);
  void PrecacheSound(SLONG slID);
  void PrecacheClass(SLONG slID, INDEX iUser = -1);

  /* Create a new entity of given class in this world. */
  CEntity *CreateEntity(const CPlacement3D &plPlacement, SLONG idModelComponent);

  /* Apply some damage directly to one entity. */
  void InflictDirectDamage(CEntity *penToDamage, CEntity *penInflictor, enum DamageType dmtType,
    FLOAT fDamageAmmount, const FLOAT3D &vHitPoint, const FLOAT3D &vDirection);
  /* Apply some damage to all entities in some range (this tests for obstacles). */
  void InflictRangeDamage(CEntity *penInflictor, enum DamageType dmtType,
    FLOAT fDamageAmmount, const FLOAT3D &vCenter, FLOAT fHotSpotRange, FLOAT fFallOffRange);
  /* Apply some damage to all entities in a box (this doesn't test for obstacles). */
  void InflictBoxDamage(CEntity *penInflictor, enum DamageType dmtType,
    FLOAT fDamageAmmount, const FLOATaabbox3D &box);

  // notify engine that gravity defined by this entity has changed
  void NotifyGravityChanged(void);
  // notify engine that collision of this entity was changed
  void NotifyCollisionChanged(void);

  // get a pseudo-random number (safe for network gaming)
  ULONG IRnd(void);   // [0x0000 , 0xFFFF]
  FLOAT FRnd(void);   // [0.0f , 1.0f]

  // DLL class overridables
  /* Called after creating and setting its properties. */
  virtual void OnInitialize(const CEntityEvent &eeInput);
  /* Called before releasing entity. */
  virtual void OnEnd(void);

  // these functions are dummy in CEntity, but are implemented in CRationalEntity
  /* Jump to a new state. */
  void Jump(SLONG slThisState, SLONG slTargetState, BOOL bOverride, const CEntityEvent &eeInput) {};
  /* Call a subautomaton. */
  void Call(SLONG slThisState, SLONG slTargetState, BOOL bOverride, const CEntityEvent &eeInput) {};
  /* Return from a subautomaton. */
  void Return(SLONG slThisState, const CEntityEvent &eeReturn) {};
  // print stack to debug output
  virtual const char *PrintStackDebug(void);

  void SetTimerAt(TIME timeAbsolute) {};
  void SetTimerAfter(TIME timeDelta) {};
  void UnsetTimer(void) {};

  // return opacity of the entity (1 is default)
  virtual FLOAT GetOpacity(void) { return 1.0f; };

  // returns ammount of memory used by entity
  virtual SLONG GetUsedMemory(void);

public:

  // construction/destruction
  /* Default constructor. */
  CEntity(void);
  /* Destructor. */
  virtual ~CEntity(void);
  /* Clear the object. */
  void Clear(void) {};

  // entities can be selected
  IMPLEMENT_SELECTING(en_ulFlags);

  // access functions
  /* Prepare entity (call after setting properties). */
  void Initialize(const CEntityEvent &eeInput = _eeVoid);
  /* Clean-up entity. */
  void End(void);

  /* Destroy this entity (entity must not be targetable). */
  void Destroy(void);

  /* Get state transition for given state and event code. */
  virtual CEntity::pEventHandler HandlerForStateAndEvent(SLONG slState, SLONG slEvent);
  /* Handle an event, return false if the event is not handled. */
  virtual BOOL HandleEvent(const CEntityEvent &ee);

  // get/set functions for use in WEd and/or entity class DLLs
  void SetPlacement(const CPlacement3D &plNew);     // use this only in WEd
  void FallDownToFloor( void);
  inline const CPlacement3D &GetPlacement(void) const { return en_plPlacement; };
  inline const FLOATmatrix3D &GetRotationMatrix(void) const { return en_mRotation; };
  // this one is used in rendering - gets lerped placement between ticks
  virtual CPlacement3D GetLerpedPlacement(void) const;
  /* Find first sector that entity is in */
  CBrushSector *GetFirstSector(void);
  /* Find first sector that entity is in (for UI purpuses) */
  CBrushSector *GetFirstSectorWithName(void);

  // teleport this entity to a new location -- takes care of telefrag damage
  void Teleport(const CPlacement3D &plNew, BOOL bTelefrag=TRUE);

  void SetFlags(ULONG ulFlags);
  inline ULONG GetFlags(void) const { return en_ulFlags; };
  inline void SetSpawnFlags(ULONG ulFlags) { en_ulSpawnFlags = ulFlags; }
  inline ULONG GetSpawnFlags(void) const { return en_ulSpawnFlags; };
  void SetPhysicsFlags(ULONG ulFlags);
  inline ULONG GetPhysicsFlags(void) const { return en_ulPhysicsFlags; };
  void SetCollisionFlags(ULONG ulFlags);
  inline ULONG GetCollisionFlags(void) const { return en_ulCollisionFlags; };
  inline BOOL IsPredictor(void) const { return en_ulFlags&ENF_PREDICTOR; };
  inline BOOL IsPredicted(void) const { return en_ulFlags&ENF_PREDICTED; };
  inline BOOL IsPredictable(void) const { return en_ulFlags&ENF_PREDICTABLE; };
  CEntity *GetPredictor(void);
  CEntity *GetPredicted(void);
  // become predictable/unpredictable
  void SetPredictable(BOOL bON);
  // check if this instance is head of prediction chain
  BOOL IsPredictionHead(void);
  // get the prediction original (predicted), or self if not predicting
  CEntity *GetPredictionTail(void);
  // check if active for prediction now
  BOOL IsAllowedForPrediction(void) const;
  // check an event for prediction, returns true if already predicted
  BOOL CheckEventPrediction(ULONG ulTypeID, ULONG ulEventID);

  inline enum RenderType GetRenderType(void) { return en_RenderType; };
  inline CEntityClass *GetClass(void) { return en_pecClass; };
  inline CWorld *GetWorld(void) { return en_pwoWorld; };
  inline CBrush3D *GetBrush(void) { return en_pbrBrush; };
  inline CModelObject *GetModelObject(void) { return en_pmoModelObject; };
  inline CModelInstance *GetModelInstance(void) { return en_pmiModelInstance; };
  inline CTerrain *GetTerrain(void) { return en_ptrTerrain; };
  inline CEntity *GetParent(void) { return en_penParent; };
  void SetParent(CEntity *penNewParent);

  // find first child of given class
  CEntity *GetChildOfClass(const char *strClass);

  /* Test if the entity is an empty brush. */
  BOOL IsEmptyBrush(void) const;

  /* Return max Game Players */
  static INDEX GetMaxPlayers(void);
  /* Return Player Entity */
  static CEntity *GetPlayerEntity(INDEX iPlayer);

  /* Get bounding box of this entity - for AI purposes only. */
  void GetBoundingBox(FLOATaabbox3D &box);
  /* Get size of this entity - for UI purposes only. */
  void GetSize(FLOATaabbox3D &box);
  /* Get last positions structure for particles. */
  CLastPositions *GetLastPositions(INDEX ctPositions);
  /* Get nearest position of nearest brush polygon to this entity if available. */
  CBrushPolygon *GetNearestPolygon(FLOAT3D &vPoint, FLOATplane3D &plPlane, FLOAT &fDistanceToEdge);
  /* Get absolute position of point on entity given relative to its size. */
  void GetEntityPointRatio(const FLOAT3D &vRatio, FLOAT3D &vAbsPoint, BOOL bLerped=FALSE);
  /* Get absolute position of point on entity given in meters. */
  void GetEntityPointFixed(const FLOAT3D &vFixed, FLOAT3D &vAbsPoint);
  /* Get sector that given point is in - point must be inside this entity. */
  CBrushSector *GetSectorFromPoint(const FLOAT3D &vPointAbs);

  // map world polygon to/from indices
  CBrushPolygon *GetWorldPolygonPointer(INDEX ibpo);
  INDEX GetWorldPolygonIndex(CBrushPolygon *pbpo);

  // virtual functions that are overridden to implement class specific behavior
  /* Get name of this entity. */
  virtual const CTString &GetName(void) const;
  virtual const CTString &GetDescription(void) const; // name + some more verbose data
  /* Get first target of this entity. */
  virtual CEntity *GetTarget(void) const;
  /* Check if entity can be used as a target. */
  virtual BOOL IsTargetable(void) const;
  /* Check if entity is marker */
  virtual BOOL IsMarker(void) const;
  /* Check if entity is important */
  virtual BOOL IsImportant(void) const;
  /* Check if entity is moved on a route set up by its targets. */
  virtual BOOL MovesByTargetedRoute(CTString &strTargetProperty) const;
  /* Check if entity can drop marker for making linked route. */
  virtual BOOL DropsMarker(CTFileName &fnmMarkerClass, CTString &strTargetProperty) const;
  /* Get light source information - return NULL if not a light source. */
  virtual CLightSource *GetLightSource(void);
  /* Is target valid. */
  virtual BOOL IsTargetValid(SLONG slPropertyOffset, CEntity *penTarget);

  /* Get force type name, return empty string if not used. */
  virtual const CTString &GetForceName(INDEX iForce);
  /* Get forces in given point. */
  virtual void GetForce(INDEX iForce, const FLOAT3D &vPoint,
    CForceStrength &fsGravity, CForceStrength &fsField);
  /* Get entity that controls the force, used for change notification checking. */
  virtual CEntity *GetForceController(INDEX iForce);

  /* Get fog type name, return empty string if not used. */
  virtual const CTString &GetFogName(INDEX iFog);
  /* Get fog, return FALSE for none. */
  virtual BOOL GetFog(INDEX iFog, class CFogParameters &fpFog);

  /* Get haze type name, return empty string if not used. */
  virtual const CTString &GetHazeName(INDEX iHaze);
  /* Get haze, return FALSE for none. */
  virtual BOOL GetHaze(INDEX iHaze, class CHazeParameters &hpHaze, FLOAT3D &vViewDir);

  /* Get mirror type name, return empty string if not used. */
  virtual const CTString &GetMirrorName(INDEX iMirror);
  /* Get mirror, return FALSE for none. */
  virtual BOOL GetMirror(INDEX iMirror, class CMirrorParameters &mpMirror);
  
  /* Get gradient type name, return empty string if not used. */
  virtual const CTString &GetGradientName(INDEX iGradient);
  /* Get gradient, return FALSE for none. */
  virtual BOOL GetGradient(INDEX iGradient, class CGradientParameters &gpGradient);

  /* Get classification box stretching vector. */
  virtual FLOAT3D GetClassificationBoxStretch(void);

  /* Get anim data for given animation property - return NULL for none. */
  virtual CAnimData *GetAnimData(SLONG slPropertyOffset);
  /* Adjust model shading parameters if needed - return TRUE if needs model shadows. */
  virtual BOOL AdjustShadingParameters(FLOAT3D &vLightDirection,
    COLOR &colLight, COLOR &colAmbient);
  /* Adjust model mip factor if needed. */
  virtual void AdjustMipFactor(FLOAT &fMipFactor);
  // get a different model object for rendering - so entity can change its appearance dynamically
  // NOTE: base model is always used for other things (physics, etc).
  virtual CModelObject *GetModelForRendering(void);
  virtual CModelInstance *GetModelInstanceForRendering(void);
  /* Get field information - return NULL if not a field. */
  virtual CFieldSettings *GetFieldSettings(void);
  /* Render particles made by this entity. */
  virtual void RenderParticles(void);
  /* Get current collision box index for this entity. */
  virtual INDEX GetCollisionBoxIndex(void);
  /* Get current collision box - override for custom collision boxes. */
  virtual void GetCollisionBoxParameters(INDEX iBox, FLOATaabbox3D &box, INDEX &iEquality);
  /* Render game view */
  virtual void RenderGameView(CDrawPort *pdp, void *pvUserData);
  // apply mirror and stretch to the entity if supported
  virtual void MirrorAndStretch(FLOAT fStretch, BOOL bMirrorX);
  // get offset for depth-sorting of alpha models (in meters, positive is nearer)
  virtual FLOAT GetDepthSortOffset(void);
  // get visibility tweaking bits
  virtual ULONG GetVisTweaks(void);

  /* Get max tessellation level. */
  virtual FLOAT GetMaxTessellationLevel(void);

  // get/set pointer to your predictor/predicted (autogenerated by ECC feature)
  virtual CEntity *GetPredictionPair(void);
  virtual void SetPredictionPair(CEntity *penPair);

  // add this entity to prediction
  void AddToPrediction(void);
  // called by other entities to set time prediction parameter
  virtual void SetPredictionTime(TIME tmAdvance);   // give time interval in advance to set
  // called by engine to get the upper time limit 
  virtual TIME GetPredictionTime(void);   // return moment in time up to which to predict this entity
  // get maximum allowed range for predicting this entity
  virtual FLOAT GetPredictionRange(void);
  // add to prediction entities that this entity depends on
  virtual void AddDependentsToPrediction(void);
  // copy for prediction
  virtual void CopyForPrediction(CEntity &enOrg);

  /* Send an event to this entity. */
  void SendEvent(const CEntityEvent &ee);
  /* Send an event to all entities in a box (box must be around this entity). */
  void SendEventInRange(const CEntityEvent &ee, const FLOATaabbox3D &boxRange);

  /* apply some damage to the entity (see event EDamage for more info) */
  virtual void ReceiveDamage(CEntity *penInflictor, enum DamageType dmtType,
    FLOAT fDamageAmmount, const FLOAT3D &vHitPoint, const FLOAT3D &vDirection);

  /* Receive item through event - for AI purposes only */
  virtual BOOL ReceiveItem(const CEntityEvent &ee);
  /* Get entity info - for AI purposes only */
  virtual void *GetEntityInfo(void);
  /* Fill in entity statistics - for AI purposes only */
  virtual BOOL FillEntityStatistics(struct EntityStats *pes);

  /* Model change notify */
  void ModelChangeNotify(void);
  /* Terrain change notify */ 
  void TerrainChangeNotify(void);
};

// check if entity is of given class
BOOL ENGINE_API IsOfClass(CEntity *pen, const char *pstrClassName);
BOOL ENGINE_API IsOfSameClass(CEntity *pen1, CEntity *pen2);
// check if entity is of given class or derived from
BOOL ENGINE_API IsDerivedFromClass(CEntity *pen, const char *pstrClassName);

// all standard smart pointer functions are here as inlines
inline CEntityPointer::CEntityPointer(void) : ep_pen(NULL) {};
inline CEntityPointer::~CEntityPointer(void) { ep_pen->RemReference(); };
inline CEntityPointer::CEntityPointer(const CEntityPointer &penOther) : ep_pen(penOther.ep_pen) {
  ep_pen->AddReference(); };
inline CEntityPointer::CEntityPointer(CEntity *pen) : ep_pen(pen) {
  ep_pen->AddReference(); };
inline const CEntityPointer &CEntityPointer::operator=(CEntity *pen) {
  pen->AddReference();    // must first add, then remove!
  ep_pen->RemReference();
  ep_pen = pen;
  return *this;
}
inline const CEntityPointer &CEntityPointer::operator=(const CEntityPointer &penOther) {
  penOther.ep_pen->AddReference();    // must first add, then remove!
  ep_pen->RemReference();
  ep_pen = penOther.ep_pen;
  return *this;
}
inline CEntity* CEntityPointer::operator->(void) const { return ep_pen; }
inline CEntityPointer::operator CEntity*(void) const { return ep_pen; }
inline CEntity& CEntityPointer::operator*(void) const { return *ep_pen; }

/////////////////////////////////////////////////////////////////////
// Reference counting functions
inline void CEntity::AddReference(void) { 
  if (this!=NULL) {
    ASSERT(en_ctReferences>=0);
    en_ctReferences++; 
  }
};
inline void CEntity::RemReference(void) { 
  if (this!=NULL) {
    ASSERT(en_ctReferences>0);
    en_ctReferences--;
    if(en_ctReferences==0) {
      delete this;
    }
  }
};

/*
 * Entity that is alive (has health).
 */
class ENGINE_API CLiveEntity : public CEntity {
public:
  FLOAT en_fHealth;            // health of the entity

  /* Copy entity from another entity of same class. */
  virtual void Copy(CEntity &enOther, ULONG ulFlags);
  /* Read from stream. */
  virtual void Read_t( CTStream *istr);  // throw char *
  /* Write to stream. */
  virtual void Write_t( CTStream *ostr); // throw char *
public:
  /* Set health of the entity. (Use only for initialization!) */
  void SetHealth(FLOAT fHealth) { en_fHealth = fHealth; };

public:
  /* Constructor. */
  CLiveEntity(void);

  /* Get health of the entity. */
  FLOAT GetHealth(void) const { return en_fHealth; };
  // apply some damage to the entity (see event EDamage for more info)
  virtual void ReceiveDamage(CEntity *penInflictor, enum DamageType dmtType,
    FLOAT fDamageAmmount, const FLOAT3D &vHitPoint, const FLOAT3D &vDirection);

  // returns bytes of memory used by this object
  inline SLONG GetUsedMemory(void) {
    return( sizeof(CLiveEntity) - sizeof(CEntity) + CEntity::GetUsedMemory());
  };
};

// flag for entities that are not waiting for thinking
#define THINKTIME_NEVER (-1.f)

/*
 * Entity that can percept things and make decisions (one that has its own AI).
 */
class ENGINE_API CRationalEntity : public CLiveEntity {
public:
  CListNode en_lnInTimers;    // node in list of waiting timers - sorted by wait time
public:
  TIME en_timeTimer;          // moment in time this entity waits for timer

  CStaticStackArray<SLONG> en_stslStateStack; // stack of states for entity AI

  /* Calculate physics for moving. */
  virtual void ClearMovingTemp(void);
  virtual void PreMoving(void);
  virtual void DoMoving(void);
  virtual void PostMoving(void);
  // create a checksum value for sync-check
  virtual void ChecksumForSync(ULONG &ulCRC, INDEX iExtensiveSyncCheck);
  // dump sync data to text file
  virtual void DumpSync_t(CTStream &strm, INDEX iExtensiveSyncCheck);  // throw char *

  /* Copy entity from another entity of same class. */
  virtual void Copy(CEntity &enOther, ULONG ulFlags);
  /* Read from stream. */
  virtual void Read_t( CTStream *istr);  // throw char *
  /* Write to stream. */
  virtual void Write_t( CTStream *ostr); // throw char *

  /* Unwind stack to a given state. */
  void UnwindStack(SLONG slThisState);

public:

  /* Jump to a new state. */
  void Jump(SLONG slThisState, SLONG slTargetState, BOOL bOverride, const CEntityEvent &eeInput);
  /* Call a subautomaton. */
  void Call(SLONG slThisState, SLONG slTargetState, BOOL bOverride, const CEntityEvent &eeInput);
  /* Return from a subautomaton. */
  void Return(SLONG slThisState, const CEntityEvent &eeReturn);
  // print stack to debug output
  const char *PrintStackDebug(void);

  /* Set next timer event to occur at given moment time. */
  void SetTimerAt(TIME timeAbsolute);
  /* Set next timer event to occur after given time has elapsed. */
  void SetTimerAfter(TIME timeDelta);
  /* Cancel eventual pending timer. */
  void UnsetTimer(void);

  /* Called after creating and setting its properties. */
  virtual void OnInitialize(const CEntityEvent &eeInput);
  /* Called before releasing entity. */
  virtual void OnEnd(void);
public:
  /* Constructor. */
  CRationalEntity(void);

  /* Handle an event - return false if event was not handled. */
  virtual BOOL HandleEvent(const CEntityEvent &ee);

  // returns bytes of memory used by this object
  inline SLONG GetUsedMemory(void) {
    SLONG slUsedMemory = sizeof(CRationalEntity) - sizeof(CLiveEntity) + CLiveEntity::GetUsedMemory();
    slUsedMemory += en_stslStateStack.sa_Count * sizeof(SLONG);
    return slUsedMemory;
  };
};


ENGINE_API void EntityAdjustBonesCallback(void *pData);
ENGINE_API void EntityAdjustShaderParamsCallback(void *pData,INDEX iSurfaceID,CShader *pShader,ShaderParams &spParams);

extern "C" ENGINE_API class CDLLEntityClass CEntity_DLLClass;
extern "C" ENGINE_API class CDLLEntityClass CLiveEntity_DLLClass;
extern "C" ENGINE_API class CDLLEntityClass CRationalEntity_DLLClass;


#endif  /* include-once check. */