Player/CellPiece.cs
using System;

/// <summary>
/// 分裂细胞 / 吐出的孢子(**纯数据实体,不是 GameObject**)。
/// 为什么不走网络对象:运行时 live create 广播到已连接客户端的可靠性未验证(M2 遗留),
/// 而食物同款"静态 RPC 快照"是本项目实测 100% 可靠的唯一同步通道。
/// host 权威模拟(冲量衰减 + 跟随主人转向),状态经 CellsState 静态 RPC 按 CellSyncInterval
/// 广播;各端本地镜像列表只负责渲染与平滑(DrawPos 追 Pos,客户端把 15Hz 快照插顺)。
/// </summary>
public sealed class CellPiece
{
	public enum Kind : byte
	{
		SplitPiece = 0,   // 分裂出的细胞(跟随主人方向移动,冷却后并回)
		EjectedMass = 1,  // 吐出的孢子(中性食物团,谁都能吃)
	}

	public int Id;

	public Kind PieceKind;

	/// <summary> 主人 SteamId(bot 为 0;bot 不分裂,此字段仅真人有值) </summary>
	public long OwnerSteamId;

	public int ColorIndex;

	public float Mass;

	/// <summary> 权威位置(host 每帧推进;客户端 = 最近一次快照值,15Hz 跳变) </summary>
	public Vector3 Pos;

	/// <summary> 渲染位置:向 Pos 指数平滑(host 逐帧权威直接贴齐;客户端插值) </summary>
	public Vector3 DrawPos;

	/// <summary> 弹射冲量(分裂/吐出瞬间),随时间指数衰减 </summary>
	public Vector3 Impulse;

	/// <summary> 跟随主人移动方向的速度分量(仅 host 模拟) </summary>
	public Vector3 SteerVel;

	/// <summary> 出生时刻:合并冷却(分身)与可食延迟(孢子)都看它 </summary>
	public TimeSince SinceSpawn;

	/// <summary> 仅 host:主人球引用(分身清理、转向方向来源);孢子恒为 null(中性存活) </summary>
	public Ball OwnerBall;

	/// <summary> 半径沿用球的质量定律:r = StartRadius × √(mass/StartMass) </summary>
	public float Radius => GameConfig.StartRadius * MathF.Sqrt( MathF.Max( 1f, Mass ) / GameConfig.StartMass );

	/// <summary> 移动速度沿用球的速度曲线:越大越慢 </summary>
	public float Speed => MathF.Max( GameConfig.MinSpeed,
		GameConfig.BaseSpeed * MathF.Pow( GameConfig.StartMass / MathF.Max( 1f, Mass ), GameConfig.SpeedCurve ) );

	/// <summary> 合并冷却时长(秒):主动分裂用全局 15s;撞刺炸出的碎片用短冷却(SpikePieceMergeCooldown) </summary>
	public float MergeCooldown = GameConfig.MergeCooldownSeconds;

	/// <summary> 合并冷却是否已过(仅分身有意义) </summary>
	public bool IsMergeReady => SinceSpawn > MergeCooldown;
}

/// <summary> CellsState RPC 的线格式(全 blittable 字段,同 FoodData 模式) </summary>
public struct CellWire
{
	public int Id;
	public byte Kind;        // CellPiece.Kind
	public byte ColorIndex;
	public long Owner;       // OwnerSteamId
	public float X;
	public float Y;
	public float Mass;
}