1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2013 - 2015, OpenJK contributors
7
8 This file is part of the OpenJK source code.
9
10 OpenJK is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License version 2 as
12 published by the Free Software Foundation.
13
14 This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
21 ===========================================================================
22 */
23
24 #include "RoffSystem.h"
25
26 #ifndef DEDICATED
27 #include "client/cl_cgameapi.h"
28 #endif
29 #include "server/sv_gameapi.h"
30
31 // The one and only instance...
32 CROFFSystem theROFFSystem;
33
34 //---------------------------------------------------------------------------
35 // CROFFSystem::CROFF::CROFF
36 // Simple constructor for CROFF object
37 //
38 // INPUTS:
39 // pass in the filepath and the id of the roff object to create
40 //
41 // RETURN:
42 // none
43 //---------------------------------------------------------------------------
CROFF(const char * file,int id)44 CROFFSystem::CROFF::CROFF( const char *file, int id )
45 {
46 strcpy( mROFFFilePath, file );
47
48 mID = id;
49 mMoveRotateList = NULL;
50 mNoteTrackIndexes = 0;
51 mUsedByClient = mUsedByServer = qfalse;
52 }
53
54
55 //---------------------------------------------------------------------------
56 // CROFFSystem::CROFF::~CROFF()
57 // Frees any resources when the CROFF object dies
58 //
59 // INPUTS:
60 // none
61 //
62 // RETURN:
63 // none
64 //---------------------------------------------------------------------------
~CROFF()65 CROFFSystem::CROFF::~CROFF()
66 {
67 if ( mMoveRotateList )
68 {
69 delete [] mMoveRotateList;
70 }
71
72 if (mNoteTrackIndexes)
73 {
74 delete mNoteTrackIndexes[0];
75 delete [] mNoteTrackIndexes;
76 }
77 }
78
79
80 //---------------------------------------------------------------------------
81 // CROFFSystem::Restart
82 // Cleans up the roff system, not sure how useful this really is
83 //
84 // INPUTS:
85 // none
86 //
87 // RETURN:
88 // success or failure
89 //---------------------------------------------------------------------------
Restart()90 qboolean CROFFSystem::Restart()
91 {
92 TROFFList::iterator itr = mROFFList.begin();
93
94 // remove everything from the list
95 while( itr != mROFFList.end() )
96 {
97 delete ((CROFF *)(*itr).second);
98
99 mROFFList.erase( itr );
100 itr = mROFFList.begin();
101 }
102
103 // clear CROFFSystem unique ID counter
104 mID = 0;
105
106 return qtrue;
107 }
108
109
110 //---------------------------------------------------------------------------
111 // CROFFSystem::IsROFF
112 // Makes sure that the requested file is actually a ROFF
113 //
114 // INPUTS:
115 // pass in the file data
116 //
117 // RETURN:
118 // returns test success or failure
119 //---------------------------------------------------------------------------
IsROFF(unsigned char * data)120 qboolean CROFFSystem::IsROFF( unsigned char *data )
121 {
122 TROFFHeader *hdr = (TROFFHeader *)data;
123 TROFF2Header *hdr2 = (TROFF2Header *)data;
124
125 if ( !strcmp( hdr->mHeader, ROFF_STRING ))
126 { // bad header
127 return qfalse;
128 }
129
130 if (LittleLong(hdr->mVersion) != ROFF_VERSION && LittleLong(hdr->mVersion) != ROFF_NEW_VERSION)
131 { // bad version
132 return qfalse;
133 }
134
135 if (LittleLong(hdr->mVersion) == ROFF_VERSION && LittleFloat(hdr->mCount) <= 0.0)
136 { // bad count
137 return qfalse;
138 }
139
140 if (LittleLong(hdr->mVersion) == ROFF_NEW_VERSION && LittleLong(hdr2->mCount) <= 0)
141 { // bad count
142 return qfalse;
143 }
144
145 return qtrue;
146 }
147
148
149 //---------------------------------------------------------------------------
150 // CROFFSystem::InitROFF
151 // Handles stuffing the roff data in the CROFF object
152 //
153 // INPUTS:
154 // pass in the file data and the object to stuff the data into.
155 //
156 // RETURN:
157 // returns initialization success or failure
158 //---------------------------------------------------------------------------
InitROFF(unsigned char * data,CROFF * obj)159 qboolean CROFFSystem::InitROFF( unsigned char *data, CROFF *obj )
160 {
161 int i;
162
163 TROFFHeader *hdr = (TROFFHeader *)data;
164
165 if (LittleLong(hdr->mVersion) == ROFF_NEW_VERSION)
166 {
167 return InitROFF2(data, obj);
168 }
169
170 obj->mROFFEntries = LittleLong(hdr->mCount);
171 obj->mMoveRotateList = new TROFF2Entry[((int)LittleFloat(hdr->mCount))];
172 obj->mFrameTime = 1000 / ROFF_SAMPLE_RATE; // default 10 hz
173 obj->mLerp = ROFF_SAMPLE_RATE;
174 obj->mNumNoteTracks = 0;
175 obj->mNoteTrackIndexes = 0;
176
177 if ( obj->mMoveRotateList != 0 )
178 { // Step past the header to get to the goods
179 TROFFEntry *roff_data = ( TROFFEntry *)&hdr[1];
180
181 // Copy all of the goods into our ROFF cache
182 for ( i = 0; i < LittleLong(hdr->mCount); i++ )
183 {
184 #ifdef Q3_BIG_ENDIAN
185 obj->mMoveRotateList[i].mOriginOffset[0] = LittleFloat(roff_data[i].mOriginOffset[0]);
186 obj->mMoveRotateList[i].mOriginOffset[1] = LittleFloat(roff_data[i].mOriginOffset[1]);
187 obj->mMoveRotateList[i].mOriginOffset[2] = LittleFloat(roff_data[i].mOriginOffset[2]);
188 obj->mMoveRotateList[i].mRotateOffset[0] = LittleFloat(roff_data[i].mRotateOffset[0]);
189 obj->mMoveRotateList[i].mRotateOffset[1] = LittleFloat(roff_data[i].mRotateOffset[1]);
190 obj->mMoveRotateList[i].mRotateOffset[2] = LittleFloat(roff_data[i].mRotateOffset[2]);
191 #else
192 VectorCopy( roff_data[i].mOriginOffset, obj->mMoveRotateList[i].mOriginOffset );
193 VectorCopy( roff_data[i].mRotateOffset, obj->mMoveRotateList[i].mRotateOffset );
194 #endif
195 obj->mMoveRotateList[i].mStartNote = -1;
196 obj->mMoveRotateList[i].mNumNotes = 0;
197 }
198
199 FixBadAngles(obj);
200 }
201 else
202 {
203 return qfalse;
204 }
205
206 return qtrue;
207 }
208
209
210 //---------------------------------------------------------------------------
211 // CROFFSystem::InitROFF2
212 // Handles stuffing the roff data in the CROFF object for version 2
213 //
214 // INPUTS:
215 // pass in the file data and the object to stuff the data into.
216 //
217 // RETURN:
218 // returns initialization success or failure
219 //---------------------------------------------------------------------------
InitROFF2(unsigned char * data,CROFF * obj)220 qboolean CROFFSystem::InitROFF2( unsigned char *data, CROFF *obj )
221 {
222 int i;
223
224 TROFF2Header *hdr = (TROFF2Header *)data;
225
226 obj->mROFFEntries = LittleLong(hdr->mCount);
227 obj->mMoveRotateList = new TROFF2Entry[LittleLong(hdr->mCount)];
228 obj->mFrameTime = LittleLong(hdr->mFrameRate);
229 obj->mLerp = 1000 / LittleLong(hdr->mFrameRate);
230 obj->mNumNoteTracks = LittleLong(hdr->mNumNotes);
231
232 if ( obj->mMoveRotateList != 0 )
233 { // Step past the header to get to the goods
234 TROFF2Entry *roff_data = ( TROFF2Entry *)&hdr[1];
235
236 // Copy all of the goods into our ROFF cache
237 for ( i = 0; i < LittleLong(hdr->mCount); i++ )
238 {
239 #ifdef Q3_BIG_ENDIAN
240 obj->mMoveRotateList[i].mOriginOffset[0] = LittleFloat(roff_data[i].mOriginOffset[0]);
241 obj->mMoveRotateList[i].mOriginOffset[1] = LittleFloat(roff_data[i].mOriginOffset[1]);
242 obj->mMoveRotateList[i].mOriginOffset[2] = LittleFloat(roff_data[i].mOriginOffset[2]);
243 obj->mMoveRotateList[i].mRotateOffset[0] = LittleFloat(roff_data[i].mRotateOffset[0]);
244 obj->mMoveRotateList[i].mRotateOffset[1] = LittleFloat(roff_data[i].mRotateOffset[1]);
245 obj->mMoveRotateList[i].mRotateOffset[2] = LittleFloat(roff_data[i].mRotateOffset[2]);
246 #else
247 VectorCopy( roff_data[i].mOriginOffset, obj->mMoveRotateList[i].mOriginOffset );
248 VectorCopy( roff_data[i].mRotateOffset, obj->mMoveRotateList[i].mRotateOffset );
249 #endif
250 obj->mMoveRotateList[i].mStartNote = LittleLong(roff_data[i].mStartNote);
251 obj->mMoveRotateList[i].mNumNotes = LittleLong(roff_data[i].mNumNotes);
252 }
253
254 FixBadAngles(obj);
255
256 if (obj->mNumNoteTracks)
257 {
258 size_t size = 0;
259 char *ptr, *start;
260
261 ptr = start = (char *)&roff_data[i];
262
263 for(i=0;i<obj->mNumNoteTracks;i++)
264 {
265 size += strlen(ptr) + 1;
266 ptr += strlen(ptr) + 1;
267 }
268
269 obj->mNoteTrackIndexes = new char *[obj->mNumNoteTracks];
270 ptr = obj->mNoteTrackIndexes[0] = new char[size];
271 memcpy(obj->mNoteTrackIndexes[0], start, size);
272
273 for(i=1;i<obj->mNumNoteTracks;i++)
274 {
275 ptr += strlen(ptr) + 1;
276 obj->mNoteTrackIndexes[i] = ptr;
277 }
278 }
279 }
280 else
281 {
282 return qfalse;
283 }
284
285 return qtrue;
286 }
287
288 /************************************************************************************************
289 * CROFFSystem::FixBadAngles *
290 * This function will attempt to fix bad angles (large) that come in from the exporter. *
291 * *
292 * Input *
293 * obj: the ROFF object *
294 * *
295 * Output / Return *
296 * none *
297 * *
298 ************************************************************************************************/
FixBadAngles(CROFF * obj)299 void CROFFSystem::FixBadAngles(CROFF *obj)
300 {
301 // Ideally we would fix the ROFF exporter, if that doesn't happen, this may be an adequate solution
302 #ifdef ROFF_AUTO_FIX_BAD_ANGLES
303 int index, t;
304
305 // Attempt to fix bad angles
306
307 for(index=0;index<obj->mROFFEntries;index++)
308 {
309 for ( t = 0; t < 3; t++ )
310 {
311 if ( obj->mMoveRotateList[index].mRotateOffset[t] > 180.0f )
312 { // found a bad angle
313 // Com_Printf( S_COLOR_YELLOW"Fixing bad roff angle\n <%6.2f> changed to <%6.2f>.\n",
314 // roff_data[i].mRotateOffset[t], roff_data[i].mRotateOffset[t] - 360.0f );
315 obj->mMoveRotateList[index].mRotateOffset[t] -= 360.0f;
316 }
317 else if ( obj->mMoveRotateList[index].mRotateOffset[t] < -180.0f )
318 { // found a bad angle
319 // Com_Printf( S_COLOR_YELLOW"Fixing bad roff angle\n <%6.2f> changed to <%6.2f>.\n",
320 // roff_data[i].mRotateOffset[t], roff_data[i].mRotateOffset[t] + 360.0f );
321 obj->mMoveRotateList[index].mRotateOffset[t] += 360.0f;
322 }
323 }
324 }
325 #endif // ROFF_AUTO_FIX_BAD_ANGLES
326 }
327
328 //---------------------------------------------------------------------------
329 // CROFFSystem::Cache
330 // Pre-caches roff data to avoid file hits during gameplay. Disallows
331 // repeated caches of existing roffs.
332 //
333 // INPUTS:
334 // pass in the filepath of the roff to cache
335 //
336 // RETURN:
337 // returns ID of the roff, whether its an existing one or new one.
338 //---------------------------------------------------------------------------
Cache(const char * file,qboolean isClient)339 int CROFFSystem::Cache( const char *file, qboolean isClient )
340 {
341 // See if this item is already cached
342 int len;
343 int id = GetID( file );
344 unsigned char *data;
345 CROFF *cROFF;
346
347 if ( id )
348 {
349 #ifdef _DEBUG
350 Com_Printf( S_COLOR_YELLOW"Ignoring. File '%s' already cached.\n", file );
351 #endif
352 }
353 else
354 { // Read the file in one fell swoop
355 len = FS_ReadFile( file, (void**) &data);
356
357 if ( len <= 0 )
358 {
359 char otherPath[1024];
360 COM_StripExtension(file, otherPath, sizeof( otherPath ));
361 len = FS_ReadFile( va("scripts/%s.rof", otherPath), (void**) &data);
362 if (len <= 0)
363 {
364 Com_Printf( S_COLOR_RED"Could not open .ROF file '%s'\n", file );
365 return 0;
366 }
367 }
368
369 // Make sure that the file is roff
370 if ( !IsROFF( data ) )
371 {
372 Com_Printf( S_COLOR_RED"cache failed: roff <%s> does not exist or is not a valid roff\n", file );
373 FS_FreeFile( data );
374
375 return 0;
376 }
377
378 // Things are looking good so far, so create a new CROFF object
379 id = NewID();
380
381 cROFF = new CROFF( file, id );
382
383 mROFFList[id] = cROFF;
384
385 if ( !InitROFF( data, cROFF ) )
386 { // something failed, so get rid of the object
387 Unload( id );
388 id = 0;
389 }
390
391 FS_FreeFile( data );
392 }
393
394 cROFF = (*mROFFList.find( id )).second;
395 if (isClient)
396 {
397 cROFF->mUsedByClient = qtrue;
398 }
399 else
400 {
401 cROFF->mUsedByServer = qtrue;
402 }
403
404 // If we haven't requested a new ID, we'll just be returning the ID of the existing roff
405 return id;
406 }
407
408
409 //---------------------------------------------------------------------------
410 // CROFFSystem::GetID
411 // Finds the associated (internal) ID of the specified roff file
412 //
413 // INPUTS:
414 // pass in the roff file path
415 //
416 // RETURN:
417 // returns ID if there is one, zero if nothing was found
418 //---------------------------------------------------------------------------
GetID(const char * file)419 int CROFFSystem::GetID( const char *file )
420 {
421 TROFFList::iterator itr;
422
423 // Attempt to find the requested roff
424 for ( itr = mROFFList.begin(); itr != mROFFList.end(); ++itr )
425 {
426 if ( !strcmp( ((CROFF *)((*itr).second))->mROFFFilePath, file ) )
427 { // return the ID to this roff
428 return (*itr).first;
429 }
430 }
431
432 // Not found
433 return 0;
434 }
435
436
437 //---------------------------------------------------------------------------
438 // CROFFSystem::Unload
439 // Removes the roff from the list, deleting it to free up any used resources
440 //
441 // INPUTS:
442 // pass in the id of the roff to delete, use GetID if you only know the roff
443 // filepath
444 //
445 // RETURN:
446 // qtrue if item was in the list, qfalse otherwise
447 //---------------------------------------------------------------------------
Unload(int id)448 qboolean CROFFSystem::Unload( int id )
449 {
450 TROFFList::iterator itr;
451
452 itr = mROFFList.find( id );
453
454 if ( itr != mROFFList.end() )
455 { // requested item found in the list, free mem, then remove from list
456 delete itr->second;
457
458 mROFFList.erase( itr++ );
459
460 #ifdef _DEBUG
461 Com_Printf( S_COLOR_GREEN "roff unloaded\n" );
462 #endif
463
464 return qtrue;
465 }
466 else
467 { // not found
468
469 #ifdef _DEBUG
470 Com_Printf( S_COLOR_RED "unload failed: roff <%i> does not exist\n", id );
471 #endif
472 return qfalse;
473 }
474 }
475
476 //---------------------------------------------------------------------------
477 // CROFFSystem::Clean
478 // Cleans out all Roffs, freeing up any used resources
479 //
480 // INPUTS:
481 // none
482 //
483 // RETURN:
484 // success of operation
485 //---------------------------------------------------------------------------
Clean(qboolean isClient)486 qboolean CROFFSystem::Clean(qboolean isClient)
487 {
488 #if 0
489 TROFFList::iterator itr, next;
490 TROFFEntList::iterator entI, nextEnt;
491 itr = mROFFList.begin();
492 while ( itr != mROFFList.end() )
493 {
494 next = itr;
495 ++next;
496
497 if (isClient)
498 {
499 (*itr).second->mUsedByClient = qfalse;
500 }
501 else
502 {
503 (*itr).second->mUsedByServer = qfalse;
504 }
505 if ((*itr).second->mUsedByClient == qfalse && (*itr).second->mUsedByServer == qfalse)
506 { // we are not used on both client and server, so unload
507 Unload( (*itr).first );
508 }
509
510 itr = next;
511 }
512
513 entI = mROFFEntList.begin();
514 while ( entI != mROFFEntList.end() )
515 {
516 nextEnt = entI;
517 ++nextEnt;
518
519 if ((*entI)->mIsClient == isClient)
520 {
521 delete (*entI);
522 mROFFEntList.erase( entI );
523 }
524
525 entI = nextEnt;
526 }
527 mROFFEntList.clear();
528
529 return qtrue;
530 #else
531 TROFFList::iterator itr;
532
533 itr = mROFFList.begin();
534
535 while ( itr != mROFFList.end() )
536 {
537 Unload( (*itr).first );
538
539 itr = mROFFList.begin();
540 }
541 return qtrue;
542 #endif
543 }
544
545 //---------------------------------------------------------------------------
546 // CROFFSystem::List
547 // Dumps the file path to the current set of cached roffs, for debug purposes
548 //
549 // INPUTS:
550 // none
551 //
552 // RETURN:
553 // none
554 //---------------------------------------------------------------------------
List()555 void CROFFSystem::List()
556 {
557 TROFFList::iterator itr;
558
559 Com_Printf( S_COLOR_GREEN"\n--Cached ROFF files--\n" );
560 Com_Printf( S_COLOR_GREEN"ID FILE\n" );
561
562 for ( itr = mROFFList.begin(); itr != mROFFList.end(); ++itr )
563 {
564 Com_Printf( S_COLOR_GREEN"%2i - %s\n", (*itr).first, ((CROFF *)((*itr).second))->mROFFFilePath );
565 }
566
567 Com_Printf( S_COLOR_GREEN"\nFiles: %i\n", mROFFList.size() );
568 }
569
570
571 //---------------------------------------------------------------------------
572 // CROFFSystem::List
573 // Overloaded version of List, dumps the specified roff data to the console
574 //
575 // INPUTS:
576 // id of roff to display
577 //
578 // RETURN:
579 // success or failure of operation
580 //---------------------------------------------------------------------------
List(int id)581 qboolean CROFFSystem::List( int id )
582 {
583 TROFFList::iterator itr;
584
585 itr = mROFFList.find( id );
586
587 if ( itr != mROFFList.end() )
588 { // requested item found in the list
589 CROFF *obj = ((CROFF *)((*itr).second));
590 TROFF2Entry *dat = obj->mMoveRotateList;
591
592 Com_Printf( S_COLOR_GREEN"File: %s\n", obj->mROFFFilePath );
593 Com_Printf( S_COLOR_GREEN"ID: %i\n", id );
594 Com_Printf( S_COLOR_GREEN"Entries: %i\n\n", obj->mROFFEntries );
595
596 Com_Printf( S_COLOR_GREEN"MOVE ROTATE\n" );
597
598 for ( int i = 0; i < obj->mROFFEntries; i++ )
599 {
600 Com_Printf( S_COLOR_GREEN"%6.2f %6.2f %6.2f %6.2f %6.2f %6.2f\n",
601 dat[i].mOriginOffset[0], dat[i].mOriginOffset[1], dat[i].mOriginOffset[2],
602 dat[i].mRotateOffset[0], dat[i].mRotateOffset[1], dat[i].mRotateOffset[2] );
603 }
604
605 return qtrue;
606 }
607
608 Com_Printf( S_COLOR_YELLOW"ROFF not found: id <%d>\n", id );
609
610 return qfalse;
611 }
612
613
614 //---------------------------------------------------------------------------
615 // CROFFSystem::Play
616 // Start roff playback on an entity
617 //
618 // INPUTS:
619 // the id of the entity that will be roffed
620 // the id of the roff to play
621 //
622 // RETURN:
623 // success or failure of add operation
624 //---------------------------------------------------------------------------
Play(int entID,int id,qboolean doTranslation,qboolean isClient)625 qboolean CROFFSystem::Play( int entID, int id, qboolean doTranslation, qboolean isClient )
626 {
627 sharedEntity_t *ent = NULL;
628
629 if ( !isClient )
630 {
631 ent = SV_GentityNum( entID );
632
633 if ( ent == NULL )
634 { // shame on you..
635 return qfalse;
636 }
637 ent->r.mIsRoffing = qtrue;
638
639 /*rjr if(ent->GetPhysics() == PHYSICS_TYPE_NONE)
640 {
641 ent->SetPhysics(PHYSICS_TYPE_BRUSHMODEL);
642 }*/
643 //bjg TODO: reset this latter?
644 }
645
646 SROFFEntity *roffing_ent = new SROFFEntity;
647
648 roffing_ent->mEntID = entID;
649 roffing_ent->mROFFID = id;
650 roffing_ent->mNextROFFTime = svs.time;
651 roffing_ent->mROFFFrame = 0;
652 roffing_ent->mKill = qfalse;
653 roffing_ent->mSignal = qtrue; // TODO: hook up the real signal code
654 roffing_ent->mTranslated = doTranslation;
655 roffing_ent->mIsClient = isClient;
656
657 if ( !isClient )
658 VectorCopy(ent->s.apos.trBase, roffing_ent->mStartAngles);
659
660 mROFFEntList.push_back( roffing_ent );
661
662 return qtrue;
663 }
664
665
666 //---------------------------------------------------------------------------
667 // CROFFSystem::ListEnts
668 // List all of the ents in the roff system
669 //
670 // INPUTS:
671 // none
672 //
673 // RETURN:
674 // none
675 //---------------------------------------------------------------------------
ListEnts()676 void CROFFSystem::ListEnts()
677 {
678 /* char *name, *file;
679 int id;
680
681 TROFFEntList::iterator itr = mROFFEntList.begin();
682 TROFFList::iterator itrRoff;
683
684 Com_Printf( S_COLOR_GREEN"\n--ROFFing Entities--\n" );
685 Com_Printf( S_COLOR_GREEN"EntID EntName RoffFile\n" );
686
687 // display everything in the end list
688 for ( itr = mROFFEntList.begin(); itr != mROFFEntList.end(); ++itr )
689 {
690 // Entity ID
691 id = ((SROFFEntity *)(*itr))->mEntID;
692 // Entity Name
693 name = entitySystem->GetEntityFromID( id )->GetName();
694 // ROFF object that will contain the roff file name
695 itrRoff = mROFFList.find( ((SROFFEntity *)(*itr))->mROFFID );
696
697 if ( itrRoff != mROFFList.end() )
698 { // grab our filename
699 file = ((CROFF *)((*itrRoff).second ))->mROFFFilePath;
700 }
701 else
702 { // roff filename not found == bad
703 file = "Error: Unknown";
704 }
705
706 Com_Printf( S_COLOR_GREEN"%3i %s %s\n", id, name, file );
707 }
708
709 Com_Printf( S_COLOR_GREEN"\nEntities: %i\n", mROFFEntList.size() );*/
710 }
711
712
713 //---------------------------------------------------------------------------
714 // CROFFSystem::PurgeEnt
715 // Prematurely purge an entity from the roff system
716 //
717 // INPUTS:
718 // the id of the entity to purge
719 //
720 // RETURN:
721 // success or failure of purge operation
722 //---------------------------------------------------------------------------
PurgeEnt(int entID,qboolean isClient)723 qboolean CROFFSystem::PurgeEnt( int entID, qboolean isClient )
724 {
725 TROFFEntList::iterator itr = mROFFEntList.begin();
726
727 for ( itr = mROFFEntList.begin(); itr != mROFFEntList.end(); ++itr )
728 {
729 if ( (*itr)->mIsClient == isClient && (*itr)->mEntID == entID)
730 {
731 // Make sure it won't stay lerping
732 ClearLerp( (*itr) );
733
734 delete (*itr);
735
736 mROFFEntList.erase( itr );
737 return qtrue;
738 }
739 }
740
741 Com_Printf( S_COLOR_RED"Purge failed: Entity <%i> not found\n", entID );
742
743 return qfalse;
744 }
745
746
747
748 //---------------------------------------------------------------------------
749 // CROFFSystem::PurgeEnt
750 // Prematurely purge an entity from the roff system
751 //
752 // INPUTS:
753 // the name fo the entity to purge
754 //
755 // RETURN:
756 // success or failure of purge operation
757 //---------------------------------------------------------------------------
PurgeEnt(char * name)758 qboolean CROFFSystem::PurgeEnt( char *name )
759 {
760 /* rjr CEntity *ent = entitySystem->GetEntityFromName( NULL, name );
761
762 if ( ent && ent->GetInUse() == qtrue )
763 {
764 return PurgeEnt( ent->GetID() );
765 }
766 else
767 {
768 Com_Printf( S_COLOR_RED"Entity <%s> not found or not in use\n", name );
769 return qfalse;
770 }*/
771
772 return qfalse;
773 }
774
775 //---------------------------------------------------------------------------
776 // CROFFSystem::UpdateEntities
777 // Update all of the entities in the system
778 //
779 // INPUTS:
780 // none
781 //
782 // RETURN:
783 // none
784 //---------------------------------------------------------------------------
UpdateEntities(qboolean isClient)785 void CROFFSystem::UpdateEntities(qboolean isClient)
786 {
787 TROFFEntList::iterator itr = mROFFEntList.begin();
788 TROFFList::iterator itrRoff;
789
790 // display everything in the entity list
791 for ( itr = mROFFEntList.begin(); itr != mROFFEntList.end(); ++itr )
792 {
793 if ((*itr)->mIsClient != isClient)
794 {
795 continue;
796 }
797
798 // Get this entities ROFF object
799 itrRoff = mROFFList.find( ((SROFFEntity *)(*itr))->mROFFID );
800
801 if ( itrRoff != mROFFList.end() )
802 { // roff that baby!
803 if ( !ApplyROFF( ((SROFFEntity *)(*itr)), ((CROFF *)((*itrRoff).second ))))
804 { // done roffing, mark for death
805 ((SROFFEntity *)(*itr))->mKill = qtrue;
806 }
807 }
808 else
809 { // roff not found == bad, dump an error message and purge this ent
810 Com_Printf( S_COLOR_RED"ROFF System Error:\n" );
811 // Com_Printf( S_COLOR_RED" -ROFF not found for entity <%s>\n",
812 // entitySystem->GetEntityFromID(((SROFFEntity *)(*itr))->mEntID)->GetName() );
813
814 ((SROFFEntity *)(*itr))->mKill = qtrue;
815
816 ClearLerp( (*itr) );
817 }
818 }
819
820 itr = mROFFEntList.begin();
821
822 // Delete killed ROFFers from the list
823 // Man, there just has to be a better way to do this
824 while ( itr != mROFFEntList.end() )
825 {
826 if ((*itr)->mIsClient != isClient)
827 {
828 itr++;
829 continue;
830 }
831
832 if ( ((SROFFEntity *)(*itr))->mKill == qtrue )
833 {
834 //make sure ICARUS knows ROFF is stopped
835 // CICARUSGameInterface::TaskIDComplete(
836 // entitySystem->GetEntityFromID(((SROFFEntity *)(*itr))->mEntID), TID_MOVE);
837 // trash this guy from the list
838 delete (*itr);
839 mROFFEntList.erase( itr );
840 itr = mROFFEntList.begin();
841 }
842 else
843 {
844 itr++;
845 }
846 }
847 }
848
849 //---------------------------------------------------------------------------
850 // CROFFSystem::ApplyROFF
851 // Does the dirty work of applying the raw ROFF data
852 //
853 // INPUTS:
854 // The the roff_entity struct and the raw roff data
855 //
856 // RETURN:
857 // True == success; False == roff playback complete or failure
858 //---------------------------------------------------------------------------
ApplyROFF(SROFFEntity * roff_ent,CROFFSystem::CROFF * roff)859 qboolean CROFFSystem::ApplyROFF( SROFFEntity *roff_ent, CROFFSystem::CROFF *roff )
860 {
861 vec3_t f, r, u, result;
862 sharedEntity_t *ent = NULL;
863 trajectory_t *originTrajectory = NULL, *angleTrajectory = NULL;
864 float *origin = NULL, *angle = NULL;
865
866
867 if ( svs.time < roff_ent->mNextROFFTime )
868 { // Not time to roff yet
869 return qtrue;
870 }
871
872 if (roff_ent->mIsClient)
873 {
874 #ifndef DEDICATED
875 vec3_t originTemp, angleTemp;
876 originTrajectory = CGVM_GetOriginTrajectory( roff_ent->mEntID );
877 angleTrajectory = CGVM_GetAngleTrajectory( roff_ent->mEntID );
878 CGVM_GetOrigin( roff_ent->mEntID, originTemp );
879 origin = originTemp;
880 CGVM_GetAngles( roff_ent->mEntID, angleTemp );
881 angle = angleTemp;
882 #endif
883 }
884 else
885 {
886 // Find the entity to apply the roff to
887 ent = SV_GentityNum( roff_ent->mEntID );
888
889 if ( ent == 0 )
890 { // bad stuff
891 return qfalse;
892 }
893
894 originTrajectory = &ent->s.pos;
895 angleTrajectory = &ent->s.apos;
896 origin = ent->r.currentOrigin;
897 angle = ent->r.currentAngles;
898 }
899
900
901 if ( roff_ent->mROFFFrame >= roff->mROFFEntries )
902 { // we are done roffing, so stop moving and flag this ent to be removed
903 SetLerp( originTrajectory, TR_STATIONARY, origin, NULL, svs.time, roff->mLerp );
904 SetLerp( angleTrajectory, TR_STATIONARY, angle, NULL, svs.time, roff->mLerp );
905 if (!roff_ent->mIsClient)
906 {
907 ent->r.mIsRoffing = qfalse;
908 }
909 return qfalse;
910 }
911
912 if (roff_ent->mTranslated)
913 {
914 AngleVectors(roff_ent->mStartAngles, f, r, u );
915 VectorScale(f, roff->mMoveRotateList[roff_ent->mROFFFrame].mOriginOffset[0], result);
916 VectorMA(result, -roff->mMoveRotateList[roff_ent->mROFFFrame].mOriginOffset[1], r, result);
917 VectorMA(result, roff->mMoveRotateList[roff_ent->mROFFFrame].mOriginOffset[2], u, result);
918 }
919 else
920 {
921 VectorCopy(roff->mMoveRotateList[roff_ent->mROFFFrame].mOriginOffset, result);
922 }
923
924 // Set up our origin interpolation
925 SetLerp( originTrajectory, TR_LINEAR, origin, result, svs.time, roff->mLerp );
926
927 // Set up our angle interpolation
928 SetLerp( angleTrajectory, TR_LINEAR, angle,
929 roff->mMoveRotateList[roff_ent->mROFFFrame].mRotateOffset, svs.time, roff->mLerp );
930
931 if (roff->mMoveRotateList[roff_ent->mROFFFrame].mStartNote >= 0)
932 {
933 int i;
934
935 for(i=0;i<roff->mMoveRotateList[roff_ent->mROFFFrame].mNumNotes;i++)
936 {
937 ProcessNote(roff_ent, roff->mNoteTrackIndexes[roff->mMoveRotateList[roff_ent->mROFFFrame].mStartNote + i]);
938 }
939 }
940
941 // Advance ROFF frames and lock to a 10hz cycle
942 roff_ent->mROFFFrame++;
943 roff_ent->mNextROFFTime = svs.time + roff->mFrameTime;
944
945 //rww - npcs need to know when they're getting roff'd
946 if ( !roff_ent->mIsClient )
947 ent->next_roff_time = roff_ent->mNextROFFTime;
948
949
950 return qtrue;
951 }
952
953
954 /************************************************************************************************
955 * CROFFSystem::ProcessNote *
956 * This function will send the note to the client. It will parse through the note for *
957 * leading or trailing white space (thus making each line feed a separate function call). *
958 * *
959 * Input *
960 * ent: the entity for which the roff is being played *
961 * note: the note that should be passed on *
962 * *
963 * Output / Return *
964 * none *
965 * *
966 ************************************************************************************************/
ProcessNote(SROFFEntity * roff_ent,char * note)967 void CROFFSystem::ProcessNote(SROFFEntity *roff_ent, char *note)
968 {
969 char temp[1024];
970 int pos, size;
971
972 pos = 0;
973 while(note[pos])
974 {
975 size = 0;
976 while(note[pos] && note[pos] < ' ')
977 {
978 pos++;
979 }
980
981 while(note[pos] && note[pos] >= ' ')
982 {
983 temp[size++] = note[pos++];
984 }
985 temp[size] = 0;
986
987 if (size)
988 {
989 if (roff_ent->mIsClient)
990 {
991 #ifndef DEDICATED
992 CGVM_ROFF_NotetrackCallback( roff_ent->mEntID, temp );
993 #endif
994 }
995 else
996 {
997 GVM_ROFF_NotetrackCallback( roff_ent->mEntID, temp );
998 }
999 }
1000 }
1001 }
1002
1003 //---------------------------------------------------------------------------
1004 // CROFFSystem::ClearLerp
1005 // Helper function to clear a given entities lerp fields
1006 //
1007 // INPUTS:
1008 // The ID of the entity to clear
1009 //
1010 // RETURN:
1011 // success or failure of the operation
1012 //---------------------------------------------------------------------------
ClearLerp(SROFFEntity * roff_ent)1013 qboolean CROFFSystem::ClearLerp( SROFFEntity *roff_ent )
1014 {
1015 sharedEntity_t *ent = NULL;
1016 trajectory_t *originTrajectory = NULL, *angleTrajectory = NULL;
1017 float *origin = NULL, *angle = NULL;
1018
1019 if (roff_ent->mIsClient)
1020 {
1021 #ifndef DEDICATED
1022 vec3_t originTemp, angleTemp;
1023 originTrajectory = CGVM_GetOriginTrajectory( roff_ent->mEntID );
1024 angleTrajectory = CGVM_GetAngleTrajectory( roff_ent->mEntID );
1025 CGVM_GetOrigin( roff_ent->mEntID, originTemp );
1026 origin = originTemp;
1027 CGVM_GetAngles( roff_ent->mEntID, angleTemp );
1028 angle = angleTemp;
1029 #endif
1030 }
1031 else
1032 {
1033 // Find the entity to apply the roff to
1034 ent = SV_GentityNum( roff_ent->mEntID );
1035
1036 if ( ent == 0 )
1037 { // bad stuff
1038 return qfalse;
1039 }
1040
1041 originTrajectory = &ent->s.pos;
1042 angleTrajectory = &ent->s.apos;
1043 origin = ent->r.currentOrigin;
1044 angle = ent->r.currentAngles;
1045 }
1046
1047 SetLerp( originTrajectory, TR_STATIONARY, origin, NULL, svs.time, ROFF_SAMPLE_RATE );
1048 SetLerp( angleTrajectory, TR_STATIONARY, angle, NULL, svs.time, ROFF_SAMPLE_RATE );
1049
1050 return qtrue;
1051 }
1052
1053 //---------------------------------------------------------------------------
1054 // CROFFSystem::SetLerp
1055 // Helper function to set up a positional or angular interpolation
1056 //
1057 // INPUTS:
1058 // The entity trajectory field to modify, the interpolation type, the base origin,
1059 // and the interpolation start time
1060 //
1061 // RETURN:
1062 // none
1063 //---------------------------------------------------------------------------
SetLerp(trajectory_t * tr,trType_t type,vec3_t origin,vec3_t delta,int time,int rate)1064 void CROFFSystem::SetLerp( trajectory_t *tr, trType_t type, vec3_t origin, vec3_t delta, int time, int rate)
1065 {
1066 tr->trType = type;
1067 tr->trTime = time;
1068 VectorCopy( origin, tr->trBase );
1069
1070 // Check for a NULL delta
1071 if ( delta )
1072 {
1073 VectorScale( delta, rate, tr->trDelta );
1074 }
1075 else
1076 {
1077 VectorClear( tr->trDelta );
1078 }
1079 }
1080
1081