/*==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 . Additional permissions under GNU GPL version 3 section 7 If you modify this Program, or any covered work, by linking or combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK (or a modified version of those libraries), containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the licensors of this Program grant you additional permission to convey the resulting work. Corresponding Source for a non-source form of such a combination shall include the source code for the parts of OpenSSL and IJG JPEG Library used as well as that of the covered work. You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ #include "plArmatureMod.h" // local #include "plAvBrain.h" #include "plAvBrainUser.h" #include "plAvatarMgr.h" #include "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 "plPhysicalControllerCore.h" #include "plAvBrainCritter.h" // global #include "plgDispatch.h" #include "hsQuat.h" #include "hsTimer.h" // other #include "../pnSceneObject/plCoordinateInterface.h" #include "../pnSceneObject/plAudioInterface.h" #include "../plInterp/plAnimTimeConvert.h" #include "../pnMessage/plEnableMsg.h" #include "../pnMessage/plTimeMsg.h" #include "../pnMessage/plSDLModifierMsg.h" #include "../pnMessage/plAttachMsg.h" #include "../pnMessage/plWarpMsg.h" #include "../pnMessage/plCorrectionMsg.h" #include "../pnMessage/plCameraMsg.h" #include "../pnMessage/plPipeResMakeMsg.h" #include "../plMessage/plAvatarMsg.h" #include "../plMessage/plAvatarFootMsg.h" #include "../plMessage/plInputEventMsg.h" #include "../plMessage/plLoadAgeMsg.h" #include "../plMessage/plAnimCmdMsg.h" #include "../plMessage/plListenerMsg.h" #include "../plMessage/plAgeLoadedMsg.h" #include "../plMessage/plParticleUpdateMsg.h" #include "../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) { EnablePhysics(!status, kDisableReasonKinematic); } 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; fIsLinkedIn = false; 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(), plWarpMsg::kFlushTransform, 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) { fIsLinkedIn = true; IFireBehaviorNotify(plHBehavior::kBehaviorTypeLinkIn, false); return true; } plAgeLoadedMsg *ageLoadMsg = plAgeLoadedMsg::ConvertNoRef(msg); if (ageLoadMsg) { if (ageLoadMsg->fLoaded) { // only the local player gets these NetworkSynch(hsTimer::GetSysSeconds(), true); } else fIsLinkedIn = false; return true; } plAgeLoaded2Msg *agePreLoadMsg = plAgeLoaded2Msg::ConvertNoRef(msg); if (agePreLoadMsg) { // all the age data is loaded -- add our physical controller to the age ValidatePhysics(); EnablePhysics(true); return true; } plAnimCmdMsg *cmdMsg = plAnimCmdMsg::ConvertNoRef(msg); if (cmdMsg) { hsAssert(false, "Illegal use of plAnimCmdMsg on an avatar."); return true; } plSubWorldMsg* subMsg = plSubWorldMsg::ConvertNoRef(msg); if (subMsg) { if (fController) { fController->SetSubworld(subMsg->fWorldKey); DirtySynchState(kSDLAvatar, plSynchedObject::kBCastToClients); } return true; } return plAGMasterMod::MsgReceive(msg); } 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()); plgDispatch::Dispatch()->RegisterForType(plAgeLoaded2Msg::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() const { 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, (fBodyType == kBoneBaseMale || fBodyType == kBoneBaseFemale)); if (fController) { if (GetTarget(0)->GetKey() == plNetClientApp::GetInstance()->GetLocalPlayerKey()) { // local avatars get added to a special LOS db just for them. fController->SetLOSDB(plSimDefs::kLOSDBLocalAvatar); } else { // non-local avatars are in the same LOS db as clickables fController->SetLOSDB(plSimDefs::kLOSDBUIItems); } hsClearBits(fWaitFlags, kNeedPhysics); return true; } else { return false; } } 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; } bool plArmatureMod::IsLinkedIn() { return fIsLinkedIn; } 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; const char* frozen = "n.a."; if (fController) frozen = fController->IsEnabled() ? "no" : "yes"; // are we in a subworld? plKey world = nil; if (fController) world = fController->GetSubworld(); 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 "); }