Fix unwanted head turn reset - FreeSlave/halflife-updated GitHub Wiki
Talk monster (scientist or security guard) may stop looking at their talk target during the speech. E.g you might noticed scientists stop looking at you as a player, even though they still have much to say. Let's search for the root of the issue.
Take a look at code choosing the idle schedule for talkmonster
if (IsTalking())
// look at who we're talking to
return slTlkIdleEyecontact;
else
// regular standing idle
return slIdleStand;
If monster is currently talking then slTlkIdleEyecontact
is chosen and everything is okay, as TASK_TLK_EYECONTACT
(which is part of the schedule) constantly makes a monster to turn the head to its talk target. Otherwise slIdleStand
is chosen. Scientist code checks for that schedule and leads to choosing the slIdleSciStand
instead.
psched = CTalkMonster::GetScheduleOfType(Type);
if (psched == slIdleStand)
return slIdleSciStand;
else
return psched;
We won't discuss how smelly this code is, instead we'll take a look at the task list
Task_t tlIdleSciStand[] =
{
{TASK_STOP_MOVING, 0},
{TASK_SET_ACTIVITY, (float)ACT_IDLE},
{TASK_WAIT, (float)2}, // repick IDLESTAND every two seconds.
{TASK_TLK_HEADRESET, (float)0}, // reset head position
};
The TASK_TLK_HEADRESET
in its turn clears the talk target
case TASK_TLK_HEADRESET:
// reset head position after looking at something
m_hTalkTarget = NULL;
TaskComplete();
break;
Also notice the TASK_WAIT
which makes the monster to wait 2 seconds before performing the head reset, just playing the idle animation.
Now imagine the situation when the monster gets something to say to other npc or a player during these 2 seconds. The monster gets talk target, but schedule continues performing as usual. There's a common code for talkmonster's tasks to check if monster has a talk target and turn the head to them, and TASK_WAIT
falls here too.
if (IsTalking() && m_hTalkTarget != NULL)
{
IdleHeadTurn(m_hTalkTarget->pev->origin);
}
else
{
SetBoneController(0, 0);
}
But when TASK_WAIT
is completed, the schedule proceeds to TASK_TLK_HEADRESET
which resets the talk monster and common code above falls into another branch resetting the head bone controller. At this point the m_hTalkTarget
for monster is lost and any other task will not be able to make a monster turn their head to the talk target as there's none.
Yay, we've got to the root of evil! What can we do about that? As a simple fix you can go to the TASK_TLK_HEADRESET
implementation and apply this modification:
case TASK_TLK_HEADRESET:
// reset head position after looking at something
- m_hTalkTarget = NULL;
+ if (!IsTalking())
+ m_hTalkTarget = NULL;
TaskComplete();
break;
If the monster's still talking the talk target won't be lost. Eventually it will reset in some other idle schedule when monster is not talking anymore.