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.

2841 lines
74 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/>.
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 "plAGModifier.h"
#include "plAvatarClothing.h"
#include "plClothingSDLModifier.h"
#include "plAvatarSDLModifier.h"
#include "plAGAnim.h"
#include "plArmatureEffects.h"
#include "plAvBrainHuman.h"
#include "plMatrixChannel.h"
#include "plAvatarTasks.h"
#include "plAvCallbackAction.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 "../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();
}
}
hsBool plArmatureModBase::MsgReceive(plMessage* msg)
{
hsBool 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);
}
hsBool plArmatureModBase::IEval(double time, hsScalar elapsed, UInt32 dirty)
{
if (IsFinal())
{
if (fBrains.size())
{
plArmatureBrain *curBrain = fBrains.back();
if (curBrain)
{
hsBool 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->ReadSwap32();
for(i = 0; i < meshKeyCount; i++)
{
plKey meshKey = mgr->ReadKey(stream);
fMeshKeys.push_back(meshKey);
plKeyVector *vec = TRACKED_NEW plKeyVector;
int boneCount = stream->ReadSwap32();
for(int j = 0; j < boneCount; j++)
vec->push_back(mgr->ReadKey(stream));
fUnusedBones.push_back(vec);
}
int nBrains = stream->ReadSwap32();
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->WriteSwap32(meshKeyCount);
for (i = 0; i < meshKeyCount; i++)
{
plKey meshKey = fMeshKeys[i];
mgr->WriteKey(stream, meshKey);
// Should be a list per mesh key
stream->WriteSwap32(fUnusedBones[i]->size());
for(int j = 0; j < fUnusedBones[i]->size(); j++)
mgr->WriteKey(stream, (*fUnusedBones[i])[j]);
}
int nBrains = fBrains.size();
stream->WriteSwap32(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, hsBool status)
{
if (!object)
return;
plEnableMsg *msg = TRACKED_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;
}
hsBool plArmatureModBase::ValidatePhysics()
{
if (!fTarget)
return false;
if (fController)
{
EnablePhysics(true);
fWaitFlags &= ~kNeedPhysics;
}
return !(fWaitFlags & kNeedPhysics);
}
hsBool 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;
}
hsBool 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;
}
}
}
}
hsBool 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;
hsScalar distanceSquared = delta.MagnitudeSquared();
if (distanceSquared < fLODDistance * fLODDistance)
SetLOD(__max(0, fMinLOD));
else if (distanceSquared < fLODDistance * fLODDistance * 4.0)
SetLOD(__max(1, fMinLOD));
else
SetLOD(2);
}
}
// Should always be called from AdjustLOD
hsBool 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 plArmatureModBase::GetNumLOD() const
{
return fMeshKeys.size();
}
void plArmatureModBase::EnablePhysics(hsBool status, UInt16 reason /* = kDisableReasonUnknown */)
{
if (status)
fDisabledPhysics &= ~reason;
else
fDisabledPhysics |= reason;
hsBool 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(hsBool status)
{
if (fController)
fController->Kinematic(status);
}
void plArmatureModBase::EnableDrawing(hsBool status, UInt16 reason /* = kDisableReasonUnknown */)
{
hsBool oldStatus = !fDisabledDraw;
if (status)
fDisabledDraw &= ~reason;
else
fDisabledDraw |= reason;
hsBool 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 = TRACKED_NEW plMatrixDifferenceApp();
fRootAnimator = differ;
volAGMod->SetApplicator(differ);
differ->Enable(false);
fWaitFlags &= ~kNeedApplicator;
}
}
void plArmatureModBase::IEnableBones(int lod, hsBool 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"};
const hsScalar plArmatureMod::kAvatarInputSynchThreshold = 10.f;
hsScalar plArmatureMod::fMouseTurnSensitivity = 1.f;
hsBool 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;
fMidLink = false;
fAlreadyPanicLinking = false;
fReverseFBOnIdle = false;
fFollowerParticleSystemSO = nil;
fPendingSynch = false;
fLastInputSynch = 0;
fOpaque = true;
fPhysHeight = 0.f;
fPhysWidth = 0.f;
fUpdateMsg = nil;
fRootName = nil;
fDontPanicLink = false;
fBodyAgeName = "GlobalAvatars";
fBodyFootstepSoundPage = "Audio";
fAnimationPrefix = "Male";
fUserStr = "";
}
plArmatureMod::plArmatureMod() : plArmatureModBase()
{
IInitDefaults();
fWaitFlags |= kNeedAudio | kNeedCamera | kNeedSpawn;
}
plArmatureMod::~plArmatureMod()
{
delete fBoneMap;
delete [] fRootName;
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 char * name) const
{
plSceneObject *result = nil;
plAGModifier * mod = GetChannelMod(name);
if (mod)
result = mod->GetTarget(0);
return result;
}
const plSceneObject *plArmatureMod::FindBone(UInt32 id) const
{
if(fBoneMap)
return fBoneMap->FindBone(id);
else
return nil;
}
void plArmatureMod::AddBoneMapping(UInt32 id, const plSceneObject *bone)
{
if(!fBoneMap)
fBoneMap = TRACKED_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);
}
}
char *plArmatureMod::fSpawnPointOverride = nil;
void plArmatureMod::SetSpawnPointOverride( const char *overrideObjName )
{
delete [] fSpawnPointOverride;
if( overrideObjName == nil )
fSpawnPointOverride = nil;
else
{
fSpawnPointOverride = hsStrcpy( overrideObjName );
strlwr( fSpawnPointOverride );
}
}
int plArmatureMod::IFindSpawnOverride( void )
{
if( fSpawnPointOverride == nil || fSpawnPointOverride[ 0 ] == 0 )
return -1;
int i;
plAvatarMgr *mgr = plAvatarMgr::GetInstance();
for( i = 0; i < mgr->NumSpawnPoints(); i++ )
{
char str2[ 256 ];
strcpy(str2, mgr->GetSpawnPoint( i )->GetTarget(0)->GetKeyName());
strlwr(str2);
if (strstr(str2, fSpawnPointOverride) != nil)
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 = TRACKED_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 = TRACKED_NEW plWarpMsg(GetKey(), follower->GetKey(), plWarpMsg::kFlushTransform | plWarpMsg::kZeroVelocity,
GetTarget(0)->GetLocalToWorld());
warp->Send();
hsgResMgr::ResMgr()->AddViaNotify(follower->GetKey(), TRACKED_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 type, hsBool behaviorStart)
{
if (fNotifyKeys.GetCount() > 0)
{
plAvatarBehaviorNotifyMsg *msg = TRACKED_NEW plAvatarBehaviorNotifyMsg();
msg->SetSender(GetKey());
msg->AddReceivers(fNotifyKeys);
msg->fType = type;
msg->state = behaviorStart;
msg->Send();
}
}
void plArmatureMod::EnterAge(hsBool 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 = TRACKED_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 = TRACKED_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(hsBool 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 = TRACKED_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 = TRACKED_NEW plAvOneShotLinkTask;
char *animName = MakeAnimationName("FallingLinkOut");
task->SetAnimName(animName);
task->SetMarkerName("touch");
plAvTaskMsg *taskMsg = TRACKED_NEW plAvTaskMsg(GetKey(), GetKey(), task);
taskMsg->Send();
delete [] animName;
}
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 = TRACKED_NEW plAvOneShotLinkTask;
char *animName = MakeAnimationName("PersonalLink");
task->SetAnimName(animName);
task->SetMarkerName("touch");
delete [] animName;
plAvTaskMsg *taskMsg = TRACKED_NEW plAvTaskMsg(GetKey(), GetKey(), task);
taskMsg->SetBCastFlag(plMessage::kNetPropagate);
taskMsg->Send();
}
}
hsBool plArmatureMod::MsgReceive(plMessage* msg)
{
hsBool 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))
{
hsBool enable = enMsg->Cmd(plEnableMsg::kEnable);
hsBool 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 = TRACKED_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)
{
hsBool 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 = TRACKED_NEW plWarpMsg(nil, GetTarget(0)->GetKey(), 0, l2w);
warpM->Send();
fWaitFlags &= ~kNeedSpawn;
}
}
}
// copy the user string over
const char* userStr = avLoadMsg->GetUserStr();
if (userStr)
fUserStr = userStr;
else
fUserStr = "";
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())
{
SetFollowerParticleSystemSO(nil);
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)
{
IFireBehaviorNotify(plHBehavior::kBehaviorTypeLinkIn, false);
return true;
}
plAgeLoadedMsg *ageLoadMsg = plAgeLoadedMsg::ConvertNoRef(msg);
if (ageLoadMsg && ageLoadMsg->fLoaded)
{
// only the local player gets these
NetworkSynch(hsTimer::GetSysSeconds(), true);
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);
}
hsBool 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();
hsBool 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 curBit;
for (i = 0, curBit = 0x1; i < plAvatarInputStateMsg::fMapSize; i++, curBit <<= 1)
{
SetInputFlag(msg->fCodeMap[i], msg->fState & curBit);
}
}
void plArmatureMod::SynchInputState(UInt32 rcvID /* = kInvalidPlayerID */)
{
if (plAvatarMgr::GetInstance()->GetLocalAvatar() != this)
return;
plAvatarInputStateMsg *msg = TRACKED_NEW plAvatarInputStateMsg();
int i;
UInt32 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();
fLastInputSynch = hsTimer::GetSysSeconds();
}
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 = TRACKED_NEW plLinkToAgeMsg( &link );
pMsg->SetLinkInAnimName("PersonalBookEnter");
pMsg->AddReceiver(nc->GetKey());
pMsg->Send();
}
hsBool plArmatureMod::IEval(double time, hsScalar elapsed, UInt32 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...
{
hsBool 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 (localPlayer == this)
{
if (time - fLastInputSynch > kAvatarInputSynchThreshold)
SynchInputState();
}
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 = TRACKED_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());
// attach a clothingSDLModifier to handle clothing saveState
delete fClothingSDLMod;
fClothingSDLMod = TRACKED_NEW plClothingSDLModifier;
fClothingSDLMod->SetClothingOutfit(GetClothingOutfit()); // ok if clothingOutfit is nil at this point
so->AddModifier(fClothingSDLMod);
// add avatar sdl modifier
delete fAvatarSDLMod;
fAvatarSDLMod = TRACKED_NEW plAvatarSDLModifier;
so->AddModifier(fAvatarSDLMod);
delete fAvatarPhysicalSDLMod;
fAvatarPhysicalSDLMod = TRACKED_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, TRACKED_NEW plAttachMsg(so->GetKey(), nil, plRefMsg::kOnRequest), plRefFlags::kActiveRef);
if (fLinkSoundSOKey != nil)
hsgResMgr::ResMgr()->AddViaNotify(fLinkSoundSOKey, TRACKED_NEW plAttachMsg(so->GetKey(), nil, plRefMsg::kOnRequest), plRefFlags::kActiveRef);
if (fUpdateMsg)
fUpdateMsg->UnRef(); // delete an old one.
fUpdateMsg = TRACKED_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->WriteSwap32(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->WriteSwap32(fBodyType);
if( fEffects == nil )
stream->WriteBool( false );
else
{
stream->WriteBool( true );
mgr->WriteKey(stream, fEffects->GetKey());
}
stream->WriteSwapFloat(fPhysHeight);
stream->WriteSwapFloat(fPhysWidth);
stream->WriteSafeString(fAnimationPrefix.c_str());
stream->WriteSafeString(fBodyAgeName.c_str());
stream->WriteSafeString(fBodyFootstepSoundPage.c_str());
}
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->ReadSwap32();
for (int i = 0; i < nBrains; i++)
{
plArmatureBrain * brain = (plArmatureBrain *)mgr->ReadCreatable(stream);
this->PushBrain(brain);
}
if( stream->ReadBool() )
mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); // plClothingBase
else
fClothingOutfit = nil;
fBodyType = stream->ReadSwap32();
if( stream->ReadBool() )
{
plKey effectMgrKey = mgr->ReadKey(stream);
mgr->AddViaNotify(effectMgrKey, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); // plArmatureEffects
// Attach the Footstep emitter scene object
hsResMgr *mgr = hsgResMgr::ResMgr();
const char *age = fBodyAgeName.c_str();
const char *page = fBodyFootstepSoundPage.c_str();
const plLocation &gLoc = plKeyFinder::Instance().FindLocation(age, page);
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(nil, 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(nil, effectUoid);
}
if (effectKey != nil)
mgr->AddViaNotify(effectKey, TRACKED_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(nil, LinkUoid);
}
}
}
else
fEffects = nil;
fPhysHeight = stream->ReadSwapFloat();
fPhysWidth = stream->ReadSwapFloat();
char* temp = stream->ReadSafeString();
fAnimationPrefix = temp;
delete [] temp;
temp = stream->ReadSafeString();
fBodyAgeName = temp;
delete [] temp;
temp = stream->ReadSafeString();
fBodyFootstepSoundPage = temp;
delete [] temp;
plgDispatch::Dispatch()->RegisterForExactType(plAvatarStealthModeMsg::Index(), GetKey());
}
hsBool plArmatureMod::DirtySynchState(const char* SDLStateName, UInt32 synchFlags)
{
// skip requests to synch non-avatar state
if (SDLStateName && stricmp(SDLStateName, kSDLAvatar))
{
return false;
}
synchFlags |= plSynchedObject::kForceFullSend; // TEMP
if(GetNumTargets() > 0)
{
plSceneObject *sObj = GetTarget(0);
if(sObj)
sObj->DirtySynchState(SDLStateName, synchFlags);
}
return false;
}
hsBool plArmatureMod::DirtyPhysicalSynchState(UInt32 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 = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kVelocity, GetTarget(0)->GetKey(), true );
msg->Send();
fWaitFlags &= ~kNeedAudio;
}
if (fWaitFlags & kNeedCamera)
{
plCameraMsg* pMsg = TRACKED_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 = TRACKED_NEW plMatrixDelayedCorrectionApplicator();
volAGMod->SetApplicator(fBoneRootAnimator);
fWaitFlags &= ~kNeedApplicator;
}
}
const plSceneObject *plArmatureMod::GetClothingSO(UInt8 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 flags = kBCastToClients | kUseRelevanceRegions;
if (force)
flags |= kForceFullSend;
DirtyPhysicalSynchState(flags);
fLastSynch = timeNow;
fPendingSynch = false;
}
else
fPendingSynch = true;
}
hsBool plArmatureMod::IsLocalAvatar()
{
return plAvatarMgr::GetInstance()->GetLocalAvatar() == this;
}
hsBool plArmatureMod::IsLocalAI()
{
plAvBrainCritter* ai = plAvBrainCritter::ConvertNoRef(FindBrainByClass(plAvBrainCritter::Index()));
if (ai)
return ai->LocallyControlled();
return false; // not an AI, obviously not local
}
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;
}
hsBool plArmatureMod::ValidatePhysics()
{
if (!fTarget)
return false;
if (!fController)
fController = plPhysicalControllerCore::Create(GetTarget(0)->GetKey(), fPhysHeight, fPhysWidth);
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;
}
}
hsBool 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;
}
hsBool 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 = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, i, 0);
hsgResMgr::ResMgr()->SendRef(meshObj->GetKey(), refMsg, plRefFlags::kPassiveRef);
}
}
if (!strcmp(GetTarget(0)->GetKeyName(), "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 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;
}
hsBool plArmatureMod::GetInputFlag (int which) const
{
return fMoveFlags.IsBitSet(which);
}
void plArmatureMod::SetInputFlag(int which, hsBool status)
{
if(status)
{
fMoveFlags.SetBit(which);
} else {
fMoveFlags.ClearBit(which);
}
}
hsBool 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);
}
hsScalar plArmatureMod::GetTurnStrength() const
{
return GetKeyTurnStrength() + GetAnalogTurnStrength();
}
hsScalar plArmatureMod::GetKeyTurnStrength() const
{
if (StrafeKeyDown())
return 0.f;
return (TurnLeftKeyDown() ? 1.f : 0.f) + (TurnRightKeyDown() ? -1.f: 0.f);
}
hsScalar plArmatureMod::GetAnalogTurnStrength() const
{
if (StrafeKeyDown())
return 0.f;
hsScalar 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);
}
hsBool 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;
}
char *plArmatureMod::MakeAnimationName(const char *baseName) const
{
std::string temp = fAnimationPrefix + baseName;
char *result = hsStrcpy(temp.c_str()); // why they want a new string I'll never know... but hey, too late to change it now
return result;
}
char *plArmatureMod::GetRootName()
{
return fRootName;
}
void plArmatureMod::SetRootName(const char *name)
{
delete [] fRootName;
fRootName = hsStrcpy(name);
}
plAGAnim *plArmatureMod::FindCustomAnim(const char *baseName) const
{
char *customName = MakeAnimationName(baseName);
plAGAnim *result = plAGAnim::FindAnim(customName);
delete[] customName;
return result;
}
void plArmatureMod::ISetupMarkerCallbacks(plATCAnim *anim, plAnimTimeConvert *atc)
{
std::vector<char*> markers;
anim->CopyMarkerNames(markers);
int i;
for (i = 0; i < markers.size(); i++)
{
hsScalar time = -1;
hsBool isLeft = false;
if (strstr(markers[i], "SndLeftFootDown") == markers[i])
{
isLeft = true;
time = anim->GetMarker(markers[i]);
}
if (strstr(markers[i], "SndRightFootDown") == markers[i])
time = anim->GetMarker(markers[i]);
if (time >= 0)
{
plEventCallbackInterceptMsg *iMsg;
plArmatureEffectMsg *msg = TRACKED_NEW plArmatureEffectMsg(fEffects->GetKey(), kTime);
msg->fEventTime = time;
msg->fTriggerIdx = AnimNameToIndex(anim->GetName());
iMsg = TRACKED_NEW plEventCallbackInterceptMsg();
iMsg->AddReceiver(fEffects->GetKey());
iMsg->fEventTime = time;
iMsg->fEvent = kTime;
iMsg->SetMessage(msg);
atc->AddCallback(iMsg);
hsRefCnt_SafeUnRef(msg);
hsRefCnt_SafeUnRef(iMsg);
plAvatarFootMsg* foot = TRACKED_NEW plAvatarFootMsg(GetKey(), this, isLeft);
foot->fEventTime = time;
iMsg = TRACKED_NEW plEventCallbackInterceptMsg();
iMsg->AddReceiver(fEffects->GetKey());
iMsg->fEventTime = time;
iMsg->fEvent = kTime;
iMsg->SetMessage(foot);
atc->AddCallback(iMsg);
hsRefCnt_SafeUnRef(foot);
hsRefCnt_SafeUnRef(iMsg);
}
delete [] markers[i]; // done with this string, nuke it
}
}
const char *plArmatureMod::GetAnimRootName(const char *name)
{
return name + fAnimationPrefix.length();
}
Int8 plArmatureMod::AnimNameToIndex(const char *name)
{
const char *rootName = GetAnimRootName(name);
Int8 result = -1;
if (!strcmp(rootName, "Walk") || !strcmp(rootName, "WalkBack") ||
!strcmp(rootName, "LadderDown") || !strcmp(rootName, "LadderDownOn") ||
!strcmp(rootName, "LadderDownOff") || !strcmp(rootName, "LadderUp") ||
!strcmp(rootName, "LadderUpOn") || !strcmp(rootName, "LadderUpOff") ||
!strcmp(rootName, "SwimSlow") || !strcmp(rootName, "SwimBackward") ||
!strcmp(rootName, "BallPushWalk"))
result = kWalk;
else if (!strcmp(rootName, "Run") || !strcmp(rootName, "SwimFast"))
result = kRun;
else if (!strcmp(rootName, "TurnLeft") || !strcmp(rootName, "TurnRight") ||
!strcmp(rootName, "StepLeft") || !strcmp(rootName, "StepRight") ||
!strcmp(rootName, "SideSwimLeft") || !strcmp(rootName, "SideSwimRight") ||
!strcmp(rootName, "TreadWaterTurnLeft") || !strcmp(rootName, "TreadWaterTurnRight"))
result = kTurn;
else if (!strcmp(rootName, "GroundImpact") || !strcmp(rootName, "RunningImpact"))
result = kImpact;
else if (strstr(rootName, "Run")) // Critters
result = kRun;
else if (strstr(rootName, "Idle")) // Critters
result = kWalk;
return result;
}
hsBool plArmatureMod::IsInStealthMode() const
{
return (fStealthMode != plAvatarStealthModeMsg::kStealthVisible);
}
bool plArmatureMod::IsOpaque()
{
return fOpaque;
}
bool plArmatureMod::IsMidLink()
{
return fMidLink;
}
hsBool 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 char *animName)
{
if (animName)
{
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 char* root_name)
: plArmatureMod()
{
fRootName = hsStrcpy(root_name);
}
plArmatureLODMod::~plArmatureLODMod()
{
}
void plArmatureLODMod::Read(hsStream *stream, hsResMgr *mgr)
{
plArmatureMod::Read(stream, mgr);
fMeshKeys.clear();
int meshKeyCount = stream->ReadSwap32();
for(int i = 0; i < meshKeyCount; i++)
{
plKey meshKey = mgr->ReadKey(stream);
fMeshKeys.push_back(meshKey);
plKeyVector *vec = TRACKED_NEW plKeyVector;
int boneCount = stream->ReadSwap32();
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->WriteSwap32(meshKeyCount);
for(int i = 0; i < meshKeyCount; i++)
{
plKey meshKey = fMeshKeys[i];
mgr->WriteKey(stream, meshKey);
// Should be a list per mesh key
stream->WriteSwap32(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();
char strBuf[ 2048 ];
int lineHeight = debugTxt.GetFontSize() + 4;
UInt32 scrnWidth, scrnHeight;
debugTxt.GetScreenSize( &scrnWidth, &scrnHeight );
int y = 10;
int x = 10;
DumpToDebugDisplay(x, y, lineHeight, strBuf, debugTxt);
return y;
}
void plArmatureMod::DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt)
{
sprintf(strBuf, "Armature <%s>:", fRootName);
debugTxt.DrawString(x, y, strBuf, 255, 128, 128);
y += lineHeight;
plSceneObject * SO = GetTarget(0);
if(SO)
{
// global location
hsMatrix44 l2w = SO->GetLocalToWorld();
hsPoint3 worldPos = l2w.GetTranslate();
char *opaque = IsOpaque() ? "yes" : "no";
sprintf(strBuf, "position(world): %.2f, %.2f, %.2f Opaque: %3s",
worldPos.fX, worldPos.fY, worldPos.fZ, opaque);
debugTxt.DrawString(x, y, strBuf);
y += lineHeight;
hsPoint3 kPos;
char *kinematic = "n.a.";
const char* frozen = "n.a.";
if (fController)
{
fController->GetKinematicPosition(kPos);
kinematic = fController->IsKinematic() ? "on" : "off";
}
sprintf(strBuf, "kinematc(world): %.2f, %.2f, %.2f Kinematic: %3s",
kPos.fX, kPos.fY, kPos.fZ,kinematic);
debugTxt.DrawString(x, y, strBuf);
y += lineHeight;
if (fController)
frozen = fController->IsEnabled() ? "no" : "yes";
// are we in a subworld?
plKey world = nil;
if (fController)
world = fController->GetSubworld();
sprintf(strBuf, "In world: %s Frozen: %s", world ? world->GetName() : "nil", frozen);
debugTxt.DrawString(x,y, strBuf);
y+= lineHeight;
if (fController)
{
hsPoint3 physPos;
GetPositionAndRotationSim(&physPos, nil);
const hsVector3& vel = fController->GetLinearVelocity();
sprintf(strBuf, "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
{
sprintf(strBuf, "position(physical): no controller");
}
debugTxt.DrawString(x, y, strBuf);
y += lineHeight;
}
DebugDumpMoveKeys(x, y, lineHeight, strBuf, debugTxt);
int i;
for(i = 0; i < fBrains.size(); i++)
{
plArmatureBrain *brain = fBrains[i];
brain->DumpToDebugDisplay(x, y, lineHeight, strBuf, debugTxt);
}
if (fClothingOutfit)
{
y += lineHeight;
debugTxt.DrawString(x, y, "ItemsWorn:");
y += lineHeight;
strBuf[0] = '\0';
int itemCount = 0;
for (i = 0; i < fClothingOutfit->fItems.GetCount(); i++)
{
if (itemCount == 0)
strcat(strBuf, " ");
strcat(strBuf, fClothingOutfit->fItems[i]->fName);
itemCount++;
if (itemCount == 4)
{
debugTxt.DrawString(x, y, strBuf);
itemCount = 0;
strBuf[0] = '\0';
y += lineHeight;
}
if (itemCount > 0)
strcat(strBuf, ", ");
}
if (itemCount > 0)
{
debugTxt.DrawString(x, y, strBuf);
y += lineHeight;
}
}
if (plRelevanceMgr::Instance()->GetEnabled())
{
y += lineHeight;
debugTxt.DrawString(x, y, "Relevance Regions:");
y += lineHeight;
sprintf(strBuf, " In: %s", plRelevanceMgr::Instance()->GetRegionNames(fRegionsImIn).c_str());
debugTxt.DrawString(x, y, strBuf);
y += lineHeight;
sprintf(strBuf, " Care about: %s", plRelevanceMgr::Instance()->GetRegionNames(fRegionsICareAbout).c_str());
debugTxt.DrawString(x, y, strBuf);
y += lineHeight;
}
}
class plAvBoneMap::BoneMapImp
{
public:
typedef std::map<UInt32, const plSceneObject *> id2SceneObjectMap;
id2SceneObjectMap fMap;
};
plAvBoneMap::plAvBoneMap()
{
fImp = TRACKED_NEW BoneMapImp;
}
plAvBoneMap::~plAvBoneMap()
{
delete fImp;
}
const plSceneObject * plAvBoneMap::FindBone(UInt32 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 boneID, const plSceneObject *SO)
{
(fImp->fMap)[boneID] = SO;
}
void plArmatureMod::DebugDumpMoveKeys(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt)
{
char buff[256];
sprintf(buff, "Mouse Input Map: %s", plAvatarInputInterface::GetInstance()->GetInputMapName());
debugTxt.DrawString(x, y, buff);
y += lineHeight;
sprintf(buff, "Turn strength: %.2f (key: %.2f, analog: %.2f)", GetTurnStrength(), GetKeyTurnStrength(), GetAnalogTurnStrength());
debugTxt.DrawString(x, y, buff);
y += lineHeight;
GetMoveKeyString(buff);
debugTxt.DrawString(x, y, buff);
y += lineHeight;
}
void plArmatureMod::GetMoveKeyString(char *buff)
{
sprintf(buff, "Move keys: ");
if(FastKeyDown())
strcat(buff, "FAST ");
if(StrafeKeyDown())
strcat(buff, "STRAFE ");
if(ForwardKeyDown())
strcat(buff, "FORWARD ");
if(BackwardKeyDown())
strcat(buff, "BACKWARD ");
if(TurnLeftKeyDown())
strcat(buff, "TURNLEFT ");
if(TurnRightKeyDown())
strcat(buff, "TURNRIGHT ");
if(StrafeLeftKeyDown())
strcat(buff, "STRAFELEFT ");
if(StrafeRightKeyDown())
strcat(buff, "STRAFERIGHT ");
if(JumpKeyDown())
strcat(buff, "JUMP ");
}