/*==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==*/ #pragma warning(disable: 4503 4786) #include "plAvatarMgr.h" // local #include "plArmatureMod.h" #include "plSeekPointMod.h" #include "plOneShotMod.h" #include "plArmatureMod.h" #include "plAnimation/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 "pnEncryption/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 "plMessage/plLoadClothingMsg.h" #include "pnMessage/plTimeMsg.h" #include "plStatusLog/plStatusLog.h" #include #include // 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 = 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(); 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 plString &name, const plString &account) { return LoadAvatar(name, account, true, nullptr, nullptr); } plKey plAvatarMgr::LoadPlayer(const plString &name, const plString &account, const plString &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, nullptr, nullptr); } plKey plAvatarMgr::LoadPlayerFromFile(const plString &name, const plString &account, const plFileName &clothingFile) { return LoadAvatar(name, account, true, nullptr, nullptr, "", clothingFile); } plKey plAvatarMgr::LoadAvatar(plString name, plString accountName, bool isPlayer, plKey spawnPoint, plAvTask *initialTask, const plString &userStr, const plFileName &clothingFile) { // *** account is currently unused. the idea is that eventually an NPC will // *** be able to use a customization account plKey result = nullptr; 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.IsEmpty(), "name required by LoadPlayer fxn"); netMgr->DebugMsg("Local: Loading player %s", name.c_str()); // 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 = nullptr; const plLocation& globalLoc = plKeyFinder::Instance().FindLocation("GlobalAvatars", name); const plLocation& maleLoc = plKeyFinder::Instance().FindLocation("GlobalAvatars", "Male"); const plLocation& custLoc = plKeyFinder::Instance().FindLocation("CustomAvatars", name); #ifdef PLASMA_EXTERNAL_RELEASE // Try global. If that doesn't work, players default to male. // If not a player, try custLoc. If that doesn't work, fall back to male const plLocation& loc = (globalLoc.IsValid() ? globalLoc : isPlayer ? maleLoc : custLoc.IsValid() ? custLoc : maleLoc); #else // Try global. If that doesn't work try custom. Otherwise fall back to male const plLocation& loc = (globalLoc.IsValid() ? globalLoc : custLoc.IsValid() ? custLoc : maleLoc); #endif if (loc == maleLoc) name = "Male"; if (loc.IsValid()) { plUoid uID(loc, plSceneObject::Index(), name); plLoadAvatarMsg *cloneMsg = new plLoadAvatarMsg(uID, requestor, 0, isPlayer, spawnPoint, initialTask, userStr); if (clothingFile.IsValid()) { plLoadClothingMsg *clothingMsg = new plLoadClothingMsg(clothingFile); cloneMsg->SetTriggerMsg(clothingMsg); } 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(const plKey& avatarKey, bool isPlayer, bool netPropagate) const { if (avatarKey) { plKey requestor = GetKey(); plLoadAvatarMsg* msg = new plLoadAvatarMsg(avatarKey, requestor, 0, isPlayer, false); // only netprop if the user has a death wish msg->SetBCastFlag(plMessage::kNetPropagate, netPropagate); 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; bool isLoading = true; plLoadAvatarMsg *msg = 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; bool isLoading = false; plLoadAvatarMsg *msg = new plLoadAvatarMsg(playerKey, requestor, 0, isPlayer, isLoading); msg->SetBCastFlag(plMessage::kLocalPropagate, false); msg->Send(); return true; } return false; } // UNLOADLOCALPLAYER void plAvatarMgr::UnLoadLocalPlayer() { plKey playerKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); if(playerKey) { plKey mgrKey = GetKey(); bool isPlayer = true; bool isLoading = false; plLoadAvatarMsg *msg = new plLoadAvatarMsg(playerKey, mgrKey, 0, isPlayer, isLoading); msg->Send(); } } // MSGRECEIVE bool 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; } bool plAvatarMgr::HandleCoopMsg(plAvCoopMsg *msg) { plAvCoopMsg::Command cmd = msg->fCommand; uint32_t id = msg->fInitiatorID; uint16_t 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); } } bool plAvatarMgr::HandleNotifyMsg(plNotifyMsg *msg) { proCoopEventData *ed = static_cast(msg->FindEventRecord(proEventData::kCoop)); if(ed) { uint32_t id = ed->fID; uint16_t serial = ed->fSerial; return IPassMessageToActiveCoop(msg, id, serial); } return false; } bool plAvatarMgr::IPassMessageToActiveCoop(plMessage *msg, uint32_t id, uint16_t 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 = 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 = 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 = 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 = 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) { plString name = seekPoint->GetTarget(0)->GetKey()->GetName(); plSeekPointMod *alreadyThere = FindSeekPoint(name); /// hsAssert( ! alreadyThere, "Tried to add a seek point with duplicate name. Ignoring second seek point."); if ( ! alreadyThere) { fSeekPoints[name] = seekPoint; } } } // REMOVESEEKPOINT void plAvatarMgr::RemoveSeekPoint(plSeekPointMod *seekPoint) { if(seekPoint) { plString name = seekPoint->GetTarget(0)->GetKey()->GetName(); plSeekPointMap::iterator found = fSeekPoints.find(name); if(found != fSeekPoints.end()) { fSeekPoints.erase(found); } } } // FINDSEEKPOINT plSeekPointMod * plAvatarMgr::FindSeekPoint(const plString &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) { plString name = oneshot->GetTarget(0)->GetKey()->GetName(); plOneShotMod *alreadyThere = FindOneShot(name); if ( ! alreadyThere) { fOneShots[name] = oneshot; } } } // REMOVEONESHOT void plAvatarMgr::RemoveOneShot(plOneShotMod *oneshot) { plOneShotMap::iterator i = fOneShots.begin(); while (i != fOneShots.end()) { plString name = i->first; plOneShotMod *thisOneshot = i->second; if(oneshot == thisOneshot) { fOneShots.erase(i++); } else { ++i; } } } // FINDONESHOT plOneShotMod *plAvatarMgr::FindOneShot(const plString &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(const plKey& avatarKey) { plSceneObject *so = plSceneObject::ConvertNoRef(avatarKey->ObjectIsLoaded()); if (so) return const_cast((plArmatureMod*)so->GetModifierByType(plArmatureMod::Index())); return nil; } plArmatureMod* plAvatarMgr::FindAvatarByPlayerID(uint32_t 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(const plString& name) { plAvatarVec::iterator it; for (it = fAvatars.begin(); it != fAvatars.end(); ++it) { plArmatureMod* armature = plArmatureMod::ConvertNoRef((*it)->ObjectIsLoaded()); if (armature && (!armature->GetTarget(0)->GetKeyName().Compare(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 && (!armature->GetTarget(0)->GetKeyName().Compare(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 && ( fSpawnPoints[ i ]->GetKey()->GetUoid().GetObjectName().Find( name ) >= 0 || fSpawnPoints[ i ]->GetTarget(0)->GetKeyName().Find( name ) >= 0 )) return i; } return -1; } int plAvatarMgr::WarpPlayerToAnother(bool iMove, uint32_t 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 = 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(float x, float y, float 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 = new plWarpMsg(nil, localSO->GetKey(), plWarpMsg::kFlushTransform, m); warp->SetBCastFlag(plMessage::kNetPropagate); plgDispatch::MsgSend(warp); return hsOK; } int plAvatarMgr::WarpPlayerToXYZ(int pid, float x, float y, float 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 = 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 = 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(); float dotView = retVec * zeroVec; float dotRight = retVec * zeroRight; float 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 = new plAvBrainCoop(plAvBrainGeneric::kExitNormal, 3.0, 3.0, plAvBrainGeneric::kMoveRelative, guestKey); plAnimStage *hostOffer = new plAnimStage("BookOffer", plAnimStage::kNotifyAdvance); // autoforward, autoadvance // repeats until the guest brain tells us that it's done plAnimStage *hostIdle = new plAnimStage("BookOfferIdle", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceNone, plAnimStage::kRegressNone, -1); plAnimStage *hostFinish = new plAnimStage("BookOfferFinish", plAnimStage::kNotifyAdvance); // autoforward, autoadvance brainH->AddStage(hostOffer); brainH->AddStage(hostIdle); brainH->AddStage(hostFinish); uint32_t hostID = brainH->GetInitiatorID(); uint32_t hostSerial = brainH->GetInitiatorSerial(); // make the guest brain plAvBrainCoop * brainG = new plAvBrainCoop(plAvBrainGeneric::kExitNormal, 3.0, 3.0, plAvBrainGeneric::kMoveRelative, hostID, (uint16_t)hostSerial, hostKey); plAnimStage *guestAccept = new plAnimStage("BookAccept", plAnimStage::kNotifyAdvance); plAnimStage *guestAcceptIdle = new plAnimStage("BookAcceptIdle", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceNone, plAnimStage::kRegressNone, -1); brainG->AddStage(guestAccept); brainG->AddStage(guestAcceptIdle); plCoopCoordinator *coord = new plCoopCoordinator(hostKey, guestKey, brainH, brainG, "Convergence", 1, 1, linkMsg, true); plAvCoopMsg *coMg = new plAvCoopMsg(hostKey, coord); coMg->SetBCastFlag(plMessage::kNetPropagate); coMg->SetBCastFlag(plMessage::kNetForce); coMg->Send(); brainH->SetRecipient(replyKey); brainG->SetRecipient(replyKey); } } }