/*==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==*/ #pragma warning(disable: 4503 4786) #include #include "plAvatarMgr.h" // local #include "plArmatureMod.h" #include "plSeekPointMod.h" #include "plOneShotMod.h" #include "plArmatureMod.h" #include "plAGModifier.h" #include "plAnimStage.h" #include "plCoopCoordinator.h" #include "plAvBrainCoop.h" // global #include "hsResMgr.h" #include "../pnNetCommon/plNetApp.h" #include "plgDispatch.h" #include "hsTimer.h" // other #include "../pnSceneObject/plSceneObject.h" #include "../pnSceneObject/plCoordinateInterface.h" #include "../pnKeyedObject/plKey.h" #include "../pnKeyedObject/plFixedKey.h" #include "../plNetClient/plNetClientMgr.h" #include "../plResMgr/plKeyFinder.h" #include "../pfCCR/plCCRMgr.h" // Only included for defined constants. #include "../plNetTransport/plNetTransport.h" #include "../plNetTransport/plNetTransportMember.h" #include "../plModifier/plSpawnModifier.h" #include "../plModifier/plMaintainersMarkerModifier.h" #include "../plVault/plDniCoordinateInfo.h" #include "../plMath/plRandom.h" #include "../pnMessage/plPlayerPageMsg.h" #include "../pnMessage/plWarpMsg.h" #include "../pnMessage/plNotifyMsg.h" #include "../plMessage/plMemberUpdateMsg.h" #include "../plMessage/plAvatarMsg.h" #include "../plMessage/plAvCoopMsg.h" #include "../pnMessage/plTimeMsg.h" #include "../plStatusLog/plStatusLog.h" // The static single instance, allocated on demand by GetInstance() plAvatarMgr *plAvatarMgr::fInstance = nil; // CTOR plAvatarMgr::plAvatarMgr() { fLog = plStatusLogMgr::GetInstance().CreateStatusLog(40, "Avatar.log", plStatusLog::kFilledBackground | plStatusLog::kAlignToTop | plStatusLog::kTimestamp); fLog->AddLine("Initalized avatar mgr"); } // DTOR plAvatarMgr::~plAvatarMgr() { IReset(); delete fLog; fLog = nil; } // GETINSTANCE plAvatarMgr * plAvatarMgr::GetInstance() { if(!fInstance) { fInstance = TRACKED_NEW plAvatarMgr; fInstance->RegisterAs(kAvatarMgr_KEY); fInstance->Ref(); } return fInstance; } // SHUTDOWN void plAvatarMgr::ShutDown() { if(fInstance) { fInstance->UnRef(); if(fInstance) fInstance->UnRegister(); fInstance = nil; } } // RESET void plAvatarMgr::IReset() { fSeekPoints.clear(); // Oneshots have copies of strings in their maps. I'm assuming the others should be the same, but until // I hear otherwise... for( plOneShotMap::iterator it = fOneShots.begin(); it != fOneShots.end(); it++ ) delete [] (char *)it->first; fOneShots.clear(); fAvatars.clear(); fSpawnPoints.clear(); fMaintainersMarkers.SetCountAndZero(0); plCoopMap::iterator acIt = fActiveCoops.begin(); while (acIt != fActiveCoops.end()) { plCoopCoordinator* deadCoop = acIt->second; delete deadCoop; acIt++; } fActiveCoops.clear(); } plKey plAvatarMgr::LoadPlayer(const char *name, const char *account) { return LoadAvatar(name, account, true, nil, nil); } plKey plAvatarMgr::LoadPlayer(const char *name, const char *account, const char *linkInName) { // what we'd like to do is turn the linkInName into a spawn point key and // put that into the plLoadAvatarMsg, which is already set up to handle // initial spawn points. // however, that will require that we can handle waiting for our spawn point to load, // so we're goin to do this the "old way" for now. plArmatureMod::SetSpawnPointOverride(linkInName); return LoadAvatar(name, account, true, nil, nil); } plKey plAvatarMgr::LoadAvatar(const char *name, const char *accountName, bool isPlayer, plKey spawnPoint, plAvTask *initialTask, const char *userStr /*=nil*/) { // *** account is currently unused. the idea is that eventually an NPC will // *** be able to use a customization account plKey result = nil; plKey requestor = GetKey(); // avatar manager is always the requestor for avatar loads plNetClientMgr *netMgr = plNetClientMgr::GetInstance(); if(netMgr) // can't clone without the net manager { hsAssert(name, "name required by LoadPlayer fxn"); netMgr->DebugMsg("Local: Loading player %s", name); // look up player by key name provided by user. // this string search should be replaced with some other method of // avatar selection and key lookup. // Get the location for the player first plKey playerKey = nil; const plLocation& globalLoc = plKeyFinder::Instance().FindLocation("GlobalAvatars", name); const plLocation& maleLoc = plKeyFinder::Instance().FindLocation("GlobalAvatars", "Male"); const plLocation& custLoc = plKeyFinder::Instance().FindLocation("CustomAvatars", name); // Silliness to make the compiler happy with const references. // and don't allow players to use custom avatars const plLocation& loc = (globalLoc.IsValid() ? globalLoc : isPlayer ? maleLoc : custLoc); const char* theName = name; if ( isPlayer && !globalLoc.IsValid() ) theName = "Male"; if (loc.IsValid()) { plUoid uID(loc, plSceneObject::Index(), theName); plLoadAvatarMsg *cloneMsg = TRACKED_NEW plLoadAvatarMsg (uID, requestor, 0, isPlayer, spawnPoint, initialTask, userStr); result = cloneMsg->GetCloneKey(); // the clone message is automatically addressed to the net client manager // we'll receive the message back (or a similar message) when the clone is loaded cloneMsg->Send(); } } return result; } void plAvatarMgr::UnLoadAvatar(plKey avatarKey, bool isPlayer) { hsBool isLoading = false; plLoadAvatarMsg *msg = TRACKED_NEW plLoadAvatarMsg(avatarKey, GetKey(), 0, isPlayer, isLoading); msg->Send(); } // our player's already loaded locally, but we've just linked into an age and others there need to be // told about us void plAvatarMgr::PropagateLocalPlayer(int spawnPoint) { plKey playerKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); if(playerKey) { plKey requestor = GetKey(); bool isPlayer = true; hsBool isLoading = true; plLoadAvatarMsg *msg = TRACKED_NEW plLoadAvatarMsg(playerKey, requestor, 0, isPlayer, isLoading); if (spawnPoint >= 0) { const plSpawnModifier * spawn = GetSpawnPoint(spawnPoint); if ( spawn ) { const plSceneObject * spawnObj = spawn->GetTarget(0); msg->SetSpawnPoint(spawnObj->GetKey()); } } // don't propagate locally. this is only for our peers msg->SetBCastFlag(plMessage::kLocalPropagate, false); msg->Send(); } else { hsStatusMessage("Tried to propagate non-existent local player."); } } // UNLOADLOCALPLAYERREMOTELY bool plAvatarMgr::UnPropagateLocalPlayer() { plKey playerKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); if(playerKey) { plKey requestor = GetKey(); bool isPlayer = true; hsBool isLoading = false; plLoadAvatarMsg *msg = TRACKED_NEW plLoadAvatarMsg(playerKey, requestor, 0, isPlayer, isLoading); msg->SetBCastFlag(plMessage::kLocalPropagate, false); msg->Send(); return true; } return false; } // UNLOADREMOTEPLAYER void plAvatarMgr::UnLoadRemotePlayer(plKey remotePlayer) { if(remotePlayer) { plKey requestor = GetKey(); bool isPlayer = true; hsBool isLoading = false; plLoadAvatarMsg * msg = TRACKED_NEW plLoadAvatarMsg(remotePlayer, requestor, 0, isPlayer, isLoading); // don't propagate over the network. this is just for removing our local version msg->SetBCastFlag(plMessage::kNetPropagate, false); msg->Send(); } } // UNLOADLOCALPLAYER void plAvatarMgr::UnLoadLocalPlayer() { plKey playerKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); if(playerKey) { plKey mgrKey = GetKey(); bool isPlayer = true; hsBool isLoading = false; plLoadAvatarMsg *msg = TRACKED_NEW plLoadAvatarMsg(playerKey, mgrKey, 0, isPlayer, isLoading); msg->Send(); } } // MSGRECEIVE hsBool plAvatarMgr::MsgReceive(plMessage *msg) { plLoadAvatarMsg *cloneM = plLoadAvatarMsg::ConvertNoRef(msg); if(cloneM) { // The only way we get clone messages is if we (or our remote counterparts) // requested them. if(cloneM->GetIsLoading()) { IFinishLoadingAvatar(cloneM); } else { IFinishUnloadingAvatar(cloneM); } return true; } plLoadCloneMsg* pCloneMsg = plLoadCloneMsg::ConvertNoRef(msg); if (pCloneMsg) { pCloneMsg->Ref(); fCloneMsgQueue.Append(pCloneMsg); plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); return true; } plEvalMsg* pEval = plEvalMsg::ConvertNoRef(msg); if (pEval) { for (int i = fCloneMsgQueue.Count() - 1; i > -1; i--) { plArmatureMod* pAvatar = FindAvatarByPlayerID(fCloneMsgQueue[i]->GetUserData()); if (pAvatar && pAvatar->GetKey()->ObjectIsLoaded()) { pAvatar->MsgReceive(fCloneMsgQueue[i]); fCloneMsgQueue[i]->UnRef(); fCloneMsgQueue.Remove(i); } } if (fCloneMsgQueue.Count() == 0) plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); return true; } plAvCoopMsg *coopM = plAvCoopMsg::ConvertNoRef(msg); if(coopM) { return HandleCoopMsg(coopM); } plNotifyMsg *notifyM = plNotifyMsg::ConvertNoRef(msg); if(notifyM) { if(proEventData * evt = notifyM->FindEventRecord(proEventData::kCoop)) { proCoopEventData *coopE = static_cast(evt); return IPassMessageToActiveCoop(msg, coopE->fID, coopE->fSerial); } } return false; } hsBool plAvatarMgr::HandleCoopMsg(plAvCoopMsg *msg) { plAvCoopMsg::Command cmd = msg->fCommand; UInt32 id = msg->fInitiatorID; UInt16 serial = msg->fInitiatorSerial; if(cmd == plAvCoopMsg::kStartNew) { // Currently, there's nothing that removes these coop coordinators when // they're done. Since I can't think of a good way to figure out when // they're done, I'm just going to clear them every time a new one starts. // With the current usage, you should only get one at a time anyway -Colin plCoopMap::iterator it = fActiveCoops.begin(); while (it != fActiveCoops.end()) { plCoopCoordinator* deadCoop = it->second; delete deadCoop; it++; } fActiveCoops.clear(); // start a new coop plCoopCoordinator *coord = msg->fCoordinator; plCoopMap::value_type newVal(id, coord); fActiveCoops.insert(newVal); coord->Run(); return true; } else { // it's a message for an existing coop... return IPassMessageToActiveCoop(msg, id, serial); } } hsBool plAvatarMgr::HandleNotifyMsg(plNotifyMsg *msg) { proCoopEventData *ed = static_cast(msg->FindEventRecord(proEventData::kCoop)); if(ed) { UInt32 id = ed->fID; UInt16 serial = ed->fSerial; return IPassMessageToActiveCoop(msg, id, serial); } return false; } hsBool plAvatarMgr::IPassMessageToActiveCoop(plMessage *msg, UInt32 id, UInt16 serial) { plCoopMap::iterator i = fActiveCoops.find(id); while(i != fActiveCoops.end() && (*i).first == id) { plCoopCoordinator *coord = (*i).second; if(coord->GetInitiatorSerial() == serial && coord->IsActiveForReal() ) { // this is the one coord->MsgReceive(msg); return true; } i++; } return false; } bool plAvatarMgr::IsACoopRunning() { bool isRunning = false; plCoopMap::iterator it = fActiveCoops.begin(); while (it != fActiveCoops.end()) { plCoopCoordinator* aCoop = it->second; if (aCoop->IsActiveForReal()) isRunning = true; it++; } return isRunning; } void plAvatarMgr::IFinishLoadingAvatar(plLoadAvatarMsg *cloneMsg) { plKey avatarKey = cloneMsg->GetCloneKey(); plUoid playerUoid = avatarKey->GetUoid(); const plArmatureMod *armature = FindAvatar(avatarKey); // we're going to re-send the clone message to the loaded avatar so he can get // any necessary details from it. cloneMsg->ClearReceivers(); // don't want it coming back to us cloneMsg->Ref(); // or going away if(armature) { cloneMsg->AddReceiver(armature->GetKey()); cloneMsg->Send(); } else { IDeferInit(avatarKey, cloneMsg); // we'll send this message when the armature mod loads. } if( cloneMsg->GetIsPlayer() ) { // notify everyone who cares that a new player has arrived // *** might want to move this to the human brain so we can make sure the // *** avatar is sufficiently initialized before anyone accesses him bool isLocal = cloneMsg->GetOriginatingPlayerID() == plNetClientMgr::GetInstance()->GetPlayerID(); plPlayerPageMsg* pageM = TRACKED_NEW plPlayerPageMsg; pageM->SetBCastFlag(plMessage::kBCastByExactType); pageM->fLocallyOriginated = isLocal; pageM->fPlayer = avatarKey; pageM->fUnload = false; pageM->fClientID = cloneMsg->GetOriginatingPlayerID(); pageM->Send(); } // This can probably be replaced by the plPlayerPageMsg: // ...keeping for the moment for compatibility plMemberUpdateMsg* mu = TRACKED_NEW plMemberUpdateMsg; mu->Send(); } // IFINISHUNLOADINGAVATAR void plAvatarMgr::IFinishUnloadingAvatar(plLoadAvatarMsg *cloneMsg) { // Note: in the corresponding FinishLoading, above, we give the incoming avatar // a look at the message that spawned him. When unloading, however, he doesn't get // that benefit because I don't think he'll actually be around to receive it. // *** need to test that theory....but it's not a problem for now. if( cloneMsg->GetIsPlayer() ) { plKey avatar = cloneMsg->GetCloneKey(); bool isLocal = cloneMsg->GetOriginatingPlayerID() == plNetClientMgr::GetInstance()->GetPlayerID(); plPlayerPageMsg *pageM = TRACKED_NEW plPlayerPageMsg; pageM->SetBCastFlag(plMessage::kBCastByExactType); pageM->fLocallyOriginated = isLocal; pageM->fPlayer = avatar; pageM->fUnload = true; pageM->fClientID = cloneMsg->GetOriginatingPlayerID(); if (plNetClientMgr::GetInstance()->RemotePlayerKeys().size() == 0) pageM->fLastOut = true; pageM->Send(); } // check on this...can it be subsumed by plPlayerPageMsg ? plMemberUpdateMsg *mu = TRACKED_NEW plMemberUpdateMsg; mu->Send(); } // IDEFERINIT void plAvatarMgr::IDeferInit(plKey playerSOKey, plMessage *initMsg) { plMessage *existing = fDeferredInits[playerSOKey]; // okay to use this form because we're going // to do the add either way if(existing) { hsStatusMessage("Avatar was registered twice for init. Discarding initial init message."); existing->UnRef(); } fDeferredInits[playerSOKey] = initMsg; initMsg->Ref(); } // ISENDDEFERREDINIT void plAvatarMgr::ISendDeferredInit(plKey avatarSOKey) { // get armaturemod const plArmatureMod * armature = FindAvatar(avatarSOKey); if(armature) { DeferredInits::iterator i = fDeferredInits.find(avatarSOKey); bool found = (i != fDeferredInits.end()); if(i != fDeferredInits.end()) { plMessage * initMsg = (*i).second; hsAssert(initMsg, "Tried to init avatar, but found nil initialization message."); if(initMsg) { initMsg->AddReceiver(armature->GetKey()); initMsg->Send(); } } } } // ADDSEEKPOINT void plAvatarMgr::AddSeekPoint(plSeekPointMod *seekPoint) { if(seekPoint) { const char *name = seekPoint->GetTarget(0)->GetKey()->GetName(); char *ourName = hsStrcpy(name); plSeekPointMod *alreadyThere = FindSeekPoint(name); /// hsAssert( ! alreadyThere, "Tried to add a seek point with duplicate name. Ignoring second seek point."); if ( ! alreadyThere) { fSeekPoints[ourName] = seekPoint; } } } // REMOVESEEKPOINT void plAvatarMgr::RemoveSeekPoint(plSeekPointMod *seekPoint) { if(seekPoint) { const char *name = seekPoint->GetTarget(0)->GetKey()->GetName(); plSeekPointMap::iterator found = fSeekPoints.find(name); if(found != fSeekPoints.end()) { const char *oldName = (*found).first; fSeekPoints.erase(found); delete[] const_cast(oldName); // retarded language, this is... } } } // FINDSEEKPOINT plSeekPointMod * plAvatarMgr::FindSeekPoint(const char *name) { plSeekPointMap::iterator found = fSeekPoints.find(name); if (found == fSeekPoints.end()) { return nil; } else { return (*found).second; } } // ADDONESHOT void plAvatarMgr::AddOneShot(plOneShotMod *oneshot) { if(oneshot) { // allocate a copy of the target name to use as a key char * name = hsStrcpy(oneshot->GetTarget(0)->GetKey()->GetName()); plOneShotMod *alreadyThere = FindOneShot(name); if ( ! alreadyThere) { fOneShots[name] = oneshot; } else delete [] name; } } // REMOVEONESHOT void plAvatarMgr::RemoveOneShot(plOneShotMod *oneshot) { plOneShotMap::iterator i = fOneShots.begin(); while (i != fOneShots.end()) { char * name = (*i).first; plOneShotMod *thisOneshot = (*i).second; if(oneshot == thisOneshot) { i = fOneShots.erase(i); // destroy our copy of the target name delete[] name; } else { i++; } } } // FINDONESHOT plOneShotMod *plAvatarMgr::FindOneShot(char *name) { plOneShotMap::iterator found = fOneShots.find(name); if (found == fOneShots.end()) { return nil; } else { return (*found).second; } } // ADDAVATAR void plAvatarMgr::AddAvatar(plArmatureMod *avatar) { // we shouldn't really need to ref this, as every time we access this object we will be checking it, and we don't care too much if it gets // pulled out from under us fAvatars.push_back(avatar->GetKey()); plSceneObject *avatarSO = avatar->GetTarget(0); hsAssert(avatarSO, "Adding avatar, but it hasn't been attached to a scene object yet."); if(avatarSO) { plKey soKey = avatarSO->GetKey(); ISendDeferredInit(soKey); } } // REMOVEAVATAR void plAvatarMgr::RemoveAvatar(plArmatureMod *avatar) { if (avatar) { plAvatarVec::iterator tail = std::remove(fAvatars.begin(), fAvatars.end(), avatar->GetKey()); if(tail != fAvatars.end()) fAvatars.erase(tail); } } plArmatureMod* plAvatarMgr::GetLocalAvatar() { plNetClientApp * app = plNetClientApp::GetInstance(); if(app) { plKey key = app->GetLocalPlayerKey(); if (key && key->ObjectIsLoaded()) { plSceneObject* so = plSceneObject::ConvertNoRef(key->GetObjectPtr()); if (so) return const_cast((plArmatureMod*)so->GetModifierByType(plArmatureMod::Index())); } } return nil; } plKey plAvatarMgr::GetLocalAvatarKey() { plArmatureMod *avatar = GetLocalAvatar(); if (avatar) return avatar->GetKey(); return nil; } plArmatureMod *plAvatarMgr::GetFirstRemoteAvatar() { plNetClientApp * app = plNetClientApp::GetInstance(); if(app) { plArmatureMod *localAvatar = GetLocalAvatar(); plAvatarVec::iterator it; for (it = fAvatars.begin(); it != fAvatars.end(); ++it) { plArmatureMod* armature = plArmatureMod::ConvertNoRef((*it)->ObjectIsLoaded()); if(armature && (armature != localAvatar)) return armature; } } return nil; } plArmatureMod* plAvatarMgr::FindAvatar(plKey& avatarKey) { plSceneObject *so = plSceneObject::ConvertNoRef(avatarKey->ObjectIsLoaded()); if (so) return const_cast((plArmatureMod*)so->GetModifierByType(plArmatureMod::Index())); return nil; } plArmatureMod* plAvatarMgr::FindAvatarByPlayerID(UInt32 pid) { plAvatarVec::iterator it; for (it = fAvatars.begin(); it != fAvatars.end(); ++it) { plArmatureMod* armature = plArmatureMod::ConvertNoRef((*it)->ObjectIsLoaded()); if (armature && (armature->GetKey()->GetUoid().GetClonePlayerID() == pid)) return armature; } return nil; } plArmatureMod *plAvatarMgr::FindAvatarByModelName(char *name) { plAvatarVec::iterator it; for (it = fAvatars.begin(); it != fAvatars.end(); ++it) { plArmatureMod* armature = plArmatureMod::ConvertNoRef((*it)->ObjectIsLoaded()); if (armature && (!strcmp(armature->GetTarget(0)->GetKeyName(), name))) return armature; } return nil; } void plAvatarMgr::FindAllAvatarsByModelName(const char* name, plArmatureModPtrVec& outVec) { plAvatarVec::iterator it; for (it = fAvatars.begin(); it != fAvatars.end(); ++it) { plArmatureMod* armature = plArmatureMod::ConvertNoRef((*it)->ObjectIsLoaded()); if (armature && (!strcmp(armature->GetTarget(0)->GetKeyName(), name))) outVec.push_back(armature); } } // ADDSPAWNPOINT void plAvatarMgr::AddSpawnPoint(plSpawnModifier *spawn) { fSpawnPoints.push_back(spawn); } // REMOVESPAWNPOINT void plAvatarMgr::RemoveSpawnPoint(plSpawnModifier *spawn) { plSpawnVec::iterator found = std::find(fSpawnPoints.begin(), fSpawnPoints.end(), spawn); if(found != fSpawnPoints.end()) { fSpawnPoints.erase(found); } } // GETSPAWNPOINT const plSpawnModifier * plAvatarMgr::GetSpawnPoint(int i) { if(i < fSpawnPoints.size()) { return fSpawnPoints[i]; } else return nil; } int plAvatarMgr::FindSpawnPoint( const char *name ) const { int i; for( i = 0; i < fSpawnPoints.size(); i++ ) { if( fSpawnPoints[ i ] != nil && (strstr( fSpawnPoints[ i ]->GetKey()->GetUoid().GetObjectName(), name ) != nil || strstr( fSpawnPoints[i]->GetTarget(0)->GetKeyName(), name) != nil)) return i; } return -1; } int plAvatarMgr::WarpPlayerToAnother(hsBool iMove, UInt32 remoteID) { plNetTransport &mgr = plNetClientMgr::GetInstance()->TransportMgr(); plNetTransportMember *mbr = mgr.GetMember(mgr.FindMember(remoteID)); if (!mbr) return plCCRError::kCantFindPlayer; if (!mbr->GetAvatarKey()) return plCCRError::kPlayerNotInAge; plSceneObject *remoteSO = plSceneObject::ConvertNoRef(mbr->GetAvatarKey()->ObjectIsLoaded()); plSceneObject *localSO = plSceneObject::ConvertNoRef(plNetClientMgr::GetInstance()->GetLocalPlayer()); if (!remoteSO) return plCCRError::kCantFindPlayer; if (!localSO) return plCCRError::kNilLocalAvatar; plWarpMsg *warp = TRACKED_NEW plWarpMsg(nil, (iMove ? localSO->GetKey() : remoteSO->GetKey()), plWarpMsg::kFlushTransform, (iMove ? remoteSO->GetLocalToWorld() : localSO->GetLocalToWorld())); warp->SetBCastFlag(plMessage::kNetPropagate); plgDispatch::MsgSend(warp); return hsOK; } int plAvatarMgr::WarpPlayerToXYZ(hsScalar x, hsScalar y, hsScalar z) { plSceneObject *localSO = plSceneObject::ConvertNoRef(plNetClientMgr::GetInstance()->GetLocalPlayer()); if (!localSO) return plCCRError::kNilLocalAvatar; hsMatrix44 m = localSO->GetLocalToWorld(); hsVector3 v(x, y, z); m.SetTranslate(&v); plWarpMsg *warp = TRACKED_NEW plWarpMsg(nil, localSO->GetKey(), plWarpMsg::kFlushTransform, m); warp->SetBCastFlag(plMessage::kNetPropagate); plgDispatch::MsgSend(warp); return hsOK; } int plAvatarMgr::WarpPlayerToXYZ(int pid, hsScalar x, hsScalar y, hsScalar z) { plNetClientMgr* nc=plNetClientMgr::GetInstance(); plNetTransportMember* mbr=nc->TransportMgr().GetMember(nc->TransportMgr().FindMember(pid)); plSceneObject *player = plSceneObject::ConvertNoRef(mbr && mbr->GetAvatarKey() ? mbr->GetAvatarKey()->ObjectIsLoaded() : nil); if (!player) return plCCRError::kNilLocalAvatar; hsMatrix44 m = player->GetLocalToWorld(); hsVector3 v(x, y, z); m.SetTranslate(&v); plWarpMsg *warp = TRACKED_NEW plWarpMsg(nil, player->GetKey(), 0, m); warp->SetBCastFlag(plMessage::kNetPropagate); plgDispatch::MsgSend(warp); return hsOK; } // ADD maintainers marker void plAvatarMgr::AddMaintainersMarker(plMaintainersMarkerModifier *mm) { fMaintainersMarkers.Append(mm); } // REMOVE maintainers marker void plAvatarMgr::RemoveMaintainersMarker(plMaintainersMarkerModifier *mm) { for (int i = 0; i < fMaintainersMarkers.Count(); i++) { if (fMaintainersMarkers[i] == mm) fMaintainersMarkers.Remove(i); } } void plAvatarMgr::PointToDniCoordinate(hsPoint3 pt, plDniCoordinateInfo* ret) { int count = fMaintainersMarkers.Count(); // plDniCoordinateInfo ret = TRACKED_NEW plDniCoordinateInfo; if (count > 0) { // find the closest maintainers marker int nearestIndex = 0; if (count > 1) { for (int i = 0; i < fMaintainersMarkers.Count(); i++) { if (fMaintainersMarkers[i]->GetTarget(0)) { hsVector3 testDist(fMaintainersMarkers[i]->GetTarget(0)->GetCoordinateInterface()->GetLocalToWorld().GetTranslate() - pt); hsVector3 baseDist(fMaintainersMarkers[nearestIndex]->GetTarget(0)->GetCoordinateInterface()->GetLocalToWorld().GetTranslate() - pt); if (testDist.MagnitudeSquared() < baseDist.MagnitudeSquared()) nearestIndex = i; } } } // convert the marker position to Dni coordinates int status = fMaintainersMarkers[nearestIndex]->GetCalibrated(); switch (status) { case plMaintainersMarkerModifier::kBroken: { plRandom rnd; rnd.SetSeed((int)(hsTimer::GetSeconds())); rnd.RandRangeI(1,999); ret->SetHSpans( rnd.RandRangeI(1,999) ); ret->SetVSpans( rnd.RandRangeI(1,999) ); ret->SetTorans( rnd.RandRangeI(1,62500) ); } break; case plMaintainersMarkerModifier::kRepaired: { ret->SetHSpans(0); ret->SetVSpans(0); ret->SetTorans(0); } break; case plMaintainersMarkerModifier::kCalibrated: { // this is the real deal here: // vertical spans: hsPoint3 retPoint = fMaintainersMarkers[nearestIndex]->GetTarget(0)->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); ret->SetVSpans( ((int)(pt.fZ - retPoint.fZ) / 16) ); // horizontal spans: // zero out the z axis... retPoint.fZ = pt.fZ = 0.0f; hsVector3 hSpanVec(retPoint - pt); ret->SetHSpans( (int)hSpanVec.Magnitude() / 16) ; // torans hsVector3 zeroVec = fMaintainersMarkers[nearestIndex]->GetTarget(0)->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); hsVector3 zeroRight = fMaintainersMarkers[nearestIndex]->GetTarget(0)->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kRight); zeroVec *= -1; // match the zero vectors to the positive X & Y axes in 3DSMax zeroRight *= -1; hsVector3 retVec(pt - retPoint); retVec.Normalize(); hsScalar dotView = retVec * zeroVec; hsScalar dotRight = retVec * zeroRight; hsScalar deg = acosf(dotView); deg*=(180/3.141592); // account for being > 180 if (dotRight < 0.0f) { deg = 360.f - deg; } // convert it to dni radians (torans) deg*=173.61; ret->SetTorans((int)deg); } break; } } } void plAvatarMgr::GetDniCoordinate(plDniCoordinateInfo* ret) { plSceneObject* localSO = plSceneObject::ConvertNoRef(plNetClientMgr::GetInstance()->GetLocalPlayer()); if (localSO) { hsPoint3 pos = localSO->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); PointToDniCoordinate(pos, ret); } } // OfferLinkingBook --------------------------------------------------------------- // ---------- void plAvatarMgr::OfferLinkingBook(plKey hostKey, plKey guestKey, plMessage *linkMsg, plKey replyKey) { if(hostKey != nil && guestKey != nil) { const plArmatureMod *hostAv = FindAvatar(hostKey); const plArmatureMod *guestAv = FindAvatar(guestKey); hsAssert(hostAv && guestAv, "Offering linking book: host or guest missing."); if(hostAv && guestAv) { // make the host brain plAvBrainCoop * brainH = TRACKED_NEW plAvBrainCoop(plAvBrainGeneric::kExitNormal, 3.0, 3.0, plAvBrainGeneric::kMoveRelative, guestKey); plAnimStage *hostOffer = TRACKED_NEW plAnimStage("BookOffer", plAnimStage::kNotifyAdvance); // autoforward, autoadvance // repeats until the guest brain tells us that it's done plAnimStage *hostIdle = TRACKED_NEW plAnimStage("BookOfferIdle", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceNone, plAnimStage::kRegressNone, -1); plAnimStage *hostFinish = TRACKED_NEW plAnimStage("BookOfferFinish", plAnimStage::kNotifyAdvance); // autoforward, autoadvance brainH->AddStage(hostOffer); brainH->AddStage(hostIdle); brainH->AddStage(hostFinish); UInt32 hostID = brainH->GetInitiatorID(); UInt32 hostSerial = brainH->GetInitiatorSerial(); // make the guest brain plAvBrainCoop * brainG = TRACKED_NEW plAvBrainCoop(plAvBrainGeneric::kExitNormal, 3.0, 3.0, plAvBrainGeneric::kMoveRelative, hostID, (UInt16)hostSerial, hostKey); plAnimStage *guestAccept = TRACKED_NEW plAnimStage("BookAccept", plAnimStage::kNotifyAdvance); plAnimStage *guestAcceptIdle = TRACKED_NEW plAnimStage("BookAcceptIdle", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceNone, plAnimStage::kRegressNone, -1); brainG->AddStage(guestAccept); brainG->AddStage(guestAcceptIdle); plCoopCoordinator *coord = TRACKED_NEW plCoopCoordinator(hostKey, guestKey, brainH, brainG, "Convergence", 1, 1, linkMsg, true); plAvCoopMsg *coMg = TRACKED_NEW plAvCoopMsg(hostKey, coord); coMg->SetBCastFlag(plMessage::kNetPropagate); coMg->SetBCastFlag(plMessage::kNetForce); coMg->Send(); brainH->SetRecipient(replyKey); brainG->SetRecipient(replyKey); } } }