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