5840 lines
159 KiB
C++
5840 lines
159 KiB
C++
/**
|
|
* @file llagent.cpp
|
|
* @brief LLAgent class implementation
|
|
*
|
|
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
|
*
|
|
* Copyright (c) 2001-2009, Linden Research, Inc.
|
|
*
|
|
* Second Life Viewer Source Code
|
|
* The source code in this file ("Source Code") is provided by Linden Lab
|
|
* to you under the terms of the GNU General Public License, version 2.0
|
|
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
*
|
|
* There are special exceptions to the terms and conditions of the GPL as
|
|
* it is applied to this Source Code. View the full text of the exception
|
|
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
|
* online at
|
|
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
*
|
|
* By copying, modifying or distributing this software, you acknowledge
|
|
* that you have read and understood your obligations described above,
|
|
* and agree to abide by those obligations.
|
|
*
|
|
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
|
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
|
* COMPLETENESS OR PERFORMANCE.
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "llagent.h"
|
|
|
|
#include "pipeline.h"
|
|
|
|
#include "llagentaccess.h"
|
|
#include "llagentcamera.h"
|
|
#include "llanimationstates.h"
|
|
#include "llcallingcard.h"
|
|
#include "llconsole.h"
|
|
#include "llfirstuse.h"
|
|
#include "llfloatercamera.h"
|
|
#include "llfloatertools.h"
|
|
|
|
#include "llgroupmgr.h"
|
|
#include "llhomelocationresponder.h"
|
|
#include "llhudmanager.h"
|
|
#include "lljoystickbutton.h"
|
|
#include "llmorphview.h"
|
|
#include "llmoveview.h"
|
|
#include "llchatbar.h"
|
|
#include "llnotify.h"
|
|
#include "llparcel.h"
|
|
#include "llrendersphere.h"
|
|
#include "llsdutil.h"
|
|
#include "llsky.h"
|
|
#include "llsmoothstep.h"
|
|
#include "llstartup.h"
|
|
#include "llstatusbar.h"
|
|
#include "lltool.h"
|
|
#include "lltoolpie.h"
|
|
#include "lltoolmgr.h"
|
|
#include "lltrans.h"
|
|
#include "llviewercontrol.h"
|
|
#include "llviewerdisplay.h"
|
|
#include "llviewerjoystick.h"
|
|
#include "llviewermediafocus.h"
|
|
#include "llviewermenu.h"
|
|
#include "llviewerobjectlist.h"
|
|
#include "llviewerparcelmgr.h"
|
|
#include "llviewerstats.h"
|
|
#include "llviewerwindow.h"
|
|
#include "llvoavatar.h"
|
|
#include "llworld.h"
|
|
#include "llworldmap.h"
|
|
|
|
//Misc non-standard includes
|
|
#include "llviewerregion.h"
|
|
#include "llurldispatcher.h"
|
|
#include "llimview.h" //For gIMMgr
|
|
//Floaters
|
|
#include "llfloatermute.h"
|
|
#include "llfloatermap.h"
|
|
#include "llfloateractivespeakers.h"
|
|
#include "llfloaterdirectory.h"
|
|
#include "llfloatergroupinfo.h"
|
|
#include "llfloatergroups.h"
|
|
#include "llfloateravatarinfo.h"
|
|
#include "llfloaterworldmap.h"
|
|
#include "llfloaterland.h"
|
|
#include "llfloatersnapshot.h"
|
|
#include "llfloaterchat.h"
|
|
|
|
//Wearables
|
|
#include "llinventoryview.h"
|
|
#include "cofmgr.h"
|
|
#include "llwearablelist.h"
|
|
#include "llfloatercustomize.h"
|
|
|
|
|
|
#include "hippogridmanager.h"
|
|
|
|
// [RLVa:KB] - Checked: 2010-09-27 (RLVa-1.1.3b)
|
|
#include "rlvhandler.h"
|
|
#include "rlvinventory.h"
|
|
#include "llattachmentsmgr.h"
|
|
// [/RLVa:KB]
|
|
|
|
using namespace LLVOAvatarDefines;
|
|
|
|
//drone wandering constants
|
|
const F32 MAX_WANDER_TIME = 20.f; // seconds
|
|
const F32 MAX_HEADING_HALF_ERROR = 0.2f; // radians
|
|
const F32 WANDER_MAX_SLEW_RATE = 2.f * DEG_TO_RAD; // radians / frame
|
|
const F32 WANDER_TARGET_MIN_DISTANCE = 10.f; // meters
|
|
|
|
// Autopilot constants
|
|
const F32 AUTOPILOT_HEADING_HALF_ERROR = 10.f * DEG_TO_RAD; // radians
|
|
const F32 AUTOPILOT_MAX_SLEW_RATE = 1.f * DEG_TO_RAD; // radians / frame
|
|
const F32 AUTOPILOT_STOP_DISTANCE = 2.f; // meters
|
|
const F32 AUTOPILOT_HEIGHT_ADJUST_DISTANCE = 8.f; // meters
|
|
const F32 AUTOPILOT_MIN_TARGET_HEIGHT_OFF_GROUND = 1.f; // meters
|
|
const F32 AUTOPILOT_MAX_TIME_NO_PROGRESS = 1.5f; // seconds
|
|
|
|
// face editing constants
|
|
const LLVector3d FACE_EDIT_CAMERA_OFFSET(0.4f, -0.05f, 0.07f);
|
|
const LLVector3d FACE_EDIT_TARGET_OFFSET(0.f, 0.f, 0.05f);
|
|
|
|
|
|
// fidget constants
|
|
const F32 MIN_FIDGET_TIME = 8.f; // seconds
|
|
const F32 MAX_FIDGET_TIME = 20.f; // seconds
|
|
|
|
const F32 MAX_VELOCITY_AUTO_LAND_SQUARED = 4.f * 4.f;
|
|
|
|
const F32 MAX_FOCUS_OFFSET = 20.f;
|
|
|
|
const F32 MIN_RADIUS_ALPHA_SIZZLE = 0.5f;
|
|
|
|
const F64 CHAT_AGE_FAST_RATE = 3.0;
|
|
|
|
// The agent instance.
|
|
LLAgent gAgent;
|
|
std::string gAuthString;
|
|
|
|
// <edit>
|
|
LLUUID gReSitTargetID;
|
|
LLVector3 gReSitOffset;
|
|
// </edit>
|
|
//
|
|
// Statics
|
|
//
|
|
|
|
BOOL LLAgent::exlPhantom = 0;
|
|
BOOL LLAgent::mForceTPose = 0;
|
|
LLVector3 LLAgent::exlStartMeasurePoint = LLVector3::zero;
|
|
LLVector3 LLAgent::exlEndMeasurePoint = LLVector3::zero;
|
|
|
|
const F32 LLAgent::TYPING_TIMEOUT_SECS = 5.f;
|
|
|
|
std::map<std::string, std::string> LLAgent::sTeleportErrorMessages;
|
|
std::map<std::string, std::string> LLAgent::sTeleportProgressMessages;
|
|
|
|
BOOL isAgentAvatarValid()
|
|
{
|
|
return (gAgent.getAvatarObject() &&
|
|
(gAgent.getAvatarObject()->getRegion() != NULL) &&
|
|
(!gAgent.getAvatarObject()->isDead()));
|
|
}
|
|
|
|
class LLAgentFriendObserver : public LLFriendObserver
|
|
{
|
|
public:
|
|
LLAgentFriendObserver() {}
|
|
virtual ~LLAgentFriendObserver() {}
|
|
virtual void changed(U32 mask);
|
|
};
|
|
|
|
void LLAgentFriendObserver::changed(U32 mask)
|
|
{
|
|
// if there's a change we're interested in.
|
|
if((mask & (LLFriendObserver::POWERS)) != 0)
|
|
{
|
|
gAgent.friendsChanged();
|
|
}
|
|
}
|
|
|
|
// ************************************************************
|
|
// Enabled this definition to compile a 'hacked' viewer that
|
|
// locally believes the end user has godlike powers.
|
|
// #define HACKED_GODLIKE_VIEWER
|
|
// For a toggled version, see viewer.h for the
|
|
// TOGGLE_HACKED_GODLIKE_VIEWER define, instead.
|
|
// ************************************************************
|
|
|
|
// Constructors and Destructors
|
|
|
|
// JC - Please try to make this order match the order in the header
|
|
// file. Otherwise it's hard to find variables that aren't initialized.
|
|
//-----------------------------------------------------------------------------
|
|
// LLAgent()
|
|
//-----------------------------------------------------------------------------
|
|
LLAgent::LLAgent() :
|
|
mGroupPowers(0),
|
|
mHideGroupTitle(FALSE),
|
|
mGroupID(),
|
|
|
|
mInitialized(FALSE),
|
|
|
|
mDoubleTapRunTimer(),
|
|
mDoubleTapRunMode(DOUBLETAP_NONE),
|
|
|
|
mbAlwaysRun(false),
|
|
mbRunning(false),
|
|
mbTeleportKeepsLookAt(false),
|
|
|
|
mAgentAccess(new LLAgentAccess(gSavedSettings)),
|
|
mTeleportState( TELEPORT_NONE ),
|
|
mRegionp(NULL),
|
|
|
|
mAgentOriginGlobal(),
|
|
mPositionGlobal(),
|
|
|
|
mDistanceTraveled(0.F),
|
|
mLastPositionGlobal(LLVector3d::zero),
|
|
|
|
mAvatarObject(NULL),
|
|
|
|
mRenderState(0),
|
|
mTypingTimer(),
|
|
|
|
mViewsPushed(FALSE),
|
|
|
|
mCustomAnim(FALSE),
|
|
mShowAvatar(TRUE),
|
|
mFrameAgent(),
|
|
|
|
mIsBusy(FALSE),
|
|
|
|
mControlFlags(0x00000000),
|
|
mbFlagsDirty(FALSE),
|
|
mbFlagsNeedReset(FALSE),
|
|
|
|
mAutoPilot(FALSE),
|
|
mAutoPilotFlyOnStop(FALSE),
|
|
mAutoPilotTargetGlobal(),
|
|
mAutoPilotStopDistance(1.f),
|
|
mAutoPilotUseRotation(FALSE),
|
|
mAutoPilotTargetFacing(LLVector3::zero),
|
|
mAutoPilotTargetDist(0.f),
|
|
mAutoPilotNoProgressFrameCount(0),
|
|
mAutoPilotRotationThreshold(0.f),
|
|
mAutoPilotFinishedCallback(NULL),
|
|
mAutoPilotCallbackData(NULL),
|
|
|
|
mEffectColor(0.f, 1.f, 1.f, 1.f),
|
|
|
|
mHaveHomePosition(FALSE),
|
|
mHomeRegionHandle( 0 ),
|
|
mNearChatRadius(CHAT_NORMAL_RADIUS / 2.f),
|
|
|
|
mNextFidgetTime(0.f),
|
|
mCurrentFidget(0),
|
|
mFirstLogin(FALSE),
|
|
mGenderChosen(FALSE),
|
|
mWearablesLoaded(FALSE),
|
|
mAppearanceSerialNum(0),
|
|
|
|
mPendingLure(NULL)
|
|
{
|
|
U32 i;
|
|
for (i = 0; i < TOTAL_CONTROLS; i++)
|
|
{
|
|
mControlsTakenCount[i] = 0;
|
|
mControlsTakenPassedOnCount[i] = 0;
|
|
}
|
|
}
|
|
|
|
// Requires gSavedSettings to be initialized.
|
|
//-----------------------------------------------------------------------------
|
|
// init()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::init()
|
|
{
|
|
|
|
setFlying( gSavedSettings.getBOOL("FlyingAtExit") );
|
|
|
|
|
|
|
|
// LLDebugVarMessageBox::show("Camera Lag", &CAMERA_FOCUS_HALF_LIFE, 0.5f, 0.01f);
|
|
|
|
mEffectColor = gSavedSettings.getColor4("EffectColor");
|
|
|
|
mInitialized = TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// cleanup()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::cleanup()
|
|
{
|
|
mAvatarObject = NULL;
|
|
mRegionp = NULL;
|
|
if(mPendingLure)
|
|
delete mPendingLure;
|
|
mPendingLure = NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// LLAgent()
|
|
//-----------------------------------------------------------------------------
|
|
LLAgent::~LLAgent()
|
|
{
|
|
cleanup();
|
|
|
|
delete mAgentAccess;
|
|
mAgentAccess = NULL;
|
|
}
|
|
|
|
|
|
// Handle any actions that need to be performed when the main app gains focus
|
|
// (such as through alt-tab).
|
|
//-----------------------------------------------------------------------------
|
|
// onAppFocusGained()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::onAppFocusGained()
|
|
{
|
|
if (CAMERA_MODE_MOUSELOOK == gAgentCamera.getCameraMode())
|
|
{
|
|
gAgentCamera.changeCameraToDefault();
|
|
LLToolMgr::getInstance()->clearSavedTool();
|
|
}
|
|
}
|
|
|
|
|
|
void LLAgent::ageChat()
|
|
{
|
|
if (isAgentAvatarValid())
|
|
{
|
|
// get amount of time since I last chatted
|
|
F64 elapsed_time = (F64)mAvatarObject->mChatTimer.getElapsedTimeF32();
|
|
// add in frame time * 3 (so it ages 4x)
|
|
mAvatarObject->mChatTimer.setAge(elapsed_time + (F64)gFrameDTClamped * (CHAT_AGE_FAST_RATE - 1.0));
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// moveAt()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::moveAt(S32 direction, bool reset)
|
|
{
|
|
// age chat timer so it fades more quickly when you are intentionally moving
|
|
ageChat();
|
|
|
|
gAgentCamera.setAtKey(LLAgentCamera::directionToKey(direction));
|
|
|
|
if (direction > 0)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_AT_POS | AGENT_CONTROL_FAST_AT);
|
|
}
|
|
else if (direction < 0)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_AT_NEG | AGENT_CONTROL_FAST_AT);
|
|
}
|
|
|
|
if (reset)
|
|
{
|
|
gAgentCamera.resetView();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// moveAtNudge()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::moveAtNudge(S32 direction)
|
|
{
|
|
// age chat timer so it fades more quickly when you are intentionally moving
|
|
ageChat();
|
|
|
|
gAgentCamera.setWalkKey(LLAgentCamera::directionToKey(direction));
|
|
|
|
if (direction > 0)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_NUDGE_AT_POS);
|
|
}
|
|
else if (direction < 0)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_NUDGE_AT_NEG);
|
|
}
|
|
|
|
gAgentCamera.resetView();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// moveLeft()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::moveLeft(S32 direction)
|
|
{
|
|
// age chat timer so it fades more quickly when you are intentionally moving
|
|
ageChat();
|
|
|
|
gAgentCamera.setLeftKey(LLAgentCamera::directionToKey(direction));
|
|
|
|
if (direction > 0)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_LEFT_POS | AGENT_CONTROL_FAST_LEFT);
|
|
}
|
|
else if (direction < 0)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_LEFT_NEG | AGENT_CONTROL_FAST_LEFT);
|
|
}
|
|
|
|
gAgentCamera.resetView();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// moveLeftNudge()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::moveLeftNudge(S32 direction)
|
|
{
|
|
// age chat timer so it fades more quickly when you are intentionally moving
|
|
ageChat();
|
|
|
|
gAgentCamera.setLeftKey(LLAgentCamera::directionToKey(direction));
|
|
|
|
if (direction > 0)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_NUDGE_LEFT_POS);
|
|
}
|
|
else if (direction < 0)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_NUDGE_LEFT_NEG);
|
|
}
|
|
|
|
gAgentCamera.resetView();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// moveUp()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::moveUp(S32 direction)
|
|
{
|
|
// age chat timer so it fades more quickly when you are intentionally moving
|
|
ageChat();
|
|
|
|
gAgentCamera.setUpKey(LLAgentCamera::directionToKey(direction));
|
|
|
|
if (direction > 0)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_UP_POS | AGENT_CONTROL_FAST_UP);
|
|
}
|
|
else if (direction < 0)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_UP_NEG | AGENT_CONTROL_FAST_UP);
|
|
}
|
|
|
|
gAgentCamera.resetView();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// moveYaw()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::moveYaw(F32 mag, bool reset_view)
|
|
{
|
|
gAgentCamera.setYawKey(mag);
|
|
|
|
if (mag > 0)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_YAW_POS);
|
|
}
|
|
else if (mag < 0)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_YAW_NEG);
|
|
}
|
|
|
|
if (reset_view)
|
|
{
|
|
gAgentCamera.resetView();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// movePitch()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::movePitch(F32 mag)
|
|
{
|
|
gAgentCamera.setPitchKey(mag);
|
|
|
|
if (mag > 0)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_PITCH_POS );
|
|
}
|
|
else if (mag < 0)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_PITCH_NEG);
|
|
}
|
|
}
|
|
|
|
|
|
// Does this parcel allow you to fly?
|
|
BOOL LLAgent::canFly()
|
|
{
|
|
// [RLVa:KB] - Checked: 2009-07-05 (RLVa-1.0.0c)
|
|
if (gRlvHandler.hasBehaviour(RLV_BHVR_FLY)) return FALSE;
|
|
// [/RLVa:KB]
|
|
if (isGodlike()) return TRUE;
|
|
|
|
// <edit>
|
|
static const LLCachedControl<bool> ascent_fly_always_enabled("AscentFlyAlwaysEnabled",false);
|
|
if(ascent_fly_always_enabled)
|
|
return TRUE;
|
|
// </edit>
|
|
|
|
LLViewerRegion* regionp = getRegion();
|
|
if (regionp && regionp->getBlockFly()) return FALSE;
|
|
|
|
LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
|
|
if (!parcel) return FALSE;
|
|
|
|
// Allow owners to fly on their own land.
|
|
if (LLViewerParcelMgr::isParcelOwnedByAgent(parcel, GP_LAND_ALLOW_FLY))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return parcel->getAllowFly();
|
|
}
|
|
|
|
BOOL LLAgent::getFlying() const
|
|
{
|
|
return mControlFlags & AGENT_CONTROL_FLY;
|
|
}
|
|
|
|
// Better Set Phantom options ~Charbl
|
|
void LLAgent::setPhantom(BOOL phantom)
|
|
{
|
|
exlPhantom = phantom;
|
|
}
|
|
|
|
BOOL LLAgent::getPhantom()
|
|
{
|
|
return exlPhantom;
|
|
}
|
|
|
|
void LLAgent::resetClientTag()
|
|
{
|
|
if (!mAvatarObject.isNull())
|
|
{
|
|
llinfos << "Resetting mClientTag." << llendl;
|
|
mAvatarObject->mClientTag = "";
|
|
}
|
|
}
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// setFlying()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::setFlying(BOOL fly)
|
|
{
|
|
if (isAgentAvatarValid())
|
|
{
|
|
if(mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_STANDUP) != mAvatarObject->mSignaledAnimations.end())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// don't allow taking off while sitting
|
|
if (fly && mAvatarObject->isSitting())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (fly)
|
|
{
|
|
// [RLVa:KB] - Checked: 2009-07-05 (RLVa-1.0.0c)
|
|
if (gRlvHandler.hasBehaviour(RLV_BHVR_FLY))
|
|
{
|
|
return;
|
|
}
|
|
// [/RLVa:KB]
|
|
|
|
BOOL was_flying = getFlying();
|
|
if (!canFly() && !was_flying)
|
|
{
|
|
// parcel doesn't let you start fly
|
|
// gods can always fly
|
|
// and it's OK if you're already flying
|
|
make_ui_sound("UISndBadKeystroke");
|
|
return;
|
|
}
|
|
if( !was_flying )
|
|
{
|
|
LLViewerStats::getInstance()->incStat(LLViewerStats::ST_FLY_COUNT);
|
|
}
|
|
setControlFlags(AGENT_CONTROL_FLY);
|
|
}
|
|
else
|
|
{
|
|
clearControlFlags(AGENT_CONTROL_FLY);
|
|
}
|
|
|
|
|
|
gSavedSettings.setBOOL("FlyBtnState",fly);
|
|
|
|
mbFlagsDirty = TRUE;
|
|
}
|
|
|
|
|
|
// UI based mechanism of setting fly state
|
|
//-----------------------------------------------------------------------------
|
|
// toggleFlying()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::toggleFlying()
|
|
{
|
|
BOOL fly = !gAgent.getFlying();
|
|
|
|
gAgent.setFlying( fly );
|
|
gAgentCamera.resetView();
|
|
}
|
|
|
|
// static
|
|
bool LLAgent::enableFlying()
|
|
{
|
|
BOOL sitting = FALSE;
|
|
if (isAgentAvatarValid())
|
|
{
|
|
sitting = gAgent.getAvatarObject()->isSitting();
|
|
}
|
|
return !sitting;
|
|
}
|
|
|
|
void LLAgent::standUp()
|
|
{
|
|
setControlFlags(AGENT_CONTROL_STAND_UP);
|
|
}
|
|
|
|
void LLAgent::togglePhantom()
|
|
{
|
|
BOOL phan = !(exlPhantom);
|
|
|
|
setPhantom( phan );
|
|
}
|
|
|
|
void LLAgent::toggleTPosed()
|
|
{
|
|
BOOL posed = !(mForceTPose);
|
|
|
|
setTPosed(posed);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// setRegion()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::setRegion(LLViewerRegion *regionp)
|
|
{
|
|
llassert(regionp);
|
|
if (mRegionp != regionp)
|
|
{
|
|
// std::string host_name;
|
|
// host_name = regionp->getHost().getHostName();
|
|
|
|
std::string ip = regionp->getHost().getString();
|
|
llinfos << "Moving agent into region: " << regionp->getName()
|
|
<< " located at " << ip << llendl;
|
|
if (mRegionp)
|
|
{
|
|
// We've changed regions, we're now going to change our agent coordinate frame.
|
|
mAgentOriginGlobal = regionp->getOriginGlobal();
|
|
LLVector3d agent_offset_global = mRegionp->getOriginGlobal();
|
|
|
|
LLVector3 delta;
|
|
delta.setVec(regionp->getOriginGlobal() - mRegionp->getOriginGlobal());
|
|
|
|
setPositionAgent(getPositionAgent() - delta);
|
|
|
|
LLVector3 camera_position_agent = LLViewerCamera::getInstance()->getOrigin();
|
|
LLViewerCamera::getInstance()->setOrigin(camera_position_agent - delta);
|
|
|
|
// Update all of the regions.
|
|
LLWorld::getInstance()->updateAgentOffset(agent_offset_global);
|
|
|
|
// Hack to keep sky in the agent's region, otherwise it may get deleted - DJS 08/02/02
|
|
// *TODO: possibly refactor into gSky->setAgentRegion(regionp)? -Brad
|
|
if (gSky.mVOSkyp)
|
|
{
|
|
gSky.mVOSkyp->setRegion(regionp);
|
|
}
|
|
if (gSky.mVOGroundp)
|
|
{
|
|
gSky.mVOGroundp->setRegion(regionp);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// First time initialization.
|
|
// We've changed regions, we're now going to change our agent coordinate frame.
|
|
mAgentOriginGlobal = regionp->getOriginGlobal();
|
|
|
|
LLVector3 delta;
|
|
delta.setVec(regionp->getOriginGlobal());
|
|
|
|
setPositionAgent(getPositionAgent() - delta);
|
|
LLVector3 camera_position_agent = LLViewerCamera::getInstance()->getOrigin();
|
|
LLViewerCamera::getInstance()->setOrigin(camera_position_agent - delta);
|
|
|
|
// Update all of the regions.
|
|
LLWorld::getInstance()->updateAgentOffset(mAgentOriginGlobal);
|
|
}
|
|
}
|
|
mRegionp = regionp;
|
|
|
|
// Must shift hole-covering water object locations because local
|
|
// coordinate frame changed.
|
|
LLWorld::getInstance()->updateWaterObjects();
|
|
|
|
// keep a list of regions we've been too
|
|
// this is just an interesting stat, logged at the dataserver
|
|
// we could trake this at the dataserver side, but that's harder
|
|
U64 handle = regionp->getHandle();
|
|
mRegionsVisited.insert(handle);
|
|
|
|
LLSelectMgr::getInstance()->updateSelectionCenter();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getRegion()
|
|
//-----------------------------------------------------------------------------
|
|
LLViewerRegion *LLAgent::getRegion() const
|
|
{
|
|
return mRegionp;
|
|
}
|
|
|
|
|
|
const LLHost& LLAgent::getRegionHost() const
|
|
{
|
|
if (mRegionp)
|
|
{
|
|
return mRegionp->getHost();
|
|
}
|
|
else
|
|
{
|
|
return LLHost::invalid;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getSLURL()
|
|
// returns empty() if getRegion() == NULL
|
|
//-----------------------------------------------------------------------------
|
|
std::string LLAgent::getSLURL() const
|
|
{
|
|
std::string slurl;
|
|
LLViewerRegion *regionp = getRegion();
|
|
if (regionp)
|
|
{
|
|
LLVector3d agentPos = getPositionGlobal();
|
|
S32 x = llround( (F32)fmod( agentPos.mdV[VX], (F64)REGION_WIDTH_METERS ) );
|
|
S32 y = llround( (F32)fmod( agentPos.mdV[VY], (F64)REGION_WIDTH_METERS ) );
|
|
S32 z = llround( (F32)agentPos.mdV[VZ] );
|
|
slurl = LLURLDispatcher::buildSLURL(regionp->getName(), x, y, z);
|
|
}
|
|
return slurl;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// inPrelude()
|
|
//-----------------------------------------------------------------------------
|
|
BOOL LLAgent::inPrelude()
|
|
{
|
|
return mRegionp && mRegionp->isPrelude();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// canManageEstate()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
BOOL LLAgent::canManageEstate() const
|
|
{
|
|
return mRegionp && mRegionp->canManageEstate();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// sendMessage()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::sendMessage()
|
|
{
|
|
if (gDisconnected)
|
|
{
|
|
llwarns << "Trying to send message when disconnected!" << llendl;
|
|
return;
|
|
}
|
|
if (!mRegionp)
|
|
{
|
|
llerrs << "No region for agent yet!" << llendl;
|
|
return;
|
|
}
|
|
gMessageSystem->sendMessage(mRegionp->getHost());
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// sendReliableMessage()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::sendReliableMessage()
|
|
{
|
|
if (gDisconnected)
|
|
{
|
|
lldebugs << "Trying to send message when disconnected!" << llendl;
|
|
return;
|
|
}
|
|
if (!mRegionp)
|
|
{
|
|
lldebugs << "LLAgent::sendReliableMessage No region for agent yet, not sending message!" << llendl;
|
|
return;
|
|
}
|
|
gMessageSystem->sendReliable(mRegionp->getHost());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getVelocity()
|
|
//-----------------------------------------------------------------------------
|
|
LLVector3 LLAgent::getVelocity() const
|
|
{
|
|
if (isAgentAvatarValid())
|
|
{
|
|
return mAvatarObject->getVelocity();
|
|
}
|
|
else
|
|
{
|
|
return LLVector3::zero;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// setPositionAgent()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::setPositionAgent(const LLVector3 &pos_agent)
|
|
{
|
|
if (!pos_agent.isFinite())
|
|
{
|
|
llerrs << "setPositionAgent is not a number" << llendl;
|
|
}
|
|
|
|
if (isAgentAvatarValid() && mAvatarObject->getParent())
|
|
{
|
|
LLVector3 pos_agent_sitting;
|
|
LLVector3d pos_agent_d;
|
|
LLViewerObject *parent = (LLViewerObject*)mAvatarObject->getParent();
|
|
|
|
pos_agent_sitting = mAvatarObject->getPosition() * parent->getRotation() + parent->getPositionAgent();
|
|
pos_agent_d.setVec(pos_agent_sitting);
|
|
|
|
mFrameAgent.setOrigin(pos_agent_sitting);
|
|
mPositionGlobal = pos_agent_d + mAgentOriginGlobal;
|
|
}
|
|
else
|
|
{
|
|
mFrameAgent.setOrigin(pos_agent);
|
|
|
|
LLVector3d pos_agent_d;
|
|
pos_agent_d.setVec(pos_agent);
|
|
mPositionGlobal = pos_agent_d + mAgentOriginGlobal;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getPositionGlobal()
|
|
//-----------------------------------------------------------------------------
|
|
const LLVector3d &LLAgent::getPositionGlobal() const
|
|
{
|
|
if (isAgentAvatarValid() && !mAvatarObject->mDrawable.isNull())
|
|
{
|
|
mPositionGlobal = getPosGlobalFromAgent(mAvatarObject->getRenderPosition());
|
|
}
|
|
else
|
|
{
|
|
mPositionGlobal = getPosGlobalFromAgent(mFrameAgent.getOrigin());
|
|
}
|
|
|
|
return mPositionGlobal;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getPositionAgent()
|
|
//-----------------------------------------------------------------------------
|
|
const LLVector3 &LLAgent::getPositionAgent()
|
|
{
|
|
if(mAvatarObject.notNull() && !mAvatarObject->mDrawable.isNull())
|
|
{
|
|
mFrameAgent.setOrigin(mAvatarObject->getRenderPosition());
|
|
}
|
|
|
|
return mFrameAgent.getOrigin();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getRegionsVisited()
|
|
//-----------------------------------------------------------------------------
|
|
S32 LLAgent::getRegionsVisited() const
|
|
{
|
|
return mRegionsVisited.size();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getDistanceTraveled()
|
|
//-----------------------------------------------------------------------------
|
|
F64 LLAgent::getDistanceTraveled() const
|
|
{
|
|
return mDistanceTraveled;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getPosAgentFromGlobal()
|
|
//-----------------------------------------------------------------------------
|
|
LLVector3 LLAgent::getPosAgentFromGlobal(const LLVector3d &pos_global) const
|
|
{
|
|
LLVector3 pos_agent;
|
|
pos_agent.setVec(pos_global - mAgentOriginGlobal);
|
|
return pos_agent;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getPosGlobalFromAgent()
|
|
//-----------------------------------------------------------------------------
|
|
LLVector3d LLAgent::getPosGlobalFromAgent(const LLVector3 &pos_agent) const
|
|
{
|
|
LLVector3d pos_agent_d;
|
|
pos_agent_d.setVec(pos_agent);
|
|
return pos_agent_d + mAgentOriginGlobal;
|
|
}
|
|
|
|
void LLAgent::sitDown()
|
|
{
|
|
setControlFlags(AGENT_CONTROL_SIT_ON_GROUND);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// resetAxes()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::resetAxes()
|
|
{
|
|
mFrameAgent.resetAxes();
|
|
}
|
|
|
|
|
|
// Copied from LLCamera::setOriginAndLookAt
|
|
// Look_at must be unit vector
|
|
//-----------------------------------------------------------------------------
|
|
// resetAxes()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::resetAxes(const LLVector3 &look_at)
|
|
{
|
|
LLVector3 skyward = getReferenceUpVector();
|
|
|
|
// if look_at has zero length, fail
|
|
// if look_at and skyward are parallel, fail
|
|
//
|
|
// Test both of these conditions with a cross product.
|
|
LLVector3 cross(look_at % skyward);
|
|
if (cross.isNull())
|
|
{
|
|
llinfos << "LLAgent::resetAxes cross-product is zero" << llendl;
|
|
return;
|
|
}
|
|
|
|
// Make sure look_at and skyward are not parallel
|
|
// and neither are zero length
|
|
LLVector3 left(skyward % look_at);
|
|
LLVector3 up(look_at % left);
|
|
|
|
mFrameAgent.setAxes(look_at, left, up);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// rotate()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::rotate(F32 angle, const LLVector3 &axis)
|
|
{
|
|
mFrameAgent.rotate(angle, axis);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// rotate()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::rotate(F32 angle, F32 x, F32 y, F32 z)
|
|
{
|
|
mFrameAgent.rotate(angle, x, y, z);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// rotate()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::rotate(const LLMatrix3 &matrix)
|
|
{
|
|
mFrameAgent.rotate(matrix);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// rotate()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::rotate(const LLQuaternion &quaternion)
|
|
{
|
|
mFrameAgent.rotate(quaternion);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getReferenceUpVector()
|
|
//-----------------------------------------------------------------------------
|
|
LLVector3 LLAgent::getReferenceUpVector()
|
|
{
|
|
// this vector is in the coordinate frame of the avatar's parent object, or the world if none
|
|
LLVector3 up_vector = LLVector3::z_axis;
|
|
if (isAgentAvatarValid() &&
|
|
mAvatarObject->getParent() &&
|
|
mAvatarObject->mDrawable.notNull())
|
|
{
|
|
U32 camera_mode = gAgentCamera.getCameraAnimating() ? gAgentCamera.getLastCameraMode() : gAgentCamera.getCameraMode();
|
|
// and in third person...
|
|
if (camera_mode == CAMERA_MODE_THIRD_PERSON)
|
|
{
|
|
// make the up vector point to the absolute +z axis
|
|
up_vector = up_vector * ~((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation();
|
|
}
|
|
else if (camera_mode == CAMERA_MODE_MOUSELOOK)
|
|
{
|
|
// make the up vector point to the avatar's +z axis
|
|
up_vector = up_vector * mAvatarObject->mDrawable->getRotation();
|
|
}
|
|
}
|
|
|
|
return up_vector;
|
|
}
|
|
|
|
|
|
// Radians, positive is forward into ground
|
|
//-----------------------------------------------------------------------------
|
|
// pitch()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::pitch(F32 angle)
|
|
{
|
|
// don't let user pitch if pointed almost all the way down or up
|
|
mFrameAgent.pitch(clampPitchToLimits(angle));
|
|
}
|
|
|
|
|
|
// Radians, positive is forward into ground
|
|
//-----------------------------------------------------------------------------
|
|
// clampPitchToLimits()
|
|
//-----------------------------------------------------------------------------
|
|
F32 LLAgent::clampPitchToLimits(F32 angle)
|
|
{
|
|
// A dot B = mag(A) * mag(B) * cos(angle between A and B)
|
|
// so... cos(angle between A and B) = A dot B / mag(A) / mag(B)
|
|
// = A dot B for unit vectors
|
|
|
|
LLVector3 skyward = getReferenceUpVector();
|
|
|
|
F32 look_down_limit;
|
|
F32 look_up_limit = 10.f * DEG_TO_RAD;
|
|
|
|
F32 angle_from_skyward = acos( mFrameAgent.getAtAxis() * skyward );
|
|
|
|
if (isAgentAvatarValid() && mAvatarObject->isSitting())
|
|
{
|
|
look_down_limit = 130.f * DEG_TO_RAD;
|
|
}
|
|
else
|
|
{
|
|
look_down_limit = 170.f * DEG_TO_RAD;
|
|
}
|
|
|
|
// clamp pitch to limits
|
|
if ((angle >= 0.f) && (angle_from_skyward + angle > look_down_limit))
|
|
{
|
|
angle = look_down_limit - angle_from_skyward;
|
|
}
|
|
else if ((angle < 0.f) && (angle_from_skyward + angle < look_up_limit))
|
|
{
|
|
angle = look_up_limit - angle_from_skyward;
|
|
}
|
|
|
|
return angle;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// roll()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::roll(F32 angle)
|
|
{
|
|
mFrameAgent.roll(angle);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// yaw()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::yaw(F32 angle)
|
|
{
|
|
if (!rotateGrabbed())
|
|
{
|
|
mFrameAgent.rotate(angle, getReferenceUpVector());
|
|
}
|
|
}
|
|
|
|
|
|
// Returns a quat that represents the rotation of the agent in the absolute frame
|
|
//-----------------------------------------------------------------------------
|
|
// getQuat()
|
|
//-----------------------------------------------------------------------------
|
|
LLQuaternion LLAgent::getQuat() const
|
|
{
|
|
return mFrameAgent.getQuaternion();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getControlFlags()
|
|
//-----------------------------------------------------------------------------
|
|
U32 LLAgent::getControlFlags()
|
|
{
|
|
/*
|
|
// HACK -- avoids maintenance of control flags when camera mode is turned on or off,
|
|
// only worries about it when the flags are measured
|
|
if (mCameraMode == CAMERA_MODE_MOUSELOOK)
|
|
{
|
|
if ( !(mControlFlags & AGENT_CONTROL_MOUSELOOK) )
|
|
{
|
|
mControlFlags |= AGENT_CONTROL_MOUSELOOK;
|
|
}
|
|
}
|
|
*/
|
|
return mControlFlags;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// setControlFlags()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::setControlFlags(U32 mask)
|
|
{
|
|
U32 old_flags = mControlFlags;
|
|
mControlFlags |= mask;
|
|
mbFlagsDirty = mControlFlags ^ old_flags;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// clearControlFlags()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::clearControlFlags(U32 mask)
|
|
{
|
|
U32 old_flags = mControlFlags;
|
|
mControlFlags &= ~mask;
|
|
if (old_flags != mControlFlags)
|
|
{
|
|
mbFlagsDirty = TRUE;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// controlFlagsDirty()
|
|
//-----------------------------------------------------------------------------
|
|
BOOL LLAgent::controlFlagsDirty() const
|
|
{
|
|
return mbFlagsDirty;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// enableControlFlagReset()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::enableControlFlagReset()
|
|
{
|
|
mbFlagsNeedReset = TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// resetControlFlags()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::resetControlFlags()
|
|
{
|
|
if (mbFlagsNeedReset)
|
|
{
|
|
mbFlagsNeedReset = FALSE;
|
|
mbFlagsDirty = FALSE;
|
|
// reset all of the ephemeral flags
|
|
// some flags are managed elsewhere
|
|
mControlFlags &= AGENT_CONTROL_AWAY | AGENT_CONTROL_FLY | AGENT_CONTROL_MOUSELOOK;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// setAFK()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::setAFK()
|
|
{
|
|
// Drones can't go AFK
|
|
if (gNoRender)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!gAgent.getRegion())
|
|
{
|
|
// Don't set AFK if we're not talking to a region yet.
|
|
return;
|
|
}
|
|
|
|
if (!(mControlFlags & AGENT_CONTROL_AWAY))
|
|
{
|
|
sendAnimationRequest(ANIM_AGENT_AWAY, ANIM_REQUEST_START);
|
|
setControlFlags(AGENT_CONTROL_AWAY | AGENT_CONTROL_STOP);
|
|
gAwayTimer.start();
|
|
if (gAFKMenu)
|
|
{
|
|
//*TODO:Translate
|
|
gAFKMenu->setLabel(std::string("Set Not Away"));
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// clearAFK()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::clearAFK()
|
|
{
|
|
gAwayTriggerTimer.reset();
|
|
if (!gSavedSettings.controlExists("FakeAway")) gSavedSettings.declareBOOL("FakeAway", FALSE, "", NO_PERSIST);
|
|
if (gSavedSettings.getBOOL("FakeAway") == TRUE) return;
|
|
|
|
// Gods can sometimes get into away state (via gestures)
|
|
// without setting the appropriate control flag. JC
|
|
if (mControlFlags & AGENT_CONTROL_AWAY
|
|
|| (isAgentAvatarValid()
|
|
&& (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AWAY) != mAvatarObject->mSignaledAnimations.end())))
|
|
{
|
|
sendAnimationRequest(ANIM_AGENT_AWAY, ANIM_REQUEST_STOP);
|
|
clearControlFlags(AGENT_CONTROL_AWAY);
|
|
if (gAFKMenu)
|
|
{
|
|
//*TODO:Translate
|
|
gAFKMenu->setLabel(std::string("Set Away"));
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getAFK()
|
|
//-----------------------------------------------------------------------------
|
|
BOOL LLAgent::getAFK() const
|
|
{
|
|
return (mControlFlags & AGENT_CONTROL_AWAY) != 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// setBusy()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::setBusy()
|
|
{
|
|
sendAnimationRequest(ANIM_AGENT_BUSY, ANIM_REQUEST_START);
|
|
mIsBusy = TRUE;
|
|
if (gBusyMenu)
|
|
{
|
|
//*TODO:Translate
|
|
gBusyMenu->setLabel(std::string("Set Not Busy"));
|
|
}
|
|
LLFloaterMute::getInstance()->updateButtons();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// clearBusy()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::clearBusy()
|
|
{
|
|
mIsBusy = FALSE;
|
|
sendAnimationRequest(ANIM_AGENT_BUSY, ANIM_REQUEST_STOP);
|
|
if (gBusyMenu)
|
|
{
|
|
//*TODO:Translate
|
|
gBusyMenu->setLabel(std::string("Set Busy"));
|
|
}
|
|
LLFloaterMute::getInstance()->updateButtons();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getBusy()
|
|
//-----------------------------------------------------------------------------
|
|
BOOL LLAgent::getBusy() const
|
|
{
|
|
return mIsBusy;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// startAutoPilotGlobal()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::startAutoPilotGlobal(
|
|
const LLVector3d &target_global,
|
|
const std::string& behavior_name,
|
|
const LLQuaternion *target_rotation,
|
|
void (*finish_callback)(BOOL, void *),
|
|
void *callback_data,
|
|
F32 stop_distance, F32 rot_threshold)
|
|
{
|
|
if (!isAgentAvatarValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
mAutoPilotFinishedCallback = finish_callback;
|
|
mAutoPilotCallbackData = callback_data;
|
|
mAutoPilotRotationThreshold = rot_threshold;
|
|
mAutoPilotBehaviorName = behavior_name;
|
|
|
|
LLVector3d delta_pos( target_global );
|
|
delta_pos -= getPositionGlobal();
|
|
F64 distance = delta_pos.magVec();
|
|
LLVector3d trace_target = target_global;
|
|
|
|
trace_target.mdV[VZ] -= 10.f;
|
|
|
|
LLVector3d intersection;
|
|
LLVector3 normal;
|
|
LLViewerObject *hit_obj;
|
|
F32 heightDelta = LLWorld::getInstance()->resolveStepHeightGlobal(NULL, target_global, trace_target, intersection, normal, &hit_obj);
|
|
|
|
if (stop_distance > 0.f)
|
|
{
|
|
mAutoPilotStopDistance = stop_distance;
|
|
}
|
|
else
|
|
{
|
|
// Guess at a reasonable stop distance.
|
|
mAutoPilotStopDistance = fsqrtf( distance );
|
|
if (mAutoPilotStopDistance < 0.5f)
|
|
{
|
|
mAutoPilotStopDistance = 0.5f;
|
|
}
|
|
}
|
|
|
|
mAutoPilotFlyOnStop = getFlying();
|
|
|
|
if (distance > 30.0)
|
|
{
|
|
setFlying(TRUE);
|
|
}
|
|
|
|
if ( distance > 1.f && heightDelta > (sqrtf(mAutoPilotStopDistance) + 1.f))
|
|
{
|
|
setFlying(TRUE);
|
|
// Do not force flying for "Sit" behavior to prevent flying after pressing "Stand"
|
|
// from an object. See EXT-1655.
|
|
if ("Sit" != mAutoPilotBehaviorName)
|
|
mAutoPilotFlyOnStop = TRUE;
|
|
}
|
|
|
|
mAutoPilot = TRUE;
|
|
mAutoPilotTargetGlobal = target_global;
|
|
|
|
// trace ray down to find height of destination from ground
|
|
LLVector3d traceEndPt = target_global;
|
|
traceEndPt.mdV[VZ] -= 20.f;
|
|
|
|
LLVector3d targetOnGround;
|
|
LLVector3 groundNorm;
|
|
LLViewerObject *obj;
|
|
|
|
LLWorld::getInstance()->resolveStepHeightGlobal(NULL, target_global, traceEndPt, targetOnGround, groundNorm, &obj);
|
|
F64 target_height = llmax((F64)gAgent.getAvatarObject()->getPelvisToFoot(), target_global.mdV[VZ] - targetOnGround.mdV[VZ]);
|
|
|
|
// clamp z value of target to minimum height above ground
|
|
mAutoPilotTargetGlobal.mdV[VZ] = targetOnGround.mdV[VZ] + target_height;
|
|
mAutoPilotTargetDist = (F32)dist_vec(gAgent.getPositionGlobal(), mAutoPilotTargetGlobal);
|
|
if (target_rotation)
|
|
{
|
|
mAutoPilotUseRotation = TRUE;
|
|
mAutoPilotTargetFacing = LLVector3::x_axis * *target_rotation;
|
|
mAutoPilotTargetFacing.mV[VZ] = 0.f;
|
|
mAutoPilotTargetFacing.normalize();
|
|
}
|
|
else
|
|
{
|
|
mAutoPilotUseRotation = FALSE;
|
|
}
|
|
|
|
mAutoPilotNoProgressFrameCount = 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// startFollowPilot()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::startFollowPilot(const LLUUID &leader_id)
|
|
{
|
|
if (!mAutoPilot) return;
|
|
|
|
mLeaderID = leader_id;
|
|
if ( mLeaderID.isNull() ) return;
|
|
|
|
LLViewerObject* object = gObjectList.findObject(mLeaderID);
|
|
if (!object)
|
|
{
|
|
mLeaderID = LLUUID::null;
|
|
return;
|
|
}
|
|
|
|
startAutoPilotGlobal(object->getPositionGlobal());
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// stopAutoPilot()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::stopAutoPilot(BOOL user_cancel)
|
|
{
|
|
if (mAutoPilot)
|
|
{
|
|
mAutoPilot = FALSE;
|
|
if (mAutoPilotUseRotation && !user_cancel)
|
|
{
|
|
resetAxes(mAutoPilotTargetFacing);
|
|
}
|
|
// Restore previous flying state before invoking mAutoPilotFinishedCallback to allow
|
|
// callback function to change the flying state (like in near_sit_down_point()).
|
|
// If the user cancelled, don't change the fly state
|
|
if (!user_cancel)
|
|
{
|
|
setFlying(mAutoPilotFlyOnStop);
|
|
}
|
|
//NB: auto pilot can terminate for a reason other than reaching the destination
|
|
if (mAutoPilotFinishedCallback)
|
|
{
|
|
mAutoPilotFinishedCallback(!user_cancel && dist_vec_squared(gAgent.getPositionGlobal(), mAutoPilotTargetGlobal) < (mAutoPilotStopDistance * mAutoPilotStopDistance), mAutoPilotCallbackData);
|
|
}
|
|
mLeaderID = LLUUID::null;
|
|
|
|
setControlFlags(AGENT_CONTROL_STOP);
|
|
|
|
if (user_cancel && !mAutoPilotBehaviorName.empty())
|
|
{
|
|
if (mAutoPilotBehaviorName == "Sit")
|
|
LLNotifications::instance().add("CancelledSit");
|
|
else if (mAutoPilotBehaviorName == "Attach")
|
|
LLNotifications::instance().add("CancelledAttach");
|
|
else
|
|
LLNotifications::instance().add("Cancelled");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Returns necessary agent pitch and yaw changes, radians.
|
|
//-----------------------------------------------------------------------------
|
|
// autoPilot()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::autoPilot(F32 *delta_yaw)
|
|
{
|
|
if (mAutoPilot)
|
|
{
|
|
if (!mLeaderID.isNull())
|
|
{
|
|
LLViewerObject* object = gObjectList.findObject(mLeaderID);
|
|
if (!object)
|
|
{
|
|
stopAutoPilot();
|
|
return;
|
|
}
|
|
mAutoPilotTargetGlobal = object->getPositionGlobal();
|
|
}
|
|
|
|
if (!isAgentAvatarValid()) return;
|
|
|
|
if (mAvatarObject->mInAir)
|
|
{
|
|
setFlying(TRUE);
|
|
}
|
|
|
|
LLVector3 at;
|
|
at.setVec(mFrameAgent.getAtAxis());
|
|
LLVector3 target_agent = getPosAgentFromGlobal(mAutoPilotTargetGlobal);
|
|
LLVector3 direction = target_agent - getPositionAgent();
|
|
|
|
F32 target_dist = direction.magVec();
|
|
|
|
if (target_dist >= mAutoPilotTargetDist)
|
|
{
|
|
mAutoPilotNoProgressFrameCount++;
|
|
if (mAutoPilotNoProgressFrameCount > AUTOPILOT_MAX_TIME_NO_PROGRESS * gFPSClamped)
|
|
{
|
|
stopAutoPilot();
|
|
return;
|
|
}
|
|
}
|
|
|
|
mAutoPilotTargetDist = target_dist;
|
|
|
|
// Make this a two-dimensional solution
|
|
at.mV[VZ] = 0.f;
|
|
direction.mV[VZ] = 0.f;
|
|
|
|
at.normalize();
|
|
F32 xy_distance = direction.normalize();
|
|
|
|
F32 yaw = 0.f;
|
|
if (mAutoPilotTargetDist > mAutoPilotStopDistance)
|
|
{
|
|
yaw = angle_between(mFrameAgent.getAtAxis(), direction);
|
|
}
|
|
else if (mAutoPilotUseRotation)
|
|
{
|
|
// we're close now just aim at target facing
|
|
yaw = angle_between(at, mAutoPilotTargetFacing);
|
|
direction = mAutoPilotTargetFacing;
|
|
}
|
|
|
|
yaw = 4.f * yaw / gFPSClamped;
|
|
|
|
// figure out which direction to turn
|
|
LLVector3 scratch(at % direction);
|
|
|
|
if (scratch.mV[VZ] > 0.f)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_YAW_POS);
|
|
}
|
|
else
|
|
{
|
|
yaw = -yaw;
|
|
setControlFlags(AGENT_CONTROL_YAW_NEG);
|
|
}
|
|
|
|
*delta_yaw = yaw;
|
|
|
|
// Compute when to start slowing down and when to stop
|
|
F32 stop_distance = mAutoPilotStopDistance;
|
|
F32 slow_distance;
|
|
if (getFlying())
|
|
{
|
|
slow_distance = llmax(6.f, mAutoPilotStopDistance + 5.f);
|
|
stop_distance = llmax(2.f, mAutoPilotStopDistance);
|
|
}
|
|
else
|
|
{
|
|
slow_distance = llmax(3.f, mAutoPilotStopDistance + 2.f);
|
|
}
|
|
|
|
// If we're flying, handle autopilot points above or below you.
|
|
if (getFlying() && xy_distance < AUTOPILOT_HEIGHT_ADJUST_DISTANCE)
|
|
{
|
|
if (isAgentAvatarValid())
|
|
{
|
|
F64 current_height = mAvatarObject->getPositionGlobal().mdV[VZ];
|
|
F32 delta_z = (F32)(mAutoPilotTargetGlobal.mdV[VZ] - current_height);
|
|
F32 slope = delta_z / xy_distance;
|
|
if (slope > 0.45f && delta_z > 6.f)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_FAST_UP | AGENT_CONTROL_UP_POS);
|
|
}
|
|
else if (slope > 0.002f && delta_z > 0.5f)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_UP_POS);
|
|
}
|
|
else if (slope < -0.45f && delta_z < -6.f && current_height > AUTOPILOT_MIN_TARGET_HEIGHT_OFF_GROUND)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_FAST_UP | AGENT_CONTROL_UP_NEG);
|
|
}
|
|
else if (slope < -0.002f && delta_z < -0.5f && current_height > AUTOPILOT_MIN_TARGET_HEIGHT_OFF_GROUND)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_UP_NEG);
|
|
}
|
|
}
|
|
}
|
|
|
|
// calculate delta rotation to target heading
|
|
F32 delta_target_heading = angle_between(mFrameAgent.getAtAxis(), mAutoPilotTargetFacing);
|
|
|
|
if (xy_distance > slow_distance && yaw < (F_PI / 10.f))
|
|
{
|
|
// walking/flying fast
|
|
setControlFlags(AGENT_CONTROL_FAST_AT | AGENT_CONTROL_AT_POS);
|
|
}
|
|
else if (mAutoPilotTargetDist > mAutoPilotStopDistance)
|
|
{
|
|
// walking/flying slow
|
|
if (at * direction > 0.9f)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_AT_POS);
|
|
}
|
|
else if (at * direction < -0.9f)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_AT_NEG);
|
|
}
|
|
}
|
|
|
|
// check to see if we need to keep rotating to target orientation
|
|
if (mAutoPilotTargetDist < mAutoPilotStopDistance)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_STOP);
|
|
if(!mAutoPilotUseRotation || (delta_target_heading < mAutoPilotRotationThreshold))
|
|
{
|
|
stopAutoPilot();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// propagate()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::propagate(const F32 dt)
|
|
{
|
|
// Update UI based on agent motion
|
|
LLFloaterMove *floater_move = LLFloaterMove::getInstance();
|
|
if (floater_move)
|
|
{
|
|
floater_move->mForwardButton ->setToggleState( gAgentCamera.getAtKey() > 0 || gAgentCamera.getWalkKey() > 0 );
|
|
floater_move->mBackwardButton ->setToggleState( gAgentCamera.getAtKey() < 0 || gAgentCamera.getWalkKey() < 0 );
|
|
floater_move->mTurnLeftButton ->setToggleState( gAgentCamera.getYawKey() > 0.f );
|
|
floater_move->mTurnRightButton ->setToggleState( gAgentCamera.getYawKey() < 0.f );
|
|
floater_move->mSlideLeftButton ->setToggleState( gAgentCamera.getLeftKey() > 0.f );
|
|
floater_move->mSlideRightButton ->setToggleState( gAgentCamera.getLeftKey() < 0.f );
|
|
floater_move->mMoveUpButton ->setToggleState( gAgentCamera.getUpKey() > 0 );
|
|
floater_move->mMoveDownButton ->setToggleState( gAgentCamera.getUpKey() < 0 );
|
|
}
|
|
|
|
// handle rotation based on keyboard levels
|
|
const F32 YAW_RATE = 90.f * DEG_TO_RAD; // radians per second
|
|
yaw(YAW_RATE * gAgentCamera.getYawKey() * dt);
|
|
|
|
const F32 PITCH_RATE = 90.f * DEG_TO_RAD; // radians per second
|
|
pitch(PITCH_RATE * gAgentCamera.getPitchKey() * dt);
|
|
|
|
// handle auto-land behavior
|
|
if (mAvatarObject.notNull())
|
|
{
|
|
BOOL in_air = mAvatarObject->mInAir;
|
|
LLVector3 land_vel = getVelocity();
|
|
land_vel.mV[VZ] = 0.f;
|
|
|
|
if (!in_air
|
|
&& gAgentCamera.getUpKey() < 0
|
|
&& land_vel.magVecSquared() < MAX_VELOCITY_AUTO_LAND_SQUARED
|
|
&& gSavedSettings.getBOOL("AutomaticFly"))
|
|
{
|
|
// land automatically
|
|
setFlying(FALSE);
|
|
}
|
|
}
|
|
|
|
gAgentCamera.clearGeneralKeys();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// updateAgentPosition()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::updateAgentPosition(const F32 dt, const F32 yaw_radians, const S32 mouse_x, const S32 mouse_y)
|
|
{
|
|
propagate(dt);
|
|
|
|
// static S32 cameraUpdateCount = 0;
|
|
|
|
rotate(yaw_radians, 0, 0, 1);
|
|
|
|
//
|
|
// Check for water and land collision, set underwater flag
|
|
//
|
|
|
|
gAgentCamera.updateLookAt(mouse_x, mouse_y);
|
|
}
|
|
|
|
|
|
// friends and operators
|
|
|
|
std::ostream& operator<<(std::ostream &s, const LLAgent &agent)
|
|
{
|
|
// This is unfinished, but might never be used.
|
|
// We'll just leave it for now; we can always delete it.
|
|
s << " { "
|
|
<< " Frame = " << agent.mFrameAgent << "\n"
|
|
<< " }";
|
|
return s;
|
|
}
|
|
|
|
|
|
// ------------------- Beginning of legacy LLCamera hack ----------------------
|
|
// This section is included for legacy LLCamera support until
|
|
// it is no longer needed. Some legacy code must exist in
|
|
// non-legacy functions, and is labeled with "// legacy" comments.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// setAvatarObject()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::setAvatarObject(LLVOAvatar *avatar)
|
|
{
|
|
mAvatarObject = avatar;
|
|
|
|
if (!avatar)
|
|
{
|
|
llinfos << "Setting LLAgent::mAvatarObject to NULL" << llendl;
|
|
return;
|
|
}
|
|
gAgentCamera.setAvatarObject(avatar);
|
|
sendAgentWearablesRequest();
|
|
}
|
|
|
|
// TRUE if your own avatar needs to be rendered. Usually only
|
|
// in third person and build.
|
|
//-----------------------------------------------------------------------------
|
|
// needsRenderAvatar()
|
|
//-----------------------------------------------------------------------------
|
|
BOOL LLAgent::needsRenderAvatar()
|
|
{
|
|
if (gAgentCamera.cameraMouselook() && !LLVOAvatar::sVisibleInFirstPerson)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return mShowAvatar && mGenderChosen;
|
|
}
|
|
|
|
// TRUE if we need to render your own avatar's head.
|
|
BOOL LLAgent::needsRenderHead()
|
|
{
|
|
return (LLVOAvatar::sVisibleInFirstPerson && LLPipeline::sReflectionRender) || (mShowAvatar && !gAgentCamera.cameraMouselook());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// startTyping()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::startTyping()
|
|
{
|
|
if (gSavedSettings.getBOOL("FakeAway"))
|
|
return;
|
|
mTypingTimer.reset();
|
|
|
|
if (getRenderState() & AGENT_STATE_TYPING)
|
|
{
|
|
// already typing, don't trigger a different animation
|
|
return;
|
|
}
|
|
setRenderState(AGENT_STATE_TYPING);
|
|
|
|
if (mChatTimer.getElapsedTimeF32() < 2.f)
|
|
{
|
|
LLVOAvatar* chatter = gObjectList.findAvatar(mLastChatterID);
|
|
if (chatter)
|
|
{
|
|
gAgentCamera.setLookAt(LOOKAT_TARGET_RESPOND, chatter, LLVector3::zero);
|
|
}
|
|
}
|
|
|
|
if (gSavedSettings.getBOOL("PlayTypingAnim"))
|
|
{
|
|
sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_START);
|
|
}
|
|
gChatBar->sendChatFromViewer("", CHAT_TYPE_START, FALSE);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// stopTyping()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::stopTyping()
|
|
{
|
|
if (mRenderState & AGENT_STATE_TYPING)
|
|
{
|
|
clearRenderState(AGENT_STATE_TYPING);
|
|
sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_STOP);
|
|
gChatBar->sendChatFromViewer("", CHAT_TYPE_STOP, FALSE);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// setRenderState()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::setRenderState(U8 newstate)
|
|
{
|
|
mRenderState |= newstate;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// clearRenderState()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::clearRenderState(U8 clearstate)
|
|
{
|
|
mRenderState &= ~clearstate;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getRenderState()
|
|
//-----------------------------------------------------------------------------
|
|
U8 LLAgent::getRenderState()
|
|
{
|
|
if (gNoRender || gKeyboard == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// *FIX: don't do stuff in a getter! This is infinite loop city!
|
|
if ((mTypingTimer.getElapsedTimeF32() > TYPING_TIMEOUT_SECS)
|
|
&& (mRenderState & AGENT_STATE_TYPING))
|
|
{
|
|
stopTyping();
|
|
}
|
|
|
|
if ((!LLSelectMgr::getInstance()->getSelection()->isEmpty() && LLSelectMgr::getInstance()->shouldShowSelection())
|
|
|| LLToolMgr::getInstance()->getCurrentTool()->isEditing() )
|
|
{
|
|
setRenderState(AGENT_STATE_EDITING);
|
|
}
|
|
else
|
|
{
|
|
clearRenderState(AGENT_STATE_EDITING);
|
|
}
|
|
|
|
return mRenderState;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static const LLFloaterView::skip_list_t& get_skip_list()
|
|
{
|
|
static LLFloaterView::skip_list_t skip_list;
|
|
skip_list.insert(LLFloaterMap::getInstance());
|
|
return skip_list;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// endAnimationUpdateUI()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::endAnimationUpdateUI()
|
|
{
|
|
if (gAgentCamera.getCameraMode() == gAgentCamera.getLastCameraMode())
|
|
{
|
|
// We're already done endAnimationUpdateUI for this transition.
|
|
return;
|
|
}
|
|
|
|
// clean up UI from mode we're leaving
|
|
if (gAgentCamera.getLastCameraMode() == CAMERA_MODE_MOUSELOOK )
|
|
{
|
|
// show mouse cursor
|
|
gViewerWindow->showCursor();
|
|
// show menus
|
|
gMenuBarView->setVisible(TRUE);
|
|
gStatusBar->setVisibleForMouselook(true);
|
|
|
|
LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset);
|
|
|
|
// Only pop if we have pushed...
|
|
if (TRUE == mViewsPushed)
|
|
{
|
|
mViewsPushed = FALSE;
|
|
gFloaterView->popVisibleAll(get_skip_list());
|
|
}
|
|
|
|
gAgentCamera.setLookAt(LOOKAT_TARGET_CLEAR);
|
|
if( gMorphView )
|
|
{
|
|
gMorphView->setVisible( FALSE );
|
|
}
|
|
|
|
// Disable mouselook-specific animations
|
|
if (isAgentAvatarValid())
|
|
{
|
|
if( mAvatarObject->isAnyAnimationSignaled(AGENT_GUN_AIM_ANIMS, NUM_AGENT_GUN_AIM_ANIMS) )
|
|
{
|
|
if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AIM_RIFLE_R) != mAvatarObject->mSignaledAnimations.end())
|
|
{
|
|
sendAnimationRequest(ANIM_AGENT_AIM_RIFLE_R, ANIM_REQUEST_STOP);
|
|
sendAnimationRequest(ANIM_AGENT_HOLD_RIFLE_R, ANIM_REQUEST_START);
|
|
}
|
|
if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AIM_HANDGUN_R) != mAvatarObject->mSignaledAnimations.end())
|
|
{
|
|
sendAnimationRequest(ANIM_AGENT_AIM_HANDGUN_R, ANIM_REQUEST_STOP);
|
|
sendAnimationRequest(ANIM_AGENT_HOLD_HANDGUN_R, ANIM_REQUEST_START);
|
|
}
|
|
if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AIM_BAZOOKA_R) != mAvatarObject->mSignaledAnimations.end())
|
|
{
|
|
sendAnimationRequest(ANIM_AGENT_AIM_BAZOOKA_R, ANIM_REQUEST_STOP);
|
|
sendAnimationRequest(ANIM_AGENT_HOLD_BAZOOKA_R, ANIM_REQUEST_START);
|
|
}
|
|
if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AIM_BOW_L) != mAvatarObject->mSignaledAnimations.end())
|
|
{
|
|
sendAnimationRequest(ANIM_AGENT_AIM_BOW_L, ANIM_REQUEST_STOP);
|
|
sendAnimationRequest(ANIM_AGENT_HOLD_BOW_L, ANIM_REQUEST_START);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (gAgentCamera.getLastCameraMode() == CAMERA_MODE_CUSTOMIZE_AVATAR)
|
|
{
|
|
// make sure we ask to save changes
|
|
|
|
LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset);
|
|
|
|
// HACK: If we're quitting, and we were in customize avatar, don't
|
|
// let the mini-map go visible again. JC
|
|
if (!LLAppViewer::instance()->quitRequested())
|
|
{
|
|
LLFloaterMap::getInstance()->popVisible();
|
|
}
|
|
|
|
if( gMorphView )
|
|
{
|
|
gMorphView->setVisible( FALSE );
|
|
}
|
|
|
|
if (isAgentAvatarValid())
|
|
{
|
|
if(mCustomAnim)
|
|
{
|
|
sendAnimationRequest(ANIM_AGENT_CUSTOMIZE, ANIM_REQUEST_STOP);
|
|
sendAnimationRequest(ANIM_AGENT_CUSTOMIZE_DONE, ANIM_REQUEST_START);
|
|
|
|
mCustomAnim = FALSE ;
|
|
}
|
|
|
|
}
|
|
gAgentCamera.setLookAt(LOOKAT_TARGET_CLEAR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Set up UI for mode we're entering
|
|
//---------------------------------------------------------------------
|
|
if (gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK)
|
|
{
|
|
// hide menus
|
|
gMenuBarView->setVisible(FALSE);
|
|
gStatusBar->setVisibleForMouselook(false);
|
|
|
|
// clear out camera lag effect
|
|
gAgentCamera.clearCameraLag();
|
|
|
|
// JC - Added for always chat in third person option
|
|
gFocusMgr.setKeyboardFocus(NULL);
|
|
|
|
LLToolMgr::getInstance()->setCurrentToolset(gMouselookToolset);
|
|
|
|
mViewsPushed = TRUE;
|
|
|
|
gFloaterView->pushVisibleAll(FALSE, get_skip_list());
|
|
|
|
if( gMorphView )
|
|
{
|
|
gMorphView->setVisible(FALSE);
|
|
}
|
|
|
|
gConsole->setVisible( TRUE );
|
|
|
|
if (isAgentAvatarValid())
|
|
{
|
|
// Trigger mouselook-specific animations
|
|
if( mAvatarObject->isAnyAnimationSignaled(AGENT_GUN_HOLD_ANIMS, NUM_AGENT_GUN_HOLD_ANIMS) )
|
|
{
|
|
if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_HOLD_RIFLE_R) != mAvatarObject->mSignaledAnimations.end())
|
|
{
|
|
sendAnimationRequest(ANIM_AGENT_HOLD_RIFLE_R, ANIM_REQUEST_STOP);
|
|
sendAnimationRequest(ANIM_AGENT_AIM_RIFLE_R, ANIM_REQUEST_START);
|
|
}
|
|
if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_HOLD_HANDGUN_R) != mAvatarObject->mSignaledAnimations.end())
|
|
{
|
|
sendAnimationRequest(ANIM_AGENT_HOLD_HANDGUN_R, ANIM_REQUEST_STOP);
|
|
sendAnimationRequest(ANIM_AGENT_AIM_HANDGUN_R, ANIM_REQUEST_START);
|
|
}
|
|
if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_HOLD_BAZOOKA_R) != mAvatarObject->mSignaledAnimations.end())
|
|
{
|
|
sendAnimationRequest(ANIM_AGENT_HOLD_BAZOOKA_R, ANIM_REQUEST_STOP);
|
|
sendAnimationRequest(ANIM_AGENT_AIM_BAZOOKA_R, ANIM_REQUEST_START);
|
|
}
|
|
if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_HOLD_BOW_L) != mAvatarObject->mSignaledAnimations.end())
|
|
{
|
|
sendAnimationRequest(ANIM_AGENT_HOLD_BOW_L, ANIM_REQUEST_STOP);
|
|
sendAnimationRequest(ANIM_AGENT_AIM_BOW_L, ANIM_REQUEST_START);
|
|
}
|
|
}
|
|
if (mAvatarObject->getParent())
|
|
{
|
|
LLVector3 at_axis = LLViewerCamera::getInstance()->getAtAxis();
|
|
LLViewerObject* root_object = (LLViewerObject*)mAvatarObject->getRoot();
|
|
if (root_object->flagCameraDecoupled())
|
|
{
|
|
resetAxes(at_axis);
|
|
}
|
|
else
|
|
{
|
|
resetAxes(at_axis * ~((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation());
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
else if (gAgentCamera.getCameraMode() == CAMERA_MODE_CUSTOMIZE_AVATAR)
|
|
{
|
|
LLToolMgr::getInstance()->setCurrentToolset(gFaceEditToolset);
|
|
|
|
LLFloaterMap::getInstance()->pushVisible(FALSE);
|
|
/*
|
|
LLView *view;
|
|
for (view = gFloaterView->getFirstChild(); view; view = gFloaterView->getNextChild())
|
|
{
|
|
view->pushVisible(FALSE);
|
|
}
|
|
*/
|
|
|
|
if( gMorphView )
|
|
{
|
|
gMorphView->setVisible( TRUE );
|
|
}
|
|
|
|
// freeze avatar
|
|
if (isAgentAvatarValid())
|
|
{
|
|
// <edit>
|
|
//mPauseRequest = mAvatarObject->requestPause();
|
|
// </edit>
|
|
}
|
|
}
|
|
|
|
if (isAgentAvatarValid())
|
|
{
|
|
gAgent.getAvatarObject()->updateAttachmentVisibility(gAgentCamera.getCameraMode());
|
|
}
|
|
|
|
gFloaterTools->dirty();
|
|
|
|
// Don't let this be called more than once if the camera
|
|
// mode hasn't changed. --JC
|
|
gAgentCamera.updateLastCamera();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// heardChat()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::heardChat(const LLUUID& id)
|
|
{
|
|
// log text and voice chat to speaker mgr
|
|
// for keeping track of active speakers, etc.
|
|
LLLocalSpeakerMgr::getInstance()->speakerChatted(id);
|
|
|
|
// don't respond to your own voice
|
|
if (id == getID()) return;
|
|
|
|
if (ll_rand(2) == 0)
|
|
{
|
|
LLViewerObject *chatter = gObjectList.findObject(mLastChatterID);
|
|
gAgentCamera.setLookAt(LOOKAT_TARGET_AUTO_LISTEN, chatter, LLVector3::zero);
|
|
}
|
|
|
|
mLastChatterID = id;
|
|
mChatTimer.reset();
|
|
}
|
|
|
|
const F32 SIT_POINT_EXTENTS = 0.2f;
|
|
|
|
LLSD ll_sdmap_from_vector3(const LLVector3& vec)
|
|
{
|
|
LLSD ret;
|
|
ret["X"] = vec.mV[VX];
|
|
ret["Y"] = vec.mV[VY];
|
|
ret["Z"] = vec.mV[VZ];
|
|
return ret;
|
|
}
|
|
|
|
LLVector3 ll_vector3_from_sdmap(const LLSD& sd)
|
|
{
|
|
LLVector3 ret;
|
|
ret.mV[VX] = F32(sd["X"].asReal());
|
|
ret.mV[VY] = F32(sd["Y"].asReal());
|
|
ret.mV[VZ] = F32(sd["Z"].asReal());
|
|
return ret;
|
|
}
|
|
|
|
void LLAgent::setStartPosition( U32 location_id )
|
|
{
|
|
if (gAgentID == LLUUID::null)
|
|
{
|
|
return;
|
|
}
|
|
if (gObjectList.findAvatar(gAgentID) == NULL)
|
|
{
|
|
llinfos << "setStartPosition - Can't find agent viewerobject id " << gAgentID << llendl;
|
|
return;
|
|
}
|
|
// we've got the viewer object
|
|
// Sometimes the agent can be velocity interpolated off of
|
|
// this simulator. Clamp it to the region the agent is
|
|
// in, a little bit in on each side.
|
|
const F32 INSET = 0.5f; //meters
|
|
const F32 REGION_WIDTH = LLWorld::getInstance()->getRegionWidthInMeters();
|
|
|
|
LLVector3 agent_pos = getPositionAgent();
|
|
|
|
if (isAgentAvatarValid())
|
|
{
|
|
// the z height is at the agent's feet
|
|
agent_pos.mV[VZ] -= 0.5f * mAvatarObject->mBodySize.mV[VZ];
|
|
}
|
|
|
|
agent_pos.mV[VX] = llclamp( agent_pos.mV[VX], INSET, REGION_WIDTH - INSET );
|
|
agent_pos.mV[VY] = llclamp( agent_pos.mV[VY], INSET, REGION_WIDTH - INSET );
|
|
|
|
// Don't let them go below ground, or too high.
|
|
agent_pos.mV[VZ] = llclamp( agent_pos.mV[VZ],
|
|
mRegionp->getLandHeightRegion( agent_pos ),
|
|
LLWorld::getInstance()->getRegionMaxHeight() );
|
|
std::string url = gAgent.getRegion()->getCapability("HomeLocation");
|
|
if( !url.empty() )
|
|
{
|
|
// Send the CapReq
|
|
LLSD request;
|
|
LLSD body;
|
|
LLSD homeLocation;
|
|
|
|
homeLocation["LocationId"] = LLSD::Integer(location_id);
|
|
homeLocation["LocationPos"] = ll_sdmap_from_vector3(agent_pos);
|
|
homeLocation["LocationLookAt"] = ll_sdmap_from_vector3(mFrameAgent.getAtAxis());
|
|
|
|
body["HomeLocation"] = homeLocation;
|
|
|
|
LLHTTPClient::post( url, body, new LLHomeLocationResponder() );
|
|
}
|
|
else
|
|
{
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_SetStartLocationRequest);
|
|
msg->nextBlockFast( _PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
|
|
msg->nextBlockFast( _PREHASH_StartLocationData);
|
|
// corrected by sim
|
|
msg->addStringFast(_PREHASH_SimName, "");
|
|
msg->addU32Fast(_PREHASH_LocationID, location_id);
|
|
msg->addVector3Fast(_PREHASH_LocationPos, agent_pos);
|
|
msg->addVector3Fast(_PREHASH_LocationLookAt,mFrameAgent.getAtAxis());
|
|
|
|
// Reliable only helps when setting home location. Last
|
|
// location is sent on quit, and we don't have time to ack
|
|
// the packets.
|
|
msg->sendReliable(mRegionp->getHost());
|
|
}
|
|
const U32 HOME_INDEX = 1;
|
|
if( HOME_INDEX == location_id )
|
|
{
|
|
setHomePosRegion( mRegionp->getHandle(), getPositionAgent() );
|
|
}
|
|
}
|
|
|
|
void LLAgent::requestStopMotion( LLMotion* motion )
|
|
{
|
|
// Notify all avatars that a motion has stopped.
|
|
// This is needed to clear the animation state bits
|
|
LLUUID anim_state = motion->getID();
|
|
onAnimStop(motion->getID());
|
|
|
|
// if motion is not looping, it could have stopped by running out of time
|
|
// so we need to tell the server this
|
|
// llinfos << "Sending stop for motion " << motion->getName() << llendl;
|
|
sendAnimationRequest( anim_state, ANIM_REQUEST_STOP );
|
|
}
|
|
|
|
void LLAgent::onAnimStop(const LLUUID& id)
|
|
{
|
|
// handle automatic state transitions (based on completion of animation playback)
|
|
if (id == ANIM_AGENT_STAND)
|
|
{
|
|
stopFidget();
|
|
}
|
|
else if (id == ANIM_AGENT_AWAY)
|
|
{
|
|
//clearAFK();
|
|
// [RLVa:KB] - Checked: 2009-10-19 (RLVa-1.1.0g) | Added: RLVa-1.1.0g
|
|
#ifdef RLV_EXTENSION_CMD_ALLOWIDLE
|
|
if (!gRlvHandler.hasBehaviour(RLV_BHVR_ALLOWIDLE))
|
|
clearAFK();
|
|
#else
|
|
clearAFK();
|
|
#endif // RLV_EXTENSION_CMD_ALLOWIDLE
|
|
// [/RLVa:KB]
|
|
}
|
|
else if (id == ANIM_AGENT_STANDUP)
|
|
{
|
|
// send stand up command
|
|
setControlFlags(AGENT_CONTROL_FINISH_ANIM);
|
|
|
|
// now trigger dusting self off animation
|
|
if (isAgentAvatarValid() && !mAvatarObject->mBelowWater && rand() % 3 == 0)
|
|
sendAnimationRequest( ANIM_AGENT_BRUSH, ANIM_REQUEST_START );
|
|
}
|
|
else if (id == ANIM_AGENT_PRE_JUMP || id == ANIM_AGENT_LAND || id == ANIM_AGENT_MEDIUM_LAND)
|
|
{
|
|
setControlFlags(AGENT_CONTROL_FINISH_ANIM);
|
|
}
|
|
}
|
|
|
|
bool LLAgent::isGodlike() const
|
|
{
|
|
return mAgentAccess->isGodlike();
|
|
}
|
|
|
|
U8 LLAgent::getGodLevel() const
|
|
{
|
|
return mAgentAccess->getGodLevel();
|
|
}
|
|
|
|
bool LLAgent::wantsPGOnly() const
|
|
{
|
|
return mAgentAccess->wantsPGOnly();
|
|
}
|
|
|
|
bool LLAgent::canAccessMature() const
|
|
{
|
|
return mAgentAccess->canAccessMature();
|
|
}
|
|
|
|
bool LLAgent::canAccessAdult() const
|
|
{
|
|
return mAgentAccess->canAccessAdult();
|
|
}
|
|
|
|
bool LLAgent::canAccessMaturityInRegion( U64 region_handle ) const
|
|
{
|
|
LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle( region_handle );
|
|
if( regionp )
|
|
{
|
|
switch( regionp->getSimAccess() )
|
|
{
|
|
case SIM_ACCESS_MATURE:
|
|
if( !canAccessMature() )
|
|
return false;
|
|
break;
|
|
case SIM_ACCESS_ADULT:
|
|
if( !canAccessAdult() )
|
|
return false;
|
|
break;
|
|
default:
|
|
// Oh, go on and hear the silly noises.
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool LLAgent::canAccessMaturityAtGlobal( LLVector3d pos_global ) const
|
|
{
|
|
U64 region_handle = to_region_handle_global( pos_global.mdV[0], pos_global.mdV[1] );
|
|
return canAccessMaturityInRegion( region_handle );
|
|
}
|
|
|
|
bool LLAgent::prefersPG() const
|
|
{
|
|
return mAgentAccess->prefersPG();
|
|
}
|
|
|
|
bool LLAgent::prefersMature() const
|
|
{
|
|
return mAgentAccess->prefersMature();
|
|
}
|
|
|
|
bool LLAgent::prefersAdult() const
|
|
{
|
|
return mAgentAccess->prefersAdult();
|
|
}
|
|
|
|
bool LLAgent::isTeen() const
|
|
{
|
|
return mAgentAccess->isTeen();
|
|
}
|
|
|
|
bool LLAgent::isMature() const
|
|
{
|
|
return mAgentAccess->isMature();
|
|
}
|
|
|
|
bool LLAgent::isAdult() const
|
|
{
|
|
return mAgentAccess->isAdult();
|
|
}
|
|
|
|
void LLAgent::setTeen(bool teen)
|
|
{
|
|
mAgentAccess->setTeen(teen);
|
|
}
|
|
|
|
//static
|
|
int LLAgent::convertTextToMaturity(char text)
|
|
{
|
|
return LLAgentAccess::convertTextToMaturity(text);
|
|
}
|
|
|
|
bool LLAgent::sendMaturityPreferenceToServer(int preferredMaturity)
|
|
{
|
|
if (!getRegion())
|
|
return false;
|
|
|
|
// Update agent access preference on the server
|
|
std::string url = getRegion()->getCapability("UpdateAgentInformation");
|
|
if (!url.empty())
|
|
{
|
|
// Set new access preference
|
|
LLSD access_prefs = LLSD::emptyMap();
|
|
if (preferredMaturity == SIM_ACCESS_PG)
|
|
{
|
|
access_prefs["max"] = "PG";
|
|
}
|
|
else if (preferredMaturity == SIM_ACCESS_MATURE)
|
|
{
|
|
access_prefs["max"] = "M";
|
|
}
|
|
if (preferredMaturity == SIM_ACCESS_ADULT)
|
|
{
|
|
access_prefs["max"] = "A";
|
|
}
|
|
|
|
LLSD body = LLSD::emptyMap();
|
|
body["access_prefs"] = access_prefs;
|
|
llinfos << "Sending access prefs update to " << (access_prefs["max"].asString()) << " via capability to: "
|
|
<< url << llendl;
|
|
LLHTTPClient::post(url, body, new LLHTTPClient::Responder()); // Ignore response
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
BOOL LLAgent::getAdminOverride() const
|
|
{
|
|
return mAgentAccess->getAdminOverride();
|
|
}
|
|
|
|
void LLAgent::setMaturity(char text)
|
|
{
|
|
mAgentAccess->setMaturity(text);
|
|
}
|
|
|
|
void LLAgent::setAdminOverride(BOOL b)
|
|
{
|
|
mAgentAccess->setAdminOverride(b);
|
|
}
|
|
|
|
void LLAgent::setGodLevel(U8 god_level)
|
|
{
|
|
mAgentAccess->setGodLevel(god_level);
|
|
}
|
|
|
|
void LLAgent::setAOTransition()
|
|
{
|
|
mAgentAccess->setTransition();
|
|
}
|
|
|
|
const LLAgentAccess& LLAgent::getAgentAccess()
|
|
{
|
|
return *mAgentAccess;
|
|
}
|
|
|
|
|
|
void LLAgent::buildFullname(std::string& name) const
|
|
{
|
|
if (isAgentAvatarValid())
|
|
{
|
|
name = mAvatarObject->getFullname();
|
|
}
|
|
}
|
|
|
|
void LLAgent::buildFullnameAndTitle(std::string& name) const
|
|
{
|
|
if (isGroupMember())
|
|
{
|
|
name = mGroupTitle;
|
|
name += ' ';
|
|
}
|
|
else
|
|
{
|
|
name.erase(0, name.length());
|
|
}
|
|
|
|
if (isAgentAvatarValid())
|
|
{
|
|
name += mAvatarObject->getFullname();
|
|
}
|
|
}
|
|
|
|
BOOL LLAgent::isInGroup(const LLUUID& group_id) const
|
|
{
|
|
if (isGodlike())
|
|
return true;
|
|
|
|
S32 count = mGroups.count();
|
|
for(S32 i = 0; i < count; ++i)
|
|
{
|
|
if(mGroups.get(i).mID == group_id)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// This implementation should mirror LLAgentInfo::hasPowerInGroup
|
|
BOOL LLAgent::hasPowerInGroup(const LLUUID& group_id, U64 power) const
|
|
{
|
|
if (isGodlike())
|
|
return true;
|
|
|
|
// GP_NO_POWERS can also mean no power is enough to grant an ability.
|
|
if (GP_NO_POWERS == power) return FALSE;
|
|
|
|
S32 count = mGroups.count();
|
|
for(S32 i = 0; i < count; ++i)
|
|
{
|
|
if(mGroups.get(i).mID == group_id)
|
|
{
|
|
return (BOOL)((mGroups.get(i).mPowers & power) > 0);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL LLAgent::hasPowerInActiveGroup(U64 power) const
|
|
{
|
|
return (mGroupID.notNull() && (hasPowerInGroup(mGroupID, power)));
|
|
}
|
|
|
|
U64 LLAgent::getPowerInGroup(const LLUUID& group_id) const
|
|
{
|
|
if (isGodlike())
|
|
return GP_ALL_POWERS;
|
|
|
|
S32 count = mGroups.count();
|
|
for(S32 i = 0; i < count; ++i)
|
|
{
|
|
if(mGroups.get(i).mID == group_id)
|
|
{
|
|
return (mGroups.get(i).mPowers);
|
|
}
|
|
}
|
|
|
|
return GP_NO_POWERS;
|
|
}
|
|
|
|
BOOL LLAgent::getGroupData(const LLUUID& group_id, LLGroupData& data) const
|
|
{
|
|
S32 count = mGroups.count();
|
|
for(S32 i = 0; i < count; ++i)
|
|
{
|
|
if(mGroups.get(i).mID == group_id)
|
|
{
|
|
data = mGroups.get(i);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
S32 LLAgent::getGroupContribution(const LLUUID& group_id) const
|
|
{
|
|
S32 count = mGroups.count();
|
|
for(S32 i = 0; i < count; ++i)
|
|
{
|
|
if(mGroups.get(i).mID == group_id)
|
|
{
|
|
S32 contribution = mGroups.get(i).mContribution;
|
|
return contribution;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
BOOL LLAgent::setGroupContribution(const LLUUID& group_id, S32 contribution)
|
|
{
|
|
S32 count = mGroups.count();
|
|
for(S32 i = 0; i < count; ++i)
|
|
{
|
|
if(mGroups.get(i).mID == group_id)
|
|
{
|
|
mGroups.get(i).mContribution = contribution;
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessage("SetGroupContribution");
|
|
msg->nextBlock("AgentData");
|
|
msg->addUUID("AgentID", gAgentID);
|
|
msg->addUUID("SessionID", gAgentSessionID);
|
|
msg->nextBlock("Data");
|
|
msg->addUUID("GroupID", group_id);
|
|
msg->addS32("Contribution", contribution);
|
|
sendReliableMessage();
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL LLAgent::setUserGroupFlags(const LLUUID& group_id, BOOL accept_notices, BOOL list_in_profile)
|
|
{
|
|
S32 count = mGroups.count();
|
|
for(S32 i = 0; i < count; ++i)
|
|
{
|
|
if(mGroups.get(i).mID == group_id)
|
|
{
|
|
mGroups.get(i).mAcceptNotices = accept_notices;
|
|
mGroups.get(i).mListInProfile = list_in_profile;
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessage("SetGroupAcceptNotices");
|
|
msg->nextBlock("AgentData");
|
|
msg->addUUID("AgentID", gAgentID);
|
|
msg->addUUID("SessionID", gAgentSessionID);
|
|
msg->nextBlock("Data");
|
|
msg->addUUID("GroupID", group_id);
|
|
msg->addBOOL("AcceptNotices", accept_notices);
|
|
msg->nextBlock("NewData");
|
|
msg->addBOOL("ListInProfile", list_in_profile);
|
|
sendReliableMessage();
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// utility to build a location string
|
|
void LLAgent::buildLocationString(std::string& str)
|
|
{
|
|
// [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.0.0a)
|
|
if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC))
|
|
{
|
|
str = RlvStrings::getString(RLV_STRING_HIDDEN);
|
|
return;
|
|
}
|
|
// [/RLVa:KB]
|
|
|
|
const LLVector3& agent_pos_region = getPositionAgent();
|
|
S32 pos_x = S32(agent_pos_region.mV[VX]);
|
|
S32 pos_y = S32(agent_pos_region.mV[VY]);
|
|
S32 pos_z = S32(agent_pos_region.mV[VZ]);
|
|
|
|
// Round the numbers based on the velocity
|
|
LLVector3 agent_velocity = getVelocity();
|
|
F32 velocity_mag_sq = agent_velocity.magVecSquared();
|
|
|
|
const F32 FLY_CUTOFF = 6.f; // meters/sec
|
|
const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF;
|
|
const F32 WALK_CUTOFF = 1.5f; // meters/sec
|
|
const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF;
|
|
|
|
if (velocity_mag_sq > FLY_CUTOFF_SQ)
|
|
{
|
|
pos_x -= pos_x % 4;
|
|
pos_y -= pos_y % 4;
|
|
}
|
|
else if (velocity_mag_sq > WALK_CUTOFF_SQ)
|
|
{
|
|
pos_x -= pos_x % 2;
|
|
pos_y -= pos_y % 2;
|
|
}
|
|
|
|
// create a defult name and description for the landmark
|
|
std::string buffer;
|
|
if( LLViewerParcelMgr::getInstance()->getAgentParcelName().empty() )
|
|
{
|
|
// the parcel doesn't have a name
|
|
buffer = llformat("%.32s (%d, %d, %d)",
|
|
getRegion()->getName().c_str(),
|
|
pos_x, pos_y, pos_z);
|
|
}
|
|
else
|
|
{
|
|
// the parcel has a name, so include it in the landmark name
|
|
buffer = llformat("%.32s, %.32s (%d, %d, %d)",
|
|
LLViewerParcelMgr::getInstance()->getAgentParcelName().c_str(),
|
|
getRegion()->getName().c_str(),
|
|
pos_x, pos_y, pos_z);
|
|
}
|
|
str = buffer;
|
|
}
|
|
|
|
LLQuaternion LLAgent::getHeadRotation()
|
|
{
|
|
if (!isAgentAvatarValid() || !mAvatarObject->mPelvisp || !mAvatarObject->mHeadp)
|
|
{
|
|
return LLQuaternion::DEFAULT;
|
|
}
|
|
|
|
if (!gAgentCamera.cameraMouselook())
|
|
{
|
|
return mAvatarObject->getRotation();
|
|
}
|
|
|
|
// We must be in mouselook
|
|
LLVector3 look_dir( LLViewerCamera::getInstance()->getAtAxis() );
|
|
LLVector3 up = look_dir % mFrameAgent.getLeftAxis();
|
|
LLVector3 left = up % look_dir;
|
|
|
|
LLQuaternion rot(look_dir, left, up);
|
|
if (mAvatarObject->getParent())
|
|
{
|
|
rot = rot * ~mAvatarObject->getParent()->getRotation();
|
|
}
|
|
|
|
return rot;
|
|
}
|
|
|
|
void LLAgent::sendAnimationRequests(LLDynamicArray<LLUUID> &anim_ids, EAnimRequest request)
|
|
{
|
|
if (gAgentID.isNull())
|
|
{
|
|
return;
|
|
}
|
|
|
|
S32 num_valid_anims = 0;
|
|
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_AgentAnimation);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
|
|
|
|
for (S32 i = 0; i < anim_ids.count(); i++)
|
|
{
|
|
if (anim_ids[i].isNull())
|
|
{
|
|
continue;
|
|
}
|
|
msg->nextBlockFast(_PREHASH_AnimationList);
|
|
msg->addUUIDFast(_PREHASH_AnimID, (anim_ids[i]) );
|
|
msg->addBOOLFast(_PREHASH_StartAnim, (request == ANIM_REQUEST_START) ? TRUE : FALSE);
|
|
num_valid_anims++;
|
|
}
|
|
|
|
msg->nextBlockFast(_PREHASH_PhysicalAvatarEventList);
|
|
msg->addBinaryDataFast(_PREHASH_TypeData, NULL, 0);
|
|
if (num_valid_anims)
|
|
{
|
|
sendReliableMessage();
|
|
}
|
|
}
|
|
|
|
void LLAgent::sendAnimationRequest(const LLUUID &anim_id, EAnimRequest request)
|
|
{
|
|
if (gAgentID.isNull() || anim_id.isNull() || !mRegionp)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_AgentAnimation);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
|
|
|
|
msg->nextBlockFast(_PREHASH_AnimationList);
|
|
msg->addUUIDFast(_PREHASH_AnimID, (anim_id) );
|
|
msg->addBOOLFast(_PREHASH_StartAnim, (request == ANIM_REQUEST_START) ? TRUE : FALSE);
|
|
|
|
msg->nextBlockFast(_PREHASH_PhysicalAvatarEventList);
|
|
msg->addBinaryDataFast(_PREHASH_TypeData, NULL, 0);
|
|
sendReliableMessage();
|
|
}
|
|
|
|
void LLAgent::sendWalkRun(bool running)
|
|
{
|
|
LLMessageSystem* msgsys = gMessageSystem;
|
|
if (msgsys)
|
|
{
|
|
msgsys->newMessageFast(_PREHASH_SetAlwaysRun);
|
|
msgsys->nextBlockFast(_PREHASH_AgentData);
|
|
msgsys->addUUIDFast(_PREHASH_AgentID, getID());
|
|
msgsys->addUUIDFast(_PREHASH_SessionID, getSessionID());
|
|
msgsys->addBOOLFast(_PREHASH_AlwaysRun, BOOL(running) );
|
|
sendReliableMessage();
|
|
}
|
|
}
|
|
|
|
void LLAgent::friendsChanged()
|
|
{
|
|
LLCollectProxyBuddies collector;
|
|
LLAvatarTracker::instance().applyFunctor(collector);
|
|
mProxyForAgents = collector.mProxy;
|
|
}
|
|
|
|
BOOL LLAgent::isGrantedProxy(const LLPermissions& perm)
|
|
{
|
|
return (mProxyForAgents.count(perm.getOwner()) > 0);
|
|
}
|
|
|
|
BOOL LLAgent::allowOperation(PermissionBit op,
|
|
const LLPermissions& perm,
|
|
U64 group_proxy_power,
|
|
U8 god_minimum)
|
|
{
|
|
// Check god level.
|
|
if (getGodLevel() >= god_minimum) return TRUE;
|
|
|
|
if (!perm.isOwned()) return FALSE;
|
|
|
|
// A group member with group_proxy_power can act as owner.
|
|
BOOL is_group_owned;
|
|
LLUUID owner_id;
|
|
perm.getOwnership(owner_id, is_group_owned);
|
|
LLUUID group_id(perm.getGroup());
|
|
LLUUID agent_proxy(getID());
|
|
|
|
if (is_group_owned)
|
|
{
|
|
if (hasPowerInGroup(group_id, group_proxy_power))
|
|
{
|
|
// Let the member assume the group's id for permission requests.
|
|
agent_proxy = owner_id;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check for granted mod permissions.
|
|
if ((PERM_OWNER != op) && isGrantedProxy(perm))
|
|
{
|
|
agent_proxy = owner_id;
|
|
}
|
|
}
|
|
|
|
// This is the group id to use for permission requests.
|
|
// Only group members may use this field.
|
|
LLUUID group_proxy = LLUUID::null;
|
|
if (group_id.notNull() && isInGroup(group_id))
|
|
{
|
|
group_proxy = group_id;
|
|
}
|
|
|
|
// We now have max ownership information.
|
|
if (PERM_OWNER == op)
|
|
{
|
|
// This this was just a check for ownership, we can now return the answer.
|
|
return (agent_proxy == owner_id);
|
|
}
|
|
|
|
return perm.allowOperationBy(op, agent_proxy, group_proxy);
|
|
}
|
|
|
|
|
|
void LLAgent::getName(std::string& name)
|
|
{
|
|
name.clear();
|
|
|
|
if (mAvatarObject.notNull())
|
|
{
|
|
LLNameValue *first_nv = mAvatarObject->getNVPair("FirstName");
|
|
LLNameValue *last_nv = mAvatarObject->getNVPair("LastName");
|
|
if (first_nv && last_nv)
|
|
{
|
|
name = first_nv->printData() + " " + last_nv->printData();
|
|
}
|
|
else
|
|
{
|
|
llwarns << "Agent is missing FirstName and/or LastName nv pair." << llendl;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
name = gSavedSettings.getString("FirstName") + " " + gSavedSettings.getString("LastName");
|
|
}
|
|
}
|
|
|
|
const LLColor4 &LLAgent::getEffectColor()
|
|
{
|
|
return mEffectColor;
|
|
}
|
|
|
|
void LLAgent::setEffectColor(const LLColor4 &color)
|
|
{
|
|
mEffectColor = color;
|
|
}
|
|
|
|
void LLAgent::initOriginGlobal(const LLVector3d &origin_global)
|
|
{
|
|
mAgentOriginGlobal = origin_global;
|
|
}
|
|
|
|
BOOL LLAgent::leftButtonGrabbed() const
|
|
{
|
|
const BOOL camera_mouse_look = gAgentCamera.cameraMouselook();
|
|
return (!camera_mouse_look && mControlsTakenCount[CONTROL_LBUTTON_DOWN_INDEX] > 0)
|
|
|| (camera_mouse_look && mControlsTakenCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0)
|
|
|| (!camera_mouse_look && mControlsTakenPassedOnCount[CONTROL_LBUTTON_DOWN_INDEX] > 0)
|
|
|| (camera_mouse_look && mControlsTakenPassedOnCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0);
|
|
}
|
|
|
|
BOOL LLAgent::rotateGrabbed() const
|
|
{
|
|
return (mControlsTakenCount[CONTROL_YAW_POS_INDEX] > 0)
|
|
|| (mControlsTakenCount[CONTROL_YAW_NEG_INDEX] > 0);
|
|
}
|
|
|
|
BOOL LLAgent::forwardGrabbed() const
|
|
{
|
|
return (mControlsTakenCount[CONTROL_AT_POS_INDEX] > 0);
|
|
}
|
|
|
|
BOOL LLAgent::backwardGrabbed() const
|
|
{
|
|
return (mControlsTakenCount[CONTROL_AT_NEG_INDEX] > 0);
|
|
}
|
|
|
|
BOOL LLAgent::upGrabbed() const
|
|
{
|
|
return (mControlsTakenCount[CONTROL_UP_POS_INDEX] > 0);
|
|
}
|
|
|
|
BOOL LLAgent::downGrabbed() const
|
|
{
|
|
return (mControlsTakenCount[CONTROL_UP_NEG_INDEX] > 0);
|
|
}
|
|
|
|
void update_group_floaters(const LLUUID& group_id)
|
|
{
|
|
LLFloaterGroupInfo::refreshGroup(group_id);
|
|
|
|
// update avatar info
|
|
LLFloaterAvatarInfo* fa = LLFloaterAvatarInfo::getInstance(gAgent.getID());
|
|
if(fa)
|
|
{
|
|
fa->resetGroupList();
|
|
}
|
|
|
|
if (gIMMgr)
|
|
{
|
|
// update the talk view
|
|
gIMMgr->refresh();
|
|
}
|
|
|
|
gAgent.fireEvent(new LLEvent(&gAgent, "new group"), "");
|
|
}
|
|
|
|
// static
|
|
void LLAgent::processAgentDropGroup(LLMessageSystem *msg, void **)
|
|
{
|
|
LLUUID agent_id;
|
|
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
|
|
|
|
if (agent_id != gAgentID)
|
|
{
|
|
llwarns << "processAgentDropGroup for agent other than me" << llendl;
|
|
return;
|
|
}
|
|
|
|
LLUUID group_id;
|
|
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id );
|
|
|
|
// Remove the group if it already exists remove it and add the new data to pick up changes.
|
|
LLGroupData gd;
|
|
gd.mID = group_id;
|
|
S32 index = gAgent.mGroups.find(gd);
|
|
if (index != -1)
|
|
{
|
|
gAgent.mGroups.remove(index);
|
|
if (gAgent.getGroupID() == group_id)
|
|
{
|
|
gAgent.mGroupID.setNull();
|
|
gAgent.mGroupPowers = 0;
|
|
gAgent.mGroupName.clear();
|
|
gAgent.mGroupTitle.clear();
|
|
}
|
|
|
|
// refresh all group information
|
|
gAgent.sendAgentDataUpdateRequest();
|
|
|
|
LLGroupMgr::getInstance()->clearGroupData(group_id);
|
|
// close the floater for this group, if any.
|
|
LLFloaterGroupInfo::closeGroup(group_id);
|
|
// refresh the group panel of the search window, if necessary.
|
|
LLFloaterDirectory::refreshGroup(group_id);
|
|
}
|
|
else
|
|
{
|
|
llwarns << "processAgentDropGroup, agent is not part of group " << group_id << llendl;
|
|
}
|
|
}
|
|
|
|
class LLAgentDropGroupViewerNode : public LLHTTPNode
|
|
{
|
|
virtual void post(
|
|
LLHTTPNode::ResponsePtr response,
|
|
const LLSD& context,
|
|
const LLSD& input) const
|
|
{
|
|
|
|
if (
|
|
!input.isMap() ||
|
|
!input.has("body") )
|
|
{
|
|
//what to do with badly formed message?
|
|
response->statusUnknownError(400);
|
|
response->result(LLSD("Invalid message parameters"));
|
|
}
|
|
|
|
LLSD body = input["body"];
|
|
if ( body.has("body") )
|
|
{
|
|
//stupid message system doubles up the "body"s
|
|
body = body["body"];
|
|
}
|
|
|
|
if (
|
|
body.has("AgentData") &&
|
|
body["AgentData"].isArray() &&
|
|
body["AgentData"][0].isMap() )
|
|
{
|
|
llinfos << "VALID DROP GROUP" << llendl;
|
|
|
|
//there is only one set of data in the AgentData block
|
|
LLSD agent_data = body["AgentData"][0];
|
|
LLUUID agent_id;
|
|
LLUUID group_id;
|
|
|
|
agent_id = agent_data["AgentID"].asUUID();
|
|
group_id = agent_data["GroupID"].asUUID();
|
|
|
|
if (agent_id != gAgentID)
|
|
{
|
|
llwarns
|
|
<< "AgentDropGroup for agent other than me" << llendl;
|
|
|
|
response->notFound();
|
|
return;
|
|
}
|
|
|
|
// Remove the group if it already exists remove it
|
|
// and add the new data to pick up changes.
|
|
LLGroupData gd;
|
|
gd.mID = group_id;
|
|
S32 index = gAgent.mGroups.find(gd);
|
|
if (index != -1)
|
|
{
|
|
gAgent.mGroups.remove(index);
|
|
if (gAgent.getGroupID() == group_id)
|
|
{
|
|
gAgent.mGroupID.setNull();
|
|
gAgent.mGroupPowers = 0;
|
|
gAgent.mGroupName.clear();
|
|
gAgent.mGroupTitle.clear();
|
|
}
|
|
|
|
// refresh all group information
|
|
gAgent.sendAgentDataUpdateRequest();
|
|
|
|
LLGroupMgr::getInstance()->clearGroupData(group_id);
|
|
// close the floater for this group, if any.
|
|
LLFloaterGroupInfo::closeGroup(group_id);
|
|
// refresh the group panel of the search window,
|
|
//if necessary.
|
|
LLFloaterDirectory::refreshGroup(group_id);
|
|
}
|
|
else
|
|
{
|
|
llwarns
|
|
<< "AgentDropGroup, agent is not part of group "
|
|
<< group_id << llendl;
|
|
}
|
|
|
|
response->result(LLSD());
|
|
}
|
|
else
|
|
{
|
|
//what to do with badly formed message?
|
|
response->statusUnknownError(400);
|
|
response->result(LLSD("Invalid message parameters"));
|
|
}
|
|
}
|
|
};
|
|
|
|
LLHTTPRegistration<LLAgentDropGroupViewerNode>
|
|
gHTTPRegistrationAgentDropGroupViewerNode(
|
|
"/message/AgentDropGroup");
|
|
|
|
// static
|
|
void LLAgent::processAgentGroupDataUpdate(LLMessageSystem *msg, void **)
|
|
{
|
|
LLUUID agent_id;
|
|
|
|
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
|
|
|
|
if (agent_id != gAgentID)
|
|
{
|
|
llwarns << "processAgentGroupDataUpdate for agent other than me" << llendl;
|
|
return;
|
|
}
|
|
|
|
S32 count = msg->getNumberOfBlocksFast(_PREHASH_GroupData);
|
|
LLGroupData group;
|
|
S32 index = -1;
|
|
bool need_floater_update = false;
|
|
for(S32 i = 0; i < count; ++i)
|
|
{
|
|
msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group.mID, i);
|
|
msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupInsigniaID, group.mInsigniaID, i);
|
|
msg->getU64(_PREHASH_GroupData, "GroupPowers", group.mPowers, i);
|
|
msg->getBOOL(_PREHASH_GroupData, "AcceptNotices", group.mAcceptNotices, i);
|
|
msg->getS32(_PREHASH_GroupData, "Contribution", group.mContribution, i);
|
|
msg->getStringFast(_PREHASH_GroupData, _PREHASH_GroupName, group.mName, i);
|
|
|
|
if(group.mID.notNull())
|
|
{
|
|
need_floater_update = true;
|
|
// Remove the group if it already exists remove it and add the new data to pick up changes.
|
|
index = gAgent.mGroups.find(group);
|
|
if (index != -1)
|
|
{
|
|
gAgent.mGroups.remove(index);
|
|
}
|
|
gAgent.mGroups.put(group);
|
|
}
|
|
if (need_floater_update)
|
|
{
|
|
update_group_floaters(group.mID);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
class LLAgentGroupDataUpdateViewerNode : public LLHTTPNode
|
|
{
|
|
virtual void post(
|
|
LLHTTPNode::ResponsePtr response,
|
|
const LLSD& context,
|
|
const LLSD& input) const
|
|
{
|
|
LLSD body = input["body"];
|
|
if(body.has("body"))
|
|
body = body["body"];
|
|
LLUUID agent_id = body["AgentData"][0]["AgentID"].asUUID();
|
|
|
|
if (agent_id != gAgentID)
|
|
{
|
|
llwarns << "processAgentGroupDataUpdate for agent other than me" << llendl;
|
|
return;
|
|
}
|
|
|
|
LLSD group_data = body["GroupData"];
|
|
|
|
LLSD::array_iterator iter_group =
|
|
group_data.beginArray();
|
|
LLSD::array_iterator end_group =
|
|
group_data.endArray();
|
|
int group_index = 0;
|
|
for(; iter_group != end_group; ++iter_group)
|
|
{
|
|
|
|
LLGroupData group;
|
|
S32 index = -1;
|
|
bool need_floater_update = false;
|
|
|
|
group.mID = (*iter_group)["GroupID"].asUUID();
|
|
group.mPowers = ll_U64_from_sd((*iter_group)["GroupPowers"]);
|
|
group.mAcceptNotices = (*iter_group)["AcceptNotices"].asBoolean();
|
|
group.mListInProfile = body["NewGroupData"][group_index]["ListInProfile"].asBoolean();
|
|
group.mInsigniaID = (*iter_group)["GroupInsigniaID"].asUUID();
|
|
group.mName = (*iter_group)["GroupName"].asString();
|
|
group.mContribution = (*iter_group)["Contribution"].asInteger();
|
|
|
|
group_index++;
|
|
|
|
if(group.mID.notNull())
|
|
{
|
|
need_floater_update = true;
|
|
// Remove the group if it already exists remove it and add the new data to pick up changes.
|
|
index = gAgent.mGroups.find(group);
|
|
if (index != -1)
|
|
{
|
|
gAgent.mGroups.remove(index);
|
|
}
|
|
gAgent.mGroups.put(group);
|
|
}
|
|
if (need_floater_update)
|
|
{
|
|
update_group_floaters(group.mID);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
LLHTTPRegistration<LLAgentGroupDataUpdateViewerNode >
|
|
gHTTPRegistrationAgentGroupDataUpdateViewerNode ("/message/AgentGroupDataUpdate");
|
|
|
|
// static
|
|
void LLAgent::processAgentDataUpdate(LLMessageSystem *msg, void **)
|
|
{
|
|
LLUUID agent_id;
|
|
|
|
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
|
|
|
|
if (agent_id != gAgentID)
|
|
{
|
|
llwarns << "processAgentDataUpdate for agent other than me" << llendl;
|
|
return;
|
|
}
|
|
|
|
msg->getStringFast(_PREHASH_AgentData, _PREHASH_GroupTitle, gAgent.mGroupTitle);
|
|
LLUUID active_id;
|
|
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_ActiveGroupID, active_id);
|
|
|
|
|
|
if(active_id.notNull())
|
|
{
|
|
gAgent.mGroupID = active_id;
|
|
msg->getU64(_PREHASH_AgentData, "GroupPowers", gAgent.mGroupPowers);
|
|
msg->getString(_PREHASH_AgentData, _PREHASH_GroupName, gAgent.mGroupName);
|
|
}
|
|
else
|
|
{
|
|
gAgent.mGroupID.setNull();
|
|
gAgent.mGroupPowers = 0;
|
|
gAgent.mGroupName.clear();
|
|
}
|
|
|
|
update_group_floaters(active_id);
|
|
}
|
|
|
|
// static
|
|
void LLAgent::processScriptControlChange(LLMessageSystem *msg, void **)
|
|
{
|
|
S32 block_count = msg->getNumberOfBlocks("Data");
|
|
for (S32 block_index = 0; block_index < block_count; block_index++)
|
|
{
|
|
BOOL take_controls;
|
|
U32 controls;
|
|
BOOL passon;
|
|
U32 i;
|
|
msg->getBOOL("Data", "TakeControls", take_controls, block_index);
|
|
if (take_controls)
|
|
{
|
|
// take controls
|
|
msg->getU32("Data", "Controls", controls, block_index );
|
|
msg->getBOOL("Data", "PassToAgent", passon, block_index );
|
|
U32 total_count = 0;
|
|
for (i = 0; i < TOTAL_CONTROLS; i++)
|
|
{
|
|
if (controls & ( 1 << i))
|
|
{
|
|
if (passon)
|
|
{
|
|
gAgent.mControlsTakenPassedOnCount[i]++;
|
|
}
|
|
else
|
|
{
|
|
gAgent.mControlsTakenCount[i]++;
|
|
}
|
|
total_count++;
|
|
}
|
|
}
|
|
|
|
// Any control taken? If so, might be first time.
|
|
if (total_count > 0)
|
|
{
|
|
LLFirstUse::useOverrideKeys();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// release controls
|
|
msg->getU32("Data", "Controls", controls, block_index );
|
|
msg->getBOOL("Data", "PassToAgent", passon, block_index );
|
|
for (i = 0; i < TOTAL_CONTROLS; i++)
|
|
{
|
|
if (controls & ( 1 << i))
|
|
{
|
|
if (passon)
|
|
{
|
|
gAgent.mControlsTakenPassedOnCount[i]--;
|
|
if (gAgent.mControlsTakenPassedOnCount[i] < 0)
|
|
{
|
|
gAgent.mControlsTakenPassedOnCount[i] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gAgent.mControlsTakenCount[i]--;
|
|
if (gAgent.mControlsTakenCount[i] < 0)
|
|
{
|
|
gAgent.mControlsTakenCount[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
// static
|
|
void LLAgent::processControlTake(LLMessageSystem *msg, void **)
|
|
{
|
|
U32 controls;
|
|
msg->getU32("Data", "Controls", controls );
|
|
U32 passon;
|
|
msg->getBOOL("Data", "PassToAgent", passon );
|
|
|
|
S32 i;
|
|
S32 total_count = 0;
|
|
for (i = 0; i < TOTAL_CONTROLS; i++)
|
|
{
|
|
if (controls & ( 1 << i))
|
|
{
|
|
if (passon)
|
|
{
|
|
gAgent.mControlsTakenPassedOnCount[i]++;
|
|
}
|
|
else
|
|
{
|
|
gAgent.mControlsTakenCount[i]++;
|
|
}
|
|
total_count++;
|
|
}
|
|
}
|
|
|
|
// Any control taken? If so, might be first time.
|
|
if (total_count > 0)
|
|
{
|
|
LLFirstUse::useOverrideKeys();
|
|
}
|
|
}
|
|
|
|
// static
|
|
void LLAgent::processControlRelease(LLMessageSystem *msg, void **)
|
|
{
|
|
U32 controls;
|
|
msg->getU32("Data", "Controls", controls );
|
|
U32 passon;
|
|
msg->getBOOL("Data", "PassToAgent", passon );
|
|
|
|
S32 i;
|
|
for (i = 0; i < TOTAL_CONTROLS; i++)
|
|
{
|
|
if (controls & ( 1 << i))
|
|
{
|
|
if (passon)
|
|
{
|
|
gAgent.mControlsTakenPassedOnCount[i]--;
|
|
if (gAgent.mControlsTakenPassedOnCount[i] < 0)
|
|
{
|
|
gAgent.mControlsTakenPassedOnCount[i] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gAgent.mControlsTakenCount[i]--;
|
|
if (gAgent.mControlsTakenCount[i] < 0)
|
|
{
|
|
gAgent.mControlsTakenCount[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
//static
|
|
void LLAgent::processAgentCachedTextureResponse(LLMessageSystem *mesgsys, void **user_data)
|
|
{
|
|
gAgentQueryManager.mNumPendingQueries--;
|
|
|
|
if (!isAgentAvatarValid())
|
|
{
|
|
llwarns << "No avatar for user in cached texture update!" << llendl;
|
|
return;
|
|
}
|
|
|
|
if (gAgentCamera.cameraCustomizeAvatar())
|
|
{
|
|
// ignore baked textures when in customize mode
|
|
return;
|
|
}
|
|
|
|
S32 query_id;
|
|
mesgsys->getS32Fast(_PREHASH_AgentData, _PREHASH_SerialNum, query_id);
|
|
|
|
S32 num_texture_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_WearableData);
|
|
|
|
|
|
S32 num_results = 0;
|
|
for (S32 texture_block = 0; texture_block < num_texture_blocks; texture_block++)
|
|
{
|
|
LLUUID texture_id;
|
|
U8 texture_index;
|
|
|
|
mesgsys->getUUIDFast(_PREHASH_WearableData, _PREHASH_TextureID, texture_id, texture_block);
|
|
mesgsys->getU8Fast(_PREHASH_WearableData, _PREHASH_TextureIndex, texture_index, texture_block);
|
|
|
|
if (texture_id.notNull()
|
|
&& (S32)texture_index < BAKED_NUM_INDICES
|
|
&& gAgentQueryManager.mActiveCacheQueries[ texture_index ] == query_id)
|
|
{
|
|
//llinfos << "Received cached texture " << (U32)texture_index << ": " << texture_id << llendl;
|
|
gAgent.getAvatarObject()->setCachedBakedTexture(getTextureIndex((EBakedTextureIndex)texture_index), texture_id);
|
|
//avatarp->setTETexture( LLVOAvatar::sBakedTextureIndices[texture_index], texture_id );
|
|
gAgentQueryManager.mActiveCacheQueries[ texture_index ] = 0;
|
|
num_results++;
|
|
}
|
|
}
|
|
|
|
llinfos << "Received cached texture response for " << num_results << " textures." << llendl;
|
|
|
|
gAgent.getAvatarObject()->updateMeshTextures();
|
|
|
|
if (gAgentQueryManager.mNumPendingQueries == 0)
|
|
{
|
|
// RN: not sure why composites are disabled at this point
|
|
gAgent.getAvatarObject()->setCompositeUpdatesEnabled(TRUE);
|
|
gAgent.sendAgentSetAppearance();
|
|
}
|
|
}
|
|
|
|
BOOL LLAgent::anyControlGrabbed() const
|
|
{
|
|
for (U32 i = 0; i < TOTAL_CONTROLS; i++)
|
|
{
|
|
if (gAgent.mControlsTakenCount[i] > 0)
|
|
return TRUE;
|
|
if (gAgent.mControlsTakenPassedOnCount[i] > 0)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL LLAgent::isControlGrabbed(S32 control_index) const
|
|
{
|
|
return mControlsTakenCount[control_index] > 0;
|
|
}
|
|
|
|
void LLAgent::forceReleaseControls()
|
|
{
|
|
gMessageSystem->newMessage("ForceScriptControlRelease");
|
|
gMessageSystem->nextBlock("AgentData");
|
|
gMessageSystem->addUUID("AgentID", getID());
|
|
gMessageSystem->addUUID("SessionID", getSessionID());
|
|
sendReliableMessage();
|
|
}
|
|
|
|
void LLAgent::setHomePosRegion( const U64& region_handle, const LLVector3& pos_region)
|
|
{
|
|
mHaveHomePosition = TRUE;
|
|
mHomeRegionHandle = region_handle;
|
|
mHomePosRegion = pos_region;
|
|
}
|
|
|
|
BOOL LLAgent::getHomePosGlobal( LLVector3d* pos_global )
|
|
{
|
|
if(!mHaveHomePosition)
|
|
{
|
|
return FALSE;
|
|
}
|
|
F32 x = 0;
|
|
F32 y = 0;
|
|
from_region_handle( mHomeRegionHandle, &x, &y);
|
|
pos_global->setVec( x + mHomePosRegion.mV[VX], y + mHomePosRegion.mV[VY], mHomePosRegion.mV[VZ] );
|
|
return TRUE;
|
|
}
|
|
|
|
void LLAgent::clearVisualParams(void *data)
|
|
{
|
|
if (isAgentAvatarValid())
|
|
{
|
|
gAgent.getAvatarObject()->clearVisualParamWeights();
|
|
gAgent.getAvatarObject()->updateVisualParams();
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Teleport
|
|
//---------------------------------------------------------------------------
|
|
|
|
// teleportCore() - stuff to do on any teleport
|
|
// protected
|
|
bool LLAgent::teleportCore(bool is_local)
|
|
{
|
|
if(TELEPORT_NONE != mTeleportState)
|
|
{
|
|
llwarns << "Attempt to teleport when already teleporting." << llendl;
|
|
// <edit>
|
|
//return false;
|
|
teleportCancel();
|
|
// </edit>
|
|
}
|
|
|
|
#if 0
|
|
// This should not exist. It has been added, removed, added, and now removed again.
|
|
// This change needs to come from the simulator. Otherwise, the agent ends up out of
|
|
// sync with other viewers. Discuss in DEV-14145/VWR-6744 before reenabling.
|
|
|
|
// Stop all animation before actual teleporting
|
|
if (isAgentAvatarValid())
|
|
{
|
|
LLVOAvatar* avatarp = gAgent.getAvatarObject();
|
|
for ( LLVOAvatar::AnimIterator anim_it= avatarp->mPlayingAnimations.begin();
|
|
anim_it != avatarp->mPlayingAnimations.end();
|
|
++anim_it)
|
|
{
|
|
avatarp->stopMotion(anim_it->first);
|
|
}
|
|
avatarp->processAnimationStateChanges();
|
|
}
|
|
#endif
|
|
|
|
// Don't call LLFirstUse::useTeleport because we don't know
|
|
// yet if the teleport will succeed. Look in
|
|
// process_teleport_location_reply
|
|
|
|
// close the map and find panels so we can see our destination
|
|
LLFloaterWorldMap::hide(NULL);
|
|
LLFloaterDirectory::hide(NULL);
|
|
|
|
// hide land floater too - it'll be out of date
|
|
LLFloaterLand::hideInstance();
|
|
|
|
LLViewerParcelMgr::getInstance()->deselectLand();
|
|
LLViewerMediaFocus::getInstance()->setFocusFace(false, NULL, 0, NULL);
|
|
|
|
// Close all pie menus, deselect land, etc.
|
|
// Don't change the camera until we know teleport succeeded. JC
|
|
// <edit>
|
|
if(gAgentCamera.getFocusOnAvatar())
|
|
// </edit>
|
|
gAgentCamera.resetView(FALSE);
|
|
|
|
// local logic
|
|
LLViewerStats::getInstance()->incStat(LLViewerStats::ST_TELEPORT_COUNT);
|
|
if (is_local)
|
|
{
|
|
gAgent.setTeleportState( LLAgent::TELEPORT_LOCAL );
|
|
}
|
|
else
|
|
{
|
|
gTeleportDisplay = TRUE;
|
|
gAgent.setTeleportState( LLAgent::TELEPORT_START );
|
|
|
|
//release geometry from old location
|
|
gPipeline.resetVertexBuffers();
|
|
|
|
if (gSavedSettings.getBOOL("SpeedRez"))
|
|
{
|
|
F32 draw_distance = gSavedSettings.getF32("RenderFarClip");
|
|
if (gSavedDrawDistance < draw_distance)
|
|
{
|
|
gSavedDrawDistance = draw_distance;
|
|
}
|
|
gSavedSettings.setF32("SavedRenderFarClip", gSavedDrawDistance);
|
|
gSavedSettings.setF32("RenderFarClip", 32.0f);
|
|
}
|
|
if(gSavedSettings.getBOOL("OptionPlayTpSound"))
|
|
make_ui_sound("UISndTeleportOut");
|
|
}
|
|
|
|
// MBW -- Let the voice client know a teleport has begun so it can leave the existing channel.
|
|
// This was breaking the case of teleporting within a single sim. Backing it out for now.
|
|
// gVoiceClient->leaveChannel();
|
|
|
|
return true;
|
|
}
|
|
|
|
void LLAgent::teleportRequest(
|
|
const U64& region_handle,
|
|
const LLVector3& pos_local,
|
|
bool look_at_from_camera)
|
|
{
|
|
LLViewerRegion* regionp = getRegion();
|
|
bool is_local = (region_handle == to_region_handle(getPositionGlobal()));
|
|
if(regionp && teleportCore(is_local))
|
|
{
|
|
llinfos << "TeleportLocationRequest: '" << region_handle << "':" << pos_local
|
|
<< llendl;
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessage("TeleportLocationRequest");
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
|
|
msg->nextBlockFast(_PREHASH_Info);
|
|
msg->addU64("RegionHandle", region_handle);
|
|
msg->addVector3("Position", pos_local);
|
|
// <edit>
|
|
//LLVector3 look_at(0,1,0);
|
|
LLVector3 look_at = LLViewerCamera::getInstance()->getAtAxis();
|
|
/*if (look_at_from_camera)
|
|
{
|
|
look_at = LLViewerCamera::getInstance()->getAtAxis();
|
|
}*/
|
|
// </edit>
|
|
msg->addVector3("LookAt", look_at);
|
|
sendReliableMessage();
|
|
}
|
|
}
|
|
|
|
// Landmark ID = LLUUID::null means teleport home
|
|
void LLAgent::teleportViaLandmark(const LLUUID& landmark_asset_id)
|
|
{
|
|
// [RLVa:KB] - Checked: 2009-07-07 (RLVa-1.0.0d)
|
|
if ( (rlv_handler_t::isEnabled()) &&
|
|
( (gRlvHandler.hasBehaviour(RLV_BHVR_TPLM)) ||
|
|
((gRlvHandler.hasBehaviour(RLV_BHVR_UNSIT)) && (mAvatarObject.notNull()) && (mAvatarObject->isSitting())) ))
|
|
{
|
|
RlvNotifications::notifyBlockedTeleport();
|
|
return;
|
|
}
|
|
// [/RLVa:KB]
|
|
|
|
LLViewerRegion *regionp = getRegion();
|
|
if(regionp && teleportCore())
|
|
{
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_TeleportLandmarkRequest);
|
|
msg->nextBlockFast(_PREHASH_Info);
|
|
msg->addUUIDFast(_PREHASH_AgentID, getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
|
|
msg->addUUIDFast(_PREHASH_LandmarkID, landmark_asset_id);
|
|
sendReliableMessage();
|
|
}
|
|
}
|
|
|
|
void LLAgent::teleportViaLure(const LLUUID& lure_id, BOOL godlike)
|
|
{
|
|
LLViewerRegion* regionp = getRegion();
|
|
if(regionp && teleportCore())
|
|
{
|
|
U32 teleport_flags = 0x0;
|
|
if (godlike)
|
|
{
|
|
teleport_flags |= TELEPORT_FLAGS_VIA_GODLIKE_LURE;
|
|
teleport_flags |= TELEPORT_FLAGS_DISABLE_CANCEL;
|
|
}
|
|
else
|
|
{
|
|
teleport_flags |= TELEPORT_FLAGS_VIA_LURE;
|
|
}
|
|
|
|
// send the message
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_TeleportLureRequest);
|
|
msg->nextBlockFast(_PREHASH_Info);
|
|
msg->addUUIDFast(_PREHASH_AgentID, getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
|
|
msg->addUUIDFast(_PREHASH_LureID, lure_id);
|
|
// teleport_flags is a legacy field, now derived sim-side:
|
|
msg->addU32("TeleportFlags", teleport_flags);
|
|
sendReliableMessage();
|
|
}
|
|
}
|
|
|
|
|
|
// James Cook, July 28, 2005
|
|
void LLAgent::teleportCancel()
|
|
{
|
|
LLViewerRegion* regionp = getRegion();
|
|
if(regionp)
|
|
{
|
|
// send the message
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessage("TeleportCancel");
|
|
msg->nextBlockFast(_PREHASH_Info);
|
|
msg->addUUIDFast(_PREHASH_AgentID, getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
|
|
sendReliableMessage();
|
|
}
|
|
gTeleportDisplay = FALSE;
|
|
gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
|
|
}
|
|
|
|
|
|
void LLAgent::teleportViaLocation(const LLVector3d& pos_global)
|
|
{
|
|
// [RLVa:KB] - Alternate: Snowglobe-1.2.4 | Checked: 2010-03-02 (RLVa-1.1.1a) | Modified: RLVa-1.2.0a
|
|
if (rlv_handler_t::isEnabled())
|
|
{
|
|
// If we're getting teleported due to @tpto we should disregard any @tploc=n or @unsit=n restrictions from the same object
|
|
if ( (gRlvHandler.hasBehaviourExcept(RLV_BHVR_TPLOC, gRlvHandler.getCurrentObject())) ||
|
|
( (mAvatarObject.notNull()) && (mAvatarObject->isSitting()) &&
|
|
(gRlvHandler.hasBehaviourExcept(RLV_BHVR_UNSIT, gRlvHandler.getCurrentObject()))) )
|
|
{
|
|
RlvNotifications::notifyBlockedTeleport();
|
|
return;
|
|
}
|
|
|
|
if ( (gRlvHandler.getCurrentCommand()) && (RLV_BHVR_TPTO == gRlvHandler.getCurrentCommand()->getBehaviourType()) )
|
|
{
|
|
gRlvHandler.setCanCancelTp(false);
|
|
}
|
|
}
|
|
// [/RLVa:KB]
|
|
|
|
LLViewerRegion* regionp = getRegion();
|
|
U64 handle = to_region_handle(pos_global);
|
|
LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle);
|
|
bool calc = gSavedSettings.getBOOL("OptionOffsetTPByAgentHeight");
|
|
LLVector3 offset = LLVector3(0.f,0.f,0.f);
|
|
if(calc)
|
|
offset += LLVector3(0.f,0.f,gAgent.getAvatarObject()->getScale().mV[2] / 2.0);
|
|
if(regionp && info)
|
|
{
|
|
LLVector3d region_origin = info->getGlobalOrigin();
|
|
LLVector3 pos_local(
|
|
(F32)(pos_global.mdV[VX] - region_origin.mdV[VX]),
|
|
(F32)(pos_global.mdV[VY] - region_origin.mdV[VY]),
|
|
(F32)(pos_global.mdV[VZ]));
|
|
pos_local += offset;
|
|
teleportRequest(handle, pos_local);
|
|
}
|
|
else if(regionp &&
|
|
teleportCore(regionp->getHandle() == to_region_handle_global((F32)pos_global.mdV[VX], (F32)pos_global.mdV[VY])))
|
|
{
|
|
llwarns << "Using deprecated teleportlocationrequest." << llendl;
|
|
// send the message
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_TeleportLocationRequest);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
|
|
|
|
msg->nextBlockFast(_PREHASH_Info);
|
|
F32 width = regionp->getWidth();
|
|
LLVector3 pos(fmod((F32)pos_global.mdV[VX], width),
|
|
fmod((F32)pos_global.mdV[VY], width),
|
|
(F32)pos_global.mdV[VZ]);
|
|
F32 region_x = (F32)(pos_global.mdV[VX]);
|
|
F32 region_y = (F32)(pos_global.mdV[VY]);
|
|
U64 region_handle = to_region_handle_global(region_x, region_y);
|
|
msg->addU64Fast(_PREHASH_RegionHandle, region_handle);
|
|
msg->addVector3Fast(_PREHASH_Position, pos);
|
|
pos.mV[VX] += 1;
|
|
// <edit>
|
|
LLVector3 lookat = LLViewerCamera::getInstance()->getAtAxis();
|
|
//msg->addVector3Fast(_PREHASH_LookAt, pos);
|
|
msg->addVector3Fast(_PREHASH_LookAt, lookat);
|
|
// </edit>
|
|
sendReliableMessage();
|
|
}
|
|
}
|
|
|
|
// Teleport to global position, but keep facing in the same direction
|
|
void LLAgent::teleportViaLocationLookAt(const LLVector3d& pos_global)
|
|
{
|
|
// [RLVa:KB] - Checked: 2010-10-07 (RLVa-1.2.1f) | Added: RLVa-1.2.1f
|
|
// RELEASE-RLVa: [SL-2.2.0] Make sure this isn't used for anything except double-click teleporting
|
|
if ( (rlv_handler_t::isEnabled()) && (!RlvUtil::isForceTp()) &&
|
|
((gRlvHandler.hasBehaviour(RLV_BHVR_SITTP)) || (!gRlvHandler.canStand())) )
|
|
{
|
|
RlvNotifications::notifyBlockedTeleport();
|
|
return;
|
|
}
|
|
// [/RLVa:KB]
|
|
|
|
mbTeleportKeepsLookAt = true;
|
|
gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); // detach camera form avatar, so it keeps direction
|
|
U64 region_handle = to_region_handle(pos_global);
|
|
LLVector3 pos_local = (LLVector3)(pos_global - from_region_handle(region_handle));
|
|
teleportRequest(region_handle, pos_local, getTeleportKeepsLookAt());
|
|
}
|
|
|
|
void LLAgent::setTeleportState(ETeleportState state)
|
|
{
|
|
mTeleportState = state;
|
|
static const LLCachedControl<bool> freeze_time("FreezeTime",false);
|
|
if (mTeleportState > TELEPORT_NONE && freeze_time)
|
|
{
|
|
LLFloaterSnapshot::hide(0);
|
|
}
|
|
if (mTeleportState == TELEPORT_NONE)
|
|
{
|
|
mbTeleportKeepsLookAt = false;
|
|
}
|
|
if ((mTeleportState == TELEPORT_MOVING))
|
|
{
|
|
// We're outa here. Save "back" slurl.
|
|
mTeleportSourceSLURL = getSLURL();
|
|
}
|
|
// [RLVa:KB] - Alternate: Snowglobe-1.2.4 | Version: 1.23.4 | Checked: 2009-07-07 (RLVa-1.0.0d) | Added: RLVa-0.2.0b
|
|
if ( (rlv_handler_t::isEnabled()) && (TELEPORT_NONE == mTeleportState) )
|
|
{
|
|
gRlvHandler.setCanCancelTp(true);
|
|
}
|
|
// [/RLVa:KB]
|
|
}
|
|
|
|
void LLAgent::stopCurrentAnimations()
|
|
{
|
|
// This function stops all current overriding animations on this
|
|
// avatar, propagating this change back to the server.
|
|
if (isAgentAvatarValid())
|
|
{
|
|
LLVOAvatar* avatarp = gAgent.getAvatarObject();
|
|
for ( LLVOAvatar::AnimIterator anim_it =
|
|
avatarp->mPlayingAnimations.begin();
|
|
anim_it != avatarp->mPlayingAnimations.end();
|
|
anim_it++)
|
|
{
|
|
if (anim_it->first ==
|
|
ANIM_AGENT_SIT_GROUND_CONSTRAINED)
|
|
{
|
|
// don't cancel a ground-sit anim, as viewers
|
|
// use this animation's status in
|
|
// determining whether we're sitting. ick.
|
|
}
|
|
else
|
|
{
|
|
// stop this animation locally
|
|
avatarp->stopMotion(anim_it->first, TRUE);
|
|
// ...and tell the server to tell everyone.
|
|
sendAnimationRequest(anim_it->first, ANIM_REQUEST_STOP);
|
|
}
|
|
}
|
|
|
|
// re-assert at least the default standing animation, because
|
|
// viewers get confused by avs with no associated anims.
|
|
sendAnimationRequest(ANIM_AGENT_STAND,
|
|
ANIM_REQUEST_START);
|
|
}
|
|
}
|
|
|
|
void LLAgent::fidget()
|
|
{
|
|
if (!getAFK())
|
|
{
|
|
F32 curTime = mFidgetTimer.getElapsedTimeF32();
|
|
if (curTime > mNextFidgetTime)
|
|
{
|
|
// pick a random fidget anim here
|
|
S32 oldFidget = mCurrentFidget;
|
|
|
|
mCurrentFidget = ll_rand(NUM_AGENT_STAND_ANIMS);
|
|
|
|
if (mCurrentFidget != oldFidget)
|
|
{
|
|
//LLAgent::stopFidget();
|
|
// <edit>
|
|
// for the sack of smaller packets, make this cancel the last one only
|
|
if(oldFidget != 0)
|
|
sendAnimationRequest(AGENT_STAND_ANIMS[oldFidget],ANIM_REQUEST_STOP);
|
|
// </edit>
|
|
|
|
switch(mCurrentFidget)
|
|
{
|
|
case 0:
|
|
mCurrentFidget = 0;
|
|
break;
|
|
case 1:
|
|
sendAnimationRequest(ANIM_AGENT_STAND_1, ANIM_REQUEST_START);
|
|
mCurrentFidget = 1;
|
|
break;
|
|
case 2:
|
|
sendAnimationRequest(ANIM_AGENT_STAND_2, ANIM_REQUEST_START);
|
|
mCurrentFidget = 2;
|
|
break;
|
|
case 3:
|
|
sendAnimationRequest(ANIM_AGENT_STAND_3, ANIM_REQUEST_START);
|
|
mCurrentFidget = 3;
|
|
break;
|
|
case 4:
|
|
sendAnimationRequest(ANIM_AGENT_STAND_4, ANIM_REQUEST_START);
|
|
mCurrentFidget = 4;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// calculate next fidget time
|
|
mNextFidgetTime = curTime + ll_frand(MAX_FIDGET_TIME - MIN_FIDGET_TIME) + MIN_FIDGET_TIME;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLAgent::stopFidget()
|
|
{
|
|
LLDynamicArray<LLUUID> anims;
|
|
anims.put(ANIM_AGENT_STAND_1);
|
|
anims.put(ANIM_AGENT_STAND_2);
|
|
anims.put(ANIM_AGENT_STAND_3);
|
|
anims.put(ANIM_AGENT_STAND_4);
|
|
|
|
gAgent.sendAnimationRequests(anims, ANIM_REQUEST_STOP);
|
|
}
|
|
|
|
|
|
void LLAgent::requestEnterGodMode()
|
|
{
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_RequestGodlikePowers);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
msg->nextBlockFast(_PREHASH_RequestBlock);
|
|
msg->addBOOLFast(_PREHASH_Godlike, TRUE);
|
|
msg->addUUIDFast(_PREHASH_Token, LLUUID::null);
|
|
|
|
// simulators need to know about your request
|
|
sendReliableMessage();
|
|
}
|
|
|
|
void LLAgent::requestLeaveGodMode()
|
|
{
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_RequestGodlikePowers);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
msg->nextBlockFast(_PREHASH_RequestBlock);
|
|
msg->addBOOLFast(_PREHASH_Godlike, FALSE);
|
|
msg->addUUIDFast(_PREHASH_Token, LLUUID::null);
|
|
|
|
// simulator needs to know about your request
|
|
sendReliableMessage();
|
|
}
|
|
|
|
// wearables
|
|
LLAgent::createStandardWearablesAllDoneCallback::~createStandardWearablesAllDoneCallback()
|
|
{
|
|
gAgent.createStandardWearablesAllDone();
|
|
}
|
|
|
|
LLAgent::sendAgentWearablesUpdateCallback::~sendAgentWearablesUpdateCallback()
|
|
{
|
|
gAgent.sendAgentWearablesUpdate();
|
|
}
|
|
|
|
LLAgent::addWearableToAgentInventoryCallback::addWearableToAgentInventoryCallback(
|
|
LLPointer<LLRefCount> cb, S32 index, LLWearable* wearable, U32 todo) :
|
|
mIndex(index),
|
|
mWearable(wearable),
|
|
mTodo(todo),
|
|
mCB(cb)
|
|
{
|
|
}
|
|
|
|
void LLAgent::addWearableToAgentInventoryCallback::fire(const LLUUID& inv_item)
|
|
{
|
|
if (inv_item.isNull())
|
|
return;
|
|
|
|
gAgent.addWearabletoAgentInventoryDone(mIndex, inv_item, mWearable);
|
|
|
|
if (mTodo & CALL_UPDATE)
|
|
{
|
|
gAgent.sendAgentWearablesUpdate();
|
|
}
|
|
if (mTodo & CALL_RECOVERDONE)
|
|
{
|
|
gAgent.recoverMissingWearableDone();
|
|
}
|
|
/*
|
|
* Do this for every one in the loop
|
|
*/
|
|
if (mTodo & CALL_CREATESTANDARDDONE)
|
|
{
|
|
gAgent.createStandardWearablesDone(mIndex);
|
|
}
|
|
if (mTodo & CALL_MAKENEWOUTFITDONE)
|
|
{
|
|
gAgent.makeNewOutfitDone(mIndex);
|
|
}
|
|
}
|
|
|
|
void LLAgent::addWearabletoAgentInventoryDone(
|
|
S32 index,
|
|
const LLUUID& item_id,
|
|
LLWearable* wearable)
|
|
{
|
|
if (item_id.isNull())
|
|
return;
|
|
|
|
LLUUID old_item_id = mWearableEntry[index].mItemID;
|
|
mWearableEntry[index].mItemID = item_id;
|
|
mWearableEntry[index].mWearable = wearable;
|
|
if (old_item_id.notNull())
|
|
gInventory.addChangedMask(LLInventoryObserver::LABEL, old_item_id);
|
|
gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
|
|
LLViewerInventoryItem* item = gInventory.getItem(item_id);
|
|
if(item && wearable)
|
|
{
|
|
// We're changing the asset id, so we both need to set it
|
|
// locally via setAssetUUID() and via setTransactionID() which
|
|
// will be decoded on the server. JC
|
|
item->setAssetUUID(wearable->getID());
|
|
item->setTransactionID(wearable->getTransactionID());
|
|
gInventory.addChangedMask(LLInventoryObserver::INTERNAL, item_id);
|
|
item->updateServer(FALSE);
|
|
}
|
|
gInventory.notifyObservers();
|
|
}
|
|
|
|
void LLAgent::sendAgentWearablesUpdate()
|
|
{
|
|
// First make sure that we have inventory items for each wearable
|
|
S32 i;
|
|
for(i=0; i < WT_COUNT; ++i)
|
|
{
|
|
LLWearable* wearable = mWearableEntry[ i ].mWearable;
|
|
if (wearable)
|
|
{
|
|
if( mWearableEntry[ i ].mItemID.isNull() )
|
|
{
|
|
LLPointer<LLInventoryCallback> cb =
|
|
new addWearableToAgentInventoryCallback(
|
|
LLPointer<LLRefCount>(NULL),
|
|
i,
|
|
wearable,
|
|
addWearableToAgentInventoryCallback::CALL_NONE);
|
|
addWearableToAgentInventory(cb, wearable);
|
|
}
|
|
else
|
|
{
|
|
gInventory.addChangedMask( LLInventoryObserver::LABEL,
|
|
mWearableEntry[i].mItemID );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Then make sure the inventory is in sync with the avatar.
|
|
gInventory.notifyObservers();
|
|
|
|
// This isn't the proper place to be doing this, but it's a good "catch-all"
|
|
LLCOFMgr::instance().synchWearables();
|
|
|
|
// Send the AgentIsNowWearing
|
|
gMessageSystem->newMessageFast(_PREHASH_AgentIsNowWearing);
|
|
|
|
gMessageSystem->nextBlockFast(_PREHASH_AgentData);
|
|
gMessageSystem->addUUIDFast(_PREHASH_AgentID, getID());
|
|
gMessageSystem->addUUIDFast(_PREHASH_SessionID, getSessionID());
|
|
|
|
LL_DEBUGS("Wearables") << "sendAgentWearablesUpdate()" << LL_ENDL;
|
|
for(i=0; i < WT_COUNT; ++i)
|
|
{
|
|
gMessageSystem->nextBlockFast(_PREHASH_WearableData);
|
|
|
|
U8 type_u8 = (U8)i;
|
|
gMessageSystem->addU8Fast(_PREHASH_WearableType, type_u8 );
|
|
|
|
LLWearable* wearable = mWearableEntry[ i ].mWearable;
|
|
if( wearable )
|
|
{
|
|
LL_DEBUGS("Wearables") << "Sending wearable " << wearable->getName() << " mItemID = " << mWearableEntry[ i ].mItemID << LL_ENDL;
|
|
LLUUID item_id = mWearableEntry[i].mItemID;
|
|
const LLViewerInventoryItem *item = gInventory.getItem(item_id);
|
|
if (item && item->getIsLinkType())
|
|
{
|
|
// Get the itemID that this item points to. i.e. make sure
|
|
// we are storing baseitems, not their links, in the database.
|
|
item_id = item->getLinkedUUID();
|
|
}
|
|
gMessageSystem->addUUIDFast(_PREHASH_ItemID, item_id);
|
|
}
|
|
else
|
|
{
|
|
LL_DEBUGS("Wearables") << "Not wearing wearable type " << LLWearable::typeToTypeName((EWearableType)i) << LL_ENDL;
|
|
gMessageSystem->addUUIDFast(_PREHASH_ItemID, LLUUID::null );
|
|
}
|
|
|
|
LL_DEBUGS("Wearables") << " " << LLWearable::typeToTypeLabel((EWearableType)i) << " : " << (wearable ? wearable->getID() : LLUUID::null) << LL_ENDL;
|
|
}
|
|
gAgent.sendReliableMessage();
|
|
}
|
|
|
|
void LLAgent::saveWearable( EWearableType type, BOOL send_update )
|
|
{
|
|
LLWearable* old_wearable = mWearableEntry[(S32)type].mWearable;
|
|
if( old_wearable && (old_wearable->isDirty() || old_wearable->isOldVersion()) )
|
|
{
|
|
LLWearable* new_wearable = gWearableList.createCopyFromAvatar( old_wearable );
|
|
mWearableEntry[(S32)type].mWearable = new_wearable;
|
|
|
|
LLInventoryItem* item = gInventory.getItem(mWearableEntry[(S32)type].mItemID);
|
|
if( item )
|
|
{
|
|
// Update existing inventory item
|
|
LLPointer<LLViewerInventoryItem> template_item =
|
|
new LLViewerInventoryItem(item->getUUID(),
|
|
item->getParentUUID(),
|
|
item->getPermissions(),
|
|
new_wearable->getID(),
|
|
new_wearable->getAssetType(),
|
|
item->getInventoryType(),
|
|
item->getName(),
|
|
item->getDescription(),
|
|
item->getSaleInfo(),
|
|
item->getFlags(),
|
|
item->getCreationDate());
|
|
template_item->setTransactionID(new_wearable->getTransactionID());
|
|
template_item->updateServer(FALSE);
|
|
gInventory.updateItem(template_item);
|
|
}
|
|
else
|
|
{
|
|
// Add a new inventory item (shouldn't ever happen here)
|
|
U32 todo = addWearableToAgentInventoryCallback::CALL_NONE;
|
|
if (send_update)
|
|
{
|
|
todo |= addWearableToAgentInventoryCallback::CALL_UPDATE;
|
|
}
|
|
LLPointer<LLInventoryCallback> cb =
|
|
new addWearableToAgentInventoryCallback(
|
|
LLPointer<LLRefCount>(NULL),
|
|
(S32)type,
|
|
new_wearable,
|
|
todo);
|
|
addWearableToAgentInventory(cb, new_wearable);
|
|
return;
|
|
}
|
|
|
|
getAvatarObject()->wearableUpdated( type );
|
|
|
|
if( send_update )
|
|
{
|
|
sendAgentWearablesUpdate();
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLAgent::saveWearableAs(
|
|
EWearableType type,
|
|
const std::string& new_name,
|
|
BOOL save_in_lost_and_found)
|
|
{
|
|
if(!isWearableCopyable(type))
|
|
{
|
|
llwarns << "LLAgent::saveWearableAs() not copyable." << llendl;
|
|
return;
|
|
}
|
|
LLWearable* old_wearable = getWearable(type);
|
|
if(!old_wearable)
|
|
{
|
|
llwarns << "LLAgent::saveWearableAs() no old wearable." << llendl;
|
|
return;
|
|
}
|
|
LLInventoryItem* item = gInventory.getItem(mWearableEntry[type].mItemID);
|
|
if(!item)
|
|
{
|
|
llwarns << "LLAgent::saveWearableAs() no inventory item." << llendl;
|
|
return;
|
|
}
|
|
std::string trunc_name(new_name);
|
|
LLStringUtil::truncate(trunc_name, DB_INV_ITEM_NAME_STR_LEN);
|
|
LLWearable* new_wearable = gWearableList.createCopyFromAvatar(
|
|
old_wearable,
|
|
trunc_name);
|
|
LLPointer<LLInventoryCallback> cb =
|
|
new addWearableToAgentInventoryCallback(
|
|
LLPointer<LLRefCount>(NULL),
|
|
type,
|
|
new_wearable,
|
|
addWearableToAgentInventoryCallback::CALL_UPDATE);
|
|
LLUUID category_id;
|
|
if (save_in_lost_and_found)
|
|
{
|
|
category_id = gInventory.findCategoryUUIDForType(
|
|
LLAssetType::AT_LOST_AND_FOUND);
|
|
}
|
|
else
|
|
{
|
|
// put in same folder as original
|
|
category_id = item->getParentUUID();
|
|
}
|
|
|
|
copy_inventory_item(
|
|
gAgent.getID(),
|
|
item->getPermissions().getOwner(),
|
|
item->getUUID(),
|
|
category_id,
|
|
new_name,
|
|
cb);
|
|
|
|
/*
|
|
LLWearable* old_wearable = getWearable( type );
|
|
if( old_wearable )
|
|
{
|
|
std::string old_name = old_wearable->getName();
|
|
old_wearable->setName( new_name );
|
|
LLWearable* new_wearable = gWearableList.createCopyFromAvatar( old_wearable );
|
|
old_wearable->setName( old_name );
|
|
|
|
LLUUID category_id;
|
|
LLInventoryItem* item = gInventory.getItem( mWearableEntry[ type ].mItemID );
|
|
if( item )
|
|
{
|
|
new_wearable->setPermissions(item->getPermissions());
|
|
if (save_in_lost_and_found)
|
|
{
|
|
category_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
|
|
}
|
|
else
|
|
{
|
|
// put in same folder as original
|
|
category_id = item->getParentUUID();
|
|
}
|
|
LLInventoryView* view = LLInventoryView::getActiveInventory();
|
|
if(view)
|
|
{
|
|
view->getPanel()->setSelection(item->getUUID(), TAKE_FOCUS_NO);
|
|
}
|
|
}
|
|
|
|
mWearableEntry[ type ].mWearable = new_wearable;
|
|
LLPointer<LLInventoryCallback> cb =
|
|
new addWearableToAgentInventoryCallback(
|
|
LLPointer<LLRefCount>(NULL),
|
|
type,
|
|
addWearableToAgentInventoryCallback::CALL_UPDATE);
|
|
addWearableToAgentInventory(cb, new_wearable, category_id);
|
|
}
|
|
*/
|
|
}
|
|
|
|
void LLAgent::revertWearable( EWearableType type )
|
|
{
|
|
LLWearable* wearable = mWearableEntry[(S32)type].mWearable;
|
|
if( wearable )
|
|
{
|
|
wearable->writeToAvatar( TRUE );
|
|
}
|
|
sendAgentSetAppearance();
|
|
}
|
|
|
|
void LLAgent::revertAllWearables()
|
|
{
|
|
for( S32 i=0; i < WT_COUNT; i++ )
|
|
{
|
|
revertWearable( (EWearableType)i );
|
|
}
|
|
}
|
|
|
|
void LLAgent::saveAllWearables()
|
|
{
|
|
//if(!gInventory.isLoaded())
|
|
//{
|
|
// return;
|
|
//}
|
|
|
|
for( S32 i=0; i < WT_COUNT; i++ )
|
|
{
|
|
saveWearable( (EWearableType)i, FALSE );
|
|
}
|
|
sendAgentWearablesUpdate();
|
|
}
|
|
|
|
// Called when the user changes the name of a wearable inventory item that is currenlty being worn.
|
|
void LLAgent::setWearableName( const LLUUID& item_id, const std::string& new_name )
|
|
{
|
|
for( S32 i=0; i < WT_COUNT; i++ )
|
|
{
|
|
if( mWearableEntry[i].mItemID == item_id )
|
|
{
|
|
LLWearable* old_wearable = mWearableEntry[i].mWearable;
|
|
llassert( old_wearable );
|
|
|
|
std::string old_name = old_wearable->getName();
|
|
old_wearable->setName( new_name );
|
|
LLWearable* new_wearable = gWearableList.createCopy( old_wearable );
|
|
LLInventoryItem* item = gInventory.getItem(item_id);
|
|
if(item)
|
|
{
|
|
new_wearable->setPermissions(item->getPermissions());
|
|
}
|
|
old_wearable->setName( old_name );
|
|
|
|
mWearableEntry[i].mWearable = new_wearable;
|
|
sendAgentWearablesUpdate();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL LLAgent::isWearableModifiable(EWearableType type)
|
|
{
|
|
LLUUID item_id = getWearableItem(type);
|
|
if(!item_id.isNull())
|
|
{
|
|
LLInventoryItem* item = gInventory.getItem(item_id);
|
|
if(item && item->getPermissions().allowModifyBy(gAgent.getID(),
|
|
gAgent.getGroupID()))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL LLAgent::isWearableCopyable(EWearableType type)
|
|
{
|
|
LLUUID item_id = getWearableItem(type);
|
|
if(!item_id.isNull())
|
|
{
|
|
LLInventoryItem* item = gInventory.getItem(item_id);
|
|
if(item && item->getPermissions().allowCopyBy(gAgent.getID(),
|
|
gAgent.getGroupID()))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
U32 LLAgent::getWearablePermMask(EWearableType type)
|
|
{
|
|
LLUUID item_id = getWearableItem(type);
|
|
if(!item_id.isNull())
|
|
{
|
|
LLInventoryItem* item = gInventory.getItem(item_id);
|
|
if(item)
|
|
{
|
|
return item->getPermissions().getMaskOwner();
|
|
}
|
|
}
|
|
return PERM_NONE;
|
|
}
|
|
|
|
LLInventoryItem* LLAgent::getWearableInventoryItem(EWearableType type)
|
|
{
|
|
LLUUID item_id = getWearableItem(type);
|
|
LLInventoryItem* item = NULL;
|
|
if(item_id.notNull())
|
|
{
|
|
item = gInventory.getItem(item_id);
|
|
}
|
|
return item;
|
|
}
|
|
|
|
LLWearable* LLAgent::getWearableFromWearableItem( const LLUUID& item_id )
|
|
{
|
|
for( S32 i=0; i < WT_COUNT; i++ )
|
|
{
|
|
if( mWearableEntry[i].mItemID == item_id )
|
|
{
|
|
return mWearableEntry[i].mWearable;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void LLAgent::sendAgentWearablesRequest()
|
|
{
|
|
gMessageSystem->newMessageFast(_PREHASH_AgentWearablesRequest);
|
|
gMessageSystem->nextBlockFast(_PREHASH_AgentData);
|
|
gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
|
|
gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
|
|
sendReliableMessage();
|
|
}
|
|
|
|
// Used to enable/disable menu items.
|
|
// static
|
|
BOOL LLAgent::selfHasWearable( void* userdata )
|
|
{
|
|
EWearableType type = (EWearableType)(intptr_t)userdata;
|
|
return gAgent.getWearable( type ) != NULL;
|
|
}
|
|
|
|
BOOL LLAgent::isWearingItem( const LLUUID& item_id )
|
|
{
|
|
const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
|
|
return (getWearableFromWearableItem(base_item_id) != NULL);
|
|
}
|
|
|
|
// static
|
|
void LLAgent::processAgentInitialWearablesUpdate( LLMessageSystem* mesgsys, void** user_data )
|
|
{
|
|
// We should only receive this message a single time. Ignore subsequent AgentWearablesUpdates
|
|
// that may result from AgentWearablesRequest having been sent more than once.
|
|
static bool first = true;
|
|
if (!first) return;
|
|
first = false;
|
|
|
|
LLUUID agent_id;
|
|
gMessageSystem->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
|
|
|
|
LLVOAvatar* avatar = gAgent.getAvatarObject();
|
|
if( avatar && (agent_id == avatar->getID()) )
|
|
{
|
|
gMessageSystem->getU32Fast(_PREHASH_AgentData, _PREHASH_SerialNum, gAgentQueryManager.mUpdateSerialNum );
|
|
|
|
S32 num_wearables = gMessageSystem->getNumberOfBlocksFast(_PREHASH_WearableData);
|
|
if( num_wearables < 4 )
|
|
{
|
|
// Transitional state. Avatars should always have at least their body parts (hair, eyes, shape and skin).
|
|
// The fact that they don't have any here (only a dummy is sent) implies that this account existed
|
|
// before we had wearables, or that the database has gotten messed up.
|
|
return;
|
|
}
|
|
|
|
//lldebugs << "processAgentInitialWearablesUpdate()" << llendl;
|
|
// Add wearables
|
|
LLUUID asset_id_array[ WT_COUNT ];
|
|
S32 i;
|
|
for( i=0; i < num_wearables; i++ )
|
|
{
|
|
U8 type_u8 = 0;
|
|
gMessageSystem->getU8Fast(_PREHASH_WearableData, _PREHASH_WearableType, type_u8, i );
|
|
if( type_u8 >= WT_COUNT )
|
|
{
|
|
continue;
|
|
}
|
|
EWearableType type = (EWearableType) type_u8;
|
|
|
|
LLUUID item_id;
|
|
gMessageSystem->getUUIDFast(_PREHASH_WearableData, _PREHASH_ItemID, item_id, i );
|
|
|
|
LLUUID asset_id;
|
|
gMessageSystem->getUUIDFast(_PREHASH_WearableData, _PREHASH_AssetID, asset_id, i );
|
|
if( asset_id.isNull() )
|
|
{
|
|
LLWearable::removeFromAvatar( type, FALSE );
|
|
}
|
|
else
|
|
{
|
|
LLAssetType::EType asset_type = LLWearable::typeToAssetType( type );
|
|
if( asset_type == LLAssetType::AT_NONE )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
gAgent.mWearableEntry[type].mItemID = item_id;
|
|
asset_id_array[type] = asset_id;
|
|
}
|
|
|
|
LL_DEBUGS("Wearables") << " " << LLWearable::typeToTypeLabel(type) << " " << asset_id << " item id " << gAgent.mWearableEntry[type].mItemID.asString() << LL_ENDL;
|
|
}
|
|
|
|
LLCOFMgr::instance().fetchCOF();
|
|
|
|
// now that we have the asset ids...request the wearable assets
|
|
for( i = 0; i < WT_COUNT; i++ )
|
|
{
|
|
LL_DEBUGS("Wearables") << " fetching " << asset_id_array[i] << LL_ENDL;
|
|
if( !gAgent.mWearableEntry[i].mItemID.isNull() )
|
|
{
|
|
gWearableList.getAsset(
|
|
asset_id_array[i],
|
|
LLStringUtil::null,
|
|
LLWearable::typeToAssetType( (EWearableType) i ),
|
|
LLAgent::onInitialWearableAssetArrived, (void*)(intptr_t)i );
|
|
}
|
|
}
|
|
|
|
// Not really sure where else to put this
|
|
gIdleCallbacks.addFunction(&LLAttachmentsMgr::onIdle, NULL);
|
|
}
|
|
}
|
|
|
|
// A single wearable that the avatar was wearing on start-up has arrived from the database.
|
|
// static
|
|
void LLAgent::onInitialWearableAssetArrived( LLWearable* wearable, void* userdata )
|
|
{
|
|
EWearableType type = (EWearableType)(intptr_t)userdata;
|
|
|
|
LLVOAvatar* avatar = gAgent.getAvatarObject();
|
|
if( !avatar )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( wearable )
|
|
{
|
|
llassert( type == wearable->getType() );
|
|
gAgent.mWearableEntry[ type ].mWearable = wearable;
|
|
|
|
// disable composites if initial textures are baked
|
|
avatar->setupComposites();
|
|
gAgent.queryWearableCache();
|
|
|
|
wearable->writeToAvatar( FALSE );
|
|
avatar->setCompositeUpdatesEnabled(TRUE);
|
|
gInventory.addChangedMask( LLInventoryObserver::LABEL, gAgent.mWearableEntry[type].mItemID );
|
|
}
|
|
else
|
|
{
|
|
// Somehow the asset doesn't exist in the database.
|
|
gAgent.recoverMissingWearable( type );
|
|
}
|
|
|
|
gInventory.notifyObservers();
|
|
|
|
// Have all the wearables that the avatar was wearing at log-in arrived?
|
|
if( !gAgent.mWearablesLoaded )
|
|
{
|
|
gAgent.mWearablesLoaded = TRUE;
|
|
for( S32 i = 0; i < WT_COUNT; i++ )
|
|
{
|
|
if( !gAgent.mWearableEntry[i].mItemID.isNull() && !gAgent.mWearableEntry[i].mWearable )
|
|
{
|
|
gAgent.mWearablesLoaded = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( gAgent.mWearablesLoaded )
|
|
{
|
|
// Make sure that the server's idea of the avatar's wearables actually match the wearables.
|
|
gAgent.sendAgentSetAppearance();
|
|
|
|
// Check to see if there are any baked textures that we hadn't uploaded before we logged off last time.
|
|
// If there are any, schedule them to be uploaded as soon as the layer textures they depend on arrive.
|
|
if( !gAgentCamera.cameraCustomizeAvatar() )
|
|
{
|
|
avatar->requestLayerSetUploads();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Normally, all wearables referred to "AgentWearablesUpdate" will correspond to actual assets in the
|
|
// database. If for some reason, we can't load one of those assets, we can try to reconstruct it so that
|
|
// the user isn't left without a shape, for example. (We can do that only after the inventory has loaded.)
|
|
void LLAgent::recoverMissingWearable( EWearableType type )
|
|
{
|
|
// Try to recover by replacing missing wearable with a new one.
|
|
LLNotifications::instance().add("ReplacedMissingWearable");
|
|
lldebugs << "Wearable " << LLWearable::typeToTypeLabel( type ) << " could not be downloaded. Replaced inventory item with default wearable." << llendl;
|
|
LLWearable* new_wearable = gWearableList.createNewWearable(type);
|
|
|
|
S32 type_s32 = (S32) type;
|
|
mWearableEntry[type_s32].mWearable = new_wearable;
|
|
new_wearable->writeToAvatar( TRUE );
|
|
|
|
// Add a new one in the lost and found folder.
|
|
// (We used to overwrite the "not found" one, but that could potentially
|
|
// destory content.) JC
|
|
LLUUID lost_and_found_id =
|
|
gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
|
|
LLPointer<LLInventoryCallback> cb =
|
|
new addWearableToAgentInventoryCallback(
|
|
LLPointer<LLRefCount>(NULL),
|
|
type_s32,
|
|
new_wearable,
|
|
addWearableToAgentInventoryCallback::CALL_RECOVERDONE);
|
|
addWearableToAgentInventory( cb, new_wearable, lost_and_found_id, TRUE);
|
|
}
|
|
|
|
void LLAgent::recoverMissingWearableDone()
|
|
{
|
|
// Have all the wearables that the avatar was wearing at log-in arrived or been fabricated?
|
|
mWearablesLoaded = TRUE;
|
|
for( S32 i = 0; i < WT_COUNT; i++ )
|
|
{
|
|
if( !mWearableEntry[i].mItemID.isNull() && !mWearableEntry[i].mWearable )
|
|
{
|
|
mWearablesLoaded = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( mWearablesLoaded )
|
|
{
|
|
// Make sure that the server's idea of the avatar's wearables actually match the wearables.
|
|
sendAgentSetAppearance();
|
|
}
|
|
else
|
|
{
|
|
gInventory.addChangedMask( LLInventoryObserver::LABEL, LLUUID::null );
|
|
gInventory.notifyObservers();
|
|
}
|
|
}
|
|
|
|
void LLAgent::createStandardWearables(BOOL female)
|
|
{
|
|
llwarns << "Creating Standard " << (female ? "female" : "male" )
|
|
<< " Wearables" << llendl;
|
|
|
|
if (mAvatarObject.isNull())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(female) mAvatarObject->setSex(SEX_FEMALE);
|
|
else mAvatarObject->setSex(SEX_MALE);
|
|
|
|
BOOL create[WT_COUNT] =
|
|
{
|
|
TRUE, //WT_SHAPE
|
|
TRUE, //WT_SKIN
|
|
TRUE, //WT_HAIR
|
|
TRUE, //WT_EYES
|
|
TRUE, //WT_SHIRT
|
|
TRUE, //WT_PANTS
|
|
TRUE, //WT_SHOES
|
|
TRUE, //WT_SOCKS
|
|
FALSE, //WT_JACKET
|
|
FALSE, //WT_GLOVES
|
|
TRUE, //WT_UNDERSHIRT
|
|
TRUE, //WT_UNDERPANTS
|
|
FALSE, //WT_SKIRT
|
|
FALSE, //WT_ALPHA
|
|
FALSE, //WT_TATTOO
|
|
FALSE, //WT_PHYSICS
|
|
};
|
|
|
|
for( S32 i=0; i < WT_COUNT; i++ )
|
|
{
|
|
bool once = false;
|
|
LLPointer<LLRefCount> donecb = NULL;
|
|
if( create[i] )
|
|
{
|
|
if (!once)
|
|
{
|
|
once = true;
|
|
donecb = new createStandardWearablesAllDoneCallback;
|
|
}
|
|
llassert( mWearableEntry[i].mWearable == NULL );
|
|
LLWearable* wearable = gWearableList.createNewWearable((EWearableType)i);
|
|
mWearableEntry[i].mWearable = wearable;
|
|
// no need to update here...
|
|
LLPointer<LLInventoryCallback> cb =
|
|
new addWearableToAgentInventoryCallback(
|
|
donecb,
|
|
i,
|
|
wearable,
|
|
addWearableToAgentInventoryCallback::CALL_CREATESTANDARDDONE);
|
|
addWearableToAgentInventory(cb, wearable, LLUUID::null, FALSE);
|
|
}
|
|
}
|
|
}
|
|
void LLAgent::createStandardWearablesDone(S32 index)
|
|
{
|
|
LLWearable* wearable = mWearableEntry[index].mWearable;
|
|
|
|
if (wearable)
|
|
{
|
|
wearable->writeToAvatar(TRUE);
|
|
}
|
|
}
|
|
|
|
void LLAgent::createStandardWearablesAllDone()
|
|
{
|
|
// ... because sendAgentWearablesUpdate will notify inventory
|
|
// observers.
|
|
mWearablesLoaded = TRUE;
|
|
sendAgentWearablesUpdate();
|
|
sendAgentSetAppearance();
|
|
|
|
// Treat this as the first texture entry message, if none received yet
|
|
mAvatarObject->onFirstTEMessageReceived();
|
|
}
|
|
|
|
void LLAgent::makeNewOutfit(
|
|
const std::string& new_folder_name,
|
|
const LLDynamicArray<S32>& wearables_to_include,
|
|
const LLDynamicArray<S32>& attachments_to_include,
|
|
BOOL rename_clothing)
|
|
{
|
|
if (mAvatarObject.isNull())
|
|
{
|
|
return;
|
|
}
|
|
|
|
BOOL fUseLinks = !gSavedSettings.getBOOL("UseInventoryLinks") ||
|
|
!gHippoGridManager->getConnectedGrid()->supportsInvLinks();
|
|
BOOL fUseOutfits = gSavedSettings.getBOOL("UseOutfitFolders") &&
|
|
gHippoGridManager->getConnectedGrid()->supportsInvLinks();
|
|
|
|
LLAssetType::EType typeDest = (fUseOutfits) ? LLAssetType::AT_MY_OUTFITS : LLAssetType::AT_CLOTHING;
|
|
LLAssetType::EType typeFolder = (fUseOutfits) ? LLAssetType::AT_OUTFIT : LLAssetType::AT_NONE;
|
|
|
|
// First, make a folder for the outfit.
|
|
LLUUID folder_id = gInventory.createNewCategory(gInventory.findCategoryUUIDForType(typeDest), typeFolder, new_folder_name);
|
|
|
|
bool found_first_item = false;
|
|
|
|
///////////////////
|
|
// Wearables
|
|
|
|
if( wearables_to_include.count() )
|
|
{
|
|
// Then, iterate though each of the wearables and save copies of them in the folder.
|
|
S32 i;
|
|
S32 count = wearables_to_include.count();
|
|
LLPointer<LLRefCount> cbdone = NULL;
|
|
for( i = 0; i < count; ++i )
|
|
{
|
|
S32 index = wearables_to_include[i];
|
|
LLWearable* old_wearable = mWearableEntry[ index ].mWearable;
|
|
if( old_wearable )
|
|
{
|
|
LLViewerInventoryItem* item = gInventory.getItem(mWearableEntry[index].mItemID);
|
|
if (fUseOutfits)
|
|
{
|
|
std::string strOrdering = llformat("@%d", item->getWearableType() * 100);
|
|
|
|
link_inventory_item(
|
|
gAgent.getID(),
|
|
item->getLinkedUUID(),
|
|
folder_id,
|
|
item->getName(),
|
|
strOrdering,
|
|
LLAssetType::AT_LINK,
|
|
LLPointer<LLInventoryCallback>(NULL));
|
|
}
|
|
else
|
|
{
|
|
std::string new_name = item->getName();
|
|
if (rename_clothing)
|
|
{
|
|
new_name = new_folder_name;
|
|
new_name.append(" ");
|
|
new_name.append(old_wearable->getTypeLabel());
|
|
LLStringUtil::truncate(new_name, DB_INV_ITEM_NAME_STR_LEN);
|
|
}
|
|
|
|
if (fUseLinks || isWearableCopyable((EWearableType)index))
|
|
{
|
|
LLWearable* new_wearable = gWearableList.createCopy(old_wearable);
|
|
if (rename_clothing)
|
|
{
|
|
new_wearable->setName(new_name);
|
|
}
|
|
|
|
S32 todo = addWearableToAgentInventoryCallback::CALL_NONE;
|
|
if (!found_first_item)
|
|
{
|
|
found_first_item = true;
|
|
/* set the focus to the first item */
|
|
todo |= addWearableToAgentInventoryCallback::CALL_MAKENEWOUTFITDONE;
|
|
/* send the agent wearables update when done */
|
|
cbdone = new sendAgentWearablesUpdateCallback;
|
|
}
|
|
LLPointer<LLInventoryCallback> cb =
|
|
new addWearableToAgentInventoryCallback(
|
|
cbdone,
|
|
index,
|
|
new_wearable,
|
|
todo);
|
|
if (isWearableCopyable((EWearableType)index))
|
|
{
|
|
copy_inventory_item(
|
|
gAgent.getID(),
|
|
item->getPermissions().getOwner(),
|
|
item->getLinkedUUID(),
|
|
folder_id,
|
|
new_name,
|
|
cb);
|
|
}
|
|
else
|
|
{
|
|
move_inventory_item(
|
|
gAgent.getID(),
|
|
gAgent.getSessionID(),
|
|
item->getLinkedUUID(),
|
|
folder_id,
|
|
new_name,
|
|
cb);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
link_inventory_item(
|
|
gAgent.getID(),
|
|
item->getLinkedUUID(),
|
|
folder_id,
|
|
item->getName(), // Apparently, links cannot have arbitrary names...
|
|
item->getDescription(),
|
|
LLAssetType::AT_LINK,
|
|
LLPointer<LLInventoryCallback>(NULL));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
gInventory.notifyObservers();
|
|
}
|
|
|
|
|
|
///////////////////
|
|
// Attachments
|
|
|
|
if( attachments_to_include.count() )
|
|
{
|
|
for( S32 i = 0; i < attachments_to_include.count(); i++ )
|
|
{
|
|
S32 attachment_pt = attachments_to_include[i];
|
|
LLViewerJointAttachment* attachment = get_if_there(mAvatarObject->mAttachmentPoints, attachment_pt, (LLViewerJointAttachment*)NULL );
|
|
if(!attachment) continue;
|
|
for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
|
|
attachment_iter != attachment->mAttachedObjects.end();
|
|
++attachment_iter)
|
|
{
|
|
LLViewerObject *attached_object = (*attachment_iter);
|
|
if (!attached_object) continue;
|
|
const LLUUID& item_id = attached_object->getAttachmentItemID();
|
|
if (item_id.isNull()) continue;
|
|
LLInventoryItem* item = gInventory.getItem(item_id);
|
|
if (!item) continue;
|
|
if (fUseOutfits)
|
|
{
|
|
link_inventory_item(
|
|
gAgent.getID(),
|
|
item->getLinkedUUID(),
|
|
folder_id,
|
|
item->getName(),
|
|
item->getDescription(),
|
|
LLAssetType::AT_LINK,
|
|
LLPointer<LLInventoryCallback>(NULL));
|
|
}
|
|
else
|
|
{
|
|
if (fUseLinks || item->getPermissions().allowCopyBy(gAgent.getID()))
|
|
{
|
|
const LLUUID& old_folder_id = item->getParentUUID();
|
|
|
|
move_inventory_item(
|
|
gAgent.getID(),
|
|
gAgent.getSessionID(),
|
|
item->getLinkedUUID(),
|
|
folder_id,
|
|
item->getName(),
|
|
LLPointer<LLInventoryCallback>(NULL));
|
|
|
|
if (item->getPermissions().allowCopyBy(gAgent.getID()))
|
|
{
|
|
copy_inventory_item(
|
|
gAgent.getID(),
|
|
item->getPermissions().getOwner(),
|
|
item->getLinkedUUID(),
|
|
old_folder_id,
|
|
item->getName(),
|
|
LLPointer<LLInventoryCallback>(NULL));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
link_inventory_item(
|
|
gAgent.getID(),
|
|
item->getLinkedUUID(),
|
|
folder_id,
|
|
item->getName(),
|
|
item->getDescription(),
|
|
LLAssetType::AT_LINK,
|
|
LLPointer<LLInventoryCallback>(NULL));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLAgent::makeNewOutfitDone(S32 index)
|
|
{
|
|
LLUUID first_item_id = mWearableEntry[index].mItemID;
|
|
// Open the inventory and select the first item we added.
|
|
if( first_item_id.notNull() )
|
|
{
|
|
LLInventoryView* view = LLInventoryView::getActiveInventory();
|
|
if(view)
|
|
{
|
|
view->getPanel()->setSelection(first_item_id, TAKE_FOCUS_NO);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void LLAgent::addWearableToAgentInventory(
|
|
LLPointer<LLInventoryCallback> cb,
|
|
LLWearable* wearable,
|
|
const LLUUID& category_id,
|
|
BOOL notify)
|
|
{
|
|
create_inventory_item(
|
|
gAgent.getID(),
|
|
gAgent.getSessionID(),
|
|
category_id,
|
|
wearable->getTransactionID(),
|
|
wearable->getName(),
|
|
wearable->getDescription(),
|
|
wearable->getAssetType(),
|
|
LLInventoryType::IT_WEARABLE,
|
|
wearable->getType(),
|
|
wearable->getPermissions().getMaskNextOwner(),
|
|
cb);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// sendAgentSetAppearance()
|
|
//-----------------------------------------------------------------------------
|
|
void LLAgent::sendAgentSetAppearance()
|
|
{
|
|
if (!isAgentAvatarValid()) return;
|
|
|
|
if (gAgentQueryManager.mNumPendingQueries > 0 && !gAgentCamera.cameraCustomizeAvatar())
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
llinfos << "TAT: Sent AgentSetAppearance: " << mAvatarObject->getBakedStatusForPrintout() << llendl;
|
|
//dumpAvatarTEs( "sendAgentSetAppearance()" );
|
|
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_AgentSetAppearance);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
|
|
|
|
// correct for the collision tolerance (to make it look like the
|
|
// agent is actually walking on the ground/object)
|
|
// NOTE -- when we start correcting all of the other Havok geometry
|
|
// to compensate for the COLLISION_TOLERANCE ugliness we will have
|
|
// to tweak this number again
|
|
LLVector3 body_size = mAvatarObject->mBodySize;
|
|
|
|
body_size.mV[VX] = body_size.mV[VX] + gSavedSettings.getF32("AscentAvatarXModifier");
|
|
body_size.mV[VY] = body_size.mV[VY] + gSavedSettings.getF32("AscentAvatarYModifier");
|
|
body_size.mV[VZ] = body_size.mV[VZ] + gSavedSettings.getF32("AscentAvatarZModifier");
|
|
|
|
msg->addVector3Fast(_PREHASH_Size, body_size);
|
|
|
|
// To guard against out of order packets
|
|
// Note: always start by sending 1. This resets the server's count. 0 on the server means "uninitialized"
|
|
mAppearanceSerialNum++;
|
|
msg->addU32Fast(_PREHASH_SerialNum, mAppearanceSerialNum );
|
|
|
|
// is texture data current relative to wearables?
|
|
// KLW - TAT this will probably need to check the local queue.
|
|
BOOL textures_current = !mAvatarObject->hasPendingBakedUploads() && mWearablesLoaded;
|
|
|
|
for(U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++ )
|
|
{
|
|
const ETextureIndex texture_index = getTextureIndex((EBakedTextureIndex)baked_index);
|
|
|
|
// if we're not wearing a skirt, we don't need the texture to be baked
|
|
if (texture_index == TEX_SKIRT_BAKED && !mAvatarObject->isWearingWearableType(WT_SKIRT))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// IMG_DEFAULT_AVATAR means not baked
|
|
if (!mAvatarObject->isTextureDefined(texture_index))
|
|
{
|
|
textures_current = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// only update cache entries if we have all our baked textures
|
|
if (textures_current)
|
|
{
|
|
llinfos << "TAT: Sending cached texture data" << llendl;
|
|
for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++)
|
|
{
|
|
const LLVOAvatarDictionary::WearableDictionaryEntry *wearable_dict = LLVOAvatarDictionary::getInstance()->getWearable((EBakedTextureIndex)baked_index);
|
|
LLUUID hash;
|
|
for (U8 i=0; i < wearable_dict->mWearablesVec.size(); i++)
|
|
{
|
|
// EWearableType wearable_type = gBakedWearableMap[baked_index][wearable_num];
|
|
const EWearableType wearable_type = wearable_dict->mWearablesVec[i];
|
|
const LLWearable* wearable = getWearable(wearable_type);
|
|
if (wearable)
|
|
{
|
|
hash ^= wearable->getID();
|
|
}
|
|
}
|
|
if (hash.notNull())
|
|
{
|
|
hash ^= wearable_dict->mHashID;
|
|
}
|
|
|
|
const ETextureIndex texture_index = getTextureIndex((EBakedTextureIndex)baked_index);
|
|
|
|
msg->nextBlockFast(_PREHASH_WearableData);
|
|
msg->addUUIDFast(_PREHASH_CacheID, hash);
|
|
msg->addU8Fast(_PREHASH_TextureIndex, (U8)texture_index);
|
|
}
|
|
msg->nextBlockFast(_PREHASH_ObjectData);
|
|
|
|
/*if (gSavedSettings.getBOOL("AscentUseCustomTag"))
|
|
{
|
|
LLColor4 color;
|
|
if (!gSavedSettings.getBOOL("AscentStoreSettingsPerAccount"))
|
|
{
|
|
color = gSavedSettings.setColor4("AscentCustomTagColor");
|
|
}
|
|
else
|
|
{
|
|
color = gSavedPerAccountSettings.getColor4("AscentCustomTagColor");
|
|
}
|
|
LLUUID old_teid;
|
|
U8 client_buffer[UUID_BYTES];
|
|
memset(&client_buffer, 0, UUID_BYTES);
|
|
LLTextureEntry* entry = (LLTextureEntry*)mAvatarObject->getTE(0);
|
|
old_teid = entry->getID();
|
|
//You edit this to change the tag in your client. Yes.
|
|
const char* tag_client = "Ascent";
|
|
strncpy((char*)&client_buffer[0], tag_client, UUID_BYTES);
|
|
LLUUID part_a;
|
|
memcpy(&part_a.mData, &client_buffer[0], UUID_BYTES);
|
|
entry->setColor(color);
|
|
//This glow is used to tell if the tag color and name is set or not.
|
|
entry->setGlow(0.1f);
|
|
entry->setID(part_a);
|
|
mAvatarObject->packTEMessage( gMessageSystem, 1, gSavedSettings.getString("AscentReportClientUUID") );
|
|
entry->setID(old_teid);
|
|
|
|
}
|
|
else
|
|
{*/
|
|
if (gSavedSettings.getBOOL("AscentUseTag"))
|
|
mAvatarObject->packTEMessage( gMessageSystem, 1, gSavedSettings.getString("AscentReportClientUUID"));
|
|
else
|
|
mAvatarObject->packTEMessage( gMessageSystem, 1, "c228d1cf-4b5d-4ba8-84f4-899a0796aa97");
|
|
//}
|
|
resetClientTag();
|
|
|
|
}
|
|
else
|
|
{
|
|
// If the textures aren't baked, send NULL for texture IDs
|
|
// This means the baked texture IDs on the server will be untouched.
|
|
// Once all textures are baked, another AvatarAppearance message will be sent to update the TEs
|
|
msg->nextBlockFast(_PREHASH_ObjectData);
|
|
gMessageSystem->addBinaryDataFast(_PREHASH_TextureEntry, NULL, 0);
|
|
}
|
|
|
|
|
|
static bool send_physics_params = false;
|
|
send_physics_params |= !!getWearable(WT_PHYSICS);
|
|
S32 transmitted_params = 0;
|
|
for (LLViewerVisualParam* param = (LLViewerVisualParam*)mAvatarObject->getFirstVisualParam();
|
|
param;
|
|
param = (LLViewerVisualParam*)mAvatarObject->getNextVisualParam())
|
|
{
|
|
if (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE) // do not transmit params of group VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT
|
|
{
|
|
//A hack to prevent ruthing on older viewers when phys wearables aren't being worn.
|
|
if(!send_physics_params && param->getID() >= 10000)
|
|
{
|
|
break;
|
|
}
|
|
msg->nextBlockFast(_PREHASH_VisualParam );
|
|
// We don't send the param ids. Instead, we assume that the receiver has the same params in the same sequence.
|
|
const F32 param_value = param->getWeight();
|
|
const U8 new_weight = F32_to_U8(param_value, param->getMinWeight(), param->getMaxWeight());
|
|
|
|
msg->addU8Fast(_PREHASH_ParamValue, new_weight );
|
|
transmitted_params++;
|
|
}
|
|
}
|
|
|
|
// llinfos << "Avatar XML num VisualParams transmitted = " << transmitted_params << llendl;
|
|
sendReliableMessage();
|
|
}
|
|
|
|
void LLAgent::sendAgentDataUpdateRequest()
|
|
{
|
|
gMessageSystem->newMessageFast(_PREHASH_AgentDataUpdateRequest);
|
|
gMessageSystem->nextBlockFast(_PREHASH_AgentData);
|
|
gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
|
|
gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
sendReliableMessage();
|
|
}
|
|
|
|
void LLAgent::removeWearable( EWearableType type )
|
|
{
|
|
LLWearable* old_wearable = mWearableEntry[ type ].mWearable;
|
|
|
|
if ( (gAgent.isTeen())
|
|
&& (type == WT_UNDERSHIRT || type == WT_UNDERPANTS))
|
|
{
|
|
// Can't take off underclothing in simple UI mode or on PG accounts
|
|
return;
|
|
}
|
|
|
|
// [RLVa:KB] - Checked: 2009-07-07 (RLVa-1.1.3b)
|
|
if ( (rlv_handler_t::isEnabled()) && (!gRlvWearableLocks.canRemove(type)) )
|
|
{
|
|
return;
|
|
}
|
|
// [/RLVa:KB]
|
|
|
|
if( old_wearable )
|
|
{
|
|
if( old_wearable->isDirty() )
|
|
{
|
|
LLSD payload;
|
|
payload["wearable_type"] = (S32)type;
|
|
// Bring up view-modal dialog: Save changes? Yes, No, Cancel
|
|
LLNotifications::instance().add("WearableSave", LLSD(), payload, &LLAgent::onRemoveWearableDialog);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
removeWearableFinal( type );
|
|
}
|
|
}
|
|
}
|
|
|
|
// static
|
|
bool LLAgent::onRemoveWearableDialog(const LLSD& notification, const LLSD& response )
|
|
{
|
|
S32 option = LLNotification::getSelectedOption(notification, response);
|
|
EWearableType type = (EWearableType)notification["payload"]["wearable_type"].asInteger();
|
|
switch( option )
|
|
{
|
|
case 0: // "Save"
|
|
gAgent.saveWearable( type );
|
|
gAgent.removeWearableFinal( type );
|
|
break;
|
|
|
|
case 1: // "Don't Save"
|
|
gAgent.removeWearableFinal( type );
|
|
break;
|
|
|
|
case 2: // "Cancel"
|
|
break;
|
|
|
|
default:
|
|
llassert(0);
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Called by removeWearable() and onRemoveWearableDialog() to actually do the removal.
|
|
void LLAgent::removeWearableFinal( EWearableType type )
|
|
{
|
|
LLWearable* old_wearable = mWearableEntry[ type ].mWearable;
|
|
|
|
gInventory.addChangedMask( LLInventoryObserver::LABEL, mWearableEntry[type].mItemID );
|
|
|
|
mWearableEntry[ type ].mWearable = NULL;
|
|
mWearableEntry[ type ].mItemID.setNull();
|
|
|
|
queryWearableCache();
|
|
|
|
if( old_wearable )
|
|
{
|
|
old_wearable->removeFromAvatar( TRUE );
|
|
}
|
|
|
|
// Update the server
|
|
sendAgentWearablesUpdate();
|
|
sendAgentSetAppearance();
|
|
gInventory.notifyObservers();
|
|
}
|
|
|
|
void LLAgent::copyWearableToInventory( EWearableType type )
|
|
{
|
|
LLWearable* wearable = mWearableEntry[ type ].mWearable;
|
|
if( wearable )
|
|
{
|
|
// Save the old wearable if it has changed.
|
|
if( wearable->isDirty() )
|
|
{
|
|
wearable = gWearableList.createCopyFromAvatar( wearable );
|
|
mWearableEntry[ type ].mWearable = wearable;
|
|
}
|
|
|
|
// Make a new entry in the inventory. (Put it in the same folder as the original item if possible.)
|
|
LLUUID category_id;
|
|
LLInventoryItem* item = gInventory.getItem( mWearableEntry[ type ].mItemID );
|
|
if( item )
|
|
{
|
|
category_id = item->getParentUUID();
|
|
wearable->setPermissions(item->getPermissions());
|
|
}
|
|
LLPointer<LLInventoryCallback> cb =
|
|
new addWearableToAgentInventoryCallback(
|
|
LLPointer<LLRefCount>(NULL),
|
|
type,
|
|
wearable);
|
|
addWearableToAgentInventory(cb, wearable, category_id);
|
|
}
|
|
}
|
|
|
|
|
|
// A little struct to let setWearable() communicate more than one value with onSetWearableDialog().
|
|
struct LLSetWearableData
|
|
{
|
|
LLSetWearableData( const LLUUID& new_item_id, LLWearable* new_wearable ) :
|
|
mNewItemID( new_item_id ), mNewWearable( new_wearable ) {}
|
|
LLUUID mNewItemID;
|
|
LLWearable* mNewWearable;
|
|
};
|
|
|
|
static bool isFirstPhysicsWearable(EWearableType type, LLInventoryItem *new_item, LLWearable *new_wearable)
|
|
{
|
|
if (type == WT_PHYSICS && gSavedSettings.getWarning("FirstPhysicsWearable"))
|
|
{
|
|
class WearableDelayedCallback
|
|
{
|
|
public:
|
|
static void setDelayedWearable( const LLSD& notification, const LLSD& response, LLUUID item_id, LLWearable *wearable )
|
|
{
|
|
if(LLNotification::getSelectedOption(notification, response) == 0) //User selected wear
|
|
{
|
|
gSavedSettings.setWarning("FirstPhysicsWearable",FALSE);
|
|
LLInventoryItem *item = gInventory.getItem(item_id);
|
|
if(item)
|
|
gAgent.setWearable(item,wearable); //re-enter.
|
|
}
|
|
}
|
|
};
|
|
LLNotifications::instance().add("FirstPhysicsWearable",LLSD(),LLSD(),boost::bind(WearableDelayedCallback::setDelayedWearable, _1, _2, new_item->getUUID(),new_wearable));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
BOOL LLAgent::needsReplacement(EWearableType wearableType, S32 remove)
|
|
{
|
|
return TRUE;
|
|
/*if (remove) return TRUE;
|
|
|
|
return getWearable(wearableType) ? TRUE : FALSE;*/
|
|
}
|
|
|
|
// Assumes existing wearables are not dirty.
|
|
void LLAgent::setWearableOutfit(
|
|
const LLInventoryItem::item_array_t& items,
|
|
const LLDynamicArray< LLWearable* >& wearables,
|
|
BOOL remove )
|
|
{
|
|
lldebugs << "setWearableOutfit() start" << llendl;
|
|
|
|
BOOL wearables_to_remove[WT_COUNT];
|
|
wearables_to_remove[WT_SHAPE] = FALSE;
|
|
wearables_to_remove[WT_SKIN] = FALSE;
|
|
wearables_to_remove[WT_HAIR] = FALSE;
|
|
wearables_to_remove[WT_EYES] = FALSE;
|
|
// [RLVa:KB] - Checked: 2009-07-06 (RLVa-1.1.3b) | Added: RLVa-0.2.2a
|
|
wearables_to_remove[WT_SHIRT] = remove && gRlvWearableLocks.canRemove(WT_SHIRT);
|
|
wearables_to_remove[WT_PANTS] = remove && gRlvWearableLocks.canRemove(WT_PANTS);
|
|
wearables_to_remove[WT_SHOES] = remove && gRlvWearableLocks.canRemove(WT_SHOES);
|
|
wearables_to_remove[WT_SOCKS] = remove && gRlvWearableLocks.canRemove(WT_SOCKS);
|
|
wearables_to_remove[WT_JACKET] = remove && gRlvWearableLocks.canRemove(WT_JACKET);
|
|
wearables_to_remove[WT_GLOVES] = remove && gRlvWearableLocks.canRemove(WT_GLOVES);
|
|
wearables_to_remove[WT_UNDERSHIRT] = (!gAgent.isTeen()) && remove && gRlvWearableLocks.canRemove(WT_UNDERSHIRT);
|
|
wearables_to_remove[WT_UNDERPANTS] = (!gAgent.isTeen()) && remove && gRlvWearableLocks.canRemove(WT_UNDERPANTS);
|
|
wearables_to_remove[WT_SKIRT] = remove && gRlvWearableLocks.canRemove(WT_SKIRT);
|
|
wearables_to_remove[WT_ALPHA] = remove && gRlvWearableLocks.canRemove(WT_ALPHA);
|
|
wearables_to_remove[WT_TATTOO] = remove && gRlvWearableLocks.canRemove(WT_TATTOO);
|
|
wearables_to_remove[WT_PHYSICS] = remove && gRlvWearableLocks.canRemove(WT_PHYSICS);
|
|
// [/RLVa:KB]
|
|
|
|
S32 count = wearables.count();
|
|
llassert( items.count() == count );
|
|
|
|
S32 i;
|
|
for( i = 0; i < count; i++ )
|
|
{
|
|
LLWearable* new_wearable = wearables[i];
|
|
LLPointer<LLInventoryItem> new_item = items[i];
|
|
|
|
EWearableType type = new_wearable->getType();
|
|
wearables_to_remove[type] = FALSE;
|
|
|
|
LLWearable* old_wearable = mWearableEntry[ type ].mWearable;
|
|
if( old_wearable )
|
|
{
|
|
const LLUUID& old_item_id = mWearableEntry[ type ].mItemID;
|
|
if( (old_wearable->getID() == new_wearable->getID()) &&
|
|
(old_item_id == new_item->getUUID()) )
|
|
{
|
|
lldebugs << "No change to wearable asset and item: " << LLWearable::typeToTypeName( type ) << llendl;
|
|
continue;
|
|
}
|
|
|
|
gInventory.addChangedMask(LLInventoryObserver::LABEL, old_item_id);
|
|
|
|
// Assumes existing wearables are not dirty.
|
|
if( old_wearable->isDirty() )
|
|
{
|
|
llassert(0);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (isFirstPhysicsWearable(type, new_item, new_wearable))
|
|
{
|
|
return;
|
|
}
|
|
|
|
mWearableEntry[ type ].mItemID = new_item->getUUID();
|
|
mWearableEntry[ type ].mWearable = new_wearable;
|
|
}
|
|
|
|
std::vector<LLWearable*> wearables_being_removed;
|
|
|
|
for( i = 0; i < WT_COUNT; i++ )
|
|
{
|
|
if( wearables_to_remove[i] )
|
|
{
|
|
wearables_being_removed.push_back(mWearableEntry[ i ].mWearable);
|
|
mWearableEntry[ i ].mWearable = NULL;
|
|
|
|
gInventory.addChangedMask(LLInventoryObserver::LABEL, mWearableEntry[ i ].mItemID);
|
|
mWearableEntry[ i ].mItemID.setNull();
|
|
}
|
|
}
|
|
|
|
gInventory.notifyObservers();
|
|
|
|
queryWearableCache();
|
|
|
|
std::vector<LLWearable*>::iterator wearable_iter;
|
|
|
|
for( wearable_iter = wearables_being_removed.begin();
|
|
wearable_iter != wearables_being_removed.end();
|
|
++wearable_iter)
|
|
{
|
|
LLWearable* wearablep = *wearable_iter;
|
|
if (wearablep)
|
|
{
|
|
wearablep->removeFromAvatar( TRUE );
|
|
}
|
|
}
|
|
|
|
for( i = 0; i < count; i++ )
|
|
{
|
|
wearables[i]->writeToAvatar( TRUE );
|
|
}
|
|
|
|
// Start rendering & update the server
|
|
mWearablesLoaded = TRUE;
|
|
sendAgentWearablesUpdate();
|
|
sendAgentSetAppearance();
|
|
|
|
lldebugs << "setWearableOutfit() end" << llendl;
|
|
}
|
|
|
|
|
|
// User has picked "wear on avatar" from a menu.
|
|
void LLAgent::setWearable( LLInventoryItem* new_item, LLWearable* new_wearable )
|
|
{
|
|
EWearableType type = new_wearable->getType();
|
|
|
|
LLWearable* old_wearable = mWearableEntry[ type ].mWearable;
|
|
|
|
// [RLVa:KB] - Checked: 2009-07-07 (RLVa-1.0.0d)
|
|
// Block if: we can't wear on that layer; or we're already wearing something there we can't take off
|
|
if ( (rlv_handler_t::isEnabled()) && (!gRlvWearableLocks.canWear(type)) )
|
|
{
|
|
return;
|
|
}
|
|
// [/RLVa:KB]
|
|
|
|
if (isFirstPhysicsWearable(type, new_item, new_wearable))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( old_wearable )
|
|
{
|
|
const LLUUID& old_item_id = mWearableEntry[ type ].mItemID;
|
|
if( (old_wearable->getID() == new_wearable->getID()) &&
|
|
(old_item_id == new_item->getUUID()) )
|
|
{
|
|
lldebugs << "No change to wearable asset and item: " << LLWearable::typeToTypeName( type ) << llendl;
|
|
return;
|
|
}
|
|
|
|
if( old_wearable->isDirty() )
|
|
{
|
|
// Bring up modal dialog: Save changes? Yes, No, Cancel
|
|
LLSD payload;
|
|
payload["item_id"] = new_item->getUUID();
|
|
LLNotifications::instance().add( "WearableSave", LLSD(), payload, boost::bind(LLAgent::onSetWearableDialog, _1, _2, new_wearable));
|
|
return;
|
|
}
|
|
}
|
|
|
|
setWearableFinal( new_item, new_wearable );
|
|
}
|
|
|
|
// static
|
|
bool LLAgent::onSetWearableDialog( const LLSD& notification, const LLSD& response, LLWearable* wearable )
|
|
{
|
|
S32 option = LLNotification::getSelectedOption(notification, response);
|
|
LLInventoryItem* new_item = gInventory.getItem( notification["payload"]["item_id"].asUUID());
|
|
if( !new_item )
|
|
{
|
|
delete wearable;
|
|
return false;
|
|
}
|
|
|
|
switch( option )
|
|
{
|
|
case 0: // "Save"
|
|
gAgent.saveWearable( wearable->getType() );
|
|
gAgent.setWearableFinal( new_item, wearable );
|
|
break;
|
|
|
|
case 1: // "Don't Save"
|
|
gAgent.setWearableFinal( new_item, wearable );
|
|
break;
|
|
|
|
case 2: // "Cancel"
|
|
break;
|
|
|
|
default:
|
|
llassert(0);
|
|
break;
|
|
}
|
|
|
|
delete wearable;
|
|
return false;
|
|
}
|
|
|
|
// Called from setWearable() and onSetWearableDialog() to actually set the wearable.
|
|
void LLAgent::setWearableFinal( LLInventoryItem* new_item, LLWearable* new_wearable )
|
|
{
|
|
EWearableType type = new_wearable->getType();
|
|
|
|
// Replace the old wearable with a new one.
|
|
llassert( new_item->getAssetUUID() == new_wearable->getID() );
|
|
LLUUID old_item_id = mWearableEntry[ type ].mItemID;
|
|
mWearableEntry[ type ].mItemID = new_item->getUUID();
|
|
mWearableEntry[ type ].mWearable = new_wearable;
|
|
|
|
if (old_item_id.notNull())
|
|
{
|
|
gInventory.addChangedMask( LLInventoryObserver::LABEL, old_item_id );
|
|
gInventory.notifyObservers();
|
|
}
|
|
|
|
//llinfos << "LLVOAvatar::setWearable()" << llendl;
|
|
queryWearableCache();
|
|
new_wearable->writeToAvatar( TRUE );
|
|
|
|
// Update the server
|
|
sendAgentWearablesUpdate();
|
|
sendAgentSetAppearance();
|
|
}
|
|
|
|
void LLAgent::queryWearableCache()
|
|
{
|
|
if (!mWearablesLoaded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Look up affected baked textures.
|
|
// If they exist:
|
|
// disallow updates for affected layersets (until dataserver responds with cache request.)
|
|
// If cache miss, turn updates back on and invalidate composite.
|
|
// If cache hit, modify baked texture entries.
|
|
//
|
|
// Cache requests contain list of hashes for each baked texture entry.
|
|
// Response is list of valid baked texture assets. (same message)
|
|
|
|
gMessageSystem->newMessageFast(_PREHASH_AgentCachedTexture);
|
|
gMessageSystem->nextBlockFast(_PREHASH_AgentData);
|
|
gMessageSystem->addUUIDFast(_PREHASH_AgentID, getID());
|
|
gMessageSystem->addUUIDFast(_PREHASH_SessionID, getSessionID());
|
|
gMessageSystem->addS32Fast(_PREHASH_SerialNum, gAgentQueryManager.mWearablesCacheQueryID);
|
|
|
|
S32 num_queries = 0;
|
|
for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++ )
|
|
{
|
|
const LLVOAvatarDictionary::WearableDictionaryEntry *wearable_dict = LLVOAvatarDictionary::getInstance()->getWearable((EBakedTextureIndex)baked_index);
|
|
LLUUID hash;
|
|
for (U8 i=0; i < wearable_dict->mWearablesVec.size(); i++)
|
|
{
|
|
// EWearableType wearable_type = gBakedWearableMap[baked_index][wearable_num];
|
|
const EWearableType wearable_type = wearable_dict->mWearablesVec[i];
|
|
const LLWearable* wearable = getWearable(wearable_type);
|
|
if (wearable)
|
|
{
|
|
hash ^= wearable->getID();
|
|
}
|
|
}
|
|
if (hash.notNull())
|
|
{
|
|
hash ^= wearable_dict->mHashID;
|
|
num_queries++;
|
|
// *NOTE: make sure at least one request gets packed
|
|
|
|
//llinfos << "Requesting texture for hash " << hash << " in baked texture slot " << baked_index << llendl;
|
|
gMessageSystem->nextBlockFast(_PREHASH_WearableData);
|
|
gMessageSystem->addUUIDFast(_PREHASH_ID, hash);
|
|
gMessageSystem->addU8Fast(_PREHASH_TextureIndex, (U8)baked_index);
|
|
}
|
|
|
|
gAgentQueryManager.mActiveCacheQueries[ baked_index ] = gAgentQueryManager.mWearablesCacheQueryID;
|
|
}
|
|
|
|
llinfos << "Requesting texture cache entry for " << num_queries << " baked textures" << llendl;
|
|
gMessageSystem->sendReliable(getRegion()->getHost());
|
|
gAgentQueryManager.mNumPendingQueries++;
|
|
gAgentQueryManager.mWearablesCacheQueryID++;
|
|
}
|
|
|
|
// User has picked "remove from avatar" from a menu.
|
|
// static
|
|
void LLAgent::userRemoveWearable( void* userdata )
|
|
{
|
|
EWearableType type = (EWearableType)(intptr_t)userdata;
|
|
|
|
if( !(type==WT_SHAPE || type==WT_SKIN || type==WT_HAIR || type==WT_EYES) ) //&&
|
|
//!((!gAgent.isTeen()) && ( type==WT_UNDERPANTS || type==WT_UNDERSHIRT )) )
|
|
{
|
|
gAgent.removeWearable( type );
|
|
}
|
|
}
|
|
|
|
void LLAgent::userRemoveAllClothes( void* userdata )
|
|
{
|
|
// We have to do this up front to avoid having to deal with the case of multiple wearables being dirty.
|
|
if( gFloaterCustomize )
|
|
{
|
|
gFloaterCustomize->askToSaveIfDirty( LLAgent::userRemoveAllClothesStep2, NULL );
|
|
}
|
|
else
|
|
{
|
|
LLAgent::userRemoveAllClothesStep2( TRUE, NULL );
|
|
}
|
|
}
|
|
|
|
void LLAgent::userRemoveAllClothesStep2( BOOL proceed, void* userdata )
|
|
{
|
|
if( proceed )
|
|
{
|
|
gAgent.removeWearable( WT_SHIRT );
|
|
gAgent.removeWearable( WT_PANTS );
|
|
gAgent.removeWearable( WT_SHOES );
|
|
gAgent.removeWearable( WT_SOCKS );
|
|
gAgent.removeWearable( WT_JACKET );
|
|
gAgent.removeWearable( WT_GLOVES );
|
|
gAgent.removeWearable( WT_UNDERSHIRT );
|
|
gAgent.removeWearable( WT_UNDERPANTS );
|
|
gAgent.removeWearable( WT_SKIRT );
|
|
gAgent.removeWearable( WT_ALPHA );
|
|
gAgent.removeWearable( WT_TATTOO );
|
|
gAgent.removeWearable( WT_PHYSICS );
|
|
}
|
|
}
|
|
|
|
void LLAgent::userRemoveAllAttachments( void* userdata )
|
|
{
|
|
LLVOAvatar* avatarp = gAgent.getAvatarObject();
|
|
if(!avatarp)
|
|
{
|
|
llwarns << "No avatar found." << llendl;
|
|
return;
|
|
}
|
|
|
|
llvo_vec_t objects_to_remove;
|
|
|
|
for (LLVOAvatar::attachment_map_t::iterator iter = avatarp->mAttachmentPoints.begin();
|
|
iter != avatarp->mAttachmentPoints.end();)
|
|
{
|
|
LLVOAvatar::attachment_map_t::iterator curiter = iter++;
|
|
LLViewerJointAttachment* attachment = curiter->second;
|
|
for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
|
|
attachment_iter != attachment->mAttachedObjects.end();
|
|
++attachment_iter)
|
|
{
|
|
LLViewerObject *attached_object = (*attachment_iter);
|
|
// if (attached_object)
|
|
// [RLVa:KB] - Checked: 2010-09-28 (RLVa-1.1.3b) | Modified: RLVa-1.1.3b
|
|
if ( (attached_object) && ((!rlv_handler_t::isEnabled()) || (!gRlvAttachmentLocks.isLockedAttachment(attached_object))) )
|
|
// [/RLVa:KB]
|
|
{
|
|
objects_to_remove.push_back(attached_object);
|
|
}
|
|
}
|
|
}
|
|
userRemoveMultipleAttachments(objects_to_remove);
|
|
}
|
|
|
|
void LLAgent::observeFriends()
|
|
{
|
|
if(!mFriendObserver)
|
|
{
|
|
mFriendObserver = new LLAgentFriendObserver;
|
|
LLAvatarTracker::instance().addObserver(mFriendObserver);
|
|
friendsChanged();
|
|
}
|
|
}
|
|
|
|
void LLAgent::parseTeleportMessages(const std::string& xml_filename)
|
|
{
|
|
LLXMLNodePtr root;
|
|
BOOL success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);
|
|
|
|
if (!success || !root || !root->hasName( "teleport_messages" ))
|
|
{
|
|
llerrs << "Problem reading teleport string XML file: "
|
|
<< xml_filename << llendl;
|
|
return;
|
|
}
|
|
|
|
for (LLXMLNode* message_set = root->getFirstChild();
|
|
message_set != NULL;
|
|
message_set = message_set->getNextSibling())
|
|
{
|
|
if ( !message_set->hasName("message_set") ) continue;
|
|
|
|
std::map<std::string, std::string> *teleport_msg_map = NULL;
|
|
std::string message_set_name;
|
|
|
|
if ( message_set->getAttributeString("name", message_set_name) )
|
|
{
|
|
//now we loop over all the string in the set and add them
|
|
//to the appropriate set
|
|
if ( message_set_name == "errors" )
|
|
{
|
|
teleport_msg_map = &sTeleportErrorMessages;
|
|
}
|
|
else if ( message_set_name == "progress" )
|
|
{
|
|
teleport_msg_map = &sTeleportProgressMessages;
|
|
}
|
|
}
|
|
|
|
if ( !teleport_msg_map ) continue;
|
|
|
|
std::string message_name;
|
|
for (LLXMLNode* message_node = message_set->getFirstChild();
|
|
message_node != NULL;
|
|
message_node = message_node->getNextSibling())
|
|
{
|
|
if ( message_node->hasName("message") &&
|
|
message_node->getAttributeString("name", message_name) )
|
|
{
|
|
(*teleport_msg_map)[message_name] =
|
|
message_node->getTextContents();
|
|
} //end if ( message exists and has a name)
|
|
} //end for (all message in set)
|
|
}//end for (all message sets in xml file)
|
|
}
|
|
|
|
// Draw a representation of current autopilot target
|
|
void LLAgent::renderAutoPilotTarget()
|
|
{
|
|
if (mAutoPilot)
|
|
{
|
|
F32 height_meters;
|
|
LLVector3d target_global;
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
gGL.pushMatrix();
|
|
|
|
// not textured
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
|
|
// lovely green
|
|
glColor4f(0.f, 1.f, 1.f, 1.f);
|
|
|
|
target_global = mAutoPilotTargetGlobal;
|
|
|
|
gGL.translatef((F32)(target_global.mdV[VX]), (F32)(target_global.mdV[VY]), (F32)(target_global.mdV[VZ]));
|
|
|
|
height_meters = 1.f;
|
|
|
|
glScalef(height_meters, height_meters, height_meters);
|
|
|
|
gSphere.render(1500.f);
|
|
|
|
gGL.popMatrix();
|
|
}
|
|
}
|
|
|
|
// Combines userRemoveAllAttachments() and userAttachMultipleAttachments() logic to
|
|
// get attachments into desired state with minimal number of adds/removes.
|
|
//void LLAgentWearables::userUpdateAttachments(LLInventoryModel::item_array_t& obj_item_array)
|
|
// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-09-22 (Catznip-2.2.0a) | Added: Catznip-2.2.0a
|
|
void LLAgent::userUpdateAttachments(LLInventoryModel::item_array_t& obj_item_array, bool fAttachOnly)
|
|
// [/SL:KB]
|
|
{
|
|
// Possible cases:
|
|
// already wearing but not in request set -> take off.
|
|
// already wearing and in request set -> leave alone.
|
|
// not wearing and in request set -> put on.
|
|
|
|
LLVOAvatar* pAvatar = gAgent.getAvatarObject();
|
|
if (!pAvatar) return;
|
|
|
|
std::set<LLUUID> requested_item_ids;
|
|
std::set<LLUUID> current_item_ids;
|
|
for (S32 i=0; i<obj_item_array.count(); i++)
|
|
requested_item_ids.insert(obj_item_array[i].get()->getLinkedUUID());
|
|
|
|
// Build up list of objects to be removed and items currently attached.
|
|
llvo_vec_t objects_to_remove;
|
|
for (LLVOAvatar::attachment_map_t::iterator iter = pAvatar->mAttachmentPoints.begin();
|
|
iter != pAvatar->mAttachmentPoints.end();)
|
|
{
|
|
LLVOAvatar::attachment_map_t::iterator curiter = iter++;
|
|
LLViewerJointAttachment* attachment = curiter->second;
|
|
for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
|
|
attachment_iter != attachment->mAttachedObjects.end();
|
|
++attachment_iter)
|
|
{
|
|
LLViewerObject *objectp = (*attachment_iter);
|
|
if (objectp)
|
|
{
|
|
LLUUID object_item_id = objectp->getAttachmentItemID();
|
|
if (requested_item_ids.find(object_item_id) != requested_item_ids.end())
|
|
{
|
|
// Object currently worn, was requested.
|
|
// Flag as currently worn so we won't have to add it again.
|
|
current_item_ids.insert(object_item_id);
|
|
}
|
|
else
|
|
{
|
|
// object currently worn, not requested.
|
|
objects_to_remove.push_back(objectp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LLInventoryModel::item_array_t items_to_add;
|
|
for (LLInventoryModel::item_array_t::iterator it = obj_item_array.begin();
|
|
it != obj_item_array.end();
|
|
++it)
|
|
{
|
|
LLUUID linked_id = (*it).get()->getLinkedUUID();
|
|
if (current_item_ids.find(linked_id) != current_item_ids.end())
|
|
{
|
|
// Requested attachment is already worn.
|
|
}
|
|
else
|
|
{
|
|
// Requested attachment is not worn yet.
|
|
items_to_add.push_back(*it);
|
|
}
|
|
}
|
|
// S32 remove_count = objects_to_remove.size();
|
|
// S32 add_count = items_to_add.size();
|
|
// llinfos << "remove " << remove_count << " add " << add_count << llendl;
|
|
|
|
// Remove everything in objects_to_remove
|
|
// userRemoveMultipleAttachments(objects_to_remove);
|
|
// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-09-22 (Catznip-2.2.0a) | Added: Catznip-2.2.0a
|
|
if (!fAttachOnly)
|
|
{
|
|
userRemoveMultipleAttachments(objects_to_remove);
|
|
}
|
|
// [/SL:KB]
|
|
|
|
// Add everything in items_to_add
|
|
userAttachMultipleAttachments(items_to_add);
|
|
}
|
|
|
|
void LLAgent::userRemoveMultipleAttachments(llvo_vec_t& objects_to_remove)
|
|
{
|
|
if (!gAgent.getAvatarObject()) return;
|
|
|
|
// [RLVa:KB] - Checked: 2010-03-04 (RLVa-1.1.3b) | Modified: RLVa-1.2.0a
|
|
// RELEASE-RLVa: [SL-2.0.0] Check our callers and verify that erasing elements from the passed vector won't break random things
|
|
if ( (rlv_handler_t::isEnabled()) && (gRlvAttachmentLocks.hasLockedAttachmentPoint(RLV_LOCK_REMOVE)) )
|
|
{
|
|
llvo_vec_t::iterator itObj = objects_to_remove.begin();
|
|
while (itObj != objects_to_remove.end())
|
|
{
|
|
const LLViewerObject* pAttachObj = *itObj;
|
|
if (gRlvAttachmentLocks.isLockedAttachment(pAttachObj))
|
|
{
|
|
itObj = objects_to_remove.erase(itObj);
|
|
|
|
// Fall-back code: re-add the attachment if it got removed from COF somehow (compensates for possible bugs elsewhere)
|
|
LLInventoryModel::cat_array_t folders; LLInventoryModel::item_array_t items;
|
|
LLLinkedItemIDMatches f(pAttachObj->getAttachmentItemID());
|
|
gInventory.collectDescendentsIf(LLCOFMgr::instance().getCOF(), folders, items, LLInventoryModel::EXCLUDE_TRASH, f);
|
|
RLV_ASSERT( 0 != items.count() );
|
|
if (0 == items.count())
|
|
LLCOFMgr::instance().addAttachment(pAttachObj->getAttachmentItemID());
|
|
}
|
|
else
|
|
{
|
|
++itObj;
|
|
}
|
|
}
|
|
}
|
|
// [/RLVa:KB]
|
|
|
|
if (objects_to_remove.empty())
|
|
return;
|
|
|
|
gMessageSystem->newMessage("ObjectDetach");
|
|
gMessageSystem->nextBlockFast(_PREHASH_AgentData);
|
|
gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
|
|
gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
|
|
for (llvo_vec_t::iterator it = objects_to_remove.begin();
|
|
it != objects_to_remove.end();
|
|
++it)
|
|
{
|
|
LLViewerObject *objectp = *it;
|
|
gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
|
|
gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, objectp->getLocalID());
|
|
}
|
|
gMessageSystem->sendReliable(gAgent.getRegionHost());
|
|
}
|
|
|
|
void LLAgent::userAttachMultipleAttachments(LLInventoryModel::item_array_t& obj_item_array)
|
|
{
|
|
// [RLVa:KB] - Checked: 2010-03-04 (RLVa-1.2.0a) | Modified: RLVa-1.2.0a
|
|
// RELEASE-RLVa: [SL-2.0.0] Check our callers and verify that erasing elements from the passed vector won't break random things
|
|
if ( (rlv_handler_t::isEnabled()) && (gRlvAttachmentLocks.hasLockedAttachmentPoint(RLV_LOCK_ANY)) )
|
|
{
|
|
// Fall-back code: everything should really already have been pruned before we get this far
|
|
for (S32 idxItem = obj_item_array.count() - 1; idxItem >= 0; idxItem--)
|
|
{
|
|
const LLInventoryItem* pItem = obj_item_array.get(idxItem).get();
|
|
if (!gRlvAttachmentLocks.canAttach(pItem))
|
|
{
|
|
obj_item_array.remove(idxItem);
|
|
RLV_ASSERT(false);
|
|
}
|
|
}
|
|
}
|
|
// [/RLVa:KB]
|
|
|
|
// Build a compound message to send all the objects that need to be rezzed.
|
|
S32 obj_count = obj_item_array.count();
|
|
|
|
// Limit number of packets to send
|
|
const S32 MAX_PACKETS_TO_SEND = 10;
|
|
const S32 OBJECTS_PER_PACKET = 4;
|
|
const S32 MAX_OBJECTS_TO_SEND = MAX_PACKETS_TO_SEND * OBJECTS_PER_PACKET;
|
|
if( obj_count > MAX_OBJECTS_TO_SEND )
|
|
{
|
|
obj_count = MAX_OBJECTS_TO_SEND;
|
|
}
|
|
|
|
// Create an id to keep the parts of the compound message together
|
|
LLUUID compound_msg_id;
|
|
compound_msg_id.generate();
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
|
|
for(S32 i = 0; i < obj_count; ++i)
|
|
{
|
|
if( 0 == (i % OBJECTS_PER_PACKET) )
|
|
{
|
|
// Start a new message chunk
|
|
msg->newMessageFast(_PREHASH_RezMultipleAttachmentsFromInv);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
msg->nextBlockFast(_PREHASH_HeaderData);
|
|
msg->addUUIDFast(_PREHASH_CompoundMsgID, compound_msg_id );
|
|
msg->addU8Fast(_PREHASH_TotalObjects, obj_count );
|
|
msg->addBOOLFast(_PREHASH_FirstDetachAll, false );
|
|
}
|
|
|
|
const LLInventoryItem* item = obj_item_array.get(i).get();
|
|
bool replace = !gHippoGridManager->getConnectedGrid()->supportsInvLinks();
|
|
msg->nextBlockFast(_PREHASH_ObjectData );
|
|
msg->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID());
|
|
msg->addUUIDFast(_PREHASH_OwnerID, item->getPermissions().getOwner());
|
|
msg->addU8Fast(_PREHASH_AttachmentPt, replace? 0 : ATTACHMENT_ADD); // Wear at the previous or default attachment point
|
|
pack_permissions_slam(msg, item->getFlags(), item->getPermissions());
|
|
msg->addStringFast(_PREHASH_Name, item->getName());
|
|
msg->addStringFast(_PREHASH_Description, item->getDescription());
|
|
|
|
if( (i+1 == obj_count) || ((OBJECTS_PER_PACKET-1) == (i % OBJECTS_PER_PACKET)) )
|
|
{
|
|
// End of message chunk
|
|
msg->sendReliable( gAgent.getRegion()->getHost() );
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLAgent::showLureDestination(const std::string fromname, const int global_x, const int global_y, const int x, const int y, const int z, const std::string maturity)
|
|
{
|
|
const LLVector3d posglobal = LLVector3d(F64(global_x), F64(global_y), F64(0));
|
|
LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromPosGlobal(posglobal);
|
|
|
|
if(mPendingLure)
|
|
delete mPendingLure;
|
|
mPendingLure = new SHLureRequest(fromname,posglobal,x,y,z);
|
|
|
|
if(siminfo) //We already have an entry? Go right on to displaying it.
|
|
{
|
|
onFoundLureDestination(siminfo);
|
|
}
|
|
else
|
|
{
|
|
U16 grid_x = (U16)(global_x / REGION_WIDTH_UNITS);
|
|
U16 grid_y = (U16)(global_y / REGION_WIDTH_UNITS);
|
|
LLWorldMap::getInstance()->sendMapBlockRequest(grid_x, grid_y, grid_x, grid_y, true); //Will call onFoundLureDestination on response
|
|
}
|
|
}
|
|
|
|
void LLAgent::onFoundLureDestination(LLSimInfo *siminfo)
|
|
{
|
|
if(!mPendingLure)
|
|
return;
|
|
|
|
if(!siminfo)
|
|
siminfo = LLWorldMap::getInstance()->simInfoFromPosGlobal(mPendingLure->mPosGlobal);
|
|
if(siminfo)
|
|
{
|
|
const std::string sim_name = siminfo->mName;
|
|
const std::string maturity = LLViewerRegion::accessToString(siminfo->mAccess);
|
|
|
|
llinfos << mPendingLure->mAvatarName << "'s teleport lure is to " << sim_name << " (" << maturity << ")" << llendl;
|
|
LLStringUtil::format_map_t args;
|
|
args["[NAME]"] = mPendingLure->mAvatarName;
|
|
args["[DESTINATION]"] = LLURLDispatcher::buildSLURL(sim_name, (S32)mPendingLure->mPosLocal[0], (S32)mPendingLure->mPosLocal[1], (S32)mPendingLure->mPosLocal[2] );
|
|
std::string msg = LLTrans::getString("TeleportOfferMaturity", args);
|
|
if (!maturity.empty())
|
|
{
|
|
msg.append(llformat(" (%s)", maturity.c_str()));
|
|
}
|
|
LLChat chat(msg);
|
|
LLFloaterChat::addChat(chat);
|
|
}
|
|
else
|
|
llwarns << "Grand scheme failed" << llendl;
|
|
delete mPendingLure;
|
|
mPendingLure = NULL;
|
|
}
|
|
|
|
/********************************************************************************/
|
|
|
|
LLAgentQueryManager gAgentQueryManager;
|
|
|
|
LLAgentQueryManager::LLAgentQueryManager() :
|
|
mWearablesCacheQueryID(0),
|
|
mNumPendingQueries(0),
|
|
mUpdateSerialNum(0)
|
|
{
|
|
for (U32 i = 0; i < BAKED_NUM_INDICES; i++)
|
|
{
|
|
mActiveCacheQueries[i] = 0;
|
|
}
|
|
}
|
|
|
|
LLAgentQueryManager::~LLAgentQueryManager()
|
|
{
|
|
}
|
|
|
|
// EOF
|
|
|