Getting Started

Install

Add the package to your mod project:

xml
<PackageReference Include="STS2.RitsuLib" />

Declare the runtime dependency in mod_manifest.json. For game API 0.105.x and newer, use the object form:

json
{
  "dependencies": [
    { "id": "STS2-RitsuLib" }
  ]
}

For older game API branches, use the legacy string form because older manifest parsers may fail on dependency objects:

json
{
  "dependencies": [
    "STS2-RitsuLib"
  ]
}

If you do not use Central Package Management, choose the current package version through your package manager or IDE. Use STS2.RitsuLib.Compat.<api-version> only when the mod targets an older Slay the Spire 2 API branch.

安装

在 Mod 项目里引用包:

xml
<PackageReference Include="STS2.RitsuLib" />

mod_manifest.json 声明运行时依赖。游戏 API 0.105.x 及之后使用对象写法:

json
{
  "dependencies": [
    { "id": "STS2-RitsuLib" }
  ]
}

旧游戏 API 分支使用旧的字符串写法;旧版 manifest 解析器可能无法解析 dependency 对象,甚至直接报错:

json
{
  "dependencies": [
    "STS2-RitsuLib"
  ]
}

如果项目没有使用 Central Package Management,请通过包管理器或 IDE 选择当前包版本。只有在 Mod 面向旧版《杀戮尖塔 2》API 分支时,才使用 STS2.RitsuLib.Compat.<api-version>

Initialize

Register your assembly for RitsuLib discovery from the mod initializer. This is required for CLR attribute registration.

csharp
using System.Reflection;
using MegaCrit.Sts2.Core.Logging;
using MegaCrit.Sts2.Core.Modding;
using STS2RitsuLib;
using STS2RitsuLib.Interop;
using STS2RitsuLib.Patching.Core;

[ModInitializer(nameof(Initialize))]
public static class MyModEntry
{
    public const string ModId = "MyMod";
    public static Logger Logger { get; private set; } = null!;

    public static void Initialize()
    {
        var assembly = Assembly.GetExecutingAssembly();

        Logger = RitsuLibFramework.CreateLogger(ModId);
        ModTypeDiscoveryHub.RegisterModAssembly(ModId, assembly);
        RitsuLibFramework.EnsureGodotScriptsRegistered(assembly, Logger);

        var patcher = RitsuLibFramework.CreatePatcher(ModId, "main");
        patcher.RegisterPatches<MyModPatches>();
        RitsuLibFramework.ApplyRequiredPatcher(patcher, DisableMod);
    }

    private static void DisableMod()
    {
        // Mark your own mod disabled when a required patch cannot apply.
    }
}

Call EnsureGodotScriptsRegistered(...) only when your mod contains C# scripts attached to .tscn scenes. Pure model or patch mods can omit it.

初始化

在 Mod 初始化入口注册当前程序集,RitsuLib 才能发现 CLR 注解注册。

csharp
using System.Reflection;
using MegaCrit.Sts2.Core.Logging;
using MegaCrit.Sts2.Core.Modding;
using STS2RitsuLib;
using STS2RitsuLib.Interop;
using STS2RitsuLib.Patching.Core;

[ModInitializer(nameof(Initialize))]
public static class MyModEntry
{
    public const string ModId = "MyMod";
    public static Logger Logger { get; private set; } = null!;

    public static void Initialize()
    {
        var assembly = Assembly.GetExecutingAssembly();

        Logger = RitsuLibFramework.CreateLogger(ModId);
        ModTypeDiscoveryHub.RegisterModAssembly(ModId, assembly);
        RitsuLibFramework.EnsureGodotScriptsRegistered(assembly, Logger);

        var patcher = RitsuLibFramework.CreatePatcher(ModId, "main");
        patcher.RegisterPatches<MyModPatches>();
        RitsuLibFramework.ApplyRequiredPatcher(patcher, DisableMod);
    }

    private static void DisableMod()
    {
        // 必要补丁无法应用时,在这里关闭你自己的 Mod。
    }
}

只有 Mod 里有挂在 .tscn 场景上的 C# 脚本时,才需要 EnsureGodotScriptsRegistered(...)。纯模型或纯补丁 Mod 可以省略。

Register Content

For most content, put registration attributes on the model class. The registration point stays next to the class that is being registered.

csharp
using Godot;
using MegaCrit.Sts2.Core.Entities.Cards;
using MegaCrit.Sts2.Core.Models;
using STS2RitsuLib.Interop.AutoRegistration;
using STS2RitsuLib.Scaffolding.Content;

public sealed class MyCardPool : TypeListCardPoolModel
{
    public override string Title => "My Cards";
    public override string EnergyColorName => "orange";
    public override string CardFrameMaterialPath => "card_frame_orange";
    public override Color DeckEntryCardColor => new("d58b2f");
    public override bool IsColorless => false;
}

[RegisterCard(typeof(MyCardPool))]
public sealed class MyStrike
    : ModCardTemplate(1, CardType.Attack, CardRarity.Common, TargetType.SingleEnemy)
{
    public override void Use(ICombatContext ctx, ICreatureState user, ICreatureState? target)
    {
        ctx.DealDamage(user, target, Damage);
    }
}

Write the display text in the matching localization table:

json
{
  "MY_MOD_CARD_MY_STRIKE.title": "Measured Strike",
  "MY_MOD_CARD_MY_STRIKE.description": "Deal {Damage} damage."
}

Use a content pack instead when the registration is easier to read as a batch:

csharp
RitsuLibFramework.CreateContentPack("MyMod")
    .Card<MyCardPool, MyStrike>()
    .Relic<MyRelicPool, MyStarterRelic>()
    .Apply();

注册内容

大多数内容可以把注册注解写在模型类上。注册点会贴近真正的内容定义,更容易维护。

csharp
using Godot;
using MegaCrit.Sts2.Core.Entities.Cards;
using MegaCrit.Sts2.Core.Models;
using STS2RitsuLib.Interop.AutoRegistration;
using STS2RitsuLib.Scaffolding.Content;

public sealed class MyCardPool : TypeListCardPoolModel
{
    public override string Title => "My Cards";
    public override string EnergyColorName => "orange";
    public override string CardFrameMaterialPath => "card_frame_orange";
    public override Color DeckEntryCardColor => new("d58b2f");
    public override bool IsColorless => false;
}

[RegisterCard(typeof(MyCardPool))]
public sealed class MyStrike
    : ModCardTemplate(1, CardType.Attack, CardRarity.Common, TargetType.SingleEnemy)
{
    public override void Use(ICombatContext ctx, ICreatureState user, ICreatureState? target)
    {
        ctx.DealDamage(user, target, Damage);
    }
}

显示文本写在对应本地化表里:

json
{
  "MY_MOD_CARD_MY_STRIKE.title": "精准打击",
  "MY_MOD_CARD_MY_STRIKE.description": "造成 {Damage} 点伤害。"
}

如果一批注册集中写更容易审查,也可以使用 content pack:

csharp
RitsuLibFramework.CreateContentPack("MyMod")
    .Card<MyCardPool, MyStrike>()
    .Relic<MyRelicPool, MyStarterRelic>()
    .Apply();

Next Steps

继续阅读