]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/d3xp/gamesys/Event.cpp
hello world
[icculus/iodoom3.git] / neo / d3xp / gamesys / Event.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
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.
13
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.
18
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/>.
21
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.
23
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.
25
26 ===========================================================================
27 */
28 /*
29 sys_event.cpp
30
31 Event are used for scheduling tasks and for linking script commands.
32
33 */
34
35 #include "../../idlib/precompiled.h"
36 #pragma hdrstop
37
38 #include "../Game_local.h"
39
40 #define MAX_EVENTSPERFRAME                      4096
41 //#define CREATE_EVENT_CODE
42
43 /***********************************************************************
44
45   idEventDef
46
47 ***********************************************************************/
48
49 idEventDef *idEventDef::eventDefList[MAX_EVENTS];
50 int idEventDef::numEventDefs = 0;
51
52 static bool eventError = false;
53 static char eventErrorMsg[ 128 ];
54
55 /*
56 ================
57 idEventDef::idEventDef
58 ================
59 */
60 idEventDef::idEventDef( const char *command, const char *formatspec, char returnType ) {
61         idEventDef      *ev;
62         int                     i;
63         unsigned int    bits;
64
65         assert( command );
66         assert( !idEvent::initialized );
67
68         // Allow NULL to indicate no args, but always store it as ""
69         // so we don't have to check for it.
70         if ( !formatspec ) {
71                 formatspec = "";
72         }
73
74         this->name = command;
75         this->formatspec = formatspec;
76         this->returnType = returnType;
77
78         numargs = strlen( formatspec );
79         assert( numargs <= D_EVENT_MAXARGS );
80         if ( numargs > D_EVENT_MAXARGS ) {
81                 eventError = true;
82                 sprintf( eventErrorMsg, "idEventDef::idEventDef : Too many args for '%s' event.", name );
83                 return;
84         }
85
86         // make sure the format for the args is valid, calculate the formatspecindex, and the offsets for each arg
87         bits = 0;
88         argsize = 0;
89         memset( argOffset, 0, sizeof( argOffset ) );
90         for( i = 0; i < numargs;i++ ) {
91                 argOffset[ i ] = argsize;
92                 switch( formatspec[ i ] ) {
93                 case D_EVENT_FLOAT :
94                         bits |= 1 << i;
95                         argsize += sizeof( float );
96                         break;
97
98                 case D_EVENT_INTEGER :
99                         argsize += sizeof( int );
100                         break;
101
102                 case D_EVENT_VECTOR :
103                         argsize += sizeof( idVec3 );
104                         break;
105
106                 case D_EVENT_STRING :
107                         argsize += MAX_STRING_LEN;
108                         break;
109
110                 case D_EVENT_ENTITY :
111                         argsize += sizeof( idEntityPtr<idEntity> );
112                         break;
113
114                 case D_EVENT_ENTITY_NULL :
115                         argsize += sizeof( idEntityPtr<idEntity> );
116                         break;
117
118                 case D_EVENT_TRACE :
119                         argsize += sizeof( trace_t ) + MAX_STRING_LEN + sizeof( bool );
120                         break;
121
122                 default :
123                         eventError = true;
124                         sprintf( eventErrorMsg, "idEventDef::idEventDef : Invalid arg format '%s' string for '%s' event.", formatspec, name );
125                         return;
126                         break;
127                 }
128         }
129
130         // calculate the formatspecindex
131         formatspecIndex = ( 1 << ( numargs + D_EVENT_MAXARGS ) ) | bits;
132
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 ) {
140                                 eventError = true;
141                                 sprintf( eventErrorMsg, "idEvent '%s' defined twice with same name but differing format strings ('%s'!='%s').",
142                                         command, formatspec, ev->formatspec );
143                                 return;
144                         }
145
146                         if ( ev->returnType != returnType ) {
147                                 eventError = true;
148                                 sprintf( eventErrorMsg, "idEvent '%s' defined twice with same name but differing return types ('%c'!='%c').",
149                                         command, returnType, ev->returnType );
150                                 return;
151                         }
152                         // Don't bother putting the duplicate event in list.
153                         eventnum = ev->eventnum;
154                         return;
155                 }
156         }
157
158         ev = this;
159
160         if ( numEventDefs >= MAX_EVENTS ) {
161                 eventError = true;
162                 sprintf( eventErrorMsg, "numEventDefs >= MAX_EVENTS" );
163                 return;
164         }
165         eventDefList[numEventDefs] = ev;
166         numEventDefs++;
167 }
168
169 /*
170 ================
171 idEventDef::NumEventCommands
172 ================
173 */
174 int     idEventDef::NumEventCommands( void ) {
175         return numEventDefs;
176 }
177
178 /*
179 ================
180 idEventDef::GetEventCommand
181 ================
182 */
183 const idEventDef *idEventDef::GetEventCommand( int eventnum ) {
184         return eventDefList[ eventnum ];
185 }
186
187 /*
188 ================
189 idEventDef::FindEvent
190 ================
191 */
192 const idEventDef *idEventDef::FindEvent( const char *name ) {
193         idEventDef      *ev;
194         int                     num;
195         int                     i;
196
197         assert( name );
198
199         num = numEventDefs;
200         for( i = 0; i < num; i++ ) {
201                 ev = eventDefList[ i ];
202                 if ( strcmp( name, ev->name ) == 0 ) {
203                         return ev;
204                 }
205         }
206
207         return NULL;
208 }
209
210 /***********************************************************************
211
212   idEvent
213
214 ***********************************************************************/
215
216 static idLinkList<idEvent> FreeEvents;
217 static idLinkList<idEvent> EventQueue;
218 #ifdef _D3XP
219 static idLinkList<idEvent> FastEventQueue;
220 #endif
221 static idEvent EventPool[ MAX_EVENTS ];
222
223 bool idEvent::initialized = false;
224
225 idDynamicBlockAlloc<byte, 16 * 1024, 256>       idEvent::eventDataAllocator;
226
227 /*
228 ================
229 idEvent::~idEvent()
230 ================
231 */
232 idEvent::~idEvent() {
233         Free();
234 }
235
236 /*
237 ================
238 idEvent::Alloc
239 ================
240 */
241 idEvent *idEvent::Alloc( const idEventDef *evdef, int numargs, va_list args ) {
242         idEvent         *ev;
243         size_t          size;
244         const char      *format;
245         idEventArg      *arg;
246         byte            *dataPtr;
247         int                     i;
248         const char      *materialName;
249
250         if ( FreeEvents.IsListEmpty() ) {
251                 gameLocal.Error( "idEvent::Alloc : No more free events" );
252         }
253
254         ev = FreeEvents.Next();
255         ev->eventNode.Remove();
256
257         ev->eventdef = evdef;
258
259         if ( numargs != evdef->GetNumArgs() ) {
260                 gameLocal.Error( "idEvent::Alloc : Wrong number of args for '%s' event.", evdef->GetName() );
261         }
262
263         size = evdef->GetArgSize();
264         if ( size ) {
265                 ev->data = eventDataAllocator.Alloc( size );
266                 memset( ev->data, 0, size );
267         } else {
268                 ev->data = NULL;
269         }
270
271         format = evdef->GetArgFormat();
272         for( i = 0; i < numargs; i++ ) {
273                 arg = va_arg( args, idEventArg * );
274                 if ( format[ i ] != arg->type ) {
275                         // when NULL is passed in for an entity, it gets cast as an integer 0, so don't give an error when it happens
276                         if ( !( ( ( format[ i ] == D_EVENT_TRACE ) || ( format[ i ] == D_EVENT_ENTITY ) ) && ( arg->type == 'd' ) && ( arg->value == 0 ) ) ) {
277                                 gameLocal.Error( "idEvent::Alloc : Wrong type passed in for arg # %d on '%s' event.", i, evdef->GetName() );
278                         }
279                 }
280
281                 dataPtr = &ev->data[ evdef->GetArgOffset( i ) ];
282
283                 switch( format[ i ] ) {
284                 case D_EVENT_FLOAT :
285                 case D_EVENT_INTEGER :
286                         *reinterpret_cast<int *>( dataPtr ) = arg->value;
287                         break;
288
289                 case D_EVENT_VECTOR :
290                         if ( arg->value ) {
291                                 *reinterpret_cast<idVec3 *>( dataPtr ) = *reinterpret_cast<const idVec3 *>( arg->value );
292                         }
293                         break;
294
295                 case D_EVENT_STRING :
296                         if ( arg->value ) {
297                                 idStr::Copynz( reinterpret_cast<char *>( dataPtr ), reinterpret_cast<const char *>( arg->value ), MAX_STRING_LEN );
298                         }
299                         break;
300
301                 case D_EVENT_ENTITY :
302                 case D_EVENT_ENTITY_NULL :
303                         *reinterpret_cast< idEntityPtr<idEntity> * >( dataPtr ) = reinterpret_cast<idEntity *>( arg->value );
304                         break;
305
306                 case D_EVENT_TRACE :
307                         if ( arg->value ) {
308                                 *reinterpret_cast<bool *>( dataPtr ) = true;
309                                 *reinterpret_cast<trace_t *>( dataPtr + sizeof( bool ) ) = *reinterpret_cast<const trace_t *>( arg->value );
310
311                                 // save off the material as a string since the pointer won't be valid in save games.
312                                 // since we save off the entire trace_t structure, if the material is NULL here,
313                                 // it will be NULL when we process it, so we don't need to save off anything in that case.
314                                 if ( reinterpret_cast<const trace_t *>( arg->value )->c.material ) {
315                                         materialName = reinterpret_cast<const trace_t *>( arg->value )->c.material->GetName();
316                                         idStr::Copynz( reinterpret_cast<char *>( dataPtr + sizeof( bool ) + sizeof( trace_t ) ), materialName, MAX_STRING_LEN );
317                                 }
318                         } else {
319                                 *reinterpret_cast<bool *>( dataPtr ) = false;
320                         }
321                         break;
322
323                 default :
324                         gameLocal.Error( "idEvent::Alloc : Invalid arg format '%s' string for '%s' event.", format, evdef->GetName() );
325                         break;
326                 }
327         }
328
329         return ev;
330 }
331
332 /*
333 ================
334 idEvent::CopyArgs
335 ================
336 */
337 void idEvent::CopyArgs( const idEventDef *evdef, int numargs, va_list args, int data[ D_EVENT_MAXARGS ] ) {
338         int                     i;
339         const char      *format;
340         idEventArg      *arg;
341
342         format = evdef->GetArgFormat();
343         if ( numargs != evdef->GetNumArgs() ) {
344                 gameLocal.Error( "idEvent::CopyArgs : Wrong number of args for '%s' event.", evdef->GetName() );
345         }
346
347         for( i = 0; i < numargs; i++ ) {
348                 arg = va_arg( args, idEventArg * );
349                 if ( format[ i ] != arg->type ) {
350                         // when NULL is passed in for an entity, it gets cast as an integer 0, so don't give an error when it happens
351                         if ( !( ( ( format[ i ] == D_EVENT_TRACE ) || ( format[ i ] == D_EVENT_ENTITY ) ) && ( arg->type == 'd' ) && ( arg->value == 0 ) ) ) {
352                                 gameLocal.Error( "idEvent::CopyArgs : Wrong type passed in for arg # %d on '%s' event.", i, evdef->GetName() );
353                         }
354                 }
355
356                 data[ i ] = arg->value;
357         }
358 }
359
360 /*
361 ================
362 idEvent::Free
363 ================
364 */
365 void idEvent::Free( void ) {
366         if ( data ) {
367                 eventDataAllocator.Free( data );
368                 data = NULL;
369         }
370
371         eventdef        = NULL;
372         time            = 0;
373         object          = NULL;
374         typeinfo        = NULL;
375
376         eventNode.SetOwner( this );
377         eventNode.AddToEnd( FreeEvents );
378 }
379
380 /*
381 ================
382 idEvent::Schedule
383 ================
384 */
385 void idEvent::Schedule( idClass *obj, const idTypeInfo *type, int time ) {
386         idEvent *event;
387
388         assert( initialized );
389         if ( !initialized ) {
390                 return;
391         }
392
393         object = obj;
394         typeinfo = type;
395
396         // wraps after 24 days...like I care. ;)
397         this->time = gameLocal.time + time;
398
399         eventNode.Remove();
400
401 #ifdef _D3XP
402         if ( obj->IsType( idEntity::Type ) && ( ( (idEntity*)(obj) )->timeGroup == TIME_GROUP2 ) ) {
403                 event = FastEventQueue.Next();
404                 while( ( event != NULL ) && ( this->time >= event->time ) ) {
405                         event = event->eventNode.Next();
406                 }
407
408                 if ( event ) {
409                         eventNode.InsertBefore( event->eventNode );
410                 } else {
411                         eventNode.AddToEnd( FastEventQueue );
412                 }
413
414                 return;
415         } else {
416                 this->time = gameLocal.slow.time + time;
417         }
418 #endif
419
420         event = EventQueue.Next();
421         while( ( event != NULL ) && ( this->time >= event->time ) ) {
422                 event = event->eventNode.Next();
423         }
424
425         if ( event ) {
426                 eventNode.InsertBefore( event->eventNode );
427         } else {
428                 eventNode.AddToEnd( EventQueue );
429         }
430 }
431
432 /*
433 ================
434 idEvent::CancelEvents
435 ================
436 */
437 void idEvent::CancelEvents( const idClass *obj, const idEventDef *evdef ) {
438         idEvent *event;
439         idEvent *next;
440
441         if ( !initialized ) {
442                 return;
443         }
444
445         for( event = EventQueue.Next(); event != NULL; event = next ) {
446                 next = event->eventNode.Next();
447                 if ( event->object == obj ) {
448                         if ( !evdef || ( evdef == event->eventdef ) ) {
449                                 event->Free();
450                         }
451                 }
452         }
453
454 #ifdef _D3XP
455         for( event = FastEventQueue.Next(); event != NULL; event = next ) {
456                 next = event->eventNode.Next();
457                 if ( event->object == obj ) {
458                         if ( !evdef || ( evdef == event->eventdef ) ) {
459                                 event->Free();
460                         }
461                 }
462         }
463 #endif
464 }
465
466 /*
467 ================
468 idEvent::ClearEventList
469 ================
470 */
471 void idEvent::ClearEventList( void ) {
472         int i;
473
474         //
475         // initialize lists
476         //
477         FreeEvents.Clear();
478         EventQueue.Clear();
479    
480         // 
481         // add the events to the free list
482         //
483         for( i = 0; i < MAX_EVENTS; i++ ) {
484                 EventPool[ i ].Free();
485         }
486 }
487
488 /*
489 ================
490 idEvent::ServiceEvents
491 ================
492 */
493 void idEvent::ServiceEvents( void ) {
494         idEvent         *event;
495         int                     num;
496         int                     args[ D_EVENT_MAXARGS ];
497         int                     offset;
498         int                     i;
499         int                     numargs;
500         const char      *formatspec;
501         trace_t         **tracePtr;
502         const idEventDef *ev;
503         byte            *data;
504         const char  *materialName;
505
506         num = 0;
507         while( !EventQueue.IsListEmpty() ) {
508                 event = EventQueue.Next();
509                 assert( event );
510
511                 if ( event->time > gameLocal.time ) {
512                         break;
513                 }
514
515                 // copy the data into the local args array and set up pointers
516                 ev = event->eventdef;
517                 formatspec = ev->GetArgFormat();
518                 numargs = ev->GetNumArgs();
519                 for( i = 0; i < numargs; i++ ) {
520                         offset = ev->GetArgOffset( i );
521                         data = event->data;
522                         switch( formatspec[ i ] ) {
523                         case D_EVENT_FLOAT :
524                         case D_EVENT_INTEGER :
525                                 args[ i ] = *reinterpret_cast<int *>( &data[ offset ] );
526                                 break;
527
528                         case D_EVENT_VECTOR :
529                                 *reinterpret_cast<idVec3 **>( &args[ i ] ) = reinterpret_cast<idVec3 *>( &data[ offset ] );
530                                 break;
531
532                         case D_EVENT_STRING :
533                                 *reinterpret_cast<const char **>( &args[ i ] ) = reinterpret_cast<const char *>( &data[ offset ] );
534                                 break;
535
536                         case D_EVENT_ENTITY :
537                         case D_EVENT_ENTITY_NULL :
538                                 *reinterpret_cast<idEntity **>( &args[ i ] ) = reinterpret_cast< idEntityPtr<idEntity> * >( &data[ offset ] )->GetEntity();
539                                 break;
540
541                         case D_EVENT_TRACE :
542                                 tracePtr = reinterpret_cast<trace_t **>( &args[ i ] );
543                                 if ( *reinterpret_cast<bool *>( &data[ offset ] ) ) {
544                                         *tracePtr = reinterpret_cast<trace_t *>( &data[ offset + sizeof( bool ) ] );
545
546                                         if ( ( *tracePtr )->c.material != NULL ) {
547                                                 // look up the material name to get the material pointer
548                                                 materialName = reinterpret_cast<const char *>( &data[ offset + sizeof( bool ) + sizeof( trace_t ) ] );
549                                                 ( *tracePtr )->c.material = declManager->FindMaterial( materialName, true );
550                                         }
551                                 } else {
552                                         *tracePtr = NULL;
553                                 }
554                                 break;
555
556                         default:
557                                 gameLocal.Error( "idEvent::ServiceEvents : Invalid arg format '%s' string for '%s' event.", formatspec, ev->GetName() );
558                         }
559                 }
560
561                 // the event is removed from its list so that if then object
562                 // is deleted, the event won't be freed twice
563                 event->eventNode.Remove();
564                 assert( event->object );
565                 event->object->ProcessEventArgPtr( ev, args );
566
567 #if 0
568                 // event functions may never leave return values on the FPU stack
569                 // enable this code to check if any event call left values on the FPU stack
570                 if ( !sys->FPU_StackIsEmpty() ) {
571                         gameLocal.Error( "idEvent::ServiceEvents %d: %s left a value on the FPU stack\n", num, ev->GetName() );
572                 }
573 #endif
574
575                 // return the event to the free list
576                 event->Free();
577
578                 // Don't allow ourselves to stay in here too long.  An abnormally high number
579                 // of events being processed is evidence of an infinite loop of events.
580                 num++;
581                 if ( num > MAX_EVENTSPERFRAME ) {
582                         gameLocal.Error( "Event overflow.  Possible infinite loop in script." );
583                 }
584         }
585 }
586
587 #ifdef _D3XP
588 /*
589 ================
590 idEvent::ServiceFastEvents
591 ================
592 */
593 void idEvent::ServiceFastEvents() {
594         idEvent *event;
595         int             num;
596         int                     args[ D_EVENT_MAXARGS ];
597         int                     offset;
598         int                     i;
599         int                     numargs;
600         const char      *formatspec;
601         trace_t         **tracePtr;
602         const idEventDef *ev;
603         byte            *data;
604         const char  *materialName;
605
606         num = 0;
607         while( !FastEventQueue.IsListEmpty() ) {
608                 event = FastEventQueue.Next();
609                 assert( event );
610
611                 if ( event->time > gameLocal.fast.time ) {
612                         break;
613                 }
614
615                 // copy the data into the local args array and set up pointers
616                 ev = event->eventdef;
617                 formatspec = ev->GetArgFormat();
618                 numargs = ev->GetNumArgs();
619                 for( i = 0; i < numargs; i++ ) {
620                         offset = ev->GetArgOffset( i );
621                         data = event->data;
622                         switch( formatspec[ i ] ) {
623                         case D_EVENT_FLOAT :
624                         case D_EVENT_INTEGER :
625                                 args[ i ] = *reinterpret_cast<int *>( &data[ offset ] );
626                                 break;
627
628                         case D_EVENT_VECTOR :
629                                 *reinterpret_cast<idVec3 **>( &args[ i ] ) = reinterpret_cast<idVec3 *>( &data[ offset ] );
630                                 break;
631
632                         case D_EVENT_STRING :
633                                 *reinterpret_cast<const char **>( &args[ i ] ) = reinterpret_cast<const char *>( &data[ offset ] );
634                                 break;
635
636                         case D_EVENT_ENTITY :
637                         case D_EVENT_ENTITY_NULL :
638                                 *reinterpret_cast<idEntity **>( &args[ i ] ) = reinterpret_cast< idEntityPtr<idEntity> * >( &data[ offset ] )->GetEntity();
639                                 break;
640
641                         case D_EVENT_TRACE :
642                                 tracePtr = reinterpret_cast<trace_t **>( &args[ i ] );
643                                 if ( *reinterpret_cast<bool *>( &data[ offset ] ) ) {
644                                         *tracePtr = reinterpret_cast<trace_t *>( &data[ offset + sizeof( bool ) ] );
645
646                                         if ( ( *tracePtr )->c.material != NULL ) {
647                                                 // look up the material name to get the material pointer
648                                                 materialName = reinterpret_cast<const char *>( &data[ offset + sizeof( bool ) + sizeof( trace_t ) ] );
649                                                 ( *tracePtr )->c.material = declManager->FindMaterial( materialName, true );
650                                         }
651                                 } else {
652                                         *tracePtr = NULL;
653                                 }
654                                 break;
655
656                         default:
657                                 gameLocal.Error( "idEvent::ServiceFastEvents : Invalid arg format '%s' string for '%s' event.", formatspec, ev->GetName() );
658                         }
659                 }
660
661                 // the event is removed from its list so that if then object
662                 // is deleted, the event won't be freed twice
663                 event->eventNode.Remove();
664                 assert( event->object );
665                 event->object->ProcessEventArgPtr( ev, args );
666
667 #if 0
668                 // event functions may never leave return values on the FPU stack
669                 // enable this code to check if any event call left values on the FPU stack
670                 if ( !sys->FPU_StackIsEmpty() ) {
671                         gameLocal.Error( "idEvent::ServiceEvents %d: %s left a value on the FPU stack\n", num, event->eventdef->GetName() );
672                 }
673 #endif
674
675                 // return the event to the free list
676                 event->Free();
677
678                 // Don't allow ourselves to stay in here too long.  An abnormally high number
679                 // of events being processed is evidence of an infinite loop of events.
680                 num++;
681                 if ( num > MAX_EVENTSPERFRAME ) {
682                         gameLocal.Error( "Event overflow.  Possible infinite loop in script." );
683                 }
684         }
685 }
686 #endif
687
688 /*
689 ================
690 idEvent::Init
691 ================
692 */
693 void idEvent::Init( void ) {
694         gameLocal.Printf( "Initializing event system\n" );
695
696         if ( eventError ) {
697                 gameLocal.Error( "%s", eventErrorMsg );
698         }
699
700 #ifdef CREATE_EVENT_CODE
701         void CreateEventCallbackHandler();
702         CreateEventCallbackHandler();
703         gameLocal.Error( "Wrote event callback handler" );
704 #endif
705
706         if ( initialized ) {
707                 gameLocal.Printf( "...already initialized\n" );
708                 ClearEventList();
709                 return;
710         }
711
712         ClearEventList();
713
714         eventDataAllocator.Init();
715
716         gameLocal.Printf( "...%i event definitions\n", idEventDef::NumEventCommands() );
717
718         // the event system has started
719         initialized = true;
720 }
721
722 /*
723 ================
724 idEvent::Shutdown
725 ================
726 */
727 void idEvent::Shutdown( void ) {
728         gameLocal.Printf( "Shutdown event system\n" );
729
730         if ( !initialized ) {
731                 gameLocal.Printf( "...not started\n" );
732                 return;
733         }
734
735         ClearEventList();
736         
737         eventDataAllocator.Shutdown();
738
739         // say it is now shutdown
740         initialized = false;
741 }
742
743 /*
744 ================
745 idEvent::Save
746 ================
747 */
748 void idEvent::Save( idSaveGame *savefile ) {
749         char *str;
750         int i, size;
751         idEvent *event;
752         byte *dataPtr;
753         bool validTrace;
754         const char      *format;
755
756         savefile->WriteInt( EventQueue.Num() );
757
758         event = EventQueue.Next();
759         while( event != NULL ) {
760                 savefile->WriteInt( event->time );
761                 savefile->WriteString( event->eventdef->GetName() );
762                 savefile->WriteString( event->typeinfo->classname );
763                 savefile->WriteObject( event->object );
764                 savefile->WriteInt( event->eventdef->GetArgSize() );
765                 format = event->eventdef->GetArgFormat();
766                 for ( i = 0, size = 0; i < event->eventdef->GetNumArgs(); ++i) {
767                         dataPtr = &event->data[ event->eventdef->GetArgOffset( i ) ];
768                         switch( format[ i ] ) {
769                                 case D_EVENT_FLOAT :
770                                         savefile->WriteFloat( *reinterpret_cast<float *>( dataPtr ) );
771                                         size += sizeof( float );
772                                         break;
773                                 case D_EVENT_INTEGER :
774                                 case D_EVENT_ENTITY :
775                                 case D_EVENT_ENTITY_NULL :
776                                         savefile->WriteInt( *reinterpret_cast<int *>( dataPtr ) );
777                                         size += sizeof( int );
778                                         break;
779                                 case D_EVENT_VECTOR :
780                                         savefile->WriteVec3( *reinterpret_cast<idVec3 *>( dataPtr ) );
781                                         size += sizeof( idVec3 );
782                                         break;
783                                 case D_EVENT_TRACE :
784                                         validTrace = *reinterpret_cast<bool *>( dataPtr );
785                                         savefile->WriteBool( validTrace );
786                                         size += sizeof( bool );
787                                         if ( validTrace ) {
788                                                 size += sizeof( trace_t );
789                                                 const trace_t &t = *reinterpret_cast<trace_t *>( dataPtr + sizeof( bool ) );
790                                                 SaveTrace( savefile, t );
791                                                 if ( t.c.material ) {
792                                                         size += MAX_STRING_LEN;
793                                                         str = reinterpret_cast<char *>( dataPtr + sizeof( bool ) + sizeof( trace_t ) );
794                                                         savefile->Write( str, MAX_STRING_LEN );
795                                                 }
796                                         }
797                                         break;
798                                 default:
799                                         break;
800                         }
801                 }
802                 assert( size == event->eventdef->GetArgSize() );
803                 event = event->eventNode.Next();
804         }
805
806 #ifdef _D3XP
807         // Save the Fast EventQueue
808         savefile->WriteInt( FastEventQueue.Num() );
809
810         event = FastEventQueue.Next();
811         while( event != NULL ) {
812                 savefile->WriteInt( event->time );
813                 savefile->WriteString( event->eventdef->GetName() );
814                 savefile->WriteString( event->typeinfo->classname );
815                 savefile->WriteObject( event->object );
816                 savefile->WriteInt( event->eventdef->GetArgSize() );
817                 savefile->Write( event->data, event->eventdef->GetArgSize() );
818
819                 event = event->eventNode.Next();
820         }
821 #endif
822 }
823
824 /*
825 ================
826 idEvent::Restore
827 ================
828 */
829 void idEvent::Restore( idRestoreGame *savefile ) {
830         char    *str;
831         int             num, argsize, i, j, size;
832         idStr   name;
833         byte *dataPtr;
834         idEvent *event;
835         const char      *format;
836
837         savefile->ReadInt( num );
838
839         for ( i = 0; i < num; i++ ) {
840                 if ( FreeEvents.IsListEmpty() ) {
841                         gameLocal.Error( "idEvent::Restore : No more free events" );
842                 }
843
844                 event = FreeEvents.Next();
845                 event->eventNode.Remove();
846                 event->eventNode.AddToEnd( EventQueue );
847
848                 savefile->ReadInt( event->time );
849
850                 // read the event name
851                 savefile->ReadString( name );
852                 event->eventdef = idEventDef::FindEvent( name );
853                 if ( !event->eventdef ) {
854                         savefile->Error( "idEvent::Restore: unknown event '%s'", name.c_str() );
855                 }
856
857                 // read the classtype
858                 savefile->ReadString( name );
859                 event->typeinfo = idClass::GetClass( name );
860                 if ( !event->typeinfo ) {
861                         savefile->Error( "idEvent::Restore: unknown class '%s' on event '%s'", name.c_str(), event->eventdef->GetName() );
862                 }
863
864                 savefile->ReadObject( event->object );
865
866                 // read the args
867                 savefile->ReadInt( argsize );
868                 if ( argsize != event->eventdef->GetArgSize() ) {
869                         savefile->Error( "idEvent::Restore: arg size (%d) doesn't match saved arg size(%d) on event '%s'", event->eventdef->GetArgSize(), argsize, event->eventdef->GetName() );
870                 }
871                 if ( argsize ) {
872                         event->data = eventDataAllocator.Alloc( argsize );
873                         format = event->eventdef->GetArgFormat();
874                         assert( format );
875                         for ( j = 0, size = 0; j < event->eventdef->GetNumArgs(); ++j) {
876                                 dataPtr = &event->data[ event->eventdef->GetArgOffset( j ) ];
877                                 switch( format[ j ] ) {
878                                         case D_EVENT_FLOAT :
879                                                 savefile->ReadFloat( *reinterpret_cast<float *>( dataPtr ) );
880                                                 size += sizeof( float );
881                                                 break;
882                                         case D_EVENT_INTEGER :
883                                         case D_EVENT_ENTITY :
884                                         case D_EVENT_ENTITY_NULL :
885                                                 savefile->ReadInt( *reinterpret_cast<int *>( dataPtr ) );
886                                                 size += sizeof( int );
887                                                 break;
888                                         case D_EVENT_VECTOR :
889                                                 savefile->ReadVec3( *reinterpret_cast<idVec3 *>( dataPtr ) );
890                                                 size += sizeof( idVec3 );
891                                                 break;
892                                         case D_EVENT_TRACE :
893                                                 savefile->ReadBool( *reinterpret_cast<bool *>( dataPtr ) );
894                                                 size += sizeof( bool );
895                                                 if ( *reinterpret_cast<bool *>( dataPtr ) ) {
896                                                         size += sizeof( trace_t );
897                                                         trace_t &t = *reinterpret_cast<trace_t *>( dataPtr + sizeof( bool ) );
898                                                         RestoreTrace( savefile,  t) ;
899                                                         if ( t.c.material ) {
900                                                                 size += MAX_STRING_LEN;
901                                                                 str = reinterpret_cast<char *>( dataPtr + sizeof( bool ) + sizeof( trace_t ) );
902                                                                 savefile->Read( str, MAX_STRING_LEN );
903                                                         }
904                                                 }
905                                                 break;
906                                         default:
907                                                 break;
908                                 }
909                         }
910                         assert( size == event->eventdef->GetArgSize() );
911                 } else {
912                         event->data = NULL;
913                 }
914         }
915
916 #ifdef _D3XP
917         // Restore the Fast EventQueue
918         savefile->ReadInt( num );
919
920         for ( i = 0; i < num; i++ ) {
921                 if ( FreeEvents.IsListEmpty() ) {
922                         gameLocal.Error( "idEvent::Restore : No more free events" );
923                 }
924
925                 event = FreeEvents.Next();
926                 event->eventNode.Remove();
927                 event->eventNode.AddToEnd( FastEventQueue );
928
929                 savefile->ReadInt( event->time );
930
931                 // read the event name
932                 savefile->ReadString( name );
933                 event->eventdef = idEventDef::FindEvent( name );
934                 if ( !event->eventdef ) {
935                         savefile->Error( "idEvent::Restore: unknown event '%s'", name.c_str() );
936                 }
937
938                 // read the classtype
939                 savefile->ReadString( name );
940                 event->typeinfo = idClass::GetClass( name );
941                 if ( !event->typeinfo ) {
942                         savefile->Error( "idEvent::Restore: unknown class '%s' on event '%s'", name.c_str(), event->eventdef->GetName() );
943                 }
944
945                 savefile->ReadObject( event->object );
946
947                 // read the args
948                 savefile->ReadInt( argsize );
949                 if ( argsize != event->eventdef->GetArgSize() ) {
950                         savefile->Error( "idEvent::Restore: arg size (%d) doesn't match saved arg size(%d) on event '%s'", event->eventdef->GetArgSize(), argsize, event->eventdef->GetName() );
951                 }
952                 if ( argsize ) {
953                         event->data = eventDataAllocator.Alloc( argsize );
954                         savefile->Read( event->data, argsize );
955                 } else {
956                         event->data = NULL;
957                 }
958         }
959 #endif
960 }
961
962 /*
963  ================
964  idEvent::ReadTrace
965  
966  idRestoreGame has a ReadTrace procedure, but unfortunately idEvent wants the material
967  string name at the of the data structure rather than in the middle
968  ================
969  */
970 void idEvent::RestoreTrace( idRestoreGame *savefile, trace_t &trace ) {
971         savefile->ReadFloat( trace.fraction );
972         savefile->ReadVec3( trace.endpos );
973         savefile->ReadMat3( trace.endAxis );
974         savefile->ReadInt( (int&)trace.c.type );
975         savefile->ReadVec3( trace.c.point );
976         savefile->ReadVec3( trace.c.normal );
977         savefile->ReadFloat( trace.c.dist );
978         savefile->ReadInt( trace.c.contents );
979         savefile->ReadInt( (int&)trace.c.material );
980         savefile->ReadInt( trace.c.contents );
981         savefile->ReadInt( trace.c.modelFeature );
982         savefile->ReadInt( trace.c.trmFeature );
983         savefile->ReadInt( trace.c.id );
984 }
985
986 /*
987  ================
988  idEvent::WriteTrace
989
990  idSaveGame has a WriteTrace procedure, but unfortunately idEvent wants the material
991  string name at the of the data structure rather than in the middle
992 ================
993  */
994 void idEvent::SaveTrace( idSaveGame *savefile, const trace_t &trace ) {
995         savefile->WriteFloat( trace.fraction );
996         savefile->WriteVec3( trace.endpos );
997         savefile->WriteMat3( trace.endAxis );
998         savefile->WriteInt( trace.c.type );
999         savefile->WriteVec3( trace.c.point );
1000         savefile->WriteVec3( trace.c.normal );
1001         savefile->WriteFloat( trace.c.dist );
1002         savefile->WriteInt( trace.c.contents );
1003         savefile->WriteInt( (int&)trace.c.material );
1004         savefile->WriteInt( trace.c.contents );
1005         savefile->WriteInt( trace.c.modelFeature );
1006         savefile->WriteInt( trace.c.trmFeature );
1007         savefile->WriteInt( trace.c.id );
1008 }
1009
1010
1011
1012 #ifdef CREATE_EVENT_CODE
1013 /*
1014 ================
1015 CreateEventCallbackHandler
1016 ================
1017 */
1018 void CreateEventCallbackHandler( void ) {
1019         int num;
1020         int count;
1021         int i, j, k;
1022         char argString[ D_EVENT_MAXARGS + 1 ];
1023         idStr string1;
1024         idStr string2;
1025         idFile *file;
1026
1027         file = fileSystem->OpenFileWrite( "Callbacks.cpp" );
1028
1029         file->Printf( "// generated file - see CREATE_EVENT_CODE\n\n" );
1030
1031         for( i = 1; i <= D_EVENT_MAXARGS; i++ ) {
1032
1033                 file->Printf( "\t/*******************************************************\n\n\t\t%d args\n\n\t*******************************************************/\n\n", i );
1034
1035                 for ( j = 0; j < ( 1 << i ); j++ ) {
1036                         for( k = 0; k < i; k++ ) {
1037                                 argString[ k ] = j & ( 1 << k ) ? 'f' : 'i';
1038                         }
1039                         argString[ i ] = '\0';
1040
1041                         string1.Empty();
1042                         string2.Empty();
1043
1044                         for( k = 0; k < i; k++ ) {
1045                                 if ( j & ( 1 << k ) ) {
1046                                         string1 += "const float";
1047                                         string2 += va( "*( float * )&data[ %d ]", k );
1048                                 } else {
1049                                         string1 += "const int";
1050                                         string2 += va( "data[ %d ]", k );
1051                                 }
1052
1053                                 if ( k < i - 1 ) {
1054                                         string1 += ", ";
1055                                         string2 += ", ";
1056                                 }
1057                         }
1058
1059                         file->Printf( "\tcase %d :\n\t\ttypedef void ( idClass::*eventCallback_%s_t )( %s );\n", ( 1 << ( i + D_EVENT_MAXARGS ) ) + j, argString, string1.c_str() );
1060                         file->Printf( "\t\t( this->*( eventCallback_%s_t )callback )( %s );\n\t\tbreak;\n\n", argString, string2.c_str() );
1061
1062                 }
1063         }
1064
1065         fileSystem->CloseFile( file );
1066 }
1067
1068 #endif