< 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:271
Uncovered lines:86
Coverable lines:357
Total lines:731
Line coverage:75.9% (271 of 357)
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%550100%
QueryNftCollections(...)0%7.464040%
RetryLoadOwnedWearables()0%2100%
PlayerRendererLoaded(...)0%6.136084.62%
LoadUserProfile(...)0%11.3911085.29%
EnsureWearablesCategoriesNotEmpty()0%7.027092.86%
WearableClicked(...)0%8.448080.95%
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.035088.89%
UnequipWearable(...)0%220100%
UnequipAllWearables()0%220100%
ProcessCatalog(...)0%440100%
AddWearable(...)0%440100%
RemoveWearable(...)0%20400%
RandomizeWearables()0%44093.75%
GetWearablesReplacedBy(...)0%7.017093.33%
SetVisibility(...)0%110100%
OnAvatarEditorVisibleChanged(...)0%110100%
SetVisibility_Internal(...)0%16.4815081.25%
Dispose()0%110100%
CleanUp()0%220100%
SetConfiguration(...)0%2100%
SaveAvatar(...)0%2.012087.5%
GoToMarketplaceOrConnectWallet()0%6200%
SellCollectible(...)0%12300%
ToggleVisibility()0%2100%
ConfigureBackpackInFullscreenMenuChanged(...)0%110100%
ExploreV2IsOpenChanged(...)0%12300%
ShouldShowHideOtherWearablesToast(...)0%12300%
IsTryingToReplaceSkin(...)0%110100%
ShouldShowReplaceOtherWearablesToast(...)0%56700%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using DCL;
 5using DCL.Helpers;
 6using DCL.Interface;
 7using DCL.NotificationModel;
 8using UnityEngine;
 9using UnityEngine.Rendering;
 10using UnityEngine.Rendering.Universal;
 11using Environment = DCL.Environment;
 12using Random = UnityEngine.Random;
 13using Type = DCL.NotificationModel.Type;
 14using Categories = WearableLiterals.Categories;
 15
 16public class AvatarEditorHUDController : IHUD
 17{
 18    private const int LOADING_OWNED_WEARABLES_RETRIES = 3;
 19    private const string LOADING_OWNED_WEARABLES_ERROR_MESSAGE = "There was a problem loading your wearables";
 20    private const string URL_MARKET_PLACE = "https://market.decentraland.org/browse?section=wearables";
 21    private const string URL_GET_A_WALLET = "https://docs.decentraland.org/get-a-wallet";
 22    private const string URL_SELL_COLLECTIBLE_GENERIC = "https://market.decentraland.org/account";
 23    private const string URL_SELL_SPECIFIC_COLLECTIBLE = "https://market.decentraland.org/contracts/{collectionId}/token
 24
 125    protected static readonly string[] categoriesThatMustHaveSelection = { Categories.BODY_SHAPE, Categories.UPPER_BODY,
 126    protected static readonly string[] categoriesToRandomize = { Categories.HAIR, Categories.EYES, Categories.EYEBROWS, 
 27
 28    [NonSerialized]
 29    public bool bypassUpdateAvatarPreview = false;
 30
 31    internal UserProfile userProfile;
 32    private BaseDictionary<string, WearableItem> catalog;
 10233    bool renderingEnabled => CommonScriptableObjects.rendererState.Get();
 9934    bool isPlayerRendererLoaded => DataStore.i.common.isPlayerRendererLoaded.Get();
 17535    BaseVariable<bool> avatarEditorVisible => DataStore.i.HUDs.avatarEditorVisible;
 13336    BaseVariable<Transform> configureBackpackInFullscreenMenu => DataStore.i.exploreV2.configureBackpackInFullscreenMenu
 8737    BaseVariable<bool> exploreV2IsOpen => DataStore.i.exploreV2.isOpen;
 4638    private bool isSkinsFeatureEnabled => DataStore.i.featureFlags.flags.Get().IsFeatureEnabled("avatar_skins");
 39
 4640    private readonly Dictionary<string, List<WearableItem>> wearablesByCategory = new Dictionary<string, List<WearableIt
 4641    protected readonly AvatarEditorHUDModel model = new AvatarEditorHUDModel();
 42
 43    private ColorList skinColorList;
 44    private ColorList eyeColorList;
 45    private ColorList hairColorList;
 46    private bool prevMouseLockState = false;
 4647    private int ownedWearablesRemainingRequests = LOADING_OWNED_WEARABLES_RETRIES;
 48    private bool ownedWearablesAlreadyLoaded = false;
 4649    private List<Nft> ownedNftCollectionsL1 = new List<Nft>();
 4650    private List<Nft> ownedNftCollectionsL2 = new List<Nft>();
 51    private bool avatarIsDirty = false;
 52    private float lastTimeOwnedWearablesChecked = 0;
 4653    private float prevRenderScale = 1.0f;
 54    private Camera mainCamera;
 55
 56    public AvatarEditorHUDView view;
 57
 58    public event Action OnOpen;
 59    public event Action OnClose;
 60
 9261    public AvatarEditorHUDController() { }
 62
 63    public void Initialize(UserProfile userProfile, BaseDictionary<string, WearableItem> catalog, bool bypassUpdateAvata
 64    {
 4665        this.userProfile = userProfile;
 4666        this.bypassUpdateAvatarPreview = bypassUpdateAvatarPreview;
 67
 4668        view = AvatarEditorHUDView.Create(this);
 69
 4670        view.skinsFeatureContainer.SetActive(isSkinsFeatureEnabled);
 4671        avatarEditorVisible.OnChange += OnAvatarEditorVisibleChanged;
 4672        OnAvatarEditorVisibleChanged(avatarEditorVisible.Get(), false);
 73
 4674        configureBackpackInFullscreenMenu.OnChange += ConfigureBackpackInFullscreenMenuChanged;
 4675        ConfigureBackpackInFullscreenMenuChanged(configureBackpackInFullscreenMenu.Get(), null);
 76
 4677        exploreV2IsOpen.OnChange += ExploreV2IsOpenChanged;
 78
 4679        skinColorList = Resources.Load<ColorList>("SkinTone");
 4680        hairColorList = Resources.Load<ColorList>("HairColor");
 4681        eyeColorList = Resources.Load<ColorList>("EyeColor");
 4682        view.SetColors(skinColorList.colors, hairColorList.colors, eyeColorList.colors);
 83
 4684        SetCatalog(catalog);
 85
 4686        LoadUserProfile(userProfile, true);
 4687        this.userProfile.OnUpdate += LoadUserProfile;
 88
 4689        DataStore.i.HUDs.isAvatarEditorInitialized.Set(true);
 4690    }
 91
 92    public void SetCatalog(BaseDictionary<string, WearableItem> catalog)
 93    {
 4694        if (this.catalog != null)
 95        {
 096            this.catalog.OnAdded -= AddWearable;
 097            this.catalog.OnRemoved -= RemoveWearable;
 98        }
 99
 46100        this.catalog = catalog;
 101
 46102        ProcessCatalog(this.catalog);
 46103        this.catalog.OnAdded += AddWearable;
 46104        this.catalog.OnRemoved += RemoveWearable;
 46105    }
 106
 107    private void LoadUserProfile(UserProfile userProfile)
 108    {
 42109        LoadUserProfile(userProfile, false);
 42110        QueryNftCollections(userProfile.userId);
 42111    }
 112
 113    private void LoadOwnedWereables(UserProfile userProfile)
 114    {
 115        // If there is more than 1 minute that we have checked the owned wearables, we try it again
 116        // This is done in order to retrieved the wearables after you has claimed them
 41117        if ((Time.realtimeSinceStartup < lastTimeOwnedWearablesChecked + 60 &&
 118             (ownedWearablesAlreadyLoaded ||
 119              ownedWearablesRemainingRequests <= 0)) ||
 120            string.IsNullOrEmpty(userProfile.userId))
 40121            return;
 122
 1123        view.ShowCollectiblesLoadingSpinner(true);
 1124        view.ShowCollectiblesLoadingRetry(false);
 1125        lastTimeOwnedWearablesChecked = Time.realtimeSinceStartup;
 126
 1127        CatalogController.RequestOwnedWearables(userProfile.userId)
 128                         .Then((ownedWearables) =>
 129                         {
 0130                             ownedWearablesAlreadyLoaded = true;
 0131                             this.userProfile.SetInventory(ownedWearables.Select(x => x.id).ToArray());
 0132                             LoadUserProfile(userProfile, true);
 0133                             view.ShowCollectiblesLoadingSpinner(false);
 0134                             view.ShowSkinPopulatedList(ownedWearables.Any(item => item.IsSkin()));
 0135                         })
 136                         .Catch((error) =>
 137                         {
 0138                             ownedWearablesRemainingRequests--;
 0139                             if (ownedWearablesRemainingRequests > 0)
 140                             {
 0141                                 Debug.LogWarning("Retrying owned wereables loading...");
 0142                                 LoadOwnedWereables(userProfile);
 0143                             }
 144                             else
 145                             {
 0146                                 NotificationsController.i.ShowNotification(new Model
 147                                 {
 148                                     message = LOADING_OWNED_WEARABLES_ERROR_MESSAGE,
 149                                     type = Type.GENERIC,
 150                                     timer = 10f,
 151                                     destroyOnFinish = true
 152                                 });
 153
 0154                                 view.ShowCollectiblesLoadingSpinner(false);
 0155                                 view.ShowCollectiblesLoadingRetry(true);
 0156                                 Debug.LogError(error);
 157                             }
 0158                         });
 1159    }
 160
 161    private void QueryNftCollections(string userId)
 162    {
 42163        if (string.IsNullOrEmpty(userId))
 42164            return;
 165
 0166        Environment.i.platform.serviceProviders.theGraph.QueryNftCollections(userProfile.userId, NftCollectionsLayer.ETH
 0167           .Then((nfts) => ownedNftCollectionsL1 = nfts)
 0168           .Catch((error) => Debug.LogError(error));
 169
 0170        Environment.i.platform.serviceProviders.theGraph.QueryNftCollections(userProfile.userId, NftCollectionsLayer.MAT
 0171           .Then((nfts) => ownedNftCollectionsL2 = nfts)
 0172           .Catch((error) => Debug.LogError(error));
 0173    }
 174
 175    public void RetryLoadOwnedWearables()
 176    {
 0177        ownedWearablesRemainingRequests = LOADING_OWNED_WEARABLES_RETRIES;
 0178        LoadOwnedWereables(userProfile);
 0179    }
 180
 181    private void PlayerRendererLoaded(bool current, bool previous)
 182    {
 14183        if (!current)
 0184            return;
 185
 14186        if (!ownedWearablesAlreadyLoaded)
 187        {
 14188            List<string> equippedOwnedWearables = new List<string>();
 58189            for (int i = 0; i < userProfile.avatar.wearables.Count; i++)
 190            {
 15191                if (catalog.TryGetValue(userProfile.avatar.wearables[i], out WearableItem wearable) &&
 192                    !wearable.data.tags.Contains("base-wearable"))
 193                {
 0194                    equippedOwnedWearables.Add(userProfile.avatar.wearables[i]);
 195                }
 196            }
 197
 14198            userProfile.SetInventory(equippedOwnedWearables.ToArray());
 199        }
 200
 14201        LoadUserProfile(userProfile, true);
 14202        DataStore.i.common.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 14203    }
 204
 205    public void LoadUserProfile(UserProfile userProfile, bool forceLoading)
 206    {
 102207        bool avatarEditorNotVisible = renderingEnabled && !view.isOpen;
 102208        bool isPlaying = !Application.isBatchMode;
 209
 102210        if (!forceLoading)
 211        {
 42212            if (isPlaying && avatarEditorNotVisible)
 0213                return;
 214        }
 215
 102216        if (userProfile == null)
 0217            return;
 218
 102219        if (userProfile.avatar == null || string.IsNullOrEmpty(userProfile.avatar.bodyShape))
 3220            return;
 221
 222        /*TODO: this has to be refactored, currently there is no other way of understanding if the user is a regular or 
 223        *       due to the execution order of things. The init cannot be done below because that would mean to do it whe
 224        *       menu is firstly opened
 225        */
 99226        view.InitializeNavigationEvents(string.IsNullOrEmpty(userProfile.userName));
 227
 99228        CatalogController.wearableCatalog.TryGetValue(userProfile.avatar.bodyShape, out var bodyShape);
 229
 99230        if (bodyShape == null)
 231        {
 0232            return;
 233        }
 234
 99235        view.SetIsWeb3(userProfile.hasConnectedWeb3);
 236
 99237        ProcessCatalog(this.catalog);
 99238        EquipBodyShape(bodyShape);
 99239        EquipSkinColor(userProfile.avatar.skinColor);
 99240        EquipHairColor(userProfile.avatar.hairColor);
 99241        EquipEyesColor(userProfile.avatar.eyeColor);
 242
 99243        model.wearables.Clear();
 99244        view.UnselectAllWearables();
 245
 99246        int wearablesCount = userProfile.avatar.wearables.Count;
 247
 99248        if (isPlayerRendererLoaded)
 249        {
 158250            for (var i = 0; i < wearablesCount; i++)
 251            {
 46252                CatalogController.wearableCatalog.TryGetValue(userProfile.avatar.wearables[i], out var wearable);
 46253                if (wearable == null)
 254                {
 0255                    Debug.LogError($"Couldn't find wearable with ID {userProfile.avatar.wearables[i]}");
 0256                    continue;
 257                }
 258
 46259                EquipWearable(wearable);
 260            }
 261        }
 262
 99263        EnsureWearablesCategoriesNotEmpty();
 264
 99265        UpdateAvatarPreview();
 99266    }
 267
 268    private void EnsureWearablesCategoriesNotEmpty()
 269    {
 145270        var categoriesInUse = model.wearables.Select(x => x.data.category).ToArray();
 1584271        for (var i = 0; i < categoriesThatMustHaveSelection.Length; i++)
 272        {
 693273            var category = categoriesThatMustHaveSelection[i];
 693274            if (category != Categories.BODY_SHAPE && !(categoriesInUse.Contains(category)))
 275            {
 276                WearableItem wearable;
 558277                var defaultItemId = WearableLiterals.DefaultWearables.GetDefaultWearable(model.bodyShape.id, category);
 558278                if (defaultItemId != null)
 279                {
 558280                    CatalogController.wearableCatalog.TryGetValue(defaultItemId, out wearable);
 558281                }
 282                else
 283                {
 0284                    wearable = wearablesByCategory[category].FirstOrDefault(x => x.SupportsBodyShape(model.bodyShape.id)
 285                }
 286
 558287                if (wearable != null)
 288                {
 558289                    EquipWearable(wearable);
 290                }
 291            }
 292        }
 99293    }
 294
 295    public void WearableClicked(string wearableId)
 296    {
 20297        CatalogController.wearableCatalog.TryGetValue(wearableId, out var wearable);
 23298        if (wearable == null) return;
 299
 17300        if (wearable.data.category == Categories.BODY_SHAPE)
 301        {
 3302            if (wearable.id == model.bodyShape.id)
 0303                return;
 3304            EquipBodyShape(wearable);
 3305        }
 306        else
 307        {
 14308            if (model.wearables.Contains(wearable))
 309            {
 2310                if (!categoriesThatMustHaveSelection.Contains(wearable.data.category))
 311                {
 0312                    UnequipWearable(wearable);
 0313                }
 314                else
 315                {
 2316                    return;
 317                }
 318            }
 319            else
 320            {
 12321                if (IsTryingToReplaceSkin(wearable))
 0322                    UnequipWearable(model.GetWearable(Categories.SKIN));
 323
 12324                var sameCategoryEquipped = model.GetWearable(wearable.data.category);
 12325                if (sameCategoryEquipped != null)
 2326                    UnequipWearable(sameCategoryEquipped);
 327
 12328                EquipWearable(wearable);
 329            }
 330        }
 331
 15332        UpdateAvatarPreview();
 15333    }
 334
 335    public void HairColorClicked(Color color)
 336    {
 2337        EquipHairColor(color);
 2338        view.SelectHairColor(model.hairColor);
 2339        UpdateAvatarPreview();
 2340    }
 341
 342    public void SkinColorClicked(Color color)
 343    {
 2344        EquipSkinColor(color);
 2345        view.SelectSkinColor(model.skinColor);
 2346        UpdateAvatarPreview();
 2347    }
 348
 349    public void EyesColorClicked(Color color)
 350    {
 2351        EquipEyesColor(color);
 2352        view.SelectEyeColor(model.eyesColor);
 2353        UpdateAvatarPreview();
 2354    }
 355
 356    protected virtual void UpdateAvatarPreview()
 357    {
 121358        if (!bypassUpdateAvatarPreview)
 121359            view.UpdateAvatarPreview(model.ToAvatarModel());
 121360    }
 361
 362    private void EquipHairColor(Color color)
 363    {
 102364        var colorToSet = color;
 1039365        if (!hairColorList.colors.Any(x => x.AproxComparison(colorToSet)))
 366        {
 92367            colorToSet = hairColorList.colors[hairColorList.defaultColor];
 368        }
 369
 102370        model.hairColor = colorToSet;
 102371        view.SelectHairColor(model.hairColor);
 102372    }
 373
 374    private void EquipEyesColor(Color color)
 375    {
 102376        var colorToSet = color;
 1040377        if (!eyeColorList.colors.Any(x => x.AproxComparison(color)))
 378        {
 92379            colorToSet = eyeColorList.colors[eyeColorList.defaultColor];
 380        }
 381
 102382        model.eyesColor = colorToSet;
 102383        view.SelectEyeColor(model.eyesColor);
 102384    }
 385
 386    private void EquipSkinColor(Color color)
 387    {
 101388        var colorToSet = color;
 1033389        if (!skinColorList.colors.Any(x => x.AproxComparison(colorToSet)))
 390        {
 92391            colorToSet = skinColorList.colors[skinColorList.defaultColor];
 392        }
 393
 101394        model.skinColor = colorToSet;
 101395        view.SelectSkinColor(model.skinColor);
 101396    }
 397
 398    private void EquipBodyShape(WearableItem bodyShape)
 399    {
 102400        if (bodyShape.data.category != Categories.BODY_SHAPE)
 401        {
 0402            Debug.LogError($"Item ({bodyShape.id} is not a body shape");
 0403            return;
 404        }
 405
 102406        if (model.bodyShape == bodyShape)
 52407            return;
 408
 50409        model.bodyShape = bodyShape;
 50410        view.UpdateSelectedBody(bodyShape);
 411
 50412        int wearablesCount = model.wearables.Count;
 140413        for (var i = wearablesCount - 1; i >= 0; i--)
 414        {
 20415            UnequipWearable(model.wearables[i]);
 416        }
 417
 50418        var defaultWearables = WearableLiterals.DefaultWearables.GetDefaultWearables(bodyShape.id);
 810419        for (var i = 0; i < defaultWearables.Length; i++)
 420        {
 355421            if (catalog.TryGetValue(defaultWearables[i], out var wearable))
 355422                EquipWearable(wearable);
 423        }
 50424    }
 425
 426    private void EquipWearable(WearableItem wearable)
 427    {
 978428        if (!wearablesByCategory.ContainsKey(wearable.data.category))
 0429            return;
 430
 978431        if (wearablesByCategory[wearable.data.category].Contains(wearable) && wearable.SupportsBodyShape(model.bodyShape
 432        {
 978433            var toReplace = GetWearablesReplacedBy(wearable);
 978434            toReplace.ForEach(UnequipWearable);
 978435            model.wearables.Add(wearable);
 978436            view.EquipWearable(wearable);
 978437            avatarIsDirty = true;
 438        }
 978439    }
 440
 441    private void UnequipWearable(WearableItem wearable)
 442    {
 24443        if (model.wearables.Contains(wearable))
 444        {
 24445            model.wearables.Remove(wearable);
 24446            view.UnequipWearable(wearable);
 24447            avatarIsDirty = true;
 448        }
 24449    }
 450
 451    public void UnequipAllWearables()
 452    {
 982453        foreach (var wearable in model.wearables)
 454        {
 419455            view.UnequipWearable(wearable);
 456        }
 457
 72458        model.wearables.Clear();
 72459    }
 460
 461    private void ProcessCatalog(BaseDictionary<string, WearableItem> catalog)
 462    {
 145463        wearablesByCategory.Clear();
 145464        view.RemoveAllWearables();
 145465        using (var iterator = catalog.Get().GetEnumerator())
 466        {
 5329467            while (iterator.MoveNext())
 468            {
 5184469                if (iterator.Current.Value.IsEmote())
 470                    continue;
 5184471                AddWearable(iterator.Current.Key, iterator.Current.Value);
 472            }
 145473        }
 145474    }
 475
 476    private void AddWearable(string id, WearableItem wearable)
 477    {
 5192478        if (!wearable.data.tags.Contains("base-wearable") && userProfile.GetItemAmount(id) == 0)
 479        {
 1142480            return;
 481        }
 482
 4050483        if (!wearablesByCategory.ContainsKey(wearable.data.category))
 484        {
 1584485            wearablesByCategory.Add(wearable.data.category, new List<WearableItem>());
 486        }
 487
 4050488        wearablesByCategory[wearable.data.category].Add(wearable);
 4050489        view.AddWearable(wearable, userProfile.GetItemAmount(id),
 490            ShouldShowHideOtherWearablesToast,
 491            ShouldShowReplaceOtherWearablesToast);
 4050492    }
 493
 494    private void RemoveWearable(string id, WearableItem wearable)
 495    {
 0496        if (wearablesByCategory.ContainsKey(wearable.data.category))
 497        {
 0498            if (wearablesByCategory[wearable.data.category].Remove(wearable))
 499            {
 0500                if (wearablesByCategory[wearable.data.category].Count == 0)
 501                {
 0502                    wearablesByCategory.Remove(wearable.data.category);
 503                }
 504            }
 505        }
 506
 0507        view.RemoveWearable(wearable);
 0508    }
 509
 510    public void RandomizeWearables()
 511    {
 1512        EquipHairColor(hairColorList.colors[Random.Range(0, hairColorList.colors.Count)]);
 1513        EquipEyesColor(eyeColorList.colors[Random.Range(0, eyeColorList.colors.Count)]);
 514
 1515        model.wearables.Clear();
 1516        view.UnselectAllWearables();
 1517        using (var iterator = wearablesByCategory.GetEnumerator())
 518        {
 12519            while (iterator.MoveNext())
 520            {
 11521                string category = iterator.Current.Key;
 11522                if (!categoriesToRandomize.Contains(category))
 523                {
 524                    continue;
 525                }
 526
 29527                var supportedWearables = iterator.Current.Value.Where(x => x.SupportsBodyShape(model.bodyShape.id)).ToAr
 7528                if (supportedWearables.Length == 0)
 529                {
 0530                    Debug.LogError($"Couldn't get any wearable for category {category} and bodyshape {model.bodyShape.id
 531                }
 532
 7533                var wearable = supportedWearables[Random.Range(0, supportedWearables.Length - 1)];
 7534                EquipWearable(wearable);
 535            }
 1536        }
 537
 1538        UpdateAvatarPreview();
 1539    }
 540
 541    private List<WearableItem> GetWearablesReplacedBy(WearableItem wearableItem)
 542    {
 978543        var wearablesToReplace = new List<WearableItem>();
 978544        var categoriesToReplace = new HashSet<string>(wearableItem.GetReplacesList(model.bodyShape.id) ?? new string[0])
 545
 978546        int wearableCount = model.wearables.Count;
 7420547        for (int i = 0; i < wearableCount; i++)
 548        {
 2732549            var wearable = model.wearables[i];
 2732550            if (wearable == null) continue;
 551
 2732552            if (categoriesToReplace.Contains(wearable.data.category))
 553            {
 2554                wearablesToReplace.Add(wearable);
 2555            }
 556            else
 557            {
 558                //For retrocompatibility's sake we check current wearables against new one (compatibility matrix is symm
 2730559                HashSet<string> replacesList = new HashSet<string>(wearable.GetReplacesList(model.bodyShape.id) ?? new s
 2730560                if (replacesList.Contains(wearableItem.data.category))
 561                {
 0562                    wearablesToReplace.Add(wearable);
 563                }
 564            }
 565        }
 566
 978567        return wearablesToReplace;
 568    }
 569
 84570    public void SetVisibility(bool visible) { avatarEditorVisible.Set(visible); }
 571
 176572    private void OnAvatarEditorVisibleChanged(bool current, bool previous) { SetVisibility_Internal(current); }
 573
 574    public void SetVisibility_Internal(bool visible)
 575    {
 88576        if (!visible && view.isOpen)
 577        {
 1578            if (DataStore.i.common.isSignUpFlow.Get())
 0579                DataStore.i.virtualAudioMixer.sceneSFXVolume.Set(1f);
 580
 1581            Environment.i.messaging.manager.paused = false;
 1582            DataStore.i.skyboxConfig.avatarMatProfile.Set(AvatarMaterialProfile.InWorld);
 1583            if (prevMouseLockState && DataStore.i.common.isSignUpFlow.Get())
 584            {
 0585                Utils.LockCursor();
 586            }
 587
 588            // NOTE(Brian): SSAO doesn't work correctly with the offseted avatar preview if the renderScale != 1.0
 1589            var asset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
 1590            asset.renderScale = prevRenderScale;
 591
 1592            if (DataStore.i.common.isSignUpFlow.Get())
 0593                CommonScriptableObjects.isFullscreenHUDOpen.Set(false);
 594
 1595            DataStore.i.common.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 596
 1597            OnClose?.Invoke();
 0598        }
 87599        else if (visible && !view.isOpen)
 600        {
 41601            if (DataStore.i.common.isSignUpFlow.Get())
 0602                DataStore.i.virtualAudioMixer.sceneSFXVolume.Set(0f);
 603
 41604            LoadOwnedWereables(userProfile);
 41605            Environment.i.messaging.manager.paused = DataStore.i.common.isSignUpFlow.Get();
 41606            DataStore.i.skyboxConfig.avatarMatProfile.Set(AvatarMaterialProfile.InEditor);
 607
 41608            prevMouseLockState = Utils.IsCursorLocked;
 609
 41610            if (DataStore.i.common.isSignUpFlow.Get() || !DataStore.i.exploreV2.isInitialized.Get())
 41611                Utils.UnlockCursor();
 612
 613            // NOTE(Brian): SSAO doesn't work correctly with the offseted avatar preview if the renderScale != 1.0
 41614            var asset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
 41615            prevRenderScale = asset.renderScale;
 41616            asset.renderScale = 1.0f;
 617
 41618            if (DataStore.i.common.isSignUpFlow.Get())
 0619                CommonScriptableObjects.isFullscreenHUDOpen.Set(true);
 620
 41621            DataStore.i.common.isPlayerRendererLoaded.OnChange += PlayerRendererLoaded;
 622
 41623            OnOpen?.Invoke();
 624        }
 625
 88626        view.SetVisibility(visible);
 88627    }
 628
 629    public void Dispose()
 630    {
 41631        avatarEditorVisible.OnChange -= OnAvatarEditorVisibleChanged;
 41632        configureBackpackInFullscreenMenu.OnChange -= ConfigureBackpackInFullscreenMenuChanged;
 41633        DataStore.i.common.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 41634        exploreV2IsOpen.OnChange -= ExploreV2IsOpenChanged;
 635
 41636        CleanUp();
 41637    }
 638
 639    public void CleanUp()
 640    {
 46641        UnequipAllWearables();
 642
 46643        if (view != null)
 46644            view.CleanUp();
 645
 46646        this.userProfile.OnUpdate -= LoadUserProfile;
 46647        this.catalog.OnAdded -= AddWearable;
 46648        this.catalog.OnRemoved -= RemoveWearable;
 46649        DataStore.i.common.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 46650    }
 651
 0652    public void SetConfiguration(HUDConfiguration configuration) { SetVisibility(configuration.active); }
 653
 654    public void SaveAvatar(Texture2D face256Snapshot, Texture2D bodySnapshot)
 655    {
 1656        var avatarModel = model.ToAvatarModel();
 657
 1658        WebInterface.SendSaveAvatar(avatarModel, face256Snapshot, bodySnapshot, DataStore.i.common.isSignUpFlow.Get());
 1659        userProfile.OverrideAvatar(avatarModel, face256Snapshot);
 1660        if (DataStore.i.common.isSignUpFlow.Get())
 0661            DataStore.i.HUDs.signupVisible.Set(true);
 662
 1663        avatarIsDirty = false;
 1664        SetVisibility(false);
 1665    }
 666
 667    public void GoToMarketplaceOrConnectWallet()
 668    {
 0669        if (userProfile.hasConnectedWeb3)
 0670            WebInterface.OpenURL(URL_MARKET_PLACE);
 671        else
 0672            WebInterface.OpenURL(URL_GET_A_WALLET);
 0673    }
 674
 675    public void SellCollectible(string collectibleId)
 676    {
 0677        var ownedCollectible = ownedNftCollectionsL1.FirstOrDefault(nft => nft.urn == collectibleId);
 0678        if (ownedCollectible == null)
 0679            ownedCollectible = ownedNftCollectionsL2.FirstOrDefault(nft => nft.urn == collectibleId);
 680
 0681        if (ownedCollectible != null)
 0682            WebInterface.OpenURL(URL_SELL_SPECIFIC_COLLECTIBLE.Replace("{collectionId}", ownedCollectible.collectionId).
 683        else
 0684            WebInterface.OpenURL(URL_SELL_COLLECTIBLE_GENERIC);
 0685    }
 686
 0687    public void ToggleVisibility() { SetVisibility(!view.isOpen); }
 688
 92689    private void ConfigureBackpackInFullscreenMenuChanged(Transform currentParentTransform, Transform previousParentTran
 690
 691    private void ExploreV2IsOpenChanged(bool current, bool previous)
 692    {
 0693        if (!current && avatarIsDirty)
 694        {
 0695            LoadUserProfile(userProfile, true);
 0696            avatarIsDirty = false;
 697        }
 0698    }
 699
 700    private bool ShouldShowHideOtherWearablesToast(WearableItem wearable)
 701    {
 0702        var isWearingSkinAlready = model.wearables.Any(item => item.IsSkin());
 0703        return wearable.IsSkin() && !isWearingSkinAlready;
 704    }
 705
 706    private bool IsTryingToReplaceSkin(WearableItem wearable)
 707    {
 12708        return model.wearables.Any(skin =>
 709        {
 80710            return skin.IsSkin()
 711                   && skin.DoesHide(wearable.data.category, model.bodyShape.id);
 712        });
 713    }
 714
 715    private bool ShouldShowReplaceOtherWearablesToast(WearableItem wearable)
 716    {
 0717        if (IsTryingToReplaceSkin(wearable)) return true;
 0718        var toReplace = GetWearablesReplacedBy(wearable);
 0719        if (wearable == null || toReplace.Count == 0) return false;
 0720        if (model.wearables.Contains(wearable)) return false;
 721
 722        // NOTE: why just 1?
 0723        if (toReplace.Count == 1)
 724        {
 0725            var w = toReplace[0];
 0726            if (w.data.category == wearable.data.category)
 0727                return false;
 728        }
 0729        return true;
 730    }
 731}

Methods/Properties

AvatarEditorHUDController()
renderingEnabled()
isPlayerRendererLoaded()
avatarEditorVisible()
configureBackpackInFullscreenMenu()
exploreV2IsOpen()
isSkinsFeatureEnabled()
AvatarEditorHUDController()
Initialize(UserProfile, .BaseDictionary[String,WearableItem], System.Boolean)
SetCatalog(.BaseDictionary[String,WearableItem])
LoadUserProfile(UserProfile)
LoadOwnedWereables(UserProfile)
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()
EquipHairColor(UnityEngine.Color)
EquipEyesColor(UnityEngine.Color)
EquipSkinColor(UnityEngine.Color)
EquipBodyShape(WearableItem)
EquipWearable(WearableItem)
UnequipWearable(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)
ShouldShowHideOtherWearablesToast(WearableItem)
IsTryingToReplaceSkin(WearableItem)
ShouldShowReplaceOtherWearablesToast(WearableItem)