openmohaa/code/fgame/simpleactor.cpp
2023-07-05 20:52:55 +02:00

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";
}