动态表情增加

This commit is contained in:
Tourswen
2025-10-22 23:06:35 +08:00
parent 88000aa1ca
commit baf1d51dae
28 changed files with 2063 additions and 10 deletions

View File

@@ -7,15 +7,15 @@
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_genestealer\\compabilityeffect_injectgenes.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\abilities\\ara_genestealer\\compabilityeffect_extractgene.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\abilities\\ara_genestealer\\compabilityeffect_extractgene.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_genestealer\\compabilityeffect_extractgene.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\abilities\\ara_genestealer\\compproperties_abilityinjectgenes.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\abilities\\ara_genestealer\\compproperties_abilityinjectgenes.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_genestealer\\compproperties_abilityinjectgenes.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\verbs\\cleave\\verb_meleeattack_cleave.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\verbs\\cleave\\verb_meleeattack_cleave.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:verbs\\cleave\\verb_meleeattack_cleave.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
@@ -126,8 +126,7 @@
"RelativeToolTip": "Abilities\\ARA_Genestealer\\CompProperties_AbilityInjectGenes.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-21T16:09:51.937Z",
"EditorCaption": ""
"WhenOpened": "2025-10-21T16:09:51.937Z"
},
{
"$type": "Document",
@@ -139,8 +138,7 @@
"RelativeToolTip": "Abilities\\ARA_Genestealer\\CompAbilityEffect_ExtractGene.cs",
"ViewState": "AgIAADUAAAAAAAAAAAAewFwAAABVAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-21T16:09:29.065Z",
"EditorCaption": ""
"WhenOpened": "2025-10-21T16:09:29.065Z"
},
{
"$type": "Document",
@@ -150,7 +148,7 @@
"RelativeDocumentMoniker": "Abilities\\ARA_Genestealer\\CompAbilityEffect_InjectGenes.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_Genestealer\\CompAbilityEffect_InjectGenes.cs",
"RelativeToolTip": "Abilities\\ARA_Genestealer\\CompAbilityEffect_InjectGenes.cs",
"ViewState": "AgIAAC0AAAAAAAAAAAAtwD4AAAAQAAAAAAAAAA==",
"ViewState": "AgIAAC0AAAAAAAAAAAAtwF0AAAAMAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-21T16:08:53.903Z",
"EditorCaption": ""
@@ -165,8 +163,7 @@
"RelativeToolTip": "Verbs\\Cleave\\Verb_MeleeAttack_Cleave.cs",
"ViewState": "AgIAAG8AAAAAAAAAAAAhwI8AAABCAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-21T12:52:47.558Z",
"EditorCaption": ""
"WhenOpened": "2025-10-21T12:52:47.558Z"
},
{
"$type": "Document",

View File

@@ -42,6 +42,9 @@
<HintPath>..\..\..\..\..\..\common\RimWorld\RimWorldWin64_Data\Managed\Assembly-CSharp.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="FacialAnimation">
<HintPath>..\..\..\..\..\..\workshop\content\294100\1635901197\1.6\Assemblies\FacialAnimation.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
@@ -317,6 +320,9 @@
<ItemGroup>
<Compile Include="Utils\BezierUtil.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="FacialAnimation\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 自定义清理任务删除obj文件夹中的临时文件 -->
<Target Name="CleanDebugFiles" AfterTargets="Build">

View File

@@ -0,0 +1,237 @@
using RimWorld;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using Verse;
namespace ArachnaeSwarm
{
[StaticConstructorOnStartup]
public static class FaceAnimationFileGenerator
{
private static readonly string OutputDirectory = "1.6/1.6/GeneratedDefs/FacialAnimation/";
static FaceAnimationFileGenerator()
{
GenerateAllFaceAnimationFiles();
}
private static void GenerateAllFaceAnimationFiles()
{
try
{
// 获取模组根目录
string modRootDir = GetModRootDirectory();
if (string.IsNullOrEmpty(modRootDir))
{
Log.Error("Cannot find mod root directory");
return;
}
// 确保输出目录存在
string fullOutputDir = Path.Combine(modRootDir, OutputDirectory);
Directory.CreateDirectory(fullOutputDir);
// 配置:种族和对应的抽象动画路径
var raceConfigs = new Dictionary<string, List<string>>
{
{
"ArachnaeNode_Race_Myrmecocystus",
new List<string> { "1.6/1.6/Defs/AnimationDefs/FacialAnimation/ARA_JobAnim.xml" }
},
{
"ArachnaeNode_Race_ShieldHead",
new List<string> { "1.6/1.6/Defs/AnimationDefs/FacialAnimation/ARA_JobAnim.xml" }
},
{
"ArachnaeNode_Race_WeaponSmith",
new List<string> { "1.6/1.6/Defs/AnimationDefs/FacialAnimation/ARA_JobAnim.xml" }
},
{
"ArachnaeNode_Race_Fighter",
new List<string> { "1.6/1.6/Defs/AnimationDefs/FacialAnimation/ARA_JobAnim.xml" }
},
{
"ArachnaeNode_Race_Facehugger",
new List<string> { "1.6/1.6/Defs/AnimationDefs/FacialAnimation/ARA_JobAnim.xml" }
},
{
"ArachnaeNode_Race_Smokepop",
new List<string> { "1.6/1.6/Defs/AnimationDefs/FacialAnimation/ARA_JobAnim.xml" }
},
{
"ArachnaeNode_Race_NeuroSwarm",
new List<string> { "1.6/1.6/Defs/AnimationDefs/FacialAnimation/ARA_JobAnim.xml" }
},
{
"ArachnaeNode_Race_Praetorian",
new List<string> { "1.6/1.6/Defs/AnimationDefs/FacialAnimation/ARA_JobAnim.xml" }
},
{
"ArachnaeNode_Race_Skyraider",
new List<string> { "1.6/1.6/Defs/AnimationDefs/FacialAnimation/ARA_JobAnim.xml" }
}
};
int totalFilesGenerated = 0;
foreach (var config in raceConfigs)
{
string raceDefName = config.Key;
List<string> paths = config.Value;
// 检查种族是否存在
if (DefDatabase<ThingDef>.GetNamedSilentFail(raceDefName) == null)
{
Log.Warning($"Race {raceDefName} not found, skipping face animation generation");
continue;
}
foreach (string relativePath in paths)
{
string fullSourcePath = Path.Combine(modRootDir, relativePath);
totalFilesGenerated += GenerateAnimationFilesForRace(fullSourcePath, raceDefName, fullOutputDir);
}
}
Log.Message($"Generated {totalFilesGenerated} face animation files in {fullOutputDir}");
}
catch (Exception ex)
{
Log.Error($"Error in FaceAnimationFileGenerator: {ex}");
}
}
/// <summary>
/// 获取模组根目录
/// </summary>
private static string GetModRootDirectory()
{
try
{
// 通过查找当前程序集所在的模组来获取根目录
var currentMod = LoadedModManager.RunningMods.FirstOrDefault(mod =>
mod.assemblies.loadedAssemblies.Contains(typeof(FaceAnimationFileGenerator).Assembly));
if (currentMod != null)
{
return currentMod.RootDir;
}
// 备选方法:通过反射获取
var assembly = typeof(FaceAnimationFileGenerator).Assembly;
string assemblyLocation = assembly.Location;
if (!string.IsNullOrEmpty(assemblyLocation))
{
// 向上查找直到找到模组根目录
DirectoryInfo dir = new FileInfo(assemblyLocation).Directory;
while (dir != null)
{
if (File.Exists(Path.Combine(dir.FullName, "About.xml")) ||
File.Exists(Path.Combine(dir.FullName, "LoadFolders.xml")))
{
return dir.FullName;
}
dir = dir.Parent;
}
}
Log.Error("Cannot determine mod root directory");
return null;
}
catch (Exception ex)
{
Log.Error($"Error getting mod root directory: {ex}");
return null;
}
}
private static int GenerateAnimationFilesForRace(string sourcePath, string raceDefName, string outputDir)
{
int filesGenerated = 0;
try
{
// 检查源文件是否存在
if (!File.Exists(sourcePath))
{
Log.Warning($"Source animation file not found: {sourcePath}");
return 0;
}
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(sourcePath);
XmlNodeList abstractDefNodes = xmlDoc.SelectNodes("//FacialAnimation.FaceAnimationDef[@Abstract='True']");
if (abstractDefNodes == null || abstractDefNodes.Count == 0)
{
Log.Message($"No abstract FaceAnimationDef found in: {sourcePath}");
return 0;
}
// 为每个种族创建单独的文件
string outputFile = Path.Combine(outputDir, $"ARA_{raceDefName}_JobAnim.xml");
using (StreamWriter writer = new StreamWriter(outputFile, false, Encoding.UTF8))
{
writer.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
writer.WriteLine("<Defs>");
foreach (XmlNode abstractNode in abstractDefNodes)
{
string concreteXml = GenerateConcreteAnimationXml(abstractNode, raceDefName);
if (!string.IsNullOrEmpty(concreteXml))
{
writer.WriteLine(concreteXml);
filesGenerated++;
}
}
writer.WriteLine("</Defs>");
}
Log.Message($"Generated {filesGenerated} animations for {raceDefName} in {outputFile}");
}
catch (Exception ex)
{
Log.Error($"Error generating animation files for race {raceDefName}: {ex}");
}
return filesGenerated;
}
private static string GenerateConcreteAnimationXml(XmlNode abstractNode, string raceDefName)
{
try
{
string abstractName = abstractNode.Attributes?["Name"]?.Value;
if (string.IsNullOrEmpty(abstractName))
{
Log.Warning("Abstract FaceAnimationDef has no Name attribute");
return null;
}
string concreteDefName = $"{raceDefName}_{abstractName.Replace("ARA_", "")}";
// 创建XML字符串
StringBuilder xmlBuilder = new StringBuilder();
xmlBuilder.AppendLine($" <FacialAnimation.FaceAnimationDef ParentName=\"{abstractName}\" MayRequire=\"Nals.FacialAnimation\">");
xmlBuilder.AppendLine($" <defName>{concreteDefName}</defName>");
xmlBuilder.AppendLine($" <raceName>{raceDefName}</raceName>");
xmlBuilder.Append(" </FacialAnimation.FaceAnimationDef>");
return xmlBuilder.ToString();
}
catch (Exception ex)
{
Log.Error($"Error generating XML for {raceDefName}: {ex}");
return null;
}
}
}
}