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"> + + +