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.

1239 lines
48 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/>.
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==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plSceneInputInterface //
// //
//////////////////////////////////////////////////////////////////////////////
#include "HeadSpin.h"
#include "plSceneInputInterface.h"
#include "plInputInterfaceMgr.h"
#include "plInputManager.h"
#include "plInputDevice.h"
#include "plPhysical/plPickingDetector.h"
#include "plMessage/plInputEventMsg.h"
#include "plMessage/plLOSRequestMsg.h"
#include "plMessage/plLOSHitMsg.h"
#include "plMessage/plPickedMsg.h"
#include "plMessage/plRenderMsg.h"
#include "plMessage/plInputIfaceMgrMsg.h"
#include "plMessage/plVaultNotifyMsg.h"
#include "pnMessage/plFakeOutMsg.h"
#include "pnMessage/plNotifyMsg.h"
#include "pnMessage/plRemoteAvatarInfoMsg.h"
#include "pnMessage/plCursorChangeMsg.h"
#include "pnMessage/plCameraMsg.h"
#include "pnMessage/plPlayerPageMsg.h"
#include "pnMessage/plCmdIfaceModMsg.h"
#include "plAvatar/plArmatureMod.h"
#include "plAvatar/plAvBrain.h"
#include "plAvatar/plAvatarMgr.h"
#include "plAvatar/plAvCallbackAction.h"
#include "plModifier/plInterfaceInfoModifier.h"
#include "pnModifier/plLogicModBase.h"
#include "plVault/plVault.h"
#include "plNetClient/plNetClientMgr.h"
#include "plNetClient/plNetLinkingMgr.h"
#include "plNetCommon/plNetServerSessionInfo.h"
#include "plNetTransport/plNetTransport.h"
#include "plNetTransport/plNetTransportMember.h"
#include "pnSceneObject/plSceneObject.h"
#include "pnSceneObject/plCoordinateInterface.h"
#include "pnKeyedObject/plKey.h"
#include "pnKeyedObject/plFixedKey.h"
#include "pnInputCore/plKeyMap.h"
#include "plPhysical.h"
#include "plgDispatch.h"
#include "plPipeline.h"
#include "plModifier/plDetectorLog.h"
#define ID_FIND_CLICKABLE 2
#define ID_FIND_LOCALPLAYER 3
#define ID_FIND_WALKABLE_GROUND 4
#define SHARE_FACING_TOLERANCE -0.70f // 45 degrees
plSceneInputInterface *plSceneInputInterface::fInstance = nil;
hsBool plSceneInputInterface::fShowLOS = false;
//// Constructor/Destructor //////////////////////////////////////////////////
plSceneInputInterface::plSceneInputInterface()
{
fPipe = nil;
fSpawnPoint = nil;
GuidClear(&fAgeInstanceGuid);
fInstance = this;
SetEnabled( true ); // Always enabled
}
plSceneInputInterface::~plSceneInputInterface()
{
ClearClickableMap();
fIgnoredAvatars.Reset();
fLocalIgnoredAvatars.Reset();
fGUIIgnoredAvatars.Reset();
fInstance = nil;
}
//// Init/Shutdown ///////////////////////////////////////////////////////////
void plSceneInputInterface::Init( plInputInterfaceMgr *manager )
{
plInputInterface::Init( manager );
// To get the pipeline
fPipe = nil;
plgDispatch::Dispatch()->RegisterForExactType( plRenderMsg::Index(), fManager->GetKey() );
plgDispatch::Dispatch()->RegisterForExactType( plInputIfaceMgrMsg::Index(), fManager->GetKey() );
plgDispatch::Dispatch()->RegisterForExactType( plPlayerPageMsg::Index(), fManager->GetKey() );
fCurrentClickable = nil;
fCurrentClickableLogicMod = nil;
fLastClicked = nil;
fButtonState = 0;
fClickability = 1; // Hack for clickable avatars, we need to always do the LOS check
fLastClickIsAvatar = false;
fCurrClickIsAvatar = false;
fFadedLocalAvatar = false;
fBookMode = kNotOffering;
fOffereeKey = nil;
fPendingLink = false;
// register for control messages
plCmdIfaceModMsg* pModMsg = TRACKED_NEW plCmdIfaceModMsg;
pModMsg->SetBCastFlag(plMessage::kBCastByExactType);
pModMsg->SetSender(fManager->GetKey());
pModMsg->SetCmd(plCmdIfaceModMsg::kAdd);
plgDispatch::MsgSend(pModMsg);
}
void plSceneInputInterface::Shutdown( void )
{
if( fPipe == nil )
plgDispatch::Dispatch()->UnRegisterForExactType( plRenderMsg::Index(), fManager->GetKey() );
else
fPipe = nil;
}
void plSceneInputInterface::ClearClickableMap()
{
for (int i = 0; i < fClickableMap.Count(); i++)
{
clickableTest* pTest = fClickableMap[i];
delete(pTest);
}
fClickableMap.SetCountAndZero(0);
}
//// IHalfFadeAvatar /////////////////////////////////////////////////////////
void plSceneInputInterface::IHalfFadeAvatar(hsBool out)
{
plIfaceFadeAvatarMsg* pMsg = TRACKED_NEW plIfaceFadeAvatarMsg();
pMsg->SetSubjectKey(plNetClientMgr::GetInstance()->GetLocalPlayerKey());
pMsg->SetBCastFlag(plMessage::kBCastByExactType);
pMsg->SetBCastFlag(plMessage::kNetPropagate, FALSE);
pMsg->SetFadeOut(out);
pMsg->Send();
fFadedLocalAvatar = out;
}
void plSceneInputInterface::ResetClickableState()
{
if( fLastClicked != nil )
ISetLastClicked( nil, hsPoint3(0,0,0) );
ClearClickableMap();
fCurrentClickable = nil;
fCurrentClickableLogicMod = nil;
fCurrentCursor = SetCurrentCursorID(kNullCursor);
fCurrClickIsAvatar = false;
}
//// IEval ///////////////////////////////////////////////////////////////////
hsBool plSceneInputInterface::IEval( double secs, float del, uint32_t dirty )
{
// this needs to always go no matter what...
// ...unless we have cliclability disabled (as in the case of certain multistage behaviors)
if (plMouseDevice::Instance()->GetCursorOpacity() > 0.f && !plMouseDevice::Instance()->GetHideCursor())
{
IRequestLOSCheck( plMouseDevice::Instance()->GetCursorX(), plMouseDevice::Instance()->GetCursorY(), ID_FIND_LOCALPLAYER );
if (fClickability)
IRequestLOSCheck( plMouseDevice::Instance()->GetCursorX(), plMouseDevice::Instance()->GetCursorY(), ID_FIND_CLICKABLE );
}
else
if (fFadedLocalAvatar)
IHalfFadeAvatar(false);
// if (!fCurrClickIsAvatar)
// fCurrentCursor = SetCurrentCursorID(kNullCursor);
// ping for possible cursor changes
int i;
for (i=0; i < fClickableMap.Count(); i++)
{
plFakeOutMsg *pMsg = TRACKED_NEW plFakeOutMsg;
pMsg->SetSender( fManager->GetKey() );
pMsg->AddReceiver( fClickableMap[i]->key );
plgDispatch::MsgSend( pMsg );
}
// then see if we have any
hsBool change = false;
for (i=0; i < fClickableMap.Count(); i++)
{
if( fClickableMap[i]->val )
{
change = true;
break;
}
}
if (change)
{
if( fLastClicked != nil )
fCurrentCursor = SetCurrentCursorID(kCursorClicked);
else
fCurrentCursor = SetCurrentCursorID(kCursorPoised);
}
return true;
}
//// MsgReceive //////////////////////////////////////////////////////////////
hsBool plSceneInputInterface::MsgReceive( plMessage *msg )
{
plLOSHitMsg *pLOSMsg = plLOSHitMsg::ConvertNoRef( msg );
if( pLOSMsg )
{
if( pLOSMsg->fRequestID == ID_FIND_CLICKABLE )
{
hsBool clearCursor = false;
if (!fClickability)
return true;
if( pLOSMsg->fObj )
{
// is this object clickable?
plSceneObject *pObj = plSceneObject::ConvertNoRef( pLOSMsg->fObj->ObjectIsLoaded() );
if( pObj )
{
if (fShowLOS)
{
if (pLOSMsg->fNoHit)
DetectorLogSpecial("%s: LOS miss", pObj->GetKeyName());
else
DetectorLogSpecial("%s: LOS hit", pObj->GetKeyName());
}
int i;
const plInterfaceInfoModifier* pMod = 0;
for( i = 0; i < pObj->GetNumModifiers(); i++ )
{
if (fBookMode == kNotOffering) // when sharing a book we don't care about other clickables
{
pMod = plInterfaceInfoModifier::ConvertNoRef( pObj->GetModifier(i) );
if (pMod) // we found our list, stop here
{
plLogicModBase* pLogicMod = (plLogicModBase*)pObj->GetModifierByType(plLogicModBase::Index());
if (!pLogicMod)
return true;
if (fCurrentClickable != pObj->GetKey())
{ // is it the current clickable already?
ClearClickableMap();
fCurrentCursor = SetCurrentCursorID(kNullCursor);
fCurrentClickable = pObj->GetKey();
fCurrentClickableLogicMod = pLogicMod->GetKey();
fCurrentClickPoint = pLOSMsg->fHitPoint;
for (int x = 0; x < pMod->GetNumReferencedKeys(); x++)
fClickableMap.Append( TRACKED_NEW clickableTest(pMod->GetReferencedKey(x)));
}
else
{
// even if this is still the same clickable object, the cursor could be
// ...at a different spot on the clickable, so save that
fCurrentClickPoint = pLOSMsg->fHitPoint;
}
fCurrClickIsAvatar = false;
return true;
}
}
// see if it is an avatar
plArmatureMod* armMod = (plArmatureMod*)plArmatureMod::ConvertNoRef( pObj->GetModifier(i));
if (armMod)
{
if (armMod->IsMidLink())
return true;
// okay, are we a CCR?
hsBool amCCR = plNetClientMgr::GetInstance()->GetCCRLevel();
// is this person a NPC or CCR?
int mbrIdx=plNetClientMgr::GetInstance()->TransportMgr().FindMember(pObj->GetKey());
plNetTransportMember* pMbr = plNetClientMgr::GetInstance()->TransportMgr().GetMember(mbrIdx);
if (!pMbr) // whoops - it's a freakin' NPC !
return true;
if (pMbr->IsCCR())
{
if (amCCR)
{
// we can click on them
plMouseDevice::AddCCRToCursor();
}
else
{
// nope
return true;
}
}
// now, if I am a CCR, let me click on anyone at any time
if (amCCR)
{
ClearClickableMap();
fCurrentClickable = pObj->GetKey();
fCurrentClickableLogicMod = nil;
fCurrClickIsAvatar = true;
fCurrentCursor = SetCurrentCursorID(kCursorPoised);
// not sure why we need to point on the avatar...
// ...but maybe something in the future will need this
fCurrentClickPoint = pLOSMsg->fHitPoint;
plMouseDevice::AddNameToCursor(plNetClientMgr::GetInstance()->GetPlayerName(fCurrentClickable));
// also add their player ID to the cursor
plMouseDevice::AddIDNumToCursor(pMbr->GetPlayerID());
return true;
}
// otherwise, cull people as necessary
// also make sure that they are not in our ignore list
else if (VaultAmIgnoringPlayer( pMbr->GetPlayerID()))
return true;
// further, if we are offering a book, only allow clicks on the person
// whom we've already offered it to (to cancel it)
else if (fBookMode == kBookOffered && pObj->GetKey() != fOffereeKey)
return true;
// within distance
// also... make sure they aren't off climbing a ladder or looking at their KI
else if (fBookMode == kOfferBook)
{
plArmatureBrain* curBrain = armMod->GetCurrentBrain();
if (curBrain)
{
if (curBrain->IsRunningTask())
{
fCurrentCursor = SetCurrentCursorID(kCursorClickDisabled);
plMouseDevice::AddNameToCursor(plNetClientMgr::GetInstance()->GetPlayerName(pObj->GetKey()));
return true;
}
}
plAvatarMgr* aMgr = plAvatarMgr::GetInstance();
if (aMgr)
{
if (aMgr->IsACoopRunning())
{
fCurrentCursor = SetCurrentCursorID(kCursorClickDisabled);
plMouseDevice::AddNameToCursor(plNetClientMgr::GetInstance()->GetPlayerName(pObj->GetKey()));
return true;
}
}
plSceneObject* locPlayer = (plSceneObject*)plNetClientMgr::GetInstance()->GetLocalPlayer();
// make sure that they are facing each other
if ( locPlayer )
{
hsVector3 ourView = locPlayer->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView);
hsVector3 theirView = pObj->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView);
float viewdot = ourView * theirView;
hsVector3 towards(locPlayer->GetCoordinateInterface()->GetLocalToWorld().GetTranslate() - pObj->GetCoordinateInterface()->GetLocalToWorld().GetTranslate());
towards.Normalize();
float towardsdot = ourView * towards;
if (viewdot > SHARE_FACING_TOLERANCE || towardsdot > SHARE_FACING_TOLERANCE )
{
ResetClickableState();
return true; // not facing enough... reject
}
}
//otherwise make sure that they are close enough to click on
if (locPlayer)
{
hsPoint3 avPt = locPlayer->GetCoordinateInterface()->GetLocalToWorld().GetTranslate();
hsPoint3 objPt = pObj->GetCoordinateInterface()->GetLocalToWorld().GetTranslate();
hsVector3 dist(avPt - objPt);
if ( dist.MagnitudeSquared() >= 16.0f ) // you are too far away
{
ResetClickableState();
return true;
}
if (hsABS(avPt.fZ - objPt.fZ) > 1.0f) // you need to also be in the same plane (some books are on top of rocks you need to jump onto)
{
ResetClickableState();
return true;
}
}
}
// finally - make sure this guy is not in our ignore lists.
int x;
for (x = 0; x < fIgnoredAvatars.Count(); x++)
{
if (fIgnoredAvatars[x] == pObj->GetKey())
{
fCurrentCursor = SetCurrentCursorID(kCursorClickDisabled);
plMouseDevice::AddNameToCursor(plNetClientMgr::GetInstance()->GetPlayerName(pObj->GetKey()));
return true;
}
}
for (x = 0; x < fGUIIgnoredAvatars.Count(); x++)
{
if (fGUIIgnoredAvatars[x] == pObj->GetKey())
{
fCurrentCursor = SetCurrentCursorID(kCursorClickDisabled);
plMouseDevice::AddNameToCursor(plNetClientMgr::GetInstance()->GetPlayerName(pObj->GetKey()));
return true;
}
}
ClearClickableMap();
fCurrentClickable = pObj->GetKey();
fCurrentClickableLogicMod = nil;
fCurrClickIsAvatar = true;
fCurrentCursor = SetCurrentCursorID(kCursorPoised);
// not sure why we need to point on the avatar...
// ...but maybe something in the future will need this
fCurrentClickPoint = pLOSMsg->fHitPoint;
plMouseDevice::AddNameToCursor(plNetClientMgr::GetInstance()->GetPlayerName(fCurrentClickable));
return true;
}
}
// here! it's an object which is not clickable
// no object, or not clickable or avatar
fCurrentClickPoint = pLOSMsg->fHitPoint;
ResetClickableState();
return false;
}
}
// no object, or not clickable or avatar
ResetClickableState();
}
else
if( pLOSMsg->fRequestID == ID_FIND_LOCALPLAYER )
{
bool result = false;
if( pLOSMsg->fObj )
{
// is this object clickable?
plSceneObject *pObj = plSceneObject::ConvertNoRef( pLOSMsg->fObj->ObjectIsLoaded() );
if( pObj )
{
if (pObj == plNetClientMgr::GetInstance()->GetLocalPlayer())
result = true;
}
}
if (result && !fFadedLocalAvatar)
{
IHalfFadeAvatar(true);
return true;
}
else
if (!result && fFadedLocalAvatar)
{
IHalfFadeAvatar(false);
return true;
}
}
if( pLOSMsg->fRequestID == ID_FIND_WALKABLE_GROUND )
{
if (!pLOSMsg->fNoHit)
{
plAvatarMgr::GetInstance()->GetLocalAvatar()->TurnToPoint(pLOSMsg->fHitPoint);
}
return true;
}
return true;
}
plCursorChangeMsg *fakeReplyMsg = plCursorChangeMsg::ConvertNoRef( msg );
if( fakeReplyMsg != nil )
{
hsBool deniedCurrent = false;
plKey key = fakeReplyMsg->GetSender();
for (int i = 0; i < fClickableMap.Count(); i++)
{
if (fClickableMap[i]->key == key)
{
if( fakeReplyMsg->fType == plCursorChangeMsg::kNullCursor )
{
// Means not clickable--gotta fix this someday
fClickableMap[i]->val = false;
if (fClickableMap[i]->key == fCurrentClickableLogicMod)
{
deniedCurrent = true;
break;
}
}
else
{
// And fix this...
fClickableMap[i]->val = true;
}
}
}
if (deniedCurrent)
ResetClickableState();
return true;
}
plRenderMsg *rMsg = plRenderMsg::ConvertNoRef( msg );
if( rMsg != nil )
{
fPipe = rMsg->Pipeline();
plgDispatch::Dispatch()->UnRegisterForExactType( plRenderMsg::Index(), fManager->GetKey() );
return true;
}
// reply from coop share book multistage
plNotifyMsg* pNMsg = plNotifyMsg::ConvertNoRef(msg);
if (pNMsg)
{
for(int x=0; x < pNMsg->GetEventCount();x++)
{
proEventData* pED = pNMsg->GetEventRecord(0);
if ( pED->fEventType == proEventData::kMultiStage )
{
proMultiStageEventData* pMS = (proMultiStageEventData*)pED;
if (pMS->fAvatar == fOffereeKey) // mojo has linked
{
// do something - they linked out but we are still in the multistage
fOffereeKey = nil;
}
else
if (pMS->fAvatar == plNetClientMgr::GetInstance()->GetLocalPlayerKey())
{
// do something else
if (fBookMode = kNotOffering && fPendingLink == false) // we just linked out
{
// make me clickable again
ISendAvatarDisabledNotification(true);
}
else // we put the book back after our target linked out
{
fBookMode = kNotOffering;
fPendingLink = false;
// make ME clickable again
ISendAvatarDisabledNotification(true);
}
}
return true;
}
}
return false;
}
// if someone pages out / in, remove them from our ignore list or notify them to ignore us
plPlayerPageMsg* pPlayerMsg = plPlayerPageMsg::ConvertNoRef(msg);
if (pPlayerMsg)
{
if (pPlayerMsg->fUnload)
{
int x;
// first, remove this avatar from my list of avatars I ingore for clickable griefing (when the 'ignore avatars' key is pressed)
for(x = 0; x < fLocalIgnoredAvatars.Count(); x++)
{
if (fLocalIgnoredAvatars[x] == pPlayerMsg->fPlayer)
fLocalIgnoredAvatars.RemoveItem(pPlayerMsg->fPlayer);
}
// now deal with avatars we are always ignoring because of their current activity
for(x = 0; x < fIgnoredAvatars.Count(); x++)
{
if (fIgnoredAvatars[x] == pPlayerMsg->fPlayer)
fIgnoredAvatars.RemoveItem(pPlayerMsg->fPlayer);
}
for(x = 0; x < fGUIIgnoredAvatars.Count(); x++)
{
if (fGUIIgnoredAvatars[x] == pPlayerMsg->fPlayer)
fGUIIgnoredAvatars.RemoveItem(pPlayerMsg->fPlayer);
}
if (fOffereeKey == pPlayerMsg->fPlayer)
{
if (fBookMode == kBookOffered)
{
// and put our own dialog back up...
ISendOfferNotification(plNetClientMgr::GetInstance()->GetLocalPlayerKey(), 0, false);
//IManageIgnoredAvatars(fOffereeKey, false);
fOffereeKey = nil;
fBookMode = kNotOffering;
ISendAvatarDisabledNotification(true);
}
}
}
else
{
// add them to the list we keep of everyone here:
// but DO NOT add the local avatar
if (pPlayerMsg->fPlayer != plNetClientMgr::GetInstance()->GetLocalPlayerKey())
fLocalIgnoredAvatars.Append(pPlayerMsg->fPlayer);
if (fBookMode != kNotOffering)
{
// tell them to ignore us
plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kDisableAvatarClickable);
pMsg->SetAvKey(plNetClientMgr::GetInstance()->GetLocalPlayerKey());
pMsg->SetBCastFlag(plMessage::kNetPropagate);
pMsg->SetBCastFlag(plMessage::kNetForce);
pMsg->SetBCastFlag(plMessage::kLocalPropagate, false);
pMsg->AddNetReceiver( pPlayerMsg->fClientID );
pMsg->Send();
// and tell them to ignore our victim
//plInputIfaceMgrMsg* pMsg2 = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kDisableAvatarClickable);
//pMsg2->SetAvKey(fOffereeKey);
//pMsg2->SetBCastFlag(plMessage::kNetPropagate);
//pMsg2->SetBCastFlag(plMessage::kNetForce);
//pMsg2->SetBCastFlag(plMessage::kLocalPropagate, false);
//pMsg2->AddNetReceiver( pPlayerMsg->fClientID );
//pMsg2->Send();
}
// tell them to ingore us if we are looking at a GUI
for(int x = 0; x < fGUIIgnoredAvatars.Count(); x++)
{
if (fGUIIgnoredAvatars[x] == plNetClientMgr::GetInstance()->GetLocalPlayerKey())
{
plInputIfaceMgrMsg* pMsg3 = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kGUIDisableAvatarClickable);
pMsg3->SetAvKey(fGUIIgnoredAvatars[x]);
pMsg3->SetBCastFlag(plMessage::kNetPropagate);
pMsg3->SetBCastFlag(plMessage::kNetForce);
pMsg3->SetBCastFlag(plMessage::kLocalPropagate, false);
pMsg3->AddNetReceiver( pPlayerMsg->fClientID );
pMsg3->Send();
return true;
}
}
}
}
plInputIfaceMgrMsg *mgrMsg = plInputIfaceMgrMsg::ConvertNoRef( msg );
if( mgrMsg != nil )
{
if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kDisableAvatarClickable )
{
// ignore if already in list or this is who WE are offering the book to...
if (mgrMsg->GetAvKey() == fOffereeKey)
return true;
for(int x = 0; x < fIgnoredAvatars.Count(); x++)
{
if (fIgnoredAvatars[x] == mgrMsg->GetAvKey())
return true;
}
fIgnoredAvatars.Append(mgrMsg->GetAvKey());
}
else
if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kEnableAvatarClickable )
{
for(int x = 0; x < fIgnoredAvatars.Count(); x++)
{
if (fIgnoredAvatars[x] == mgrMsg->GetAvKey())
fIgnoredAvatars.RemoveItem(mgrMsg->GetAvKey());
}
}
else
if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kGUIDisableAvatarClickable )
{
// ignore if already in list or this is who WE are offering the book to...
if (mgrMsg->GetAvKey() == fOffereeKey)
return true;
for(int x = 0; x < fGUIIgnoredAvatars.Count(); x++)
{
if (fGUIIgnoredAvatars[x] == mgrMsg->GetAvKey())
return true;
}
fGUIIgnoredAvatars.Append(mgrMsg->GetAvKey());
}
else
if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kGUIEnableAvatarClickable )
{
for(int x = 0; x < fGUIIgnoredAvatars.Count(); x++)
{
if (fGUIIgnoredAvatars[x] == mgrMsg->GetAvKey())
fGUIIgnoredAvatars.RemoveItem(mgrMsg->GetAvKey());
}
}
else
if( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kEnableClickables )
{
fClickability = true;
return true;
}
else if( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kDisableClickables )
{
fClickability = false;
ResetClickableState();
return true;
}
else if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kSetOfferBookMode )
{
fBookMode = kOfferBook;
fOffereeKey = nil;
fBookKey = mgrMsg->GetSender();
fOfferedAgeInstance = mgrMsg->GetAgeName();
fOfferedAgeFile = mgrMsg->GetAgeFileName();
ISendAvatarDisabledNotification(false);
}
else if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kClearOfferBookMode )
{
if (fBookMode == kOfferAccepted || fBookMode == kOfferLinkPending)
{
fPendingLink = true;
}
else
if (fOffereeKey != nil)
{
// notify any offeree that the offer is rescinded
ISendOfferNotification(fOffereeKey, -999, true);
//IManageIgnoredAvatars(fOffereeKey, false);
fOffereeKey = nil;
}
// shut down offer book mode
fBookMode = kNotOffering;
ISendAvatarDisabledNotification(true);
}
else if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kNotifyOfferRejected)
{
if (fBookMode == kBookOffered)
{
// and put our own dialog back up...
ISendOfferNotification(plNetClientMgr::GetInstance()->GetLocalPlayerKey(), 0, false);
//IManageIgnoredAvatars(fOffereeKey, false);
fBookMode = kOfferBook;
fOffereeKey = nil;
}
else
if (mgrMsg->GetSender() == plNetClientMgr::GetInstance()->GetLocalPlayerKey())
{
fBookMode = kNotOffering;
ISendAvatarDisabledNotification(true);
}
}
else if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kNotifyOfferAccepted && fBookMode == kBookOffered)
{
fBookMode = kOfferAccepted;
}
else if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kNotifyOfferCompleted )
{
// must have actually offered the book...
if (!fPendingLink)
{
if (fBookMode == kOfferBook || fBookMode == kBookOffered)
return true;
}
if (!plNetClientMgr::GetInstance())
return true;
fOffereeID = mgrMsg->GetPageID();
ILinkOffereeToAge();
}
else if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kSetShareSpawnPoint )
{
fSpawnPoint = mgrMsg->GetSpawnPoint();
}
else if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kSetShareAgeInstanceGuid )
{
fAgeInstanceGuid = mgrMsg->GetAgeInstanceGuid();
}
}
plVaultNotifyMsg* pVaultMsg = plVaultNotifyMsg::ConvertNoRef(msg);
if (pVaultMsg && pVaultMsg->GetType()==plNetCommon::VaultTasks::kRegisterOwnedAge )
{
//sanity check -
if (fBookMode != kOfferLinkPending && fPendingLink == false)
return true;
// stop looking for this message and reset interface to 'offer book' mode again
plgDispatch::Dispatch()->UnRegisterForExactType(plVaultNotifyMsg::Index(), fManager->GetKey());
ILinkOffereeToAge();
return true;
}
return false;
}
///// ILinkOffereeToAge
void plSceneInputInterface::ILinkOffereeToAge()
{
// check vault to see if we've got an instance of the offered age now, if not create one and wait until we get a reply...
plAgeInfoStruct info;
info.SetAgeFilename(fOfferedAgeFile);
info.SetAgeInstanceName(fOfferedAgeInstance);
bool isAgeInstanceGuidSet = !GuidIsNil(fAgeInstanceGuid);
plAgeLinkStruct link;
if (isAgeInstanceGuidSet) {
info.SetAgeInstanceGuid(&plUUID(fAgeInstanceGuid));
link.GetAgeInfo()->CopyFrom(&info);
GuidClear(&fAgeInstanceGuid);
}
else if (!VaultGetOwnedAgeLink(&info, &link)) {
// We must have an owned copy of the age before we can offer it, so make one now
info.SetAgeInstanceGuid(&plUUID(GuidGenerate()));
std::string title;
std::string desc;
unsigned nameLen = StrLen(plNetClientMgr::GetInstance()->GetPlayerName());
if (plNetClientMgr::GetInstance()->GetPlayerName()[nameLen - 1] == 's' || plNetClientMgr::GetInstance()->GetPlayerName()[nameLen - 1] == 'S') {
xtl::format( title, "%s'", plNetClientMgr::GetInstance()->GetPlayerName() );
xtl::format( desc, "%s' %s", plNetClientMgr::GetInstance()->GetPlayerName(), link.GetAgeInfo()->GetAgeInstanceName() );
}
else {
xtl::format( title, "%s's", plNetClientMgr::GetInstance()->GetPlayerName() );
xtl::format( desc, "%s's %s", plNetClientMgr::GetInstance()->GetPlayerName(), link.GetAgeInfo()->GetAgeInstanceName() );
}
info.SetAgeUserDefinedName( title.c_str() );
info.SetAgeDescription( desc.c_str() );
link.GetAgeInfo()->CopyFrom(&info);
if (!VaultRegisterOwnedAgeAndWait(&link)) {
// failed to become an owner of the age for some reason, offer cannot continue
return;
}
}
else if (RelVaultNode * linkNode = VaultGetOwnedAgeLinkIncRef(&info)) {
// We have the age in our AgesIOwnFolder. If its volatile, dump it for the new one.
VaultAgeLinkNode linkAcc(linkNode);
if (linkAcc.volat) {
if (VaultUnregisterOwnedAgeAndWait(link.GetAgeInfo())) {
link.GetAgeInfo()->SetAgeInstanceGuid(&plUUID(GuidGenerate()));
VaultRegisterOwnedAgeAndWait(&link);
}
}
linkNode->DecRef();
}
if (fSpawnPoint) {
plSpawnPointInfo spawnPoint;
spawnPoint.SetName(fSpawnPoint);
link.SetSpawnPoint(spawnPoint);
}
// We now own the age, offer it
if (0 == stricmp(fOfferedAgeFile, kPersonalAgeFilename))
plNetLinkingMgr::GetInstance()->OfferLinkToPlayer(&link, fOffereeID, fManager->GetKey());
else
plNetLinkingMgr::GetInstance()->LinkPlayerToAge(&link, fOffereeID);
if (!fPendingLink && stricmp(fOfferedAgeFile, kPersonalAgeFilename))
{
// tell our local dialog to pop up again...
ISendOfferNotification(plNetClientMgr::GetInstance()->GetLocalPlayerKey(), 0, false);
// make them clickable again(in case they come back?)
//IManageIgnoredAvatars(fOffereeKey, false);
fBookMode = kNotOffering;
fOffereeKey = nil;
fPendingLink = false;
}
else // this is a yeesha book link, must wait for multistage callbacks
{
// commented out until after 0.9
fBookMode = kOfferLinkPending;
fPendingLink = true;
// fBookMode = kNotOffering;
// fOffereeKey = nil;
// fPendingLink = false;
// ISendAvatarDisabledNotification(true);
}
}
//// ISetLastClicked /////////////////////////////////////////////////////////
#define MATT_WAS_HERE
void plSceneInputInterface::ISetLastClicked( plKey obj, hsPoint3 hitPoint )
{
if (fBookMode != kNotOffering)
return;
if( fLastClicked != nil )
{
// Send an "un-picked" message to it
if( !fLastClickIsAvatar )
{
plPickedMsg *pPickedMsg = TRACKED_NEW plPickedMsg;
pPickedMsg->AddReceiver( fLastClicked );
pPickedMsg->fPicked = false;
plgDispatch::MsgSend( pPickedMsg );
}
else
{
plRemoteAvatarInfoMsg *pMsg = TRACKED_NEW plRemoteAvatarInfoMsg;
pMsg->SetAvatarKey( nil );
plgDispatch::MsgSend( pMsg );
}
}
fLastClicked = obj;
fLastClickIsAvatar = ( obj == nil ) ? false : fCurrClickIsAvatar;
if( fLastClicked != nil )
{
#ifdef MATT_WAS_HERE
// now we send pick messages to avatars as well...
plPickedMsg *pPickedMsg = TRACKED_NEW plPickedMsg;
pPickedMsg->AddReceiver( fLastClicked );
pPickedMsg->fHitPoint = hitPoint;
plgDispatch::MsgSend( pPickedMsg );
// if it's an avatar, we also send this thing
if(fLastClickIsAvatar)
{
plRemoteAvatarInfoMsg *pMsg = TRACKED_NEW plRemoteAvatarInfoMsg;
pMsg->SetAvatarKey( fLastClicked );
plgDispatch::MsgSend( pMsg );
}
#else
// Send a "picked" message to it
if( !fLastClickIsAvatar )
{
plPickedMsg *pPickedMsg = TRACKED_NEW plPickedMsg;
pPickedMsg->AddReceiver( fLastClicked );
pPickedMsg->fHitPoint = hitPoint;
plgDispatch::MsgSend( pPickedMsg );
}
else
{
plRemoteAvatarInfoMsg *pMsg = TRACKED_NEW plRemoteAvatarInfoMsg;
pMsg->SetAvatarKey( fLastClicked );
plgDispatch::MsgSend( pMsg );
}
#endif
}
}
//// InterpretInputEvent /////////////////////////////////////////////////////
hsBool plSceneInputInterface::InterpretInputEvent( plInputEventMsg *pMsg )
{
plControlEventMsg* pControlEvent = plControlEventMsg::ConvertNoRef(pMsg);
if (pControlEvent)
{
if (pControlEvent->GetControlCode() == B_CONTROL_IGNORE_AVATARS)
{
for (int i = 0; i < fLocalIgnoredAvatars.Count(); i++)
{
plSceneObject* pObj = plSceneObject::ConvertNoRef(fLocalIgnoredAvatars[i]->ObjectIsLoaded());
if (!pObj)
continue;
const plArmatureMod* pArm = (const plArmatureMod*)pObj->GetModifierByType(plArmatureMod::Index());
if (!pArm)
continue;
plPhysicalControllerCore* controller = pArm->GetController();
if (controller)
{
if (pControlEvent->ControlActivated())
controller->SetLOSDB(plSimDefs::kLOSDBNone);
else
controller->SetLOSDB(plSimDefs::kLOSDBUIItems);
}
}
return true;
}
return false;
}
plMouseEventMsg *mouseMsg = plMouseEventMsg::ConvertNoRef( pMsg );
if( mouseMsg != nil )
{
// you're suspended when in this mode...
if (fBookMode == kOfferLinkPending || fBookMode == kOfferAccepted)
return true;
if( mouseMsg->GetButton() == kLeftButtonDown )
{
if( fCurrentClickable != nil && fLastClicked == nil && fCurrentCursor != kNullCursor )
{
fButtonState |= kLeftButtonDown;
ISetLastClicked( fCurrentClickable, fCurrentClickPoint );
fCurrentCursor = SetCurrentCursorID(kCursorClicked);
return true;
}
// right here
if (fBookMode == kOfferBook)
{
fBookMode = kNotOffering;
fOffereeKey = nil;
ISendAvatarDisabledNotification(true);
}
}
else if( mouseMsg->GetButton() == kLeftButtonUp )
{
if (fBookMode != kNotOffering)
{
if (fBookMode == kOfferBook && fCurrClickIsAvatar)
{
// send the avatar a message to put up his appropriate book
ISendOfferNotification(fCurrentClickable, 999, true);
//IManageIgnoredAvatars(fCurrentClickable, true);
fBookMode = kBookOffered;
fOffereeKey = fCurrentClickable;
}
else
if (fBookMode == kBookOffered && fCurrClickIsAvatar)
{
// and put our own dialog back up...
ISendOfferNotification(fOffereeKey, -999, true);
ISendOfferNotification(plNetClientMgr::GetInstance()->GetLocalPlayerKey(), 0, false);
//IManageIgnoredAvatars(fOffereeKey, false);
fBookMode = kOfferBook;
fOffereeKey = nil;
}
else
if (fBookMode == kOfferBook)
{
fBookMode = kNotOffering;
fOffereeKey = nil;
ISendAvatarDisabledNotification(true);
}
}
if( fLastClicked != nil )
{
fButtonState &= ~kLeftButtonDown;
ISetLastClicked( nil, hsPoint3(0,0,0) );
return true;
}
}
}
return false;
}
//// ISendOfferNotification ////////////////////////////////////////////////////////
void plSceneInputInterface::IManageIgnoredAvatars(plKey& offeree, hsBool add)
{
// tell everyone else to be able to / not to be able to select this avatar
plInputIfaceMgrMsg* pMsg = 0;
if (!add)
pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kEnableAvatarClickable);
else
pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kDisableAvatarClickable);
pMsg->SetAvKey(offeree);
pMsg->SetBCastFlag(plMessage::kNetPropagate);
pMsg->SetBCastFlag(plMessage::kNetForce);
pMsg->SetBCastFlag(plMessage::kLocalPropagate, false);
pMsg->Send();
}
void plSceneInputInterface::ISendOfferNotification(plKey& offeree, int ID, hsBool net)
{
int offereeID = -1;
if (offeree == plNetClientMgr::GetInstance()->GetLocalPlayerKey())
{
offereeID = plNetClientMgr::GetInstance()->GetPlayerID();
}
else
{
plNetTransportMember **members = nil;
plNetClientMgr::GetInstance()->TransportMgr().GetMemberListDistSorted( members );
if( members != nil)
{
for(int i = 0; i < plNetClientMgr::GetInstance()->TransportMgr().GetNumMembers(); i++ )
{
plNetTransportMember *mbr = members[ i ];
if( mbr != nil && mbr->GetAvatarKey() == offeree)
{
offereeID = mbr->GetPlayerID();
break;
}
}
}
delete [] members;
}
plNotifyMsg* pMsg = TRACKED_NEW plNotifyMsg;
pMsg->AddOfferBookEvent(plNetClientMgr::GetInstance()->GetLocalPlayerKey(), ID, offereeID);
pMsg->AddReceiver(fBookKey);
if (net)
{
pMsg->SetBCastFlag(plMessage::kNetPropagate);
pMsg->SetBCastFlag(plMessage::kNetForce);
pMsg->SetBCastFlag(plMessage::kLocalPropagate,false);
pMsg->AddNetReceiver( offereeID );
pMsg->Send();
}
else
{
pMsg->SetBCastFlag(plMessage::kNetPropagate, false); // don't deliver networked!
pMsg->Send();
}
}
void plSceneInputInterface::ISendAvatarDisabledNotification(hsBool enabled)
{
plInputIfaceMgrMsg* pMsg = 0;
if (enabled)
pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kEnableAvatarClickable);
else
pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kDisableAvatarClickable);
pMsg->SetAvKey(plNetClientMgr::GetInstance()->GetLocalPlayerKey());
pMsg->SetBCastFlag(plMessage::kNetPropagate);
pMsg->SetBCastFlag(plMessage::kNetForce);
pMsg->SetBCastFlag(plMessage::kLocalPropagate, false);
pMsg->Send();
}
//// IRequestLOSCheck ////////////////////////////////////////////////////////
void plSceneInputInterface::IRequestLOSCheck( float xPos, float yPos, int ID )
{
if( fPipe == nil )
return;
int32_t x=(int32_t) ( xPos * fPipe->Width() );
int32_t y=(int32_t) ( yPos * fPipe->Height() );
hsPoint3 endPos, startPos;
fPipe->ScreenToWorldPoint( 1,0, &x, &y, 10000, 0, &endPos );
startPos = fPipe->GetViewPositionWorld();
// move the start pos out a little to avoid backing up against physical objects...
hsVector3 view(endPos - startPos);
view.Normalize();
startPos = startPos + (view * 0.3f);
plLOSRequestMsg* pMsg;
if(ID == ID_FIND_CLICKABLE) {
pMsg = TRACKED_NEW plLOSRequestMsg( fManager->GetKey(), startPos, endPos, plSimDefs::kLOSDBUIItems, plLOSRequestMsg::kTestClosest );
pMsg->SetCullDB(plSimDefs::kLOSDBUIBlockers);
} else if(ID == ID_FIND_WALKABLE_GROUND) {
pMsg = TRACKED_NEW plLOSRequestMsg( fManager->GetKey(), startPos, endPos, plSimDefs::kLOSDBAvatarWalkable, plLOSRequestMsg::kTestClosest);
} else
pMsg = TRACKED_NEW plLOSRequestMsg( fManager->GetKey(), startPos, endPos, plSimDefs::kLOSDBLocalAvatar, plLOSRequestMsg::kTestClosest);
pMsg->SetReportType( plLOSRequestMsg::kReportHitOrMiss );
pMsg->SetRequestID( ID );
plgDispatch::MsgSend( pMsg );
fLastStartPt = startPos;
fLastEndPt = endPos;
}
//// IWorldPosMovedSinceLastLOSCheck /////////////////////////////////////////
hsBool plSceneInputInterface::IWorldPosMovedSinceLastLOSCheck( void )
{
if( fPipe == nil )
return false;
int32_t x=(int32_t) ( plMouseDevice::Instance()->GetCursorX() * fPipe->Width() );
int32_t y=(int32_t) ( plMouseDevice::Instance()->GetCursorY() * fPipe->Height() );
hsPoint3 endPos, startPos;
startPos = fPipe->GetViewPositionWorld();
if( !( startPos == fLastStartPt ) )
return true;
fPipe->ScreenToWorldPoint( 1,0, &x, &y, 10000, 0, &endPos );
if( !( endPos == fLastEndPt ) )
return true;
return false;
}
//// GetCurrentCursorID ///////////////////////////////////////////////////////
uint32_t plSceneInputInterface::SetCurrentCursorID(uint32_t id)
{
if (fBookMode == kOfferBook || fBookMode == kBookOffered)
{
switch(id)
{
case kCursorPoised:
return kCursorOfferBookHilite;
case kNullCursor:
return kCursorOfferBook;
case kCursorClicked:
return kCursorOfferBookClicked;
}
}
else
if (fBookMode == kOfferAccepted || fBookMode == kOfferLinkPending)
return kCursorOfferBook;
return id;
}
void plSceneInputInterface::RequestAvatarTurnToPointLOS()
{
IRequestLOSCheck( plMouseDevice::Instance()->GetCursorX(), plMouseDevice::Instance()->GetCursorY(), ID_FIND_WALKABLE_GROUND );
}