]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/game/gamesys/Event.cpp
hello world
[icculus/iodoom3.git] / neo / game / 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 static idEvent EventPool[ MAX_EVENTS ];
219
220 bool idEvent::initialized = false;
221
222 idDynamicBlockAlloc<byte, 16 * 1024, 256>       idEvent::eventDataAllocator;
223
224 /*
225 ================
226 idEvent::~idEvent()
227 ================
228 */
229 idEvent::~idEvent() {
230         Free();
231 }
232
233 /*
234 ================
235 idEvent::Alloc
236 ================
237 */
238 idEvent *idEvent::Alloc( const idEventDef *evdef, int numargs, va_list args ) {
239         idEvent         *ev;
240         size_t          size;
241         const char      *format;
242         idEventArg      *arg;
243         byte            *dataPtr;
244         int                     i;
245         const char      *materialName;
246
247         if ( FreeEvents.IsListEmpty() ) {
248                 gameLocal.Error( "idEvent::Alloc : No more free events" );
249         }
250
251         ev = FreeEvents.Next();
252         ev->eventNode.Remove();
253
254         ev->eventdef = evdef;
255
256         if ( numargs != evdef->GetNumArgs() ) {
257                 gameLocal.Error( "idEvent::Alloc : Wrong number of args for '%s' event.", evdef->GetName() );
258         }
259
260         size = evdef->GetArgSize();
261         if ( size ) {
262                 ev->data = eventDataAllocator.Alloc( size );
263                 memset( ev->data, 0, size );
264         } else {
265                 ev->data = NULL;
266         }
267
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() );
275                         }
276                 }
277
278                 dataPtr = &ev->data[ evdef->GetArgOffset( i ) ];
279
280                 switch( format[ i ] ) {
281                 case D_EVENT_FLOAT :
282                 case D_EVENT_INTEGER :
283                         *reinterpret_cast<int *>( dataPtr ) = arg->value;
284                         break;
285
286                 case D_EVENT_VECTOR :
287                         if ( arg->value ) {
288                                 *reinterpret_cast<idVec3 *>( dataPtr ) = *reinterpret_cast<const idVec3 *>( arg->value );
289                         }
290                         break;
291
292                 case D_EVENT_STRING :
293                         if ( arg->value ) {
294                                 idStr::Copynz( reinterpret_cast<char *>( dataPtr ), reinterpret_cast<const char *>( arg->value ), MAX_STRING_LEN );
295                         }
296                         break;
297
298                 case D_EVENT_ENTITY :
299                 case D_EVENT_ENTITY_NULL :
300                         *reinterpret_cast< idEntityPtr<idEntity> * >( dataPtr ) = reinterpret_cast<idEntity *>( arg->value );
301                         break;
302
303                 case D_EVENT_TRACE :
304                         if ( arg->value ) {
305                                 *reinterpret_cast<bool *>( dataPtr ) = true;
306                                 *reinterpret_cast<trace_t *>( dataPtr + sizeof( bool ) ) = *reinterpret_cast<const trace_t *>( arg->value );
307
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 );
314                                 }
315                         } else {
316                                 *reinterpret_cast<bool *>( dataPtr ) = false;
317                         }
318                         break;
319
320                 default :
321                         gameLocal.Error( "idEvent::Alloc : Invalid arg format '%s' string for '%s' event.", format, evdef->GetName() );
322                         break;
323                 }
324         }
325
326         return ev;
327 }
328
329 /*
330 ================
331 idEvent::CopyArgs
332 ================
333 */
334 void idEvent::CopyArgs( const idEventDef *evdef, int numargs, va_list args, int data[ D_EVENT_MAXARGS ] ) {
335         int                     i;
336         const char      *format;
337         idEventArg      *arg;
338
339         format = evdef->GetArgFormat();
340         if ( numargs != evdef->GetNumArgs() ) {
341                 gameLocal.Error( "idEvent::CopyArgs : Wrong number of args for '%s' event.", evdef->GetName() );
342         }
343
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() );
350                         }
351                 }
352
353                 data[ i ] = arg->value;
354         }
355 }
356
357 /*
358 ================
359 idEvent::Free
360 ================
361 */
362 void idEvent::Free( void ) {
363         if ( data ) {
364                 eventDataAllocator.Free( data );
365                 data = NULL;
366         }
367
368         eventdef        = NULL;
369         time            = 0;
370         object          = NULL;
371         typeinfo        = NULL;
372
373         eventNode.SetOwner( this );
374         eventNode.AddToEnd( FreeEvents );
375 }
376
377 /*
378 ================
379 idEvent::Schedule
380 ================
381 */
382 void idEvent::Schedule( idClass *obj, const idTypeInfo *type, int time ) {
383         idEvent *event;
384
385         assert( initialized );
386         if ( !initialized ) {
387                 return;
388         }
389
390         object = obj;
391         typeinfo = type;
392
393         // wraps after 24 days...like I care. ;)
394         this->time = gameLocal.time + time;
395
396         eventNode.Remove();
397
398         event = EventQueue.Next();
399         while( ( event != NULL ) && ( this->time >= event->time ) ) {
400                 event = event->eventNode.Next();
401         }
402
403         if ( event ) {
404                 eventNode.InsertBefore( event->eventNode );
405         } else {
406                 eventNode.AddToEnd( EventQueue );
407         }
408 }
409
410 /*
411 ================
412 idEvent::CancelEvents
413 ================
414 */
415 void idEvent::CancelEvents( const idClass *obj, const idEventDef *evdef ) {
416         idEvent *event;
417         idEvent *next;
418
419         if ( !initialized ) {
420                 return;
421         }
422
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 ) ) {
427                                 event->Free();
428                         }
429                 }
430         }
431 }
432
433 /*
434 ================
435 idEvent::ClearEventList
436 ================
437 */
438 void idEvent::ClearEventList( void ) {
439         int i;
440
441         //
442         // initialize lists
443         //
444         FreeEvents.Clear();
445         EventQueue.Clear();
446    
447         // 
448         // add the events to the free list
449         //
450         for( i = 0; i < MAX_EVENTS; i++ ) {
451                 EventPool[ i ].Free();
452         }
453 }
454
455 /*
456 ================
457 idEvent::ServiceEvents
458 ================
459 */
460 void idEvent::ServiceEvents( void ) {
461         idEvent         *event;
462         int                     num;
463         int                     args[ D_EVENT_MAXARGS ];
464         int                     offset;
465         int                     i;
466         int                     numargs;
467         const char      *formatspec;
468         trace_t         **tracePtr;
469         const idEventDef *ev;
470         byte            *data;
471         const char  *materialName;
472
473         num = 0;
474         while( !EventQueue.IsListEmpty() ) {
475                 event = EventQueue.Next();
476                 assert( event );
477
478                 if ( event->time > gameLocal.time ) {
479                         break;
480                 }
481
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 );
488                         data = event->data;
489                         switch( formatspec[ i ] ) {
490                         case D_EVENT_FLOAT :
491                         case D_EVENT_INTEGER :
492                                 args[ i ] = *reinterpret_cast<int *>( &data[ offset ] );
493                                 break;
494
495                         case D_EVENT_VECTOR :
496                                 *reinterpret_cast<idVec3 **>( &args[ i ] ) = reinterpret_cast<idVec3 *>( &data[ offset ] );
497                                 break;
498
499                         case D_EVENT_STRING :
500                                 *reinterpret_cast<const char **>( &args[ i ] ) = reinterpret_cast<const char *>( &data[ offset ] );
501                                 break;
502
503                         case D_EVENT_ENTITY :
504                         case D_EVENT_ENTITY_NULL :
505                                 *reinterpret_cast<idEntity **>( &args[ i ] ) = reinterpret_cast< idEntityPtr<idEntity> * >( &data[ offset ] )->GetEntity();
506                                 break;
507
508                         case D_EVENT_TRACE :
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 ) ] );
512
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 );
517                                         }
518                                 } else {
519                                         *tracePtr = NULL;
520                                 }
521                                 break;
522
523                         default:
524                                 gameLocal.Error( "idEvent::ServiceEvents : Invalid arg format '%s' string for '%s' event.", formatspec, ev->GetName() );
525                         }
526                 }
527
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 );
533
534 #if 0
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() );
539                 }
540 #endif
541
542                 // return the event to the free list
543                 event->Free();
544
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.
547                 num++;
548                 if ( num > MAX_EVENTSPERFRAME ) {
549                         gameLocal.Error( "Event overflow.  Possible infinite loop in script." );
550                 }
551         }
552 }
553
554 /*
555 ================
556 idEvent::Init
557 ================
558 */
559 void idEvent::Init( void ) {
560         gameLocal.Printf( "Initializing event system\n" );
561
562         if ( eventError ) {
563                 gameLocal.Error( "%s", eventErrorMsg );
564         }
565
566 #ifdef CREATE_EVENT_CODE
567         void CreateEventCallbackHandler();
568         CreateEventCallbackHandler();
569         gameLocal.Error( "Wrote event callback handler" );
570 #endif
571
572         if ( initialized ) {
573                 gameLocal.Printf( "...already initialized\n" );
574                 ClearEventList();
575                 return;
576         }
577
578         ClearEventList();
579
580         eventDataAllocator.Init();
581
582         gameLocal.Printf( "...%i event definitions\n", idEventDef::NumEventCommands() );
583
584         // the event system has started
585         initialized = true;
586 }
587
588 /*
589 ================
590 idEvent::Shutdown
591 ================
592 */
593 void idEvent::Shutdown( void ) {
594         gameLocal.Printf( "Shutdown event system\n" );
595
596         if ( !initialized ) {
597                 gameLocal.Printf( "...not started\n" );
598                 return;
599         }
600
601         ClearEventList();
602         
603         eventDataAllocator.Shutdown();
604
605         // say it is now shutdown
606         initialized = false;
607 }
608
609 /*
610 ================
611 idEvent::Save
612 ================
613 */
614 void idEvent::Save( idSaveGame *savefile ) {
615         char *str;
616         int i, size;
617         idEvent *event;
618         byte *dataPtr;
619         bool validTrace;
620         const char      *format;
621
622         savefile->WriteInt( EventQueue.Num() );
623
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 ] ) {
635                                 case D_EVENT_FLOAT :
636                                         savefile->WriteFloat( *reinterpret_cast<float *>( dataPtr ) );
637                                         size += sizeof( float );
638                                         break;
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 );
644                                         break;
645                                 case D_EVENT_VECTOR :
646                                         savefile->WriteVec3( *reinterpret_cast<idVec3 *>( dataPtr ) );
647                                         size += sizeof( idVec3 );
648                                         break;
649                                 case D_EVENT_TRACE :
650                                         validTrace = *reinterpret_cast<bool *>( dataPtr );
651                                         savefile->WriteBool( validTrace );
652                                         size += sizeof( bool );
653                                         if ( validTrace ) {
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 );
661                                                 }
662                                         }
663                                         break;
664                                 default:
665                                         break;
666                         }
667                 }
668                 assert( size == event->eventdef->GetArgSize() );
669                 event = event->eventNode.Next();
670         }
671 }
672
673 /*
674 ================
675 idEvent::Restore
676 ================
677 */
678 void idEvent::Restore( idRestoreGame *savefile ) {
679         char    *str;
680         int             num, argsize, i, j, size;
681         idStr   name;
682         byte *dataPtr;
683         idEvent *event;
684         const char      *format;
685
686         savefile->ReadInt( num );
687
688         for ( i = 0; i < num; i++ ) {
689                 if ( FreeEvents.IsListEmpty() ) {
690                         gameLocal.Error( "idEvent::Restore : No more free events" );
691                 }
692
693                 event = FreeEvents.Next();
694                 event->eventNode.Remove();
695                 event->eventNode.AddToEnd( EventQueue );
696
697                 savefile->ReadInt( event->time );
698
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() );
704                 }
705
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() );
711                 }
712
713                 savefile->ReadObject( event->object );
714
715                 // read the args
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() );
719                 }
720                 if ( argsize ) {
721                         event->data = eventDataAllocator.Alloc( argsize );
722                         format = event->eventdef->GetArgFormat();
723                         assert( format );
724                         for ( j = 0, size = 0; j < event->eventdef->GetNumArgs(); ++j) {
725                                 dataPtr = &event->data[ event->eventdef->GetArgOffset( j ) ];
726                                 switch( format[ j ] ) {
727                                         case D_EVENT_FLOAT :
728                                                 savefile->ReadFloat( *reinterpret_cast<float *>( dataPtr ) );
729                                                 size += sizeof( float );
730                                                 break;
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 );
736                                                 break;
737                                         case D_EVENT_VECTOR :
738                                                 savefile->ReadVec3( *reinterpret_cast<idVec3 *>( dataPtr ) );
739                                                 size += sizeof( idVec3 );
740                                                 break;
741                                         case D_EVENT_TRACE :
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 );
752                                                         }
753                                                 }
754                                                 break;
755                                         default:
756                                                 break;
757                                 }
758                         }
759                         assert( size == event->eventdef->GetArgSize() );
760                 } else {
761                         event->data = NULL;
762                 }
763         }
764 }
765
766 /*
767  ================
768  idEvent::ReadTrace
769  
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
772  ================
773  */
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 );
788 }
789
790 /*
791  ================
792  idEvent::WriteTrace
793
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
796 ================
797  */
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 );
812 }
813
814
815
816 #ifdef CREATE_EVENT_CODE
817 /*
818 ================
819 CreateEventCallbackHandler
820 ================
821 */
822 void CreateEventCallbackHandler( void ) {
823         int num;
824         int count;
825         int i, j, k;
826         char argString[ D_EVENT_MAXARGS + 1 ];
827         idStr string1;
828         idStr string2;
829         idFile *file;
830
831         file = fileSystem->OpenFileWrite( "Callbacks.cpp" );
832
833         file->Printf( "// generated file - see CREATE_EVENT_CODE\n\n" );
834
835         for( i = 1; i <= D_EVENT_MAXARGS; i++ ) {
836
837                 file->Printf( "\t/*******************************************************\n\n\t\t%d args\n\n\t*******************************************************/\n\n", i );
838
839                 for ( j = 0; j < ( 1 << i ); j++ ) {
840                         for ( k = 0; k < i; k++ ) {
841                                 argString[ k ] = j & ( 1 << k ) ? 'f' : 'i';
842                         }
843                         argString[ i ] = '\0';
844                         
845                         string1.Empty();
846                         string2.Empty();
847
848                         for( k = 0; k < i; k++ ) {
849                                 if ( j & ( 1 << k ) ) {
850                                         string1 += "const float";
851                                         string2 += va( "*( float * )&data[ %d ]", k );
852                                 } else {
853                                         string1 += "const int";
854                                         string2 += va( "data[ %d ]", k );
855                                 }
856
857                                 if ( k < i - 1 ) {
858                                         string1 += ", ";
859                                         string2 += ", ";
860                                 }
861                         }
862
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() );
865
866                 }
867         }
868
869         fileSystem->CloseFile( file );
870 }
871
872 #endif