/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code 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. Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #ifndef __GAME_ENTITY_H__ #define __GAME_ENTITY_H__ /* =============================================================================== Game entity base class. =============================================================================== */ static const int DELAY_DORMANT_TIME = 3000; extern const idEventDef EV_PostSpawn; extern const idEventDef EV_FindTargets; extern const idEventDef EV_Touch; extern const idEventDef EV_Use; extern const idEventDef EV_Activate; extern const idEventDef EV_ActivateTargets; extern const idEventDef EV_Hide; extern const idEventDef EV_Show; extern const idEventDef EV_GetShaderParm; extern const idEventDef EV_SetShaderParm; extern const idEventDef EV_SetOwner; extern const idEventDef EV_GetAngles; extern const idEventDef EV_SetAngles; extern const idEventDef EV_SetLinearVelocity; extern const idEventDef EV_SetAngularVelocity; extern const idEventDef EV_SetSkin; extern const idEventDef EV_StartSoundShader; extern const idEventDef EV_StopSound; extern const idEventDef EV_CacheSoundShader; // Think flags enum { TH_ALL = -1, TH_THINK = 1, // run think function each frame TH_PHYSICS = 2, // run physics each frame TH_ANIMATE = 4, // update animation each frame TH_UPDATEVISUALS = 8, // update renderEntity TH_UPDATEPARTICLES = 16 }; // // Signals // make sure to change script/doom_defs.script if you add any, or change their order // typedef enum { SIG_TOUCH, // object was touched SIG_USE, // object was used SIG_TRIGGER, // object was activated SIG_REMOVED, // object was removed from the game SIG_DAMAGE, // object was damaged SIG_BLOCKED, // object was blocked SIG_MOVER_POS1, // mover at position 1 (door closed) SIG_MOVER_POS2, // mover at position 2 (door open) SIG_MOVER_1TO2, // mover changing from position 1 to 2 SIG_MOVER_2TO1, // mover changing from position 2 to 1 NUM_SIGNALS } signalNum_t; // FIXME: At some point we may want to just limit it to one thread per signal, but // for now, I'm allowing multiple threads. We should reevaluate this later in the project #define MAX_SIGNAL_THREADS 16 // probably overkill, but idList uses a granularity of 16 struct signal_t { int threadnum; const function_t* function; }; class signalList_t { public: idList signal[ NUM_SIGNALS ]; }; /* ================================================ idNetEvent Utility for detecting a bool state change: -server calls ::Set -client ::Get will return true (once only) Useful because: -Hides client from having to manually declare "last" state and manually checking against it -using int counter prevents problems w/ dropped snapshots (ie if we just serialized a bool to true for a single ss, if that ss is dropped,skipped,whatever the client would never handle it. By incrementing a wrapped counter, we are guaranteed to detect the state change no matter what happens at the net layer). ================================================ */ template < int max > struct idNetEvent { idNetEvent() : count( 0 ), lastCount( 0 ) { } void Set() { count = ( ( count + 1 ) % max ); } bool Get() { if( count != lastCount ) { lastCount = count; return true; } return false; } void Serialize( idSerializer& ser ) { if( count >= max ) { idLib::Warning( "idNetEvent. count %d > max %d", count, max ); } ser.SerializeUMax( count, max ); } public: static const int Maximum = max; int count; int lastCount; }; typedef idNetEvent< 7 > netBoolEvent_t; inline void WriteToBitMsg( const netBoolEvent_t& netEvent, idBitMsg& msg ) { msg.WriteBits( netEvent.count, idMath::BitsForInteger( netBoolEvent_t::Maximum ) ); assert( netEvent.count <= netBoolEvent_t::Maximum ); } inline void ReadFromBitMsg( netBoolEvent_t& netEvent, const idBitMsg& msg ) { netEvent.count = msg.ReadBits( idMath::BitsForInteger( netBoolEvent_t::Maximum ) ); assert( netEvent.count <= netBoolEvent_t::Maximum ); } class idEntity : public idClass { public: static const int MAX_PVS_AREAS = 4; static const uint32 INVALID_PREDICTION_KEY = 0xFFFFFFFF; int entityNumber; // index into the entity list int entityDefNumber; // index into the entity def list idLinkList spawnNode; // for being linked into spawnedEntities list idLinkList activeNode; // for being linked into activeEntities list idLinkList aimAssistNode; // linked into gameLocal.aimAssistEntities idLinkList snapshotNode; // for being linked into snapshotEntities list int snapshotChanged; // used to detect snapshot state changes int snapshotBits; // number of bits this entity occupied in the last snapshot bool snapshotStale; // Set to true if this entity is considered stale in the snapshot idStr name; // name of entity idDict spawnArgs; // key/value pairs used to spawn and initialize entity idScriptObject scriptObject; // contains all script defined data for this entity int thinkFlags; // TH_? flags int dormantStart; // time that the entity was first closed off from player bool cinematic; // during cinematics, entity will only think if cinematic is set renderView_t* renderView; // for camera views from this entity idEntity* cameraTarget; // any remoteRenderMap shaders will use this idList< idEntityPtr, TAG_ENTITY > targets; // when this entity is activated these entities entity are activated int health; // FIXME: do all objects really need health? struct entityFlags_s { bool notarget : 1; // if true never attack or target this entity bool noknockback : 1; // if true no knockback from hits bool takedamage : 1; // if true this entity can be damaged bool hidden : 1; // if true this entity is not visible bool bindOrientated : 1; // if true both the master orientation is used for binding bool solidForTeam : 1; // if true this entity is considered solid when a physics team mate pushes entities bool forcePhysicsUpdate : 1; // if true always update from the physics whether the object moved or not bool selected : 1; // if true the entity is selected for editing bool neverDormant : 1; // if true the entity never goes dormant bool isDormant : 1; // if true the entity is dormant bool hasAwakened : 1; // before a monster has been awakened the first time, use full PVS for dormant instead of area-connected bool networkSync : 1; // if true the entity is synchronized over the network bool grabbed : 1; // if true object is currently being grabbed bool skipReplication : 1; // don't replicate this entity over the network. } fl; int timeGroup; bool noGrab; renderEntity_t xrayEntity; qhandle_t xrayEntityHandle; const idDeclSkin* xraySkin; void DetermineTimeGroup( bool slowmo ); void SetGrabbedState( bool grabbed ); bool IsGrabbed(); public: ABSTRACT_PROTOTYPE( idEntity ); idEntity(); ~idEntity(); void Spawn(); void Save( idSaveGame* savefile ) const; void Restore( idRestoreGame* savefile ); const char* GetEntityDefName() const; void SetName( const char* name ); const char* GetName() const; virtual void UpdateChangeableSpawnArgs( const idDict* source ); int GetEntityNumber() const { return entityNumber; } // clients generate views based on all the player specific options, // cameras have custom code, and everything else just uses the axis orientation virtual renderView_t* GetRenderView(); // thinking virtual void Think(); bool CheckDormant(); // dormant == on the active list, but out of PVS virtual void DormantBegin(); // called when entity becomes dormant virtual void DormantEnd(); // called when entity wakes from being dormant bool IsActive() const; void BecomeActive( int flags ); void BecomeInactive( int flags ); void UpdatePVSAreas( const idVec3& pos ); void BecomeReplicated(); // visuals virtual void Present(); virtual renderEntity_t* GetRenderEntity(); virtual int GetModelDefHandle(); virtual void SetModel( const char* modelname ); void SetSkin( const idDeclSkin* skin ); const idDeclSkin* GetSkin() const; void SetShaderParm( int parmnum, float value ); virtual void SetColor( float red, float green, float blue ); virtual void SetColor( const idVec3& color ); virtual void GetColor( idVec3& out ) const; virtual void SetColor( const idVec4& color ); virtual void GetColor( idVec4& out ) const; virtual void FreeModelDef(); virtual void FreeLightDef(); virtual void Hide(); virtual void Show(); bool IsHidden() const; void UpdateVisuals(); void UpdateModel(); void UpdateModelTransform(); virtual void ProjectOverlay( const idVec3& origin, const idVec3& dir, float size, const char* material ); int GetNumPVSAreas(); const int* GetPVSAreas(); void ClearPVSAreas(); bool PhysicsTeamInPVS( pvsHandle_t pvsHandle ); // animation virtual bool UpdateAnimationControllers(); bool UpdateRenderEntity( renderEntity_s* renderEntity, const renderView_t* renderView ); static bool ModelCallback( renderEntity_s* renderEntity, const renderView_t* renderView ); virtual idAnimator* GetAnimator(); // returns animator object used by this entity // sound virtual bool CanPlayChatterSounds() const; bool StartSound( const char* soundName, const s_channelType channel, int soundShaderFlags, bool broadcast, int* length ); bool StartSoundShader( const idSoundShader* shader, const s_channelType channel, int soundShaderFlags, bool broadcast, int* length ); void StopSound( const s_channelType channel, bool broadcast ); // pass SND_CHANNEL_ANY to stop all sounds void SetSoundVolume( float volume ); void UpdateSound(); int GetListenerId() const; idSoundEmitter* GetSoundEmitter() const; void FreeSoundEmitter( bool immediate ); // entity binding virtual void PreBind(); virtual void PostBind(); virtual void PreUnbind(); virtual void PostUnbind(); void JoinTeam( idEntity* teammember ); void Bind( idEntity* master, bool orientated ); void BindToJoint( idEntity* master, const char* jointname, bool orientated ); void BindToJoint( idEntity* master, jointHandle_t jointnum, bool orientated ); void BindToBody( idEntity* master, int bodyId, bool orientated ); void Unbind(); bool IsBound() const; bool IsBoundTo( idEntity* master ) const; idEntity* GetBindMaster() const; jointHandle_t GetBindJoint() const; int GetBindBody() const; idEntity* GetTeamMaster() const; idEntity* GetNextTeamEntity() const; void ConvertLocalToWorldTransform( idVec3& offset, idMat3& axis ); idVec3 GetLocalVector( const idVec3& vec ) const; idVec3 GetLocalCoordinates( const idVec3& vec ) const; idVec3 GetWorldVector( const idVec3& vec ) const; idVec3 GetWorldCoordinates( const idVec3& vec ) const; bool GetMasterPosition( idVec3& masterOrigin, idMat3& masterAxis ) const; void GetWorldVelocities( idVec3& linearVelocity, idVec3& angularVelocity ) const; // physics // set a new physics object to be used by this entity void SetPhysics( idPhysics* phys ); // get the physics object used by this entity idPhysics* GetPhysics() const; // restore physics pointer for save games void RestorePhysics( idPhysics* phys ); // run the physics for this entity bool RunPhysics(); // Interpolates the physics, used on MP clients. void InterpolatePhysics( const float fraction ); // InterpolatePhysics actually calls evaluate, this version doesn't. void InterpolatePhysicsOnly( const float fraction, bool updateTeam = false ); // set the origin of the physics object (relative to bindMaster if not NULL) void SetOrigin( const idVec3& org ); // set the axis of the physics object (relative to bindMaster if not NULL) void SetAxis( const idMat3& axis ); // use angles to set the axis of the physics object (relative to bindMaster if not NULL) void SetAngles( const idAngles& ang ); // get the floor position underneath the physics object bool GetFloorPos( float max_dist, idVec3& floorpos ) const; // retrieves the transformation going from the physics origin/axis to the visual origin/axis virtual bool GetPhysicsToVisualTransform( idVec3& origin, idMat3& axis ); // retrieves the transformation going from the physics origin/axis to the sound origin/axis virtual bool GetPhysicsToSoundTransform( idVec3& origin, idMat3& axis ); // called from the physics object when colliding, should return true if the physics simulation should stop virtual bool Collide( const trace_t& collision, const idVec3& velocity ); // retrieves impact information, 'ent' is the entity retrieving the info virtual void GetImpactInfo( idEntity* ent, int id, const idVec3& point, impactInfo_t* info ); // apply an impulse to the physics object, 'ent' is the entity applying the impulse virtual void ApplyImpulse( idEntity* ent, int id, const idVec3& point, const idVec3& impulse ); // add a force to the physics object, 'ent' is the entity adding the force virtual void AddForce( idEntity* ent, int id, const idVec3& point, const idVec3& force ); // activate the physics object, 'ent' is the entity activating this entity virtual void ActivatePhysics( idEntity* ent ); // returns true if the physics object is at rest virtual bool IsAtRest() const; // returns the time the physics object came to rest virtual int GetRestStartTime() const; // add a contact entity virtual void AddContactEntity( idEntity* ent ); // remove a touching entity virtual void RemoveContactEntity( idEntity* ent ); // damage // returns true if this entity can be damaged from the given origin virtual bool CanDamage( const idVec3& origin, idVec3& damagePoint ) const; // applies damage to this entity virtual void Damage( idEntity* inflictor, idEntity* attacker, const idVec3& dir, const char* damageDefName, const float damageScale, const int location ); // adds a damage effect like overlays, blood, sparks, debris etc. virtual void AddDamageEffect( const trace_t& collision, const idVec3& velocity, const char* damageDefName ); // callback function for when another entity received damage from this entity. damage can be adjusted and returned to the caller. virtual void DamageFeedback( idEntity* victim, idEntity* inflictor, int& damage ); // notifies this entity that it is in pain virtual bool Pain( idEntity* inflictor, idEntity* attacker, int damage, const idVec3& dir, int location ); // notifies this entity that is has been killed virtual void Killed( idEntity* inflictor, idEntity* attacker, int damage, const idVec3& dir, int location ); // scripting virtual bool ShouldConstructScriptObjectAtSpawn() const; virtual idThread* ConstructScriptObject(); virtual void DeconstructScriptObject(); void SetSignal( signalNum_t signalnum, idThread* thread, const function_t* function ); void ClearSignal( idThread* thread, signalNum_t signalnum ); void ClearSignalThread( signalNum_t signalnum, idThread* thread ); bool HasSignal( signalNum_t signalnum ) const; void Signal( signalNum_t signalnum ); void SignalEvent( idThread* thread, signalNum_t signalnum ); // gui void TriggerGuis(); bool HandleGuiCommands( idEntity* entityGui, const char* cmds ); virtual bool HandleSingleGuiCommand( idEntity* entityGui, idLexer* src ); // targets void FindTargets(); void RemoveNullTargets(); void ActivateTargets( idEntity* activator ) const; // misc virtual void Teleport( const idVec3& origin, const idAngles& angles, idEntity* destination ); bool TouchTriggers() const; idCurve_Spline* GetSpline() const; virtual void ShowEditingDialog(); enum { EVENT_STARTSOUNDSHADER, EVENT_STOPSOUNDSHADER, EVENT_MAXEVENTS }; // Called on clients in an MP game, does the actual interpolation for the entity. // This function will eventually replace ClientPredictionThink completely. virtual void ClientThink( const int curTime, const float fraction, const bool predict ); virtual void ClientPredictionThink(); virtual void WriteToSnapshot( idBitMsg& msg ) const; void ReadFromSnapshot_Ex( const idBitMsg& msg ); virtual void ReadFromSnapshot( const idBitMsg& msg ); virtual bool ServerReceiveEvent( int event, int time, const idBitMsg& msg ); virtual bool ClientReceiveEvent( int event, int time, const idBitMsg& msg ); void WriteBindToSnapshot( idBitMsg& msg ) const; void ReadBindFromSnapshot( const idBitMsg& msg ); void WriteColorToSnapshot( idBitMsg& msg ) const; void ReadColorFromSnapshot( const idBitMsg& msg ); void WriteGUIToSnapshot( idBitMsg& msg ) const; void ReadGUIFromSnapshot( const idBitMsg& msg ); void ServerSendEvent( int eventId, const idBitMsg* msg, bool saveEvent, lobbyUserID_t excluding = lobbyUserID_t() ) const; void ClientSendEvent( int eventId, const idBitMsg* msg ) const; void SetUseClientInterpolation( bool use ) { useClientInterpolation = use; } void SetSkipReplication( const bool skip ) { fl.skipReplication = skip; } bool GetSkipReplication() const { return fl.skipReplication; } bool IsReplicated() const { return GetEntityNumber() < ENTITYNUM_FIRST_NON_REPLICATED; } void CreateDeltasFromOldOriginAndAxis( const idVec3& oldOrigin, const idMat3& oldAxis ); void DecayOriginAndAxisDelta(); uint32 GetPredictedKey() { return predictionKey; } void SetPredictedKey( uint32 key_ ) { predictionKey = key_; } void FlagNewSnapshot(); idEntity* GetTeamChain() { return teamChain; } // It is only safe to interpolate if this entity has received two snapshots. enum interpolationBehavior_t { USE_NO_INTERPOLATION, USE_LATEST_SNAP_ONLY, USE_INTERPOLATION }; interpolationBehavior_t GetInterpolationBehavior() const { return interpolationBehavior; } unsigned int GetNumSnapshotsReceived() const { return snapshotsReceived; } protected: renderEntity_t renderEntity; // used to present a model to the renderer int modelDefHandle; // handle to static renderer model refSound_t refSound; // used to present sound to the audio engine idVec3 GetOriginDelta() const { return originDelta; } idMat3 GetAxisDelta() const { return axisDelta; } private: idPhysics_Static defaultPhysicsObj; // default physics object idPhysics* physics; // physics used for this entity idEntity* bindMaster; // entity bound to if unequal NULL jointHandle_t bindJoint; // joint bound to if unequal INVALID_JOINT int bindBody; // body bound to if unequal -1 idEntity* teamMaster; // master of the physics team idEntity* teamChain; // next entity in physics team bool useClientInterpolation; // disables interpolation for some objects (handy for weapon world models) int numPVSAreas; // number of renderer areas the entity covers int PVSAreas[MAX_PVS_AREAS]; // numbers of the renderer areas the entity covers signalList_t* signals; int mpGUIState; // local cache to avoid systematic SetStateInt uint32 predictionKey; // Unique key used to sync predicted ents (projectiles) in MP. // Delta values that are set when the server or client disagree on where the render model should be. If this happens, // they resolve it through DecayOriginAndAxisDelta() idVec3 originDelta; idMat3 axisDelta; interpolationBehavior_t interpolationBehavior; unsigned int snapshotsReceived; private: void FixupLocalizedStrings(); bool DoDormantTests(); // dormant == on the active list, but out of PVS // physics // initialize the default physics void InitDefaultPhysics( const idVec3& origin, const idMat3& axis ); // update visual position from the physics void UpdateFromPhysics( bool moveBack ); // get physics timestep virtual int GetPhysicsTimeStep() const; // entity binding bool InitBind( idEntity* master ); // initialize an entity binding void FinishBind(); // finish an entity binding void RemoveBinds(); // deletes any entities bound to this object void QuitTeam(); // leave the current team void UpdatePVSAreas(); // events void Event_GetName(); void Event_SetName( const char* name ); void Event_FindTargets(); void Event_ActivateTargets( idEntity* activator ); void Event_NumTargets(); void Event_GetTarget( float index ); void Event_RandomTarget( const char* ignore ); void Event_Bind( idEntity* master ); void Event_BindPosition( idEntity* master ); void Event_BindToJoint( idEntity* master, const char* jointname, float orientated ); void Event_Unbind(); void Event_RemoveBinds(); void Event_SpawnBind(); void Event_SetOwner( idEntity* owner ); void Event_SetModel( const char* modelname ); void Event_SetSkin( const char* skinname ); void Event_GetShaderParm( int parmnum ); void Event_SetShaderParm( int parmnum, float value ); void Event_SetShaderParms( float parm0, float parm1, float parm2, float parm3 ); void Event_SetColor( float red, float green, float blue ); void Event_GetColor(); void Event_IsHidden(); void Event_Hide(); void Event_Show(); void Event_CacheSoundShader( const char* soundName ); void Event_StartSoundShader( const char* soundName, int channel ); void Event_StopSound( int channel, int netSync ); void Event_StartSound( const char* soundName, int channel, int netSync ); void Event_FadeSound( int channel, float to, float over ); void Event_GetWorldOrigin(); void Event_SetWorldOrigin( idVec3 const& org ); void Event_GetOrigin(); void Event_SetOrigin( const idVec3& org ); void Event_GetAngles(); void Event_SetAngles( const idAngles& ang ); void Event_SetLinearVelocity( const idVec3& velocity ); void Event_GetLinearVelocity(); void Event_SetAngularVelocity( const idVec3& velocity ); void Event_GetAngularVelocity(); void Event_SetSize( const idVec3& mins, const idVec3& maxs ); void Event_GetSize(); void Event_GetMins(); void Event_GetMaxs(); void Event_Touches( idEntity* ent ); void Event_SetGuiParm( const char* key, const char* val ); void Event_SetGuiFloat( const char* key, float f ); void Event_GetNextKey( const char* prefix, const char* lastMatch ); void Event_SetKey( const char* key, const char* value ); void Event_GetKey( const char* key ); void Event_GetIntKey( const char* key ); void Event_GetFloatKey( const char* key ); void Event_GetVectorKey( const char* key ); void Event_GetEntityKey( const char* key ); void Event_RestorePosition(); void Event_UpdateCameraTarget(); void Event_DistanceTo( idEntity* ent ); void Event_DistanceToPoint( const idVec3& point ); void Event_StartFx( const char* fx ); void Event_WaitFrame(); void Event_Wait( float time ); void Event_HasFunction( const char* name ); void Event_CallFunction( const char* name ); void Event_SetNeverDormant( int enable ); void Event_SetGui( int guiNum, const char* guiName ); void Event_PrecacheGui( const char* guiName ); void Event_GetGuiParm( int guiNum, const char* key ); void Event_GetGuiParmFloat( int guiNum, const char* key ); void Event_GuiNamedEvent( int guiNum, const char* event ); }; /* =============================================================================== Animated entity base class. =============================================================================== */ typedef struct damageEffect_s { jointHandle_t jointNum; idVec3 localOrigin; idVec3 localNormal; int time; const idDeclParticle* type; struct damageEffect_s* next; } damageEffect_t; class idAnimatedEntity : public idEntity { public: CLASS_PROTOTYPE( idAnimatedEntity ); idAnimatedEntity(); ~idAnimatedEntity(); void Save( idSaveGame* savefile ) const; void Restore( idRestoreGame* savefile ); virtual void ClientPredictionThink(); virtual void ClientThink( const int curTime, const float fraction, const bool predict ); virtual void Think(); void UpdateAnimation(); virtual idAnimator* GetAnimator(); virtual void SetModel( const char* modelname ); bool GetJointWorldTransform( jointHandle_t jointHandle, int currentTime, idVec3& offset, idMat3& axis ); bool GetJointTransformForAnim( jointHandle_t jointHandle, int animNum, int currentTime, idVec3& offset, idMat3& axis ) const; virtual int GetDefaultSurfaceType() const; virtual void AddDamageEffect( const trace_t& collision, const idVec3& velocity, const char* damageDefName ); void AddLocalDamageEffect( jointHandle_t jointNum, const idVec3& localPoint, const idVec3& localNormal, const idVec3& localDir, const idDeclEntityDef* def, const idMaterial* collisionMaterial ); void UpdateDamageEffects(); virtual bool ClientReceiveEvent( int event, int time, const idBitMsg& msg ); enum { EVENT_ADD_DAMAGE_EFFECT = idEntity::EVENT_MAXEVENTS, EVENT_MAXEVENTS }; protected: idAnimator animator; damageEffect_t* damageEffects; private: void Event_GetJointHandle( const char* jointname ); void Event_ClearAllJoints(); void Event_ClearJoint( jointHandle_t jointnum ); void Event_SetJointPos( jointHandle_t jointnum, jointModTransform_t transform_type, const idVec3& pos ); void Event_SetJointAngle( jointHandle_t jointnum, jointModTransform_t transform_type, const idAngles& angles ); void Event_GetJointPos( jointHandle_t jointnum ); void Event_GetJointAngle( jointHandle_t jointnum ); }; class SetTimeState { bool activated; bool previousFast; bool fast; public: SetTimeState(); SetTimeState( int timeGroup ); ~SetTimeState(); void PushState( int timeGroup ); }; ID_INLINE SetTimeState::SetTimeState() { activated = false; } ID_INLINE SetTimeState::SetTimeState( int timeGroup ) { activated = false; PushState( timeGroup ); } ID_INLINE void SetTimeState::PushState( int timeGroup ) { // Don't mess with time in Multiplayer if( !common->IsMultiplayer() ) { activated = true; // determine previous fast setting if( gameLocal.time == gameLocal.slow.time ) { previousFast = false; } else { previousFast = true; } // determine new fast setting if( timeGroup ) { fast = true; } else { fast = false; } // set correct time gameLocal.SelectTimeGroup( timeGroup ); } } ID_INLINE SetTimeState::~SetTimeState() { if( activated && !common->IsMultiplayer() ) { // set previous correct time gameLocal.SelectTimeGroup( previousFast ); } } #endif /* !__GAME_ENTITY_H__ */