|
|
|
/*==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==*/
|
|
|
|
|
|
|
|
#include <Python.h>
|
|
|
|
#include "plgDispatch.h"
|
|
|
|
#include "pyKey.h"
|
|
|
|
#include "plPhysical.h"
|
|
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
#include "cyAvatar.h"
|
|
|
|
|
|
|
|
#include "plAvatar/plAvatarMgr.h"
|
|
|
|
#include "plMessage/plAvatarMsg.h"
|
|
|
|
#include "plMessage/plLinkToAgeMsg.h"
|
|
|
|
#include "plMessage/plOneShotCallbacks.h"
|
|
|
|
#include "plMessage/plOneShotMsg.h"
|
|
|
|
#include "plMessage/plMultistageMsg.h"
|
|
|
|
#include "pnMessage/plNotifyMsg.h"
|
|
|
|
#include "pnKeyedObject/plFixedKey.h"
|
|
|
|
#include "plGImage/plMipmap.h"
|
|
|
|
#include "pySceneObject.h"
|
|
|
|
#include "pyColor.h"
|
|
|
|
#include "pyImage.h"
|
|
|
|
|
|
|
|
#include "plAvatar/plOneShotMod.h"
|
|
|
|
#include "plAvatar/plMultistageBehMod.h"
|
|
|
|
#include "plAvatar/plAvatarClothing.h"
|
|
|
|
#include "plAvatar/plClothingLayout.h"
|
|
|
|
#include "plAvatar/plArmatureMod.h"
|
|
|
|
#include "plAvatar/plAvBrainHuman.h" // needed to call the emote
|
|
|
|
#include "plAvatar/plAGAnim.h" // to get the BodyUsage enum
|
|
|
|
#include "plInputCore/plAvatarInputInterface.h"
|
|
|
|
#include "plMessage/plSimStateMsg.h"
|
|
|
|
|
|
|
|
#include "plVault/plVault.h"
|
|
|
|
|
|
|
|
#include "plDrawable/plSharedMesh.h"
|
|
|
|
|
|
|
|
#include "pnSceneObject/plSceneObject.h"
|
|
|
|
#include "pnSceneObject/plCoordinateInterface.h"
|
|
|
|
#include "plDrawable/plMorphSequence.h"
|
|
|
|
#include "pnNetCommon/plNetApp.h"
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// LOCAL FORWARD DECLs
|
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
bool IEnterGenericMode(const char *enterAnim, const char *idleAnim, const char *exitAnim, bool autoExit, plAGAnim::BodyUsage bodyUsage,
|
|
|
|
plAvBrainGeneric::BrainType = plAvBrainGeneric::kGeneric);
|
|
|
|
bool IExitTopmostGenericMode();
|
|
|
|
|
|
|
|
|
|
|
|
cyAvatar::cyAvatar(plKey sender, plKey recvr)
|
|
|
|
{
|
|
|
|
SetSender(sender);
|
|
|
|
AddRecvr(recvr);
|
|
|
|
fNetForce = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// setters
|
|
|
|
void cyAvatar::SetSender(plKey &sender)
|
|
|
|
{
|
|
|
|
fSender = sender;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cyAvatar::AddRecvr(plKey &recvr)
|
|
|
|
{
|
|
|
|
if ( recvr != nil )
|
|
|
|
fRecvr.Append(recvr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cyAvatar::SetSenderKey(pyKey& pKey)
|
|
|
|
{
|
|
|
|
plKey k = pKey.getKey();
|
|
|
|
SetSender(k);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : IFindArmatureModKey
|
|
|
|
// PARAMETERS : avObj - avatar sceneobject
|
|
|
|
//
|
|
|
|
// PURPOSE : find the armature mod for this sceneoabject (if its an avatar)
|
|
|
|
//
|
|
|
|
const plArmatureMod* cyAvatar::IFindArmatureMod(plKey avKey)
|
|
|
|
{
|
|
|
|
plSceneObject* avObj = plSceneObject::ConvertNoRef(avKey->ObjectIsLoaded());
|
|
|
|
if ( avObj )
|
|
|
|
{
|
|
|
|
// search through its modifiers to see if one of them is an avatar modifier
|
|
|
|
int i;
|
|
|
|
for ( i=0; i<avObj->GetNumModifiers(); i++ )
|
|
|
|
{
|
|
|
|
const plModifier* mod = avObj->GetModifier(i);
|
|
|
|
// see if it is an avatar mod base class
|
|
|
|
const plArmatureMod* avmod = plArmatureMod::ConvertNoRef(mod);
|
|
|
|
if ( avmod )
|
|
|
|
return avmod;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// otherwise we didn't find anything
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : IFindArmatureModKey
|
|
|
|
// PARAMETERS : avObj - avatar sceneobject
|
|
|
|
//
|
|
|
|
// PURPOSE : find the armature mod for this sceneoabject (if its an avatar)
|
|
|
|
//
|
|
|
|
plKey cyAvatar::IFindArmatureModKey(plKey avKey)
|
|
|
|
{
|
|
|
|
const plArmatureMod* avatar = IFindArmatureMod(avKey);
|
|
|
|
if ( avatar )
|
|
|
|
return avatar->GetKey();
|
|
|
|
// otherwise we didn't find anything
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : oneShot
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : oneShot Avatar (must already be there)
|
|
|
|
//
|
|
|
|
void cyAvatar::OneShot(pyKey &seekKey, float duration, bool usePhysics,
|
|
|
|
const plString &animName, bool drivable, bool reversible)
|
|
|
|
{
|
|
|
|
if ( fRecvr.Count() > 0 )
|
|
|
|
{
|
|
|
|
// create message
|
|
|
|
plAvOneShotMsg* pMsg = new plAvOneShotMsg(
|
|
|
|
(plKey )fSender,
|
|
|
|
nil,
|
|
|
|
seekKey.getKey(), // Mark D told me to do it ...paulg
|
|
|
|
duration,
|
|
|
|
usePhysics,
|
|
|
|
animName,
|
|
|
|
drivable,
|
|
|
|
reversible);
|
|
|
|
|
|
|
|
// check if this needs to be network forced to all clients
|
|
|
|
if (fNetForce )
|
|
|
|
{
|
|
|
|
// set the network propagate flag to make sure it gets to the other clients
|
|
|
|
pMsg->SetBCastFlag(plMessage::kNetPropagate);
|
|
|
|
pMsg->SetBCastFlag(plMessage::kNetForce);
|
|
|
|
}
|
|
|
|
// must have a receiver!
|
|
|
|
pMsg->SetBCastFlag(plMessage::kPropagateToModifiers);
|
|
|
|
// add all our receivers to the message receiver list
|
|
|
|
int i;
|
|
|
|
for ( i=0; i<fRecvr.Count(); i++ )
|
|
|
|
{
|
|
|
|
pMsg->AddReceiver(fRecvr[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
plgDispatch::MsgSend( pMsg ); // whoosh... off it goes
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : RunBehavior
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Run Behavior, could be single or multi-stage shot
|
|
|
|
//
|
|
|
|
void cyAvatar::RunBehavior(pyKey &behKey, bool netForce, bool netProp)
|
|
|
|
{
|
|
|
|
// first there is someone to send to and make sure that we an avatar to send this to
|
|
|
|
if ( behKey.getKey() != nil && fRecvr.Count() > 0)
|
|
|
|
{
|
|
|
|
// must determine if the behKey is pointing to Single or Multi Shot behavior
|
|
|
|
if ( plOneShotMod::ConvertNoRef(behKey.getKey()->GetObjectPtr()) != nil )
|
|
|
|
{
|
|
|
|
// create a message OneShotMessage
|
|
|
|
plOneShotMsg* pMsg = new plOneShotMsg;
|
|
|
|
// check if this needs to be network forced to all clients
|
|
|
|
if (netProp)
|
|
|
|
{
|
|
|
|
pMsg->SetBCastFlag(plMessage::kNetPropagate);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pMsg->SetBCastFlag(plMessage::kNetPropagate, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (netForce)
|
|
|
|
{
|
|
|
|
// set the network propagate flag to make sure it gets to the other clients
|
|
|
|
pMsg->SetBCastFlag(plMessage::kNetPropagate);
|
|
|
|
pMsg->SetBCastFlag(plMessage::kNetForce);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pMsg->SetBCastFlag(plMessage::kNetForce, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
pMsg->SetSender(fSender);
|
|
|
|
pMsg->AddReceiver(behKey.getKey());
|
|
|
|
int i;
|
|
|
|
for ( i=0; i<fRecvr.Count(); i++ )
|
|
|
|
{
|
|
|
|
// make sure there is an avatar to set
|
|
|
|
if ( fRecvr[i] != nil )
|
|
|
|
{
|
|
|
|
pMsg->fPlayerKey = (plKey)fRecvr[i];
|
|
|
|
plgDispatch::MsgSend( pMsg ); // send off command for each valid avatar we find
|
|
|
|
// ... really, should only be one... though
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// else if it is a Multistage guy
|
|
|
|
else if ( plMultistageBehMod::ConvertNoRef(behKey.getKey()->GetObjectPtr()) != nil )
|
|
|
|
{
|
|
|
|
// its a multistage thingy... need to send it a plNotifyMsg
|
|
|
|
// create new notify message to do the actual send with
|
|
|
|
plNotifyMsg* pNMsg = new plNotifyMsg;
|
|
|
|
|
|
|
|
// set whether this should be forced over the network (ignoring net-cascading)
|
|
|
|
if (netProp)
|
|
|
|
{
|
|
|
|
pNMsg->SetBCastFlag(plMessage::kNetPropagate);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pNMsg->SetBCastFlag(plMessage::kNetPropagate, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( netForce )
|
|
|
|
{
|
|
|
|
pNMsg->SetBCastFlag(plMessage::kNetPropagate);
|
|
|
|
pNMsg->SetBCastFlag(plMessage::kNetForce);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pNMsg->SetBCastFlag(plMessage::kNetForce, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// copy data and event records to new NotifyMsg
|
|
|
|
pNMsg->fState = 1.0;
|
|
|
|
// need to recreate all the events in the new message by Adding them
|
|
|
|
if ( fRecvr.Count() > 0 && fRecvr[0] != nil )
|
|
|
|
{
|
|
|
|
pNMsg->AddPickEvent( (plKey)fRecvr[0], nil, true, hsPoint3(0,0,0) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// add receivers
|
|
|
|
// loop though adding the ones that want to be notified of the change
|
|
|
|
pNMsg->AddReceiver(behKey.getKey());
|
|
|
|
pNMsg->SetSender(fSender);
|
|
|
|
plgDispatch::MsgSend( pNMsg );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : RunBehaviorAndReply
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Run Behavior, multistage only, reply to specified key'd object
|
|
|
|
//
|
|
|
|
void cyAvatar::RunBehaviorAndReply(pyKey& behKey, pyKey& replyKey, bool netForce, bool netProp)
|
|
|
|
{
|
|
|
|
plMultistageBehMod* pMod = plMultistageBehMod::ConvertNoRef(behKey.getKey()->GetObjectPtr());
|
|
|
|
if ( pMod )
|
|
|
|
{
|
|
|
|
// its a multistage thingy... need to send it a plNotifyMsg
|
|
|
|
// create new notify message to do the actual send with
|
|
|
|
plNotifyMsg* pNMsg = new plNotifyMsg;
|
|
|
|
|
|
|
|
// set whether this should be forced over the network (ignoring net-cascading)
|
|
|
|
if (netProp)
|
|
|
|
{
|
|
|
|
pNMsg->SetBCastFlag(plMessage::kNetPropagate);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pNMsg->SetBCastFlag(plMessage::kNetPropagate, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (netForce)
|
|
|
|
{
|
|
|
|
// set the network propagate flag to make sure it gets to the other clients
|
|
|
|
pNMsg->SetBCastFlag(plMessage::kNetPropagate);
|
|
|
|
pNMsg->SetBCastFlag(plMessage::kNetForce);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pNMsg->SetBCastFlag(plMessage::kNetForce, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// copy data and event records to new NotifyMsg
|
|
|
|
pNMsg->fState = 1.0;
|
|
|
|
// need to recreate all the events in the new message by Adding them
|
|
|
|
if ( fRecvr.Count() > 0 && fRecvr[0] != nil )
|
|
|
|
{
|
|
|
|
pNMsg->AddPickEvent( (plKey)fRecvr[0], nil, true, hsPoint3(0,0,0) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// add receivers
|
|
|
|
// loop though adding the ones that want to be notified of the change
|
|
|
|
pNMsg->AddReceiver(behKey.getKey());
|
|
|
|
pNMsg->SetSender(replyKey.getKey());
|
|
|
|
plgDispatch::MsgSend( pNMsg );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : NextStage
|
|
|
|
// PARAMETERS : behKey - behavior pyKey
|
|
|
|
// : transTime - the transition time to the next stage
|
|
|
|
// (optional): rewind - whether to rewind to the front of the next stage
|
|
|
|
//
|
|
|
|
// PURPOSE : Go to the next stage in a multi-stage behavior
|
|
|
|
//
|
|
|
|
// NOTE: only works with multi-stage behaviors
|
|
|
|
//
|
|
|
|
void cyAvatar::NextStage(pyKey &behKey, float transTime, bool setTime, float newTime,
|
|
|
|
bool setDirection, bool isForward, bool netForce)
|
|
|
|
{
|
|
|
|
// first there is someone to send to and make sure that we an avatar to send this to
|
|
|
|
if ( behKey.getKey() != nil && fRecvr.Count() > 0)
|
|
|
|
{
|
|
|
|
// if it is a Multistage guy
|
|
|
|
if ( plMultistageBehMod::ConvertNoRef(behKey.getKey()->GetObjectPtr()) != nil )
|
|
|
|
{
|
|
|
|
plKey avKey = IFindArmatureModKey( (plKey)fRecvr[0] );
|
|
|
|
if ( avKey )
|
|
|
|
{
|
|
|
|
// create the message
|
|
|
|
plAvBrainGenericMsg* pMsg = new plAvBrainGenericMsg((plKey)fSender, avKey,
|
|
|
|
plAvBrainGenericMsg::kNextStage, 0, setTime, newTime,
|
|
|
|
setDirection, isForward, transTime);
|
|
|
|
|
|
|
|
if ( netForce )
|
|
|
|
pMsg->SetBCastFlag(plMessage::kNetForce | plMessage::kNetPropagate);
|
|
|
|
|
|
|
|
plgDispatch::MsgSend( pMsg );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : PreviousStage
|
|
|
|
// PARAMETERS : behKey - behavior pyKey
|
|
|
|
// : transTime - the transition time to the next stage
|
|
|
|
// (optional): rewind - whether to rewind to the front of the next stage
|
|
|
|
//
|
|
|
|
// PURPOSE : Go to the previous stage in a multi-stage behavior
|
|
|
|
//
|
|
|
|
// NOTE: only works with multi-stage behaviors
|
|
|
|
//
|
|
|
|
void cyAvatar::PreviousStage(pyKey &behKey, float transTime, bool setTime, float newTime,
|
|
|
|
bool setDirection, bool isForward, bool netForce)
|
|
|
|
{
|
|
|
|
// first there is someone to send to and make sure that we an avatar to send this to
|
|
|
|
if ( behKey.getKey() != nil && fRecvr.Count() > 0)
|
|
|
|
{
|
|
|
|
// if it is a Multistage guy
|
|
|
|
if ( plMultistageBehMod::ConvertNoRef(behKey.getKey()->GetObjectPtr()) != nil )
|
|
|
|
{
|
|
|
|
plKey avKey = IFindArmatureModKey( (plKey)fRecvr[0] );
|
|
|
|
if ( avKey )
|
|
|
|
{
|
|
|
|
// create the message
|
|
|
|
plAvBrainGenericMsg* pMsg = new plAvBrainGenericMsg((plKey)fSender, avKey,
|
|
|
|
plAvBrainGenericMsg::kPrevStage, 0, setTime, newTime,
|
|
|
|
setDirection, isForward, transTime);
|
|
|
|
|
|
|
|
if ( netForce )
|
|
|
|
pMsg->SetBCastFlag(plMessage::kNetForce | plMessage::kNetPropagate);
|
|
|
|
|
|
|
|
plgDispatch::MsgSend( pMsg );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : GotoStage
|
|
|
|
// PARAMETERS : behKey - behavior pyKey
|
|
|
|
// : stage - stage number to go to
|
|
|
|
// : transTime - the transition time to the next stage
|
|
|
|
// (optional): rewind - whether to rewind to the front of the next stage
|
|
|
|
//
|
|
|
|
// PURPOSE : Go to a particular stage in a multi-stage behavior
|
|
|
|
//
|
|
|
|
// NOTE: only works with multi-stage behaviors
|
|
|
|
//
|
|
|
|
void cyAvatar::GoToStage(pyKey &behKey, int32_t stage, float transTime, bool setTime, float newTime,
|
|
|
|
bool setDirection, bool isForward, bool netForce)
|
|
|
|
{
|
|
|
|
// first there is someone to send to and make sure that we an avatar to send this to
|
|
|
|
if ( behKey.getKey() != nil && fRecvr.Count() > 0)
|
|
|
|
{
|
|
|
|
// if it is a Multistage guy
|
|
|
|
if ( plMultistageBehMod::ConvertNoRef(behKey.getKey()->GetObjectPtr()) != nil )
|
|
|
|
{
|
|
|
|
plKey avKey = IFindArmatureModKey( (plKey)fRecvr[0] );
|
|
|
|
if ( avKey )
|
|
|
|
{
|
|
|
|
// create the message
|
|
|
|
plAvBrainGenericMsg* pMsg = new plAvBrainGenericMsg((plKey)fSender, avKey,
|
|
|
|
plAvBrainGenericMsg::kGotoStage, stage, setTime, newTime,
|
|
|
|
setDirection, isForward, transTime);
|
|
|
|
|
|
|
|
if ( netForce )
|
|
|
|
pMsg->SetBCastFlag(plMessage::kNetForce | plMessage::kNetPropagate);
|
|
|
|
|
|
|
|
plgDispatch::MsgSend( pMsg );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cyAvatar::SetLoopCount(pyKey &behKey, int32_t stage, int32_t loopCount, bool netForce)
|
|
|
|
{
|
|
|
|
// if it is a Multistage guy
|
|
|
|
if ( plMultistageBehMod::ConvertNoRef(behKey.getKey()->GetObjectPtr()) != nil )
|
|
|
|
{
|
|
|
|
plMultistageModMsg* pMsg = new plMultistageModMsg((plKey)nil, behKey.getKey());
|
|
|
|
pMsg->SetCommand(plMultistageModMsg::kSetLoopCount);
|
|
|
|
pMsg->fStageNum = (uint8_t)stage;
|
|
|
|
pMsg->fNumLoops = (uint8_t)loopCount;
|
|
|
|
|
|
|
|
if ( netForce )
|
|
|
|
pMsg->SetBCastFlag(plMessage::kNetForce | plMessage::kNetPropagate);
|
|
|
|
|
|
|
|
plgDispatch::MsgSend( pMsg );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : seek
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : seek Avatar (must already be there)
|
|
|
|
//
|
|
|
|
|
|
|
|
/* Unsupported. Ask Bob if you want it back.
|
|
|
|
|
|
|
|
void cyAvatar::Seek(pyKey &seekKey, float duration, bool usePhysics)
|
|
|
|
{
|
|
|
|
// must have a receiver!
|
|
|
|
if ( fRecvr.Count() > 0 )
|
|
|
|
{
|
|
|
|
// create message
|
|
|
|
plAvSeekMsg* pMsg = new plAvSeekMsg(
|
|
|
|
(plKey)fSender,nil, seekKey.getKey(),duration,usePhysics);
|
|
|
|
|
|
|
|
// check if this needs to be network forced to all clients
|
|
|
|
if (fNetForce )
|
|
|
|
{
|
|
|
|
// set the network propagate flag to make sure it gets to the other clients
|
|
|
|
pMsg->SetBCastFlag(plMessage::kNetPropagate);
|
|
|
|
pMsg->SetBCastFlag(plMessage::kNetForce);
|
|
|
|
}
|
|
|
|
pMsg->SetBCastFlag(plMessage::kPropagateToModifiers);
|
|
|
|
// add all our receivers to the message receiver list
|
|
|
|
int i;
|
|
|
|
for ( i=0; i<fRecvr.Count(); i++ )
|
|
|
|
{
|
|
|
|
pMsg->AddReceiver(fRecvr[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
plgDispatch::MsgSend( pMsg ); // whoosh... off it goes
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : GetAvatarClothingGroup
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Return what clothing group the avatar is in
|
|
|
|
//
|
|
|
|
int32_t cyAvatar::GetAvatarClothingGroup()
|
|
|
|
{
|
|
|
|
// find the avatar's armature modifier
|
|
|
|
const plArmatureMod *avMod = nil;
|
|
|
|
|
|
|
|
// we can really only talk to one avatar, so just get the first one (which is probably the only one)
|
|
|
|
if ( fRecvr.Count() > 0 && fRecvr[0] != nil )
|
|
|
|
{
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr());
|
|
|
|
if (so != nil)
|
|
|
|
{
|
|
|
|
avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index());
|
|
|
|
if ( avMod )
|
|
|
|
{
|
|
|
|
return avMod->GetClothingOutfit()->fGroup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : GetClosetClothingList
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Return a list of the wearable items for this avatar of that clothing_type
|
|
|
|
//
|
|
|
|
std::vector<std::string> cyAvatar::GetEntireClothingList(int32_t clothing_type)
|
|
|
|
{
|
|
|
|
// Currently, just all the clothing available will be returned
|
|
|
|
hsTArray<plClothingItem*> clothingList = plClothingMgr::GetClothingMgr()->GetItemList();
|
|
|
|
int numItems = clothingList.GetCount();
|
|
|
|
|
|
|
|
// create the string list to send to python...
|
|
|
|
std::vector<std::string> retVal;
|
|
|
|
for (int i = 0; i < numItems; i++)
|
|
|
|
retVal.push_back(clothingList[i]->GetName());
|
|
|
|
|
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : GetClosetClothingList
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Return a list of the wearable items for this avatar of that clothing_type
|
|
|
|
//
|
|
|
|
std::vector<PyObject*> cyAvatar::GetClosetClothingList(int32_t clothing_type)
|
|
|
|
{
|
|
|
|
std::vector<PyObject*> retVal;
|
|
|
|
|
|
|
|
// find the avatar's armature modifier
|
|
|
|
const plArmatureMod *avMod = nil;
|
|
|
|
|
|
|
|
// we can really only talk to one avatar, so just get the first one (which is probably the only one)
|
|
|
|
if ( fRecvr.Count() > 0 && fRecvr[0] != nil )
|
|
|
|
{
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr());
|
|
|
|
if (so != nil)
|
|
|
|
{
|
|
|
|
avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index());
|
|
|
|
if ( avMod )
|
|
|
|
{
|
|
|
|
// Get all the clothes that we can wear
|
|
|
|
hsTArray<plClothingItem*> clothingList;
|
|
|
|
plClothingMgr::GetClothingMgr()->GetItemsByGroup(avMod->GetClothingOutfit()->fGroup, clothingList);
|
|
|
|
int numItems = clothingList.GetCount();
|
|
|
|
// create the string list to send to python... as a python object
|
|
|
|
int i;
|
|
|
|
for ( i=0; i<numItems; i++ )
|
|
|
|
{
|
|
|
|
plClothingItem* item = clothingList[i];
|
|
|
|
if ( clothing_type == -1 || item->fType == clothing_type )
|
|
|
|
{
|
|
|
|
// add this event record to the main event list (lists within a list)
|
|
|
|
// create list
|
|
|
|
PyObject* clothingItem = PyList_New(5);
|
|
|
|
|
|
|
|
// [0] = clothing name
|
|
|
|
PyList_SetItem(clothingItem, 0, PyString_FromString(item->GetName()));
|
|
|
|
|
|
|
|
// [1] = clothing type
|
|
|
|
PyList_SetItem(clothingItem, 1, PyInt_FromLong(item->fType));
|
|
|
|
|
|
|
|
// [2] = description
|
|
|
|
const char* description = ""; // assume an empty string
|
|
|
|
if ( item->fDescription != nil )
|
|
|
|
description = item->fDescription;
|
|
|
|
PyList_SetItem(clothingItem, 2, PyString_FromString(description));
|
|
|
|
|
|
|
|
// [3] = ptImage of icon
|
|
|
|
if ( item->fThumbnail != nil )
|
|
|
|
// create a ptImage
|
|
|
|
PyList_SetItem(clothingItem, 3, pyImage::New(item->fThumbnail->GetKey()));
|
|
|
|
else
|
|
|
|
PyList_SetItem(clothingItem, 3, PyInt_FromLong(0));
|
|
|
|
|
|
|
|
// [4] = fCustomText
|
|
|
|
const char* custom = ""; // assume an empty string
|
|
|
|
if ( item->fCustomText != nil )
|
|
|
|
custom = item->fCustomText;
|
|
|
|
PyList_SetItem(clothingItem, 4, PyString_FromString(custom));
|
|
|
|
|
|
|
|
retVal.push_back(clothingItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : GetAvatarClothingList
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Return a list of the wearable items for this avatar of that clothing_type
|
|
|
|
//
|
|
|
|
std::vector<PyObject*> cyAvatar::GetAvatarClothingList()
|
|
|
|
{
|
|
|
|
std::vector<PyObject*> retVal;
|
|
|
|
// find the avatar's armature modifier
|
|
|
|
const plArmatureMod *avMod = nil;
|
|
|
|
// we can really only talk to one avatar, so just get the first one (which is probably the only one)
|
|
|
|
if ( fRecvr.Count() > 0 && fRecvr[0] != nil )
|
|
|
|
{
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr());
|
|
|
|
if (so != nil)
|
|
|
|
{
|
|
|
|
avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index());
|
|
|
|
if ( avMod )
|
|
|
|
{
|
|
|
|
// Currently, just all the clothing available will be returned
|
|
|
|
hsTArray<plClothingItem*> clothingList = avMod->GetClothingOutfit()->GetItemList();
|
|
|
|
int numItems = clothingList.GetCount();
|
|
|
|
// create the string list to send to python... as a python object
|
|
|
|
int i;
|
|
|
|
for ( i=0; i<numItems; i++ )
|
|
|
|
{
|
|
|
|
// add this event record to the main event list (lists within a list)
|
|
|
|
// create list
|
|
|
|
PyObject* clothingItem = PyList_New(5);
|
|
|
|
plClothingItem* item = clothingList[i];
|
|
|
|
|
|
|
|
// [0] = clothing name
|
|
|
|
PyList_SetItem(clothingItem, 0, PyString_FromString(item->GetName()));
|
|
|
|
|
|
|
|
// [1] = clothing type
|
|
|
|
PyList_SetItem(clothingItem, 1, PyInt_FromLong(item->fType));
|
|
|
|
|
|
|
|
// [2] = description
|
|
|
|
const char* description = ""; // assume an empty string
|
|
|
|
if ( item->fDescription != nil )
|
|
|
|
description = item->fDescription;
|
|
|
|
PyList_SetItem(clothingItem, 2, PyString_FromString(description));
|
|
|
|
|
|
|
|
// [3] = ptImage of icon
|
|
|
|
if ( item->fThumbnail != nil )
|
|
|
|
// create a ptImage
|
|
|
|
PyList_SetItem(clothingItem, 3, pyImage::New(item->fThumbnail->GetKey()));
|
|
|
|
else
|
|
|
|
PyList_SetItem(clothingItem, 3, PyInt_FromLong(0));
|
|
|
|
|
|
|
|
// [4] = fCustomText
|
|
|
|
const char* custom = ""; // assume an empty string
|
|
|
|
if ( item->fCustomText != nil )
|
|
|
|
custom = item->fCustomText;
|
|
|
|
PyList_SetItem(clothingItem, 4, PyString_FromString(custom));
|
|
|
|
|
|
|
|
retVal.push_back(clothingItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : GetWardrobeClothingList
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Return a list of items that are in the avatars closet
|
|
|
|
//
|
|
|
|
std::vector<PyObject*> cyAvatar::GetWardrobeClothingList()
|
|
|
|
{
|
|
|
|
std::vector<PyObject*> retVal;
|
|
|
|
hsTArray<plClosetItem> closetList;
|
|
|
|
plClothingMgr::GetClothingMgr()->GetClosetItems(closetList);
|
|
|
|
int numItems = closetList.GetCount();
|
|
|
|
// create the string list to send to python... as a python object
|
|
|
|
int i;
|
|
|
|
for ( i=0; i<numItems; i++ )
|
|
|
|
{
|
|
|
|
// add this event record to the main event list (lists within a list)
|
|
|
|
// create list
|
|
|
|
PyObject* closetItem = PyList_New(7);
|
|
|
|
|
|
|
|
// [0] = clothing name
|
|
|
|
PyList_SetItem(closetItem, 0, PyString_FromString(closetList[i].fItem->GetName()));
|
|
|
|
|
|
|
|
// [1] = clothing type
|
|
|
|
PyList_SetItem(closetItem, 1, PyInt_FromLong(closetList[i].fItem->fType));
|
|
|
|
|
|
|
|
// [2] = description
|
|
|
|
const char* description = ""; // assume an empty string
|
|
|
|
if ( closetList[i].fItem->fDescription != nil )
|
|
|
|
description = closetList[i].fItem->fDescription;
|
|
|
|
PyList_SetItem(closetItem, 2, PyString_FromString(description));
|
|
|
|
|
|
|
|
// [3] = ptImage of icon
|
|
|
|
if ( closetList[i].fItem->fThumbnail != nil )
|
|
|
|
// create a ptImage
|
|
|
|
PyList_SetItem(closetItem, 3, pyImage::New(closetList[i].fItem->fThumbnail->GetKey()));
|
|
|
|
else
|
|
|
|
PyList_SetItem(closetItem, 3, PyInt_FromLong(0));
|
|
|
|
|
|
|
|
// [4] = fCustomText
|
|
|
|
const char* custom = ""; // assume an empty string
|
|
|
|
if ( closetList[i].fItem->fCustomText != nil )
|
|
|
|
custom = closetList[i].fItem->fCustomText;
|
|
|
|
PyList_SetItem(closetItem, 4, PyString_FromString(custom));
|
|
|
|
|
|
|
|
// [5] = fTint1
|
|
|
|
PyList_SetItem(closetItem, 5, pyColor::New(closetList[i].fOptions.fTint1));
|
|
|
|
|
|
|
|
// [6] = fTint2
|
|
|
|
PyList_SetItem(closetItem, 6, pyColor::New(closetList[i].fOptions.fTint2));
|
|
|
|
|
|
|
|
retVal.push_back(closetItem);
|
|
|
|
}
|
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : AddWardrobeClothingItem
|
|
|
|
// PARAMETERS : clothing_name - the name of the clothing item to add to your wardrobe
|
|
|
|
// : tint1 - layer one color
|
|
|
|
// : tint2 - layer two color
|
|
|
|
//
|
|
|
|
// PURPOSE : To add a clothing item to the avatar's wardrobe (closet)
|
|
|
|
//
|
|
|
|
void cyAvatar::AddWardrobeClothingItem(const char* clothing_name,pyColor& tint1,pyColor& tint2)
|
|
|
|
{
|
|
|
|
plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name);
|
|
|
|
if ( item )
|
|
|
|
{
|
|
|
|
hsTArray<plClosetItem> items;
|
|
|
|
items.SetCount(1);
|
|
|
|
items[0].fItem = item;
|
|
|
|
items[0].fOptions.fTint1.Set(tint1.getRed(), tint1.getGreen(), tint1.getBlue(), 1.f);
|
|
|
|
items[0].fOptions.fTint2.Set(tint2.getRed(), tint2.getGreen(), tint2.getBlue(), 1.f);
|
|
|
|
|
|
|
|
plClothingMgr::GetClothingMgr()->AddItemsToCloset(items);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : GetUniqueMeshList
|
|
|
|
// PARAMETERS : clothing_type - the type of clothing to get
|
|
|
|
//
|
|
|
|
// PURPOSE : Return a list of unique clothing items (each has a different mesh)
|
|
|
|
// : that belong to the specific type
|
|
|
|
//
|
|
|
|
std::vector<PyObject*> cyAvatar::GetUniqueMeshList(int32_t clothing_type)
|
|
|
|
{
|
|
|
|
std::vector<PyObject*> retVal;
|
|
|
|
|
|
|
|
// find the avatar's armature modifier
|
|
|
|
const plArmatureMod *avMod = nil;
|
|
|
|
|
|
|
|
// we can really only talk to one avatar, so just get the first one (which is probably the only one)
|
|
|
|
if ( fRecvr.Count() > 0 && fRecvr[0] != nil )
|
|
|
|
{
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr());
|
|
|
|
if (so != nil)
|
|
|
|
{
|
|
|
|
avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index());
|
|
|
|
if ( avMod )
|
|
|
|
{
|
|
|
|
// Get all the clothes that we can wear
|
|
|
|
hsTArray<plClothingItem*> clothingList;
|
|
|
|
plClothingMgr::GetClothingMgr()->GetItemsByGroup(avMod->GetClothingOutfit()->fGroup, clothingList);
|
|
|
|
plClothingMgr::GetClothingMgr()->FilterUniqueMeshes(clothingList); // filter all redundant meshes
|
|
|
|
int numItems = clothingList.GetCount();
|
|
|
|
// create the string list to send to python... as a python object
|
|
|
|
int i;
|
|
|
|
for ( i=0; i<numItems; i++ )
|
|
|
|
{
|
|
|
|
plClothingItem* item = clothingList[i];
|
|
|
|
if ( clothing_type == -1 || item->fType == clothing_type )
|
|
|
|
{
|
|
|
|
// add this event record to the main event list (lists within a list)
|
|
|
|
// create list
|
|
|
|
PyObject* clothingItem = PyList_New(5);
|
|
|
|
|
|
|
|
// [0] = clothing name
|
|
|
|
PyList_SetItem(clothingItem, 0, PyString_FromString(item->GetName()));
|
|
|
|
|
|
|
|
// [1] = clothing type
|
|
|
|
PyList_SetItem(clothingItem, 1, PyInt_FromLong(item->fType));
|
|
|
|
|
|
|
|
// [2] = description
|
|
|
|
const char* description = ""; // assume an empty string
|
|
|
|
if ( item->fDescription != nil )
|
|
|
|
description = item->fDescription;
|
|
|
|
PyList_SetItem(clothingItem, 2, PyString_FromString(description));
|
|
|
|
|
|
|
|
// [3] = ptImage of icon
|
|
|
|
if ( item->fThumbnail != nil )
|
|
|
|
// create a ptImage
|
|
|
|
PyList_SetItem(clothingItem, 3, pyImage::New(item->fThumbnail->GetKey()));
|
|
|
|
else
|
|
|
|
PyList_SetItem(clothingItem, 3, PyInt_FromLong(0));
|
|
|
|
|
|
|
|
// [4] = fCustomText
|
|
|
|
const char* custom = ""; // assume an empty string
|
|
|
|
if ( item->fCustomText != nil )
|
|
|
|
custom = item->fCustomText;
|
|
|
|
PyList_SetItem(clothingItem, 4, PyString_FromString(custom));
|
|
|
|
|
|
|
|
retVal.push_back(clothingItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : GetAllWithSameMesh
|
|
|
|
// PARAMETERS : clothing_name - the name of the mesh to get the textures of
|
|
|
|
//
|
|
|
|
// PURPOSE : Return a list of clothing items that have the same mesh as
|
|
|
|
// : the item passed in
|
|
|
|
//
|
|
|
|
std::vector<PyObject*> cyAvatar::GetAllWithSameMesh(const char* clothing_name)
|
|
|
|
{
|
|
|
|
std::vector<PyObject*> retVal;
|
|
|
|
|
|
|
|
// find the avatar's armature modifier
|
|
|
|
const plArmatureMod *avMod = nil;
|
|
|
|
|
|
|
|
// we can really only talk to one avatar, so just get the first one (which is probably the only one)
|
|
|
|
if ( fRecvr.Count() > 0 && fRecvr[0] != nil )
|
|
|
|
{
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr());
|
|
|
|
if (so != nil)
|
|
|
|
{
|
|
|
|
avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index());
|
|
|
|
if ( avMod )
|
|
|
|
{
|
|
|
|
// Get all clothes with the same mesh as the one passed in
|
|
|
|
hsTArray<plClothingItem*> clothingList;
|
|
|
|
plClothingMgr::GetClothingMgr()->GetAllWithSameMesh(plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name), clothingList);
|
|
|
|
int numItems = clothingList.GetCount();
|
|
|
|
// create the string list to send to python... as a python object
|
|
|
|
int i;
|
|
|
|
for ( i=0; i<numItems; i++ )
|
|
|
|
{
|
|
|
|
// add this event record to the main event list (lists within a list)
|
|
|
|
// create list
|
|
|
|
PyObject* clothingItem = PyList_New(5);
|
|
|
|
plClothingItem* item = clothingList[i];
|
|
|
|
|
|
|
|
// [0] = clothing name
|
|
|
|
PyList_SetItem(clothingItem, 0, PyString_FromString(item->GetName()));
|
|
|
|
|
|
|
|
// [1] = clothing type
|
|
|
|
PyList_SetItem(clothingItem, 1, PyInt_FromLong(item->fType));
|
|
|
|
|
|
|
|
// [2] = description
|
|
|
|
const char* description = ""; // assume an empty string
|
|
|
|
if ( item->fDescription != nil )
|
|
|
|
description = item->fDescription;
|
|
|
|
PyList_SetItem(clothingItem, 2, PyString_FromString(description));
|
|
|
|
|
|
|
|
// [3] = ptImage of icon
|
|
|
|
if ( item->fThumbnail != nil )
|
|
|
|
// create a ptImage
|
|
|
|
PyList_SetItem(clothingItem, 3, pyImage::New(item->fThumbnail->GetKey()));
|
|
|
|
else
|
|
|
|
PyList_SetItem(clothingItem, 3, PyInt_FromLong(0));
|
|
|
|
|
|
|
|
// [4] = fCustomText
|
|
|
|
const char* custom = ""; // assume an empty string
|
|
|
|
if ( item->fCustomText != nil )
|
|
|
|
custom = item->fCustomText;
|
|
|
|
PyList_SetItem(clothingItem, 4, PyString_FromString(custom));
|
|
|
|
|
|
|
|
retVal.push_back(clothingItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : GetMatchingClothingItem
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Return the clothing item that matches this one
|
|
|
|
// : If no match then returns the number 0
|
|
|
|
//
|
|
|
|
PyObject* cyAvatar::GetMatchingClothingItem(const char* clothing_name)
|
|
|
|
{
|
|
|
|
// Get all the clothes that we can wear
|
|
|
|
hsTArray<plClothingItem*> clothingList;
|
|
|
|
plClothingItem* match = plClothingMgr::GetClothingMgr()->GetLRMatch(plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name));
|
|
|
|
if ( match )
|
|
|
|
{
|
|
|
|
// create list
|
|
|
|
PyObject* clothingItem = PyList_New(5);
|
|
|
|
|
|
|
|
// [0] = clothing name
|
|
|
|
PyList_SetItem(clothingItem, 0, PyString_FromString(match->GetName()));
|
|
|
|
|
|
|
|
// [1] = clothing type
|
|
|
|
PyList_SetItem(clothingItem, 1, PyInt_FromLong(match->fType));
|
|
|
|
|
|
|
|
// [2] = description
|
|
|
|
const char* description = ""; // assume an empty string
|
|
|
|
if ( match->fDescription != nil )
|
|
|
|
description = match->fDescription;
|
|
|
|
PyList_SetItem(clothingItem, 2, PyString_FromString(description));
|
|
|
|
|
|
|
|
// [3] = ptImage of icon
|
|
|
|
if ( match->fThumbnail != nil )
|
|
|
|
// create a ptImage
|
|
|
|
PyList_SetItem(clothingItem, 3, pyImage::New(match->fThumbnail->GetKey()));
|
|
|
|
else
|
|
|
|
PyList_SetItem(clothingItem, 3, PyInt_FromLong(0));
|
|
|
|
|
|
|
|
// [4] = fCustomText
|
|
|
|
const char* custom = ""; // assume an empty string
|
|
|
|
if ( match->fCustomText != nil )
|
|
|
|
custom = match->fCustomText;
|
|
|
|
PyList_SetItem(clothingItem, 4, PyString_FromString(custom));
|
|
|
|
|
|
|
|
return clothingItem;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return PyInt_FromLong(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : WearClothingItem
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Wear a particular piece of clothing based on name of clothing item
|
|
|
|
// : returns 0, if clothing item was not found
|
|
|
|
//
|
|
|
|
bool cyAvatar::WearClothingItem(const char* clothing_name)
|
|
|
|
{
|
|
|
|
return WearClothingItemU(clothing_name,true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : RemoveClothingItem
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Wear a particular piece of clothing based on name of clothing item
|
|
|
|
// : returns false, if clothing item was not found
|
|
|
|
//
|
|
|
|
bool cyAvatar::RemoveClothingItem(const char* clothing_name)
|
|
|
|
{
|
|
|
|
return RemoveClothingItemU(clothing_name,true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : TintClothingItem
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Tint a clothing item, i.e. change the color of it
|
|
|
|
//
|
|
|
|
bool cyAvatar::TintClothingItem(const char* clothing_name, pyColor& tint)
|
|
|
|
{
|
|
|
|
return TintClothingItemU(clothing_name,tint,true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : TintClothingItemLayer
|
|
|
|
// PARAMETERS : clothing_name - name of the clothing item to change the color of
|
|
|
|
// : tint - what color to change it to
|
|
|
|
// : layer - which layer to change (1 or 2)
|
|
|
|
//
|
|
|
|
// PURPOSE : Tint a clothing item, i.e. change the color of it
|
|
|
|
//
|
|
|
|
bool cyAvatar::TintClothingItemLayer(const char* clothing_name, pyColor& tint, uint8_t layer)
|
|
|
|
{
|
|
|
|
return TintClothingItemLayerU(clothing_name,tint,layer,true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : WearClothingItem
|
|
|
|
// PARAMETERS : --- with update flag
|
|
|
|
//
|
|
|
|
// PURPOSE : Wear a particular piece of clothing based on name of clothing item
|
|
|
|
// : returns 0, if clothing item was not found
|
|
|
|
//
|
|
|
|
bool cyAvatar::WearClothingItemU(const char* clothing_name, bool update)
|
|
|
|
{
|
|
|
|
const plArmatureMod *avMod = nil;
|
|
|
|
// we can really only talk to one avatar, so just get the first one (which is probably the only one)
|
|
|
|
if ( fRecvr.Count() > 0 && fRecvr[0] != nil )
|
|
|
|
{
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr());
|
|
|
|
if (so != nil)
|
|
|
|
{
|
|
|
|
avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index());
|
|
|
|
plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name);
|
|
|
|
|
|
|
|
if (avMod && item)
|
|
|
|
{
|
|
|
|
if ( fNetForce )
|
|
|
|
avMod->GetClothingOutfit()->AddItem(item, update, true, true);
|
|
|
|
else
|
|
|
|
avMod->GetClothingOutfit()->AddItem(item, update);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : RemoveClothingItemU
|
|
|
|
// PARAMETERS : --- with update flag
|
|
|
|
//
|
|
|
|
// PURPOSE : Wear a particular piece of clothing based on name of clothing item
|
|
|
|
// : returns false, if clothing item was not found
|
|
|
|
//
|
|
|
|
bool cyAvatar::RemoveClothingItemU(const char* clothing_name, bool update)
|
|
|
|
{
|
|
|
|
const plArmatureMod *avMod = nil;
|
|
|
|
// we can really only talk to one avatar, so just get the first one (which is probably the only one)
|
|
|
|
if ( fRecvr.Count() > 0 && fRecvr[0] != nil )
|
|
|
|
{
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr());
|
|
|
|
if (so != nil)
|
|
|
|
{
|
|
|
|
avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index());
|
|
|
|
|
|
|
|
plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name);
|
|
|
|
|
|
|
|
if (avMod && item)
|
|
|
|
{
|
|
|
|
if ( fNetForce )
|
|
|
|
avMod->GetClothingOutfit()->RemoveItem(item,update,true);
|
|
|
|
else
|
|
|
|
avMod->GetClothingOutfit()->RemoveItem(item,update);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : TintClothingItemU
|
|
|
|
// PARAMETERS : --- with update flag
|
|
|
|
//
|
|
|
|
// PURPOSE : Tint a clothing item, i.e. change the color of it
|
|
|
|
//
|
|
|
|
bool cyAvatar::TintClothingItemU(const char* clothing_name, pyColor& tint, bool update)
|
|
|
|
{
|
|
|
|
const plArmatureMod *avMod = nil;
|
|
|
|
// we can really only talk to one avatar, so just get the first one (which is probably the only one)
|
|
|
|
if ( fRecvr.Count() > 0 && fRecvr[0] != nil )
|
|
|
|
{
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr());
|
|
|
|
if (so != nil)
|
|
|
|
{
|
|
|
|
avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index());
|
|
|
|
|
|
|
|
plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name);
|
|
|
|
|
|
|
|
if (avMod && item)
|
|
|
|
{
|
|
|
|
avMod->GetClothingOutfit()->TintItem(item, tint.getRed(),tint.getGreen(),tint.getBlue(),update,true,fNetForce,true,plClothingElement::kLayerTint1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : TintClothingItemLayer
|
|
|
|
// PARAMETERS : clothing_name - name of the clothing item to change the color of
|
|
|
|
// : tint - what color to change it to
|
|
|
|
// : layer - which layer to change (1 or 2)
|
|
|
|
//
|
|
|
|
// PURPOSE : Tint a clothing item, i.e. change the color of it
|
|
|
|
//
|
|
|
|
bool cyAvatar::TintClothingItemLayerU(const char* clothing_name, pyColor& tint, uint8_t layer, bool update)
|
|
|
|
{
|
|
|
|
const plArmatureMod *avMod = nil;
|
|
|
|
// we can really only talk to one avatar, so just get the first one (which is probably the only one)
|
|
|
|
if ( fRecvr.Count() > 0 && fRecvr[0] != nil )
|
|
|
|
{
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr());
|
|
|
|
if (so != nil)
|
|
|
|
{
|
|
|
|
avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index());
|
|
|
|
|
|
|
|
plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name);
|
|
|
|
|
|
|
|
if (avMod && item)
|
|
|
|
{
|
|
|
|
// Convert Python layer number to clothing code...
|
|
|
|
if (layer == 2)
|
|
|
|
layer = plClothingElement::kLayerTint2;
|
|
|
|
else
|
|
|
|
layer = plClothingElement::kLayerTint1;
|
|
|
|
avMod->GetClothingOutfit()->TintItem(item, tint.getRed(),tint.getGreen(),tint.getBlue(),update,true,fNetForce,true,layer);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : GetClothingItemParameterString
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Get the custom parameter string for a clothing item
|
|
|
|
//
|
|
|
|
const char* cyAvatar::GetClothingItemParameterString(const char* clothing_name)
|
|
|
|
{
|
|
|
|
const plArmatureMod *avMod = nil;
|
|
|
|
// we can really only talk to one avatar, so just get the first one (which is probably the only one)
|
|
|
|
if ( fRecvr.Count() > 0 && fRecvr[0] != nil )
|
|
|
|
{
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr());
|
|
|
|
if (so != nil)
|
|
|
|
{
|
|
|
|
avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index());
|
|
|
|
|
|
|
|
plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name);
|
|
|
|
|
|
|
|
if (avMod && item)
|
|
|
|
{
|
|
|
|
if ( item->fCustomText != nil )
|
|
|
|
return item->fCustomText;
|
|
|
|
else
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : GetTintClothingItem
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Get the tint a clothing item, i.e. change the color of it
|
|
|
|
//
|
|
|
|
PyObject* cyAvatar::GetTintClothingItem(const char* clothing_name)
|
|
|
|
{
|
|
|
|
return GetTintClothingItemL(clothing_name,1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : GetTintClothingItem
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Get the tint a clothing item, i.e. change the color of it
|
|
|
|
//
|
|
|
|
PyObject* cyAvatar::GetTintClothingItemL(const char* clothing_name, uint8_t layer)
|
|
|
|
{
|
|
|
|
const plArmatureMod *avMod = nil;
|
|
|
|
// we can really only talk to one avatar, so just get the first one (which is probably the only one)
|
|
|
|
if ( fRecvr.Count() > 0 && fRecvr[0] != nil )
|
|
|
|
{
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr());
|
|
|
|
if (so != nil)
|
|
|
|
{
|
|
|
|
avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index());
|
|
|
|
|
|
|
|
plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name);
|
|
|
|
|
|
|
|
if (avMod && item)
|
|
|
|
{
|
|
|
|
// Convert Python layer number to clothing code...
|
|
|
|
if (layer == 2)
|
|
|
|
layer = plClothingElement::kLayerTint2;
|
|
|
|
else
|
|
|
|
layer = plClothingElement::kLayerTint1;
|
|
|
|
hsColorRGBA tint = avMod->GetClothingOutfit()->GetItemTint(item,layer);
|
|
|
|
return pyColor::New(tint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plString errmsg = plString::Format("Cannot find clothing item %s to find out what tint it is", clothing_name);
|
|
|
|
PyErr_SetString(PyExc_KeyError, errmsg.c_str());
|
|
|
|
// returning nil means an error occurred
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : TintSkin
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Tint the skin of the player's avatar
|
|
|
|
//
|
|
|
|
void cyAvatar::TintSkin(pyColor& tint)
|
|
|
|
{
|
|
|
|
TintSkinU(tint,true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : TintSkinU
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Tint the skin of the player's avatar with optional update flag
|
|
|
|
//
|
|
|
|
void cyAvatar::TintSkinU(pyColor& tint, bool update)
|
|
|
|
{
|
|
|
|
const plArmatureMod *avMod = nil;
|
|
|
|
// we can really only talk to one avatar, so just get the first one (which is probably the only one)
|
|
|
|
if ( fRecvr.Count() > 0 && fRecvr[0] != nil )
|
|
|
|
{
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr());
|
|
|
|
if (so != nil)
|
|
|
|
{
|
|
|
|
avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index());
|
|
|
|
avMod->GetClothingOutfit()->TintSkin(tint.getRed(),tint.getGreen(),tint.getBlue(),update,true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : GetTintSkin
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Get the tint of the skin of the player's avatar
|
|
|
|
//
|
|
|
|
PyObject* cyAvatar::GetTintSkin()
|
|
|
|
{
|
|
|
|
const plArmatureMod *avMod = nil;
|
|
|
|
// we can really only talk to one avatar, so just get the first one (which is probably the only one)
|
|
|
|
if ( fRecvr.Count() > 0 && fRecvr[0] != nil )
|
|
|
|
{
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr());
|
|
|
|
if (so != nil)
|
|
|
|
{
|
|
|
|
avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index());
|
|
|
|
hsColorRGBA tint = avMod->GetClothingOutfit()->fSkinTint;
|
|
|
|
// now create the ptColor Python object
|
|
|
|
return pyColor::New(tint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PyErr_SetString(PyExc_KeyError, "Cannot find the skin of the player. Whatever that means!");
|
|
|
|
// returning nil means an error occurred
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
plMorphSequence* cyAvatar::LocalMorphSequence()
|
|
|
|
{
|
|
|
|
plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
|
|
|
if (!avMod)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
|
|
|
|
const plSceneObject *so = avMod->GetClothingSO(0); // grabbing the high LOD node
|
|
|
|
if (!so)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
const plModifier* constSeq = nil;
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < so->GetNumModifiers(); i++)
|
|
|
|
{
|
|
|
|
constSeq = so->GetModifier(i);
|
|
|
|
if (constSeq && plMorphSequence::ConvertNoRef(constSeq))
|
|
|
|
{
|
|
|
|
return (plMorphSequence*)constSeq; // safe cast, we've already checked type (plus we're const_cast'ing).
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : SetMorph
|
|
|
|
// PARAMETERS : clothing_name - the name of the clothing to morph
|
|
|
|
// : layer - the layer to affect
|
|
|
|
// : value - what the new value should be (clipped between -1 and 1)
|
|
|
|
//
|
|
|
|
// PURPOSE : Set the morph value of a specific layer of clothing
|
|
|
|
//
|
|
|
|
void cyAvatar::SetMorph(const char* clothing_name, uint8_t layer, float value)
|
|
|
|
{
|
|
|
|
plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName(clothing_name);
|
|
|
|
if( !item )
|
|
|
|
{
|
|
|
|
PyErr_SetString(PyExc_KeyError, "Item not found");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
float wgtPlus;
|
|
|
|
float wgtMinus;
|
|
|
|
|
|
|
|
if(value > 1.0) value = 1.0;
|
|
|
|
if(value < -1.0) value = -1.0;
|
|
|
|
|
|
|
|
if (value > 0)
|
|
|
|
{
|
|
|
|
wgtPlus = value;
|
|
|
|
wgtMinus = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wgtMinus = -value;
|
|
|
|
wgtPlus = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( fRecvr.Count() > 0 && fRecvr[0] != nil )
|
|
|
|
{
|
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr());
|
|
|
|
if (so != nil)
|
|
|
|
{
|
|
|
|
const plArmatureMod *avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index());
|
|
|
|
if (avMod && avMod->GetClothingOutfit())
|
|
|
|
{
|
|
|
|
avMod->GetClothingOutfit()->MorphItem(item, layer, 0, wgtPlus, true);
|
|
|
|
avMod->GetClothingOutfit()->MorphItem(item, layer, 1, wgtMinus, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : GetMorph
|
|
|
|
// PARAMETERS : clothing_name - the name of the clothing to get the value from
|
|
|
|
// : layer - the layer to get the value from
|
|
|
|
//
|
|
|
|
// PURPOSE : Returns the current morph value of the specific layer of clothing
|
|
|
|
//
|
|
|
|
float cyAvatar::GetMorph(const char* clothing_name, uint8_t layer)
|
|
|
|
{
|
|
|
|
plMorphSequence* seq = LocalMorphSequence();
|
|
|
|
if( !seq )
|
|
|
|
{
|
|
|
|
PyErr_SetString(PyExc_KeyError, "Sequence not found");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName(clothing_name);
|
|
|
|
if( !item )
|
|
|
|
{
|
|
|
|
PyErr_SetString(PyExc_KeyError, "Item not found");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
plKey meshKey = item->fMeshes[0]->GetKey();
|
|
|
|
|
|
|
|
if (layer >= seq->GetNumLayers(meshKey))
|
|
|
|
{
|
|
|
|
PyErr_SetString(PyExc_KeyError, "Layer index too high");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
float wgtPlus;
|
|
|
|
float wgtMinus;
|
|
|
|
|
|
|
|
wgtPlus = seq->GetWeight(layer,0,meshKey);
|
|
|
|
wgtMinus = seq->GetWeight(layer,1,meshKey);
|
|
|
|
|
|
|
|
if (wgtPlus > 0)
|
|
|
|
return wgtPlus;
|
|
|
|
else
|
|
|
|
return -wgtMinus;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : SetSkinBlend
|
|
|
|
// PARAMETERS : layer - the layer to affect
|
|
|
|
// : value - what the new value should be (clipped between 0 and 1)
|
|
|
|
//
|
|
|
|
// PURPOSE : Set the skin blend for the specified layer
|
|
|
|
//
|
|
|
|
void cyAvatar::SetSkinBlend(uint8_t layer, float value)
|
|
|
|
{
|
|
|
|
if (value < 0.0) value = 0.0;
|
|
|
|
if (value > 1.0) value = 1.0;
|
|
|
|
|
|
|
|
plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
|
|
|
|
|
|
|
if (avMod)
|
|
|
|
{
|
|
|
|
avMod->GetClothingOutfit()->SetSkinBlend(value, (int)layer + plClothingElement::kLayerSkinBlend1 - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : GetSkinBlend
|
|
|
|
// PARAMETERS : layer - the layer to get the blend for
|
|
|
|
//
|
|
|
|
// PURPOSE : Returns the current layer's skin blend
|
|
|
|
//
|
|
|
|
float cyAvatar::GetSkinBlend(uint8_t layer)
|
|
|
|
{
|
|
|
|
plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
|
|
|
|
|
|
|
if (avMod)
|
|
|
|
{
|
|
|
|
return avMod->GetClothingOutfit()->GetSkinBlend((int)layer + plClothingElement::kLayerSkinBlend1 - 1);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : SaveClothing
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : Saves the current clothing to the vault (including morphs)
|
|
|
|
//
|
|
|
|
void cyAvatar::SaveClothing()
|
|
|
|
{
|
|
|
|
plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
|
|
|
|
|
|
|
if (avMod)
|
|
|
|
avMod->GetClothingOutfit()->SaveCustomizations();
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : EnterSubWorld
|
|
|
|
// PARAMETERS : object - a sceneobject that is in the subworld
|
|
|
|
//
|
|
|
|
// PURPOSE : Place the Avatar into the subworld of the sceneobject specified
|
|
|
|
//
|
|
|
|
void cyAvatar::EnterSubWorld(pySceneObject& object)
|
|
|
|
{
|
|
|
|
// make sure that there is atleast one avatar scene object attached (should be)
|
|
|
|
if ( fRecvr.Count() > 0)
|
|
|
|
{
|
|
|
|
// find the armature modifier
|
|
|
|
plArmatureMod* avatar = (plArmatureMod*)IFindArmatureMod((plKey)fRecvr[0]);
|
|
|
|
if(avatar)
|
|
|
|
{
|
|
|
|
// get the sceneobject that we will use to find the subworld
|
|
|
|
plKey SOkey = object.getObjKey();
|
|
|
|
if ( SOkey )
|
|
|
|
{
|
|
|
|
plSceneObject *SO = plSceneObject::ConvertNoRef(SOkey->ObjectIsLoaded());
|
|
|
|
if(SO)
|
|
|
|
{
|
|
|
|
plKey subWorldKey = SOkey;
|
|
|
|
plKey physKey = avatar->GetKey();
|
|
|
|
plKey nilKey; // sorry
|
|
|
|
plSubWorldMsg *swMsg = new plSubWorldMsg(nilKey, physKey, subWorldKey);
|
|
|
|
swMsg->Send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : ExitSubWorld
|
|
|
|
// PARAMETERS : (none)
|
|
|
|
//
|
|
|
|
// PURPOSE : Exit the avatar from the subworld, back into the ... <whatever> world
|
|
|
|
//
|
|
|
|
void cyAvatar::ExitSubWorld()
|
|
|
|
{
|
|
|
|
// make sure that there is atleast one avatar scene object attached (should be)
|
|
|
|
if ( fRecvr.Count() > 0)
|
|
|
|
{
|
|
|
|
// find the armature modifier
|
|
|
|
plArmatureMod* avatar = (plArmatureMod*)IFindArmatureMod((plKey)fRecvr[0]);
|
|
|
|
if(avatar)
|
|
|
|
{
|
|
|
|
plKey subWorldKey; // we're going to the nil subworld
|
|
|
|
plKey physKey = avatar->GetKey();
|
|
|
|
plKey nilKey; // sorry
|
|
|
|
plSubWorldMsg *swMsg = new plSubWorldMsg(nilKey, physKey, subWorldKey);
|
|
|
|
swMsg->Send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : PlaySimpleAnimation
|
|
|
|
// PARAMETERS : object - a sceneobject that is in the subworld
|
|
|
|
//
|
|
|
|
// PURPOSE : Place the Avatar into the subworld of the sceneobject specified
|
|
|
|
//
|
|
|
|
void cyAvatar::PlaySimpleAnimation(const plString& animName)
|
|
|
|
{
|
|
|
|
// make sure that there is atleast one avatar scene object attached (should be)
|
|
|
|
if ( fRecvr.Count() > 0)
|
|
|
|
{
|
|
|
|
// find the armature modifier
|
|
|
|
plArmatureMod* avatar = (plArmatureMod*)IFindArmatureMod((plKey)fRecvr[0]);
|
|
|
|
if(avatar)
|
|
|
|
{
|
|
|
|
avatar->PlaySimpleAnim(animName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : ChangeAvatar
|
|
|
|
// PARAMETERS : gender name - is a string of the name of the gender to go to
|
|
|
|
//
|
|
|
|
// PURPOSE : Change the local avatar's gender.
|
|
|
|
//
|
|
|
|
// Valid genders:
|
|
|
|
// Male
|
|
|
|
// Female
|
|
|
|
//
|
|
|
|
void cyAvatar::ChangeAvatar(const char* genderName)
|
|
|
|
{
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
plClothingMgr::ChangeAvatar((char*)genderName);
|
|
|
|
|
|
|
|
wchar_t wStr[MAX_PATH];
|
|
|
|
StrToUnicode(wStr, genderName, arrsize(wStr));
|
|
|
|
|
|
|
|
RelVaultNode * rvnPlr = VaultGetPlayerNodeIncRef();
|
|
|
|
if (rvnPlr) {
|
|
|
|
VaultPlayerNode plr(rvnPlr);
|
|
|
|
plr.SetAvatarShapeName(wStr);
|
|
|
|
rvnPlr->DecRef();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : ChangePlayerName
|
|
|
|
// PARAMETERS : name - is a string of the new name for the player
|
|
|
|
//
|
|
|
|
// PURPOSE : Change the local player's avatar name
|
|
|
|
//
|
|
|
|
void cyAvatar::ChangePlayerName(const char* playerName)
|
|
|
|
{
|
|
|
|
wchar_t wStr[MAX_PATH];
|
|
|
|
StrToUnicode(wStr, playerName, arrsize(wStr));
|
|
|
|
|
|
|
|
RelVaultNode * rvnPlr = VaultGetPlayerNodeIncRef();
|
|
|
|
if (rvnPlr) {
|
|
|
|
VaultPlayerNode plr(rvnPlr);
|
|
|
|
plr.SetPlayerName(wStr);
|
|
|
|
rvnPlr->DecRef();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : Emote
|
|
|
|
// PARAMETERS : emoteName - name of the emote to play on the avatar
|
|
|
|
//
|
|
|
|
// PURPOSE : plays an emote on a the local avatar (net propagated)
|
|
|
|
//
|
|
|
|
bool cyAvatar::Emote(const char* emoteName)
|
|
|
|
{
|
|
|
|
// can we find an emote of this name?
|
|
|
|
plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
|
|
|
|
|
|
|
return AvatarEmote(avatar, emoteName);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : Sit
|
|
|
|
// PARAMETERS : none
|
|
|
|
//
|
|
|
|
// PURPOSE : Makes the avatar sit down on the ground where they are.
|
|
|
|
// The avatar will automatically stand when the user tries to move.
|
|
|
|
//
|
|
|
|
bool cyAvatar::Sit()
|
|
|
|
{
|
|
|
|
return IEnterGenericMode("SitDownGround", "SitIdleGround", "SitStandGround", true, plAGAnim::kBodyLower, plAvBrainGeneric::kSitOnGround);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : EnterKiMode
|
|
|
|
// PARAMETERS : none
|
|
|
|
//
|
|
|
|
// PURPOSE : Makes the avatar appear to be using the ki.
|
|
|
|
//
|
|
|
|
bool cyAvatar::EnterKiMode()
|
|
|
|
{
|
|
|
|
return IEnterGenericMode("KiBegin", "KiUse", "KiEnd", false, plAGAnim::kBodyFull);
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : ExitKiMode
|
|
|
|
// PARAMETERS : none
|
|
|
|
//
|
|
|
|
// PURPOSE : Makes the avatar stop appearing to use the ki.
|
|
|
|
// May cause problems if EnterKiMode() was not called earlier.
|
|
|
|
//
|
|
|
|
bool cyAvatar::ExitKiMode()
|
|
|
|
{
|
|
|
|
return IExitTopmostGenericMode();
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : EnterAFKMode
|
|
|
|
// PARAMETERS : none
|
|
|
|
//
|
|
|
|
// PURPOSE : Tell the avatar to enter the AFK mode (sitting, head down)
|
|
|
|
//
|
|
|
|
bool cyAvatar::EnterAFKMode()
|
|
|
|
{
|
|
|
|
return IEnterGenericMode("AFKEnter", "AFKIdle", "AFKExit", true, plAGAnim::kBodyFull, plAvBrainGeneric::kAFK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : ExitAFKMode
|
|
|
|
// PARAMETERS : none
|
|
|
|
//
|
|
|
|
// PURPOSE : Tell the avatar to exit the AFK mode
|
|
|
|
// May cause problems if EnterKiMode() was not called earlier.
|
|
|
|
//
|
|
|
|
bool cyAvatar::ExitAFKMode()
|
|
|
|
{
|
|
|
|
return IExitTopmostGenericMode();
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : EnterPBMode
|
|
|
|
// PARAMETERS : none
|
|
|
|
//
|
|
|
|
// PURPOSE : Enter the personal book mode...stay until further notice.
|
|
|
|
//
|
|
|
|
bool cyAvatar::EnterPBMode()
|
|
|
|
{
|
|
|
|
return IEnterGenericMode("PersonalBookEnter", "PersonalBookIdle", "PersonalBookExit", false, plAGAnim::kBodyFull);
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : ExitPBMode
|
|
|
|
// PARAMETERS : none
|
|
|
|
//
|
|
|
|
// PURPOSE : Leave the personal book mode. Currently leaves any mode; will become
|
|
|
|
// : more specific in future version
|
|
|
|
//
|
|
|
|
bool cyAvatar::ExitPBMode()
|
|
|
|
{
|
|
|
|
return IExitTopmostGenericMode();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int cyAvatar::GetCurrentMode()
|
|
|
|
{
|
|
|
|
// make sure that there is atleast one avatar scene object attached (should be)
|
|
|
|
if ( fRecvr.Count() > 0)
|
|
|
|
{
|
|
|
|
// find the armature modifier
|
|
|
|
plArmatureMod* avatar = (plArmatureMod*)IFindArmatureMod((plKey)fRecvr[0]);
|
|
|
|
if(avatar)
|
|
|
|
{
|
|
|
|
return avatar->GetCurrentGenericType();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return plAvBrainGeneric::kNonGeneric;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : disable movement controls
|
|
|
|
// PARAMETERS :
|
|
|
|
//
|
|
|
|
// PURPOSE : something tells me python shouldn't do this this way
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
void cyAvatar::DisableMovementControls()
|
|
|
|
{
|
|
|
|
plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
|
|
|
if (avatar)
|
|
|
|
{
|
|
|
|
if (!avatar->IsInputSuspended())
|
|
|
|
avatar->SuspendInput();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cyAvatar::EnableMovementControls()
|
|
|
|
{
|
|
|
|
plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
|
|
|
if (avatar)
|
|
|
|
{
|
|
|
|
if (avatar->IsInputSuspended())
|
|
|
|
avatar->ResumeInput();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cyAvatar::DisableMouseMovement()
|
|
|
|
{
|
|
|
|
plAvatarInputInterface::GetInstance()->SuspendMouseMovement();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cyAvatar::EnableMouseMovement()
|
|
|
|
{
|
|
|
|
plAvatarInputInterface::GetInstance()->EnableMouseMovement();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cyAvatar::EnableAvatarJump()
|
|
|
|
{
|
|
|
|
plAvatarInputInterface::GetInstance()->EnableJump(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cyAvatar::DisableAvatarJump()
|
|
|
|
{
|
|
|
|
plAvatarInputInterface::GetInstance()->EnableJump(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cyAvatar::EnableForwardMovement()
|
|
|
|
{
|
|
|
|
plAvatarInputInterface::GetInstance()->EnableForwardMovement(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cyAvatar::DisableForwardMovement()
|
|
|
|
{
|
|
|
|
plAvatarInputInterface::GetInstance()->EnableForwardMovement(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cyAvatar::LocalAvatarRunKeyDown()
|
|
|
|
{
|
|
|
|
plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
|
|
|
if (avMod)
|
|
|
|
return avMod->FastKeyDown();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cyAvatar::LocalAvatarIsMoving()
|
|
|
|
{
|
|
|
|
plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
|
|
|
if (avMod)
|
|
|
|
return avMod->ForwardKeyDown() || avMod->BackwardKeyDown() || avMod->StrafeRightKeyDown() ||
|
|
|
|
avMod->StrafeLeftKeyDown() || avMod->TurnRightKeyDown() || avMod->TurnLeftKeyDown() ||
|
|
|
|
avMod->JumpKeyDown();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cyAvatar::SetMouseTurnSensitivity(float val)
|
|
|
|
{
|
|
|
|
plArmatureMod::SetMouseTurnSensitivity(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
float cyAvatar::GetMouseTurnSensitivity()
|
|
|
|
{
|
|
|
|
return plArmatureMod::GetMouseTurnSensitivity();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cyAvatar::SpawnNext()
|
|
|
|
{
|
|
|
|
static int whichSpawn = 0;
|
|
|
|
plAvatarMgr *mgr = plAvatarMgr::GetInstance();
|
|
|
|
int max = mgr->NumSpawnPoints();
|
|
|
|
|
|
|
|
whichSpawn = ++whichSpawn < max ? whichSpawn : 0;
|
|
|
|
|
|
|
|
plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
|
|
|
if(avatar)
|
|
|
|
{
|
|
|
|
double fakeTime = 0.0f;
|
|
|
|
avatar->SpawnAt(whichSpawn, fakeTime);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : RegisterForBehaviorNotify()
|
|
|
|
// PARAMETERS : none
|
|
|
|
//
|
|
|
|
// PURPOSE : To register for notifies from the avatar for any kind of behavior notify
|
|
|
|
//
|
|
|
|
void cyAvatar::RegisterForBehaviorNotify(pyKey &selfKey)
|
|
|
|
{
|
|
|
|
// make sure that there is atleast one avatar scene object attached (should be)
|
|
|
|
if ( fRecvr.Count() > 0)
|
|
|
|
{
|
|
|
|
// find the armature modifier
|
|
|
|
plArmatureMod* avatar = (plArmatureMod*)IFindArmatureMod((plKey)fRecvr[0]);
|
|
|
|
if(avatar)
|
|
|
|
{
|
|
|
|
avatar->RegisterForBehaviorNotify(selfKey.getKey());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : UnRegisterForBehaviorNotify()
|
|
|
|
// PARAMETERS : none
|
|
|
|
//
|
|
|
|
// PURPOSE : To remove the registeration for notifies from the avatar
|
|
|
|
//
|
|
|
|
void cyAvatar::UnRegisterForBehaviorNotify(pyKey &selfKey)
|
|
|
|
{
|
|
|
|
// make sure that there is atleast one avatar scene object attached (should be)
|
|
|
|
if ( fRecvr.Count() > 0)
|
|
|
|
{
|
|
|
|
// find the armature modifier
|
|
|
|
plArmatureMod* avatar = (plArmatureMod*)IFindArmatureMod((plKey)fRecvr[0]);
|
|
|
|
if(avatar)
|
|
|
|
{
|
|
|
|
avatar->UnRegisterForBehaviorNotify(selfKey.getKey());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : IEnterGenericMode
|
|
|
|
// PARAMETERS : none
|
|
|
|
//
|
|
|
|
// PURPOSE : Three-stage multistage animations (sit down, sit, get up) are really common.
|
|
|
|
// : This does the basic setup.
|
|
|
|
//
|
|
|
|
bool IEnterGenericMode(const char *enterAnim, const char *idleAnim, const char *exitAnim, bool autoExit, plAGAnim::BodyUsage bodyUsage,
|
|
|
|
plAvBrainGeneric::BrainType type /* = kGeneric */)
|
|
|
|
{
|
|
|
|
plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
|
|
|
return PushSimpleMultiStage(avatar, enterAnim, idleAnim, exitAnim, true, autoExit, bodyUsage, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : IExitTopmostGenericMode
|
|
|
|
// PARAMETERS : none
|
|
|
|
//
|
|
|
|
// PURPOSE : Exits whatever multistage animation you're in. We currently don't discriminate;
|
|
|
|
// : that will be added later.
|
|
|
|
//
|
|
|
|
bool IExitTopmostGenericMode()
|
|
|
|
{
|
|
|
|
plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
|
|
|
|
|
|
|
plAvBrainGenericMsg* pMsg = new plAvBrainGenericMsg(nil, avatar->GetKey(),
|
|
|
|
plAvBrainGenericMsg::kGotoStage, 2, false, 0.0,
|
|
|
|
false, false, 0.0);
|
|
|
|
|
|
|
|
pMsg->SetBCastFlag(plMessage::kNetForce | plMessage::kNetPropagate);
|
|
|
|
|
|
|
|
plgDispatch::MsgSend( pMsg );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Function : IsCurrentBrainHuman
|
|
|
|
// PARAMETERS : none
|
|
|
|
//
|
|
|
|
// PURPOSE : Returns whether the top most brain is a human brain
|
|
|
|
//
|
|
|
|
bool cyAvatar::IsCurrentBrainHuman()
|
|
|
|
{
|
|
|
|
plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
|
|
|
if (avatar)
|
|
|
|
{
|
|
|
|
plArmatureBrain *brain = avatar->GetCurrentBrain();
|
|
|
|
plAvBrainHuman *human = plAvBrainHuman::ConvertNoRef(brain);
|
|
|
|
if (human)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|