角色与解锁模板
本文是角色 Mod 的实践搭建指南:角色模板、内容池定义、纪元模板与解锁注册,并附完整示例。
概览
一个完整的角色 Mod 通常包含以下部分:
| 内容 | 基类 | 示例 |
|---|---|---|
| 卡池 | TypeListCardPoolModel | MyCardPool |
| 遗物池 | TypeListRelicPoolModel | MyRelicPool |
| 药水池 | TypeListPotionPoolModel | MyPotionPool |
| 角色 | ModCharacterTemplate<TCard, TRelic, TPotion> | MyCharacter |
| 故事 | ModStoryTemplate | MyStory |
| 纪元 | CharacterUnlockEpochTemplate<T> 或自定义 | MyEpoch2 |
内容池定义
- 卡池:
TypeListCardPoolModel的池成员在CreateContentPack/ Manifest 中通过.Card<卡池, 卡牌>()/CardRegistrationEntry登记;基类已提供默认空的CardTypes([Obsolete]),无需覆写。 - 遗物池 / 药水池:现在与卡池保持一致,
TypeListRelicPoolModel/TypeListPotionPoolModel的RelicTypes/PotionTypes已提供默认空实现并标记为[Obsolete]。新 Mod 请通过CreateContentPack/ Manifest 的.Relic<池, 遗物>()、.Potion<池, 药水>()、RelicRegistrationEntry、PotionRegistrationEntry注册内容。
using Godot;
public class MyCardPool : TypeListCardPoolModel
{
public override string Title => "My Pool";
public override string EnergyColorName => "orange";
public override string CardFrameMaterialPath => "card_frame_orange";
public override Color DeckEntryCardColor => new("d2a15a");
public override bool IsColorless => false;
}
public class MyRelicPool : TypeListRelicPoolModel
{
}
public class MyPotionPool : TypeListPotionPoolModel
{
}配置卡牌边框颜色(HSV)
TypeListCardPoolModel 支持直接覆盖 PoolFrameMaterial。当该属性返回非空材质时,会优先使用这个材质渲染卡牌边框,不再依赖 CardFrameMaterialPath。
using Godot;
using STS2RitsuLib.Utils;
public class MyCardPool : TypeListCardPoolModel
{
public override Material? PoolFrameMaterial =>
MaterialUtils.CreateHsvShaderMaterial(0.55f, 0.45f, 0.95f);
}角色模板
继承 ModCharacterTemplate<TCardPool, TRelicPool, TPotionPool> 负责角色本身,然后把 starter 内容放到内容注册阶段做追加式登记。
未填写的角色资源会自动回退到 PlaceholderCharacterId,默认值为 ironclad。
public class MyCharacter : ModCharacterTemplate<MyCardPool, MyRelicPool, MyPotionPool>
{
public override string? PlaceholderCharacterId => "ironclad";
// 资源路径(使用 AssetProfile 统一配置)
public override CharacterAssetProfile AssetProfile => new(
Spine: new(
CombatSkeletonDataPath: "res://MyMod/spine/my_character.tres"),
Ui: new(
IconTexturePath: "res://MyMod/art/icon.png",
CharacterSelectBgPath: "res://MyMod/art/select_bg.tscn"),
Scenes: new(
RestSiteAnimPath: "res://MyMod/scenes/rest_site/my_character_rest_site.tscn"));
}
var character = new CharacterRegistrationEntry<MyCharacter>()
.AddStartingCard<MyStrike>(4)
.AddStartingCard<MyDefend>(4)
.AddStartingCard<MySpecialStarter>()
.AddStartingRelic<MyStarterRelic>();别的 mod 之后也可以继续给这个角色追加内容:可以用 CharacterStarterCardRegistrationEntry<MyCharacter, OtherCard>(count),也可以直接调用 ModContentRegistry.RegisterCharacterStarterCard<MyCharacter, OtherCard>(count)。
选人界面解锁说明({Prerequisite})
本地化 unlockText 可使用 {Prerequisite} 占位符。在 ModCharacterTemplate 上通过 UnlocksAfterRunAsType 指定前置角色的 CLR 类型。
故事模板
继承 ModStoryTemplate 提供故事标识(StoryKey → slug)。纪元顺序在注册阶段用 RegisterStoryEpoch / TimelineColumnPackEntry / .StoryEpoch<,>() 绑定。
public class MyStory : ModStoryTemplate
{
protected override string StoryKey => "my-character";
}Ancient 对话本地化
RitsuLib 会在游戏原版 AncientDialogueSet.PopulateLocKeys 之前,自动为已注册的 Mod 角色追加基于本地化定义的 Ancient 对话。
键格式与原版保持一致:
| 键组件 | 说明 |
|---|---|
<ancientEntry>.talk.<characterEntry>.<dialogueIndex>-<lineIndex>.ancient | Ancient 台词 |
<ancientEntry>.talk.<characterEntry>.<dialogueIndex>-<lineIndex>.char | 角色台词 |
可选后缀 .sfx | 音效 |
可选后缀 -visit | 访问覆盖 |
可选后缀 -attack | Architect 专用攻击者覆盖 |
纪元模板
RitsuLib 提供预置的纪元模板,用于常见解锁目标:
| 模板 | 说明 |
|---|---|
CharacterUnlockEpochTemplate<TCharacter> | 解锁角色本身的纪元 |
CardUnlockEpochTemplate | 解锁额外卡牌的纪元 |
RelicUnlockEpochTemplate | 解锁额外遗物的纪元 |
PotionUnlockEpochTemplate | 解锁额外药水的纪元 |
public class MyCharacterEpoch : CharacterUnlockEpochTemplate<MyCharacter>
{
}
public class MyEpoch2 : CardUnlockEpochTemplate
{
protected override IEnumerable<Type> CardTypes =>
[
typeof(MyAdvancedCard),
];
}完整注册示例
RitsuLibFramework.CreateContentPack("MyMod")
// 卡牌(指定所属池)
.Card<MyCardPool, MyStrike>()
.Card<MyCardPool, MyDefend>()
.Card<MyCardPool, MySignatureCard>()
.Card<MyCardPool, MyAdvancedCard>()
// 遗物
.Relic<MyRelicPool, MyStarterRelic>()
// 角色
.Character<MyCharacter>()
// 故事与纪元
.Story<MyStory>()
.Epoch<MyCharacterEpoch>()
.Epoch<MyEpoch2>()
// 解锁规则
.RequireEpoch<MyAdvancedCard, MyEpoch2>() // 纪元 2 才显示该卡
.UnlockEpochAfterRunAs<MyCharacter, MyEpoch2>() // 完成一局后解锁纪元 2
.Apply();