From d36032ab65090a84733d53fb1f4a103c99397c4f Mon Sep 17 00:00:00 2001 From: RandomlyFish Date: Thu, 19 Feb 2026 10:40:18 +0100 Subject: [PATCH 1/2] Support for inheriting color between attachments --- .../wardrobe/api/cosmetic/Cosmetic.java | 4 ++- .../cosmetic/appearance/TextureConfig.java | 5 ++++ .../wardrobe/impl/cosmetic/CosmeticAsset.java | 3 +- .../cosmetic/ModelAttachmentCosmetic.java | 8 ++++-- .../impl/cosmetic/PlayerModelCosmetic.java | 4 +-- .../HytaleBodyCharacteristicCosmetic.java | 8 ++++-- .../impl/cosmetic/builtin/HytaleCosmetic.java | 16 +++++------ .../builtin/HytaleHaircutCosmetic.java | 5 ++-- .../texture/GradientTextureConfig.java | 18 +++++++++++- .../impl/player/PlayerWardrobeComponent.java | 8 ++++-- .../impl/player/PlayerWardrobeSystems.java | 28 +++++++++++++++++-- 11 files changed, 80 insertions(+), 27 deletions(-) diff --git a/src/main/java/dev/hardaway/wardrobe/api/cosmetic/Cosmetic.java b/src/main/java/dev/hardaway/wardrobe/api/cosmetic/Cosmetic.java index 57eaca6..682b2bd 100644 --- a/src/main/java/dev/hardaway/wardrobe/api/cosmetic/Cosmetic.java +++ b/src/main/java/dev/hardaway/wardrobe/api/cosmetic/Cosmetic.java @@ -2,10 +2,12 @@ import dev.hardaway.wardrobe.api.player.PlayerCosmetic; +import javax.annotation.Nullable; + public interface Cosmetic { String getId(); - void applyCosmetic(WardrobeContext context, WardrobeCosmeticSlot slot, PlayerCosmetic playerCosmetic); + void applyCosmetic(WardrobeContext context, WardrobeCosmeticSlot slot, PlayerCosmetic playerCosmetic, @Nullable String gradientSet, @Nullable String gradientId); } diff --git a/src/main/java/dev/hardaway/wardrobe/api/cosmetic/appearance/TextureConfig.java b/src/main/java/dev/hardaway/wardrobe/api/cosmetic/appearance/TextureConfig.java index f2a2723..ba5dc3c 100644 --- a/src/main/java/dev/hardaway/wardrobe/api/cosmetic/appearance/TextureConfig.java +++ b/src/main/java/dev/hardaway/wardrobe/api/cosmetic/appearance/TextureConfig.java @@ -15,5 +15,10 @@ default String getGradientSet() { return null; } + @Nullable + default String getGradientFrom() { + return null; + } + String[] collectVariants(); } diff --git a/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/CosmeticAsset.java b/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/CosmeticAsset.java index 5bf69e9..03f2428 100644 --- a/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/CosmeticAsset.java +++ b/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/CosmeticAsset.java @@ -20,6 +20,7 @@ import dev.hardaway.wardrobe.api.property.validator.WardrobeValidators; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.function.Supplier; public abstract class CosmeticAsset implements WardrobeCosmetic, JsonAssetWithMap> { @@ -139,7 +140,7 @@ public String[] getHiddenCosmeticSlotIds() { } @Override - public void applyCosmetic(WardrobeContext context, WardrobeCosmeticSlot slot, PlayerCosmetic playerCosmetic) { + public void applyCosmetic(WardrobeContext context, WardrobeCosmeticSlot slot, PlayerCosmetic playerCosmetic, @Nullable String gradientSet, @Nullable String gradientId) { context.hideSlots(this.getHiddenCosmeticSlotIds()); } } \ No newline at end of file diff --git a/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/ModelAttachmentCosmetic.java b/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/ModelAttachmentCosmetic.java index 0ea80cb..c5b31be 100644 --- a/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/ModelAttachmentCosmetic.java +++ b/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/ModelAttachmentCosmetic.java @@ -159,8 +159,8 @@ public List getVariantEntries(@Nullable String variantId) } @Override - public void applyCosmetic(WardrobeContext context, WardrobeCosmeticSlot slot, PlayerCosmetic playerCosmetic) { - super.applyCosmetic(context, slot, playerCosmetic); + public void applyCosmetic(WardrobeContext context, WardrobeCosmeticSlot slot, PlayerCosmetic playerCosmetic, @Nullable String gradientSet, @Nullable String gradientId) { + super.applyCosmetic(context, slot, playerCosmetic, gradientSet, gradientId); Appearance appearance = this.getAppearance(); if (slot.getArmorSlot() != null && this.getArmorAppearance() != null) { @@ -207,12 +207,14 @@ public void applyCosmetic(WardrobeContext context, WardrobeCosmeticSlot slot, Pl option = appearance.collectVariants()[0]; } - String variant = playerCosmetic.getVariantId(); + // EDIT + String variant = gradientId == null ? playerCosmetic.getVariantId() : gradientId; if (appearance.getTextureConfig(option).getTexture(variant) == null) { variant = appearance.getTextureConfig(option).collectVariants()[0]; } TextureConfig textureConfig = appearance.getTextureConfig(option); + System.out.println("textureConfig: " + textureConfig.getGradientFrom()); context.addAttachment(slot.getId(), new ModelAttachment( appearance.getModel(option), textureConfig.getTexture(variant), diff --git a/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/PlayerModelCosmetic.java b/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/PlayerModelCosmetic.java index 02f94d0..a7293e9 100644 --- a/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/PlayerModelCosmetic.java +++ b/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/PlayerModelCosmetic.java @@ -78,8 +78,8 @@ public Appearance getAppearance() { } @Override - public void applyCosmetic(WardrobeContext context, WardrobeCosmeticSlot slot, PlayerCosmetic playerCosmetic) { - super.applyCosmetic(context, slot, playerCosmetic); + public void applyCosmetic(WardrobeContext context, WardrobeCosmeticSlot slot, PlayerCosmetic playerCosmetic, @Nullable String gradientSet, @Nullable String gradientId) { + super.applyCosmetic(context, slot, playerCosmetic, gradientSet, gradientId); Appearance appearance = this.getAppearance(); String option = playerCosmetic.getOptionId(); diff --git a/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/builtin/HytaleBodyCharacteristicCosmetic.java b/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/builtin/HytaleBodyCharacteristicCosmetic.java index 47f0106..a7fdf71 100644 --- a/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/builtin/HytaleBodyCharacteristicCosmetic.java +++ b/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/builtin/HytaleBodyCharacteristicCosmetic.java @@ -7,13 +7,15 @@ import dev.hardaway.wardrobe.api.cosmetic.WardrobeCosmeticSlot; import dev.hardaway.wardrobe.api.player.PlayerCosmetic; +import javax.annotation.Nullable; + public class HytaleBodyCharacteristicCosmetic extends HytaleCosmetic { public HytaleBodyCharacteristicCosmetic(CosmeticType type, PlayerSkinPart part) { super(type, part); } @Override - public void applyCosmetic(WardrobeContext context, WardrobeCosmeticSlot slot, PlayerCosmetic playerCosmetic) { + public void applyCosmetic(WardrobeContext context, WardrobeCosmeticSlot slot, PlayerCosmetic playerCosmetic, @Nullable String gradientSet, @Nullable String gradientId) { Model model = context.getPlayerModel(); context.setPlayerModel(new Model( model.getModelAssetId(), @@ -23,8 +25,8 @@ public void applyCosmetic(WardrobeContext context, WardrobeCosmeticSlot slot, Pl model.getBoundingBox(), this.getPart().getModel(), this.getPart().getGreyscaleTexture(), - this.getPart().getGradientSet(), - playerCosmetic.getVariantId(), + gradientSet == null ? this.getPart().getGradientSet() : gradientSet, + gradientId == null ? playerCosmetic.getVariantId() : gradientId, model.getEyeHeight(), model.getCrouchOffset(), model.getSittingOffset(), diff --git a/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/builtin/HytaleCosmetic.java b/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/builtin/HytaleCosmetic.java index f0b61e5..300a1c4 100644 --- a/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/builtin/HytaleCosmetic.java +++ b/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/builtin/HytaleCosmetic.java @@ -44,11 +44,9 @@ public PlayerSkinPart getPart() { } @Nullable - protected ModelAttachment createAttachment(WardrobeContext context, WardrobeCosmeticSlot slot, PlayerCosmetic playerCosmetic) { + protected ModelAttachment createAttachment(WardrobeContext context, WardrobeCosmeticSlot slot, PlayerCosmetic playerCosmetic, @Nullable String gradientSet, @Nullable String gradientId) { String model; String texture; - @Nullable String gradientSet = null; - @Nullable String gradientId = null; if (!this.variantMap.isEmpty()) { PlayerSkinPart.Variant variant = this.variantMap.get(playerCosmetic.getOptionId()); @@ -63,8 +61,8 @@ protected ModelAttachment createAttachment(WardrobeContext context, WardrobeCosm texture = partTexture.getTexture(); } else { texture = variant.getGreyscaleTexture(); - gradientSet = this.part.getGradientSet(); - gradientId = playerCosmetic.getVariantId(); + gradientSet = gradientSet == null ? this.part.getGradientSet() : gradientSet; + gradientId = gradientId == null ? playerCosmetic.getVariantId() : gradientId; } } else { model = this.part.getModel(); @@ -76,8 +74,8 @@ protected ModelAttachment createAttachment(WardrobeContext context, WardrobeCosm texture = partTexture.getTexture(); } else { texture = this.part.getGreyscaleTexture(); - gradientSet = this.part.getGradientSet(); - gradientId = playerCosmetic.getVariantId(); + gradientSet = gradientSet == null ? this.part.getGradientSet() : gradientSet; + gradientId = gradientId == null ? playerCosmetic.getVariantId() : gradientId; } } @@ -95,8 +93,8 @@ protected ModelAttachment createAttachment(WardrobeContext context, WardrobeCosm } @Override - public void applyCosmetic(WardrobeContext context, WardrobeCosmeticSlot slot, PlayerCosmetic playerCosmetic) { - ModelAttachment attachment = this.createAttachment(context, slot, playerCosmetic); + public void applyCosmetic(WardrobeContext context, WardrobeCosmeticSlot slot, PlayerCosmetic playerCosmetic, @Nullable String gradientSet, @Nullable String gradientId) { + ModelAttachment attachment = this.createAttachment(context, slot, playerCosmetic, gradientSet, gradientId); if (attachment == null) return; // TODO: warn? context.addAttachment(slot.getId(), attachment); diff --git a/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/builtin/HytaleHaircutCosmetic.java b/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/builtin/HytaleHaircutCosmetic.java index 6ee88f7..a838461 100644 --- a/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/builtin/HytaleHaircutCosmetic.java +++ b/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/builtin/HytaleHaircutCosmetic.java @@ -11,6 +11,7 @@ import dev.hardaway.wardrobe.api.player.PlayerCosmetic; import dev.hardaway.wardrobe.impl.cosmetic.ModelAttachmentCosmetic; +import javax.annotation.Nullable; import java.util.Objects; public class HytaleHaircutCosmetic extends HytaleCosmetic { @@ -19,8 +20,8 @@ public HytaleHaircutCosmetic(CosmeticType type, PlayerSkinPart part) { } @Override - protected ModelAttachment createAttachment(WardrobeContext context, WardrobeCosmeticSlot slot, PlayerCosmetic playerCosmetic) { - ModelAttachment attachment = super.createAttachment(context, slot, playerCosmetic); + protected ModelAttachment createAttachment(WardrobeContext context, WardrobeCosmeticSlot slot, PlayerCosmetic playerCosmetic, @Nullable String gradientSet, @Nullable String gradientId) { + ModelAttachment attachment = super.createAttachment(context, slot, playerCosmetic, gradientSet, gradientId); if (attachment == null) return null; CosmeticRegistry cosmeticRegistry = CosmeticsModule.get().getRegistry(); diff --git a/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/texture/GradientTextureConfig.java b/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/texture/GradientTextureConfig.java index 345da85..6e5dcb1 100644 --- a/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/texture/GradientTextureConfig.java +++ b/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/texture/GradientTextureConfig.java @@ -26,6 +26,14 @@ public class GradientTextureConfig implements TextureConfig { .metadata(new UIPropertyTitle("Gradient Set")).documentation("The Gradient Set determines which colors appear under the 'Variants' section in the Wardrobe Menu.") .add() + .append(new KeyedCodec<>("GradientFrom", Codec.STRING, true), + (t, value) -> t.gradientFrom = value, + t -> t.gradientFrom + ) + // .addValidator(CommonAssetValidator.TEXTURE_CHARACTER_ATTACHMENT) // TODO: Create validator + .metadata(new UIPropertyTitle("Gradient From")).documentation("The slot to take the gradient and color from.") + .add() + .append(new KeyedCodec<>("GrayscaleTexture", Codec.STRING, true), (t, value) -> t.grayscaleTexture = value, t -> t.grayscaleTexture @@ -37,13 +45,15 @@ public class GradientTextureConfig implements TextureConfig { .build(); private String gradientSet; + private String gradientFrom; private String grayscaleTexture; private GradientTextureConfig() { } - public GradientTextureConfig(String gradientSet, String grayscaleTexture) { + public GradientTextureConfig(String gradientSet, String gradientFrom, String grayscaleTexture) { this.gradientSet = gradientSet; + this.gradientFrom = gradientFrom; this.grayscaleTexture = grayscaleTexture; } @@ -59,6 +69,12 @@ public String getGradientSet() { return gradientSet; } + @Nonnull + @Override + public String getGradientFrom() { + return gradientFrom; + } + @Override public String[] collectVariants() { return CosmeticsModule.get().getRegistry().getGradientSets().get(this.getGradientSet()).getGradients().keySet().toArray(String[]::new); diff --git a/src/main/java/dev/hardaway/wardrobe/impl/player/PlayerWardrobeComponent.java b/src/main/java/dev/hardaway/wardrobe/impl/player/PlayerWardrobeComponent.java index f435e21..7b49c71 100644 --- a/src/main/java/dev/hardaway/wardrobe/impl/player/PlayerWardrobeComponent.java +++ b/src/main/java/dev/hardaway/wardrobe/impl/player/PlayerWardrobeComponent.java @@ -82,12 +82,14 @@ public void setCosmetic(String slot, PlayerCosmetic cosmetic) { } if (CosmeticAsset.getAssetMap().getAsset(cosmetic.getCosmeticId()) instanceof PlayerModelCosmetic) { - for (Map.Entry entry : this.cosmetics.entrySet()) { - String slotId = entry.getKey(); + Iterator> iterator = this.cosmetics.entrySet().iterator(); + + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); CosmeticSaveData existingCosmetic = entry.getValue(); if (CosmeticAsset.getAssetMap().getAsset(existingCosmetic.getCosmeticId()) instanceof PlayerModelCosmetic) { - this.cosmetics.remove(slotId); + iterator.remove(); this.cosmeticIdSet.remove(existingCosmetic.getCosmeticId()); } } diff --git a/src/main/java/dev/hardaway/wardrobe/impl/player/PlayerWardrobeSystems.java b/src/main/java/dev/hardaway/wardrobe/impl/player/PlayerWardrobeSystems.java index 1c9cea1..8529e9c 100644 --- a/src/main/java/dev/hardaway/wardrobe/impl/player/PlayerWardrobeSystems.java +++ b/src/main/java/dev/hardaway/wardrobe/impl/player/PlayerWardrobeSystems.java @@ -22,11 +22,13 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import dev.hardaway.wardrobe.WardrobeUtil; +import dev.hardaway.wardrobe.api.cosmetic.Cosmetic; import dev.hardaway.wardrobe.api.cosmetic.WardrobeCosmeticSlot; import dev.hardaway.wardrobe.api.player.PlayerCosmetic; import dev.hardaway.wardrobe.api.player.PlayerWardrobe; import dev.hardaway.wardrobe.impl.cosmetic.CosmeticAsset; import dev.hardaway.wardrobe.impl.cosmetic.CosmeticSlotAsset; +import dev.hardaway.wardrobe.impl.cosmetic.ModelAttachmentCosmetic; import dev.hardaway.wardrobe.impl.cosmetic.builtin.HytaleCosmetic; import dev.hardaway.wardrobe.impl.cosmetic.builtin.HytalePlayerCosmetic; @@ -109,6 +111,8 @@ public static Model buildWardrobeModel(Player player, PlayerSettings playerSetti } } + Map applyAfter = new HashMap<>(); + // Apply custom cosmetics context.getCosmeticMap().forEach((slotId, cosmetic) -> { WardrobeCosmeticSlot slot = slots.get(slotId); // TODO: replace with registry @@ -121,11 +125,31 @@ public static Model buildWardrobeModel(Player player, PlayerSettings playerSetti cosmeticData = new HytalePlayerCosmetic(part); } - - cosmetic.applyCosmetic(context, slot, cosmeticData); + if (cosmetic instanceof ModelAttachmentCosmetic modelAttachmentCosmetic) { + if (modelAttachmentCosmetic.getAppearance().getTextureConfig(null).getGradientFrom() != null) { + applyAfter.put(slotId, modelAttachmentCosmetic); + return; + } + } + cosmetic.applyCosmetic(context, slot, cosmeticData, null, null); } }); + for (String slotId : applyAfter.keySet()) { + ModelAttachmentCosmetic cosmetic = applyAfter.get(slotId); + String gradientFrom = cosmetic.getAppearance().getTextureConfig(null).getGradientFrom(); + Cosmetic gradientFromCosmetic = context.getCosmeticMap().get(gradientFrom); + PlayerCosmetic gradientFromPlayerCosmetic = context.getCosmetic(gradientFrom); + if (gradientFromCosmetic == null || gradientFromPlayerCosmetic == null) continue; + WardrobeCosmeticSlot slot = slots.get(slotId); // TODO: replace with registry + if (slot != null) { + // TODO: validate & warn + String gradientId = gradientFromPlayerCosmetic.getVariantId(); + PlayerCosmetic cosmeticData = wardrobeComponent.getCosmetic(slotId); + cosmetic.applyCosmetic(context, slot, cosmeticData, null, gradientId); + } + } + // Handle armor hiding Set hiddenHytaleSlots = new HashSet<>(); ItemContainer armorContainer = player.getInventory().getArmor(); From 9094561aca71c120b8b4ca781cc0214328c929c8 Mon Sep 17 00:00:00 2001 From: notSafeForDev Date: Mon, 23 Feb 2026 19:27:51 +0100 Subject: [PATCH 2/2] Removed left over comment --- .../hardaway/wardrobe/impl/cosmetic/ModelAttachmentCosmetic.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/ModelAttachmentCosmetic.java b/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/ModelAttachmentCosmetic.java index c5b31be..33126f8 100644 --- a/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/ModelAttachmentCosmetic.java +++ b/src/main/java/dev/hardaway/wardrobe/impl/cosmetic/ModelAttachmentCosmetic.java @@ -207,7 +207,6 @@ public void applyCosmetic(WardrobeContext context, WardrobeCosmeticSlot slot, Pl option = appearance.collectVariants()[0]; } - // EDIT String variant = gradientId == null ? playerCosmetic.getVariantId() : gradientId; if (appearance.getTextureConfig(option).getTexture(variant) == null) { variant = appearance.getTextureConfig(option).collectVariants()[0];