/*==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==*/ #include "plExcludeRegionModifier.h" #include "../plMessage/plExcludeRegionMsg.h" #include "hsTemplates.h" #include "hsResMgr.h" #include "../pnSceneObject/plCoordinateInterface.h" #include "plDetectorLog.h" // For MsgReceive #include "../plMessage/plCollideMsg.h" #include "../pnSceneObject/plSceneObject.h" // For IClear and IRelease #include "../pnMessage/plWarpMsg.h" #include "../plMessage/plAvatarMsg.h" #include "plPhysical.h" #include "../plPhysical/plSimDefs.h" #include "../plAvatar/plAvCallbackAction.h" #include "../plAvatar/plAvBrainGeneric.h" #include "../plSDL/plSDL.h" #include "../pnMessage/plSDLModifierMsg.h" //for hack #include "../plPhysX/plPXPhysical.h" #include "../plPhysX/plPXPhysicalControllerCore.h" #include "NxCapsule.h" static plPhysical* GetPhysical(plSceneObject* obj) { if (obj) { const plSimulationInterface* si = obj->GetSimulationInterface(); if (si) return si->GetPhysical(); } return nil; } plExcludeRegionModifier::plExcludeRegionModifier() : fSDLModifier(nil), fSeek(false), fSeekTime(10.0f) { } plExcludeRegionModifier::~plExcludeRegionModifier() { } void plExcludeRegionModifier::Read(hsStream* stream, hsResMgr* mgr) { plSingleModifier::Read(stream, mgr); int numPoints = stream->ReadSwap32(); for (int i = 0; i < numPoints; i++) { fSafePoints.push_back(mgr->ReadKey(stream)); } fSeek = stream->ReadBool(); fSeekTime = stream->ReadSwapScalar(); } void plExcludeRegionModifier::Write(hsStream* stream, hsResMgr* mgr) { plSingleModifier::Write(stream, mgr); int numPoints = fSafePoints.size(); stream->WriteSwap32(numPoints); for (int i = 0; i < numPoints; i++) { mgr->WriteKey(stream,fSafePoints[i]); } stream->WriteBool(fSeek); stream->WriteSwapScalar(fSeekTime); } void plExcludeRegionModifier::ISetPhysicalState(bool cleared) { plPhysical* phys = GetPhysical(GetTarget()); if (phys) { phys->ExcludeRegionHack(cleared); if (cleared) { phys->AddLOSDB(plSimDefs::kLOSDBUIBlockers); if (HasFlag(kBlockCameras)) phys->AddLOSDB(plSimDefs::kLOSDBCameraBlockers); } else { phys->RemoveLOSDB(plSimDefs::kLOSDBUIBlockers); if (HasFlag(kBlockCameras)) phys->RemoveLOSDB(plSimDefs::kLOSDBCameraBlockers); } } } hsBool plExcludeRegionModifier::MsgReceive(plMessage* msg) { plExcludeRegionMsg *exclMsg = plExcludeRegionMsg::ConvertNoRef(msg); if (exclMsg) { if (exclMsg->GetCmd() == plExcludeRegionMsg::kClear) { DetectorLogSpecial("Clearing exclude region %s", GetKeyName()); IMoveAvatars(); fContainedAvatars.Reset(); ISetPhysicalState(true); } else if (exclMsg->GetCmd() == plExcludeRegionMsg::kRelease) { DetectorLogSpecial("Releasing exclude region %s", GetKeyName()); ISetPhysicalState(false); } GetTarget()->DirtySynchState(kSDLXRegion, exclMsg->fSynchFlags); return true; } // Avatar entering or exiting our volume. Only the owner of the SO logs this since // it does all the moving at clear time. plCollideMsg *collideMsg = plCollideMsg::ConvertNoRef(msg); if (collideMsg) { if (collideMsg->fEntering) { //DetectorLogSpecial("Avatar enter exclude region %s",GetKeyName()); fContainedAvatars.Append(collideMsg->fOtherKey); } else { //DetectorLogSpecial("Avatar exit exclude region %s",GetKeyName()); int idx = fContainedAvatars.Find(collideMsg->fOtherKey); if (idx != fContainedAvatars.kMissingIndex) fContainedAvatars.Remove(idx); } return true; } return plSingleModifier::MsgReceive(msg); } void plExcludeRegionModifier::AddTarget(plSceneObject* so) { if (so) { delete fSDLModifier; fSDLModifier = TRACKED_NEW plExcludeRegionSDLModifier(this); so->AddModifier(fSDLModifier); } plSingleModifier::SetTarget(so); } void plExcludeRegionModifier::RemoveTarget( plSceneObject *so ) { if (so && fSDLModifier) { so->RemoveModifier(fSDLModifier); delete fSDLModifier; fSDLModifier = nil; } plSingleModifier::RemoveTarget(so); } void plExcludeRegionModifier::AddSafePoint(plKey& key) { fSafePoints.push_back(key); } int plExcludeRegionModifier::IFindClosestSafePoint(plKey avatar) { float closestDist = 0.f; int closestIdx = -1; plSceneObject* avObj = plSceneObject::ConvertNoRef(avatar->GetObjectPtr()); if (!avObj) return -1; hsVector3 avPos; avObj->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(&avPos); for (int i = 0; i < fSafePoints.size(); i++) { plSceneObject* safeObj = plSceneObject::ConvertNoRef(fSafePoints[i]->GetObjectPtr()); hsVector3 safePos; safeObj->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(&safePos); float dist = (safePos - avPos).Magnitude(); if (dist < closestDist || closestIdx == -1) { closestDist = dist; closestIdx = i; } } return closestIdx; } // check to make sure that the avatar and the exclude region are in the same subworld bool plExcludeRegionModifier::ICheckSubworlds(plKey avatar) { plSceneObject* avObj = plSceneObject::ConvertNoRef(avatar->GetObjectPtr()); if (avObj) { // get the avatar modifier const plArmatureMod *avMod = (plArmatureMod*)avObj->GetModifierByType(plArmatureMod::Index()); if (avMod) { // get the avatar controller plPhysicalControllerCore* avController = avMod->GetController(); if (avController) { // get physical of the detector region plPhysical* phys = GetPhysical(GetTarget()); if (phys) { // are they in the same subworld? if ( phys->GetWorldKey() == avController->GetSubworld() ) return true; else return false; } } } } return false; } // Move avatars out of volume void plExcludeRegionModifier::IMoveAvatars() { //some reason this is not always finding all avatars might have something to do with //Physx trigger flutter. Adding in brute force method /* for (int i = 0; i < fContainedAvatars.Count(); i++) { if ( ICheckSubworlds(fContainedAvatars[i]) ) { int closestIdx = IFindClosestSafePoint(fContainedAvatars[i]); if (closestIdx != -1) { plAvSeekMsg* msg = TRACKED_NEW plAvSeekMsg; msg->SetBCastFlag(plMessage::kPropagateToModifiers); msg->AddReceiver(fContainedAvatars[i]); msg->fSmartSeek = fSeek; msg->fDuration = fSeekTime; msg->fSeekPoint = fSafePoints[closestIdx]; msg->fFlags |= plAvSeekMsg::kSeekFlagUnForce3rdPersonOnFinish; msg->Send(); } } } */ plPXPhysical* phys =(plPXPhysical*) GetPhysical(GetTarget()); if (phys) { plKey DetectorWorldKey = phys->GetWorldKey(); int numControllers = plPXPhysicalControllerCore::GetNumberOfControllersInThisSubWorld(phys->GetWorldKey()); if (numControllers > 0) { plPXPhysicalControllerCore** controllers = TRACKED_NEW plPXPhysicalControllerCore*[numControllers]; int actualCount = plPXPhysicalControllerCore::GetControllersInThisSubWorld(phys->GetWorldKey(), numControllers, controllers); for (int i=0;iGetWorldSpaceCapsule(cap); if(phys->OverlapWithCapsule(cap)) { plSceneObject* so = plSceneObject::ConvertNoRef(controllers[i]->GetOwner()->ObjectIsLoaded()); const plArmatureMod* constAvMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index()); if(constAvMod) { plAvBrainGeneric *curGenBrain = (plAvBrainGeneric *)constAvMod->FindBrainByClass(plAvBrainGeneric::Index()); // *** warning; if there's more than one generic brain active, this will only look at the first if (curGenBrain && curGenBrain->GetType() == plAvBrainGeneric::kLadder) { plAvBrainGenericMsg* pMsg = TRACKED_NEW plAvBrainGenericMsg( nil, constAvMod->GetKey(), plAvBrainGenericMsg::kGotoStage, -1, false, 0.0, false, false, 0.0 ); pMsg->Send(); } else { int closestIdx = IFindClosestSafePoint(controllers[i]->GetOwner()); if (closestIdx != -1) { plAvSeekMsg* msg = TRACKED_NEW plAvSeekMsg; msg->SetBCastFlag(plMessage::kPropagateToModifiers); msg->AddReceiver(controllers[i]->GetOwner()); msg->fSmartSeek = fSeek; msg->fDuration = fSeekTime; msg->fSeekPoint = fSafePoints[closestIdx]; msg->fFlags |= plAvSeekMsg::kSeekFlagUnForce3rdPersonOnFinish; msg->Send(); } } } } } delete[] controllers; } } } /////////////////////////////////////////////////////////////////////////////// plExcludeRegionSDLModifier::plExcludeRegionSDLModifier() : fXRegion(nil) { } plExcludeRegionSDLModifier::plExcludeRegionSDLModifier(plExcludeRegionModifier* xregion) : fXRegion(xregion) { } static const char* kXRegionSDLCleared = "cleared"; void plExcludeRegionSDLModifier::IPutCurrentStateIn(plStateDataRecord* dstState) { plSimpleStateVariable* var = dstState->FindVar(kXRegionSDLCleared); if (var) { plPhysical* phys = GetPhysical(GetTarget()); if (phys) { //bool cleared = phys->GetGroup() == plSimDefs::kGroupStatic; bool cleared = phys->GetGroup() == plSimDefs::kGroupExcludeRegion; var->Set(cleared); } } } void plExcludeRegionSDLModifier::ISetCurrentStateFrom(const plStateDataRecord* srcState) { plSimpleStateVariable* var = srcState->FindVar(kXRegionSDLCleared); if (var) { bool cleared; var->Get(&cleared); DetectorLogSpecial("SDL %s exclude region %s", cleared ? "clearing" : "releasing", fXRegion->GetKeyName()); fXRegion->ISetPhysicalState(cleared); } }