CharacterKit/CharacterTailData.cs

Data class for optional cosmetic tail settings on a character. Stores enabled flag, tip type, size/shape parameters and tip color, exposes computed properties for TipColor, EffectiveTipColor, TipScale, ShaftBulk, validation and a DeepCopy method.

using System;
using System.Text.Json.Serialization;
using Sandbox;

namespace CharacterEditor;
/// <summary>Optional cosmetic tail settings. Missing data in older saves leaves the tail disabled.</summary>
public sealed class CharacterTailData
{
	public bool Enabled { get; set; }
	public string Tip { get; set; } = "barbed";
	public float Length { get; set; } = .6f;
	public float Thickness { get; set; }
	public float TipSize { get; set; } = .5f;
	public bool CustomTipColor { get; set; }
	public float TipColorR { get; set; } = .18f;
	public float TipColorG { get; set; } = .12f;
	public float TipColorB { get; set; } = .10f;

	[JsonIgnore]
	public Color TipColor
	{
		get => new(TipColorR, TipColorG, TipColorB);
		set
		{
			TipColorR = value.r;
			TipColorG = value.g;
			TipColorB = value.b;
		}
	}

	public Color EffectiveTipColor(CharacterAppearanceData data) => CustomTipColor ? TipColor : data.SkinColor;
	[JsonIgnore]
	public float TipScale => TipSize <= .5f ? .5f + TipSize : TipSize * 2;

	// Short shafts have a tighter bend radius. Preserve their original girth,
	// but bound the extra bulk so the thick endpoint cannot swallow its own curl.
	[JsonIgnore]
	public float ShaftBulk => Thickness * (.35f + .65f * MathF.Min(1, Length / .6f));

	public static bool IsTip(string tip) => tip is "barbed" or "arrow" or "spade" or "tapered" or "single" or "tuft" or "spikes";
	[JsonIgnore]
	public bool Valid => IsTip(Tip) && Unit(Length) && Unit(Thickness) && Unit(TipSize) && Unit(TipColorR) && Unit(TipColorG) && Unit(TipColorB);

	static bool Unit(float value) => float.IsFinite(value) && value >= 0 && value <= 1;
	public CharacterTailData DeepCopy() => new()
	{
		Enabled = Enabled,
		Tip = Tip,
		Length = Length,
		Thickness = Thickness,
		TipSize = TipSize,
		CustomTipColor = CustomTipColor,
		TipColor = TipColor
	};
}