mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-27 22:52:44 +00:00
fad995722c
Required for events with string arguments. On debug builds an assert() is triggered when trying to save such an event, while events could not be properly restored in any build. This happens when going from map delta4 to hell1 and the autosave feature kicks in. The trigger event 'selectWeapon' with the string argument 'weapon_fists' is in the event queue. With the binary from id a warning is issued: WARNING: player1 is not carrying weapon '' so the bug exists in there too, just that its a release build and doesn't abort(). I also managed to trigger this while saving shortly after activating an elevator switch.
1082 lines
28 KiB
C++
1082 lines
28 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 GPL Source Code
|
|
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
|
|
|
|
Doom 3 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 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 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 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 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.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "sys/platform.h"
|
|
#include "script/Script_Program.h"
|
|
#include "Entity.h"
|
|
#include "Game_local.h"
|
|
|
|
#include "Event.h"
|
|
|
|
/*
|
|
sys_event.cpp
|
|
|
|
Event are used for scheduling tasks and for linking script commands.
|
|
|
|
*/
|
|
|
|
#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;
|
|
argsize += sizeof( intptr_t );
|
|
break;
|
|
|
|
case D_EVENT_INTEGER :
|
|
argsize += sizeof( intptr_t );
|
|
break;
|
|
|
|
case D_EVENT_VECTOR :
|
|
argsize += E_EVENT_SIZEOF_VEC;
|
|
break;
|
|
|
|
case D_EVENT_STRING :
|
|
argsize += MAX_STRING_LEN;
|
|
break;
|
|
|
|
case D_EVENT_ENTITY :
|
|
argsize += sizeof( idEntityPtr<idEntity> );
|
|
break;
|
|
|
|
case D_EVENT_ENTITY_NULL :
|
|
argsize += sizeof( idEntityPtr<idEntity> );
|
|
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( void ) {
|
|
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;
|
|
#ifdef _D3XP
|
|
static idLinkList<idEvent> FastEventQueue;
|
|
#endif
|
|
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;
|
|
}
|
|
|
|
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
|
|
================
|
|
*/
|
|
void idEvent::CopyArgs( const idEventDef *evdef, int numargs, va_list args, intptr_t data[ D_EVENT_MAXARGS ] ) {
|
|
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( void ) {
|
|
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();
|
|
|
|
#ifdef _D3XP
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef _D3XP
|
|
for( event = FastEventQueue.Next(); event != NULL; event = next ) {
|
|
next = event->eventNode.Next();
|
|
if ( event->object == obj ) {
|
|
if ( !evdef || ( evdef == event->eventdef ) ) {
|
|
event->Free();
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEvent::ClearEventList
|
|
================
|
|
*/
|
|
void idEvent::ClearEventList( void ) {
|
|
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( void ) {
|
|
idEvent *event;
|
|
int num;
|
|
intptr_t args[ D_EVENT_MAXARGS ];
|
|
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;
|
|
}
|
|
|
|
// 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." );
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef _D3XP
|
|
/*
|
|
================
|
|
idEvent::ServiceFastEvents
|
|
================
|
|
*/
|
|
void idEvent::ServiceFastEvents() {
|
|
idEvent *event;
|
|
int num;
|
|
intptr_t args[ D_EVENT_MAXARGS ];
|
|
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." );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
================
|
|
idEvent::Init
|
|
================
|
|
*/
|
|
void idEvent::Init( void ) {
|
|
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( void ) {
|
|
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;
|
|
idStr s;
|
|
|
|
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 ) );
|
|
size += sizeof( float );
|
|
break;
|
|
case D_EVENT_INTEGER :
|
|
case D_EVENT_ENTITY :
|
|
case D_EVENT_ENTITY_NULL :
|
|
savefile->WriteInt( *reinterpret_cast<int *>( dataPtr ) );
|
|
size += sizeof( int );
|
|
break;
|
|
case D_EVENT_VECTOR :
|
|
savefile->WriteVec3( *reinterpret_cast<idVec3 *>( dataPtr ) );
|
|
size += sizeof( idVec3 );
|
|
break;
|
|
case D_EVENT_STRING :
|
|
s.Clear();
|
|
s.Append(reinterpret_cast<char *>(dataPtr), MAX_STRING_LEN);
|
|
savefile->WriteString(s);
|
|
size += MAX_STRING_LEN;
|
|
break;
|
|
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 == event->eventdef->GetArgSize() );
|
|
event = event->eventNode.Next();
|
|
}
|
|
|
|
#ifdef _D3XP
|
|
// 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();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
================
|
|
idEvent::Restore
|
|
================
|
|
*/
|
|
void idEvent::Restore( idRestoreGame *savefile ) {
|
|
char *str;
|
|
int num, argsize, i, j, size;
|
|
idStr name;
|
|
byte *dataPtr;
|
|
idEvent *event;
|
|
const char *format;
|
|
idStr s;
|
|
|
|
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 ) {
|
|
savefile->Error( "idEvent::Restore: unknown event '%s'", name.c_str() );
|
|
}
|
|
|
|
// read the classtype
|
|
savefile->ReadString( name );
|
|
event->typeinfo = idClass::GetClass( name );
|
|
if ( !event->typeinfo ) {
|
|
savefile->Error( "idEvent::Restore: unknown class '%s' on event '%s'", name.c_str(), event->eventdef->GetName() );
|
|
}
|
|
|
|
savefile->ReadObject( event->object );
|
|
|
|
// read the args
|
|
savefile->ReadInt( argsize );
|
|
if ( argsize != event->eventdef->GetArgSize() ) {
|
|
savefile->Error( "idEvent::Restore: arg size (%zd) doesn't match saved arg size(%d) on event '%s'", event->eventdef->GetArgSize(), argsize, event->eventdef->GetName() );
|
|
}
|
|
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 ) );
|
|
size += sizeof( float );
|
|
break;
|
|
case D_EVENT_INTEGER :
|
|
case D_EVENT_ENTITY :
|
|
case D_EVENT_ENTITY_NULL :
|
|
savefile->ReadInt( *reinterpret_cast<int *>( dataPtr ) );
|
|
size += sizeof( int );
|
|
break;
|
|
case D_EVENT_VECTOR :
|
|
savefile->ReadVec3( *reinterpret_cast<idVec3 *>( dataPtr ) );
|
|
size += sizeof( idVec3 );
|
|
break;
|
|
case D_EVENT_STRING :
|
|
savefile->ReadString(s);
|
|
idStr::Copynz(reinterpret_cast<char *>(dataPtr), s, MAX_STRING_LEN);
|
|
size += MAX_STRING_LEN;
|
|
break;
|
|
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 == event->eventdef->GetArgSize() );
|
|
} else {
|
|
event->data = NULL;
|
|
}
|
|
}
|
|
|
|
#ifdef _D3XP
|
|
// 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 ) {
|
|
savefile->Error( "idEvent::Restore: unknown event '%s'", name.c_str() );
|
|
}
|
|
|
|
// read the classtype
|
|
savefile->ReadString( name );
|
|
event->typeinfo = idClass::GetClass( name );
|
|
if ( !event->typeinfo ) {
|
|
savefile->Error( "idEvent::Restore: unknown class '%s' on event '%s'", name.c_str(), event->eventdef->GetName() );
|
|
}
|
|
|
|
savefile->ReadObject( event->object );
|
|
|
|
// read the args
|
|
savefile->ReadInt( argsize );
|
|
if ( argsize != event->eventdef->GetArgSize() ) {
|
|
savefile->Error( "idEvent::Restore: arg size (%zd) 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;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
================
|
|
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( void ) {
|
|
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 {
|
|
string1 += "const intptr_t";
|
|
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
|