/** 
 * @file llagent.cpp
 * @brief LLAgent class implementation
 *
 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */


#include "llviewerprecompiledheaders.h"

#include "llagent.h" 

#include "pipeline.h"

#include "llagentaccess.h"
#include "llagentbenefits.h"
#include "llagentcamera.h"
#include "llagentlistener.h"
#include "llagentwearables.h"
#include "llagentui.h"
#include "llappearancemgr.h"
#include "llanimationstates.h"
#include "llavatarappearancedefines.h"
#include "llcallingcard.h"
#include "llchannelmanager.h"
#include "llchicletbar.h"
#include "llconsole.h"
#include "lldonotdisturbnotificationstorage.h"
#include "llfirstuse.h"
#include "llfloatercamera.h"
#include "llfloaterimcontainer.h"
#include "llfloaterperms.h"
#include "llfloaterpreference.h"
#include "llfloaterreg.h"
#include "llfloatersnapshot.h"
#include "llfloatertools.h"
#include "llgroupactions.h"
#include "llgroupmgr.h"
#include "llhudmanager.h"
#include "lljoystickbutton.h"
#include "llmorphview.h"
#include "llmoveview.h"
#include "llnavigationbar.h" // to show/hide navigation bar when changing mouse look state
// <FS:Ansariel> [FS Communication UI]
//#include "llfloaterimnearbychat.h"
#include "fsnearbychathub.h"
// </FS:Ansariel> [FS Communication UI]
#include "llspeakers.h"
#include "llnotificationsutil.h"
// <FS:Zi> We don't use the mini location panel in Firestorm
// #include "llpaneltopinfobar.h"
#include "llparcel.h"
#include "llrendersphere.h"
#include "llscriptruntimeperms.h"
#include "llsdutil.h"
#include "llsky.h"
#include "llslurl.h"
#include "llsmoothstep.h"
#include "llstartup.h"
#include "llstatusbar.h"
#include "llteleportflags.h"
#include "lltool.h"
#include "lltoolbarview.h"
#include "lltoolpie.h"
#include "lltoolmgr.h"
#include "lltrans.h"
#include "lluictrl.h"
#include "llurlentry.h"
#include "llviewercontrol.h"
#include "llviewerdisplay.h"
#include "llviewerjoystick.h"
#include "llviewermediafocus.h"
#include "llviewermenu.h"
#include "llviewernetwork.h" // <FS:PP> For FIRE-10718: llGetSimulatorHostname() is causing LSL Bridge to sleep for 10 seconds
#include "llviewerobjectlist.h"
#include "llviewerparcelmgr.h"
#include "llviewerregion.h"
#include "llviewerstats.h"
#include "llviewerwindow.h"
#include "llvoavatarself.h"
#include "llwindow.h"
#include "llworld.h"
#include "llworldmap.h"
#include "stringize.h"
#include "boost/foreach.hpp"
#include "llcorehttputil.h"
// [RLVa:KB] - Checked: 2011-11-04 (RLVa-1.4.4a)
#include "rlvactions.h"
#include "rlvhandler.h"
#include "rlvhelper.h"
#include "rlvui.h"
// [/RLVa:KB]

// Firestorm includes
#include "fsfloaternearbychat.h"
#include "fslslbridge.h"
#include "llpresetsmanager.h"
#include "NACLantispam.h"

using namespace LLAvatarAppearanceDefines;

extern LLMenuBarGL* gMenuBarView;

const BOOL ANIMATE = TRUE;
const U8 AGENT_STATE_TYPING =	0x04;
const U8 AGENT_STATE_EDITING =  0x10;

// Autopilot constants
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_WALK = 1.5f;		// seconds
const F32 AUTOPILOT_MAX_TIME_NO_PROGRESS_FLY = 2.5f;		// seconds. Flying is less presize, needs a bit more time

const F32 MAX_VELOCITY_AUTO_LAND_SQUARED = 4.f * 4.f;
const F64 CHAT_AGE_FAST_RATE = 3.0;

// fidget constants
const F32 MIN_FIDGET_TIME = 8.f; // seconds
const F32 MAX_FIDGET_TIME = 20.f; // seconds

// The agent instance.
LLAgent gAgent;

class LLTeleportRequest
{
public:
	enum EStatus
	{
		kPending,
		kStarted,
		kFailed,
		kRestartPending
	};

	LLTeleportRequest();
	virtual ~LLTeleportRequest();

	EStatus getStatus() const          {return mStatus;};
	void    setStatus(EStatus pStatus) {mStatus = pStatus;};

	static std::map<S32, std::string> sTeleportStatusName;
	static const std::string& statusName(EStatus status);
	virtual void toOstream(std::ostream& os) const;

	virtual bool canRestartTeleport();

	virtual void startTeleport() = 0;
	virtual void restartTeleport();

protected:

private:
	EStatus mStatus;
};

std::map<S32, std::string> LLTeleportRequest::sTeleportStatusName = { { kPending, "kPending" },
																	  { kStarted, "kStarted" },
																	  { kFailed, "kFailed" },
																	  { kRestartPending, "kRestartPending"} };

class LLTeleportRequestViaLandmark : public LLTeleportRequest
{
public:
	LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId);
	virtual ~LLTeleportRequestViaLandmark();

	virtual void toOstream(std::ostream& os) const;

	virtual bool canRestartTeleport();

	virtual void startTeleport();
	virtual void restartTeleport();

protected:
	inline const LLUUID &getLandmarkId() const {return mLandmarkId;};

private:
	LLUUID mLandmarkId;
};

class LLTeleportRequestViaLure : public LLTeleportRequestViaLandmark
{
public:
	LLTeleportRequestViaLure(const LLUUID &pLureId, BOOL pIsLureGodLike);
	virtual ~LLTeleportRequestViaLure();

	virtual void toOstream(std::ostream& os) const;

	virtual bool canRestartTeleport();

	virtual void startTeleport();

protected:
	inline BOOL isLureGodLike() const {return mIsLureGodLike;};

private:
	BOOL mIsLureGodLike;
};

class LLTeleportRequestViaLocation : public LLTeleportRequest
{
public:
	LLTeleportRequestViaLocation(const LLVector3d &pPosGlobal);
	virtual ~LLTeleportRequestViaLocation();

	virtual void toOstream(std::ostream& os) const;

	virtual bool canRestartTeleport();

	virtual void startTeleport();
	virtual void restartTeleport();

protected:
	inline const LLVector3d &getPosGlobal() const {return mPosGlobal;};

private:
	LLVector3d mPosGlobal;
};


class LLTeleportRequestViaLocationLookAt : public LLTeleportRequestViaLocation
{
public:
// [RLVa:KB] - Checked: RLVa-2.0.0
	LLTeleportRequestViaLocationLookAt(const LLVector3d &pPosGlobal, const LLVector3& look_at);
// [/RLVa:KB]
//	LLTeleportRequestViaLocationLookAt(const LLVector3d &pPosGlobal);
	virtual ~LLTeleportRequestViaLocationLookAt();

	virtual void toOstream(std::ostream& os) const;

	virtual bool canRestartTeleport();

	virtual void startTeleport();
	virtual void restartTeleport();

protected:
// [RLVa:KB] - Checked: RLVa-2.0.0
	const LLVector3& getLookAt() const { return mLookAt; }
// [/RLVa:KB]

private:
// [RLVa:KB] - Checked: RLVa-2.0.0
	LLVector3 mLookAt;
// [/RLVa:KB]

};

//--------------------------------------------------------------------
// Statics
//

/// minimum time after setting away state before coming back based on movement
const F32 LLAgent::MIN_AFK_TIME = 10.0f;

const F32 LLAgent::TYPING_TIMEOUT_SECS = 5.f;

std::map<std::string, std::string> LLAgent::sTeleportErrorMessages;
std::map<std::string, std::string> LLAgent::sTeleportProgressMessages;

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();
	}
}

bool handleSlowMotionAnimation(const LLSD& newvalue)
{
	if (newvalue.asBoolean())
	{
		gAgentAvatarp->setAnimTimeFactor(0.2f);
	}
	else
	{
		gAgentAvatarp->setAnimTimeFactor(1.0f);
	}
	return true;
}

void LLAgent::setCanEditParcel() // called via mParcelChangedSignal
{
	bool can_edit = LLToolMgr::getInstance()->canEdit();
	gAgent.mCanEditParcel = can_edit;
}

// static
bool LLAgent::isActionAllowed(const LLSD& sdname)
{
	bool retval = false;

	const std::string& param = sdname.asString();

	if (param == "speak")
	{
        bool allow_agent_voice = false;
        LLVoiceChannel* channel = LLVoiceChannel::getCurrentVoiceChannel();
        if (channel != NULL)
        {
            if (channel->getSessionName().empty() && channel->getSessionID().isNull())
            {
                // default channel
                allow_agent_voice = LLViewerParcelMgr::getInstance()->allowAgentVoice();
            }
            else
            {
                allow_agent_voice = channel->isActive() && channel->callStarted();
            }
        }

        if (gAgent.isVoiceConnected() &&
            allow_agent_voice &&
            !LLVoiceClient::getInstance()->inTuningMode())
		{
			retval = true;
		}
		else
		{
			retval = false;
		}
	}
	else if (param == "fs_when_not_sitting")
	{
		if (!gAgentAvatarp)
		{
			return false;
		}
		return !gAgentAvatarp->isSitting();
	}

	return retval;
}

// static 
void LLAgent::pressMicrophone(const LLSD& name)
{
	LLFirstUse::speak(false);

	 LLVoiceClient::getInstance()->inputUserControlState(true);
}

// static 
void LLAgent::releaseMicrophone(const LLSD& name)
{
	LLVoiceClient::getInstance()->inputUserControlState(false);
}

// static
void LLAgent::toggleMicrophone(const LLSD& name)
{
	LLVoiceClient::getInstance()->toggleUserPTTState();
}

// static
bool LLAgent::isMicrophoneOn(const LLSD& sdname)
{
	return LLVoiceClient::getInstance()->getUserPTTState();
}

// ************************************************************
// 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),
	mListener(),

	mDoubleTapRunTimer(),
	mDoubleTapRunMode(DOUBLETAP_NONE),

	mbAlwaysRun(false),
//	mbRunning(false),
// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i
	mbTempRun(false),
// [/RLVa:KB]
	mbTeleportKeepsLookAt(false),

	mAgentAccess(new LLAgentAccess(gSavedSettings)),
	mGodLevelChangeSignal(),
	mCanEditParcel(false),
	mTeleportSourceSLURL(new LLSLURL),
	mTeleportRequest(),
	mTeleportFinishedSlot(),
	mTeleportFailedSlot(),
	mIsMaturityRatingChangingDuringTeleport(false),
	mTPNeedsNeabyChatSeparator(false),
	mMaturityRatingChange(0U),
	mIsDoSendMaturityPreferenceToServer(false),
	mMaturityPreferenceRequestId(0U),
	mMaturityPreferenceResponseId(0U),
	mMaturityPreferenceNumRetries(0U),
	mLastKnownRequestMaturity(SIM_ACCESS_MIN),
	mLastKnownResponseMaturity(SIM_ACCESS_MIN),
	mHttpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID),
	mTeleportState(TELEPORT_NONE),
	mRegionp(NULL),

	mAgentOriginGlobal(),
	mPositionGlobal(),
    mLastTestGlobal(),

	mDistanceTraveled(0.F),
	mLastPositionGlobal(LLVector3d::zero),

	mRenderState(0),
	mTypingTimer(),

	mViewsPushed(FALSE),

	mCustomAnim(FALSE),
	mShowAvatar(TRUE),
	mFrameAgent(),

	mIsDoNotDisturb(false),
	mIsAutorespond(FALSE),
	mIsAutorespondNonFriends(FALSE),
	mIsRejectTeleportOffers(FALSE), // <FS:PP> FIRE-1245: Option to block/reject teleport offers
	mIsRejectFriendshipRequests(FALSE), // <FS:PP> FIRE-15233: Automatic friendship request refusal
	mIsRejectAllGroupInvites(FALSE), // <FS:PP> Option to block/reject all group invites
	mAfkSitting(false), // <FS:Ansariel> FIRE-1568: Fix sit on AFK issues (standing up when sitting before)

	// <FS> Ignore prejump and always fly
	mIgnorePrejump(FALSE),
	mAlwaysFly(FALSE),
	// </FS>

	mControlFlags(0x00000000),
	mbFlagsDirty(FALSE),
	mbFlagsNeedReset(FALSE),

	mAutoPilot(FALSE),
	mAutoPilotFlyOnStop(FALSE),
	mAutoPilotAllowFlying(TRUE),
	mAutoPilotTargetGlobal(),
	mAutoPilotStopDistance(1.f),
	mAutoPilotUseRotation(FALSE),
	mAutoPilotTargetFacing(LLVector3::zero),
	mAutoPilotTargetDist(0.f),
	mAutoPilotNoProgressFrameCount(0),
	mAutoPilotRotationThreshold(0.f),
	mAutoPilotFinishedCallback(NULL),
	mAutoPilotCallbackData(NULL),
	
	mMovementKeysLocked(FALSE),

	mEffectColor(new LLUIColor(LLColor4(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),
	mOutfitChosen(FALSE),

	mVoiceConnected(false),

	// <FS:Ansariel> [Legacy Bake]
	mAppearanceSerialNum(0),

	mMouselookModeInSignal(NULL),
	mMouselookModeOutSignal(NULL),
	
	mPhantom(FALSE),
	restoreToWorld(false)
{
	for (U32 i = 0; i < TOTAL_CONTROLS; i++)
	{
		mControlsTakenCount[i] = 0;
		mControlsTakenPassedOnCount[i] = 0;
	}

	mListener.reset(new LLAgentListener(*this));

	addParcelChangedCallback(&setCanEditParcel);

	mMoveTimer.stop();
}

// <FS> Ignore prejump and always fly
void LLAgent::updateIgnorePrejump(const LLSD &data)
{
	mIgnorePrejump = data.asBoolean();
}

void LLAgent::updateFSAlwaysFly(const LLSD &data)
{
	mAlwaysFly = data.asBoolean();
	if (mAlwaysFly) 
	{
		LL_INFOS() << "Enabling Fly Override" << LL_ENDL;
		if (gSavedSettings.getBOOL("FirstUseFlyOverride"))
		{
			LLNotificationsUtil::add("FirstUseFlyOverride");
			gSavedSettings.setBOOL("FirstUseFlyOverride", FALSE);
		}
	}
}
// </FS> Ignore prejump and always fly

// Requires gSavedSettings to be initialized.
//-----------------------------------------------------------------------------
// init()
//-----------------------------------------------------------------------------
void LLAgent::init()
{
	mMoveTimer.start();

	gSavedSettings.declareBOOL("SlowMotionAnimation", FALSE, "Declared in code", LLControlVariable::PERSIST_NO);
	gSavedSettings.getControl("SlowMotionAnimation")->getSignal()->connect(boost::bind(&handleSlowMotionAnimation, _2));
	
	// *Note: this is where LLViewerCamera::getInstance() used to be constructed.

	setFlying( gSavedSettings.getBOOL("FlyingAtExit") );

	*mEffectColor = LLUIColorTable::instance().getColor("EffectColor");

	gSavedSettings.getControl("PreferredMaturity")->getValidateSignal()->connect(boost::bind(&LLAgent::validateMaturity, this, _2));
	gSavedSettings.getControl("PreferredMaturity")->getSignal()->connect(boost::bind(&LLAgent::handleMaturity, this, _2));
	mLastKnownResponseMaturity = static_cast<U8>(gSavedSettings.getU32("PreferredMaturity"));
	mLastKnownRequestMaturity = mLastKnownResponseMaturity;
	mIsDoSendMaturityPreferenceToServer = true;
	mIgnorePrejump = gSavedSettings.getBOOL("FSIgnoreFinishAnimation");
	gSavedSettings.getControl("FSIgnoreFinishAnimation")->getSignal()->connect(boost::bind(&LLAgent::updateIgnorePrejump, this, _2));
	mAlwaysFly = gSavedSettings.getBOOL("FSAlwaysFly");
	gSavedSettings.getControl("FSAlwaysFly")->getSignal()->connect(boost::bind(&LLAgent::updateFSAlwaysFly, this, _2));
	selectAutorespond(gSavedPerAccountSettings.getBOOL("FSAutorespondMode"));
	selectAutorespondNonFriends(gSavedPerAccountSettings.getBOOL("FSAutorespondNonFriendsMode"));
	selectRejectTeleportOffers(gSavedPerAccountSettings.getBOOL("FSRejectTeleportOffersMode")); // <FS:PP> FIRE-1245: Option to block/reject teleport offers
	selectRejectFriendshipRequests(gSavedPerAccountSettings.getBOOL("FSRejectFriendshipRequestsMode")); // <FS:PP> FIRE-15233: Automatic friendship request refusal
	selectRejectAllGroupInvites(gSavedPerAccountSettings.getBOOL("FSRejectAllGroupInvitesMode")); // <FS:PP> Option to block/reject all group invites

	if (!mTeleportFinishedSlot.connected())
	{
		mTeleportFinishedSlot = LLViewerParcelMgr::getInstance()->setTeleportFinishedCallback(boost::bind(&LLAgent::handleTeleportFinished, this));
	}
	if (!mTeleportFailedSlot.connected())
	{
		mTeleportFailedSlot = LLViewerParcelMgr::getInstance()->setTeleportFailedCallback(boost::bind(&LLAgent::handleTeleportFailed, this));
	}

	LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp());

	mHttpPolicy = app_core_http.getPolicy(LLAppCoreHttp::AP_AGENT);

	mInitialized = TRUE;
}

//-----------------------------------------------------------------------------
// cleanup()
//-----------------------------------------------------------------------------
void LLAgent::cleanup()
{
	mRegionp = NULL;
    mTeleportRequest = NULL;
    mTeleportCanceled = NULL;
	if (mTeleportFinishedSlot.connected())
	{
		mTeleportFinishedSlot.disconnect();
	}
	if (mTeleportFailedSlot.connected())
	{
		mTeleportFailedSlot.disconnect();
	}
}

//-----------------------------------------------------------------------------
// LLAgent()
//-----------------------------------------------------------------------------
LLAgent::~LLAgent()
{
	cleanup();

	delete mMouselookModeInSignal;
	mMouselookModeInSignal = NULL;
	delete mMouselookModeOutSignal;
	mMouselookModeOutSignal = NULL;

	delete mAgentAccess;
	mAgentAccess = NULL;
	delete mEffectColor;
	mEffectColor = NULL;
	delete mTeleportSourceSLURL;
	mTeleportSourceSLURL = 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)gAgentAvatarp->mChatTimer.getElapsedTimeF32();
		// add in frame time * 3 (so it ages 4x)
		gAgentAvatarp->mChatTimer.setAge(elapsed_time + (F64)gFrameDTClamped * (CHAT_AGE_FAST_RATE - 1.0));
	}
}

//-----------------------------------------------------------------------------
// moveAt()
//-----------------------------------------------------------------------------
void LLAgent::moveAt(S32 direction, bool reset)
{
	mMoveTimer.reset();
	LLFirstUse::notMoving(false);

	// 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)
	{
// <FS:CR> FIRE-8798: Option to prevent camera reset on movement
		//gAgentCamera.resetView();
		gAgentCamera.resetView(TRUE, FALSE, TRUE);
// </FS:CR>
	}
}

//-----------------------------------------------------------------------------
// moveAtNudge()
//-----------------------------------------------------------------------------
void LLAgent::moveAtNudge(S32 direction)
{
	mMoveTimer.reset();
	LLFirstUse::notMoving(false);

	// 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);
	}

// <FS:CR> FIRE-8798: Option to prevent camera reset on movement
	//gAgentCamera.resetView();
	gAgentCamera.resetView(TRUE, FALSE, TRUE);
// </FS:CR>
}

//-----------------------------------------------------------------------------
// moveLeft()
//-----------------------------------------------------------------------------
void LLAgent::moveLeft(S32 direction)
{
	mMoveTimer.reset();
	LLFirstUse::notMoving(false);

	// 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);
	}

// <FS:CR> FIRE-8798: Option to prevent camera reset on movement
	//gAgentCamera.resetView();
	gAgentCamera.resetView(TRUE, FALSE, TRUE);
// </FS:CR>
}

//-----------------------------------------------------------------------------
// moveLeftNudge()
//-----------------------------------------------------------------------------
void LLAgent::moveLeftNudge(S32 direction)
{
	mMoveTimer.reset();
	LLFirstUse::notMoving(false);

	// 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);
	}

// <FS:CR> FIRE-8798: Option to prevent camera reset on movement
	//gAgentCamera.resetView();
	gAgentCamera.resetView(TRUE, FALSE, TRUE);
// </FS:CR>
}

//-----------------------------------------------------------------------------
// moveUp()
//-----------------------------------------------------------------------------
void LLAgent::moveUp(S32 direction)
{
// [RLVa:KB] - Checked: RLVa-2.2 (@jump)
	if ( (!RlvActions::canJump()) && (direction > 0) && (!getFlying()) )
	{
		return;
	}
// [/Sl:KB]

	mMoveTimer.reset();
	LLFirstUse::notMoving(false);

	// 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);
		// <FS:Ansariel> Chalice Yao's crouch toggle
		gAgentCamera.resetView(TRUE, FALSE, TRUE);
		// </FS:Ansariel>
	}
	else if (direction < 0)
	{
		setControlFlags(AGENT_CONTROL_UP_NEG | AGENT_CONTROL_FAST_UP);
		// <FS:Ansariel> Chalice Yao's crouch toggle
		if (!gSavedPerAccountSettings.getBOOL("FSCrouchToggleStatus") || !gSavedPerAccountSettings.getBOOL("FSCrouchToggle"))
		{
			gAgentCamera.resetView(TRUE, FALSE, TRUE);
		}
		// </FS:Ansariel>
	}

	// <FS:Ansariel> Chalice Yao's crouch toggle
	//gAgentCamera.resetView();
	// </FS:Ansariel>
}

//-----------------------------------------------------------------------------
// 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)
	{
// <FS:CR> FIRE-8798: Option to prevent camera reset on movement
		//gAgentCamera.resetView();
        gAgentCamera.resetView(TRUE, FALSE, TRUE);
// </FS:CR>
	}
}

//-----------------------------------------------------------------------------
// 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: RLVa-1.0
	if (!RlvActions::canFly())
	{
		return FALSE;
	}
// [/RLVa:KB]
	if (isGodlike()) return TRUE;
	// <FS> Always fly
	if (mAlwaysFly)
	{
		return TRUE;
	}
	// </FS>

	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; 
}

//-----------------------------------------------------------------------------
// setFlying()
//-----------------------------------------------------------------------------
void LLAgent::setFlying(BOOL fly, BOOL fail_sound)
{
	if (isAgentAvatarValid())
	{
		// *HACK: Don't allow to start the flying mode if we got ANIM_AGENT_STANDUP signal
		// because in this case we won't get a signal to start avatar flying animation and
		// it will be walking with flying mode "ON" indication. However we allow to switch
		// the flying mode off if we get ANIM_AGENT_STANDUP signal. See process_avatar_animation().
		// See EXT-2781.
		if(fly && gAgentAvatarp->mSignaledAnimations.find(ANIM_AGENT_STANDUP) != gAgentAvatarp->mSignaledAnimations.end())
		{
			return;
		}

		// don't allow taking off while sitting
		if (fly && gAgentAvatarp->isSitting())
		{
			return;
		}
	}

	if (fly)
	{
// [RLVa:KB] - Checked: RLVa-1.0
		if (!RlvActions::canFly())
		{
			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
			if (fail_sound)
			{
				make_ui_sound("UISndBadKeystroke");
			}
			return;
		}
		if( !was_flying )
		{
			add(LLStatViewer::FLY, 1);
		}
		setControlFlags(AGENT_CONTROL_FLY);
	}
	else
	{
		clearControlFlags(AGENT_CONTROL_FLY);
	}


	// Update Movement Controls according to Fly mode
	LLFloaterMove::setFlyingMode(fly);

	mbFlagsDirty = TRUE;
}


// UI based mechanism of setting fly state
//-----------------------------------------------------------------------------
// toggleFlying()
//-----------------------------------------------------------------------------
// static
void LLAgent::toggleFlying()
{
	if ( gAgent.mAutoPilot )
	{
		LLToolPie::instance().stopClickToWalk();
	}

	// <FS:Ansariel> Chalice Yao's crouch toggle
	if (gSavedPerAccountSettings.getBOOL("FSCrouchToggleStatus"))
	{
		gSavedPerAccountSettings.setBOOL("FSCrouchToggleStatus", FALSE);
	}
	// </FS:Ansariel>

	BOOL fly = !gAgent.getFlying();

	gAgent.mMoveTimer.reset();
	LLFirstUse::notMoving(false);

	gAgent.setFlying( fly );
// <FS:CR> FIRE-8798: Option to prevent camera reset on movement
	//gAgentCamera.resetView();
	gAgentCamera.resetView(TRUE, FALSE, TRUE);
// </FS:CR>
}

// static
bool LLAgent::enableFlying()
{
	// <FS:Zi> Fix "Agent.enableFlying" function for menu entry and toolbar button
	// BOOL sitting = FALSE;
	// if (isAgentAvatarValid())
	// {
	// 	sitting = gAgentAvatarp->isSitting();
	// }
	// return !sitting;
	if(isAgentAvatarValid())
	{
		return gAgent.canFly() && (!gAgentAvatarp->isSitting());
	}
	return false;
	// </FS:Zi>
}

// static
bool LLAgent::isSitting()
{
    BOOL sitting = FALSE;
    if (isAgentAvatarValid())
    {
        sitting = gAgentAvatarp->isSitting();
    }
    return sitting;
}

void LLAgent::standUp()
{
//	setControlFlags(AGENT_CONTROL_STAND_UP);
// [RLVa:KB] - Checked: 2010-03-07 (RLVa-1.2.0c) | Added: RLVa-1.2.0a
	// RELEASE-RLVa: [SL-2.0.0] Check this function's callers since usually they require explicit blocking
	if ( (!rlv_handler_t::isEnabled()) || (RlvActions::canStand()) )
	{
		setControlFlags(AGENT_CONTROL_STAND_UP);
	}
// [/RLVa:KB]
}

void LLAgent::changeParcels()
{
	LL_DEBUGS("AgentLocation") << "Calling ParcelChanged callbacks" << LL_ENDL;
	// Notify anything that wants to know about parcel changes
	mParcelChangedSignal();
}

boost::signals2::connection LLAgent::addParcelChangedCallback(parcel_changed_callback_t cb)
{
	return mParcelChangedSignal.connect(cb);
}

// static
void LLAgent::capabilityReceivedCallback(const LLUUID &region_id, LLViewerRegion *regionp)
{
    if (regionp && regionp->getRegionID() == region_id)
    {
        regionp->requestSimulatorFeatures();
        LLAppViewer::instance()->updateNameLookupUrl(regionp);
    }
}

//-----------------------------------------------------------------------------
// setRegion()
//-----------------------------------------------------------------------------
void LLAgent::setRegion(LLViewerRegion *regionp)
{
	llassert(regionp);
	if (mRegionp != regionp)
	{

		LL_INFOS("AgentLocation","Teleport") << "Moving agent into region: handle " << regionp->getHandle() 
											 << " id " << regionp->getRegionID()
											 << " name " << regionp->getName()
											 << " previous region "
											 << (mRegionp ? mRegionp->getRegionID() : LLUUID::null)
											 << LL_ENDL;
		if (mRegionp)
		{
			// NaCl - Antispam Registry clear anti-spam queues when changing regions
			NACLAntiSpamRegistry::instance().purgeAllQueues();
			// NaCl End

			// 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);
			}

            if (regionp->capabilitiesReceived())
            {
                regionp->requestSimulatorFeatures();
                LLAppViewer::instance()->updateNameLookupUrl(regionp);
            }
            else
            {
                regionp->setCapabilitiesReceivedCallback(LLAgent::capabilityReceivedCallback);
            }

		}
		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);

            if (regionp->capabilitiesReceived())
            {
                LLAppViewer::instance()->updateNameLookupUrl(regionp);
            }
            else
            {
                regionp->setCapabilitiesReceivedCallback([](const LLUUID &region_id, LLViewerRegion* regionp) {LLAppViewer::instance()->updateNameLookupUrl(regionp); });
            }
		}

		// Pass new region along to metrics components that care about this level of detail.
		LLAppViewer::metricsUpdateRegion(regionp->getHandle());
	}

	mRegionp = regionp;

	// TODO - most of what follows probably should be moved into callbacks

	// Pass the region host to LLUrlEntryParcel to resolve parcel name
	// with a server request.
	LLUrlEntryParcel::setRegionHost(getRegionHost());

	// 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();

//	LLFloaterMove::sUpdateFlyingStatus();
// [RLVa:KB] - Checked: 2011-05-27 (RLVa-1.4.0a) | Added: RLVa-1.4.0a
	LLFloaterMove::sUpdateMovementStatus();
// [/RLVa:KB]

	// <FS:Ansariel> [Legacy Bake]
	// If the newly entered region is using server bakes, and our
	// current appearance is non-baked, request appearance update from
	// server.
	if (mRegionp->capabilitiesReceived())
	{
		handleServerBakeRegionTransition(mRegionp->getRegionID());
	}
	else
	{
		// Need to handle via callback after caps arrive.
		mRegionp->setCapabilitiesReceivedCallback(boost::bind(&LLAgent::handleServerBakeRegionTransition,this,_1));
	}
	// </FS:Ansariel> [Legacy Bake]

	// <FS:Zi> Run Prio 0 default bento pose in the background to fix splayed hands, open mouths, etc.
	if (gSavedSettings.getBOOL("FSPlayDefaultBentoAnimation"))
	{
		sendAnimationRequest(ANIM_AGENT_BENTO_IDLE, ANIM_REQUEST_START);
	}
	// </FS:Zi>

	LL_DEBUGS("AgentLocation") << "Calling RegionChanged callbacks" << LL_ENDL;
	mRegionChangedSignal();
}


//-----------------------------------------------------------------------------
// getRegion()
//-----------------------------------------------------------------------------
LLViewerRegion *LLAgent::getRegion() const
{
	return mRegionp;
}


LLHost LLAgent::getRegionHost() const
{
	if (mRegionp)
	{
		return mRegionp->getHost();
	}
	else
	{
		return LLHost();
	}
}

boost::signals2::connection LLAgent::addRegionChangedCallback(const region_changed_signal_t::slot_type& cb)
{
	return mRegionChangedSignal.connect(cb);
}

void LLAgent::removeRegionChangedCallback(boost::signals2::connection callback)
{
	mRegionChangedSignal.disconnect(callback);
}

//-----------------------------------------------------------------------------
// inPrelude()
//-----------------------------------------------------------------------------
BOOL LLAgent::inPrelude()
{
	return mRegionp && mRegionp->isPrelude();
}


std::string LLAgent::getRegionCapability(const std::string &name)
{
    if (!mRegionp)
        return std::string();
    
    return mRegionp->getCapability(name);
}


//-----------------------------------------------------------------------------
// canManageEstate()
//-----------------------------------------------------------------------------

BOOL LLAgent::canManageEstate() const
{
	return mRegionp && mRegionp->canManageEstate();
}

//-----------------------------------------------------------------------------
// sendMessage()
//-----------------------------------------------------------------------------
void LLAgent::sendMessage()
{
	if (gDisconnected)
	{
		LL_WARNS() << "Trying to send message when disconnected!" << LL_ENDL;
		return;
	}
	if (!mRegionp)
	{
		LL_ERRS() << "No region for agent yet!" << LL_ENDL;
		return;
	}
	gMessageSystem->sendMessage(mRegionp->getHost());
}


//-----------------------------------------------------------------------------
// sendReliableMessage()
//-----------------------------------------------------------------------------
void LLAgent::sendReliableMessage()
{
	if (gDisconnected)
	{
		LL_DEBUGS() << "Trying to send message when disconnected!" << LL_ENDL;
		return;
	}
	if (!mRegionp)
	{
		LL_DEBUGS() << "LLAgent::sendReliableMessage No region for agent yet, not sending message!" << LL_ENDL;
		return;
	}
	gMessageSystem->sendReliable(mRegionp->getHost());
}

//-----------------------------------------------------------------------------
// getVelocity()
//-----------------------------------------------------------------------------
LLVector3 LLAgent::getVelocity() const
{
	if (isAgentAvatarValid())
	{
		return gAgentAvatarp->getVelocity();
	}
	else
	{
		return LLVector3::zero;
	}
}


//-----------------------------------------------------------------------------
// setPositionAgent()
//-----------------------------------------------------------------------------
void LLAgent::setPositionAgent(const LLVector3 &pos_agent)
{
	if (!pos_agent.isFinite())
	{
		LL_ERRS() << "setPositionAgent is not a number" << LL_ENDL;
	}

	if (isAgentAvatarValid() && gAgentAvatarp->getParent())
	{
		LLVector3 pos_agent_sitting;
		LLVector3d pos_agent_d;
		LLViewerObject *parent = (LLViewerObject*)gAgentAvatarp->getParent();

		pos_agent_sitting = gAgentAvatarp->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;
	}

    if (((mLastTestGlobal - mPositionGlobal).lengthSquared() > 1.0) && !mOnPositionChanged.empty())
    {   // If the position has changed my more than 1 meter since the last time we triggered.
        // filters out some noise. 
        mLastTestGlobal = mPositionGlobal;
        mOnPositionChanged(mFrameAgent.getOrigin(), mPositionGlobal);
    }
}

//-----------------------------------------------------------------------------
// getPositionGlobal()
//-----------------------------------------------------------------------------
const LLVector3d &LLAgent::getPositionGlobal() const
{
	if (isAgentAvatarValid() && !gAgentAvatarp->mDrawable.isNull())
	{
		mPositionGlobal = getPosGlobalFromAgent(gAgentAvatarp->getRenderPosition());
	}
	else
	{
		mPositionGlobal = getPosGlobalFromAgent(mFrameAgent.getOrigin());
	}

	return mPositionGlobal;
}

//-----------------------------------------------------------------------------
// getPositionAgent()
//-----------------------------------------------------------------------------
const LLVector3 &LLAgent::getPositionAgent()
{
	if (isAgentAvatarValid())
	{
		if(gAgentAvatarp->mDrawable.isNull())
		{
			mFrameAgent.setOrigin(gAgentAvatarp->getPositionAgent());
		}
		else
	{
		mFrameAgent.setOrigin(gAgentAvatarp->getRenderPosition());	
	}
	}


	return mFrameAgent.getOrigin();
}

boost::signals2::connection LLAgent::whenPositionChanged(position_signal_t::slot_type fn)
{
    return mOnPositionChanged.connect(fn);
}


//-----------------------------------------------------------------------------
// 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()
{
// [RLVa:KB] - Checked: RLVa-1.2.1
	if (!RlvActions::canGroundSit())
		return;
// [/RLVa:KB]

	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())
	{
		LL_INFOS() << "LLAgent::resetAxes cross-product is zero" << LL_ENDL;
		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() && 
		gAgentAvatarp->getParent() &&
		gAgentAvatarp->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*)gAgentAvatarp->getParent())->getRenderRotation();
		}
		else if (camera_mode == CAMERA_MODE_MOUSELOOK)
		{
			// make the up vector point to the avatar's +z axis
			up_vector = up_vector * gAgentAvatarp->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();

	const F32 look_down_limit = 179.f * DEG_TO_RAD;;
	const F32 look_up_limit   =   1.f * DEG_TO_RAD;

	F32 angle_from_skyward = acos( mFrameAgent.getAtAxis() * skyward );

	// 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()
{
	// <FS> Ignore prejump and always fly
	//return mControlFlags;
	if (LLAgent::mIgnorePrejump)
	{
		return mControlFlags | AGENT_CONTROL_FINISH_ANIM;
	}
	else
	{
		return mControlFlags;
	}
	// </FS>
}

//-----------------------------------------------------------------------------
// setControlFlags()
//-----------------------------------------------------------------------------
void LLAgent::setControlFlags(U32 mask)
{
	mControlFlags |= mask;
	mbFlagsDirty = TRUE;
}


//-----------------------------------------------------------------------------
// 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()
{
	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();

		// <FS:AO> Gsit on away, antigrief protection
		if (gSavedSettings.getBOOL("AvatarSitOnAway"))
		{
			if (isAgentAvatarValid() && !gAgentAvatarp->isSitting() && !gRlvHandler.hasBehaviour(RLV_BHVR_SIT))
			{
				gAgent.sitDown();
				gAgent.setIsAfkSitting(true);
			}
		}
		// </FS:AO>
	}
}

//-----------------------------------------------------------------------------
// clearAFK()
//-----------------------------------------------------------------------------
void LLAgent::clearAFK()
{
	gAwayTriggerTimer.reset();

	// Gods can sometimes get into away state (via gestures)
	// without setting the appropriate control flag. JC
	if (mControlFlags & AGENT_CONTROL_AWAY
		|| (isAgentAvatarValid()
			&& (gAgentAvatarp->mSignaledAnimations.find(ANIM_AGENT_AWAY) != gAgentAvatarp->mSignaledAnimations.end())))
	{
		sendAnimationRequest(ANIM_AGENT_AWAY, ANIM_REQUEST_STOP);
		clearControlFlags(AGENT_CONTROL_AWAY);

		// <FS:AO> if we sat while away, stand back up on clear
		if (gSavedSettings.getBOOL("AvatarSitOnAway"))
		{
			if (gAgent.isAfkSitting() && !gRlvHandler.hasBehaviour(RLV_BHVR_UNSIT))
			{
				gAgent.standUp();
			}
		}
		// </FS:AO>
	}

	// <FS:Ansariel> FIRE-1568: Fix sit on AFK issues (standing up when sitting before)
	gAgent.setIsAfkSitting(false);
}

//-----------------------------------------------------------------------------
// getAFK()
//-----------------------------------------------------------------------------
BOOL LLAgent::getAFK() const
{
	return (mControlFlags & AGENT_CONTROL_AWAY) != 0;
}

//-----------------------------------------------------------------------------
// setDoNotDisturb()
//-----------------------------------------------------------------------------
void LLAgent::setDoNotDisturb(bool pIsDoNotDisturb)
{
	bool isDoNotDisturbSwitchedOff = (mIsDoNotDisturb && !pIsDoNotDisturb);
	mIsDoNotDisturb = pIsDoNotDisturb;
	sendAnimationRequest(ANIM_AGENT_DO_NOT_DISTURB, (pIsDoNotDisturb ? ANIM_REQUEST_START : ANIM_REQUEST_STOP));
	LLNotificationsUI::LLChannelManager::getInstance()->muteAllChannels(pIsDoNotDisturb);
	if (isDoNotDisturbSwitchedOff)
	{
		LLDoNotDisturbNotificationStorage::getInstance()->updateNotifications();
	}
	gIMMgr->updateDNDMessageStatus();
}

//-----------------------------------------------------------------------------
// isDoNotDisturb()
//-----------------------------------------------------------------------------
bool LLAgent::isDoNotDisturb() const
{
	return mIsDoNotDisturb;
}

//-----------------------------------------------------------------------------
// setAutorespond()
//-----------------------------------------------------------------------------
void LLAgent::setAutorespond()
{
	selectAutorespond(TRUE);
}

//-----------------------------------------------------------------------------
// clearAutorespond()
//-----------------------------------------------------------------------------
void LLAgent::clearAutorespond()
{
	selectAutorespond(FALSE);
}

//-----------------------------------------------------------------------------
// selectAutorespond()
//-----------------------------------------------------------------------------
void LLAgent::selectAutorespond(BOOL selected)
{
	LL_INFOS() << "Setting autorespond mode to " << selected << LL_ENDL;
	mIsAutorespond = selected;
	gSavedPerAccountSettings.setBOOL("FSAutorespondMode",selected);
	// [SJ - FIRE-2177 - Making Autorespons a simple Check in the menu again for clarity]
	//if (gAutorespondMenu)
	//{
	//	if (selected)
	//	{
	//		gAutorespondMenu->setLabel(LLTrans::getString("AvatarSetNotAutorespond"));
	//	}
	//	else
	//	{
	//		gAutorespondMenu->setLabel(LLTrans::getString("AvatarSetAutorespond"));
	//	}
	//}
}

//-----------------------------------------------------------------------------
// getAutorespond()
//-----------------------------------------------------------------------------
BOOL LLAgent::getAutorespond() const
{
	return mIsAutorespond;
}

//-----------------------------------------------------------------------------
// setAutorespondNonFriends()
//-----------------------------------------------------------------------------
void LLAgent::setAutorespondNonFriends()
{
	selectAutorespondNonFriends(TRUE);
}

//-----------------------------------------------------------------------------
// clearAutorespondNonFriends()
//-----------------------------------------------------------------------------
void LLAgent::clearAutorespondNonFriends()
{
	selectAutorespondNonFriends(FALSE);
}

//-----------------------------------------------------------------------------
// selectAutorespondNonFriends()
//-----------------------------------------------------------------------------
void LLAgent::selectAutorespondNonFriends(BOOL selected)
{
	LL_INFOS() << "Setting autorespond non-friends mode to " << selected << LL_ENDL;
	mIsAutorespondNonFriends = selected;
	gSavedPerAccountSettings.setBOOL("FSAutorespondNonFriendsMode",selected);
	// [SJ - FIRE-2177 - Making Autorespons a simple Check in the menu again for clarity]
	//if (gAutorespondNonFriendsMenu)
	//{
	//	if (selected)
	//	{
	//		gAutorespondNonFriendsMenu->setLabel(LLTrans::getString("AvatarSetNotAutorespondNonFriends"));
	//	}
	//	else
	//	{
	//		gAutorespondNonFriendsMenu->setLabel(LLTrans::getString("AvatarSetAutorespondNonFriends"));
	//	}
	//}
}

//-----------------------------------------------------------------------------
// getAutorespondNonFriends()
//-----------------------------------------------------------------------------
BOOL LLAgent::getAutorespondNonFriends() const
{
	return mIsAutorespondNonFriends;
}

// <FS:PP> FIRE-1245: Option to block/reject teleport offers

//-----------------------------------------------------------------------------
// setRejectTeleportOffers()
//-----------------------------------------------------------------------------
void LLAgent::setRejectTeleportOffers()
{
	selectRejectTeleportOffers(TRUE);
}

//-----------------------------------------------------------------------------
// clearRejectTeleportOffers()
//-----------------------------------------------------------------------------
void LLAgent::clearRejectTeleportOffers()
{
	selectRejectTeleportOffers(FALSE);
}

//-----------------------------------------------------------------------------
// selectRejectTeleportOffers()
//-----------------------------------------------------------------------------
void LLAgent::selectRejectTeleportOffers(BOOL selected)
{
	LL_INFOS() << "Setting rejecting teleport offers mode to " << selected << LL_ENDL;
	mIsRejectTeleportOffers = selected;
	gSavedPerAccountSettings.setBOOL("FSRejectTeleportOffersMode", selected);
}

//-----------------------------------------------------------------------------
// getRejectTeleportOffers()
//-----------------------------------------------------------------------------
BOOL LLAgent::getRejectTeleportOffers() const
{
	return mIsRejectTeleportOffers;
}

// </FS:PP> FIRE-1245: Option to block/reject teleport offers

// <FS:PP> FIRE-15233: Automatic friendship request refusal

//-----------------------------------------------------------------------------
// setRejectFriendshipRequests()
//-----------------------------------------------------------------------------
void LLAgent::setRejectFriendshipRequests()
{
	selectRejectFriendshipRequests(TRUE);
}

//-----------------------------------------------------------------------------
// clearRejectFriendshipRequests()
//-----------------------------------------------------------------------------
void LLAgent::clearRejectFriendshipRequests()
{
	selectRejectFriendshipRequests(FALSE);
}

//-----------------------------------------------------------------------------
// selectRejectFriendshipRequests()
//-----------------------------------------------------------------------------
void LLAgent::selectRejectFriendshipRequests(BOOL selected)
{
	LL_INFOS() << "Setting rejecting friendship requests mode to " << selected << LL_ENDL;
	mIsRejectFriendshipRequests = selected;
	gSavedPerAccountSettings.setBOOL("FSRejectFriendshipRequestsMode", selected);
}

//-----------------------------------------------------------------------------
// getRejectFriendshipRequests()
//-----------------------------------------------------------------------------
BOOL LLAgent::getRejectFriendshipRequests() const
{
	return mIsRejectFriendshipRequests;
}

// </FS:PP> FIRE-15233: Automatic friendship request refusal

// <FS:PP> Option to block/reject all group invites

//-----------------------------------------------------------------------------
// setRejectAllGroupInvites()
//-----------------------------------------------------------------------------
void LLAgent::setRejectAllGroupInvites()
{
	selectRejectAllGroupInvites(TRUE);
}

//-----------------------------------------------------------------------------
// clearRejectAllGroupInvites()
//-----------------------------------------------------------------------------
void LLAgent::clearRejectAllGroupInvites()
{
	selectRejectAllGroupInvites(FALSE);
}

//-----------------------------------------------------------------------------
// selectRejectAllGroupInvites()
//-----------------------------------------------------------------------------
void LLAgent::selectRejectAllGroupInvites(BOOL selected)
{
	LL_INFOS() << "Setting rejecting group invites mode to " << selected << LL_ENDL;
	mIsRejectAllGroupInvites = selected;
	gSavedPerAccountSettings.setBOOL("FSRejectAllGroupInvitesMode", selected);
}

//-----------------------------------------------------------------------------
// getRejectAllGroupInvites()
//-----------------------------------------------------------------------------
BOOL LLAgent::getRejectAllGroupInvites() const
{
	return mIsRejectAllGroupInvites;
}

// </FS:PP> Option to block/reject all group invites

//-----------------------------------------------------------------------------
// 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,
	BOOL allow_flying)
{
	if (!isAgentAvatarValid())
	{
		return;
	}

    if (target_global.isExactlyZero())
    {
        LL_WARNS() << "Canceling attempt to start autopilot towards invalid position" << LL_ENDL;
        return;
    }
	
	// Are there any pending callbacks from previous auto pilot requests?
	if (mAutoPilotFinishedCallback)
	{
		mAutoPilotFinishedCallback(dist_vec(gAgent.getPositionGlobal(), mAutoPilotTargetGlobal) < mAutoPilotStopDistance, mAutoPilotCallbackData);
	}

	mAutoPilotFinishedCallback = finish_callback;
	mAutoPilotCallbackData = callback_data;
	mAutoPilotRotationThreshold = rot_threshold;
	mAutoPilotBehaviorName = behavior_name;
	mAutoPilotAllowFlying = allow_flying;

	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 = (F32) sqrt( distance );
		if (mAutoPilotStopDistance < 0.5f) 
		{
			mAutoPilotStopDistance = 0.5f;
		}
	}

	if (mAutoPilotAllowFlying)
	{
		mAutoPilotFlyOnStop = getFlying();
	}
	else
	{
		mAutoPilotFlyOnStop = FALSE;
	}

	if (distance > 30.0 && mAutoPilotAllowFlying)
	{
		setFlying(TRUE);
	}

	if ( distance > 1.f && 
		mAutoPilotAllowFlying &&
		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;
	setAutoPilotTargetGlobal(target_global);

	if (target_rotation)
	{
		mAutoPilotUseRotation = TRUE;
		mAutoPilotTargetFacing = LLVector3::x_axis * *target_rotation;
		mAutoPilotTargetFacing.mV[VZ] = 0.f;
		mAutoPilotTargetFacing.normalize();
	}
	else
	{
		mAutoPilotUseRotation = FALSE;
	}

	mAutoPilotNoProgressFrameCount = 0;
}


//-----------------------------------------------------------------------------
// setAutoPilotTargetGlobal
//-----------------------------------------------------------------------------
void LLAgent::setAutoPilotTargetGlobal(const LLVector3d &target_global)
{
	if (mAutoPilot)
	{
		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);
		// Note: this might malfunction for sitting agent, since pelvis stays same, but agent's position becomes lower
		// But for autopilot to work we assume that agent is standing and ready to go.
		F64 target_height = llmax((F64)gAgentAvatarp->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);
	}
}

//-----------------------------------------------------------------------------
// startFollowPilot()
//-----------------------------------------------------------------------------
void LLAgent::startFollowPilot(const LLUUID &leader_id, BOOL allow_flying, F32 stop_distance)
{
	mLeaderID = leader_id;
	if ( mLeaderID.isNull() ) return;

	LLViewerObject* object = gObjectList.findObject(mLeaderID);
	if (!object) 
	{
		mLeaderID = LLUUID::null;
		return;
	}

	startAutoPilotGlobal(object->getPositionGlobal(), 
						 std::string(),	// behavior_name
						 NULL,			// target_rotation
						 NULL,			// finish_callback
						 NULL,			// callback_data
						 stop_distance,
						 0.03f,			// rotation_threshold
						 allow_flying);
}


//-----------------------------------------------------------------------------
// 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(gAgent.getPositionGlobal(), mAutoPilotTargetGlobal) < mAutoPilotStopDistance, mAutoPilotCallbackData);
			mAutoPilotFinishedCallback = NULL;
		}
		mLeaderID = LLUUID::null;

		setControlFlags(AGENT_CONTROL_STOP);

		if (user_cancel && !mAutoPilotBehaviorName.empty())
		{
			if (mAutoPilotBehaviorName == "Sit")
				LL_INFOS("Agent") << "Autopilot-Sit was canceled by user action" << LL_ENDL;
			else if (mAutoPilotBehaviorName == "Attach")
				LLNotificationsUtil::add("CancelledAttach");
			else
				LLNotificationsUtil::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 (gAgentAvatarp->mInAir && mAutoPilotAllowFlying)
		{
			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++;
            bool out_of_time = false;
            if (getFlying())
            {
                out_of_time = mAutoPilotNoProgressFrameCount > AUTOPILOT_MAX_TIME_NO_PROGRESS_FLY * gFPSClamped;
            }
            else
            {
                out_of_time = mAutoPilotNoProgressFrameCount > AUTOPILOT_MAX_TIME_NO_PROGRESS_WALK * gFPSClamped;
            }
			if (out_of_time)
			{
				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
		F32 slow_distance;
		if (getFlying())
		{
			slow_distance = llmax(8.f, mAutoPilotStopDistance + 5.f);
		}
		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 = gAgentAvatarp->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
            U32 movement_flag = 0;

			if (at * direction > 0.9f)
			{
                movement_flag = AGENT_CONTROL_AT_POS;
            }
            else if (at * direction < -0.9f)
            {
                movement_flag = AGENT_CONTROL_AT_NEG;
            }

            if (getFlying())
            {
                // flying is too fast and has high inertia, artificially slow it down
                // Don't update flags too often, server might not react
                static U64 last_time_microsec = 0;
                U64 time_microsec = LLTimer::getTotalTime();
                U64 delta = time_microsec - last_time_microsec;
                // fly during ~0-40 ms, stop during ~40-250 ms
                if (delta > 250000) // 250ms
                {
                    // reset even if !movement_flag
                    last_time_microsec = time_microsec;
                }
                else if (delta > 40000) // 40 ms
                {
                    clearControlFlags(AGENT_CONTROL_AT_POS | AGENT_CONTROL_AT_POS);
                    movement_flag = 0;
                }
            }

            if (movement_flag)
            {
                setControlFlags(movement_flag);
			}
		}

		// 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 = LLFloaterReg::findTypedInstance<LLFloaterMove>("moveview");
	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 (isAgentAvatarValid())
	{
		BOOL in_air = gAgentAvatarp->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)
{
    static LLCachedControl<F32> hint_timeout(gSavedSettings, "NotMovingHintTimeout");
	if (mMoveTimer.getStarted() && mMoveTimer.getElapsedTimeF32() > hint_timeout)
	{
		LLFirstUse::notMoving();
	}

	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;
}

// 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)
// [RLVa:KB] - Checked: RLVa-2.0.2
	if ( (gAgentCamera.cameraMouselook() && !LLVOAvatar::sVisibleInFirstPerson) || (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWSELF)) )
// [/RLVa:KB]
	{
		return FALSE;
	}

	return mShowAvatar && mOutfitChosen;
}

// TRUE if we need to render your own avatar's head.
BOOL LLAgent::needsRenderHead()
{
// [RLVa:KB] - Checked: RLVa-2.0.2
	return ((LLVOAvatar::sVisibleInFirstPerson && LLPipeline::sReflectionRender) || (mShowAvatar && !gAgentCamera.cameraMouselook())) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWSELFHEAD));
// [/RLVa:KB]
//	return (LLVOAvatar::sVisibleInFirstPerson && LLPipeline::sReflectionRender) || (mShowAvatar && !gAgentCamera.cameraMouselook());
}

//-----------------------------------------------------------------------------
// startTyping()
//-----------------------------------------------------------------------------
void LLAgent::startTyping()
{
// [RLVa:KB] - @redirchat
	if (!RlvActions::canSendTypingStart())
	{
		return;
	}
// [/RLVa:KB]

	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)
	{
		LLViewerObject* chatter = gObjectList.findObject(mLastChatterID);
		if (chatter && chatter->isAvatar())
		{
			gAgentCamera.setLookAt(LOOKAT_TARGET_RESPOND, chatter, LLVector3::zero);
		}
	}

	if (gSavedSettings.getBOOL("PlayTypingAnim"))
	{
		sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_START);
	}
	// <FS:Ansariel> [FS Communication UI]
	//(LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))->
	//		sendChatFromViewer("", CHAT_TYPE_START, FALSE);
	FSNearbyChat::instance().sendChatFromViewer("", CHAT_TYPE_START, FALSE);
	// </FS:Ansariel> [FS Communication UI]
}

//-----------------------------------------------------------------------------
// stopTyping()
//-----------------------------------------------------------------------------
void LLAgent::stopTyping()
{
	if (mRenderState & AGENT_STATE_TYPING)
	{
		clearRenderState(AGENT_STATE_TYPING);
		sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_STOP);
		// <FS:Ansariel> [FS Communication UI]
		//(LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))->
		//		sendChatFromViewer("", CHAT_TYPE_STOP, FALSE);
		FSNearbyChat::instance().sendChatFromViewer("", CHAT_TYPE_STOP, FALSE);
		// </FS:Ansariel> [FS Communication UI]
	}
}

//-----------------------------------------------------------------------------
// setRenderState()
//-----------------------------------------------------------------------------
void LLAgent::setRenderState(U8 newstate)
{
	mRenderState |= newstate;
}

//-----------------------------------------------------------------------------
// clearRenderState()
//-----------------------------------------------------------------------------
void LLAgent::clearRenderState(U8 clearstate)
{
	mRenderState &= ~clearstate;
}


//-----------------------------------------------------------------------------
// getRenderState()
//-----------------------------------------------------------------------------
U8 LLAgent::getRenderState()
{
	// *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;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// endAnimationUpdateUI()
//-----------------------------------------------------------------------------
void LLAgent::endAnimationUpdateUI()
{
	if (LLApp::isExiting()
		|| !gViewerWindow
		|| !gMenuBarView
		|| !gToolBarView
		|| !gStatusBar)
	{
		return;
	}
	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 )
	{
		// <FS:Zi> Unhide chat bar, unless autohide is enabled
		gSavedSettings.setBOOL("MouseLookEnabled",FALSE);
		if(!gSavedSettings.getBOOL("AutohideChatBar"))
			FSNearbyChat::instance().showDefaultChatBar(TRUE);
		// </FS:Zi>

		gToolBarView->setToolBarsVisible(true);
		// show mouse cursor
		gViewerWindow->showCursor();
		// show menus
		gMenuBarView->setVisible(TRUE);
		// <FS:Ansariel> Separate navigation and favorites panel
		LLNavigationBar::instance().getView()->setVisible(TRUE);
		gStatusBar->setVisibleForMouselook(true);

		// <FS:Zi> We don't use the mini location panel in Firestorm
        // static LLCachedControl<bool> show_mini_location_panel(gSavedSettings, "ShowMiniLocationPanel");
		// if (show_mini_location_panel)
		// {
		// 	LLPanelTopInfoBar::getInstance()->setVisible(TRUE);
		// }
		// </FS:Zi>

		LLChicletBar::getInstance()->setVisible(TRUE);

		LLPanelStandStopFlying::getInstance()->setVisible(TRUE);

		LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset);

		LLFloaterCamera::onLeavingMouseLook();

		if (mMouselookModeOutSignal)
		{
			(*mMouselookModeOutSignal)();
		}

		// Only pop if we have pushed...
		if (TRUE == mViewsPushed)
		{
#if 0 // Use this once all floaters are registered
			LLFloaterReg::restoreVisibleInstances();
#else // Use this for now
			LLFloaterView::skip_list_t skip_list;
			if (LLFloaterReg::findInstance("mini_map"))
			{
				skip_list.insert(LLFloaterReg::findInstance("mini_map"));
			}
			// <FS:Ansariel> Beacons floater doesn't need to be open for us to show beacons
			//if (LLFloaterReg::findInstance("beacons"))
			//{
			//	skip_list.insert(LLFloaterReg::findInstance("beacons"));
			//}
			// </FS:Ansariel>
			if(gSavedSettings.getBOOL("FSShowStatsBarInMouselook"))
			{
				skip_list.insert(LLFloaterReg::findInstance("stats"));
			}
			// <FS:LO> //FIRE-6385: Show all script dialogs still after leaving mouselook
			BOOST_FOREACH(LLFloater *tmp, LLFloaterReg::getFloaterList("script_floater"))
			{
				skip_list.insert(tmp);
			}
			// </FS:LO>
			// <FS:Ansariel> Skip restoring conversations and radar floater
			if (gSavedSettings.getBOOL("FSShowConvoAndRadarInML"))
			{
				skip_list.insert(LLFloaterReg::findInstance("fs_radar"));
				skip_list.insert(LLFloaterReg::findInstance("fs_im_container"));
			}
			// </FS:Ansariel>

			// <FS:Ansariel> [FS communication UI] Commented out so far.
			//               Maybe check if need it to close standalone IM floater
			//               when going into mouselook
			//LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
			//LLFloaterIMContainer::floater_list_t conversations;
			//im_box->getDetachedConversationFloaters(conversations);
			//BOOST_FOREACH(LLFloater* conversation, conversations)
			//{
			//	LL_INFOS() << "skip_list.insert(session_floater): " << conversation->getTitle() << LL_ENDL;
			//	skip_list.insert(conversation);
			//}
			// </FS:Ansariel> [FS communication UI]

			gFloaterView->popVisibleAll(skip_list);
#endif
			mViewsPushed = FALSE;
			gFocusMgr.setKeyboardFocus(NULL);	// <FS:Zi> make sure no floater has focus  after restoring them
		}
		// <FS:PP> FIRE-11312: Exiting Mouselook puts keyboard focus on Nearby Chat bar
		else if(gSavedSettings.getBOOL("FSShowInterfaceInMouselook"))
		{
			gFocusMgr.setKeyboardFocus(NULL);
		}
		// </FS:PP>

		
		gAgentCamera.setLookAt(LOOKAT_TARGET_CLEAR);
		if( gMorphView )
		{
			gMorphView->setVisible( FALSE );
		}

		// Disable mouselook-specific animations
		if (isAgentAvatarValid())
		{
			if( gAgentAvatarp->isAnyAnimationSignaled(AGENT_GUN_AIM_ANIMS, NUM_AGENT_GUN_AIM_ANIMS) )
			{
				if (gAgentAvatarp->mSignaledAnimations.find(ANIM_AGENT_AIM_RIFLE_R) != gAgentAvatarp->mSignaledAnimations.end())
				{
					sendAnimationRequest(ANIM_AGENT_AIM_RIFLE_R, ANIM_REQUEST_STOP);
					sendAnimationRequest(ANIM_AGENT_HOLD_RIFLE_R, ANIM_REQUEST_START);
				}
				if (gAgentAvatarp->mSignaledAnimations.find(ANIM_AGENT_AIM_HANDGUN_R) != gAgentAvatarp->mSignaledAnimations.end())
				{
					sendAnimationRequest(ANIM_AGENT_AIM_HANDGUN_R, ANIM_REQUEST_STOP);
					sendAnimationRequest(ANIM_AGENT_HOLD_HANDGUN_R, ANIM_REQUEST_START);
				}
				if (gAgentAvatarp->mSignaledAnimations.find(ANIM_AGENT_AIM_BAZOOKA_R) != gAgentAvatarp->mSignaledAnimations.end())
				{
					sendAnimationRequest(ANIM_AGENT_AIM_BAZOOKA_R, ANIM_REQUEST_STOP);
					sendAnimationRequest(ANIM_AGENT_HOLD_BAZOOKA_R, ANIM_REQUEST_START);
				}
				if (gAgentAvatarp->mSignaledAnimations.find(ANIM_AGENT_AIM_BOW_L) != gAgentAvatarp->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);

		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);

		LLFloaterCamera::onAvatarEditingAppearance(false);
	}

	//---------------------------------------------------------------------
	// Set up UI for mode we're entering
	//---------------------------------------------------------------------
	if (gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK)
	{

		// <FS:PP> FIRE-8868: Show UI in mouselook
		if(!gSavedSettings.getBOOL("FSShowInterfaceInMouselook"))
		{
		// </FS:PP>

		// clean up UI
		// first show anything hidden by UI toggle
		gViewerWindow->setUIVisibility(TRUE);

		// then hide stuff we want hidden for mouselook 
		gToolBarView->setToolBarsVisible(false);
		gMenuBarView->setVisible(FALSE);
		// <FS:Ansariel> Separate navigation and favorites panel
		LLNavigationBar::instance().getView()->setVisible(FALSE);
		gStatusBar->setVisibleForMouselook(false);

		// <FS:Zi> We don't use the mini location panel in Firestorm
		// LLPanelTopInfoBar::getInstance()->setVisible(FALSE);

		LLChicletBar::getInstance()->setVisible(FALSE);

		LLPanelStandStopFlying::getInstance()->setVisible(FALSE);

		// <FS:Zi> Hide chat bar in mouselook mode, unless there is text in it
		gSavedSettings.setBOOL("MouseLookEnabled",TRUE);
		if(FSNearbyChat::instance().defaultChatBarIsIdle())
			FSNearbyChat::instance().showDefaultChatBar(FALSE);
		// </FS:Zi>

		// clear out camera lag effect
		// <FS:PP> Commented out and moved lower for FIRE-8868: Show UI in mouselook
		// gAgentCamera.clearCameraLag();
		// </FS:PP>

		// JC - Added for always chat in third person option
		gFocusMgr.setKeyboardFocus(NULL);

		LLToolMgr::getInstance()->setCurrentToolset(gMouselookToolset);

		mViewsPushed = TRUE;

		// <FS:PP> Commented out and moved lower for FIRE-8868: Show UI in mouselook
		// if (mMouselookModeInSignal)
		// {
		//	(*mMouselookModeInSignal)();
		// }
		// </FS:PP>

		// hide all floaters except the mini map

#if 0 // Use this once all floaters are registered
		std::set<std::string> exceptions;
		exceptions.insert("mini_map");
		if(gSavedSettings.getBOOL("FSShowStatsBarInMouselook"))
		{
			exceptions.insert("stats");
		}
		exceptions.insert("script_floater"); // <FS:LO> //FIRE-6385: Show all script dialogs still after leaving mouselook
		LLFloaterReg::hideVisibleInstances(exceptions);
#else // Use this for now
		LLFloaterView::skip_list_t skip_list;
		skip_list.insert(LLFloaterReg::findInstance("mini_map"));
		// <FS:Ansariel> Beacons floater doesn't need to be open for us to show beacon
		//skip_list.insert(LLFloaterReg::findInstance("beacons"));
		if(gSavedSettings.getBOOL("FSShowStatsBarInMouselook"))
		{
			skip_list.insert(LLFloaterReg::findInstance("stats"));
		}
		// <FS:LO> //FIRE-6385: Show all script dialogs still when entering mouselook
		BOOST_FOREACH(LLFloater *tmp, LLFloaterReg::getFloaterList("script_floater"))
		{
			skip_list.insert(tmp);
		}
		// </FS:LO>
		// <FS:Ansariel> Skip hiding conversations and radar floater
		if (gSavedSettings.getBOOL("FSShowConvoAndRadarInML"))
		{
			skip_list.insert(LLFloaterReg::findInstance("fs_radar"));
			skip_list.insert(LLFloaterReg::findInstance("fs_im_container"));
		}
		// </FS:Ansariel>
		gFloaterView->pushVisibleAll(FALSE, skip_list);
#endif

		// <FS:PP> FIRE-8868: Show UI in mouselook
		gConsole->setVisible( TRUE );

		} // Check ends here, anything below will be executed regardless of its state

		gAgentCamera.clearCameraLag();

		if (mMouselookModeInSignal)
		{
			(*mMouselookModeInSignal)();
		}
		// </FS:PP>

		if( gMorphView )
		{
			gMorphView->setVisible(FALSE);
		}

		// <FS:PP> Commented out and moved few lines higher for FIRE-8868: Show UI in mouselook
		// gConsole->setVisible( TRUE );

		if (isAgentAvatarValid())
		{
			// Trigger mouselook-specific animations
			if( gAgentAvatarp->isAnyAnimationSignaled(AGENT_GUN_HOLD_ANIMS, NUM_AGENT_GUN_HOLD_ANIMS) )
			{
				if (gAgentAvatarp->mSignaledAnimations.find(ANIM_AGENT_HOLD_RIFLE_R) != gAgentAvatarp->mSignaledAnimations.end())
				{
					sendAnimationRequest(ANIM_AGENT_HOLD_RIFLE_R, ANIM_REQUEST_STOP);
					sendAnimationRequest(ANIM_AGENT_AIM_RIFLE_R, ANIM_REQUEST_START);
				}
				if (gAgentAvatarp->mSignaledAnimations.find(ANIM_AGENT_HOLD_HANDGUN_R) != gAgentAvatarp->mSignaledAnimations.end())
				{
					sendAnimationRequest(ANIM_AGENT_HOLD_HANDGUN_R, ANIM_REQUEST_STOP);
					sendAnimationRequest(ANIM_AGENT_AIM_HANDGUN_R, ANIM_REQUEST_START);
				}
				if (gAgentAvatarp->mSignaledAnimations.find(ANIM_AGENT_HOLD_BAZOOKA_R) != gAgentAvatarp->mSignaledAnimations.end())
				{
					sendAnimationRequest(ANIM_AGENT_HOLD_BAZOOKA_R, ANIM_REQUEST_STOP);
					sendAnimationRequest(ANIM_AGENT_AIM_BAZOOKA_R, ANIM_REQUEST_START);
				}
				if (gAgentAvatarp->mSignaledAnimations.find(ANIM_AGENT_HOLD_BOW_L) != gAgentAvatarp->mSignaledAnimations.end())
				{
					sendAnimationRequest(ANIM_AGENT_HOLD_BOW_L, ANIM_REQUEST_STOP);
					sendAnimationRequest(ANIM_AGENT_AIM_BOW_L, ANIM_REQUEST_START);
				}
			}
			if (gAgentAvatarp->getParent())
			{
				LLVector3 at_axis = LLViewerCamera::getInstance()->getAtAxis();
				LLViewerObject* root_object = (LLViewerObject*)gAgentAvatarp->getRoot();
				if (root_object->flagCameraDecoupled())
				{
					resetAxes(at_axis);
				}
				else
				{
					resetAxes(at_axis * ~((LLViewerObject*)gAgentAvatarp->getParent())->getRenderRotation());
				}
			}
		}
	}
	else if (gAgentCamera.getCameraMode() == CAMERA_MODE_CUSTOMIZE_AVATAR)
	{
		LLToolMgr::getInstance()->setCurrentToolset(gFaceEditToolset);

		if( gMorphView )
		{
			gMorphView->setVisible( TRUE );
		}

		// freeze avatar
		if (isAgentAvatarValid())
		{
			mPauseRequest = gAgentAvatarp->requestPause();
		}

		LLFloaterCamera::onAvatarEditingAppearance(true);
	}

	if (isAgentAvatarValid())
	{
		gAgentAvatarp->updateAttachmentVisibility(gAgentCamera.getCameraMode());
	}

	gFloaterTools->dirty();

	// Don't let this be called more than once if the camera
	// mode hasn't changed.  --JC
	gAgentCamera.updateLastCamera();
}

boost::signals2::connection LLAgent::setMouselookModeInCallback( const camera_signal_t::slot_type& cb )
{
	if (!mMouselookModeInSignal) mMouselookModeInSignal = new camera_signal_t();
	return mMouselookModeInSignal->connect(cb);
}

boost::signals2::connection LLAgent::setMouselookModeOutCallback( const camera_signal_t::slot_type& cb )
{
	if (!mMouselookModeOutSignal) mMouselookModeOutSignal = new camera_signal_t();
	return mMouselookModeOutSignal->connect(cb);
}

//-----------------------------------------------------------------------------
// 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();
}

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 )
{
    LLViewerObject          *object;

    if (gAgentID == LLUUID::null)
    {
        return;
    }
    // we've got an ID for an agent viewerobject
    object = gObjectList.findObject(gAgentID);
    if (! object)
    {
        LL_INFOS() << "setStartPosition - Can't find agent viewerobject id " << gAgentID << LL_ENDL;
        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
// <FS:CR> Aurora Sim
    //const F32 REGION_WIDTH = LLWorld::getInstance()->getRegionWidthInMeters();
    const F32 REGION_WIDTH = getRegion()->getWidth();
// </FS:CR> Aurora Sim

    LLVector3 agent_pos = getPositionAgent();

    if (isAgentAvatarValid())
    {
        // the z height is at the agent's feet
        agent_pos.mV[VZ] -= 0.5f * (gAgentAvatarp->mBodySize.mV[VZ] + gAgentAvatarp->mAvatarOffset.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() );
    // 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;

    if (!requestPostCapability("HomeLocation", body, 
            boost::bind(&LLAgent::setStartPositionSuccess, this, _1)))
    // <FS:Ansariel> FIRE-19894: Setting Home location fails in OpenSim after Coroutines update
        //LL_WARNS() << "Unable to post to HomeLocation capability." << LL_ENDL;
    {
        LLMessageSystem* msg = gMessageSystem;
        msg->newMessageFast(_PREHASH_SetStartLocationRequest);
        msg->nextBlockFast( _PREHASH_AgentData);
        msg->addUUIDFast(_PREHASH_AgentID, gAgentID);
        msg->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
        msg->nextBlockFast( _PREHASH_StartLocationData);
        // corrected by sim
        msg->addStringFast(_PREHASH_SimName, "");
        msg->addU32Fast(_PREHASH_LocationID, homeLocation["LocationId"].asInteger());
        msg->addVector3Fast(_PREHASH_LocationPos,
                            ll_vector3_from_sdmap(homeLocation["LocationPos"]));
        msg->addVector3Fast(_PREHASH_LocationLookAt,
                            ll_vector3_from_sdmap(homeLocation["LocationLookAt"]));
        gAgent.sendReliableMessage();
    }
    // </FS:Ansariel>

    const U32 HOME_INDEX = 1;
    if( HOME_INDEX == location_id )
    {
        setHomePosRegion( mRegionp->getHandle(), getPositionAgent() );
    }
}

void LLAgent::setStartPositionSuccess(const LLSD &result)
{
    LLVector3 agent_pos;
    bool      error = true;

    do {
        // was the call to /agent/<agent-id>/home-location successful?
        // If not, we keep error set to true
        if (!result.has("success"))
            break;

        if (0 != strncmp("true", result["success"].asString().c_str(), 4))
            break;

        // did the simulator return a "justified" home location?
        // If no, we keep error set to true
        if (!result.has("HomeLocation"))
            break;

        if ((!result["HomeLocation"].has("LocationPos")) ||
                (!result["HomeLocation"]["LocationPos"].has("X")) ||
                (!result["HomeLocation"]["LocationPos"].has("Y")) ||
                (!result["HomeLocation"]["LocationPos"].has("Z")))
            break;

        agent_pos.mV[VX] = result["HomeLocation"]["LocationPos"]["X"].asInteger();
        agent_pos.mV[VY] = result["HomeLocation"]["LocationPos"]["Y"].asInteger();
        agent_pos.mV[VZ] = result["HomeLocation"]["LocationPos"]["Z"].asInteger();

        error = false;

    } while (0);

    if (error)
    {
        LL_WARNS() << "Error in response to home position set." << LL_ENDL;
    }
    else
    {
        LL_INFOS() << "setting home position" << LL_ENDL;

        LLViewerRegion *viewer_region = gAgent.getRegion();
        setHomePosRegion(viewer_region->getHandle(), agent_pos);
    }
}

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
//	LL_INFOS() << "Sending stop for motion " << motion->getName() << LL_ENDL;
	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)
	{
// [RLVa:KB] - Checked: 2010-05-03 (RLVa-1.2.0g) | Added: RLVa-1.1.0g
		if (!gRlvHandler.hasBehaviour(RLV_BHVR_ALLOWIDLE))
			clearAFK();
// [/RLVa:KB]
//		clearAFK();
	}
	else if (id == ANIM_AGENT_STANDUP)
	{
		// send stand up command
		setControlFlags(AGENT_CONTROL_FINISH_ANIM);

		// now trigger dusting self off animation
		if (isAgentAvatarValid() && !gAgentAvatarp->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();
}

bool LLAgent::isGodlikeWithoutAdminMenuFakery() const
{
	return mAgentAccess->isGodlikeWithoutAdminMenuFakery();
}

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();
}

//static 
int LLAgent::convertTextToMaturity(char text)
{
	return LLAgentAccess::convertTextToMaturity(text);
}

void LLAgent::handlePreferredMaturityResult(U8 pServerMaturity)
{
	// Update the number of responses received
	++mMaturityPreferenceResponseId;
	llassert(mMaturityPreferenceResponseId <= mMaturityPreferenceRequestId);

	// Update the last known server maturity response
	mLastKnownResponseMaturity = pServerMaturity;

	// Ignore all responses if we know there are more unanswered requests that are expected
	if (mMaturityPreferenceResponseId == mMaturityPreferenceRequestId)
	{
		// If we received a response that matches the last known request, then we are good
		if (mLastKnownRequestMaturity == mLastKnownResponseMaturity)
		{
			mMaturityPreferenceNumRetries = 0;
			reportPreferredMaturitySuccess();
			llassert(static_cast<U8>(gSavedSettings.getU32("PreferredMaturity")) == mLastKnownResponseMaturity);
		}
		// Else, the viewer is out of sync with the server, so let's try to re-sync with the
		// server by re-sending our last known request.  Cap the re-tries at 3 just to be safe.
		else if (++mMaturityPreferenceNumRetries <= 3)
		{
			LL_INFOS() << "Retrying attempt #" << mMaturityPreferenceNumRetries << " to set viewer preferred maturity to '"
				<< LLViewerRegion::accessToString(mLastKnownRequestMaturity) << "'" << LL_ENDL;
			sendMaturityPreferenceToServer(mLastKnownRequestMaturity);
		}
		// Else, the viewer is style out of sync with the server after 3 retries, so inform the user
		else
		{
			mMaturityPreferenceNumRetries = 0;
			LL_WARNS() << "Too many retries for maturity preference" << LL_ENDL;
			reportPreferredMaturityError();
		}
	}
}

void LLAgent::handlePreferredMaturityError()
{
	// Update the number of responses received
	++mMaturityPreferenceResponseId;
	llassert(mMaturityPreferenceResponseId <= mMaturityPreferenceRequestId);

	// Ignore all responses if we know there are more unanswered requests that are expected
	if (mMaturityPreferenceResponseId == mMaturityPreferenceRequestId)
	{
		mMaturityPreferenceNumRetries = 0;

		// If we received a response that matches the last known request, then we are synced with
		// the server, but not quite sure why we are
		if (mLastKnownRequestMaturity == mLastKnownResponseMaturity)
		{
			LL_WARNS() << "Got an error but maturity preference '" << LLViewerRegion::accessToString(mLastKnownRequestMaturity)
				<< "' seems to be in sync with the server" << LL_ENDL;
			reportPreferredMaturitySuccess();
		}
		// Else, the more likely case is that the last request does not match the last response,
		// so inform the user
		else
		{
			reportPreferredMaturityError();
		}
	}
}

void LLAgent::reportPreferredMaturitySuccess()
{
	// If there is a pending teleport request waiting for the maturity preference to be synced with
	// the server, let's start the pending request
	if (hasPendingTeleportRequest())
	{
		startTeleportRequest();
	}
}

void LLAgent::reportPreferredMaturityError()
{
	// If there is a pending teleport request waiting for the maturity preference to be synced with
	// the server, we were unable to successfully sync with the server on maturity preference, so let's
	// just raise the screen.
	mIsMaturityRatingChangingDuringTeleport = false;
	if (hasPendingTeleportRequest())
	{
		LL_WARNS("Teleport") << "Teleport failing due to preferred maturity error" << LL_ENDL;
		setTeleportState(LLAgent::TELEPORT_NONE);
	}

	// Get the last known maturity request from the user activity
	std::string preferredMaturity = LLViewerRegion::accessToString(mLastKnownRequestMaturity);
	LLStringUtil::toLower(preferredMaturity);

	// Get the last known maturity response from the server
	std::string actualMaturity = LLViewerRegion::accessToString(mLastKnownResponseMaturity);
	LLStringUtil::toLower(actualMaturity);

	// Notify the user
	LLSD args = LLSD::emptyMap();
	args["PREFERRED_MATURITY"] = preferredMaturity;
	args["ACTUAL_MATURITY"] = actualMaturity;
	LLNotificationsUtil::add("MaturityChangeError", args);

	// Check the saved settings to ensure that we are consistent.  If we are not consistent, update
	// the viewer, but do not send anything to server
	U8 localMaturity = static_cast<U8>(gSavedSettings.getU32("PreferredMaturity"));
	if (localMaturity != mLastKnownResponseMaturity)
	{
		bool tmpIsDoSendMaturityPreferenceToServer = mIsDoSendMaturityPreferenceToServer;
		mIsDoSendMaturityPreferenceToServer = false;
		LL_INFOS() << "Setting viewer preferred maturity to '" << LLViewerRegion::accessToString(mLastKnownResponseMaturity) << "'" << LL_ENDL;
		gSavedSettings.setU32("PreferredMaturity", static_cast<U32>(mLastKnownResponseMaturity));
		mIsDoSendMaturityPreferenceToServer = tmpIsDoSendMaturityPreferenceToServer;
	}
}

bool LLAgent::isMaturityPreferenceSyncedWithServer() const
{
	return (mMaturityPreferenceRequestId == mMaturityPreferenceResponseId);
}

void LLAgent::sendMaturityPreferenceToServer(U8 pPreferredMaturity)
{
	// Only send maturity preference to the server if enabled
	if (mIsDoSendMaturityPreferenceToServer)
	{
		// Increment the number of requests.  The handlers manage a separate count of responses.
		++mMaturityPreferenceRequestId;

		// Update the last know maturity request
		mLastKnownRequestMaturity = pPreferredMaturity;

		// If we don't have a region, report it as an error
		if (getRegion() == NULL)
		{
			LL_WARNS("Agent") << "Region is not defined, can not change Maturity setting." << LL_ENDL;
			return;
		}

		LLSD access_prefs = LLSD::emptyMap();
		access_prefs["max"] = LLViewerRegion::accessToShortString(pPreferredMaturity);

		LLSD postData = LLSD::emptyMap();
		postData["access_prefs"] = access_prefs;
		LL_INFOS() << "Sending viewer preferred maturity to '" << LLViewerRegion::accessToString(pPreferredMaturity) << LL_ENDL;

        if (!requestPostCapability("UpdateAgentInformation", postData,
            static_cast<httpCallback_t>(boost::bind(&LLAgent::processMaturityPreferenceFromServer, this, _1, pPreferredMaturity)),
            static_cast<httpCallback_t>(boost::bind(&LLAgent::handlePreferredMaturityError, this))
            ))
        {
            LL_WARNS("Agent") << "Maturity request post failed." << LL_ENDL;
        }
	}
}


void LLAgent::processMaturityPreferenceFromServer(const LLSD &result, U8 perferredMaturity)
{
    U8 maturity = SIM_ACCESS_MIN;

    llassert(result.isDefined());
    llassert(result.isMap());
    llassert(result.has("access_prefs"));
    llassert(result.get("access_prefs").isMap());
    llassert(result.get("access_prefs").has("max"));
    llassert(result.get("access_prefs").get("max").isString());
    if (result.isDefined() && result.isMap() && result.has("access_prefs")
        && result.get("access_prefs").isMap() && result.get("access_prefs").has("max")
        && result.get("access_prefs").get("max").isString())
    {
        LLSD::String actualPreference = result.get("access_prefs").get("max").asString();
        LLStringUtil::trim(actualPreference);
        maturity = LLViewerRegion::shortStringToAccess(actualPreference);
    }

    if (maturity != perferredMaturity)
    {
        LL_WARNS() << "while attempting to change maturity preference from '"
            << LLViewerRegion::accessToString(mLastKnownResponseMaturity)
            << "' to '" << LLViewerRegion::accessToString(perferredMaturity)
            << "', the server responded with '"
            << LLViewerRegion::accessToString(maturity)
            << "' [value:" << static_cast<U32>(maturity)
            << "], " << LL_ENDL;
    }
    handlePreferredMaturityResult(maturity);
}


bool LLAgent::requestPostCapability(const std::string &capName, LLSD &postData, httpCallback_t cbSuccess, httpCallback_t cbFailure)
{
    std::string url;

    // <FS:Ansariel> FIRE-21323: Crashfix
    //url = getRegion()->getCapability(capName);
    url = getRegion() ? getRegion()->getCapability(capName) : "";
    // </FS:Ansariel>

    if (url.empty())
    {
        LL_WARNS("Agent") << "Could not retrieve region capability \"" << capName << "\"" << LL_ENDL;
        return false;
    }

    LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, mHttpPolicy, postData, cbSuccess, cbFailure);
    return true;
}

bool LLAgent::requestGetCapability(const std::string &capName, httpCallback_t cbSuccess, httpCallback_t cbFailure)
{
    std::string url;

    url = getRegionCapability(capName);

    if (url.empty())
    {
        LL_WARNS("Agent") << "Could not retrieve region capability \"" << capName << "\"" << LL_ENDL;
        return false;
    }

    LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpGet(url, mHttpPolicy, cbSuccess, cbFailure);
    return true;
}

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);
	mGodLevelChangeSignal(god_level);
}

LLAgent::god_level_change_slot_t LLAgent::registerGodLevelChanageListener(god_level_change_callback_t pGodLevelChangeCallback)
{
	return mGodLevelChangeSignal.connect(pGodLevelChangeCallback);
}

const LLAgentAccess& LLAgent::getAgentAccess()
{
	return *mAgentAccess;
}

bool LLAgent::validateMaturity(const LLSD& newvalue)
{
	return mAgentAccess->canSetMaturity(newvalue.asInteger());
}

void LLAgent::handleMaturity(const LLSD &pNewValue)
{
	sendMaturityPreferenceToServer(static_cast<U8>(pNewValue.asInteger()));
}

//----------------------------------------------------------------------------

void LLAgent::buildFullname(std::string& name) const
{
	if (gAgentAvatarp)
	{
		name = gAgentAvatarp->getFullname();
	}
}

//*TODO remove, is not used anywhere as of August 20, 2009
void LLAgent::buildFullnameAndTitle(std::string& name) const
{
	if (isGroupMember())
	{
		name = mGroupTitle;
		name += ' ';
	}
	else
	{
		name.erase(0, name.length());
	}

	if (isAgentAvatarValid())
	{
		name += gAgentAvatarp->getFullname();
	}
}

BOOL LLAgent::isInGroup(const LLUUID& group_id, BOOL ignore_god_mode /* FALSE */) const
{
	if (!ignore_god_mode && isGodlike())
		return true;

	U32 count = mGroups.size();
	for(U32 i = 0; i < count; ++i)
	{
		if(mGroups[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 (isGodlikeWithoutAdminMenuFakery())
		return true;

	// GP_NO_POWERS can also mean no power is enough to grant an ability.
	if (GP_NO_POWERS == power) return FALSE;

	U32 count = mGroups.size();
	for(U32 i = 0; i < count; ++i)
	{
		if(mGroups[i].mID == group_id)
		{
			return (BOOL)((mGroups[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;
	
	U32 count = mGroups.size();
	for(U32 i = 0; i < count; ++i)
	{
		if(mGroups[i].mID == group_id)
		{
			return (mGroups[i].mPowers);
		}
	}

	return GP_NO_POWERS;
}

BOOL LLAgent::getGroupData(const LLUUID& group_id, LLGroupData& data) const
{
	S32 count = mGroups.size();
	for(S32 i = 0; i < count; ++i)
	{
		if(mGroups[i].mID == group_id)
		{
			data = mGroups[i];
			return TRUE;
		}
	}
	return FALSE;
}

S32 LLAgent::getGroupContribution(const LLUUID& group_id) const
{
	S32 count = mGroups.size();
	for(S32 i = 0; i < count; ++i)
	{
		if(mGroups[i].mID == group_id)
		{
			S32 contribution = mGroups[i].mContribution;
			return contribution;
		}
	}
	return 0;
}

BOOL LLAgent::setGroupContribution(const LLUUID& group_id, S32 contribution)
{
	S32 count = mGroups.size();
	for(S32 i = 0; i < count; ++i)
	{
		if(mGroups[i].mID == group_id)
		{
			mGroups[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.size();
	for(S32 i = 0; i < count; ++i)
	{
		if(mGroups[i].mID == group_id)
		{
			mGroups[i].mAcceptNotices = accept_notices;
			mGroups[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();

			// <FS:Ansariel> Mark groups hidden in profile
			gAgent.fireEvent(new LLOldEvents::LLValueChangedEvent(&gAgent, LLSD().with("group_id", group_id).with("visible", list_in_profile)), "");

			return TRUE;
		}
	}
	return FALSE;
}

BOOL LLAgent::canJoinGroups() const
{
	// <FS:CR> FIRE-12229
	//return (S32)mGroups.size() < LLAgentBenefitsMgr::current().getGroupMembershipLimit();
	if (LLGridManager::instance().isInSecondLife())
	{
		return (S32)mGroups.size() < LLAgentBenefitsMgr::current().getGroupMembershipLimit();
	}
	else
	{
		return ((!gMaxAgentGroups) || ((S32)mGroups.size() < gMaxAgentGroups));
	}
	// </FS:CR>
}

LLQuaternion LLAgent::getHeadRotation()
{
	if (!isAgentAvatarValid() || !gAgentAvatarp->mPelvisp || !gAgentAvatarp->mHeadp)
	{
		return LLQuaternion::DEFAULT;
	}

	if (!gAgentCamera.cameraMouselook())
	{
		return gAgentAvatarp->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 (gAgentAvatarp->getParent())
	{
		rot = rot * ~gAgentAvatarp->getParent()->getRotation();
	}

	return rot;
}

void LLAgent::sendAnimationRequests(const std::vector<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.size(); 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();
	}
	// <FS> Crash fix by Henri Beauchamp
	else
	{
		msg->clearMessage();
	}
	// </FS>
}

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();
}

// Send a message to the region to stop the NULL animation state
// This will reset animation state overrides for the agent.
void LLAgent::sendAnimationStateReset()
{
	if (gAgentID.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, LLUUID::null );
	msg->addBOOLFast(_PREHASH_StartAnim, FALSE);

	msg->nextBlockFast(_PREHASH_PhysicalAvatarEventList);
	msg->addBinaryDataFast(_PREHASH_TypeData, NULL, 0);
	sendReliableMessage();
}


// Send a message to the region to revoke sepecified permissions on ALL scripts in the region
// If the target is an object in the region, permissions in scripts on that object are cleared.
// If it is the region ID, all scripts clear the permissions for this agent
void LLAgent::sendRevokePermissions(const LLUUID & target, U32 permissions)
{
	// Currently only the bits for SCRIPT_PERMISSION_TRIGGER_ANIMATION and SCRIPT_PERMISSION_OVERRIDE_ANIMATIONS
	// are supported by the server.  Sending any other bits will cause the message to be dropped without changing permissions

	if (gAgentID.notNull() && gMessageSystem)
	{
		LLMessageSystem* msg = gMessageSystem;
		msg->newMessageFast(_PREHASH_RevokePermissions);
		msg->nextBlockFast(_PREHASH_AgentData);
		msg->addUUIDFast(_PREHASH_AgentID, getID());		// Must be our ID
		msg->addUUIDFast(_PREHASH_SessionID, getSessionID());

		msg->nextBlockFast(_PREHASH_Data);
		msg->addUUIDFast(_PREHASH_ObjectID, target);		// Must be in the region
		msg->addU32Fast(_PREHASH_ObjectPermissions, (U32) permissions);

		sendReliableMessage();
	}
}

// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i
void LLAgent::setAlwaysRun()
{
	mbAlwaysRun = (!rlv_handler_t::isEnabled()) || (!gRlvHandler.hasBehaviour(RLV_BHVR_ALWAYSRUN));
	sendWalkRun();
}

void LLAgent::setTempRun()
{
	mbTempRun = (!rlv_handler_t::isEnabled()) || (!gRlvHandler.hasBehaviour(RLV_BHVR_TEMPRUN));
	sendWalkRun();
}

void LLAgent::clearAlwaysRun()
{
	mbAlwaysRun = false;
	sendWalkRun();
}

void LLAgent::clearTempRun()
{
	mbTempRun = false;
	sendWalkRun();
}
// [/RLVa:KB]

//void LLAgent::sendWalkRun(bool running)
// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i
void LLAgent::sendWalkRun()
// [/RLVa:KB]
{
	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) );
// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i
		msgsys->addBOOLFast(_PREHASH_AlwaysRun, BOOL(getRunning()) );
// [/RLVa:KB]
		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);
}

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)
{
	
	LLGroupActions::refresh(group_id);
	//*TODO Implement group update for Profile View 
	// still actual as of July 31, 2009 (DZ)

	gAgent.fireEvent(new LLOldEvents::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)
	{
		LL_WARNS() << "processAgentDropGroup for agent other than me" << LL_ENDL;
		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;
	std::vector<LLGroupData>::iterator found_it = std::find(gAgent.mGroups.begin(), gAgent.mGroups.end(), gd);
	if (found_it != gAgent.mGroups.end())
	{
		gAgent.mGroups.erase(found_it);
		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.
		
		// <FS:AO> Don't assume that because we drop a group, we want floaters to change.
		//LLGroupActions::closeGroup(group_id);
	}
	else
	{
		LL_WARNS() << "processAgentDropGroup, agent is not part of group " << group_id << LL_ENDL;
	}
}

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->extendedResult(HTTP_BAD_REQUEST, 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() )
		{
			LL_INFOS() << "VALID DROP GROUP" << LL_ENDL;

			//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)
			{
				LL_WARNS()
					<< "AgentDropGroup for agent other than me" << LL_ENDL;

				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;
			std::vector<LLGroupData>::iterator found_it = std::find(gAgent.mGroups.begin(), gAgent.mGroups.end(), gd);
			if (found_it != gAgent.mGroups.end())
			{
				gAgent.mGroups.erase(found_it);
				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.
				LLGroupActions::closeGroup(group_id);
			}
			else
			{
				LL_WARNS()
					<< "AgentDropGroup, agent is not part of group "
					<< group_id << LL_ENDL;
			}

			response->result(LLSD());
		}
		else
		{
			//what to do with badly formed message?
			response->extendedResult(HTTP_BAD_REQUEST, 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)
	{
		LL_WARNS() << "processAgentGroupDataUpdate for agent other than me" << LL_ENDL;
		return;
	}	
	// <FS:Ansariel> Groupdata debug
	else
	{
		LL_INFOS("Agent_GroupData") << "GROUPDEBUG: Executing deprecated processAgentGroupDataUpdate" << LL_ENDL;
	}
	// </FS:Ansariel>
	
	S32 count = msg->getNumberOfBlocksFast(_PREHASH_GroupData);
	LLGroupData group;
	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.
			std::vector<LLGroupData>::iterator found_it = std::find(gAgent.mGroups.begin(), gAgent.mGroups.end(), group);
			if (found_it != gAgent.mGroups.end())
			{
				gAgent.mGroups.erase(found_it);
			}
			gAgent.mGroups.push_back(group);
		}
		if (need_floater_update)
		{
			update_group_floaters(group.mID);
		}
	}

	// <FS:Ansariel> Fire event for group title overview
	gAgent.fireEvent(new LLOldEvents::LLEvent(&gAgent, "update grouptitle list"), "");
}

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)
		{
			LL_WARNS() << "processAgentGroupDataUpdate for agent other than me" << LL_ENDL;
			return;
		}	
		// <FS:Ansariel> Groupdata debug
		else
		{
			LL_INFOS("Agent_GroupData") << "GROUPDEBUG: Executing processAgentGroupDataUpdate" << LL_ENDL;
		}
		// </FS:Ansariel>

		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;
			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.
				std::vector<LLGroupData>::iterator found_it = std::find(gAgent.mGroups.begin(), gAgent.mGroups.end(), group);
				if (found_it != gAgent.mGroups.end())
				{
					gAgent.mGroups.erase(found_it);
				}
				gAgent.mGroups.push_back(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)
	{
		LL_WARNS() << "processAgentDataUpdate for agent other than me" << LL_ENDL;
		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();
	}
	// <FS> Restore to world
	if (gAgent.restoreToWorld)
	{
		//This fires if we're trying to restore an item to world using the correct group.
		bool remove_from_inventory = false;

		// <FS:Zi> Do not allow "Restore To Last Position" for no-copy items
#ifdef OPENSIM
		if(LLGridManager::instance().isInSecondLife())
		{
#endif
			// protect from restoring to last position when the item is no-copy to prevent
			// inventory loss
			if(!gAgent.restoreToWorldItem->getPermissions().allowCopyBy(gAgent.getID()))
			{
				if(!gSavedSettings.getBOOL("AllowNoCopyRezRestoreToWorld"))
				{
					// for some reason, we still came this far, even though we should not have
					// allowed RezRestoreToWorld on a no copy item on Second Life grids, so
					// log this message and stop proceeding. This will result in the user wearing
					// the land group, but hopefully this problem will not come up ever.
					LL_WARNS("Avatar") << "Tried RezRestoreToWorld on a no-copy item! Attempt blocked." << LL_ENDL;

					// copied from the end of this function to here, to avoid indention mess
					gAgent.restoreToWorld = false;
					update_group_floaters(active_id);
					// <FS:Ansariel> Fire event for group title overview
					gAgent.fireEvent(new LLOldEvents::LLEvent(&gAgent, "update grouptitle list"), "");

					return;
				}
			}
#ifdef OPENSIM
		}
#endif
		// </FS:Zi>

		msg->newMessage("RezRestoreToWorld");
		msg->nextBlockFast(_PREHASH_AgentData);
		msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
		msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());

		msg->nextBlockFast(_PREHASH_InventoryData);
		gAgent.restoreToWorldItem->packMessage(msg);
		msg->sendReliable(gAgent.getRegion()->getHost());
		//remove local inventory copy, sim will deal with permissions and removing the item
		//from the actual inventory if its a no-copy etc
		if(!gAgent.restoreToWorldItem->getPermissions().allowCopyBy(gAgent.getID()))
		{
			remove_from_inventory = true;
		}
		
		// Check if it's in the trash. (again similar to the normal rez logic)
		const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
		if(gInventory.isObjectDescendentOf(gAgent.restoreToWorldItem->getUUID(), trash_id))
		{
			remove_from_inventory = true;
		}
		if(remove_from_inventory)
		{
			gInventory.deleteObject(gAgent.restoreToWorldItem->getUUID());
			gInventory.notifyObservers();
		}

		//Now, restore the old group
		gAgent.restoreToWorld = false;
		msg->newMessageFast(_PREHASH_ActivateGroup);
		msg->nextBlockFast(_PREHASH_AgentData);
		msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
		msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
		msg->addUUIDFast(_PREHASH_GroupID, gAgent.restoreToWorldGroup);
		gAgent.sendReliableMessage();
	}
	// </FS>

	update_group_floaters(active_id);

	// <FS:Ansariel> Fire event for group title overview
	gAgent.fireEvent(new LLOldEvents::LLEvent(&gAgent, "update grouptitle list"), "");
}

// 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++;
				}
			}
		}
		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;
				}
			}
		}
	}
}
*/

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;
}

bool LLAgent::isInHomeRegion()
{
	if(!mHaveHomePosition)
	{
		return false;
	}
	if (!getRegion())
	{
		return false;
	}
	if (getRegion()->getHandle() != mHomeRegionHandle)
	{
		return false;
	}
	return true;
}

void LLAgent::clearVisualParams(void *data)
{
	if (isAgentAvatarValid())
	{
		gAgentAvatarp->clearVisualParamWeights();
		gAgentAvatarp->updateVisualParams();
	}
}

//---------------------------------------------------------------------------
// Teleport
//---------------------------------------------------------------------------
// <FS:TT> Client LSL Bridge
bool LLAgent::teleportBridgeLocal(LLVector3& pos_local)
{
	std::stringstream msgstream;
	msgstream << std::setiosflags(std::ios::fixed) << std::setprecision(6); 
	msgstream << pos_local.mV[VX] << ", " << pos_local.mV[VY] << ", "  << pos_local.mV[VZ];

	// Check for FIRE-10718: llGetSimulatorHostname() is causing LSL Bridge to sleep for 10 seconds during check for current hostname in order to prevent double-click teleport on SL grid, so let's check this in the viewer itself and send 1/0 as additional value
#ifdef OPENSIM
	const std::string isLindenLabHost = LLGridManager::getInstance()->isInSecondLife() ? "1" : "0";
#else
	const std::string isLindenLabHost = "1";
#endif

	return FSLSLBridge::instance().viewerToLSL("llMoveToTarget|" + msgstream.str() + "|" + isLindenLabHost);
}

bool LLAgent::teleportBridgeGlobal(const LLVector3d& pos_global)
{
	// <FS:Beq> FIRE-30534 VarRegion fixes
	// U64 region_handle = to_region_handle(pos_global);
	U64 region_handle{};
	auto regionp = getRegion();
	if(regionp)
	{
		region_handle = to_region_handle(pos_global, regionp->getOriginGlobal(), regionp->getWidth());
	}
	else
	{
		region_handle = to_region_handle(pos_global);
	}
	// </FS:Beq>
	LLVector3 pos_local = (LLVector3)(pos_global - from_region_handle(region_handle));

	return teleportBridgeLocal(pos_local);
}
// </FS:TT> Client LSL Bridge 

// teleportCore() - stuff to do on any teleport
// protected
bool LLAgent::teleportCore(bool is_local)
{
    LL_DEBUGS("Teleport") << "In teleport core" << LL_ENDL;
	if ((TELEPORT_NONE != mTeleportState) && (mTeleportState != TELEPORT_PENDING))
	{
		LL_WARNS() << "Attempt to teleport when already teleporting." << LL_ENDL;
		//return false; //LO - yea, lets not return here, we may be stuck in TP and if we are, letting this go through will get us out;
	}

	// force stand up and stop a sitting animation (if any), see MAINT-3969
	if (isAgentAvatarValid() && gAgentAvatarp->getParent() && gAgentAvatarp->isSitting())
	{
		gAgentAvatarp->getOffObject();
	}

#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())
	{
		for ( LLVOAvatar::AnimIterator anim_it= gAgentAvatarp->mPlayingAnimations.begin();
		      anim_it != gAgentAvatarp->mPlayingAnimations.end();
		      ++anim_it)
               {
                       gAgentAvatarp->stopMotion(anim_it->first);
               }
               gAgentAvatarp->processAnimationStateChanges();
       }
#endif

	// Don't call LLFirstUse::useTeleport because we don't know
	// yet if the teleport will succeed.  Look in 
	// process_teleport_location_reply

	// <FS:Ansariel> FIRE-17779: Option to not close world map on teleport
	if (!gSavedSettings.getBOOL("FSDoNotHideMapOnTeleport"))
		LLFloaterReg::hideInstance("world_map");

	// hide land floater too - it'll be out of date
	LLFloaterReg::hideInstance("about_land");

	// hide the Region/Estate floater
	LLFloaterReg::hideInstance("region_info");

	LLViewerParcelMgr::getInstance()->deselectLand();
	LLViewerMediaFocus::getInstance()->clearFocus();

	// Close all pie menus, deselect land, etc.
	// Don't change the camera until we know teleport succeeded. JC
	gAgentCamera.resetView(FALSE);

	// local logic
	add(LLStatViewer::TELEPORT, 1);
	if (is_local)
	{
		LL_INFOS("Teleport") << "Setting teleport state to TELEPORT_LOCAL" << LL_ENDL;
		gAgent.setTeleportState( LLAgent::TELEPORT_LOCAL );
	}
	else
	{
		gTeleportDisplay = TRUE;
		LL_INFOS("Teleport") << "Non-local, setting teleport state to TELEPORT_START" << LL_ENDL;
		gAgent.setTeleportState( LLAgent::TELEPORT_START );

		//release geometry from old location
		gPipeline.resetVertexBuffers();
		LLSpatialPartition::sTeleportRequested = TRUE;
		
		// <FS:Ansariel> Draw Distance stepping; originally based on SpeedRez by Henri Beauchamp, licensed under LGPL
		if (gSavedSettings.getBOOL("FSRenderFarClipStepping"))
		{
			LLPresetsManager::instance().setIsDrawDistanceSteppingActive(true);
			F32 draw_distance = gSavedSettings.getF32("RenderFarClip");
			if (gSavedDrawDistance < draw_distance)
			{
				gSavedDrawDistance = draw_distance;
			}
			gSavedSettings.setF32("FSSavedRenderFarClip", gSavedDrawDistance);
			gSavedSettings.setF32("RenderFarClip", 32.0f);
			gLastDrawDistanceStep = 32.0f;
		}
		// </FS:Ansariel>
	}
	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.
//	LLVoiceClient::getInstance()->leaveChannel();
	
	gObjectList.resetDerenderList();

	return true;
}

bool LLAgent::hasRestartableFailedTeleportRequest()
{
	return ((mTeleportRequest != NULL) && (mTeleportRequest->getStatus() == LLTeleportRequest::kFailed) &&
		mTeleportRequest->canRestartTeleport());
}

void LLAgent::restartFailedTeleportRequest()
{
    LL_INFOS("Teleport") << "Agent wishes to restart failed teleport." << LL_ENDL;
	if (hasRestartableFailedTeleportRequest())
	{
		mTeleportRequest->setStatus(LLTeleportRequest::kRestartPending);
		startTeleportRequest();
	}
}

void LLAgent::clearTeleportRequest()
{
    if(LLVoiceClient::instanceExists())
    {
        LLVoiceClient::getInstance()->setHidden(FALSE);
    }
	mTeleportRequest.reset();
    mTPNeedsNeabyChatSeparator = false;
}

void LLAgent::setMaturityRatingChangeDuringTeleport(U8 pMaturityRatingChange)
{
	mIsMaturityRatingChangingDuringTeleport = true;
	mMaturityRatingChange = pMaturityRatingChange;
}

void LLAgent::sheduleTeleportIM()
{
    // is supposed to be called during teleport so we are still waiting for parcel
    mTPNeedsNeabyChatSeparator = true;
}

bool LLAgent::hasPendingTeleportRequest()
{
	return ((mTeleportRequest != NULL) &&
		((mTeleportRequest->getStatus() == LLTeleportRequest::kPending) ||
		(mTeleportRequest->getStatus() == LLTeleportRequest::kRestartPending)));
}

void LLAgent::startTeleportRequest()
{
    LL_INFOS("Teleport") << "Agent handling start teleport request." << LL_ENDL; // <FS:Beq> fix mistyped tag
    if(LLVoiceClient::instanceExists())
    {
        LLVoiceClient::getInstance()->setHidden(TRUE);
    }
	if (hasPendingTeleportRequest())
	{
        mTeleportCanceled.reset();
		if  (!isMaturityPreferenceSyncedWithServer())
		{
			gTeleportDisplay = TRUE;
			LL_INFOS("Teleport") << "Maturity preference not synced yet, setting teleport state to TELEPORT_PENDING" << LL_ENDL;
			setTeleportState(TELEPORT_PENDING);
		}
		else
		{
			switch (mTeleportRequest->getStatus())
			{
			case LLTeleportRequest::kPending :
				mTeleportRequest->setStatus(LLTeleportRequest::kStarted);
				mTeleportRequest->startTeleport();
				break;
			case LLTeleportRequest::kRestartPending :
				llassert(mTeleportRequest->canRestartTeleport());
				mTeleportRequest->setStatus(LLTeleportRequest::kStarted);
				mTeleportRequest->restartTeleport();
				break;
			default :
				llassert(0);
				break;
			}
		}
	}
}

void LLAgent::handleTeleportFinished()
{
    LL_INFOS("Teleport") << "Agent handling teleport finished." << LL_ENDL;
    if (mTPNeedsNeabyChatSeparator)
    {
        // parcel is ready at this point
        addTPNearbyChatSeparator();
        mTPNeedsNeabyChatSeparator = false;
    }
	clearTeleportRequest();
    mTeleportCanceled.reset();
	if (mIsMaturityRatingChangingDuringTeleport)
	{
		// notify user that the maturity preference has been changed
		std::string maturityRating = LLViewerRegion::accessToString(mMaturityRatingChange);
		LLStringUtil::toLower(maturityRating);
		LLSD args;
		args["RATING"] = maturityRating;
		LLNotificationsUtil::add("PreferredMaturityChanged", args);
		mIsMaturityRatingChangingDuringTeleport = false;
	}
    
    if (mRegionp)
    {
        if (mRegionp->capabilitiesReceived())
        {
			LL_DEBUGS("Teleport") << "capabilities have been received for region handle "
								  << mRegionp->getHandle()
								  << " id " << mRegionp->getRegionID()
								  << ", calling onCapabilitiesReceivedAfterTeleport()"
								  << LL_ENDL;
            onCapabilitiesReceivedAfterTeleport();
        }
        else
        {
			LL_DEBUGS("Teleport") << "Capabilities not yet received for region handle "
								  << mRegionp->getHandle()
								  << " id " << mRegionp->getRegionID()
								  << LL_ENDL;
            mRegionp->setCapabilitiesReceivedCallback(boost::bind(&LLAgent::onCapabilitiesReceivedAfterTeleport));
        }
    }
}

void LLAgent::handleTeleportFailed()
{
    LL_WARNS("Teleport") << "Agent handling teleport failure!" << LL_ENDL;
    if(LLVoiceClient::instanceExists())
    {
        LLVoiceClient::getInstance()->setHidden(FALSE);
    }

    setTeleportState(LLAgent::TELEPORT_NONE);
    // Unlock the UI if the progress bar has been shown.
//     gViewerWindow->setShowProgress(FALSE);
//     gTeleportDisplay = FALSE;

    if (mTeleportRequest)
	{
		mTeleportRequest->setStatus(LLTeleportRequest::kFailed);
	}
	if (mIsMaturityRatingChangingDuringTeleport)
	{
		// notify user that the maturity preference has been changed
		std::string maturityRating = LLViewerRegion::accessToString(mMaturityRatingChange);
		LLStringUtil::toLower(maturityRating);
		LLSD args;
		args["RATING"] = maturityRating;
		LLNotificationsUtil::add("PreferredMaturityChanged", args);
		mIsMaturityRatingChangingDuringTeleport = false;
	}

    mTPNeedsNeabyChatSeparator = false;
}

/*static*/
void LLAgent::addTPNearbyChatSeparator()
{
    LLViewerRegion* agent_region = gAgent.getRegion();
    LLParcel* agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
    if (!agent_region || !agent_parcel)
    {
        return;
    }

    // <FS:Ansariel> [FS communication UI]
    //LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat");
    FSFloaterNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<FSFloaterNearbyChat>("fs_nearby_chat");
    // </FS:Ansariel> [FS communication UI]
    if (nearby_chat)
    {
        std::string location_name;
        LLAgentUI::ELocationFormat format = LLAgentUI::LOCATION_FORMAT_NO_MATURITY;

        // Might be better to provide slurl to chat
        if (!LLAgentUI::buildLocationString(location_name, format))
        {
            location_name = "Teleport to new region"; // Shouldn't happen
        }

        LLChat chat;
        chat.mFromName = location_name;
        chat.mMuted = FALSE;
        chat.mFromID = LLUUID::null;
        chat.mSourceType = CHAT_SOURCE_TELEPORT;
        chat.mChatStyle = CHAT_STYLE_TELEPORT_SEP;
        chat.mText = "";

        LLSD args;
        args["do_not_log"] = TRUE;
        nearby_chat->addMessage(chat, true, args);
    }
}

/*static*/
void LLAgent::onCapabilitiesReceivedAfterTeleport()
{
	if (gAgent.getRegion())
	{
		LL_DEBUGS("Teleport") << "running after capabilities received callback has been triggered, agent region "
							  << gAgent.getRegion()->getHandle()
							  << " id " << gAgent.getRegion()->getRegionID()
							  << " name " << gAgent.getRegion()->getName()
							  << LL_ENDL;
	}
	else
	{
		LL_WARNS("Teleport") << "called when agent region is null!" << LL_ENDL;
	}

    check_merchant_status();
}


//void LLAgent::teleportRequest(
//	const U64& region_handle,
//	const LLVector3& pos_local,
//	bool look_at_from_camera)
// [RLVa:KB] - Checked: RLVa-2.0.0
void LLAgent::teleportRequest(const U64& region_handle, const LLVector3& pos_local, const LLVector3& look_at)
// [/RLVa:KB]
{
	LLViewerRegion* regionp = getRegion();
	if (regionp && teleportCore(region_handle == regionp->getHandle()))
	{
		LL_INFOS("Teleport") << "Sending TeleportLocationRequest: '" << region_handle << "':"
							 << pos_local << LL_ENDL;
		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);
//		LLVector3 look_at(0,1,0);
//		if (look_at_from_camera)
//		{
//			look_at = LLViewerCamera::getInstance()->getAtAxis();
//		}
		msg->addVector3("LookAt", look_at);
		sendReliableMessage();
	}
}

// Landmark ID = LLUUID::null means teleport home
void LLAgent::teleportViaLandmark(const LLUUID& landmark_asset_id)
{
	// <FS:Ansariel> FIRE-21576: Prevent TPing home while still logging in if RLVa is enabled
	if (RlvActions::isRlvEnabled() && LLStartUp::getStartupState() < STATE_STARTED)
	{
		return;
	}
	// </FS:Ansariel>

// [RLVa:KB] - Checked: 2010-08-22 (RLVa-1.2.1a) | Modified: RLVa-1.2.1a
	// NOTE: we'll allow teleporting home unless both @tplm=n *and* @tploc=n restricted
	if ( (rlv_handler_t::isEnabled()) &&
		 ( ( (landmark_asset_id.notNull()) ? gRlvHandler.hasBehaviour(RLV_BHVR_TPLM)
		                                   : gRlvHandler.hasBehaviour(RLV_BHVR_TPLM) && gRlvHandler.hasBehaviour(RLV_BHVR_TPLOC) ) ||
		   ((gRlvHandler.hasBehaviour(RLV_BHVR_UNSIT)) && (isAgentAvatarValid()) && (gAgentAvatarp->isSitting())) ))
	{
		RlvUtil::notifyBlocked(RlvStringKeys::Blocked::Teleport);
		return;
	}
// [/RLVa:KB]

	// <FS:Ansariel> FIRE-7273: Typing does not stop after using Cmd Line tph
	if (landmark_asset_id.isNull())
	{
		// This might mean a local TP, so pro-actively stop typing
		gAgent.stopTyping();
	}
	// </FS:Ansariel>

	mTeleportRequest = LLTeleportRequestPtr(new LLTeleportRequestViaLandmark(landmark_asset_id));
	startTeleportRequest();
}

void LLAgent::doTeleportViaLandmark(const LLUUID& landmark_asset_id)
{
	LLViewerRegion *regionp = getRegion();
	if(regionp && teleportCore())
	{
		LL_INFOS("Teleport") << "Sending TeleportLandmarkRequest. Current region handle " << regionp->getHandle()
							 << " region id " << regionp->getRegionID()
							 << " requested landmark id " << landmark_asset_id
							 << LL_ENDL;

		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)
{
	mTeleportRequest = LLTeleportRequestPtr(new LLTeleportRequestViaLure(lure_id, godlike));
	startTeleportRequest();
}

void LLAgent::doTeleportViaLure(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;
		}

		LL_INFOS("Teleport") << "Sending TeleportLureRequest."
							 << " Current region handle " << regionp->getHandle()
							 << " region id " << regionp->getRegionID()
							 << " lure id " << lure_id
							 << LL_ENDL;
		// 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()
{
	if (!hasPendingTeleportRequest())
	{
		LLViewerRegion* regionp = getRegion();
		if(regionp)
		{
			LL_INFOS("Teleport") << "Sending TeleportCancel" << LL_ENDL;
			
			// send the message
			LLMessageSystem* msg = gMessageSystem;
			msg->newMessage("TeleportCancel");
			msg->nextBlockFast(_PREHASH_Info);
			msg->addUUIDFast(_PREHASH_AgentID, getID());
			msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
			sendReliableMessage();
		}
		mTeleportCanceled = mTeleportRequest;
	}
	clearTeleportRequest();
	gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
	gPipeline.resetVertexBuffers(); 
}

void LLAgent::restoreCanceledTeleportRequest()
{
    if (mTeleportCanceled != NULL)
    {
		LL_INFOS() << "Restoring canceled teleport request, setting state to TELEPORT_REQUESTED" << LL_ENDL;
        gAgent.setTeleportState( LLAgent::TELEPORT_REQUESTED );
        mTeleportRequest = mTeleportCanceled;
        mTeleportCanceled.reset();
        gTeleportDisplay = TRUE;
        gTeleportDisplayTimer.reset();
    }
}

void LLAgent::teleportViaLocation(const LLVector3d& pos_global)
{
// [RLVa:KB] - Checked: RLVa-2.0.0
	if ( (RlvActions::isRlvEnabled()) && (!RlvUtil::isForceTp()) )
	{
		if (LLStartUp::getStartupState() < STATE_STARTED)
		{
			return;
		}

		if ( (RlvActions::isLocalTp(pos_global)) ? !RlvActions::canTeleportToLocal(pos_global) : !RlvActions::canTeleportToLocation() )
		{
			RlvUtil::notifyBlocked(RlvStringKeys::Blocked::Teleport);
			return;
		}

		if ( (gRlvHandler.getCurrentCommand()) && (RLV_BHVR_TPTO == gRlvHandler.getCurrentCommand()->getBehaviourType()) )
		{
			gRlvHandler.setCanCancelTp(false);
		}
	}
// [/RLVa:KB]

	mTeleportRequest = LLTeleportRequestPtr(new LLTeleportRequestViaLocation(pos_global));
	startTeleportRequest();
}

void LLAgent::doTeleportViaLocation(const LLVector3d& pos_global)
{
	LLViewerRegion* regionp = getRegion();

	if (!regionp)
	{
		return;
	}
	// <FS:Beq> FIRE-30534 Var Region tp / map locaton fixes
// 	U64 handle = to_region_handle(pos_global);
// 	bool isLocal = (regionp->getHandle() == to_region_handle_global((F32)pos_global.mdV[VX], (F32)pos_global.mdV[VY]));
// 	LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle);
// 	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]));
// // <FS:CR> Aurora-sim var region teleports
// 		//teleportRequest(handle, pos_local);
// 		teleportRequest(info->getHandle(), pos_local);
// // </FS:CR>
// 	}
// 	else if(regionp && teleportCore(isLocal))
// 	{
// 		// 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);
// 		// <FS:Ansariel> FIRE-17262: Wrong local teleports on a large opensim region (apparently need to divide by grid unit size)
// 		F32 width = REGION_WIDTH_METERS;// 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;
// 		msg->addVector3Fast(_PREHASH_LookAt, pos);

// 		LL_WARNS("Teleport") << "Sending deprecated(?) TeleportLocationRequest."
// 							 << " pos_global " << pos_global
// 							 << " region_x " << region_x
// 							 << " region_y " << region_y
// 							 << " region_handle " << region_handle
// 							 << LL_ENDL; 

// 		sendReliableMessage();
	auto region_origin { regionp->getOriginGlobal() };
	LLVector3 pos_local{};
	U64 handle { to_region_handle(pos_global, region_origin, regionp->getWidth()) };
	bool is_local { regionp->getHandle() == handle };
	if(is_local)
	{
		pos_local.set(
			(F32)(pos_global.mdV[VX] - region_origin.mdV[VX]),
			(F32)(pos_global.mdV[VY] - region_origin.mdV[VY]),
			(F32)(pos_global.mdV[VZ]));
		LL_INFOS("Teleport") << "Local in-region TP:"
							 << " pos_global " << pos_global
							 << " region " << region_origin
							 << " local " << pos_local
							 << " region_handle " << handle
							 << LL_ENDL;
	}
	else
	{
		// determine non-local region location as best we can using global coords
		// In SL we have uniform region size. This is normal.
		// In opensim the handle will resolve to a 256m quantised world tile which the server maps back to a region
		// it "should" also compensate for the local coords. Handle has been "correctly" determined already so we use global % 256
		static const F32 width = REGION_WIDTH_METERS;// Note: reverted back to previous hardcode 256 for non-local. Whilst this appears incorrect server side logic expects %256 and will overshoot otherwise.
		pos_local.set( fmod((F32)pos_global.mdV[VX], width),
					   fmod((F32)pos_global.mdV[VY], width),
					   (F32)pos_global.mdV[VZ] );
		LL_INFOS("Teleport") << "Non-local TP:"
							 << " pos_global " << pos_global
							 << " region " << region_origin
							 << " local " << pos_local
							 << " region_handle " << handle
							 << LL_ENDL;
	}

	if(teleportCore(is_local)) // Rather a pointless if as teleportCore currently always returns true
	{
		// 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);
		msg->addU64Fast(_PREHASH_RegionHandle, handle);
		msg->addVector3Fast(_PREHASH_Position, pos_local);
		pos_local.mV[VX] += 1;
		msg->addVector3Fast(_PREHASH_LookAt, pos_local);

		sendReliableMessage();
		LL_INFOS("Teleport") << "Sending deprecated TeleportLocationRequest."
							 << " pos_global " << pos_global
							 << " region coord (" << (pos_global.mdV[VX] - pos_local.mV[VX])
							 << "," << (pos_global.mdV[VY] - pos_local.mV[VY])
							 << " pos_local " << pos_local
							 << ") region_handle " << handle
							 << LL_ENDL; 
	// </FS:Beq>
	}
// <FS:TT> Client LSL Bridge
	if (FSLSLBridge::instance().canUseBridge() && is_local)
	{
		teleportBridgeGlobal(pos_global);
	}
// </FS:TT>
}

// Teleport to global position, but keep facing in the same direction 
// [RLVa:KB] - Checked: RLVa-2.0.0
void LLAgent::teleportViaLocationLookAt(const LLVector3d& pos_global, const LLVector3& look_at)
{
	if ( (RlvActions::isRlvEnabled()) && (!RlvUtil::isForceTp()) )
	{
		if (LLStartUp::getStartupState() < STATE_STARTED)
		{
			return;
		}

		if ( (RlvActions::isLocalTp(pos_global)) ? !RlvActions::canTeleportToLocal(pos_global) : !RlvActions::canTeleportToLocation() )
		{
			RlvUtil::notifyBlocked(RlvStringKeys::Blocked::Teleport);
			return;
		}

		if ( (gRlvHandler.getCurrentCommand()) && (RLV_BHVR_TPTO == gRlvHandler.getCurrentCommand()->getBehaviourType()) )
		{
			gRlvHandler.setCanCancelTp(false);
		}
	}

	mTeleportRequest = LLTeleportRequestPtr(new LLTeleportRequestViaLocationLookAt(pos_global, (look_at.isExactlyZero()) ? LLViewerCamera::getInstance()->getAtAxis() : look_at));
	startTeleportRequest();
}
// [/RLVa:KB]
//void LLAgent::teleportViaLocationLookAt(const LLVector3d& pos_global)
//{
//	mTeleportRequest = LLTeleportRequestPtr(new LLTeleportRequestViaLocationLookAt(pos_global));
//	startTeleportRequest();
//}

// [RLVa:KB] - Checked: RLVa-2.0.0
// <FS:Beq> FIRE-30534 VarRegion TP fixes
// void LLAgent::doTeleportViaLocationLookAt(const LLVector3d& pos_global, const LLVector3& look_at)
// {
// 	mbTeleportKeepsLookAt = look_at.isExactlyZero();

// 	if(!gAgentCamera.isfollowCamLocked())
// 	{
// 		gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE);	// detach camera form avatar, so it keeps direction
// 	}

// 	U64 region_handle = to_region_handle(pos_global);
// // <FS:CR> Aurora-sim var region teleports
// 	LLSimInfo* simInfo = LLWorldMap::instance().simInfoFromHandle(region_handle);
// 	if (simInfo)
// 	{
// 		region_handle = simInfo->getHandle();
// 	}
// // </FS:CR>
// 	LLVector3 pos_local = (LLVector3)(pos_global - from_region_handle(region_handle));
// 	teleportRequest(region_handle, pos_local, look_at);

// // <FS:TT> Client LSL Bridge
// 	if (FSLSLBridge::instance().canUseBridge())
// 	{
// 		if (region_handle == to_region_handle(getPositionGlobal()))
// 		{
// 			teleportBridgeLocal(pos_local);
// 		}
// 	}
// // </FS:TT>
// }
void LLAgent::doTeleportViaLocationLookAt(const LLVector3d& pos_global, const LLVector3& look_at)
{
	mbTeleportKeepsLookAt = look_at.isExactlyZero();

	if(!gAgentCamera.isfollowCamLocked())
	{
		gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE);	// detach camera form avatar, so it keeps direction
	}

	U64 region_handle{};
	auto regionp = getRegion();
	if(regionp)
	{
		region_handle = to_region_handle(pos_global, regionp->getOriginGlobal(), regionp->getWidth());
	}
	else
	{
		region_handle = to_region_handle(pos_global);
	}
// Beq Note: if region_handle was obtained to the nearest 256m tile map lookup might give us a better result.

	LLVector3 pos_within_target_region = (LLVector3)(pos_global - from_region_handle(region_handle));
	teleportRequest(region_handle, pos_within_target_region, look_at);

// <FS:TT> Client LSL Bridge
	if ( FSLSLBridge::instance().canUseBridge() )
	{
		// refresh regionp 
		regionp = getRegion();
		if( regionp && ( region_handle == regionp->getHandle() ) ) 
		{
			teleportBridgeLocal(pos_within_target_region);
		}
	}
// </FS:TT>
}
// </FS:Beq>
LLAgent::ETeleportState	LLAgent::getTeleportState() const
{
    return (mTeleportRequest && (mTeleportRequest->getStatus() == LLTeleportRequest::kFailed)) ? 
        TELEPORT_NONE : mTeleportState;
}
// [/RLVa:KB]
//void LLAgent::doTeleportViaLocationLookAt(const LLVector3d& pos_global, const LLVector3& look_at)
//{
//	mbTeleportKeepsLookAt = true;
//
//	if(!gAgentCamera.isfollowCamLocked())
//	{
//		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, look_at);
//	teleportRequest(region_handle, pos_local, getTeleportKeepsLookAt());
//}


void LLAgent::setTeleportState(ETeleportState state)
{
    if (mTeleportRequest && (state != TELEPORT_NONE) && (mTeleportRequest->getStatus() == LLTeleportRequest::kFailed))
    {   // A late message has come in regarding a failed teleport.  
        // We have already decided that it failed so should not reinitiate the teleport sequence in the viewer.
        LL_WARNS("Teleport") << "Attempt to set teleport state to " << state <<
            " for previously failed teleport.  Ignore!" << LL_ENDL;
        return;
    }
    LL_DEBUGS("Teleport") << "Setting teleport state to "
						  << LLAgent::teleportStateName(state) << "(" << state << ")"
						  << " Previous state: "
						  << teleportStateName(mTeleportState) << "(" << mTeleportState << ")"
						  << LL_ENDL;
	mTeleportState = state;
	if (mTeleportState > TELEPORT_NONE && gSavedSettings.getBOOL("FreezeTime"))
	{
		LLFloaterReg::hideInstance("snapshot");
	}

	switch (mTeleportState)
	{
		case TELEPORT_NONE:
			mbTeleportKeepsLookAt = false;
			// <FS:Ansariel> FIRE-12004: Attachments getting lost on TP; always reset region crossing state after a finished TP
			if (isAgentAvatarValid())
			{
				gAgentAvatarp->setIsCrossingRegion(false);
			}
			// </FS:Ansariel>
			break;

		case TELEPORT_MOVING:
		// We're outa here. Save "back" slurl.
		LLAgentUI::buildSLURL(*mTeleportSourceSLURL);
			break;

		case TELEPORT_ARRIVING:
		// First two position updates after a teleport tend to be weird
		//LLViewerStats::getInstance()->mAgentPositionSnaps.mCountOfNextUpdatesToIgnore = 2;

		// Let the interested parties know we've teleported.
		LLViewerParcelMgr::getInstance()->onTeleportFinished(false, getPositionGlobal());
			break;

		default:
			break;
	}
}

// <FS:Ansariel> FIRE-12148: Pose stand breaks XPOSE animations
//void LLAgent::stopCurrentAnimations()
void LLAgent::stopCurrentAnimations(bool force_keep_script_perms /*= false*/)
// </FS:Ansariel>
{
    LL_DEBUGS("Avatar") << "Stopping current animations" << LL_ENDL;

	// This function stops all current overriding animations on this
	// avatar, propagating this change back to the server.
	if (isAgentAvatarValid())
	{
		std::vector<LLUUID> anim_ids;

		// <FS:Zi> assume we need to restore the default standing animation
		bool restore_stand_animation = true;

		for ( LLVOAvatar::AnimIterator anim_it =
			      gAgentAvatarp->mPlayingAnimations.begin();
		      anim_it != gAgentAvatarp->mPlayingAnimations.end();
		      anim_it++)
		{
			// <FS:Zi> don't restore stand animation when ground sitting because it is not needed
			// and an AO would not play a standing animation on top of the ground sit
			if (anim_it->first == ANIM_AGENT_SIT_GROUND_CONSTRAINED)
			{
				restore_stand_animation = false;
			}
			// </FS:Zi>

			if ((anim_it->first == ANIM_AGENT_DO_NOT_DISTURB)||
				(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.
                LL_DEBUGS("Avatar") << "sit or do-not-disturb animation will not be stopped" << LL_ENDL;
			}
			else
			{
				// stop this animation locally
				gAgentAvatarp->stopMotion(anim_it->first, TRUE);
				// ...and tell the server to tell everyone.
				anim_ids.push_back(anim_it->first);
			}
		}

		sendAnimationRequests(anim_ids, ANIM_REQUEST_STOP);

		// Tell the region to clear any animation state overrides
		sendAnimationStateReset();

		// Revoke all animation permissions
		if (mRegionp &&
			// <FS:Ansariel> FIRE-12148: Pose stand breaks XPOSE animations
			!force_keep_script_perms &&
			// </FS:Ansariel>
			gSavedSettings.getBOOL("RevokePermsOnStopAnimation"))
		{
			U32 permissions = SCRIPT_PERMISSIONS[SCRIPT_PERMISSION_TRIGGER_ANIMATION].permbit | SCRIPT_PERMISSIONS[SCRIPT_PERMISSION_OVERRIDE_ANIMATIONS].permbit;
			sendRevokePermissions(mRegionp->getRegionID(), permissions);
			if (gAgentAvatarp->isSitting())
			{	// Also stand up, since auto-granted sit animation permission has been revoked
				gAgent.standUp();
			}
		}

		// re-assert at least the default standing animation, because
		// viewers get confused by avs with no associated anims.

		// <FS:Zi> Only restore stand animation when the avatar was not sitting on ground
		// sendAnimationRequest(ANIM_AGENT_STAND, ANIM_REQUEST_START);
		if (restore_stand_animation)
		{
			sendAnimationRequest(ANIM_AGENT_STAND, ANIM_REQUEST_START);
		}
		// </FS:Zi>

		// <FS:Zi> Run Prio 0 default bento pose in the background to fix splayed hands, open mouths, etc.
		if (gSavedSettings.getBOOL("FSPlayDefaultBentoAnimation"))
		{
			sendAnimationRequest(ANIM_AGENT_BENTO_IDLE, ANIM_REQUEST_START);
		}
		// </FS:Zi>
	}
}

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();

				
				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()
{
	std::vector<LLUUID> anims;
	anims.reserve(4);
	anims.push_back(ANIM_AGENT_STAND_1);
	anims.push_back(ANIM_AGENT_STAND_2);
	anims.push_back(ANIM_AGENT_STAND_3);
	anims.push_back(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();
}

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::sendAgentUserInfoRequest()
{
    std::string cap;

    if (getID().isNull())
        return; // not logged in

    if (mRegionp)
        cap = mRegionp->getCapability("UserInfo");

    if (!cap.empty())
    {
        LLCoros::instance().launch("requestAgentUserInfoCoro",
            boost::bind(&LLAgent::requestAgentUserInfoCoro, this, cap));
    }
    else
    { 
        sendAgentUserInfoRequestMessage();
    }
}

void LLAgent::requestAgentUserInfoCoro(std::string capurl)
{
    LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
        httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("requestAgentUserInfoCoro", httpPolicy));
    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
    LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
    LLCore::HttpHeaders::ptr_t httpHeaders;

    httpOpts->setFollowRedirects(true);

    LLSD result = httpAdapter->getAndSuspend(httpRequest, capurl, httpOpts, httpHeaders);

    LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
    LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);

    if (!status)
    {
        LL_WARNS("UserInfo") << "Failed to get user information." << LL_ENDL;
        return;
    }
    else if (!result["success"].asBoolean())
    {
        LL_WARNS("UserInfo") << "Failed to get user information: " << result["message"] << LL_ENDL;
        return;
    }

    std::string email;
    std::string dir_visibility;

    // <FS:Ansariel> Keep this for OpenSim
    bool im_via_email = false;
    if (!LLGridManager::instance().isInSecondLife())
    {
        im_via_email = result["im_via_email"].asBoolean();
    }
    // </FS:Ansariel>
    email = result["email"].asString();
    dir_visibility = result["directory_visibility"].asString();

    // TODO: This should probably be changed.  I'm not entirely comfortable 
    // having LLAgent interact directly with the UI in this way.
    // <FS:Ansariel> Show email address in preferences (FIRE-1071) and keep IM to email setting for OpenSim
    //LLFloaterPreference::updateUserInfo(dir_visibility);
    LLFloaterPreference::updateUserInfo(dir_visibility, im_via_email, email);
    // </FS:Ansariel>
    LLFloaterSnapshot::setAgentEmail(email);
}

// <FS:Ansariel> Keep this for OpenSim
//void LLAgent::sendAgentUpdateUserInfo(const std::string& directory_visibility)
void LLAgent::sendAgentUpdateUserInfo(bool im_via_email, const std::string& directory_visibility){
    std::string cap;

    if (getID().isNull())
        return; // not logged in

    if (mRegionp)
        cap = mRegionp->getCapability("UserInfo");

    if (!cap.empty())
    {
        LLCoros::instance().launch("updateAgentUserInfoCoro",
            // <FS:Ansariel> Keep this for OpenSim
            //boost::bind(&LLAgent::updateAgentUserInfoCoro, this, cap, directory_visibility));
            boost::bind(&LLAgent::updateAgentUserInfoCoro, this, cap, im_via_email, directory_visibility));
    }
    else
    {
        // <FS:Ansariel> Keep this for OpenSim
        //sendAgentUpdateUserInfoMessage(directory_visibility);
        sendAgentUpdateUserInfoMessage(im_via_email, directory_visibility);
    }
}


// <FS:Ansariel> Keep this for OpenSim
//void LLAgent::updateAgentUserInfoCoro(std::string capurl, std::string directory_visibility)
void LLAgent::updateAgentUserInfoCoro(std::string capurl, bool im_via_email, std::string directory_visibility)
{
    LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
        httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("requestAgentUserInfoCoro", httpPolicy));
    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
    LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
    LLCore::HttpHeaders::ptr_t httpHeaders;

    httpOpts->setFollowRedirects(true);
    LLSD body(LLSDMap
        ("dir_visibility",  LLSD::String(directory_visibility)));

    // <FS:Ansariel> Keep this for OpenSim
    if (!LLGridManager::instance().isInSecondLife())
        body.insert("im_via_email", LLSD::Boolean(im_via_email));

    LLSD result = httpAdapter->postAndSuspend(httpRequest, capurl, body, httpOpts, httpHeaders);

    LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
    LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);

    if (!status)
    {
        LL_WARNS("UserInfo") << "Failed to set user information." << LL_ENDL;
    }
    else if (!result["success"].asBoolean())
    {
        LL_WARNS("UserInfo") << "Failed to set user information: " << result["message"] << LL_ENDL;
    }
}

// deprecated:
// May be removed when UserInfo cap propagates to all simhosts in grid
void LLAgent::sendAgentUserInfoRequestMessage()
{
    gMessageSystem->newMessageFast(_PREHASH_UserInfoRequest);
    gMessageSystem->nextBlockFast(_PREHASH_AgentData);
    gMessageSystem->addUUIDFast(_PREHASH_AgentID, getID());
    gMessageSystem->addUUIDFast(_PREHASH_SessionID, getSessionID());
    sendReliableMessage();
}

// <FS:Ansariel> Keep this for OpenSim
//void LLAgent::sendAgentUpdateUserInfoMessage(const std::string& directory_visibility)
void LLAgent::sendAgentUpdateUserInfoMessage(bool im_via_email, const std::string& directory_visibility)
{
    gMessageSystem->newMessageFast(_PREHASH_UpdateUserInfo);
    gMessageSystem->nextBlockFast(_PREHASH_AgentData);
    gMessageSystem->addUUIDFast(_PREHASH_AgentID, getID());
    gMessageSystem->addUUIDFast(_PREHASH_SessionID, getSessionID());
    gMessageSystem->nextBlockFast(_PREHASH_UserData);
    // <FS:Ansariel> Keep this for OpenSim
    if (!LLGridManager::instance().isInSecondLife())
        gMessageSystem->addBOOLFast(_PREHASH_IMViaEMail, im_via_email);
    // </FS:Ansariel>
    gMessageSystem->addString("DirectoryVisibility", directory_visibility);
    gAgent.sendReliableMessage();

}
// end deprecated
//------

void LLAgent::observeFriends()
{
	if(!mFriendObserver)
	{
		mFriendObserver = new LLAgentFriendObserver;
		LLAvatarTracker::instance().addObserver(mFriendObserver);
		friendsChanged();
	}
}

std::map<S32, std::string> LLAgent::sTeleportStateName = { { TELEPORT_NONE, "TELEPORT_NONE" },
														   { TELEPORT_START, "TELEPORT_START" },
														   { TELEPORT_REQUESTED, "TELEPORT_REQUESTED" },
														   { TELEPORT_MOVING, "TELEPORT_MOVING" },
														   { TELEPORT_START_ARRIVAL, "TELEPORT_START_ARRIVAL" },
														   { TELEPORT_ARRIVING, "TELEPORT_ARRIVING" },
														   { TELEPORT_LOCAL, "TELEPORT_LOCAL" },
														   { TELEPORT_PENDING, "TELEPORT_PENDING" } };

const std::string& LLAgent::teleportStateName(S32 state)
{
	static std::string invalid_state_str("INVALID");
	auto iter = LLAgent::sTeleportStateName.find(state);
	if (iter != LLAgent::sTeleportStateName.end())
	{
		return iter->second;
	}
	else
	{
		return invalid_state_str;
	}
}

const std::string& LLAgent::getTeleportStateName() const
{
	return teleportStateName(getTeleportState());
}

void LLAgent::parseTeleportMessages(const std::string& xml_filename)
{
	LLXMLNodePtr root;
	BOOL success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);

	if (!success || !root || !root->hasName( "teleport_messages" ))
	{
		LL_ERRS() << "Problem reading teleport string XML file: " 
			   << xml_filename << LL_ENDL;
		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)
}

const void LLAgent::getTeleportSourceSLURL(LLSLURL& slurl) const
{
	slurl = *mTeleportSourceSLURL;
}

// static
void LLAgent::dumpGroupInfo()
{
	LL_INFOS() << "group   " << gAgent.mGroupName << LL_ENDL;
	LL_INFOS() << "ID      " << gAgent.mGroupID << LL_ENDL;
	LL_INFOS() << "powers " << gAgent.mGroupPowers << LL_ENDL;
	LL_INFOS() << "title   " << gAgent.mGroupTitle << LL_ENDL;
	//LL_INFOS() << "insig   " << gAgent.mGroupInsigniaID << LL_ENDL;
}

// Draw a representation of current autopilot target
void LLAgent::renderAutoPilotTarget()
{
	if (mAutoPilot)
	{
		F32 height_meters;
		LLVector3d target_global;

		gGL.matrixMode(LLRender::MM_MODELVIEW);
		gGL.pushMatrix();

		// not textured
		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);

		// lovely green
		gGL.color4f(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;

		gGL.scalef(height_meters, height_meters, height_meters);

		gSphere.render();

		gGL.popMatrix();
	}
}

// <FS> Phantom mode
void LLAgent::togglePhantom()
{
	make_ui_sound("UISndMovelockToggle");
	mPhantom = !mPhantom;
	if (mPhantom)
	{
		LLNotificationsUtil::add("PhantomOn", LLSD());
	}
	else
	{
		LLNotificationsUtil::add("PhantomOff", LLSD());
	}
}

bool LLAgent::getPhantom() const
{
	return mPhantom;
}
// </FS> Phantom mode

/********************************************************************************/

// <FS:Ansariel> [Legacy Bake]
LLAgentQueryManager gAgentQueryManager;

LLAgentQueryManager::LLAgentQueryManager() :
	mWearablesCacheQueryID(0),
	mNumPendingQueries(0),
	mUpdateSerialNum(0)
{
	for (U32 i = 0; i < BAKED_NUM_INDICES; i++)
	{
		mActiveCacheQueries[i] = 0;
	}
}

LLAgentQueryManager::~LLAgentQueryManager()
{
}
// </FS:Ansariel> [Legacy Bake]

//-----------------------------------------------------------------------------
// LLTeleportRequest
//-----------------------------------------------------------------------------

LLTeleportRequest::LLTeleportRequest()
	: mStatus(kPending)
{
}

LLTeleportRequest::~LLTeleportRequest()
{
}

bool LLTeleportRequest::canRestartTeleport()
{
	return false;
}

void LLTeleportRequest::restartTeleport()
{
	llassert(0);
}

// TODO this enum -> name idiom should be in a common class rather than repeated various places.
const std::string& LLTeleportRequest::statusName(EStatus status)
{
	static std::string invalid_status_str("INVALID");
	auto iter = LLTeleportRequest::sTeleportStatusName.find(status);
	if (iter != LLTeleportRequest::sTeleportStatusName.end())
	{
		return iter->second;
	}
	else
	{
		return invalid_status_str;
	}
}

std::ostream& operator<<(std::ostream& os, const LLTeleportRequest& req)
{
	req.toOstream(os);
	return os;
}

void LLTeleportRequest::toOstream(std::ostream& os) const
{
	os << "status " << statusName(mStatus) << "(" << mStatus << ")";
}

//-----------------------------------------------------------------------------
// LLTeleportRequestViaLandmark
//-----------------------------------------------------------------------------
LLTeleportRequestViaLandmark::LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId)
	: LLTeleportRequest(),
	mLandmarkId(pLandmarkId)
{
    LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark created, " << *this << LL_ENDL;
}

LLTeleportRequestViaLandmark::~LLTeleportRequestViaLandmark()
{
    LL_INFOS("Teleport") << "~LLTeleportRequestViaLandmark, " << *this << LL_ENDL;
}

void LLTeleportRequestViaLandmark::toOstream(std::ostream& os) const
{
	os << "landmark " << mLandmarkId << " ";
	LLTeleportRequest::toOstream(os);
}

bool LLTeleportRequestViaLandmark::canRestartTeleport()
{
    LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark::canRestartTeleport? -> true, " << *this << LL_ENDL;
	return true;
}

void LLTeleportRequestViaLandmark::startTeleport()
{
    LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark::startTeleport, " << *this << LL_ENDL;
	gAgent.doTeleportViaLandmark(getLandmarkId());
}

void LLTeleportRequestViaLandmark::restartTeleport()
{
    LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark::restartTeleport, " << *this << LL_ENDL;
	gAgent.doTeleportViaLandmark(getLandmarkId());
}
//-----------------------------------------------------------------------------
// LLTeleportRequestViaLure
//-----------------------------------------------------------------------------

LLTeleportRequestViaLure::LLTeleportRequestViaLure(const LLUUID &pLureId, BOOL pIsLureGodLike)
	: LLTeleportRequestViaLandmark(pLureId),
	mIsLureGodLike(pIsLureGodLike)
{
    LL_INFOS("Teleport") << "LLTeleportRequestViaLure created" << LL_ENDL;
}

LLTeleportRequestViaLure::~LLTeleportRequestViaLure()
{
    LL_INFOS("Teleport") << "~LLTeleportRequestViaLure" << LL_ENDL;
}

void LLTeleportRequestViaLure::toOstream(std::ostream& os) const
{
	os << "mIsLureGodLike " << (S32) mIsLureGodLike << " ";
	LLTeleportRequestViaLandmark::toOstream(os);
}

bool LLTeleportRequestViaLure::canRestartTeleport()
{
	// stinson 05/17/2012 : cannot restart a teleport via lure because of server-side restrictions
	// The current scenario is as follows:
	//    1. User A initializes a request for User B to teleport via lure
	//    2. User B accepts the teleport via lure request
	//    3. The server sees the init request from User A and the accept request from User B and matches them up
	//    4. The server then removes the paired requests up from the "queue"
	//    5. The server then fails User B's teleport for reason of maturity level (for example)
	//    6. User B's viewer prompts user to increase their maturity level profile value.
	//    7. User B confirms and accepts increase in maturity level
	//    8. User B's viewer then attempts to teleport via lure again
	//    9. This request will time-out on the viewer-side because User A's initial request has been removed from the "queue" in step 4

    LL_INFOS("Teleport") << "LLTeleportRequestViaLure::canRestartTeleport? -> false" << LL_ENDL;
    return false;
}

void LLTeleportRequestViaLure::startTeleport()
{
    LL_INFOS("Teleport") << "LLTeleportRequestViaLure::startTeleport" << LL_ENDL;
	gAgent.doTeleportViaLure(getLandmarkId(), isLureGodLike());
}

//-----------------------------------------------------------------------------
// LLTeleportRequestViaLocation
//-----------------------------------------------------------------------------

LLTeleportRequestViaLocation::LLTeleportRequestViaLocation(const LLVector3d &pPosGlobal)
	: LLTeleportRequest(),
	mPosGlobal(pPosGlobal)
{
}

LLTeleportRequestViaLocation::~LLTeleportRequestViaLocation()
{
}

void LLTeleportRequestViaLocation::toOstream(std::ostream& os) const
{
	os << "mPosGlobal " << mPosGlobal << " ";
	LLTeleportRequest::toOstream(os);
}

bool LLTeleportRequestViaLocation::canRestartTeleport()
{
    LL_INFOS("Teleport") << "LLTeleportRequestViaLocation::canRestartTeleport -> true" << LL_ENDL;
	return true;
}

void LLTeleportRequestViaLocation::startTeleport()
{
    LL_INFOS("Teleport") << "LLTeleportRequestViaLocation::startTeleport" << LL_ENDL;
	gAgent.doTeleportViaLocation(getPosGlobal());
}

void LLTeleportRequestViaLocation::restartTeleport()
{
    LL_INFOS("Teleport") << "LLTeleportRequestViaLocation::restartTeleport" << LL_ENDL;
    gAgent.doTeleportViaLocation(getPosGlobal());
}

//-----------------------------------------------------------------------------
// LLTeleportRequestViaLocationLookAt
//-----------------------------------------------------------------------------

// [RLVa:KB] - Checked: RLVa-2.0.0
LLTeleportRequestViaLocationLookAt::LLTeleportRequestViaLocationLookAt(const LLVector3d &pPosGlobal, const LLVector3& look_at)
	: LLTeleportRequestViaLocation(pPosGlobal), mLookAt(look_at)
{
    LL_INFOS("Teleport") << "LLTeleportRequestViaLocationLookAt created" << LL_ENDL;
}
// [/RLVa:KB]

//LLTeleportRequestViaLocationLookAt::LLTeleportRequestViaLocationLookAt(const LLVector3d &pPosGlobal)
//	: LLTeleportRequestViaLocation(pPosGlobal)
//{
//}

LLTeleportRequestViaLocationLookAt::~LLTeleportRequestViaLocationLookAt()
{
}

void LLTeleportRequestViaLocationLookAt::toOstream(std::ostream& os) const
{
	LLTeleportRequestViaLocation::toOstream(os);
}

bool LLTeleportRequestViaLocationLookAt::canRestartTeleport()
{
    LL_INFOS("Teleport") << "LLTeleportRequestViaLocationLookAt::canRestartTeleport -> true" << LL_ENDL;
    return true;
}

void LLTeleportRequestViaLocationLookAt::startTeleport()
{
    LL_INFOS("Teleport") << "LLTeleportRequestViaLocationLookAt::startTeleport" << LL_ENDL;
// [RLVa:KB] - Checked: RLVa-2.0.0
	gAgent.doTeleportViaLocationLookAt(getPosGlobal(), getLookAt());
// [/RLVa:KB]
//    gAgent.doTeleportViaLocationLookAt(getPosGlobal());
}

void LLTeleportRequestViaLocationLookAt::restartTeleport()
{
    LL_INFOS("Teleport") << "LLTeleportRequestViaLocationLookAt::restartTeleport" << LL_ENDL;
// [RLVa:KB] - Checked: RLVa-2.0.0
	gAgent.doTeleportViaLocationLookAt(getPosGlobal(), getLookAt());
// [/RLVa:KB]
//    gAgent.doTeleportViaLocationLookAt(getPosGlobal());
}


// <FS:Ansariel> [Legacy Bake]
//-----------------------------------------------------------------------------
// Legacy baking
//-----------------------------------------------------------------------------
void LLAgent::handleServerBakeRegionTransition(const LLUUID& region_id)
{
	LL_INFOS() << "called" << LL_ENDL;

	// Old-style appearance entering a server-bake region.
	if (isAgentAvatarValid() &&
		!gAgentAvatarp->isUsingServerBakes() &&
		(mRegionp->getCentralBakeVersion()>0))
	{
		LL_INFOS() << "update requested due to region transition" << LL_ENDL;
		LLAppearanceMgr::instance().requestServerAppearanceUpdate();
	}
	// new-style appearance entering a non-bake region,
	// need to check for existence of the baking service.
	else if (isAgentAvatarValid() &&
			 gAgentAvatarp->isUsingServerBakes() &&
			 mRegionp->getCentralBakeVersion()==0)
	{
		gAgentAvatarp->checkForUnsupportedServerBakeAppearance();
	}
}

//static
void LLAgent::processAgentCachedTextureResponse(LLMessageSystem *mesgsys, void **user_data)
{
	gAgentQueryManager.mNumPendingQueries--;
	if (gAgentQueryManager.mNumPendingQueries == 0)
	{
		selfStopPhase("fetch_texture_cache_entries");
	}

	if (!isAgentAvatarValid() || gAgentAvatarp->isDead())
	{
		LL_WARNS() << "No avatar for user in cached texture update!" << LL_ENDL;
		return;
	}

	if (isAgentAvatarValid() && gAgentAvatarp->isEditingAppearance())
	{
		// 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 ((S32)texture_index < TEX_NUM_INDICES )
		{
			const LLAvatarAppearanceDictionary::TextureEntry *texture_entry = LLAvatarAppearance::getDictionary()->getTexture((ETextureIndex)texture_index);
			if (texture_entry)
			{
				EBakedTextureIndex baked_index = texture_entry->mBakedTextureIndex;

				if (gAgentQueryManager.mActiveCacheQueries[baked_index] == query_id)
				{
					if (texture_id.notNull())
					{
						//LL_INFOS() << "Received cached texture " << (U32)texture_index << ": " << texture_id << LL_ENDL;
						gAgentAvatarp->setCachedBakedTexture((ETextureIndex)texture_index, texture_id);
						//gAgentAvatarp->setTETexture( LLVOAvatar::sBakedTextureIndices[texture_index], texture_id );
						gAgentQueryManager.mActiveCacheQueries[baked_index] = 0;
						num_results++;
					}
					else
					{
						// no cache of this bake. request upload.
						gAgentAvatarp->invalidateComposite(gAgentAvatarp->getLayerSet(baked_index),TRUE);
					}
				}
			}
		}
	}
	LL_INFOS() << "Received cached texture response for " << num_results << " textures." << LL_ENDL;
	gAgentAvatarp->outputRezTiming("Fetched agent wearables textures from cache. Will now load them");

	gAgentAvatarp->updateMeshTextures();

	if (gAgentQueryManager.mNumPendingQueries == 0)
	{
		// RN: not sure why composites are disabled at this point
		gAgentAvatarp->setCompositeUpdatesEnabled(TRUE);
		gAgent.sendAgentSetAppearance();
	}
}

// For debugging, trace agent state at times appearance message are sent out.
void LLAgent::dumpSentAppearance(const std::string& dump_prefix)
{
	std::string outfilename = get_sequential_numbered_file_name(dump_prefix,".xml");

	LLAPRFile outfile;
	std::string fullpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,outfilename);
	outfile.open(fullpath, LL_APR_WB );

	// <FS:ND> Remove LLVolatileAPRPool/apr_file_t and use FILE* instead
	// apr_file_t* file = outfile.getFileHandle();
	LLAPRFile::tFiletype* file = outfile.getFileHandle();
	// </FS:ND>

	if (!file)
	{
		return;
	}
	else
	{
		LL_DEBUGS("Avatar") << "dumping sent appearance message to " << fullpath << LL_ENDL;
	}

	LLVisualParam* appearance_version_param = gAgentAvatarp->getVisualParam(11000);
	if (appearance_version_param)
	{
		F32 value = appearance_version_param->getWeight();
		dump_visual_param(file, appearance_version_param, value);
	}
	for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearance::getDictionary()->getTextures().begin();
		 iter != LLAvatarAppearance::getDictionary()->getTextures().end();
		 ++iter)
	{
		const ETextureIndex index = iter->first;
		const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter->second;
		if (texture_dict->mIsBakedTexture)
		{
			LLTextureEntry* entry = gAgentAvatarp->getTE((U8) index);
			const LLUUID& uuid = entry->getID();
			apr_file_printf( file, "\t\t<texture te=\"%i\" uuid=\"%s\"/>\n", index, uuid.asString().c_str());
		}
	}
}

//-----------------------------------------------------------------------------
// sendAgentSetAppearance()
//-----------------------------------------------------------------------------
void LLAgent::sendAgentSetAppearance()
{
	if (gAgentQueryManager.mNumPendingQueries > 0) 
	{
		return;
	}

	if (!isAgentAvatarValid() || (getRegion() && getRegion()->getCentralBakeVersion())) return;

	// At this point we have a complete appearance to send and are in a non-baking region.
	// DRANO FIXME
	//gAgentAvatarp->setIsUsingServerBakes(FALSE);
	S32 sb_count, host_count, both_count, neither_count;
	gAgentAvatarp->bakedTextureOriginCounts(sb_count, host_count, both_count, neither_count);
	if (both_count != 0 || neither_count != 0)
	{
		LL_WARNS() << "bad bake texture state " << sb_count << "," << host_count << "," << both_count << "," << neither_count << LL_ENDL;
	}
	if (sb_count != 0 && host_count == 0)
	{
		gAgentAvatarp->setIsUsingServerBakes(true);
	}
	else if (sb_count == 0 && host_count != 0)
	{
		gAgentAvatarp->setIsUsingServerBakes(false);
	}
	else if (sb_count + host_count > 0)
	{
		LL_WARNS() << "unclear baked texture state, not sending appearance" << LL_ENDL;
		return;
	}
	
	
	LL_DEBUGS("Avatar") << gAgentAvatarp->avString() << "TAT: Sent AgentSetAppearance: " << gAgentAvatarp->getBakedStatusForPrintout() << LL_ENDL;
	//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
	
	// <FS:BEQ> Havok hover adjustment is not present on OS
	// const LLVector3 body_size = gAgentAvatarp->mBodySize + gAgentAvatarp->mAvatarOffset;
	// msg->addVector3Fast(_PREHASH_Size, body_size);	
	if(!LLGridManager::getInstance()->isInSecondLife())
	{
		msg->addVector3Fast(_PREHASH_Size, gAgentAvatarp->mBodySize);	
	}
	else
	{
		const LLVector3 body_size = gAgentAvatarp->mBodySize + gAgentAvatarp->mAvatarOffset;
		msg->addVector3Fast(_PREHASH_Size, body_size);	
	}//</FS:BEQ>

	// 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 = gAgentAvatarp->areTexturesCurrent();
	//<FS:Beq> BOM fallback legacy opensim (extended for FIRE-29903 contrib from Ubit Umarov)
	// for(U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++ )
	// {
	// 	const ETextureIndex texture_index = LLAvatarAppearanceDictionary::bakedToLocalTextureIndex((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 && !gAgentAvatarp->isWearingWearableType(LLWearableType::WT_SKIRT))
	// 	{
	// 		continue;
	// 	}

	// 	// IMG_DEFAULT_AVATAR means not baked. 0 index should be ignored for baked textures
	// 	if (!gAgentAvatarp->isTextureDefined(texture_index, 0))
	// 	{
	// 		LL_DEBUGS("Avatar") << "texture not current for baked " << (S32)baked_index << " local " << (S32)texture_index << LL_ENDL;
	// 		textures_current = FALSE;
	// 		break;
	// 	}
	// }
	if(textures_current)
	{
		for(U8 baked_index = 0; baked_index < gAgentAvatarp->getNumBakes(); baked_index++ )
		{
			const ETextureIndex texture_index = LLAvatarAppearance::getDictionary()->bakedToLocalTextureIndex((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 && !gAgentAvatarp->isWearingWearableType(LLWearableType::WT_SKIRT))
			{
				continue;
			}
			// if we're not wearing a universal
		    if ( (texture_index >= TEX_LEFT_ARM_BAKED && texture_index <= TEX_AUX3_BAKED) && !gAgentAvatarp->isWearingWearableType(LLWearableType::WT_UNIVERSAL))
        	{
          		continue; // ignore universal that is also optional
        	}
			// IMG_DEFAULT_AVATAR means not baked. 0 index should be ignored for baked textures
			if (!gAgentAvatarp->isTextureDefined(texture_index, 0))
			{
				LL_DEBUGS("Avatar") << "texture not current for baked " << (S32)baked_index << " local " << (S32)texture_index << LL_ENDL;
				textures_current = FALSE;
				break;
			}
		}
	}
	//</FS:Beq>

	// only update cache entries if we have all our baked textures

	// FIXME DRANO need additional check for not in appearance editing
	// mode, if still using local composites need to set using local
	// composites to false, and update mesh textures.
	if (textures_current)
	{
		bool enable_verbose_dumps = gSavedSettings.getBOOL("DebugAvatarAppearanceMessage");
		std::string dump_prefix = gAgentAvatarp->getFullname() + "_sent_appearance";
		if (enable_verbose_dumps)
		{
			dumpSentAppearance(dump_prefix);
		}
		LL_DEBUGS("Avatar") << gAgentAvatarp->avString() << "TAT: Sending cached texture data" << LL_ENDL;
		//<FS:Beq> BOM fallback for legacy opensim
		// for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++)
		for (U8 baked_index = 0; baked_index < gAgentAvatarp->getNumBakes(); baked_index++)
		//</FS:Beq>
		{
			BOOL generate_valid_hash = TRUE;
			if (isAgentAvatarValid() && !gAgentAvatarp->isBakedTextureFinal((LLAvatarAppearanceDefines::EBakedTextureIndex)baked_index))
			{
				generate_valid_hash = FALSE;
				LL_DEBUGS("Avatar") << gAgentAvatarp->avString() << "Not caching baked texture upload for " << (U32)baked_index << " due to being uploaded at low resolution." << LL_ENDL;
			}
			// <FS:Beq> Exclude BAKED_SKIRT from being sent if no skirt is worn (should only reach here if it were already baked)
			if (baked_index == BAKED_SKIRT && !gAgentAvatarp->isWearingWearableType(LLWearableType::WT_SKIRT))
			{
				LL_DEBUGS("Avatar") << "Not caching baked texture for unworn skirt." << LL_ENDL;
				generate_valid_hash = FALSE;
			}
			// </FS:Beq>

			const LLUUID hash = gAgentWearables.computeBakedTextureHash((EBakedTextureIndex) baked_index, generate_valid_hash);
			if (hash.notNull())
			{
				ETextureIndex texture_index = LLAvatarAppearance::getDictionary()->bakedToLocalTextureIndex((EBakedTextureIndex) baked_index);
				msg->nextBlockFast(_PREHASH_WearableData);
				msg->addUUIDFast(_PREHASH_CacheID, hash);
				msg->addU8Fast(_PREHASH_TextureIndex, (U8)texture_index);
			}
		}
		msg->nextBlockFast(_PREHASH_ObjectData);
		// gAgentAvatarp->dumpAvatarTEs("sendAppearance"); // <FS:Beq> useful when debugging appeanrce updates
		gAgentAvatarp->sendAppearanceMessage( gMessageSystem );
	}
	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);
	}


	S32 transmitted_params = 0;
	for (LLViewerVisualParam* param = (LLViewerVisualParam*)gAgentAvatarp->getFirstVisualParam();
		 param;
		 param = (LLViewerVisualParam*)gAgentAvatarp->getNextVisualParam())
	{
		if (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ||
				param->getGroup() == VISUAL_PARAM_GROUP_TRANSMIT_NOT_TWEAKABLE) // do not transmit params of group VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT
		{
			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++;
		}
	}

	//LL_INFOS() << "Avatar XML num VisualParams transmitted = " << transmitted_params << LL_ENDL;
	sendReliableMessage();
}
// </FS:Ansariel> [Legacy Bake]
// EOF
