Terraria Mod開発の話

暇なときのメモ書き

1.4でのNPCの話

Mod開発の話ではない

1.4が来て、いろいろ変わったけどそのうちの目玉の一つが町とNPCの幸福度 幸福度によって売るものの値段が変わる、ワープ装置が利用可能になるなど幸福度が高ければ様々な恩恵を受けられる

ついでにコードベースにも大きく変更が加わっているがそれはまた別の機会に

NPCの種類

まずはNPCの種類だが、大きく分けて2種類に分けられる。居住可能NPC、居住不可能NPCの2種類。 居住可能NPCはガイド、商人などで、居住不可能NPCは旅商人、骨商人

また、取りうる値段の範囲は75%から150%となっている

居住可能NPCと判定

居住可能NPCNPC同士の距離と同じ町にいるのが好みのNPCかどうか、好きなバイオームかどうかが大きな要因になっている

まず"同じ部屋"であると判定される距離は他のNPCが住んでいる家から(家がない場合はそのNPCがいる位置から)25タイル以内、"同じ町"と判定される距離は120タイルとなっている。

"同じ部屋"に4人以上(本人を除いて3人)のNPCがいると販売価格があがる。(4人目から一人につき4%、積算)

また、値段を下げる条件として、"同じ部屋"には2人以下(本人以外に1人)のNPCであること、"同じ町"に4人以下(本人以外に3人)のNPCしかいなければ、90%の値段になる(他の条件と積算)

従って、家判定をコントロールして、NPCが実際にいる場所とNPCの家の場所を変えてしまえば安く買える、今までのような監獄ハウスが作れる。

好き/嫌いなNPCの判定は、"同じ町"に住んでいるかどうか そのため、配置次第では好きなNPCを共有可能になる。 また、好き/嫌いなバイオームの判定はプレイヤーに依存する。1.3.5.3の釣り掘のようなことをすれば一応バイオームの調整が可能になる。その場合Pylon側に制限が入る可能性がある 好き/嫌いには段階がある。上から順に 大好き/好き/嫌い/大嫌い 90%/95%/150%/110%

嫌い、のほうが金額の上昇幅が大きいことに留意したい

Pylonの販売条件は85%以下であること。好きなバイオームで2人だけの場合、90%x90%=81%で販売する。

これらを踏まえた上で、次が好きなバイオーム/NPCの表である 表を見ればわかるようにバイオームは好き/大嫌いが基本で、大好き/嫌いなのはサンタクロースだけ redditの表のほうがわかりやすいが、嫌いなバイオームの表記がないのと、テラリアのバグでテキストと実際の値に差があるため注意

https://i.redd.it/davabn4chpz41.png

NPC 好きなバイオーム 嫌いなバイオーム 大好きなNPC 好き 嫌い 大嫌い
Merchant 森林/好き 砂漠/大嫌い Golfer, Nurse Tax Collector Angler
Guide 森林/好き 海/大嫌い Clothier, Zoologist Steam Punker Painter
Golfer 森林/好き 地下/大嫌い Angler Painter, Zoologist Pirate Merchant
Zoologist 森林/好き 砂漠/大嫌い Witch Doctor Golfer Angler Arms Dealer
Mechanic 雪原/好き 地下/大嫌い Goblin Cyborg Arms Dealer Clothier
Cyborg 雪原/好き ジャングル/大嫌い Stylist, Pirate, Steam Punker Zoologist Wizard
Tax Collector 雪原/好き 聖域/嫌い Merchant Party Girl Demolitionist, Mechanic Santa Claus
Santa Claus 雪原/大好き 砂漠/嫌い Tax Collector
Arms Dealer 砂漠/好き 雪原/大嫌い Nurse Steam punker Golfer Demolitionist
Steam Punker 砂漠/好き ジャングル/大嫌い Cyborg Painter Party Girl, Wizard, Dryad
Dye Trader 砂漠/好き 森林/大嫌い Arms dealer, Painter Steam Punker Pirate
Dryad ジャングル/好き 砂漠/大嫌い Witch Doctor, Truffle Angler Golfer
Painter ジャングル/好き 森林/大嫌い Dryad Party Girl Cyborg, Truffle
Witch Doctor ジャングル/好き 聖域/嫌い Dryad, Guide Nurse Truffle
Pirate 海/好き 地下/大嫌い Angler Bartender Stylist Guide
Stylist 海/好き 雪原/大嫌い Dye Trader Pirate Bartender Goblin
Angler 海/好き 砂漠/大嫌い Party Girl, Demolitionist, Tax Collector Bartender
Demolitionist 地下/好き 海/大嫌い Bartender Mechanic Goblin, Arms dealer
Clothier 地下/好き 聖域/嫌い Truffle Tax Collector Nurse Mechanic
Goblin Tinker 地下/好き ジャングル/大嫌い Mechanic Dye Trader Clothier Stylist
Nurse 聖域/好き 雪原/大嫌い Arms Dealer Wizard Party Girl, Dryad Zoologist
Wizard 聖域/好き 海/大嫌い Golfer Merchant Witch Doctor Cyborg
Party Girl 聖域/好き 地下/大嫌い Wizard, Zoologist Stylist Merchant Tax Collector
Bartender 聖域/好き 雪原/大嫌い Demolitionist Goblin Guide Dye Trader
Truffle Guide Dryad Clothier Witch Doctor

家がない、家から遠いなら強制的に最大の値段(150%)になる また、プレイヤーが不浄、クリムゾン、ダンジョン環境にいる場合も最大になる

居住不可能NPC

旅商人はワールドのスポーン地点から遠ければ遠いほど値段が下がる。スポーン地点がワールド中心(マップの中心)から右寄りなら左の海、左寄りなら右の海に呼べれば安く買える。

骨商人は月齢によって変わり、新月が最も安く、満月が最も高い(1段階につき10%変化)。また日中はさらに10%値上がりし、満月の昼は新月の夜に比べて150%の値段になる。

情報は間違っている可能性があるので注意、自分で検証に勝る情報ソースはない

MonoMod

この記事初心者向けじゃないからよろしくな

tModLoaderには次のアプデでMonoModってのが追加される。 ちなみに今はBetaだからDLしてないやつはDLしとけ、さすがに必須

これが圧倒的最強で、なんと今まで手を入れられなかったバニラテラリアのprivateなあんなとこやそんなとこをいじくりまわせるのだ

まるで男優と女優と犬が超高速移動する時間停止モノ...話が逸れた

とりあえず、GithubのWikiにtML公式が用意してるチュートリアルがあるのでそれを開いて同時に読んでほしい。 画像用意するのめんどいからな、あと英語読めるやつはこのサイト見てないでWikiだけ見てればいいぞ

OK、開いた? そしたらdnspyインストールしろって書いてあるからサクッとインストールする...って言っても一番上にあるnet-472.zip的なやつをDLして解凍するだけだが。

tMLはHivePackをアップグレードして、アップグレードしたアクセサリを装備しているときに蜂が強化される代わりにBeenadeを出したいらしい。バニラの武器でも。

まあこっちはModでタイマー追加できないことにいら立ってるからModでタイマー追加できるようにコードを変えるんだが。

なんでこれができないかって言うと、Wiring.HitSwitchでタイルのID(type)を照合してバニラの一部のタイル(スイッチとかタイマーとかその他もろもろ)だけ信号を送るようになっているから。

その前に何やらリファレンス追加しないといけないらしい。 公式は不親切なことにリンク貼ってないから貼っておく

github.com

この中でDLしなきゃいけないものは結構あって、

  • Mono.Cecil.dll
  • Mono.Cecil.Pdb.dll
  • MonoMod.Utils.dll
  • TerrariaHooks.FNA.dll
  • TerrariaHooks.XNA.dll

どれが必須かはわからんが自分の環境じゃこれで動いたからまあディスクきつい人はきっちり選ぶ前にディスク整理するなり全部クラウドに置いとけ

これを全部DLしたらModCompileフォルダに入れて今から作業するプロジェクトでリファレンスに入れる。

これで環境セットアップおわり

したらdnspyでtModLoader.exeを指定して開く。 tMLチュートリアルだとplayer.beeType()を開いてる。 こっちだとWiring.HitSwitch()を開く。

で、上の方にStartとかUndo-Redoと同じ高さにC#って書いてあるやつがある。 これで表示する言語を選べる ILが中間言語。ILはスタックマシンになってる。

開いたら、右のAssembly ExplorerでHitSwitchの上で右クリックしてEdit Method(C#)

f:id:kodamamiyabi:20190615162458p:plain
今回追加するコード(カーソルの位置から下)

ここで自分がコード書いてこうなってくれたらいいなっていう未来予想図を書く。 このステップは飛ばしてもいいんだが。 今回追加するのはコード末尾のカーソルのあるreturnから下

公式のチュートリアルに書いてあるから一応コンパイルしてみる。 右下のCompileってボタンを押す...がこのままだと通らない。 TileObjectData見たことねえぞって言われるので、

using Terraria.ObjectData;

とかやってやる。まあコンパイルは通る。 コンパイルしたら最後のelseの代わりにreturnが入ったが。まあいい

そしたら表示言語をILにしてみる。やたら長くてどの部分が変わったかぱっと見じゃわからないが、 今回はコード末尾に追加してあるのと、(結果的に)Returnが2つ追加されてるから、 下から見ていってretを2個探せばいい。 と思うじゃん。関数の最後にretが勝手に追加されるので3つです。

IL表示の時のピンクの文字にマウスカーソルを合わせるとその記号がどういうやつか英語で教えてくれる。 クリックするとMSのリファレンスWebページに飛ぶ

見慣れない記号列ばっかりだがretはreturnの略だったりcallは普通に関数呼び出しだったり、 英単語の略が多いから普通に読める、抵抗感なくしていけ

ピンク文字の左についているIL_0000みたいなやつはラベルって言う。 if文とかfor文とかその他もろもろ諸星きらりはこのラベルを使って指定位置に飛ぶ (正確には違うっぽい挙動してたが何を基に飛んでるか調べるのがめんどくさかった)

ifコードブロックの終わりに文字通り飛んでるってこと

いろいろわかったところで、 公式はコード変えるのに3つのアプローチを書いているけど実質2つ

1,2はC#を書いて、 3はILを書いてる。

んで、ぶっちゃけ言うと3でしかさっきのCompileは必要ない。 1,2はILは見るけどCompileはイメトレ程度

そしたら実際にコード書いていく

まず、ModTileのAutoLoad()に

public override bool Autoload(ref string name)
{
    IL.Terraria.Wiring.HitSwitch += HitSwitchHook;
    return base.Autoload(ref name);
}

って書いてやる。AutoLoad()でやる理由は不明。1回きりでいいならModのコンストラクタでやってもよさそうだけどわからないのでAutoLoad()使いましょう(脳死)。tMLではModItemのAutoLoad()に追加している。 IL.Terrariaの行がHitSwitchって関数にHitSwitchHookで変更加えますよ的な感じ。批判受けそう。 HitSwitchHookって関数を定義とついでに操作できるやつを追加

using MonoMod.Cil;

//(略)

private void HitSwitchHook(ILContext il)
{
    var c = new ILCursor(il);
}

んで、基本的にこの操作をするときは、

  1. コードを追加したい場所の特徴を把握、移動
  2. コードを追加

の2ステップが必要になる。 とりあえず公式の1,2のバージョンだけ書く。正直IL書きたくないでしょ?編み物みたいで楽しい

まずは末尾(のretの前)に入れたいので、

c.Index = c.Body.Instructions.Count - 1;

でretの一つ前に移動。コードを追加する。

c.EmitDelegate<Func<int, int, bool>>((i, j) =>
{
    Tile tile = Main.tile[i, j];
    Main.PlaySound(28, i * 16, j * 16, 0, 1f, 0f);
    TileObjectData data = TileObjectData.GetTileData(tile);
    if (data != null)
    {
        int left = i - (int)(tile.frameX / 18);
        int top = j - (int)(tile.frameY / 18);
        Wiring.TripWire(left, top, data.Width, data.Height);
    }
    else
    {
        Wiring.TripWire(i, j, 1, 1);
    }
    return true;
});
c.Emit(Pop);

EmitでILのコード1個追加するみたいな。

Emit(コード, 引数);

EmitDelegateはdelegateしてあげる必要があるのでFuncを使う。 Funcは戻り値一つ(末尾)要求するので、何でもよかったけどboolで。 で、このboolが残っていると末尾のretでboolがスタックから読まれて返されてしまう(エラーを吐く)ので、末尾のPopで消す。

後は通常のC#コードなのでわかると思う。わからなかったらコード読め

できました!!!とはいかない

問題点その1

iとjはどこから引っ張ってくるの?

c.Emit(Ldarg_0);
c.Emit(Ldarg_1);

その2

さっき既存のif文の末尾にreturn追加してたよね?

c.Emit(Ret);

完成!!でもない。

WHY???????完成ジャナーイ???????

(Retを消してから)Main.newText()でなんか出せばわかるんだけど、なぜかLeverを押したときは反応するけど、ほかのタイルでは反応しない。

ってことはその手前にあるレバーのif文の中に追加されてしまっていることが考えられる。 コード追加していないHitSwitchのILコードを見てみる。

レバーのif文は411と132が入っているif文なので、411と132を手掛かりに探す。

f:id:kodamamiyabi:20190615174240p:plain
ILの画面

132の行の次(312行目)と411の行の次(320行目)に両方ともラベルが引数になっていることがわかる。

132の次は322行目へのラベルだったので(カーソルを合わせるとハイライト表示される)、if文の中に入っていそうだ。 411の次は...末尾のretに飛んでいた。

つまり、

  1. ifの中に入る→コードを順番に実行→新たに追加されたreturn呼び出し→終了
  2. ifの外→追加されたコードを実行→末尾のreturnを呼び出して終了

という動作を期待していたのだが、

  1. ifの中に入る→コードを順番に実行→追加されたコードを実行→終了
  2. ifの外→ラベルを基に末尾のreturnまでジャンプ(追加コードはスキップ)→終了

となっていた。 ラベルを基に末尾まで飛んでしまうので、ラベルを追加コードの手前に設定することで修正する。

まずは改変する場所を探す。 ここでID決め打ちすると他のModと被った時に簡単に競合してしまうので、ほかの情報を使う。

今回はbne.unとラベルが飛ぶ先がretであることを利用する。手前で411がスタックに積まれていることを利用してもいい。

var label = c.DefineLabel();
 ILLabel target = null;
while(c.TryGotoNext(i => i.MatchBneUn(out target)))
{
    if (target.Target.Match(Ret))
    {
        c.Remove();
        c.Emit(Bne_Un, label);
        break;
    }
}

var label = c.DefineLabel(); でラベル使いますよ辞書に追加しといてねってやる。批判受けそう。

ILLabel target = null; これもラベルだけど、これはbne.unに一致したやつをいれておく用

while(c.TryGotoNext(i => i.MatchBneUn(out target))) これでbne.unに一致するものがある間はbne.unのとこに移動するループをする。

if (target.Target.Match(Ret)) これでラベルの飛び先がRetであることを確認する

c.Remove(); で今まであった行を消して、

c.Emit(Bne_Un, label); で代わりに自分で設定できるラベルに書き換えたものにすり替えておく

そしたら、ラベルの飛び先はまだ設定できてないので、設定する

i,jを読むところにラベルを設定する。

c.MarkLabel(label);
c.Emit(Ldarg_0);
c.Emit(Ldarg_1);

今度こそ完成!!! お疲れ様でした!!!!

ふるこーど

using System;
using Terraria;
using Terraria.ModLoader;
using Terraria.ObjectData;
using MonoMod.Cil;

namespace BlogMod
{
    class blogSwitch : ModTile
    {
        public override bool Autoload(ref string name)
        {
            IL.Terraria.Wiring.HitSwitch += HitSwitchHook;
            return base.Autoload(ref name);
        }

        private void HitSwitchHook(ILContext il)
        {
            var c = new ILCursor(il);

            var label = c.DefineLabel();
            ILLabel target = null;
            while(c.TryGotoNext(i => i.MatchBneUn(out target)))
            {
                if (target.Target.Match(Ret))
                {
                    c.Remove();
                    c.Emit(Bne_Un, label);
                    break;
                }
            }

            c.Index = c.Body.Instructions.Count - 1;
            c.Emit(Ret);

            c.MarkLabel(label);
            c.Emit(Ldarg_0);
            c.Emit(Ldarg_1);
            
            c.EmitDelegate<Func<int, int, bool>>((i, j) =>
            {
                Tile tile = Main.tile[i, j];
                Main.PlaySound(28, i * 16, j * 16, 0, 1f, 0f);
                TileObjectData data = TileObjectData.GetTileData(tile);
                if (data != null)
                {
                    int left = i - (int)(tile.frameX / 18);
                    int top = j - (int)(tile.frameY / 18);
                    Wiring.TripWire(left, top, data.Width, data.Height);
                }
                else
                {
                    Wiring.TripWire(i, j, 1, 1);
                }
                return true;
            });
            c.Emit(Pop);
        }
    }
}

ここまで書くのにだいぶ疲れたから3は適当。 基本的にc.Emit()をコンパイル結果見ながら連打していくんだけど、どうやって関数呼ぶんや系が発生しがち。 おそらくこっちの方がパフォーマンスに優れている

var type = typeof(System.Int32);
var concat = typeof(System.String).GetMethod("Concat", new Type[] { typeof(object), typeof(object), typeof(object) });
c.Emit(Ldarg_0); //i
c.Emit(Box, type); //type(System.Int32)でキャスト
c.Emit(Ldstr, " Hook "); //文字をスタックに
c.Emit(Ldarg_1); //j
c.Emit(Box, type); //type(System.Int32)でキャスト
c.Emit(Call, concat); //concat関数(引数はobjectの長さ3配列)を呼ぶ、結果はスタックへ
c.Emit(Ldc_I4, 255); //コンパイルしたら設定しろって言われた(デフォルト値), 255をスタックへ
c.Emit(Ldc_I4, 255); //同
c.Emit(Ldc_I4, 255); //同
c.Emit(Ldc_I4_0); //同, falseをスタックへ
var newText = typeof(Main).GetMethod("NewText", new Type[] { typeof(string), typeof(byte), typeof(byte), typeof(byte), typeof(bool) });
c.Emit(Call, newText); //newText関数を呼ぶ

開発環境セットアップについて

VisualStudioインストールからModテンプレートDLまでは省略

Documents/My Games/Terraria/ModLoader下にMod Sourcesフォルダがなければ作る

打ち込むのめんどくせえ!って人はtModLoader(tMLとか略すことがある)のメニューのMod Sourcesってとこクリックすれば作られた気がする

Mod Sourcesフォルダに解凍したModテンプレートを入れる

んで、tModLoaderインストールしたときにtModLoaderのインストール先がTerrariaってフォルダじゃないとデフォルトでエラー吐くので変えた方がいい 一人のプロジェクトならpost build eventの設定すればいい(後述)

まあたぶん大体エラー吐かないんだけど、UIとか作るときにフォントを描画する必要が出てきて、そうなるとReLogic.dllがないよ!って怒られることがある

そしたら

github.com

からReLogic.dllってのをDLする 自分のOSのやつな

で、そいつを適当な場所(Terraria.exeと同じ場所がいいかも)に置いて、

VisualStudio側でソリューションエクスプローラー開いて

f:id:kodamamiyabi:20190606131016p:plain
ソリューションエクスプローラー(英語版)

リファレンス?参照?ソリューションの下のプロジェクトのとこにある今青くなっているとこを右クリックして 参照を追加?リファレンスを追加?を押す

そうするとなんかウィンドウが開くんで右下の3つのボタンのOKでもキャンセルでもないやつを選ぶ で、さっきDLしたReLogic.dllを指定して、OK押すとリファレンスに追加される

日本語版インストールするのがめんどくさいから誰か正しい名前教えて

こうするとだいたいエラーなくなる

で、tMLのインストール先がC:\Program Files (x86)\Steam\steamapps\common\Terrariaフォルダじゃない場合 2か所修正する必要がある

まずはさっきReLogic.dll指定したリファレンスにTerrariaがあるから それを一回右クリして消す で、追加でさっきやったのと同じようにTerraria.exeを指定する

もう一か所は、プロジェクトを右クリックしてプロパティを開く ビルドイベント的なメニューが上から3番目にあるからそれを押して、ボックスが2つあると思うからその下の方の C:(略)\tModLoaderServer.exeを自分のインストール先へのフルパス\tModLoaderServer.exeにする

たぶんこれで大丈夫だと思う なんか他に問題聞いたら追記する

NPCのlifeとか

NPCにはlifeに関する変数がいくつかある。見落としてたら追加するかもしれない。

変数名 デフォルト値 役割
life 0 現在体力
lifeMax 0 体力の最大値
realLife -1 ワームなどの体力共通のNPCにある実際の体力を保持しているNPCへのインデックス
lifeRegen 0 ライフの秒間自然回復量x2。基本的にデバフに使う
lifeRegenCount 0 絶対に触らない。タイマー
lifeRegenExpectedLossPerSecond -1 一度にどれだけのダメージを受けるか
friendlyRegen 0 基本的に触らない。味方NPCの自然回復

まあどうせわからないんで補足

ゲーム内にスポーンするNPCはMain.npc[1000]に格納されているんだけど、これのインデックスがrealLifeに入る。 realLifeが0以上ならrealLife(のインデックスにいるNPCのlife)を参照するようにする。

lifeRegen 2秒間での回復量を指定する。ほかのデバフと重複させるために+=とか使う。

lifeRegenExpectedLossPerSecond ヘルストーンの上に乗ったら体力が1づつ減っていく代わりに5づつ減っていくアレ。5づつ減らすときは5と指定する(符号不要)。

friendlyRegen 基本的に3秒に1度1回復して、Dryadのバフがあれば17tickに1回復する。180になったときに1回復するので止めたければ0代入すればいいし加速したいなら直接加算する。

テクスチャの話

ブログタイトル負けしている今日この頃って挨拶文書こうとしたけどやめて本題

テラリアのテクスチャをどう作るか

まず、Mod開発においては、基本的にpng形式で、透過チャンネルを含めた4チャンネル(32bit)の画像を使います。 テクスチャと言っても、 + NPCやタイルなどのゲーム内で使われるもの + UIなどのキャラクター作成やメニューに使われるもの の大きく分けて2種類があります。

ゲーム内で使われるものは雰囲気を統一するために、テクスチャの1ドットを2x2ドットにしてテクスチャを作成しています。 なので、20x30のドット絵を打とうとした場合、実際の画像サイズは40x60となります。 UIではその制限は特にありません。実際のテラリアのテクスチャを見ながらどちらのタイプか判断するといいと思います。

で、肝心のテラリアのテクスチャですが、Terraria.exeがあるフォルダのContentというフォルダを見ると、謎のxnbというファイルが大量にあります。 これが画像ファイル(音声もあります)なので、こいつを解凍してやるとpng画像が出てきます。 解凍ツールは探せばいくらでも出てくると思います。 github.com

ドット絵を作る

自分で1からドット絵を打つ場合は

EDGE takabosoft.com とかを使えばいいんですが、テラリアのテクスチャを基に作りたいというときなどは、EDGEでは直接読み込むことはできません。

Yukari aoriika.exout.net などを使うといいと思います。画像をまとめて処理できるそうです。 hayakawa-pencil-1951.hatenablog.com

ドット絵の打ち方は知らないので適当に

TShock Pluginの作成法だけ

TShock Pluginは.dllファイルで作成する必要があります。 新規プロジェクトからクラスライブラリを作成します。 f:id:kodamamiyabi:20181013192410p:plain

そうしたら、リファレンスに(tShockの)TerrariaServer,exeと、ServerPlugins\TShockAPI.dllを追加します。コンフィグ読み込みする場合には、Newtonsoft.Json.dllも追加します。

テキスト書くのめんどいんで適当にテンプレ貼っておきます。

using System.IO;
using Terraria;
using TShockAPI;
using TerrariaApi.Server;
[ApiVersion(2, 1)]
public class BlogMain : TerrariaPlugin
{
  public override string Author => "Name";
  public override string Description => "Omae ha mou shindeiru";
  public override string Name => "Plugin Name";
  public override Version Version => Assembly.GetExecutingAssembly().GetName().Version;

  //public static BlogConfig config = new BlogConfig();

  public BlogMain(Main game) : base(game)
  {
  }

  #region Initialize/Dispose
  public override void Initialize()
  {
   ServerApi.Hooks.GameInitialize.Register(this, OnInitialize);
  }

  protected override void Dispose(bool disposing)
  {
      if (disposing)
      {
        ServerApi.Hooks.GameInitialize.Deregister(this, OnInitialize);
      }
  }
  #endregion
  
  #region OnInitialize
  private static void OnInitialize(EventArgs args)
  {
    //var configPath = Path.Combine(TShock.SavePath, "BlogConfig.json");
    //config = Config.Read(configPath);
  }
  #endregion
}

大体Githubとかのリンクあるからそこから見ればいいんじゃないですかね。 tshock.co

Terraria本体について少しだけ

ちょくちょくアクセス貰っているのにコンテンツが充実していないのが申し訳ないので、 ちょっとした早見表みたいなのを置いておきます。 今後更新はそこまでしない気がします...

Terraria本体のコードを見ないと進まなさそうな場合 * Main * 迷ったらここ * NPC、Projectile他大体の配列が入ってる * UIに近い内容も * NPC * NPC.NewNPC()でNPCスポーン。AIとか見たいときに。 * Projectile * 弾、槍、ペットなどの処理はここ。NewProjectile()で弾作成。 * Item * アイテム系は大体ここ。NewItem()はワールドにアイテムをスポーン。 * Player * 防具セット効果、バフなどのステータスとか、アイテム使ったりとか色々。 * インベントリは枠ごとに配列が分かれている。 * NetMessage * マルチプレイの時のデータ受信 * MessageBuffer * マルチプレイの時のデータ送信 * Tile * 見ない。 * WorldGen * タイル操作は大体ここ * Wiring * メカニズム系は大体ここ * Dust * タイル殴った時の粉とか、血しぶきとかああいうエフェクトはここ。 * Terraria.ID下 * 大体がID直接書かれているので探したい場合。公式WikiにもID表はある。 * どこ探しても見つからねえぞハゲ * このジャングルにガイドはいない

NPCやProjectileなどは内部でIDで書かれているので、ID表を作るなりなんなりしてあげるといいと思います。

※関数は引数を書いていないので統合開発環境でやるなりエラーメッセージ読むなりしてください。

※大体の例はExample Modに入ってるので見て、それでもどうしても何もわからんとかあったらコメントつけるなりなんなりしてください。