Bläddra i källkod

支持名称和描述修改

KikkaSSP 1 månad sedan
förälder
incheckning
03a8eb983f

+ 422 - 423
GameProject/AvatarDoll/Struct/AvatarDollStruct.cs

@@ -5,456 +5,455 @@ using System.Text.Json.Serialization;
 
 namespace AvatarDoll
 {
-    /// <summary>
-    /// Avatar作者的元信息
-    /// </summary>
-    [Serializable]
-    public struct Metadata
-    {
-        [JsonInclude] public string Author { get; set; }
-        [JsonInclude] public string Introduction { get; set; }
-        [JsonInclude] public string Version { get; set; }
-        [JsonInclude] public string Link { get; set; }
-    }
+	/// <summary>
+	/// Avatar作者的元信息
+	/// </summary>
+	[Serializable]
+	public struct Metadata
+	{
+		[JsonInclude] public string Author { get; set; }
+		[JsonInclude] public string Introduction { get; set; }
+		[JsonInclude] public string Version { get; set; }
+		[JsonInclude] public string Link { get; set; }
+	}
 
-    
-    [Serializable]
-    public struct PartState
-    {
-        public PartState() { }
-        
-        [JsonInclude] public string Name { get; set; } = "";
-        [JsonInclude] public PartScope Scope { get; set; } = 0;
-        [JsonInclude] public byte Default { get; set; } = 0;
-        [JsonInclude] public List<string> Values { get; set; } = new();
-        [JsonInclude] public List<bool> Required { get; set; } = new();
-        
-        public override bool Equals(object obj)
-        {
-            return obj is PartState state && Equals(state);
-        }
-        
-        public bool Equals(PartState other)
-        {
-            return Name == other.Name &&
-                   Scope == other.Scope &&
-                   Default == other.Default &&
-                   System.Linq.Enumerable.SequenceEqual(Values, other.Values) &&
-                   System.Linq.Enumerable.SequenceEqual(Required, other.Required);
-        }
-    }
-    
-    /// <summary>
-    /// Avatar配置项
-    /// </summary>
-    [Serializable]
-    public struct ProjectSettings
-    {
-        public ProjectSettings() { }
+	
+	[Serializable]
+	public struct PartState
+	{
+		public PartState() { }
+		
+		[JsonInclude] public string Name { get; set; } = "";
+		[JsonInclude] public PartScope Scope { get; set; } = 0;
+		[JsonInclude] public byte Default { get; set; } = 0;
+		[JsonInclude] public List<string> Values { get; set; } = new();
+		[JsonInclude] public List<bool> Required { get; set; } = new();
+		
+		public override bool Equals(object obj)
+		{
+			return obj is PartState state && Equals(state);
+		}
+		
+		public bool Equals(PartState other)
+		{
+			return Name == other.Name &&
+				   Scope == other.Scope &&
+				   Default == other.Default &&
+				   System.Linq.Enumerable.SequenceEqual(Values, other.Values) &&
+				   System.Linq.Enumerable.SequenceEqual(Required, other.Required);
+		}
+	}
+	
+	/// <summary>
+	/// Avatar配置项
+	/// </summary>
+	[Serializable]
+	public struct ProjectSettings
+	{
+		public ProjectSettings() { }
 
-        [JsonInclude] public int MaxBodySlotCost { get; set; } = 5;
-        [JsonInclude] public List<PartState> States { get; set; } = new();
-        
-        public static int GetStatusesCode(ProjectSettings settings)
-        {
-            int finalHash = 0;
-            foreach (var state in settings.States)
-            {
-                int listHash = 0;
-                foreach (var value in state.Required)
-                {
-                    int valueHash = value ? 1 : 0;
-                    listHash = CombineHashes(listHash, valueHash);
-                }
-                finalHash = CombineHashes(finalHash, listHash);
-            }
-            return finalHash;
-        }
+		[JsonInclude] public int MaxBodySlotCost { get; set; } = 5;
+		[JsonInclude] public List<PartState> States { get; set; } = new();
+		
+		public static int GetStatusesCode(ProjectSettings settings)
+		{
+			int finalHash = 0;
+			foreach (var state in settings.States)
+			{
+				int listHash = 0;
+				foreach (var value in state.Required)
+				{
+					int valueHash = value ? 1 : 0;
+					listHash = CombineHashes(listHash, valueHash);
+				}
+				finalHash = CombineHashes(finalHash, listHash);
+			}
+			return finalHash;
+		}
 
-        private static int CombineHashes(int h1, int h2)
-        {
-            long hash = 2166136261L;
-    
-            hash = (hash ^ (h1 & 0xFF)) * 16777619;
-            hash = (hash ^ ((h1 >> 8) & 0xFF)) * 16777619;
-            hash = (hash ^ ((h1 >> 16) & 0xFF)) * 16777619;
-            hash = (hash ^ ((h1 >> 24) & 0xFF)) * 16777619;
-    
-            hash = (hash ^ (h2 & 0xFF)) * 16777619;
-            hash = (hash ^ ((h2 >> 8) & 0xFF)) * 16777619;
-            hash = (hash ^ ((h2 >> 16) & 0xFF)) * 16777619;
-            hash = (hash ^ ((h2 >> 24) & 0xFF)) * 16777619;
-    
-            return (int)(hash & 0x7FFFFFFF);
-        }
-        
-        public override bool Equals(object obj)
-        {
-            return obj is ProjectSettings settings && Equals(settings);
-        }
-        
-        public bool Equals(ProjectSettings other)
-        {
-            bool equal = MaxBodySlotCost == other.MaxBodySlotCost
-                         && States.Count == other.States.Count;
-            if (equal == false) return false;
-            
-            for (int i = 0; i < States.Count; i++)
-                equal &= States[i].Equals(other.States[i]);
-            return equal;
-        }
-        
-        public override int GetHashCode()
-        {
-            return GetStatusesCode(this);
-        }
-    }
-    
-    /// <summary>
-    /// 
-    /// </summary>
-    [Serializable]
-    public struct PaletteColor
-    {
-        [JsonInclude] public float R { get; set; }
-        [JsonInclude] public float G { get; set; }
-        [JsonInclude] public float B { get; set; }
-        [JsonInclude] public float A { get; set; }
-        
-        public PaletteColor(float r, float g, float b, float a = 1.0f) { R = r; G = g; B = b; A = a; }
-        public PaletteColor(Godot.Color color) { R = color.R; G = color.G; B = color.B; A = color.A; }
-        
-        public static implicit operator Godot.Color(PaletteColor color) { return new Godot.Color(color.R, color.G, color.B, color.A); }
-        public static implicit operator PaletteColor(Godot.Color color) { return new PaletteColor(color); }
-        
-        public override bool Equals(object obj) 
-        {
-            if (obj is PaletteColor other)
-            {
-                return (double) this.R == (double) other.R && (double) this.G == (double) other.G && (double) this.B == (double) other.B && (double) this.A == (double) other.A;
-            }
-            return false;
-        }
-        
-        public override int GetHashCode()
-        {
-            return HashCode.Combine(R, G, B, A);
-        }
-    }
-    
-    /// <summary>
-    /// 图片调色参数
-    /// </summary>
-    [Serializable]
-    public struct PaletteParam()
-    {
-        [JsonInclude] public int ElementIndex = -1;
-        [JsonInclude] public PaletteColor BasicColor { get; set; } = new PaletteColor(0,0,0,0);
-        [JsonInclude] public int Tolerance = 0;
-        [JsonInclude] public PaletteColor ModulateColor { get; set; } = new PaletteColor(0,0,0,0);
-        [JsonInclude] public int BlendMode = 0;
-        
-        public override bool Equals(object obj)
-        {
-            return obj is PaletteParam other && Equals(other);
-        }
+		private static int CombineHashes(int h1, int h2)
+		{
+			long hash = 2166136261L;
+	
+			hash = (hash ^ (h1 & 0xFF)) * 16777619;
+			hash = (hash ^ ((h1 >> 8) & 0xFF)) * 16777619;
+			hash = (hash ^ ((h1 >> 16) & 0xFF)) * 16777619;
+			hash = (hash ^ ((h1 >> 24) & 0xFF)) * 16777619;
+	
+			hash = (hash ^ (h2 & 0xFF)) * 16777619;
+			hash = (hash ^ ((h2 >> 8) & 0xFF)) * 16777619;
+			hash = (hash ^ ((h2 >> 16) & 0xFF)) * 16777619;
+			hash = (hash ^ ((h2 >> 24) & 0xFF)) * 16777619;
+	
+			return (int)(hash & 0x7FFFFFFF);
+		}
+		
+		public override bool Equals(object obj)
+		{
+			return obj is ProjectSettings settings && Equals(settings);
+		}
+		
+		public bool Equals(ProjectSettings other)
+		{
+			bool equal = MaxBodySlotCost == other.MaxBodySlotCost
+						 && States.Count == other.States.Count;
+			if (equal == false) return false;
+			
+			for (int i = 0; i < States.Count; i++)
+				equal &= States[i].Equals(other.States[i]);
+			return equal;
+		}
+		
+		public override int GetHashCode()
+		{
+			return GetStatusesCode(this);
+		}
+	}
+	
+	/// <summary>
+	/// 
+	/// </summary>
+	[Serializable]
+	public struct PaletteColor
+	{
+		[JsonInclude] public float R { get; set; }
+		[JsonInclude] public float G { get; set; }
+		[JsonInclude] public float B { get; set; }
+		[JsonInclude] public float A { get; set; }
+		
+		public PaletteColor(float r, float g, float b, float a = 1.0f) { R = r; G = g; B = b; A = a; }
+		public PaletteColor(Godot.Color color) { R = color.R; G = color.G; B = color.B; A = color.A; }
+		
+		public static implicit operator Godot.Color(PaletteColor color) { return new Godot.Color(color.R, color.G, color.B, color.A); }
+		public static implicit operator PaletteColor(Godot.Color color) { return new PaletteColor(color); }
+		
+		public override bool Equals(object obj) 
+		{
+			if (obj is PaletteColor other)
+			{
+				return (double) this.R == (double) other.R && (double) this.G == (double) other.G && (double) this.B == (double) other.B && (double) this.A == (double) other.A;
+			}
+			return false;
+		}
+		
+		public override int GetHashCode()
+		{
+			return HashCode.Combine(R, G, B, A);
+		}
+	}
+	
+	/// <summary>
+	/// 图片调色参数
+	/// </summary>
+	[Serializable]
+	public struct PaletteParam()
+	{
+		[JsonInclude] public int ElementIndex = -1;
+		[JsonInclude] public PaletteColor BasicColor { get; set; } = new PaletteColor(0,0,0,0);
+		[JsonInclude] public int Tolerance = 0;
+		[JsonInclude] public PaletteColor ModulateColor { get; set; } = new PaletteColor(0,0,0,0);
+		[JsonInclude] public int BlendMode = 0;
+		
+		public override bool Equals(object obj)
+		{
+			return obj is PaletteParam other && Equals(other);
+		}
 
-        public bool Equals(PaletteParam other)
-        {
-            return BasicColor.Equals(other.BasicColor) &&
-                   ModulateColor.Equals(other.ModulateColor) &&
-                   Tolerance == other.Tolerance &&
-                   BlendMode == other.BlendMode;
-        }
-        
-        public override int GetHashCode()
-        {
-            return HashCode.Combine(ElementIndex, Tolerance, BlendMode, BasicColor.GetHashCode(), ModulateColor.GetHashCode());
-        }
-    }
+		public bool Equals(PaletteParam other)
+		{
+			return BasicColor.Equals(other.BasicColor) &&
+				   ModulateColor.Equals(other.ModulateColor) &&
+				   Tolerance == other.Tolerance &&
+				   BlendMode == other.BlendMode;
+		}
+		
+		public override int GetHashCode()
+		{
+			return HashCode.Combine(ElementIndex, Tolerance, BlendMode, BasicColor.GetHashCode(), ModulateColor.GetHashCode());
+		}
+	}
 
-        
-    /// <summary>
-    /// 调色板。用于保存其他上色方案,且可以指定不同配色下的别名
-    /// </summary>
-    [Serializable]
-    public struct Palette()
-    {
-        [JsonInclude] public uint Guid { get; set; } = 0;
-        [JsonInclude] public string Name { get; set; }
-        [JsonInclude] public string Introduction { get; set; }
-        
-        [JsonInclude] public List<ThemeColor> ThemeColors { get; set; } = [];
-        [JsonInclude] public List<PaletteParam> IconPaletteParams { get; set; } = [];
-        [JsonInclude] public List<PaletteParam> ElementPaletteParams { get; set; } = [];
-    }
+		
+	/// <summary>
+	/// 调色板。用于保存其他上色方案,且可以指定不同配色下的别名
+	/// </summary>
+	[Serializable]
+	public struct Palette()
+	{
+		[JsonInclude] public uint Guid { get; set; } = 0;
+		[JsonInclude] public string Name { get; set; }
+		[JsonInclude] public string Introduction { get; set; }
+		
+		[JsonInclude] public List<ThemeColor> ThemeColors { get; set; } = [];
+		[JsonInclude] public List<PaletteParam> IconPaletteParams { get; set; } = [];
+		[JsonInclude] public List<PaletteParam> ElementPaletteParams { get; set; } = [];
+	}
 
-    
-    /// <summary>
-    /// Avatar的某个元素的集合,可以是一个身形,或者一个脸型,一件服装等
-    /// 例如对于头发,就是一个发型在所有Pose下的样子的集合。脸型、服装等同理
-    /// </summary>
-    [Serializable]
-    public class AvatarPart()
-    {
-        [JsonInclude] public uint Guid { get; set; } = 0;
-        [JsonInclude] public string Name { get; set; }
-        [JsonInclude] public int IconAssetId { get; set; }
-        [JsonInclude] public string Introduction { get; set; }
-        
-        [JsonInclude] public List<ThemeColor> ThemeColors { get; set; } = [];
+	
+	/// <summary>
+	/// Avatar的某个元素的集合,可以是一个身形,或者一个脸型,一件服装等
+	/// 例如对于头发,就是一个发型在所有Pose下的样子的集合。脸型、服装等同理
+	/// </summary>
+	[Serializable]
+	public class AvatarPart()
+	{
+		[JsonInclude] public uint Guid { get; set; } = 0;
+		[JsonInclude] public string Name { get; set; }
+		[JsonInclude] public int IconAssetId { get; set; }
+		[JsonInclude] public string Introduction { get; set; }
+		
+		[JsonInclude] public List<ThemeColor> ThemeColors { get; set; } = [];
 
-        /// <summary>
-        /// 调色板。如果该服装有除了默认颜色外的其他上色方案,可以在这里保存
-        /// </summary>
-        [JsonInclude] public List<Palette> Palettes { get; set; } = [];
-        
-        /// <summary>
-        /// 每个pose的数据
-        /// key: PoseID
-        /// value: AvatarPoses是个AvatarPosePart数组。 例如一个装备在站立状态下的pose图可能由多张图片组合而成
-        /// </summary>
-        [JsonInclude] public Dictionary<string, List<AvatarElement>> Elements { get; set; } = new();
+		/// <summary>
+		/// 调色板。如果该服装有除了默认颜色外的其他上色方案,可以在这里保存
+		/// </summary>
+		[JsonInclude] public List<Palette> Palettes { get; set; } = [];
+		
+		/// <summary>
+		/// 每个pose的数据
+		/// key: PoseID
+		/// value: AvatarPoses是个AvatarPosePart数组。 例如一个装备在站立状态下的pose图可能由多张图片组合而成
+		/// </summary>
+		[JsonInclude] public Dictionary<string, List<AvatarElement>> Elements { get; set; } = new();
 
-        public override bool Equals(object obj)
-        {
-            return obj is AvatarPart other && Equals(other);
-        }
+		public override bool Equals(object obj)
+		{
+			return obj is AvatarPart other && Equals(other);
+		}
 
-        public bool Equals(AvatarPart obj)
-        {
-            bool equal = true;
-            equal &= Name == obj.Name;
-            equal &= IconAssetId == obj.IconAssetId;
-            equal &= Introduction == obj.Introduction;
-            equal &= Palettes.Count == obj.Palettes.Count;
-            if (equal == false) return false;
-            for (int i = 0; i < Palettes.Count; ++i)
-            {
-                equal &= Palettes[i].Guid == obj.Palettes[i].Guid;
-                equal &= Palettes[i].Name == obj.Palettes[i].Name;
-                equal &= Palettes[i].Introduction == obj.Palettes[i].Introduction;
-                equal &= Palettes[i].IconPaletteParams.Count == obj.Palettes[i].IconPaletteParams.Count;
-                equal &= Palettes[i].ElementPaletteParams.Count == obj.Palettes[i].ElementPaletteParams.Count;
-                if (equal == false) return false;
-                
-                for (int j = 0; j < Palettes[i].IconPaletteParams.Count; ++j)
-                    equal &= Palettes[i].IconPaletteParams[j].Equals(obj.Palettes[i].IconPaletteParams[j]);
-                for (int j = 0; j < Palettes[i].ElementPaletteParams.Count; ++j)
-                    equal &= Palettes[i].ElementPaletteParams[j].Equals(obj.Palettes[i].ElementPaletteParams[j]);
-                
-                if (equal == false) return false;
-            }
+		public bool Equals(AvatarPart obj)
+		{
+			bool equal = true;
+			equal &= Name == obj.Name;
+			equal &= IconAssetId == obj.IconAssetId;
+			equal &= Introduction == obj.Introduction;
+			equal &= Palettes.Count == obj.Palettes.Count;
+			if (equal == false) return false;
+			for (int i = 0; i < Palettes.Count; ++i)
+			{
+				equal &= Palettes[i].Guid == obj.Palettes[i].Guid;
+				equal &= Palettes[i].Name == obj.Palettes[i].Name;
+				equal &= Palettes[i].Introduction == obj.Palettes[i].Introduction;
+				equal &= Palettes[i].IconPaletteParams.Count == obj.Palettes[i].IconPaletteParams.Count;
+				equal &= Palettes[i].ElementPaletteParams.Count == obj.Palettes[i].ElementPaletteParams.Count;
+				if (equal == false) return false;
+				
+				for (int j = 0; j < Palettes[i].IconPaletteParams.Count; ++j)
+					equal &= Palettes[i].IconPaletteParams[j].Equals(obj.Palettes[i].IconPaletteParams[j]);
+				for (int j = 0; j < Palettes[i].ElementPaletteParams.Count; ++j)
+					equal &= Palettes[i].ElementPaletteParams[j].Equals(obj.Palettes[i].ElementPaletteParams[j]);
+				
+				if (equal == false) return false;
+			}
 
-            equal &= Elements.Count == obj.Elements.Count;
-            foreach (var pair in Elements)
-            {
-                equal &= obj.Elements.ContainsKey(pair.Key);
-                equal &= pair.Value.Count == obj.Elements[pair.Key].Count;
-                if (equal == false) return false;
-                
-                for (int i = 0; i < pair.Value.Count; ++i)
-                    equal &= obj.Elements[pair.Key][i].Equals(pair.Value[i]);
-                if (equal == false) return false;
-            }
-            return equal;
-        }
-        
-        public override int GetHashCode() { return (int)Guid; }
-    }
+			equal &= Elements.Count == obj.Elements.Count;
+			foreach (var pair in Elements)
+			{
+				equal &= obj.Elements.ContainsKey(pair.Key);
+				equal &= pair.Value.Count == obj.Elements[pair.Key].Count;
+				if (equal == false) return false;
+				
+				for (int i = 0; i < pair.Value.Count; ++i)
+					equal &= obj.Elements[pair.Key][i].Equals(pair.Value[i]);
+				if (equal == false) return false;
+			}
+			return equal;
+		}
+		
+		public override int GetHashCode() { return (int)Guid; }
+	}
 
-    /// <summary>
-    /// 一张Asset图片在pose中的偏移和层级等数据
-    /// </summary>
-    [Serializable]
-    public struct AvatarElement
-    {
-        [JsonInclude] public int AssetId { get; set; }
-        [JsonInclude] public int Layer { get; set; }
-        [JsonInclude] public int LayerOffset { get; set; }
+	/// <summary>
+	/// 一张Asset图片在pose中的偏移和层级等数据
+	/// </summary>
+	[Serializable]
+	public struct AvatarElement
+	{
+		[JsonInclude] public int AssetId { get; set; }
+		[JsonInclude] public int Layer { get; set; }
+		[JsonInclude] public int LayerOffset { get; set; }
 
-        [JsonInclude] public float PositionX { get; set; }
-        [JsonInclude] public float PositionY { get; set; }
-        [JsonInclude] public float Rotation { get; set; }
-        [JsonInclude] public float ScaleX { get; set; }
-        [JsonInclude] public float ScaleY { get; set; }
-        [JsonInclude] public float Skew { get; set; }
+		[JsonInclude] public float PositionX { get; set; }
+		[JsonInclude] public float PositionY { get; set; }
+		[JsonInclude] public float Rotation { get; set; }
+		[JsonInclude] public float ScaleX { get; set; }
+		[JsonInclude] public float ScaleY { get; set; }
+		[JsonInclude] public float Skew { get; set; }
 
-    }
-    
-    [Serializable]
-    public class AvatarBody() : AvatarPart { }
-    
-    [Serializable]
-    public class AvatarHead(): AvatarPart { }
-    
-    [Serializable]
-    public class AvatarHair(): AvatarPart { }
-    
-    [Serializable]
-    public class AvatarFace() : AvatarPart { }
-    
-    [Serializable]
-    public struct ClothClasses : IEquatable<ClothClasses>
-    {
-        [JsonInclude] public int Hash { get; set; }
+	}
+	
+	[Serializable]
+	public class AvatarBody() : AvatarPart { }
+	
+	[Serializable]
+	public class AvatarHead(): AvatarPart { }
+	
+	[Serializable]
+	public class AvatarHair(): AvatarPart { }
+	
+	[Serializable]
+	public class AvatarFace() : AvatarPart { }
+	
+	[Serializable]
+	public struct ClothClasses : IEquatable<ClothClasses>
+	{
+		[JsonInclude] public int Hash { get; set; }
 		
-        public EClothClasses_Type1 Type1()
-        {
-            return (EClothClasses_Type1)((Hash >> 16) & 0xFFFF);
-        }
+		public EClothClasses_Type1 Type1()
+		{
+			return (EClothClasses_Type1)((Hash >> 16) & 0xFFFF);
+		}
 
-        public void SetType1(EClothClasses_Type1 value)
-        {
-            Hash = ((Hash & 0x0000FFFF) | (((short)value & 0xFFFF) << 16));
-        }
+		public void SetType1(EClothClasses_Type1 value)
+		{
+			Hash = ((Hash & 0x0000FFFF) | (((short)value & 0xFFFF) << 16));
+		}
 
-        public short Type2()
-        {
-            return (short)(Hash & 0xFFFF);
-        }
-        
-        public void SetType2(short value)
-        {
-            Hash = (int)((Hash & 0xFFFF0000u) | (uint)(value & 0xFFFF));
-        }
+		public short Type2()
+		{
+			return (short)(Hash & 0xFFFF);
+		}
+		
+		public void SetType2(short value)
+		{
+			Hash = (int)((Hash & 0xFFFF0000u) | (uint)(value & 0xFFFF));
+		}
 
-        public ClothClasses(int hash)
-        {
-            Hash = hash;
-        }
-        
-        public ClothClasses(EClothClasses_Type1 type1, short type2)
-        {
-            Hash = (((short)type1 & 0xFFFF) << 16) | (type2 & 0xFFFF);
-        }
+		public ClothClasses(int hash)
+		{
+			Hash = hash;
+		}
+		
+		public ClothClasses(EClothClasses_Type1 type1, short type2)
+		{
+			Hash = (((short)type1 & 0xFFFF) << 16) | (type2 & 0xFFFF);
+		}
 
-        public static implicit operator ClothClasses(int hash) => new ClothClasses(hash);
-    
-        public static implicit operator int(ClothClasses clothClasses) => clothClasses.Hash;
-        
-        public override bool Equals(object obj)
-        {
-            return obj is ClothClasses other && this.Hash == other.Hash;
-        }
-        public bool Equals(ClothClasses other)
-        {
-            return Hash == other.Hash;
-        }
-        public override int GetHashCode() => Hash;
+		public static implicit operator ClothClasses(int hash) => new ClothClasses(hash);
+	
+		public static implicit operator int(ClothClasses clothClasses) => clothClasses.Hash;
+		
+		public override bool Equals(object obj)
+		{
+			return obj is ClothClasses other && this.Hash == other.Hash;
+		}
+		public bool Equals(ClothClasses other)
+		{
+			return Hash == other.Hash;
+		}
+		public override int GetHashCode() => Hash;
 
-        public static bool operator ==(ClothClasses left, ClothClasses right)
-        {
-            return left.Equals(right);
-        }
+		public static bool operator ==(ClothClasses left, ClothClasses right)
+		{
+			return left.Equals(right);
+		}
 
-        public static bool operator !=(ClothClasses left, ClothClasses right)
-        {
-            return !(left == right);
-        }
-    }
-    
-    /// <summary>
-    /// 角色服装数据
-    /// </summary>
-    [Serializable]
-    public class AvatarCloth() : AvatarPart
-    {
-        /// <summary>
-        /// 服装分类
-        /// </summary>
-        [JsonInclude] public ClothClasses ClothClasses = new();
-        
-        /// <summary>
-        /// 占用的身体槽。例如连衣裙会同时占用上身和下身的槽位,使得不能再穿其他上衣。
-        /// key: BodySlot.Hash
-        /// value: cost
-        /// </summary>
-        [JsonInclude] public Dictionary<int, int> BodySlotCost { get; set; } = new();
-        
-        public override bool Equals(object obj) { return obj is AvatarCloth other && Equals(other); }
-        public bool Equals(AvatarCloth obj)
-        {
-            bool equal = base.Equals(obj);
-            equal &= ClothClasses.Equals(obj.ClothClasses);
-            equal &= BodySlotCost.Count == obj.BodySlotCost.Count;
-            if (equal == false) return false;
-            
-            equal &= BodySlotCost.Count == obj.BodySlotCost.Count;
-            foreach (var pair in BodySlotCost)
-            {
-                equal &= obj.BodySlotCost.ContainsKey(pair.Key);
-                if (equal == false) return false;
-                
-                equal &= obj.BodySlotCost[pair.Key] == pair.Value;
-                if (equal == false) return false;
-            }
-            return equal;
-        }
-        
-        public override int GetHashCode() { return (int)Guid; }
+		public static bool operator !=(ClothClasses left, ClothClasses right)
+		{
+			return !(left == right);
+		}
+	}
+	
+	/// <summary>
+	/// 角色服装数据
+	/// </summary>
+	[Serializable]
+	public class AvatarCloth() : AvatarPart
+	{
+		/// <summary>
+		/// 服装分类
+		/// </summary>
+		[JsonInclude] public ClothClasses ClothClasses = new();
+		
+		/// <summary>
+		/// 占用的身体槽。例如连衣裙会同时占用上身和下身的槽位,使得不能再穿其他上衣。
+		/// key: BodySlot.Hash
+		/// value: cost
+		/// </summary>
+		[JsonInclude] public Dictionary<int, int> BodySlotCost { get; set; } = new();
+		
+		public override bool Equals(object obj) { return obj is AvatarCloth other && Equals(other); }
+		public bool Equals(AvatarCloth obj)
+		{
+			bool equal = base.Equals(obj);
+			equal &= ClothClasses.Equals(obj.ClothClasses);
+			equal &= BodySlotCost.Count == obj.BodySlotCost.Count;
+			if (equal == false) return false;
+			
+			equal &= BodySlotCost.Count == obj.BodySlotCost.Count;
+			foreach (var pair in BodySlotCost)
+			{
+				equal &= obj.BodySlotCost.ContainsKey(pair.Key);
+				if (equal == false) return false;
+				
+				equal &= obj.BodySlotCost[pair.Key] == pair.Value;
+				if (equal == false) return false;
+			}
+			return equal;
+		}
+		
+		public override int GetHashCode() { return (int)Guid; }
 
-    }
-    
-    [Serializable]
-    public struct AvatarDollData()
-    {
-        [JsonInclude] public int FormatVersion { get; set; } = 0;
+	}
+	
+	[Serializable]
+	public struct AvatarDollData()
+	{
+		[JsonInclude] public int FormatVersion { get; set; } = 0;
 
-        [JsonInclude] public Metadata Metadata = new();
+		[JsonInclude] public Metadata Metadata = new();
 
-        [JsonInclude] public ProjectSettings Settings = new();
-        
-        /// <summary>
-        /// Asset资源映射表
-        /// 把AssetID对应到文件
-        /// </summary>
-        [JsonInclude] public Dictionary<int, string> AssetMap { get; set; } = new();
+		[JsonInclude] public ProjectSettings Settings = new();
+		
+		/// <summary>
+		/// Asset资源映射表
+		/// 把AssetID对应到文件
+		/// </summary>
+		[JsonInclude] public Dictionary<int, string> AssetMap { get; set; } = new();
 
-        [JsonInclude] public List<AvatarBody> Bodies { get; set; } = [];
-        [JsonInclude] public List<AvatarHead> Heads { get; set; } = [];
-        [JsonInclude] public List<AvatarHair> Hairs { get; set; } = [];
-        [JsonInclude] public List<AvatarFace> Faces { get; set; } = [];
-        [JsonInclude] public List<AvatarCloth> Clothes { get; set; } = [];
-        
-        public override bool Equals(object obj)
-        {
-            return obj is AvatarDollData other && Equals(other);
-        }
+		[JsonInclude] public List<AvatarBody> Bodies { get; set; } = [];
+		[JsonInclude] public List<AvatarHead> Heads { get; set; } = [];
+		[JsonInclude] public List<AvatarHair> Hairs { get; set; } = [];
+		[JsonInclude] public List<AvatarFace> Faces { get; set; } = [];
+		[JsonInclude] public List<AvatarCloth> Clothes { get; set; } = [];
+		
+		public override bool Equals(object obj)
+		{
+			return obj is AvatarDollData other && Equals(other);
+		}
 
-        public bool Equals(AvatarDollData data)
-        {
-            bool equal = true;
-            equal &= FormatVersion == data.FormatVersion;
-            equal &= Metadata.Equals(data.Metadata);
+		public bool Equals(AvatarDollData data)
+		{
+			bool equal = true;
+			equal &= FormatVersion == data.FormatVersion;
+			equal &= Metadata.Equals(data.Metadata);
 
-            equal &= AssetMap.Count == data.AssetMap.Count;
-            foreach (var pair in AssetMap)
-            {
-                equal &= data.AssetMap.ContainsKey(pair.Key);
-                equal &= pair.Value == data.AssetMap[pair.Key];
-            }
-            
-            equal &= Bodies.Count == data.Bodies.Count;
-            for (int i = 0; i < data.Bodies.Count; ++i)
-                equal &= Bodies[i].Equals(data.Bodies[i]);
+			equal &= AssetMap.Count == data.AssetMap.Count;
+			foreach (var pair in AssetMap)
+			{
+				equal &= data.AssetMap.ContainsKey(pair.Key);
+				equal &= pair.Value == data.AssetMap[pair.Key];
+			}
 			
-            equal &= Faces.Count == data.Faces.Count;
-            for (int i = 0; i < data.Faces.Count; ++i)
-                equal &= Faces[i].Equals(data.Faces[i]);
+			equal &= Bodies.Count == data.Bodies.Count;
+			for (int i = 0; i < data.Bodies.Count; ++i)
+				equal &= Bodies[i].Equals(data.Bodies[i]);
+			
+			equal &= Faces.Count == data.Faces.Count;
+			for (int i = 0; i < data.Faces.Count; ++i)
+				equal &= Faces[i].Equals(data.Faces[i]);
 
-            equal &= Hairs.Count == data.Hairs.Count;
-            for (int i = 0; i < data.Hairs.Count; ++i)
-                equal &= Hairs[i].Equals(data.Hairs[i]);
+			equal &= Hairs.Count == data.Hairs.Count;
+			for (int i = 0; i < data.Hairs.Count; ++i)
+				equal &= Hairs[i].Equals(data.Hairs[i]);
 			
-            equal &= Clothes.Count == data.Clothes.Count;
-            for (int i = 0; i < data.Clothes.Count; ++i)
-                equal &= Clothes[i].Equals(data.Clothes[i]);
-            return equal;
-        }
-        
-        
-    }
-    
+			equal &= Clothes.Count == data.Clothes.Count;
+			for (int i = 0; i < data.Clothes.Count; ++i)
+				equal &= Clothes[i].Equals(data.Clothes[i]);
+			return equal;
+		}
+		
+		
+	}
+	
 }
-

+ 1 - 0
GameProject/Scenes/panel/StateTreePanel.tscn

@@ -105,4 +105,5 @@ layout_mode = 2
 size_flags_vertical = 3
 theme = ExtResource("2_p1xr4")
 columns = 2
+allow_reselect = true
 select_mode = 1

+ 1 - 1
GameProject/Scenes/window/MainWorkWindow.tscn

@@ -138,7 +138,7 @@ theme = ExtResource("1_id3kd")
 [node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer/MarginContainer"]
 layout_mode = 2
 
-[node name="RichTextLabel" type="RichTextLabel" parent="VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer/MarginContainer/VBoxContainer"]
+[node name="PalettePropertyRichTextLabel" type="RichTextLabel" parent="VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer/MarginContainer/VBoxContainer"]
 custom_minimum_size = Vector2(120, 0)
 layout_mode = 2
 bbcode_enabled = true

+ 6 - 7
GameProject/Scenes/window/PaletteWindow.tscn

@@ -87,6 +87,7 @@ size_flags_vertical = 3
 theme = ExtResource("1_heawm")
 columns = 2
 column_titles_visible = true
+allow_reselect = true
 hide_root = true
 select_mode = 1
 
@@ -132,9 +133,9 @@ layout_mode = 2
 [node name="PaletteListview" type="Tree" parent="VBoxContainer/HSplitContainer/VSplitContainer/PanelContainer2/MarginContainer2/VBoxContainer"]
 layout_mode = 2
 size_flags_vertical = 3
-theme = ExtResource("1_heawm")
-columns = 2
+columns = 5
 column_titles_visible = true
+allow_reselect = true
 hide_root = true
 select_mode = 1
 
@@ -202,11 +203,11 @@ theme = ExtResource("1_heawm")
 [node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer2/PanelContainer/MarginContainer"]
 layout_mode = 2
 
-[node name="RichTextLabel" type="RichTextLabel" parent="VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer2/PanelContainer/MarginContainer/VBoxContainer"]
+[node name="PalettePropertyRichTextLabel" type="RichTextLabel" parent="VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer2/PanelContainer/MarginContainer/VBoxContainer"]
 custom_minimum_size = Vector2(120, 0)
 layout_mode = 2
 bbcode_enabled = true
-text = "[font_size=20][b]Palette Property[/b][/font_size]"
+text = "[font_size=20][b]Property [Default][/b][/font_size]"
 fit_content = true
 scroll_active = false
 
@@ -221,7 +222,7 @@ columns = 2
 [node name="Label" type="Label" parent="VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer2/PanelContainer/MarginContainer/VBoxContainer/GridContainer"]
 custom_minimum_size = Vector2(120, 0)
 layout_mode = 2
-text = "ID"
+text = "GUID"
 
 [node name="GuidLineEdit" type="LineEdit" parent="VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer2/PanelContainer/MarginContainer/VBoxContainer/GridContainer"]
 layout_mode = 2
@@ -237,7 +238,6 @@ text = "Name"
 layout_mode = 2
 size_flags_horizontal = 3
 theme = ExtResource("1_heawm")
-editable = false
 
 [node name="Label4" type="Label" parent="VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer2/PanelContainer/MarginContainer/VBoxContainer/GridContainer"]
 layout_mode = 2
@@ -248,7 +248,6 @@ text = "Intro"
 custom_minimum_size = Vector2(0, 120)
 layout_mode = 2
 theme = ExtResource("1_heawm")
-editable = false
 
 [node name="PanelContainer2" type="PanelContainer" parent="VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer2"]
 layout_mode = 2

+ 0 - 1
GameProject/Scripts/panel/canvas_2d.gd

@@ -345,7 +345,6 @@ func zoom_fit():
 	var visible_size = get_viewport().get_visible_rect().size
 	var sprite_size = root.texture.get_size()
 	var zoom = 0.8 * min(visible_size.x, visible_size.y) / max(sprite_size.x, sprite_size.y)
-	print(visible_size, sprite_size, zoom)
 	zoom_camera(zoom * 100)
 	camera.position = Vector2.ZERO
 	

+ 0 - 3
GameProject/Scripts/panel/state_treeview.gd

@@ -29,9 +29,6 @@ var item_collection = {}
 func _ready() -> void:
 	_init_gui()
 
-func _process(_delta: float) -> void:
-	pass
-
 func _init_gui():
 	icon_checked = load("res://Resources/UI/checked.png")
 	icon_warning = load("res://Resources/UI/warning.png")

+ 86 - 26
GameProject/Scripts/window/main_work_window.gd

@@ -22,10 +22,18 @@ extends PanelContainer
 @onready var popup_panel: PopupPanel = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer2/MarginContainer/VBoxContainer/GridContainer/HBoxContainer/LayerButton/PopupPanel
 @onready var layer_selecter: Panel = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer2/MarginContainer/VBoxContainer/GridContainer/HBoxContainer/LayerButton/PopupPanel/LayerSelecter
 
+@onready var palette_property_rich_text_label: RichTextLabel = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer/MarginContainer/VBoxContainer/PalettePropertyRichTextLabel
+@onready var guid_line_edit: LineEdit = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer/MarginContainer/VBoxContainer/GridContainer/GuidLineEdit
+@onready var name_line_edit: LineEdit = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer/MarginContainer/VBoxContainer/GridContainer/NameLineEdit
+@onready var intro_text_edit: TextEdit = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer/MarginContainer/VBoxContainer/GridContainer/IntroTextEdit
+
+
 var avatar_type: String = "Clothes"
 
 var _current_part = null
-var _current_state = null
+var _current_part_index = null
+var _current_state_code = null
+var _current_palette_index = -1
 var _current_elements = null
 var _current_element_index = -1
 var _current_element = null
@@ -60,10 +68,12 @@ func _init_gui():
 	layer_button.pressed.connect(_on_layer_button_pressed)
 	layer_selecter.pressed.connect(_on_layer_selecter_pressed)
 
+	name_line_edit.text_changed.connect(_on_name_line_edit_text_submitted)
+	intro_text_edit.text_changed.connect(_on_intro_text_edit_text_submitted)
+
 func _on_visibility_changed() -> void:
 	if is_visible_in_tree() == true:
-		#load_data()
-		update_data()
+		set_select(avatar_type, _current_part_index, _current_state_code, _current_element_index)
 		canvas_2d.sync_background()
 	else:
 		canvas_2d.cancle_free_transform()
@@ -96,21 +106,21 @@ func load_data():
 
 func update_data():
 	if _current_part != null:
-		var part_index = avatar_part_combo.selected
-		_current_state = state_panel.current_state_code 
-		_current_part = AvatarDollDataMgr.avatar_doll_data[avatar_type][part_index]
+		_current_part_index = avatar_part_combo.selected
+		_current_state_code = state_panel.current_state_code 
+		_current_part = AvatarDollDataMgr.avatar_doll_data[avatar_type][_current_part_index]
 		_current_element = null
 		_current_element_index = -1
 
-		state_panel.set_avatar_type(part_index, false)
-		if _current_state in _current_part["Elements"]:
-			_current_elements = _current_part["Elements"][_current_state]
+		state_panel.set_avatar_type(_current_part_index, false)
+		if _current_state_code in _current_part["Elements"]:
+			_current_elements = _current_part["Elements"][_current_state_code]
 			canvas_2d.set_editable(true)
 			canvas_2d.load_elements(_current_elements)
 			
-		elif _current_state != "":
+		elif _current_state_code != "":
 			_current_elements = []
-			_current_part["Elements"][_current_state] = _current_elements
+			_current_part["Elements"][_current_state_code] = _current_elements
 			canvas_2d.set_editable(true)
 			canvas_2d.load_elements(_current_elements)
 			
@@ -133,9 +143,34 @@ func update_data():
 		canvas_2d.set_editable(false)
 
 	update_property()
+	update_asset_property()
 	pass
 	
 func update_property():
+	if AvatarDollDataMgr.initialed == false or _current_part == null:
+		name_line_edit.editable = false
+		intro_text_edit.editable = false
+		guid_line_edit.text = ""
+		name_line_edit.text = ""
+		intro_text_edit.text = ""
+		palette_property_rich_text_label.text = "[font_size=20][b]Property[/b][/font_size]"
+		return
+		
+	name_line_edit.editable = true
+	intro_text_edit.editable = true
+	
+	if _current_palette_index < 0:
+		palette_property_rich_text_label.text = "[font_size=20][b]Property [Default][/b][/font_size]"
+		guid_line_edit.text = str(_current_palette_index)
+		name_line_edit.text = _current_part["Name"]
+		intro_text_edit.text = _current_part["Introduction"]
+	else:
+		palette_property_rich_text_label.text = "[font_size=20][b]Property [Palette %d][/b][/font_size]" % _current_palette_index
+		guid_line_edit.text = str(_current_palette_index)
+		name_line_edit.text = _current_part["Palettes"][_current_palette_index]["Name"]
+		intro_text_edit.text = _current_part["Palettes"][_current_palette_index]["Introduction"]
+	
+func update_asset_property():
 	if _current_element != null:
 		asset_line_edit.text = AvatarDollDataMgr.get_asset_name(_current_element["AssetId"])
 		layer_line_edit.text = str(_current_element["Layer"])
@@ -184,12 +219,13 @@ func set_select(new_type, part_index, state_code, element_index):
 	load_data()
 	
 	if part_index < AvatarDollDataMgr.avatar_doll_data[avatar_type].size():
+		_current_part_index = part_index
 		avatar_part_combo.selected = part_index
 		_current_part = AvatarDollDataMgr.avatar_doll_data[avatar_type][part_index]
 	
 	if state_code in _current_part["Elements"]:
 		state_panel.set_state_code(state_code)
-		_current_state = state_code
+		_current_state_code = state_code
 	
 	if element_index >= 0 and element_index < _current_part["Elements"][state_code].size():
 		_current_elements = _current_part["Elements"][state_code]
@@ -197,16 +233,18 @@ func set_select(new_type, part_index, state_code, element_index):
 		_current_element = _current_elements[element_index]
 		canvas_2d.load_elements(_current_elements, element_index)
 	
-	#update_property()
+	#update_asset_property()
 
 func set_palette(index):
-	var state_code = state_panel.current_state_code 
+	_current_palette_index = index
+	update_property()
+	
+	var state_code = state_panel.current_state_code
 	if state_code not in _current_part["Elements"]:
-		_current_state = null
+		_current_state_code = null
 		return
 
-	var palettes = AvatarDollDataMgr.get_render_palettes(avatar_type, avatar_part_combo.selected, state_code, index - 1, false)
-
+	var palettes = AvatarDollDataMgr.get_render_palettes(avatar_type, avatar_part_combo.selected, state_code, index, false)
 	for asset_name in palettes:
 		canvas_2d.update_palette(asset_name, palettes[asset_name])
 
@@ -217,16 +255,16 @@ func _on_avatar_type_combo_item_selected(index):
 
 func _on_avatar_part_combo_item_selected(index):
 	_current_part = AvatarDollDataMgr.avatar_doll_data[avatar_type][index]
-	
+	_current_palette_index = -1
 	update_data()
 	canvas_2d.zoom_fit()
 
 func _on_palette_combo_item_selected(index):
-	set_palette(index)
+	set_palette(index - 1)
 
 func _on_state_treeview_select_change(_avatar_type, _index, _state_code):
 	update_data()
-	set_palette(palette_combo.selected)
+	set_palette(_current_palette_index)
 	canvas_2d.zoom_fit()
 
 func _on_canvas_mouse_entered():
@@ -238,10 +276,10 @@ func _on_canvas_mouse_exited():
 func _on_canvas_element_selected(index: int):
 	_current_element_index = index
 	_current_element = _current_elements[index] if index != -1 else null
-	update_property()
+	update_asset_property()
 
 func _on_canvas_element_property_changed(_index: int, _position: Vector2, _rotation: float, _scale: Vector2, _skew: float):
-	if _current_part == null or _current_state == null:
+	if _current_part == null or _current_state_code == null:
 		return
 		
 	_current_element["PositionX"] = _position.x
@@ -250,7 +288,7 @@ func _on_canvas_element_property_changed(_index: int, _position: Vector2, _rotat
 	_current_element["ScaleX"] = _scale.x
 	_current_element["ScaleY"] = _scale.y
 	_current_element["Skew"] = _skew
-	update_property()
+	update_asset_property()
 
 func _on_canvas_element_action(index: int, action: String, data: Dictionary):
 	if action == "UP" and index > 0:
@@ -279,9 +317,31 @@ func _on_canvas_element_action(index: int, action: String, data: Dictionary):
 	
 func _on_property_changed(value, property: String):
 	_current_element[property] = value
-	update_property()
+	update_asset_property()
 	canvas_2d.update_element(_current_element_index, _current_element)
 
+func _on_name_line_edit_text_submitted(new_text):
+	if not AvatarDollDataMgr.initialed or _current_part == null:
+		return
+		
+	var new_name = name_line_edit.text
+	if _current_palette_index < 0:
+		_current_part["Name"] = new_name
+		avatar_part_combo.set_item_text(_current_part_index, new_name)
+		
+	else:
+		_current_part["Palettes"][_current_palette_index]["Name"] = new_name
+		palette_combo.set_item_text(_current_palette_index, new_name)
+	
+func _on_intro_text_edit_text_submitted():
+	if not AvatarDollDataMgr.initialed or _current_part == null:
+		return
+		
+	if _current_palette_index < 0:
+		_current_part["Introduction"] = intro_text_edit.text
+	else:
+		_current_part["Palettes"][_current_palette_index]["Introduction"] = intro_text_edit.text
+	
 func _on_layer_button_pressed():
 	var pos = layer_button.global_position + layer_button.get_rect().size
 	pos.x -= popup_panel.size.x
@@ -291,14 +351,14 @@ func _on_layer_button_pressed():
 func _on_layer_selecter_pressed(index):
 	popup_panel.hide()
 	_current_element["Layer"] = index
-	update_property()
+	update_asset_property()
 	
 func _on_files_dropped(files):
 	if not AvatarDollDataMgr.initialed:
 		return
 	
 	if mouse_over_control(sub_viewport_container):
-		if _current_state:
+		if _current_state_code:
 			for file_path in files:
 				var asset_id = AvatarDollDataMgr.load_asset(file_path)
 				if _current_elements != null:

+ 140 - 28
GameProject/Scripts/window/palette_window.gd

@@ -11,12 +11,16 @@ extends Control
 @onready var palette_param_add_button: Button = $VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer2/PanelContainer2/MarginContainer/VBoxContainer/HBoxContainer/Add
 @onready var canvas_2d: Node2D = $VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer/VBoxContainer/PanelContainer/SubViewportContainer/SubViewport/Canvas2d
 
+@onready var palette_property_rich_text_label: RichTextLabel = $VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer2/PanelContainer/MarginContainer/VBoxContainer/PalettePropertyRichTextLabel
 @onready var guid_line_edit: LineEdit = $VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer2/PanelContainer/MarginContainer/VBoxContainer/GridContainer/GuidLineEdit
 @onready var name_line_edit: LineEdit = $VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer2/PanelContainer/MarginContainer/VBoxContainer/GridContainer/NameLineEdit
 @onready var intro_text_edit: TextEdit = $VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer2/PanelContainer/MarginContainer/VBoxContainer/GridContainer/IntroTextEdit
 
-const PALETTE_PARAM_PANEL = preload("res://Scenes/control/PaletteParamPanel.tscn")
 const MAX_PALETTE_PARAM_PANEL = 8
+const PALETTE_PARAM_PANEL = preload("res://Scenes/control/PaletteParamPanel.tscn")
+const ICON_DELETE: Texture2D = preload("res://Resources/UI/Trash.png")
+const ICON_UP: Texture2D = preload("res://Resources/UI/Up.png")
+const ICON_DOWN: Texture2D = preload("res://Resources/UI/Down.png")
 
 var palette_param_panels = []
 var palette_param_panel_data = []
@@ -49,20 +53,26 @@ func _init_gui():
 	avatar_part_listview.hide_root = true
 	avatar_part_listview.set_column_title(0, "ID")
 	avatar_part_listview.set_column_title(1, "Name")
-	avatar_part_listview.set_column_custom_minimum_width(0, 40)
+	avatar_part_listview.set_column_custom_minimum_width(0, 80)
+	avatar_part_listview.set_column_expand(0, false)
 	avatar_part_listview.set_column_expand(1, true)
-	avatar_part_listview.set_column_expand_ratio(1, 3)
 	avatar_part_listview.item_selected.connect(_on_avatar_part_listview_item_selected)
 	
 	# palette
 	palette_add_button.pressed.connect(_on_palette_add_button_preessed)
-	palette_listview.columns = 2
+	palette_listview.columns = 5
 	palette_listview.hide_root = true
 	palette_listview.set_column_title(0, "ID")
 	palette_listview.set_column_title(1, "Name")
 	palette_listview.set_column_custom_minimum_width(0, 40)
+	palette_listview.set_column_custom_minimum_width(2, 16)
+	palette_listview.set_column_custom_minimum_width(3, 16)
+	palette_listview.set_column_custom_minimum_width(4, 16)
+	palette_listview.set_column_expand(0, false)
 	palette_listview.set_column_expand(1, true)
-	palette_listview.set_column_expand_ratio(1, 3)
+	palette_listview.set_column_expand(2, false)
+	palette_listview.set_column_expand(3, false)
+	palette_listview.set_column_expand(4, false)
 	palette_listview.item_selected.connect(_on_palette_listview_item_selected)
 
 	# palette_param
@@ -81,15 +91,15 @@ func _init_gui():
 		param_panel.action.connect(_on_palette_param_panel_action)
 		
 	# palette property
-	name_line_edit.text_submitted.connect(_on_name_line_edit_text_submitted)
-	intro_text_edit.text_submitted.connect(_on_intro_text_edit_text_submitted)
+	name_line_edit.text_changed.connect(_on_name_line_edit_text_submitted)
+	intro_text_edit.text_changed.connect(_on_intro_text_edit_text_submitted)
 	
 func load_data():
 	if AvatarDollDataMgr.initialed == false:
 		return
 	update_avatar_part_listview()
 
-func update_avatar_part_listview():
+func update_avatar_part_listview(selected_index=-1):
 	if AvatarDollDataMgr.initialed == false:
 		return
 	
@@ -110,6 +120,9 @@ func update_avatar_part_listview():
 		var row = avatar_part_listview.create_item()
 		row.set_text(0, "%d" % i)
 		row.set_text(1, part["Name"])
+		
+		if i == selected_index:
+			avatar_part_listview.set_selected(row, 0)
 	
 func update_state_selecter(select_code = null):
 	if AvatarDollDataMgr.initialed == false or _current_part == null:
@@ -127,50 +140,86 @@ func update_state_selecter(select_code = null):
 		_current_state_code = _current_part["Elements"].keys()[0]
 		state_selecter.set_state_list_select(_current_state_code)
 
-func update_palette_listview(selected_index = -1):
+func update_palette_listview(selected_index=-1, rebuild=true):
+	palette_listview.deselect_all()
+	
+	if rebuild:
+		palette_listview.clear()
+		palette_listview.create_item() # root
+		
 	if AvatarDollDataMgr.initialed == false or _current_part == null or _current_state_code == null:
 		_current_part_assets = {}
 		return
 	
 	# palette_listview
-	palette_listview.clear()
-	palette_listview.create_item() # root
+	palette_listview.set_block_signals(true)
 	
-	var row = palette_listview.create_item() # default
-	row.set_text(0, "-1")
-	row.set_text(1, "Default")
+	if rebuild:
+		var row = palette_listview.create_item() # default
+		row.set_text(0, "-1")
+		row.set_text(1, "Default")
 	
 	for i in _current_part["Palettes"].size():
 		var palette = _current_part["Palettes"][i]
-		row = palette_listview.create_item()
-		row.set_text(0, "%d" % i)
-		row.set_text(1, palette["Name"])
+		if rebuild:
+			var row = palette_listview.create_item()
+			row.set_cell_mode(2, TreeItem.CELL_MODE_ICON)
+			row.set_cell_mode(3, TreeItem.CELL_MODE_ICON)
+			row.set_cell_mode(4, TreeItem.CELL_MODE_ICON)
+			row.set_text(0, "%d" % i)
+			row.set_text(1, palette["Name"])
+			row.set_icon(2, ICON_UP if i > 0 else null)
+			row.set_icon(3, ICON_DOWN if i < _current_part["Palettes"].size() - 1 else null)
+			row.set_icon(4, ICON_DELETE)
+		else:
+			var row = palette_listview.get_root().get_child(i + 1)
+			row.set_text(0, "%d" % i)
+			row.set_text(1, palette["Name"])
+			row.set_icon(2, ICON_UP if i > 0 else null)
+			row.set_icon(3, ICON_DOWN if i < _current_part["Palettes"].size() - 1 else null)
 
 	# finish
-	if selected_index < _current_part["Palettes"].size():
+	if selected_index >= 0 and selected_index < _current_part["Palettes"].size():
 		_current_palette_index = selected_index
-		for item in palette_listview.get_root().get_children():
-			if selected_index == int(item.get_text(0)):
-				palette_listview.set_selected(item, 0)
+		var item = palette_listview.get_root().get_child(selected_index + 1)
+		palette_listview.set_selected(item, 0)
 	else:
 		_current_palette_index = -1
 		palette_listview.set_selected(palette_listview.get_root().get_child(0), 0)
+		
+	palette_listview.set_block_signals(false)
+	palette_listview.queue_redraw()
 	
 func update_palette_property():
 	if AvatarDollDataMgr.initialed == false or _current_part == null or _current_state_code == null:
+		name_line_edit.editable = false
+		intro_text_edit.editable = false
+		guid_line_edit.text = ""
+		name_line_edit.text = ""
+		intro_text_edit.text = ""
+		palette_property_rich_text_label.text = "[font_size=20][b]Property[/b][/font_size]"
 		return
 		
+	name_line_edit.editable = true
+	intro_text_edit.editable = true
+	
 	if _current_palette_index < 0:
+		palette_property_rich_text_label.text = "[font_size=20][b]Property [Default][/b][/font_size]"
 		guid_line_edit.text = str(_current_palette_index)
 		name_line_edit.text = _current_part["Name"]
 		intro_text_edit.text = _current_part["Introduction"]
 	else:
+		palette_property_rich_text_label.text = "[font_size=20][b]Property [Palette %d][/b][/font_size]" % _current_palette_index
 		guid_line_edit.text = str(_current_palette_index)
 		name_line_edit.text = _current_part["Palettes"][_current_palette_index]["Name"]
 		intro_text_edit.text = _current_part["Palettes"][_current_palette_index]["Introduction"]
 
 func update_palette_param():
 	if AvatarDollDataMgr.initialed == false or _current_part == null or _current_state_code == null:
+		_current_part_assets = {}
+		palette_param_add_button.disabled = true
+		for i in range(MAX_PALETTE_PARAM_PANEL):
+			palette_param_panels[i].visible = false
 		return
 		
 	# set enable
@@ -257,6 +306,7 @@ func update_canvas():
 
 func _on_visibility_changed() -> void:
 	if is_visible_in_tree() == true:
+		update_avatar_part_listview(_current_avatar_index)
 		update_state_selecter(_current_state_code)
 		update_palette_listview(_current_palette_index)
 		update_palette_property()
@@ -269,24 +319,71 @@ func _on_visibility_changed() -> void:
 func _on_avatar_type_combo_item_selected(_index: int) -> void:
 	_current_avatar_type = avatar_type_combo.get_item_text(avatar_type_combo.selected)
 	update_avatar_part_listview()
-	
-	canvas_2d.load_elements([])
-	canvas_2d.zoom_fit()
+	update_state_selecter()
+	update_palette_listview()
+	update_palette_property()
+	update_palette_param()
+	update_canvas()
 
 func _on_avatar_part_listview_item_selected():
 	_current_avatar_index = int(avatar_part_listview.get_selected().get_text(0))
 	_current_part = AvatarDollDataMgr.avatar_doll_data[_current_avatar_type][_current_avatar_index]
 	update_state_selecter(_current_state_code)
 	update_palette_listview()
+	update_palette_property()
+	update_palette_param()
 	update_canvas()
 	canvas_2d.zoom_fit()
 	
 func _on_palette_add_button_preessed():
-	pass
+	if _current_part == null:
+		return
+	
+	var palette_data = {
+		"Guid": 0,
+		"Name": "Palette %d" % _current_part["Palettes"].size(),
+		"Introduction": "",
+		"IconPaletteParam": [],
+		"ElementPaletteParam": []
+	}
+	_current_part["Palettes"].append(palette_data)
 	
+	update_palette_listview(_current_part["Palettes"].size() - 1)
+	update_palette_property()
+	update_palette_param()
+	update_canvas()
+
 func _on_palette_listview_item_selected():
 	var index = int(palette_listview.get_selected().get_text(0))
 	_current_palette_index = index
+	
+	var column = palette_listview.get_column_at_position(palette_listview.get_local_mouse_position())
+	if _current_palette_index >= 0:
+		if column == 2 and _current_palette_index > 0:
+			var temp = _current_part["Palettes"][_current_palette_index - 1]
+			_current_part["Palettes"][_current_palette_index - 1] = _current_part["Palettes"][_current_palette_index]
+			_current_part["Palettes"][_current_palette_index] = temp
+			update_palette_listview(_current_palette_index - 1, false)
+			
+		elif column == 3 and _current_palette_index >= 0 and _current_palette_index < _current_part["Palettes"].size() - 1:
+			var temp = _current_part["Palettes"][_current_palette_index + 1]
+			_current_part["Palettes"][_current_palette_index + 1] = _current_part["Palettes"][_current_palette_index]
+			_current_part["Palettes"][_current_palette_index] = temp
+			update_palette_listview(_current_palette_index + 1, false)
+			
+		elif column == 4:
+			var msg = "确定要删除 Palette %d (%s) 吗?" % [_current_palette_index, _current_part["Palettes"][_current_palette_index]["Name"]]
+			var msg_box = MessageBox.question(self, 
+				"提示", msg, 
+				MessageBox.StandardButton.YES | MessageBox.StandardButton.NO)
+			msg_box.accepted.connect(func(): 
+				_current_part["Palettes"].remove_at(_current_palette_index)
+				_current_palette_index = -1
+				update_palette_listview()
+				update_palette_property()
+				update_palette_param()
+				update_canvas()
+			)
 	update_palette_property()
 	update_palette_param()
 	update_canvas()
@@ -365,8 +462,23 @@ func _on_state_selected(state_code: String):
 	update_canvas()
 
 func _on_name_line_edit_text_submitted(new_text):
-	pass
+	if not AvatarDollDataMgr.initialed or _current_part == null:
+		return
+		
+	var new_name = name_line_edit.text
+	if _current_palette_index < 0:
+		_current_part["Name"] = new_name
+		avatar_part_listview.get_root().get_child(_current_avatar_index).set_text(1, new_name)
+	else:
+		_current_part["Palettes"][_current_palette_index]["Name"] = new_name
+		palette_listview.get_root().get_child(_current_palette_index + 1).set_text(1, new_name)
 	
-func _on_intro_text_edit_text_submitted(new_text):
-	pass
+func _on_intro_text_edit_text_submitted():
+	if not AvatarDollDataMgr.initialed or _current_part == null:
+		return
+		
+	if _current_palette_index < 0:
+		_current_part["Introduction"] = intro_text_edit.text
+	else:
+		_current_part["Palettes"][_current_palette_index]["Introduction"] = intro_text_edit.text