Added typeinfogen with state system

This commit is contained in:
Justin Marshall
2026-05-18 21:35:07 -07:00
parent 4686349b62
commit b841fcb33b
27 changed files with 21899 additions and 194 deletions
+1
View File
@@ -90,6 +90,7 @@ class idLocationEntity;
void gameError( const char *fmt, ... );
#include "gamesys/Event.h"
#include "gamesys/State.h"
#include "gamesys/Class.h"
#include "gamesys/SysCvar.h"
#include "gamesys/SysCmds.h"
-2
View File
@@ -747,8 +747,6 @@ public:
private:
void Event_Activate( idEntity *activator );
void Event_Throw( void );
void Event_ShakeObject( idEntity *object, int starttime );
int end_time;
float throw_time;
-2
View File
@@ -229,8 +229,6 @@ public:
protected:
virtual void DoneMoving( void );
virtual void BeginMove( idThread *thread = NULL );
void SpawnTrigger( const idVec3 &pos );
void GetLocalTriggerPosition();
void Event_Touch( idEntity *other, trace_t *trace );
private:
-2
View File
@@ -639,7 +639,6 @@ private:
idAngles GunTurningOffset( void );
idVec3 GunAcceleratingOffset( void );
void UseObjects( void );
void CrashLand( const idVec3 &oldOrigin, const idVec3 &oldVelocity );
void BobCycle( const idVec3 &pushVelocity );
void UpdateViewAngles( void );
@@ -676,7 +675,6 @@ private:
void Event_SelectWeapon( const char *weaponName );
void Event_GetWeaponEntity( void );
void Event_OpenPDA( void );
void Event_PDAAvailable( void );
void Event_InPDA( void );
void Event_ExitTeleporter( void );
void Event_HideTip( void );
-1
View File
@@ -221,7 +221,6 @@ private:
void FreeBeams();
void Event_RemoveBeams();
void ApplyDamage();
};
/*
+10 -2
View File
@@ -422,7 +422,6 @@ protected:
void Think(void);
void Activate(idEntity* activator);
int ReactionTo(const idEntity* ent);
bool CheckForEnemy(void);
void EnemyDead(void);
virtual bool CanPlayChatterSounds(void) const;
void SetChatSound(void);
@@ -485,7 +484,6 @@ protected:
// effects
const idDeclParticle* SpawnParticlesOnJoint(particleEmitter_t& pe, const char* particleName, const char* jointName);
void SpawnParticles(const char* keyName);
bool ParticlesActive(void);
// turning
bool FacingIdeal(void);
@@ -801,4 +799,14 @@ private:
void Event_MarkUsed(void);
};
class idAI_Vagary : public idAI {
public:
CLASS_PROTOTYPE(idAI_Vagary);
private:
void Event_ChooseObjectToThrow(const idVec3& mins, const idVec3& maxs, float speed, float minDist, float offset);
void Event_ThrowObjectAtEnemy(idEntity* ent, float speed);
};
#endif /* !__AI_H__ */
-9
View File
@@ -38,15 +38,6 @@ Vagary specific AI code
#include "../Game_local.h"
class idAI_Vagary : public idAI {
public:
CLASS_PROTOTYPE( idAI_Vagary );
private:
void Event_ChooseObjectToThrow( const idVec3 &mins, const idVec3 &maxs, float speed, float minDist, float offset );
void Event_ThrowObjectAtEnemy( idEntity *ent, float speed );
};
const idEventDef AI_Vagary_ChooseObjectToThrow( "vagary_ChooseObjectToThrow", "vvfff", 'e' );
const idEventDef AI_Vagary_ThrowObjectAtEnemy( "vagary_ThrowObjectAtEnemy", "ef" );
+24
View File
@@ -97,6 +97,8 @@ public: \
static idClass *CreateInstance( void ); \
virtual idTypeInfo *GetType( void ) const; \
static idTypeInfo &GetClassType( void ); \
virtual intptr_t Invoke(const char* functionName, void *param1) override; \
virtual bool HasNativeFunction(const char* functionName) override; \
static idEventFunc<nameofclass> eventCallbacks[]
/*
@@ -205,6 +207,28 @@ public:
const char* GetSuperclass(void) const;
void FindUninitializedMemory(void);
// jmarshall
template< typename T >
T* Cast(void)
{
return this ? (IsType(T::Type) ? static_cast<T*>(this) : NULL) : NULL;
}
template< typename T >
const T* Cast(void) const
{
return this ? (IsType(T::Type) ? static_cast<const T*>(this) : NULL) : NULL;
}
virtual void StateThreadChanged(void) {};
virtual idClass* InvokeChild()
{
return NULL;
}
virtual intptr_t Invoke(const char* functionName, void* param1);
virtual bool HasNativeFunction(const char* functionName);
// jmarshall end
void Save(idSaveGame* savefile) const {};
void Restore(idRestoreGame* savefile) {};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+496
View File
@@ -0,0 +1,496 @@
#include "precompiled.h"
#include "../Game_local.h"
const int HISTORY_COUNT = 50;
/*
=====================
stateParms_t::Wait
=====================
*/
void stateParms_t::Wait( float seconds )
{
time = gameLocal.time + SEC2MS( seconds );
}
/*
=====================
stateParms_t::Save
=====================
*/
void stateParms_t::Save( idSaveGame* saveFile ) const
{
saveFile->WriteInt( blendFrames );
saveFile->WriteInt( time );
saveFile->WriteInt( stage );
}
/*
=====================
stateParms_t::Restore
=====================
*/
void stateParms_t::Restore( idRestoreGame* saveFile )
{
saveFile->ReadInt( blendFrames );
saveFile->ReadInt( time );
saveFile->ReadInt( stage );
}
/*
=====================
stateCall_t::Save
=====================
*/
void stateCall_t::Save( idSaveGame* saveFile ) const
{
saveFile->WriteString( state );
// TOSAVE: idLinkList<stateCall_t> node;
saveFile->WriteInt( flags );
saveFile->WriteInt( delay );
parms.Save( saveFile );
}
/*
=====================
stateCall_t::Save
=====================
*/
void stateCall_t::Restore( idRestoreGame* saveFile, const idClass* owner )
{
saveFile->ReadString( state );
saveFile->ReadInt( flags );
saveFile->ReadInt( delay );
parms.Restore( saveFile );
}
/*
=====================
rvStateThread::rvStateThread
=====================
*/
rvStateThread::rvStateThread( void )
{
owner = NULL;
insertAfter = NULL;
lastResult = SRESULT_DONE;
states.Clear( );
interrupted.Clear( );
memset( &fl, 0, sizeof( fl ) );
}
/*
=====================
rvStateThread::~rvStateThread
=====================
*/
rvStateThread::~rvStateThread( void )
{
Clear( true );
}
/*
=====================
rvStateThread::SetOwner
=====================
*/
void rvStateThread::SetOwner( idClass* _owner )
{
owner = _owner;
}
/*
=====================
rvStateThread::Post
=====================
*/
stateResult_t rvStateThread::PostState( const char* name, int blendFrames, int delay, int flags )
{
stateCall_t* call;
call = new stateCall_t;
call->state = name;
call->delay = delay;
call->flags = flags;
call->parms.blendFrames = blendFrames;
call->parms.time = -1;
call->parms.stage = 0;
call->parms.substage = 0;
call->parms.param1 = 0;
call->parms.param2 = 0;
call->parms.subparam1 = 0;
call->parms.subparam2 = 0;
call->node.SetOwner( call );
if( fl.executing && insertAfter )
{
call->node.InsertAfter( insertAfter->node );
}
else
{
call->node.AddToEnd( states );
}
insertAfter = call;
return SRESULT_OK;
}
/*
=====================
rvStateThread::Set
=====================
*/
stateResult_t rvStateThread::SetState( const char* name, int blendFrames, int delay, int flags )
{
Clear( );
return PostState( name, blendFrames, delay, flags );
}
/*
=====================
rvStateThread::InterruptState
=====================
*/
stateResult_t rvStateThread::InterruptState( const char* name, int blendFrames, int delay, int flags )
{
stateCall_t* call;
// Move all states to the front of the interrupted list in the same order
for( call = states.Prev(); call; call = states.Prev() )
{
call->node.Remove( );
call->node.AddToFront( interrupted );
}
// Nothing to insert after anymore
insertAfter = NULL;
fl.stateInterrupted = true;
// Post the state now
return PostState( name, blendFrames, delay, flags );
}
/*
=====================
rvStateThread::CurrentStateIs
=====================
*/
bool rvStateThread::CurrentStateIs( const char* name ) const
{
return ( !IsIdle() ) ? name == GetState()->state : false;
}
/*
=====================
rvStateThread::Clear
=====================
*/
void rvStateThread::Clear( bool ignoreStateCalls )
{
stateCall_t* call;
// Clear all states from the main state list
for( call = states.Next(); call != NULL; call = states.Next() )
{
if( !ignoreStateCalls && ( call->flags & ( SFLAG_ONCLEAR | SFLAG_ONCLEARONLY ) ) )
{
//owner->ProcessState ( call->state, call->parms );
//owner->SetStateParms(call->parms);
owner->Invoke( call->state, &call->parms );
}
call->node.Remove();
delete call;
}
// Clear all interrupted states
for( call = interrupted.Next(); call != NULL; call = interrupted.Next() )
{
if( !ignoreStateCalls && ( call->flags & ( SFLAG_ONCLEAR | SFLAG_ONCLEARONLY ) ) )
{
//owner->ProcessState ( call->state, call->parms );
// owner->SetStateParms(call->parms);
owner->Invoke( call->state, &call->parms );
}
call->node.Remove();
delete call;
}
insertAfter = NULL;
fl.stateCleared = true;
states.Clear( );
interrupted.Clear( );
}
/*
=====================
rvStateThread::Execute
=====================
*/
stateResult_t rvStateThread::Execute( void )
{
stateCall_t* call = NULL;
int count;
const char* stateName;
int stateStage;
const char* historyState[HISTORY_COUNT];
int historyStage[HISTORY_COUNT];
int historyStart;
int historyEnd;
// If our main state loop is empty copy over any states in the interrupted state
if( !states.Next( ) )
{
for( call = interrupted.Next(); call; call = interrupted.Next() )
{
call->node.Remove( );
call->node.AddToEnd( states );
}
assert( !interrupted.Next( ) );
}
// State thread is idle if there are no states
if( !states.Next() )
{
return SRESULT_IDLE;
}
fl.executing = true;
// Run through the states until there are no more or one of them tells us to wait
count = 0;
historyStart = 0;
historyEnd = 0;
for( call = states.Next(); call && count < HISTORY_COUNT; call = states.Next(), ++count )
{
insertAfter = call;
fl.stateCleared = false;
fl.stateInterrupted = false;
// If this state is only called when being cleared then just skip it
if( call->flags & SFLAG_ONCLEARONLY )
{
call->node.Remove( );
delete call;
continue;
}
// If the call has a delay on it the time will be set to negative initially and then
// converted to game time.
if( call->parms.time <= 0 )
{
call->parms.time = gameLocal.time;
}
// Check for delayed states
if( gameLocal.time < call->parms.time + call->delay )
{
fl.executing = false;
return SRESULT_WAIT;
}
// Debugging
if( lastResult != SRESULT_WAIT )
{
// Keep a history of the called states so we can dump them on an overflow
historyState[historyEnd] = call->state;
historyStage[historyEnd] = call->parms.stage;
historyEnd = ( historyEnd + 1 ) % HISTORY_COUNT;
if( historyEnd == historyStart )
{
historyStart = ( historyEnd + 1 ) % HISTORY_COUNT;
}
}
// Cache name and stage for error messages
stateName = call->state;
stateStage = call->parms.stage;
if( g_debugState.GetBool() )
{
if( call->parms.stage )
{
gameLocal.Printf( "%s: %s (%d)\n", ((idEntity *)owner)->GetClassname(), call->state.c_str(), call->parms.stage );
}
else
{
gameLocal.Printf( "%s: %s\n", ((idEntity*)owner)->GetClassname(), call->state.c_str() );
}
}
// Actually call the state function
lastResult = ( stateResult_t )owner->Invoke( call->state, &call->parms );
switch( lastResult )
{
case SRESULT_DONE_FRAME:
fl.executing = false;
return SRESULT_DONE;
case SRESULT_WAIT:
fl.executing = false;
return SRESULT_WAIT;
case SRESULT_ERROR:
gameLocal.Error( "rvStateThread: error reported by state '%s (%d)'", stateName, stateStage );
fl.executing = false;
return SRESULT_ERROR;
}
if (lastResult == SRESULT_DONE) {
owner->StateThreadChanged();
}
// Dont remove the node if it was interrupted or cleared in the last process
if( !fl.stateCleared && !fl.stateInterrupted )
{
if( lastResult >= SRESULT_SETDELAY )
{
call->delay = lastResult - SRESULT_SETDELAY;
call->parms.time = gameLocal.GetTime();
continue;
}
else if( lastResult >= SRESULT_SETSTAGE )
{
call->parms.stage = lastResult - SRESULT_SETSTAGE;
continue;
}
// Done with state so remove it from list
call->node.Remove( );
delete call;
}
// Finished the last state but wait a frame for next one
if( lastResult == SRESULT_DONE_WAIT )
{
fl.executing = false;
return SRESULT_WAIT;
}
}
// Runaway state loop?
if( count >= HISTORY_COUNT )
{
idFile* file;
fileSystem->RemoveFile( "statedump.txt" );
file = fileSystem->OpenFileWrite( "statedump.txt" );
for( ; historyStart != historyEnd; historyStart = ( historyStart + 1 ) % HISTORY_COUNT )
{
if( historyStage[historyStart] )
{
gameLocal.Printf( "rvStateThread: %s (%d)\n", historyState[historyStart], historyStage[historyStart] );
}
else
{
gameLocal.Printf( "rvStateThread: %s\n", historyState[historyStart] );
}
if( file )
{
if( historyStage[historyStart] )
{
file->Printf( "rvStateThread: %s (%d)\n", historyState[historyStart], historyStage[historyStart] );
}
else
{
file->Printf( "rvStateThread: %s\n", historyState[historyStart] );
}
}
}
if( file )
{
fileSystem->CloseFile( file );
}
gameLocal.Error( "rvStateThread: run away state loop '%s'", name.c_str() );
}
insertAfter = NULL;
fl.executing = false;
// Move interrupted states back into the main state list when the main state list is empty
if( !states.Next() && interrupted.Next( ) )
{
return Execute( );
}
return lastResult;
}
/*
=====================
rvStateThread::Save
=====================
*/
void rvStateThread::Save( idSaveGame* saveFile ) const
{
saveFile->WriteString( name.c_str() );
// No need to save owner, its setup in restore
saveFile->WriteInt( lastResult );
saveFile->Write( &fl, sizeof( fl ) );
saveFile->WriteInt( states.Num() );
for( idLinkList<stateCall_t>* node = states.NextNode(); node; node = node->NextNode() )
{
node->Owner()->Save( saveFile );
}
saveFile->WriteInt( interrupted.Num() );
for( idLinkList<stateCall_t>* node = interrupted.NextNode(); node; node = node->NextNode() )
{
node->Owner()->Save( saveFile );
}
// TOSAVE: stateCall_t* insertAfter;
// TOSAVE: stateResult_t lastResult;
}
/*
=====================
rvStateThread::Restore
=====================
*/
void rvStateThread::Restore( idRestoreGame* saveFile, idClass* owner )
{
int numStates;
stateCall_t* call = NULL;
saveFile->ReadString( name );
this->owner = owner;
saveFile->ReadInt( ( int& )lastResult );
saveFile->Read( &fl, sizeof( fl ) );
saveFile->ReadInt( numStates );
for( ; numStates > 0; numStates-- )
{
call = new stateCall_t;
assert( call );
call->Restore( saveFile, owner );
call->node.SetOwner( call );
call->node.AddToEnd( states );
}
saveFile->ReadInt( numStates );
for( ; numStates > 0; numStates-- )
{
call = new stateCall_t;
assert( call );
call->Restore( saveFile, owner );
call->node.SetOwner( call );
call->node.AddToEnd( interrupted );
}
}
+168
View File
@@ -0,0 +1,168 @@
#ifndef __SYS_STATE_H__
#define __SYS_STATE_H__
typedef enum
{
SRESULT_OK, // Call was made successfully
SRESULT_ERROR, // An unrecoverable error occurred
SRESULT_DONE, // Done with current state, move to next
SRESULT_DONE_WAIT, // Done with current state, wait a frame then move to next
SRESULT_WAIT, // Wait a frame and re-run current state
SRESULT_IDLE, // State thread is currently idle (ie. no states)
SRESULT_SETSTAGE, // Sets the current stage of the current state and reruns the state
SRESULT_DONE_FRAME,
// NOTE: this has to be the last result becuase the stage is added to
// the result.
SRESULT_SETDELAY = SRESULT_SETSTAGE + 20
} stateResult_t;
#define MAX_STATE_CALLS 50
#define SRESULT_STAGE(x) ((stateResult_t)((int)SRESULT_SETSTAGE + (int)(x)))
#define SRESULT_DELAY(x) ((stateResult_t)((int)SRESULT_SETDELAY + (int)(x)))
struct stateParms_t
{
int blendFrames;
int time;
int stage;
int substage;
float param1;
float param2;
float subparam1;
float subparam2;
void Wait( float seconds );
void Save( idSaveGame* saveFile ) const;
void Restore( idRestoreGame* saveFile );
};
/*
================
CLASS_STATES_PROTOTYPE
This macro must be included in the definition of any subclass of idClass that
wishes to have its own custom states. Its prototypes variables used in the process
of managing states.
================
*/
#define CLASS_STATES_PROTOTYPE(nameofclass) \
protected: \
static rvStateFunc<nameofclass> stateCallbacks[]
/*
================
CLASS_STATES_DECLARATION
This macro must be included in the code to properly initialize variables
used in state processing for a idClass dervied class
================
*/
#define CLASS_STATES_DECLARATION(nameofclass) \
rvStateFunc<nameofclass> nameofclass::stateCallbacks[] = {
/*
================
STATE
This macro declares a single state. It must be surrounded by the CLASS_STATES_DECLARATION
and END_CLASS_STATES macros.
================
*/
#define STATE(statename,function) { statename, (stateCallback_t)( &function ) },
/*
================
END_CLASS_STATES
Terminates a state block
================
*/
#define END_CLASS_STATES { NULL, NULL } };
struct stateCall_t
{
idStr state;
idLinkList<stateCall_t> node;
int flags;
int delay;
stateParms_t parms;
void Save( idSaveGame* saveFile ) const;
void Restore( idRestoreGame* saveFile, const idClass* owner );
};
class idClass;
const int SFLAG_ONCLEAR = BIT( 0 ); // Executes, even if the state queue is cleared
const int SFLAG_ONCLEARONLY = BIT( 1 ); // Executes only if the state queue is cleared
class rvStateThread
{
public:
rvStateThread( void );
~rvStateThread( void );
void SetName( const char* name );
void SetOwner( idClass* owner );
bool Interrupt( void );
stateResult_t InterruptState( const char* state, int blendFrames = 0, int delay = 0, int flags = 0 );
stateResult_t PostState( const char* state, int blendFrames = 0, int delay = 0, int flags = 0 );
stateResult_t SetState( const char* state, int blendFrames = 0, int delay = 0, int flags = 0 );
stateCall_t* GetState( void ) const;
bool CurrentStateIs( const char* name ) const;
stateResult_t Execute( void );
void Clear( bool ignoreStateCalls = false );
bool IsIdle( void ) const;
bool IsExecuting( void ) const;
void Save( idSaveGame* saveFile ) const;
void Restore( idRestoreGame* saveFile, idClass* owner );
protected:
struct flags
{
bool stateCleared : 1; // State list was cleared
bool stateInterrupted : 1; // State list was interrupted
bool executing : 1; // Execute is currently processing states
} fl;
idStr name;
idClass* owner;
idLinkList<stateCall_t> states;
idLinkList<stateCall_t> interrupted;
stateCall_t* insertAfter;
stateResult_t lastResult;
};
ID_INLINE void rvStateThread::SetName( const char* _name )
{
name = _name;
}
ID_INLINE stateCall_t* rvStateThread::GetState( void ) const
{
return states.Next();
}
ID_INLINE bool rvStateThread::IsIdle( void ) const
{
return !states.Next() && !interrupted.Next();
}
ID_INLINE bool rvStateThread::IsExecuting( void ) const
{
return fl.executing;
}
#endif // __SYS_STATE_H__