< 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:252
Uncovered lines:73
Coverable lines:325
Total lines:654
Line coverage:77.5% (252 of 325)
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%6.136084.62%
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.55%
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;
 9829    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    }
 70
 71    public void SetCatalog(BaseDictionary<string, WearableItem> catalog)
 72    {
 4473        if (this.catalog != null)
 74        {
 075            this.catalog.OnAdded -= AddWearable;
 076            this.catalog.OnRemoved -= RemoveWearable;
 77        }
 78
 4479        this.catalog = catalog;
 80
 4481        ProcessCatalog(this.catalog);
 4482        this.catalog.OnAdded += AddWearable;
 4483        this.catalog.OnRemoved += RemoveWearable;
 4484    }
 85
 86    private void LoadUserProfile(UserProfile userProfile)
 87    {
 4088        LoadUserProfile(userProfile, false);
 4089        QueryNftCollections(userProfile.userId);
 4090    }
 91
 92    private void LoadOwnedWereables(UserProfile userProfile)
 93    {
 3994        if (ownedWearablesAlreadyLoaded || ownedWearablesRemainingRequests <= 0 || string.IsNullOrEmpty(userProfile.user
 3895            return;
 96
 197        view.ShowCollectiblesLoadingSpinner(true);
 198        view.ShowCollectiblesLoadingRetry(false);
 199        CatalogController.RequestOwnedWearables(userProfile.userId)
 100            .Then((ownedWearables) =>
 101            {
 0102                ownedWearablesAlreadyLoaded = true;
 0103                this.userProfile.SetInventory(ownedWearables.Select(x => x.id).ToArray());
 0104                LoadUserProfile(userProfile, true);
 0105                view.ShowCollectiblesLoadingSpinner(false);
 0106            })
 107            .Catch((error) =>
 108            {
 0109                ownedWearablesRemainingRequests--;
 0110                if (ownedWearablesRemainingRequests > 0)
 111                {
 0112                    Debug.LogWarning("Retrying owned wereables loading...");
 0113                    LoadOwnedWereables(userProfile);
 0114                }
 115                else
 116                {
 0117                    NotificationsController.i.ShowNotification(new DCL.NotificationModel.Model
 118                    {
 119                        message = LOADING_OWNED_WEARABLES_ERROR_MESSAGE,
 120                        type = DCL.NotificationModel.Type.GENERIC,
 121                        timer = 10f,
 122                        destroyOnFinish = true
 123                    });
 124
 0125                    view.ShowCollectiblesLoadingSpinner(false);
 0126                    view.ShowCollectiblesLoadingRetry(true);
 0127                    Debug.LogError(error);
 128                }
 0129            });
 1130    }
 131
 132    private void QueryNftCollections(string userId)
 133    {
 40134        if (string.IsNullOrEmpty(userId))
 40135            return;
 136
 0137        DCL.Environment.i.platform.serviceProviders.theGraph.QueryNftCollections(userProfile.userId, NftCollectionsLayer
 0138            .Then((nfts) => ownedNftCollectionsL1 = nfts)
 0139            .Catch((error) => Debug.LogError(error));
 140
 0141        DCL.Environment.i.platform.serviceProviders.theGraph.QueryNftCollections(userProfile.userId, NftCollectionsLayer
 0142            .Then((nfts) => ownedNftCollectionsL2 = nfts)
 0143            .Catch((error) => Debug.LogError(error));
 0144    }
 145
 146    public void RetryLoadOwnedWearables()
 147    {
 0148        ownedWearablesRemainingRequests = LOADING_OWNED_WEARABLES_RETRIES;
 0149        LoadOwnedWereables(userProfile);
 0150    }
 151
 152    private void PlayerRendererLoaded(bool current, bool previous)
 153    {
 14154        if (!current)
 0155            return;
 156
 14157        if (!ownedWearablesAlreadyLoaded)
 158        {
 14159            List<string> equippedOwnedWearables = new List<string>();
 58160            for (int i = 0; i < userProfile.avatar.wearables.Count; i++)
 161            {
 15162                if (catalog.TryGetValue(userProfile.avatar.wearables[i], out WearableItem wearable) &&
 163                    !wearable.data.tags.Contains("base-wearable"))
 164                {
 0165                    equippedOwnedWearables.Add(userProfile.avatar.wearables[i]);
 166                }
 167            }
 168
 14169            userProfile.SetInventory(equippedOwnedWearables.ToArray());
 170        }
 171
 14172        LoadUserProfile(userProfile, true);
 14173        DataStore.i.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 14174    }
 175
 176    public void LoadUserProfile(UserProfile userProfile, bool forceLoading)
 177    {
 98178        bool avatarEditorNotVisible = renderingEnabled && !view.isOpen;
 98179        bool isPlaying = !Application.isBatchMode;
 180
 98181        if (!forceLoading)
 182        {
 40183            if (isPlaying && avatarEditorNotVisible)
 0184                return;
 185        }
 186
 98187        if (userProfile == null)
 0188            return;
 189
 98190        if (userProfile.avatar == null || string.IsNullOrEmpty(userProfile.avatar.bodyShape))
 3191            return;
 192
 95193        CatalogController.wearableCatalog.TryGetValue(userProfile.avatar.bodyShape, out var bodyShape);
 194
 95195        if (bodyShape == null)
 196        {
 0197            return;
 198        }
 199
 95200        view.SetIsWeb3(userProfile.hasConnectedWeb3);
 201
 95202        ProcessCatalog(this.catalog);
 95203        EquipBodyShape(bodyShape);
 95204        EquipSkinColor(userProfile.avatar.skinColor);
 95205        EquipHairColor(userProfile.avatar.hairColor);
 95206        EquipEyesColor(userProfile.avatar.eyeColor);
 207
 95208        model.wearables.Clear();
 95209        view.UnselectAllWearables();
 210
 95211        int wearablesCount = userProfile.avatar.wearables.Count;
 212
 95213        if (isPlayerRendererLoaded)
 214        {
 158215            for (var i = 0; i < wearablesCount; i++)
 216            {
 46217                CatalogController.wearableCatalog.TryGetValue(userProfile.avatar.wearables[i], out var wearable);
 46218                if (wearable == null)
 219                {
 0220                    Debug.LogError($"Couldn't find wearable with ID {userProfile.avatar.wearables[i]}");
 0221                    continue;
 222                }
 223
 46224                EquipWearable(wearable);
 225            }
 226        }
 227
 95228        EnsureWearablesCategoriesNotEmpty();
 229
 95230        UpdateAvatarPreview();
 95231    }
 232
 233    private void EnsureWearablesCategoriesNotEmpty()
 234    {
 141235        var categoriesInUse = model.wearables.Select(x => x.data.category).ToArray();
 1520236        for (var i = 0; i < categoriesThatMustHaveSelection.Length; i++)
 237        {
 665238            var category = categoriesThatMustHaveSelection[i];
 665239            if (category != Categories.BODY_SHAPE && !(categoriesInUse.Contains(category)))
 240            {
 241                WearableItem wearable;
 534242                var defaultItemId = WearableLiterals.DefaultWearables.GetDefaultWearable(model.bodyShape.id, category);
 534243                if (defaultItemId != null)
 244                {
 534245                    CatalogController.wearableCatalog.TryGetValue(defaultItemId, out wearable);
 534246                }
 247                else
 248                {
 0249                    wearable = wearablesByCategory[category].FirstOrDefault(x => x.SupportsBodyShape(model.bodyShape.id)
 250                }
 251
 534252                if (wearable != null)
 253                {
 534254                    EquipWearable(wearable);
 255                }
 256            }
 257        }
 95258    }
 259
 260    public void WearableClicked(string wearableId)
 261    {
 20262        CatalogController.wearableCatalog.TryGetValue(wearableId, out var wearable);
 20263        if (wearable == null)
 3264            return;
 265
 17266        if (wearable.data.category == Categories.BODY_SHAPE)
 267        {
 3268            if (wearable.id == model.bodyShape.id)
 0269                return;
 3270            EquipBodyShape(wearable);
 3271        }
 272        else
 273        {
 14274            if (model.wearables.Contains(wearable))
 275            {
 2276                if (!categoriesThatMustHaveSelection.Contains(wearable.data.category))
 277                {
 0278                    UnequipWearable(wearable);
 0279                }
 280                else
 281                {
 2282                    return;
 283                }
 284            }
 285            else
 286            {
 91287                var sameCategoryEquipped = model.wearables.FirstOrDefault(x => x.data.category == wearable.data.category
 12288                if (sameCategoryEquipped != null)
 289                {
 2290                    UnequipWearable(sameCategoryEquipped);
 291                }
 292
 12293                EquipWearable(wearable);
 294            }
 295        }
 296
 15297        UpdateAvatarPreview();
 15298    }
 299
 300    public void HairColorClicked(Color color)
 301    {
 2302        EquipHairColor(color);
 2303        view.SelectHairColor(model.hairColor);
 2304        UpdateAvatarPreview();
 2305    }
 306
 307    public void SkinColorClicked(Color color)
 308    {
 2309        EquipSkinColor(color);
 2310        view.SelectSkinColor(model.skinColor);
 2311        UpdateAvatarPreview();
 2312    }
 313
 314    public void EyesColorClicked(Color color)
 315    {
 2316        EquipEyesColor(color);
 2317        view.SelectEyeColor(model.eyesColor);
 2318        UpdateAvatarPreview();
 2319    }
 320
 321    protected virtual void UpdateAvatarPreview()
 322    {
 117323        if (!bypassUpdateAvatarPreview)
 117324            view.UpdateAvatarPreview(model.ToAvatarModel());
 117325    }
 326
 327    private void EquipHairColor(Color color)
 328    {
 98329        var colorToSet = color;
 991330        if (!hairColorList.colors.Any(x => x.AproxComparison(colorToSet)))
 331        {
 88332            colorToSet = hairColorList.colors[hairColorList.defaultColor];
 333        }
 334
 98335        model.hairColor = colorToSet;
 98336        view.SelectHairColor(model.hairColor);
 98337    }
 338
 339    private void EquipEyesColor(Color color)
 340    {
 98341        var colorToSet = color;
 991342        if (!eyeColorList.colors.Any(x => x.AproxComparison(color)))
 343        {
 88344            colorToSet = eyeColorList.colors[eyeColorList.defaultColor];
 345        }
 346
 98347        model.eyesColor = colorToSet;
 98348        view.SelectEyeColor(model.eyesColor);
 98349    }
 350
 351    private void EquipSkinColor(Color color)
 352    {
 97353        var colorToSet = color;
 989354        if (!skinColorList.colors.Any(x => x.AproxComparison(colorToSet)))
 355        {
 88356            colorToSet = skinColorList.colors[skinColorList.defaultColor];
 357        }
 358
 97359        model.skinColor = colorToSet;
 97360        view.SelectSkinColor(model.skinColor);
 97361    }
 362
 363    private void EquipBodyShape(WearableItem bodyShape)
 364    {
 98365        if (bodyShape.data.category != Categories.BODY_SHAPE)
 366        {
 0367            Debug.LogError($"Item ({bodyShape.id} is not a body shape");
 0368            return;
 369        }
 370
 98371        if (model.bodyShape == bodyShape)
 50372            return;
 373
 48374        model.bodyShape = bodyShape;
 48375        view.UpdateSelectedBody(bodyShape);
 376
 48377        int wearablesCount = model.wearables.Count;
 136378        for (var i = wearablesCount - 1; i >= 0; i--)
 379        {
 20380            UnequipWearable(model.wearables[i]);
 381        }
 382
 48383        var defaultWearables = WearableLiterals.DefaultWearables.GetDefaultWearables(bodyShape.id);
 778384        for (var i = 0; i < defaultWearables.Length; i++)
 385        {
 341386            if (catalog.TryGetValue(defaultWearables[i], out var wearable))
 341387                EquipWearable(wearable);
 388        }
 48389    }
 390
 391    private void EquipWearable(WearableItem wearable)
 392    {
 940393        if (!wearablesByCategory.ContainsKey(wearable.data.category))
 0394            return;
 395
 940396        if (wearablesByCategory[wearable.data.category].Contains(wearable) && wearable.SupportsBodyShape(model.bodyShape
 397        {
 940398            var toReplace = GetWearablesReplacedBy(wearable);
 940399            toReplace.ForEach(UnequipWearable);
 940400            model.wearables.Add(wearable);
 940401            view.EquipWearable(wearable);
 402        }
 940403    }
 404
 405    private void UnequipWearable(WearableItem wearable)
 406    {
 24407        if (model.wearables.Contains(wearable))
 408        {
 24409            model.wearables.Remove(wearable);
 24410            view.UnequipWearable(wearable);
 411        }
 24412    }
 413
 414    public void UnequipAllWearables()
 415    {
 926416        foreach (var wearable in model.wearables)
 417        {
 395418            view.UnequipWearable(wearable);
 419        }
 420
 68421        model.wearables.Clear();
 68422    }
 423
 424    private void ProcessCatalog(BaseDictionary<string, WearableItem> catalog)
 425    {
 139426        wearablesByCategory.Clear();
 139427        view.RemoveAllWearables();
 139428        using (var iterator = catalog.Get().GetEnumerator())
 429        {
 5107430            while (iterator.MoveNext())
 431            {
 4968432                AddWearable(iterator.Current.Key, iterator.Current.Value);
 433            }
 139434        }
 139435    }
 436
 437    private void AddWearable(string id, WearableItem wearable)
 438    {
 4974439        if (!wearable.data.tags.Contains("base-wearable") && userProfile.GetItemAmount(id) == 0)
 440        {
 1094441            return;
 442        }
 443
 3880444        if (!wearablesByCategory.ContainsKey(wearable.data.category))
 445        {
 1518446            wearablesByCategory.Add(wearable.data.category, new List<WearableItem>());
 447        }
 448
 3880449        wearablesByCategory[wearable.data.category].Add(wearable);
 3880450        view.AddWearable(wearable, userProfile.GetItemAmount(id));
 3880451    }
 452
 453    private void RemoveWearable(string id, WearableItem wearable)
 454    {
 0455        if (wearablesByCategory.ContainsKey(wearable.data.category))
 456        {
 0457            if (wearablesByCategory[wearable.data.category].Remove(wearable))
 458            {
 0459                if (wearablesByCategory[wearable.data.category].Count == 0)
 460                {
 0461                    wearablesByCategory.Remove(wearable.data.category);
 462                }
 463            }
 464        }
 465
 0466        view.RemoveWearable(wearable);
 0467    }
 468
 469    public void RandomizeWearables()
 470    {
 1471        EquipHairColor(hairColorList.colors[UnityEngine.Random.Range(0, hairColorList.colors.Count)]);
 1472        EquipEyesColor(eyeColorList.colors[UnityEngine.Random.Range(0, eyeColorList.colors.Count)]);
 473
 1474        model.wearables.Clear();
 1475        view.UnselectAllWearables();
 1476        using (var iterator = wearablesByCategory.GetEnumerator())
 477        {
 12478            while (iterator.MoveNext())
 479            {
 11480                string category = iterator.Current.Key;
 11481                if (!categoriesToRandomize.Contains(category))
 482                {
 483                    continue;
 484                }
 485
 29486                var supportedWearables = iterator.Current.Value.Where(x => x.SupportsBodyShape(model.bodyShape.id)).ToAr
 7487                if (supportedWearables.Length == 0)
 488                {
 0489                    Debug.LogError($"Couldn't get any wearable for category {category} and bodyshape {model.bodyShape.id
 490                }
 491
 7492                var wearable = supportedWearables[UnityEngine.Random.Range(0, supportedWearables.Length - 1)];
 7493                EquipWearable(wearable);
 494            }
 1495        }
 496
 1497        UpdateAvatarPreview();
 1498    }
 499
 500    public List<WearableItem> GetWearablesReplacedBy(WearableItem wearableItem)
 501    {
 940502        List<WearableItem> wearablesToReplace = new List<WearableItem>();
 503
 940504        HashSet<string> categoriesToReplace = new HashSet<string>(wearableItem.GetReplacesList(model.bodyShape.id) ?? ne
 505
 940506        int wearableCount = model.wearables.Count;
 7140507        for (int i = 0; i < wearableCount; i++)
 508        {
 2630509            var wearable = model.wearables[i];
 2630510            if (wearable == null)
 511                continue;
 512
 2630513            if (categoriesToReplace.Contains(wearable.data.category))
 514            {
 2515                wearablesToReplace.Add(wearable);
 2516            }
 517            else
 518            {
 519                //For retrocompatibility's sake we check current wearables against new one (compatibility matrix is symm
 2628520                HashSet<string> replacesList = new HashSet<string>(wearable.GetReplacesList(model.bodyShape.id) ?? new s
 2628521                if (replacesList.Contains(wearableItem.data.category))
 522                {
 0523                    wearablesToReplace.Add(wearable);
 524                }
 525            }
 526        }
 527
 940528        return wearablesToReplace;
 529    }
 530
 46531    private float prevRenderScale = 1.0f;
 532    private Camera mainCamera;
 533
 534    public void SetVisibility(bool visible)
 535    {
 40536        var currentRenderProfile = DCL.RenderProfileManifest.i.currentProfile;
 537
 40538        if (!visible && view.isOpen)
 539        {
 1540            DataStore.i.virtualAudioMixer.sceneSFXVolume.Set(1f);
 1541            DCL.Environment.i.messaging.manager.paused = false;
 1542            currentRenderProfile.avatarProfile.currentProfile = currentRenderProfile.avatarProfile.inWorld;
 1543            currentRenderProfile.avatarProfile.Apply();
 1544            if (prevMouseLockState)
 545            {
 0546                Utils.LockCursor();
 547            }
 548
 549            // NOTE(Brian): SSAO doesn't work correctly with the offseted avatar preview if the renderScale != 1.0
 1550            var asset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
 1551            asset.renderScale = prevRenderScale;
 552
 1553            CommonScriptableObjects.isFullscreenHUDOpen.Set(false);
 1554            DataStore.i.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 555
 1556            OnClose?.Invoke();
 0557        }
 39558        else if (visible && !view.isOpen)
 559        {
 39560            DataStore.i.virtualAudioMixer.sceneSFXVolume.Set(0f);
 39561            LoadOwnedWereables(userProfile);
 39562            DCL.Environment.i.messaging.manager.paused = DataStore.i.isSignUpFlow.Get();
 39563            currentRenderProfile.avatarProfile.currentProfile = currentRenderProfile.avatarProfile.avatarEditor;
 39564            currentRenderProfile.avatarProfile.Apply();
 565
 39566            prevMouseLockState = Utils.isCursorLocked;
 39567            Utils.UnlockCursor();
 568
 569            // NOTE(Brian): SSAO doesn't work correctly with the offseted avatar preview if the renderScale != 1.0
 39570            var asset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
 39571            prevRenderScale = asset.renderScale;
 39572            asset.renderScale = 1.0f;
 573
 39574            CommonScriptableObjects.isFullscreenHUDOpen.Set(true);
 39575            DataStore.i.isPlayerRendererLoaded.OnChange += PlayerRendererLoaded;
 576
 39577            OnOpen?.Invoke();
 578        }
 579
 40580        currentRenderProfile.avatarProfile.Apply();
 40581        view.SetVisibility(visible);
 40582    }
 583
 584    public void Dispose()
 585    {
 39586        view.OnToggleActionTriggered -= ToggleVisibility;
 39587        view.OnCloseActionTriggered -= DiscardAndClose;
 39588        DataStore.i.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 589
 39590        CleanUp();
 39591    }
 592
 593    public void CleanUp()
 594    {
 44595        UnequipAllWearables();
 596
 44597        if (view != null)
 44598            view.CleanUp();
 599
 44600        this.userProfile.OnUpdate -= LoadUserProfile;
 44601        this.catalog.OnAdded -= AddWearable;
 44602        this.catalog.OnRemoved -= RemoveWearable;
 44603        DataStore.i.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 44604    }
 605
 0606    public void SetConfiguration(HUDConfiguration configuration) { SetVisibility(configuration.active); }
 607
 608    public void SaveAvatar(Texture2D faceSnapshot, Texture2D face128Snapshot, Texture2D face256Snapshot, Texture2D bodyS
 609    {
 1610        var avatarModel = model.ToAvatarModel();
 611
 1612        WebInterface.SendSaveAvatar(avatarModel, faceSnapshot, face128Snapshot, face256Snapshot, bodySnapshot, DataStore
 1613        userProfile.OverrideAvatar(avatarModel, face256Snapshot);
 1614        if (DataStore.i.isSignUpFlow.Get())
 0615            DataStore.i.HUDs.signupVisible.Set(true);
 616
 1617        SetVisibility(false);
 1618    }
 619
 620    public void DiscardAndClose()
 621    {
 0622        if (!view.isOpen)
 0623            return;
 624
 0625        if (!DataStore.i.isSignUpFlow.Get())
 0626            LoadUserProfile(userProfile);
 627        else
 0628            WebInterface.SendCloseUserAvatar(true);
 629
 0630        SetVisibility(false);
 0631    }
 632
 633    public void GoToMarketplace()
 634    {
 0635        if (userProfile.hasConnectedWeb3)
 0636            WebInterface.OpenURL(URL_MARKET_PLACE);
 637        else
 0638            WebInterface.OpenURL(URL_GET_A_WALLET);
 0639    }
 640
 641    public void SellCollectible(string collectibleId)
 642    {
 0643        var ownedCollectible = ownedNftCollectionsL1.FirstOrDefault(nft => nft.urn == collectibleId);
 0644        if (ownedCollectible == null)
 0645            ownedCollectible = ownedNftCollectionsL2.FirstOrDefault(nft => nft.urn == collectibleId);
 646
 0647        if (ownedCollectible != null)
 0648            WebInterface.OpenURL(URL_SELL_SPECIFIC_COLLECTIBLE.Replace("{collectionId}", ownedCollectible.collectionId).
 649        else
 0650            WebInterface.OpenURL(URL_SELL_COLLECTIBLE_GENERIC);
 0651    }
 652
 0653    public void ToggleVisibility() { SetVisibility(!view.isOpen); }
 654}