< Summary

Class:AvatarEditorHUDController
Assembly:AvatarEditorHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/AvatarEditorHUD/Scripts/AvatarEditorHUDController.cs
Covered lines:364
Uncovered lines:187
Coverable lines:551
Total lines:1105
Line coverage:66% (364 of 551)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
AvatarEditorHUDController()0%110100%
AvatarEditorHUDController(...)0%110100%
Initialize(...)0%110100%
SetCatalog(...)0%2.062075%
OnAdditionalWearableRemoved(...)0%2100%
OnAdditionalWearableAdded(...)0%110100%
LoadUserProfile(...)0%110100%
LoadOwnedWereables(...)0%12.415033.33%
LoadOwnedEmotes()0%7.057090%
LoadOwnedEmotesTask()0%77.7517040.54%
QueryNftCollections(...)0%7.464040%
RetryLoadOwnedWearables()0%2100%
PlayerRendererLoaded(...)0%6.136084.62%
LoadUserProfile(...)0%12.6112083.78%
EnsureWearablesCategoriesNotEmpty()0%8.028092.86%
WearableClicked(...)0%8.448080.95%
HairColorClicked(...)0%110100%
SkinColorClicked(...)0%110100%
EyesColorClicked(...)0%110100%
UpdateAvatarPreview(...)0%4.024088.89%
EquipHairColor(...)0%110100%
EquipEyesColor(...)0%110100%
EquipSkinColor(...)0%110100%
EquipBodyShape(...)0%6.046090%
EquipWearable(...)0%6.226081.82%
UnequipWearable(...)0%3.033085.71%
EquipEmote(...)0%6200%
UnequipEmote(...)0%6200%
UnequipAllWearables()0%330100%
ProcessCatalog(...)0%440100%
AddWearable(...)0%440100%
RemoveWearable(...)0%20400%
RandomizeWearables()0%66095%
GetWearablesReplacedBy(...)0%7.017093.33%
SetVisibility(...)0%110100%
OnAvatarEditorVisibleChanged(...)0%110100%
SetVisibility_Internal(...)0%16.815080%
Dispose()0%330100%
CleanUp()0%220100%
SetConfiguration(...)0%2100%
SaveAvatar(...)0%4.054085%
GoToMarketplaceOrConnectWallet()0%6200%
SellCollectible(...)0%12300%
ToggleVisibility()0%2100%
ConfigureBackpackInFullscreenMenuChanged(...)0%110100%
ExploreV2IsOpenChanged(...)0%12300%
LoadCollections()0%64050%
LoadUserThirdPartyWearables()0%42600%
ToggleThirdPartyCollection(...)0%2.52050%
FetchAndShowThirdPartyCollection(...)0%110100%
RemoveThirdPartyCollection(...)0%12300%
ShouldShowHideOtherWearablesToast(...)0%12300%
IsTryingToReplaceSkin(...)0%110100%
ShouldShowReplaceOtherWearablesToast(...)0%56700%
HandleEmotesCostumizationSelection(...)0%6200%
OnNewEmoteAdded(...)0%6200%
OnPreviewEmote(...)0%2100%
OnEmoteEquipped(...)0%6200%
OnEmoteUnequipped(...)0%6200%
OnRedirectToEmoteSelling(...)0%2100%
SendNewEquippedWearablesAnalytics(...)0%550100%
SendEquipWearableAnalytic(...)0%110100%
CreateEmotesController()0%110100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/AvatarEditorHUD/Scripts/AvatarEditorHUDController.cs

#LineLine coverage
 1using DCL;
 2using DCL.EmotesCustomization;
 3using DCL.Helpers;
 4using DCL.Interface;
 5using DCL.NotificationModel;
 6using System;
 7using System.Collections.Generic;
 8using System.Linq;
 9using System.Threading;
 10using Cysharp.Threading.Tasks;
 11using DCL.Emotes;
 12using UnityEngine;
 13using UnityEngine.Rendering;
 14using UnityEngine.Rendering.Universal;
 15using Categories = WearableLiterals.Categories;
 16using Environment = DCL.Environment;
 17using Random = UnityEngine.Random;
 18using Type = DCL.NotificationModel.Type;
 19
 20public class AvatarEditorHUDController : IHUD
 21{
 22    private const int LOADING_OWNED_WEARABLES_RETRIES = 3;
 23    private const string LOADING_OWNED_WEARABLES_ERROR_MESSAGE = "There was a problem loading your wearables";
 24    private const string URL_MARKET_PLACE = "https://market.decentraland.org/browse?section=wearables";
 25    private const string URL_GET_A_WALLET = "https://docs.decentraland.org/get-a-wallet";
 26    private const string URL_SELL_COLLECTIBLE_GENERIC = "https://market.decentraland.org/account";
 27    private const string URL_SELL_SPECIFIC_COLLECTIBLE = "https://market.decentraland.org/contracts/{collectionId}/token
 28    private const string THIRD_PARTY_COLLECTIONS_FEATURE_FLAG = "third_party_collections";
 29    internal const string EQUIP_WEARABLE_METRIC = "equip_wearable";
 130    protected static readonly string[] categoriesThatMustHaveSelection = { Categories.BODY_SHAPE, Categories.UPPER_BODY,
 131    protected static readonly string[] categoriesToRandomize = { Categories.HAIR, Categories.EYES, Categories.EYEBROWS, 
 32
 33    [NonSerialized]
 34    public bool bypassUpdateAvatarPreview = false;
 35
 36    internal UserProfile userProfile;
 37    internal readonly IAnalytics analytics;
 38    internal readonly INewUserExperienceAnalytics newUserExperienceAnalytics;
 39    private BaseDictionary<string, WearableItem> catalog;
 10640    bool renderingEnabled => CommonScriptableObjects.rendererState.Get();
 10341    bool isPlayerRendererLoaded => DataStore.i.common.isPlayerRendererLoaded.Get();
 18342    BaseVariable<bool> avatarEditorVisible => DataStore.i.HUDs.avatarEditorVisible;
 13943    BaseVariable<Transform> configureBackpackInFullscreenMenu => DataStore.i.exploreV2.configureBackpackInFullscreenMenu
 9144    BaseVariable<bool> exploreV2IsOpen => DataStore.i.exploreV2.isOpen;
 36345    DataStore_EmotesCustomization emotesCustomizationDataStore => DataStore.i.emotesCustomization;
 046    DataStore_FeatureFlag featureFlagsDataStore => DataStore.i.featureFlags;
 47
 48    private readonly DataStore_FeatureFlag featureFlags;
 49
 4850    private readonly Dictionary<string, List<WearableItem>> wearablesByCategory = new Dictionary<string, List<WearableIt
 4851    protected readonly AvatarEditorHUDModel model = new AvatarEditorHUDModel();
 52
 53    private ColorList skinColorList;
 54    private ColorList eyeColorList;
 55    private ColorList hairColorList;
 56    private bool prevMouseLockState = false;
 4857    private int ownedWearablesRemainingRequests = LOADING_OWNED_WEARABLES_RETRIES;
 58    private bool ownedWearablesAlreadyLoaded = false;
 4859    private List<Nft> ownedNftCollectionsL1 = new List<Nft>();
 4860    private List<Nft> ownedNftCollectionsL2 = new List<Nft>();
 61    private bool avatarIsDirty = false;
 62    private float lastTimeOwnedWearablesChecked = 0;
 4863    private float lastTimeOwnedEmotesChecked = float.MinValue;
 64    internal bool collectionsAlreadyLoaded = false;
 4865    private float prevRenderScale = 1.0f;
 66    private bool isAvatarPreviewReady;
 4867    private List<string> thirdPartyWearablesLoaded = new List<string>();
 4868    private CancellationTokenSource loadEmotesCTS = new CancellationTokenSource();
 69
 70    internal IEmotesCustomizationComponentController emotesCustomizationComponentController;
 71
 9172    private bool isThirdPartyCollectionsEnabled => featureFlags.flags.Get().IsFeatureEnabled(THIRD_PARTY_COLLECTIONS_FEA
 73
 74    public AvatarEditorHUDView view;
 75
 76    private bool loadingWearables;
 77    private WearableItem[] emotesLoadedAsWearables;
 78
 79    public event Action OnOpen;
 80    public event Action OnClose;
 81
 4882    public AvatarEditorHUDController(DataStore_FeatureFlag featureFlags, IAnalytics analytics)
 83    {
 4884        this.featureFlags = featureFlags;
 4885        this.analytics = analytics;
 4886        this.newUserExperienceAnalytics = new NewUserExperienceAnalytics(analytics);
 4887    }
 88
 89    public void Initialize(UserProfile userProfile,
 90        BaseDictionary<string, WearableItem> catalog,
 91        bool bypassUpdateAvatarPreview = false)
 92    {
 4893        this.userProfile = userProfile;
 4894        this.bypassUpdateAvatarPreview = bypassUpdateAvatarPreview;
 95
 4896        view = AvatarEditorHUDView.Create(this);
 97
 4898        view.skinsFeatureContainer.SetActive(true);
 4899        avatarEditorVisible.OnChange += OnAvatarEditorVisibleChanged;
 48100        OnAvatarEditorVisibleChanged(avatarEditorVisible.Get(), false);
 101
 48102        configureBackpackInFullscreenMenu.OnChange += ConfigureBackpackInFullscreenMenuChanged;
 48103        ConfigureBackpackInFullscreenMenuChanged(configureBackpackInFullscreenMenu.Get(), null);
 104
 48105        exploreV2IsOpen.OnChange += ExploreV2IsOpenChanged;
 106
 48107        skinColorList = Resources.Load<ColorList>("SkinTone");
 48108        hairColorList = Resources.Load<ColorList>("HairColor");
 48109        eyeColorList = Resources.Load<ColorList>("EyeColor");
 48110        view.SetColors(skinColorList.colors, hairColorList.colors, eyeColorList.colors);
 111
 48112        SetCatalog(catalog);
 113
 48114        this.userProfile.OnUpdate += LoadUserProfile;
 115
 48116        view.SetSectionActive(AvatarEditorHUDView.EMOTES_SECTION_INDEX, false);
 117
 48118        emotesCustomizationComponentController = CreateEmotesController();
 48119        IEmotesCustomizationComponentView emotesSectionView = emotesCustomizationComponentController.Initialize(
 120            DataStore.i.emotesCustomization,
 121            DataStore.i.emotes,
 122            DataStore.i.exploreV2,
 123            DataStore.i.HUDs);
 124        //Initialize with embedded emotes
 48125        emotesCustomizationComponentController.SetEmotes(EmbeddedEmotesSO.Provide().emotes.ToArray());
 48126        emotesSectionView.viewTransform.SetParent(view.emotesSection.transform, false);
 48127        view.SetSectionActive(AvatarEditorHUDView.EMOTES_SECTION_INDEX, true);
 128
 48129        emotesCustomizationDataStore.isEmotesCustomizationSelected.OnChange += HandleEmotesCostumizationSelection;
 48130        emotesCustomizationDataStore.currentLoadedEmotes.OnAdded += OnNewEmoteAdded;
 131
 48132        emotesCustomizationComponentController.onEmotePreviewed += OnPreviewEmote;
 48133        emotesCustomizationComponentController.onEmoteEquipped += OnEmoteEquipped;
 48134        emotesCustomizationComponentController.onEmoteUnequipped += OnEmoteUnequipped;
 48135        emotesCustomizationComponentController.onEmoteSell += OnRedirectToEmoteSelling;
 136
 48137        LoadUserProfile(userProfile, true);
 138
 48139        DataStore.i.HUDs.isAvatarEditorInitialized.Set(true);
 140
 48141        view.SetThirdPartyCollectionsVisibility(isThirdPartyCollectionsEnabled);
 48142    }
 143
 144    public void SetCatalog(BaseDictionary<string, WearableItem> catalog)
 145    {
 48146        if (this.catalog != null)
 147        {
 0148            this.catalog.OnAdded -= OnAdditionalWearableAdded;
 0149            this.catalog.OnRemoved -= OnAdditionalWearableRemoved;
 150        }
 151
 48152        this.catalog = catalog;
 153
 48154        ProcessCatalog(this.catalog);
 48155        this.catalog.OnAdded += OnAdditionalWearableAdded;
 48156        this.catalog.OnRemoved += OnAdditionalWearableRemoved;
 48157    }
 158
 159    private void OnAdditionalWearableRemoved(string s, WearableItem item)
 160    {
 0161        RemoveWearable(s, item);
 0162        view.RefreshSelectorsSize();
 0163    }
 164
 165    private void OnAdditionalWearableAdded(string id, WearableItem item)
 166    {
 11167        AddWearable(id, item);
 11168        view.RefreshSelectorsSize();
 11169    }
 170
 171    private void LoadUserProfile(UserProfile userProfile)
 172    {
 43173        LoadUserProfile(userProfile, false);
 43174        QueryNftCollections(userProfile.userId);
 43175    }
 176
 177    private void LoadOwnedWereables(UserProfile userProfile)
 178    {
 179        // If there is more than 1 minute that we have checked the owned wearables, we try it again
 180        // This is done in order to retrieved the wearables after you has claimed them
 43181        if ((Time.realtimeSinceStartup < lastTimeOwnedWearablesChecked + 60 &&
 182             (ownedWearablesAlreadyLoaded ||
 183              ownedWearablesRemainingRequests <= 0)) ||
 184            string.IsNullOrEmpty(userProfile.userId))
 43185            return;
 186
 0187        lastTimeOwnedWearablesChecked = Time.realtimeSinceStartup;
 188
 0189        loadingWearables = true;
 0190        CatalogController.RequestOwnedWearables(userProfile.userId)
 191                         .Then((ownedWearables) =>
 192                         {
 0193                             ownedWearablesAlreadyLoaded = true;
 194                             //Prior profile V1 emotes must be retrieved along the wearables, onwards they will be reque
 0195                             this.userProfile.SetInventory(ownedWearables.Select(x => x.id).Concat(thirdPartyWearablesLo
 0196                             LoadUserProfile(userProfile, true);
 0197                             if (userProfile != null && userProfile.avatar != null)
 198                             {
 0199                                 emotesLoadedAsWearables = ownedWearables.Where(x => x.IsEmote()).ToArray();
 200                             }
 0201                             view.ShowSkinPopulatedList(ownedWearables.Any(item => item.IsSkin()));
 0202                             loadingWearables = false;
 0203                         })
 204                         .Catch((error) =>
 205                         {
 0206                             ownedWearablesRemainingRequests--;
 0207                             if (ownedWearablesRemainingRequests > 0)
 208                             {
 0209                                 Debug.LogWarning("Retrying owned wereables loading...");
 0210                                 LoadOwnedWereables(userProfile);
 0211                             }
 212                             else
 213                             {
 0214                                 NotificationsController.i.ShowNotification(new Model
 215                                 {
 216                                     message = LOADING_OWNED_WEARABLES_ERROR_MESSAGE,
 217                                     type = Type.GENERIC,
 218                                     timer = 10f,
 219                                     destroyOnFinish = true
 220                                 });
 221
 0222                                 Debug.LogError(error);
 0223                                 loadingWearables = false;
 224                             }
 0225                         });
 0226    }
 227
 228    private void LoadOwnedEmotes()
 229    {
 230        //Only check emotes once every 60 seconds
 43231        if (Time.realtimeSinceStartup < lastTimeOwnedEmotesChecked + 60)
 0232            return;
 233
 43234        lastTimeOwnedEmotesChecked = Time.realtimeSinceStartup;
 235        //TODO only request OwnedEmotes once every minute
 43236        loadEmotesCTS?.Cancel();
 43237        loadEmotesCTS?.Dispose();
 43238        loadEmotesCTS = null;
 239        // we only follow this flow with new profiles
 43240        if (userProfile?.avatar != null)
 241        {
 43242            loadEmotesCTS = new CancellationTokenSource();
 43243            LoadOwnedEmotesTask(loadEmotesCTS.Token);
 244        }
 43245    }
 246
 247    private async UniTaskVoid LoadOwnedEmotesTask(CancellationToken ct = default, int retries = LOADING_OWNED_WEARABLES_
 248    {
 43249        var emotesCatalog = Environment.i.serviceLocator.Get<IEmotesCatalogService>();
 250        try
 251        {
 43252            var embeddedEmotes = Resources.Load<EmbeddedEmotesSO>("EmbeddedEmotes").emotes;
 43253            var emotes = await emotesCatalog.RequestOwnedEmotesAsync(userProfile.userId, ct);
 43254            var emotesList = emotes == null ? embeddedEmotes.Cast<WearableItem>().ToList() : emotes.Concat(embeddedEmote
 43255            var emotesFilter = new HashSet<string>();
 1720256            foreach (var e in emotesList)
 817257                emotesFilter.Add(e.id);
 258
 43259            if(loadingWearables)
 0260                await UniTask.WaitWhile(() => loadingWearables, cancellationToken: ct);
 261
 43262            if (emotesLoadedAsWearables != null)
 263            {
 0264                foreach (var emoteAsWearable in emotesLoadedAsWearables)
 265                {
 0266                    if (emotesFilter.Contains(emoteAsWearable.id))
 267                        continue;
 268
 0269                    emotesList.Add(emoteAsWearable);
 270                }
 271
 0272                emotesLoadedAsWearables = null;
 273            }
 274
 43275            emotesCustomizationDataStore.UnequipMissingEmotes(emotesList);
 43276            emotesCustomizationComponentController.SetEmotes(emotesList.ToArray());
 277
 43278        }
 0279        catch (Exception e)
 280        {
 0281            OperationCanceledException opCanceled = e as OperationCanceledException;
 282            // If the cancellation was requested upwards, dont retry
 0283            if (opCanceled != null && ct.IsCancellationRequested)
 0284                return;
 285
 0286            if (retries > 0)
 287            {
 0288                LoadOwnedEmotesTask(ct, retries - 1);
 0289            }
 290            else
 291            {
 0292                if (opCanceled == null) // Ignore operation canceled exceptions when logging
 0293                    Debug.LogWarning(e.ToString());
 294                const string ERROR = "There was a problem loading your emotes";
 0295                NotificationsController.i.ShowNotification(new Model
 296                {
 297                    message = ERROR,
 298                    type = Type.GENERIC,
 299                    timer = 10f,
 300                    destroyOnFinish = true
 301                });
 302            }
 0303        }
 43304    }
 305
 306    private void QueryNftCollections(string userId)
 307    {
 43308        if (string.IsNullOrEmpty(userId))
 43309            return;
 310
 0311        Environment.i.platform.serviceProviders.theGraph.QueryNftCollections(userProfile.userId, NftCollectionsLayer.ETH
 0312           .Then((nfts) => ownedNftCollectionsL1 = nfts)
 0313           .Catch((error) => Debug.LogError(error));
 314
 0315        Environment.i.platform.serviceProviders.theGraph.QueryNftCollections(userProfile.userId, NftCollectionsLayer.MAT
 0316           .Then((nfts) => ownedNftCollectionsL2 = nfts)
 0317           .Catch((error) => Debug.LogError(error));
 0318    }
 319
 320    public void RetryLoadOwnedWearables()
 321    {
 0322        ownedWearablesRemainingRequests = LOADING_OWNED_WEARABLES_RETRIES;
 0323        LoadOwnedWereables(userProfile);
 0324        LoadOwnedEmotes();
 0325    }
 326
 327    private void PlayerRendererLoaded(bool current, bool previous)
 328    {
 15329        if (!current)
 0330            return;
 331
 15332        if (!ownedWearablesAlreadyLoaded)
 333        {
 15334            List<string> equippedOwnedWearables = new List<string>();
 78335            for (int i = 0; i < userProfile.avatar.wearables.Count; i++)
 336            {
 24337                if (catalog.TryGetValue(userProfile.avatar.wearables[i], out WearableItem wearable) &&
 338                    !wearable.data.tags.Contains(WearableLiterals.Tags.BASE_WEARABLE))
 339                {
 0340                    equippedOwnedWearables.Add(userProfile.avatar.wearables[i]);
 341                }
 342            }
 343
 15344            userProfile.SetInventory(equippedOwnedWearables.ToArray());
 345        }
 346
 15347        LoadUserProfile(userProfile, true);
 15348        DataStore.i.common.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 15349    }
 350
 351    public void LoadUserProfile(UserProfile userProfile, bool forceLoading)
 352    {
 106353        bool avatarEditorNotVisible = renderingEnabled && !view.isOpen;
 106354        bool isPlaying = !Application.isBatchMode;
 355
 106356        if (!forceLoading)
 357        {
 43358            if (isPlaying && avatarEditorNotVisible)
 0359                return;
 360        }
 361
 106362        if (userProfile == null)
 0363            return;
 364
 106365        if (userProfile.avatar == null || string.IsNullOrEmpty(userProfile.avatar.bodyShape))
 3366            return;
 367
 103368        view.InitializeNavigationEvents(!userProfile.hasConnectedWeb3);
 369
 103370        CatalogController.wearableCatalog.TryGetValue(userProfile.avatar.bodyShape, out var bodyShape);
 371
 103372        if (bodyShape == null)
 373        {
 0374            return;
 375        }
 376
 103377        view.SetIsWeb3(userProfile.hasConnectedWeb3);
 378
 103379        ProcessCatalog(this.catalog);
 103380        EquipBodyShape(bodyShape);
 103381        EquipSkinColor(userProfile.avatar.skinColor);
 103382        EquipHairColor(userProfile.avatar.hairColor);
 103383        EquipEyesColor(userProfile.avatar.eyeColor);
 384
 103385        model.wearables.Clear();
 103386        view.UnselectAllWearables();
 387
 103388        int wearablesCount = userProfile.avatar.wearables.Count;
 389
 103390        if (isPlayerRendererLoaded)
 391        {
 180392            for (var i = 0; i < wearablesCount; i++)
 393            {
 55394                CatalogController.wearableCatalog.TryGetValue(userProfile.avatar.wearables[i], out var wearable);
 55395                if (wearable == null)
 396                {
 0397                    Debug.LogError($"Couldn't find wearable with ID {userProfile.avatar.wearables[i]}");
 0398                    continue;
 399                }
 400
 55401                if (wearable.IsEmote())
 0402                    EquipEmote(wearable);
 403                else
 55404                    EquipWearable(wearable);
 405            }
 406        }
 407
 103408        EnsureWearablesCategoriesNotEmpty();
 409
 103410        UpdateAvatarPreview(true);
 103411        isAvatarPreviewReady = true;
 103412    }
 413
 414    private void EnsureWearablesCategoriesNotEmpty()
 415    {
 103416        var categoriesInUse = model.wearables
 55417            .Where(x => !x.IsEmote())
 55418            .Select(x => x.data.category).ToArray();
 419
 1648420        for (var i = 0; i < categoriesThatMustHaveSelection.Length; i++)
 421        {
 721422            var category = categoriesThatMustHaveSelection[i];
 721423            if (category != Categories.BODY_SHAPE && !(categoriesInUse.Contains(category)))
 424            {
 425                WearableItem wearable;
 576426                var defaultItemId = WearableLiterals.DefaultWearables.GetDefaultWearable(model.bodyShape.id, category);
 576427                if (defaultItemId != null)
 428                {
 576429                    CatalogController.wearableCatalog.TryGetValue(defaultItemId, out wearable);
 576430                }
 431                else
 432                {
 0433                    wearable = wearablesByCategory[category].FirstOrDefault(x => x.SupportsBodyShape(model.bodyShape.id)
 434                }
 435
 576436                if (wearable != null)
 437                {
 576438                    EquipWearable(wearable);
 439                }
 440            }
 441        }
 103442    }
 443
 444    public void WearableClicked(string wearableId)
 445    {
 20446        CatalogController.wearableCatalog.TryGetValue(wearableId, out var wearable);
 23447        if (wearable == null) return;
 448
 17449        if (wearable.data.category == Categories.BODY_SHAPE)
 450        {
 3451            if (wearable.id == model.bodyShape.id)
 0452                return;
 3453            EquipBodyShape(wearable);
 3454        }
 455        else
 456        {
 14457            if (model.wearables.Contains(wearable))
 458            {
 2459                if (!categoriesThatMustHaveSelection.Contains(wearable.data.category))
 460                {
 0461                    UnequipWearable(wearable);
 0462                }
 463                else
 464                {
 2465                    return;
 466                }
 467            }
 468            else
 469            {
 12470                if (IsTryingToReplaceSkin(wearable))
 0471                    UnequipWearable(model.GetWearable(Categories.SKIN));
 472
 12473                var sameCategoryEquipped = model.GetWearable(wearable.data.category);
 12474                if (sameCategoryEquipped != null)
 2475                    UnequipWearable(sameCategoryEquipped);
 476
 12477                EquipWearable(wearable);
 478            }
 479        }
 480
 15481        UpdateAvatarPreview(false);
 15482    }
 483
 484    public void HairColorClicked(Color color)
 485    {
 2486        EquipHairColor(color);
 2487        view.SelectHairColor(model.hairColor);
 2488        UpdateAvatarPreview(true);
 2489    }
 490
 491    public void SkinColorClicked(Color color)
 492    {
 2493        EquipSkinColor(color);
 2494        view.SelectSkinColor(model.skinColor);
 2495        UpdateAvatarPreview(true);
 2496    }
 497
 498    public void EyesColorClicked(Color color)
 499    {
 2500        EquipEyesColor(color);
 2501        view.SelectEyeColor(model.eyesColor);
 2502        UpdateAvatarPreview(true);
 2503    }
 504
 505    protected virtual void UpdateAvatarPreview(bool skipAudio)
 506    {
 125507        if (bypassUpdateAvatarPreview)
 0508            return;
 509
 125510        AvatarModel modelToUpdate = model.ToAvatarModel();
 511
 512        // We always keep the loaded emotes into the Avatar Preview
 5000513        foreach (string emoteId in emotesCustomizationDataStore.currentLoadedEmotes.Get())
 514        {
 2375515            modelToUpdate.emotes.Add(new AvatarModel.AvatarEmoteEntry() { urn = emoteId });
 516        }
 517
 125518        view.UpdateAvatarPreview(modelToUpdate, skipAudio);
 125519    }
 520
 521    private void EquipHairColor(Color color)
 522    {
 106523        model.hairColor = color;
 106524        view.SelectHairColor(model.hairColor);
 106525    }
 526
 527    private void EquipEyesColor(Color color)
 528    {
 106529        model.eyesColor = color;
 106530        view.SelectEyeColor(model.eyesColor);
 106531    }
 532
 533    private void EquipSkinColor(Color color)
 534    {
 105535        model.skinColor = color;
 105536        view.SelectSkinColor(model.skinColor);
 105537    }
 538
 539    private void EquipBodyShape(WearableItem bodyShape)
 540    {
 106541        if (bodyShape.data.category != Categories.BODY_SHAPE)
 542        {
 0543            Debug.LogError($"Item ({bodyShape.id} is not a body shape");
 0544            return;
 545        }
 546
 106547        if (model.bodyShape == bodyShape)
 44548            return;
 549
 62550        model.bodyShape = bodyShape;
 62551        emotesCustomizationComponentController.SetEquippedBodyShape(bodyShape.id);
 62552        view.UpdateSelectedBody(bodyShape);
 553
 62554        int wearablesCount = model.wearables.Count;
 182555        for (var i = wearablesCount - 1; i >= 0; i--)
 556        {
 29557            UnequipWearable(model.wearables[i]);
 558        }
 559
 62560        var defaultWearables = WearableLiterals.DefaultWearables.GetDefaultWearables(bodyShape.id);
 1022561        for (var i = 0; i < defaultWearables.Length; i++)
 562        {
 449563            if (catalog.TryGetValue(defaultWearables[i], out var wearable))
 449564                EquipWearable(wearable);
 565        }
 62566    }
 567
 568    private void EquipWearable(WearableItem wearable)
 569    {
 1099570        if (wearable.IsEmote())
 0571            return;
 572
 1099573        if (!wearablesByCategory.ContainsKey(wearable.data.category))
 0574            return;
 575
 1099576        if (wearablesByCategory[wearable.data.category].Contains(wearable) && wearable.SupportsBodyShape(model.bodyShape
 577        {
 1099578            var toReplace = GetWearablesReplacedBy(wearable);
 1099579            toReplace.ForEach(UnequipWearable);
 1099580            model.wearables.Add(wearable);
 1099581            view.EquipWearable(wearable);
 1099582            avatarIsDirty = true;
 583        }
 1099584    }
 585
 586    private void UnequipWearable(WearableItem wearable)
 587    {
 33588        if (wearable.IsEmote())
 0589            return;
 590
 33591        if (model.wearables.Contains(wearable))
 592        {
 33593            model.wearables.Remove(wearable);
 33594            view.UnequipWearable(wearable);
 33595            avatarIsDirty = true;
 596        }
 33597    }
 598
 599    private void EquipEmote(WearableItem emote)
 600    {
 0601        if (!emote.IsEmote())
 0602            return;
 0603        avatarIsDirty = true;
 0604    }
 605
 606    private void UnequipEmote(WearableItem emote)
 607    {
 0608        if (!emote.IsEmote())
 0609            return;
 610
 0611        avatarIsDirty = true;
 0612    }
 613
 614    public void UnequipAllWearables()
 615    {
 1012616        foreach (var wearable in model.wearables)
 617        {
 431618            if (!wearable.IsEmote())
 431619                view.UnequipWearable(wearable);
 620        }
 621
 75622        model.wearables.Clear();
 75623    }
 624
 625    private void ProcessCatalog(BaseDictionary<string, WearableItem> catalog)
 626    {
 151627        wearablesByCategory.Clear();
 151628        view.RemoveAllWearables();
 151629        using (var iterator = catalog.Get().GetEnumerator())
 630        {
 5551631            while (iterator.MoveNext())
 632            {
 5400633                if (iterator.Current.Value.IsEmote())
 634                    continue;
 635
 5400636                AddWearable(iterator.Current.Key, iterator.Current.Value);
 637            }
 151638        }
 639
 151640        view.RefreshSelectorsSize();
 151641    }
 642
 643    private void AddWearable(string id, WearableItem wearable)
 644    {
 5411645        if (!wearable.data.tags.Contains(WearableLiterals.Tags.BASE_WEARABLE) && userProfile.GetItemAmount(id) == 0)
 1190646            return;
 647
 4221648        if (!wearablesByCategory.ContainsKey(wearable.data.category))
 1650649            wearablesByCategory.Add(wearable.data.category, new List<WearableItem>());
 650
 4221651        wearablesByCategory[wearable.data.category].Add(wearable);
 4221652        view.AddWearable(wearable, userProfile.GetItemAmount(id),
 653            ShouldShowHideOtherWearablesToast,
 654            ShouldShowReplaceOtherWearablesToast);
 4221655    }
 656
 657    private void RemoveWearable(string id, WearableItem wearable)
 658    {
 0659        if (wearablesByCategory.ContainsKey(wearable.data.category))
 660        {
 0661            if (wearablesByCategory[wearable.data.category].Remove(wearable))
 662            {
 0663                if (wearablesByCategory[wearable.data.category].Count == 0)
 664                {
 0665                    wearablesByCategory.Remove(wearable.data.category);
 666                }
 667            }
 668        }
 669
 0670        view.RemoveWearable(wearable);
 0671    }
 672
 673    public void RandomizeWearables()
 674    {
 1675        EquipHairColor(view.GetRandomColor());
 1676        EquipEyesColor(view.GetRandomColor());
 677
 7678        List<WearableItem> wearablesToRemove = model.wearables.Where(x => !x.IsEmote()).ToList();
 14679        foreach (var wearable in wearablesToRemove)
 680        {
 6681            model.wearables.Remove(wearable);
 682        }
 683
 1684        view.UnselectAllWearables();
 1685        using (var iterator = wearablesByCategory.GetEnumerator())
 686        {
 12687            while (iterator.MoveNext())
 688            {
 11689                string category = iterator.Current.Key;
 11690                if (!categoriesToRandomize.Contains(category))
 691                {
 692                    continue;
 693                }
 694
 29695                var supportedWearables = iterator.Current.Value.Where(x => x.SupportsBodyShape(model.bodyShape.id)).ToAr
 7696                if (supportedWearables.Length == 0)
 697                {
 0698                    Debug.LogError($"Couldn't get any wearable for category {category} and bodyshape {model.bodyShape.id
 699                }
 700
 7701                var wearable = supportedWearables[Random.Range(0, supportedWearables.Length - 1)];
 7702                EquipWearable(wearable);
 703            }
 1704        }
 705
 1706        UpdateAvatarPreview(false);
 1707    }
 708
 709    private List<WearableItem> GetWearablesReplacedBy(WearableItem wearableItem)
 710    {
 1099711        var wearablesToReplace = new List<WearableItem>();
 1099712        var categoriesToReplace = new HashSet<string>(wearableItem.GetReplacesList(model.bodyShape.id) ?? new string[0])
 713
 1099714        int wearableCount = model.wearables.Count;
 8468715        for (int i = 0; i < wearableCount; i++)
 716        {
 3135717            var wearable = model.wearables[i];
 3135718            if (wearable == null) continue;
 719
 3135720            if (categoriesToReplace.Contains(wearable.data.category))
 721            {
 2722                wearablesToReplace.Add(wearable);
 2723            }
 724            else
 725            {
 726                //For retrocompatibility's sake we check current wearables against new one (compatibility matrix is symm
 3133727                HashSet<string> replacesList = new HashSet<string>(wearable.GetReplacesList(model.bodyShape.id) ?? new s
 3133728                if (replacesList.Contains(wearableItem.data.category))
 729                {
 0730                    wearablesToReplace.Add(wearable);
 731                }
 732            }
 733        }
 734
 1099735        return wearablesToReplace;
 736    }
 737
 88738    public void SetVisibility(bool visible) { avatarEditorVisible.Set(visible); }
 739
 184740    private void OnAvatarEditorVisibleChanged(bool current, bool previous) { SetVisibility_Internal(current); }
 741
 742    private void SetVisibility_Internal(bool visible)
 743    {
 92744        bool isSignUpFlow = DataStore.i.common.isSignUpFlow.Get();
 92745        if (!visible && view.isOpen)
 746        {
 1747            view.ResetPreviewEmote();
 748
 1749            if (isSignUpFlow)
 0750                DataStore.i.virtualAudioMixer.sceneSFXVolume.Set(1f);
 751
 1752            Environment.i.messaging.manager.paused = false;
 1753            DataStore.i.skyboxConfig.avatarMatProfile.Set(AvatarMaterialProfile.InWorld);
 1754            if (prevMouseLockState && isSignUpFlow)
 755            {
 0756                Utils.LockCursor();
 757            }
 758
 759            // NOTE(Brian): SSAO doesn't work correctly with the offseted avatar preview if the renderScale != 1.0
 1760            var asset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
 1761            asset.renderScale = prevRenderScale;
 762
 1763            if (isSignUpFlow)
 0764                CommonScriptableObjects.isFullscreenHUDOpen.Set(false);
 765
 1766            DataStore.i.common.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 767
 1768            OnClose?.Invoke();
 0769        }
 91770        else if (visible && !view.isOpen)
 771        {
 43772            if (isSignUpFlow)
 773            {
 0774                DataStore.i.virtualAudioMixer.sceneSFXVolume.Set(0f);
 0775                view.sectionSelector.Hide(true);
 0776            }
 777            else
 778            {
 43779                view.sectionSelector.Show(true);
 780            }
 781
 43782            LoadOwnedWereables(userProfile);
 43783            if (!isSignUpFlow)
 43784                LoadOwnedEmotes();
 785
 43786            LoadCollections();
 43787            Environment.i.messaging.manager.paused = isSignUpFlow;
 43788            DataStore.i.skyboxConfig.avatarMatProfile.Set(AvatarMaterialProfile.InEditor);
 789
 43790            prevMouseLockState = Utils.IsCursorLocked;
 791
 43792            if (isSignUpFlow || !DataStore.i.exploreV2.isInitialized.Get())
 43793                Utils.UnlockCursor();
 794
 795            // NOTE(Brian): SSAO doesn't work correctly with the offseted avatar preview if the renderScale != 1.0
 43796            var asset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
 43797            prevRenderScale = asset.renderScale;
 43798            asset.renderScale = 1.0f;
 799
 43800            if (isSignUpFlow)
 0801                CommonScriptableObjects.isFullscreenHUDOpen.Set(true);
 802
 43803            DataStore.i.common.isPlayerRendererLoaded.OnChange += PlayerRendererLoaded;
 804
 43805            OnOpen?.Invoke();
 806        }
 807
 92808        view.SetVisibility(visible);
 92809    }
 810
 811    public void Dispose()
 812    {
 43813        loadEmotesCTS?.Cancel();
 43814        loadEmotesCTS?.Dispose();
 43815        loadEmotesCTS = null;
 816
 43817        avatarEditorVisible.OnChange -= OnAvatarEditorVisibleChanged;
 43818        configureBackpackInFullscreenMenu.OnChange -= ConfigureBackpackInFullscreenMenuChanged;
 43819        DataStore.i.common.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 43820        exploreV2IsOpen.OnChange -= ExploreV2IsOpenChanged;
 43821        emotesCustomizationDataStore.isEmotesCustomizationSelected.OnChange -= HandleEmotesCostumizationSelection;
 43822        emotesCustomizationDataStore.currentLoadedEmotes.OnAdded -= OnNewEmoteAdded;
 823
 43824        emotesCustomizationComponentController.onEmotePreviewed -= OnPreviewEmote;
 43825        emotesCustomizationComponentController.onEmoteEquipped -= OnEmoteEquipped;
 43826        emotesCustomizationComponentController.onEmoteUnequipped -= OnEmoteUnequipped;
 43827        emotesCustomizationComponentController.onEmoteSell -= OnRedirectToEmoteSelling;
 828
 43829        CleanUp();
 43830    }
 831
 832    public void CleanUp()
 833    {
 48834        UnequipAllWearables();
 835
 48836        if (view != null)
 48837            view.CleanUp();
 838
 48839        this.userProfile.OnUpdate -= LoadUserProfile;
 48840        this.catalog.OnAdded -= AddWearable;
 48841        this.catalog.OnRemoved -= RemoveWearable;
 48842        DataStore.i.common.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 48843    }
 844
 0845    public void SetConfiguration(HUDConfiguration configuration) { SetVisibility(configuration.active); }
 846
 847    public void SaveAvatar(Texture2D face256Snapshot, Texture2D bodySnapshot)
 848    {
 1849        var avatarModel = model.ToAvatarModel();
 850
 851        // Add the equipped emotes to the avatar model
 1852        List<AvatarModel.AvatarEmoteEntry> emoteEntries = new List<AvatarModel.AvatarEmoteEntry>();
 1853        int equippedEmotesCount = emotesCustomizationDataStore.unsavedEquippedEmotes.Count();
 22854        for (int i = 0; i < equippedEmotesCount; i++)
 855        {
 10856            var equippedEmote = emotesCustomizationDataStore.unsavedEquippedEmotes[i];
 10857            if (equippedEmote == null)
 858                continue;
 0859            emoteEntries.Add(new AvatarModel.AvatarEmoteEntry { slot = i, urn = equippedEmote.id });
 860        }
 861
 1862        avatarModel.emotes = emoteEntries;
 863
 1864        SendNewEquippedWearablesAnalytics(userProfile.avatar.wearables, avatarModel.wearables);
 1865        emotesCustomizationDataStore.equippedEmotes.Set(emotesCustomizationDataStore.unsavedEquippedEmotes.Get());
 866
 867
 1868        WebInterface.SendSaveAvatar(avatarModel, face256Snapshot, bodySnapshot, DataStore.i.common.isSignUpFlow.Get());
 1869        userProfile.OverrideAvatar(avatarModel, face256Snapshot);
 870
 1871        if (DataStore.i.common.isSignUpFlow.Get())
 872        {
 0873            DataStore.i.HUDs.signupVisible.Set(true);
 0874            newUserExperienceAnalytics.AvatarEditSuccessNux();
 875        }
 876
 877
 1878        avatarIsDirty = false;
 1879        SetVisibility(false);
 1880    }
 881
 882    public void GoToMarketplaceOrConnectWallet()
 883    {
 0884        if (userProfile.hasConnectedWeb3)
 0885            WebInterface.OpenURL(URL_MARKET_PLACE);
 886        else
 0887            WebInterface.OpenURL(URL_GET_A_WALLET);
 0888    }
 889
 890    public void SellCollectible(string collectibleId)
 891    {
 0892        var ownedCollectible = ownedNftCollectionsL1.FirstOrDefault(nft => nft.urn == collectibleId);
 0893        if (ownedCollectible == null)
 0894            ownedCollectible = ownedNftCollectionsL2.FirstOrDefault(nft => nft.urn == collectibleId);
 895
 0896        if (ownedCollectible != null)
 0897            WebInterface.OpenURL(URL_SELL_SPECIFIC_COLLECTIBLE.Replace("{collectionId}", ownedCollectible.collectionId).
 898        else
 0899            WebInterface.OpenURL(URL_SELL_COLLECTIBLE_GENERIC);
 0900    }
 901
 0902    public void ToggleVisibility() { SetVisibility(!view.isOpen); }
 903
 96904    private void ConfigureBackpackInFullscreenMenuChanged(Transform currentParentTransform, Transform previousParentTran
 905
 906    private void ExploreV2IsOpenChanged(bool current, bool previous)
 907    {
 0908        if (!current && avatarIsDirty)
 909        {
 0910            LoadUserProfile(userProfile, true);
 911
 0912            emotesCustomizationComponentController.RestoreEmoteSlots();
 913
 0914            avatarIsDirty = false;
 915        }
 0916    }
 917
 918    private void LoadCollections()
 919    {
 43920        if (!isThirdPartyCollectionsEnabled || collectionsAlreadyLoaded)
 43921            return;
 922
 0923        WearablesFetchingHelper.GetThirdPartyCollections()
 924            .Then((collections) =>
 925            {
 0926                view.LoadCollectionsDropdown(collections);
 0927                collectionsAlreadyLoaded = true;
 0928                LoadUserThirdPartyWearables();
 0929            })
 0930            .Catch((error) => Debug.LogError(error));
 0931    }
 932
 933    private void LoadUserThirdPartyWearables()
 934    {
 0935        List<string> collectionIdsToLoad = new List<string>();
 0936        foreach (string wearableId in userProfile.avatar.wearables)
 937        {
 0938            CatalogController.wearableCatalog.TryGetValue(wearableId, out var wearable);
 939
 0940            if (wearable != null && wearable.IsFromThirdPartyCollection)
 941            {
 0942                if (!collectionIdsToLoad.Contains(wearable.ThirdPartyCollectionId))
 0943                    collectionIdsToLoad.Add(wearable.ThirdPartyCollectionId);
 944            }
 945        }
 946
 0947        foreach (string collectionId in collectionIdsToLoad)
 948        {
 0949            view.ToggleThirdPartyCollection(collectionId, true);
 950        }
 0951    }
 952
 953    public void ToggleThirdPartyCollection(bool isOn, string collectionId, string collectionName)
 954    {
 1955        if (isOn)
 1956            FetchAndShowThirdPartyCollection(collectionId, collectionName);
 957        else
 0958            RemoveThirdPartyCollection(collectionId);
 0959    }
 960
 961    private void FetchAndShowThirdPartyCollection(string collectionId, string collectionName)
 962    {
 1963        view.BlockCollectionsDropdown(true);
 1964        CatalogController.RequestThirdPartyWearablesByCollection(userProfile.userId, collectionId)
 965            .Then(wearables =>
 966            {
 0967                if (wearables.Count().Equals(0)) view.ShowNoItemOfWearableCollectionWarning();
 968
 0969                foreach (var wearable in wearables)
 970                {
 0971                    if (!userProfile.ContainsInInventory(wearable.id))
 972                    {
 0973                        userProfile.AddToInventory(wearable.id);
 974
 0975                        if (!thirdPartyWearablesLoaded.Contains(wearable.id))
 0976                            thirdPartyWearablesLoaded.Add(wearable.id);
 977                    }
 978                }
 979
 0980                view.BlockCollectionsDropdown(false);
 0981                LoadUserProfile(userProfile, true);
 0982            })
 983            .Catch((error) =>
 984            {
 0985                view.BlockCollectionsDropdown(false);
 0986                Debug.LogError(error);
 0987            });
 1988    }
 989
 990    private void RemoveThirdPartyCollection(string collectionId)
 991    {
 0992        var wearablesToRemove = CatalogController.i.Wearables.GetValues()
 0993            .Where(wearable => !userProfile.HasEquipped(wearable.id)
 994                               && wearable.ThirdPartyCollectionId == collectionId)
 0995            .Select(item => item.id)
 996            .ToList();
 0997        CatalogController.i.Remove(wearablesToRemove);
 998
 0999        foreach (string wearableId in wearablesToRemove)
 1000        {
 01001            userProfile.RemoveFromInventory(wearableId);
 01002            thirdPartyWearablesLoaded.Remove(wearableId);
 1003        }
 1004
 01005        LoadUserProfile(userProfile, true);
 01006    }
 1007
 1008    private bool ShouldShowHideOtherWearablesToast(WearableItem wearable)
 1009    {
 01010        var isWearingSkinAlready = model.wearables.Any(item => item.IsSkin());
 01011        return wearable.IsSkin() && !isWearingSkinAlready;
 1012    }
 1013
 1014    private bool IsTryingToReplaceSkin(WearableItem wearable)
 1015    {
 121016        return model.wearables.Any(skin =>
 1017        {
 801018            return skin.IsSkin()
 1019                   && skin.DoesHide(wearable.data.category, model.bodyShape.id);
 1020        });
 1021    }
 1022
 1023    private bool ShouldShowReplaceOtherWearablesToast(WearableItem wearable)
 1024    {
 01025        if (IsTryingToReplaceSkin(wearable)) return true;
 01026        var toReplace = GetWearablesReplacedBy(wearable);
 01027        if (wearable == null || toReplace.Count == 0) return false;
 01028        if (model.wearables.Contains(wearable)) return false;
 1029
 1030        // NOTE: why just 1?
 01031        if (toReplace.Count == 1)
 1032        {
 01033            var w = toReplace[0];
 01034            if (w.data.category == wearable.data.category)
 01035                return false;
 1036        }
 01037        return true;
 1038    }
 1039
 1040
 1041    private void HandleEmotesCostumizationSelection(bool current, bool previous)
 1042    {
 01043        if (!current)
 01044            return;
 1045
 01046        view.sectionSelector.GetSection(AvatarEditorHUDView.EMOTES_SECTION_INDEX).SelectToggle();
 01047    }
 1048
 1049    private void OnNewEmoteAdded(string emoteId)
 1050    {
 01051        if (!isAvatarPreviewReady)
 01052            return;
 1053
 01054        UpdateAvatarPreview(true);
 01055    }
 1056
 01057    private void OnPreviewEmote(string emoteId) { view.PlayPreviewEmote(emoteId); }
 1058
 1059    private void OnEmoteEquipped(string emoteId)
 1060    {
 01061        catalog.TryGetValue(emoteId, out WearableItem equippedEmote);
 1062
 01063        if (equippedEmote != null)
 01064            EquipEmote(equippedEmote);
 01065    }
 1066
 1067    private void OnEmoteUnequipped(string emoteId)
 1068    {
 01069        catalog.TryGetValue(emoteId, out WearableItem unequippedEmote);
 1070
 01071        if (unequippedEmote != null)
 01072            UnequipEmote(unequippedEmote);
 01073    }
 1074
 01075    private void OnRedirectToEmoteSelling(string emoteId) { SellCollectible(emoteId); }
 1076
 1077    internal void SendNewEquippedWearablesAnalytics(List<string> oldWearables, List<string> newWearables)
 1078    {
 281079        for (int i = 0; i < newWearables.Count; i++)
 1080        {
 121081            if (oldWearables.Contains(newWearables[i]))
 1082                continue;
 1083
 91084            catalog.TryGetValue(newWearables[i], out WearableItem newEquippedEmote);
 91085            if (newEquippedEmote != null && !newEquippedEmote.IsEmote())
 91086                SendEquipWearableAnalytic(newEquippedEmote);
 1087        }
 21088    }
 1089
 1090    private void SendEquipWearableAnalytic(WearableItem equippedWearable)
 1091    {
 91092        Dictionary<string, string> data = new Dictionary<string, string>();
 91093        data.Add("name", equippedWearable.GetName());
 91094        data.Add("rarity", equippedWearable.rarity);
 91095        data.Add("category", equippedWearable.data.category);
 91096        data.Add("linked_wearable", equippedWearable.IsFromThirdPartyCollection.ToString());
 91097        data.Add("third_party_collection_id", equippedWearable.ThirdPartyCollectionId);
 91098        data.Add("is_in_l2", equippedWearable.IsInL2().ToString());
 91099        data.Add("smart_item", equippedWearable.IsSmart().ToString());
 1100
 91101        analytics.SendAnalytic(EQUIP_WEARABLE_METRIC, data);
 91102    }
 1103
 481104    internal virtual IEmotesCustomizationComponentController CreateEmotesController() => new EmotesCustomizationComponen
 1105}

Methods/Properties

AvatarEditorHUDController()
renderingEnabled()
isPlayerRendererLoaded()
avatarEditorVisible()
configureBackpackInFullscreenMenu()
exploreV2IsOpen()
emotesCustomizationDataStore()
featureFlagsDataStore()
AvatarEditorHUDController(DCL.DataStore_FeatureFlag, IAnalytics)
isThirdPartyCollectionsEnabled()
Initialize(UserProfile, .BaseDictionary[String,WearableItem], System.Boolean)
SetCatalog(.BaseDictionary[String,WearableItem])
OnAdditionalWearableRemoved(System.String, WearableItem)
OnAdditionalWearableAdded(System.String, WearableItem)
LoadUserProfile(UserProfile)
LoadOwnedWereables(UserProfile)
LoadOwnedEmotes()
LoadOwnedEmotesTask()
QueryNftCollections(System.String)
RetryLoadOwnedWearables()
PlayerRendererLoaded(System.Boolean, System.Boolean)
LoadUserProfile(UserProfile, System.Boolean)
EnsureWearablesCategoriesNotEmpty()
WearableClicked(System.String)
HairColorClicked(UnityEngine.Color)
SkinColorClicked(UnityEngine.Color)
EyesColorClicked(UnityEngine.Color)
UpdateAvatarPreview(System.Boolean)
EquipHairColor(UnityEngine.Color)
EquipEyesColor(UnityEngine.Color)
EquipSkinColor(UnityEngine.Color)
EquipBodyShape(WearableItem)
EquipWearable(WearableItem)
UnequipWearable(WearableItem)
EquipEmote(WearableItem)
UnequipEmote(WearableItem)
UnequipAllWearables()
ProcessCatalog(.BaseDictionary[String,WearableItem])
AddWearable(System.String, WearableItem)
RemoveWearable(System.String, WearableItem)
RandomizeWearables()
GetWearablesReplacedBy(WearableItem)
SetVisibility(System.Boolean)
OnAvatarEditorVisibleChanged(System.Boolean, System.Boolean)
SetVisibility_Internal(System.Boolean)
Dispose()
CleanUp()
SetConfiguration(HUDConfiguration)
SaveAvatar(UnityEngine.Texture2D, UnityEngine.Texture2D)
GoToMarketplaceOrConnectWallet()
SellCollectible(System.String)
ToggleVisibility()
ConfigureBackpackInFullscreenMenuChanged(UnityEngine.Transform, UnityEngine.Transform)
ExploreV2IsOpenChanged(System.Boolean, System.Boolean)
LoadCollections()
LoadUserThirdPartyWearables()
ToggleThirdPartyCollection(System.Boolean, System.String, System.String)
FetchAndShowThirdPartyCollection(System.String, System.String)
RemoveThirdPartyCollection(System.String)
ShouldShowHideOtherWearablesToast(WearableItem)
IsTryingToReplaceSkin(WearableItem)
ShouldShowReplaceOtherWearablesToast(WearableItem)
HandleEmotesCostumizationSelection(System.Boolean, System.Boolean)
OnNewEmoteAdded(System.String)
OnPreviewEmote(System.String)
OnEmoteEquipped(System.String)
OnEmoteUnequipped(System.String)
OnRedirectToEmoteSelling(System.String)
SendNewEquippedWearablesAnalytics(System.Collections.Generic.List[String], System.Collections.Generic.List[String])
SendEquipWearableAnalytic(WearableItem)
CreateEmotesController()