You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1029 lines
30 KiB
1029 lines
30 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
#pragma warning(disable: 4503 4786) |
|
|
|
#include <algorithm> |
|
|
|
#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<proCoopEventData *>(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<proCoopEventData *>(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<char *>(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*>((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*>((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); |
|
|
|
} |
|
} |
|
}
|
|
|