Player/Components/PlayerDresser.cs

Component attached to a player that configures and applies clothing to a SkinnedModelRenderer. It reads avatar JSON from the network owner on spawn, builds a ClothingContainer, optionally overrides height scale, merges preset Clothing entries, normalizes and applies them to the renderer, and sets team-specific materials for clothing child model objects.

Networking
using Sandbox;
using Sandbox.Diagnostics;
using System.Numerics;

namespace KOTH;

public sealed class PlayerDresser : Component, Component.INetworkSpawn
{
	[Property] public SkinnedModelRenderer ModelRenderer { get; set; }
	[Property] public bool ApplyHeightScale { get; set; } = true;
	[Property] public List<ClothingContainer.ClothingEntry> Clothing { get; set; }
	[Property] public PlayerPawn LocalPlayer { get; set; }
	[Property] Material CTMaterial { get; set; }
	[Property] Material TMaterial { get; set; }

	public ClothingContainer EquippedClothes { get; private set; } = null;
	string ClotheJSON { get; set; }

	protected override void OnStart()
	{
		base.OnStart();

		SetupClothes();
	}

	public void OnNetworkSpawn(Connection Owner)
	{
		ClotheJSON = Owner.GetUserData("avatar");

		ApplyClothing();
	}

	public void ApplyClothing()
	{
		Assert.IsValid(ModelRenderer);

		EquippedClothes = ClotheJSON == "" ? new ClothingContainer() : ClothingContainer.CreateFromJson(ClotheJSON);

		if (!ApplyHeightScale)
		{
			EquippedClothes.Height = 1;
		}

		EquippedClothes.AddRange(Clothing);
		EquippedClothes.Normalize();
		EquippedClothes.Apply(ModelRenderer);
	}

	public void SetupClothes()
	{
		// why don't we keep a reference of these ANYWHERE when we spawn them? @facepunch

		if (!LocalPlayer.IsValid())
		{
			return;
		}

		foreach (var ChildBodyObject in GameObject.Children)
		{
			if (!ChildBodyObject.IsValid() || !ChildBodyObject.Tags.Contains("clothing"))
			{
				continue;
			}

			if (ChildBodyObject.GetComponent<SkinnedModelRenderer>() is { } ClothModel)
			{
				if (LocalPlayer.Team == Team.CounterTerrorist)
				{
					ClothModel.SetMaterial(CTMaterial);
				}
				else if (LocalPlayer.Team == Team.Terrorist)
				{
					ClothModel.SetMaterial(TMaterial);
				}

				ClothModel.RenderType = LocalPlayer.IsLocallyControlled ? Sandbox.ModelRenderer.ShadowRenderType.ShadowsOnly : Sandbox.ModelRenderer.ShadowRenderType.On;
			}
		}

	}
}