< 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:349
Uncovered lines:195
Coverable lines:544
Total lines:1100
Line coverage:64.1% (349 of 544)
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%
LoadUserProfile(...)0%110100%
LoadOwnedWereables(...)0%14.115028.57%
LoadOwnedEmotes()0%9.738070%
LoadOwnedEmotesTask()0%90900%
QueryNftCollections(...)0%7.464040%
RetryLoadOwnedWearables()0%2100%
PlayerRendererLoaded(...)0%6.136084.62%
LoadUserProfile(...)0%12.6112083.78%
EnsureWearablesCategoriesNotEmpty()0%8.038092.31%
WearableClicked(...)0%8.258084.21%
HairColorClicked(...)0%110100%
SkinColorClicked(...)0%110100%
EyesColorClicked(...)0%110100%
UpdateAvatarPreview(...)0%6.176083.33%
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.027092.86%
SetVisibility(...)0%110100%
OnAvatarEditorVisibleChanged(...)0%110100%
SetVisibility_Internal(...)0%15.8915084.21%
Dispose()0%330100%
CleanUp()0%220100%
SetConfiguration(...)0%2100%
SaveAvatar(...)0%11.2711086.96%
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;
 32145    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    public event Action OnOpen;
 77    public event Action OnClose;
 78
 4879    public AvatarEditorHUDController(DataStore_FeatureFlag featureFlags, IAnalytics analytics)
 80    {
 4881        this.featureFlags = featureFlags;
 4882        this.analytics = analytics;
 4883        this.newUserExperienceAnalytics = new NewUserExperienceAnalytics(analytics);
 4884    }
 85
 86    public void Initialize(UserProfile userProfile,
 87        BaseDictionary<string, WearableItem> catalog,
 88        bool bypassUpdateAvatarPreview = false)
 89    {
 4890        this.userProfile = userProfile;
 4891        this.bypassUpdateAvatarPreview = bypassUpdateAvatarPreview;
 92
 4893        view = AvatarEditorHUDView.Create(this);
 94
 4895        view.skinsFeatureContainer.SetActive(true);
 4896        avatarEditorVisible.OnChange += OnAvatarEditorVisibleChanged;
 4897        OnAvatarEditorVisibleChanged(avatarEditorVisible.Get(), false);
 98
 4899        configureBackpackInFullscreenMenu.OnChange += ConfigureBackpackInFullscreenMenuChanged;
 48100        ConfigureBackpackInFullscreenMenuChanged(configureBackpackInFullscreenMenu.Get(), null);
 101
 48102        exploreV2IsOpen.OnChange += ExploreV2IsOpenChanged;
 103
 48104        skinColorList = Resources.Load<ColorList>("SkinTone");
 48105        hairColorList = Resources.Load<ColorList>("HairColor");
 48106        eyeColorList = Resources.Load<ColorList>("EyeColor");
 48107        view.SetColors(skinColorList.colors, hairColorList.colors, eyeColorList.colors);
 108
 48109        SetCatalog(catalog);
 110
 48111        this.userProfile.OnUpdate += LoadUserProfile;
 112
 48113        view.SetSectionActive(AvatarEditorHUDView.EMOTES_SECTION_INDEX, false);
 114
 48115        emotesCustomizationComponentController = CreateEmotesController();
 48116        IEmotesCustomizationComponentView emotesSectionView = emotesCustomizationComponentController.Initialize(
 117            DataStore.i.emotesCustomization,
 118            DataStore.i.emotes,
 119            DataStore.i.exploreV2,
 120            DataStore.i.HUDs);
 121        //Initialize with embedded emotes
 48122        emotesCustomizationComponentController.SetEmotes(EmbeddedEmotesSO.Provide().emotes.ToArray());
 48123        emotesSectionView.viewTransform.SetParent(view.emotesSection.transform, false);
 48124        view.SetSectionActive(AvatarEditorHUDView.EMOTES_SECTION_INDEX, true);
 125
 48126        emotesCustomizationDataStore.isEmotesCustomizationSelected.OnChange += HandleEmotesCostumizationSelection;
 48127        emotesCustomizationDataStore.currentLoadedEmotes.OnAdded += OnNewEmoteAdded;
 128
 48129        emotesCustomizationComponentController.onEmotePreviewed += OnPreviewEmote;
 48130        emotesCustomizationComponentController.onEmoteEquipped += OnEmoteEquipped;
 48131        emotesCustomizationComponentController.onEmoteUnequipped += OnEmoteUnequipped;
 48132        emotesCustomizationComponentController.onEmoteSell += OnRedirectToEmoteSelling;
 133
 48134        LoadUserProfile(userProfile, true);
 135
 48136        DataStore.i.HUDs.isAvatarEditorInitialized.Set(true);
 137
 48138        view.SetThirdPartyCollectionsVisibility(isThirdPartyCollectionsEnabled);
 48139    }
 140
 141    public void SetCatalog(BaseDictionary<string, WearableItem> catalog)
 142    {
 48143        if (this.catalog != null)
 144        {
 0145            this.catalog.OnAdded -= AddWearable;
 0146            this.catalog.OnRemoved -= RemoveWearable;
 147        }
 148
 48149        this.catalog = catalog;
 150
 48151        ProcessCatalog(this.catalog);
 48152        this.catalog.OnAdded += AddWearable;
 48153        this.catalog.OnRemoved += RemoveWearable;
 48154    }
 155
 156    private void LoadUserProfile(UserProfile userProfile)
 157    {
 43158        LoadUserProfile(userProfile, false);
 43159        QueryNftCollections(userProfile.userId);
 43160    }
 161
 162    private void LoadOwnedWereables(UserProfile userProfile)
 163    {
 164        // If there is more than 1 minute that we have checked the owned wearables, we try it again
 165        // This is done in order to retrieved the wearables after you has claimed them
 43166        if ((Time.realtimeSinceStartup < lastTimeOwnedWearablesChecked + 60 &&
 167             (ownedWearablesAlreadyLoaded ||
 168              ownedWearablesRemainingRequests <= 0)) ||
 169            string.IsNullOrEmpty(userProfile.userId))
 43170            return;
 171
 0172        view.ShowCollectiblesLoadingSpinner(true);
 0173        view.ShowCollectiblesLoadingRetry(false);
 0174        lastTimeOwnedWearablesChecked = Time.realtimeSinceStartup;
 175
 0176        CatalogController.RequestOwnedWearables(userProfile.userId)
 177                         .Then((ownedWearables) =>
 178                         {
 0179                             ownedWearablesAlreadyLoaded = true;
 180                             //Prior profile V1 emotes must be retrieved along the wearables, onwards they will be reque
 0181                             this.userProfile.SetInventory(ownedWearables.Select(x => x.id).Concat(thirdPartyWearablesLo
 0182                             LoadUserProfile(userProfile, true);
 0183                             if (userProfile?.avatar != null && !DataStore.i.emotes.newFlowEnabled.Get())
 184                             {
 0185                                 var emotes = ownedWearables.Where(x => x.IsEmote()).ToArray();
 186                                 //Add embedded emotes
 0187                                 var allEmotes = emotes.Concat(EmbeddedEmotesSO.Provide().emotes).ToArray();
 0188                                 emotesCustomizationDataStore.FilterOutNotOwnedEquippedEmotes(allEmotes);
 0189                                 emotesCustomizationComponentController.SetEmotes(allEmotes);
 190                             }
 0191                             view.ShowCollectiblesLoadingSpinner(false);
 0192                             view.ShowSkinPopulatedList(ownedWearables.Any(item => item.IsSkin()));
 0193                         })
 194                         .Catch((error) =>
 195                         {
 0196                             ownedWearablesRemainingRequests--;
 0197                             if (ownedWearablesRemainingRequests > 0)
 198                             {
 0199                                 Debug.LogWarning("Retrying owned wereables loading...");
 0200                                 LoadOwnedWereables(userProfile);
 201                             }
 202                             else
 203                             {
 0204                                 NotificationsController.i.ShowNotification(new Model
 205                                 {
 206                                     message = LOADING_OWNED_WEARABLES_ERROR_MESSAGE,
 207                                     type = Type.GENERIC,
 208                                     timer = 10f,
 209                                     destroyOnFinish = true
 210                                 });
 211
 0212                                 view.ShowCollectiblesLoadingSpinner(false);
 0213                                 view.ShowCollectiblesLoadingRetry(true);
 0214                                 Debug.LogError(error);
 215                             }
 0216                         });
 0217    }
 218
 219    private void LoadOwnedEmotes()
 220    {
 221        //Only check emotes once every 60 seconds
 43222        if (Time.realtimeSinceStartup < lastTimeOwnedEmotesChecked + 60)
 0223            return;
 224
 43225        lastTimeOwnedEmotesChecked = Time.realtimeSinceStartup;
 226        //TODO only request OwnedEmotes once every minute
 43227        loadEmotesCTS?.Cancel();
 43228        loadEmotesCTS?.Dispose();
 43229        loadEmotesCTS = null;
 230        // we only follow this flow with new profiles
 43231        if (userProfile?.avatar != null && DataStore.i.emotes.newFlowEnabled.Get())
 232        {
 0233            loadEmotesCTS = new CancellationTokenSource();
 0234            LoadOwnedEmotesTask(loadEmotesCTS.Token);
 235        }
 43236    }
 237
 238    private async UniTaskVoid LoadOwnedEmotesTask(CancellationToken ct = default, int retries = LOADING_OWNED_WEARABLES_
 239    {
 0240        var emotesCatalog = Environment.i.serviceLocator.Get<IEmotesCatalogService>();
 241        try
 242        {
 0243            var embeddedEmotes = Resources.Load<EmbeddedEmotesSO>("EmbeddedEmotes").emotes;
 0244            var emotes = await emotesCatalog.RequestOwnedEmotesAsync(userProfile.userId, ct);
 0245            if (emotes != null)
 246            {
 0247                var allEmotes = emotes.Concat(embeddedEmotes).ToArray();
 0248                emotesCustomizationDataStore.FilterOutNotOwnedEquippedEmotes(allEmotes);
 0249                emotesCustomizationComponentController.SetEmotes(allEmotes);
 250            }
 251            else
 252            {
 0253                emotesCustomizationDataStore.FilterOutNotOwnedEquippedEmotes(embeddedEmotes);
 0254                emotesCustomizationComponentController.SetEmotes(embeddedEmotes);
 255            }
 256
 0257        }
 0258        catch (Exception e)
 259        {
 0260            OperationCanceledException opCanceled = e as OperationCanceledException;
 261            // If the cancellation was requested upwards, dont retry
 0262            if (opCanceled != null && ct.IsCancellationRequested)
 0263                return;
 264
 0265            if (retries > 0)
 266            {
 0267                LoadOwnedEmotesTask(ct, retries - 1);
 268            }
 269            else
 270            {
 0271                if (opCanceled == null) // Ignore operation canceled exceptions when logging
 0272                    Debug.LogWarning(e.ToString());
 273                const string ERROR = "There was a problem loading your emotes";
 0274                NotificationsController.i.ShowNotification(new Model
 275                {
 276                    message = ERROR,
 277                    type = Type.GENERIC,
 278                    timer = 10f,
 279                    destroyOnFinish = true
 280                });
 0281                view.ShowCollectiblesLoadingRetry(true);
 282            }
 0283        }
 0284    }
 285
 286    private void QueryNftCollections(string userId)
 287    {
 43288        if (string.IsNullOrEmpty(userId))
 43289            return;
 290
 0291        Environment.i.platform.serviceProviders.theGraph.QueryNftCollections(userProfile.userId, NftCollectionsLayer.ETH
 0292           .Then((nfts) => ownedNftCollectionsL1 = nfts)
 0293           .Catch((error) => Debug.LogError(error));
 294
 0295        Environment.i.platform.serviceProviders.theGraph.QueryNftCollections(userProfile.userId, NftCollectionsLayer.MAT
 0296           .Then((nfts) => ownedNftCollectionsL2 = nfts)
 0297           .Catch((error) => Debug.LogError(error));
 0298    }
 299
 300    public void RetryLoadOwnedWearables()
 301    {
 0302        ownedWearablesRemainingRequests = LOADING_OWNED_WEARABLES_RETRIES;
 0303        LoadOwnedWereables(userProfile);
 0304        LoadOwnedEmotes();
 0305    }
 306
 307    private void PlayerRendererLoaded(bool current, bool previous)
 308    {
 15309        if (!current)
 0310            return;
 311
 15312        if (!ownedWearablesAlreadyLoaded)
 313        {
 15314            List<string> equippedOwnedWearables = new List<string>();
 78315            for (int i = 0; i < userProfile.avatar.wearables.Count; i++)
 316            {
 24317                if (catalog.TryGetValue(userProfile.avatar.wearables[i], out WearableItem wearable) &&
 318                    !wearable.data.tags.Contains(WearableLiterals.Tags.BASE_WEARABLE))
 319                {
 0320                    equippedOwnedWearables.Add(userProfile.avatar.wearables[i]);
 321                }
 322            }
 323
 15324            userProfile.SetInventory(equippedOwnedWearables.ToArray());
 325        }
 326
 15327        LoadUserProfile(userProfile, true);
 15328        DataStore.i.common.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 15329    }
 330
 331    public void LoadUserProfile(UserProfile userProfile, bool forceLoading)
 332    {
 106333        bool avatarEditorNotVisible = renderingEnabled && !view.isOpen;
 106334        bool isPlaying = !Application.isBatchMode;
 335
 106336        if (!forceLoading)
 337        {
 43338            if (isPlaying && avatarEditorNotVisible)
 0339                return;
 340        }
 341
 106342        if (userProfile == null)
 0343            return;
 344
 106345        if (userProfile.avatar == null || string.IsNullOrEmpty(userProfile.avatar.bodyShape))
 3346            return;
 347
 103348        view.InitializeNavigationEvents(!userProfile.hasConnectedWeb3);
 349
 103350        CatalogController.wearableCatalog.TryGetValue(userProfile.avatar.bodyShape, out var bodyShape);
 351
 103352        if (bodyShape == null)
 353        {
 0354            return;
 355        }
 356
 103357        view.SetIsWeb3(userProfile.hasConnectedWeb3);
 358
 103359        ProcessCatalog(this.catalog);
 103360        EquipBodyShape(bodyShape);
 103361        EquipSkinColor(userProfile.avatar.skinColor);
 103362        EquipHairColor(userProfile.avatar.hairColor);
 103363        EquipEyesColor(userProfile.avatar.eyeColor);
 364
 103365        model.wearables.Clear();
 103366        view.UnselectAllWearables();
 367
 103368        int wearablesCount = userProfile.avatar.wearables.Count;
 369
 103370        if (isPlayerRendererLoaded)
 371        {
 180372            for (var i = 0; i < wearablesCount; i++)
 373            {
 55374                CatalogController.wearableCatalog.TryGetValue(userProfile.avatar.wearables[i], out var wearable);
 55375                if (wearable == null)
 376                {
 0377                    Debug.LogError($"Couldn't find wearable with ID {userProfile.avatar.wearables[i]}");
 0378                    continue;
 379                }
 380
 55381                if (wearable.IsEmote())
 0382                    EquipEmote(wearable);
 383                else
 55384                    EquipWearable(wearable);
 385            }
 386        }
 387
 103388        EnsureWearablesCategoriesNotEmpty();
 389
 103390        UpdateAvatarPreview(true);
 103391        isAvatarPreviewReady = true;
 103392    }
 393
 394    private void EnsureWearablesCategoriesNotEmpty()
 395    {
 103396        var categoriesInUse = model.wearables
 55397            .Where(x => !x.IsEmote())
 55398            .Select(x => x.data.category).ToArray();
 399
 1648400        for (var i = 0; i < categoriesThatMustHaveSelection.Length; i++)
 401        {
 721402            var category = categoriesThatMustHaveSelection[i];
 721403            if (category != Categories.BODY_SHAPE && !(categoriesInUse.Contains(category)))
 404            {
 405                WearableItem wearable;
 576406                var defaultItemId = WearableLiterals.DefaultWearables.GetDefaultWearable(model.bodyShape.id, category);
 576407                if (defaultItemId != null)
 408                {
 576409                    CatalogController.wearableCatalog.TryGetValue(defaultItemId, out wearable);
 410                }
 411                else
 412                {
 0413                    wearable = wearablesByCategory[category].FirstOrDefault(x => x.SupportsBodyShape(model.bodyShape.id)
 414                }
 415
 576416                if (wearable != null)
 417                {
 576418                    EquipWearable(wearable);
 419                }
 420            }
 421        }
 103422    }
 423
 424    public void WearableClicked(string wearableId)
 425    {
 20426        CatalogController.wearableCatalog.TryGetValue(wearableId, out var wearable);
 23427        if (wearable == null) return;
 428
 17429        if (wearable.data.category == Categories.BODY_SHAPE)
 430        {
 3431            if (wearable.id == model.bodyShape.id)
 0432                return;
 3433            EquipBodyShape(wearable);
 434        }
 435        else
 436        {
 14437            if (model.wearables.Contains(wearable))
 438            {
 2439                if (!categoriesThatMustHaveSelection.Contains(wearable.data.category))
 440                {
 0441                    UnequipWearable(wearable);
 442                }
 443                else
 444                {
 2445                    return;
 446                }
 447            }
 448            else
 449            {
 12450                if (IsTryingToReplaceSkin(wearable))
 0451                    UnequipWearable(model.GetWearable(Categories.SKIN));
 452
 12453                var sameCategoryEquipped = model.GetWearable(wearable.data.category);
 12454                if (sameCategoryEquipped != null)
 2455                    UnequipWearable(sameCategoryEquipped);
 456
 12457                EquipWearable(wearable);
 458            }
 459        }
 460
 15461        UpdateAvatarPreview(false);
 15462    }
 463
 464    public void HairColorClicked(Color color)
 465    {
 2466        EquipHairColor(color);
 2467        view.SelectHairColor(model.hairColor);
 2468        UpdateAvatarPreview(true);
 2469    }
 470
 471    public void SkinColorClicked(Color color)
 472    {
 2473        EquipSkinColor(color);
 2474        view.SelectSkinColor(model.skinColor);
 2475        UpdateAvatarPreview(true);
 2476    }
 477
 478    public void EyesColorClicked(Color color)
 479    {
 2480        EquipEyesColor(color);
 2481        view.SelectEyeColor(model.eyesColor);
 2482        UpdateAvatarPreview(true);
 2483    }
 484
 485    protected virtual void UpdateAvatarPreview(bool skipAudio)
 486    {
 125487        if (bypassUpdateAvatarPreview)
 0488            return;
 489
 125490        AvatarModel modelToUpdate = model.ToAvatarModel(userProfile.avatar.version);
 491
 492        // We always keep the loaded emotes into the Avatar Preview
 5000493        foreach (string emoteId in emotesCustomizationDataStore.currentLoadedEmotes.Get())
 494        {
 2375495            if (DataStore.i.emotes.newFlowEnabled.Get())
 0496                modelToUpdate.emotes.Add(new AvatarModel.AvatarEmoteEntry() { urn = emoteId });
 497            else
 498            {
 2375499                if (!modelToUpdate.wearables.Contains(emoteId))
 2375500                    modelToUpdate.wearables.Add(emoteId);
 501            }
 502        }
 503
 125504        view.UpdateAvatarPreview(modelToUpdate, skipAudio);
 125505    }
 506
 507    private void EquipHairColor(Color color)
 508    {
 106509        model.hairColor = color;
 106510        view.SelectHairColor(model.hairColor);
 106511    }
 512
 513    private void EquipEyesColor(Color color)
 514    {
 106515        model.eyesColor = color;
 106516        view.SelectEyeColor(model.eyesColor);
 106517    }
 518
 519    private void EquipSkinColor(Color color)
 520    {
 105521        model.skinColor = color;
 105522        view.SelectSkinColor(model.skinColor);
 105523    }
 524
 525    private void EquipBodyShape(WearableItem bodyShape)
 526    {
 106527        if (bodyShape.data.category != Categories.BODY_SHAPE)
 528        {
 0529            Debug.LogError($"Item ({bodyShape.id} is not a body shape");
 0530            return;
 531        }
 532
 106533        if (model.bodyShape == bodyShape)
 53534            return;
 535
 53536        model.bodyShape = bodyShape;
 53537        emotesCustomizationComponentController.SetEquippedBodyShape(bodyShape.id);
 53538        view.UpdateSelectedBody(bodyShape);
 539
 53540        int wearablesCount = model.wearables.Count;
 164541        for (var i = wearablesCount - 1; i >= 0; i--)
 542        {
 29543            UnequipWearable(model.wearables[i]);
 544        }
 545
 53546        var defaultWearables = WearableLiterals.DefaultWearables.GetDefaultWearables(bodyShape.id);
 860547        for (var i = 0; i < defaultWearables.Length; i++)
 548        {
 377549            if (catalog.TryGetValue(defaultWearables[i], out var wearable))
 377550                EquipWearable(wearable);
 551        }
 53552    }
 553
 554    private void EquipWearable(WearableItem wearable)
 555    {
 1027556        if (wearable.IsEmote())
 0557            return;
 558
 1027559        if (!wearablesByCategory.ContainsKey(wearable.data.category))
 0560            return;
 561
 1027562        if (wearablesByCategory[wearable.data.category].Contains(wearable) && wearable.SupportsBodyShape(model.bodyShape
 563        {
 1027564            var toReplace = GetWearablesReplacedBy(wearable);
 1027565            toReplace.ForEach(UnequipWearable);
 1027566            model.wearables.Add(wearable);
 1027567            view.EquipWearable(wearable);
 1027568            avatarIsDirty = true;
 569        }
 1027570    }
 571
 572    private void UnequipWearable(WearableItem wearable)
 573    {
 33574        if (wearable.IsEmote())
 0575            return;
 576
 33577        if (model.wearables.Contains(wearable))
 578        {
 33579            model.wearables.Remove(wearable);
 33580            view.UnequipWearable(wearable);
 33581            avatarIsDirty = true;
 582        }
 33583    }
 584
 585    private void EquipEmote(WearableItem emote)
 586    {
 0587        if (!emote.IsEmote())
 0588            return;
 0589        avatarIsDirty = true;
 0590    }
 591
 592    private void UnequipEmote(WearableItem emote)
 593    {
 0594        if (!emote.IsEmote())
 0595            return;
 596
 0597        avatarIsDirty = true;
 0598    }
 599
 600    public void UnequipAllWearables()
 601    {
 1012602        foreach (var wearable in model.wearables)
 603        {
 431604            if (!wearable.IsEmote())
 431605                view.UnequipWearable(wearable);
 606        }
 607
 75608        model.wearables.Clear();
 75609    }
 610
 611    private void ProcessCatalog(BaseDictionary<string, WearableItem> catalog)
 612    {
 151613        wearablesByCategory.Clear();
 151614        view.RemoveAllWearables();
 151615        using (var iterator = catalog.Get().GetEnumerator())
 616        {
 5551617            while (iterator.MoveNext())
 618            {
 5400619                if (iterator.Current.Value.IsEmote())
 620                    continue;
 621
 5400622                AddWearable(iterator.Current.Key, iterator.Current.Value);
 623            }
 151624        }
 625
 151626        view.RefreshSelectorsSize();
 151627    }
 628
 629    private void AddWearable(string id, WearableItem wearable)
 630    {
 5411631        if (!wearable.data.tags.Contains(WearableLiterals.Tags.BASE_WEARABLE) && userProfile.GetItemAmount(id) == 0)
 1190632            return;
 633
 4221634        if (!wearablesByCategory.ContainsKey(wearable.data.category))
 1650635            wearablesByCategory.Add(wearable.data.category, new List<WearableItem>());
 636
 4221637        wearablesByCategory[wearable.data.category].Add(wearable);
 4221638        view.AddWearable(wearable, userProfile.GetItemAmount(id),
 639            ShouldShowHideOtherWearablesToast,
 640            ShouldShowReplaceOtherWearablesToast);
 4221641    }
 642
 643    private void RemoveWearable(string id, WearableItem wearable)
 644    {
 0645        if (wearablesByCategory.ContainsKey(wearable.data.category))
 646        {
 0647            if (wearablesByCategory[wearable.data.category].Remove(wearable))
 648            {
 0649                if (wearablesByCategory[wearable.data.category].Count == 0)
 650                {
 0651                    wearablesByCategory.Remove(wearable.data.category);
 652                }
 653            }
 654        }
 655
 0656        view.RemoveWearable(wearable);
 0657    }
 658
 659    public void RandomizeWearables()
 660    {
 1661        EquipHairColor(view.GetRandomColor());
 1662        EquipEyesColor(view.GetRandomColor());
 663
 7664        List<WearableItem> wearablesToRemove = model.wearables.Where(x => !x.IsEmote()).ToList();
 14665        foreach (var wearable in wearablesToRemove)
 666        {
 6667            model.wearables.Remove(wearable);
 668        }
 669
 1670        view.UnselectAllWearables();
 1671        using (var iterator = wearablesByCategory.GetEnumerator())
 672        {
 12673            while (iterator.MoveNext())
 674            {
 11675                string category = iterator.Current.Key;
 11676                if (!categoriesToRandomize.Contains(category))
 677                {
 678                    continue;
 679                }
 680
 29681                var supportedWearables = iterator.Current.Value.Where(x => x.SupportsBodyShape(model.bodyShape.id)).ToAr
 7682                if (supportedWearables.Length == 0)
 683                {
 0684                    Debug.LogError($"Couldn't get any wearable for category {category} and bodyshape {model.bodyShape.id
 685                }
 686
 7687                var wearable = supportedWearables[Random.Range(0, supportedWearables.Length - 1)];
 7688                EquipWearable(wearable);
 689            }
 1690        }
 691
 1692        UpdateAvatarPreview(false);
 1693    }
 694
 695    private List<WearableItem> GetWearablesReplacedBy(WearableItem wearableItem)
 696    {
 1027697        var wearablesToReplace = new List<WearableItem>();
 1027698        var categoriesToReplace = new HashSet<string>(wearableItem.GetReplacesList(model.bodyShape.id) ?? new string[0])
 699
 1027700        int wearableCount = model.wearables.Count;
 7820701        for (int i = 0; i < wearableCount; i++)
 702        {
 2883703            var wearable = model.wearables[i];
 2883704            if (wearable == null) continue;
 705
 2883706            if (categoriesToReplace.Contains(wearable.data.category))
 707            {
 2708                wearablesToReplace.Add(wearable);
 709            }
 710            else
 711            {
 712                //For retrocompatibility's sake we check current wearables against new one (compatibility matrix is symm
 2881713                HashSet<string> replacesList = new HashSet<string>(wearable.GetReplacesList(model.bodyShape.id) ?? new s
 2881714                if (replacesList.Contains(wearableItem.data.category))
 715                {
 0716                    wearablesToReplace.Add(wearable);
 717                }
 718            }
 719        }
 720
 1027721        return wearablesToReplace;
 722    }
 723
 88724    public void SetVisibility(bool visible) { avatarEditorVisible.Set(visible); }
 725
 184726    private void OnAvatarEditorVisibleChanged(bool current, bool previous) { SetVisibility_Internal(current); }
 727
 728    private void SetVisibility_Internal(bool visible)
 729    {
 92730        bool isSignUpFlow = DataStore.i.common.isSignUpFlow.Get();
 92731        if (!visible && view.isOpen)
 732        {
 1733            view.ResetPreviewEmote();
 734
 1735            if (isSignUpFlow)
 0736                DataStore.i.virtualAudioMixer.sceneSFXVolume.Set(1f);
 737
 1738            Environment.i.messaging.manager.paused = false;
 1739            DataStore.i.skyboxConfig.avatarMatProfile.Set(AvatarMaterialProfile.InWorld);
 1740            if (prevMouseLockState && isSignUpFlow)
 741            {
 0742                Utils.LockCursor();
 743            }
 744
 745            // NOTE(Brian): SSAO doesn't work correctly with the offseted avatar preview if the renderScale != 1.0
 1746            var asset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
 1747            asset.renderScale = prevRenderScale;
 748
 1749            if (isSignUpFlow)
 0750                CommonScriptableObjects.isFullscreenHUDOpen.Set(false);
 751
 1752            DataStore.i.common.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 753
 1754            OnClose?.Invoke();
 755        }
 91756        else if (visible && !view.isOpen)
 757        {
 43758            if (isSignUpFlow)
 759            {
 0760                DataStore.i.virtualAudioMixer.sceneSFXVolume.Set(0f);
 0761                view.sectionSelector.Hide(true);
 762            }
 763            else
 764            {
 43765                view.sectionSelector.Show(true);
 766            }
 767
 43768            LoadOwnedWereables(userProfile);
 43769            if (!isSignUpFlow)
 43770                LoadOwnedEmotes();
 771
 43772            LoadCollections();
 43773            Environment.i.messaging.manager.paused = isSignUpFlow;
 43774            DataStore.i.skyboxConfig.avatarMatProfile.Set(AvatarMaterialProfile.InEditor);
 775
 43776            prevMouseLockState = Utils.IsCursorLocked;
 777
 43778            if (isSignUpFlow || !DataStore.i.exploreV2.isInitialized.Get())
 43779                Utils.UnlockCursor();
 780
 781            // NOTE(Brian): SSAO doesn't work correctly with the offseted avatar preview if the renderScale != 1.0
 43782            var asset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
 43783            prevRenderScale = asset.renderScale;
 43784            asset.renderScale = 1.0f;
 785
 43786            if (isSignUpFlow)
 0787                CommonScriptableObjects.isFullscreenHUDOpen.Set(true);
 788
 43789            DataStore.i.common.isPlayerRendererLoaded.OnChange += PlayerRendererLoaded;
 790
 43791            OnOpen?.Invoke();
 792        }
 793
 92794        view.SetVisibility(visible);
 92795    }
 796
 797    public void Dispose()
 798    {
 43799        loadEmotesCTS?.Cancel();
 43800        loadEmotesCTS?.Dispose();
 43801        loadEmotesCTS = null;
 802
 43803        avatarEditorVisible.OnChange -= OnAvatarEditorVisibleChanged;
 43804        configureBackpackInFullscreenMenu.OnChange -= ConfigureBackpackInFullscreenMenuChanged;
 43805        DataStore.i.common.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 43806        exploreV2IsOpen.OnChange -= ExploreV2IsOpenChanged;
 43807        emotesCustomizationDataStore.isEmotesCustomizationSelected.OnChange -= HandleEmotesCostumizationSelection;
 43808        emotesCustomizationDataStore.currentLoadedEmotes.OnAdded -= OnNewEmoteAdded;
 809
 43810        emotesCustomizationComponentController.onEmotePreviewed -= OnPreviewEmote;
 43811        emotesCustomizationComponentController.onEmoteEquipped -= OnEmoteEquipped;
 43812        emotesCustomizationComponentController.onEmoteUnequipped -= OnEmoteUnequipped;
 43813        emotesCustomizationComponentController.onEmoteSell -= OnRedirectToEmoteSelling;
 814
 43815        CleanUp();
 43816    }
 817
 818    public void CleanUp()
 819    {
 48820        UnequipAllWearables();
 821
 48822        if (view != null)
 48823            view.CleanUp();
 824
 48825        this.userProfile.OnUpdate -= LoadUserProfile;
 48826        this.catalog.OnAdded -= AddWearable;
 48827        this.catalog.OnRemoved -= RemoveWearable;
 48828        DataStore.i.common.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 48829    }
 830
 0831    public void SetConfiguration(HUDConfiguration configuration) { SetVisibility(configuration.active); }
 832
 833    public void SaveAvatar(Texture2D face256Snapshot, Texture2D bodySnapshot)
 834    {
 1835        int version = userProfile?.avatar?.version ?? 0;
 1836        var avatarModel = model.ToAvatarModel(version);
 837
 838        // Add the equipped emotes to the avatar model
 1839        List<AvatarModel.AvatarEmoteEntry> emoteEntries = new List<AvatarModel.AvatarEmoteEntry>();
 1840        int equippedEmotesCount = emotesCustomizationDataStore.unsavedEquippedEmotes.Count();
 22841        for (int i = 0; i < equippedEmotesCount; i++)
 842        {
 10843            var equippedEmote = emotesCustomizationDataStore.unsavedEquippedEmotes[i];
 10844            if (equippedEmote == null)
 845                continue;
 0846            emoteEntries.Add(new AvatarModel.AvatarEmoteEntry { slot = i, urn = equippedEmote.id });
 847        }
 848
 849        //Add emotes to wearables if Old flow
 1850        if (!DataStore.i.emotes.newFlowEnabled.Get())
 851        {
 852            //Filter out embedded emotes
 1853            avatarModel.wearables.AddRange(emotesCustomizationDataStore.unsavedEquippedEmotes.Get()
 10854                                                                       .Where(x => x != null && x.id.StartsWith("urn:"))
 0855                                                                       .Select(x => x.id));
 856        }
 1857        avatarModel.emotes = emoteEntries;
 858
 1859        SendNewEquippedWearablesAnalytics(userProfile.avatar.wearables, avatarModel.wearables);
 1860        emotesCustomizationDataStore.equippedEmotes.Set(emotesCustomizationDataStore.unsavedEquippedEmotes.Get());
 861
 862
 1863        WebInterface.SendSaveAvatar(avatarModel, face256Snapshot, bodySnapshot, DataStore.i.common.isSignUpFlow.Get());
 1864        userProfile.OverrideAvatar(avatarModel, face256Snapshot);
 865
 1866        if (DataStore.i.common.isSignUpFlow.Get())
 867        {
 0868            DataStore.i.HUDs.signupVisible.Set(true);
 0869            newUserExperienceAnalytics.AvatarEditSuccessNux();
 870        }
 871
 872
 1873        avatarIsDirty = false;
 1874        SetVisibility(false);
 1875    }
 876
 877    public void GoToMarketplaceOrConnectWallet()
 878    {
 0879        if (userProfile.hasConnectedWeb3)
 0880            WebInterface.OpenURL(URL_MARKET_PLACE);
 881        else
 0882            WebInterface.OpenURL(URL_GET_A_WALLET);
 0883    }
 884
 885    public void SellCollectible(string collectibleId)
 886    {
 0887        var ownedCollectible = ownedNftCollectionsL1.FirstOrDefault(nft => nft.urn == collectibleId);
 0888        if (ownedCollectible == null)
 0889            ownedCollectible = ownedNftCollectionsL2.FirstOrDefault(nft => nft.urn == collectibleId);
 890
 0891        if (ownedCollectible != null)
 0892            WebInterface.OpenURL(URL_SELL_SPECIFIC_COLLECTIBLE.Replace("{collectionId}", ownedCollectible.collectionId).
 893        else
 0894            WebInterface.OpenURL(URL_SELL_COLLECTIBLE_GENERIC);
 0895    }
 896
 0897    public void ToggleVisibility() { SetVisibility(!view.isOpen); }
 898
 96899    private void ConfigureBackpackInFullscreenMenuChanged(Transform currentParentTransform, Transform previousParentTran
 900
 901    private void ExploreV2IsOpenChanged(bool current, bool previous)
 902    {
 0903        if (!current && avatarIsDirty)
 904        {
 0905            LoadUserProfile(userProfile, true);
 906
 0907            emotesCustomizationComponentController.RestoreEmoteSlots();
 908
 0909            avatarIsDirty = false;
 910        }
 0911    }
 912
 913    private void LoadCollections()
 914    {
 43915        if (!isThirdPartyCollectionsEnabled || collectionsAlreadyLoaded)
 43916            return;
 917
 0918        WearablesFetchingHelper.GetThirdPartyCollections()
 919            .Then((collections) =>
 920            {
 0921                view.LoadCollectionsDropdown(collections);
 0922                collectionsAlreadyLoaded = true;
 0923                LoadUserThirdPartyWearables();
 0924            })
 0925            .Catch((error) => Debug.LogError(error));
 0926    }
 927
 928    private void LoadUserThirdPartyWearables()
 929    {
 0930        List<string> collectionIdsToLoad = new List<string>();
 0931        foreach (string wearableId in userProfile.avatar.wearables)
 932        {
 0933            CatalogController.wearableCatalog.TryGetValue(wearableId, out var wearable);
 934
 0935            if (wearable != null && wearable.IsFromThirdPartyCollection)
 936            {
 0937                if (!collectionIdsToLoad.Contains(wearable.ThirdPartyCollectionId))
 0938                    collectionIdsToLoad.Add(wearable.ThirdPartyCollectionId);
 939            }
 940        }
 941
 0942        foreach (string collectionId in collectionIdsToLoad)
 943        {
 0944            view.ToggleThirdPartyCollection(collectionId, true);
 945        }
 0946    }
 947
 948    public void ToggleThirdPartyCollection(bool isOn, string collectionId, string collectionName)
 949    {
 1950        if (isOn)
 1951            FetchAndShowThirdPartyCollection(collectionId, collectionName);
 952        else
 0953            RemoveThirdPartyCollection(collectionId);
 0954    }
 955
 956    private void FetchAndShowThirdPartyCollection(string collectionId, string collectionName)
 957    {
 1958        view.BlockCollectionsDropdown(true);
 1959        CatalogController.RequestThirdPartyWearablesByCollection(userProfile.userId, collectionId)
 960            .Then(wearables =>
 961            {
 0962                if (wearables.Count().Equals(0)) view.ShowNoItemOfWearableCollectionWarning();
 963
 0964                foreach (var wearable in wearables)
 965                {
 0966                    if (!userProfile.ContainsInInventory(wearable.id))
 967                    {
 0968                        userProfile.AddToInventory(wearable.id);
 969
 0970                        if (!thirdPartyWearablesLoaded.Contains(wearable.id))
 0971                            thirdPartyWearablesLoaded.Add(wearable.id);
 972                    }
 973                }
 974
 0975                view.BlockCollectionsDropdown(false);
 0976                LoadUserProfile(userProfile, true);
 0977            })
 978            .Catch((error) =>
 979            {
 0980                view.BlockCollectionsDropdown(false);
 0981                Debug.LogError(error);
 0982            });
 1983    }
 984
 985    private void RemoveThirdPartyCollection(string collectionId)
 986    {
 0987        var wearablesToRemove = CatalogController.i.Wearables.GetValues()
 0988            .Where(wearable => !userProfile.HasEquipped(wearable.id)
 989                               && wearable.ThirdPartyCollectionId == collectionId)
 0990            .Select(item => item.id)
 991            .ToList();
 0992        CatalogController.i.Remove(wearablesToRemove);
 993
 0994        foreach (string wearableId in wearablesToRemove)
 995        {
 0996            userProfile.RemoveFromInventory(wearableId);
 0997            thirdPartyWearablesLoaded.Remove(wearableId);
 998        }
 999
 01000        LoadUserProfile(userProfile, true);
 01001    }
 1002
 1003    private bool ShouldShowHideOtherWearablesToast(WearableItem wearable)
 1004    {
 01005        var isWearingSkinAlready = model.wearables.Any(item => item.IsSkin());
 01006        return wearable.IsSkin() && !isWearingSkinAlready;
 1007    }
 1008
 1009    private bool IsTryingToReplaceSkin(WearableItem wearable)
 1010    {
 121011        return model.wearables.Any(skin =>
 1012        {
 801013            return skin.IsSkin()
 1014                   && skin.DoesHide(wearable.data.category, model.bodyShape.id);
 1015        });
 1016    }
 1017
 1018    private bool ShouldShowReplaceOtherWearablesToast(WearableItem wearable)
 1019    {
 01020        if (IsTryingToReplaceSkin(wearable)) return true;
 01021        var toReplace = GetWearablesReplacedBy(wearable);
 01022        if (wearable == null || toReplace.Count == 0) return false;
 01023        if (model.wearables.Contains(wearable)) return false;
 1024
 1025        // NOTE: why just 1?
 01026        if (toReplace.Count == 1)
 1027        {
 01028            var w = toReplace[0];
 01029            if (w.data.category == wearable.data.category)
 01030                return false;
 1031        }
 01032        return true;
 1033    }
 1034
 1035
 1036    private void HandleEmotesCostumizationSelection(bool current, bool previous)
 1037    {
 01038        if (!current)
 01039            return;
 1040
 01041        view.sectionSelector.GetSection(AvatarEditorHUDView.EMOTES_SECTION_INDEX).SelectToggle();
 01042    }
 1043
 1044    private void OnNewEmoteAdded(string emoteId)
 1045    {
 01046        if (!isAvatarPreviewReady)
 01047            return;
 1048
 01049        UpdateAvatarPreview(true);
 01050    }
 1051
 01052    private void OnPreviewEmote(string emoteId) { view.PlayPreviewEmote(emoteId); }
 1053
 1054    private void OnEmoteEquipped(string emoteId)
 1055    {
 01056        catalog.TryGetValue(emoteId, out WearableItem equippedEmote);
 1057
 01058        if (equippedEmote != null)
 01059            EquipEmote(equippedEmote);
 01060    }
 1061
 1062    private void OnEmoteUnequipped(string emoteId)
 1063    {
 01064        catalog.TryGetValue(emoteId, out WearableItem unequippedEmote);
 1065
 01066        if (unequippedEmote != null)
 01067            UnequipEmote(unequippedEmote);
 01068    }
 1069
 01070    private void OnRedirectToEmoteSelling(string emoteId) { SellCollectible(emoteId); }
 1071
 1072    internal void SendNewEquippedWearablesAnalytics(List<string> oldWearables, List<string> newWearables)
 1073    {
 281074        for (int i = 0; i < newWearables.Count; i++)
 1075        {
 121076            if (oldWearables.Contains(newWearables[i]))
 1077                continue;
 1078
 91079            catalog.TryGetValue(newWearables[i], out WearableItem newEquippedEmote);
 91080            if (newEquippedEmote != null && !newEquippedEmote.IsEmote())
 91081                SendEquipWearableAnalytic(newEquippedEmote);
 1082        }
 21083    }
 1084
 1085    private void SendEquipWearableAnalytic(WearableItem equippedWearable)
 1086    {
 91087        Dictionary<string, string> data = new Dictionary<string, string>();
 91088        data.Add("name", equippedWearable.GetName());
 91089        data.Add("rarity", equippedWearable.rarity);
 91090        data.Add("category", equippedWearable.data.category);
 91091        data.Add("linked_wearable", equippedWearable.IsFromThirdPartyCollection.ToString());
 91092        data.Add("third_party_collection_id", equippedWearable.ThirdPartyCollectionId);
 91093        data.Add("is_in_l2", equippedWearable.IsInL2().ToString());
 91094        data.Add("smart_item", equippedWearable.IsSmart().ToString());
 1095
 91096        analytics.SendAnalytic(EQUIP_WEARABLE_METRIC, data);
 91097    }
 1098
 481099    internal virtual IEmotesCustomizationComponentController CreateEmotesController() => new EmotesCustomizationComponen
 1100}

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])
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()