< 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:361
Uncovered lines:190
Coverable lines:551
Total lines:1099
Line coverage:65.5% (361 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%
LoadUserProfile(...)0%110100%
LoadOwnedWereables(...)0%15.555025%
LoadOwnedEmotes()0%7.057090%
LoadOwnedEmotesTask()0%81.0917039.47%
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 -= AddWearable;
 0149            this.catalog.OnRemoved -= RemoveWearable;
 150        }
 151
 48152        this.catalog = catalog;
 153
 48154        ProcessCatalog(this.catalog);
 48155        this.catalog.OnAdded += AddWearable;
 48156        this.catalog.OnRemoved += RemoveWearable;
 48157    }
 158
 159    private void LoadUserProfile(UserProfile userProfile)
 160    {
 43161        LoadUserProfile(userProfile, false);
 43162        QueryNftCollections(userProfile.userId);
 43163    }
 164
 165    private void LoadOwnedWereables(UserProfile userProfile)
 166    {
 167        // If there is more than 1 minute that we have checked the owned wearables, we try it again
 168        // This is done in order to retrieved the wearables after you has claimed them
 43169        if ((Time.realtimeSinceStartup < lastTimeOwnedWearablesChecked + 60 &&
 170             (ownedWearablesAlreadyLoaded ||
 171              ownedWearablesRemainingRequests <= 0)) ||
 172            string.IsNullOrEmpty(userProfile.userId))
 43173            return;
 174
 0175        view.ShowCollectiblesLoadingSpinner(true);
 0176        view.ShowCollectiblesLoadingRetry(false);
 0177        lastTimeOwnedWearablesChecked = Time.realtimeSinceStartup;
 178
 0179        loadingWearables = true;
 0180        CatalogController.RequestOwnedWearables(userProfile.userId)
 181                         .Then((ownedWearables) =>
 182                         {
 0183                             ownedWearablesAlreadyLoaded = true;
 184                             //Prior profile V1 emotes must be retrieved along the wearables, onwards they will be reque
 0185                             this.userProfile.SetInventory(ownedWearables.Select(x => x.id).Concat(thirdPartyWearablesLo
 0186                             LoadUserProfile(userProfile, true);
 0187                             if (userProfile != null && userProfile.avatar != null)
 188                             {
 0189                                 emotesLoadedAsWearables = ownedWearables.Where(x => x.IsEmote()).ToArray();
 190                             }
 0191                             view.ShowCollectiblesLoadingSpinner(false);
 0192                             view.ShowSkinPopulatedList(ownedWearables.Any(item => item.IsSkin()));
 0193                             loadingWearables = false;
 0194                         })
 195                         .Catch((error) =>
 196                         {
 0197                             ownedWearablesRemainingRequests--;
 0198                             if (ownedWearablesRemainingRequests > 0)
 199                             {
 0200                                 Debug.LogWarning("Retrying owned wereables loading...");
 0201                                 LoadOwnedWereables(userProfile);
 0202                             }
 203                             else
 204                             {
 0205                                 NotificationsController.i.ShowNotification(new Model
 206                                 {
 207                                     message = LOADING_OWNED_WEARABLES_ERROR_MESSAGE,
 208                                     type = Type.GENERIC,
 209                                     timer = 10f,
 210                                     destroyOnFinish = true
 211                                 });
 212
 0213                                 view.ShowCollectiblesLoadingSpinner(false);
 0214                                 view.ShowCollectiblesLoadingRetry(true);
 0215                                 Debug.LogError(error);
 0216                                 loadingWearables = false;
 217                             }
 0218                         });
 0219    }
 220
 221    private void LoadOwnedEmotes()
 222    {
 223        //Only check emotes once every 60 seconds
 43224        if (Time.realtimeSinceStartup < lastTimeOwnedEmotesChecked + 60)
 0225            return;
 226
 43227        lastTimeOwnedEmotesChecked = Time.realtimeSinceStartup;
 228        //TODO only request OwnedEmotes once every minute
 43229        loadEmotesCTS?.Cancel();
 43230        loadEmotesCTS?.Dispose();
 43231        loadEmotesCTS = null;
 232        // we only follow this flow with new profiles
 43233        if (userProfile?.avatar != null)
 234        {
 43235            loadEmotesCTS = new CancellationTokenSource();
 43236            LoadOwnedEmotesTask(loadEmotesCTS.Token);
 237        }
 43238    }
 239
 240    private async UniTaskVoid LoadOwnedEmotesTask(CancellationToken ct = default, int retries = LOADING_OWNED_WEARABLES_
 241    {
 43242        var emotesCatalog = Environment.i.serviceLocator.Get<IEmotesCatalogService>();
 243        try
 244        {
 43245            var embeddedEmotes = Resources.Load<EmbeddedEmotesSO>("EmbeddedEmotes").emotes;
 43246            var emotes = await emotesCatalog.RequestOwnedEmotesAsync(userProfile.userId, ct);
 43247            var emotesList = emotes == null ? embeddedEmotes.Cast<WearableItem>().ToList() : emotes.Concat(embeddedEmote
 43248            var emotesFilter = new HashSet<string>();
 1720249            foreach (var e in emotesList)
 817250                emotesFilter.Add(e.id);
 251
 43252            if(loadingWearables)
 0253                await UniTask.WaitWhile(() => loadingWearables, cancellationToken: ct);
 254
 43255            if (emotesLoadedAsWearables != null)
 256            {
 0257                foreach (var emoteAsWearable in emotesLoadedAsWearables)
 258                {
 0259                    if (emotesFilter.Contains(emoteAsWearable.id))
 260                        continue;
 261
 0262                    emotesList.Add(emoteAsWearable);
 263                }
 264
 0265                emotesLoadedAsWearables = null;
 266            }
 267
 43268            emotesCustomizationDataStore.UnequipMissingEmotes(emotesList);
 43269            emotesCustomizationComponentController.SetEmotes(emotesList.ToArray());
 270
 43271        }
 0272        catch (Exception e)
 273        {
 0274            OperationCanceledException opCanceled = e as OperationCanceledException;
 275            // If the cancellation was requested upwards, dont retry
 0276            if (opCanceled != null && ct.IsCancellationRequested)
 0277                return;
 278
 0279            if (retries > 0)
 280            {
 0281                LoadOwnedEmotesTask(ct, retries - 1);
 0282            }
 283            else
 284            {
 0285                if (opCanceled == null) // Ignore operation canceled exceptions when logging
 0286                    Debug.LogWarning(e.ToString());
 287                const string ERROR = "There was a problem loading your emotes";
 0288                NotificationsController.i.ShowNotification(new Model
 289                {
 290                    message = ERROR,
 291                    type = Type.GENERIC,
 292                    timer = 10f,
 293                    destroyOnFinish = true
 294                });
 0295                view.ShowCollectiblesLoadingRetry(true);
 296            }
 0297        }
 43298    }
 299
 300    private void QueryNftCollections(string userId)
 301    {
 43302        if (string.IsNullOrEmpty(userId))
 43303            return;
 304
 0305        Environment.i.platform.serviceProviders.theGraph.QueryNftCollections(userProfile.userId, NftCollectionsLayer.ETH
 0306           .Then((nfts) => ownedNftCollectionsL1 = nfts)
 0307           .Catch((error) => Debug.LogError(error));
 308
 0309        Environment.i.platform.serviceProviders.theGraph.QueryNftCollections(userProfile.userId, NftCollectionsLayer.MAT
 0310           .Then((nfts) => ownedNftCollectionsL2 = nfts)
 0311           .Catch((error) => Debug.LogError(error));
 0312    }
 313
 314    public void RetryLoadOwnedWearables()
 315    {
 0316        ownedWearablesRemainingRequests = LOADING_OWNED_WEARABLES_RETRIES;
 0317        LoadOwnedWereables(userProfile);
 0318        LoadOwnedEmotes();
 0319    }
 320
 321    private void PlayerRendererLoaded(bool current, bool previous)
 322    {
 15323        if (!current)
 0324            return;
 325
 15326        if (!ownedWearablesAlreadyLoaded)
 327        {
 15328            List<string> equippedOwnedWearables = new List<string>();
 78329            for (int i = 0; i < userProfile.avatar.wearables.Count; i++)
 330            {
 24331                if (catalog.TryGetValue(userProfile.avatar.wearables[i], out WearableItem wearable) &&
 332                    !wearable.data.tags.Contains(WearableLiterals.Tags.BASE_WEARABLE))
 333                {
 0334                    equippedOwnedWearables.Add(userProfile.avatar.wearables[i]);
 335                }
 336            }
 337
 15338            userProfile.SetInventory(equippedOwnedWearables.ToArray());
 339        }
 340
 15341        LoadUserProfile(userProfile, true);
 15342        DataStore.i.common.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 15343    }
 344
 345    public void LoadUserProfile(UserProfile userProfile, bool forceLoading)
 346    {
 106347        bool avatarEditorNotVisible = renderingEnabled && !view.isOpen;
 106348        bool isPlaying = !Application.isBatchMode;
 349
 106350        if (!forceLoading)
 351        {
 43352            if (isPlaying && avatarEditorNotVisible)
 0353                return;
 354        }
 355
 106356        if (userProfile == null)
 0357            return;
 358
 106359        if (userProfile.avatar == null || string.IsNullOrEmpty(userProfile.avatar.bodyShape))
 3360            return;
 361
 103362        view.InitializeNavigationEvents(!userProfile.hasConnectedWeb3);
 363
 103364        CatalogController.wearableCatalog.TryGetValue(userProfile.avatar.bodyShape, out var bodyShape);
 365
 103366        if (bodyShape == null)
 367        {
 0368            return;
 369        }
 370
 103371        view.SetIsWeb3(userProfile.hasConnectedWeb3);
 372
 103373        ProcessCatalog(this.catalog);
 103374        EquipBodyShape(bodyShape);
 103375        EquipSkinColor(userProfile.avatar.skinColor);
 103376        EquipHairColor(userProfile.avatar.hairColor);
 103377        EquipEyesColor(userProfile.avatar.eyeColor);
 378
 103379        model.wearables.Clear();
 103380        view.UnselectAllWearables();
 381
 103382        int wearablesCount = userProfile.avatar.wearables.Count;
 383
 103384        if (isPlayerRendererLoaded)
 385        {
 180386            for (var i = 0; i < wearablesCount; i++)
 387            {
 55388                CatalogController.wearableCatalog.TryGetValue(userProfile.avatar.wearables[i], out var wearable);
 55389                if (wearable == null)
 390                {
 0391                    Debug.LogError($"Couldn't find wearable with ID {userProfile.avatar.wearables[i]}");
 0392                    continue;
 393                }
 394
 55395                if (wearable.IsEmote())
 0396                    EquipEmote(wearable);
 397                else
 55398                    EquipWearable(wearable);
 399            }
 400        }
 401
 103402        EnsureWearablesCategoriesNotEmpty();
 403
 103404        UpdateAvatarPreview(true);
 103405        isAvatarPreviewReady = true;
 103406    }
 407
 408    private void EnsureWearablesCategoriesNotEmpty()
 409    {
 103410        var categoriesInUse = model.wearables
 55411            .Where(x => !x.IsEmote())
 55412            .Select(x => x.data.category).ToArray();
 413
 1648414        for (var i = 0; i < categoriesThatMustHaveSelection.Length; i++)
 415        {
 721416            var category = categoriesThatMustHaveSelection[i];
 721417            if (category != Categories.BODY_SHAPE && !(categoriesInUse.Contains(category)))
 418            {
 419                WearableItem wearable;
 576420                var defaultItemId = WearableLiterals.DefaultWearables.GetDefaultWearable(model.bodyShape.id, category);
 576421                if (defaultItemId != null)
 422                {
 576423                    CatalogController.wearableCatalog.TryGetValue(defaultItemId, out wearable);
 576424                }
 425                else
 426                {
 0427                    wearable = wearablesByCategory[category].FirstOrDefault(x => x.SupportsBodyShape(model.bodyShape.id)
 428                }
 429
 576430                if (wearable != null)
 431                {
 576432                    EquipWearable(wearable);
 433                }
 434            }
 435        }
 103436    }
 437
 438    public void WearableClicked(string wearableId)
 439    {
 20440        CatalogController.wearableCatalog.TryGetValue(wearableId, out var wearable);
 23441        if (wearable == null) return;
 442
 17443        if (wearable.data.category == Categories.BODY_SHAPE)
 444        {
 3445            if (wearable.id == model.bodyShape.id)
 0446                return;
 3447            EquipBodyShape(wearable);
 3448        }
 449        else
 450        {
 14451            if (model.wearables.Contains(wearable))
 452            {
 2453                if (!categoriesThatMustHaveSelection.Contains(wearable.data.category))
 454                {
 0455                    UnequipWearable(wearable);
 0456                }
 457                else
 458                {
 2459                    return;
 460                }
 461            }
 462            else
 463            {
 12464                if (IsTryingToReplaceSkin(wearable))
 0465                    UnequipWearable(model.GetWearable(Categories.SKIN));
 466
 12467                var sameCategoryEquipped = model.GetWearable(wearable.data.category);
 12468                if (sameCategoryEquipped != null)
 2469                    UnequipWearable(sameCategoryEquipped);
 470
 12471                EquipWearable(wearable);
 472            }
 473        }
 474
 15475        UpdateAvatarPreview(false);
 15476    }
 477
 478    public void HairColorClicked(Color color)
 479    {
 2480        EquipHairColor(color);
 2481        view.SelectHairColor(model.hairColor);
 2482        UpdateAvatarPreview(true);
 2483    }
 484
 485    public void SkinColorClicked(Color color)
 486    {
 2487        EquipSkinColor(color);
 2488        view.SelectSkinColor(model.skinColor);
 2489        UpdateAvatarPreview(true);
 2490    }
 491
 492    public void EyesColorClicked(Color color)
 493    {
 2494        EquipEyesColor(color);
 2495        view.SelectEyeColor(model.eyesColor);
 2496        UpdateAvatarPreview(true);
 2497    }
 498
 499    protected virtual void UpdateAvatarPreview(bool skipAudio)
 500    {
 125501        if (bypassUpdateAvatarPreview)
 0502            return;
 503
 125504        AvatarModel modelToUpdate = model.ToAvatarModel();
 505
 506        // We always keep the loaded emotes into the Avatar Preview
 5000507        foreach (string emoteId in emotesCustomizationDataStore.currentLoadedEmotes.Get())
 508        {
 2375509            modelToUpdate.emotes.Add(new AvatarModel.AvatarEmoteEntry() { urn = emoteId });
 510        }
 511
 125512        view.UpdateAvatarPreview(modelToUpdate, skipAudio);
 125513    }
 514
 515    private void EquipHairColor(Color color)
 516    {
 106517        model.hairColor = color;
 106518        view.SelectHairColor(model.hairColor);
 106519    }
 520
 521    private void EquipEyesColor(Color color)
 522    {
 106523        model.eyesColor = color;
 106524        view.SelectEyeColor(model.eyesColor);
 106525    }
 526
 527    private void EquipSkinColor(Color color)
 528    {
 105529        model.skinColor = color;
 105530        view.SelectSkinColor(model.skinColor);
 105531    }
 532
 533    private void EquipBodyShape(WearableItem bodyShape)
 534    {
 106535        if (bodyShape.data.category != Categories.BODY_SHAPE)
 536        {
 0537            Debug.LogError($"Item ({bodyShape.id} is not a body shape");
 0538            return;
 539        }
 540
 106541        if (model.bodyShape == bodyShape)
 53542            return;
 543
 53544        model.bodyShape = bodyShape;
 53545        emotesCustomizationComponentController.SetEquippedBodyShape(bodyShape.id);
 53546        view.UpdateSelectedBody(bodyShape);
 547
 53548        int wearablesCount = model.wearables.Count;
 164549        for (var i = wearablesCount - 1; i >= 0; i--)
 550        {
 29551            UnequipWearable(model.wearables[i]);
 552        }
 553
 53554        var defaultWearables = WearableLiterals.DefaultWearables.GetDefaultWearables(bodyShape.id);
 860555        for (var i = 0; i < defaultWearables.Length; i++)
 556        {
 377557            if (catalog.TryGetValue(defaultWearables[i], out var wearable))
 377558                EquipWearable(wearable);
 559        }
 53560    }
 561
 562    private void EquipWearable(WearableItem wearable)
 563    {
 1027564        if (wearable.IsEmote())
 0565            return;
 566
 1027567        if (!wearablesByCategory.ContainsKey(wearable.data.category))
 0568            return;
 569
 1027570        if (wearablesByCategory[wearable.data.category].Contains(wearable) && wearable.SupportsBodyShape(model.bodyShape
 571        {
 1027572            var toReplace = GetWearablesReplacedBy(wearable);
 1027573            toReplace.ForEach(UnequipWearable);
 1027574            model.wearables.Add(wearable);
 1027575            view.EquipWearable(wearable);
 1027576            avatarIsDirty = true;
 577        }
 1027578    }
 579
 580    private void UnequipWearable(WearableItem wearable)
 581    {
 33582        if (wearable.IsEmote())
 0583            return;
 584
 33585        if (model.wearables.Contains(wearable))
 586        {
 33587            model.wearables.Remove(wearable);
 33588            view.UnequipWearable(wearable);
 33589            avatarIsDirty = true;
 590        }
 33591    }
 592
 593    private void EquipEmote(WearableItem emote)
 594    {
 0595        if (!emote.IsEmote())
 0596            return;
 0597        avatarIsDirty = true;
 0598    }
 599
 600    private void UnequipEmote(WearableItem emote)
 601    {
 0602        if (!emote.IsEmote())
 0603            return;
 604
 0605        avatarIsDirty = true;
 0606    }
 607
 608    public void UnequipAllWearables()
 609    {
 1012610        foreach (var wearable in model.wearables)
 611        {
 431612            if (!wearable.IsEmote())
 431613                view.UnequipWearable(wearable);
 614        }
 615
 75616        model.wearables.Clear();
 75617    }
 618
 619    private void ProcessCatalog(BaseDictionary<string, WearableItem> catalog)
 620    {
 151621        wearablesByCategory.Clear();
 151622        view.RemoveAllWearables();
 151623        using (var iterator = catalog.Get().GetEnumerator())
 624        {
 5551625            while (iterator.MoveNext())
 626            {
 5400627                if (iterator.Current.Value.IsEmote())
 628                    continue;
 629
 5400630                AddWearable(iterator.Current.Key, iterator.Current.Value);
 631            }
 151632        }
 633
 151634        view.RefreshSelectorsSize();
 151635    }
 636
 637    private void AddWearable(string id, WearableItem wearable)
 638    {
 5411639        if (!wearable.data.tags.Contains(WearableLiterals.Tags.BASE_WEARABLE) && userProfile.GetItemAmount(id) == 0)
 1190640            return;
 641
 4221642        if (!wearablesByCategory.ContainsKey(wearable.data.category))
 1650643            wearablesByCategory.Add(wearable.data.category, new List<WearableItem>());
 644
 4221645        wearablesByCategory[wearable.data.category].Add(wearable);
 4221646        view.AddWearable(wearable, userProfile.GetItemAmount(id),
 647            ShouldShowHideOtherWearablesToast,
 648            ShouldShowReplaceOtherWearablesToast);
 4221649    }
 650
 651    private void RemoveWearable(string id, WearableItem wearable)
 652    {
 0653        if (wearablesByCategory.ContainsKey(wearable.data.category))
 654        {
 0655            if (wearablesByCategory[wearable.data.category].Remove(wearable))
 656            {
 0657                if (wearablesByCategory[wearable.data.category].Count == 0)
 658                {
 0659                    wearablesByCategory.Remove(wearable.data.category);
 660                }
 661            }
 662        }
 663
 0664        view.RemoveWearable(wearable);
 0665    }
 666
 667    public void RandomizeWearables()
 668    {
 1669        EquipHairColor(view.GetRandomColor());
 1670        EquipEyesColor(view.GetRandomColor());
 671
 7672        List<WearableItem> wearablesToRemove = model.wearables.Where(x => !x.IsEmote()).ToList();
 14673        foreach (var wearable in wearablesToRemove)
 674        {
 6675            model.wearables.Remove(wearable);
 676        }
 677
 1678        view.UnselectAllWearables();
 1679        using (var iterator = wearablesByCategory.GetEnumerator())
 680        {
 12681            while (iterator.MoveNext())
 682            {
 11683                string category = iterator.Current.Key;
 11684                if (!categoriesToRandomize.Contains(category))
 685                {
 686                    continue;
 687                }
 688
 29689                var supportedWearables = iterator.Current.Value.Where(x => x.SupportsBodyShape(model.bodyShape.id)).ToAr
 7690                if (supportedWearables.Length == 0)
 691                {
 0692                    Debug.LogError($"Couldn't get any wearable for category {category} and bodyshape {model.bodyShape.id
 693                }
 694
 7695                var wearable = supportedWearables[Random.Range(0, supportedWearables.Length - 1)];
 7696                EquipWearable(wearable);
 697            }
 1698        }
 699
 1700        UpdateAvatarPreview(false);
 1701    }
 702
 703    private List<WearableItem> GetWearablesReplacedBy(WearableItem wearableItem)
 704    {
 1027705        var wearablesToReplace = new List<WearableItem>();
 1027706        var categoriesToReplace = new HashSet<string>(wearableItem.GetReplacesList(model.bodyShape.id) ?? new string[0])
 707
 1027708        int wearableCount = model.wearables.Count;
 7820709        for (int i = 0; i < wearableCount; i++)
 710        {
 2883711            var wearable = model.wearables[i];
 2883712            if (wearable == null) continue;
 713
 2883714            if (categoriesToReplace.Contains(wearable.data.category))
 715            {
 2716                wearablesToReplace.Add(wearable);
 2717            }
 718            else
 719            {
 720                //For retrocompatibility's sake we check current wearables against new one (compatibility matrix is symm
 2881721                HashSet<string> replacesList = new HashSet<string>(wearable.GetReplacesList(model.bodyShape.id) ?? new s
 2881722                if (replacesList.Contains(wearableItem.data.category))
 723                {
 0724                    wearablesToReplace.Add(wearable);
 725                }
 726            }
 727        }
 728
 1027729        return wearablesToReplace;
 730    }
 731
 88732    public void SetVisibility(bool visible) { avatarEditorVisible.Set(visible); }
 733
 184734    private void OnAvatarEditorVisibleChanged(bool current, bool previous) { SetVisibility_Internal(current); }
 735
 736    private void SetVisibility_Internal(bool visible)
 737    {
 92738        bool isSignUpFlow = DataStore.i.common.isSignUpFlow.Get();
 92739        if (!visible && view.isOpen)
 740        {
 1741            view.ResetPreviewEmote();
 742
 1743            if (isSignUpFlow)
 0744                DataStore.i.virtualAudioMixer.sceneSFXVolume.Set(1f);
 745
 1746            Environment.i.messaging.manager.paused = false;
 1747            DataStore.i.skyboxConfig.avatarMatProfile.Set(AvatarMaterialProfile.InWorld);
 1748            if (prevMouseLockState && isSignUpFlow)
 749            {
 0750                Utils.LockCursor();
 751            }
 752
 753            // NOTE(Brian): SSAO doesn't work correctly with the offseted avatar preview if the renderScale != 1.0
 1754            var asset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
 1755            asset.renderScale = prevRenderScale;
 756
 1757            if (isSignUpFlow)
 0758                CommonScriptableObjects.isFullscreenHUDOpen.Set(false);
 759
 1760            DataStore.i.common.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 761
 1762            OnClose?.Invoke();
 0763        }
 91764        else if (visible && !view.isOpen)
 765        {
 43766            if (isSignUpFlow)
 767            {
 0768                DataStore.i.virtualAudioMixer.sceneSFXVolume.Set(0f);
 0769                view.sectionSelector.Hide(true);
 0770            }
 771            else
 772            {
 43773                view.sectionSelector.Show(true);
 774            }
 775
 43776            LoadOwnedWereables(userProfile);
 43777            if (!isSignUpFlow)
 43778                LoadOwnedEmotes();
 779
 43780            LoadCollections();
 43781            Environment.i.messaging.manager.paused = isSignUpFlow;
 43782            DataStore.i.skyboxConfig.avatarMatProfile.Set(AvatarMaterialProfile.InEditor);
 783
 43784            prevMouseLockState = Utils.IsCursorLocked;
 785
 43786            if (isSignUpFlow || !DataStore.i.exploreV2.isInitialized.Get())
 43787                Utils.UnlockCursor();
 788
 789            // NOTE(Brian): SSAO doesn't work correctly with the offseted avatar preview if the renderScale != 1.0
 43790            var asset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
 43791            prevRenderScale = asset.renderScale;
 43792            asset.renderScale = 1.0f;
 793
 43794            if (isSignUpFlow)
 0795                CommonScriptableObjects.isFullscreenHUDOpen.Set(true);
 796
 43797            DataStore.i.common.isPlayerRendererLoaded.OnChange += PlayerRendererLoaded;
 798
 43799            OnOpen?.Invoke();
 800        }
 801
 92802        view.SetVisibility(visible);
 92803    }
 804
 805    public void Dispose()
 806    {
 43807        loadEmotesCTS?.Cancel();
 43808        loadEmotesCTS?.Dispose();
 43809        loadEmotesCTS = null;
 810
 43811        avatarEditorVisible.OnChange -= OnAvatarEditorVisibleChanged;
 43812        configureBackpackInFullscreenMenu.OnChange -= ConfigureBackpackInFullscreenMenuChanged;
 43813        DataStore.i.common.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 43814        exploreV2IsOpen.OnChange -= ExploreV2IsOpenChanged;
 43815        emotesCustomizationDataStore.isEmotesCustomizationSelected.OnChange -= HandleEmotesCostumizationSelection;
 43816        emotesCustomizationDataStore.currentLoadedEmotes.OnAdded -= OnNewEmoteAdded;
 817
 43818        emotesCustomizationComponentController.onEmotePreviewed -= OnPreviewEmote;
 43819        emotesCustomizationComponentController.onEmoteEquipped -= OnEmoteEquipped;
 43820        emotesCustomizationComponentController.onEmoteUnequipped -= OnEmoteUnequipped;
 43821        emotesCustomizationComponentController.onEmoteSell -= OnRedirectToEmoteSelling;
 822
 43823        CleanUp();
 43824    }
 825
 826    public void CleanUp()
 827    {
 48828        UnequipAllWearables();
 829
 48830        if (view != null)
 48831            view.CleanUp();
 832
 48833        this.userProfile.OnUpdate -= LoadUserProfile;
 48834        this.catalog.OnAdded -= AddWearable;
 48835        this.catalog.OnRemoved -= RemoveWearable;
 48836        DataStore.i.common.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 48837    }
 838
 0839    public void SetConfiguration(HUDConfiguration configuration) { SetVisibility(configuration.active); }
 840
 841    public void SaveAvatar(Texture2D face256Snapshot, Texture2D bodySnapshot)
 842    {
 1843        var avatarModel = model.ToAvatarModel();
 844
 845        // Add the equipped emotes to the avatar model
 1846        List<AvatarModel.AvatarEmoteEntry> emoteEntries = new List<AvatarModel.AvatarEmoteEntry>();
 1847        int equippedEmotesCount = emotesCustomizationDataStore.unsavedEquippedEmotes.Count();
 22848        for (int i = 0; i < equippedEmotesCount; i++)
 849        {
 10850            var equippedEmote = emotesCustomizationDataStore.unsavedEquippedEmotes[i];
 10851            if (equippedEmote == null)
 852                continue;
 0853            emoteEntries.Add(new AvatarModel.AvatarEmoteEntry { slot = i, urn = equippedEmote.id });
 854        }
 855
 1856        avatarModel.emotes = emoteEntries;
 857
 1858        SendNewEquippedWearablesAnalytics(userProfile.avatar.wearables, avatarModel.wearables);
 1859        emotesCustomizationDataStore.equippedEmotes.Set(emotesCustomizationDataStore.unsavedEquippedEmotes.Get());
 860
 861
 1862        WebInterface.SendSaveAvatar(avatarModel, face256Snapshot, bodySnapshot, DataStore.i.common.isSignUpFlow.Get());
 1863        userProfile.OverrideAvatar(avatarModel, face256Snapshot);
 864
 1865        if (DataStore.i.common.isSignUpFlow.Get())
 866        {
 0867            DataStore.i.HUDs.signupVisible.Set(true);
 0868            newUserExperienceAnalytics.AvatarEditSuccessNux();
 869        }
 870
 871
 1872        avatarIsDirty = false;
 1873        SetVisibility(false);
 1874    }
 875
 876    public void GoToMarketplaceOrConnectWallet()
 877    {
 0878        if (userProfile.hasConnectedWeb3)
 0879            WebInterface.OpenURL(URL_MARKET_PLACE);
 880        else
 0881            WebInterface.OpenURL(URL_GET_A_WALLET);
 0882    }
 883
 884    public void SellCollectible(string collectibleId)
 885    {
 0886        var ownedCollectible = ownedNftCollectionsL1.FirstOrDefault(nft => nft.urn == collectibleId);
 0887        if (ownedCollectible == null)
 0888            ownedCollectible = ownedNftCollectionsL2.FirstOrDefault(nft => nft.urn == collectibleId);
 889
 0890        if (ownedCollectible != null)
 0891            WebInterface.OpenURL(URL_SELL_SPECIFIC_COLLECTIBLE.Replace("{collectionId}", ownedCollectible.collectionId).
 892        else
 0893            WebInterface.OpenURL(URL_SELL_COLLECTIBLE_GENERIC);
 0894    }
 895
 0896    public void ToggleVisibility() { SetVisibility(!view.isOpen); }
 897
 96898    private void ConfigureBackpackInFullscreenMenuChanged(Transform currentParentTransform, Transform previousParentTran
 899
 900    private void ExploreV2IsOpenChanged(bool current, bool previous)
 901    {
 0902        if (!current && avatarIsDirty)
 903        {
 0904            LoadUserProfile(userProfile, true);
 905
 0906            emotesCustomizationComponentController.RestoreEmoteSlots();
 907
 0908            avatarIsDirty = false;
 909        }
 0910    }
 911
 912    private void LoadCollections()
 913    {
 43914        if (!isThirdPartyCollectionsEnabled || collectionsAlreadyLoaded)
 43915            return;
 916
 0917        WearablesFetchingHelper.GetThirdPartyCollections()
 918            .Then((collections) =>
 919            {
 0920                view.LoadCollectionsDropdown(collections);
 0921                collectionsAlreadyLoaded = true;
 0922                LoadUserThirdPartyWearables();
 0923            })
 0924            .Catch((error) => Debug.LogError(error));
 0925    }
 926
 927    private void LoadUserThirdPartyWearables()
 928    {
 0929        List<string> collectionIdsToLoad = new List<string>();
 0930        foreach (string wearableId in userProfile.avatar.wearables)
 931        {
 0932            CatalogController.wearableCatalog.TryGetValue(wearableId, out var wearable);
 933
 0934            if (wearable != null && wearable.IsFromThirdPartyCollection)
 935            {
 0936                if (!collectionIdsToLoad.Contains(wearable.ThirdPartyCollectionId))
 0937                    collectionIdsToLoad.Add(wearable.ThirdPartyCollectionId);
 938            }
 939        }
 940
 0941        foreach (string collectionId in collectionIdsToLoad)
 942        {
 0943            view.ToggleThirdPartyCollection(collectionId, true);
 944        }
 0945    }
 946
 947    public void ToggleThirdPartyCollection(bool isOn, string collectionId, string collectionName)
 948    {
 1949        if (isOn)
 1950            FetchAndShowThirdPartyCollection(collectionId, collectionName);
 951        else
 0952            RemoveThirdPartyCollection(collectionId);
 0953    }
 954
 955    private void FetchAndShowThirdPartyCollection(string collectionId, string collectionName)
 956    {
 1957        view.BlockCollectionsDropdown(true);
 1958        CatalogController.RequestThirdPartyWearablesByCollection(userProfile.userId, collectionId)
 959            .Then(wearables =>
 960            {
 0961                if (wearables.Count().Equals(0)) view.ShowNoItemOfWearableCollectionWarning();
 962
 0963                foreach (var wearable in wearables)
 964                {
 0965                    if (!userProfile.ContainsInInventory(wearable.id))
 966                    {
 0967                        userProfile.AddToInventory(wearable.id);
 968
 0969                        if (!thirdPartyWearablesLoaded.Contains(wearable.id))
 0970                            thirdPartyWearablesLoaded.Add(wearable.id);
 971                    }
 972                }
 973
 0974                view.BlockCollectionsDropdown(false);
 0975                LoadUserProfile(userProfile, true);
 0976            })
 977            .Catch((error) =>
 978            {
 0979                view.BlockCollectionsDropdown(false);
 0980                Debug.LogError(error);
 0981            });
 1982    }
 983
 984    private void RemoveThirdPartyCollection(string collectionId)
 985    {
 0986        var wearablesToRemove = CatalogController.i.Wearables.GetValues()
 0987            .Where(wearable => !userProfile.HasEquipped(wearable.id)
 988                               && wearable.ThirdPartyCollectionId == collectionId)
 0989            .Select(item => item.id)
 990            .ToList();
 0991        CatalogController.i.Remove(wearablesToRemove);
 992
 0993        foreach (string wearableId in wearablesToRemove)
 994        {
 0995            userProfile.RemoveFromInventory(wearableId);
 0996            thirdPartyWearablesLoaded.Remove(wearableId);
 997        }
 998
 0999        LoadUserProfile(userProfile, true);
 01000    }
 1001
 1002    private bool ShouldShowHideOtherWearablesToast(WearableItem wearable)
 1003    {
 01004        var isWearingSkinAlready = model.wearables.Any(item => item.IsSkin());
 01005        return wearable.IsSkin() && !isWearingSkinAlready;
 1006    }
 1007
 1008    private bool IsTryingToReplaceSkin(WearableItem wearable)
 1009    {
 121010        return model.wearables.Any(skin =>
 1011        {
 801012            return skin.IsSkin()
 1013                   && skin.DoesHide(wearable.data.category, model.bodyShape.id);
 1014        });
 1015    }
 1016
 1017    private bool ShouldShowReplaceOtherWearablesToast(WearableItem wearable)
 1018    {
 01019        if (IsTryingToReplaceSkin(wearable)) return true;
 01020        var toReplace = GetWearablesReplacedBy(wearable);
 01021        if (wearable == null || toReplace.Count == 0) return false;
 01022        if (model.wearables.Contains(wearable)) return false;
 1023
 1024        // NOTE: why just 1?
 01025        if (toReplace.Count == 1)
 1026        {
 01027            var w = toReplace[0];
 01028            if (w.data.category == wearable.data.category)
 01029                return false;
 1030        }
 01031        return true;
 1032    }
 1033
 1034
 1035    private void HandleEmotesCostumizationSelection(bool current, bool previous)
 1036    {
 01037        if (!current)
 01038            return;
 1039
 01040        view.sectionSelector.GetSection(AvatarEditorHUDView.EMOTES_SECTION_INDEX).SelectToggle();
 01041    }
 1042
 1043    private void OnNewEmoteAdded(string emoteId)
 1044    {
 01045        if (!isAvatarPreviewReady)
 01046            return;
 1047
 01048        UpdateAvatarPreview(true);
 01049    }
 1050
 01051    private void OnPreviewEmote(string emoteId) { view.PlayPreviewEmote(emoteId); }
 1052
 1053    private void OnEmoteEquipped(string emoteId)
 1054    {
 01055        catalog.TryGetValue(emoteId, out WearableItem equippedEmote);
 1056
 01057        if (equippedEmote != null)
 01058            EquipEmote(equippedEmote);
 01059    }
 1060
 1061    private void OnEmoteUnequipped(string emoteId)
 1062    {
 01063        catalog.TryGetValue(emoteId, out WearableItem unequippedEmote);
 1064
 01065        if (unequippedEmote != null)
 01066            UnequipEmote(unequippedEmote);
 01067    }
 1068
 01069    private void OnRedirectToEmoteSelling(string emoteId) { SellCollectible(emoteId); }
 1070
 1071    internal void SendNewEquippedWearablesAnalytics(List<string> oldWearables, List<string> newWearables)
 1072    {
 281073        for (int i = 0; i < newWearables.Count; i++)
 1074        {
 121075            if (oldWearables.Contains(newWearables[i]))
 1076                continue;
 1077
 91078            catalog.TryGetValue(newWearables[i], out WearableItem newEquippedEmote);
 91079            if (newEquippedEmote != null && !newEquippedEmote.IsEmote())
 91080                SendEquipWearableAnalytic(newEquippedEmote);
 1081        }
 21082    }
 1083
 1084    private void SendEquipWearableAnalytic(WearableItem equippedWearable)
 1085    {
 91086        Dictionary<string, string> data = new Dictionary<string, string>();
 91087        data.Add("name", equippedWearable.GetName());
 91088        data.Add("rarity", equippedWearable.rarity);
 91089        data.Add("category", equippedWearable.data.category);
 91090        data.Add("linked_wearable", equippedWearable.IsFromThirdPartyCollection.ToString());
 91091        data.Add("third_party_collection_id", equippedWearable.ThirdPartyCollectionId);
 91092        data.Add("is_in_l2", equippedWearable.IsInL2().ToString());
 91093        data.Add("smart_item", equippedWearable.IsSmart().ToString());
 1094
 91095        analytics.SendAnalytic(EQUIP_WEARABLE_METRIC, data);
 91096    }
 1097
 481098    internal virtual IEmotesCustomizationComponentController CreateEmotesController() => new EmotesCustomizationComponen
 1099}

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