summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/llappearancelistener.cpp110
-rw-r--r--indra/newview/llappearancelistener.h44
-rw-r--r--indra/newview/llappearancemgr.cpp28
-rw-r--r--indra/newview/llappearancemgr.h2
-rw-r--r--indra/newview/llviewermenu.cpp4
-rw-r--r--indra/newview/scripts/lua/LLAppearance.lua25
-rw-r--r--indra/newview/scripts/lua/luafloater_outfits_list.xml21
-rw-r--r--indra/newview/scripts/lua/test_outfits_list.lua27
9 files changed, 255 insertions, 8 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index ab65d71916..4e7e072289 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -95,6 +95,7 @@ set(viewer_SOURCE_FILES
llagentwearables.cpp
llanimstatelabels.cpp
llappcorehttp.cpp
+ llappearancelistener.cpp
llappearancemgr.cpp
llappviewer.cpp
llappviewerlistener.cpp
@@ -760,6 +761,7 @@ set(viewer_HEADER_FILES
llanimstatelabels.h
llappcorehttp.h
llappearance.h
+ llappearancelistener.h
llappearancemgr.h
llappviewer.h
llappviewerlistener.h
diff --git a/indra/newview/llappearancelistener.cpp b/indra/newview/llappearancelistener.cpp
new file mode 100644
index 0000000000..2d5ff6bee4
--- /dev/null
+++ b/indra/newview/llappearancelistener.cpp
@@ -0,0 +1,110 @@
+/**
+ * @file llappearancelistener.cpp
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llappearancelistener.h"
+
+#include "llappearancemgr.h"
+#include "llinventoryfunctions.h"
+#include "stringize.h"
+
+LLAppearanceListener::LLAppearanceListener()
+ : LLEventAPI("LLAppearance",
+ "API to wear a specified outfit and wear/remove individual items")
+{
+ add("wearOutfit",
+ "Wear outfit by folder id: [folder_id]"
+ "When [\"append\"] is true, outfit will be added to COF\n"
+ "otherwise it will replace current oufit",
+ &LLAppearanceListener::wearOutfit,
+ llsd::map("folder_id", LLSD(), "append", LLSD()));
+
+ add("wearOutfitByName",
+ "Wear outfit by folder name: [folder_name]"
+ "When [\"append\"] is true, outfit will be added to COF\n"
+ "otherwise it will replace current oufit",
+ &LLAppearanceListener::wearOutfitByName,
+ llsd::map("folder_name", LLSD(), "append", LLSD()));
+
+ add("getOutfitsList",
+ "Return the table of Outfits(id and name) which are send to the script",
+ &LLAppearanceListener::getOutfitsList);
+}
+
+
+void LLAppearanceListener::wearOutfit(LLSD const &data)
+{
+ Response response(LLSD(), data);
+ LLViewerInventoryCategory* cat = gInventory.getCategory(data["folder_id"].asUUID());
+ if (!cat)
+ {
+ response.error(stringize("Couldn't find outfit ", data["folder_id"].asUUID()));
+ return;
+ }
+ if (LLFolderType::lookupIsProtectedType(cat->getPreferredType()))
+ {
+ response.error(stringize("Can't wear system folder ", data["folder_id"].asUUID()));
+ return;
+ }
+ bool append = data["append"].asBoolean();
+ bool can_wear = append ? LLAppearanceMgr::instance().getCanAddToCOF(cat->getUUID()) : LLAppearanceMgr::instance().getCanReplaceCOF(cat->getUUID());
+ if (!can_wear)
+ {
+ std::string msg = append ? "Can't add to COF outfit " : "Can't replace COF with outfit ";
+ response.error(stringize(msg, std::quoted(cat->getName()), " , id: ", cat->getUUID()));
+ return;
+ }
+ LLAppearanceMgr::instance().wearInventoryCategory(cat, false, append);
+}
+
+void LLAppearanceListener::wearOutfitByName(LLSD const &data)
+{
+ Response response(LLSD(), data);
+ std::string error_msg;
+ if (!LLAppearanceMgr::instance().wearOutfitByName(data["folder_name"].asString(), data["append"].asBoolean(), error_msg))
+ {
+ response.error(error_msg);
+ }
+}
+
+void LLAppearanceListener::getOutfitsList(LLSD const &data)
+{
+ Response response(LLSD(), data);
+ const LLUUID outfits_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
+
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+
+ LLIsType is_category(LLAssetType::AT_CATEGORY);
+ gInventory.collectDescendentsIf(outfits_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, is_category);
+
+ LLSD outfits_data;
+ for (const LLPointer<LLViewerInventoryCategory> &cat : cat_array)
+ {
+ outfits_data[cat->getUUID().asString()] = cat->getName();
+ }
+ response["outfits"] = outfits_data;
+}
diff --git a/indra/newview/llappearancelistener.h b/indra/newview/llappearancelistener.h
new file mode 100644
index 0000000000..8448b2ede9
--- /dev/null
+++ b/indra/newview/llappearancelistener.h
@@ -0,0 +1,44 @@
+/**
+ * @file llappearancelistener.h
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+#ifndef LL_LLAPPEARANCELISTENER_H
+#define LL_LLAPPEARANCELISTENER_H
+
+#include "lleventapi.h"
+
+class LLAppearanceListener : public LLEventAPI
+{
+public:
+ LLAppearanceListener();
+
+private:
+ void wearOutfit(LLSD const &data);
+ void wearOutfitByName(LLSD const &data);
+ void getOutfitsList(LLSD const &data);
+};
+
+#endif // LL_LLAPPEARANCELISTENER_H
+
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index 30f07a873b..19b81f5a79 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -31,6 +31,7 @@
#include "llagent.h"
#include "llagentcamera.h"
#include "llagentwearables.h"
+#include "llappearancelistener.h"
#include "llappearancemgr.h"
#include "llattachmentsmgr.h"
#include "llcommandhandler.h"
@@ -71,6 +72,8 @@
#pragma warning (disable:4702)
#endif
+LLAppearanceListener sAppearanceListener;
+
namespace
{
const S32 BAKE_RETRY_MAX_COUNT = 5;
@@ -2900,8 +2903,7 @@ void LLAppearanceMgr::wearInventoryCategoryOnAvatar( LLInventoryCategory* catego
LLAppearanceMgr::changeOutfit(TRUE, category->getUUID(), append);
}
-// FIXME do we really want to search entire inventory for matching name?
-void LLAppearanceMgr::wearOutfitByName(const std::string& name)
+bool LLAppearanceMgr::wearOutfitByName(const std::string& name, bool append, std::string& error_msg)
{
LL_INFOS("Avatar") << self_av_string() << "Wearing category " << name << LL_ENDL;
@@ -2936,13 +2938,29 @@ void LLAppearanceMgr::wearOutfitByName(const std::string& name)
if(cat)
{
- LLAppearanceMgr::wearInventoryCategory(cat, copy_items, false);
+ bool is_system_folder = LLFolderType::lookupIsProtectedType(cat->getPreferredType());
+ if (is_system_folder)
+ {
+ error_msg = stringize("Can't wear system folder ", std::quoted(name));
+ return false;
+ }
+ bool can_wear = append ? getCanAddToCOF(cat->getUUID()) : getCanReplaceCOF(cat->getUUID());
+ if (!can_wear)
+ {
+ std::string msg = append ? "Can't add to COF outfit " : "Can't replace COF with outfit ";
+ error_msg = stringize(msg, std::quoted(name), " , id: ", cat->getUUID());
+ LL_WARNS() << error_msg << LL_ENDL;
+ return false;
+ }
+ LLAppearanceMgr::wearInventoryCategory(cat, copy_items, append);
}
else
{
- LL_WARNS() << "Couldn't find outfit " <<name<< " in wearOutfitByName()"
- << LL_ENDL;
+ error_msg = stringize("Couldn't find outfit ", std::quoted(name));
+ LL_WARNS() << error_msg << LL_ENDL;
+ return false;
}
+ return true;
}
bool areMatchingWearables(const LLViewerInventoryItem *a, const LLViewerInventoryItem *b)
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
index e5de92b653..d7b6cd5a61 100644
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -59,7 +59,7 @@ public:
void wearInventoryCategory(LLInventoryCategory* category, bool copy, bool append);
void wearInventoryCategoryOnAvatar(LLInventoryCategory* category, bool append);
void wearCategoryFinal(const LLUUID& cat_id, bool copy_items, bool append);
- void wearOutfitByName(const std::string& name);
+ bool wearOutfitByName(const std::string &name, bool append = false, std::string &error_msg = std::string());
void changeOutfit(bool proceed, const LLUUID& category, bool append);
void replaceCurrentOutfit(const LLUUID& new_outfit);
void renameOutfit(const LLUUID& outfit_id);
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 90b7c43047..cea7180187 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -9745,8 +9745,8 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedEnableAppearanceToXML(), "Advanced.EnableAppearanceToXML");
view_listener_t::addMenu(new LLAdvancedToggleCharacterGeometry(), "Advanced.ToggleCharacterGeometry");
- view_listener_t::addMenu(new LLAdvancedTestMale(), "Advanced.TestMale");
- view_listener_t::addMenu(new LLAdvancedTestFemale(), "Advanced.TestFemale");
+ view_listener_t::addMenu(new LLAdvancedTestMale(), "Advanced.TestMale", cb_info::UNTRUSTED_THROTTLE);
+ view_listener_t::addMenu(new LLAdvancedTestFemale(), "Advanced.TestFemale", cb_info::UNTRUSTED_THROTTLE);
// Advanced > Character > Animation Speed
view_listener_t::addMenu(new LLAdvancedAnimTenFaster(), "Advanced.AnimTenFaster");
diff --git a/indra/newview/scripts/lua/LLAppearance.lua b/indra/newview/scripts/lua/LLAppearance.lua
new file mode 100644
index 0000000000..ec7a25197f
--- /dev/null
+++ b/indra/newview/scripts/lua/LLAppearance.lua
@@ -0,0 +1,25 @@
+leap = require 'leap'
+
+local LLAppearance = {}
+
+function LLAppearance.addOutfit(folder)
+ leap.request('LLAppearance', {op='wearOutfit', append = true, folder_id=folder})
+end
+
+function LLAppearance.replaceOutfit(folder)
+ leap.request('LLAppearance', {op='wearOutfit', append = false, folder_id=folder})
+end
+
+function LLAppearance.addOutfitByName(folder)
+ leap.request('LLAppearance', {op='wearOutfitByName', append = true, folder_name=folder})
+end
+
+function LLAppearance.replaceOutfitByName(folder)
+ leap.request('LLAppearance', {op='wearOutfitByName', append = false, folder_name=folder})
+end
+
+function LLAppearance.getOutfitsList()
+ return leap.request('LLAppearance', {op='getOutfitsList'})['outfits']
+end
+
+return LLAppearance
diff --git a/indra/newview/scripts/lua/luafloater_outfits_list.xml b/indra/newview/scripts/lua/luafloater_outfits_list.xml
new file mode 100644
index 0000000000..1f6505cb8d
--- /dev/null
+++ b/indra/newview/scripts/lua/luafloater_outfits_list.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ legacy_header_height="18"
+ height="185"
+ layout="topleft"
+ name="lua_outfits"
+ title="Outfits"
+ width="320">
+ <scroll_list
+ draw_heading="false"
+ left="5"
+ width="310"
+ height="150"
+ top_pad ="25"
+ follows="all"
+ name="outfits_list">
+ <scroll_list.columns
+ name="outfit_name"
+ label="Name"/>
+ </scroll_list>
+</floater>
diff --git a/indra/newview/scripts/lua/test_outfits_list.lua b/indra/newview/scripts/lua/test_outfits_list.lua
new file mode 100644
index 0000000000..5875fd51da
--- /dev/null
+++ b/indra/newview/scripts/lua/test_outfits_list.lua
@@ -0,0 +1,27 @@
+local Floater = require 'Floater'
+local LLAppearance = require 'LLAppearance'
+local startup = require 'startup'
+
+local flt = Floater:new(
+ "luafloater_outfits_list.xml",
+ {outfits_list = {"double_click"}})
+
+function flt:post_build(event_data)
+ local outfits_map = LLAppearance.getOutfitsList()
+ local action_data = {}
+ action_data.action = "add_list_element"
+ action_data.ctrl_name = "outfits_list"
+ local outfits = {}
+ for uuid, name in pairs(outfits_map) do
+ table.insert(outfits, {value = uuid, columns={column = "outfit_name", value = name}})
+ end
+ action_data.value = outfits
+ self:post(action_data)
+end
+
+function flt:double_click_outfits_list(event_data)
+ LLAppearance.replaceOutfit(event_data.value)
+end
+
+startup.wait('STATE_STARTED')
+flt:show()