You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
513 lines
13 KiB
513 lines
13 KiB
/*** |
|
* |
|
* Copyright (c) 1996-2002, Valve LLC. All rights reserved. |
|
* |
|
* This product contains software technology licensed from Id |
|
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. |
|
* All Rights Reserved. |
|
* |
|
* Use, distribution, and modification of this source code and/or resulting |
|
* object code is restricted to non-commercial enhancements to products from |
|
* Valve LLC. All other use, distribution, or modification is prohibited |
|
* without written permission from Valve LLC. |
|
* |
|
****/ |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
|
|
typedef int BOOL; |
|
#define TRUE 1 |
|
#define FALSE 0 |
|
|
|
// hack into header files that we can ship |
|
typedef int qboolean; |
|
typedef unsigned char byte; |
|
#include "mathlib.h" |
|
#include "const.h" |
|
#include "progdefs.h" |
|
#include "edict.h" |
|
#include "eiface.h" |
|
|
|
#include "studio.h" |
|
|
|
#ifndef ACTIVITY_H |
|
#include "activity.h" |
|
#endif |
|
|
|
#include "activitymap.h" |
|
|
|
#ifndef ANIMATION_H |
|
#include "animation.h" |
|
#endif |
|
|
|
#ifndef SCRIPTEVENT_H |
|
#include "scriptevent.h" |
|
#endif |
|
|
|
#ifndef ENGINECALLBACK_H |
|
#include "enginecallback.h" |
|
#endif |
|
|
|
extern globalvars_t *gpGlobals; |
|
|
|
#pragma warning( disable : 4244 ) |
|
|
|
int ExtractBbox( void *pmodel, int sequence, float *mins, float *maxs ) |
|
{ |
|
studiohdr_t *pstudiohdr; |
|
|
|
pstudiohdr = (studiohdr_t *)pmodel; |
|
if( !pstudiohdr ) |
|
return 0; |
|
|
|
mstudioseqdesc_t *pseqdesc; |
|
|
|
pseqdesc = (mstudioseqdesc_t *)( (byte *)pstudiohdr + pstudiohdr->seqindex ); |
|
|
|
mins[0] = pseqdesc[sequence].bbmin[0]; |
|
mins[1] = pseqdesc[sequence].bbmin[1]; |
|
mins[2] = pseqdesc[sequence].bbmin[2]; |
|
|
|
maxs[0] = pseqdesc[sequence].bbmax[0]; |
|
maxs[1] = pseqdesc[sequence].bbmax[1]; |
|
maxs[2] = pseqdesc[sequence].bbmax[2]; |
|
|
|
return 1; |
|
} |
|
|
|
int LookupActivity( void *pmodel, entvars_t *pev, int activity ) |
|
{ |
|
studiohdr_t *pstudiohdr; |
|
|
|
pstudiohdr = (studiohdr_t *)pmodel; |
|
if( !pstudiohdr ) |
|
return 0; |
|
|
|
mstudioseqdesc_t *pseqdesc; |
|
|
|
pseqdesc = (mstudioseqdesc_t *)( (byte *)pstudiohdr + pstudiohdr->seqindex ); |
|
|
|
int weighttotal = 0; |
|
int seq = ACTIVITY_NOT_AVAILABLE; |
|
for( int i = 0; i < pstudiohdr->numseq; i++ ) |
|
{ |
|
if( pseqdesc[i].activity == activity ) |
|
{ |
|
weighttotal += pseqdesc[i].actweight; |
|
if( !weighttotal || RANDOM_LONG( 0, weighttotal - 1 ) < pseqdesc[i].actweight ) |
|
seq = i; |
|
} |
|
} |
|
|
|
return seq; |
|
} |
|
|
|
int LookupActivityHeaviest( void *pmodel, entvars_t *pev, int activity ) |
|
{ |
|
studiohdr_t *pstudiohdr; |
|
|
|
pstudiohdr = (studiohdr_t *)pmodel; |
|
if( !pstudiohdr ) |
|
return 0; |
|
|
|
mstudioseqdesc_t *pseqdesc; |
|
|
|
pseqdesc = (mstudioseqdesc_t *)( (byte *)pstudiohdr + pstudiohdr->seqindex ); |
|
|
|
int weight = 0; |
|
int seq = ACTIVITY_NOT_AVAILABLE; |
|
for( int i = 0; i < pstudiohdr->numseq; i++ ) |
|
{ |
|
if( pseqdesc[i].activity == activity ) |
|
{ |
|
if( pseqdesc[i].actweight > weight ) |
|
{ |
|
weight = pseqdesc[i].actweight; |
|
seq = i; |
|
} |
|
} |
|
} |
|
|
|
return seq; |
|
} |
|
|
|
void GetEyePosition( void *pmodel, float *vecEyePosition ) |
|
{ |
|
studiohdr_t *pstudiohdr; |
|
|
|
pstudiohdr = (studiohdr_t *)pmodel; |
|
|
|
if( !pstudiohdr ) |
|
{ |
|
ALERT( at_console, "GetEyePosition() Can't get pstudiohdr ptr!\n" ); |
|
return; |
|
} |
|
|
|
VectorCopy( pstudiohdr->eyeposition, vecEyePosition ); |
|
} |
|
|
|
int LookupSequence( void *pmodel, const char *label ) |
|
{ |
|
studiohdr_t *pstudiohdr; |
|
|
|
pstudiohdr = (studiohdr_t *)pmodel; |
|
if( !pstudiohdr ) |
|
return 0; |
|
|
|
mstudioseqdesc_t *pseqdesc; |
|
|
|
pseqdesc = (mstudioseqdesc_t *)( (byte *)pstudiohdr + pstudiohdr->seqindex ); |
|
|
|
for( int i = 0; i < pstudiohdr->numseq; i++ ) |
|
{ |
|
if( stricmp( pseqdesc[i].label, label ) == 0 ) |
|
return i; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
int IsSoundEvent( int eventNumber ) |
|
{ |
|
if( eventNumber == SCRIPT_EVENT_SOUND || eventNumber == SCRIPT_EVENT_SOUND_VOICE ) |
|
return 1; |
|
return 0; |
|
} |
|
|
|
void SequencePrecache( void *pmodel, const char *pSequenceName ) |
|
{ |
|
int index = LookupSequence( pmodel, pSequenceName ); |
|
if( index >= 0 ) |
|
{ |
|
studiohdr_t *pstudiohdr; |
|
|
|
pstudiohdr = (studiohdr_t *)pmodel; |
|
if( !pstudiohdr || index >= pstudiohdr->numseq ) |
|
return; |
|
|
|
mstudioseqdesc_t *pseqdesc; |
|
mstudioevent_t *pevent; |
|
|
|
pseqdesc = (mstudioseqdesc_t *)( (byte *)pstudiohdr + pstudiohdr->seqindex ) + index; |
|
pevent = (mstudioevent_t *)( (byte *)pstudiohdr + pseqdesc->eventindex ); |
|
|
|
for( int i = 0; i < pseqdesc->numevents; i++ ) |
|
{ |
|
// Don't send client-side events to the server AI |
|
if( pevent[i].event >= EVENT_CLIENT ) |
|
continue; |
|
|
|
// UNDONE: Add a callback to check to see if a sound is precached yet and don't allocate a copy |
|
// of it's name if it is. |
|
if( IsSoundEvent( pevent[i].event ) ) |
|
{ |
|
if( pevent[i].options[0] == '\0' ) |
|
{ |
|
ALERT( at_error, "Bad sound event %d in sequence %s :: %s (sound is \"%s\")\n", pevent[i].event, pstudiohdr->name, pSequenceName, pevent[i].options ); |
|
} |
|
|
|
PRECACHE_SOUND( gpGlobals->pStringBase + ALLOC_STRING( pevent[i].options ) ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
void GetSequenceInfo( void *pmodel, entvars_t *pev, float *pflFrameRate, float *pflGroundSpeed ) |
|
{ |
|
studiohdr_t *pstudiohdr; |
|
|
|
pstudiohdr = (studiohdr_t *)pmodel; |
|
if( !pstudiohdr ) |
|
return; |
|
|
|
mstudioseqdesc_t *pseqdesc; |
|
|
|
if( pev->sequence >= pstudiohdr->numseq ) |
|
{ |
|
*pflFrameRate = 0.0; |
|
*pflGroundSpeed = 0.0; |
|
return; |
|
} |
|
|
|
pseqdesc = (mstudioseqdesc_t *)( (byte *)pstudiohdr + pstudiohdr->seqindex ) + (int)pev->sequence; |
|
|
|
if( pseqdesc->numframes > 1 ) |
|
{ |
|
*pflFrameRate = 256 * pseqdesc->fps / ( pseqdesc->numframes - 1 ); |
|
*pflGroundSpeed = sqrt( pseqdesc->linearmovement[0] * pseqdesc->linearmovement[0] + pseqdesc->linearmovement[1] * pseqdesc->linearmovement[1] + pseqdesc->linearmovement[2] * pseqdesc->linearmovement[2] ); |
|
*pflGroundSpeed = *pflGroundSpeed * pseqdesc->fps / ( pseqdesc->numframes - 1 ); |
|
} |
|
else |
|
{ |
|
*pflFrameRate = 256.0; |
|
*pflGroundSpeed = 0.0; |
|
} |
|
} |
|
|
|
int GetSequenceFlags( void *pmodel, entvars_t *pev ) |
|
{ |
|
studiohdr_t *pstudiohdr; |
|
|
|
pstudiohdr = (studiohdr_t *)pmodel; |
|
if( !pstudiohdr || pev->sequence >= pstudiohdr->numseq ) |
|
return 0; |
|
|
|
mstudioseqdesc_t *pseqdesc; |
|
pseqdesc = (mstudioseqdesc_t *)( (byte *)pstudiohdr + pstudiohdr->seqindex ) + (int)pev->sequence; |
|
|
|
return pseqdesc->flags; |
|
} |
|
|
|
int GetAnimationEvent( void *pmodel, entvars_t *pev, MonsterEvent_t *pMonsterEvent, float flStart, float flEnd, int index ) |
|
{ |
|
studiohdr_t *pstudiohdr; |
|
|
|
pstudiohdr = (studiohdr_t *)pmodel; |
|
if( !pstudiohdr || pev->sequence >= pstudiohdr->numseq || !pMonsterEvent ) |
|
return 0; |
|
|
|
mstudioseqdesc_t *pseqdesc; |
|
mstudioevent_t *pevent; |
|
|
|
pseqdesc = (mstudioseqdesc_t *)( (byte *)pstudiohdr + pstudiohdr->seqindex ) + (int)pev->sequence; |
|
pevent = (mstudioevent_t *)( (byte *)pstudiohdr + pseqdesc->eventindex ); |
|
|
|
if( pseqdesc->numevents == 0 || index > pseqdesc->numevents ) |
|
return 0; |
|
|
|
if( pseqdesc->numframes > 1 ) |
|
{ |
|
flStart *= ( pseqdesc->numframes - 1 ) / 256.0; |
|
flEnd *= (pseqdesc->numframes - 1) / 256.0; |
|
} |
|
else |
|
{ |
|
flStart = 0; |
|
flEnd = 1.0; |
|
} |
|
|
|
for( ; index < pseqdesc->numevents; index++ ) |
|
{ |
|
// Don't send client-side events to the server AI |
|
if( pevent[index].event >= EVENT_CLIENT ) |
|
continue; |
|
|
|
if( ( pevent[index].frame >= flStart && pevent[index].frame < flEnd ) || |
|
( ( pseqdesc->flags & STUDIO_LOOPING ) && flEnd >= pseqdesc->numframes - 1 && pevent[index].frame < flEnd - pseqdesc->numframes + 1 ) ) |
|
{ |
|
pMonsterEvent->event = pevent[index].event; |
|
pMonsterEvent->options = pevent[index].options; |
|
return index + 1; |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
float SetController( void *pmodel, entvars_t *pev, int iController, float flValue ) |
|
{ |
|
studiohdr_t *pstudiohdr; |
|
int i; |
|
|
|
pstudiohdr = (studiohdr_t *)pmodel; |
|
if( !pstudiohdr ) |
|
return flValue; |
|
|
|
mstudiobonecontroller_t *pbonecontroller = (mstudiobonecontroller_t *)( (byte *)pstudiohdr + pstudiohdr->bonecontrollerindex ); |
|
|
|
// find first controller that matches the index |
|
for( i = 0; i < pstudiohdr->numbonecontrollers; i++, pbonecontroller++ ) |
|
{ |
|
if( pbonecontroller->index == iController ) |
|
break; |
|
} |
|
if( i >= pstudiohdr->numbonecontrollers ) |
|
return flValue; |
|
|
|
// wrap 0..360 if it's a rotational controller |
|
if( pbonecontroller->type & ( STUDIO_XR | STUDIO_YR | STUDIO_ZR ) ) |
|
{ |
|
// ugly hack, invert value if end < start |
|
if( pbonecontroller->end < pbonecontroller->start ) |
|
flValue = -flValue; |
|
|
|
// does the controller not wrap? |
|
if( pbonecontroller->start + 359.0 >= pbonecontroller->end ) |
|
{ |
|
if( flValue > ( ( pbonecontroller->start + pbonecontroller->end ) / 2.0 ) + 180 ) |
|
flValue = flValue - 360; |
|
if( flValue < ( ( pbonecontroller->start + pbonecontroller->end) / 2.0 ) - 180 ) |
|
flValue = flValue + 360; |
|
} |
|
else |
|
{ |
|
if( flValue > 360 ) |
|
flValue = flValue - (int)( flValue / 360.0 ) * 360.0; |
|
else if( flValue < 0 ) |
|
flValue = flValue + (int)( ( flValue / -360.0 ) + 1 ) * 360.0; |
|
} |
|
} |
|
|
|
int setting = (int)( 255 * ( flValue - pbonecontroller->start ) / ( pbonecontroller->end - pbonecontroller->start ) ); |
|
|
|
if( setting < 0 ) |
|
setting = 0; |
|
if( setting > 255 ) |
|
setting = 255; |
|
pev->controller[iController] = setting; |
|
|
|
return setting * ( 1.0 / 255.0 ) * (pbonecontroller->end - pbonecontroller->start ) + pbonecontroller->start; |
|
} |
|
|
|
float SetBlending( void *pmodel, entvars_t *pev, int iBlender, float flValue ) |
|
{ |
|
studiohdr_t *pstudiohdr; |
|
|
|
pstudiohdr = (studiohdr_t *)pmodel; |
|
if( !pstudiohdr ) |
|
return flValue; |
|
|
|
mstudioseqdesc_t *pseqdesc; |
|
|
|
pseqdesc = (mstudioseqdesc_t *)( (byte *)pstudiohdr + pstudiohdr->seqindex ) + (int)pev->sequence; |
|
|
|
if( pseqdesc->blendtype[iBlender] == 0 ) |
|
return flValue; |
|
|
|
if( pseqdesc->blendtype[iBlender] & ( STUDIO_XR | STUDIO_YR | STUDIO_ZR ) ) |
|
{ |
|
// ugly hack, invert value if end < start |
|
if( pseqdesc->blendend[iBlender] < pseqdesc->blendstart[iBlender] ) |
|
flValue = -flValue; |
|
|
|
// does the controller not wrap? |
|
if( pseqdesc->blendstart[iBlender] + 359.0 >= pseqdesc->blendend[iBlender] ) |
|
{ |
|
if( flValue > ( ( pseqdesc->blendstart[iBlender] + pseqdesc->blendend[iBlender] ) / 2.0 ) + 180 ) |
|
flValue = flValue - 360; |
|
if( flValue < ( ( pseqdesc->blendstart[iBlender] + pseqdesc->blendend[iBlender] ) / 2.0 ) - 180 ) |
|
flValue = flValue + 360; |
|
} |
|
} |
|
|
|
int setting = (int)( 255 * ( flValue - pseqdesc->blendstart[iBlender] ) / ( pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender] ) ); |
|
|
|
if( setting < 0 ) |
|
setting = 0; |
|
if(setting > 255) |
|
setting = 255; |
|
|
|
pev->blending[iBlender] = setting; |
|
|
|
return setting * ( 1.0 / 255.0 ) * ( pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender] ) + pseqdesc->blendstart[iBlender]; |
|
} |
|
|
|
int FindTransition( void *pmodel, int iEndingAnim, int iGoalAnim, int *piDir ) |
|
{ |
|
studiohdr_t *pstudiohdr; |
|
|
|
pstudiohdr = (studiohdr_t *)pmodel; |
|
if( !pstudiohdr ) |
|
return iGoalAnim; |
|
|
|
mstudioseqdesc_t *pseqdesc; |
|
pseqdesc = (mstudioseqdesc_t *)( (byte *)pstudiohdr + pstudiohdr->seqindex ); |
|
|
|
// bail if we're going to or from a node 0 |
|
if( pseqdesc[iEndingAnim].entrynode == 0 || pseqdesc[iGoalAnim].entrynode == 0 ) |
|
{ |
|
return iGoalAnim; |
|
} |
|
|
|
int iEndNode; |
|
|
|
// ALERT( at_console, "from %d to %d: ", pEndNode->iEndNode, pGoalNode->iStartNode ); |
|
|
|
if( *piDir > 0 ) |
|
{ |
|
iEndNode = pseqdesc[iEndingAnim].exitnode; |
|
} |
|
else |
|
{ |
|
iEndNode = pseqdesc[iEndingAnim].entrynode; |
|
} |
|
|
|
if( iEndNode == pseqdesc[iGoalAnim].entrynode ) |
|
{ |
|
*piDir = 1; |
|
return iGoalAnim; |
|
} |
|
|
|
byte *pTransition = ( (byte *)pstudiohdr + pstudiohdr->transitionindex ); |
|
|
|
int iInternNode = pTransition[( iEndNode - 1 ) * pstudiohdr->numtransitions + ( pseqdesc[iGoalAnim].entrynode - 1 )]; |
|
|
|
if( iInternNode == 0 ) |
|
return iGoalAnim; |
|
|
|
int i; |
|
|
|
// look for someone going |
|
for( i = 0; i < pstudiohdr->numseq; i++ ) |
|
{ |
|
if( pseqdesc[i].entrynode == iEndNode && pseqdesc[i].exitnode == iInternNode ) |
|
{ |
|
*piDir = 1; |
|
return i; |
|
} |
|
if( pseqdesc[i].nodeflags ) |
|
{ |
|
if( pseqdesc[i].exitnode == iEndNode && pseqdesc[i].entrynode == iInternNode ) |
|
{ |
|
*piDir = -1; |
|
return i; |
|
} |
|
} |
|
} |
|
|
|
ALERT( at_console, "error in transition graph" ); |
|
return iGoalAnim; |
|
} |
|
|
|
void SetBodygroup( void *pmodel, entvars_t *pev, int iGroup, int iValue ) |
|
{ |
|
studiohdr_t *pstudiohdr; |
|
|
|
pstudiohdr = (studiohdr_t *)pmodel; |
|
if( !pstudiohdr ) |
|
return; |
|
|
|
if( iGroup > pstudiohdr->numbodyparts ) |
|
return; |
|
|
|
mstudiobodyparts_t *pbodypart = (mstudiobodyparts_t *)( (byte *)pstudiohdr + pstudiohdr->bodypartindex ) + iGroup; |
|
|
|
if( iValue >= pbodypart->nummodels ) |
|
return; |
|
|
|
int iCurrent = ( pev->body / pbodypart->base ) % pbodypart->nummodels; |
|
|
|
pev->body = ( pev->body - ( iCurrent * pbodypart->base ) + ( iValue * pbodypart->base ) ); |
|
} |
|
|
|
int GetBodygroup( void *pmodel, entvars_t *pev, int iGroup ) |
|
{ |
|
studiohdr_t *pstudiohdr; |
|
|
|
pstudiohdr = (studiohdr_t *)pmodel; |
|
if( !pstudiohdr ) |
|
return 0; |
|
|
|
if( iGroup > pstudiohdr->numbodyparts ) |
|
return 0; |
|
|
|
mstudiobodyparts_t *pbodypart = (mstudiobodyparts_t *)( (byte *)pstudiohdr + pstudiohdr->bodypartindex ) + iGroup; |
|
|
|
if( pbodypart->nummodels <= 1 ) |
|
return 0; |
|
|
|
int iCurrent = ( pev->body / pbodypart->base ) % pbodypart->nummodels; |
|
|
|
return iCurrent; |
|
}
|
|
|