5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Terraria】タイル破壊時のドロップアイテムの情報をリフレクションにてコードを生成する

Posted at

はじめに

この記事はテラリアというゲームでのMod開発に関する情報です。
サンプルコードや、公開されているコードを参考にMod開発をしている状況で、備忘録としてのまとめでもあり、記事投稿時点での知識で書いていますので、間違いもあるかもしれませんのでご注意ください。

やりたいこと

指定したブロックや壁紙を破壊しないようにするModを作成したい。
そのためには、TileオブジェクトからドロップするItemオブジェクトの情報が欲しい。

背景

テラリアでは、ブロックなどを破壊した際にそのアイテムがドロップするが、ブロックなどを管理するオブジェクトはTileオブジェクトであり、そこには破壊した際にドロップするItemオブジェクトの情報は紐付いていない。
では、Tileを破壊した際にどのようにしてドロップしているかと言うと、Terraria.WorldGenクラスに、KillTileメソッドがある。
Tile破壊時にはこれが呼ばれるようで、途中の判定でTileの破壊が失敗しなかった場合で、Itemがドロップする場合に、次のような感じで個別に設定されている。

	int num23 = 0;
	int num24 = 0;
	if (tile.type == 0 || tile.type == 2 || tile.type == 109)
	{
		num23 = 2;
	}
	else if (tile.type == 426)
	{
		num23 = 3621;
	}

※ソースコードはtModLoaderにてTerraria.exeを逆コンパイルして作成されたもの

この中から、ブロック系のみを対象にして、どのtile.typeが、どのitem.typeをドロップするかを抜き出したいが、いかんせんわかりづらい。
また、今後の事も考えて、tile->item を取得するようなメソッドを作っておきたい。

このため、この部分のコードを丸ごとコピーし、不要な処理を排除して、かつ、TileIDとItemIDで表記したコードにしたい。

なお、Tileのテクスチャは、次のようにおり、ゲーム上でどのテクスチャを表示するかは、tile.frameXで管理されている。
Tile_0013.png

ブロック系のテクスチャは、表現が異なっていても、ドロップするアイテムは同一だが、上記のようなタイプはそれぞれドロップするアイテムが異なる。

今回生成するコードでは、単純にtile.typeのみから判断できるものを対象とすることにしている。

実際のコード

やっていることは、

  1. TileIDクラスとItemIDクラスをリフレクションにて、値と名のディクショナリーを作成
  2. TileIDの数分、Terraria.WorldGen.KillTileメソッドのドロップ処理部分を別途抜き出し、不要な処理や、判断できない部分をコメントかしたメソッドを呼び出して、ItemIDを取得する(長いコードなのでここでは省略)
  3. 取得できたItemID分の値とアイテム名のディクショナリーを作成
  4. TileIDからItemIDを取得するメソッドのコードをファイルへ出力する
public static void MakeCodeOfTileTypeToDropItemType()
{
    //タイルタイプとTileID名のディクショナリーを作成する
    var dicTileID = new Dictionary<int, string>();
    var classTileID = typeof(TileID);
    classTileID.GetFields().ToList().ForEach(x => dicTileID.Add(int.Parse(x.GetValue(classTileID).ToString()), x.Name));

    //アイテムタイプとItemID名のディクショナリーを作成する
    var dicItemID = new Dictionary<int, string>();
    var classItemID = typeof(ItemID);
    classItemID.GetFields().ToList().ForEach(x =>
    {
        if (x.FieldType.Equals(typeof(short))) dicItemID.Add(int.Parse(x.GetValue(classItemID).ToString()), x.Name);
    });

    //タイルタイプからドロップするアイテムのリストを作成する
    List<Tuple<int, int>> listOK = new List<Tuple<int, int>>();
    List<int> listNG = new List<int>();
    for (int i = 0; i < dicTileID.Count; i++)
    {
        Tile tile = new Tile();
        tile.type = (ushort)i;
        int itemType = TileUtils.getKillDropItemType(tile);
        if (0 < itemType)
        {
            listOK.Add(new Tuple<int, int>(i, itemType));
        }
        else
        {
            listNG.Add(i);
        }
    }

    //ドロップするアイテムのアイテムタイプとアイテム名のディクショナリーを作成する
    var dicItemName = new Dictionary<int, string>();
    foreach (var x in listOK)
    {
        Item item = new Item();
        item.SetDefaults(x.Item2);
        if (!dicItemName.ContainsKey(x.Item2))
            dicItemName.Add(x.Item2, item.Name);
    }

    //TileIDとItemIDで置き換えたコードを生成する
    using (FileStream fs = new FileStream($@"{Main.SavePath}\TileUtils.cs", FileMode.Create))
    using (StreamWriter sw = new StreamWriter(fs))
    {
        sw.WriteLine("public static class TileUtils");
        sw.WriteLine("{");
        sw.WriteLine("    public static int getKillDropItemType(Tile tile)");
        sw.WriteLine("    {");
        sw.WriteLine("        int result = 0;");
        sw.WriteLine("        switch (tile.type)");
        sw.WriteLine("        {");
        foreach (var x in listOK)
        {
            sw.WriteLine($"            //{dicItemName[x.Item2]}");
            sw.WriteLine($"            case TileID.{dicTileID[x.Item1]}:");
            sw.WriteLine($"                result = ItemID.{dicItemID[x.Item2]};");
            sw.WriteLine("                break;");
        }
        foreach (var x in listNG)
        {
            sw.WriteLine($"            case TileID.{dicTileID[x]}:");
            sw.WriteLine("                break;");
        }
        sw.WriteLine("        }");
        sw.WriteLine("        return result;");
        sw.WriteLine("    }");
        sw.WriteLine("}");
    }
}

実際に出力されたものの一部

public static class TileUtils
{
    public static int getKillDropItemType(Tile tile)
    {
        int result = 0;
        switch (tile.type)
        {
            //土のブロック
            case TileID.Dirt:
                result = ItemID.DirtBlock;
                break;
            //石のブロック
            case TileID.Stone:
                result = ItemID.StoneBlock;
                break;

と言った感じで出力したので、後はここから必要な修正を施したりします。

以上

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?