home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2006 March / Gamestar_82_2006-03_dvd.iso / DVDStar / Editace / quake4_sdkv10.exe / source / game / gamesys / Event.cpp < prev    next >
C/C++ Source or Header  |  2005-11-14  |  20KB  |  810 lines

  1. /*
  2. sys_event.cpp
  3.  
  4. Event are used for scheduling tasks and for linking script commands.
  5.  
  6. */
  7.  
  8. #include "../../idlib/precompiled.h"
  9. #pragma hdrstop
  10.  
  11. #include "../Game_local.h"
  12.  
  13. #define MAX_EVENTSPERFRAME            8192        // Upped from 4096
  14. //#define CREATE_EVENT_CODE
  15.  
  16. /***********************************************************************
  17.  
  18.   idEventDef
  19.  
  20. ***********************************************************************/
  21.  
  22. idEventDef *idEventDef::eventDefList[MAX_EVENTS];
  23. int idEventDef::numEventDefs = 0;
  24.  
  25. static bool eventError = false;
  26. static char eventErrorMsg[ 128 ];
  27.  
  28. /*
  29. ================
  30. idEventDef::idEventDef
  31. ================
  32. */
  33. idEventDef::idEventDef( const char *command, const char *formatspec, char returnType ) {
  34.     idEventDef        *ev;
  35.     int                i;
  36.     unsigned int    bits;
  37.  
  38.     assert( command );
  39.     assert( !idEvent::initialized );
  40.  
  41.     // Allow NULL to indicate no args, but always store it as ""
  42.     // so we don't have to check for it.
  43.     if ( !formatspec ) {
  44.         formatspec = "";
  45.     }
  46.     
  47.     this->name = command;
  48.     this->formatspec = formatspec;
  49.     this->returnType = returnType;
  50.  
  51.     numargs = strlen( formatspec );
  52.     assert( numargs <= D_EVENT_MAXARGS );
  53.     if ( numargs > D_EVENT_MAXARGS ) {
  54.         eventError = true;
  55.         sprintf( eventErrorMsg, "idEventDef::idEventDef : Too many args for '%s' event.", name );
  56.         return;
  57.     }
  58.  
  59.     // make sure the format for the args is valid, calculate the formatspecindex, and the offsets for each arg
  60.     bits = 0;
  61.     argsize = 0;
  62.     memset( argOffset, 0, sizeof( argOffset ) );
  63.     for( i = 0; i < numargs; i++ ) {
  64.         argOffset[ i ] = argsize;
  65.         switch( formatspec[ i ] ) {
  66.         case D_EVENT_FLOAT :
  67.             bits |= 1 << i;
  68.             argsize += sizeof( float );
  69.             break;
  70.  
  71.         case D_EVENT_INTEGER :
  72.             argsize += sizeof( int );
  73.             break;
  74.  
  75.         case D_EVENT_VECTOR :
  76.             argsize += sizeof( idVec3 );
  77.             break;
  78.  
  79.         case D_EVENT_STRING :
  80.             argsize += MAX_STRING_LEN;
  81.             break;
  82.  
  83.         case D_EVENT_ENTITY :
  84.             argsize += sizeof( idEntityPtr<idEntity> );
  85.             break;
  86.  
  87.         case D_EVENT_ENTITY_NULL :
  88.             argsize += sizeof( idEntityPtr<idEntity> );
  89.             break;
  90.  
  91.         case D_EVENT_TRACE :
  92.             argsize += sizeof( trace_t ) + MAX_STRING_LEN + sizeof( bool );
  93.             break;
  94.  
  95.         default :
  96.             eventError = true;
  97.             sprintf( eventErrorMsg, "idEventDef::idEventDef : Invalid arg format '%s' string for '%s' event.", formatspec, name );
  98.             return;
  99.             break;
  100.         }
  101.     }
  102.  
  103.     // calculate the formatspecindex
  104.     formatspecIndex = ( 1 << ( numargs + D_EVENT_MAXARGS ) ) | bits;
  105.  
  106.     // go through the list of defined events and check for duplicates
  107.     // and mismatched format strings
  108.     eventnum = numEventDefs;
  109.     for( i = 0; i < eventnum; i++ ) {
  110.         ev = eventDefList[ i ];
  111.         if ( idStr::Cmp( command, ev->name ) == 0 ) {
  112.             if ( idStr::Cmp( formatspec, ev->formatspec ) != 0 ) {
  113.                 eventError = true;
  114.                 sprintf( eventErrorMsg, "idEvent '%s' defined twice with same name but differing format strings ('%s'!='%s').",
  115.                     command, formatspec, ev->formatspec );
  116.                 return;
  117.             }
  118.  
  119.             if ( ev->returnType != returnType ) {
  120.                 eventError = true;
  121.                 sprintf( eventErrorMsg, "idEvent '%s' defined twice with same name but differing return types ('%c'!='%c').",
  122.                     command, returnType, ev->returnType );
  123.                 return;
  124.             }
  125.             // Don't bother putting the duplicate event in list.
  126.             eventnum = ev->eventnum;
  127.             return;
  128.         }
  129.     }
  130.  
  131.     ev = this;
  132.  
  133.     if ( numEventDefs >= MAX_EVENTS ) {
  134.         eventError = true;
  135.         sprintf( eventErrorMsg, "numEventDefs >= MAX_EVENTS" );
  136.         return;
  137.     }
  138.     eventDefList[numEventDefs] = ev;
  139.     numEventDefs++;
  140. }
  141.  
  142. /*
  143. ================
  144. idEventDef::NumEventCommands
  145. ================
  146. */
  147. int    idEventDef::NumEventCommands( void ) {
  148.     return numEventDefs;
  149. }
  150.  
  151. /*
  152. ================
  153. idEventDef::GetEventCommand
  154. ================
  155. */
  156. const idEventDef *idEventDef::GetEventCommand( int eventnum ) {
  157.     return eventDefList[ eventnum ];
  158. }
  159.  
  160. /*
  161. ================
  162. idEventDef::FindEvent
  163. ================
  164. */
  165. const idEventDef *idEventDef::FindEvent( const char *name ) {
  166.     idEventDef    *ev;
  167.     int            num;
  168.     int            i;
  169.  
  170.     assert( name );
  171.  
  172.     num = numEventDefs;
  173.     for( i = 0; i < num; i++ ) {
  174.         ev = eventDefList[ i ];
  175.         if ( idStr::Cmp( name, ev->name ) == 0 ) {
  176.             return ev;
  177.         }
  178.     }
  179.  
  180.     return NULL;
  181. }
  182.  
  183. /***********************************************************************
  184.  
  185.   idEvent
  186.  
  187. ***********************************************************************/
  188.  
  189. static idLinkList<idEvent> FreeEvents;
  190. static idLinkList<idEvent> EventQueue;
  191. static idEvent EventPool[ MAX_EVENTS ];
  192.  
  193. bool idEvent::initialized = false;
  194.  
  195. idDynamicBlockAlloc<byte, 16 * 1024, 256, MA_EVENT>    idEvent::eventDataAllocator;
  196.  
  197. /*
  198. ================
  199. idEvent::~idEvent()
  200. ================
  201. */
  202. idEvent::~idEvent() {
  203.     Free();
  204. }
  205.  
  206.  
  207. void idEvent::WriteDebugInfo( void ) {
  208.     idEvent    *event;
  209.     int        count = 0;
  210.  
  211.     idFile *FH = fileSystem->OpenFileAppend( "idEvents.txt" );
  212.  
  213.     FH->Printf( "Num Events = %d\n", EventQueue.Num() );
  214.  
  215.     event = EventQueue.Next();
  216.     while( event != NULL ) {
  217.         count++;
  218.  
  219.         FH->Printf( "%d. %d - %s - %s - %s\n", count, event->time, event->eventdef->GetName(), event->typeinfo->classname, event->object->GetClassname() );
  220.  
  221.         event = event->eventNode.Next();
  222.     }
  223.  
  224.     FH->Printf( "\n\n" );
  225.     fileSystem->CloseFile( FH );
  226. }
  227.  
  228. /*
  229. ================
  230. idEvent::Alloc
  231. ================
  232. */
  233. idEvent *idEvent::Alloc( const idEventDef *evdef, int numargs, va_list args ) {
  234.     idEvent        *ev;
  235.     size_t        size;
  236.     const char    *format;
  237.     idEventArg    *arg;
  238.     byte        *dataPtr;
  239.     int            i;
  240.     const char    *materialName;
  241.  
  242.     if ( FreeEvents.IsListEmpty() ) {
  243.         WriteDebugInfo( );
  244.         gameLocal.Error( "idEvent::Alloc : No more free events for '%s' event.", evdef->GetName() );
  245.     }
  246.  
  247.     ev = FreeEvents.Next();
  248.     ev->eventNode.Remove();
  249.  
  250.     ev->eventdef = evdef;
  251.  
  252.     if ( numargs != evdef->GetNumArgs() ) {
  253.         gameLocal.Error( "idEvent::Alloc : Wrong number of args for '%s' event.", evdef->GetName() );
  254.     }
  255.  
  256.     size = evdef->GetArgSize();
  257.     if ( size ) {
  258.         ev->data = eventDataAllocator.Alloc( size );
  259.         memset( ev->data, 0, size );
  260.     } else {
  261.         ev->data = NULL;
  262.     }
  263.  
  264.     format = evdef->GetArgFormat();
  265.     for( i = 0; i < numargs; i++ ) {
  266.         arg = va_arg( args, idEventArg * );
  267.         if ( format[ i ] != arg->type ) {
  268. // RAVEN BEGIN
  269. // abahr: type checking change as per Jim D.
  270.             if ( ( format[ i ] == D_EVENT_ENTITY_NULL ) && ( arg->type == D_EVENT_ENTITY ) ) {
  271.             // these types are identical, so allow them
  272.             } else if ( ( arg->type == D_EVENT_INTEGER ) && ( arg->value == 0 ) ) {
  273.                 if ( ( format[ i ] == D_EVENT_ENTITY ) || ( format[ i ] == D_EVENT_ENTITY_NULL ) || ( format[ i ] == D_EVENT_TRACE ) ) {
  274.                  // when NULL is passed in for an entity or trace, it gets cast as an integer 0, so don't give an error when it happens
  275.                 } else {
  276.                      gameLocal.Error( "idEvent::Alloc : Wrong type passed in for arg # %d on '%s' event.", i, evdef->GetName() );
  277.                 }
  278.             } else {
  279.                 gameLocal.Error( "idEvent::Alloc : Wrong type passed in for arg # %d on '%s' event.", i, evdef->GetName() );
  280.             }
  281. // RAVEN END
  282.         }
  283.  
  284.         dataPtr = &ev->data[ evdef->GetArgOffset( i ) ];
  285.  
  286.         switch( format[ i ] ) {
  287.         case D_EVENT_FLOAT :
  288.         case D_EVENT_INTEGER :
  289.             *reinterpret_cast<int *>( dataPtr ) = arg->value;
  290.             break;
  291.  
  292.         case D_EVENT_VECTOR :
  293.             if ( arg->value ) {
  294.                 *reinterpret_cast<idVec3 *>( dataPtr ) = *reinterpret_cast<const idVec3 *>( arg->value );
  295.             }
  296.             break;
  297.  
  298.         case D_EVENT_STRING :
  299.             if ( arg->value ) {
  300.                 idStr::Copynz( reinterpret_cast<char *>( dataPtr ), reinterpret_cast<const char *>( arg->value ), MAX_STRING_LEN );
  301.             }
  302.             break;
  303.  
  304. // RAVEN BEGIN
  305. // abahr: type checking change as per Jim D.
  306. // jshepard: TODO FIXME HACK this never ever produces desired, positive results. Events should be built to prepare for null entities, especially when dealing with 
  307. //                             script events. This will throw a warning, and events should be prepared to deal with null entities. 
  308.         case D_EVENT_ENTITY :
  309.             if ( reinterpret_cast<idEntity *>( arg->value ) == NULL ) {
  310.                 gameLocal.Warning( "idEvent::Alloc : NULL entity passed in to event function that expects a non-NULL pointer on arg # %d on '%s' event.", i, evdef->GetName() );
  311.             }
  312.             *reinterpret_cast< idEntityPtr<idEntity> * >( dataPtr ) = reinterpret_cast<idEntity *>( arg->value );
  313.             break;
  314.  
  315.         case D_EVENT_ENTITY_NULL :
  316.             *reinterpret_cast< idEntityPtr<idEntity> * >( dataPtr ) = reinterpret_cast<idEntity *>( arg->value );
  317.             break;
  318. //RAVEN END
  319.  
  320.         case D_EVENT_TRACE :
  321.             if ( arg->value ) {
  322.                 *reinterpret_cast<bool *>( dataPtr ) = true;
  323.                 *reinterpret_cast<trace_t *>( dataPtr + sizeof( bool ) ) = *reinterpret_cast<const trace_t *>( arg->value );
  324.  
  325.                 // save off the material as a string since the pointer won't be valid in save games.
  326.                 // since we save off the entire trace_t structure, if the material is NULL here,
  327.                 // it will be NULL when we process it, so we don't need to save off anything in that case.
  328.                 if ( reinterpret_cast<const trace_t *>( arg->value )->c.material ) {
  329.                     materialName = reinterpret_cast<const trace_t *>( arg->value )->c.material->GetName();
  330.                     idStr::Copynz( reinterpret_cast<char *>( dataPtr + sizeof( bool ) + sizeof( trace_t ) ), materialName, MAX_STRING_LEN );
  331.                 }
  332.             } else {
  333.                 *reinterpret_cast<bool *>( dataPtr ) = false;
  334.             }
  335.             break;
  336.  
  337.         default :
  338.             gameLocal.Error( "idEvent::Alloc : Invalid arg format '%s' string for '%s' event.", format, evdef->GetName() );
  339.             break;
  340.         }
  341.     }
  342.  
  343.     return ev;
  344. }
  345.  
  346. /*
  347. ================
  348. idEvent::CopyArgs
  349. ================
  350. */
  351. void idEvent::CopyArgs( const idEventDef *evdef, int numargs, va_list args, int data[ D_EVENT_MAXARGS ] ) {
  352.     int            i;
  353.     const char    *format;
  354.     idEventArg    *arg;
  355.  
  356.     format = evdef->GetArgFormat();
  357.     if ( numargs != evdef->GetNumArgs() ) {
  358.         gameLocal.Error( "idEvent::CopyArgs : Wrong number of args for '%s' event.", evdef->GetName() );
  359.     }
  360.  
  361.     for( i = 0; i < numargs; i++ ) {
  362.         arg = va_arg( args, idEventArg * );
  363.         if ( format[ i ] != arg->type ) {
  364. // RAVEN BEGIN
  365. // abahr: type checking change as per Jim D.
  366.             if ( ( format[ i ] == D_EVENT_ENTITY_NULL ) && ( arg->type == D_EVENT_ENTITY ) ) {
  367.             // these types are identical, so allow them
  368.             } else if ( ( arg->type == D_EVENT_INTEGER ) && ( arg->value == 0 ) ) {
  369.                 if ( ( format[ i ] == D_EVENT_ENTITY ) || ( format[ i ] == D_EVENT_ENTITY_NULL ) ) {
  370.                 // when NULL is passed in for an entity, it gets cast as an integer 0, so don't give an error when it happens
  371.                 } else {
  372.                     gameLocal.Error( "idEvent::Alloc : Wrong type passed in for arg # %d on '%s' event.", i, evdef->GetName() );
  373.                 }
  374.             } else {
  375.                 gameLocal.Error( "idEvent::Alloc : Wrong type passed in for arg # %d on '%s' event.", i, evdef->GetName() );
  376.             }
  377. // RAVEN END
  378.         }
  379.  
  380.         data[ i ] = arg->value;
  381.     }
  382. }
  383.  
  384. /*
  385. ================
  386. idEvent::Free
  387. ================
  388. */
  389. void idEvent::Free( void ) {
  390.     if ( data ) {
  391.         eventDataAllocator.Free( data );
  392.         data = NULL;
  393.     }
  394.  
  395.     eventdef    = NULL;
  396.     time        = 0;
  397.     object        = NULL;
  398.     typeinfo    = NULL;
  399.  
  400.     eventNode.SetOwner( this );
  401.     eventNode.AddToEnd( FreeEvents );
  402. }
  403.  
  404. /*
  405. ================
  406. idEvent::Schedule
  407. ================
  408. */
  409. void idEvent::Schedule( idClass *obj, const idTypeInfo *type, int time ) {
  410.     idEvent *event;
  411.  
  412.     assert( initialized );
  413.     if ( !initialized ) {
  414.         return;
  415.     }
  416.  
  417.     object = obj;
  418.     typeinfo = type;
  419.  
  420.     // wraps after 24 days...like I care. ;)
  421.     this->time = gameLocal.time + time;
  422.  
  423.     eventNode.Remove();
  424.  
  425.     event = EventQueue.Next();
  426.     while( ( event != NULL ) && ( this->time >= event->time ) ) {
  427.         event = event->eventNode.Next();
  428.     }
  429.  
  430.     if ( event ) {
  431.         eventNode.InsertBefore( event->eventNode );
  432.     } else {
  433.         eventNode.AddToEnd( EventQueue );
  434.     }
  435. }
  436.  
  437. /*
  438. ================
  439. idEvent::CancelEvents
  440. ================
  441. */
  442. void idEvent::CancelEvents( const idClass *obj, const idEventDef *evdef ) {
  443.     idEvent *event;
  444.     idEvent *next;
  445.  
  446.     if ( !initialized ) {
  447.         return;
  448.     }
  449.  
  450.     for( event = EventQueue.Next(); event != NULL; event = next ) {
  451.         next = event->eventNode.Next();
  452.         if ( event->object == obj ) {
  453.             if ( !evdef || ( evdef == event->eventdef ) ) {
  454.                 event->Free();
  455.             }
  456.         }
  457.     }
  458. }
  459.  
  460. // RAVEN BEGIN
  461. // abahr:
  462. /*
  463. ================
  464. idEvent::EventIsPosted
  465. ================
  466. */
  467. bool idEvent::EventIsPosted( const idClass *obj, const idEventDef *evdef ) {
  468.     idEvent *event;
  469.     idEvent *next;
  470.  
  471.     if ( !initialized ) {
  472.         return false;
  473.     }
  474.  
  475.     for( event = EventQueue.Next(); event != NULL; event = next ) {
  476.         next = event->eventNode.Next();
  477.         if( event->object == obj && evdef == event->eventdef ) {
  478.             return true;
  479.         }
  480.     }
  481.  
  482.     return false;
  483. }
  484. // RAVEN END
  485.  
  486. /*
  487. ================
  488. idEvent::ClearEventList
  489. ================
  490. */
  491. void idEvent::ClearEventList( void ) {
  492.     int i;
  493.  
  494.     //
  495.     // initialize lists
  496.     //
  497.     FreeEvents.Clear();
  498.     EventQueue.Clear();
  499.    
  500.     // 
  501.     // add the events to the free list
  502.     //
  503.     for( i = 0; i < MAX_EVENTS; i++ ) {
  504.         EventPool[ i ].Free();
  505.     }
  506. }
  507.  
  508. /*
  509. ================
  510. idEvent::ServiceEvents
  511. ================
  512. */
  513. void idEvent::ServiceEvents( void ) {
  514.     idEvent        *event;
  515.     int            num;
  516.     int            args[ D_EVENT_MAXARGS ];
  517.     int            offset;
  518.     int            i;
  519.     int            numargs;
  520.     const char    *formatspec;
  521.     trace_t        **tracePtr;
  522.     const idEventDef *ev;
  523.     byte        *data;
  524.     const char  *materialName;
  525.  
  526.     num = 0;
  527.     while( !EventQueue.IsListEmpty() ) {
  528.         
  529. #ifdef _XENON
  530.         session->PacifierUpdate();
  531. #endif
  532.         
  533.         event = EventQueue.Next();
  534.         assert( event );
  535.  
  536.         if ( event->time > gameLocal.time ) {
  537.             break;
  538.         }
  539.  
  540.         // copy the data into the local args array and set up pointers
  541.         ev = event->eventdef;
  542.         formatspec = ev->GetArgFormat();
  543.         numargs = ev->GetNumArgs();
  544.         for( i = 0; i < numargs; i++ ) {
  545.             offset = ev->GetArgOffset( i );
  546.             data = event->data;
  547.             switch( formatspec[ i ] ) {
  548.             case D_EVENT_FLOAT :
  549.             case D_EVENT_INTEGER :
  550.                 args[ i ] = *reinterpret_cast<int *>( &data[ offset ] );
  551.                 break;
  552.  
  553.             case D_EVENT_VECTOR :
  554.                 *reinterpret_cast<idVec3 **>( &args[ i ] ) = reinterpret_cast<idVec3 *>( &data[ offset ] );
  555.                 break;
  556.  
  557.             case D_EVENT_STRING :
  558.                 *reinterpret_cast<const char **>( &args[ i ] ) = reinterpret_cast<const char *>( &data[ offset ] );
  559.                 break;
  560. // RAVEN BEGIN
  561. // abahr: type checking change as per Jim D.
  562.             case D_EVENT_ENTITY :
  563.                 *reinterpret_cast<idEntity **>( &args[ i ] ) = reinterpret_cast< idEntityPtr<idEntity> * >( &data[ offset ] )->GetEntity();
  564.                 if ( *reinterpret_cast<idEntity **>( &args[ i ] ) == NULL ) {
  565.                     gameLocal.Warning( "idEvent::ServiceEvents : NULL entity passed in to event function that expects a non-NULL pointer on arg # %d on '%s' event.", i, ev->GetName() );
  566.                 }
  567.                 break;
  568.  
  569.             case D_EVENT_ENTITY_NULL :
  570.                 *reinterpret_cast<idEntity **>( &args[ i ] ) = reinterpret_cast< idEntityPtr<idEntity> * >( &data[ offset ] )->GetEntity();
  571.                 break;
  572. // RAVEN END
  573.             case D_EVENT_TRACE :
  574.                 tracePtr = reinterpret_cast<trace_t **>( &args[ i ] );
  575.                 if ( *reinterpret_cast<bool *>( &data[ offset ] ) ) {
  576.                     *tracePtr = reinterpret_cast<trace_t *>( &data[ offset + sizeof( bool ) ] );
  577.  
  578.                     if ( ( *tracePtr )->c.material != NULL ) {
  579.                         // look up the material name to get the material pointer
  580.                         materialName = reinterpret_cast<const char *>( &data[ offset + sizeof( bool ) + sizeof( trace_t ) ] );
  581.                         ( *tracePtr )->c.material = declManager->FindMaterial( materialName, true );
  582.                     }
  583.                 } else {
  584.                     *tracePtr = NULL;
  585.                 }
  586.                 break;
  587.  
  588.             default:
  589.                 gameLocal.Error( "idEvent::ServiceEvents : Invalid arg format '%s' string for '%s' event.", formatspec, ev->GetName() );
  590.             }
  591.         }
  592.  
  593.         // the event is removed from its list so that if then object
  594.         // is deleted, the event won't be freed twice
  595.         event->eventNode.Remove();
  596.         assert( event->object );
  597.         event->object->ProcessEventArgPtr( ev, args );
  598.  
  599. #if 0
  600.         // event functions may never leave return values on the FPU stack
  601.         // enable this code to check if any event call left values on the FPU stack
  602.         if ( !sys->FPU_StackIsEmpty() ) {
  603.             gameLocal.Error( "idEvent::ServiceEvents %d: %s left a value on the FPU stack\n", num, ev->GetName() );
  604.         }
  605. #endif
  606.  
  607.         // return the event to the free list
  608.         event->Free();
  609.  
  610.         // Don't allow ourselves to stay in here too long.  An abnormally high number
  611.         // of events being processed is evidence of an infinite loop of events.
  612.         num++;
  613.         if ( num > MAX_EVENTSPERFRAME ) {
  614.             gameLocal.Error( "Event overflow.  Possible infinite loop in script." );
  615.         }
  616.     }
  617. }
  618.  
  619. /*
  620. ================
  621. idEvent::Init
  622. ================
  623. */
  624. void idEvent::Init( void ) {
  625.     gameLocal.Printf( "Initializing event system\n" );
  626.  
  627.     if ( eventError ) {
  628.         gameLocal.Error( "%s", eventErrorMsg );
  629.     }
  630.  
  631. #ifdef CREATE_EVENT_CODE
  632.     void CreateEventCallbackHandler();
  633.     CreateEventCallbackHandler();
  634.     gameLocal.Error( "Wrote event callback handler" );
  635. #endif
  636.  
  637.     if ( initialized ) {
  638.         gameLocal.Printf( "...already initialized\n" );
  639.         ClearEventList();
  640.         return;
  641.     }
  642.  
  643.     ClearEventList();
  644.  
  645.     eventDataAllocator.Init();
  646.  
  647.     gameLocal.Printf( "...%i event definitions\n", idEventDef::NumEventCommands() );
  648.  
  649.     // the event system has started
  650.     initialized = true;
  651. }
  652.  
  653. /*
  654. ================
  655. idEvent::Shutdown
  656. ================
  657. */
  658. void idEvent::Shutdown( void ) {
  659.     gameLocal.Printf( "Shutdown event system\n" );
  660.  
  661.     if ( !initialized ) {
  662.         gameLocal.Printf( "...not started\n" );
  663.         return;
  664.     }
  665.  
  666.     ClearEventList();
  667.     
  668.     eventDataAllocator.Shutdown();
  669.  
  670.     // say it is now shutdown
  671.     initialized = false;
  672. }
  673.  
  674. /*
  675. ================
  676. idEvent::Save
  677. ================
  678. */
  679. void idEvent::Save( idSaveGame *savefile ) {
  680.     idEvent    *event;
  681.  
  682.     savefile->WriteInt( EventQueue.Num() );
  683.  
  684.     event = EventQueue.Next();
  685.     while( event != NULL ) {
  686.         savefile->WriteInt( event->time );
  687.         savefile->WriteString( event->eventdef->GetName() );
  688.         savefile->WriteString( event->typeinfo->classname );
  689.         savefile->WriteObject( event->object );
  690.         savefile->WriteInt( event->eventdef->GetArgSize() );
  691.         savefile->Write( event->data, event->eventdef->GetArgSize() );
  692.  
  693.         event = event->eventNode.Next();
  694.     }
  695. }
  696.  
  697. /*
  698. ================
  699. idEvent::Restore
  700. ================
  701. */
  702. void idEvent::Restore( idRestoreGame *savefile ) {
  703.     int        i;
  704.     int        num;
  705.     int        argsize;
  706.     idStr    name;
  707.     idEvent    *event;
  708.  
  709.     savefile->ReadInt( num );
  710.  
  711.     for ( i = 0; i < num; i++ ) {
  712.         if ( FreeEvents.IsListEmpty() ) {
  713.             gameLocal.Error( "idEvent::Restore : No more free events" );
  714.         }
  715.  
  716.         event = FreeEvents.Next();
  717.         event->eventNode.Remove();
  718.         event->eventNode.AddToEnd( EventQueue );
  719.  
  720.         savefile->ReadInt( event->time );
  721.  
  722.         // read the event name
  723.         savefile->ReadString( name );
  724.         event->eventdef = idEventDef::FindEvent( name );
  725.         if ( !event->eventdef ) {
  726.             savefile->Error( "idEvent::Restore: unknown event '%s'", name.c_str() );
  727.         }
  728.  
  729.         // read the classtype
  730.         savefile->ReadString( name );
  731.         event->typeinfo = idClass::GetClass( name );
  732.         if ( !event->typeinfo ) {
  733.             savefile->Error( "idEvent::Restore: unknown class '%s' on event '%s'", name.c_str(), event->eventdef->GetName() );
  734.         }
  735.  
  736.         savefile->ReadObject( event->object );
  737.         assert( event->object );
  738.  
  739.         // read the args
  740.         savefile->ReadInt( argsize );
  741.         if ( argsize != event->eventdef->GetArgSize() ) {
  742.             savefile->Error( "idEvent::Restore: arg size (%d) doesn't match saved arg size(%d) on event '%s'", event->eventdef->GetArgSize(), argsize, event->eventdef->GetName() );
  743.         }
  744.         if ( argsize ) {
  745.             event->data = eventDataAllocator.Alloc( argsize );
  746.             savefile->Read( event->data, argsize );
  747.         } else {
  748.             event->data = NULL;
  749.         }
  750.     }
  751. }
  752.  
  753. #ifdef CREATE_EVENT_CODE
  754. /*
  755. ================
  756. CreateEventCallbackHandler
  757. ================
  758. */
  759. void CreateEventCallbackHandler( void ) {
  760.     int num;
  761.     int count;
  762.     int i, j, k;
  763.     char argString[ D_EVENT_MAXARGS + 1 ];
  764.     idStr string1;
  765.     idStr string2;
  766.     idFile *file;
  767.  
  768.     file = fileSystem->OpenFileWrite( "Callbacks.cpp" );
  769.  
  770.     file->Printf( "// generated file - see CREATE_EVENT_CODE\n\n" );
  771.  
  772.     for( i = 1; i <= D_EVENT_MAXARGS; i++ ) {
  773.  
  774.         file->Printf( "\t/*******************************************************\n\n\t\t%d args\n\n\t*******************************************************/\n\n", i );
  775.  
  776.         for ( j = 0; j < ( 1 << i ); j++ ) {
  777.             for ( k = 0; k < i; k++ ) {
  778.                 argString[ k ] = j & ( 1 << k ) ? 'f' : 'i';
  779.             }
  780.             argString[ i ] = '\0';
  781.             
  782.             string1.Empty();
  783.             string2.Empty();
  784.  
  785.             for( k = 0; k < i; k++ ) {
  786.                 if ( j & ( 1 << k ) ) {
  787.                     string1 += "const float";
  788.                     string2 += va( "*( float * )&data[ %d ]", k );
  789.                 } else {
  790.                     string1 += "const int";
  791.                     string2 += va( "data[ %d ]", k );
  792.                 }
  793.  
  794.                 if ( k < i - 1 ) {
  795.                     string1 += ", ";
  796.                     string2 += ", ";
  797.                 }
  798.             }
  799.  
  800.             file->Printf( "\tcase %d :\n\t\ttypedef void ( idClass::*eventCallback_%s_t )( %s );\n", ( 1 << ( i + D_EVENT_MAXARGS ) ) + j, argString, string1.c_str() );
  801.             file->Printf( "\t\t( this->*( eventCallback_%s_t )callback )( %s );\n\t\tbreak;\n\n", argString, string2.c_str() );
  802.  
  803.         }
  804.     }
  805.  
  806.     fileSystem->CloseFile( file );
  807. }
  808.  
  809. #endif
  810.