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.
923 lines
25 KiB
923 lines
25 KiB
14 years ago
|
/*==LICENSE==*
|
||
|
|
||
|
CyanWorlds.com Engine - MMOG client, server and tools
|
||
13 years ago
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
||
14 years ago
|
|
||
|
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
|
||
13 years ago
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
14 years ago
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
13 years ago
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
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.
|
||
14 years ago
|
|
||
|
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 "hsTypes.h"
|
||
|
#include "plCollisionDetector.h"
|
||
|
#include "../plMessage/plCollideMsg.h"
|
||
|
#include "plgDispatch.h"
|
||
|
#include "../plMessage/plActivatorMsg.h"
|
||
|
#include "../pnMessage/plCameraMsg.h"
|
||
|
#include "../pnMessage/plTimeMsg.h"
|
||
|
#include "../plMessage/plInputIfaceMgrMsg.h"
|
||
|
#include "../pnInputCore/plControlEventCodes.h"
|
||
|
#include "../pnNetCommon/plNetApp.h"
|
||
|
#include "../pnSceneObject/plSceneObject.h"
|
||
|
#include "../pnNetCommon/plNetApp.h"
|
||
|
#include "../plNetClient/plNetLinkingMgr.h"
|
||
|
|
||
|
#include "plPhysical.h"
|
||
|
|
||
|
#include "../pnMessage/plPlayerPageMsg.h"
|
||
|
#include "../plMessage/plSimStateMsg.h"
|
||
|
|
||
|
#include "../pnSceneObject/plCoordinateInterface.h"
|
||
|
#include "../plAvatar/plArmatureMod.h"
|
||
|
#include "../plAvatar/plAvatarMgr.h"
|
||
|
#include "../plAvatar/plAvBrainHuman.h"
|
||
|
#include "../plAvatar/plAvBrainDrive.h"
|
||
13 years ago
|
#include "../plAvatar/plPhysicalControllerCore.h"
|
||
14 years ago
|
|
||
|
#include "../plModifier/plDetectorLog.h"
|
||
|
|
||
12 years ago
|
#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND
|
||
|
#include "../plPhysX/plSimulationMgr.h"
|
||
|
#endif
|
||
14 years ago
|
|
||
|
plArmatureMod* plCollisionDetector::IGetAvatarModifier(plKey key)
|
||
|
{
|
||
|
plSceneObject* avObj = plSceneObject::ConvertNoRef(key->ObjectIsLoaded());
|
||
|
if (avObj)
|
||
|
{
|
||
|
// search through its modifiers to see if one of them is an avatar modifier
|
||
|
plArmatureMod* avMod = nil;
|
||
|
for (int i = 0; i < avObj->GetNumModifiers(); i++)
|
||
|
{
|
||
|
const plModifier* mod = avObj->GetModifier(i);
|
||
|
// see if it is an avatar mod base class
|
||
|
avMod = const_cast<plArmatureMod*>(plArmatureMod::ConvertNoRef(mod));
|
||
|
if (avMod)
|
||
|
return avMod;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
bool plCollisionDetector::IIsDisabledAvatar(plKey key)
|
||
|
{
|
||
|
plArmatureMod* avMod = IGetAvatarModifier(key);
|
||
|
plArmatureBrain* avBrain = avMod ? avMod->GetCurrentBrain() : nil;
|
||
|
return (plAvBrainDrive::ConvertNoRef(avBrain) != nil);
|
||
|
}
|
||
|
|
||
|
hsBool plCollisionDetector::MsgReceive(plMessage* msg)
|
||
|
{
|
||
|
plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg);
|
||
|
|
||
|
if (pCollMsg)
|
||
|
{
|
||
|
// If the avatar is disabled (flying around), don't trigger
|
||
|
if (IIsDisabledAvatar(pCollMsg->fOtherKey))
|
||
|
return false;
|
||
|
|
||
|
if (fType & kTypeBump)
|
||
|
{
|
||
|
if (!fBumped && !fTriggered)
|
||
|
{
|
||
|
for (int i = 0; i < fReceivers.Count(); i++)
|
||
|
{
|
||
|
plActivatorMsg* pMsg = TRACKED_NEW plActivatorMsg;
|
||
|
pMsg->AddReceiver( fReceivers[i] );
|
||
|
|
||
|
if (fProxyKey)
|
||
|
pMsg->fHiteeObj = fProxyKey;
|
||
|
else
|
||
|
pMsg->fHiteeObj = GetTarget()->GetKey();
|
||
|
pMsg->fHitterObj = pCollMsg->fOtherKey;
|
||
|
pMsg->SetSender(GetKey());
|
||
|
pMsg->SetTriggerType( plActivatorMsg::kCollideContact );
|
||
|
plgDispatch::MsgSend( pMsg );
|
||
|
}
|
||
|
fBumped = true;
|
||
|
fTriggered = true;
|
||
|
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
|
||
|
return true;
|
||
|
}
|
||
|
if (fTriggered)
|
||
|
{
|
||
|
fBumped = true;
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < fReceivers.Count(); i++)
|
||
|
{
|
||
|
plActivatorMsg* pMsg = TRACKED_NEW plActivatorMsg;
|
||
|
pMsg->AddReceiver( fReceivers[i] );
|
||
|
if (fProxyKey)
|
||
|
pMsg->fHiteeObj = fProxyKey;
|
||
|
else
|
||
|
pMsg->fHiteeObj = GetTarget()->GetKey();
|
||
|
pMsg->fHitterObj = pCollMsg->fOtherKey;
|
||
|
pMsg->SetSender(GetKey());
|
||
|
|
||
|
if (fType & kTypeEnter && pCollMsg->fEntering)
|
||
|
{
|
||
|
pMsg->SetTriggerType( plActivatorMsg::kCollideEnter );
|
||
|
plgDispatch::MsgSend( pMsg );
|
||
|
continue;
|
||
|
}
|
||
|
if (fType & kTypeUnEnter && pCollMsg->fEntering)
|
||
|
{
|
||
|
pMsg->SetTriggerType( plActivatorMsg::kEnterUnTrigger );
|
||
|
plgDispatch::MsgSend( pMsg );
|
||
|
continue;
|
||
|
}
|
||
|
if(fType & kTypeExit && !pCollMsg->fEntering)
|
||
|
{
|
||
|
pMsg->SetTriggerType( plActivatorMsg::kCollideExit );
|
||
|
plgDispatch::MsgSend( pMsg );
|
||
|
continue;
|
||
|
}
|
||
|
if(fType & kTypeUnExit && !pCollMsg->fEntering)
|
||
|
{
|
||
|
pMsg->SetTriggerType( plActivatorMsg::kExitUnTrigger );
|
||
|
plgDispatch::MsgSend( pMsg );
|
||
|
continue;
|
||
|
}
|
||
|
if (fType & kTypeAny)
|
||
|
{
|
||
|
pMsg->SetTriggerType( plActivatorMsg::kCollideContact );
|
||
|
plgDispatch::MsgSend( pMsg );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
delete (pMsg);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
plEvalMsg* pEval = plEvalMsg::ConvertNoRef(msg);
|
||
|
if (pEval)
|
||
|
{
|
||
|
if (!fBumped && fTriggered)
|
||
|
{
|
||
|
plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey());
|
||
|
for (int i = 0; i < fReceivers.Count(); i++)
|
||
|
{
|
||
|
plActivatorMsg* pMsg = TRACKED_NEW plActivatorMsg;
|
||
|
pMsg->AddReceiver( fReceivers[i] );
|
||
|
if (fProxyKey)
|
||
|
pMsg->fHiteeObj = fProxyKey;
|
||
|
else
|
||
|
pMsg->fHiteeObj = GetTarget()->GetKey();
|
||
|
pMsg->SetSender(GetKey());
|
||
|
pMsg->SetTriggerType( plActivatorMsg::kCollideUnTrigger );
|
||
|
plgDispatch::MsgSend( pMsg );
|
||
|
fTriggered = false;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
if (fTriggered && fBumped)
|
||
|
{
|
||
|
fBumped = false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return plDetectorModifier::MsgReceive(msg);
|
||
|
}
|
||
|
|
||
|
void plCollisionDetector::Read(hsStream* stream, hsResMgr* mgr)
|
||
|
{
|
||
|
plDetectorModifier::Read(stream, mgr);
|
||
|
stream->ReadSwap(&fType);
|
||
|
}
|
||
|
void plCollisionDetector::Write(hsStream* stream, hsResMgr* mgr)
|
||
|
{
|
||
|
plDetectorModifier::Write(stream, mgr);
|
||
|
stream->WriteSwap(fType);
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////
|
||
|
/////////////////////////////////
|
||
|
/////////////////////////////////
|
||
|
/////////////////////////////////
|
||
|
// camera region detector
|
||
|
|
||
|
plCameraRegionDetector::~plCameraRegionDetector()
|
||
|
{
|
||
|
for(int i = 0; i < fMessages.Count(); i++)
|
||
|
{
|
||
|
plMessage* pMsg = fMessages[i];
|
||
|
fMessages.Remove(i);
|
||
|
delete(pMsg);
|
||
|
}
|
||
|
fMessages.SetCountAndZero(0);
|
||
|
}
|
||
|
|
||
12 years ago
|
void plCameraRegionDetector::ISendTriggerMsg()
|
||
14 years ago
|
{
|
||
12 years ago
|
for (int i = 0; i < fMessages.Count(); ++i)
|
||
|
{
|
||
|
hsRefCnt_SafeRef(fMessages[i]);
|
||
|
if (fIsInside)
|
||
|
fMessages[i]->SetCmd(plCameraMsg::kEntering);
|
||
|
else
|
||
|
fMessages[i]->ClearCmd(plCameraMsg::kEntering);
|
||
14 years ago
|
|
||
12 years ago
|
plgDispatch::MsgSend(fMessages[i]);
|
||
14 years ago
|
}
|
||
|
}
|
||
|
|
||
|
hsBool plCameraRegionDetector::MsgReceive(plMessage* msg)
|
||
|
{
|
||
|
plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg);
|
||
|
if (pCollMsg)
|
||
|
{
|
||
|
// camera collisions are only for the local player
|
||
|
if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pCollMsg->fOtherKey)
|
||
|
return true;
|
||
|
|
||
|
if (!fWaitingForEval)
|
||
12 years ago
|
IRegisterForEval();
|
||
14 years ago
|
|
||
12 years ago
|
fEntering = (pCollMsg->fEntering != 0);
|
||
|
|
||
|
#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND
|
||
|
fLastStep = plSimulationMgr::GetInstance()->GetStepCount();
|
||
|
#endif
|
||
|
return true;
|
||
14 years ago
|
}
|
||
12 years ago
|
|
||
14 years ago
|
return plObjectInVolumeDetector::MsgReceive(msg);
|
||
|
}
|
||
|
void plCameraRegionDetector::Read(hsStream* stream, hsResMgr* mgr)
|
||
|
{
|
||
|
plDetectorModifier::Read(stream, mgr);
|
||
|
int n = stream->ReadSwap32();
|
||
|
fMessages.SetCountAndZero(n);
|
||
|
for(int i = 0; i < n; i++ )
|
||
|
{
|
||
|
plCameraMsg* pMsg = plCameraMsg::ConvertNoRef(mgr->ReadCreatable(stream));
|
||
|
fMessages[i] = pMsg;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
void plCameraRegionDetector::Write(hsStream* stream, hsResMgr* mgr)
|
||
|
{
|
||
|
plDetectorModifier::Write(stream, mgr);
|
||
|
stream->WriteSwap32(fMessages.GetCount());
|
||
|
for(int i = 0; i < fMessages.GetCount(); i++ )
|
||
|
mgr->WriteCreatable( stream, fMessages[i] );
|
||
|
|
||
|
}
|
||
12 years ago
|
|
||
|
void plCameraRegionDetector::IHandleEval(plEvalMsg*)
|
||
14 years ago
|
{
|
||
12 years ago
|
#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND
|
||
|
if (plSimulationMgr::GetInstance()->GetStepCount() - fLastStep > 1)
|
||
14 years ago
|
{
|
||
12 years ago
|
#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND
|
||
|
if (fIsInside != fEntering)
|
||
|
{
|
||
|
fIsInside = fEntering;
|
||
|
DetectorLog("%s CameraRegion: %s", fIsInside ? "Entering" : "Exiting", GetKeyName());
|
||
|
ISendTriggerMsg();
|
||
|
}
|
||
14 years ago
|
plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey());
|
||
|
fWaitingForEval = false;
|
||
12 years ago
|
#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND
|
||
14 years ago
|
}
|
||
12 years ago
|
#endif
|
||
14 years ago
|
}
|
||
|
|
||
|
/////////////////////////////////
|
||
|
/////////////////////////////////
|
||
|
/////////////////////////////////
|
||
|
/////////////////////////////////
|
||
|
// object-in-volume detector
|
||
|
|
||
12 years ago
|
void plObjectInVolumeDetector::ITrigger(plKey hitter, bool entering)
|
||
14 years ago
|
{
|
||
|
#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND
|
||
12 years ago
|
for (bookKeepingList::iterator it = fCollisionList.begin(); it != fCollisionList.end(); ++it)
|
||
14 years ago
|
{
|
||
12 years ago
|
plCollisionBookKeepingInfo* collisionInfo = *it;
|
||
|
if (collisionInfo->fHitter == hitter)
|
||
14 years ago
|
{
|
||
12 years ago
|
collisionInfo->fEntering = entering;
|
||
|
collisionInfo->fLastStep = plSimulationMgr::GetInstance()->GetStepCount();
|
||
|
return;
|
||
14 years ago
|
}
|
||
|
}
|
||
12 years ago
|
#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND
|
||
14 years ago
|
|
||
12 years ago
|
plCollisionBookKeepingInfo* collisionInfo = new plCollisionBookKeepingInfo(hitter, entering);
|
||
|
fCollisionList.push_back(collisionInfo);
|
||
|
#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND
|
||
|
collisionInfo->fLastStep = plSimulationMgr::GetInstance()->GetStepCount();
|
||
|
#endif
|
||
|
}
|
||
14 years ago
|
|
||
12 years ago
|
void plObjectInVolumeDetector::IRegisterForEval()
|
||
|
{
|
||
|
fWaitingForEval = true;
|
||
|
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
|
||
|
}
|
||
14 years ago
|
|
||
12 years ago
|
void plObjectInVolumeDetector::ISendTriggerMsg(plKey hitter, bool entering)
|
||
|
{
|
||
|
plActivatorMsg* activatorMsg = new plActivatorMsg();
|
||
|
activatorMsg->SetSender(GetKey());
|
||
|
activatorMsg->AddReceivers(fReceivers);
|
||
|
activatorMsg->fHiteeObj = fProxyKey ? fProxyKey : GetTarget()->GetKey();
|
||
|
activatorMsg->fHitterObj = hitter;
|
||
14 years ago
|
if (entering)
|
||
12 years ago
|
activatorMsg->SetTriggerType(plActivatorMsg::kVolumeEnter);
|
||
14 years ago
|
else
|
||
12 years ago
|
activatorMsg->SetTriggerType(plActivatorMsg::kVolumeExit);
|
||
14 years ago
|
|
||
12 years ago
|
plgDispatch::MsgSend(activatorMsg);
|
||
14 years ago
|
}
|
||
|
|
||
|
hsBool plObjectInVolumeDetector::MsgReceive(plMessage* msg)
|
||
|
{
|
||
|
plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg);
|
||
|
if (pCollMsg)
|
||
|
{
|
||
|
// If the avatar is disabled (flying around), don't trigger
|
||
|
if (IIsDisabledAvatar(pCollMsg->fOtherKey))
|
||
|
return false;
|
||
|
|
||
|
if (!fWaitingForEval)
|
||
12 years ago
|
IRegisterForEval();
|
||
14 years ago
|
|
||
12 years ago
|
ITrigger(pCollMsg->fOtherKey, (pCollMsg->fEntering != 0));
|
||
14 years ago
|
return true;
|
||
|
}
|
||
|
|
||
|
plEvalMsg* pEval = plEvalMsg::ConvertNoRef(msg);
|
||
|
if (pEval)
|
||
12 years ago
|
IHandleEval(pEval);
|
||
14 years ago
|
|
||
|
plPlayerPageMsg* pageMsg = plPlayerPageMsg::ConvertNoRef(msg);
|
||
|
if (pageMsg && pageMsg->fUnload)
|
||
|
{
|
||
|
ITrigger(pageMsg->fPlayer, false);
|
||
|
}
|
||
|
|
||
|
return plCollisionDetector::MsgReceive(msg);
|
||
|
}
|
||
|
|
||
12 years ago
|
void plObjectInVolumeDetector::IHandleEval(plEvalMsg*)
|
||
14 years ago
|
{
|
||
12 years ago
|
bookKeepingList::iterator it = fCollisionList.begin();
|
||
|
while (it != fCollisionList.end())
|
||
|
{
|
||
|
plCollisionBookKeepingInfo* collisionInfo = *it;
|
||
|
#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND
|
||
|
if (plSimulationMgr::GetInstance()->GetStepCount() - collisionInfo->fLastStep > 1)
|
||
14 years ago
|
{
|
||
12 years ago
|
#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND
|
||
|
bool wasInside = fCurrentResidents.find(collisionInfo->fHitter) != fCurrentResidents.end();
|
||
|
if (collisionInfo->fEntering != wasInside)
|
||
14 years ago
|
{
|
||
12 years ago
|
if (collisionInfo->fEntering)
|
||
|
{
|
||
|
fCurrentResidents.insert(collisionInfo->fHitter);
|
||
|
DetectorLog("%s: Sending Volume Enter ActivatorMsg", GetKeyName());
|
||
|
ISendTriggerMsg(collisionInfo->fHitter, true);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fCurrentResidents.erase(collisionInfo->fHitter);
|
||
|
DetectorLog("%s: Sending Volume Exit ActivatorMsg", GetKeyName());
|
||
|
ISendTriggerMsg(collisionInfo->fHitter, false);
|
||
|
}
|
||
14 years ago
|
}
|
||
12 years ago
|
|
||
|
delete collisionInfo;
|
||
|
#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND
|
||
|
it = fCollisionList.erase(it);
|
||
14 years ago
|
}
|
||
12 years ago
|
else
|
||
14 years ago
|
{
|
||
12 years ago
|
++it;
|
||
14 years ago
|
}
|
||
12 years ago
|
#else
|
||
|
++it;
|
||
|
#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND
|
||
|
}
|
||
14 years ago
|
|
||
12 years ago
|
#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND
|
||
|
if (fCollisionList.empty())
|
||
|
{
|
||
|
plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey());
|
||
|
fWaitingForEval = false;
|
||
|
}
|
||
|
#else
|
||
|
fCollisionList.clear();
|
||
|
plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey());
|
||
|
fWaitingForEval = false;
|
||
|
#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND
|
||
14 years ago
|
}
|
||
|
|
||
|
void plObjectInVolumeDetector::SetTarget(plSceneObject* so)
|
||
|
{
|
||
|
plCollisionDetector::SetTarget(so);
|
||
|
|
||
|
if (so)
|
||
|
plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey());
|
||
|
else
|
||
|
plgDispatch::Dispatch()->UnRegisterForExactType(plPlayerPageMsg::Index(), GetKey());
|
||
|
}
|
||
|
|
||
|
void plObjectInVolumeDetector::Read(hsStream* stream, hsResMgr* mgr)
|
||
|
{
|
||
|
plCollisionDetector::Read(stream, mgr);
|
||
|
}
|
||
|
|
||
|
void plObjectInVolumeDetector::Write(hsStream* stream, hsResMgr* mgr)
|
||
|
{
|
||
|
plCollisionDetector::Write(stream, mgr);
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
|
||
|
plObjectInVolumeAndFacingDetector::plObjectInVolumeAndFacingDetector() :
|
||
|
fFacingTolerance(0),
|
||
|
fNeedWalkingForward(false),
|
||
|
fAvatarInVolume(false),
|
||
|
fTriggered(false)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
plObjectInVolumeAndFacingDetector::~plObjectInVolumeAndFacingDetector()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void plObjectInVolumeAndFacingDetector::SetFacingTolerance(int degrees)
|
||
|
{
|
||
|
fFacingTolerance = hsCosine(hsScalarDegToRad(degrees));
|
||
|
}
|
||
|
|
||
|
void plObjectInVolumeAndFacingDetector::ICheckForTrigger()
|
||
|
{
|
||
|
plArmatureMod* armMod = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
||
|
plSceneObject* avatar = armMod ? armMod->GetTarget(0) : nil;
|
||
|
plSceneObject* target = GetTarget();
|
||
|
|
||
|
if (armMod && target)
|
||
|
{
|
||
|
hsVector3 playerView = avatar->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView);
|
||
|
hsVector3 objView = target->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView);
|
||
|
|
||
|
playerView.Normalize();
|
||
|
objView.Normalize();
|
||
|
|
||
|
hsScalar dot = playerView * objView;
|
||
|
// hsStatusMessageF("Dot: %f Tolerance: %f", dot, fFacingTolerance);
|
||
|
bool facing = dot >= fFacingTolerance;
|
||
|
|
||
|
bool movingForward = false;
|
||
|
if (fNeedWalkingForward)
|
||
|
{
|
||
|
// And are we walking towards it?
|
||
|
plArmatureBrain* abrain = armMod->FindBrainByClass(plAvBrainHuman::Index()); //armMod->GetCurrentBrain();
|
||
|
plAvBrainHuman* brain = plAvBrainHuman::ConvertNoRef(abrain);
|
||
13 years ago
|
if (brain && brain->IsMovingForward() && brain->fWalkingStrategy->IsOnGround())
|
||
14 years ago
|
movingForward = true;
|
||
|
}
|
||
|
else
|
||
|
movingForward = true;
|
||
|
|
||
|
if (facing && movingForward && !fTriggered)
|
||
|
{
|
||
|
DetectorLog("%s: Trigger InVolume&Facing", GetKeyName());
|
||
|
fTriggered = true;
|
||
12 years ago
|
ISendTriggerMsg(avatar->GetKey(), true);
|
||
14 years ago
|
}
|
||
|
else if (!facing && fTriggered)
|
||
|
{
|
||
|
DetectorLog("%s: Untrigger InVolume&Facing", GetKeyName());
|
||
|
fTriggered = false;
|
||
12 years ago
|
ISendTriggerMsg(avatar->GetKey(), false);
|
||
14 years ago
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
hsBool plObjectInVolumeAndFacingDetector::MsgReceive(plMessage* msg)
|
||
|
{
|
||
|
// Avatar is entering or exiting our detector box
|
||
|
plCollideMsg* collMsg = plCollideMsg::ConvertNoRef(msg);
|
||
|
if (collMsg)
|
||
|
{
|
||
|
// make sure this is the local player... the notify will be the thing that propagates over the network
|
||
|
if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != collMsg->fOtherKey)
|
||
|
return true;
|
||
|
|
||
|
// If the avatar is disabled (flying around), don't trigger
|
||
|
if (IIsDisabledAvatar(collMsg->fOtherKey))
|
||
|
return false;
|
||
|
|
||
|
fAvatarInVolume = (collMsg->fEntering != 0);
|
||
|
|
||
|
if (fAvatarInVolume)
|
||
|
{
|
||
|
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
|
||
|
ICheckForTrigger();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey());
|
||
|
|
||
|
// Avatar is leaving the volume, make sure to untrigger if we haven't already
|
||
|
if (fTriggered)
|
||
|
{
|
||
|
fTriggered = false;
|
||
12 years ago
|
ISendTriggerMsg(plNetClientApp::GetInstance()->GetLocalPlayerKey(), false);
|
||
14 years ago
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Avatar is inside our detector box, so every frame we check if we need to trigger
|
||
|
plEvalMsg* evalMsg = plEvalMsg::ConvertNoRef(msg);
|
||
|
if (evalMsg)
|
||
|
{
|
||
|
ICheckForTrigger();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return plObjectInVolumeDetector::MsgReceive(msg);
|
||
|
}
|
||
|
|
||
|
void plObjectInVolumeAndFacingDetector::Read(hsStream* stream, hsResMgr* mgr)
|
||
|
{
|
||
|
plObjectInVolumeDetector::Read(stream, mgr);
|
||
|
|
||
|
fFacingTolerance = stream->ReadSwapScalar();
|
||
|
fNeedWalkingForward = stream->Readbool();
|
||
|
}
|
||
|
|
||
|
void plObjectInVolumeAndFacingDetector::Write(hsStream* stream, hsResMgr* mgr)
|
||
|
{
|
||
|
plObjectInVolumeDetector::Write(stream, mgr);
|
||
|
|
||
|
stream->WriteSwapScalar(fFacingTolerance);
|
||
|
stream->Writebool(fNeedWalkingForward);
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////
|
||
|
/////////////////////////////////
|
||
|
/////////////////////////////////
|
||
|
/////////////////////////////////
|
||
|
// subworld region detector
|
||
|
|
||
|
plSubworldRegionDetector::~plSubworldRegionDetector()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
hsBool plSubworldRegionDetector::MsgReceive(plMessage* msg)
|
||
|
{
|
||
|
plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg);
|
||
|
|
||
|
if (pCollMsg)
|
||
|
{
|
||
|
if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pCollMsg->fOtherKey)
|
||
|
return true;
|
||
|
|
||
|
plArmatureMod* avMod = IGetAvatarModifier(pCollMsg->fOtherKey);
|
||
|
if (avMod)
|
||
|
{
|
||
|
DetectorLog("%s subworld detector %s", pCollMsg->fEntering ? "Entering" : "Exiting", GetKeyName());
|
||
|
|
||
|
if ((pCollMsg->fEntering && !fOnExit) ||
|
||
|
(!pCollMsg->fEntering && fOnExit))
|
||
|
{
|
||
|
if (fSub)
|
||
|
{
|
||
|
plSceneObject* SO = plSceneObject::ConvertNoRef(fSub->ObjectIsLoaded());
|
||
|
if (SO)
|
||
|
{
|
||
|
DetectorLogSpecial("Switching to subworld %s", fSub->GetName());
|
||
|
|
||
|
plKey nilKey;
|
||
|
plSubWorldMsg* msg = TRACKED_NEW plSubWorldMsg(GetKey(), avMod->GetKey(), fSub);
|
||
|
msg->Send();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DetectorLogSpecial("Switching to main subworld");
|
||
|
plSubWorldMsg* msg = TRACKED_NEW plSubWorldMsg(GetKey(), avMod->GetKey(), nil);
|
||
|
msg->Send();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return plCollisionDetector::MsgReceive(msg);
|
||
|
}
|
||
|
|
||
|
void plSubworldRegionDetector::Read(hsStream* stream, hsResMgr* mgr)
|
||
|
{
|
||
|
plDetectorModifier::Read(stream, mgr);
|
||
|
fSub = mgr->ReadKey(stream);
|
||
|
fOnExit = stream->ReadBool();
|
||
|
}
|
||
|
void plSubworldRegionDetector::Write(hsStream* stream, hsResMgr* mgr)
|
||
|
{
|
||
|
plDetectorModifier::Write(stream, mgr);
|
||
|
mgr->WriteKey(stream, fSub);
|
||
|
stream->WriteBool(fOnExit);
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////
|
||
|
///////////////////////////////////
|
||
|
/// plPanicLinkDetector
|
||
|
///////////////////////////////////
|
||
|
hsBool plPanicLinkRegion::MsgReceive(plMessage* msg)
|
||
|
{
|
||
|
|
||
5 years ago
|
if (plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg))
|
||
14 years ago
|
{
|
||
|
if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pCollMsg->fOtherKey)
|
||
|
return true;
|
||
|
|
||
|
if (pCollMsg->fEntering)
|
||
|
{
|
||
|
plArmatureMod* avMod = IGetAvatarModifier(pCollMsg->fOtherKey);
|
||
|
if (avMod)
|
||
|
{
|
||
5 years ago
|
if (avMod->IsLinkedIn())
|
||
|
{
|
||
|
hsPoint3 kinPos;
|
||
|
if (avMod->GetController())
|
||
|
{
|
||
|
avMod->GetController()->GetPositionSim(kinPos);
|
||
5 years ago
|
DetectorLogSpecial("Avatar is panic linking. KinPos at %f,%f,%f and is %s", kinPos.fX, kinPos.fY, kinPos.fZ, avMod->GetController()->IsEnabled() ? "enabled" : "disabled");
|
||
5 years ago
|
}
|
||
|
avMod->PanicLink(fPlayLinkOutAnim);
|
||
|
} else
|
||
|
DetectorLogRed("PANIC LINK %s before we actually linked in!", GetKey()->GetName());
|
||
14 years ago
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return plCollisionDetector::MsgReceive(msg);
|
||
|
}
|
||
|
|
||
|
void plPanicLinkRegion::Read(hsStream* stream, hsResMgr* mgr)
|
||
|
{
|
||
|
plCollisionDetector::Read(stream, mgr);
|
||
|
|
||
|
fPlayLinkOutAnim = stream->ReadBool();
|
||
|
}
|
||
|
|
||
|
void plPanicLinkRegion::Write(hsStream* stream, hsResMgr* mgr)
|
||
|
{
|
||
|
plCollisionDetector::Write(stream, mgr);
|
||
|
|
||
|
stream->WriteBool(fPlayLinkOutAnim);
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// PLSIMPLEREGIONSENSOR
|
||
|
//
|
||
|
/////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// ctor default
|
||
|
plSimpleRegionSensor::plSimpleRegionSensor()
|
||
|
: fEnterMsg(nil), fExitMsg(nil)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// ctor canonical
|
||
|
plSimpleRegionSensor::plSimpleRegionSensor(plMessage *enterMsg, plMessage *exitMsg)
|
||
|
: fEnterMsg(enterMsg), fExitMsg(exitMsg)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// dtor
|
||
|
plSimpleRegionSensor::~plSimpleRegionSensor()
|
||
|
{
|
||
|
if(fEnterMsg)
|
||
|
fEnterMsg->UnRef();
|
||
|
if(fExitMsg)
|
||
|
fExitMsg->UnRef();
|
||
|
}
|
||
|
|
||
|
// WRITE
|
||
|
void plSimpleRegionSensor::Write(hsStream *stream, hsResMgr *mgr)
|
||
|
{
|
||
|
plSingleModifier::Write(stream, mgr);
|
||
|
if(fEnterMsg)
|
||
|
{
|
||
|
stream->Writebool(true);
|
||
|
mgr->WriteCreatable(stream, fEnterMsg);
|
||
|
} else {
|
||
|
stream->Writebool(false);
|
||
|
}
|
||
|
if(fExitMsg)
|
||
|
{
|
||
|
stream->Writebool(true);
|
||
|
mgr->WriteCreatable(stream, fExitMsg);
|
||
|
} else {
|
||
|
stream->Writebool(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// READ
|
||
|
void plSimpleRegionSensor::Read(hsStream *stream, hsResMgr *mgr)
|
||
|
{
|
||
|
plSingleModifier::Read(stream, mgr);
|
||
|
if(stream->Readbool())
|
||
|
{
|
||
|
fEnterMsg = plMessage::ConvertNoRef(mgr->ReadCreatable(stream));
|
||
|
} else {
|
||
|
fEnterMsg = nil;
|
||
|
}
|
||
|
|
||
|
if(stream->Readbool())
|
||
|
{
|
||
|
fExitMsg = plMessage::ConvertNoRef(mgr->ReadCreatable(stream));
|
||
|
hsAssert(fExitMsg, "Corrupted plSimpleRegionSensor during read.");
|
||
|
} else {
|
||
|
fExitMsg = nil;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MSGRECEIVE
|
||
|
hsBool plSimpleRegionSensor::MsgReceive(plMessage *msg)
|
||
|
{
|
||
|
plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg);
|
||
|
|
||
|
if (pCollMsg)
|
||
|
{
|
||
|
// make sure this is the local player... the notify will be the thing that propagates over the network
|
||
|
if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pCollMsg->fOtherKey)
|
||
|
return true;
|
||
|
|
||
|
plKey theThingWhatDoneHitUs = pCollMsg->fOtherKey;
|
||
|
|
||
|
if(pCollMsg->fEntering)
|
||
|
{
|
||
|
if(fEnterMsg)
|
||
|
{
|
||
|
fEnterMsg->ClearReceivers();
|
||
|
fEnterMsg->AddReceiver(theThingWhatDoneHitUs);
|
||
|
fEnterMsg->Ref();
|
||
|
fEnterMsg->Send();
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if(fExitMsg)
|
||
|
{
|
||
|
fExitMsg->ClearReceivers();
|
||
|
fExitMsg->AddReceiver(theThingWhatDoneHitUs);
|
||
|
fExitMsg->Ref();
|
||
|
fExitMsg->Send();
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
return plSingleModifier::MsgReceive(msg);
|
||
|
}
|
||
|
|
||
|
// IEVAL
|
||
|
hsBool plSimpleRegionSensor::IEval(double secs, hsScalar del, UInt32 dirty)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// Nuke the Read/Write functions on the next file format change
|
||
|
void plSwimDetector::Write(hsStream *stream, hsResMgr *mgr)
|
||
|
{
|
||
|
plSimpleRegionSensor::Write(stream, mgr);
|
||
|
|
||
|
stream->WriteByte(0);
|
||
|
stream->WriteSwapScalar(0);
|
||
|
stream->WriteSwapScalar(0);
|
||
|
}
|
||
|
|
||
|
void plSwimDetector::Read(hsStream *stream, hsResMgr *mgr)
|
||
|
{
|
||
|
plSimpleRegionSensor::Read(stream, mgr);
|
||
|
|
||
|
stream->ReadByte();
|
||
|
stream->ReadSwapScalar();
|
||
|
stream->ReadSwapScalar();
|
||
|
}
|
||
|
hsBool plSwimDetector::MsgReceive(plMessage *msg)
|
||
|
{
|
||
|
plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg);
|
||
|
|
||
|
if (pCollMsg)
|
||
|
{
|
||
|
//removed local player check because this will apply the brain to the local
|
||
|
//controller of the foreign avatar which we still want.
|
||
|
//and if we prop swim state by notify messages we still have a chance of missing it from players
|
||
|
//who were in the region before we linked in
|
||
|
plKey theThingWhatDoneHitUs = pCollMsg->fOtherKey;
|
||
|
if(pCollMsg->fEntering)
|
||
|
{
|
||
|
if(fEnterMsg)
|
||
|
{
|
||
|
fEnterMsg->ClearReceivers();
|
||
|
fEnterMsg->AddReceiver(theThingWhatDoneHitUs);
|
||
|
fEnterMsg->Ref();
|
||
|
fEnterMsg->Send();
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if(fExitMsg)
|
||
|
{
|
||
|
fExitMsg->ClearReceivers();
|
||
|
fExitMsg->AddReceiver(theThingWhatDoneHitUs);
|
||
|
fExitMsg->Ref();
|
||
|
fExitMsg->Send();
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
return plSimpleRegionSensor::MsgReceive(msg);
|
||
|
}
|
||
|
hsBool plRidingAnimatedPhysicalDetector::MsgReceive(plMessage *msg)
|
||
|
{
|
||
|
|
||
|
plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg);
|
||
|
|
||
|
if (pCollMsg)
|
||
|
{
|
||
|
//removed local player check because this will apply the brain to the local
|
||
|
//controller of the foreign avatar which we still want.
|
||
|
//and if we prop state by notify messages we still have a chance of missing it from players
|
||
|
//who were in the region before we linked in
|
||
|
plKey theThingWhatDoneHitUs = pCollMsg->fOtherKey;
|
||
|
if(pCollMsg->fEntering)
|
||
|
{
|
||
|
if(fEnterMsg)
|
||
|
{
|
||
|
fEnterMsg->ClearReceivers();
|
||
|
fEnterMsg->AddReceiver(theThingWhatDoneHitUs);
|
||
|
fEnterMsg->Ref();
|
||
|
fEnterMsg->Send();
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if(fExitMsg)
|
||
|
{
|
||
|
fExitMsg->ClearReceivers();
|
||
|
fExitMsg->AddReceiver(theThingWhatDoneHitUs);
|
||
|
fExitMsg->Ref();
|
||
|
fExitMsg->Send();
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
return plSimpleRegionSensor::MsgReceive(msg);
|
||
13 years ago
|
}
|