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.
823 lines
24 KiB
823 lines
24 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program 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 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program 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 this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
Additional permissions under GNU GPL version 3 section 7 |
|
|
|
If you modify this Program, or any covered work, by linking or |
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
|
(or a modified version of those libraries), |
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
|
licensors of this Program grant you additional |
|
permission to convey the resulting work. Corresponding Source for a |
|
non-source form of such a combination shall include the source code for |
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
|
work. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
#include "HeadSpin.h" |
|
|
|
// singular |
|
#include "plAvatarTasks.h" |
|
|
|
// local |
|
#include "plArmatureMod.h" |
|
#include "plSeekPointMod.h" |
|
#include "plAvBrainHuman.h" |
|
#include "plAnimation/plAGAnim.h" |
|
#include "plAnimation/plAGAnimInstance.h" |
|
#include "plAnimation/plAGModifier.h" |
|
#include "plAnimation/plMatrixChannel.h" |
|
#include "plPhysicalControllerCore.h" |
|
#include "plAvatarMgr.h" |
|
|
|
// global |
|
|
|
|
|
// other |
|
#include "plgDispatch.h" |
|
#include "plMessage/plAvatarMsg.h" |
|
#include "plMessage/plAnimCmdMsg.h" |
|
#include "plMessage/plOneShotCallbacks.h" |
|
#include "plMessage/plConsoleMsg.h" |
|
#include "pnKeyedObject/plKey.h" |
|
#include "pnSceneObject/plCoordinateInterface.h" |
|
#include "plPipeline/plDebugText.h" |
|
#include "plInputCore/plInputInterfaceMgr.h" |
|
#include "plNetClient/plNetClientMgr.h" |
|
#include "plNetCommon/plNetCommon.h" |
|
#include "plMessage/plLinkToAgeMsg.h" |
|
#include "pfMessage/pfKIMsg.h" |
|
|
|
// for console hack |
|
bool plAvOneShotTask::fForce3rdPerson = true; |
|
#include "pnMessage/plCameraMsg.h" |
|
|
|
///////////// |
|
// |
|
// PLAVTASK |
|
// Abstract definition for the avatar task class |
|
// |
|
///////////// |
|
plAvTask::plAvTask() |
|
{ |
|
} |
|
|
|
// START |
|
bool plAvTask::Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, float elapsed) |
|
{ |
|
return true; // true indicates the task has started succesfully |
|
} |
|
|
|
// PROCESS |
|
bool plAvTask::Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, float elapsed) |
|
{ |
|
return false; |
|
} |
|
|
|
// Finish ----------------------------------------------------------------------------------- |
|
// ------- |
|
void plAvTask::Finish(plArmatureMod *avatar, plArmatureBrain *brain, double time, float elapsed) |
|
{ |
|
} |
|
|
|
|
|
// DUMPDEBUG |
|
void plAvTask::DumpDebug(const char *name, int &x, int&y, int lineHeight, plDebugText &debugTxt) |
|
{ |
|
debugTxt.DrawString(x, y, "<anonymous task>"); |
|
y += lineHeight; |
|
} |
|
|
|
// READ |
|
void plAvTask::Read(hsStream *stream, hsResMgr *mgr) |
|
{ |
|
plCreatable::Read(stream, mgr); |
|
} |
|
|
|
// WRITE |
|
void plAvTask::Write(hsStream *stream, hsResMgr *mgr) |
|
{ |
|
plCreatable::Write(stream, mgr); |
|
} |
|
|
|
void plAvTask::ILimitPlayersInput(plArmatureMod *avatar) |
|
{ |
|
// make sure this is the local avatar we are talking about |
|
if (avatar == plAvatarMgr::GetInstance()->GetLocalAvatar()) |
|
{ |
|
plInputInterfaceMgr::GetInstance()->ForceCursorHidden(true); |
|
// tell the KI to be disabled while we are busy |
|
pfKIMsg* msg = new pfKIMsg(pfKIMsg::kTempDisableKIandBB); |
|
plgDispatch::MsgSend( msg ); |
|
} |
|
} |
|
|
|
void plAvTask::IUndoLimitPlayersInput(plArmatureMod *avatar) |
|
{ |
|
// make sure this is the local avatar we are talking about |
|
if (avatar == plAvatarMgr::GetInstance()->GetLocalAvatar()) |
|
{ |
|
plInputInterfaceMgr::GetInstance()->ForceCursorHidden(false); |
|
// tell the KI to be re-enabled |
|
pfKIMsg* msg = new pfKIMsg(pfKIMsg::kTempEnableKIandBB); |
|
plgDispatch::MsgSend( msg ); |
|
} |
|
} |
|
|
|
///////////// |
|
// |
|
// AVSEEKTASK |
|
// |
|
///////////// |
|
|
|
// CTOR default |
|
plAvSeekTask::plAvSeekTask() |
|
: fAlign(kAlignHandle), |
|
fDuration(0.25), |
|
fTarget(nil), |
|
fAnimInstance(nil), |
|
fTargetTime(0), |
|
fPhysicalAtStart(false), |
|
fCleanup(false) |
|
{ |
|
} |
|
|
|
// CTOR target, align, animName |
|
plAvSeekTask::plAvSeekTask(plKey target, plAvAlignment align, const plString& animName) |
|
: fAnimName(animName), |
|
fAlign(align), |
|
fDuration(0.25), |
|
fTarget(target), |
|
fAnimInstance(nil), |
|
fTargetTime(0), |
|
fPhysicalAtStart(false), |
|
fCleanup(false) |
|
{ |
|
} |
|
|
|
// CTOR target |
|
plAvSeekTask::plAvSeekTask(plKey target) |
|
: fAlign(kAlignHandle), |
|
fDuration(0.25), |
|
fTarget(target), |
|
fAnimInstance(nil), |
|
fTargetTime(0), |
|
fPhysicalAtStart(false), |
|
fCleanup(false) |
|
{ |
|
} |
|
|
|
void GetPositionAndRotation(hsMatrix44 transform, hsScalarTriple *position, hsQuat *rotation) |
|
{ |
|
hsPoint3 p = (hsPoint3)transform.GetTranslate(); |
|
position->fX = p.fX; position->fY = p.fY; position->fZ = p.fZ; |
|
|
|
|
|
transform.RemoveScale(); |
|
|
|
rotation->SetFromMatrix(&transform); |
|
rotation->Normalize(); |
|
|
|
float angle; |
|
hsVector3 axis; |
|
|
|
rotation->GetAngleAxis(&angle, &axis); |
|
} |
|
|
|
// START |
|
// Adjust our goal time based on our duration and the current time |
|
bool plAvSeekTask::Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, float elapsed) |
|
{ |
|
fTargetTime = time + fDuration; // clock starts now.... |
|
fPhysicalAtStart = avatar->IsPhysicsEnabled(); |
|
avatar->EnablePhysics(false); // always turn physics off for seek |
|
plAvBrainHuman *huBrain = plAvBrainHuman::ConvertNoRef(brain); |
|
if(huBrain) |
|
huBrain->IdleOnly(); |
|
|
|
ILimitPlayersInput(avatar); |
|
|
|
if (!fTarget || !fTarget->ObjectIsLoaded()) |
|
{ |
|
fCleanup = true; |
|
return true; |
|
} |
|
|
|
plSceneObject* seekTarget = plSceneObject::ConvertNoRef(fTarget->ObjectIsLoaded()); |
|
hsMatrix44 targetL2W = seekTarget->GetLocalToWorld(); |
|
const plCoordinateInterface* subworldCI = nil; |
|
if (avatar->GetController()) |
|
subworldCI = avatar->GetController()->GetSubworldCI(); |
|
if (subworldCI) |
|
targetL2W = subworldCI->GetWorldToLocal() * targetL2W; |
|
|
|
switch(fAlign) |
|
{ |
|
// just match our handle to the target matrix |
|
case kAlignHandle: |
|
// targetL2Sim is already correct |
|
break; |
|
// match our handle to the target matrix at the end of the given animation |
|
case kAlignHandleAnimEnd: |
|
{ |
|
hsMatrix44 adjustment; |
|
plAGAnim *anim = avatar->FindCustomAnim(fAnimName); |
|
GetStartToEndTransform(anim, nil, &adjustment, "Handle"); // actually getting end-to-start |
|
targetL2W = targetL2W * adjustment; |
|
} |
|
break; |
|
default: |
|
break; |
|
}; |
|
|
|
GetPositionAndRotation(targetL2W, &fTargetPosition, &fTargetRotation); |
|
Process(avatar, brain, time, elapsed); |
|
return true; |
|
} |
|
|
|
// CALCHANDLETARGETPOSITION |
|
void CalcHandleTargetPosition(hsMatrix44 &result, plSceneObject *handle, plSceneObject *target, hsMatrix44 &bodyToHandle) |
|
{ |
|
hsMatrix44 targetToWorld = target->GetLocalToWorld(); |
|
|
|
result = bodyToHandle * targetToWorld; |
|
} |
|
|
|
// CALCHANDLETARGETPOSITION |
|
// where should I move my insertion point so that my bodyRoot lines up with the target? |
|
void CalcHandleTargetPosition(hsMatrix44 &result, plSceneObject *insert, plSceneObject *target, plSceneObject *bodyRoot) |
|
{ |
|
hsMatrix44 bodyToHandle = bodyRoot->GetLocalToParent(); |
|
CalcHandleTargetPosition(result, insert, target, bodyToHandle); |
|
} |
|
|
|
// PROCESS |
|
// Move closer to the goal position and orientation |
|
bool plAvSeekTask::Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, float elapsed) |
|
{ |
|
hsQuat rotation; |
|
hsPoint3 position; |
|
avatar->GetPositionAndRotationSim(&position, &rotation); |
|
|
|
// We've had a history of odd bugs caused by assuming a rotation quat is normalized. |
|
// This line here seems to be fixing one of them. (Avatars scaling oddly when smart seeking.) |
|
rotation.Normalize(); |
|
|
|
double timeToGo = fTargetTime - time - elapsed; // time from *beginning* of this interval to the goal |
|
if (fCleanup) |
|
{ |
|
avatar->EnablePhysics( fPhysicalAtStart ); |
|
IUndoLimitPlayersInput(avatar); |
|
|
|
return false; // we're done processing |
|
} |
|
else if(timeToGo < .01) |
|
{ |
|
fTargetRotation.Normalize(); |
|
avatar->SetPositionAndRotationSim(&fTargetPosition, &fTargetRotation); |
|
fCleanup = true; // we're going to wait one frame for the transform to propagate |
|
return true; // still running until next frame/cleanup |
|
} |
|
else |
|
{ |
|
hsPoint3 posToGo = fTargetPosition - position; // vec from here to the goal |
|
float thisPercentage = (float)(elapsed / timeToGo); |
|
|
|
hsPoint3 newPosition = position + posToGo * thisPercentage; |
|
hsQuat newRotation; |
|
newRotation.SetFromSlerp(rotation, fTargetRotation, thisPercentage); |
|
|
|
newRotation.Normalize(); |
|
avatar->SetPositionAndRotationSim(&newPosition, &newRotation); |
|
return true; // we're still processing |
|
} |
|
} |
|
|
|
void plAvSeekTask::LeaveAge(plArmatureMod *avatar) |
|
{ |
|
fTarget = nil; |
|
fCleanup = true; |
|
} |
|
|
|
/////////////////// |
|
// |
|
// PLAVANIMTASK |
|
// |
|
/////////////////// |
|
|
|
// CTOR default |
|
plAvAnimTask::plAvAnimTask() |
|
: fInitialBlend(0.0f), |
|
fTargetBlend(0.0f), |
|
fFadeSpeed(0.0f), |
|
fSetTime(0.0f), |
|
fStart(false), |
|
fLoop(false), |
|
fAttach(false), |
|
fAnimInstance(nil) |
|
{ |
|
} |
|
|
|
// CTOR animName, initialBlend, targetBlend, fadeSpeed, start, loop, attach |
|
plAvAnimTask::plAvAnimTask(const plString &animName, |
|
float initialBlend, |
|
float targetBlend, |
|
float fadeSpeed, |
|
float setTime, |
|
bool start, |
|
bool loop, |
|
bool attach) |
|
: fAnimName(animName), |
|
fInitialBlend(initialBlend), |
|
fTargetBlend(targetBlend), |
|
fFadeSpeed(fadeSpeed), |
|
fSetTime(setTime), |
|
fStart(start), |
|
fLoop(loop), |
|
fAttach(attach), |
|
fAnimInstance(nil) |
|
{ |
|
} |
|
|
|
// CTOR animName, fadeSpeed, attach |
|
plAvAnimTask::plAvAnimTask(const plString &animName, float fadeSpeed, bool attach) |
|
: fAnimName(animName), |
|
fInitialBlend(0.0f), |
|
fTargetBlend(0.0f), |
|
fFadeSpeed(fadeSpeed), |
|
fSetTime(0.0f), |
|
fStart(false), |
|
fLoop(false), |
|
fAttach(attach), |
|
fAnimInstance(nil) |
|
{ |
|
} |
|
|
|
|
|
|
|
|
|
// START |
|
bool plAvAnimTask::Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, float elapsed) |
|
{ |
|
bool result = false; |
|
if(fAttach) |
|
{ |
|
plAGAnimInstance * aInstance = avatar->FindOrAttachInstance(fAnimName, fInitialBlend); |
|
|
|
if(aInstance) |
|
{ |
|
if(fStart) |
|
aInstance->Start(fStart); |
|
if(fSetTime > 0) |
|
aInstance->SetCurrentTime(fSetTime, true); |
|
if(fTargetBlend > fInitialBlend) |
|
{ |
|
aInstance->Fade(fTargetBlend, fFadeSpeed); |
|
} |
|
aInstance->SetLoop(fLoop); |
|
|
|
result = true; |
|
} |
|
else |
|
{ |
|
hsStatusMessageF("Couldn't find animation <%s> for plAvAnimTask: will try again", fAnimName.c_str()); |
|
} |
|
} |
|
else |
|
{ |
|
fAnimInstance = avatar->FindAnimInstance(fAnimName); |
|
if(fAnimInstance) |
|
{ |
|
// start fading towards zero |
|
fAnimInstance->Fade(0.0, fFadeSpeed); |
|
} |
|
// if we started the fade, we're done and ready to process |
|
// if we couldn't find the animation, we're still done. |
|
result = true; |
|
} |
|
return result; |
|
} |
|
|
|
// PROCESS |
|
bool plAvAnimTask::Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, float elapsed) |
|
{ |
|
// the only reason we need this function is to watch the animation until it fades out |
|
bool result = false; |
|
if(fAttach) |
|
{ |
|
// we finished in the Start() function |
|
} |
|
else |
|
{ |
|
if(fAnimInstance) |
|
{ |
|
if(fAnimInstance->GetBlend() < 0.1) |
|
{ |
|
avatar->DetachAnimation(fAnimInstance); |
|
} |
|
else |
|
{ |
|
// still waiting for the fadeout; keep the task alive |
|
result = true; |
|
} |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
// LEAVEAGE |
|
void plAvAnimTask::LeaveAge(plArmatureMod *avatar) |
|
{ |
|
// if we are supposed to be removing the animation anyway, kill it completely on link out |
|
if (!fAttach) |
|
{ |
|
fAnimInstance = avatar->FindAnimInstance(fAnimName); |
|
if(fAnimInstance) |
|
avatar->DetachAnimation(fAnimInstance); |
|
} |
|
} |
|
|
|
// READ |
|
void plAvAnimTask::Read(hsStream *stream, hsResMgr *mgr) |
|
{ |
|
fAnimName = stream->ReadSafeString(); |
|
fInitialBlend = stream->ReadLEScalar(); |
|
fTargetBlend = stream->ReadLEScalar(); |
|
fFadeSpeed = stream->ReadLEScalar(); |
|
fSetTime = stream->ReadLEScalar(); |
|
fStart = stream->ReadBool(); |
|
fLoop = stream->ReadBool(); |
|
fAttach = stream->ReadBool(); |
|
} |
|
|
|
// WRITE |
|
void plAvAnimTask::Write(hsStream *stream, hsResMgr *mgr) |
|
{ |
|
stream->WriteSafeString(fAnimName); |
|
stream->WriteLEScalar(fInitialBlend); |
|
stream->WriteLEScalar(fTargetBlend); |
|
stream->WriteLEScalar(fFadeSpeed); |
|
stream->WriteLEScalar(fSetTime); |
|
stream->WriteBool(fStart); |
|
stream->WriteBool(fLoop); |
|
stream->WriteBool(fAttach); |
|
} |
|
|
|
//////////////// |
|
// |
|
// AVONESHOTTASK |
|
// OBSOLETE -- DEPRECATED |
|
// |
|
//////////////// |
|
|
|
void plAvOneShotTask::InitDefaults() |
|
{ |
|
fBackwards = false; |
|
fDisableLooping = false; |
|
fDisablePhysics = true; |
|
fMoveHandle = false; |
|
fAnimInstance = nil; |
|
fDrivable = false; |
|
fReversible = false; |
|
fEnablePhysicsAtEnd = false; |
|
fDetachAnimation = false; |
|
fIgnore = false; |
|
fCallbacks = nil; |
|
fWaitFrames = 0; |
|
} |
|
|
|
// CTOR default |
|
plAvOneShotTask::plAvOneShotTask() |
|
{ |
|
InitDefaults(); |
|
} |
|
|
|
// CTOR (animName, drivable, reversible) |
|
// this construct is typically used when you want to create a one-shot task as part of a sequence |
|
// of tasks |
|
// it's different than the message-based constructor in that fDetachAnimation and fMoveHandle default to false |
|
plAvOneShotTask::plAvOneShotTask(const plString &animName, bool drivable, bool reversible, plOneShotCallbacks *callbacks) |
|
{ |
|
InitDefaults(); |
|
|
|
fDrivable = drivable; |
|
fReversible = reversible; |
|
fCallbacks = callbacks; |
|
|
|
// we're going to use this sometime in the future, better ref it so someone else doesn't release it |
|
hsRefCnt_SafeRef(fCallbacks); |
|
fAnimName = animName; |
|
} |
|
|
|
// CTOR (plAvOneShotMsg, plArmatureMod) |
|
// this constructor is typically used when we're doing a classic, isolated one-shot |
|
// fDetachAnimation and fMoveHandle both default to *true* |
|
plAvOneShotTask::plAvOneShotTask (plAvOneShotMsg *msg, plArmatureMod *avatar, plArmatureBrain *brain) |
|
{ |
|
InitDefaults(); |
|
|
|
fDrivable = msg->fDrivable; |
|
fReversible = msg->fReversible; |
|
fCallbacks = msg->fCallbacks; |
|
fDetachAnimation = true; |
|
fMoveHandle = true; |
|
|
|
// we're going to use this sometime in the future, better ref it so someone else doesn't release it |
|
hsRefCnt_SafeRef(fCallbacks); |
|
fAnimName = msg->fAnimName; |
|
} |
|
|
|
// DTOR |
|
plAvOneShotTask::~plAvOneShotTask() |
|
{ |
|
hsRefCnt_SafeUnRef(fCallbacks); |
|
} |
|
|
|
|
|
// START |
|
bool plAvOneShotTask::Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, float elapsed) |
|
{ |
|
bool result = false; |
|
|
|
if (fIgnore) |
|
return true; |
|
|
|
plAGMasterMod * master = avatar; |
|
|
|
fAnimInstance = master->AttachAnimationBlended(fAnimName, 0); |
|
fDetachAnimation = true; |
|
|
|
if(fAnimInstance) |
|
{ |
|
fEnablePhysicsAtEnd = (avatar->IsPhysicsEnabled() && fDisablePhysics); |
|
if (fEnablePhysicsAtEnd) |
|
{ |
|
// Must do the physics re-enable through a callback so that it happens before the "done" callback and we don't |
|
// step over some script's attempt to disable physics again. |
|
plAvatarPhysicsEnableCallbackMsg *epMsg = new plAvatarPhysicsEnableCallbackMsg(avatar->GetKey(), kStop, 0, 0, 0, 0); |
|
fAnimInstance->GetTimeConvert()->AddCallback(epMsg); |
|
hsRefCnt_SafeUnRef(epMsg); |
|
} |
|
|
|
if (fCallbacks) |
|
{ |
|
fAnimInstance->AttachCallbacks(fCallbacks); |
|
// ok, we're done with it, release it back to the river |
|
hsRefCnt_SafeUnRef(fCallbacks); |
|
fCallbacks = nil; |
|
} |
|
|
|
fAnimInstance->SetBlend(1.0f); |
|
fAnimInstance->SetSpeed(1.0f); |
|
plAnimTimeConvert *atc = fAnimInstance->GetTimeConvert(); |
|
if (fBackwards) |
|
atc->Backwards(); |
|
if (fDisableLooping) |
|
atc->Loop(false); |
|
|
|
fAnimInstance->SetCurrentTime(fBackwards ? atc->GetEnd() : atc->GetBegin(), true); |
|
fAnimInstance->Start(time); |
|
|
|
fWaitFrames = 2; // wait two frames after animation finishes before finalizing |
|
|
|
|
|
if (fDisablePhysics) |
|
avatar->EnablePhysics(false); |
|
|
|
ILimitPlayersInput(avatar); |
|
|
|
// this is for a console command hack |
|
if (plAvOneShotTask::fForce3rdPerson && avatar->IsLocalAvatar()) |
|
{ |
|
// create message |
|
plCameraMsg* pMsg = new plCameraMsg; |
|
pMsg->SetBCastFlag(plMessage::kBCastByExactType); |
|
pMsg->SetBCastFlag(plMessage::kNetPropagate, false); |
|
pMsg->SetCmd(plCameraMsg::kResponderSetThirdPerson); |
|
plgDispatch::MsgSend( pMsg ); // whoosh... off it goes |
|
} |
|
|
|
fMoveHandle = (fAnimInstance->GetAnimation()->GetChannel("Handle") != nil); |
|
if(fMoveHandle) |
|
{ |
|
plMatrixDifferenceApp *differ = avatar->GetRootAnimator(); |
|
differ->Reset(time); // throw away any old state |
|
differ->Enable(true); |
|
} |
|
|
|
avatar->ApplyAnimations(time, elapsed); |
|
|
|
result = true; |
|
} |
|
else |
|
{ |
|
plString buf = plFormat("Oneshot: Can't find animation <{}>; all bets are off.", fAnimName); |
|
hsAssert(false, buf.c_str()); |
|
result = true; |
|
} |
|
return result; |
|
} |
|
|
|
// PROCESS |
|
bool plAvOneShotTask::Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, float elapsed) |
|
{ |
|
// *** if we are under mouse control, adjust it here |
|
|
|
avatar->ApplyAnimations(time, elapsed); |
|
if(fAnimInstance) |
|
{ |
|
if(fAnimInstance->IsFinished()) |
|
{ |
|
const plAGAnim * animation = fAnimInstance->GetAnimation(); |
|
double endTime = (fBackwards ? animation->GetStart() : animation->GetEnd()); |
|
fAnimInstance->SetCurrentTime((float)endTime); |
|
avatar->ApplyAnimations(time, elapsed); |
|
|
|
if(--fWaitFrames == 0) |
|
{ |
|
avatar->DetachAnimation(fAnimInstance); |
|
avatar->GetRootAnimator()->Enable(false); |
|
plAvBrainHuman *humanBrain = plAvBrainHuman::ConvertNoRef(brain); |
|
if(fEnablePhysicsAtEnd) |
|
{ |
|
#if 0//ndef PLASMA_EXTERNAL_RELEASE |
|
if (!humanBrain || humanBrain->fWalkingStrategy->HitGroundInThisAge()) |
|
{ |
|
// For some reason, calling CheckValidPosition at the beginning of |
|
// an age can cause detectors to incorrectly report collisions. So |
|
// we only call this if we're in the age. |
|
// |
|
// It's only debugging code anyway to help the artist check that |
|
// their oneshot doesn't end while penetrating geometry. |
|
char *overlaps = nil; |
|
if (avatar->GetPhysical()) |
|
avatar->GetPhysical()->CheckValidPosition(&overlaps); |
|
if (overlaps) |
|
{ |
|
char *buffy = new char[64 + strlen(overlaps)]; |
|
sprintf(buffy, "Oneshot ends overlapping %s", overlaps); |
|
plConsoleMsg *showLine = new plConsoleMsg( plConsoleMsg::kAddLine, buffy ); |
|
showLine->Send(); |
|
delete[] overlaps; |
|
delete[] buffy; |
|
} |
|
} |
|
#endif |
|
} |
|
if (humanBrain) |
|
humanBrain->ResetIdle(); |
|
|
|
IUndoLimitPlayersInput(avatar); |
|
// this is for a console command hack |
|
if (plAvOneShotTask::fForce3rdPerson && avatar->IsLocalAvatar()) |
|
{ |
|
// create message |
|
plCameraMsg* pMsg = new plCameraMsg; |
|
pMsg->SetBCastFlag(plMessage::kBCastByExactType); |
|
pMsg->SetBCastFlag(plMessage::kNetPropagate, false); |
|
pMsg->SetCmd(plCameraMsg::kResponderUndoThirdPerson); |
|
plgDispatch::MsgSend( pMsg ); // whoosh... off it goes |
|
} |
|
|
|
return false; |
|
} |
|
else |
|
return true; // still running; waiting for fWaitFrames == 0 |
|
} |
|
else |
|
return true; |
|
} |
|
else |
|
return false; |
|
} |
|
|
|
void plAvOneShotTask::LeaveAge(plArmatureMod *avatar) |
|
{ |
|
if (fAnimInstance) |
|
fAnimInstance->Stop(); |
|
|
|
if (fEnablePhysicsAtEnd) |
|
avatar->EnablePhysics(true); |
|
|
|
IUndoLimitPlayersInput(avatar); |
|
fIgnore = true; |
|
} |
|
|
|
void plAvOneShotTask::SetAnimName(const plString &name) |
|
{ |
|
fAnimName = name; |
|
} |
|
|
|
////////////////////// |
|
// |
|
// PLAVONESHOTLINKTASK |
|
// |
|
////////////////////// |
|
|
|
plAvOneShotLinkTask::plAvOneShotLinkTask() : plAvOneShotTask(), |
|
fMarkerTime(-1), |
|
fStartTime(0), |
|
fLinkFired(false) |
|
{ |
|
fDisablePhysics = false; |
|
} |
|
|
|
plAvOneShotLinkTask::~plAvOneShotLinkTask() |
|
{ |
|
} |
|
|
|
// task protocol |
|
bool plAvOneShotLinkTask::Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, float elapsed) |
|
{ |
|
bool result = plAvOneShotTask::Start(avatar, brain, time, elapsed); |
|
fStartTime = time; |
|
|
|
if (fAnimInstance && !fMarkerName.IsNull()) |
|
{ |
|
const plATCAnim *anim = plATCAnim::ConvertNoRef(fAnimInstance->GetAnimation()); |
|
if (anim) |
|
{ |
|
// GetMarker returns -1 if the marker isn't found |
|
fMarkerTime = anim->GetMarker(fMarkerName); |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
bool plAvOneShotLinkTask::Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, float elapsed) |
|
{ |
|
bool result = plAvOneShotTask::Process(avatar, brain, time, elapsed); |
|
if (fIgnore) |
|
return result; |
|
|
|
if (avatar->GetTarget(0) == plNetClientApp::GetInstance()->GetLocalPlayer()) |
|
{ |
|
if (!fLinkFired && (fStartTime + fMarkerTime < time)) |
|
{ |
|
avatar->ILinkToPersonalAge(); |
|
|
|
avatar->EnablePhysics(false, plArmatureMod::kDisableReasonLinking); |
|
fLinkFired = true; |
|
} |
|
} |
|
|
|
return result; |
|
} |
|
|
|
void plAvOneShotLinkTask::Write(hsStream *stream, hsResMgr *mgr) |
|
{ |
|
plAvOneShotTask::Write(stream, mgr); |
|
stream->WriteSafeString(fAnimName); |
|
stream->WriteSafeString(fMarkerName); |
|
} |
|
|
|
void plAvOneShotLinkTask::Read(hsStream *stream, hsResMgr *mgr) |
|
{ |
|
plAvOneShotTask::Read(stream, mgr); |
|
fAnimName = stream->ReadSafeString(); |
|
fMarkerName = stream->ReadSafeString(); |
|
} |
|
|
|
void plAvOneShotLinkTask::SetMarkerName(const plString &name) |
|
{ |
|
fMarkerName = name; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|