Skip to content

Commit 2ba5d63

Browse files
authored
fix: fallback to default wearables when changing body shape (#5628)
1 parent 02efde9 commit 2ba5d63

File tree

3 files changed

+516
-2
lines changed

3 files changed

+516
-2
lines changed

unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/BackpackEditorHUDV2/BackpackEditorHUDController.cs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using MainScripts.DCL.Controllers.HUD.CharacterPreview;
77
using System;
88
using System.Collections.Generic;
9+
using System.Linq;
910
using System.Text;
1011
using System.Threading;
1112
using UnityEngine;
@@ -26,6 +27,30 @@ public class BackpackEditorHUDController
2627
private readonly OutfitsController outfitsController;
2728
private readonly IVRMExporter vrmExporter;
2829
private readonly IDCLFileBrowserService fileBrowser;
30+
private readonly Dictionary<string, Dictionary<string, string>> fallbackWearables = new ()
31+
{
32+
{"urn:decentraland:off-chain:base-avatars:BaseFemale", new Dictionary<string, string>
33+
{
34+
{WearableLiterals.Categories.UPPER_BODY, "urn:decentraland:off-chain:base-avatars:f_blue_jacket"},
35+
{WearableLiterals.Categories.LOWER_BODY, "urn:decentraland:off-chain:base-avatars:f_capris"},
36+
{WearableLiterals.Categories.FEET, "urn:decentraland:off-chain:base-avatars:ruby_blue_loafer"},
37+
{WearableLiterals.Categories.HAIR, "urn:decentraland:off-chain:base-avatars:pony_tail"},
38+
{WearableLiterals.Categories.MOUTH, "urn:decentraland:off-chain:base-avatars:f_mouth_05"},
39+
{WearableLiterals.Categories.EYEBROWS, "urn:decentraland:off-chain:base-avatars:f_eyebrows_02"},
40+
{WearableLiterals.Categories.EYES, "urn:decentraland:off-chain:base-avatars:f_eyes_06"},
41+
}},
42+
{"urn:decentraland:off-chain:base-avatars:BaseMale", new Dictionary<string, string>
43+
{
44+
{WearableLiterals.Categories.UPPER_BODY, "urn:decentraland:off-chain:base-avatars:m_sweater_02"},
45+
{WearableLiterals.Categories.LOWER_BODY, "urn:decentraland:off-chain:base-avatars:comfortablepants"},
46+
{WearableLiterals.Categories.FEET, "urn:decentraland:off-chain:base-avatars:Espadrilles"},
47+
{WearableLiterals.Categories.HAIR, "urn:decentraland:off-chain:base-avatars:cool_hair"},
48+
{WearableLiterals.Categories.FACIAL_HAIR, "urn:decentraland:off-chain:base-avatars:beard"},
49+
{WearableLiterals.Categories.EYEBROWS, "urn:decentraland:off-chain:base-avatars:eyebrows_00"},
50+
{WearableLiterals.Categories.EYES, "urn:decentraland:off-chain:base-avatars:eyes_00"},
51+
}}
52+
};
53+
2954
private string currentSlotSelected;
3055
private bool avatarIsDirty;
3156
private CancellationTokenSource loadProfileCancellationToken = new ();
@@ -350,7 +375,8 @@ private void UpdateAvatarPreview()
350375

351376
// We always keep the loaded emotes into the Avatar Preview
352377
foreach (string emoteId in dataStore.emotesCustomization.currentLoadedEmotes.Get())
353-
modelToUpdate.emotes.Add(new AvatarModel.AvatarEmoteEntry() { urn = emoteId });
378+
modelToUpdate.emotes.Add(new AvatarModel.AvatarEmoteEntry
379+
{ urn = emoteId });
354380

355381
UpdateAvatarModel(modelToUpdate);
356382
}
@@ -508,6 +534,7 @@ private void EquipWearable(WearableItem wearable,
508534
{
509535
UnEquipCurrentBodyShape();
510536
EquipBodyShape(wearable);
537+
ReplaceIncompatibleWearablesWithDefaultWearables();
511538
}
512539
else
513540
{
@@ -526,7 +553,7 @@ private void EquipWearable(WearableItem wearable,
526553
if (resetOverride)
527554
ResetOverridesOfAffectedCategories(wearable, setAsDirty);
528555

529-
avatarSlotsHUDController.Equip(wearable, ownUserProfile.avatar.bodyShape, model.forceRender);
556+
avatarSlotsHUDController.Equip(wearable, model.bodyShape.id, model.forceRender);
530557
wearableGridController.Equip(wearableId);
531558
}
532559

@@ -543,6 +570,36 @@ private void EquipWearable(WearableItem wearable,
543570
}
544571
}
545572

573+
private void ReplaceIncompatibleWearablesWithDefaultWearables()
574+
{
575+
WearableItem bodyShape = model.bodyShape;
576+
577+
if (bodyShape == null) return;
578+
if (!fallbackWearables.ContainsKey(bodyShape.id)) return;
579+
580+
HashSet<string> replacedCategories = new ();
581+
582+
foreach (var w in model.wearables.Values.ToArray())
583+
{
584+
if (w.SupportsBodyShape(bodyShape.id)) continue;
585+
586+
UnEquipWearable(w, UnequipWearableSource.None, true, false);
587+
588+
string category = w.data.category;
589+
590+
if (!string.IsNullOrEmpty(category) && !replacedCategories.Contains(category))
591+
replacedCategories.Add(category);
592+
}
593+
594+
Dictionary<string, string> fallbackWearablesByCategory = fallbackWearables[bodyShape.id];
595+
596+
foreach (string category in replacedCategories)
597+
{
598+
if (!fallbackWearablesByCategory.ContainsKey(category)) continue;
599+
EquipWearable(fallbackWearablesByCategory[category], EquipWearableSource.None, true, false);
600+
}
601+
}
602+
546603
private void UnEquipCurrentBodyShape(bool setAsDirty = true)
547604
{
548605
if (model.bodyShape.id == "NOT_LOADED") return;

unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/BackpackEditorHUDV2/Tests/BackpackEditorHUDControllerShould.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,34 @@ public async Task ExportVrm()
392392
Object.Destroy(smrs[i].gameObject);
393393
}
394394

395+
[Test]
396+
public void FallbackIncompatibleWearablesWhenChangingBodyShape()
397+
{
398+
userProfile.avatar.bodyShape = WearableLiterals.BodyShapes.FEMALE;
399+
userProfile.avatar.wearables.Add("urn:decentraland:off-chain:base-avatars:f_sweater");
400+
userProfile.avatar.wearables.Add("urn:decentraland:off-chain:base-avatars:f_jeans");
401+
userProfile.avatar.wearables.Add("urn:decentraland:off-chain:base-avatars:sneakers");
402+
403+
view.Configure().TakeSnapshotsAfterStopPreviewAnimation(
404+
Arg.InvokeDelegate<IBackpackEditorHUDView.OnSnapshotsReady>(testFace256Texture, testBodyTexture),
405+
Arg.Any<Action>());
406+
407+
dataStore.HUDs.avatarEditorVisible.Set(true, true);
408+
409+
wearableGridView.OnWearableEquipped += Raise.Event<Action<WearableGridItemModel, EquipWearableSource>>(new WearableGridItemModel
410+
{
411+
WearableId = WearableLiterals.BodyShapes.MALE,
412+
}, EquipWearableSource.Wearable);
413+
414+
dataStore.HUDs.avatarEditorVisible.Set(false, true);
415+
416+
Assert.IsTrue(userProfile.avatar.wearables.Contains("urn:decentraland:off-chain:base-avatars:m_sweater_02"));
417+
Assert.IsTrue(userProfile.avatar.wearables.Contains("urn:decentraland:off-chain:base-avatars:comfortablepants"));
418+
Assert.IsTrue(userProfile.avatar.wearables.Contains("urn:decentraland:off-chain:base-avatars:sneakers"));
419+
Assert.IsFalse(userProfile.avatar.wearables.Contains("urn:decentraland:off-chain:base-avatars:f_sweater"));
420+
Assert.IsFalse(userProfile.avatar.wearables.Contains("urn:decentraland:off-chain:base-avatars:f_jeans"));
421+
}
422+
395423
private static UserProfileModel GetTestUserProfileModel() =>
396424
new ()
397425
{

0 commit comments

Comments
 (0)