1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein single player GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”).
8 
9 RTCW SP 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 RTCW SP 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 RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW SP 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 RTCW SP 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 
30 //===========================================================================
31 // bg_animation.c
32 //
33 //	Incorporates several elements related to the new flexible animation system.
34 //
35 //	This includes scripting elements, support routines for new animation set
36 //	reference system and other bits and pieces.
37 //
38 //===========================================================================
39 
40 #include "../qcommon/q_shared.h"
41 #include "bg_public.h"
42 
43 // debug defines, to prevent doing costly string cvar lookups
44 //#define	DBGANIMS
45 //#define	DBGANIMEVENTS
46 
47 // this is used globally within this file to reduce redundant params
48 animScriptData_t *globalScriptData = NULL;
49 
50 #define MAX_ANIM_DEFINES    16
51 
52 static char *globalFilename;    // to prevent redundant params
53 static int parseClient;
54 
55 // these are used globally during script parsing
56 static int numDefines[NUM_ANIM_CONDITIONS];
57 static char defineStrings[10000];       // stores the actual strings
58 static int defineStringsOffset;
59 static animStringItem_t defineStr[NUM_ANIM_CONDITIONS][MAX_ANIM_DEFINES];
60 static int defineBits[NUM_ANIM_CONDITIONS][MAX_ANIM_DEFINES][2];
61 
62 static scriptAnimMoveTypes_t parseMovetype;
63 static int parseEvent;
64 
65 animStringItem_t weaponStrings[WP_NUM_WEAPONS];
66 qboolean weaponStringsInited = qfalse;
67 
68 animStringItem_t animStateStr[] =
69 {
70 	{"RELAXED", -1},
71 	{"QUERY", -1},
72 	{"ALERT", -1},
73 	{"COMBAT", -1},
74 
75 	{NULL, -1},
76 };
77 
78 static animStringItem_t animMoveTypesStr[] =
79 {
80 	{"** UNUSED **", -1},
81 	{"IDLE", -1},
82 	{"IDLECR", -1},
83 	{"WALK", -1},
84 	{"WALKBK", -1},
85 	{"WALKCR", -1},
86 	{"WALKCRBK", -1},
87 	{"RUN", -1},
88 	{"RUNBK", -1},
89 	{"SWIM", -1},
90 	{"SWIMBK", -1},
91 	{"STRAFERIGHT", -1},
92 	{"STRAFELEFT", -1},
93 	{"TURNRIGHT", -1},
94 	{"TURNLEFT", -1},
95 	{"CLIMBUP", -1},
96 	{"CLIMBDOWN", -1},
97 
98 	{NULL, -1},
99 };
100 
101 static animStringItem_t animEventTypesStr[] =
102 {
103 	{"PAIN", -1},
104 	{"DEATH", -1},
105 	{"FIREWEAPON", -1},
106 	{"JUMP", -1},
107 	{"JUMPBK", -1},
108 	{"LAND", -1},
109 	{"DROPWEAPON", -1},
110 	{"RAISEWEAPON", -1},
111 	{"CLIMBMOUNT", -1},
112 	{"CLIMBDISMOUNT", -1},
113 	{"RELOAD", -1},
114 	{"PICKUPGRENADE", -1},
115 	{"KICKGRENADE", -1},
116 	{"QUERY", -1},
117 	{"INFORM_FRIENDLY_OF_ENEMY", -1},
118 	{"KICK", -1},
119 	{"REVIVE", -1},
120 	{"FIRSTSIGHT", -1},
121 	{"ROLL", -1},
122 	{"FLIP", -1},
123 	{"DIVE", -1},
124 	{"PRONE_TO_CROUCH", -1},
125 	{"BULLETIMPACT", -1},
126 	{"INSPECTSOUND", -1},
127 
128 	{NULL, -1},
129 };
130 
131 animStringItem_t animBodyPartsStr[] =
132 {
133 	{"** UNUSED **", -1},
134 	{"LEGS", -1},
135 	{"TORSO", -1},
136 	{"BOTH", -1},
137 
138 	{NULL, -1},
139 };
140 
141 //------------------------------------------------------------
142 // conditions
143 
144 static animStringItem_t animConditionPositionsStr[] =
145 {
146 	{"** UNUSED **", -1},
147 	{"BEHIND", -1},
148 	{"INFRONT", -1},
149 	{"RIGHT", -1},
150 	{"LEFT", -1},
151 
152 	{NULL, -1},
153 };
154 
155 static animStringItem_t animConditionMountedStr[] =
156 {
157 	{"** UNUSED **", -1},
158 	{"MG42", -1},
159 
160 	{NULL, -1},
161 };
162 
163 static animStringItem_t animConditionLeaningStr[] =
164 {
165 	{"** UNUSED **", -1},
166 	{"RIGHT", -1},
167 	{"LEFT", -1},
168 
169 	{NULL, -1},
170 };
171 
172 // !!! NOTE: this must be kept in sync with the tag names in ai_cast_characters.c
173 static animStringItem_t animConditionImpactPointsStr[] =
174 {
175 	{"** UNUSED **", -1},
176 	{"HEAD", -1},
177 	{"CHEST", -1},
178 	{"GUT", -1},
179 	{"GROIN", -1},
180 	{"SHOULDER_RIGHT", -1},
181 	{"SHOULDER_LEFT", -1},
182 	{"KNEE_RIGHT", -1},
183 	{"KNEE_LEFT", -1},
184 
185 	{NULL, -1},
186 };
187 
188 // !!! NOTE: this must be kept in sync with the teams in ai_cast.h
189 static animStringItem_t animEnemyTeamsStr[] =
190 {
191 	{"NAZI", -1},
192 	{"ALLIES", -1},
193 	{"MONSTER", -1},
194 	{"SPARE1", -1},
195 	{"SPARE2", -1},
196 	{"SPARE3", -1},
197 	{"SPARE4", -1},
198 	{"NEUTRAL", -1}
199 };
200 
201 static animStringItem_t animHealthLevelStr[] =
202 {
203 	{"1", -1},
204 	{"2", -1},
205 	{"3", -1},
206 };
207 
208 static animStringItem_t animConditionSpecialConditionStr[] =
209 {
210 	{"** UNUSED **", -1},
211 	{"ESCAPE1_CUTSCENE_1", -1},
212 	{"VILL1_KARL", -1},
213 	{"VILL1_KESSLER", -1},
214 
215 	{NULL, -1},
216 };
217 
218 typedef enum
219 {
220 	ANIM_CONDTYPE_BITFLAGS,
221 	ANIM_CONDTYPE_VALUE,
222 
223 	NUM_ANIM_CONDTYPES
224 } animScriptConditionTypes_t;
225 
226 typedef struct
227 {
228 	animScriptConditionTypes_t type;
229 	animStringItem_t            *values;
230 } animConditionTable_t;
231 
232 static animStringItem_t animConditionsStr[] =
233 {
234 	{"WEAPONS", -1},
235 	{"ENEMY_POSITION", -1},
236 	{"ENEMY_WEAPON", -1},
237 	{"UNDERWATER", -1},
238 	{"MOUNTED", -1},
239 	{"MOVETYPE", -1},
240 	{"UNDERHAND", -1},
241 	{"LEANING", -1},
242 	{"IMPACT_POINT", -1},
243 	{"CROUCHING", -1},
244 	{"STUNNED", -1},
245 	{"FIRING", -1},
246 	{"SHORT_REACTION", -1},
247 	{"ENEMY_TEAM", -1},
248 	{"PARACHUTE", -1},
249 	{"CHARGING", -1},
250 	{"SECONDLIFE", -1},
251 	{"HEALTH_LEVEL", -1},
252 	{"DEFENSE", -1},
253 	{"SPECIAL_CONDITION", -1},
254 
255 	{NULL, -1},
256 };
257 
258 static animConditionTable_t animConditionsTable[NUM_ANIM_CONDITIONS] =
259 {
260 	{ANIM_CONDTYPE_BITFLAGS,        weaponStrings},
261 	{ANIM_CONDTYPE_BITFLAGS,        animConditionPositionsStr},
262 	{ANIM_CONDTYPE_BITFLAGS,        weaponStrings},
263 	{ANIM_CONDTYPE_VALUE,           NULL},
264 	{ANIM_CONDTYPE_VALUE,           animConditionMountedStr},
265 	{ANIM_CONDTYPE_BITFLAGS,        animMoveTypesStr},
266 	{ANIM_CONDTYPE_VALUE,           NULL},
267 	{ANIM_CONDTYPE_VALUE,           animConditionLeaningStr},
268 	{ANIM_CONDTYPE_VALUE,           animConditionImpactPointsStr},
269 	{ANIM_CONDTYPE_VALUE,           NULL},
270 	{ANIM_CONDTYPE_VALUE,           NULL},
271 	{ANIM_CONDTYPE_VALUE,           NULL},
272 	{ANIM_CONDTYPE_VALUE,           NULL},
273 	{ANIM_CONDTYPE_VALUE,           animEnemyTeamsStr},
274 	{ANIM_CONDTYPE_VALUE,           NULL},
275 	{ANIM_CONDTYPE_VALUE,           NULL},
276 	{ANIM_CONDTYPE_VALUE,           NULL},
277 	{ANIM_CONDTYPE_VALUE,           animHealthLevelStr},
278 	{ANIM_CONDTYPE_VALUE,           NULL},
279 	{ANIM_CONDTYPE_VALUE,           animConditionSpecialConditionStr},
280 };
281 
282 //------------------------------------------------------------
283 
284 /*
285 ================
286 return a hash value for the given string
287 ================
288 */
BG_StringHashValue(const char * fname)289 static long BG_StringHashValue( const char *fname ) {
290 	int i;
291 	long hash;
292 	char letter;
293 
294 	hash = 0;
295 	i = 0;
296 	while ( fname[i] != '\0' ) {
297 		letter = tolower( fname[i] );
298 		hash += (long)( letter ) * ( i + 119 );
299 		i++;
300 	}
301 	if ( hash == -1 ) {
302 		hash = 0;   // never return -1
303 	}
304 	return hash;
305 }
306 
307 /*
308 =================
309 BG_AnimParseError
310 =================
311 */
BG_AnimParseError(const char * msg,...)312 void QDECL BG_AnimParseError( const char *msg, ... ) {
313 	va_list argptr;
314 	char text[1024];
315 
316 	va_start( argptr, msg );
317 	Q_vsnprintf( text, sizeof( text ), msg, argptr );
318 	va_end( argptr );
319 
320 	if ( globalFilename ) {
321 		Com_Error( ERR_DROP,  "%s: (%s, line %i)", text, globalFilename, COM_GetCurrentParseLine() + 1 );
322 	} else {
323 		Com_Error( ERR_DROP,  "%s", text );
324 	}
325 }
326 
327 /*
328 =================
329 BG_ModelInfoForClient
330 =================
331 */
BG_ModelInfoForClient(int client)332 animModelInfo_t *BG_ModelInfoForClient( int client ) {
333 	if ( !globalScriptData ) {
334 		BG_AnimParseError( "BG_ModelInfoForClient: NULL globalScriptData" );
335 	}
336 	//
337 	if ( !globalScriptData->clientModels[client] ) {
338 		BG_AnimParseError( "BG_ModelInfoForClient: client %i has no modelinfo", client );
339 	}
340 	//
341 	return globalScriptData->modelInfo[globalScriptData->clientModels[client] - 1];
342 }
343 
344 /*
345 =================
346 BG_ModelInfoForModelname
347 =================
348 */
BG_ModelInfoForModelname(char * modelname)349 animModelInfo_t *BG_ModelInfoForModelname( char *modelname ) {
350 	int i;
351 	animModelInfo_t *modelInfo;
352 	//
353 	if ( !globalScriptData ) {
354 		BG_AnimParseError( "BG_ModelInfoForModelname: NULL globalScriptData" );
355 	}
356 	//
357 	for ( i = 0; i < MAX_ANIMSCRIPT_MODELS; i++ ) {
358 		modelInfo = globalScriptData->modelInfo[i];
359 		if ( modelInfo == NULL ) {
360 			continue;
361 		}
362 
363 		if ( !modelInfo->modelname[0] ) {
364 			continue;
365 		}
366 		if ( !Q_stricmp( modelname, modelInfo->modelname ) ) {
367 			return modelInfo;
368 		}
369 	}
370 	//
371 	return NULL;
372 }
373 
374 /*
375 =================
376 BG_AnimationIndexForString
377 =================
378 */
BG_AnimationIndexForString(char * string,int client)379 int BG_AnimationIndexForString( char *string, int client ) {
380 	int i, hash;
381 	animation_t *anim;
382 	animModelInfo_t *modelInfo;
383 
384 	modelInfo = BG_ModelInfoForClient( client );
385 
386 	hash = BG_StringHashValue( string );
387 
388 	for ( i = 0, anim = modelInfo->animations; i < modelInfo->numAnimations; i++, anim++ ) {
389 		if ( ( hash == anim->nameHash ) && !Q_stricmp( string, anim->name ) ) {
390 			// found a match
391 			return i;
392 		}
393 	}
394 	// no match found
395 	BG_AnimParseError( "BG_AnimationIndexForString: unknown index '%s' for model '%s'", string, modelInfo->modelname );
396 	return -1;  // shutup compiler
397 }
398 
399 /*
400 =================
401 BG_AnimationForString
402 =================
403 */
BG_AnimationForString(char * string,animModelInfo_t * modelInfo)404 animation_t *BG_AnimationForString( char *string, animModelInfo_t *modelInfo ) {
405 	int i, hash;
406 	animation_t *anim;
407 
408 	hash = BG_StringHashValue( string );
409 
410 	for ( i = 0, anim = modelInfo->animations; i < modelInfo->numAnimations; i++, anim++ ) {
411 		if ( ( hash == anim->nameHash ) && !Q_stricmp( string, anim->name ) ) {
412 			// found a match
413 			return anim;
414 		}
415 	}
416 	// no match found
417 	Com_Error( ERR_DROP, "BG_AnimationForString: unknown animation '%s' for model '%s'", string, modelInfo->modelname );
418 	return NULL;    // shutup compiler
419 }
420 
421 /*
422 =================
423 BG_IndexForString
424 
425   errors out if no match found
426 =================
427 */
BG_IndexForString(char * token,animStringItem_t * strings,qboolean allowFail)428 int BG_IndexForString( char *token, animStringItem_t *strings, qboolean allowFail ) {
429 	int i, hash;
430 	animStringItem_t *strav;
431 
432 	hash = BG_StringHashValue( token );
433 
434 	for ( i = 0, strav = strings; strav->string; strav++, i++ ) {
435 		if ( strav->hash == -1 ) {
436 			strav->hash = BG_StringHashValue( strav->string );
437 		}
438 		if ( ( hash == strav->hash ) && !Q_stricmp( token, strav->string ) ) {
439 			// found a match
440 			return i;
441 		}
442 	}
443 	// no match found
444 	if ( !allowFail ) {
445 		BG_AnimParseError( "BG_IndexForString: unknown token '%s'", token );
446 	}
447 	//
448 	return -1;
449 }
450 
451 /*
452 ===============
453 BG_CopyStringIntoBuffer
454 ===============
455 */
BG_CopyStringIntoBuffer(char * string,char * buffer,int bufSize,int * offset)456 char *BG_CopyStringIntoBuffer( char *string, char *buffer, int bufSize, int *offset ) {
457 	char *pch;
458 
459 	// check for overloaded buffer
460 	if ( *offset + strlen( string ) + 1 >= bufSize ) {
461 		BG_AnimParseError( "BG_CopyStringIntoBuffer: out of buffer space" );
462 	}
463 
464 	pch = &buffer[*offset];
465 
466 	// safe to do a strcpy since we've already checked for overrun
467 	strcpy( pch, string );
468 
469 	// move the offset along
470 	*offset += strlen( string ) + 1;
471 
472 	return pch;
473 }
474 
475 /*
476 ============
477 BG_InitWeaponStrings
478 
479   Builds the list of weapon names from the item list. This is done here rather
480   than hardcoded to ease the process of modifying the weapons.
481 ============
482 */
BG_InitWeaponStrings(void)483 void BG_InitWeaponStrings( void ) {
484 	int i;
485 	gitem_t *item;
486 
487 	memset( weaponStrings, 0, sizeof( weaponStrings ) );
488 
489 	for ( i = 0; i < WP_NUM_WEAPONS; i++ ) {
490 		// find this weapon in the itemslist, and extract the name
491 		for ( item = bg_itemlist + 1; item->classname; item++ ) {
492 			if ( item->giType == IT_WEAPON && item->giTag == i ) {
493 				// found a match
494 				weaponStrings[i].string = item->pickup_name;
495 				weaponStrings[i].hash = BG_StringHashValue( weaponStrings[i].string );
496 				break;
497 			}
498 		}
499 
500 		if ( !item->classname ) {
501 			weaponStrings[i].string = "(unknown)";
502 			weaponStrings[i].hash = BG_StringHashValue( weaponStrings[i].string );
503 		}
504 	}
505 
506 	weaponStringsInited = qtrue;
507 }
508 
509 /*
510 ============
511 BG_AnimParseAnimConfig
512 
513   returns qfalse if error, qtrue otherwise
514 ============
515 */
BG_AnimParseAnimConfig(animModelInfo_t * animModelInfo,const char * filename,const char * input)516 qboolean BG_AnimParseAnimConfig( animModelInfo_t *animModelInfo, const char *filename, const char *input ) {
517 	char    *text_p, *token, *oldtext_p;
518 	animation_t *animations;
519 	headAnimation_t *headAnims;
520 	int i, fps, skip = -1;
521 
522 	if ( !weaponStringsInited ) {
523 		BG_InitWeaponStrings();
524 	}
525 
526 	globalFilename = (char *)filename;
527 
528 	animations = animModelInfo->animations;
529 	animModelInfo->numAnimations = 0;
530 	headAnims = animModelInfo->headAnims;
531 
532 	text_p = (char *)input;
533 	COM_BeginParseSession( "BG_AnimParseAnimConfig" );
534 
535 	animModelInfo->footsteps = FOOTSTEP_NORMAL;
536 	VectorClear( animModelInfo->headOffset );
537 	animModelInfo->gender = GENDER_MALE;
538 	animModelInfo->isSkeletal = qfalse;
539 	animModelInfo->version = 0;
540 
541 	// read optional parameters
542 	while ( 1 ) {
543 		token = COM_Parse( &text_p );
544 		if ( !token[0] ) {
545 			break;
546 		}
547 		if ( !Q_stricmp( token, "footsteps" ) ) {
548 			token = COM_Parse( &text_p );
549 			if ( !token[0] ) {
550 				break;
551 			}
552 			if ( !Q_stricmp( token, "default" ) || !Q_stricmp( token, "normal" ) ) {
553 				animModelInfo->footsteps = FOOTSTEP_NORMAL;
554 			} else if ( !Q_stricmp( token, "boot" ) ) {
555 				animModelInfo->footsteps = FOOTSTEP_BOOT;
556 			} else if ( !Q_stricmp( token, "flesh" ) ) {
557 				animModelInfo->footsteps = FOOTSTEP_FLESH;
558 			} else if ( !Q_stricmp( token, "mech" ) ) {
559 				animModelInfo->footsteps = FOOTSTEP_MECH;
560 			} else if ( !Q_stricmp( token, "energy" ) ) {
561 				animModelInfo->footsteps = FOOTSTEP_ENERGY;
562 			} else {
563 				BG_AnimParseError( "Bad footsteps parm '%s'\n", token );
564 			}
565 			continue;
566 		} else if ( !Q_stricmp( token, "headoffset" ) ) {
567 			for ( i = 0 ; i < 3 ; i++ ) {
568 				token = COM_Parse( &text_p );
569 				if ( !token[0] ) {
570 					break;
571 				}
572 				animModelInfo->headOffset[i] = atof( token );
573 			}
574 			continue;
575 		} else if ( !Q_stricmp( token, "sex" ) ) {
576 			token = COM_Parse( &text_p );
577 			if ( !token[0] ) {
578 				break;
579 			}
580 			if ( token[0] == 'f' || token[0] == 'F' ) {
581 				animModelInfo->gender = GENDER_FEMALE;
582 			} else if ( token[0] == 'n' || token[0] == 'N' ) {
583 				animModelInfo->gender = GENDER_NEUTER;
584 			} else {
585 				animModelInfo->gender = GENDER_MALE;
586 			}
587 			continue;
588 		} else if ( !Q_stricmp( token, "version" ) ) {
589 			token = COM_Parse( &text_p );
590 			if ( !token[0] ) {
591 				break;
592 			}
593 			animModelInfo->version = atoi( token );
594 			continue;
595 		} else if ( !Q_stricmp( token, "skeletal" ) ) {
596 			animModelInfo->isSkeletal = qtrue;
597 			continue;
598 		}
599 
600 		if ( animModelInfo->version < 2 ) {
601 			// if it is a number, start parsing animations
602 			if ( token[0] >= '0' && token[0] <= '9' ) {
603 				text_p -= strlen( token );    // unget the token
604 				break;
605 			}
606 		}
607 
608 		// STARTANIMS marks the start of the animations
609 		if ( !Q_stricmp( token, "STARTANIMS" ) ) {
610 			break;
611 		}
612 		BG_AnimParseError( "unknown token '%s'", token );
613 	}
614 
615 	// read information for each frame
616 	for ( i = 0 ; ( animModelInfo->version > 1 ) || ( i < MAX_ANIMATIONS ) ; i++ ) {
617 
618 		token = COM_Parse( &text_p );
619 		if ( !token[0] ) {
620 			break;
621 		}
622 
623 		if ( animModelInfo->version > 1 ) {   // includes animation names at start of each line
624 
625 			if ( !Q_stricmp( token, "ENDANIMS" ) ) {   // end of animations
626 				break;
627 			}
628 
629 			Q_strncpyz( animations[i].name, token, sizeof( animations[i].name ) );
630 			// convert to all lower case
631 			Q_strlwr( animations[i].name );
632 			//
633 			token = COM_ParseExt( &text_p, qfalse );
634 			if ( !token[0] ) {
635 				BG_AnimParseError( "end of file without ENDANIMS" );
636 				break;
637 			}
638 		} else {
639 			// just set it to the equivalent animStrings[]
640 			Q_strncpyz( animations[i].name, animStrings[i], sizeof( animations[i].name ) );
641 			// convert to all lower case
642 			Q_strlwr( animations[i].name );
643 		}
644 
645 		animations[i].firstFrame = atoi( token );
646 
647 		if ( !animModelInfo->isSkeletal ) { // skeletal models dont require adjustment
648 
649 			// leg only frames are adjusted to not count the upper body only frames
650 			if ( i == LEGS_WALKCR ) {
651 				skip = animations[LEGS_WALKCR].firstFrame - animations[TORSO_GESTURE].firstFrame;
652 			}
653 			if ( i >= LEGS_WALKCR ) {
654 				animations[i].firstFrame -= skip;
655 			}
656 
657 		}
658 
659 		token = COM_ParseExt( &text_p, qfalse );
660 		if ( !token[0] ) {
661 			BG_AnimParseError( "end of file without ENDANIMS" );
662 			break;
663 		}
664 		animations[i].numFrames = atoi( token );
665 
666 		token = COM_ParseExt( &text_p, qfalse );
667 		if ( !token[0] ) {
668 			BG_AnimParseError( "end of file without ENDANIMS: line %i", COM_GetCurrentParseLine() + 1 );
669 			break;
670 		}
671 		animations[i].loopFrames = atoi( token );
672 
673 		token = COM_ParseExt( &text_p, qfalse );
674 		if ( !token[0] ) {
675 			BG_AnimParseError( "end of file without ENDANIMS: line %i", COM_GetCurrentParseLine() + 1 );
676 			break;
677 		}
678 		fps = atof( token );
679 		if ( fps == 0 ) {
680 			fps = 1;
681 		}
682 		animations[i].frameLerp = 1000 / fps;
683 		animations[i].initialLerp = 1000 / fps;
684 
685 		// movespeed
686 		token = COM_ParseExt( &text_p, qfalse );
687 		if ( !token[0] ) {
688 			BG_AnimParseError( "end of file without ENDANIMS" );
689 			break;
690 		}
691 		animations[i].moveSpeed = atoi( token );
692 
693 		// animation blending
694 		oldtext_p = text_p;
695 		token = COM_ParseExt( &text_p, qfalse );    // must be on same line
696 		if ( !token[0] ) {
697 			text_p = oldtext_p;
698 			animations[i].animBlend = 0;
699 		} else {
700 			animations[i].animBlend = atoi( token );
701 		}
702 
703 		// priority
704 		oldtext_p = text_p;
705 		token = COM_ParseExt( &text_p, qfalse );    // must be on same line
706 		if ( !token[0] ) {
707 			text_p = oldtext_p;
708 			// death anims have highest priority
709 			if ( !Q_strncmp( animations[i].name, "death", 5 ) ) {
710 				animations[i].priority = 99;
711 			} else {
712 				animations[i].priority = 0;
713 			}
714 		} else {
715 			animations[i].priority = atoi( token );
716 		}
717 
718 		// calculate the duration
719 		animations[i].duration = animations[i].initialLerp
720 								 + animations[i].frameLerp * animations[i].numFrames
721 								 + animations[i].animBlend;
722 
723 		// get the nameHash
724 		animations[i].nameHash = BG_StringHashValue( animations[i].name );
725 
726 		if ( !Q_strncmp( animations[i].name, "climb", 5 ) ) {
727 			animations[i].flags |= ANIMFL_LADDERANIM;
728 		}
729 		if ( strstr( animations[i].name, "firing" ) ) {
730 			animations[i].flags |= ANIMFL_FIRINGANIM;
731 			animations[i].initialLerp = 40;
732 		}
733 
734 	}
735 
736 	animModelInfo->numAnimations = i;
737 
738 	if ( animModelInfo->version < 2 && i != MAX_ANIMATIONS ) {
739 		BG_AnimParseError( "Incorrect number of animations" );
740 		return qfalse;
741 	}
742 
743 	// check for head anims
744 	token = COM_Parse( &text_p );
745 	if ( token && token[0] ) {
746 		if ( animModelInfo->version < 2 || !Q_stricmp( token, "HEADFRAMES" ) ) {
747 
748 			// read information for each head frame
749 			for ( i = 0 ; i < MAX_HEAD_ANIMS ; i++ ) {
750 
751 				token = COM_Parse( &text_p );
752 				if ( !token[0] ) {
753 					break;
754 				}
755 
756 				if ( animModelInfo->version > 1 ) {   // includes animation names at start of each line
757 					// just throw this information away, not required for head
758 					token = COM_ParseExt( &text_p, qfalse );
759 					if ( !token[0] ) {
760 						break;
761 					}
762 				}
763 
764 				if ( !i ) {
765 					skip = atoi( token );
766 				}
767 
768 				headAnims[i].firstFrame = atoi( token );
769 				// modify according to last frame of the main animations, since the head is totally seperate
770 				headAnims[i].firstFrame -= animations[MAX_ANIMATIONS - 1].firstFrame + animations[MAX_ANIMATIONS - 1].numFrames + skip;
771 
772 				token = COM_ParseExt( &text_p, qfalse );
773 				if ( !token[0] ) {
774 					break;
775 				}
776 				headAnims[i].numFrames = atoi( token );
777 
778 				// skip the movespeed
779 				COM_ParseExt( &text_p, qfalse );
780 			}
781 
782 			animModelInfo->numHeadAnims = i;
783 
784 			if ( i != MAX_HEAD_ANIMS ) {
785 				BG_AnimParseError( "Incorrect number of head frames" );
786 				return qfalse;
787 			}
788 
789 		}
790 	}
791 
792 	return qtrue;
793 }
794 
795 /*
796 =================
797 BG_ParseConditionBits
798 
799   convert the string into a single int containing bit flags, stopping at a ',' or end of line
800 =================
801 */
802 #define RESULT_SIZE 2
BG_ParseConditionBits(char ** text_pp,animStringItem_t * stringTable,int condIndex,int result[RESULT_SIZE])803 void BG_ParseConditionBits( char **text_pp, animStringItem_t *stringTable, int condIndex, int result[RESULT_SIZE] ) {
804 	qboolean endFlag = qfalse;
805 	int indexFound;
806 	int tempBits[2];
807 	char currentString[MAX_QPATH];
808 	qboolean minus = qfalse;
809 	char *token;
810 
811 	currentString[0] = '\0';
812 	memset( result, 0, sizeof( result[0] ) * RESULT_SIZE );
813 	memset( tempBits, 0, sizeof( tempBits ) );
814 
815 	while ( !endFlag ) {
816 
817 		token = COM_ParseExt( text_pp, qfalse );
818 		if ( !token[0] ) {
819 			COM_RestoreParseSession( text_pp ); // go back to the previous token
820 			endFlag = qtrue;    // done parsing indexes
821 			if ( !strlen( currentString ) ) {
822 				break;
823 			}
824 		}
825 
826 		if ( !Q_stricmp( token, "," ) ) {
827 			endFlag = qtrue;    // end of indexes
828 		}
829 
830 		if ( !Q_stricmp( token, "none" ) ) { // first bit is always the "unused" bit
831 			COM_BitSet( result, 0 );
832 			continue;
833 		}
834 
835 		if ( !Q_stricmp( token, "none," ) ) {    // first bit is always the "unused" bit
836 			COM_BitSet( result, 0 );
837 			endFlag = qtrue;    // end of indexes
838 			continue;
839 		}
840 
841 		if ( !Q_stricmp( token, "NOT" ) ) {
842 			token = "MINUS"; // NOT is equivalent to MINUS
843 		}
844 
845 		if ( !endFlag && Q_stricmp( token, "AND" ) && Q_stricmp( token, "MINUS" ) ) { // must be a index
846 			// check for a comma (end of indexes)
847 			if ( token[strlen( token ) - 1] == ',' ) {
848 				endFlag = qtrue;
849 				token[strlen( token ) - 1] = '\0';
850 			}
851 			// append this to the currentString
852 			if ( strlen( currentString ) ) {
853 				Q_strcat( currentString, sizeof( currentString ), " " );
854 			}
855 			Q_strcat( currentString, sizeof( currentString ), token );
856 		}
857 
858 		if ( !Q_stricmp( token, "AND" ) || !Q_stricmp( token, "MINUS" ) || endFlag ) {
859 			// process the currentString
860 			if ( !strlen( currentString ) ) {
861 				if ( endFlag ) {
862 					BG_AnimParseError( "BG_AnimParseAnimScript: unexpected end of condition" );
863 				} else {
864 					// check for minus indexes to follow
865 					if ( !Q_stricmp( token, "MINUS" ) ) {
866 						minus = qtrue;
867 						continue;
868 					}
869 					BG_AnimParseError( "BG_AnimParseAnimScript: unexpected '%s'", token );
870 				}
871 			}
872 			if ( !Q_stricmp( currentString, "all" ) ) {
873 				tempBits[0] = ~0x0;
874 				tempBits[1] = ~0x0;
875 			} else {
876 				// first check this string with our defines
877 				indexFound = BG_IndexForString( currentString, defineStr[condIndex], qtrue );
878 				if ( indexFound >= 0 ) {
879 					// we have precalculated the bitflags for the defines
880 					tempBits[0] = defineBits[condIndex][indexFound][0];
881 					tempBits[1] = defineBits[condIndex][indexFound][1];
882 				} else {
883 					// convert the string into an index
884 					indexFound = BG_IndexForString( currentString, stringTable, qfalse );
885 					// convert the index into a bitflag
886 					COM_BitSet( tempBits, indexFound );
887 				}
888 			}
889 			// perform operation
890 			if ( minus ) {    // subtract
891 				result[0] &= ~tempBits[0];
892 				result[1] &= ~tempBits[1];
893 			} else {        // add
894 				result[0] |= tempBits[0];
895 				result[1] |= tempBits[1];
896 			}
897 			// clear the currentString
898 			currentString[0] = '\0';
899 			// check for minus indexes to follow
900 			if ( !Q_stricmp( token, "MINUS" ) ) {
901 				minus = qtrue;
902 			}
903 		}
904 
905 	}
906 }
907 
908 /*
909 =================
910 BG_ParseConditions
911 
912   returns qtrue if everything went ok, error drops otherwise
913 =================
914 */
BG_ParseConditions(char ** text_pp,animScriptItem_t * scriptItem)915 qboolean BG_ParseConditions( char **text_pp, animScriptItem_t *scriptItem ) {
916 	int conditionIndex, conditionValue[2];
917 	char    *token;
918 
919 	conditionValue[0] = 0;
920 	conditionValue[1] = 0;
921 
922 	while ( 1 ) {
923 
924 		token = COM_ParseExt( text_pp, qfalse );
925 		if ( !token[0] ) {
926 			break;
927 		}
928 
929 		// special case, "default" has no conditions
930 		if ( !Q_stricmp( token, "default" ) ) {
931 			return qtrue;
932 		}
933 
934 		conditionIndex = BG_IndexForString( token, animConditionsStr, qfalse );
935 
936 		switch ( animConditionsTable[conditionIndex].type ) {
937 		case ANIM_CONDTYPE_BITFLAGS:
938 			BG_ParseConditionBits( text_pp, animConditionsTable[conditionIndex].values, conditionIndex, conditionValue );
939 			break;
940 		case ANIM_CONDTYPE_VALUE:
941 			if ( animConditionsTable[conditionIndex].values ) {
942 				token = COM_ParseExt( text_pp, qfalse );
943 				if ( !token[0] ) {
944 					BG_AnimParseError( "BG_AnimParseAnimScript: expected condition value, found end of line" );  // RF modification
945 					break;
946 				}
947 				// check for a comma (condition divider)
948 				if ( token[strlen( token ) - 1] == ',' ) {
949 					token[strlen( token ) - 1] = '\0';
950 				}
951 				conditionValue[0] = BG_IndexForString( token, animConditionsTable[conditionIndex].values, qfalse );
952 			} else {
953 				conditionValue[0] = 1;  // not used, just check for a positive condition
954 			}
955 			break;
956 		default:
957 			break; // TTimo NUM_ANIM_CONDTYPES
958 		}
959 
960 		// now append this condition to the item
961 		scriptItem->conditions[scriptItem->numConditions].index = conditionIndex;
962 		scriptItem->conditions[scriptItem->numConditions].value[0] = conditionValue[0];
963 		scriptItem->conditions[scriptItem->numConditions].value[1] = conditionValue[1];
964 		scriptItem->numConditions++;
965 	}
966 
967 	if ( scriptItem->numConditions == 0 ) {
968 		BG_AnimParseError( "BG_ParseConditions: no conditions found" );  // RF mod
969 	}
970 
971 	return qtrue;
972 }
973 
974 /*
975 =================
976 BG_ParseCommands
977 =================
978 */
BG_ParseCommands(char ** input,animScriptItem_t * scriptItem,animModelInfo_t * modelInfo,animScriptData_t * scriptData)979 void BG_ParseCommands( char **input, animScriptItem_t *scriptItem, animModelInfo_t *modelInfo, animScriptData_t *scriptData ) {
980 	char    *token;
981 	animScriptCommand_t *command = NULL; // TTimo: init
982 	int partIndex = 0;
983 
984 	globalScriptData = scriptData;
985 	while ( 1 ) {
986 
987 		// parse the body part
988 		token = COM_ParseExt( input, ( partIndex < 1 ) );
989 		if ( !token[0] ) {
990 			break;
991 		}
992 		if ( !Q_stricmp( token, "}" ) ) {
993 			// unget the bracket and get out of here
994 			*input -= strlen( token );
995 			break;
996 		}
997 
998 		// new command?
999 		if ( partIndex == 0 ) {
1000 			// have we exceeded the maximum number of commands?
1001 			if ( scriptItem->numCommands >= MAX_ANIMSCRIPT_ANIMCOMMANDS ) {
1002 				BG_AnimParseError( "BG_ParseCommands: exceeded maximum number of animations (%i)", MAX_ANIMSCRIPT_ANIMCOMMANDS );
1003 			}
1004 			command = &scriptItem->commands[scriptItem->numCommands++];
1005 			memset( command, 0, sizeof( *command ) );
1006 		}
1007 
1008 		command->bodyPart[partIndex] = BG_IndexForString( token, animBodyPartsStr, qtrue );
1009 		if ( command->bodyPart[partIndex] > 0 ) {
1010 			// parse the animation
1011 			token = COM_ParseExt( input, qfalse );
1012 			if ( !token[0] ) {
1013 				BG_AnimParseError( "BG_ParseCommands: expected animation" );
1014 			}
1015 			command->animIndex[partIndex] = BG_AnimationIndexForString( token, parseClient );
1016 			command->animDuration[partIndex] = modelInfo->animations[command->animIndex[partIndex]].duration;
1017 			// if this is a locomotion, set the movetype of the animation so we can reverse engineer the movetype from the animation, on the client
1018 			if ( parseMovetype != ANIM_MT_UNUSED && command->bodyPart[partIndex] != ANIM_BP_TORSO ) {
1019 				modelInfo->animations[command->animIndex[partIndex]].movetype |= ( 1 << parseMovetype );
1020 			}
1021 			// if this is a fireweapon event, then this is a firing animation
1022 			if ( parseEvent == ANIM_ET_FIREWEAPON ) {
1023 				modelInfo->animations[command->animIndex[partIndex]].flags |= ANIMFL_FIRINGANIM;
1024 				modelInfo->animations[command->animIndex[partIndex]].initialLerp = 40;
1025 			}
1026 			// check for a duration for this animation instance
1027 			token = COM_ParseExt( input, qfalse );
1028 			if ( token && token[0] ) {
1029 				if ( !Q_stricmp( token, "duration" ) ) {
1030 					// read the duration
1031 					token = COM_ParseExt( input, qfalse );
1032 					if ( !token[0] ) {
1033 						BG_AnimParseError( "BG_ParseCommands: expected duration value" );
1034 						break;
1035 					}
1036 					command->animDuration[partIndex] = atoi( token );
1037 				} else {    // unget the token
1038 					COM_RestoreParseSession( input );
1039 				}
1040 			} else {
1041 				COM_RestoreParseSession( input );
1042 			}
1043 
1044 			if ( command->bodyPart[partIndex] != ANIM_BP_BOTH && partIndex++ < 1 ) {
1045 				continue;   // allow parsing of another bodypart
1046 			}
1047 		} else {
1048 			// unget the token
1049 			*input -= strlen( token );
1050 		}
1051 
1052 		// parse optional parameters (sounds, etc)
1053 		while ( 1 ) {
1054 			token = COM_ParseExt( input, qfalse );
1055 			if ( !token[0] ) {
1056 				break;
1057 			}
1058 
1059 			if ( !Q_stricmp( token, "sound" ) ) {
1060 
1061 				token = COM_ParseExt( input, qfalse );
1062 				if ( !token[0] ) {
1063 					BG_AnimParseError( "BG_ParseCommands: expected sound" );
1064 					break;
1065 				}
1066 				// NOTE: only sound script are supported at this stage
1067 				if ( strstr( token, ".wav" ) ) {
1068 					BG_AnimParseError( "BG_ParseCommands: wav files not supported, only sound scripts" );    // RF mod
1069 				}
1070 				command->soundIndex = globalScriptData->soundIndex( token );
1071 
1072 //----(SA)	added
1073 			} else if ( !Q_stricmp( token, "showpart" ) ) {    // show
1074 				token = COM_ParseExt( input, qfalse );
1075 				if ( !token[0] ) {
1076 					BG_AnimParseError( "BG_ParseCommands: expected showpart number" );
1077 					break;
1078 				}
1079 				if ( atoi( token ) > 7 ) {
1080 					BG_AnimParseError( "BG_ParseCommands: showpart number '%d' is too big! (max 8)", atoi( token ) ) ;
1081 				}
1082 
1083 				command->accShowBits &= atoi( token );
1084 			} else if ( !Q_stricmp( token, "hidepart" ) ) {
1085 				token = COM_ParseExt( input, qfalse );
1086 				if ( !token[0] ) {
1087 					BG_AnimParseError( "BG_ParseCommands: expected hidepart number" );
1088 					break;
1089 				}
1090 				if ( atoi( token ) > 7 ) {
1091 					BG_AnimParseError( "BG_ParseCommands: hidepart number '%d' is too big! (max 8)", atoi( token ) ) ;
1092 				}
1093 
1094 				command->accHideBits &= atoi( token );
1095 //----(SA)	end
1096 			} else {
1097 				// unknown??
1098 				BG_AnimParseError( "BG_ParseCommands: unknown parameter '%s'", token );
1099 			}
1100 		}
1101 
1102 		partIndex = 0;
1103 	}
1104 }
1105 
1106 /*
1107 =================
1108 BG_AnimParseAnimScript
1109 
1110   Parse the animation script for this model, converting it into run-time structures
1111 =================
1112 */
1113 
1114 typedef enum
1115 {
1116 	PARSEMODE_DEFINES,
1117 	PARSEMODE_ANIMATION,
1118 	PARSEMODE_CANNED_ANIMATIONS,
1119 	PARSEMODE_STATECHANGES,
1120 	PARSEMODE_EVENTS
1121 } animScriptParseMode_t;
1122 
1123 static animStringItem_t animParseModesStr[] =
1124 {
1125 	{"defines", -1},
1126 	{"animations", -1},
1127 	{"canned_animations", -1},
1128 	{"statechanges", -1},
1129 	{"events", -1},
1130 
1131 	{NULL, -1},
1132 };
1133 
BG_AnimParseAnimScript(animModelInfo_t * modelInfo,animScriptData_t * scriptData,int client,char * filename,char * input)1134 void BG_AnimParseAnimScript( animModelInfo_t *modelInfo, animScriptData_t *scriptData, int client, char *filename, char *input ) {
1135 	#define MAX_INDENT_LEVELS   3
1136 
1137 	char    *text_p, *token;
1138 	animScriptParseMode_t parseMode;
1139 	animScript_t        *currentScript;
1140 	animScriptItem_t tempScriptItem, *currentScriptItem = NULL;    // TTimo: init
1141 	int indexes[MAX_INDENT_LEVELS], indentLevel, oldState, newParseMode;
1142 	int i, defineType;
1143 
1144 	// the scriptData passed into here must be the one this binary is using
1145 	globalScriptData = scriptData;
1146 
1147 	// current client being parsed
1148 	parseClient = client;
1149 
1150 	// start at the defines
1151 	parseMode = PARSEMODE_DEFINES;
1152 
1153 	// record which modelInfo this client is using
1154 	// Duffy
1155 	// This is done in each of the calling routines, and assumes all sorts of badness doing it this way anyway
1156 	//scriptData->clientModels[client] = 1 + (int)(modelInfo - *scriptData->modelInfo);
1157 
1158 	// init the global defines
1159 	globalFilename = filename;
1160 	memset( defineStr, 0, sizeof( defineStr ) );
1161 	memset( defineStrings, 0, sizeof( defineStrings ) );
1162 	memset( numDefines, 0, sizeof( numDefines ) );
1163 	defineStringsOffset = 0;
1164 
1165 	for ( i = 0; i < MAX_INDENT_LEVELS; i++ )
1166 		indexes[i] = -1;
1167 	indentLevel = 0;
1168 	currentScript = NULL;
1169 
1170 	text_p = input;
1171 	COM_BeginParseSession( "BG_AnimParseAnimScript" );
1172 
1173 	// read in the weapon defines
1174 	while ( 1 ) {
1175 
1176 		token = COM_Parse( &text_p );
1177 		if ( !token[0] ) {
1178 			if ( indentLevel ) {
1179 				BG_AnimParseError( "BG_AnimParseAnimScript: unexpected end of file: %s", token );
1180 			}
1181 			break;
1182 		}
1183 
1184 		// check for a new section
1185 		newParseMode = BG_IndexForString( token, animParseModesStr, qtrue );
1186 		if ( newParseMode >= 0 ) {
1187 			if ( indentLevel ) {
1188 				BG_AnimParseError( "BG_AnimParseAnimScript: unexpected '%s'", token );   // RF mod
1189 			}
1190 
1191 			parseMode = newParseMode;
1192 			parseMovetype = ANIM_MT_UNUSED;
1193 			parseEvent = -1;
1194 			continue;
1195 		}
1196 
1197 		switch ( parseMode ) {
1198 
1199 		case PARSEMODE_DEFINES:
1200 
1201 			if ( !Q_stricmp( token, "set" ) ) {
1202 
1203 				// read in the define type
1204 				token = COM_ParseExt( &text_p, qfalse );
1205 				if ( !token[0] ) {
1206 					BG_AnimParseError( "BG_AnimParseAnimScript: expected condition type string" );   // RF mod
1207 				}
1208 				defineType = BG_IndexForString( token, animConditionsStr, qfalse );
1209 
1210 				// read in the define
1211 				token = COM_ParseExt( &text_p, qfalse );
1212 				if ( !token[0] ) {
1213 					BG_AnimParseError( "BG_AnimParseAnimScript: expected condition define string" ); // RF mod
1214 				}
1215 
1216 				// copy the define to the strings list
1217 				defineStr[defineType][numDefines[defineType]].string = BG_CopyStringIntoBuffer( token, defineStrings, sizeof( defineStrings ), &defineStringsOffset );
1218 				defineStr[defineType][numDefines[defineType]].hash = BG_StringHashValue( defineStr[defineType][numDefines[defineType]].string );
1219 				// expecting an =
1220 				token = COM_ParseExt( &text_p, qfalse );
1221 				if ( !token[0] ) {
1222 					BG_AnimParseError( "BG_AnimParseAnimScript: expected '=', found end of line" );  // RF mod
1223 				}
1224 				if ( Q_stricmp( token, "=" ) ) {
1225 					BG_AnimParseError( "BG_AnimParseAnimScript: expected '=', found '%s'", token );  // RF mod
1226 				}
1227 
1228 				// parse the bits
1229 				BG_ParseConditionBits( &text_p, animConditionsTable[defineType].values, defineType, defineBits[defineType][numDefines[defineType]] );
1230 				numDefines[defineType]++;
1231 
1232 				// copy the weapon defines over to the enemy_weapon defines
1233 				memcpy( &defineStr[ANIM_COND_ENEMY_WEAPON][0], &defineStr[ANIM_COND_WEAPON][0], sizeof( animStringItem_t ) * MAX_ANIM_DEFINES );
1234 				memcpy( &defineBits[ANIM_COND_ENEMY_WEAPON][0], &defineBits[ANIM_COND_WEAPON][0], sizeof( defineBits[ANIM_COND_ENEMY_WEAPON][0] ) * MAX_ANIM_DEFINES );
1235 				numDefines[ANIM_COND_ENEMY_WEAPON] = numDefines[ANIM_COND_WEAPON];
1236 
1237 			}
1238 
1239 			break;
1240 
1241 		case PARSEMODE_ANIMATION:
1242 		case PARSEMODE_CANNED_ANIMATIONS:
1243 
1244 			if ( !Q_stricmp( token, "{" ) ) {
1245 
1246 				// about to increment indent level, check that we have enough information to do this
1247 				if ( indentLevel >= MAX_INDENT_LEVELS ) { // too many indentations
1248 					BG_AnimParseError( "BG_AnimParseAnimScript: unexpected '%s'", token );   // RF mod
1249 				}
1250 				if ( indexes[indentLevel] < 0 ) {     // we havent found out what this new group is yet
1251 					BG_AnimParseError( "BG_AnimParseAnimScript: unexpected '%s'", token );   // RF mod
1252 				}
1253 				//
1254 				indentLevel++;
1255 
1256 			} else if ( !Q_stricmp( token, "}" ) ) {
1257 
1258 				// reduce the indentLevel
1259 				indentLevel--;
1260 				if ( indentLevel < 0 ) {
1261 					BG_AnimParseError( "BG_AnimParseAnimScript: unexpected '%s'", token );   // RF mod
1262 				}
1263 				if ( indentLevel == 1 ) {
1264 					currentScript = NULL;
1265 				}
1266 				// make sure we read a new index before next indent
1267 				indexes[indentLevel] = -1;
1268 
1269 			} else if ( indentLevel == 0 && ( indexes[indentLevel] < 0 ) ) {
1270 
1271 				if ( Q_stricmp( token, "state" ) ) {
1272 					BG_AnimParseError( "BG_AnimParseAnimScript: expected 'state'" ); // RF mod
1273 				}
1274 
1275 				// read in the state type
1276 				token = COM_ParseExt( &text_p, qfalse );
1277 				if ( !token[0] ) {
1278 					BG_AnimParseError( "BG_AnimParseAnimScript: expected state type" );  // RF mod
1279 				}
1280 				indexes[indentLevel] = BG_IndexForString( token, animStateStr, qfalse );
1281 
1282 //----(SA) // RF mod
1283 				// check for the open bracket
1284 				token = COM_ParseExt( &text_p, qtrue );
1285 				if ( !token[0] || Q_stricmp( token, "{" ) ) {
1286 					BG_AnimParseError( "BG_AnimParseAnimScript: expected '{'" );
1287 				}
1288 				indentLevel++;
1289 //----(SA) // RF mod
1290 			} else if ( ( indentLevel == 1 ) && ( indexes[indentLevel] < 0 ) ) {
1291 
1292 				// we are expecting a movement type
1293 				indexes[indentLevel] = BG_IndexForString( token, animMoveTypesStr, qfalse );
1294 				if ( parseMode == PARSEMODE_ANIMATION ) {
1295 					currentScript = &modelInfo->scriptAnims[indexes[0]][indexes[1]];
1296 					parseMovetype = indexes[1];
1297 				} else if ( parseMode == PARSEMODE_CANNED_ANIMATIONS ) {
1298 					currentScript = &modelInfo->scriptCannedAnims[indexes[0]][indexes[1]];
1299 				}
1300 				memset( currentScript, 0, sizeof( *currentScript ) );
1301 
1302 			} else if ( ( indentLevel == 2 ) && ( indexes[indentLevel] < 0 ) ) {
1303 
1304 				// we are expecting a condition specifier
1305 				// move the text_p backwards so we can read in the last token again
1306 				text_p -= strlen( token );
1307 				// sanity check that
1308 				if ( Q_strncmp( text_p, token, strlen( token ) ) ) {
1309 					// this should never happen, just here to check that this operation is correct before code goes live
1310 					BG_AnimParseError( "BG_AnimParseAnimScript: internal error" );
1311 				}
1312 				//
1313 				memset( &tempScriptItem, 0, sizeof( tempScriptItem ) );
1314 				indexes[indentLevel] = BG_ParseConditions( &text_p, &tempScriptItem );
1315 				// do we have enough room in this script for another item?
1316 				if ( currentScript->numItems >= MAX_ANIMSCRIPT_ITEMS ) {
1317 					BG_AnimParseError( "BG_AnimParseAnimScript: exceeded maximum items per script (%i)", MAX_ANIMSCRIPT_ITEMS ); // RF mod
1318 				}
1319 				// are there enough items left in the global list?
1320 				if ( modelInfo->numScriptItems >= MAX_ANIMSCRIPT_ITEMS_PER_MODEL ) {
1321 					BG_AnimParseError( "BG_AnimParseAnimScript: exceeded maximum global items (%i)", MAX_ANIMSCRIPT_ITEMS_PER_MODEL );   // RF mod
1322 				}
1323 				// it was parsed ok, so grab an item from the global list to use
1324 				currentScript->items[currentScript->numItems] = &modelInfo->scriptItems[ modelInfo->numScriptItems++ ];
1325 				currentScriptItem = currentScript->items[currentScript->numItems];
1326 				currentScript->numItems++;
1327 				// copy the data across from the temp script item
1328 				*currentScriptItem = tempScriptItem;
1329 
1330 			} else if ( indentLevel == 3 ) {
1331 
1332 				// we are reading the commands, so parse this line as if it were a command
1333 				// move the text_p backwards so we can read in the last token again
1334 				text_p -= strlen( token );
1335 				// sanity check that
1336 				if ( Q_strncmp( text_p, token, strlen( token ) ) ) {
1337 					// this should never happen, just here to check that this operation is correct before code goes live
1338 					BG_AnimParseError( "BG_AnimParseAnimScript: internal error" );
1339 				}
1340 				//
1341 				BG_ParseCommands( &text_p, currentScriptItem, modelInfo, scriptData );
1342 
1343 			} else {
1344 
1345 				// huh ??
1346 				BG_AnimParseError( "BG_AnimParseAnimScript: unexpected '%s'", token );   // RF mod
1347 
1348 			}
1349 
1350 			break;
1351 
1352 		case PARSEMODE_STATECHANGES:
1353 		case PARSEMODE_EVENTS:
1354 
1355 			if ( !Q_stricmp( token, "{" ) ) {
1356 
1357 				// about to increment indent level, check that we have enough information to do this
1358 				if ( indentLevel >= MAX_INDENT_LEVELS ) { // too many indentations
1359 					BG_AnimParseError( "BG_AnimParseAnimScript: unexpected '%s'", token );   // RF mod
1360 				}
1361 				if ( indexes[indentLevel] < 0 ) {     // we havent found out what this new group is yet
1362 					BG_AnimParseError( "BG_AnimParseAnimScript: unexpected '%s'", token );   // RF mod
1363 				}
1364 				//
1365 				indentLevel++;
1366 
1367 			} else if ( !Q_stricmp( token, "}" ) ) {
1368 
1369 				// reduce the indentLevel
1370 				indentLevel--;
1371 				if ( indentLevel < 0 ) {
1372 					BG_AnimParseError( "BG_AnimParseAnimScript: unexpected '%s'", token );   // RF mod
1373 				}
1374 				if ( indentLevel == 0 ) {
1375 					currentScript = NULL;
1376 				}
1377 				// make sure we read a new index before next indent
1378 				indexes[indentLevel] = -1;
1379 
1380 			} else if ( indentLevel == 0 && ( indexes[indentLevel] < 0 ) ) {
1381 
1382 				if ( parseMode == PARSEMODE_STATECHANGES ) {
1383 
1384 					if ( Q_stricmp( token, "statechange" ) ) {
1385 						BG_AnimParseError( "BG_AnimParseAnimScript: expected 'statechange', got '%s'", token );  // RF mod
1386 					}
1387 
1388 					// read in the old state type
1389 					token = COM_ParseExt( &text_p, qfalse );
1390 					if ( !token[0] ) {
1391 						BG_AnimParseError( "BG_AnimParseAnimScript: expected <state type>" );    // RF mod
1392 					}
1393 					oldState = BG_IndexForString( token, animStateStr, qfalse );
1394 
1395 					// read in the new state type
1396 					token = COM_ParseExt( &text_p, qfalse );
1397 					if ( !token[0] ) {
1398 						BG_AnimParseError( "BG_AnimParseAnimScript: expected <state type>" );    // RF mod
1399 					}
1400 					indexes[indentLevel] = BG_IndexForString( token, animStateStr, qfalse );
1401 
1402 					currentScript = &modelInfo->scriptStateChange[oldState][indexes[indentLevel]];
1403 
1404 //----(SA)		// RF mod
1405 					// check for the open bracket
1406 					token = COM_ParseExt( &text_p, qtrue );
1407 					if ( !token[0] || Q_stricmp( token, "{" ) ) {
1408 						BG_AnimParseError( "BG_AnimParseAnimScript: expected '{'" );
1409 					}
1410 					indentLevel++;
1411 //----(SA)		// RF mod
1412 				} else {
1413 
1414 					// read in the event type
1415 					indexes[indentLevel] = BG_IndexForString( token, animEventTypesStr, qfalse );
1416 					currentScript = &modelInfo->scriptEvents[indexes[0]];
1417 
1418 					parseEvent = indexes[indentLevel];
1419 
1420 				}
1421 
1422 				memset( currentScript, 0, sizeof( *currentScript ) );
1423 
1424 			} else if ( ( indentLevel == 1 ) && ( indexes[indentLevel] < 0 ) ) {
1425 
1426 				// we are expecting a condition specifier
1427 				// move the text_p backwards so we can read in the last token again
1428 				text_p -= strlen( token );
1429 				// sanity check that
1430 				if ( Q_strncmp( text_p, token, strlen( token ) ) ) {
1431 					// this should never happen, just here to check that this operation is correct before code goes live
1432 					BG_AnimParseError( "BG_AnimParseAnimScript: internal error" );
1433 				}
1434 				//
1435 				memset( &tempScriptItem, 0, sizeof( tempScriptItem ) );
1436 				indexes[indentLevel] = BG_ParseConditions( &text_p, &tempScriptItem );
1437 				// do we have enough room in this script for another item?
1438 				if ( currentScript->numItems >= MAX_ANIMSCRIPT_ITEMS ) {
1439 					BG_AnimParseError( "BG_AnimParseAnimScript: exceeded maximum items per script (%i)", MAX_ANIMSCRIPT_ITEMS ); // RF mod
1440 				}
1441 				// are there enough items left in the global list?
1442 				if ( modelInfo->numScriptItems >= MAX_ANIMSCRIPT_ITEMS_PER_MODEL ) {
1443 					BG_AnimParseError( "BG_AnimParseAnimScript: exceeded maximum global items (%i)", MAX_ANIMSCRIPT_ITEMS_PER_MODEL );   // RF mod
1444 				}
1445 				// it was parsed ok, so grab an item from the global list to use
1446 				currentScript->items[currentScript->numItems] = &modelInfo->scriptItems[ modelInfo->numScriptItems++ ];
1447 				currentScriptItem = currentScript->items[currentScript->numItems];
1448 				currentScript->numItems++;
1449 				// copy the data across from the temp script item
1450 				*currentScriptItem = tempScriptItem;
1451 
1452 			} else if ( indentLevel == 2 ) {
1453 
1454 				// we are reading the commands, so parse this line as if it were a command
1455 				// move the text_p backwards so we can read in the last token again
1456 				text_p -= strlen( token );
1457 				// sanity check that
1458 				if ( Q_strncmp( text_p, token, strlen( token ) ) ) {
1459 					// this should never happen, just here to check that this operation is correct before code goes live
1460 					BG_AnimParseError( "BG_AnimParseAnimScript: internal error" );
1461 				}
1462 				//
1463 				BG_ParseCommands( &text_p, currentScriptItem, modelInfo, scriptData );
1464 
1465 			} else {
1466 
1467 				// huh ??
1468 				BG_AnimParseError( "BG_AnimParseAnimScript: unexpected '%s'", token );   // RF mod
1469 
1470 			}
1471 
1472 		}
1473 
1474 	}
1475 
1476 	globalFilename = NULL;
1477 
1478 }
1479 
1480 //------------------------------------------------------------------------
1481 //
1482 // run-time gameplay functions, these are called during gameplay, so they must be
1483 // cpu efficient.
1484 //
1485 
1486 /*
1487 ===============
1488 BG_EvaluateConditions
1489 
1490   returns qfalse if the set of conditions fails, qtrue otherwise
1491 ===============
1492 */
BG_EvaluateConditions(int client,animScriptItem_t * scriptItem)1493 qboolean BG_EvaluateConditions( int client, animScriptItem_t *scriptItem ) {
1494 	int i;
1495 	animScriptCondition_t *cond;
1496 
1497 	for ( i = 0, cond = scriptItem->conditions; i < scriptItem->numConditions; i++, cond++ )
1498 	{
1499 		switch ( animConditionsTable[cond->index].type ) {
1500 		case ANIM_CONDTYPE_BITFLAGS:
1501 			if ( !( globalScriptData->clientConditions[client][cond->index][0] & cond->value[0] ) &&
1502 				 !( globalScriptData->clientConditions[client][cond->index][1] & cond->value[1] ) ) {
1503 				return qfalse;
1504 			}
1505 			break;
1506 		case ANIM_CONDTYPE_VALUE:
1507 			if ( !( globalScriptData->clientConditions[client][cond->index][0] == cond->value[0] ) ) {
1508 				return qfalse;
1509 			}
1510 			break;
1511 		default:
1512 			break; // TTimo: NUM_ANIM_CONDTYPES
1513 		}
1514 	}
1515 	//
1516 	// all conditions must have passed
1517 	return qtrue;
1518 }
1519 
1520 /*
1521 ===============
1522 BG_FirstValidItem
1523 
1524   scroll through the script items, returning the first script found to pass all conditions
1525 
1526   returns NULL if no match found
1527 ===============
1528 */
BG_FirstValidItem(int client,animScript_t * script)1529 animScriptItem_t *BG_FirstValidItem( int client, animScript_t *script ) {
1530 	animScriptItem_t **ppScriptItem;
1531 
1532 	int i;
1533 
1534 	for ( i = 0, ppScriptItem = script->items; i < script->numItems; i++, ppScriptItem++ )
1535 	{
1536 		if ( BG_EvaluateConditions( client, *ppScriptItem ) ) {
1537 			return *ppScriptItem;
1538 		}
1539 	}
1540 	//
1541 	return NULL;
1542 }
1543 
1544 /*
1545 ===============
1546 BG_PlayAnim
1547 ===============
1548 */
BG_PlayAnim(playerState_t * ps,int animNum,animBodyPart_t bodyPart,int forceDuration,qboolean setTimer,qboolean isContinue,qboolean force)1549 int BG_PlayAnim( playerState_t *ps, int animNum, animBodyPart_t bodyPart, int forceDuration, qboolean setTimer, qboolean isContinue, qboolean force ) {
1550 	int duration;
1551 	qboolean wasSet = qfalse;
1552 	animModelInfo_t *modelInfo;
1553 	animation_t *oldAnim, *newAnim;
1554 	#define TIMER_PADDING   150
1555 
1556 	modelInfo = BG_ModelInfoForClient( ps->clientNum );
1557 
1558 	if ( forceDuration ) {
1559 		duration = forceDuration;
1560 	} else {
1561 		duration = modelInfo->animations[animNum].duration; // account for lerping between anims
1562 	}
1563 
1564 	switch ( bodyPart ) {
1565 	case ANIM_BP_BOTH:
1566 	case ANIM_BP_LEGS:
1567 
1568 		oldAnim = &modelInfo->animations[( ps->legsAnim & ~ANIM_TOGGLEBIT )];
1569 		newAnim = &modelInfo->animations[animNum];
1570 
1571 		if ( ( ps->legsTimer < 50 ) || ( force && ( newAnim->priority >= oldAnim->priority ) ) ) {
1572 			if ( !isContinue || !( ( ps->legsAnim & ~ANIM_TOGGLEBIT ) == animNum ) ) {
1573 				wasSet = qtrue;
1574 				ps->legsAnim = ( ( ps->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | animNum;
1575 				if ( setTimer ) {
1576 					ps->legsTimer = duration + TIMER_PADDING;
1577 				}
1578 			} else if ( setTimer && modelInfo->animations[animNum].loopFrames ) {
1579 				ps->legsTimer = duration + TIMER_PADDING;
1580 			}
1581 		}
1582 
1583 		if ( bodyPart == ANIM_BP_LEGS ) {
1584 			break;
1585 		}
1586 
1587 	case ANIM_BP_TORSO:
1588 
1589 		oldAnim = &modelInfo->animations[ps->torsoAnim & ~ANIM_TOGGLEBIT];
1590 		newAnim = &modelInfo->animations[animNum];
1591 
1592 		if ( ( ps->torsoTimer < 50 ) || ( force && ( newAnim->priority >= oldAnim->priority ) ) ) {
1593 			if ( !isContinue || !( ( ps->torsoAnim & ~ANIM_TOGGLEBIT ) == animNum ) ) {
1594 				ps->torsoAnim = ( ( ps->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | animNum;
1595 				if ( setTimer ) {
1596 					ps->torsoTimer = duration + TIMER_PADDING;
1597 				}
1598 			} else if ( setTimer && modelInfo->animations[animNum].loopFrames ) {
1599 				ps->torsoTimer = duration + TIMER_PADDING;
1600 			}
1601 		}
1602 
1603 		break;
1604 	default:
1605 		break; // TTimo:
1606 	}
1607 
1608 	if ( !wasSet ) {
1609 		return -1;
1610 	}
1611 
1612 	return duration;
1613 }
1614 
1615 /*
1616 ===============
1617 BG_PlayAnimName
1618 ===============
1619 */
BG_PlayAnimName(playerState_t * ps,char * animName,animBodyPart_t bodyPart,qboolean setTimer,qboolean isContinue,qboolean force)1620 int BG_PlayAnimName( playerState_t *ps, char *animName, animBodyPart_t bodyPart, qboolean setTimer, qboolean isContinue, qboolean force ) {
1621 	return BG_PlayAnim( ps, BG_AnimationIndexForString( animName, ps->clientNum ), bodyPart, 0, setTimer, isContinue, force );
1622 }
1623 
1624 /*
1625 ===============
1626 BG_ExecuteCommand
1627 
1628   returns the duration of the animation, -1 if no anim was set
1629 ===============
1630 */
BG_ExecuteCommand(playerState_t * ps,animScriptCommand_t * scriptCommand,qboolean setTimer,qboolean isContinue,qboolean force)1631 int BG_ExecuteCommand( playerState_t *ps, animScriptCommand_t *scriptCommand, qboolean setTimer, qboolean isContinue, qboolean force ) {
1632 	int duration = -1;
1633 	qboolean playedLegsAnim = qfalse;
1634 
1635 	if ( scriptCommand->bodyPart[0] ) {
1636 		duration = scriptCommand->animDuration[0] + 50;
1637 		// FIXME: how to sync torso/legs anims accounting for transition blends, etc
1638 		if ( scriptCommand->bodyPart[0] == ANIM_BP_BOTH || scriptCommand->bodyPart[0] == ANIM_BP_LEGS ) {
1639 			playedLegsAnim = ( BG_PlayAnim( ps, scriptCommand->animIndex[0], scriptCommand->bodyPart[0], duration, setTimer, isContinue, force ) > -1 );
1640 		} else {
1641 			BG_PlayAnim( ps, scriptCommand->animIndex[0], scriptCommand->bodyPart[0], duration, setTimer, isContinue, force );
1642 		}
1643 	}
1644 	if ( scriptCommand->bodyPart[1] ) {
1645 		duration = scriptCommand->animDuration[0] + 50;
1646 		// FIXME: how to sync torso/legs anims accounting for transition blends, etc
1647 		// just play the animation for the torso
1648 		if ( scriptCommand->bodyPart[1] == ANIM_BP_BOTH || scriptCommand->bodyPart[1] == ANIM_BP_LEGS ) {
1649 			playedLegsAnim = ( BG_PlayAnim( ps, scriptCommand->animIndex[1], scriptCommand->bodyPart[1], duration, setTimer, isContinue, force ) > -1 );
1650 		} else {
1651 			BG_PlayAnim( ps, scriptCommand->animIndex[1], scriptCommand->bodyPart[1], duration, setTimer, isContinue, force );
1652 		}
1653 	}
1654 
1655 	if ( scriptCommand->soundIndex ) {
1656 		globalScriptData->playSound( scriptCommand->soundIndex, ps->origin, ps->clientNum );
1657 	}
1658 
1659 //----(SA)	added
1660 	ps->accShowBits = scriptCommand->accShowBits;
1661 	ps->accHideBits = scriptCommand->accHideBits;
1662 //----(SA)	end
1663 
1664 	if ( !playedLegsAnim ) {
1665 		return -1;
1666 	}
1667 
1668 	return duration;
1669 }
1670 
1671 /*
1672 ================
1673 BG_AnimScriptAnimation
1674 
1675   runs the normal locomotive animations
1676 
1677   returns 1 if an animation was set, -1 if no animation was found, 0 otherwise
1678 ================
1679 */
BG_AnimScriptAnimation(playerState_t * ps,aistateEnum_t estate,scriptAnimMoveTypes_t movetype,qboolean isContinue)1680 int BG_AnimScriptAnimation( playerState_t *ps, aistateEnum_t estate, scriptAnimMoveTypes_t movetype, qboolean isContinue ) {
1681 	animModelInfo_t     *modelInfo = NULL;
1682 	animScript_t        *script = NULL;
1683 	int state = estate;                                 // enum types are not always signed
1684 	animScriptItem_t    *scriptItem = NULL;
1685 	animScriptCommand_t *scriptCommand = NULL;
1686 
1687 
1688 	if ( ps->eFlags & EF_DEAD ) {
1689 		return -1;
1690 	}
1691 
1692 	modelInfo = BG_ModelInfoForClient( ps->clientNum );
1693 
1694 #ifdef DBGANIMS
1695 	if ( ps->clientNum ) {
1696 		Com_Printf( "script anim: cl %i, mt %s, ", ps->clientNum, animMoveTypesStr[movetype] );
1697 	}
1698 #endif
1699 
1700 	// try finding a match in all states below the given state
1701 	while ( !scriptItem && state >= 0 ) {
1702 		script = &modelInfo->scriptAnims[ state ][ movetype ];
1703 		if ( !script->numItems ) {
1704 			state--;
1705 			continue;
1706 		}
1707 		// find the first script item, that passes all the conditions for this event
1708 		scriptItem = BG_FirstValidItem( ps->clientNum, script );
1709 		if ( !scriptItem ) {
1710 			state--;
1711 			continue;
1712 		}
1713 	}
1714 	//
1715 	if ( !scriptItem ) {
1716 #ifdef DBGANIMS
1717 		if ( ps->clientNum ) {
1718 			Com_Printf( "no valid conditions\n" );
1719 		}
1720 #endif
1721 		return -1;
1722 	}
1723 	// save this as our current movetype
1724 	BG_UpdateConditionValue( ps->clientNum, ANIM_COND_MOVETYPE, movetype, qtrue );
1725 	// pick the correct animation for this character (animations must be constant for each character, otherwise they'll constantly change)
1726 	scriptCommand = &scriptItem->commands[ ps->clientNum % scriptItem->numCommands ];
1727 
1728 #ifdef DBGANIMS
1729 	if ( scriptCommand->bodyPart[0] ) {
1730 		if ( ps->clientNum ) {
1731 			Com_Printf( "anim0 (%s): %s", animBodyPartsStr[scriptCommand->bodyPart[0]].string, modelInfo->animations[scriptCommand->animIndex[0]].name );
1732 		}
1733 	}
1734 	if ( scriptCommand->bodyPart[1] ) {
1735 		if ( ps->clientNum ) {
1736 			Com_Printf( "anim1 (%s): %s", animBodyPartsStr[scriptCommand->bodyPart[1]].string, modelInfo->animations[scriptCommand->animIndex[1]].name );
1737 		}
1738 	}
1739 	if ( ps->clientNum ) {
1740 		Com_Printf( "\n" );
1741 	}
1742 #endif
1743 
1744 	// run it
1745 	return ( BG_ExecuteCommand( ps, scriptCommand, qfalse, isContinue, qfalse ) != -1 );
1746 }
1747 
1748 /*
1749 ================
1750 BG_AnimScriptCannedAnimation
1751 
1752   uses the current movetype for this client to play a canned animation
1753 
1754   returns the duration in milliseconds that this model should be paused. -1 if no anim found
1755 ================
1756 */
BG_AnimScriptCannedAnimation(playerState_t * ps,aistateEnum_t state)1757 int BG_AnimScriptCannedAnimation( playerState_t *ps, aistateEnum_t state ) {
1758 	animModelInfo_t     *modelInfo;
1759 	animScript_t        *script;
1760 	animScriptItem_t    *scriptItem;
1761 	animScriptCommand_t *scriptCommand;
1762 	scriptAnimMoveTypes_t movetype;
1763 
1764 	if ( ps->eFlags & EF_DEAD ) {
1765 		return -1;
1766 	}
1767 
1768 	movetype = globalScriptData->clientConditions[ ps->clientNum ][ ANIM_COND_MOVETYPE ][0];
1769 	if ( !movetype ) {    // no valid movetype yet for this client
1770 		return -1;
1771 	}
1772 	//
1773 	modelInfo = BG_ModelInfoForClient( ps->clientNum );
1774 	script = &modelInfo->scriptCannedAnims[ state ][ movetype ];
1775 	if ( !script->numItems ) {
1776 		return -1;
1777 	}
1778 	// find the first script item, that passes all the conditions for this event
1779 	scriptItem = BG_FirstValidItem( ps->clientNum, script );
1780 	if ( !scriptItem ) {
1781 		return -1;
1782 	}
1783 	// pick a random command
1784 	scriptCommand = &scriptItem->commands[ rand() % scriptItem->numCommands ];
1785 	// run it
1786 	return BG_ExecuteCommand( ps, scriptCommand, qtrue, qfalse, qfalse );
1787 }
1788 
1789 /*
1790 ================
1791 BG_AnimScriptStateChange
1792 
1793   returns the duration in milliseconds that this model should be paused. -1 if no anim found
1794 ================
1795 */
BG_AnimScriptStateChange(playerState_t * ps,aistateEnum_t newState,aistateEnum_t oldState)1796 int BG_AnimScriptStateChange( playerState_t *ps, aistateEnum_t newState, aistateEnum_t oldState ) {
1797 	animModelInfo_t     *modelInfo;
1798 	animScript_t        *script;
1799 	animScriptItem_t    *scriptItem;
1800 	animScriptCommand_t *scriptCommand;
1801 
1802 	if ( ps->eFlags & EF_DEAD ) {
1803 		return -1;
1804 	}
1805 
1806 	modelInfo = BG_ModelInfoForClient( ps->clientNum );
1807 	script = &modelInfo->scriptStateChange[ oldState ][ newState ];
1808 	if ( !script->numItems ) {
1809 		return -1;
1810 	}
1811 	// find the first script item, that passes all the conditions for this event
1812 	scriptItem = BG_FirstValidItem( ps->clientNum, script );
1813 	if ( !scriptItem ) {
1814 		return -1;
1815 	}
1816 	// pick a random command
1817 	scriptCommand = &scriptItem->commands[ rand() % scriptItem->numCommands ];
1818 	// run it
1819 	return BG_ExecuteCommand( ps, scriptCommand, qtrue, qfalse, qfalse );
1820 }
1821 
1822 /*
1823 ================
1824 BG_AnimScriptEvent
1825 
1826   returns the duration in milliseconds that this model should be paused. -1 if no event found
1827 ================
1828 */
BG_AnimScriptEvent(playerState_t * ps,scriptAnimEventTypes_t event,qboolean isContinue,qboolean force)1829 int BG_AnimScriptEvent( playerState_t *ps, scriptAnimEventTypes_t event, qboolean isContinue, qboolean force ) {
1830 	animModelInfo_t     *modelInfo;
1831 	animScript_t        *script;
1832 	animScriptItem_t    *scriptItem;
1833 	animScriptCommand_t *scriptCommand;
1834 
1835 	if ( event != ANIM_ET_DEATH && ps->eFlags & EF_DEAD ) {
1836 		return -1;
1837 	}
1838 
1839 #ifdef DBGANIMEVENTS
1840 	Com_Printf( "script event: cl %i, ev %s, ", ps->clientNum, animEventTypesStr[event] );
1841 #endif
1842 
1843 	modelInfo = BG_ModelInfoForClient( ps->clientNum );
1844 	script = &modelInfo->scriptEvents[ event ];
1845 	if ( !script->numItems ) {
1846 #ifdef DBGANIMEVENTS
1847 		Com_Printf( "no entry\n" );
1848 #endif
1849 		return -1;
1850 	}
1851 	// find the first script item, that passes all the conditions for this event
1852 	scriptItem = BG_FirstValidItem( ps->clientNum, script );
1853 	if ( !scriptItem ) {
1854 #ifdef DBGANIMEVENTS
1855 		Com_Printf( "no valid conditions\n" );
1856 #endif
1857 		return -1;
1858 	}
1859 	//
1860 	// if no command, dont do anything
1861 	if ( !scriptItem->numCommands ) {
1862 		return -1;
1863 	}
1864 	//
1865 	// pick a random command
1866 	scriptCommand = &scriptItem->commands[ rand() % scriptItem->numCommands ];
1867 
1868 #ifdef DBGANIMEVENTS
1869 	if ( scriptCommand->bodyPart[0] ) {
1870 		Com_Printf( "anim0 (%s): %s", animBodyPartsStr[scriptCommand->bodyPart[0]].string, modelInfo->animations[scriptCommand->animIndex[0]].name );
1871 	}
1872 	if ( scriptCommand->bodyPart[1] ) {
1873 		Com_Printf( "anim1 (%s): %s", animBodyPartsStr[scriptCommand->bodyPart[1]].string, modelInfo->animations[scriptCommand->animIndex[1]].name );
1874 	}
1875 	Com_Printf( "\n" );
1876 #endif
1877 
1878 	// run it
1879 	return BG_ExecuteCommand( ps, scriptCommand, qtrue, isContinue, force );
1880 }
1881 
1882 /*
1883 ===============
1884 BG_ValidAnimScript
1885 
1886   returns qtrue if the given client has animation scripts
1887 ===============
1888 */
BG_ValidAnimScript(int clientNum)1889 qboolean BG_ValidAnimScript( int clientNum ) {
1890 	if ( !globalScriptData->clientModels[clientNum] ) {
1891 		return qfalse;
1892 	}
1893 	//
1894 	if ( !globalScriptData->modelInfo[ globalScriptData->clientModels[clientNum] ]->numScriptItems ) {
1895 		return qfalse;
1896 	}
1897 	//
1898 	return qtrue;
1899 }
1900 
1901 /*
1902 ===============
1903 BG_GetAnimString
1904 ===============
1905 */
BG_GetAnimString(int client,int anim)1906 char *BG_GetAnimString( int client, int anim ) {
1907 	animModelInfo_t *modelinfo = BG_ModelInfoForClient( client );
1908 	//
1909 	if ( anim >= modelinfo->numAnimations ) {
1910 		BG_AnimParseError( "BG_GetAnimString: anim index is out of range" );
1911 	}
1912 	//
1913 	return modelinfo->animations[anim].name;
1914 }
1915 
1916 /*
1917 ==============
1918 BG_UpdateConditionValue
1919 ==============
1920 */
BG_UpdateConditionValue(int client,int condition,int value,qboolean checkConversion)1921 void BG_UpdateConditionValue( int client, int condition, int value, qboolean checkConversion ) {
1922 	if ( checkConversion ) {
1923 		// we may need to convert to bitflags
1924 		if ( animConditionsTable[condition].type == ANIM_CONDTYPE_BITFLAGS ) {
1925 
1926 			// DHM - Nerve :: We want to set the ScriptData to the explicit value passed in.
1927 			//				COM_BitSet will OR values on top of each other, so clear it first.
1928 			globalScriptData->clientConditions[client][condition][0] = 0;
1929 			globalScriptData->clientConditions[client][condition][1] = 0;
1930 			// dhm - end
1931 
1932 			COM_BitSet( globalScriptData->clientConditions[client][condition], value );
1933 			return;
1934 		}
1935 	}
1936 	globalScriptData->clientConditions[client][condition][0] = value;
1937 }
1938 
1939 
1940 /*
1941 ==============
1942 BG_UpdateConditionValueStrings
1943 ==============
1944 */
BG_UpdateConditionValueStrings(int client,char * conditionStr,char * valueStr)1945 void BG_UpdateConditionValueStrings( int client, char *conditionStr, char *valueStr ) {
1946 	int condition, value;
1947 	//
1948 	condition = BG_IndexForString( conditionStr, animConditionsStr, qfalse );
1949 	value = BG_IndexForString( valueStr, animConditionsTable[condition].values, qfalse );
1950 	//
1951 	globalScriptData->clientConditions[client][condition][0] = value;
1952 }
1953 
1954 /*
1955 ==============
1956 BG_GetConditionValue
1957 ==============
1958 */
BG_GetConditionValue(int client,int condition,qboolean checkConversion)1959 int BG_GetConditionValue( int client, int condition, qboolean checkConversion ) {
1960 	int value, i;
1961 
1962 	value = (intptr_t)globalScriptData->clientConditions[client][condition][0];
1963 
1964 	if ( checkConversion ) {
1965 		// we may need to convert to a value
1966 		if ( animConditionsTable[condition].type == ANIM_CONDTYPE_BITFLAGS ) {
1967 			//if (!value)
1968 			//	return 0;
1969 			for ( i = 0; i < 8 * sizeof( globalScriptData->clientConditions[0][0] ); i++ ) {
1970 				if ( COM_BitCheck( globalScriptData->clientConditions[client][condition], i ) ) {
1971 					return i;
1972 				}
1973 			}
1974 			// nothing found
1975 			return 0;
1976 			//BG_AnimParseError( "BG_GetConditionValue: internal error" );
1977 		}
1978 	}
1979 
1980 	return value;
1981 }
1982 
1983 /*
1984 ================
1985 BG_GetAnimScriptAnimation
1986 
1987   returns the locomotion animation index, -1 if no animation was found, 0 otherwise
1988 ================
1989 */
BG_GetAnimScriptAnimation(int client,aistateEnum_t estate,scriptAnimMoveTypes_t movetype)1990 int BG_GetAnimScriptAnimation( int client, aistateEnum_t estate, scriptAnimMoveTypes_t movetype ) {
1991 	animModelInfo_t     *modelInfo;
1992 	animScript_t        *script;
1993 	animScriptItem_t    *scriptItem = NULL;
1994 	animScriptCommand_t *scriptCommand;
1995 	int state = estate;                                 // enums are not always signed
1996 
1997 	modelInfo = BG_ModelInfoForClient( client );
1998 
1999 	// try finding a match in all states below the given state
2000 	while ( !scriptItem && state >= 0 ) {
2001 		script = &modelInfo->scriptAnims[ state ][ movetype ];
2002 		if ( !script->numItems ) {
2003 			state--;
2004 			continue;
2005 		}
2006 		// find the first script item, that passes all the conditions for this event
2007 		scriptItem = BG_FirstValidItem( client, script );
2008 		if ( !scriptItem ) {
2009 			state--;
2010 			continue;
2011 		}
2012 	}
2013 	//
2014 	if ( !scriptItem ) {
2015 		return -1;
2016 	}
2017 	// pick the correct animation for this character (animations must be constant for each character, otherwise they'll constantly change)
2018 	scriptCommand = &scriptItem->commands[ client % scriptItem->numCommands ];
2019 	if ( !scriptCommand->bodyPart[0] ) {
2020 		return -1;
2021 	}
2022 	// return the animation
2023 	return scriptCommand->animIndex[0];
2024 }
2025 
2026 /*
2027 ================
2028 BG_GetAnimScriptEvent
2029 
2030   returns the animation index for this event
2031 ================
2032 */
BG_GetAnimScriptEvent(playerState_t * ps,scriptAnimEventTypes_t event)2033 int BG_GetAnimScriptEvent( playerState_t *ps, scriptAnimEventTypes_t event ) {
2034 	animModelInfo_t     *modelInfo;
2035 	animScript_t        *script;
2036 	animScriptItem_t    *scriptItem;
2037 	animScriptCommand_t *scriptCommand;
2038 
2039 	if ( event != ANIM_ET_DEATH && ps->eFlags & EF_DEAD ) {
2040 		return -1;
2041 	}
2042 
2043 	modelInfo = BG_ModelInfoForClient( ps->clientNum );
2044 	script = &modelInfo->scriptEvents[ event ];
2045 	if ( !script->numItems ) {
2046 		return -1;
2047 	}
2048 	// find the first script item, that passes all the conditions for this event
2049 	scriptItem = BG_FirstValidItem( ps->clientNum, script );
2050 	if ( !scriptItem ) {
2051 		return -1;
2052 	}
2053 	// pick a random command
2054 	scriptCommand = &scriptItem->commands[ rand() % scriptItem->numCommands ];
2055 
2056 	// return the animation
2057 	return scriptCommand->animIndex[0];
2058 }
2059 
2060 /*
2061 ===============
2062 BG_GetAnimationForIndex
2063 
2064   returns the animation_t for the given index
2065 ===============
2066 */
BG_GetAnimationForIndex(int client,int index)2067 animation_t *BG_GetAnimationForIndex( int client, int index ) {
2068 	animModelInfo_t     *modelInfo;
2069 
2070 	modelInfo = BG_ModelInfoForClient( client );
2071 	if ( index < 0 || index >= modelInfo->numAnimations ) {
2072 		Com_Error( ERR_DROP, "BG_GetAnimationForIndex: index out of bounds" );
2073 	}
2074 
2075 	return &modelInfo->animations[index];
2076 }
2077 
2078 /*
2079 =================
2080 BG_AnimUpdatePlayerStateConditions
2081 =================
2082 */
BG_AnimUpdatePlayerStateConditions(pmove_t * pmove)2083 void BG_AnimUpdatePlayerStateConditions( pmove_t *pmove ) {
2084 	playerState_t *ps = pmove->ps;
2085 
2086 	// WEAPON
2087 	BG_UpdateConditionValue( ps->clientNum, ANIM_COND_WEAPON, ps->weapon, qtrue );
2088 
2089 	// MOUNTED
2090 	if ( ps->eFlags & EF_MG42_ACTIVE ) {
2091 		BG_UpdateConditionValue( ps->clientNum, ANIM_COND_MOUNTED, MOUNTED_MG42, qtrue );
2092 	} else {
2093 		BG_UpdateConditionValue( ps->clientNum, ANIM_COND_MOUNTED, MOUNTED_UNUSED, qtrue );
2094 	}
2095 
2096 	// UNDERHAND
2097 	BG_UpdateConditionValue( ps->clientNum, ANIM_COND_UNDERHAND, ps->viewangles[0] > 0, qtrue );
2098 
2099 	// LEANING
2100 	if ( ps->leanf > 0 ) {
2101 		BG_UpdateConditionValue( ps->clientNum, ANIM_COND_LEANING, LEANING_RIGHT, qtrue );
2102 	} else if ( ps->leanf < 0 ) {
2103 		BG_UpdateConditionValue( ps->clientNum, ANIM_COND_LEANING, LEANING_LEFT, qtrue );
2104 	} else {
2105 		BG_UpdateConditionValue( ps->clientNum, ANIM_COND_LEANING, LEANING_UNUSED, qtrue );
2106 	}
2107 
2108 	if ( ps->viewheight == ps->crouchViewHeight ) {
2109 		ps->eFlags |= EF_CROUCHING;
2110 	} else {
2111 		ps->eFlags &= ~EF_CROUCHING;
2112 	}
2113 
2114 	if ( pmove->cmd.buttons & BUTTON_ATTACK ) {
2115 		BG_UpdateConditionValue( ps->clientNum, ANIM_COND_FIRING, qtrue, qtrue );
2116 	} else {
2117 		BG_UpdateConditionValue( ps->clientNum, ANIM_COND_FIRING, qfalse, qtrue );
2118 	}
2119 }
2120 
2121 /*
2122 ===================
2123 BG_AnimGetFootstepGap
2124 ===================
2125 */
BG_AnimGetFootstepGap(playerState_t * ps,float xyspeed)2126 float BG_AnimGetFootstepGap( playerState_t *ps, float xyspeed ) {
2127 	animModelInfo_t     *modelInfo;
2128 	int index;
2129 	animation_t     *anim;
2130 	float gap;
2131 #define MAX_ANIM_SCALE  1.1
2132 
2133 	modelInfo = BG_ModelInfoForClient( ps->clientNum );
2134 	index = ps->legsAnim & ~ANIM_TOGGLEBIT;
2135 	if ( index < 0 || index >= modelInfo->numAnimations ) {
2136 		Com_Error( ERR_DROP, "BG_AnimGetFootstepGap: anim index out of bounds" );
2137 	}
2138 
2139 	anim = &modelInfo->animations[index];
2140 
2141 	if ( !anim->moveSpeed ) {
2142 		// ACK, return -1, use old method
2143 		return -1;
2144 	}
2145 
2146 	gap = anim->stepGap;
2147 
2148 	// if they are travelling faster than the moveSpeed, then scale up the gap to counter, since the
2149 	// animation can't play faster than MAX_ANIM_SCALE speed
2150 	if ( xyspeed > anim->moveSpeed * MAX_ANIM_SCALE ) {
2151 		gap *= xyspeed / anim->moveSpeed * MAX_ANIM_SCALE;
2152 	}
2153 
2154 	return gap;
2155 }
2156 
2157