mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-12-11 13:11:47 +00:00
1263 lines
31 KiB
C++
1263 lines
31 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
Copyright (C) 2012 Robert Beckebans
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
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.
|
|
|
|
===========================================================================
|
|
*/
|
|
/*
|
|
sys_event.cpp
|
|
|
|
Event are used for scheduling tasks and for linking script commands.
|
|
|
|
*/
|
|
|
|
#pragma hdrstop
|
|
#include "../../idlib/precompiled.h"
|
|
|
|
|
|
#include "../Game_local.h"
|
|
|
|
#define MAX_EVENTSPERFRAME 4096
|
|
//#define CREATE_EVENT_CODE
|
|
|
|
/***********************************************************************
|
|
|
|
idEventDef
|
|
|
|
***********************************************************************/
|
|
|
|
idEventDef* idEventDef::eventDefList[MAX_EVENTS];
|
|
int idEventDef::numEventDefs = 0;
|
|
|
|
static bool eventError = false;
|
|
static char eventErrorMsg[ 128 ];
|
|
|
|
/*
|
|
================
|
|
idEventDef::idEventDef
|
|
================
|
|
*/
|
|
idEventDef::idEventDef( const char* command, const char* formatspec, char returnType )
|
|
{
|
|
idEventDef* ev;
|
|
int i;
|
|
unsigned int bits;
|
|
|
|
assert( command );
|
|
assert( !idEvent::initialized );
|
|
|
|
// Allow NULL to indicate no args, but always store it as ""
|
|
// so we don't have to check for it.
|
|
if( !formatspec )
|
|
{
|
|
formatspec = "";
|
|
}
|
|
|
|
this->name = command;
|
|
this->formatspec = formatspec;
|
|
this->returnType = returnType;
|
|
|
|
numargs = strlen( formatspec );
|
|
assert( numargs <= D_EVENT_MAXARGS );
|
|
if( numargs > D_EVENT_MAXARGS )
|
|
{
|
|
eventError = true;
|
|
sprintf( eventErrorMsg, "idEventDef::idEventDef : Too many args for '%s' event.", name );
|
|
return;
|
|
}
|
|
|
|
// make sure the format for the args is valid, calculate the formatspecindex, and the offsets for each arg
|
|
bits = 0;
|
|
argsize = 0;
|
|
memset( argOffset, 0, sizeof( argOffset ) );
|
|
for( i = 0; i < numargs; i++ )
|
|
{
|
|
argOffset[ i ] = argsize;
|
|
switch( formatspec[ i ] )
|
|
{
|
|
case D_EVENT_FLOAT :
|
|
bits |= 1 << i;
|
|
// RB: 64 bit fix, changed sizeof( float ) to sizeof( intptr_t )
|
|
argsize += sizeof( intptr_t );
|
|
// RB end
|
|
break;
|
|
|
|
case D_EVENT_INTEGER :
|
|
// RB: 64 bit fix, changed sizeof( int ) to sizeof( intptr_t )
|
|
argsize += sizeof( intptr_t );
|
|
// RB end
|
|
break;
|
|
|
|
case D_EVENT_VECTOR :
|
|
// RB: 64 bit fix, changed sizeof( idVec3 ) to E_EVENT_SIZEOF_VEC
|
|
argsize += E_EVENT_SIZEOF_VEC;
|
|
// RB end
|
|
break;
|
|
|
|
case D_EVENT_STRING :
|
|
argsize += MAX_STRING_LEN;
|
|
break;
|
|
|
|
case D_EVENT_ENTITY :
|
|
// RB: 64 bit fix, sizeof( idEntityPtr<idEntity> ) to sizeof( intptr_t )
|
|
argsize += sizeof( intptr_t );
|
|
// RB end
|
|
break;
|
|
|
|
case D_EVENT_ENTITY_NULL :
|
|
// RB: 64 bit fix, sizeof( idEntityPtr<idEntity> ) to sizeof( intptr_t )
|
|
argsize += sizeof( intptr_t );
|
|
// RB end
|
|
break;
|
|
|
|
case D_EVENT_TRACE :
|
|
argsize += sizeof( trace_t ) + MAX_STRING_LEN + sizeof( bool );
|
|
break;
|
|
|
|
default :
|
|
eventError = true;
|
|
sprintf( eventErrorMsg, "idEventDef::idEventDef : Invalid arg format '%s' string for '%s' event.", formatspec, name );
|
|
return;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// calculate the formatspecindex
|
|
formatspecIndex = ( 1 << ( numargs + D_EVENT_MAXARGS ) ) | bits;
|
|
|
|
// go through the list of defined events and check for duplicates
|
|
// and mismatched format strings
|
|
eventnum = numEventDefs;
|
|
for( i = 0; i < eventnum; i++ )
|
|
{
|
|
ev = eventDefList[ i ];
|
|
if( strcmp( command, ev->name ) == 0 )
|
|
{
|
|
if( strcmp( formatspec, ev->formatspec ) != 0 )
|
|
{
|
|
eventError = true;
|
|
sprintf( eventErrorMsg, "idEvent '%s' defined twice with same name but differing format strings ('%s'!='%s').",
|
|
command, formatspec, ev->formatspec );
|
|
return;
|
|
}
|
|
|
|
if( ev->returnType != returnType )
|
|
{
|
|
eventError = true;
|
|
sprintf( eventErrorMsg, "idEvent '%s' defined twice with same name but differing return types ('%c'!='%c').",
|
|
command, returnType, ev->returnType );
|
|
return;
|
|
}
|
|
// Don't bother putting the duplicate event in list.
|
|
eventnum = ev->eventnum;
|
|
return;
|
|
}
|
|
}
|
|
|
|
ev = this;
|
|
|
|
if( numEventDefs >= MAX_EVENTS )
|
|
{
|
|
eventError = true;
|
|
sprintf( eventErrorMsg, "numEventDefs >= MAX_EVENTS" );
|
|
return;
|
|
}
|
|
eventDefList[numEventDefs] = ev;
|
|
numEventDefs++;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEventDef::NumEventCommands
|
|
================
|
|
*/
|
|
int idEventDef::NumEventCommands()
|
|
{
|
|
return numEventDefs;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEventDef::GetEventCommand
|
|
================
|
|
*/
|
|
const idEventDef* idEventDef::GetEventCommand( int eventnum )
|
|
{
|
|
return eventDefList[ eventnum ];
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEventDef::FindEvent
|
|
================
|
|
*/
|
|
const idEventDef* idEventDef::FindEvent( const char* name )
|
|
{
|
|
idEventDef* ev;
|
|
int num;
|
|
int i;
|
|
|
|
assert( name );
|
|
|
|
num = numEventDefs;
|
|
for( i = 0; i < num; i++ )
|
|
{
|
|
ev = eventDefList[ i ];
|
|
if( strcmp( name, ev->name ) == 0 )
|
|
{
|
|
return ev;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
idEvent
|
|
|
|
***********************************************************************/
|
|
|
|
static idLinkList<idEvent> FreeEvents;
|
|
static idLinkList<idEvent> EventQueue;
|
|
static idLinkList<idEvent> FastEventQueue;
|
|
static idEvent EventPool[ MAX_EVENTS ];
|
|
|
|
bool idEvent::initialized = false;
|
|
|
|
idDynamicBlockAlloc<byte, 16* 1024, 256> idEvent::eventDataAllocator;
|
|
|
|
/*
|
|
================
|
|
idEvent::~idEvent()
|
|
================
|
|
*/
|
|
idEvent::~idEvent()
|
|
{
|
|
Free();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEvent::Alloc
|
|
================
|
|
*/
|
|
idEvent* idEvent::Alloc( const idEventDef* evdef, int numargs, va_list args )
|
|
{
|
|
idEvent* ev;
|
|
size_t size;
|
|
const char* format;
|
|
idEventArg* arg;
|
|
byte* dataPtr;
|
|
int i;
|
|
const char* materialName;
|
|
|
|
if( FreeEvents.IsListEmpty() )
|
|
{
|
|
gameLocal.Error( "idEvent::Alloc : No more free events" );
|
|
}
|
|
|
|
ev = FreeEvents.Next();
|
|
ev->eventNode.Remove();
|
|
|
|
ev->eventdef = evdef;
|
|
|
|
if( numargs != evdef->GetNumArgs() )
|
|
{
|
|
gameLocal.Error( "idEvent::Alloc : Wrong number of args for '%s' event.", evdef->GetName() );
|
|
}
|
|
|
|
size = evdef->GetArgSize();
|
|
if( size )
|
|
{
|
|
ev->data = eventDataAllocator.Alloc( size );
|
|
memset( ev->data, 0, size );
|
|
}
|
|
else
|
|
{
|
|
ev->data = NULL;
|
|
return ev;
|
|
}
|
|
|
|
format = evdef->GetArgFormat();
|
|
for( i = 0; i < numargs; i++ )
|
|
{
|
|
arg = va_arg( args, idEventArg* );
|
|
if( format[ i ] != arg->type )
|
|
{
|
|
// when NULL is passed in for an entity, it gets cast as an integer 0, so don't give an error when it happens
|
|
if( !( ( ( format[ i ] == D_EVENT_TRACE ) || ( format[ i ] == D_EVENT_ENTITY ) ) && ( arg->type == 'd' ) && ( arg->value == 0 ) ) )
|
|
{
|
|
gameLocal.Error( "idEvent::Alloc : Wrong type passed in for arg # %d on '%s' event.", i, evdef->GetName() );
|
|
}
|
|
}
|
|
|
|
dataPtr = &ev->data[ evdef->GetArgOffset( i ) ];
|
|
|
|
switch( format[ i ] )
|
|
{
|
|
case D_EVENT_FLOAT :
|
|
case D_EVENT_INTEGER :
|
|
*reinterpret_cast<int*>( dataPtr ) = arg->value;
|
|
break;
|
|
|
|
case D_EVENT_VECTOR :
|
|
if( arg->value )
|
|
{
|
|
*reinterpret_cast<idVec3*>( dataPtr ) = *reinterpret_cast<const idVec3*>( arg->value );
|
|
}
|
|
break;
|
|
|
|
case D_EVENT_STRING :
|
|
if( arg->value )
|
|
{
|
|
idStr::Copynz( reinterpret_cast<char*>( dataPtr ), reinterpret_cast<const char*>( arg->value ), MAX_STRING_LEN );
|
|
}
|
|
break;
|
|
|
|
case D_EVENT_ENTITY :
|
|
case D_EVENT_ENTITY_NULL :
|
|
*reinterpret_cast< idEntityPtr<idEntity> * >( dataPtr ) = reinterpret_cast<idEntity*>( arg->value );
|
|
break;
|
|
|
|
case D_EVENT_TRACE :
|
|
if( arg->value )
|
|
{
|
|
*reinterpret_cast<bool*>( dataPtr ) = true;
|
|
*reinterpret_cast<trace_t*>( dataPtr + sizeof( bool ) ) = *reinterpret_cast<const trace_t*>( arg->value );
|
|
|
|
// save off the material as a string since the pointer won't be valid in save games.
|
|
// since we save off the entire trace_t structure, if the material is NULL here,
|
|
// it will be NULL when we process it, so we don't need to save off anything in that case.
|
|
if( reinterpret_cast<const trace_t*>( arg->value )->c.material )
|
|
{
|
|
materialName = reinterpret_cast<const trace_t*>( arg->value )->c.material->GetName();
|
|
idStr::Copynz( reinterpret_cast<char*>( dataPtr + sizeof( bool ) + sizeof( trace_t ) ), materialName, MAX_STRING_LEN );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*reinterpret_cast<bool*>( dataPtr ) = false;
|
|
}
|
|
break;
|
|
|
|
default :
|
|
gameLocal.Error( "idEvent::Alloc : Invalid arg format '%s' string for '%s' event.", format, evdef->GetName() );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ev;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEvent::CopyArgs
|
|
================
|
|
*/
|
|
// RB: 64 bit fixes, changed int to intptr_t
|
|
void idEvent::CopyArgs( const idEventDef* evdef, int numargs, va_list args, intptr_t data[ D_EVENT_MAXARGS ] )
|
|
{
|
|
// RB end
|
|
int i;
|
|
const char* format;
|
|
idEventArg* arg;
|
|
|
|
format = evdef->GetArgFormat();
|
|
if( numargs != evdef->GetNumArgs() )
|
|
{
|
|
gameLocal.Error( "idEvent::CopyArgs : Wrong number of args for '%s' event.", evdef->GetName() );
|
|
}
|
|
|
|
for( i = 0; i < numargs; i++ )
|
|
{
|
|
arg = va_arg( args, idEventArg* );
|
|
if( format[ i ] != arg->type )
|
|
{
|
|
// when NULL is passed in for an entity, it gets cast as an integer 0, so don't give an error when it happens
|
|
if( !( ( ( format[ i ] == D_EVENT_TRACE ) || ( format[ i ] == D_EVENT_ENTITY ) ) && ( arg->type == 'd' ) && ( arg->value == 0 ) ) )
|
|
{
|
|
gameLocal.Error( "idEvent::CopyArgs : Wrong type passed in for arg # %d on '%s' event.", i, evdef->GetName() );
|
|
}
|
|
}
|
|
|
|
data[ i ] = arg->value;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEvent::Free
|
|
================
|
|
*/
|
|
void idEvent::Free()
|
|
{
|
|
if( data )
|
|
{
|
|
eventDataAllocator.Free( data );
|
|
data = NULL;
|
|
}
|
|
|
|
eventdef = NULL;
|
|
time = 0;
|
|
object = NULL;
|
|
typeinfo = NULL;
|
|
|
|
eventNode.SetOwner( this );
|
|
eventNode.AddToEnd( FreeEvents );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEvent::Schedule
|
|
================
|
|
*/
|
|
void idEvent::Schedule( idClass* obj, const idTypeInfo* type, int time )
|
|
{
|
|
idEvent* event;
|
|
|
|
assert( initialized );
|
|
if( !initialized )
|
|
{
|
|
return;
|
|
}
|
|
|
|
object = obj;
|
|
typeinfo = type;
|
|
|
|
// wraps after 24 days...like I care. ;)
|
|
this->time = gameLocal.time + time;
|
|
|
|
eventNode.Remove();
|
|
|
|
if( obj->IsType( idEntity::Type ) && ( ( ( idEntity* )( obj ) )->timeGroup == TIME_GROUP2 ) )
|
|
{
|
|
event = FastEventQueue.Next();
|
|
while( ( event != NULL ) && ( this->time >= event->time ) )
|
|
{
|
|
event = event->eventNode.Next();
|
|
}
|
|
|
|
if( event )
|
|
{
|
|
eventNode.InsertBefore( event->eventNode );
|
|
}
|
|
else
|
|
{
|
|
eventNode.AddToEnd( FastEventQueue );
|
|
}
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
this->time = gameLocal.slow.time + time;
|
|
}
|
|
|
|
event = EventQueue.Next();
|
|
while( ( event != NULL ) && ( this->time >= event->time ) )
|
|
{
|
|
event = event->eventNode.Next();
|
|
}
|
|
|
|
if( event )
|
|
{
|
|
eventNode.InsertBefore( event->eventNode );
|
|
}
|
|
else
|
|
{
|
|
eventNode.AddToEnd( EventQueue );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEvent::CancelEvents
|
|
================
|
|
*/
|
|
void idEvent::CancelEvents( const idClass* obj, const idEventDef* evdef )
|
|
{
|
|
idEvent* event;
|
|
idEvent* next;
|
|
|
|
if( !initialized )
|
|
{
|
|
return;
|
|
}
|
|
|
|
for( event = EventQueue.Next(); event != NULL; event = next )
|
|
{
|
|
next = event->eventNode.Next();
|
|
if( event->object == obj )
|
|
{
|
|
if( !evdef || ( evdef == event->eventdef ) )
|
|
{
|
|
event->Free();
|
|
}
|
|
}
|
|
}
|
|
|
|
for( event = FastEventQueue.Next(); event != NULL; event = next )
|
|
{
|
|
next = event->eventNode.Next();
|
|
if( event->object == obj )
|
|
{
|
|
if( !evdef || ( evdef == event->eventdef ) )
|
|
{
|
|
event->Free();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEvent::ClearEventList
|
|
================
|
|
*/
|
|
void idEvent::ClearEventList()
|
|
{
|
|
int i;
|
|
|
|
//
|
|
// initialize lists
|
|
//
|
|
FreeEvents.Clear();
|
|
EventQueue.Clear();
|
|
|
|
//
|
|
// add the events to the free list
|
|
//
|
|
for( i = 0; i < MAX_EVENTS; i++ )
|
|
{
|
|
EventPool[ i ].Free();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEvent::ServiceEvents
|
|
================
|
|
*/
|
|
void idEvent::ServiceEvents()
|
|
{
|
|
idEvent* event;
|
|
int num;
|
|
// RB: 64 bit fixes, changed int to intptr_t
|
|
intptr_t args[ D_EVENT_MAXARGS ];
|
|
// RB end
|
|
int offset;
|
|
int i;
|
|
int numargs;
|
|
const char* formatspec;
|
|
trace_t** tracePtr;
|
|
const idEventDef* ev;
|
|
byte* data;
|
|
const char* materialName;
|
|
|
|
num = 0;
|
|
while( !EventQueue.IsListEmpty() )
|
|
{
|
|
event = EventQueue.Next();
|
|
assert( event );
|
|
|
|
if( event->time > gameLocal.time )
|
|
{
|
|
break;
|
|
}
|
|
|
|
common->UpdateLevelLoadPacifier();
|
|
|
|
// copy the data into the local args array and set up pointers
|
|
ev = event->eventdef;
|
|
formatspec = ev->GetArgFormat();
|
|
numargs = ev->GetNumArgs();
|
|
for( i = 0; i < numargs; i++ )
|
|
{
|
|
offset = ev->GetArgOffset( i );
|
|
data = event->data;
|
|
switch( formatspec[ i ] )
|
|
{
|
|
case D_EVENT_FLOAT :
|
|
case D_EVENT_INTEGER :
|
|
args[ i ] = *reinterpret_cast<int*>( &data[ offset ] );
|
|
break;
|
|
|
|
case D_EVENT_VECTOR :
|
|
*reinterpret_cast<idVec3**>( &args[ i ] ) = reinterpret_cast<idVec3*>( &data[ offset ] );
|
|
break;
|
|
|
|
case D_EVENT_STRING :
|
|
*reinterpret_cast<const char**>( &args[ i ] ) = reinterpret_cast<const char*>( &data[ offset ] );
|
|
break;
|
|
|
|
case D_EVENT_ENTITY :
|
|
case D_EVENT_ENTITY_NULL :
|
|
*reinterpret_cast<idEntity**>( &args[ i ] ) = reinterpret_cast< idEntityPtr<idEntity> * >( &data[ offset ] )->GetEntity();
|
|
break;
|
|
|
|
case D_EVENT_TRACE :
|
|
tracePtr = reinterpret_cast<trace_t**>( &args[ i ] );
|
|
if( *reinterpret_cast<bool*>( &data[ offset ] ) )
|
|
{
|
|
*tracePtr = reinterpret_cast<trace_t*>( &data[ offset + sizeof( bool ) ] );
|
|
|
|
if( ( *tracePtr )->c.material != NULL )
|
|
{
|
|
// look up the material name to get the material pointer
|
|
materialName = reinterpret_cast<const char*>( &data[ offset + sizeof( bool ) + sizeof( trace_t ) ] );
|
|
( *tracePtr )->c.material = declManager->FindMaterial( materialName, true );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*tracePtr = NULL;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
gameLocal.Error( "idEvent::ServiceEvents : Invalid arg format '%s' string for '%s' event.", formatspec, ev->GetName() );
|
|
}
|
|
}
|
|
|
|
// the event is removed from its list so that if then object
|
|
// is deleted, the event won't be freed twice
|
|
event->eventNode.Remove();
|
|
assert( event->object );
|
|
event->object->ProcessEventArgPtr( ev, args );
|
|
|
|
#if 0
|
|
// event functions may never leave return values on the FPU stack
|
|
// enable this code to check if any event call left values on the FPU stack
|
|
if( !sys->FPU_StackIsEmpty() )
|
|
{
|
|
gameLocal.Error( "idEvent::ServiceEvents %d: %s left a value on the FPU stack\n", num, ev->GetName() );
|
|
}
|
|
#endif
|
|
|
|
// return the event to the free list
|
|
event->Free();
|
|
|
|
// Don't allow ourselves to stay in here too long. An abnormally high number
|
|
// of events being processed is evidence of an infinite loop of events.
|
|
num++;
|
|
if( num > MAX_EVENTSPERFRAME )
|
|
{
|
|
gameLocal.Error( "Event overflow. Possible infinite loop in script." );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEvent::ServiceFastEvents
|
|
================
|
|
*/
|
|
void idEvent::ServiceFastEvents()
|
|
{
|
|
idEvent* event;
|
|
int num;
|
|
// RB: 64 bit fixes, changed int to intptr_t
|
|
intptr_t args[ D_EVENT_MAXARGS ];
|
|
// RB end
|
|
int offset;
|
|
int i;
|
|
int numargs;
|
|
const char* formatspec;
|
|
trace_t** tracePtr;
|
|
const idEventDef* ev;
|
|
byte* data;
|
|
const char* materialName;
|
|
|
|
num = 0;
|
|
while( !FastEventQueue.IsListEmpty() )
|
|
{
|
|
event = FastEventQueue.Next();
|
|
assert( event );
|
|
|
|
if( event->time > gameLocal.fast.time )
|
|
{
|
|
break;
|
|
}
|
|
|
|
// copy the data into the local args array and set up pointers
|
|
ev = event->eventdef;
|
|
formatspec = ev->GetArgFormat();
|
|
numargs = ev->GetNumArgs();
|
|
for( i = 0; i < numargs; i++ )
|
|
{
|
|
offset = ev->GetArgOffset( i );
|
|
data = event->data;
|
|
switch( formatspec[ i ] )
|
|
{
|
|
case D_EVENT_FLOAT :
|
|
case D_EVENT_INTEGER :
|
|
args[ i ] = *reinterpret_cast<int*>( &data[ offset ] );
|
|
break;
|
|
|
|
case D_EVENT_VECTOR :
|
|
*reinterpret_cast<idVec3**>( &args[ i ] ) = reinterpret_cast<idVec3*>( &data[ offset ] );
|
|
break;
|
|
|
|
case D_EVENT_STRING :
|
|
*reinterpret_cast<const char**>( &args[ i ] ) = reinterpret_cast<const char*>( &data[ offset ] );
|
|
break;
|
|
|
|
case D_EVENT_ENTITY :
|
|
case D_EVENT_ENTITY_NULL :
|
|
*reinterpret_cast<idEntity**>( &args[ i ] ) = reinterpret_cast< idEntityPtr<idEntity> * >( &data[ offset ] )->GetEntity();
|
|
break;
|
|
|
|
case D_EVENT_TRACE :
|
|
tracePtr = reinterpret_cast<trace_t**>( &args[ i ] );
|
|
if( *reinterpret_cast<bool*>( &data[ offset ] ) )
|
|
{
|
|
*tracePtr = reinterpret_cast<trace_t*>( &data[ offset + sizeof( bool ) ] );
|
|
|
|
if( ( *tracePtr )->c.material != NULL )
|
|
{
|
|
// look up the material name to get the material pointer
|
|
materialName = reinterpret_cast<const char*>( &data[ offset + sizeof( bool ) + sizeof( trace_t ) ] );
|
|
( *tracePtr )->c.material = declManager->FindMaterial( materialName, true );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*tracePtr = NULL;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
gameLocal.Error( "idEvent::ServiceFastEvents : Invalid arg format '%s' string for '%s' event.", formatspec, ev->GetName() );
|
|
}
|
|
}
|
|
|
|
// the event is removed from its list so that if then object
|
|
// is deleted, the event won't be freed twice
|
|
event->eventNode.Remove();
|
|
assert( event->object );
|
|
event->object->ProcessEventArgPtr( ev, args );
|
|
|
|
#if 0
|
|
// event functions may never leave return values on the FPU stack
|
|
// enable this code to check if any event call left values on the FPU stack
|
|
if( !sys->FPU_StackIsEmpty() )
|
|
{
|
|
gameLocal.Error( "idEvent::ServiceEvents %d: %s left a value on the FPU stack\n", num, event->eventdef->GetName() );
|
|
}
|
|
#endif
|
|
|
|
// return the event to the free list
|
|
event->Free();
|
|
|
|
// Don't allow ourselves to stay in here too long. An abnormally high number
|
|
// of events being processed is evidence of an infinite loop of events.
|
|
num++;
|
|
if( num > MAX_EVENTSPERFRAME )
|
|
{
|
|
gameLocal.Error( "Event overflow. Possible infinite loop in script." );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEvent::Init
|
|
================
|
|
*/
|
|
void idEvent::Init()
|
|
{
|
|
gameLocal.Printf( "Initializing event system\n" );
|
|
|
|
if( eventError )
|
|
{
|
|
gameLocal.Error( "%s", eventErrorMsg );
|
|
}
|
|
|
|
#ifdef CREATE_EVENT_CODE
|
|
void CreateEventCallbackHandler();
|
|
CreateEventCallbackHandler();
|
|
gameLocal.Error( "Wrote event callback handler" );
|
|
#endif
|
|
|
|
if( initialized )
|
|
{
|
|
gameLocal.Printf( "...already initialized\n" );
|
|
ClearEventList();
|
|
return;
|
|
}
|
|
|
|
ClearEventList();
|
|
|
|
eventDataAllocator.Init();
|
|
|
|
gameLocal.Printf( "...%i event definitions\n", idEventDef::NumEventCommands() );
|
|
|
|
// the event system has started
|
|
initialized = true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEvent::Shutdown
|
|
================
|
|
*/
|
|
void idEvent::Shutdown()
|
|
{
|
|
gameLocal.Printf( "Shutdown event system\n" );
|
|
|
|
if( !initialized )
|
|
{
|
|
gameLocal.Printf( "...not started\n" );
|
|
return;
|
|
}
|
|
|
|
ClearEventList();
|
|
|
|
eventDataAllocator.Shutdown();
|
|
|
|
// say it is now shutdown
|
|
initialized = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEvent::Save
|
|
================
|
|
*/
|
|
void idEvent::Save( idSaveGame* savefile )
|
|
{
|
|
char* str;
|
|
int i, size;
|
|
idEvent* event;
|
|
byte* dataPtr;
|
|
bool validTrace;
|
|
const char* format;
|
|
// RB: for missing D_EVENT_STRING
|
|
idStr s;
|
|
// RB end
|
|
|
|
savefile->WriteInt( EventQueue.Num() );
|
|
|
|
event = EventQueue.Next();
|
|
while( event != NULL )
|
|
{
|
|
savefile->WriteInt( event->time );
|
|
savefile->WriteString( event->eventdef->GetName() );
|
|
savefile->WriteString( event->typeinfo->classname );
|
|
savefile->WriteObject( event->object );
|
|
savefile->WriteInt( event->eventdef->GetArgSize() );
|
|
format = event->eventdef->GetArgFormat();
|
|
for( i = 0, size = 0; i < event->eventdef->GetNumArgs(); ++i )
|
|
{
|
|
dataPtr = &event->data[ event->eventdef->GetArgOffset( i ) ];
|
|
switch( format[ i ] )
|
|
{
|
|
case D_EVENT_FLOAT :
|
|
savefile->WriteFloat( *reinterpret_cast<float*>( dataPtr ) );
|
|
// RB: 64 bit fix, changed sizeof( float ) to sizeof( intptr_t )
|
|
size += sizeof( intptr_t );
|
|
// RB end
|
|
break;
|
|
case D_EVENT_INTEGER :
|
|
// RB: 64 bit fix, changed sizeof( int ) to sizeof( intptr_t )
|
|
savefile->WriteInt( *reinterpret_cast<int*>( dataPtr ) );
|
|
size += sizeof( intptr_t );
|
|
break;
|
|
// RB end
|
|
case D_EVENT_ENTITY :
|
|
case D_EVENT_ENTITY_NULL :
|
|
// RB: 64 bit fix, changed alignment to sizeof( intptr_t )
|
|
reinterpret_cast< idEntityPtr<idEntity> * >( dataPtr )->Save( savefile );
|
|
size += sizeof( intptr_t );
|
|
// RB end
|
|
break;
|
|
case D_EVENT_VECTOR :
|
|
savefile->WriteVec3( *reinterpret_cast<idVec3*>( dataPtr ) );
|
|
// RB: 64 bit fix, changed sizeof( int ) to E_EVENT_SIZEOF_VEC
|
|
size += E_EVENT_SIZEOF_VEC;
|
|
// RB end
|
|
break;
|
|
#if 1
|
|
// RB: added missing D_EVENT_STRING case
|
|
case D_EVENT_STRING :
|
|
s.Clear();
|
|
s.Append( reinterpret_cast<char*>( dataPtr ) );
|
|
savefile->WriteString( s );
|
|
//size += s.Length();
|
|
size += MAX_STRING_LEN;
|
|
break;
|
|
// RB end
|
|
#endif
|
|
case D_EVENT_TRACE :
|
|
validTrace = *reinterpret_cast<bool*>( dataPtr );
|
|
savefile->WriteBool( validTrace );
|
|
size += sizeof( bool );
|
|
if( validTrace )
|
|
{
|
|
size += sizeof( trace_t );
|
|
const trace_t& t = *reinterpret_cast<trace_t*>( dataPtr + sizeof( bool ) );
|
|
SaveTrace( savefile, t );
|
|
if( t.c.material )
|
|
{
|
|
size += MAX_STRING_LEN;
|
|
str = reinterpret_cast<char*>( dataPtr + sizeof( bool ) + sizeof( trace_t ) );
|
|
savefile->Write( str, MAX_STRING_LEN );
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
assert( size == ( int )event->eventdef->GetArgSize() );
|
|
event = event->eventNode.Next();
|
|
}
|
|
|
|
// Save the Fast EventQueue
|
|
savefile->WriteInt( FastEventQueue.Num() );
|
|
|
|
event = FastEventQueue.Next();
|
|
while( event != NULL )
|
|
{
|
|
savefile->WriteInt( event->time );
|
|
savefile->WriteString( event->eventdef->GetName() );
|
|
savefile->WriteString( event->typeinfo->classname );
|
|
savefile->WriteObject( event->object );
|
|
savefile->WriteInt( event->eventdef->GetArgSize() );
|
|
savefile->Write( event->data, event->eventdef->GetArgSize() );
|
|
|
|
event = event->eventNode.Next();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEvent::Restore
|
|
================
|
|
*/
|
|
void idEvent::Restore( idRestoreGame* savefile )
|
|
{
|
|
char* str;
|
|
int num, argsize, i, j, size;
|
|
idStr name;
|
|
byte* dataPtr;
|
|
idEvent* event;
|
|
const char* format;
|
|
// RB: for missing D_EVENT_STRING
|
|
idStr s;
|
|
// RB end
|
|
|
|
savefile->ReadInt( num );
|
|
|
|
for( i = 0; i < num; i++ )
|
|
{
|
|
if( FreeEvents.IsListEmpty() )
|
|
{
|
|
gameLocal.Error( "idEvent::Restore : No more free events" );
|
|
}
|
|
|
|
event = FreeEvents.Next();
|
|
event->eventNode.Remove();
|
|
event->eventNode.AddToEnd( EventQueue );
|
|
|
|
savefile->ReadInt( event->time );
|
|
|
|
// read the event name
|
|
savefile->ReadString( name );
|
|
event->eventdef = idEventDef::FindEvent( name );
|
|
if( event->eventdef == NULL )
|
|
{
|
|
savefile->Error( "idEvent::Restore: unknown event '%s'", name.c_str() );
|
|
return;
|
|
}
|
|
|
|
// read the classtype
|
|
savefile->ReadString( name );
|
|
event->typeinfo = idClass::GetClass( name );
|
|
if( event->typeinfo == NULL )
|
|
{
|
|
savefile->Error( "idEvent::Restore: unknown class '%s' on event '%s'", name.c_str(), event->eventdef->GetName() );
|
|
return;
|
|
}
|
|
|
|
savefile->ReadObject( event->object );
|
|
|
|
// read the args
|
|
savefile->ReadInt( argsize );
|
|
if( argsize != ( int )event->eventdef->GetArgSize() )
|
|
{
|
|
// RB: fixed wrong formatting
|
|
savefile->Error( "idEvent::Restore: arg size (%zd) doesn't match saved arg size(%zd) on event '%s'", event->eventdef->GetArgSize(), argsize, event->eventdef->GetName() );
|
|
// RB end
|
|
}
|
|
if( argsize )
|
|
{
|
|
event->data = eventDataAllocator.Alloc( argsize );
|
|
format = event->eventdef->GetArgFormat();
|
|
assert( format );
|
|
for( j = 0, size = 0; j < event->eventdef->GetNumArgs(); ++j )
|
|
{
|
|
dataPtr = &event->data[ event->eventdef->GetArgOffset( j ) ];
|
|
switch( format[ j ] )
|
|
{
|
|
case D_EVENT_FLOAT :
|
|
savefile->ReadFloat( *reinterpret_cast<float*>( dataPtr ) );
|
|
// RB: 64 bit fix, changed sizeof( float ) to sizeof( intptr_t )
|
|
size += sizeof( intptr_t );
|
|
// RB end
|
|
break;
|
|
case D_EVENT_INTEGER :
|
|
// RB: 64 bit fix
|
|
savefile->ReadInt( *reinterpret_cast<int*>( dataPtr ) );
|
|
size += sizeof( intptr_t );
|
|
break;
|
|
// RB end
|
|
case D_EVENT_ENTITY :
|
|
case D_EVENT_ENTITY_NULL :
|
|
// RB: 64 bit fix, changed alignment to sizeof( intptr_t )
|
|
reinterpret_cast<idEntityPtr<idEntity> *>( dataPtr )->Restore( savefile );
|
|
size += sizeof( intptr_t );
|
|
// RB end
|
|
break;
|
|
case D_EVENT_VECTOR :
|
|
savefile->ReadVec3( *reinterpret_cast<idVec3*>( dataPtr ) );
|
|
// RB: 64 bit fix, changed sizeof( int ) to E_EVENT_SIZEOF_VEC
|
|
size += E_EVENT_SIZEOF_VEC;
|
|
// RB end
|
|
break;
|
|
#if 1
|
|
// RB: added missing D_EVENT_STRING case
|
|
case D_EVENT_STRING :
|
|
savefile->ReadString( s );
|
|
//idStr::Copynz(reinterpret_cast<char *>( dataPtr ), s, s.Length() );
|
|
//size += s.Length();
|
|
idStr::Copynz( reinterpret_cast<char*>( dataPtr ), s, MAX_STRING_LEN );
|
|
size += MAX_STRING_LEN;
|
|
break;
|
|
// RB end
|
|
#endif
|
|
case D_EVENT_TRACE :
|
|
savefile->ReadBool( *reinterpret_cast<bool*>( dataPtr ) );
|
|
size += sizeof( bool );
|
|
if( *reinterpret_cast<bool*>( dataPtr ) )
|
|
{
|
|
size += sizeof( trace_t );
|
|
trace_t& t = *reinterpret_cast<trace_t*>( dataPtr + sizeof( bool ) );
|
|
RestoreTrace( savefile, t ) ;
|
|
if( t.c.material )
|
|
{
|
|
size += MAX_STRING_LEN;
|
|
str = reinterpret_cast<char*>( dataPtr + sizeof( bool ) + sizeof( trace_t ) );
|
|
savefile->Read( str, MAX_STRING_LEN );
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
assert( size == ( int )event->eventdef->GetArgSize() );
|
|
}
|
|
else
|
|
{
|
|
event->data = NULL;
|
|
}
|
|
}
|
|
|
|
// Restore the Fast EventQueue
|
|
savefile->ReadInt( num );
|
|
|
|
for( i = 0; i < num; i++ )
|
|
{
|
|
if( FreeEvents.IsListEmpty() )
|
|
{
|
|
gameLocal.Error( "idEvent::Restore : No more free events" );
|
|
}
|
|
|
|
event = FreeEvents.Next();
|
|
event->eventNode.Remove();
|
|
event->eventNode.AddToEnd( FastEventQueue );
|
|
|
|
savefile->ReadInt( event->time );
|
|
|
|
// read the event name
|
|
savefile->ReadString( name );
|
|
event->eventdef = idEventDef::FindEvent( name );
|
|
if( event->eventdef == NULL )
|
|
{
|
|
savefile->Error( "idEvent::Restore: unknown event '%s'", name.c_str() );
|
|
return;
|
|
}
|
|
|
|
// read the classtype
|
|
savefile->ReadString( name );
|
|
event->typeinfo = idClass::GetClass( name );
|
|
if( event->typeinfo == NULL )
|
|
{
|
|
savefile->Error( "idEvent::Restore: unknown class '%s' on event '%s'", name.c_str(), event->eventdef->GetName() );
|
|
return;
|
|
}
|
|
|
|
savefile->ReadObject( event->object );
|
|
|
|
// read the args
|
|
savefile->ReadInt( argsize );
|
|
if( argsize != ( int )event->eventdef->GetArgSize() )
|
|
{
|
|
savefile->Error( "idEvent::Restore: arg size (%d) doesn't match saved arg size(%d) on event '%s'", event->eventdef->GetArgSize(), argsize, event->eventdef->GetName() );
|
|
}
|
|
if( argsize )
|
|
{
|
|
event->data = eventDataAllocator.Alloc( argsize );
|
|
savefile->Read( event->data, argsize );
|
|
}
|
|
else
|
|
{
|
|
event->data = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEvent::ReadTrace
|
|
|
|
idRestoreGame has a ReadTrace procedure, but unfortunately idEvent wants the material
|
|
string name at the of the data structure rather than in the middle
|
|
================
|
|
*/
|
|
void idEvent::RestoreTrace( idRestoreGame* savefile, trace_t& trace )
|
|
{
|
|
savefile->ReadFloat( trace.fraction );
|
|
savefile->ReadVec3( trace.endpos );
|
|
savefile->ReadMat3( trace.endAxis );
|
|
savefile->ReadInt( ( int& )trace.c.type );
|
|
savefile->ReadVec3( trace.c.point );
|
|
savefile->ReadVec3( trace.c.normal );
|
|
savefile->ReadFloat( trace.c.dist );
|
|
savefile->ReadInt( trace.c.contents );
|
|
savefile->ReadInt( ( int& )trace.c.material );
|
|
savefile->ReadInt( trace.c.contents );
|
|
savefile->ReadInt( trace.c.modelFeature );
|
|
savefile->ReadInt( trace.c.trmFeature );
|
|
savefile->ReadInt( trace.c.id );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEvent::WriteTrace
|
|
|
|
idSaveGame has a WriteTrace procedure, but unfortunately idEvent wants the material
|
|
string name at the of the data structure rather than in the middle
|
|
================
|
|
*/
|
|
void idEvent::SaveTrace( idSaveGame* savefile, const trace_t& trace )
|
|
{
|
|
savefile->WriteFloat( trace.fraction );
|
|
savefile->WriteVec3( trace.endpos );
|
|
savefile->WriteMat3( trace.endAxis );
|
|
savefile->WriteInt( trace.c.type );
|
|
savefile->WriteVec3( trace.c.point );
|
|
savefile->WriteVec3( trace.c.normal );
|
|
savefile->WriteFloat( trace.c.dist );
|
|
savefile->WriteInt( trace.c.contents );
|
|
savefile->WriteInt( ( int& )trace.c.material );
|
|
savefile->WriteInt( trace.c.contents );
|
|
savefile->WriteInt( trace.c.modelFeature );
|
|
savefile->WriteInt( trace.c.trmFeature );
|
|
savefile->WriteInt( trace.c.id );
|
|
}
|
|
|
|
|
|
|
|
#ifdef CREATE_EVENT_CODE
|
|
/*
|
|
================
|
|
CreateEventCallbackHandler
|
|
================
|
|
*/
|
|
void CreateEventCallbackHandler()
|
|
{
|
|
int num;
|
|
int count;
|
|
int i, j, k;
|
|
char argString[ D_EVENT_MAXARGS + 1 ];
|
|
idStr string1;
|
|
idStr string2;
|
|
idFile* file;
|
|
|
|
file = fileSystem->OpenFileWrite( "Callbacks.cpp" );
|
|
|
|
file->Printf( "// generated file - see CREATE_EVENT_CODE\n\n" );
|
|
|
|
for( i = 1; i <= D_EVENT_MAXARGS; i++ )
|
|
{
|
|
|
|
file->Printf( "\t/*******************************************************\n\n\t\t%d args\n\n\t*******************************************************/\n\n", i );
|
|
|
|
for( j = 0; j < ( 1 << i ); j++ )
|
|
{
|
|
for( k = 0; k < i; k++ )
|
|
{
|
|
argString[ k ] = j & ( 1 << k ) ? 'f' : 'i';
|
|
}
|
|
argString[ i ] = '\0';
|
|
|
|
string1.Empty();
|
|
string2.Empty();
|
|
|
|
for( k = 0; k < i; k++ )
|
|
{
|
|
if( j & ( 1 << k ) )
|
|
{
|
|
string1 += "const float";
|
|
string2 += va( "*( float * )&data[ %d ]", k );
|
|
}
|
|
else
|
|
{
|
|
// RB: 64 bit fix, changed int to intptr_t
|
|
string1 += "const intptr_t";
|
|
// RB end
|
|
string2 += va( "data[ %d ]", k );
|
|
}
|
|
|
|
if( k < i - 1 )
|
|
{
|
|
string1 += ", ";
|
|
string2 += ", ";
|
|
}
|
|
}
|
|
|
|
file->Printf( "\tcase %d :\n\t\ttypedef void ( idClass::*eventCallback_%s_t )( %s );\n", ( 1 << ( i + D_EVENT_MAXARGS ) ) + j, argString, string1.c_str() );
|
|
file->Printf( "\t\t( this->*( eventCallback_%s_t )callback )( %s );\n\t\tbreak;\n\n", argString, string2.c_str() );
|
|
|
|
}
|
|
}
|
|
|
|
fileSystem->CloseFile( file );
|
|
}
|
|
|
|
#endif
|