|
|
|
/*==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 "plArmatureMod.h"
|
|
|
|
|
|
|
|
// local
|
|
|
|
#include "plAvBrain.h"
|
|
|
|
#include "plAvBrainUser.h"
|
|
|
|
#include "plAvatarMgr.h"
|
|
|
|
#include "plAnimation/plAGModifier.h"
|
|
|
|
#include "plAvatarClothing.h"
|
|
|
|
#include "plClothingSDLModifier.h"
|
|
|
|
#include "plAvatarSDLModifier.h"
|
|
|
|
#include "plAnimation/plAGAnim.h"
|
|
|
|
#include "plArmatureEffects.h"
|
|
|
|
#include "plAvBrainHuman.h"
|
|
|
|
#include "plAnimation/plMatrixChannel.h"
|
|
|
|
#include "plAvatarTasks.h"
|
|
|
|
#include "plPhysicalControllerCore.h"
|
|
|
|
#include "plAvBrainCritter.h"
|
|
|
|
|
|
|
|
// global
|
|
|
|
#include "plgDispatch.h"
|
|
|
|
#include "hsQuat.h"
|
|
|
|
#include "hsTimer.h"
|
|
|
|
|
|
|
|
// other
|
|
|
|
#include "pnSceneObject/plCoordinateInterface.h"
|
|
|
|
#include "pnSceneObject/plAudioInterface.h"
|
|
|
|
#include "plInterp/plAnimTimeConvert.h"
|
|
|
|
|
|
|
|
#include "pnMessage/plEnableMsg.h"
|
|
|
|
#include "pnMessage/plTimeMsg.h"
|
|
|
|
#include "pnMessage/plSDLModifierMsg.h"
|
|
|
|
#include "pnMessage/plAttachMsg.h"
|
|
|
|
#include "pnMessage/plWarpMsg.h"
|
|
|
|
#include "pnMessage/plCorrectionMsg.h"
|
|
|
|
#include "pnMessage/plCameraMsg.h"
|
|
|
|
#include "pnMessage/plPipeResMakeMsg.h"
|
|
|
|
|
|
|
|
#include "plMessage/plAvatarMsg.h"
|
|
|
|
#include "plMessage/plAvatarFootMsg.h"
|
|
|
|
#include "plMessage/plInputEventMsg.h"
|
|
|
|
#include "plMessage/plLoadAgeMsg.h"
|
|
|
|
#include "plMessage/plAnimCmdMsg.h"
|
|
|
|
#include "plMessage/plListenerMsg.h"
|
|
|
|
#include "plMessage/plAgeLoadedMsg.h"
|
|
|
|
#include "plMessage/plParticleUpdateMsg.h"
|
|
|
|
#include "plMessage/plLoadClothingMsg.h"
|
|
|
|
|
|
|
|
#include "plParticleSystem/plParticleSystem.h"
|
|
|
|
#include "plParticleSystem/plParticleSDLMod.h"
|
|
|
|
|
|
|
|
#include "pfMessage/plArmatureEffectMsg.h"
|
|
|
|
#include "pfMessage/pfKIMsg.h"
|
|
|
|
#include "plVault/plVault.h"
|
|
|
|
|
|
|
|
#include "pnKeyedObject/plFixedKey.h"
|
|
|
|
#include "pnKeyedObject/plKey.h"
|
|
|
|
#include "pnKeyedObject/plKeyImp.h"
|
|
|
|
|
|
|
|
#include "plDrawable/plInstanceDrawInterface.h"
|
|
|
|
#include "plDrawable/plDrawableSpans.h"
|
|
|
|
#include "plSurface/plLayerAnimation.h"
|
|
|
|
#include "plSurface/hsGMaterial.h"
|
|
|
|
|
|
|
|
#include "pnNetCommon/plNetApp.h"
|
|
|
|
#include "plNetClient/plNetClientMgr.h" // for CCR stuff..
|
|
|
|
#include "plNetClient/plNetLinkingMgr.h"
|
|
|
|
#include "plModifier/plSpawnModifier.h"
|
|
|
|
#include "plPipeline/plDebugText.h"
|
|
|
|
#include "plResMgr/plKeyFinder.h"
|
|
|
|
#include "plAudio/plWin32StaticSound.h"
|
|
|
|
#include "plAudio/plAudioSystem.h"
|
|
|
|
#include "plNetMessage/plNetMessage.h"
|
|
|
|
#include "plInputCore/plAvatarInputInterface.h"
|
|
|
|
#include "plInputCore/plSceneInputInterface.h"
|
|
|
|
#include "plInputCore/plInputDevice.h"
|
|
|
|
#include "pfCamera/plVirtualCamNeu.h"
|
|
|
|
#include "plScene/plRelevanceMgr.h"
|
|
|
|
#include "plMessage/plSimStateMsg.h"
|
|
|
|
|
|
|
|
#include "plGImage/plLODMipmap.h"
|
|
|
|
#include "plPipeline.h"
|
|
|
|
#include "plTweak.h"
|
|
|
|
#include "plDrawable/plVisLOSMgr.h"
|
|
|
|
|
|
|
|
int plArmatureModBase::fMinLOD = 0; // standard is 3 levels of LOD
|
|
|
|
double plArmatureModBase::fLODDistance = 50.0;
|
|
|
|
|
|
|
|
|
|
|
|
plArmatureModBase::plArmatureModBase() :
|
|
|
|
fWaitFlags(kNeedMesh | kNeedPhysics | kNeedApplicator | kNeedBrainActivation),
|
|
|
|
fController(nil),
|
|
|
|
fCurLOD(-1),
|
|
|
|
fRootAnimator(nil),
|
|
|
|
fDisabledPhysics(0),
|
|
|
|
fDisabledDraw(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
plArmatureModBase::~plArmatureModBase()
|
|
|
|
{
|
|
|
|
delete fController;
|
|
|
|
|
|
|
|
while (fUnusedBones.size() > 0)
|
|
|
|
{
|
|
|
|
delete fUnusedBones.back();
|
|
|
|
fUnusedBones.pop_back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureModBase::MsgReceive(plMessage* msg)
|
|
|
|
{
|
|
|
|
bool result = false;
|
|
|
|
|
|
|
|
plArmatureBrain *curBrain = nil;
|
|
|
|
if (fBrains.size() > 0)
|
|
|
|
{
|
|
|
|
curBrain = fBrains.back();
|
|
|
|
if(curBrain->MsgReceive(msg))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return plAGMasterMod::MsgReceive(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureModBase::AddTarget(plSceneObject* so)
|
|
|
|
{
|
|
|
|
plAGMasterMod::AddTarget(so);
|
|
|
|
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureModBase::RemoveTarget(plSceneObject* so)
|
|
|
|
{
|
|
|
|
int count = fBrains.size();
|
|
|
|
for(int i = count - 1; i >= 0; i--)
|
|
|
|
{
|
|
|
|
plArmatureBrain *brain = fBrains[i];
|
|
|
|
if (brain)
|
|
|
|
brain->Deactivate();
|
|
|
|
delete brain;
|
|
|
|
fBrains.pop_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
plAGMasterMod::RemoveTarget(so);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureModBase::IEval(double time, float elapsed, uint32_t dirty)
|
|
|
|
{
|
|
|
|
if (IsFinal())
|
|
|
|
{
|
|
|
|
if (fBrains.size())
|
|
|
|
{
|
|
|
|
plArmatureBrain *curBrain = fBrains.back();
|
|
|
|
if (curBrain)
|
|
|
|
{
|
|
|
|
bool result = curBrain->Apply(time, elapsed);
|
|
|
|
if (!result)
|
|
|
|
{
|
|
|
|
PopBrain();
|
|
|
|
delete curBrain;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
AdjustLOD();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
IFinalize();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureModBase::Read(hsStream * stream, hsResMgr *mgr)
|
|
|
|
{
|
|
|
|
plAGMasterMod::Read(stream, mgr);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
int meshKeyCount = stream->ReadLE32();
|
|
|
|
for(i = 0; i < meshKeyCount; i++)
|
|
|
|
{
|
|
|
|
plKey meshKey = mgr->ReadKey(stream);
|
|
|
|
fMeshKeys.push_back(meshKey);
|
|
|
|
|
|
|
|
plKeyVector *vec = new plKeyVector;
|
|
|
|
int boneCount = stream->ReadLE32();
|
|
|
|
for(int j = 0; j < boneCount; j++)
|
|
|
|
vec->push_back(mgr->ReadKey(stream));
|
|
|
|
fUnusedBones.push_back(vec);
|
|
|
|
}
|
|
|
|
|
|
|
|
int nBrains = stream->ReadLE32();
|
|
|
|
for (i = 0; i < nBrains; i++)
|
|
|
|
{
|
|
|
|
plArmatureBrain * brain = (plArmatureBrain *)mgr->ReadCreatable(stream);
|
|
|
|
this->PushBrain(brain);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureModBase::Write(hsStream *stream, hsResMgr *mgr)
|
|
|
|
{
|
|
|
|
plAGMasterMod::Write(stream, mgr);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
int meshKeyCount = fMeshKeys.size();
|
|
|
|
stream->WriteLE32(meshKeyCount);
|
|
|
|
for (i = 0; i < meshKeyCount; i++)
|
|
|
|
{
|
|
|
|
plKey meshKey = fMeshKeys[i];
|
|
|
|
mgr->WriteKey(stream, meshKey);
|
|
|
|
|
|
|
|
// Should be a list per mesh key
|
|
|
|
stream->WriteLE32(fUnusedBones[i]->size());
|
|
|
|
for(int j = 0; j < fUnusedBones[i]->size(); j++)
|
|
|
|
mgr->WriteKey(stream, (*fUnusedBones[i])[j]);
|
|
|
|
}
|
|
|
|
|
|
|
|
int nBrains = fBrains.size();
|
|
|
|
stream->WriteLE32(nBrains);
|
|
|
|
for (i = 0; i < nBrains; i++)
|
|
|
|
{
|
|
|
|
mgr->WriteCreatable(stream, fBrains[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureModBase::AddressMessageToDescendants(const plCoordinateInterface * CI, plMessage *msg)
|
|
|
|
{
|
|
|
|
if (!CI)
|
|
|
|
return;
|
|
|
|
|
|
|
|
msg->AddReceiver(CI->GetOwnerKey());
|
|
|
|
for (int i = 0; i < CI->GetNumChildren(); i++)
|
|
|
|
AddressMessageToDescendants(CI->GetChild(i), msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureModBase::EnableDrawingTree(const plSceneObject *object, bool status)
|
|
|
|
{
|
|
|
|
if (!object)
|
|
|
|
return;
|
|
|
|
|
|
|
|
plEnableMsg *msg = new plEnableMsg;
|
|
|
|
if (status)
|
|
|
|
msg->SetCmd( plEnableMsg::kEnable );
|
|
|
|
else
|
|
|
|
msg->SetCmd( plEnableMsg::kDisable );
|
|
|
|
msg->SetCmd( plEnableMsg::kDrawable );
|
|
|
|
|
|
|
|
const plCoordinateInterface *pCI = object->GetCoordinateInterface();
|
|
|
|
AddressMessageToDescendants(pCI, msg);
|
|
|
|
plgDispatch::MsgSend(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
plKey plArmatureModBase::GetWorldKey() const
|
|
|
|
{
|
|
|
|
if (fController)
|
|
|
|
return fController->GetSubworld();
|
|
|
|
else
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureModBase::ValidatePhysics()
|
|
|
|
{
|
|
|
|
if (!fTarget)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (fController)
|
|
|
|
{
|
|
|
|
EnablePhysics(true);
|
|
|
|
fWaitFlags &= ~kNeedPhysics;
|
|
|
|
}
|
|
|
|
|
|
|
|
return !(fWaitFlags & kNeedPhysics);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureModBase::ValidateMesh()
|
|
|
|
{
|
|
|
|
if (fWaitFlags & kNeedMesh)
|
|
|
|
{
|
|
|
|
fWaitFlags &= ~kNeedMesh;
|
|
|
|
int n = fMeshKeys.size();
|
|
|
|
|
|
|
|
for(int i = 0; i < n; i++)
|
|
|
|
{
|
|
|
|
plKey meshKey = fMeshKeys[i];
|
|
|
|
plSceneObject *meshObj = (plSceneObject *)meshKey->GetObjectPtr();
|
|
|
|
|
|
|
|
if (!meshObj)
|
|
|
|
{
|
|
|
|
fWaitFlags |= kNeedMesh;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
bool visible = (i == fCurLOD ? true : false);
|
|
|
|
EnableDrawingTree(meshObj, visible);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return !(fWaitFlags & kNeedMesh);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureModBase::PushBrain(plArmatureBrain *brain)
|
|
|
|
{
|
|
|
|
plArmatureBrain *oldBrain = GetCurrentBrain();
|
|
|
|
if (oldBrain)
|
|
|
|
oldBrain->Suspend();
|
|
|
|
|
|
|
|
|
|
|
|
fBrains.push_back(brain);
|
|
|
|
// don't activate brains until we are attached to our target
|
|
|
|
// addTarget will do activation...
|
|
|
|
if (GetTarget(0))
|
|
|
|
brain->Activate(this);
|
|
|
|
|
|
|
|
DirtySynchState(kSDLAvatar, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureModBase::PopBrain()
|
|
|
|
{
|
|
|
|
plArmatureBrain *oldBrain = nil;
|
|
|
|
if (fBrains.size() > 0)
|
|
|
|
{
|
|
|
|
oldBrain = fBrains.back();
|
|
|
|
oldBrain->Deactivate();
|
|
|
|
fBrains.pop_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
plArmatureBrain *newBrain = GetCurrentBrain();
|
|
|
|
if (newBrain)
|
|
|
|
newBrain->Resume();
|
|
|
|
|
|
|
|
DirtySynchState(kSDLAvatar, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
plArmatureBrain *plArmatureModBase::GetCurrentBrain() const
|
|
|
|
{
|
|
|
|
plArmatureBrain *result = nil;
|
|
|
|
if (fBrains.size() > 0)
|
|
|
|
result = fBrains.back();
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
plDrawable *plArmatureModBase::FindDrawable() const
|
|
|
|
{
|
|
|
|
if (fMeshKeys[0] == nil)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(fMeshKeys[0]->ObjectIsLoaded());
|
|
|
|
if (so == nil)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
const plDrawInterface *di = so->GetDrawInterface();
|
|
|
|
if (di && di->GetNumDrawables() > 0)
|
|
|
|
{
|
|
|
|
plDrawable *spans = plDrawable::ConvertNoRef(di->GetDrawable(0));
|
|
|
|
if (spans)
|
|
|
|
return spans;
|
|
|
|
}
|
|
|
|
|
|
|
|
const plInstanceDrawInterface *idi = plInstanceDrawInterface::ConvertNoRef(di);
|
|
|
|
if (idi)
|
|
|
|
return idi->GetInstanceDrawable();
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureModBase::LeaveAge()
|
|
|
|
{
|
|
|
|
int nBrains = fBrains.size();
|
|
|
|
for (int i = nBrains - 1; i >= 0; i--)
|
|
|
|
{
|
|
|
|
plArmatureBrain * curBrain = fBrains[i];
|
|
|
|
if (curBrain->LeaveAge())
|
|
|
|
{
|
|
|
|
if (curBrain == GetCurrentBrain())
|
|
|
|
{
|
|
|
|
PopBrain();
|
|
|
|
delete curBrain;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureModBase::IsFinal()
|
|
|
|
{
|
|
|
|
return !fWaitFlags;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureModBase::AdjustLOD()
|
|
|
|
{
|
|
|
|
if (!IsDrawEnabled())
|
|
|
|
return;
|
|
|
|
|
|
|
|
hsPoint3 camPos = plVirtualCam1::Instance()->GetCameraPos();
|
|
|
|
|
|
|
|
plSceneObject * SO = GetTarget(0);
|
|
|
|
if (SO)
|
|
|
|
{
|
|
|
|
hsMatrix44 l2w = SO->GetLocalToWorld();
|
|
|
|
hsPoint3 ourPos = l2w.GetTranslate();
|
|
|
|
hsPoint3 delta = ourPos - camPos;
|
|
|
|
float distanceSquared = delta.MagnitudeSquared();
|
|
|
|
if (distanceSquared < fLODDistance * fLODDistance)
|
|
|
|
SetLOD(std::max(0, fMinLOD));
|
|
|
|
else if (distanceSquared < fLODDistance * fLODDistance * 4.0)
|
|
|
|
SetLOD(std::max(1, fMinLOD));
|
|
|
|
else
|
|
|
|
SetLOD(2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Should always be called from AdjustLOD
|
|
|
|
bool plArmatureModBase::SetLOD(int iNewLOD)
|
|
|
|
{
|
|
|
|
if (iNewLOD >= fMeshKeys.size())
|
|
|
|
iNewLOD = fMeshKeys.size() - 1;
|
|
|
|
|
|
|
|
int oldLOD = fCurLOD;
|
|
|
|
if (iNewLOD != fCurLOD)
|
|
|
|
{
|
|
|
|
int oldLOD = fCurLOD;
|
|
|
|
if(fMeshKeys.size() > iNewLOD)
|
|
|
|
{
|
|
|
|
if (fCurLOD != -1)
|
|
|
|
{
|
|
|
|
plSceneObject * oldMesh = (plSceneObject *)fMeshKeys[oldLOD]->GetObjectPtr();
|
|
|
|
EnableDrawingTree(oldMesh, false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// just starting up; turn all except current off
|
|
|
|
for (int i = 0; i < fMeshKeys.size(); i++)
|
|
|
|
{
|
|
|
|
if (i != iNewLOD)
|
|
|
|
{
|
|
|
|
plKey offKey = fMeshKeys[i];
|
|
|
|
plSceneObject * offMesh = (plSceneObject *)offKey->GetObjectPtr();
|
|
|
|
EnableDrawingTree(offMesh, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fCurLOD = iNewLOD;
|
|
|
|
plSceneObject * newMesh = (plSceneObject *)fMeshKeys[fCurLOD]->GetObjectPtr();
|
|
|
|
EnableDrawingTree(newMesh, true);
|
|
|
|
|
|
|
|
int boneLOD;
|
|
|
|
for (boneLOD = 0; boneLOD < fMeshKeys.size(); boneLOD++)
|
|
|
|
IEnableBones(boneLOD, boneLOD <= iNewLOD ? false: true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return oldLOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureModBase::RefreshTree()
|
|
|
|
{
|
|
|
|
fCurLOD = -1;
|
|
|
|
AdjustLOD();
|
|
|
|
}
|
|
|
|
|
|
|
|
int plArmatureModBase::AppendMeshKey(plKey meshKey)
|
|
|
|
{
|
|
|
|
fMeshKeys.push_back(meshKey);
|
|
|
|
return fMeshKeys.size() - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int plArmatureModBase::AppendBoneVec(plKeyVector *boneVec)
|
|
|
|
{
|
|
|
|
fUnusedBones.push_back(boneVec);
|
|
|
|
return fUnusedBones.size() - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t plArmatureModBase::GetNumLOD() const
|
|
|
|
{
|
|
|
|
return fMeshKeys.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureModBase::EnablePhysics(bool status, uint16_t reason /* = kDisableReasonUnknown */)
|
|
|
|
{
|
|
|
|
if (status)
|
|
|
|
fDisabledPhysics &= ~reason;
|
|
|
|
else
|
|
|
|
fDisabledPhysics |= reason;
|
|
|
|
|
|
|
|
bool newStatus = !fDisabledPhysics;
|
|
|
|
|
|
|
|
if (fController)
|
|
|
|
fController->Enable(newStatus);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Enabling Kinematics (state=true) will have the affect of:
|
|
|
|
// - disabling outside forces (gravity and other physics objects pushing us)
|
|
|
|
// - but leave on collisions with detector regions
|
|
|
|
// Disabling Kinematics (state=false) means:
|
|
|
|
// - all outside forces will affect us and collisions on
|
|
|
|
// i.e. normal enabled physical
|
|
|
|
void plArmatureModBase::EnablePhysicsKinematic(bool status)
|
|
|
|
{
|
|
|
|
EnablePhysics(!status, kDisableReasonKinematic);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureModBase::EnableDrawing(bool status, uint16_t reason /* = kDisableReasonUnknown */)
|
|
|
|
{
|
|
|
|
bool oldStatus = !fDisabledDraw;
|
|
|
|
if (status)
|
|
|
|
fDisabledDraw &= ~reason;
|
|
|
|
else
|
|
|
|
fDisabledDraw |= reason;
|
|
|
|
|
|
|
|
bool newStatus = !fDisabledDraw;
|
|
|
|
if (oldStatus == newStatus)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < fMeshKeys.size(); i++)
|
|
|
|
{
|
|
|
|
plSceneObject *obj = plSceneObject::ConvertNoRef(fMeshKeys[i]->ObjectIsLoaded());
|
|
|
|
if (obj)
|
|
|
|
EnableDrawingTree(obj, newStatus);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
fCurLOD = -1; // We just enabled all LOD. Need to force ourselves to recompute current LOD
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureModBase::IFinalize()
|
|
|
|
{
|
|
|
|
if (fWaitFlags & kNeedMesh)
|
|
|
|
ValidateMesh();
|
|
|
|
if (fWaitFlags & kNeedPhysics)
|
|
|
|
ValidatePhysics();
|
|
|
|
if (fWaitFlags & kNeedApplicator)
|
|
|
|
ICustomizeApplicator();
|
|
|
|
if (fWaitFlags & kNeedBrainActivation)
|
|
|
|
{
|
|
|
|
int nBrains = fBrains.size();
|
|
|
|
for (int i = 0; i < nBrains; i++)
|
|
|
|
{
|
|
|
|
// every brain gets activated, but all except the top brain
|
|
|
|
// also get suspended when the one above them is activated.
|
|
|
|
if (i > 0)
|
|
|
|
fBrains[i - 1]->Suspend();
|
|
|
|
|
|
|
|
fBrains[i]->Activate(this);
|
|
|
|
}
|
|
|
|
fWaitFlags &= ~kNeedBrainActivation;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureModBase::ICustomizeApplicator()
|
|
|
|
{
|
|
|
|
const plAGModifier *agMod = plAGModifier::ConvertNoRef(FindModifierByClass(GetTarget(0), plAGModifier::Index()));
|
|
|
|
if (agMod)
|
|
|
|
{
|
|
|
|
plAGApplicator *app = agMod->GetApplicator(kAGPinTransform);
|
|
|
|
if (app)
|
|
|
|
{
|
|
|
|
plMatrixDifferenceApp *differ = plMatrixDifferenceApp::ConvertNoRef(app);
|
|
|
|
|
|
|
|
if (differ)
|
|
|
|
{
|
|
|
|
fRootAnimator = differ;
|
|
|
|
fWaitFlags &= ~kNeedApplicator;
|
|
|
|
return; // already there
|
|
|
|
}
|
|
|
|
}
|
|
|
|
plAGModifier *volAGMod = const_cast<plAGModifier *>(agMod);
|
|
|
|
plMatrixDifferenceApp *differ = new plMatrixDifferenceApp();
|
|
|
|
|
|
|
|
fRootAnimator = differ;
|
|
|
|
volAGMod->SetApplicator(differ);
|
|
|
|
differ->Enable(false);
|
|
|
|
fWaitFlags &= ~kNeedApplicator;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureModBase::IEnableBones(int lod, bool enable)
|
|
|
|
{
|
|
|
|
if (lod < fUnusedBones.size())
|
|
|
|
{
|
|
|
|
plKeyVector *vec = fUnusedBones[lod];
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < vec->size(); i++)
|
|
|
|
((plAGModifier *)(*vec)[i]->GetObjectPtr())->Enable(enable);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
const char *plArmatureMod::BoneStrings[] = {"Male", "Female", "Critter", "Actor"};
|
|
|
|
|
|
|
|
float plArmatureMod::fMouseTurnSensitivity = 1.f;
|
|
|
|
bool plArmatureMod::fClickToTurn = true;
|
|
|
|
|
|
|
|
void plArmatureMod::IInitDefaults()
|
|
|
|
{
|
|
|
|
fBoneRootAnimator = nil;
|
|
|
|
fRootAGMod = nil;
|
|
|
|
fFootSoundSOKey = nil;
|
|
|
|
fLinkSoundSOKey = nil;
|
|
|
|
fBodyType = kBoneBaseMale;
|
|
|
|
fClothingOutfit = nil;
|
|
|
|
fClothingSDLMod = nil;
|
|
|
|
fAvatarSDLMod = nil;
|
|
|
|
fAvatarPhysicalSDLMod = nil;
|
|
|
|
fEffects = nil;
|
|
|
|
fDebugOn = false;
|
|
|
|
fBoneMap = nil;
|
|
|
|
fStealthMode = plAvatarStealthModeMsg::kStealthVisible;
|
|
|
|
fStealthLevel = 0;
|
|
|
|
fMouseFrameTurnStrength = 0.f;
|
|
|
|
fSuspendInputCount = 0;
|
|
|
|
fIsLinkedIn = false;
|
|
|
|
fMidLink = false;
|
|
|
|
fAlreadyPanicLinking = false;
|
|
|
|
fReverseFBOnIdle = false;
|
|
|
|
fFollowerParticleSystemSO = nil;
|
|
|
|
fPendingSynch = false;
|
|
|
|
fOpaque = true;
|
|
|
|
fPhysHeight = 0.f;
|
|
|
|
fPhysWidth = 0.f;
|
|
|
|
fUpdateMsg = nil;
|
|
|
|
fRootName = plString::Null;
|
|
|
|
fDontPanicLink = false;
|
|
|
|
fBodyAgeName = "GlobalAvatars";
|
|
|
|
fBodyFootstepSoundPage = "Audio";
|
|
|
|
fAnimationPrefix = "Male";
|
|
|
|
fUserStr = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
plArmatureMod::plArmatureMod() : plArmatureModBase()
|
|
|
|
{
|
|
|
|
IInitDefaults();
|
|
|
|
fWaitFlags |= kNeedAudio | kNeedCamera | kNeedSpawn;
|
|
|
|
}
|
|
|
|
|
|
|
|
plArmatureMod::~plArmatureMod()
|
|
|
|
{
|
|
|
|
delete fBoneMap;
|
|
|
|
|
|
|
|
if (fUpdateMsg)
|
|
|
|
fUpdateMsg->UnRef();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SetPositionAndRotationSim(const hsPoint3* position, const hsQuat* rotation)
|
|
|
|
{
|
|
|
|
const plCoordinateInterface* subworldCI = nil;
|
|
|
|
if (fController)
|
|
|
|
subworldCI = fController->GetSubworldCI();
|
|
|
|
|
|
|
|
hsMatrix44 l2w, w2l;
|
|
|
|
|
|
|
|
// If we need the position or rotation, grab it from the avatar
|
|
|
|
if (!position || !rotation)
|
|
|
|
{
|
|
|
|
// Get the current position of the avatar in sim space
|
|
|
|
hsMatrix44 avatarL2S = GetTarget(0)->GetLocalToWorld();
|
|
|
|
if (subworldCI)
|
|
|
|
avatarL2S = subworldCI->GetWorldToLocal() * avatarL2S;
|
|
|
|
|
|
|
|
if (!rotation)
|
|
|
|
l2w = avatarL2S;
|
|
|
|
else
|
|
|
|
rotation->MakeMatrix(&l2w);
|
|
|
|
|
|
|
|
if (!position)
|
|
|
|
{
|
|
|
|
hsPoint3 simPos;
|
|
|
|
avatarL2S.GetTranslate(&simPos);
|
|
|
|
l2w.SetTranslate(&simPos);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
l2w.SetTranslate(position);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rotation->MakeMatrix(&l2w);
|
|
|
|
l2w.SetTranslate(position);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We've got the requested position of the avatar in subworld space, convert
|
|
|
|
// it to world space if necessary
|
|
|
|
if (subworldCI)
|
|
|
|
l2w = subworldCI->GetLocalToWorld() * l2w;
|
|
|
|
l2w.GetInverse(&w2l);
|
|
|
|
|
|
|
|
GetTarget(0)->SetTransform(l2w, w2l);
|
|
|
|
GetTarget(0)->FlushTransform();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::GetPositionAndRotationSim(hsPoint3* position, hsQuat* rotation)
|
|
|
|
{
|
|
|
|
hsMatrix44 l2s = GetTarget(0)->GetLocalToWorld();
|
|
|
|
|
|
|
|
if (fController)
|
|
|
|
{
|
|
|
|
const plCoordinateInterface* subworldCI = fController->GetSubworldCI();
|
|
|
|
if (subworldCI)
|
|
|
|
l2s = subworldCI->GetWorldToLocal() * l2s;
|
|
|
|
|
|
|
|
if (position)
|
|
|
|
l2s.GetTranslate(position);
|
|
|
|
if (rotation)
|
|
|
|
rotation->SetFromMatrix(&l2s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const plSceneObject *plArmatureMod::FindBone(const plString & name) const
|
|
|
|
{
|
|
|
|
plSceneObject *result = nil;
|
|
|
|
|
|
|
|
plAGModifier * mod = GetChannelMod(name);
|
|
|
|
|
|
|
|
if (mod)
|
|
|
|
result = mod->GetTarget(0);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
const plSceneObject *plArmatureMod::FindBone(uint32_t id) const
|
|
|
|
{
|
|
|
|
if(fBoneMap)
|
|
|
|
return fBoneMap->FindBone(id);
|
|
|
|
else
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::AddBoneMapping(uint32_t id, const plSceneObject *bone)
|
|
|
|
{
|
|
|
|
if(!fBoneMap)
|
|
|
|
fBoneMap = new plAvBoneMap();
|
|
|
|
|
|
|
|
fBoneMap->AddBoneMapping(id, bone);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::WindowActivate(bool active)
|
|
|
|
{
|
|
|
|
if (!active) // We don't want the avatar to move while we don't have focus
|
|
|
|
{
|
|
|
|
if (!plAvatarMgr::GetInstance())
|
|
|
|
return;
|
|
|
|
|
|
|
|
plArmatureMod *localAv = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
|
|
|
if (localAv)
|
|
|
|
localAv->ClearInputFlags(true, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plString plArmatureMod::fSpawnPointOverride;
|
|
|
|
|
|
|
|
void plArmatureMod::SetSpawnPointOverride(const plString &overrideObjName)
|
|
|
|
{
|
|
|
|
fSpawnPointOverride = overrideObjName.ToLower();
|
|
|
|
}
|
|
|
|
|
|
|
|
int plArmatureMod::IFindSpawnOverride()
|
|
|
|
{
|
|
|
|
if (fSpawnPointOverride.IsEmpty())
|
|
|
|
return -1;
|
|
|
|
int i;
|
|
|
|
plAvatarMgr *mgr = plAvatarMgr::GetInstance();
|
|
|
|
for (i = 0; i < mgr->NumSpawnPoints(); i++)
|
|
|
|
{
|
|
|
|
const plString &name = mgr->GetSpawnPoint(i)->GetTarget(0)->GetKeyName();
|
|
|
|
if (name.Find(fSpawnPointOverride, plString::kCaseInsensitive) >= 0)
|
|
|
|
return i; // Found it!
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::Spawn(double timeNow)
|
|
|
|
{
|
|
|
|
plAvatarMgr *mgr = plAvatarMgr::GetInstance();
|
|
|
|
int numSpawnPoints = mgr->NumSpawnPoints();
|
|
|
|
|
|
|
|
int spawnNum = IFindSpawnOverride();
|
|
|
|
if( spawnNum == -1 )
|
|
|
|
{
|
|
|
|
spawnNum = plAvatarMgr::GetInstance()->FindSpawnPoint( "LinkInPointDefault" );
|
|
|
|
if( spawnNum == -1 )
|
|
|
|
{
|
|
|
|
spawnNum = plNetClientApp::GetInstance()->GetJoinOrder();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hsAssert(numSpawnPoints, "No spawn points! You are about to crash!");
|
|
|
|
|
|
|
|
if ( numSpawnPoints )
|
|
|
|
spawnNum %= numSpawnPoints;
|
|
|
|
|
|
|
|
ValidatePhysics();
|
|
|
|
|
|
|
|
SpawnAt(spawnNum, timeNow);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SpawnAt(int spawnNum, double time)
|
|
|
|
{
|
|
|
|
plAvatarMgr *mgr = plAvatarMgr::GetInstance();
|
|
|
|
const plSpawnModifier * spawn = mgr->GetSpawnPoint(spawnNum);
|
|
|
|
|
|
|
|
hsMatrix44 l2w;
|
|
|
|
hsMatrix44 w2l;
|
|
|
|
if ( spawn )
|
|
|
|
{
|
|
|
|
const plSceneObject * spawnObj = spawn->GetTarget(0);
|
|
|
|
l2w = spawnObj->GetLocalToWorld();
|
|
|
|
w2l = spawnObj->GetWorldToLocal();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fController)
|
|
|
|
fController->ResetAchievedLinearVelocity();
|
|
|
|
plCoordinateInterface* ci = (plCoordinateInterface*)GetTarget(0)->GetCoordinateInterface();
|
|
|
|
l2w.RemoveScale();
|
|
|
|
w2l.RemoveScale();
|
|
|
|
ci->SetTransform(l2w, w2l);
|
|
|
|
ci->FlushTransform();
|
|
|
|
|
|
|
|
if (plVirtualCam1::Instance())
|
|
|
|
plVirtualCam1::Instance()->SetCutNextTrans();
|
|
|
|
|
|
|
|
if (GetFollowerParticleSystemSO())
|
|
|
|
{
|
|
|
|
// Since particles are in world space, if we've got some surrounding us, we've got to translate them to compensate for our warp.
|
|
|
|
plParticleSystem *sys = const_cast<plParticleSystem*>(plParticleSystem::ConvertNoRef(GetFollowerParticleSystemSO()->GetModifierByType(plParticleSystem::Index())));
|
|
|
|
if (sys)
|
|
|
|
{
|
|
|
|
hsPoint3 trans = l2w.GetTranslate() - GetTarget(0)->GetLocalToWorld().GetTranslate();
|
|
|
|
sys->TranslateAllParticles(trans);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int nBrains = fBrains.size();
|
|
|
|
for (int i = 0; i < nBrains; i++)
|
|
|
|
{
|
|
|
|
plArmatureBrain * curBrain = fBrains[i];
|
|
|
|
curBrain->Spawn(time);
|
|
|
|
}
|
|
|
|
fWaitFlags &= ~kNeedSpawn;
|
|
|
|
|
|
|
|
plAvatarSpawnNotifyMsg *notify = new plAvatarSpawnNotifyMsg();
|
|
|
|
notify->SetTimeStamp(hsTimer::GetSysSeconds() + 0.1);
|
|
|
|
notify->SetBCastFlag(plMessage::kBCastByExactType);
|
|
|
|
notify->Send();
|
|
|
|
|
|
|
|
EnablePhysics(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SetFollowerParticleSystemSO(plSceneObject *follower)
|
|
|
|
{
|
|
|
|
// TODO: Check for old one and clean up.
|
|
|
|
hsPoint3 trans = GetTarget(0)->GetLocalToWorld().GetTranslate() - follower->GetLocalToWorld().GetTranslate();
|
|
|
|
|
|
|
|
plWarpMsg *warp = new plWarpMsg(GetKey(), follower->GetKey(), plWarpMsg::kFlushTransform | plWarpMsg::kZeroVelocity,
|
|
|
|
GetTarget(0)->GetLocalToWorld());
|
|
|
|
warp->Send();
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(follower->GetKey(), new plAttachMsg(GetTarget(0)->GetKey(), nil, plRefMsg::kOnRequest), plRefFlags::kActiveRef);
|
|
|
|
fFollowerParticleSystemSO = follower;
|
|
|
|
|
|
|
|
plParticleSystem *sys = const_cast<plParticleSystem*>(plParticleSystem::ConvertNoRef(follower->GetModifierByType(plParticleSystem::Index())));
|
|
|
|
if (sys)
|
|
|
|
{
|
|
|
|
sys->fMiscFlags |= plParticleSystem::kParticleSystemAlwaysUpdate;
|
|
|
|
sys->TranslateAllParticles(trans);
|
|
|
|
sys->SetAttachedToAvatar(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plSceneObject *plArmatureMod::GetFollowerParticleSystemSO()
|
|
|
|
{
|
|
|
|
return fFollowerParticleSystemSO;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::RegisterForBehaviorNotify(plKey key)
|
|
|
|
{
|
|
|
|
if (fNotifyKeys.Find(key) == fNotifyKeys.kMissingIndex)
|
|
|
|
fNotifyKeys.Append(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::UnRegisterForBehaviorNotify(plKey key)
|
|
|
|
{
|
|
|
|
fNotifyKeys.RemoveItem(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::IFireBehaviorNotify(uint32_t type, bool behaviorStart)
|
|
|
|
{
|
|
|
|
if (fNotifyKeys.GetCount() > 0)
|
|
|
|
{
|
|
|
|
plAvatarBehaviorNotifyMsg *msg = new plAvatarBehaviorNotifyMsg();
|
|
|
|
msg->SetSender(GetKey());
|
|
|
|
msg->AddReceivers(fNotifyKeys);
|
|
|
|
msg->fType = type;
|
|
|
|
msg->state = behaviorStart;
|
|
|
|
msg->Send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::EnterAge(bool reSpawn)
|
|
|
|
{
|
|
|
|
fMidLink = false;
|
|
|
|
fAlreadyPanicLinking = false;
|
|
|
|
EnablePhysics(true, kDisableReasonLinking);
|
|
|
|
ValidatePhysics();
|
|
|
|
|
|
|
|
// force regions to send an update whenever we enter an age
|
|
|
|
fOldRegionsICareAbout.Clear();
|
|
|
|
fOldRegionsImIn.Clear();
|
|
|
|
|
|
|
|
if (GetFollowerParticleSystemSO())
|
|
|
|
{
|
|
|
|
const plParticleSystem *sys = plParticleSystem::ConvertNoRef(GetFollowerParticleSystemSO()->GetModifierByType(plParticleSystem::Index()));
|
|
|
|
hsAssert(sys, "We have a particle system SO, but no system?");
|
|
|
|
if (sys)
|
|
|
|
{
|
|
|
|
// Need to tell other clients about this
|
|
|
|
plLoadCloneMsg *clone = new plLoadCloneMsg(GetFollowerParticleSystemSO()->GetKey(), plAvatarMgr::GetInstance()->GetKey(), GetKey()->GetUoid().GetClonePlayerID(), true);
|
|
|
|
clone->SetBCastFlag(plMessage::kLocalPropagate, false);
|
|
|
|
clone->Send();
|
|
|
|
|
|
|
|
GetFollowerParticleSystemSO()->DirtySynchState(plParticleSDLMod::kStrNumParticles, plSynchedObject::kBCastToClients);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ClearInputFlags(true, false);
|
|
|
|
if (reSpawn)
|
|
|
|
fWaitFlags |= kNeedSpawn;
|
|
|
|
|
|
|
|
// In case we personal age linked out of a situation and didn't properly
|
|
|
|
// re-enable input...
|
|
|
|
plAvatarInputInterface::GetInstance()->EnableForwardMovement(true);
|
|
|
|
plAvatarInputInterface::GetInstance()->EnableJump(true);
|
|
|
|
while (fSuspendInputCount > 0)
|
|
|
|
ResumeInput();
|
|
|
|
plAvatarInputInterface::GetInstance()->EnableMouseMovement();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::LeaveAge()
|
|
|
|
{
|
|
|
|
plArmatureModBase::LeaveAge();
|
|
|
|
|
|
|
|
fMidLink = true;
|
|
|
|
|
|
|
|
if (fController)
|
|
|
|
{
|
|
|
|
fController->LeaveAge();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GetFollowerParticleSystemSO())
|
|
|
|
{
|
|
|
|
// Need to tell other clients to remove this
|
|
|
|
plLoadCloneMsg *clone = new plLoadCloneMsg(GetFollowerParticleSystemSO()->GetKey(), plAvatarMgr::GetInstance()->GetKey(), GetKey()->GetUoid().GetClonePlayerID(), false);
|
|
|
|
clone->SetBCastFlag(plMessage::kLocalPropagate, false);
|
|
|
|
clone->Send();
|
|
|
|
}
|
|
|
|
|
|
|
|
GetArmatureEffects()->ResetEffects();
|
|
|
|
ClearInputFlags(true, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::PanicLink(bool playLinkOutAnim /* = true */)
|
|
|
|
{
|
|
|
|
// console override... just go back to the beginning
|
|
|
|
if (fDontPanicLink)
|
|
|
|
{
|
|
|
|
Spawn(0.f);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fAlreadyPanicLinking)
|
|
|
|
return;
|
|
|
|
fAlreadyPanicLinking = true;
|
|
|
|
|
|
|
|
|
|
|
|
plNetApp::StaticDebugMsg("plArmatureMod::PanicLink()");
|
|
|
|
|
|
|
|
// make the player book blink as they are linking out
|
|
|
|
pfKIMsg *msg = new pfKIMsg( pfKIMsg::kStartBookAlert );
|
|
|
|
plgDispatch::MsgSend( msg );
|
|
|
|
|
|
|
|
// Can't depend on the anim to link if the human brain isn't ready to deal with it
|
|
|
|
plAvBrainHuman *brain = plAvBrainHuman::ConvertNoRef(GetCurrentBrain());
|
|
|
|
// If things break then uncomment the code below
|
|
|
|
if (!brain)// || brain->IsRunningTask())
|
|
|
|
playLinkOutAnim = false;
|
|
|
|
|
|
|
|
if (playLinkOutAnim)
|
|
|
|
{
|
|
|
|
plAvOneShotLinkTask *task = new plAvOneShotLinkTask;
|
|
|
|
|
|
|
|
plString animName = MakeAnimationName("FallingLinkOut");
|
|
|
|
task->SetAnimName(animName);
|
|
|
|
task->SetMarkerName("touch");
|
|
|
|
|
|
|
|
plAvTaskMsg *taskMsg = new plAvTaskMsg(GetKey(), GetKey(), task);
|
|
|
|
taskMsg->Send();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
EnablePhysics(false, plArmatureMod::kDisableReasonLinking);
|
|
|
|
ILinkToPersonalAge();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::PersonalLink()
|
|
|
|
{
|
|
|
|
// Can't depend on the anim to link if the human brain isn't ready to deal with it
|
|
|
|
plAvBrainHuman *brain = plAvBrainHuman::ConvertNoRef(GetCurrentBrain());
|
|
|
|
if (!brain || brain->IsRunningTask())
|
|
|
|
ILinkToPersonalAge();
|
|
|
|
else
|
|
|
|
{
|
|
|
|
plAvOneShotLinkTask *task = new plAvOneShotLinkTask;
|
|
|
|
plString animName = MakeAnimationName("PersonalLink");
|
|
|
|
task->SetAnimName(animName);
|
|
|
|
task->SetMarkerName("touch");
|
|
|
|
|
|
|
|
plAvTaskMsg *taskMsg = new plAvTaskMsg(GetKey(), GetKey(), task);
|
|
|
|
taskMsg->SetBCastFlag(plMessage::kNetPropagate);
|
|
|
|
taskMsg->Send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::MsgReceive(plMessage* msg)
|
|
|
|
{
|
|
|
|
bool result = false;
|
|
|
|
|
|
|
|
plArmatureBrain *curBrain = nil;
|
|
|
|
if (fBrains.size() > 0)
|
|
|
|
{
|
|
|
|
curBrain = fBrains.back();
|
|
|
|
if(curBrain->MsgReceive(msg))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plAvatarInputStateMsg *aisMsg = plAvatarInputStateMsg::ConvertNoRef(msg);
|
|
|
|
if (aisMsg)
|
|
|
|
{
|
|
|
|
IHandleInputStateMsg(aisMsg);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plControlEventMsg *control = plControlEventMsg::ConvertNoRef(msg);
|
|
|
|
if(control)
|
|
|
|
{
|
|
|
|
IHandleControlMsg(control);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plCorrectionMsg *corMsg = plCorrectionMsg::ConvertNoRef(msg);
|
|
|
|
if (corMsg)
|
|
|
|
{
|
|
|
|
hsMatrix44 correction = corMsg->fWorldToLocal * GetTarget(0)->GetLocalToWorld();
|
|
|
|
if (fBoneRootAnimator)
|
|
|
|
fBoneRootAnimator->SetCorrection(correction);
|
|
|
|
}
|
|
|
|
|
|
|
|
plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef(msg);
|
|
|
|
if (refMsg)
|
|
|
|
{
|
|
|
|
plClothingOutfit *outfit = plClothingOutfit::ConvertNoRef(refMsg->GetRef());
|
|
|
|
if (outfit)
|
|
|
|
{
|
|
|
|
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
|
|
|
|
{
|
|
|
|
fClothingOutfit = outfit;
|
|
|
|
fClothingOutfit->fAvatar = this;
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plPipeRTMakeMsg::Index(), fClothingOutfit->GetKey());
|
|
|
|
}
|
|
|
|
else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) )
|
|
|
|
{
|
|
|
|
if (fClothingOutfit)
|
|
|
|
plgDispatch::Dispatch()->UnRegisterForExactType(plPipeRTMakeMsg::Index(), fClothingOutfit->GetKey());
|
|
|
|
fClothingOutfit = nil;
|
|
|
|
outfit->fAvatar = nil;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
plArmatureEffectsMgr *effects = plArmatureEffectsMgr::ConvertNoRef(refMsg->GetRef());
|
|
|
|
if (effects)
|
|
|
|
{
|
|
|
|
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
|
|
|
|
{
|
|
|
|
fEffects = effects;
|
|
|
|
fEffects->fArmature = this;
|
|
|
|
}
|
|
|
|
else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) )
|
|
|
|
{
|
|
|
|
fEffects->fArmature = nil;
|
|
|
|
fEffects = nil;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(refMsg->GetRef());
|
|
|
|
if (so)
|
|
|
|
{
|
|
|
|
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
|
|
|
|
{
|
|
|
|
fClothToSOMap.ExpandAndZero(refMsg->fWhich + 1);
|
|
|
|
fClothToSOMap[refMsg->fWhich] = so;
|
|
|
|
}
|
|
|
|
else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) )
|
|
|
|
{
|
|
|
|
fClothToSOMap[refMsg->fWhich] = nil;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fEffects)
|
|
|
|
{
|
|
|
|
plArmatureEffectMsg *aeMsg = plArmatureEffectMsg::ConvertNoRef(msg);
|
|
|
|
plArmatureEffectStateMsg *aesMsg = plArmatureEffectStateMsg::ConvertNoRef(msg);
|
|
|
|
if (aeMsg || aesMsg)
|
|
|
|
return fEffects->MsgReceive(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
plAttachMsg *aMsg = plAttachMsg::ConvertNoRef(msg);
|
|
|
|
if (aMsg)
|
|
|
|
{
|
|
|
|
GetTarget(0)->MsgReceive(aMsg);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plEnableMsg *enMsg = plEnableMsg::ConvertNoRef(msg);
|
|
|
|
if(enMsg && enMsg->Type(plEnableMsg::kDrawable))
|
|
|
|
{
|
|
|
|
bool enable = enMsg->Cmd(plEnableMsg::kEnable);
|
|
|
|
bool disable = enMsg->Cmd(plEnableMsg::kDisable);
|
|
|
|
|
|
|
|
hsAssert(enable != disable, "Conflicting or missing commands to enable message");
|
|
|
|
|
|
|
|
if(enable != disable)
|
|
|
|
EnableDrawing(enable);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if(enMsg && enMsg->Cmd(plEnableMsg::kPhysical))
|
|
|
|
{
|
|
|
|
EnablePhysics( enMsg->Cmd(plEnableMsg::kEnable));
|
|
|
|
}
|
|
|
|
|
|
|
|
plAvatarOpacityCallbackMsg *opacMsg = plAvatarOpacityCallbackMsg::ConvertNoRef(msg);
|
|
|
|
if (opacMsg)
|
|
|
|
{
|
|
|
|
plLayerLinkAnimation *linkAnim = IFindLayerLinkAnim();
|
|
|
|
if (linkAnim)
|
|
|
|
{
|
|
|
|
bool trans = (linkAnim->GetTimeConvert().CurrentAnimTime() != 0.f);
|
|
|
|
ISetTransparentDrawOrder(trans);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plAvatarPhysicsEnableCallbackMsg *epMsg = plAvatarPhysicsEnableCallbackMsg::ConvertNoRef(msg);
|
|
|
|
if (epMsg)
|
|
|
|
{
|
|
|
|
EnablePhysics(true);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plAvatarStealthModeMsg *stealthMsg = plAvatarStealthModeMsg::ConvertNoRef(msg);
|
|
|
|
if (stealthMsg && stealthMsg->GetSender() == GetTarget(0)->GetKey() &&
|
|
|
|
(stealthMsg->fLevel != fStealthLevel || stealthMsg->fMode != fStealthMode))
|
|
|
|
{
|
|
|
|
fStealthMode = stealthMsg->fMode;
|
|
|
|
fStealthLevel = (stealthMsg->fMode == plAvatarStealthModeMsg::kStealthVisible) ? 0 : stealthMsg->fLevel;
|
|
|
|
|
|
|
|
if (fStealthMode == plAvatarStealthModeMsg::kStealthCloaked)
|
|
|
|
{
|
|
|
|
if (fUpdateMsg)
|
|
|
|
fUpdateMsg->SetInvis(true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (fUpdateMsg)
|
|
|
|
fUpdateMsg->SetInvis(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fEffects)
|
|
|
|
fEffects->MsgReceive(stealthMsg);
|
|
|
|
|
|
|
|
if (stealthMsg->fMode == plAvatarStealthModeMsg::kStealthCloaked)
|
|
|
|
EnableDrawing(false, kDisableReasonCCR);
|
|
|
|
else
|
|
|
|
EnableDrawing(true, kDisableReasonCCR);
|
|
|
|
|
|
|
|
DirtySynchState(kSDLAvatar, 0); // changed invisibility state
|
|
|
|
plNetApp::StaticDebugMsg("ArmatureMod: rcvd avatarStealth msg, cloaked=%d", stealthMsg->fMode == plAvatarStealthModeMsg::kStealthCloaked);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plParticleTransferMsg *partMsg = plParticleTransferMsg::ConvertNoRef(msg);
|
|
|
|
if (partMsg)
|
|
|
|
{
|
|
|
|
// First, do we have the system?
|
|
|
|
plSceneObject *dstSysSO = GetFollowerParticleSystemSO();
|
|
|
|
if (!dstSysSO || ((plKeyImp*)dstSysSO->GetKey())->GetCloneOwner() != partMsg->fSysSOKey)
|
|
|
|
{
|
|
|
|
// Need to clone and resend.
|
|
|
|
if (plNetClientApp::GetInstance()->GetLocalPlayer() != GetTarget(0))
|
|
|
|
return true; // Only the local player can create the clone.
|
|
|
|
|
|
|
|
// Clone is sent to all players.
|
|
|
|
plLoadCloneMsg *cloneMsg = new plLoadCloneMsg(partMsg->fSysSOKey->GetUoid(), plAvatarMgr::GetInstance()->GetKey(), GetKey()->GetUoid().GetClonePlayerID());
|
|
|
|
cloneMsg->SetTriggerMsg(partMsg);
|
|
|
|
cloneMsg->SetBCastFlag(plMessage::kNetForce);
|
|
|
|
cloneMsg->Send();
|
|
|
|
|
|
|
|
// Expect to receive a clone message later. Return for now.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
plParticleSystem *dstSys = const_cast<plParticleSystem*>(plParticleSystem::ConvertNoRef(dstSysSO->GetModifierByType(plParticleSystem::Index())));
|
|
|
|
if (dstSys)
|
|
|
|
{
|
|
|
|
// Got the system. Time to steal particles!
|
|
|
|
plParticleSystem *srcSys = nil;
|
|
|
|
plSceneObject *srcSysSO = plSceneObject::ConvertNoRef(partMsg->fSysSOKey->ObjectIsLoaded());
|
|
|
|
if (srcSysSO)
|
|
|
|
srcSys = const_cast<plParticleSystem*>(plParticleSystem::ConvertNoRef(srcSysSO->GetModifierByType(plParticleSystem::Index())));
|
|
|
|
|
|
|
|
// A nil source system is ok. It just won't copy anything.
|
|
|
|
int numToGen = partMsg->fNumToTransfer - dstSys->StealParticlesFrom(srcSys, partMsg->fNumToTransfer);
|
|
|
|
if (numToGen > 0)
|
|
|
|
{
|
|
|
|
dstSys->GenerateParticles(numToGen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plLoadAvatarMsg *avLoadMsg = plLoadAvatarMsg::ConvertNoRef(msg);
|
|
|
|
if (avLoadMsg)
|
|
|
|
{
|
|
|
|
bool isPlayer = avLoadMsg->GetIsPlayer();
|
|
|
|
if (!isPlayer)
|
|
|
|
plgDispatch::Dispatch()->UnRegisterForExactType(plAgeLoadedMsg::Index(), GetKey());
|
|
|
|
|
|
|
|
// see if we're being spawned explictly
|
|
|
|
plKey spawnPoint = avLoadMsg->GetSpawnPoint();
|
|
|
|
|
|
|
|
if (spawnPoint)
|
|
|
|
{
|
|
|
|
hsKeyedObject * spawnKO = spawnPoint->ObjectIsLoaded();
|
|
|
|
if (spawnKO)
|
|
|
|
{
|
|
|
|
plSceneObject * spawnSO = plSceneObject::ConvertNoRef(spawnKO);
|
|
|
|
if(spawnSO)
|
|
|
|
{
|
|
|
|
hsMatrix44 l2w = spawnSO->GetLocalToWorld();
|
|
|
|
plWarpMsg *warpM = new plWarpMsg(nil, GetTarget(0)->GetKey(), plWarpMsg::kFlushTransform, l2w);
|
|
|
|
warpM->Send();
|
|
|
|
fWaitFlags &= ~kNeedSpawn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We also want to use the trigger msg when loading an avatar
|
|
|
|
MsgReceive(avLoadMsg->GetTriggerMsg());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plLoadCloneMsg *cloneMsg = plLoadCloneMsg::ConvertNoRef(msg);
|
|
|
|
if (cloneMsg)
|
|
|
|
{
|
|
|
|
if (cloneMsg->GetIsLoading())
|
|
|
|
{
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(cloneMsg->GetCloneKey()->ObjectIsLoaded());
|
|
|
|
if (so)
|
|
|
|
{
|
|
|
|
so->SetSynchFlagsBit(plSynchedObject::kAllStateIsVolatile);
|
|
|
|
plParticleSystem *sys = const_cast<plParticleSystem*>(plParticleSystem::ConvertNoRef(so->GetModifierByType(plParticleSystem::Index())));
|
|
|
|
hsAssert(sys, "Modifier not loaded yet.");
|
|
|
|
if (sys)
|
|
|
|
{
|
|
|
|
sys->DisableGenerators();
|
|
|
|
sys->MsgReceive(cloneMsg); // Let the system know to finish its init stuff
|
|
|
|
SetFollowerParticleSystemSO(so);
|
|
|
|
if (!plNetClientApp::GetInstance()->IsLoadingInitialAgeState())
|
|
|
|
{
|
|
|
|
// Just happened. Redirect the trigger and transfer the particles
|
|
|
|
MsgReceive(cloneMsg->GetTriggerMsg());
|
|
|
|
}
|
|
|
|
// otherwise we'll get SDL state about it and handle it in plParticleSDLMod
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (cloneMsg->GetCloneKey() == GetFollowerParticleSystemSO()->GetKey())
|
|
|
|
{
|
|
|
|
SetFollowerParticleSystemSO(nil);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plLoadClothingMsg *clothingMsg = plLoadClothingMsg::ConvertNoRef(msg);
|
|
|
|
if (clothingMsg)
|
|
|
|
{
|
|
|
|
// We got a clothing file and are supposed to load our avatar from it.
|
|
|
|
// Let's tell our outfit to do so!
|
|
|
|
fClothingOutfit->SetClothingFile(clothingMsg->GetClothingFile());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plLinkEffectBCMsg *linkBCMsg = plLinkEffectBCMsg::ConvertNoRef(msg);
|
|
|
|
if (linkBCMsg)
|
|
|
|
{
|
|
|
|
if (GetTarget(0)->GetKey() == linkBCMsg->fLinkKey)
|
|
|
|
{
|
|
|
|
if (linkBCMsg->HasLinkFlag(plLinkEffectBCMsg::kLeavingAge))
|
|
|
|
{
|
|
|
|
fMidLink = true;
|
|
|
|
IFireBehaviorNotify(plHBehavior::kBehaviorTypeLinkOut, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
IFireBehaviorNotify(plHBehavior::kBehaviorTypeLinkIn, true);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plLinkInDoneMsg *doneMsg = plLinkInDoneMsg::ConvertNoRef(msg);
|
|
|
|
if (doneMsg)
|
|
|
|
{
|
|
|
|
fIsLinkedIn = true;
|
|
|
|
IFireBehaviorNotify(plHBehavior::kBehaviorTypeLinkIn, false);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plAgeLoadedMsg *ageLoadMsg = plAgeLoadedMsg::ConvertNoRef(msg);
|
|
|
|
if (ageLoadMsg)
|
|
|
|
{
|
|
|
|
if (ageLoadMsg->fLoaded)
|
|
|
|
// only the local player gets these
|
|
|
|
NetworkSynch(hsTimer::GetSysSeconds(), true);
|
|
|
|
else
|
|
|
|
fIsLinkedIn = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plAgeLoaded2Msg *agePreLoadMsg = plAgeLoaded2Msg::ConvertNoRef(msg);
|
|
|
|
if (agePreLoadMsg)
|
|
|
|
{
|
|
|
|
// all the age data is loaded -- add our physical controller to the age
|
|
|
|
ValidatePhysics();
|
|
|
|
EnablePhysics(true);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimCmdMsg *cmdMsg = plAnimCmdMsg::ConvertNoRef(msg);
|
|
|
|
if (cmdMsg)
|
|
|
|
{
|
|
|
|
hsAssert(false, "Illegal use of plAnimCmdMsg on an avatar.");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plSubWorldMsg* subMsg = plSubWorldMsg::ConvertNoRef(msg);
|
|
|
|
if (subMsg)
|
|
|
|
{
|
|
|
|
if (fController)
|
|
|
|
{
|
|
|
|
fController->SetSubworld(subMsg->fWorldKey);
|
|
|
|
DirtySynchState(kSDLAvatar, plSynchedObject::kBCastToClients);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return plAGMasterMod::MsgReceive(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::IHandleControlMsg(plControlEventMsg* pMsg)
|
|
|
|
{
|
|
|
|
// Slight change in design here...
|
|
|
|
// Avatar input control messages are only sent locally.
|
|
|
|
// When things change (that a remote client would care about),
|
|
|
|
// we call SynchInputState and that sends off the bit vector
|
|
|
|
// for the state of all avatar input controls.
|
|
|
|
//
|
|
|
|
// This means a remote player will only know which controls are
|
|
|
|
// active at a certain point. It might miss a particular keypress.
|
|
|
|
// Don't rely on them receiving it.
|
|
|
|
|
|
|
|
if (fSuspendInputCount > 0)
|
|
|
|
{
|
|
|
|
fQueuedCtrlMessages.push_back(pMsg);
|
|
|
|
pMsg->Ref();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ControlEventCode moveCode = pMsg->GetControlCode();
|
|
|
|
|
|
|
|
bool flagChanged = false;
|
|
|
|
if(pMsg->ControlActivated())
|
|
|
|
{
|
|
|
|
if (moveCode == B_CONTROL_TURN_TO && fClickToTurn)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
// This will do an LOS and call TurnToPoint()
|
|
|
|
plSceneInputInterface::GetInstance()->RequestAvatarTurnToPointLOS();
|
|
|
|
#else
|
|
|
|
plVisHit hit;
|
|
|
|
if( plVisLOSMgr::Instance()->CursorCheck(hit) )
|
|
|
|
TurnToPoint(hit.fPos);
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This control is intended for turning while walking/running.
|
|
|
|
// It is never net propagated. We just flag our physics state
|
|
|
|
// as dirty, and let the physics synch (with dead reckoning) handle it.
|
|
|
|
if (moveCode == A_CONTROL_TURN)
|
|
|
|
{
|
|
|
|
// Filter out the messages that are just the mouse recentering
|
|
|
|
if (pMsg->GetPct() < 0.4 && pMsg->GetPct() > -0.4)
|
|
|
|
{
|
|
|
|
fMouseFrameTurnStrength += pMsg->GetPct() * fMouseTurnSensitivity;
|
|
|
|
SynchIfLocal(hsTimer::GetSysSeconds(), false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!GetInputFlag( moveCode ) )
|
|
|
|
{
|
|
|
|
SetInputFlag( moveCode, true );
|
|
|
|
flagChanged = true;
|
|
|
|
|
|
|
|
if (moveCode == B_CONTROL_JUMP)
|
|
|
|
SetInputFlag(B_CONTROL_CONSUMABLE_JUMP, true);
|
|
|
|
|
|
|
|
if(plNetClientMgr::GetInstance()->AmCCR())
|
|
|
|
{
|
|
|
|
// special case for clipping: synch every key change
|
|
|
|
SynchIfLocal( hsTimer::GetSysSeconds(), false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( GetInputFlag( moveCode ) )
|
|
|
|
{
|
|
|
|
SetInputFlag( moveCode, false );
|
|
|
|
flagChanged = true;
|
|
|
|
|
|
|
|
if (fReverseFBOnIdle && (moveCode == B_CONTROL_MOVE_FORWARD || moveCode == B_CONTROL_MOVE_BACKWARD))
|
|
|
|
{
|
|
|
|
if (!GetInputFlag(B_CONTROL_MOVE_FORWARD) && !GetInputFlag(B_CONTROL_MOVE_BACKWARD))
|
|
|
|
SetInputFlag(B_CONTROL_LADDER_INVERTED, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (flagChanged && plAvatarInputStateMsg::IsCodeInMap(moveCode))
|
|
|
|
SynchInputState();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::IHandleInputStateMsg(plAvatarInputStateMsg *msg)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
uint32_t curBit;
|
|
|
|
for (i = 0, curBit = 0x1; i < plAvatarInputStateMsg::fMapSize; i++, curBit <<= 1)
|
|
|
|
{
|
|
|
|
SetInputFlag(msg->fCodeMap[i], msg->fState & curBit);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SynchInputState(uint32_t rcvID /* = kInvalidPlayerID */)
|
|
|
|
{
|
|
|
|
if (plAvatarMgr::GetInstance()->GetLocalAvatar() != this)
|
|
|
|
return;
|
|
|
|
|
|
|
|
plAvatarInputStateMsg *msg = new plAvatarInputStateMsg();
|
|
|
|
int i;
|
|
|
|
uint32_t curBit;
|
|
|
|
for (i = 0, curBit = 0x1; i < plAvatarInputStateMsg::fMapSize; i++, curBit <<= 1)
|
|
|
|
{
|
|
|
|
if (GetInputFlag(msg->fCodeMap[i]))
|
|
|
|
msg->fState |= curBit;
|
|
|
|
}
|
|
|
|
msg->AddReceiver(GetKey());
|
|
|
|
msg->SetBCastFlag(plMessage::kNetPropagate);
|
|
|
|
msg->SetBCastFlag(plMessage::kNetUseRelevanceRegions);
|
|
|
|
msg->SetBCastFlag(plMessage::kLocalPropagate, false);
|
|
|
|
msg->SetBCastFlag(plMessage::kNetSendUnreliable, true);
|
|
|
|
if (rcvID != kInvalidPlayerID)
|
|
|
|
msg->AddNetReceiver(rcvID);
|
|
|
|
|
|
|
|
msg->Send();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::ILinkToPersonalAge()
|
|
|
|
{
|
|
|
|
plNetClientMgr * nc = plNetClientMgr::GetInstance();
|
|
|
|
|
|
|
|
plAgeLinkStruct link;
|
|
|
|
link.GetAgeInfo()->SetAgeFilename( kPersonalAgeFilename );
|
|
|
|
link.GetAgeInfo()->SetAgeInstanceName( kPersonalAgeInstanceName );
|
|
|
|
|
|
|
|
plSpawnPointInfo hutSpawnPoint;
|
|
|
|
hutSpawnPoint.SetName(kPersonalAgeLinkInPointCloset);
|
|
|
|
link.SetSpawnPoint(hutSpawnPoint);
|
|
|
|
|
|
|
|
link.SetLinkingRules( plNetCommon::LinkingRules::kOriginalBook );
|
|
|
|
plLinkToAgeMsg* pMsg = new plLinkToAgeMsg( &link );
|
|
|
|
pMsg->SetLinkInAnimName("PersonalBookEnter");
|
|
|
|
pMsg->AddReceiver(nc->GetKey());
|
|
|
|
pMsg->Send();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::IEval(double time, float elapsed, uint32_t dirty)
|
|
|
|
{
|
|
|
|
if (IsFinal())
|
|
|
|
{
|
|
|
|
bool noOverlap = false;
|
|
|
|
|
|
|
|
const plArmatureMod *localPlayer = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
|
|
|
if (plRelevanceMgr::Instance()->GetEnabled() && (localPlayer != nil))
|
|
|
|
{
|
|
|
|
// (May decide to update this elsewhere instead.)
|
|
|
|
plRelevanceMgr::Instance()->SetRegionVectors(GetTarget(0)->GetLocalToWorld().GetTranslate(), fRegionsImIn, fRegionsICareAbout);
|
|
|
|
if (localPlayer != this)
|
|
|
|
{
|
|
|
|
if (!fRegionsImIn.Overlap(localPlayer->fRegionsICareAbout))
|
|
|
|
{
|
|
|
|
noOverlap = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else // Bookkeeping for the local player...
|
|
|
|
{
|
|
|
|
bool update = false;
|
|
|
|
if (fOldRegionsICareAbout != fRegionsICareAbout)
|
|
|
|
{
|
|
|
|
update = true;
|
|
|
|
fOldRegionsICareAbout = fRegionsICareAbout;
|
|
|
|
}
|
|
|
|
if (fOldRegionsImIn != fRegionsImIn)
|
|
|
|
{
|
|
|
|
update = true;
|
|
|
|
fOldRegionsImIn = fRegionsImIn;
|
|
|
|
}
|
|
|
|
if (update)
|
|
|
|
{
|
|
|
|
// Send message to the server here.
|
|
|
|
plNetMsgRelevanceRegions relRegionsNetMsg;
|
|
|
|
relRegionsNetMsg.SetNetProtocol(kNetProtocolCli2Game);
|
|
|
|
relRegionsNetMsg.SetRegionsICareAbout(fRegionsICareAbout);
|
|
|
|
relRegionsNetMsg.SetRegionsImIn(fRegionsImIn);
|
|
|
|
plNetClientApp::GetInstance()->SendMsg(&relRegionsNetMsg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (noOverlap)
|
|
|
|
{
|
|
|
|
EnablePhysics(false, kDisableReasonRelRegion);
|
|
|
|
EnableDrawing(false, kDisableReasonRelRegion);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
EnablePhysics(true, kDisableReasonRelRegion);
|
|
|
|
EnableDrawing(true, kDisableReasonRelRegion);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fMouseFrameTurnStrength == 0 && GetInputFlag(A_CONTROL_TURN))
|
|
|
|
SetInputFlag(A_CONTROL_TURN, false);
|
|
|
|
|
|
|
|
if (!fMidLink)
|
|
|
|
plArmatureModBase::IEval(time, elapsed, dirty);
|
|
|
|
|
|
|
|
fUpdateMsg->Ref();
|
|
|
|
fUpdateMsg->Send();
|
|
|
|
|
|
|
|
if (fPendingSynch)
|
|
|
|
NetworkSynch(time, false);
|
|
|
|
if (fDebugOn)
|
|
|
|
RefreshDebugDisplay();
|
|
|
|
|
|
|
|
fMouseFrameTurnStrength = 0.f; // Processed this frame. Clear it.
|
|
|
|
|
|
|
|
// update our attached particle system if necessary
|
|
|
|
if (GetFollowerParticleSystemSO())
|
|
|
|
{
|
|
|
|
plSceneObject* follower = GetFollowerParticleSystemSO();
|
|
|
|
hsPoint3 trans = GetTarget(0)->GetLocalToWorld().GetTranslate() - follower->GetLocalToWorld().GetTranslate();
|
|
|
|
if (trans.MagnitudeSquared() > 1) // we can be a bit fuzzy about this, since the particle system is rather large and people won't notice it being off
|
|
|
|
{
|
|
|
|
plWarpMsg *warp = new plWarpMsg(GetKey(), follower->GetKey(), plWarpMsg::kFlushTransform | plWarpMsg::kZeroVelocity,
|
|
|
|
GetTarget(0)->GetLocalToWorld());
|
|
|
|
warp->Send();
|
|
|
|
|
|
|
|
plParticleSystem *sys = const_cast<plParticleSystem*>(plParticleSystem::ConvertNoRef(follower->GetModifierByType(plParticleSystem::Index())));
|
|
|
|
if (sys)
|
|
|
|
{
|
|
|
|
sys->fMiscFlags |= plParticleSystem::kParticleSystemAlwaysUpdate;
|
|
|
|
sys->TranslateAllParticles(trans);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
IFinalize();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::AddTarget(plSceneObject* so)
|
|
|
|
{
|
|
|
|
plArmatureModBase::AddTarget(so);
|
|
|
|
|
|
|
|
plAvatarMgr::GetInstance()->AddAvatar(this); // register for easy lookup
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plAvatarMsg::Index(), GetKey());
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plLinkEffectBCMsg::Index(), GetKey());
|
|
|
|
|
|
|
|
// all avatars will register for the age loaded message.
|
|
|
|
// only players need it, but we don't know that we're a player until it's too late to get it.
|
|
|
|
// non-players will unregister when they learn the truth.
|
|
|
|
if (IsLocallyOwned())
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plAgeLoadedMsg::Index(), GetKey());
|
|
|
|
plgDispatch::Dispatch()->RegisterForType(plAgeLoaded2Msg::Index(), GetKey());
|
|
|
|
|
|
|
|
// attach a clothingSDLModifier to handle clothing saveState
|
|
|
|
delete fClothingSDLMod;
|
|
|
|
fClothingSDLMod = new plClothingSDLModifier;
|
|
|
|
fClothingSDLMod->SetClothingOutfit(GetClothingOutfit()); // ok if clothingOutfit is nil at this point
|
|
|
|
so->AddModifier(fClothingSDLMod);
|
|
|
|
|
|
|
|
// add avatar sdl modifier
|
|
|
|
delete fAvatarSDLMod;
|
|
|
|
fAvatarSDLMod = new plAvatarSDLModifier;
|
|
|
|
so->AddModifier(fAvatarSDLMod);
|
|
|
|
|
|
|
|
delete fAvatarPhysicalSDLMod;
|
|
|
|
fAvatarPhysicalSDLMod = new plAvatarPhysicalSDLModifier;
|
|
|
|
so->AddModifier(fAvatarPhysicalSDLMod);
|
|
|
|
|
|
|
|
// At export time, this key will be nil. This is important, or else we'll overwrite the page the key comes from.
|
|
|
|
if (fFootSoundSOKey != nil)
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(fFootSoundSOKey, new plAttachMsg(so->GetKey(), nil, plRefMsg::kOnRequest), plRefFlags::kActiveRef);
|
|
|
|
if (fLinkSoundSOKey != nil)
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(fLinkSoundSOKey, new plAttachMsg(so->GetKey(), nil, plRefMsg::kOnRequest), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
if (fUpdateMsg)
|
|
|
|
fUpdateMsg->UnRef(); // delete an old one.
|
|
|
|
|
|
|
|
fUpdateMsg = new plArmatureUpdateMsg(GetKey(), so->IsLocallyOwned(), true, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::RemoveTarget(plSceneObject* so)
|
|
|
|
{
|
|
|
|
if (so)
|
|
|
|
{
|
|
|
|
if (fClothingSDLMod)
|
|
|
|
so->RemoveModifier(fClothingSDLMod);
|
|
|
|
if (fAvatarSDLMod)
|
|
|
|
so->RemoveModifier(fAvatarSDLMod);
|
|
|
|
if (fAvatarPhysicalSDLMod)
|
|
|
|
so->RemoveModifier(fAvatarPhysicalSDLMod);
|
|
|
|
}
|
|
|
|
delete fClothingSDLMod;
|
|
|
|
fClothingSDLMod = nil;
|
|
|
|
delete fAvatarSDLMod;
|
|
|
|
fAvatarSDLMod = nil;
|
|
|
|
delete fAvatarPhysicalSDLMod;
|
|
|
|
fAvatarPhysicalSDLMod = nil;
|
|
|
|
|
|
|
|
plArmatureModBase::RemoveTarget(so);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::Write(hsStream *stream, hsResMgr *mgr)
|
|
|
|
{
|
|
|
|
// Temporarily going around plArmatureModBase so that we don't
|
|
|
|
// break format
|
|
|
|
plAGMasterMod::Write(stream, mgr);
|
|
|
|
|
|
|
|
mgr->WriteKey(stream, fMeshKeys[0]);
|
|
|
|
stream->WriteSafeString(fRootName);
|
|
|
|
int nBrains = fBrains.size();
|
|
|
|
stream->WriteLE32(nBrains);
|
|
|
|
for (int i = 0; i < nBrains; i++)
|
|
|
|
mgr->WriteCreatable(stream, fBrains[i]);
|
|
|
|
|
|
|
|
if (fClothingOutfit == nil)
|
|
|
|
{
|
|
|
|
stream->WriteBool( false );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
stream->WriteBool( true );
|
|
|
|
mgr->WriteKey(stream, fClothingOutfit->GetKey());
|
|
|
|
}
|
|
|
|
|
|
|
|
stream->WriteLE32(fBodyType);
|
|
|
|
if( fEffects == nil )
|
|
|
|
stream->WriteBool( false );
|
|
|
|
else
|
|
|
|
{
|
|
|
|
stream->WriteBool( true );
|
|
|
|
mgr->WriteKey(stream, fEffects->GetKey());
|
|
|
|
}
|
|
|
|
|
|
|
|
stream->WriteLEFloat(fPhysHeight);
|
|
|
|
stream->WriteLEFloat(fPhysWidth);
|
|
|
|
|
|
|
|
stream->WriteSafeString(fAnimationPrefix);
|
|
|
|
stream->WriteSafeString(fBodyAgeName);
|
|
|
|
stream->WriteSafeString(fBodyFootstepSoundPage);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::Read(hsStream * stream, hsResMgr *mgr)
|
|
|
|
{
|
|
|
|
// Temporarily going around plArmatureModBase so that we don't
|
|
|
|
// break format
|
|
|
|
plAGMasterMod::Read(stream, mgr);
|
|
|
|
|
|
|
|
fMeshKeys.push_back(mgr->ReadKey(stream));
|
|
|
|
|
|
|
|
// read the root name string
|
|
|
|
fRootName = stream->ReadSafeString();
|
|
|
|
|
|
|
|
// read in the brains
|
|
|
|
int nBrains = stream->ReadLE32();
|
|
|
|
for (int i = 0; i < nBrains; i++)
|
|
|
|
{
|
|
|
|
plArmatureBrain * brain = (plArmatureBrain *)mgr->ReadCreatable(stream);
|
|
|
|
this->PushBrain(brain);
|
|
|
|
}
|
|
|
|
|
|
|
|
if( stream->ReadBool() )
|
|
|
|
mgr->ReadKeyNotifyMe(stream, new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); // plClothingBase
|
|
|
|
else
|
|
|
|
fClothingOutfit = nil;
|
|
|
|
|
|
|
|
fBodyType = stream->ReadLE32();
|
|
|
|
|
|
|
|
if( stream->ReadBool() )
|
|
|
|
{
|
|
|
|
plKey effectMgrKey = mgr->ReadKey(stream);
|
|
|
|
mgr->AddViaNotify(effectMgrKey, new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); // plArmatureEffects
|
|
|
|
|
|
|
|
// Attach the Footstep emitter scene object
|
|
|
|
hsResMgr *mgr = hsgResMgr::ResMgr();
|
|
|
|
const plLocation &gLoc = plKeyFinder::Instance().FindLocation(fBodyAgeName, fBodyFootstepSoundPage);
|
|
|
|
|
|
|
|
if (gLoc.IsValid())
|
|
|
|
{
|
|
|
|
const plUoid &myUoid = GetKey()->GetUoid();
|
|
|
|
plUoid SOUoid(gLoc, plSceneObject::Index(), "FootstepSoundObject");
|
|
|
|
fFootSoundSOKey = mgr->FindKey(SOUoid);
|
|
|
|
if (fFootSoundSOKey)
|
|
|
|
{
|
|
|
|
// So it exists... but FindKey won't properly create our clone. So we do.
|
|
|
|
SOUoid.SetClone(myUoid.GetClonePlayerID(), myUoid.GetCloneID());
|
|
|
|
fFootSoundSOKey = mgr->ReRegister(plString::Null, SOUoid);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the effect to our effects manager
|
|
|
|
plUoid effectUoid(gLoc, plArmatureEffectFootSound::Index(), "FootstepSounds");
|
|
|
|
plKey effectKey = mgr->FindKey(effectUoid);
|
|
|
|
if (effectKey)
|
|
|
|
{
|
|
|
|
effectUoid.SetClone(myUoid.GetClonePlayerID(), myUoid.GetCloneID());
|
|
|
|
effectKey = mgr->ReRegister(plString::Null, effectUoid);
|
|
|
|
}
|
|
|
|
if (effectKey != nil)
|
|
|
|
mgr->AddViaNotify(effectKey, new plGenRefMsg(effectMgrKey, plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
// Get the linking sound
|
|
|
|
plUoid LinkUoid(gLoc, plSceneObject::Index(), "LinkSoundSource");
|
|
|
|
fLinkSoundSOKey = mgr->FindKey(LinkUoid);
|
|
|
|
if (fLinkSoundSOKey)
|
|
|
|
{
|
|
|
|
LinkUoid.SetClone(myUoid.GetClonePlayerID(), myUoid.GetCloneID());
|
|
|
|
fLinkSoundSOKey = mgr->ReRegister(plString::Null, LinkUoid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fEffects = nil;
|
|
|
|
|
|
|
|
fPhysHeight = stream->ReadLEFloat();
|
|
|
|
fPhysWidth = stream->ReadLEFloat();
|
|
|
|
|
|
|
|
fAnimationPrefix = stream->ReadSafeString();
|
|
|
|
fBodyAgeName = stream->ReadSafeString();
|
|
|
|
fBodyFootstepSoundPage = stream->ReadSafeString();
|
|
|
|
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plAvatarStealthModeMsg::Index(), GetKey());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::DirtySynchState(const plString& SDLStateName, uint32_t synchFlags)
|
|
|
|
{
|
|
|
|
// skip requests to synch non-avatar state
|
|
|
|
if (SDLStateName.CompareI(kSDLAvatar) != 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
synchFlags |= plSynchedObject::kForceFullSend; // TEMP
|
|
|
|
|
|
|
|
if(GetNumTargets() > 0)
|
|
|
|
{
|
|
|
|
plSceneObject *sObj = GetTarget(0);
|
|
|
|
if(sObj)
|
|
|
|
sObj->DirtySynchState(SDLStateName, synchFlags);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::DirtyPhysicalSynchState(uint32_t synchFlags)
|
|
|
|
{
|
|
|
|
synchFlags |= plSynchedObject::kForceFullSend; // TEMP
|
|
|
|
synchFlags |= plSynchedObject::kBCastToClients;
|
|
|
|
|
|
|
|
if(GetNumTargets() > 0)
|
|
|
|
{
|
|
|
|
plSceneObject *sObj = GetTarget(0);
|
|
|
|
if(sObj)
|
|
|
|
sObj->DirtySynchState(kSDLAvatarPhysical, synchFlags);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::IFinalize()
|
|
|
|
{
|
|
|
|
plArmatureModBase::IFinalize();
|
|
|
|
|
|
|
|
if (fWaitFlags & kNeedAudio)
|
|
|
|
{
|
|
|
|
plSetListenerMsg *msg = new plSetListenerMsg( plSetListenerMsg::kVelocity, GetTarget(0)->GetKey(), true );
|
|
|
|
msg->Send();
|
|
|
|
fWaitFlags &= ~kNeedAudio;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fWaitFlags & kNeedCamera)
|
|
|
|
{
|
|
|
|
plCameraMsg* pMsg = new plCameraMsg;
|
|
|
|
pMsg->SetCmd(plCameraMsg::kCreateNewDefaultCam);
|
|
|
|
pMsg->SetCmd(plCameraMsg::kSetSubject);
|
|
|
|
pMsg->SetSubject(GetTarget(0));
|
|
|
|
pMsg->SetBCastFlag( plMessage::kBCastByExactType );
|
|
|
|
pMsg->Send();
|
|
|
|
fWaitFlags &= ~kNeedCamera;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fWaitFlags & kNeedSpawn)
|
|
|
|
{
|
|
|
|
Spawn(hsTimer::GetSysSeconds());
|
|
|
|
fWaitFlags &= ~kNeedSpawn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::ICustomizeApplicator()
|
|
|
|
{
|
|
|
|
plArmatureModBase::ICustomizeApplicator();
|
|
|
|
|
|
|
|
const plAGModifier *agMod = GetChannelMod("Bone_Root", true);
|
|
|
|
if (agMod)
|
|
|
|
{
|
|
|
|
// are there any applicators that manipulate the transform?
|
|
|
|
plAGApplicator *app = agMod->GetApplicator(kAGPinTransform);
|
|
|
|
if(app)
|
|
|
|
{
|
|
|
|
plMatrixDelayedCorrectionApplicator *corApp = plMatrixDelayedCorrectionApplicator::ConvertNoRef(app);
|
|
|
|
if (corApp)
|
|
|
|
{
|
|
|
|
fBoneRootAnimator = corApp;
|
|
|
|
fWaitFlags &= ~kNeedApplicator;
|
|
|
|
return; // already there
|
|
|
|
}
|
|
|
|
}
|
|
|
|
plAGModifier *volAGMod = const_cast<plAGModifier *>(agMod);
|
|
|
|
fBoneRootAnimator = new plMatrixDelayedCorrectionApplicator();
|
|
|
|
volAGMod->SetApplicator(fBoneRootAnimator);
|
|
|
|
fWaitFlags &= ~kNeedApplicator;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const plSceneObject *plArmatureMod::GetClothingSO(uint8_t lod) const
|
|
|
|
{
|
|
|
|
if (fClothToSOMap.GetCount() <= lod)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
return fClothToSOMap[lod];
|
|
|
|
}
|
|
|
|
|
|
|
|
#define kSynchInterval 1 // synch once per second
|
|
|
|
|
|
|
|
void plArmatureMod::NetworkSynch(double timeNow, int force)
|
|
|
|
{
|
|
|
|
if (force || ((timeNow - fLastSynch) > kSynchInterval))
|
|
|
|
{
|
|
|
|
// make sure state change gets sent out over the network
|
|
|
|
// avatar state should use relevance region filtering
|
|
|
|
uint32_t flags = kBCastToClients | kUseRelevanceRegions;
|
|
|
|
if (force)
|
|
|
|
flags |= kForceFullSend;
|
|
|
|
DirtyPhysicalSynchState(flags);
|
|
|
|
fLastSynch = timeNow;
|
|
|
|
fPendingSynch = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fPendingSynch = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::IsLocalAvatar()
|
|
|
|
{
|
|
|
|
return plAvatarMgr::GetInstance()->GetLocalAvatar() == this;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::IsLocalAI()
|
|
|
|
{
|
|
|
|
// Formerly a lot of silly cached rigamaroll... Now, we'll just rely
|
|
|
|
// on the fact that one player is the game master. HACK TURD if net groups
|
|
|
|
// are ever brought back.
|
|
|
|
return plNetClientApp::GetInstance()->IsLocallyOwned(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SynchIfLocal(double timeNow, int force)
|
|
|
|
{
|
|
|
|
if (IsLocalAvatar() || IsLocalAI())
|
|
|
|
{
|
|
|
|
NetworkSynch(timeNow, force);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plLayerLinkAnimation *plArmatureMod::IFindLayerLinkAnim()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
hsGMaterial *mat = fClothingOutfit->fMaterial;
|
|
|
|
if (!mat)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
for (i = 0; i < mat->GetNumLayers(); i++)
|
|
|
|
{
|
|
|
|
plLayerInterface *li = mat->GetLayer(i);
|
|
|
|
while (li != nil)
|
|
|
|
{
|
|
|
|
plLayerLinkAnimation *anim = plLayerLinkAnimation::ConvertNoRef(li);
|
|
|
|
if (anim)
|
|
|
|
return anim;
|
|
|
|
|
|
|
|
li = li->GetUnderLay();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::ValidatePhysics()
|
|
|
|
{
|
|
|
|
if (!fTarget)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!fController)
|
|
|
|
{
|
|
|
|
// The kinematic actor is made taller if the avatar is human (male or female)
|
|
|
|
fController = plPhysicalControllerCore::Create(GetTarget(0)->GetKey(), fPhysHeight,
|
|
|
|
fPhysWidth, (fBodyType == kBoneBaseMale || fBodyType == kBoneBaseFemale));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fController)
|
|
|
|
{
|
|
|
|
if (GetTarget(0)->GetKey() == plNetClientApp::GetInstance()->GetLocalPlayerKey())
|
|
|
|
{
|
|
|
|
// local avatars get added to a special LOS db just for them.
|
|
|
|
fController->SetLOSDB(plSimDefs::kLOSDBLocalAvatar);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// non-local avatars are in the same LOS db as clickables
|
|
|
|
fController->SetLOSDB(plSimDefs::kLOSDBUIItems);
|
|
|
|
}
|
|
|
|
|
|
|
|
hsClearBits(fWaitFlags, kNeedPhysics);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::ValidateMesh()
|
|
|
|
{
|
|
|
|
if (fWaitFlags & kNeedMesh)
|
|
|
|
{
|
|
|
|
fWaitFlags &= ~kNeedMesh;
|
|
|
|
int n = fMeshKeys.size();
|
|
|
|
|
|
|
|
for(int i = 0; i < n; i++)
|
|
|
|
{
|
|
|
|
plKey meshKey = fMeshKeys[i];
|
|
|
|
plSceneObject * meshObj = (plSceneObject *)meshKey->GetObjectPtr();
|
|
|
|
|
|
|
|
if( ! meshObj)
|
|
|
|
{
|
|
|
|
fWaitFlags |= kNeedMesh;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
bool visible = (i == fCurLOD) ? true : false;
|
|
|
|
|
|
|
|
EnableDrawingTree(meshObj, visible);
|
|
|
|
|
|
|
|
// If we haven't created the mapping yet...
|
|
|
|
if (fClothToSOMap.GetCount() <= i || fClothToSOMap[i] == nil)
|
|
|
|
{
|
|
|
|
plGenRefMsg *refMsg = new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, i, 0);
|
|
|
|
hsgResMgr::ResMgr()->SendRef(meshObj->GetKey(), refMsg, plRefFlags::kPassiveRef);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!GetTarget(0)->GetKeyName().Compare("Yeesha"))
|
|
|
|
ISetTransparentDrawOrder(true);
|
|
|
|
else
|
|
|
|
ISetTransparentDrawOrder(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return !(fWaitFlags & kNeedMesh);
|
|
|
|
}
|
|
|
|
|
|
|
|
plArmatureBrain * plArmatureMod::GetNextBrain(plArmatureBrain *brain)
|
|
|
|
{
|
|
|
|
plArmatureBrain * result = nil;
|
|
|
|
bool passedTarget = false;
|
|
|
|
|
|
|
|
int count = fBrains.size();
|
|
|
|
for(int i = count - 1; i >= 0; i--)
|
|
|
|
{
|
|
|
|
plArmatureBrain *curBrain = fBrains.at(i);
|
|
|
|
if(passedTarget)
|
|
|
|
result = curBrain;
|
|
|
|
else {
|
|
|
|
if(curBrain == brain)
|
|
|
|
passedTarget = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int plArmatureMod::GetBrainCount()
|
|
|
|
{
|
|
|
|
return fBrains.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
plArmatureBrain * plArmatureMod::FindBrainByClass(uint32_t classID) const
|
|
|
|
{
|
|
|
|
int n = fBrains.size();
|
|
|
|
|
|
|
|
for(int i = 0; i < n; i++)
|
|
|
|
{
|
|
|
|
plArmatureBrain *brain = fBrains.at(i);
|
|
|
|
|
|
|
|
if(brain->ClassIndex() == classID)
|
|
|
|
{
|
|
|
|
return brain;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::TurnToPoint(hsPoint3 &point)
|
|
|
|
{
|
|
|
|
plAvBrainHuman *brain = plAvBrainHuman::ConvertNoRef(FindBrainByClass(plAvBrainHuman::Index()));
|
|
|
|
if (brain)
|
|
|
|
brain->TurnToPoint(point);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SuspendInput()
|
|
|
|
{
|
|
|
|
if (fSuspendInputCount == 0 && plNetClientApp::GetInstance()->GetLocalPlayer() == GetTarget(0))
|
|
|
|
{
|
|
|
|
plAvatarInputInterface::GetInstance()->SuspendMouseMovement();
|
|
|
|
PreserveInputState();
|
|
|
|
ClearInputFlags(false, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
fSuspendInputCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::ResumeInput()
|
|
|
|
{
|
|
|
|
if (fSuspendInputCount > 0) // decrementing an unsigned variable set to 0 is BAD
|
|
|
|
fSuspendInputCount--;
|
|
|
|
if(fSuspendInputCount == 0)
|
|
|
|
{
|
|
|
|
RestoreInputState();
|
|
|
|
IProcessQueuedInput();
|
|
|
|
if (plNetClientApp::GetInstance()->GetLocalPlayer() == GetTarget(0))
|
|
|
|
plAvatarInputInterface::GetInstance()->EnableMouseMovement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::IProcessQueuedInput()
|
|
|
|
{
|
|
|
|
CtrlMessageVec::iterator i = fQueuedCtrlMessages.begin();
|
|
|
|
CtrlMessageVec::iterator end = fQueuedCtrlMessages.end();
|
|
|
|
while(i != end)
|
|
|
|
{
|
|
|
|
plControlEventMsg *ctrlMsg = *i;
|
|
|
|
IHandleControlMsg(ctrlMsg);
|
|
|
|
ctrlMsg->UnRef();
|
|
|
|
(*i) = nil;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
fQueuedCtrlMessages.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::PreserveInputState()
|
|
|
|
{
|
|
|
|
fMoveFlagsBackup = fMoveFlags;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::RestoreInputState()
|
|
|
|
{
|
|
|
|
fMoveFlags = fMoveFlagsBackup;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::GetInputFlag (int which) const
|
|
|
|
{
|
|
|
|
return fMoveFlags.IsBitSet(which);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SetInputFlag(int which, bool status)
|
|
|
|
{
|
|
|
|
if(status)
|
|
|
|
{
|
|
|
|
fMoveFlags.SetBit(which);
|
|
|
|
} else {
|
|
|
|
fMoveFlags.ClearBit(which);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::HasMovementFlag() const
|
|
|
|
{
|
|
|
|
return (fMoveFlags.IsBitSet(B_CONTROL_MOVE_FORWARD) ||
|
|
|
|
fMoveFlags.IsBitSet(B_CONTROL_MOVE_BACKWARD) ||
|
|
|
|
fMoveFlags.IsBitSet(B_CONTROL_ROTATE_LEFT) ||
|
|
|
|
fMoveFlags.IsBitSet(B_CONTROL_ROTATE_RIGHT) ||
|
|
|
|
fMoveFlags.IsBitSet(A_CONTROL_TURN) ||
|
|
|
|
fMoveFlags.IsBitSet(B_CONTROL_STRAFE_LEFT) ||
|
|
|
|
fMoveFlags.IsBitSet(B_CONTROL_STRAFE_RIGHT));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::ForwardKeyDown() const
|
|
|
|
{
|
|
|
|
return GetInputFlag(B_CONTROL_MOVE_FORWARD) ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::BackwardKeyDown() const
|
|
|
|
{
|
|
|
|
return GetInputFlag(B_CONTROL_MOVE_BACKWARD) ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::StrafeLeftKeyDown() const
|
|
|
|
{
|
|
|
|
return GetInputFlag(B_CONTROL_STRAFE_LEFT) ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::StrafeRightKeyDown() const
|
|
|
|
{
|
|
|
|
return GetInputFlag(B_CONTROL_STRAFE_RIGHT) ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::FastKeyDown() const
|
|
|
|
{
|
|
|
|
return ((GetInputFlag(B_CONTROL_MODIFIER_FAST) && !GetInputFlag(B_CONTROL_ALWAYS_RUN)) ||
|
|
|
|
(!GetInputFlag(B_CONTROL_MODIFIER_FAST) && GetInputFlag(B_CONTROL_ALWAYS_RUN)));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::StrafeKeyDown() const
|
|
|
|
{
|
|
|
|
return GetInputFlag(B_CONTROL_MODIFIER_STRAFE) ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::TurnLeftKeyDown() const
|
|
|
|
{
|
|
|
|
return GetInputFlag(B_CONTROL_ROTATE_LEFT) ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::TurnRightKeyDown() const
|
|
|
|
{
|
|
|
|
return GetInputFlag(B_CONTROL_ROTATE_RIGHT) ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::JumpKeyDown() const
|
|
|
|
{
|
|
|
|
return GetInputFlag(B_CONTROL_JUMP) ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::ExitModeKeyDown() const
|
|
|
|
{
|
|
|
|
return GetInputFlag(B_CONTROL_EXIT_MODE) ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SetForwardKeyDown()
|
|
|
|
{
|
|
|
|
SetInputFlag(B_CONTROL_MOVE_FORWARD, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SetBackwardKeyDown()
|
|
|
|
{
|
|
|
|
SetInputFlag(B_CONTROL_MOVE_BACKWARD, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SetStrafeLeftKeyDown(bool status)
|
|
|
|
{
|
|
|
|
SetInputFlag(B_CONTROL_STRAFE_LEFT, status);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SetStrafeRightKeyDown(bool status)
|
|
|
|
{
|
|
|
|
SetInputFlag(B_CONTROL_STRAFE_RIGHT, status);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SetFastKeyDown()
|
|
|
|
{
|
|
|
|
SetInputFlag(B_CONTROL_MODIFIER_FAST, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SetTurnLeftKeyDown(bool status)
|
|
|
|
{
|
|
|
|
SetInputFlag(B_CONTROL_ROTATE_LEFT, status);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SetTurnRightKeyDown(bool status)
|
|
|
|
{
|
|
|
|
SetInputFlag(B_CONTROL_ROTATE_RIGHT, status);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SetJumpKeyDown()
|
|
|
|
{
|
|
|
|
SetInputFlag(B_CONTROL_JUMP, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
float plArmatureMod::GetTurnStrength() const
|
|
|
|
{
|
|
|
|
return GetKeyTurnStrength() + GetAnalogTurnStrength();
|
|
|
|
}
|
|
|
|
|
|
|
|
float plArmatureMod::GetKeyTurnStrength() const
|
|
|
|
{
|
|
|
|
if (StrafeKeyDown())
|
|
|
|
return 0.f;
|
|
|
|
|
|
|
|
return (TurnLeftKeyDown() ? 1.f : 0.f) + (TurnRightKeyDown() ? -1.f: 0.f);
|
|
|
|
}
|
|
|
|
|
|
|
|
float plArmatureMod::GetAnalogTurnStrength() const
|
|
|
|
{
|
|
|
|
if (StrafeKeyDown())
|
|
|
|
return 0.f;
|
|
|
|
|
|
|
|
float elapsed = hsTimer::GetDelSysSeconds();
|
|
|
|
if (elapsed > 0)
|
|
|
|
return fMouseFrameTurnStrength / elapsed;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SetReverseFBOnIdle(bool val)
|
|
|
|
{
|
|
|
|
fReverseFBOnIdle = val;
|
|
|
|
if (val)
|
|
|
|
SetInputFlag(B_CONTROL_LADDER_INVERTED, !(ForwardKeyDown() || BackwardKeyDown()));
|
|
|
|
else
|
|
|
|
SetInputFlag(B_CONTROL_LADDER_INVERTED, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::IsFBReversed()
|
|
|
|
{
|
|
|
|
return GetInputFlag(B_CONTROL_LADDER_INVERTED);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::ClearInputFlags(bool saveAlwaysRun, bool clearBackup)
|
|
|
|
{
|
|
|
|
bool alwaysRun = (fMoveFlags.IsBitSet(B_CONTROL_ALWAYS_RUN) != 0);
|
|
|
|
bool fast = (fMoveFlags.IsBitSet(B_CONTROL_MODIFIER_FAST) != 0);
|
|
|
|
fMoveFlags.Clear();
|
|
|
|
|
|
|
|
if (saveAlwaysRun)
|
|
|
|
{
|
|
|
|
if (alwaysRun)
|
|
|
|
fMoveFlags.SetBit(B_CONTROL_ALWAYS_RUN);
|
|
|
|
if (fast)
|
|
|
|
fMoveFlags.SetBit(B_CONTROL_MODIFIER_FAST);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (clearBackup)
|
|
|
|
{
|
|
|
|
alwaysRun = (fMoveFlagsBackup.IsBitSet(B_CONTROL_ALWAYS_RUN) != 0);
|
|
|
|
fast = (fMoveFlagsBackup.IsBitSet(B_CONTROL_MODIFIER_FAST) != 0);
|
|
|
|
fMoveFlagsBackup.Clear();
|
|
|
|
|
|
|
|
if (saveAlwaysRun)
|
|
|
|
{
|
|
|
|
if (alwaysRun)
|
|
|
|
fMoveFlagsBackup.SetBit(B_CONTROL_ALWAYS_RUN);
|
|
|
|
if (fast)
|
|
|
|
fMoveFlagsBackup.SetBit(B_CONTROL_MODIFIER_FAST);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plAGModifier * plArmatureMod::GetRootAGMod()
|
|
|
|
{
|
|
|
|
if(fRootAGMod)
|
|
|
|
return fRootAGMod;
|
|
|
|
|
|
|
|
if(fTarget)
|
|
|
|
{
|
|
|
|
const plAGModifier * shit = plAGModifier::ConvertNoRef(FindModifierByClass(fTarget, plAGModifier::Index()));
|
|
|
|
fRootAGMod = const_cast<plAGModifier *>(shit);
|
|
|
|
}
|
|
|
|
|
|
|
|
return fRootAGMod;
|
|
|
|
}
|
|
|
|
|
|
|
|
int plArmatureMod::GetCurrentGenericType()
|
|
|
|
{
|
|
|
|
plAvBrainGeneric *brain = plAvBrainGeneric::ConvertNoRef(GetCurrentBrain());
|
|
|
|
|
|
|
|
if (!brain)
|
|
|
|
return plAvBrainGeneric::kNonGeneric;
|
|
|
|
else
|
|
|
|
return brain->GetType();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::FindMatchingGenericBrain(const char *names[], int count)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < GetBrainCount(); i++)
|
|
|
|
{
|
|
|
|
plAvBrainGeneric *brain = plAvBrainGeneric::ConvertNoRef(GetBrain(i));
|
|
|
|
if (brain && brain->MatchAnimNames(names, count))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
plString plArmatureMod::MakeAnimationName(const plString& baseName) const
|
|
|
|
{
|
|
|
|
return fAnimationPrefix + baseName;
|
|
|
|
}
|
|
|
|
|
|
|
|
plString plArmatureMod::GetRootName()
|
|
|
|
{
|
|
|
|
return fRootName;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SetRootName(const plString &name)
|
|
|
|
{
|
|
|
|
fRootName = name;
|
|
|
|
}
|
|
|
|
|
|
|
|
plAGAnim *plArmatureMod::FindCustomAnim(const plString& baseName) const
|
|
|
|
{
|
|
|
|
plString customName = MakeAnimationName(baseName);
|
|
|
|
plAGAnim *result = plAGAnim::FindAnim(customName);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::ISetupMarkerCallbacks(plATCAnim *anim, plAnimTimeConvert *atc)
|
|
|
|
{
|
|
|
|
std::vector<plString> markers;
|
|
|
|
anim->CopyMarkerNames(markers);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < markers.size(); i++)
|
|
|
|
{
|
|
|
|
|
|
|
|
float time = -1;
|
|
|
|
bool isLeft = false;
|
|
|
|
if (markers[i].Find("SndLeftFootDown") == 0)
|
|
|
|
{
|
|
|
|
isLeft = true;
|
|
|
|
time = anim->GetMarker(markers[i]);
|
|
|
|
}
|
|
|
|
if (markers[i].Find("SndRightFootDown") == 0)
|
|
|
|
time = anim->GetMarker(markers[i]);
|
|
|
|
|
|
|
|
if (time >= 0)
|
|
|
|
{
|
|
|
|
plEventCallbackInterceptMsg *iMsg;
|
|
|
|
|
|
|
|
plArmatureEffectMsg *msg = new plArmatureEffectMsg(fEffects->GetKey(), kTime);
|
|
|
|
msg->fEventTime = time;
|
|
|
|
msg->fTriggerIdx = AnimNameToIndex(anim->GetName());
|
|
|
|
|
|
|
|
iMsg = new plEventCallbackInterceptMsg();
|
|
|
|
iMsg->AddReceiver(fEffects->GetKey());
|
|
|
|
iMsg->fEventTime = time;
|
|
|
|
iMsg->fEvent = kTime;
|
|
|
|
iMsg->SetMessageRef(msg);
|
|
|
|
atc->AddCallback(iMsg);
|
|
|
|
hsRefCnt_SafeUnRef(msg);
|
|
|
|
hsRefCnt_SafeUnRef(iMsg);
|
|
|
|
|
|
|
|
plAvatarFootMsg* foot = new plAvatarFootMsg(GetKey(), this, isLeft);
|
|
|
|
foot->fEventTime = time;
|
|
|
|
|
|
|
|
iMsg = new plEventCallbackInterceptMsg();
|
|
|
|
iMsg->AddReceiver(fEffects->GetKey());
|
|
|
|
iMsg->fEventTime = time;
|
|
|
|
iMsg->fEvent = kTime;
|
|
|
|
iMsg->SetMessageRef(foot);
|
|
|
|
atc->AddCallback(iMsg);
|
|
|
|
hsRefCnt_SafeUnRef(foot);
|
|
|
|
hsRefCnt_SafeUnRef(iMsg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plString plArmatureMod::GetAnimRootName(const plString &name)
|
|
|
|
{
|
|
|
|
return name.Substr(fAnimationPrefix.GetSize());
|
|
|
|
}
|
|
|
|
|
|
|
|
int8_t plArmatureMod::AnimNameToIndex(const plString &name)
|
|
|
|
{
|
|
|
|
plString rootName = GetAnimRootName(name);
|
|
|
|
int8_t result = -1;
|
|
|
|
|
|
|
|
if (!rootName.Compare("Walk") || !rootName.Compare("WalkBack") ||
|
|
|
|
!rootName.Compare("LadderDown") || !rootName.Compare("LadderDownOn") ||
|
|
|
|
!rootName.Compare("LadderDownOff") || !rootName.Compare("LadderUp") ||
|
|
|
|
!rootName.Compare("LadderUpOn") || !rootName.Compare("LadderUpOff") ||
|
|
|
|
!rootName.Compare("SwimSlow") || !rootName.Compare("SwimBackward") ||
|
|
|
|
!rootName.Compare("BallPushWalk"))
|
|
|
|
result = kWalk;
|
|
|
|
else if (!rootName.Compare("Run") || !rootName.Compare("SwimFast"))
|
|
|
|
result = kRun;
|
|
|
|
else if (!rootName.Compare("TurnLeft") || !rootName.Compare("TurnRight") ||
|
|
|
|
!rootName.Compare("StepLeft") || !rootName.Compare("StepRight") ||
|
|
|
|
!rootName.Compare("SideSwimLeft") || !rootName.Compare("SideSwimRight") ||
|
|
|
|
!rootName.Compare("TreadWaterTurnLeft") || !rootName.Compare("TreadWaterTurnRight"))
|
|
|
|
result = kTurn;
|
|
|
|
else if (!rootName.Compare("GroundImpact") || !rootName.Compare("RunningImpact"))
|
|
|
|
result = kImpact;
|
|
|
|
else if (rootName.Find("Run") >= 0) // Critters
|
|
|
|
result = kRun;
|
|
|
|
else if (rootName.Find("Idle") >= 0) // Critters
|
|
|
|
result = kWalk;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::IsInStealthMode() const
|
|
|
|
{
|
|
|
|
return (fStealthMode != plAvatarStealthModeMsg::kStealthVisible);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::IsOpaque()
|
|
|
|
{
|
|
|
|
return fOpaque;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::IsMidLink()
|
|
|
|
{
|
|
|
|
return fMidLink;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::IsLinkedIn()
|
|
|
|
{
|
|
|
|
return fIsLinkedIn;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::ConsumeJump()
|
|
|
|
{
|
|
|
|
if (!GetInputFlag(B_CONTROL_CONSUMABLE_JUMP))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
SetInputFlag(B_CONTROL_CONSUMABLE_JUMP, false);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::ISetTransparentDrawOrder(bool val)
|
|
|
|
{
|
|
|
|
if (fOpaque != val)
|
|
|
|
return;
|
|
|
|
|
|
|
|
fOpaque = !val;
|
|
|
|
|
|
|
|
plDrawableSpans *spans = plDrawableSpans::ConvertNoRef(FindDrawable());
|
|
|
|
if (spans)
|
|
|
|
{
|
|
|
|
spans->SetNativeProperty(plDrawable::kPropPartialSort, true);
|
|
|
|
spans->SetNativeProperty(plDrawable::kPropSortAsOne, true);
|
|
|
|
if (val)
|
|
|
|
{
|
|
|
|
spans->SetNativeProperty(plDrawable::kPropSortSpans, true);
|
|
|
|
spans->SetRenderLevel(plRenderLevel(plRenderLevel::kBlendRendMajorLevel, plRenderLevel::kDefRendMinorLevel));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
spans->SetNativeProperty(plDrawable::kPropSortSpans, false);
|
|
|
|
spans->SetRenderLevel(plRenderLevel(0, plRenderLevel::kAvatarRendMinorLevel));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plArmatureMod::IsKILowestLevel()
|
|
|
|
{
|
|
|
|
if ( GetKILevel() == pfKIMsg::kNanoKI )
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int plArmatureMod::GetKILevel()
|
|
|
|
{
|
|
|
|
return VaultGetKILevel();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::SetLinkInAnim(const plString &animName)
|
|
|
|
{
|
|
|
|
if (!animName.IsNull())
|
|
|
|
{
|
|
|
|
plAGAnim *anim = FindCustomAnim(animName);
|
|
|
|
fLinkInAnimKey = (anim ? anim->GetKey() : nil);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fLinkInAnimKey = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
plKey plArmatureMod::GetLinkInAnimKey() const
|
|
|
|
{
|
|
|
|
return fLinkInAnimKey;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////
|
|
|
|
//
|
|
|
|
// PLARMATURELODMOD
|
|
|
|
//
|
|
|
|
//////////////////////
|
|
|
|
|
|
|
|
plArmatureLODMod::plArmatureLODMod()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// CTOR (physical, name)
|
|
|
|
plArmatureLODMod::plArmatureLODMod(const plString& root_name)
|
|
|
|
: plArmatureMod()
|
|
|
|
{
|
|
|
|
fRootName = root_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
plArmatureLODMod::~plArmatureLODMod()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureLODMod::Read(hsStream *stream, hsResMgr *mgr)
|
|
|
|
{
|
|
|
|
plArmatureMod::Read(stream, mgr);
|
|
|
|
|
|
|
|
fMeshKeys.clear();
|
|
|
|
int meshKeyCount = stream->ReadLE32();
|
|
|
|
for(int i = 0; i < meshKeyCount; i++)
|
|
|
|
{
|
|
|
|
plKey meshKey = mgr->ReadKey(stream);
|
|
|
|
fMeshKeys.push_back(meshKey);
|
|
|
|
|
|
|
|
plKeyVector *vec = new plKeyVector;
|
|
|
|
int boneCount = stream->ReadLE32();
|
|
|
|
for(int j = 0; j < boneCount; j++)
|
|
|
|
vec->push_back(mgr->ReadKey(stream));
|
|
|
|
fUnusedBones.push_back(vec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WRITE
|
|
|
|
void plArmatureLODMod::Write(hsStream *stream, hsResMgr *mgr)
|
|
|
|
{
|
|
|
|
plArmatureMod::Write(stream, mgr);
|
|
|
|
|
|
|
|
int meshKeyCount = fMeshKeys.size();
|
|
|
|
stream->WriteLE32(meshKeyCount);
|
|
|
|
|
|
|
|
for(int i = 0; i < meshKeyCount; i++)
|
|
|
|
{
|
|
|
|
plKey meshKey = fMeshKeys[i];
|
|
|
|
mgr->WriteKey(stream, meshKey);
|
|
|
|
|
|
|
|
// Should be a list per mesh key
|
|
|
|
stream->WriteLE32(fUnusedBones[i]->size());
|
|
|
|
for(int j = 0; j < fUnusedBones[i]->size(); j++)
|
|
|
|
mgr->WriteKey(stream, (*fUnusedBones[i])[j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// DEBUG SUPPORT
|
|
|
|
|
|
|
|
int plArmatureMod::RefreshDebugDisplay()
|
|
|
|
{
|
|
|
|
plDebugText &debugTxt = plDebugText::Instance();
|
|
|
|
int lineHeight = debugTxt.GetFontSize() + 4;
|
|
|
|
uint32_t scrnWidth, scrnHeight;
|
|
|
|
|
|
|
|
debugTxt.GetScreenSize( &scrnWidth, &scrnHeight );
|
|
|
|
int y = 10;
|
|
|
|
int x = 10;
|
|
|
|
|
|
|
|
DumpToDebugDisplay(x, y, lineHeight, debugTxt);
|
|
|
|
return y;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::DumpToDebugDisplay(int &x, int &y, int lineHeight, plDebugText &debugTxt)
|
|
|
|
{
|
|
|
|
debugTxt.DrawString(x, y, plFormat("Armature <{}>:", fRootName), 255, 128, 128);
|
|
|
|
y += lineHeight;
|
|
|
|
|
|
|
|
plSceneObject * SO = GetTarget(0);
|
|
|
|
if(SO)
|
|
|
|
{
|
|
|
|
// global location
|
|
|
|
hsMatrix44 l2w = SO->GetLocalToWorld();
|
|
|
|
hsPoint3 worldPos = l2w.GetTranslate();
|
|
|
|
|
|
|
|
debugTxt.DrawString(x, y, plFormat("position(world): {.2f}, {.2f}, {.2f} Opaque: {>3}",
|
|
|
|
worldPos.fX, worldPos.fY, worldPos.fZ, IsOpaque() ? "yes" : "no"));
|
|
|
|
y += lineHeight;
|
|
|
|
|
|
|
|
const char* frozen = "n.a.";
|
|
|
|
if (fController)
|
|
|
|
frozen = fController->IsEnabled() ? "no" : "yes";
|
|
|
|
|
|
|
|
// are we in a subworld?
|
|
|
|
plKey world = nil;
|
|
|
|
if (fController)
|
|
|
|
world = fController->GetSubworld();
|
|
|
|
debugTxt.DrawString(x, y, plFormat("In world: {} Frozen: {}",
|
|
|
|
world ? world->GetName() : "nil", frozen));
|
|
|
|
y+= lineHeight;
|
|
|
|
|
|
|
|
plString details;
|
|
|
|
if (fController)
|
|
|
|
{
|
|
|
|
hsPoint3 physPos;
|
|
|
|
GetPositionAndRotationSim(&physPos, nil);
|
|
|
|
const hsVector3& vel = fController->GetLinearVelocity();
|
|
|
|
details = plFormat("position(physical): <{.2f}, {.2f}, {.2f}> velocity: <{5.2f}, {5.2f}, {5.2f}>",
|
|
|
|
physPos.fX, physPos.fY, physPos.fZ, vel.fX, vel.fY, vel.fZ);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
details = "position(physical): no controller";
|
|
|
|
}
|
|
|
|
debugTxt.DrawString(x, y, details);
|
|
|
|
y += lineHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
DebugDumpMoveKeys(x, y, lineHeight, debugTxt);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for(i = 0; i < fBrains.size(); i++)
|
|
|
|
{
|
|
|
|
plArmatureBrain *brain = fBrains[i];
|
|
|
|
brain->DumpToDebugDisplay(x, y, lineHeight, debugTxt);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fClothingOutfit)
|
|
|
|
{
|
|
|
|
y += lineHeight;
|
|
|
|
|
|
|
|
debugTxt.DrawString(x, y, "ItemsWorn:");
|
|
|
|
y += lineHeight;
|
|
|
|
plStringStream outfit;
|
|
|
|
int itemCount = 0;
|
|
|
|
for (i = 0; i < fClothingOutfit->fItems.GetCount(); i++)
|
|
|
|
{
|
|
|
|
if (itemCount == 0)
|
|
|
|
outfit << " ";
|
|
|
|
|
|
|
|
outfit << fClothingOutfit->fItems[i]->fName;
|
|
|
|
itemCount++;
|
|
|
|
|
|
|
|
if (itemCount == 4)
|
|
|
|
{
|
|
|
|
debugTxt.DrawString(x, y, outfit.GetString());
|
|
|
|
itemCount = 0;
|
|
|
|
outfit.Truncate();
|
|
|
|
y += lineHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (itemCount > 0)
|
|
|
|
outfit << ", ";
|
|
|
|
}
|
|
|
|
if (itemCount > 0)
|
|
|
|
{
|
|
|
|
debugTxt.DrawString(x, y, outfit.GetString());
|
|
|
|
y += lineHeight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (plRelevanceMgr::Instance()->GetEnabled())
|
|
|
|
{
|
|
|
|
y += lineHeight;
|
|
|
|
|
|
|
|
debugTxt.DrawString(x, y, "Relevance Regions:");
|
|
|
|
y += lineHeight;
|
|
|
|
debugTxt.DrawString(x, y, plFormat(" In: {}",
|
|
|
|
plRelevanceMgr::Instance()->GetRegionNames(fRegionsImIn)));
|
|
|
|
y += lineHeight;
|
|
|
|
debugTxt.DrawString(x, y, plFormat(" Care about: %s",
|
|
|
|
plRelevanceMgr::Instance()->GetRegionNames(fRegionsICareAbout)));
|
|
|
|
y += lineHeight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class plAvBoneMap::BoneMapImp
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
typedef std::map<uint32_t, const plSceneObject *> id2SceneObjectMap;
|
|
|
|
id2SceneObjectMap fMap;
|
|
|
|
};
|
|
|
|
|
|
|
|
plAvBoneMap::plAvBoneMap()
|
|
|
|
{
|
|
|
|
fImp = new BoneMapImp;
|
|
|
|
}
|
|
|
|
|
|
|
|
plAvBoneMap::~plAvBoneMap()
|
|
|
|
{
|
|
|
|
delete fImp;
|
|
|
|
}
|
|
|
|
|
|
|
|
const plSceneObject * plAvBoneMap::FindBone(uint32_t boneID)
|
|
|
|
{
|
|
|
|
BoneMapImp::id2SceneObjectMap::iterator i = fImp->fMap.find(boneID);
|
|
|
|
const plSceneObject *result = nil;
|
|
|
|
|
|
|
|
if(i != fImp->fMap.end())
|
|
|
|
{
|
|
|
|
result = (*i).second;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plAvBoneMap::AddBoneMapping(uint32_t boneID, const plSceneObject *SO)
|
|
|
|
{
|
|
|
|
(fImp->fMap)[boneID] = SO;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plArmatureMod::DebugDumpMoveKeys(int &x, int &y, int lineHeight, plDebugText &debugTxt)
|
|
|
|
{
|
|
|
|
debugTxt.DrawString(x, y, plFormat("Mouse Input Map: {}",
|
|
|
|
plAvatarInputInterface::GetInstance()->GetInputMapName()));
|
|
|
|
y += lineHeight;
|
|
|
|
|
|
|
|
debugTxt.DrawString(x, y, plFormat("Turn strength: {.2f} (key: {.2f}, analog: {.2f})",
|
|
|
|
GetTurnStrength(), GetKeyTurnStrength(), GetAnalogTurnStrength()));
|
|
|
|
y += lineHeight;
|
|
|
|
|
|
|
|
debugTxt.DrawString(x, y, GetMoveKeyString());
|
|
|
|
y += lineHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
plString plArmatureMod::GetMoveKeyString() const
|
|
|
|
{
|
|
|
|
plStringStream keys;
|
|
|
|
keys << "Move keys: ";
|
|
|
|
|
|
|
|
if(FastKeyDown())
|
|
|
|
keys << "FAST ";
|
|
|
|
if(StrafeKeyDown())
|
|
|
|
keys << "STRAFE ";
|
|
|
|
if(ForwardKeyDown())
|
|
|
|
keys << "FORWARD ";
|
|
|
|
if(BackwardKeyDown())
|
|
|
|
keys << "BACKWARD ";
|
|
|
|
if(TurnLeftKeyDown())
|
|
|
|
keys << "TURNLEFT ";
|
|
|
|
if(TurnRightKeyDown())
|
|
|
|
keys << "TURNRIGHT ";
|
|
|
|
if(StrafeLeftKeyDown())
|
|
|
|
keys << "STRAFELEFT ";
|
|
|
|
if(StrafeRightKeyDown())
|
|
|
|
keys << "STRAFERIGHT ";
|
|
|
|
if(JumpKeyDown())
|
|
|
|
keys << "JUMP ";
|
|
|
|
|
|
|
|
return keys.GetString();
|
|
|
|
}
|