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