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
};
}