Ok, looking at how I did sitting down on the ground in my third module I actually just did a Stand Your Ground thing in the script, then they sit down with the kemo_animations script, like I use here too, and then in one of the first nodes I run a script like this where I force them to watch the fireplace in the middle of camp, and here I actually have SetOrientOnDialog(oCompanion,TRUE);
I’ll try in my new module and do a SetOrientOnDialog(oCompanion,FALSE); and see if this helps the animation. The companion isn’t supposed to face the PC anyway since he’s in another room lying in a bed so. Well, I’ll see what happens…
Edit: Actually, later in another node in my previous module I do a SetOrientOnDialog(GetNearestObjectByTag(“companiontag”,oPC),FALSE);
Edit: SetOrientOnDialog doesn’t seem to solve the issue. I’m thinking that maybe I should make the “idle” animation a loop? Cause right now, I noticed that even if the companion stands up, it seems after a few seconds she actually sits down again if I don’t talk to her. So, it’s like, whenever the idle animation has done its run, she reverts back to the kemo_animation. Could that be so? Can any of you see in the kemo_animation script here if that may be the case? I’m not always that good at reading more complicated scripts…
// kemo_animation
/*
Conversation script wrapper for PlayCustomAnimation() command.
Parameters:
sTarget - tag of creature to play animation - default is conversation owner.
sAnim - animation to play - uses PlayCustomAnimation's *, # and % rules.
iFrames - as the delay with ga_play_custom_animation, but counted in fps from 3DSMax to make
it easier to enter the length of the *previous* animation
Animation special symbols:
When "%" is passed to PlayCustomAnimation, it resets the creature to its idle animation,
it kind of clears the current animation for the creature.
Asterisk "*" can be used to fill in the prefix of an animation. For example: PlayCustomAnimation("*dodge01")
will fill out the skeleton and gender data and choose correctly between MD009_dodge01, FH007_dodge01, etc.
Pound "#" is for facial animations. For example #bigsmile to get a character to smile. These didn't prove to
look as expected and are to be avoided if possible.
*/
// DBR 6/20/06
//EPF -- removed the AssignCommand from the delay code, since that seems to be a likely cause of interrupting animations.
// ChazM 5/1/07 added comments collected from DBR & EPF
// 7/13/08 modified by KEMO for Custom Animations; called by conversation through gui_hss_pc_tool
// 2/09 now called by KEMO_ANIM GUI
#include "ginc_param_const"
#include "kemo_includes"
void main(string sTarget, string sAnimA, int iFrames, int iNoLoop)
{
object oTarget = GetTarget(sTarget);
float fFrames = IntToFloat(iFrames); // frames of first animation (usually between 60 and 120)
float fFramerate = 1.0/30.0;
float fDelayUntilStart = fFrames*fFramerate; // duration of first anim based on frames
fDelayUntilStart = fDelayUntilStart * RacialDelay(oTarget);
string sAnimB = "kemo_" + sAnimA;
string sAnimC = sAnimB + "i";
if (iNoLoop) sAnimC = "idle"; // if this is a fireandforget animation, break the loop
// with an idle instead of animation #2
AssignCommand(oTarget,StoreAnimation(sAnimB,fDelayUntilStart));
AssignCommand(oTarget,StoreIdle(sAnimC));
SetLocalLocation(oTarget,"AnimationLocation",GetLocation(oTarget));
int iOverlap = GetLocalInt(oTarget,"AnimationOverlap"); //animation count (to check overlapped animations)
iOverlap++; SetLocalInt(oTarget,"AnimationOverlap",iOverlap);
//SetOrientOnDialog(oTarget,FALSE);
PlayKemoAnimation(oTarget,sAnimB); // fires the transitional animation
// a graphical bug causes some races to lose their eyes and lips on a non-looping animation,
// so this uses a loop on animation #1, which must be ~30 frames longer than the frame count ---
// this prevents the "jump" between animations, and may help counteract possible server lag
// issues
DelayCommand(fDelayUntilStart,AnimationLoop(oTarget,GetLocation(oTarget))); // fires the looping idle animation
}
It seems, when looking in kemo_includes, that the animation is stored. I’m beginning to believe that’s why the animation begins again. Still, I can’t wrap my head around how to reset that thing when I need to. Can anyone of you scripting masters grasp how to reset the animation?:
//kemo_includes
//
// Determines the loop animation trigger delay based on PC's race/subrace; KEMO Emotes/Partners only
float RacialDelay(object oTarget);
// Plays the custom animation with an automatic loop, which works around the animation bug
void PlayKemoAnimation(object oTarget, string sAnim);
// Turns the PC to face the direction of the chair
void SetChairFacing(object oPC, object oChair);
// This function works around the NWN2 bug where PCs entering an area do not see looping animations that
// are currently in progress. AnimationLoop re-triggers the animation at a set interval as long as the PC has
// not moved from the original spot. lCurrentSpot is the animation's starting location, which is checked against
// the PC's current location.
void AnimationLoop(object oPC, location lCurrentSpot);
// Stores the parameters necessary to re-trigger the starting current/previous animation.
void StoreAnimation(string sAnim, float fDelay);
// Stores the parameters necessary to re-trigger the idle of the current/previous animation.
void StoreIdle(string sAnim);
float RacialDelay(object oTarget)
{
switch (GetSubRace(oTarget)) // attempts to deal with the varying race speeds
// I use this instead of a fractional animation speed because a fractional speed causes the
// PC to move more slowly than is normal for his/her race
// without these switches, a non-human-sized PC will "jump" right before the looping animation
{
case RACIAL_SUBTYPE_DROW: return 0.8f; break;
}
switch (GetRacialType(oTarget)) {
case RACIAL_TYPE_HALFLING: return 0.5f; break;
case RACIAL_TYPE_ELF: return 0.9f; break;
case RACIAL_TYPE_HALFELF: return 0.95f; break;
}
return 1.0f;
}
void PlayKemoAnimation(object oTarget, string sAnim) //so it returns a void
{
PlayCustomAnimation(oTarget,sAnim,1);
}
void SetChairFacing(object oPC, object oChair)
{
float fChairFace = GetFacing(oChair);
string sChairTag = GetTag(oChair);
if (sChairTag == "kemo_chair_4") return;
else if (sChairTag == "kemo_bench_2" ||
sChairTag == "kemo_chair_5") AssignCommand(oPC,SetFacing(fChairFace));
else if (sChairTag == "kemo_booth_1") AssignCommand(oPC,SetFacing(fChairFace-90.0f));
else AssignCommand(oPC,SetFacing(fChairFace+180.0f));
}
void AnimationLoop(object oPC, location lCurrentSpot)
{
/* This script now sets the animation information on a Secret Object and starts its heartbeat
on a 48 second interval. If the Ipoint already exists, it transfers the new animation
name to that Secret Object. Thanks to GrinningFool for this more elegant solution. 7/29/08 */
object oPoint = GetLocalObject(oPC,"AnimationPoint");
string sAnim = GetLocalString(oPC,"StoredAnimationIdle");
SetCollision(oPC,0); //turns collision off for the course of the animation.
if (oPoint != OBJECT_INVALID)
{
SetLocalString(oPoint,"StoredAnimationIdle",sAnim);
PlayKemoAnimation(oPC,sAnim);
}
else
{
object oPoint = CreateObject(OBJECT_TYPE_PLACEABLE,"plc_secretobject",lCurrentSpot,FALSE,"AnimationPoint");
SetLocalObject(oPC,"AnimationPoint",oPoint);
SetLocalObject(oPoint,"StoredAnimationPC",oPC);
SetLocalString(oPoint,"StoredAnimationIdle",sAnim);
SetUseableFlag(oPoint,0);
SetCustomHeartbeat(oPoint,12000);
SetEventHandler(oPoint,SCRIPT_PLACEABLE_ON_HEARTBEAT,"kemo_animation_hb");
PlayKemoAnimation(oPC,sAnim);
}
}
/* the following is the original loop script, now replaced with a heartbeat on an Ipoint 7/29/08
{
object oPC = OBJECT_SELF;
location lSpot = GetLocalLocation(oPC,"AnimationLocation");
vector vSpot = GetPositionFromLocation(lSpot);
vector vCurrentSpot = GetPositionFromLocation(lCurrentSpot);
string sAnim = GetLocalString(oPC,"StoredAnimationIdle");
int iOverlap = GetLocalInt(oPC,"AnimationOverlap");
// This function gets re-called for each new animation, so that more than one iteration of the
// function can be running at the same time if the PC hasn't spent at least 48 seconds not in an
// animation. The first two IF statements eliminate the excess function iterations (the overlaps)
// while continuing to show the current animation. The third IF statement continues the animation
// if the PC is still in the same spot. The ELSE statement clears the overlap count if all animations
// have ceased (through PC movement without a new animation).
//SendMessageToPC(oPC,"Overlap: " + IntToString(iOverlap)); //debug line
//SendMessageToPC(oPC,"Animation: " + sAnim); //debug line
if (vSpot != vCurrentSpot)
{
iOverlap = 1;
SetLocalInt(oPC,"AnimationOverlap",iOverlap);
return;
}
if (iOverlap > 1)
{
iOverlap--;
SetLocalInt(oPC,"AnimationOverlap",iOverlap);
ActionDoCommand(PlayKemoAnimation(oPC,sAnim));
return;
}
if (vSpot == GetPosition(oPC))
{
ActionDoCommand(PlayKemoAnimation(oPC,sAnim));
ActionDoCommand(DelayCommand(48.0f,AnimationLoop(lCurrentSpot)));
}
else SetLocalInt(oPC,"AnimationOverlap",0);
}*/
void StoreAnimation(string sAnim, float fDelay)
{
SetLocalString(OBJECT_SELF,"StoredAnimation",sAnim); // stores the animation name for re-triggering
SetLocalFloat(OBJECT_SELF,"StoredDelay",fDelay); // stores the delay for re-triggering
}
void StoreIdle(string sAnim)
{
SetLocalString(OBJECT_SELF,"StoredAnimationIdle",sAnim);
}