mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 13:47:58 +03:00
1907 lines
33 KiB
C++
1907 lines
33 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 2015 the OpenMoHAA team
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
OpenMoHAA source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
OpenMoHAA source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with OpenMoHAA source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
// simpleactor.cpp: Base class for character AI
|
|
|
|
#include "actor.h"
|
|
#include "bg_local.h"
|
|
#include "scriptexception.h"
|
|
#include <tiki.h>
|
|
|
|
CLASS_DECLARATION( Sentient, SimpleActor, NULL )
|
|
{
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
#define OVERLOADED_ERROR() assert( !"overloaded version should always get called" )
|
|
|
|
SimpleActor::SimpleActor()
|
|
{
|
|
if( LoadingSavegame )
|
|
return;
|
|
|
|
m_AnimMotionHigh = true;
|
|
m_DesiredGunDir = vec_zero;
|
|
m_eEmotionMode = EMOTION_NEUTRAL;
|
|
m_eNextAnimMode = -1;
|
|
m_fPathGoalTime = 0.0f;
|
|
m_csPathGoalEndAnimScript = STRING_EMPTY;
|
|
|
|
m_AnimActionHigh = true;
|
|
m_AnimDialogHigh = true;
|
|
m_fAimLimit_up = 60.0f;
|
|
m_fAimLimit_down = -60.0f;
|
|
|
|
m_csNextAnimString = STRING_NULL;
|
|
m_bNextForceStart = false;
|
|
m_fCrossblendTime = 0.5f;
|
|
m_csCurrentPosition = STRING_STAND;
|
|
m_bStartPathGoalEndAnim = false;
|
|
|
|
for( int i = 0; i < MAX_FRAMEINFOS; i++ )
|
|
{
|
|
m_weightType[ i ] = 0.0f;
|
|
}
|
|
|
|
m_bMotionAnimSet = false;
|
|
m_ChangeActionAnimIndex = -1;
|
|
m_ChangeActionAnimIndex = -1;
|
|
m_ChangeSayAnimIndex = -1;
|
|
m_iMotionSlot = -1;
|
|
m_iActionSlot = -1;
|
|
m_bActionAnimSet = false;
|
|
m_bSayAnimSet = false;
|
|
m_iVoiceTime = 0;
|
|
m_bAimAnimSet = false;
|
|
m_iSaySlot = -1;
|
|
m_bLevelMotionAnim = false;
|
|
m_bLevelActionAnim = false;
|
|
m_bLevelSayAnim = 0;
|
|
m_bNextLevelSayAnim = 0;
|
|
m_DesiredYaw = 0.0f;
|
|
m_YawAchieved = true;
|
|
m_bPathErrorTime = -10000000.0f;
|
|
|
|
m_PainHandler.TrySetScript( STRING_ANIM_PAIN_SCR );
|
|
m_DeathHandler.TrySetScript( STRING_ANIM_KILLED_SCR );
|
|
m_AttackHandler.TrySetScript( STRING_ANIM_ATTACK_SCR );
|
|
m_SniperHandler.TrySetScript( STRING_ANIM_SNIPER_SCR );
|
|
|
|
m_bHasDesiredLookDest = false;
|
|
m_bUpdateAnimDoneFlags = false;
|
|
|
|
m_NearestNode = NULL;
|
|
m_fCrouchWeight = 0.0f;
|
|
|
|
m_csMood = STRING_BORED;
|
|
m_csIdleMood = STRING_BORED;
|
|
|
|
m_groundPlane = qfalse;
|
|
m_walking = qfalse;
|
|
m_groundPlaneNormal = vec_zero;
|
|
m_maxspeed = 1000000.0f;
|
|
}
|
|
|
|
void SimpleActor::Archive
|
|
(
|
|
Archiver& arc
|
|
)
|
|
{
|
|
Sentient::Archive( arc );
|
|
|
|
arc.ArchiveInteger( &m_eAnimMode );
|
|
m_Anim.Archive( arc );
|
|
|
|
arc.ArchiveBool( &m_bHasDesiredLookDest );
|
|
arc.ArchiveBool( &m_bHasDesiredLookAngles );
|
|
arc.ArchiveVector( &m_vDesiredLookDest );
|
|
arc.ArchiveVec3( m_DesiredLookAngles );
|
|
arc.ArchiveVec3( m_DesiredGunDir );
|
|
|
|
m_Path.Archive( arc );
|
|
arc.ArchiveVec3( m_Dest );
|
|
arc.ArchiveVec3( m_NoClipDest );
|
|
|
|
arc.ArchiveFloat( &path_failed_time );
|
|
arc.ArchiveFloat( &m_fPathGoalTime );
|
|
arc.ArchiveBool( &m_bStartPathGoalEndAnim );
|
|
Director.ArchiveString( arc, m_csPathGoalEndAnimScript );
|
|
|
|
arc.ArchiveInteger( &m_eNextAnimMode );
|
|
Director.ArchiveString( arc, m_csNextAnimString );
|
|
m_NextAnimLabel.Archive( arc );
|
|
arc.ArchiveBool( &m_bNextForceStart );
|
|
|
|
arc.ArchiveBoolean( &m_walking );
|
|
arc.ArchiveBoolean( &m_groundPlane );
|
|
arc.ArchiveVec3( m_groundPlaneNormal );
|
|
|
|
arc.ArchiveVector( &watch_offset );
|
|
arc.ArchiveBool( &m_bThink );
|
|
arc.ArchiveInteger( &m_PainTime );
|
|
|
|
arc.ArchiveBool( &m_bAimAnimSet );
|
|
arc.ArchiveBool( &m_bActionAnimSet );
|
|
|
|
Director.ArchiveString( arc, m_csMood );
|
|
Director.ArchiveString( arc, m_csIdleMood );
|
|
|
|
ArchiveEnum(m_eEmotionMode, eEmotionMode);
|
|
|
|
arc.ArchiveFloat( &m_fAimLimit_up );
|
|
arc.ArchiveFloat( &m_fAimLimit_down );
|
|
|
|
for( int i = MAX_FRAMEINFOS - 1; i >= 0; i-- )
|
|
{
|
|
arc.ArchiveUnsigned( &m_weightType[ i ] );
|
|
}
|
|
|
|
for( int i = MAX_FRAMEINFOS - 1; i >= 0; i-- )
|
|
{
|
|
arc.ArchiveFloat( &m_weightBase[ i ] );
|
|
}
|
|
|
|
for( int i = MAX_FRAMEINFOS - 1; i >= 0; i-- )
|
|
{
|
|
arc.ArchiveFloat( &m_weightCrossBlend[ i ] );
|
|
}
|
|
|
|
arc.ArchiveBool( &m_AnimMotionHigh );
|
|
arc.ArchiveBool( &m_AnimActionHigh );
|
|
arc.ArchiveBool( &m_AnimDialogHigh );
|
|
|
|
arc.ArchiveVec2( obstacle_vel );
|
|
|
|
Director.ArchiveString( arc, m_csCurrentPosition );
|
|
|
|
arc.ArchiveBool( &m_bMotionAnimSet );
|
|
arc.ArchiveBool( &m_bDoAI );
|
|
|
|
arc.ArchiveFloat( &m_fCrossblendTime );
|
|
|
|
arc.ArchiveSafePointer( &m_pAnimThread );
|
|
|
|
arc.ArchiveBool( &m_YawAchieved );
|
|
arc.ArchiveFloat( &m_DesiredYaw );
|
|
|
|
arc.ArchiveInteger( &m_iVoiceTime );
|
|
arc.ArchiveBool( &m_bSayAnimSet );
|
|
|
|
arc.ArchiveInteger( &hit_obstacle_time );
|
|
|
|
Director.ArchiveString( arc, m_csAnimName );
|
|
|
|
arc.ArchiveInteger( &m_bPathErrorTime );
|
|
arc.ArchiveInteger( &m_iMotionSlot );
|
|
arc.ArchiveInteger( &m_iActionSlot );
|
|
arc.ArchiveInteger( &m_iSaySlot );
|
|
|
|
arc.ArchiveBool( &m_bLevelMotionAnim );
|
|
arc.ArchiveBool( &m_bLevelActionAnim );
|
|
arc.ArchiveByte( &m_bLevelSayAnim );
|
|
arc.ArchiveByte( &m_bNextLevelSayAnim );
|
|
|
|
Director.ArchiveString( arc, m_csSayAnim );
|
|
Director.ArchiveString( arc, m_csUpperAnim );
|
|
|
|
m_PainHandler.Archive( arc );
|
|
m_DeathHandler.Archive( arc );
|
|
m_AttackHandler.Archive( arc );
|
|
m_SniperHandler.Archive( arc );
|
|
|
|
arc.ArchiveObjectPointer( ( Class ** )&m_NearestNode );
|
|
arc.ArchiveVector( &m_vNearestNodePos );
|
|
|
|
arc.ArchiveFloat( &m_fCrouchWeight );
|
|
arc.ArchiveFloat( &m_maxspeed );
|
|
}
|
|
|
|
void SimpleActor::SetMoveInfo
|
|
(
|
|
mmove_t *
|
|
)
|
|
{
|
|
OVERLOADED_ERROR();
|
|
}
|
|
|
|
void SimpleActor::GetMoveInfo
|
|
(
|
|
mmove_t *
|
|
)
|
|
{
|
|
OVERLOADED_ERROR();
|
|
}
|
|
|
|
void SimpleActor::StopAnimating
|
|
(
|
|
int slot
|
|
)
|
|
{
|
|
int index = 0;
|
|
|
|
m_weightType[ slot ] = 0;
|
|
DoExitCommands( slot );
|
|
|
|
if( edict->s.frameInfo[ slot ].index || gi.TIKI_NumAnims( edict->tiki ) <= 1 )
|
|
{
|
|
edict->s.frameInfo[ slot ].index = 0;
|
|
}
|
|
else
|
|
{
|
|
edict->s.frameInfo[ slot ].index = 1;
|
|
}
|
|
|
|
animFlags[ slot ] = ANIM_LOOP | ANIM_NODELTA | ANIM_NOEXIT | ANIM_PAUSED;
|
|
edict->s.frameInfo[ slot ].weight = 0;
|
|
animtimes[ slot ] = 0;
|
|
animFlags[ slot ] = ( animFlags[ slot ] | ANIM_NODELTA ) & ~ANIM_FINISHED;
|
|
}
|
|
|
|
void SimpleActor::AnimFinished
|
|
(
|
|
int slot
|
|
)
|
|
{
|
|
assert( !DumpCallTrace("") );
|
|
}
|
|
|
|
bool SimpleActor::CanTarget
|
|
(
|
|
void
|
|
)
|
|
{
|
|
OVERLOADED_ERROR();
|
|
return false;
|
|
}
|
|
|
|
bool SimpleActor::IsImmortal
|
|
(
|
|
void
|
|
)
|
|
{
|
|
OVERLOADED_ERROR();
|
|
return false;
|
|
}
|
|
|
|
bool SimpleActor::DoesTheoreticPathExist
|
|
(
|
|
Vector vDestPos,
|
|
float fMaxPath
|
|
)
|
|
{
|
|
return m_Path.DoesTheoreticPathExist( origin, vDestPos, this, fMaxPath, NULL, 0 );
|
|
}
|
|
|
|
void SimpleActor::SetPath
|
|
(
|
|
Vector vDestPos,
|
|
const char *description,
|
|
int iMaxDirtyTime,
|
|
float *vLeashHome,
|
|
float fLeashDistSquared
|
|
)
|
|
{
|
|
if (!PathExists()
|
|
|| ( ( level.inttime >= m_Path.Time() + iMaxDirtyTime
|
|
|| m_Path.Complete(origin) )
|
|
&& PathGoal() != vDestPos)
|
|
)
|
|
{
|
|
m_Path.FindPath(origin,
|
|
vDestPos,
|
|
this,
|
|
0.0,
|
|
vLeashHome,
|
|
fLeashDistSquared);
|
|
|
|
if (!PathExists())
|
|
{
|
|
if (g_patherror->integer)
|
|
{
|
|
if (description)
|
|
{
|
|
int thinkState = ((Actor*)this)->m_ThinkState;
|
|
if (g_patherror->integer == 1 || (g_patherror->integer == 2 && (thinkState == THINKSTATE_IDLE || thinkState == THINKSTATE_CURIOUS)))
|
|
{
|
|
if (m_bPathErrorTime + 5000 < level.inttime)
|
|
{
|
|
m_bPathErrorTime = level.inttime;
|
|
Com_Printf(
|
|
"^~^~^ Path not found in '%s' for (entnum %d, radnum %d, targetname '%s') from (%f %f %f) to (%f %f %f)\n",
|
|
description,
|
|
entnum,
|
|
radnum,
|
|
targetname.c_str(),
|
|
origin.x,
|
|
origin.y,
|
|
origin.z,
|
|
vDestPos.x,
|
|
vDestPos.y,
|
|
vDestPos.z
|
|
);
|
|
Com_Printf("Reason: %s\n", PathSearch::last_error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SimpleActor::SetPath
|
|
(
|
|
SimpleEntity *pDestNode,
|
|
const char *description,
|
|
int iMaxDirtyTime
|
|
)
|
|
{
|
|
if (pDestNode)
|
|
{
|
|
SetPath(
|
|
pDestNode->origin,
|
|
description,
|
|
iMaxDirtyTime,
|
|
NULL,
|
|
0.0);
|
|
}
|
|
else
|
|
{
|
|
if (m_bPathErrorTime + 5000 < level.inttime)
|
|
{
|
|
m_bPathErrorTime = level.inttime;
|
|
Com_Printf(
|
|
"^~^~^ No destination node specified for '%s' at (%f %f %f)\n",
|
|
targetname.c_str(),
|
|
origin.x,
|
|
origin.y,
|
|
origin.z);
|
|
}
|
|
ClearPath();
|
|
}
|
|
}
|
|
|
|
void SimpleActor::SetPathWithinDistance
|
|
(
|
|
Vector vDestPos,
|
|
char *description,
|
|
float fMaxPath,
|
|
int iMaxDirtyTime
|
|
)
|
|
{
|
|
SetPath(vDestPos, description, iMaxDirtyTime, NULL, 0);
|
|
}
|
|
|
|
void SimpleActor::FindPathAway
|
|
(
|
|
vec_t *vAwayFrom,
|
|
vec_t *vDirPreferred,
|
|
float fMinSafeDist
|
|
)
|
|
{
|
|
m_Path.FindPathAway(origin, vAwayFrom, vDirPreferred, this, fMinSafeDist, NULL, 0);
|
|
|
|
ShortenPathToAvoidSquadMates();
|
|
}
|
|
|
|
void SimpleActor::ClearPath
|
|
(
|
|
void
|
|
)
|
|
{
|
|
m_Path.Clear();
|
|
}
|
|
|
|
bool SimpleActor::PathComplete
|
|
(
|
|
void
|
|
) const
|
|
{
|
|
if( level.time >= m_fPathGoalTime )
|
|
{
|
|
if( m_Path.Complete( origin ) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SimpleActor::PathExists
|
|
(
|
|
void
|
|
) const
|
|
{
|
|
return m_Path.CurrentNode() != NULL;
|
|
}
|
|
|
|
bool SimpleActor::PathIsValid
|
|
(
|
|
void
|
|
) const
|
|
{
|
|
//Called by SetPath...
|
|
return true;
|
|
}
|
|
|
|
bool SimpleActor::PathAvoidsSquadMates
|
|
(
|
|
void
|
|
) const
|
|
{
|
|
Entity* player;
|
|
float fDelta;
|
|
float fDistSoFar;
|
|
float fDistCap;
|
|
float vDelta2[2];
|
|
float vMins[3];
|
|
float vMaxs[3];
|
|
float vPos[3];
|
|
//Sentient *pOther;
|
|
Sentient *pBuddy[256];
|
|
int iNumBuddies;
|
|
int i;
|
|
//float fRatio;
|
|
|
|
if (ai_pathchecktime->value <= 0.0)
|
|
return true;
|
|
player = G_GetEntity(0);
|
|
if (!player)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//player = G_GetEntity(0);
|
|
//player = G_GetEntity(0);
|
|
VectorSub2D(player->origin, origin, vDelta2);
|
|
if (VectorLength2D(vDelta2) > Square(ai_pathcheckdist->value))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
fDistCap = (ai_pathchecktime->value * 250.0);
|
|
fDistSoFar = 0;
|
|
|
|
VectorCopy(vMins, CurrentPathNode()->point);
|
|
VectorCopy(vMaxs, CurrentPathNode()->point);
|
|
PathInfo *pNode = CurrentPathNode() - 1;
|
|
|
|
while (1)
|
|
{
|
|
if (pNode < LastPathNode())
|
|
break;
|
|
|
|
if (fDistSoFar <= fDistCap)
|
|
break;
|
|
|
|
fDelta = fDistCap + 0.001 - fDistSoFar;
|
|
|
|
if (pNode->dist < fDelta)
|
|
{
|
|
VectorCopy(pNode->point, vPos);
|
|
}
|
|
else
|
|
{
|
|
VectorSubtract(pNode[1].point, pNode[0].point, vPos);
|
|
VectorMA(pNode[1].point, fDelta / pNode->dist, vPos, vPos);
|
|
}
|
|
|
|
if (vPos[0] > vMaxs[0])
|
|
{
|
|
vMaxs[0] = vPos[0];
|
|
}
|
|
else
|
|
{
|
|
if (vPos[0] < vMins[0] )
|
|
{
|
|
vMins[0] = vPos[0];
|
|
}
|
|
}
|
|
|
|
if (vPos[1] > vMaxs[1])
|
|
{
|
|
vMaxs[1] = vPos[1];
|
|
}
|
|
else
|
|
{
|
|
if (vPos[1] < vMins[1])
|
|
{
|
|
vMins[1] = vPos[1];
|
|
}
|
|
}
|
|
|
|
if (vPos[2] > vMaxs[2])
|
|
{
|
|
vMaxs[2] = vPos[2];
|
|
}
|
|
else
|
|
{
|
|
if (vPos[2] < vMins[2])
|
|
{
|
|
vMins[2] = vPos[2];
|
|
}
|
|
}
|
|
|
|
fDistSoFar += fDelta;
|
|
pNode--;
|
|
}
|
|
|
|
vMins[0] -= 30;
|
|
vMins[1] -= 30;
|
|
vMins[2] -= 94;
|
|
|
|
vMaxs[0] += 30;
|
|
vMaxs[1] += 30;
|
|
vMaxs[2] += 94;
|
|
|
|
iNumBuddies = 0;
|
|
if ((Sentient *)m_pNextSquadMate != this)
|
|
{
|
|
do
|
|
{
|
|
if (m_pNextSquadMate->origin[0] > vMins[0] && m_pNextSquadMate->origin[0] < vMaxs[0]
|
|
&& m_pNextSquadMate->origin[1] > vMins[1] && m_pNextSquadMate->origin[1] < vMaxs[1]
|
|
&& m_pNextSquadMate->origin[2] > vMins[2] && m_pNextSquadMate->origin[2] < vMaxs[2]
|
|
)
|
|
{
|
|
VectorSub2D(m_pNextSquadMate->origin, origin, vDelta2);
|
|
if (vDelta2[0] <= -32 || vDelta2[0] >= 32 || vDelta2[1] <= -32 || vDelta2[1] >= 32)
|
|
{
|
|
if (DotProduct2D(vDelta2, m_pNextSquadMate->velocity) <= 0)
|
|
{
|
|
pBuddy[iNumBuddies++] = m_pNextSquadMate;
|
|
}
|
|
}
|
|
}
|
|
} while ((Sentient *)m_pNextSquadMate != this && iNumBuddies <= 255);
|
|
}
|
|
|
|
if (iNumBuddies == 0)
|
|
{
|
|
return true;
|
|
}
|
|
float fDist;
|
|
while (1)
|
|
{
|
|
i = 0;
|
|
|
|
if (iNumBuddies > 0)
|
|
break;
|
|
weird_lbl:
|
|
pNode++;
|
|
|
|
VectorCopy2D(pNode->point, vPos);
|
|
fDist = pNode->dist;
|
|
|
|
if (pNode >= CurrentPathNode())
|
|
return true;
|
|
}
|
|
float fDot;
|
|
while (1)
|
|
{
|
|
VectorSub2D(pBuddy[i]->origin, vPos, vDelta2);
|
|
if (VectorLength2D(vDelta2) <= 900)
|
|
{
|
|
return false;
|
|
}
|
|
fDot = DotProduct2D(vDelta2, pNode->dir);
|
|
if (fDot < 0.0 && -fDist <= fDot)
|
|
{
|
|
if (Square(CrossProduct2D(vDelta2, pNode->dir)) <= 900)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
if (++i >= iNumBuddies)
|
|
{
|
|
goto weird_lbl;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SimpleActor::ShortenPathToAvoidSquadMates
|
|
(
|
|
void
|
|
)
|
|
{
|
|
if (PathExists() && !PathComplete())
|
|
{
|
|
Vector vBuddyPos;
|
|
Vector vDelta;
|
|
Vector vGoal;
|
|
do
|
|
{
|
|
vGoal = PathGoal();
|
|
Actor *pSquadMate = (Actor *)m_pNextSquadMate.Pointer();
|
|
if (pSquadMate == this)
|
|
{
|
|
break;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
vGoal = PathGoal();
|
|
vBuddyPos = pSquadMate->origin;
|
|
if (pSquadMate->IsSubclassOfActor() && pSquadMate->PathExists())
|
|
{
|
|
vBuddyPos = pSquadMate->PathGoal();
|
|
}
|
|
vDelta.x = vGoal[0] - vBuddyPos.x;
|
|
if (vDelta.x >= -15.0 && vDelta.x <= 15.0)
|
|
{
|
|
vDelta.y = vGoal[1] - vBuddyPos.y;
|
|
vDelta.z = vGoal[2] - vBuddyPos.z;
|
|
|
|
if (vDelta.y >= -15.0 && vDelta.y <= 15.0 && vDelta.z >= 0.0 && vDelta.z <= 94.0)
|
|
break;
|
|
}
|
|
pSquadMate = (Actor *)pSquadMate->m_pNextSquadMate.Pointer();
|
|
if (pSquadMate == this)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
m_Path.Shorten(45.0);
|
|
|
|
} while (PathExists());
|
|
}
|
|
}
|
|
|
|
PathInfo *SimpleActor::CurrentPathNode
|
|
(
|
|
void
|
|
) const
|
|
{
|
|
return m_Path.CurrentNode();
|
|
}
|
|
|
|
PathInfo *SimpleActor::LastPathNode
|
|
(
|
|
void
|
|
) const
|
|
{
|
|
return m_Path.LastNode();
|
|
}
|
|
|
|
float SimpleActor::PathDist
|
|
(
|
|
void
|
|
) const
|
|
{
|
|
return m_Path.TotalDist();
|
|
}
|
|
|
|
bool SimpleActor::PathHasCompleteLookahead
|
|
(
|
|
void
|
|
) const
|
|
{
|
|
return m_Path.HasCompleteLookahead();
|
|
}
|
|
|
|
Vector SimpleActor::PathGoal
|
|
(
|
|
void
|
|
) const
|
|
{
|
|
return LastPathNode()->point;
|
|
}
|
|
|
|
float *SimpleActor::PathDelta
|
|
(
|
|
void
|
|
) const
|
|
{
|
|
return m_Path.CurrentDelta();
|
|
}
|
|
|
|
bool SimpleActor::PathGoalSlowdownStarted
|
|
(
|
|
void
|
|
) const
|
|
{
|
|
return m_fPathGoalTime >= level.time;
|
|
}
|
|
|
|
void SimpleActor::SetDest
|
|
(
|
|
vec3_t dest
|
|
)
|
|
{
|
|
VectorCopy(dest, m_Dest);
|
|
}
|
|
|
|
void SimpleActor::StopTurning
|
|
(
|
|
void
|
|
)
|
|
{
|
|
m_YawAchieved = true;
|
|
}
|
|
|
|
void SimpleActor::SetDesiredYaw
|
|
(
|
|
float yaw
|
|
)
|
|
{
|
|
m_YawAchieved = false;
|
|
m_DesiredYaw = yaw;
|
|
}
|
|
|
|
void SimpleActor::SetDesiredYawDir
|
|
(
|
|
vec3_t vec
|
|
)
|
|
{
|
|
m_YawAchieved = false;
|
|
m_DesiredYaw = vectoyaw(vec);
|
|
}
|
|
|
|
void SimpleActor::SetDesiredYawDest
|
|
(
|
|
vec3_t vec
|
|
)
|
|
{
|
|
float facedir[2];
|
|
VectorSub2D(vec, origin, facedir);
|
|
if (facedir[0] == 0 || facedir[1] == 0)
|
|
{
|
|
m_YawAchieved = false;
|
|
m_DesiredYaw = vectoyaw(vec);
|
|
}
|
|
}
|
|
|
|
void SimpleActor::UpdateEmotion
|
|
(
|
|
void
|
|
)
|
|
{
|
|
int anim;
|
|
if (IsDead())
|
|
{
|
|
Anim_Emotion(EMOTION_DEAD);
|
|
}
|
|
|
|
anim = GetEmotionAnim();
|
|
|
|
if (anim == -1)
|
|
{
|
|
Com_Printf(
|
|
"Failed to set emotion for (entnum %d, radnum %d, targetname '%s'\n",
|
|
entnum,
|
|
radnum,
|
|
targetname.c_str());
|
|
}
|
|
else
|
|
{
|
|
m_bSayAnimSet = true;
|
|
StartSayAnimSlot(anim);
|
|
}
|
|
}
|
|
|
|
int SimpleActor::GetEmotionAnim
|
|
(
|
|
void
|
|
)
|
|
{
|
|
const char *emotionanim = NULL;
|
|
int anim;
|
|
|
|
if (m_eEmotionMode)
|
|
{
|
|
switch (m_eEmotionMode)
|
|
{
|
|
case EMOTION_NEUTRAL:
|
|
case EMOTION_AIMING:
|
|
emotionanim = "facial_idle_neutral";
|
|
break;
|
|
case EMOTION_WORRY:
|
|
emotionanim = "facial_idle_worry";
|
|
break;
|
|
case EMOTION_PANIC:
|
|
emotionanim = "facial_idle_panic";
|
|
break;
|
|
case EMOTION_FEAR:
|
|
emotionanim = "facial_idle_fear";
|
|
break;
|
|
case EMOTION_DISGUST:
|
|
emotionanim = "facial_idle_disgust";
|
|
break;
|
|
case EMOTION_ANGER:
|
|
emotionanim = "facial_idle_anger";
|
|
break;
|
|
case EMOTION_DETERMINED:
|
|
case EMOTION_CURIOUS:
|
|
emotionanim = "facial_idle_determined";
|
|
break;
|
|
case EMOTION_DEAD:
|
|
emotionanim = "facial_idle_dead";
|
|
break;
|
|
default:
|
|
|
|
char assertStr[16317] = { 0 };
|
|
strcpy(assertStr, "\"Unknown value for m_EmotionMode in SimpleActor::GetEmotionAnim\"\n\tMessage: ");
|
|
Q_strcat(assertStr, sizeof(assertStr), DumpCallTrace(""));
|
|
assert(!assertStr);
|
|
return -1;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_csMood == STRING_NERVOUS)
|
|
{
|
|
emotionanim = "facial_idle_determined";
|
|
}
|
|
else if (m_csMood <= STRING_NERVOUS)
|
|
{
|
|
if (m_csMood != STRING_BORED)
|
|
{
|
|
assert(!"Unknown value for m_csMood");
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
emotionanim = "facial_idle_neutral";
|
|
}
|
|
|
|
}
|
|
else if (m_csMood == STRING_CURIOUS)
|
|
{
|
|
emotionanim = "facial_idle_determined";
|
|
}
|
|
else if (m_csMood != STRING_ALERT)
|
|
{
|
|
assert(!"Unknown value for m_csMood");
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
if (emotionanim == NULL)
|
|
{
|
|
emotionanim = "facial_idle_anger";
|
|
//assert(!"Unexpected behaviour in SimpleActor::GetEmotionAnim");
|
|
}
|
|
|
|
anim = gi.Anim_NumForName(edict->tiki, emotionanim);
|
|
if (anim == -1)
|
|
Com_Printf(
|
|
"^~^~^ SimpleActor::GetEmotionAnim: unknown animation '%s' in '%s'\n",
|
|
emotionanim,
|
|
edict->tiki->a->name);
|
|
return anim;
|
|
}
|
|
|
|
int SimpleActor::GetMotionSlot
|
|
(
|
|
int slot
|
|
)
|
|
{
|
|
if( m_AnimMotionHigh )
|
|
return slot + 3;
|
|
else
|
|
return slot;
|
|
}
|
|
|
|
int SimpleActor::GetActionSlot
|
|
(
|
|
int slot
|
|
)
|
|
{
|
|
if( m_AnimActionHigh )
|
|
return slot + 9;
|
|
else
|
|
return slot + 6;
|
|
}
|
|
|
|
int SimpleActor::GetSaySlot
|
|
(
|
|
void
|
|
)
|
|
{
|
|
return m_AnimDialogHigh ? 13 : 12;
|
|
}
|
|
|
|
void SimpleActor::StartCrossBlendAnimSlot
|
|
(
|
|
int slot
|
|
)
|
|
{
|
|
if (m_weightType[slot] == 1)
|
|
{
|
|
m_weightType[slot] = 4;
|
|
}
|
|
else
|
|
{
|
|
if (m_weightType[slot] < 1)
|
|
return;
|
|
if (m_weightType[slot] == 6)
|
|
m_weightType[slot] = 5;
|
|
else
|
|
m_weightType[slot] = 3;
|
|
}
|
|
m_weightCrossBlend[slot] = 1.0;
|
|
m_weightBase[slot] = edict->s.frameInfo[slot].weight;
|
|
}
|
|
|
|
void SimpleActor::StartMotionAnimSlot
|
|
(
|
|
int slot,
|
|
int anim,
|
|
float weight
|
|
)
|
|
{
|
|
int iSlot = GetMotionSlot(slot);
|
|
|
|
m_weightCrossBlend[iSlot] = 0.0;
|
|
m_weightType[iSlot] = 1;
|
|
m_weightBase[iSlot] = weight;
|
|
|
|
NewAnim(anim, iSlot, 1.0);
|
|
animFlags[iSlot] |= ANIM_NOACTION;
|
|
|
|
SetTime(iSlot, 0.0);
|
|
|
|
UpdateNormalAnimSlot(iSlot);
|
|
}
|
|
|
|
void SimpleActor::StartAimMotionAnimSlot
|
|
(
|
|
int slot,
|
|
int anim
|
|
)
|
|
{
|
|
int iSlot = GetMotionSlot(slot);
|
|
|
|
m_weightCrossBlend[iSlot] = 0.0;
|
|
m_weightType[iSlot] = 1;
|
|
|
|
NewAnim(anim, iSlot, 1.0);
|
|
animFlags[iSlot] |= ANIM_NOACTION;
|
|
|
|
SetTime(iSlot, 0.0);
|
|
|
|
UpdateNormalAnimSlot(iSlot);
|
|
}
|
|
|
|
void SimpleActor::StartActionAnimSlot
|
|
(
|
|
int anim
|
|
)
|
|
{
|
|
int iSlot = GetActionSlot(0);
|
|
|
|
m_weightCrossBlend[iSlot] = 0.0;
|
|
m_weightType[iSlot] = 2;
|
|
m_weightBase[iSlot] = 1.0;
|
|
|
|
NewAnim(anim, iSlot, 1.0);
|
|
|
|
SetTime(iSlot, 0.0);
|
|
|
|
UpdateNormalAnimSlot(iSlot);
|
|
}
|
|
|
|
void SimpleActor::StartSayAnimSlot
|
|
(
|
|
int anim
|
|
)
|
|
{
|
|
int iSlot = GetSaySlot();
|
|
|
|
m_weightCrossBlend[iSlot] = 0.0;
|
|
m_weightType[iSlot] = 6;
|
|
m_weightBase[iSlot] = 1.0;
|
|
|
|
NewAnim(anim, iSlot, 1.0);
|
|
animFlags[iSlot] |= ANIM_NOACTION;
|
|
|
|
SetTime(iSlot, 0.0);
|
|
UpdateSayAnimSlot(iSlot);
|
|
}
|
|
|
|
void SimpleActor::StartAimAnimSlot
|
|
(
|
|
int slot,
|
|
int anim
|
|
)
|
|
{
|
|
int iSlot = GetActionSlot(slot);
|
|
|
|
m_weightCrossBlend[iSlot] = 0.0;
|
|
m_weightType[iSlot] = 7;
|
|
|
|
NewAnim(anim, iSlot, 1.0);
|
|
|
|
SetTime(iSlot, 0.0);
|
|
|
|
UpdateNormalAnimSlot(iSlot);
|
|
}
|
|
|
|
void SimpleActor::SetBlendedWeight
|
|
(
|
|
int slot
|
|
)
|
|
{
|
|
m_bUpdateAnimDoneFlags |= 1 << slot;
|
|
if (m_weightCrossBlend[slot] < 1.0)
|
|
{
|
|
edict->s.frameInfo[slot].weight = (3.0 - m_weightCrossBlend[slot] - m_weightCrossBlend[slot])
|
|
* Square(m_weightCrossBlend[slot])
|
|
* m_weightBase[slot];
|
|
}
|
|
else
|
|
{
|
|
m_weightCrossBlend[slot] = 1.0;
|
|
edict->s.frameInfo[slot].weight = m_weightBase[slot];
|
|
}
|
|
}
|
|
|
|
void SimpleActor::EventSetAnimLength
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
int slot;
|
|
float length;
|
|
|
|
if (ev->NumArgs() != 1)
|
|
{
|
|
ScriptError("bad number of arguments");
|
|
}
|
|
|
|
length = ev->GetFloat(1);
|
|
|
|
if (length <= 0)
|
|
{
|
|
ScriptError("Positive lengths only allowed");
|
|
}
|
|
|
|
if (m_bMotionAnimSet)
|
|
{
|
|
ScriptError("Must set anim before length");
|
|
}
|
|
|
|
slot = GetMotionSlot(0);
|
|
|
|
if (animFlags[slot] & ANIM_LOOP)
|
|
{
|
|
gi.Anim_Frametime(edict->tiki, edict->s.frameInfo[slot].index);
|
|
|
|
animFlags[slot] = (animFlags[slot] | ANIM_NODELTA) & ~ANIM_FINISHED;
|
|
|
|
animtimes[slot] = Square(gi.Anim_NumFrames(edict->tiki, edict->s.frameInfo[slot].index) - 1);
|
|
|
|
SetOnceType(slot);
|
|
}
|
|
|
|
SetSyncTime(0);
|
|
|
|
if (length > animtimes[slot])
|
|
{
|
|
ScriptError("cannot lengthen animation which has length %f", animtimes[slot]);
|
|
}
|
|
|
|
animtimes[slot] = length;
|
|
animFlags[slot] = (animFlags[slot] | ANIM_NODELTA) & ~ANIM_FINISHED;
|
|
}
|
|
|
|
void SimpleActor::UpdateNormalAnimSlot
|
|
(
|
|
int slot
|
|
)
|
|
{
|
|
m_weightCrossBlend[slot] += m_fCrossblendTime == 0.0 ? 1.0 : level.frametime / m_fCrossblendTime;
|
|
|
|
SetBlendedWeight(slot);
|
|
}
|
|
|
|
void SimpleActor::UpdateCrossBlendAnimSlot
|
|
(
|
|
int slot
|
|
)
|
|
{
|
|
m_weightCrossBlend[slot] -= m_fCrossblendTime == 0.0 ? 1.0 : level.frametime / m_fCrossblendTime;
|
|
|
|
if (m_weightCrossBlend[slot] > 0.0)
|
|
{
|
|
SetBlendedWeight(slot);
|
|
}
|
|
else
|
|
{
|
|
m_weightType[slot] = 8;
|
|
edict->s.frameInfo[slot].weight = 0.0;
|
|
}
|
|
}
|
|
|
|
void SimpleActor::UpdateCrossBlendDialogAnimSlot
|
|
(
|
|
int slot
|
|
)
|
|
{
|
|
m_weightCrossBlend[slot] -= m_iSaySlot < 0 ? level.frametime + level.frametime : level.frametime / 0.1;
|
|
|
|
if (m_weightCrossBlend[slot] > 0.0)
|
|
{
|
|
SetBlendedWeight(slot);
|
|
}
|
|
else
|
|
{
|
|
m_weightType[slot] = 8;
|
|
edict->s.frameInfo[slot].weight = 0.0;
|
|
}
|
|
}
|
|
|
|
void SimpleActor::UpdateSayAnimSlot
|
|
(
|
|
int slot
|
|
)
|
|
{
|
|
m_weightCrossBlend[slot] += m_iSaySlot < 0 ? level.frametime + level.frametime : level.frametime / 0.1;
|
|
|
|
SetBlendedWeight(slot);
|
|
}
|
|
|
|
void SimpleActor::UpdateLastFrameSlot
|
|
(
|
|
int slot
|
|
)
|
|
{
|
|
StopAnimating(slot);
|
|
}
|
|
|
|
void SimpleActor::UpdateAnimSlot
|
|
(
|
|
int slot
|
|
)
|
|
{
|
|
int weightType = m_weightType[slot];
|
|
switch (weightType)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
case 7:
|
|
UpdateNormalAnimSlot(slot);
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
UpdateCrossBlendAnimSlot(slot);
|
|
break;
|
|
case 5:
|
|
UpdateCrossBlendDialogAnimSlot(slot);
|
|
break;
|
|
case 6:
|
|
UpdateSayAnimSlot(slot);
|
|
break;
|
|
case 8:
|
|
UpdateLastFrameSlot(slot);
|
|
break;
|
|
default:
|
|
assert(weightType && !"impleActor::UpdateAnimSlot: Bad weight type.");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SimpleActor::StopAllAnimating
|
|
(
|
|
void
|
|
)
|
|
{
|
|
SetSyncTime(0);
|
|
|
|
for (int slot = 0; slot < MAX_FRAMEINFOS; slot++)
|
|
{
|
|
StopAnimating(slot);
|
|
}
|
|
}
|
|
|
|
void SimpleActor::ChangeMotionAnim
|
|
(
|
|
void
|
|
)
|
|
{
|
|
//int lastMotionSlot;
|
|
//int firstMotionSlot;
|
|
int iSlot;
|
|
int i;
|
|
|
|
m_bMotionAnimSet = false;
|
|
m_iMotionSlot = -1;
|
|
m_bLevelMotionAnim = false;
|
|
|
|
if (m_ChangeMotionAnimIndex != level.frame_skel_index)
|
|
{
|
|
m_ChangeMotionAnimIndex = level.frame_skel_index;
|
|
|
|
MPrintf("Swapping motion channels....\n");
|
|
for (iSlot = GetMotionSlot(0), i = 0; i < 3; i++, iSlot++)
|
|
{
|
|
StartCrossBlendAnimSlot(iSlot);
|
|
}
|
|
m_AnimDialogHigh = !m_AnimDialogHigh;
|
|
|
|
}
|
|
|
|
for (iSlot = GetMotionSlot(0), i = 0; i < 3; i++, iSlot++)
|
|
{
|
|
StopAnimating(iSlot);
|
|
}
|
|
}
|
|
|
|
void SimpleActor::ChangeActionAnim
|
|
(
|
|
void
|
|
)
|
|
{
|
|
int iSlot;
|
|
int i;
|
|
|
|
m_bAimAnimSet = false;
|
|
m_bActionAnimSet = false;
|
|
m_iActionSlot = -1;
|
|
m_bLevelActionAnim = false;
|
|
|
|
|
|
if (m_ChangeActionAnimIndex != level.frame_skel_index)
|
|
{
|
|
m_ChangeActionAnimIndex = level.frame_skel_index;
|
|
|
|
MPrintf("Swapping action channels....\n");
|
|
|
|
iSlot = GetActionSlot(0);
|
|
for (i = iSlot; i < iSlot + 3; i++)
|
|
{
|
|
animFlags[i] |= ANIM_NOACTION;
|
|
StartCrossBlendAnimSlot(i);
|
|
}
|
|
m_AnimDialogHigh = !m_AnimDialogHigh; // toggle
|
|
}
|
|
|
|
iSlot = GetActionSlot(0);
|
|
for (i = iSlot; i < iSlot + 3; i++)
|
|
{
|
|
StopAnimating(iSlot);
|
|
}
|
|
}
|
|
|
|
void SimpleActor::ChangeSayAnim
|
|
(
|
|
void
|
|
)
|
|
{
|
|
int iSlot;
|
|
|
|
m_bSayAnimSet = false;
|
|
m_bLevelSayAnim = 0;
|
|
m_iVoiceTime = level.inttime;
|
|
m_iSaySlot = -1;
|
|
|
|
if (m_ChangeSayAnimIndex != level.frame_skel_index)
|
|
{
|
|
m_ChangeSayAnimIndex = level.frame_skel_index;
|
|
|
|
MPrintf("Swapping dialog channel....\n");
|
|
|
|
iSlot = GetSaySlot();
|
|
StartCrossBlendAnimSlot(iSlot);
|
|
|
|
m_AnimDialogHigh = !m_AnimDialogHigh; // toggle
|
|
}
|
|
|
|
iSlot = GetSaySlot();
|
|
StopAnimating(iSlot);
|
|
}
|
|
|
|
void SimpleActor::UpdateAim
|
|
(
|
|
void
|
|
)
|
|
{
|
|
float dir;
|
|
|
|
int aimForwardSlot;
|
|
int aimUpSlot;
|
|
int aimDownSlot;
|
|
|
|
if (m_bAimAnimSet)
|
|
{
|
|
dir = AngleNormalize180(-m_DesiredGunDir[0]);
|
|
|
|
aimForwardSlot = GetActionSlot(0);
|
|
aimUpSlot = aimForwardSlot + 1;
|
|
aimDownSlot = aimForwardSlot + 2;
|
|
|
|
|
|
float factor;
|
|
if (dir < 0)
|
|
{
|
|
if (dir < m_fAimLimit_down)
|
|
dir = m_fAimLimit_down;
|
|
|
|
factor = dir / m_fAimLimit_down;
|
|
|
|
m_weightBase[aimForwardSlot] = 0;
|
|
m_weightBase[aimUpSlot] = 1 - factor;
|
|
m_weightBase[aimDownSlot] = factor;
|
|
}
|
|
else
|
|
{
|
|
if (dir > m_fAimLimit_up)
|
|
dir = m_fAimLimit_up;
|
|
|
|
factor = dir / m_fAimLimit_up;
|
|
|
|
m_weightBase[aimForwardSlot] = factor;
|
|
m_weightBase[aimUpSlot] = 1 - factor;
|
|
m_weightBase[aimDownSlot] = 0;
|
|
|
|
}
|
|
SetControllerAngles(TORSO_TAG, vec_origin);
|
|
}
|
|
}
|
|
|
|
void SimpleActor::UpdateAimMotion
|
|
(
|
|
void
|
|
)
|
|
{
|
|
int slot = GetMotionSlot(0);
|
|
|
|
if (m_fCrouchWeight < 0.0)
|
|
{
|
|
m_weightBase[slot] = 0.0;
|
|
m_weightBase[slot + 1] = m_fCrouchWeight + 1.0;
|
|
m_weightBase[slot + 2] = -m_fCrouchWeight;
|
|
}
|
|
else
|
|
{
|
|
m_weightBase[slot] = m_fCrouchWeight;
|
|
m_weightBase[slot + 1] = 1.0 - m_fCrouchWeight;
|
|
m_weightBase[slot + 2] = 0.0;
|
|
}
|
|
}
|
|
|
|
void SimpleActor::EventAIOn
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
m_bDoAI = true;
|
|
}
|
|
|
|
void SimpleActor::EventAIOff
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
m_bDoAI = false;
|
|
}
|
|
|
|
void SimpleActor::EventGetWeaponGroup
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
const_str csWeaponGroup;
|
|
Weapon *weapon = GetActiveWeapon(WEAPON_MAIN);
|
|
if (!weapon)
|
|
{
|
|
csWeaponGroup = STRING_UNARMED;
|
|
}
|
|
else
|
|
{
|
|
csWeaponGroup = weapon->GetWeaponGroup();
|
|
if (csWeaponGroup == STRING_EMPTY)
|
|
{
|
|
csWeaponGroup = STRING_UNARMED;
|
|
}
|
|
}
|
|
ev->AddConstString(csWeaponGroup);
|
|
}
|
|
|
|
void SimpleActor::EventGetWeaponType
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
Weapon *weapon;
|
|
const_str csWeaponType;
|
|
|
|
if (!m_pTurret)
|
|
{
|
|
weapon = GetActiveWeapon(WEAPON_MAIN);
|
|
}
|
|
|
|
if (!weapon)
|
|
{
|
|
csWeaponType = STRING_RIFLE;
|
|
}
|
|
else
|
|
{
|
|
int iWeaponClass = weapon->GetWeaponClass();
|
|
|
|
switch (iWeaponClass)
|
|
{
|
|
case WEAPON_CLASS_PISTOL:
|
|
csWeaponType = STRING_PISTOL;
|
|
break;
|
|
case WEAPON_CLASS_RIFLE:
|
|
csWeaponType = STRING_RIFLE;
|
|
break;
|
|
case WEAPON_CLASS_SMG:
|
|
csWeaponType = STRING_SMG;
|
|
break;
|
|
case WEAPON_CLASS_MG:
|
|
csWeaponType = STRING_MG;
|
|
break;
|
|
case WEAPON_CLASS_GRENADE:
|
|
csWeaponType = STRING_GRENADE;
|
|
break;
|
|
case WEAPON_CLASS_HEAVY:
|
|
csWeaponType = STRING_HEAVY;
|
|
break;
|
|
case WEAPON_CLASS_CANNON:
|
|
csWeaponType = STRING_CANNON;
|
|
break;
|
|
case WEAPON_CLASS_ITEM:
|
|
csWeaponType = STRING_ITEM;
|
|
break;
|
|
case WEAPON_CLASS_ITEM2:
|
|
csWeaponType = STRING_ITEM2;
|
|
break;
|
|
case WEAPON_CLASS_ITEM3:
|
|
csWeaponType = STRING_ITEM3;
|
|
break;
|
|
case WEAPON_CLASS_ITEM4:
|
|
csWeaponType = STRING_ITEM4;
|
|
break;
|
|
default:
|
|
csWeaponType = STRING_EMPTY;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
ev->AddConstString(csWeaponType);
|
|
}
|
|
|
|
void SimpleActor::EventGetPainHandler
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
ScriptVariable var;
|
|
|
|
m_PainHandler.GetScriptValue(&var);
|
|
|
|
ev->AddValue(var);
|
|
}
|
|
|
|
void SimpleActor::EventSetPainHandler
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
if (ev->IsFromScript())
|
|
{
|
|
ScriptVariable var = ev->GetValue(1);
|
|
|
|
m_PainHandler.SetScript(var);
|
|
}
|
|
else
|
|
{
|
|
str varString = ev->GetString(1);
|
|
m_PainHandler.SetScript(varString);
|
|
}
|
|
}
|
|
|
|
void SimpleActor::EventGetDeathHandler
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
ScriptVariable var;
|
|
|
|
m_DeathHandler.GetScriptValue(&var);
|
|
|
|
ev->AddValue(var);
|
|
}
|
|
|
|
void SimpleActor::EventSetDeathHandler
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
if (ev->IsFromScript())
|
|
{
|
|
ScriptVariable var = ev->GetValue(1);
|
|
|
|
m_DeathHandler.SetScript(var);
|
|
}
|
|
else
|
|
{
|
|
str varString = ev->GetString(1);
|
|
m_DeathHandler.SetScript(varString);
|
|
}
|
|
}
|
|
|
|
void SimpleActor::EventGetAttackHandler
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
ScriptVariable var;
|
|
|
|
m_AttackHandler.GetScriptValue(&var);
|
|
|
|
ev->AddValue(var);
|
|
}
|
|
|
|
void SimpleActor::EventSetAttackHandler
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
if (ev->IsFromScript())
|
|
{
|
|
ScriptVariable var = ev->GetValue(1);
|
|
|
|
m_AttackHandler.SetScript(var);
|
|
}
|
|
else
|
|
{
|
|
str varString = ev->GetString(1);
|
|
m_AttackHandler.SetScript(varString);
|
|
}
|
|
}
|
|
|
|
void SimpleActor::EventGetSniperHandler
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
ScriptVariable var;
|
|
|
|
m_SniperHandler.GetScriptValue(&var);
|
|
|
|
ev->AddValue(var);
|
|
}
|
|
|
|
void SimpleActor::EventSetSniperHandler
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
if (ev->IsFromScript())
|
|
{
|
|
ScriptVariable var = ev->GetValue(1);
|
|
|
|
m_SniperHandler.SetScript(var);
|
|
}
|
|
else
|
|
{
|
|
str varString = ev->GetString(1);
|
|
m_SniperHandler.SetScript(varString);
|
|
}
|
|
}
|
|
|
|
void SimpleActor::EventSetCrossblendTime
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
m_fCrossblendTime = ev->GetFloat(1);
|
|
}
|
|
|
|
void SimpleActor::EventGetCrossblendTime
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
ev->AddFloat(m_fCrossblendTime);
|
|
}
|
|
|
|
void SimpleActor::EventSetEmotion
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
switch (ev->GetConstString(1))
|
|
{
|
|
case STRING_EMOTION_NONE:
|
|
Anim_Emotion(EMOTION_NONE);
|
|
break;
|
|
case STRING_EMOTION_NEUTRAL:
|
|
Anim_Emotion(EMOTION_NEUTRAL);
|
|
break;
|
|
case STRING_EMOTION_WORRY:
|
|
Anim_Emotion(EMOTION_WORRY);
|
|
break;
|
|
case STRING_EMOTION_PANIC:
|
|
Anim_Emotion(EMOTION_PANIC);
|
|
break;
|
|
case STRING_EMOTION_FEAR:
|
|
Anim_Emotion(EMOTION_FEAR);
|
|
break;
|
|
case STRING_EMOTION_DISGUST:
|
|
Anim_Emotion(EMOTION_DISGUST);
|
|
break;
|
|
case STRING_EMOTION_ANGER:
|
|
Anim_Emotion(EMOTION_ANGER);
|
|
break;
|
|
case STRING_EMOTION_AIMING:
|
|
Anim_Emotion(EMOTION_AIMING);
|
|
break;
|
|
case STRING_EMOTION_DETERMINED:
|
|
Anim_Emotion(EMOTION_DETERMINED);
|
|
break;
|
|
case STRING_EMOTION_DEAD:
|
|
Anim_Emotion(EMOTION_DEAD);
|
|
break;
|
|
case STRING_EMOTION_CURIOUS:
|
|
Anim_Emotion(EMOTION_CURIOUS);
|
|
break;
|
|
default:
|
|
assert(!"Unknown emotion mode specified in script.");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SimpleActor::EventGetPosition
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
ev->AddConstString(m_csCurrentPosition);
|
|
}
|
|
|
|
void SimpleActor::EventSetPosition
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
m_csCurrentPosition = ev->GetConstString(1);
|
|
}
|
|
|
|
void SimpleActor::EventGetAnimMode
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
// not found in ida
|
|
}
|
|
|
|
void SimpleActor::EventSetAnimMode
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
// not found in ida
|
|
}
|
|
|
|
void SimpleActor::EventSetAnimFinal
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
ScriptError("animfinal is obsolete");
|
|
}
|
|
|
|
void SimpleActor::DesiredAnimation
|
|
(
|
|
int eAnimMode,
|
|
const_str csAnimString
|
|
)
|
|
{
|
|
//fixme: this is an inline function.
|
|
m_eNextAnimMode = eAnimMode;
|
|
m_csNextAnimString = csAnimString;
|
|
m_bNextForceStart = false;
|
|
}
|
|
|
|
void SimpleActor::StartAnimation
|
|
(
|
|
int eAnimMode,
|
|
const_str csAnimString
|
|
)
|
|
{
|
|
//fixme: this is an inline function.
|
|
m_eNextAnimMode = eAnimMode;
|
|
m_csNextAnimString = csAnimString;
|
|
m_bNextForceStart = true;
|
|
}
|
|
|
|
void SimpleActor::DesiredAnimation
|
|
(
|
|
int eAnimMode,
|
|
ScriptThreadLabel AnimLabel
|
|
)
|
|
{
|
|
//fixme: this is an inline function.
|
|
m_eNextAnimMode = eAnimMode;
|
|
m_csNextAnimString = STRING_NULL;
|
|
m_NextAnimLabel = AnimLabel;
|
|
m_bNextForceStart = false;
|
|
}
|
|
|
|
void SimpleActor::StartAnimation
|
|
(
|
|
int eAnimMode,
|
|
ScriptThreadLabel AnimLabel
|
|
)
|
|
{
|
|
//fixme: this is an inline function.
|
|
m_eNextAnimMode = eAnimMode;
|
|
m_csNextAnimString = STRING_NULL;
|
|
m_NextAnimLabel = AnimLabel;
|
|
m_bNextForceStart = true;
|
|
}
|
|
|
|
void SimpleActor::ContinueAnimationAllowNoPath
|
|
(
|
|
void
|
|
)
|
|
{
|
|
if (m_eNextAnimMode < 0)
|
|
{
|
|
m_bNextForceStart = false;
|
|
m_csNextAnimString = STRING_NULL;
|
|
m_eNextAnimMode = m_eAnimMode;
|
|
m_NextAnimLabel = m_Anim;
|
|
}
|
|
}
|
|
|
|
void SimpleActor::ContinueAnimation
|
|
(
|
|
void
|
|
)
|
|
{
|
|
int eAnimMode = m_eNextAnimMode;
|
|
if (eAnimMode < 0)
|
|
{
|
|
m_bNextForceStart = false;
|
|
m_csNextAnimString = STRING_NULL;
|
|
m_eNextAnimMode = m_eAnimMode;
|
|
m_NextAnimLabel = m_Anim;
|
|
eAnimMode = m_eAnimMode;
|
|
}
|
|
|
|
if (eAnimMode <= 3 && !PathExists())
|
|
{
|
|
//assert(!DumpCallTrace("ContinueAnimation() called on a pathed animation, but no path exists"));
|
|
Anim_Stand();
|
|
}
|
|
}
|
|
|
|
void SimpleActor::SetPathGoalEndAnim
|
|
(
|
|
const_str csEndAnim
|
|
)
|
|
{
|
|
//fixme: this is an inline function
|
|
m_csPathGoalEndAnimScript = csEndAnim;
|
|
}
|
|
|
|
bool SimpleActor::UpdateSelectedAnimation
|
|
(
|
|
void
|
|
)
|
|
{
|
|
if (m_csNextAnimString == STRING_NULL)
|
|
{
|
|
if (m_bNextForceStart)
|
|
{
|
|
m_Anim = m_NextAnimLabel;
|
|
m_eAnimMode = m_eNextAnimMode;
|
|
if (m_eNextAnimMode != 3)
|
|
SetPathGoalEndAnim(STRING_EMPTY);
|
|
m_bStartPathGoalEndAnim = false;
|
|
m_eNextAnimMode = -1;
|
|
return true;
|
|
}
|
|
|
|
if (m_pAnimThread)
|
|
{
|
|
if (m_eAnimMode == m_eNextAnimMode)
|
|
{
|
|
if (m_Anim == m_NextAnimLabel)
|
|
{
|
|
m_bStartPathGoalEndAnim = false;
|
|
m_eNextAnimMode = -1;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
m_Anim = m_NextAnimLabel;
|
|
m_eAnimMode = m_eNextAnimMode;
|
|
if (m_eNextAnimMode != 3)
|
|
SetPathGoalEndAnim(STRING_EMPTY);
|
|
m_bStartPathGoalEndAnim = false;
|
|
m_eNextAnimMode = -1;
|
|
return true;
|
|
}
|
|
|
|
if (m_bNextForceStart)
|
|
{
|
|
|
|
Com_Printf("UpdateSelectedAnimation\n");
|
|
m_Anim.TrySetScript(m_csNextAnimString);
|
|
m_eAnimMode = m_eNextAnimMode;
|
|
if (m_eNextAnimMode != 3)
|
|
SetPathGoalEndAnim(STRING_EMPTY);
|
|
m_bStartPathGoalEndAnim = false;
|
|
m_eNextAnimMode = -1;
|
|
return true;
|
|
}
|
|
|
|
if (!m_pAnimThread || m_eAnimMode != m_eNextAnimMode)
|
|
{
|
|
m_Anim.TrySetScript(m_csNextAnimString);
|
|
m_eAnimMode = m_eNextAnimMode;
|
|
if (m_eNextAnimMode != 3)
|
|
SetPathGoalEndAnim(STRING_EMPTY);
|
|
m_bStartPathGoalEndAnim = false;
|
|
m_eNextAnimMode = -1;
|
|
return true;
|
|
}
|
|
|
|
if (m_fPathGoalTime <= level.time)
|
|
{
|
|
if (!m_Anim.IsFile(m_csNextAnimString))
|
|
{
|
|
m_Anim.TrySetScript(m_csNextAnimString);
|
|
m_eAnimMode = m_eNextAnimMode;
|
|
if (m_eNextAnimMode != 3)
|
|
SetPathGoalEndAnim(STRING_EMPTY);
|
|
m_bStartPathGoalEndAnim = false;
|
|
m_eNextAnimMode = -1;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
m_eNextAnimMode = -1;
|
|
if (m_bStartPathGoalEndAnim)
|
|
{
|
|
m_bStartPathGoalEndAnim = false;
|
|
|
|
if (!m_Anim.IsFile(m_csPathGoalEndAnimScript))
|
|
{
|
|
m_Anim.TrySetScript(m_csPathGoalEndAnimScript);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const char *SimpleActor::DumpCallTrace
|
|
(
|
|
const char *pszFmt,
|
|
...
|
|
) const
|
|
{
|
|
OVERLOADED_ERROR();
|
|
return "overloaded version should always get called";
|
|
}
|