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