2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 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.
24 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.
26 ===========================================================================
31 Event are used for scheduling tasks and for linking script commands.
35 #include "../../idlib/precompiled.h"
38 #include "../Game_local.h"
40 #define MAX_EVENTSPERFRAME 4096
41 //#define CREATE_EVENT_CODE
43 /***********************************************************************
47 ***********************************************************************/
49 idEventDef *idEventDef::eventDefList[MAX_EVENTS];
50 int idEventDef::numEventDefs = 0;
52 static bool eventError = false;
53 static char eventErrorMsg[ 128 ];
57 idEventDef::idEventDef
60 idEventDef::idEventDef( const char *command, const char *formatspec, char returnType ) {
66 assert( !idEvent::initialized );
68 // Allow NULL to indicate no args, but always store it as ""
69 // so we don't have to check for it.
75 this->formatspec = formatspec;
76 this->returnType = returnType;
78 numargs = strlen( formatspec );
79 assert( numargs <= D_EVENT_MAXARGS );
80 if ( numargs > D_EVENT_MAXARGS ) {
82 sprintf( eventErrorMsg, "idEventDef::idEventDef : Too many args for '%s' event.", name );
86 // make sure the format for the args is valid, calculate the formatspecindex, and the offsets for each arg
89 memset( argOffset, 0, sizeof( argOffset ) );
90 for( i = 0; i < numargs; i++ ) {
91 argOffset[ i ] = argsize;
92 switch( formatspec[ i ] ) {
95 argsize += sizeof( float );
98 case D_EVENT_INTEGER :
99 argsize += sizeof( int );
102 case D_EVENT_VECTOR :
103 argsize += sizeof( idVec3 );
106 case D_EVENT_STRING :
107 argsize += MAX_STRING_LEN;
110 case D_EVENT_ENTITY :
111 argsize += sizeof( idEntityPtr<idEntity> );
114 case D_EVENT_ENTITY_NULL :
115 argsize += sizeof( idEntityPtr<idEntity> );
119 argsize += sizeof( trace_t ) + MAX_STRING_LEN + sizeof( bool );
124 sprintf( eventErrorMsg, "idEventDef::idEventDef : Invalid arg format '%s' string for '%s' event.", formatspec, name );
130 // calculate the formatspecindex
131 formatspecIndex = ( 1 << ( numargs + D_EVENT_MAXARGS ) ) | bits;
133 // go through the list of defined events and check for duplicates
134 // and mismatched format strings
135 eventnum = numEventDefs;
136 for( i = 0; i < eventnum; i++ ) {
137 ev = eventDefList[ i ];
138 if ( strcmp( command, ev->name ) == 0 ) {
139 if ( strcmp( formatspec, ev->formatspec ) != 0 ) {
141 sprintf( eventErrorMsg, "idEvent '%s' defined twice with same name but differing format strings ('%s'!='%s').",
142 command, formatspec, ev->formatspec );
146 if ( ev->returnType != returnType ) {
148 sprintf( eventErrorMsg, "idEvent '%s' defined twice with same name but differing return types ('%c'!='%c').",
149 command, returnType, ev->returnType );
152 // Don't bother putting the duplicate event in list.
153 eventnum = ev->eventnum;
160 if ( numEventDefs >= MAX_EVENTS ) {
162 sprintf( eventErrorMsg, "numEventDefs >= MAX_EVENTS" );
165 eventDefList[numEventDefs] = ev;
171 idEventDef::NumEventCommands
174 int idEventDef::NumEventCommands( void ) {
180 idEventDef::GetEventCommand
183 const idEventDef *idEventDef::GetEventCommand( int eventnum ) {
184 return eventDefList[ eventnum ];
189 idEventDef::FindEvent
192 const idEventDef *idEventDef::FindEvent( const char *name ) {
200 for( i = 0; i < num; i++ ) {
201 ev = eventDefList[ i ];
202 if ( strcmp( name, ev->name ) == 0 ) {
210 /***********************************************************************
214 ***********************************************************************/
216 static idLinkList<idEvent> FreeEvents;
217 static idLinkList<idEvent> EventQueue;
218 static idEvent EventPool[ MAX_EVENTS ];
220 bool idEvent::initialized = false;
222 idDynamicBlockAlloc<byte, 16 * 1024, 256> idEvent::eventDataAllocator;
229 idEvent::~idEvent() {
238 idEvent *idEvent::Alloc( const idEventDef *evdef, int numargs, va_list args ) {
245 const char *materialName;
247 if ( FreeEvents.IsListEmpty() ) {
248 gameLocal.Error( "idEvent::Alloc : No more free events" );
251 ev = FreeEvents.Next();
252 ev->eventNode.Remove();
254 ev->eventdef = evdef;
256 if ( numargs != evdef->GetNumArgs() ) {
257 gameLocal.Error( "idEvent::Alloc : Wrong number of args for '%s' event.", evdef->GetName() );
260 size = evdef->GetArgSize();
262 ev->data = eventDataAllocator.Alloc( size );
263 memset( ev->data, 0, size );
268 format = evdef->GetArgFormat();
269 for( i = 0; i < numargs; i++ ) {
270 arg = va_arg( args, idEventArg * );
271 if ( format[ i ] != arg->type ) {
272 // when NULL is passed in for an entity, it gets cast as an integer 0, so don't give an error when it happens
273 if ( !( ( ( format[ i ] == D_EVENT_TRACE ) || ( format[ i ] == D_EVENT_ENTITY ) ) && ( arg->type == 'd' ) && ( arg->value == 0 ) ) ) {
274 gameLocal.Error( "idEvent::Alloc : Wrong type passed in for arg # %d on '%s' event.", i, evdef->GetName() );
278 dataPtr = &ev->data[ evdef->GetArgOffset( i ) ];
280 switch( format[ i ] ) {
282 case D_EVENT_INTEGER :
283 *reinterpret_cast<int *>( dataPtr ) = arg->value;
286 case D_EVENT_VECTOR :
288 *reinterpret_cast<idVec3 *>( dataPtr ) = *reinterpret_cast<const idVec3 *>( arg->value );
292 case D_EVENT_STRING :
294 idStr::Copynz( reinterpret_cast<char *>( dataPtr ), reinterpret_cast<const char *>( arg->value ), MAX_STRING_LEN );
298 case D_EVENT_ENTITY :
299 case D_EVENT_ENTITY_NULL :
300 *reinterpret_cast< idEntityPtr<idEntity> * >( dataPtr ) = reinterpret_cast<idEntity *>( arg->value );
305 *reinterpret_cast<bool *>( dataPtr ) = true;
306 *reinterpret_cast<trace_t *>( dataPtr + sizeof( bool ) ) = *reinterpret_cast<const trace_t *>( arg->value );
308 // save off the material as a string since the pointer won't be valid in save games.
309 // since we save off the entire trace_t structure, if the material is NULL here,
310 // it will be NULL when we process it, so we don't need to save off anything in that case.
311 if ( reinterpret_cast<const trace_t *>( arg->value )->c.material ) {
312 materialName = reinterpret_cast<const trace_t *>( arg->value )->c.material->GetName();
313 idStr::Copynz( reinterpret_cast<char *>( dataPtr + sizeof( bool ) + sizeof( trace_t ) ), materialName, MAX_STRING_LEN );
316 *reinterpret_cast<bool *>( dataPtr ) = false;
321 gameLocal.Error( "idEvent::Alloc : Invalid arg format '%s' string for '%s' event.", format, evdef->GetName() );
334 void idEvent::CopyArgs( const idEventDef *evdef, int numargs, va_list args, int data[ D_EVENT_MAXARGS ] ) {
339 format = evdef->GetArgFormat();
340 if ( numargs != evdef->GetNumArgs() ) {
341 gameLocal.Error( "idEvent::CopyArgs : Wrong number of args for '%s' event.", evdef->GetName() );
344 for( i = 0; i < numargs; i++ ) {
345 arg = va_arg( args, idEventArg * );
346 if ( format[ i ] != arg->type ) {
347 // when NULL is passed in for an entity, it gets cast as an integer 0, so don't give an error when it happens
348 if ( !( ( ( format[ i ] == D_EVENT_TRACE ) || ( format[ i ] == D_EVENT_ENTITY ) ) && ( arg->type == 'd' ) && ( arg->value == 0 ) ) ) {
349 gameLocal.Error( "idEvent::CopyArgs : Wrong type passed in for arg # %d on '%s' event.", i, evdef->GetName() );
353 data[ i ] = arg->value;
362 void idEvent::Free( void ) {
364 eventDataAllocator.Free( data );
373 eventNode.SetOwner( this );
374 eventNode.AddToEnd( FreeEvents );
382 void idEvent::Schedule( idClass *obj, const idTypeInfo *type, int time ) {
385 assert( initialized );
386 if ( !initialized ) {
393 // wraps after 24 days...like I care. ;)
394 this->time = gameLocal.time + time;
398 event = EventQueue.Next();
399 while( ( event != NULL ) && ( this->time >= event->time ) ) {
400 event = event->eventNode.Next();
404 eventNode.InsertBefore( event->eventNode );
406 eventNode.AddToEnd( EventQueue );
412 idEvent::CancelEvents
415 void idEvent::CancelEvents( const idClass *obj, const idEventDef *evdef ) {
419 if ( !initialized ) {
423 for( event = EventQueue.Next(); event != NULL; event = next ) {
424 next = event->eventNode.Next();
425 if ( event->object == obj ) {
426 if ( !evdef || ( evdef == event->eventdef ) ) {
435 idEvent::ClearEventList
438 void idEvent::ClearEventList( void ) {
448 // add the events to the free list
450 for( i = 0; i < MAX_EVENTS; i++ ) {
451 EventPool[ i ].Free();
457 idEvent::ServiceEvents
460 void idEvent::ServiceEvents( void ) {
463 int args[ D_EVENT_MAXARGS ];
467 const char *formatspec;
469 const idEventDef *ev;
471 const char *materialName;
474 while( !EventQueue.IsListEmpty() ) {
475 event = EventQueue.Next();
478 if ( event->time > gameLocal.time ) {
482 // copy the data into the local args array and set up pointers
483 ev = event->eventdef;
484 formatspec = ev->GetArgFormat();
485 numargs = ev->GetNumArgs();
486 for( i = 0; i < numargs; i++ ) {
487 offset = ev->GetArgOffset( i );
489 switch( formatspec[ i ] ) {
491 case D_EVENT_INTEGER :
492 args[ i ] = *reinterpret_cast<int *>( &data[ offset ] );
495 case D_EVENT_VECTOR :
496 *reinterpret_cast<idVec3 **>( &args[ i ] ) = reinterpret_cast<idVec3 *>( &data[ offset ] );
499 case D_EVENT_STRING :
500 *reinterpret_cast<const char **>( &args[ i ] ) = reinterpret_cast<const char *>( &data[ offset ] );
503 case D_EVENT_ENTITY :
504 case D_EVENT_ENTITY_NULL :
505 *reinterpret_cast<idEntity **>( &args[ i ] ) = reinterpret_cast< idEntityPtr<idEntity> * >( &data[ offset ] )->GetEntity();
509 tracePtr = reinterpret_cast<trace_t **>( &args[ i ] );
510 if ( *reinterpret_cast<bool *>( &data[ offset ] ) ) {
511 *tracePtr = reinterpret_cast<trace_t *>( &data[ offset + sizeof( bool ) ] );
513 if ( ( *tracePtr )->c.material != NULL ) {
514 // look up the material name to get the material pointer
515 materialName = reinterpret_cast<const char *>( &data[ offset + sizeof( bool ) + sizeof( trace_t ) ] );
516 ( *tracePtr )->c.material = declManager->FindMaterial( materialName, true );
524 gameLocal.Error( "idEvent::ServiceEvents : Invalid arg format '%s' string for '%s' event.", formatspec, ev->GetName() );
528 // the event is removed from its list so that if then object
529 // is deleted, the event won't be freed twice
530 event->eventNode.Remove();
531 assert( event->object );
532 event->object->ProcessEventArgPtr( ev, args );
535 // event functions may never leave return values on the FPU stack
536 // enable this code to check if any event call left values on the FPU stack
537 if ( !sys->FPU_StackIsEmpty() ) {
538 gameLocal.Error( "idEvent::ServiceEvents %d: %s left a value on the FPU stack\n", num, ev->GetName() );
542 // return the event to the free list
545 // Don't allow ourselves to stay in here too long. An abnormally high number
546 // of events being processed is evidence of an infinite loop of events.
548 if ( num > MAX_EVENTSPERFRAME ) {
549 gameLocal.Error( "Event overflow. Possible infinite loop in script." );
559 void idEvent::Init( void ) {
560 gameLocal.Printf( "Initializing event system\n" );
563 gameLocal.Error( "%s", eventErrorMsg );
566 #ifdef CREATE_EVENT_CODE
567 void CreateEventCallbackHandler();
568 CreateEventCallbackHandler();
569 gameLocal.Error( "Wrote event callback handler" );
573 gameLocal.Printf( "...already initialized\n" );
580 eventDataAllocator.Init();
582 gameLocal.Printf( "...%i event definitions\n", idEventDef::NumEventCommands() );
584 // the event system has started
593 void idEvent::Shutdown( void ) {
594 gameLocal.Printf( "Shutdown event system\n" );
596 if ( !initialized ) {
597 gameLocal.Printf( "...not started\n" );
603 eventDataAllocator.Shutdown();
605 // say it is now shutdown
614 void idEvent::Save( idSaveGame *savefile ) {
622 savefile->WriteInt( EventQueue.Num() );
624 event = EventQueue.Next();
625 while( event != NULL ) {
626 savefile->WriteInt( event->time );
627 savefile->WriteString( event->eventdef->GetName() );
628 savefile->WriteString( event->typeinfo->classname );
629 savefile->WriteObject( event->object );
630 savefile->WriteInt( event->eventdef->GetArgSize() );
631 format = event->eventdef->GetArgFormat();
632 for ( i = 0, size = 0; i < event->eventdef->GetNumArgs(); ++i) {
633 dataPtr = &event->data[ event->eventdef->GetArgOffset( i ) ];
634 switch( format[ i ] ) {
636 savefile->WriteFloat( *reinterpret_cast<float *>( dataPtr ) );
637 size += sizeof( float );
639 case D_EVENT_INTEGER :
640 case D_EVENT_ENTITY :
641 case D_EVENT_ENTITY_NULL :
642 savefile->WriteInt( *reinterpret_cast<int *>( dataPtr ) );
643 size += sizeof( int );
645 case D_EVENT_VECTOR :
646 savefile->WriteVec3( *reinterpret_cast<idVec3 *>( dataPtr ) );
647 size += sizeof( idVec3 );
650 validTrace = *reinterpret_cast<bool *>( dataPtr );
651 savefile->WriteBool( validTrace );
652 size += sizeof( bool );
654 size += sizeof( trace_t );
655 const trace_t &t = *reinterpret_cast<trace_t *>( dataPtr + sizeof( bool ) );
656 SaveTrace( savefile, t );
657 if ( t.c.material ) {
658 size += MAX_STRING_LEN;
659 str = reinterpret_cast<char *>( dataPtr + sizeof( bool ) + sizeof( trace_t ) );
660 savefile->Write( str, MAX_STRING_LEN );
668 assert( size == event->eventdef->GetArgSize() );
669 event = event->eventNode.Next();
678 void idEvent::Restore( idRestoreGame *savefile ) {
680 int num, argsize, i, j, size;
686 savefile->ReadInt( num );
688 for ( i = 0; i < num; i++ ) {
689 if ( FreeEvents.IsListEmpty() ) {
690 gameLocal.Error( "idEvent::Restore : No more free events" );
693 event = FreeEvents.Next();
694 event->eventNode.Remove();
695 event->eventNode.AddToEnd( EventQueue );
697 savefile->ReadInt( event->time );
699 // read the event name
700 savefile->ReadString( name );
701 event->eventdef = idEventDef::FindEvent( name );
702 if ( !event->eventdef ) {
703 savefile->Error( "idEvent::Restore: unknown event '%s'", name.c_str() );
706 // read the classtype
707 savefile->ReadString( name );
708 event->typeinfo = idClass::GetClass( name );
709 if ( !event->typeinfo ) {
710 savefile->Error( "idEvent::Restore: unknown class '%s' on event '%s'", name.c_str(), event->eventdef->GetName() );
713 savefile->ReadObject( event->object );
716 savefile->ReadInt( argsize );
717 if ( argsize != event->eventdef->GetArgSize() ) {
718 savefile->Error( "idEvent::Restore: arg size (%d) doesn't match saved arg size(%d) on event '%s'", event->eventdef->GetArgSize(), argsize, event->eventdef->GetName() );
721 event->data = eventDataAllocator.Alloc( argsize );
722 format = event->eventdef->GetArgFormat();
724 for ( j = 0, size = 0; j < event->eventdef->GetNumArgs(); ++j) {
725 dataPtr = &event->data[ event->eventdef->GetArgOffset( j ) ];
726 switch( format[ j ] ) {
728 savefile->ReadFloat( *reinterpret_cast<float *>( dataPtr ) );
729 size += sizeof( float );
731 case D_EVENT_INTEGER :
732 case D_EVENT_ENTITY :
733 case D_EVENT_ENTITY_NULL :
734 savefile->ReadInt( *reinterpret_cast<int *>( dataPtr ) );
735 size += sizeof( int );
737 case D_EVENT_VECTOR :
738 savefile->ReadVec3( *reinterpret_cast<idVec3 *>( dataPtr ) );
739 size += sizeof( idVec3 );
742 savefile->ReadBool( *reinterpret_cast<bool *>( dataPtr ) );
743 size += sizeof( bool );
744 if ( *reinterpret_cast<bool *>( dataPtr ) ) {
745 size += sizeof( trace_t );
746 trace_t &t = *reinterpret_cast<trace_t *>( dataPtr + sizeof( bool ) );
747 RestoreTrace( savefile, t) ;
748 if ( t.c.material ) {
749 size += MAX_STRING_LEN;
750 str = reinterpret_cast<char *>( dataPtr + sizeof( bool ) + sizeof( trace_t ) );
751 savefile->Read( str, MAX_STRING_LEN );
759 assert( size == event->eventdef->GetArgSize() );
770 idRestoreGame has a ReadTrace procedure, but unfortunately idEvent wants the material
771 string name at the of the data structure rather than in the middle
774 void idEvent::RestoreTrace( idRestoreGame *savefile, trace_t &trace ) {
775 savefile->ReadFloat( trace.fraction );
776 savefile->ReadVec3( trace.endpos );
777 savefile->ReadMat3( trace.endAxis );
778 savefile->ReadInt( (int&)trace.c.type );
779 savefile->ReadVec3( trace.c.point );
780 savefile->ReadVec3( trace.c.normal );
781 savefile->ReadFloat( trace.c.dist );
782 savefile->ReadInt( trace.c.contents );
783 savefile->ReadInt( (int&)trace.c.material );
784 savefile->ReadInt( trace.c.contents );
785 savefile->ReadInt( trace.c.modelFeature );
786 savefile->ReadInt( trace.c.trmFeature );
787 savefile->ReadInt( trace.c.id );
794 idSaveGame has a WriteTrace procedure, but unfortunately idEvent wants the material
795 string name at the of the data structure rather than in the middle
798 void idEvent::SaveTrace( idSaveGame *savefile, const trace_t &trace ) {
799 savefile->WriteFloat( trace.fraction );
800 savefile->WriteVec3( trace.endpos );
801 savefile->WriteMat3( trace.endAxis );
802 savefile->WriteInt( trace.c.type );
803 savefile->WriteVec3( trace.c.point );
804 savefile->WriteVec3( trace.c.normal );
805 savefile->WriteFloat( trace.c.dist );
806 savefile->WriteInt( trace.c.contents );
807 savefile->WriteInt( (int&)trace.c.material );
808 savefile->WriteInt( trace.c.contents );
809 savefile->WriteInt( trace.c.modelFeature );
810 savefile->WriteInt( trace.c.trmFeature );
811 savefile->WriteInt( trace.c.id );
816 #ifdef CREATE_EVENT_CODE
819 CreateEventCallbackHandler
822 void CreateEventCallbackHandler( void ) {
826 char argString[ D_EVENT_MAXARGS + 1 ];
831 file = fileSystem->OpenFileWrite( "Callbacks.cpp" );
833 file->Printf( "// generated file - see CREATE_EVENT_CODE\n\n" );
835 for( i = 1; i <= D_EVENT_MAXARGS; i++ ) {
837 file->Printf( "\t/*******************************************************\n\n\t\t%d args\n\n\t*******************************************************/\n\n", i );
839 for ( j = 0; j < ( 1 << i ); j++ ) {
840 for ( k = 0; k < i; k++ ) {
841 argString[ k ] = j & ( 1 << k ) ? 'f' : 'i';
843 argString[ i ] = '\0';
848 for( k = 0; k < i; k++ ) {
849 if ( j & ( 1 << k ) ) {
850 string1 += "const float";
851 string2 += va( "*( float * )&data[ %d ]", k );
853 string1 += "const int";
854 string2 += va( "data[ %d ]", k );
863 file->Printf( "\tcase %d :\n\t\ttypedef void ( idClass::*eventCallback_%s_t )( %s );\n", ( 1 << ( i + D_EVENT_MAXARGS ) ) + j, argString, string1.c_str() );
864 file->Printf( "\t\t( this->*( eventCallback_%s_t )callback )( %s );\n\t\tbreak;\n\n", argString, string2.c_str() );
869 fileSystem->CloseFile( file );