< 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:249
Uncovered lines:74
Coverable lines:323
Total lines:650
Line coverage:77% (249 of 323)
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%440100%
QueryNftCollections(...)0%7.464040%
RetryLoadOwnedWearables()0%2100%
PlayerRendererLoaded(...)0%7.056069.23%
LoadUserProfile(...)0%11.4211084.85%
EnsureWearablesCategoriesNotEmpty()0%7.027092.86%
WearableClicked(...)0%7.197084.21%
HairColorClicked(...)0%110100%
SkinColorClicked(...)0%110100%
EyesColorClicked(...)0%110100%
UpdateAvatarPreview()0%220100%
EquipHairColor(...)0%220100%
EquipEyesColor(...)0%220100%
EquipSkinColor(...)0%220100%
EquipBodyShape(...)0%6.046089.47%
EquipWearable(...)0%5.055087.5%
UnequipWearable(...)0%220100%
UnequipAllWearables()0%220100%
ProcessCatalog(...)0%330100%
AddWearable(...)0%440100%
RemoveWearable(...)0%20400%
RandomizeWearables()0%44093.75%
GetWearablesReplacedBy(...)0%7.017093.33%
SetVisibility(...)0%8.028093.1%
Dispose()0%110100%
CleanUp()0%220100%
SetConfiguration(...)0%2100%
SaveAvatar(...)0%2.012085.71%
DiscardAndClose()0%12300%
GoToMarketplace()0%6200%
SellCollectible(...)0%12300%
ToggleVisibility()0%2100%

File(s)

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

#LineLine coverage
 1using DCL;
 2using DCL.Helpers;
 3using DCL.Interface;
 4using System;
 5using System.Collections.Generic;
 6using System.Linq;
 7using UnityEngine;
 8using UnityEngine.Rendering;
 9using UnityEngine.Rendering.Universal;
 10using Categories = WearableLiterals.Categories;
 11
 12public class AvatarEditorHUDController : IHUD
 13{
 14    private const int LOADING_OWNED_WEARABLES_RETRIES = 3;
 15    private const string LOADING_OWNED_WEARABLES_ERROR_MESSAGE = "There was a problem loading your wearables";
 16    private const string URL_MARKET_PLACE = "https://market.decentraland.org/browse?section=wearables";
 17    private const string URL_GET_A_WALLET = "https://docs.decentraland.org/get-a-wallet";
 18    private const string URL_SELL_COLLECTIBLE_GENERIC = "https://market.decentraland.org/account";
 19    private const string URL_SELL_SPECIFIC_COLLECTIBLE = "https://market.decentraland.org/contracts/{collectionId}/token
 20
 121    protected static readonly string[] categoriesThatMustHaveSelection = { Categories.BODY_SHAPE, Categories.UPPER_BODY,
 122    protected static readonly string[] categoriesToRandomize = { Categories.HAIR, Categories.EYES, Categories.EYEBROWS, 
 23
 24    [NonSerialized]
 25    public bool bypassUpdateAvatarPreview = false;
 26
 27    private UserProfile userProfile;
 28    private BaseDictionary<string, WearableItem> catalog;
 8729    bool renderingEnabled => CommonScriptableObjects.rendererState.Get();
 030    bool isPlayerRendererLoaded => DataStore.i.isPlayerRendererLoaded.Get();
 4631    private readonly Dictionary<string, List<WearableItem>> wearablesByCategory = new Dictionary<string, List<WearableIt
 4632    protected readonly AvatarEditorHUDModel model = new AvatarEditorHUDModel();
 33
 34    private ColorList skinColorList;
 35    private ColorList eyeColorList;
 36    private ColorList hairColorList;
 37    private bool prevMouseLockState = false;
 4638    private int ownedWearablesRemainingRequests = LOADING_OWNED_WEARABLES_RETRIES;
 39    private bool ownedWearablesAlreadyLoaded = false;
 4640    private List<Nft> ownedNftCollectionsL1 = new List<Nft>();
 4641    private List<Nft> ownedNftCollectionsL2 = new List<Nft>();
 42
 43    public AvatarEditorHUDView view;
 44
 45    public event Action OnOpen;
 46    public event Action OnClose;
 47
 9248    public AvatarEditorHUDController() { }
 49
 50    public void Initialize(UserProfile userProfile, BaseDictionary<string, WearableItem> catalog, bool bypassUpdateAvata
 51    {
 4452        this.userProfile = userProfile;
 4453        this.bypassUpdateAvatarPreview = bypassUpdateAvatarPreview;
 54
 4455        view = AvatarEditorHUDView.Create(this);
 56
 4457        view.OnToggleActionTriggered += ToggleVisibility;
 4458        view.OnCloseActionTriggered += DiscardAndClose;
 59
 4460        skinColorList = Resources.Load<ColorList>("SkinTone");
 4461        hairColorList = Resources.Load<ColorList>("HairColor");
 4462        eyeColorList = Resources.Load<ColorList>("EyeColor");
 4463        view.SetColors(skinColorList.colors, hairColorList.colors, eyeColorList.colors);
 64
 4465        SetCatalog(catalog);
 66
 4467        LoadUserProfile(userProfile, true);
 4468        this.userProfile.OnUpdate += LoadUserProfile;
 4469        DataStore.i.isPlayerRendererLoaded.OnChange += PlayerRendererLoaded;
 4470    }
 71
 72    public void SetCatalog(BaseDictionary<string, WearableItem> catalog)
 73    {
 4474        if (this.catalog != null)
 75        {
 076            this.catalog.OnAdded -= AddWearable;
 077            this.catalog.OnRemoved -= RemoveWearable;
 78        }
 79
 4480        this.catalog = catalog;
 81
 4482        ProcessCatalog(this.catalog);
 4483        this.catalog.OnAdded += AddWearable;
 4484        this.catalog.OnRemoved += RemoveWearable;
 4485    }
 86
 87    private void LoadUserProfile(UserProfile userProfile)
 88    {
 4189        LoadUserProfile(userProfile, false);
 4190        QueryNftCollections(userProfile.userId);
 4191    }
 92
 93    private void LoadOwnedWereables(UserProfile userProfile)
 94    {
 3995        if (ownedWearablesAlreadyLoaded || ownedWearablesRemainingRequests <= 0 || string.IsNullOrEmpty(userProfile.user
 3896            return;
 97
 198        view.ShowCollectiblesLoadingSpinner(true);
 199        view.ShowCollectiblesLoadingRetry(false);
 1100        CatalogController.RequestOwnedWearables(userProfile.userId)
 101                         .Then((ownedWearables) =>
 102                         {
 0103                             ownedWearablesAlreadyLoaded = true;
 0104                             this.userProfile.SetInventory(ownedWearables.Select(x => x.id).ToArray());
 0105                             LoadUserProfile(userProfile, true);
 0106                             view.ShowCollectiblesLoadingSpinner(false);
 0107                         })
 108                         .Catch((error) =>
 109                         {
 0110                             ownedWearablesRemainingRequests--;
 0111                             if (ownedWearablesRemainingRequests > 0)
 112                             {
 0113                                 Debug.LogWarning("Retrying owned wereables loading...");
 0114                                 LoadOwnedWereables(userProfile);
 0115                             }
 116                             else
 117                             {
 0118                                 NotificationsController.i.ShowNotification(new Notification.Model
 119                                 {
 120                                     message = LOADING_OWNED_WEARABLES_ERROR_MESSAGE,
 121                                     type = NotificationFactory.Type.GENERIC,
 122                                     timer = 10f,
 123                                     destroyOnFinish = true
 124                                 });
 125
 0126                                 view.ShowCollectiblesLoadingSpinner(false);
 0127                                 view.ShowCollectiblesLoadingRetry(true);
 0128                                 Debug.LogError(error);
 129                             }
 0130                         });
 1131    }
 132
 133    private void QueryNftCollections(string userId)
 134    {
 41135        if (string.IsNullOrEmpty(userId))
 41136            return;
 137
 0138        DCL.Environment.i.platform.serviceProviders.theGraph.QueryNftCollections(userProfile.userId, NftCollectionsLayer
 0139           .Then((nfts) => ownedNftCollectionsL1 = nfts)
 0140           .Catch((error) => Debug.LogError(error));
 141
 0142        DCL.Environment.i.platform.serviceProviders.theGraph.QueryNftCollections(userProfile.userId, NftCollectionsLayer
 0143           .Then((nfts) => ownedNftCollectionsL2 = nfts)
 0144           .Catch((error) => Debug.LogError(error));
 0145    }
 146
 147    public void RetryLoadOwnedWearables()
 148    {
 0149        ownedWearablesRemainingRequests = LOADING_OWNED_WEARABLES_RETRIES;
 0150        LoadOwnedWereables(userProfile);
 0151    }
 152
 153    private void PlayerRendererLoaded(bool current, bool previous)
 154    {
 2155        if (!current)
 0156            return;
 157
 2158        if (!ownedWearablesAlreadyLoaded)
 159        {
 2160            List<string> equippedOwnedWearables = new List<string>();
 4161            for (int i = 0; i < userProfile.avatar.wearables.Count; i++)
 162            {
 0163                if (catalog.TryGetValue(userProfile.avatar.wearables[i], out WearableItem wearable) &&
 164                    !wearable.data.tags.Contains("base-wearable"))
 165                {
 0166                    equippedOwnedWearables.Add(userProfile.avatar.wearables[i]);
 167                }
 168            }
 169
 2170            userProfile.SetInventory(equippedOwnedWearables.ToArray());
 171        }
 172
 2173        LoadUserProfile(userProfile, true);
 2174        DataStore.i.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 2175    }
 176
 177    public void LoadUserProfile(UserProfile userProfile, bool forceLoading)
 178    {
 87179        bool avatarEditorNotVisible = renderingEnabled && !view.isOpen;
 87180        bool isPlaying = !Application.isBatchMode;
 181
 87182        if (!forceLoading)
 183        {
 41184            if (isPlaying && avatarEditorNotVisible)
 0185                return;
 186        }
 187
 87188        if (userProfile == null)
 0189            return;
 190
 87191        if (userProfile.avatar == null || string.IsNullOrEmpty(userProfile.avatar.bodyShape))
 3192            return;
 193
 84194        CatalogController.wearableCatalog.TryGetValue(userProfile.avatar.bodyShape, out var bodyShape);
 195
 84196        if (bodyShape == null)
 197        {
 0198            return;
 199        }
 200
 84201        view.SetIsWeb3(userProfile.hasConnectedWeb3);
 202
 84203        ProcessCatalog(this.catalog);
 84204        EquipBodyShape(bodyShape);
 84205        EquipSkinColor(userProfile.avatar.skinColor);
 84206        EquipHairColor(userProfile.avatar.hairColor);
 84207        EquipEyesColor(userProfile.avatar.eyeColor);
 208
 84209        model.wearables.Clear();
 84210        view.UnselectAllWearables();
 211
 84212        int wearablesCount = userProfile.avatar.wearables.Count;
 213
 84214        if (isPlayerRendererLoaded)
 215        {
 184216            for (var i = 0; i < wearablesCount; i++)
 217            {
 53218                CatalogController.wearableCatalog.TryGetValue(userProfile.avatar.wearables[i], out var wearable);
 53219                if (wearable == null)
 220                {
 0221                    Debug.LogError($"Couldn't find wearable with ID {userProfile.avatar.wearables[i]}");
 0222                    continue;
 223                }
 224
 53225                EquipWearable(wearable);
 226            }
 227        }
 228
 84229        EnsureWearablesCategoriesNotEmpty();
 230
 84231        UpdateAvatarPreview();
 84232    }
 233
 234    private void EnsureWearablesCategoriesNotEmpty()
 235    {
 137236        var categoriesInUse = model.wearables.Select(x => x.data.category).ToArray();
 1344237        for (var i = 0; i < categoriesThatMustHaveSelection.Length; i++)
 238        {
 588239            var category = categoriesThatMustHaveSelection[i];
 588240            if (category != Categories.BODY_SHAPE && !(categoriesInUse.Contains(category)))
 241            {
 242                WearableItem wearable;
 462243                var defaultItemId = WearableLiterals.DefaultWearables.GetDefaultWearable(model.bodyShape.id, category);
 462244                if (defaultItemId != null)
 245                {
 462246                    CatalogController.wearableCatalog.TryGetValue(defaultItemId, out wearable);
 462247                }
 248                else
 249                {
 0250                    wearable = wearablesByCategory[category].FirstOrDefault(x => x.SupportsBodyShape(model.bodyShape.id)
 251                }
 252
 462253                if (wearable != null)
 254                {
 462255                    EquipWearable(wearable);
 256                }
 257            }
 258        }
 84259    }
 260
 261    public void WearableClicked(string wearableId)
 262    {
 20263        CatalogController.wearableCatalog.TryGetValue(wearableId, out var wearable);
 20264        if (wearable == null)
 3265            return;
 266
 17267        if (wearable.data.category == Categories.BODY_SHAPE)
 268        {
 3269            if (wearable.id == model.bodyShape.id)
 0270                return;
 3271            EquipBodyShape(wearable);
 3272        }
 273        else
 274        {
 14275            if (model.wearables.Contains(wearable))
 276            {
 2277                if (!categoriesThatMustHaveSelection.Contains(wearable.data.category))
 278                {
 0279                    UnequipWearable(wearable);
 0280                }
 281                else
 282                {
 2283                    return;
 284                }
 285            }
 286            else
 287            {
 91288                var sameCategoryEquipped = model.wearables.FirstOrDefault(x => x.data.category == wearable.data.category
 12289                if (sameCategoryEquipped != null)
 290                {
 2291                    UnequipWearable(sameCategoryEquipped);
 292                }
 293
 12294                EquipWearable(wearable);
 295            }
 296        }
 297
 15298        UpdateAvatarPreview();
 15299    }
 300
 301    public void HairColorClicked(Color color)
 302    {
 2303        EquipHairColor(color);
 2304        view.SelectHairColor(model.hairColor);
 2305        UpdateAvatarPreview();
 2306    }
 307
 308    public void SkinColorClicked(Color color)
 309    {
 2310        EquipSkinColor(color);
 2311        view.SelectSkinColor(model.skinColor);
 2312        UpdateAvatarPreview();
 2313    }
 314
 315    public void EyesColorClicked(Color color)
 316    {
 2317        EquipEyesColor(color);
 2318        view.SelectEyeColor(model.eyesColor);
 2319        UpdateAvatarPreview();
 2320    }
 321
 322    protected virtual void UpdateAvatarPreview()
 323    {
 106324        if (!bypassUpdateAvatarPreview)
 106325            view.UpdateAvatarPreview(model.ToAvatarModel());
 106326    }
 327
 328    private void EquipHairColor(Color color)
 329    {
 87330        var colorToSet = color;
 880331        if (!hairColorList.colors.Any(x => x.AproxComparison(colorToSet)))
 332        {
 78333            colorToSet = hairColorList.colors[hairColorList.defaultColor];
 334        }
 335
 87336        model.hairColor = colorToSet;
 87337        view.SelectHairColor(model.hairColor);
 87338    }
 339
 340    private void EquipEyesColor(Color color)
 341    {
 87342        var colorToSet = color;
 886343        if (!eyeColorList.colors.Any(x => x.AproxComparison(color)))
 344        {
 78345            colorToSet = eyeColorList.colors[eyeColorList.defaultColor];
 346        }
 347
 87348        model.eyesColor = colorToSet;
 87349        view.SelectEyeColor(model.eyesColor);
 87350    }
 351
 352    private void EquipSkinColor(Color color)
 353    {
 86354        var colorToSet = color;
 877355        if (!skinColorList.colors.Any(x => x.AproxComparison(colorToSet)))
 356        {
 78357            colorToSet = skinColorList.colors[skinColorList.defaultColor];
 358        }
 359
 86360        model.skinColor = colorToSet;
 86361        view.SelectSkinColor(model.skinColor);
 86362    }
 363
 364    private void EquipBodyShape(WearableItem bodyShape)
 365    {
 87366        if (bodyShape.data.category != Categories.BODY_SHAPE)
 367        {
 0368            Debug.LogError($"Item ({bodyShape.id} is not a body shape");
 0369            return;
 370        }
 371
 87372        if (model.bodyShape == bodyShape)
 37373            return;
 374
 50375        model.bodyShape = bodyShape;
 50376        view.UpdateSelectedBody(bodyShape);
 377
 50378        int wearablesCount = model.wearables.Count;
 172379        for (var i = wearablesCount - 1; i >= 0; i--)
 380        {
 36381            UnequipWearable(model.wearables[i]);
 382        }
 383
 50384        var defaultWearables = WearableLiterals.DefaultWearables.GetDefaultWearables(bodyShape.id);
 812385        for (var i = 0; i < defaultWearables.Length; i++)
 386        {
 356387            if (catalog.TryGetValue(defaultWearables[i], out var wearable))
 356388                EquipWearable(wearable);
 389        }
 50390    }
 391
 392    private void EquipWearable(WearableItem wearable)
 393    {
 890394        if (!wearablesByCategory.ContainsKey(wearable.data.category))
 0395            return;
 396
 890397        if (wearablesByCategory[wearable.data.category].Contains(wearable) && wearable.SupportsBodyShape(model.bodyShape
 398        {
 890399            var toReplace = GetWearablesReplacedBy(wearable);
 890400            toReplace.ForEach(UnequipWearable);
 890401            model.wearables.Add(wearable);
 890402            view.EquipWearable(wearable);
 403        }
 890404    }
 405
 406    private void UnequipWearable(WearableItem wearable)
 407    {
 40408        if (model.wearables.Contains(wearable))
 409        {
 40410            model.wearables.Remove(wearable);
 40411            view.UnequipWearable(wearable);
 412        }
 40413    }
 414
 415    public void UnequipAllWearables()
 416    {
 926417        foreach (var wearable in model.wearables)
 418        {
 395419            view.UnequipWearable(wearable);
 420        }
 421
 68422        model.wearables.Clear();
 68423    }
 424
 425    private void ProcessCatalog(BaseDictionary<string, WearableItem> catalog)
 426    {
 128427        wearablesByCategory.Clear();
 128428        view.RemoveAllWearables();
 128429        using (var iterator = catalog.Get().GetEnumerator())
 430        {
 4446431            while (iterator.MoveNext())
 432            {
 4318433                AddWearable(iterator.Current.Key, iterator.Current.Value);
 434            }
 128435        }
 128436    }
 437
 438    private void AddWearable(string id, WearableItem wearable)
 439    {
 4324440        if (!wearable.data.tags.Contains("base-wearable") && userProfile.GetItemAmount(id) == 0)
 441        {
 752442            return;
 443        }
 444
 3572445        if (!wearablesByCategory.ContainsKey(wearable.data.category))
 446        {
 1397447            wearablesByCategory.Add(wearable.data.category, new List<WearableItem>());
 448        }
 449
 3572450        wearablesByCategory[wearable.data.category].Add(wearable);
 3572451        view.AddWearable(wearable, userProfile.GetItemAmount(id));
 3572452    }
 453
 454    private void RemoveWearable(string id, WearableItem wearable)
 455    {
 0456        if (wearablesByCategory.ContainsKey(wearable.data.category))
 457        {
 0458            if (wearablesByCategory[wearable.data.category].Remove(wearable))
 459            {
 0460                if (wearablesByCategory[wearable.data.category].Count == 0)
 461                {
 0462                    wearablesByCategory.Remove(wearable.data.category);
 463                }
 464            }
 465        }
 466
 0467        view.RemoveWearable(wearable);
 0468    }
 469
 470    public void RandomizeWearables()
 471    {
 1472        EquipHairColor(hairColorList.colors[UnityEngine.Random.Range(0, hairColorList.colors.Count)]);
 1473        EquipEyesColor(eyeColorList.colors[UnityEngine.Random.Range(0, eyeColorList.colors.Count)]);
 474
 1475        model.wearables.Clear();
 1476        view.UnselectAllWearables();
 1477        using (var iterator = wearablesByCategory.GetEnumerator())
 478        {
 12479            while (iterator.MoveNext())
 480            {
 11481                string category = iterator.Current.Key;
 11482                if (!categoriesToRandomize.Contains(category))
 483                {
 484                    continue;
 485                }
 486
 29487                var supportedWearables = iterator.Current.Value.Where(x => x.SupportsBodyShape(model.bodyShape.id)).ToAr
 7488                if (supportedWearables.Length == 0)
 489                {
 0490                    Debug.LogError($"Couldn't get any wearable for category {category} and bodyshape {model.bodyShape.id
 491                }
 492
 7493                var wearable = supportedWearables[UnityEngine.Random.Range(0, supportedWearables.Length - 1)];
 7494                EquipWearable(wearable);
 495            }
 1496        }
 497
 1498        UpdateAvatarPreview();
 1499    }
 500
 501    public List<WearableItem> GetWearablesReplacedBy(WearableItem wearableItem)
 502    {
 890503        List<WearableItem> wearablesToReplace = new List<WearableItem>();
 504
 890505        HashSet<string> categoriesToReplace = new HashSet<string>(wearableItem.GetReplacesList(model.bodyShape.id) ?? ne
 506
 890507        int wearableCount = model.wearables.Count;
 6820508        for (int i = 0; i < wearableCount; i++)
 509        {
 2520510            var wearable = model.wearables[i];
 2520511            if (wearable == null)
 512                continue;
 513
 2520514            if (categoriesToReplace.Contains(wearable.data.category))
 515            {
 2516                wearablesToReplace.Add(wearable);
 2517            }
 518            else
 519            {
 520                //For retrocompatibility's sake we check current wearables against new one (compatibility matrix is symm
 2518521                HashSet<string> replacesList = new HashSet<string>(wearable.GetReplacesList(model.bodyShape.id) ?? new s
 2518522                if (replacesList.Contains(wearableItem.data.category))
 523                {
 0524                    wearablesToReplace.Add(wearable);
 525                }
 526            }
 527        }
 528
 890529        return wearablesToReplace;
 530    }
 531
 46532    private float prevRenderScale = 1.0f;
 533    private Camera mainCamera;
 534
 535    public void SetVisibility(bool visible)
 536    {
 40537        var currentRenderProfile = DCL.RenderProfileManifest.i.currentProfile;
 538
 40539        if (!visible && view.isOpen)
 540        {
 1541            DataStore.i.virtualAudioMixer.sceneSFXVolume.Set(1f);
 1542            DCL.Environment.i.messaging.manager.paused = false;
 1543            currentRenderProfile.avatarProfile.currentProfile = currentRenderProfile.avatarProfile.inWorld;
 1544            currentRenderProfile.avatarProfile.Apply();
 1545            if (prevMouseLockState)
 546            {
 0547                Utils.LockCursor();
 548            }
 549
 550            // NOTE(Brian): SSAO doesn't work correctly with the offseted avatar preview if the renderScale != 1.0
 1551            var asset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
 1552            asset.renderScale = prevRenderScale;
 553
 1554            CommonScriptableObjects.isFullscreenHUDOpen.Set(false);
 1555            OnClose?.Invoke();
 0556        }
 39557        else if (visible && !view.isOpen)
 558        {
 39559            DataStore.i.virtualAudioMixer.sceneSFXVolume.Set(0f);
 39560            LoadOwnedWereables(userProfile);
 39561            DCL.Environment.i.messaging.manager.paused = DataStore.i.isSignUpFlow.Get();
 39562            currentRenderProfile.avatarProfile.currentProfile = currentRenderProfile.avatarProfile.avatarEditor;
 39563            currentRenderProfile.avatarProfile.Apply();
 564
 39565            prevMouseLockState = Utils.isCursorLocked;
 39566            Utils.UnlockCursor();
 567
 568            // NOTE(Brian): SSAO doesn't work correctly with the offseted avatar preview if the renderScale != 1.0
 39569            var asset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
 39570            prevRenderScale = asset.renderScale;
 39571            asset.renderScale = 1.0f;
 572
 39573            CommonScriptableObjects.isFullscreenHUDOpen.Set(true);
 39574            OnOpen?.Invoke();
 575        }
 576
 40577        currentRenderProfile.avatarProfile.Apply();
 40578        view.SetVisibility(visible);
 40579    }
 580
 581    public void Dispose()
 582    {
 39583        view.OnToggleActionTriggered -= ToggleVisibility;
 39584        view.OnCloseActionTriggered -= DiscardAndClose;
 585
 39586        CleanUp();
 39587    }
 588
 589    public void CleanUp()
 590    {
 44591        UnequipAllWearables();
 592
 44593        if (view != null)
 44594            view.CleanUp();
 595
 44596        this.userProfile.OnUpdate -= LoadUserProfile;
 44597        this.catalog.OnAdded -= AddWearable;
 44598        this.catalog.OnRemoved -= RemoveWearable;
 44599        DataStore.i.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 44600    }
 601
 0602    public void SetConfiguration(HUDConfiguration configuration) { SetVisibility(configuration.active); }
 603
 604    public void SaveAvatar(Texture2D faceSnapshot, Texture2D face128Snapshot, Texture2D face256Snapshot, Texture2D bodyS
 605    {
 1606        var avatarModel = model.ToAvatarModel();
 607
 1608        WebInterface.SendSaveAvatar(avatarModel, faceSnapshot, face128Snapshot, face256Snapshot, bodySnapshot, DataStore
 1609        userProfile.OverrideAvatar(avatarModel, face256Snapshot);
 1610        if (DataStore.i.isSignUpFlow.Get())
 0611            DataStore.i.HUDs.signupVisible.Set(true);
 612
 1613        SetVisibility(false);
 1614    }
 615
 616    public void DiscardAndClose()
 617    {
 0618        if (!view.isOpen)
 0619            return;
 620
 0621        if (!DataStore.i.isSignUpFlow.Get())
 0622            LoadUserProfile(userProfile);
 623        else
 0624            WebInterface.SendCloseUserAvatar(true);
 625
 0626        SetVisibility(false);
 0627    }
 628
 629    public void GoToMarketplace()
 630    {
 0631        if (userProfile.hasConnectedWeb3)
 0632            WebInterface.OpenURL(URL_MARKET_PLACE);
 633        else
 0634            WebInterface.OpenURL(URL_GET_A_WALLET);
 0635    }
 636
 637    public void SellCollectible(string collectibleId)
 638    {
 0639        var ownedCollectible = ownedNftCollectionsL1.FirstOrDefault(nft => nft.urn == collectibleId);
 0640        if (ownedCollectible == null)
 0641            ownedCollectible = ownedNftCollectionsL2.FirstOrDefault(nft => nft.urn == collectibleId);
 642
 0643        if (ownedCollectible != null)
 0644            WebInterface.OpenURL(URL_SELL_SPECIFIC_COLLECTIBLE.Replace("{collectionId}", ownedCollectible.collectionId).
 645        else
 0646            WebInterface.OpenURL(URL_SELL_COLLECTIBLE_GENERIC);
 0647    }
 648
 0649    public void ToggleVisibility() { SetVisibility(!view.isOpen); }
 650}