Skip to content

角色与解锁模板

本文是角色 Mod 的实践搭建指南:角色模板、内容池定义、纪元模板与解锁注册,并附完整示例。

概览

一个完整的角色 Mod 通常包含以下部分:

内容基类示例
卡池TypeListCardPoolModelMyCardPool
遗物池TypeListRelicPoolModelMyRelicPool
药水池TypeListPotionPoolModelMyPotionPool
角色ModCharacterTemplate<TCard, TRelic, TPotion>MyCharacter
故事ModStoryTemplateMyStory
纪元CharacterUnlockEpochTemplate<T> 或自定义MyEpoch2

内容池定义

  • 卡池TypeListCardPoolModel 的池成员在 CreateContentPack / Manifest 中通过 .Card<卡池, 卡牌>() / CardRegistrationEntry 登记;基类已提供默认空的 CardTypes[Obsolete]),无需覆写
  • 遗物池 / 药水池:现在与卡池保持一致,TypeListRelicPoolModel / TypeListPotionPoolModelRelicTypes / PotionTypes 已提供默认空实现并标记为 [Obsolete]。新 Mod 请通过 CreateContentPack / Manifest 的 .Relic<池, 遗物>().Potion<池, 药水>()RelicRegistrationEntryPotionRegistrationEntry 注册内容。
csharp
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

csharp
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

csharp
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<,>() 绑定。

csharp
public class MyStory : ModStoryTemplate
{
    protected override string StoryKey => "my-character";
}

Ancient 对话本地化

RitsuLib 会在游戏原版 AncientDialogueSet.PopulateLocKeys 之前,自动为已注册的 Mod 角色追加基于本地化定义的 Ancient 对话。

键格式与原版保持一致:

键组件说明
<ancientEntry>.talk.<characterEntry>.<dialogueIndex>-<lineIndex>.ancientAncient 台词
<ancientEntry>.talk.<characterEntry>.<dialogueIndex>-<lineIndex>.char角色台词
可选后缀 .sfx音效
可选后缀 -visit访问覆盖
可选后缀 -attackArchitect 专用攻击者覆盖

纪元模板

RitsuLib 提供预置的纪元模板,用于常见解锁目标:

模板说明
CharacterUnlockEpochTemplate<TCharacter>解锁角色本身的纪元
CardUnlockEpochTemplate解锁额外卡牌的纪元
RelicUnlockEpochTemplate解锁额外遗物的纪元
PotionUnlockEpochTemplate解锁额外药水的纪元
csharp
public class MyCharacterEpoch : CharacterUnlockEpochTemplate<MyCharacter>
{
}

public class MyEpoch2 : CardUnlockEpochTemplate
{
    protected override IEnumerable<Type> CardTypes =>
    [
        typeof(MyAdvancedCard),
    ];
}

完整注册示例

csharp
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();