/*==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 . 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(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::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::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::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::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::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::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(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(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 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 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 "); }