diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index c019c33fe..20657e64d 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -118,7 +118,7 @@ void dec_busy_count()
// Function declarations
struct LLWearableHoldingPattern;
-void wear_inventory_category_on_avatar(LLInventoryCategory* category, BOOL append);
+void wear_inventory_category_on_avatar(LLInventoryCategory* category, BOOL append, BOOL replace = FALSE);
void wear_inventory_category_on_avatar_step2( BOOL proceed, void* userdata);
void wear_inventory_category_on_avatar_loop(LLWearable* wearable, void*);
void wear_inventory_category_on_avatar_step3(LLWearableHoldingPattern* holder, BOOL append);
@@ -183,6 +183,7 @@ struct LLWearInfo
{
LLUUID mCategoryID;
BOOL mAppend;
+ BOOL mReplace;
};
@@ -2106,6 +2107,10 @@ void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model
{
modifyOutfit(TRUE);
}
+ else if ("wearitems" == action)
+ {
+ modifyOutfit(TRUE, TRUE);
+ }
else if ("removefromoutfit" == action)
{
// derf
@@ -2495,6 +2500,7 @@ void LLFolderBridge::folderOptionsMenu()
{
//
mItems.push_back(std::string("Add To Outfit"));
+ mItems.push_back(std::string("Wear Items"));
mItems.push_back(std::string("Replace Outfit"));
//
}
@@ -2857,7 +2863,7 @@ void LLFolderBridge::createWearable(LLUUID parent_id, EWearableType type)
LLPointer(NULL));
}
-void LLFolderBridge::modifyOutfit(BOOL append)
+void LLFolderBridge::modifyOutfit(BOOL append, BOOL replace)
{
// derf
if(std::find(LLInventoryPanel::sInstances.begin(), LLInventoryPanel::sInstances.end(), mInventoryPanel) == LLInventoryPanel::sInstances.end())
@@ -2871,7 +2877,7 @@ void LLFolderBridge::modifyOutfit(BOOL append)
LLViewerInventoryCategory* cat = getCategory();
if(!cat) return;
- wear_inventory_category_on_avatar( cat, append );
+ wear_inventory_category_on_avatar(cat, append, replace);
}
// helper stuff
@@ -4791,7 +4797,7 @@ void wear_inventory_category(LLInventoryCategory* category, bool copy, bool appe
}
// *NOTE: hack to get from avatar inventory to avatar
-void wear_inventory_category_on_avatar( LLInventoryCategory* category, BOOL append )
+void wear_inventory_category_on_avatar(LLInventoryCategory* category, BOOL append, BOOL replace)
{
// Avoid unintentionally overwriting old wearables. We have to do
// this up front to avoid having to deal with the case of multiple
@@ -4802,6 +4808,7 @@ void wear_inventory_category_on_avatar( LLInventoryCategory* category, BOOL appe
LLWearInfo* userdata = new LLWearInfo;
userdata->mAppend = append;
+ userdata->mReplace = replace;
userdata->mCategoryID = category->getUUID();
if( gFloaterCustomize )
@@ -4999,7 +5006,7 @@ void wear_inventory_category_on_avatar_step2( BOOL proceed, void* userdata )
msg->nextBlockFast(_PREHASH_ObjectData );
msg->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID());
msg->addUUIDFast(_PREHASH_OwnerID, item->getPermissions().getOwner());
- msg->addU8Fast(_PREHASH_AttachmentPt, 0 | ATTACHMENT_ADD); // Wear at the previous or default attachment point
+ msg->addU8Fast(_PREHASH_AttachmentPt, wear_info->mReplace ? 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());
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h
index 8f9226a8f..a99168583 100755
--- a/indra/newview/llinventorybridge.h
+++ b/indra/newview/llinventorybridge.h
@@ -344,7 +344,7 @@ protected:
BOOL checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& typeToCheck);
- void modifyOutfit(BOOL append);
+ void modifyOutfit(BOOL append, BOOL replace = FALSE);
public:
static LLFolderBridge* sSelf;
static void staticFolderOptionsMenu();
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 9ef0a2697..59b2755a7 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -39,6 +39,7 @@
#include "llaudioengine.h"
#include "noise.h"
+#include "llsdserialize.h"
#include "llagent.h" // Get state values from here
#include "llviewercontrol.h"
@@ -53,6 +54,7 @@
#include "llhudeffecttrail.h"
#include "llhudmanager.h"
+#include "llinventorybridge.h"
#include "llinventoryview.h"
#include "llkeyframefallmotion.h"
#include "llkeyframestandmotion.h"
@@ -720,6 +722,10 @@ F32 LLVOAvatar::sGreyTime = 0.f;
F32 LLVOAvatar::sGreyUpdateTime = 0.f;
bool LLVOAvatar::sDoProperArc = true;
+// Globals
+LLFrameTimer gAttachmentsTimer;
+bool gAttachmentsListDirty = true;
+
//-----------------------------------------------------------------------------
// Helper functions
//-----------------------------------------------------------------------------
@@ -834,6 +840,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
{
mIsSelf = TRUE;
gAgent.setAvatarObject(this);
+ gAttachmentsTimer.reset();
lldebugs << "Marking avatar as self " << id << llendl;
}
else
@@ -2720,6 +2727,10 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
// attach objects that were waiting for a drawable
lazyAttach();
+ if (mIsSelf)
+ {
+ checkAttachments();
+ }
// animate the character
// store off last frame's root position to be consistent with camera position
@@ -2756,6 +2767,133 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
return TRUE;
}
+void LLVOAvatar::checkAttachments()
+{
+ const F32 LAZY_ATTACH_DELAY = 15.0f;
+ static bool first_run = true;
+
+ if (!mIsSelf)
+ {
+ return;
+ }
+
+ if (mPendingAttachment.size() == 0)
+ {
+ if (first_run)
+ {
+ if (gAttachmentsTimer.getElapsedTimeF32() > LAZY_ATTACH_DELAY)
+ {
+ first_run = false;
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ if (!avatarp) return;
+ std::set worn;
+ 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)
+ {
+ worn.insert(attached_object->getAttachmentItemID());
+ }
+ }
+ }
+ std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "attachments.xml");
+ //llinfos << "Reading the saved worn attachments list from: " << filename << llendl;
+ LLSD list;
+ llifstream llsd_xml;
+ llsd_xml.open(filename.c_str(), std::ios::in | std::ios::binary);
+ if (llsd_xml.is_open())
+ {
+ LLSDSerialize::fromXML(list, llsd_xml);
+ for (LLSD::map_iterator iter = list.beginMap(); iter != list.endMap(); iter++)
+ {
+ LLSD array = iter->second;
+ if (array.isArray())
+ {
+ for (int i = 0; i < array.size(); i++)
+ {
+ LLSD map = array[i];
+ if (map.has("inv_item_id"))
+ {
+ LLUUID item_id = map.get("inv_item_id");
+ if (worn.find(item_id) == worn.end())
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(item_id);
+ if (item)
+ {
+ rez_attachment(item, NULL, false);
+ }
+ else
+ {
+ llwarns << item_id.asString() << " not found in inventory, could not reattach." << llendl;
+ }
+ }
+ }
+ else
+ {
+ llwarns << "Malformed attachments list file (no \"inv_item_id\" key). Aborting." << llendl;
+ llsd_xml.close();
+ return;
+ }
+ }
+ }
+ else
+ {
+ llwarns << "Malformed attachments list file (not an array). Aborting." << llendl;
+ llsd_xml.close();
+ return;
+ }
+ }
+ llsd_xml.close();
+ }
+ }
+ }
+ else if (gAttachmentsListDirty)
+ {
+ gAttachmentsListDirty = false;
+ LLSD list;
+ LLSD array = list.emptyArray();
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ if (!avatarp) return;
+ 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)
+ {
+ LLSD entry = list.emptyMap();
+ entry.insert("inv_item_id", attached_object->getAttachmentItemID());
+ array.append(entry);
+ }
+ }
+ }
+ list.insert("attachments", array);
+ std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "attachments.xml");
+ llofstream list_file(filename);
+ LLSDSerialize::toPrettyXML(list, list_file);
+ list_file.close();
+ //llinfos << "Worn attachments list saved to: " << filename << llendl;
+ }
+ }
+ else
+ {
+ gAttachmentsListDirty = true;
+ gAttachmentsTimer.reset();
+ }
+}
+
void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled)
{
// disable voice visualizer when in mouselook
@@ -3511,9 +3649,6 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
new_name = TRUE;
}
- LLNameValue *title = getNVPair("Title");
- LLNameValue* firstname = getNVPair("FirstName");
- LLNameValue* lastname = getNVPair("LastName");
//
std::string client;
@@ -3599,40 +3734,39 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
mClientTag = "Friend";
}
}
- }
-
- if (!mIsSelf && gSavedSettings.getBOOL("AscentUseStatusColors"))
- {
- LLViewerRegion* parent_estate = LLWorld::getInstance()->getRegionFromPosGlobal(this->getPositionGlobal());
- LLUUID estate_owner = LLUUID::null;
- if(parent_estate && parent_estate->isAlive())
+ if (!mIsSelf && gSavedSettings.getBOOL("AscentUseStatusColors"))
{
- estate_owner = parent_estate->getOwner();
- }
-
- std::string name;
- name += firstname->getString();
- name += " ";
- name += lastname->getString();
- //Lindens are always more Linden than your friend, make that take precedence
- if(LLMuteList::getInstance()->isLinden(name))
- {
- mClientColor = LLSavedSettingsGlue::getCOAColor4("AscentLindenColor").getValue();
- }
- //check if they are an estate owner at their current position
- else if(estate_owner.notNull() && this->getID() == estate_owner)
- {
- mClientColor = LLSavedSettingsGlue::getCOAColor4("AscentEstateOwnerColor").getValue();
- }
- //without these dots, SL would suck.
- else if (LLAvatarTracker::instance().getBuddyInfo(this->getID()) != NULL)
- {
- mClientColor = LLSavedSettingsGlue::getCOAColor4("AscentFriendColor");
- }
- //big fat jerkface who is probably a jerk, display them as such.
- else if(LLMuteList::getInstance()->isMuted(this->getID()))
- {
- mClientColor = LLSavedSettingsGlue::getCOAColor4("AscentMutedColor").getValue();
+ LLViewerRegion* parent_estate = LLWorld::getInstance()->getRegionFromPosGlobal(this->getPositionGlobal());
+ LLUUID estate_owner = LLUUID::null;
+ if(parent_estate && parent_estate->isAlive())
+ {
+ estate_owner = parent_estate->getOwner();
+ }
+ /*this->getClientInfo
+ std::string name;
+ name += firstname->getString();
+ name += " ";
+ name += lastname->getString();
+ //Lindens are always more Linden than your friend, make that take precedence
+ if(LLMuteList::getInstance()->isLinden(name))
+ {
+ mClientColor = LLSavedSettingsGlue::getCOAColor4("AscentLindenColor").getValue();
+ }*/
+ //check if they are an estate owner at their current position
+ else if(estate_owner.notNull() && this->getID() == estate_owner)
+ {
+ mClientColor = LLSavedSettingsGlue::getCOAColor4("AscentEstateOwnerColor").getValue();
+ }
+ //without these dots, SL would suck.
+ else if (LLAvatarTracker::instance().getBuddyInfo(this->getID()) != NULL)
+ {
+ mClientColor = LLSavedSettingsGlue::getCOAColor4("AscentFriendColor");
+ }
+ //big fat jerkface who is probably a jerk, display them as such.
+ else if(LLMuteList::getInstance()->isMuted(this->getID()))
+ {
+ mClientColor = LLSavedSettingsGlue::getCOAColor4("AscentMutedColor").getValue();
+ }
}
}
@@ -3677,8 +3811,10 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
sNumVisibleChatBubbles--;
}
}
-
-
+
+ LLNameValue *title = getNVPair("Title");
+ LLNameValue* firstname = getNVPair("FirstName");
+ LLNameValue* lastname = getNVPair("LastName");
if (mNameText.notNull() && firstname && lastname)
{
@@ -6965,12 +7101,22 @@ void LLVOAvatar::addChild(LLViewerObject *childp)
{
mPendingAttachment.push_back(childp);
}
+ if (mIsSelf)
+ {
+ gAttachmentsListDirty = true;
+ gAttachmentsTimer.reset();
+ }
}
void LLVOAvatar::removeChild(LLViewerObject *childp)
{
LLViewerObject::removeChild(childp);
detachObject(childp);
+ if (mIsSelf)
+ {
+ gAttachmentsListDirty = true;
+ gAttachmentsTimer.reset();
+ }
}
LLViewerJointAttachment* LLVOAvatar::getTargetAttachmentPoint(LLViewerObject* viewer_object)
@@ -7093,6 +7239,11 @@ void LLVOAvatar::lazyAttach()
if (mPendingAttachment[i]->mDrawable)
{
attachObject(mPendingAttachment[i]);
+ if (mIsSelf)
+ {
+ gAttachmentsListDirty = true;
+ gAttachmentsTimer.reset();
+ }
}
else
{
@@ -7101,6 +7252,11 @@ void LLVOAvatar::lazyAttach()
}
mPendingAttachment = still_pending;
+ if (mIsSelf && still_pending.size() > 0)
+ {
+ gAttachmentsListDirty = true;
+ gAttachmentsTimer.reset();
+ }
}
void LLVOAvatar::resetHUDAttachments()
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 82e135593..08077c7cd 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -301,6 +301,7 @@ public:
BOOL attachObject(LLViewerObject *viewer_object);
BOOL detachObject(LLViewerObject *viewer_object);
void lazyAttach();
+ void checkAttachments();
void sitOnObject(LLViewerObject *sit_object);
void getOffObject();
diff --git a/indra/newview/skins/default/xui/en-us/menu_inventory.xml b/indra/newview/skins/default/xui/en-us/menu_inventory.xml
index 7a1047e05..dc3a0b738 100644
--- a/indra/newview/skins/default/xui/en-us/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/en-us/menu_inventory.xml
@@ -208,6 +208,10 @@
mouse_opaque="true" name="Add To Outfit" width="128">
+
+
+