Slay the Spire MODの作り方(メモ)
はじめに
Default Mod BaseのWikiを足掛かりに、すべてを読めばSlay the SpireのMODは作れるようになるはずです。
すべてを説明するのは大変なので、私が悩んだ部分のみを書いておきます。
開発環境構築
Default Mod Baseの使用
Default Mod Baseを使用します。使用しなくても頑張ればMODは作れるでしょうが、すべて一からを用意するのは大変なので使わせてもらいます。
Default Mod Base Wikiの指示通り、Default Mod BaseのGitリポジトリをzip形式でダウンロードし、自分で用意した任意のディレクトリに展開しておきます。
3つのMODの導入
下記3つのMODをSteam Workshopからインストールしてください。Steamクライアント上でサブスクライブボタンをクリックするだけです。
各MODのGithubのReleaseページからインストールしないでください。
IntelliJでの既存プロジェクトのインポート
Maven開発環境上でMODのためのコードを編集するので、IntelliJを使用します。
Default Mod BaseのWikiでは、ダウンロードしたDefault Mod Baseのプロジェクトをインポートする際に、画面上のインポートボタンから進めています。しかし、IntelliJ IDEA 2022.2.3 (Community Edition)ではそのインポートボタンがありません。下記の通りインポートします。
ウェルカム画面が開いたら、Ctrl+Shift+A を押し、project from existing sources と入力して、ポップアップで既存ソースのプロジェクトをインポートアクションをクリックします。
JDK
最新のJDKではMODのビルドに失敗しました。JDK 8が必要です。下記からOpen JDK 8をダウンロードしてインストールします。
File -> Project Structure -> Project Settings -> Project
とたどり、SDKの設定をします。
Mavenプロジェクトとして認識させる
プロジェクトをインポートした後は、IntelliJにMavenプロジェクトとして認識させて作業をする必要があります。
If you do not have a Maven tab, double-tap Shift to open the intelliJ search (your best friend!) and type "Maven" - you'll find it there.
Default Mod Base Wikiには上記のようにありますが、簡単に見つけられません。下記の手順でIntelliJにMavenプロジェクトとして認識させます。
pom.xml
を選択する。- Shiftキーを2回連続で押して、IntelliJ検索画面を出す。
- その選択画面で
Add as Maven Project
を検索して選択する。
DefaultMod.javaの編集
DefaultMod.java内のTODO:
から始まるコメント行の内容にしたがって編集します。
パッケージ名の設定
//TODO: FIRST THINGS FIRST: RENAME YOUR PACKAGE AND ID NAMES FIRST-THING!!!
左ペインの黒ドットがついたthe Default
を自分のMOD名に変更します。
4か所の変更
DefaultMod()メソッド内に4か所を変更する指示があります。その通りに変更すればOKです。
パッケージ化
Mavenプロジェクト内のpackage
をダブルクリックすれば、[yourmodname].jar
が生成されてMODは完成です。clean
でそのパッケージを削除できます。
SpirePatchの使い方(一部)
Insertの使用
Insertメソッドを使用して、オリジナルのSlay the Spireのメソッドのコードの間に、自分のコードを挿入できます。
挿入箇所の指定の仕方は、loc
、rloc
、locs
、rlocs
をそれぞれ使用する方法もありますが、Matcherを使用した方法もあります。Matcherを使用した方法では、自分のコードを、任意のメソッドの実行の直前などで実行できます。
@SpirePatch( clz = ShopScreen.class, //コードを挿入したいメソッドが含まれるクラスを指定 method = "purchaseCard" //コードを挿入したいメソッド名 ) public class ClickBuyCard { @SpireInsertPatch( locator= ClickBuyMod.ClickBuyCard.Locator.class //[packagename].[thisclassname].Locator.class ) public static void Insert(ShopScreen __instance) { AbstractDungeon.player.gold += 1; //ここに挿入したい自分のコードを書く } private static class Locator extends SpireInsertLocator { public int[] Locate(CtBehavior ctMethodToPatch) throws CannotCompileException, PatchingException { //MethodCallMatcher()の1つ目に対象のメソッドが含まれるクラスを指定する。 //MethodCallMatcher()の2つ目の引数に対象のメソッド名を渡す。 //その関数の直前にInsert()で指定した自分のコードが実行される。 Matcher finalMatcher = new Matcher.MethodCallMatcher(ShopScreen.class, "playCantBuySfx"); return LineFinder.findInOrder(ctMethodToPatch, new ArrayList<Matcher>(), finalMatcher); } }
フィールドにアクセスがあったときに自分のコードが実行されるようにするには、FieldAccessMatcher()
を使用します。
また、オリジナルのコードの中で元のクラスのフィールドにアクセスしたいときは、localvars
にそのフィールドの名前を指定したうえで、Insertメソッドの引数にその引数を指定します。下記の例ではフィールドcardAtlas
を読み書き可能です。
@SpirePatch2 (clz = AbstractCard.class, method ="initialize") public class CardImgMod { @SpireInsertPatch ( locator = Locator.class, localvars = {"cardAtlas"} ) public static void Insert(TextureAtlas cardAtlas) { //ここに挿入したい自分のコードを書く。ここではフィールドcardAtlasにアクセス可能。 } public static class Locator extends SpireInsertLocator { public int[] Locate(CtBehavior ctMethodToPatch) throws CannotCompileException, PatchingException { Matcher.FieldAccessMatcher fieldAccessMatcher = new Matcher.FieldAccessMatcher(AbstractCard.class, "cardAtlas"); return LineFinder.findInOrder(ctMethodToPatch, new ArrayList<Matcher>(), fieldAccessMatcher); } } }
質問は@hotpot774までお願いします。