見出し画像

Macro / Keybind Modコード集

Minecraft1.12.2で建築をしている人なら必ず使っている便利なmodの1つであるMacro / Keybind Mod(以下マクロmod)。

ここでは私が書いたコードの導入方法と仕組みを説明します。なおmodの導入方法やボタンの作成などは色んな方が解説しているので省かせていただきます。

流れとしては「コードの動作の様子→導入方法→コードの仕組み」を機能ごとに解説していき、最後に参考にした記事や変数、関数集のリンクや自分なりのコードの編み方などを載せていきます。

マクロという性質上、度々プログラミング用語が出てきます。プログラミングに触ったことのない人でも分かりやすいように心がけていますが多少知識があると理解しやすいです。(ifとか変数とかあの辺りが理解できていれば十分です)

定数や変数などの説明はあまぐらすさんのnote記事ケイヅキさんのブロマガ(Internet archiveに接続されます)でされているのでそちらをご覧ください。

下は目次となります。ここから必要な機能のセクションへ飛んでください。

座標記憶&戻る機能

動作の様子はこんな感じです。

「座標を記憶」を押すことで現在の座標が記憶され、離れた地点にいても「座標にTP」を押すと、記憶した座標に戻ることができます。
また視点や視野角も記憶するので視点を固定した建築などに便利です。
「戻る」を押すことでTPする前の座標に戻るだけなく視点・視野角もTP前のものに戻るので作業復帰が楽になります。

1.導入方法

①「座標を記憶」、「座標にTP」、「 戻る」ボタンをチャット画面に作成します。名前は覚えやすいもので大丈夫です。
(キーボードのキーにも割り振れます)

②下のコードを「座標を記憶」ボタン(もしくはキー)にコピペします。

$${@#tpx=%XPOS%;@#tpy=%YPOS%;@#tpz=%ZPOS%;@#tpfov=%FOV%;@#tpyaw=%CARDINALYAW%;@#tppitch=%PITCH%}$$

③下のコードを「座標にTP」ボタン(もしくはキー)にコピペします。

$${if(@#tpy=0);echo("先に座標を記憶してください。");else;@#x=%XPOS%;@#y=%YPOS%;@#z=%ZPOS%;@#fov=%FOV%;@#yaw=%CARDINALYAW%;@#pitch=%PITCH%;echo(/tp @s %@#tpx% %@#tpy% %@#tpz%);look(%@#tpyaw%,%@#tppitch%);fov(%@#tpfov%);endif}$$

④下のコードを「戻る」ボタン(もしくはキー)にコピペします。

$${if(@#y!=0);echo(/tp @s %@#x% %@#y% %@#z%);look(%@#yaw%,%@#pitch%);fov(%@#fov%);endif}$$

各ボタンを押して動作してれば完了です。
動作しない場合は、グローバル変数の重複などを確認してください。


この後は追加オプションになります。無くても動作しますが必要な方は適宜追加してください。

オプションA:記憶した座標をワールドに入る度リセットする

キーボードのキーバインド画面(赤文字のキーがたくさん並んでいる画面)を開き左上のマクロ設定一覧から「イベント」画面に切り替え、「onJoinGame」という項目に下のコードをコピペします。

$${unset(@#tpx);unset(@#tpy);unset(@#tpz);unset(@#x);unset(@#y);unset(@#z)}$$

オプションB:記憶している座標を表示させる

チャット画面もしくはゲーム画面(キーボードのキーバインド画面→左上にあるマクロ設定一覧をキーからGUIに変更することでゲーム画面の編集に入れます)にラベルを3つ配置しラベル名を%%、バインディングをそれぞれ@#tpx@#tpy@#tpzにします。
「座標を記憶」ボタンを押すと座標が表示されればOKです。

2.コードの仕組み

コードの構造は大まかにこんな感じです。

「座標を記憶」ボタンを押すことで現在の座標を取得する

取得した座標XYZや視点、視野角を記憶用のグローバル変数に渡す

「座標にTP」を押すと記憶座標にTPする前に現在の座標、視点、視野角を「戻る」機能用に別グローバル変数に記憶させる。

記憶座標用のグローバル変数に入っている値をTPコマンドや視野角を設定する関数に代入する。

「戻る」ボタンを押すとTP前の座標などが代入される。

つまり「座標にTP」と「戻る」ボタンは、記憶させるグローバル変数名が違うだけで基本的には同じコードで表されています。
では、各ボタンのコードについて解説していきます。

①「座標を記憶」のコード

$${@#tpx=%XPOS%;
   @#tpy=%YPOS%;
   @#tpz=%ZPOS%;
   @#tpfov=%FOV%;
   @#tpyaw=%CARDINALYAW%;
   @#tppitch=%PITCH%
}$$

(見やすいように改行しています)
ここで使われている定数は6つあります。
XPOS・・・現在のX座標を取得する
YPOS・・・現在のX座標を取得する
ZPOS・・・現在のX座標を取得する
FOV・・・現在の視野角を取得する
CARDINALYAW・・現在のヨー角(水平の回転方向)を取得し180を加えたもの
PITCH・・・現在のピッチ角(垂直の回転方向)を取得する

ヨー角など見慣れない言葉が出てきましたが、これは主に飛行機業界で使われる言葉で、下図にある通り横振りをヨー角、縦振りをピッチ角といいます。(ロール角は視点方向の軸の回転になるのでマクロmod定数に存在しません)
また、マクロmodにはYAWとCARDINALYAWの2つの定数があり、どちらもヨー角を測定できるのですがLOOK関数(後ほど出てきます)はCARDINALYAWの値を使っているらしくCARDINALYAWを使っています。

Wikipediaより引用

この2つの値を使うことでプレイヤーの視点を決定できます。

また、このコードでは定数で取得した値を変数に代入する作業もしています。
6つの変数は全て数値なので#(Int型)を使います。
@をつけてグローバル変数にしているのはこの後の「座標にTP」で代入した値を使いたいからです。

②「座標にTP」のコード

$${if(@#tpy=0);
     echo("先に座標を記憶してください。");
   else;
     @#x=%XPOS%;@#y=%YPOS%;@#z=%ZPOS%;@#fov=%FOV%;@#yaw=%CARDINALYAW%;@#pitch=%PITCH%;
     echo(/tp @s %@#tpx% %@#tpy% %@#tpz%);look(%@#tpyaw%,%@#tppitch%);fov(%@#tpfov%);
   endif
}$$

このコードは長いですがやっていることは単純で、
まず座標が記憶されているかをY座標を見て判断し、されていない場合は文章を表示、されている場合は次の処理へ行かせます。
else句以後のコードの前半部(4行目)では現在の座標などを取得し先程とは別のグローバル変数に代入、後半部(5行目)では「座標を記憶」で記憶させた座標をグローバル変数から取り出しTPコマンドなどに代入しているだけです。
前半部の説明は先程と同じなので省略します。(グローバル変数名だけ注意)
後半部はお馴染みのTPコマンドの他にマクロmod独自の関数を使っています。

  • LOOK関数は特定のヨー角、ピッチ角を入れることでプレイヤーの視点を自由に変えれます。

  • FOV関数は引数に数値を入れることで視野角がその角度に変更されます。

したがって6つの定数で「現在見ている画面を保存」でき、TPコマンドと2つの関数で「保存した画面を再現」できるというわけです。

③「戻る」のコード

$${if(@#y!=0);echo(/tp @s %@#x% %@#y% %@#z%);look(%@#yaw%,%@#pitch%);fov(%@#fov%);endif}$$

このコードは「座標にTP」のコードの後半部と同じ構造なので説明は省きます。
こっちもif分岐でTPの履歴がない場合は戻る機能が発動しないようにしています。

3.使っている変数・グローバル変数名一覧

下がこの機能全体で使われた変数名一覧です。
変数名重複のチェック等にお使いください。

@#tpx、@#tpy、@#tpz
@#tpfov、@#tpyaw、@#tppitch
@#x、@#y、@#z
@#fov、@#yaw、@#pitch


細分型時間設定

動作の様子はこんな感じです。

Minecraftのゲーム内時間はティック(Tick)という単位で表され、1日は24000Tickです。マクロmodにはスライダーという機能があり、これを使うことでコマンドを打たずとも時間変更ができますが、スライダーの値の幅を24000に設定すると微調整がしずらいです。

そこで時間帯を朝・昼・夕・夜の4つに分けることで、スライダーの幅が1/4の6000になり微調整がしやすくなりました。
また、スライダーの上には調整しやすいよう目盛りを振り、時間帯が変更されると目盛りも変わるようにしています。
他にもマルチ用のptimeコマンド切り替え機能、現実の24時間表記機能なども付いています。

1.導入方法

①・時間を調整する用にスライダーを1つ
 ・4つの時間帯を切り替える用にボタンを4つ
 ・目盛りを表示する用にラベルを5つ
 ・現在Tick時刻、24時間表記用ラベルを1つ
 ・ptime切り替え用にボタンを1つ
 ・現在使用しているのがtimeなのかptimeなのかを表示する用にラベルを1つ
これらをチャット画面に配置します。
(私は時間帯を4つに分けましたが5つ以上でも可能ですし、目盛りをもっと増やすこともできます。詳しくは解説編にて)

②下のコードをスライダーにコピペし、バインディング名@#timebarとし、最小値を0、最大値を6000にします。

$${$$<time.txt>}$$

また、下のtime.txtをダウンロードしliteconfig/common/macros配下に置きます。(txtファイルの中身をコピペし自分でファイルを作ってリンクさせるとかでもいいです。)

③下のコードを「朝」ボタンにコピペします。

$${if(@timetype=0);echo(/time set 0);else;echo(/ptime set 0);endif;@#time=1;for(#m,0,1);#n=(#m*1500)+21000;setlabel(time%#m%,%#n%);next;for(#m,2,4);#n=(#m*1500)-3000;setlabel(time%#m%,%#n%);next}$$

④下のコードを「昼」ボタンにコピペします。

$${if(@timetype=0);echo(/time set 6000);else;echo(/ptime set 6000);endif;@#time=2;for(#m,0,4);#n=(#m*1500)+3000;setlabel(time%#m%,%#n%);next}$$

⑤下のコードを「夕」ボタンにコピペします。

$${if(@timetype=0);echo(/time set 12000);else;echo(/ptime set 12000);endif;@#time=3;for(#m,0,4);#n=(#m*1500)+9000;setlabel(time%#m%,%#n%);next}$$

⑥下のコードを「夜」ボタンにコピペします。

$${if(@timetype=0);echo(/time set 18000);else;echo(/ptime set 18000);endif;@#time=4;for(#m,0,4);#n=(#m*1500)+15000;setlabel(time%#m%,%#n%);next}$$

⑦目盛りを表示するラベルの内部名をそれぞれ「time0」「time1」「time2」「time3」「time4」にします。

⑧Tick時間などを表示するラベルの内部名をtimeにします。

⑨ptime切り替えボタン(動画内では「⇆」)のコード入力画面に行き通常マクロでは無く条件付きマクロの方へ行き画像の通りに入力します
(シングルのみの想定で切り替えが必要ない場合はこの動作をしなくて大丈夫です)

コピペ用
  $${setlabel(timetype,/time);@timetype=0}$$   
  $${setlabel(timetype,/ptime);@timetype=1}$$

⑩ptimeを使用しているかを表示する用のラベルの内部名をtimetypeにします。

各ボタンを押して動作してれば完了です。
動作しない場合は、グローバル変数の重複などを確認してください。
ラベルがうまく表示されない場合は内部名やバインディングを確認してください。

2.コードの仕組み

コードの構造は大まかにこんな感じです。(かなりややこしい作りです)

ボタンを押すとその時間帯に変更され、ラベルも時間帯に合わせて変更される。
(昼の場合:時間は6000Tickに、目盛りが3000〜9000の幅に設定される)

timeというグローバル変数が変化する
(ここら辺の仕組みは下記)

スライダーの値とtimeの値を組み合わせて/time setコマンドに値を入れる。

ラベルにセットした時間を表示させる。

流れを見ても分かりづらいと思います。(すみません、、)
簡単な図を書いたので、先に細分型時間設定の仕組みを解説してからコードの解説へ行きます。

図の左は私が以前作ったマクロの中に時間設定用のものを表しています。
スペースの関係で短いスライダーになってしまい調整しづらいもので、内部コードはスライダーから受け取った値をそのままコマンドに代入するというものでした。
そこで今回の機構はスライダーのほかに4つの時間帯のボタンを置き、どの時間帯が選択されているかの判断をして、昼ならば+3000Tick、夜ならば+15000Tickをスライダーの値に加えるという構造にしました。
こうすることで、6000という短い幅のスライダーでも全時間帯の調整が可能になりました。

この「◯の時間帯の設定ならば」を判断する基準として上のフローチャートでのtimeというグローバル変数が使われます。
「昼」のボタンが押されたらtimeに2という値が、「夜」のボタンを押すと4が代入され、スライダーのコードでtimeの値を読み取り、計算式に代入、計算することによって最終的な値が決定します。

ここからコードの解説に入ります。

①ptime切り替えボタンのコード

ここでは条件付きマクロを使ってトグル処理(切り替えのこと)を行っています。
トグル処理については、はやなささんのブログを参考にさせていただきました。

②時間帯を設定するボタンのコード(朝のボタン以外)

$${if(@timetype=0);
     echo(/time set 6000);
  else;
     echo(/ptime set 6000);
  endif;
  @#time=2;
  for(#m,0,4);
     #n=(#m*1500)+3000;
  setlabel(time%#m%,%#n%);
  next
}$$

コードは「昼」のボタンのもので、「夕」「夜」のボタンのコードも似たようなものになっています。「朝」のボタンのコードは少し複雑なので後で説明します。

1~5行目はif分岐でptimeを使うかを判断し、6000Tickに設定するtime setコマンド部、6行目はtimeを2に設定する部分、7~11行目はラベル名を3000、4500、6000、7500、9000に設定する部分になっています。

1~5行目はptimeを使わない場合は削除し、echo(/time set 6000);に書き換えてください(数字は各時間帯の中央値なので適宜変えてください)

7~11行目が少し難しいですね。
まずプログラミングの基本として、何度も同じような基準を繰り返すのはナンセンスで非効率とされています。タイプミスも起こりやすいですし記述量も多くなります。
仮にラベル名がtime0、time1・・time4のものに、それぞれ3000、4500・・9000と表示させる場合setlabel関数を使って書くと

$${setlabel(time0,3000);
   setlabel(time1,4500);
   setlabel(time2,6000);
   setlabel(time3,4500);
   setlabel(time4,9000)
}$$

というとんでもなく長いコードになります。今回は5つですが他のボタンにも同じ記述をするとなるとなかなかめんどくさいです。

そこで同じような処理をする場合、FOR文というものがよく使われます。

FOR文とはループ処理の1つで、文中にはカウンター変数(今回は#m)というFOR文特有の変数が使われます。この変数は、処理が終わるごとに1ずつ(設定すれば刻み値は変えれます)増えるという特性を持ちます。FOR文ではこのカウンター変数がどの値からどの値までカウントするかをあらかじめ決めることができ、最終値にカウンター変数が到達したらループ処理が終了します。

今回でいうとカウンター変数の開始地点は0、終了地点は4となり、3000など表示する値も1500ずつ増えていることがわかるので一次関数を使えばコードを簡略化出来ることがわかります。
簡略化したコードがこちらです。

for(#m,0,4);
     #n=(#m*1500)+3000;
  setlabel(time%#m%,%#n%);
  next

「昼」ボタンのコードの中間部と同じであることが分かると思います。
カウンター変数「i」が0から始まり、数式やラベルに含まれる全ての「i」に0が入ります。
この時の処理を言語化すると
『time0というラベルに表示する数値は0×1500+3000=3000、表示させたらカウンター変数を1増加させ、次はtime1という・・(中略)・・time4に数値を表示してカウンター変数が上限に達したのでループ処理を完了スル』
こんな感じでしょうか。

このようにFOR文を使えば少ない文字数で書けます。

別時間帯のコードでも「夕」のコードの場合は3000の部分(一次関数の切片部分)を6000増やして9000、「夜」のコードの場合はさらに6000増やして15000と1箇所変えるだけで対応できます。

③「朝」のボタンのコード

$${if(@timetype=0);
    echo(/time set 0);
   else;
    echo(/ptime set 0);
   endif;
   @#time=1;
   for(#m,0,1);
     #n=(#m*1500)+21000;
     setlabel(time%#m%,%#n%);
   next;
   for(#m,2,4);
     #n=(#m*1500)-3000;
     setlabel(time%#m%,%#n%);
   next
}$$

こちらも1~5行目はptimeのif分岐なので使わない場合は削除し、echo(/time set 0);に書き換えてください。
「朝」のボタンのコードは少し特殊でFOR文が2つあります。(ループ回数が少ないとFOR文の恩恵は少なくなるんであまり良い記述ではない)
これは目盛りの表示が21000、22500、0、1500、3000と途中で0を跨ぐことになってしまい1つのFOR文では処理できないからです。
(前2つのラベルはFOR文を使わずそのまま記述し、あとの3つのラベルでFOR文を使うとかでもいいと思います)

ラベルの表示を増やす場合
ラベルの数を増やした上で、FOR文のカウンター変数を増やすことで変更できます。
ラベルの数値の間隔を変更する場合
計算式の一次関数の係数部分(×1500の数字部分)を変えることで刻み値を変更できる。

④スライダーのコード(time.txt内部)

if(%@#time%=1)
    if(%@#timebar%<3000)
        #t=@#timebar+21000
    else
        #t=@#timebar-3000
    endif
else
    #t=@#timebar+((@#time*6000)-9000)
endif
if(@timetype=0)
    echo(/time set %#t%)
else
    echo(/ptime set %#t%)
endif
setlabel(time,%#t%(%DAYTIME%))

スライダーのコードでは時間帯ボタンで設定したtimeを受け取り、2ならば3000Tick(6000×2−9000)、3ならば9000Tick(6000×3−9000)、4ならば15000Tick(6000×4−9000)のTick時間に、スライダーで設定された0〜6000の値を加え、その合算値tをtime setコマンドに代入しています。

ただし1(時間帯が朝)の場合はそのままだとスライダーの値が0の時にコマンドに入れる値がマイナスになってしまいます。
これを回避するためにifで分岐させています。

また時間をセットした後、変数tに入っているTick時間を表示させると共に、DAYTIME(現在Tick時刻を24時間表記で出力する定数)を%%で囲うことで数値としてラベルに表示しています。

timeの数を増やした場合(時間帯を細かくした場合)
スライダー内の数式をよく見ると(時間帯の幅のTick時間)×(timeの値)−(X)=(各時間帯の目盛りラベルの最小値)になっています。
各時間帯の目盛りラベルの最小値とは昼ならば3000、夕ならば9000といった数値です。
Xの値は調整用なので左辺と右辺が等しくなるように調整してください。
ただし合算値tが負の値にならないように注意してください。

3.使っている変数・グローバル変数名一覧

下がこの機能全体で使われた変数名一覧です。
変数名重複のチェック等にお使いください。

@#time、@#timebar、@tymetype、#m、#n、#t


三相以上切り替え機構

三相以上の切り替えとは3つ以上の状態・コマンドを切り替えて実行できるという意味です。
二相の切り替えとはオンオフ機能のことでいわゆるトグル機能のことです。

三相以上の切り替えが可能になることで

  • クリエイティブモード・サバイバルモード・スペクテイターモードの切り替え

  • 3種類以上のツールの切り替え

  • コマンドモードの切り替え(他のコマンドでも同じボタンを使用したい場合に使用)

などができます。
三相切り替えの解説ではクリエイティブモードの切り替えを例に出します。
その後、作例としてコマンドモード切り替えなどを紹介したいと思います。

構造の仕組み

$${&chat[0]=リリースしたゲームです。;
   &chat[1]=Minecraftは;
   &chat[2]=20111118日に;
   if((%#i%>=0)&&(%#i%<2));
      #i=%#i%+1;
      echo("%&chat[%#i%]%");
   elseif(%#i%>=2);
      #i=0;
      echo("%&chat[0]%");
   endif
}$$

これはボタンを押すごとにチャットに表示される文章が変わるコードです。
試しにテキトーなボタンに割り振って押してみると1〜3行目の文章が順に表示されると思います。


このコードの流れを解説すると

ボタンが押される

カウンター変数「i」を1増加させる
ただし、「i」≧2の場合は0にする

配列変数「chat」のインデックス「i」番目に格納されている文章を表示する

1~3行目では切り替えたいコマンドや文章を配列変数に代入しています。

配列変数とは変数を複数個収納できるもので、インデックスとは配列変数に収納されている変数の席順のようなものです。ただしインデックスの番号は大抵、0から始まります

引用:プログラミングの「配列」とは?変数との違いや具体的な使い方をわかりやすく解説

なぜ配列変数を使うのかというと、3行目以降の処理でインデックス番号を使うととても楽だからです。

4~10行目ではカウンター変数「i」をカウントアップする処理です。ただし制限をつけないと「i」が永遠に増え続けます。
なので「i」が2以上のときにボタンがクリックされたら次の「i」の値は0にする、という処理をしています。
(ifの後ですでに0以上2未満と制限をつけているからelseifじゃなくelseでもよかったかも)
こうすることでループ化できるようにしています。

先ほど配列変数を使うと楽になると書きましたが、コードの中に&chat[%#i%]というインデックスが「i」になった配列変数「chat」が入っていることにお気づきでしょうか。

仮に「i」=0を代入したコードがこちらです。

 if((0>=0)&&(0<2));
      1=0+1;
      echo("%&chat[1]%");

if句で条件をパスした後、値をプラス1され、配列変数「chat」の1番目の文字列をechoコマンドに代入し実行しています。
「i」=1の時も同様な処理が行われます。
もし配列変数を使わずに同等の処理を行うとしたらif・elseifで「i」の状態ごとに分岐させなければならず、とても長ったらしいコードになります。

$${if((%@#i%>=0)&&(%@#i%<=1));
      @#i=%@#i%+1;
   elseif(%@#i%>1);
      @#i=0;
   endif;
   if(%@#i%=0);
      echo(Minecraftは);
   elseif(%@#i%=1);
      echo(20111118日に);
   elseif(%@#i%=2);
      echo(リリースしたゲームです。);
   endif
}$$

3つならまだ書けますが5個10個となるとめんどくさいです。
このように配列変数を使うことで打ち間違いを抑えたり、コードの編集がしやすくなったりします。

ここでゲームモードの切り替えのコードを考えてみましょう。
配列変数にはコマンド全文を入れるのではなくspectatorなど引数をいれるだけで、if句の方にコマンド部を入れればいいでしょう。

$${&chat[0]=survival;
   &chat[1]=creative;
   &chat[2]=spectator;
   if((%#i%>=0)&&(%#i%<2));
      #i=%#i%+1;
      echo("/gamemode %&chat[%#i%]%");
   elseif(%#i%>=2);
      #i=0;
      echo("/gamemode %&chat[0]%");
   endif
}$$

もし毎回のゲーム時に前の状態を引き継ぎたいのならカウンター変数「i」をグローバル変数化します。
また、このコードではボタンが押される前の状態は「0」と仮定して組んでいます。(なので一番最初のコードでは状態「1」から文章が始まるように代入しています)

例:着替え機能

ここで私がデモンストレーション用にTwitterに上げた「千寿さん作の大明神クローゼットの衣装を切り替えるボタン」の作成方法を基本例として解説します。動作の様子はこんな感じです。

ボタンのコードはテキストファイルを実行する処理のみで、テキストファイルの中身は下の通りです。

&a[16]=iron
&a[17]=chainmail
&a[18]=golden
&a[19]=diamond
if((%#b%>=0)&&(%#b%<15))
    #b=%#b%+1
    echo("/replaceitem entity @e[type=armor_stand,r=3] slot.armor.head daimyouzinclozet:item_armor%#b%_helmet")
    echo("/replaceitem entity @e[type=armor_stand,r=3] slot.armor.chest daimyouzinclozet:item_armor%#b%_chestplate")
    echo("/replaceitem entity @e[type=armor_stand,r=3] slot.armor.feet daimyouzinclozet:item_armor%#b%_boots")
elseif((%#b%>=15)&&(%#b%<19))
    #b=%#b%+1
    echo("/replaceitem entity @e[type=armor_stand,r=3] slot.armor.head %&a[%#b%]%_helmet")
    echo("/replaceitem entity @e[type=armor_stand,r=3] slot.armor.chest %&a[%#b%]%_chestplate")
    echo("/replaceitem entity @e[type=armor_stand,r=3] slot.armor.feet %&a[%#b%]%_boots")
elseif(%#k%>=19)
    #k=0
    echo("/replaceitem entity @e[type=armor_stand,r=3] slot.armor.head air")
    echo("/replaceitem entity @e[type=armor_stand,r=3] slot.armor.chest air")
    echo("/replaceitem entity @e[type=armor_stand,r=3] slot.armor.feet air")
endif

大明神クローゼットのアイテム名はdaimyouzinclozet:item_armorXX_(各部位)となっておりXXには1~15の数値が入ります。
つまりこの数値を三相以上切り替え機構を使って循環させることで衣装の切り替えが可能になります。

しかしコードは15以降も続いています。これはリソースパックを導入することで鉄や金装備にも衣装が追加されるのでコードを延長しました。
さらに配列変数を使うことでコードの短縮化もしています。(最初の代入部はこれのため)

そして#bが19のときにボタンを押したら0に戻る処理と同時に、アーマースタンドの衣装欄を空気に置換し、アーマースタンドが何も装備していない状態にする処理をしています。

ここでは対象物をアーマースタンドにし、ワールド内全部のアーマースタンドが置き換わることを防ぐため半径rを設定していますが、対象物を自分(@p)にすることもできます。(使うとしたら@pがいいかも。半径を設定しているとはいえマルチでアーマースタンドの装備置換は怖い。。)

仕組みの理解も深まってきたところで、もう少し難しい作例を紹介したいと思います。

発展例:方向キーに複数コマンドを配置

動作の様子はこんな感じです。

これは私が前に作成した「↑↓→←キー」+「何かのキー」を押すことでコマンドの内容がstackだったり、shiftだったりと変わるという機構を改良したものです。
前機構は同時に押すボタンを変えればコマンドが変わるというシンプルなものでしたが、どのボタンを押せばstackなのかが分かりずらく、またstackやshiftのマス数が1で固定だったため、中距離には向かず何回もボタンを押す必要があるなど問題点がありました。(マス数とは//stack 1の1の部分のことです)

今回の機構はextend、contractのコマンドを削除しmove、shift、stackの3種に絞り、同時押しキー判別方式から先ほどの三相切り替え機構を使用したものに変更しています。また、この切り替えはctrlを押しながら「←」、「→」キーでできます。
また、ctrlを押しながら「↑」、「↓」キーでマス数を変更できます。

さらに前機構は方向引数にrやfなどの相対方角を使っていたため下や上を向くと機能しませんでした。これを改良し下を向いても機能するようにしました。(仕組みも解説します)

 1.導入方法

①下の写真のように「↑」ボタンの条件付きマクロに入力します

↓ 真の時の処理コード ↓
$${inc(@#k);if(@#k=0);@#num=1;else;@#num=5*(%@#k%);endif}$$



②下の写真のように「→」ボタンの条件付きマクロに入力します
(@#keyの値に注意)

↓ 真の時の処理コード ↓
$${&command[0]=move;&command[1]=shift;&command[2]=stack;if((%@#type%>=0)&&(%@#type%<2));inc(@#type);else;@#type=0;endif;@&label=//%&command[%@#type%]%}$$



③下の写真のように「↓」ボタンの条件付きマクロに入力します
(@#keyの値に注意)

↓ 真の時の処理コード ↓
$${if(@#k !=0);dec(@#k);if(@#k=0);@#num=1;else;@#num=5*(%@#k%);endif;endif}$$



④下の写真のように「←」ボタンの条件付きマクロに入力します
(@#keyの値に注意)

↓ 真の時の処理コード ↓
$${&command[0]=move;&command[1]=shift;&command[2]=stack;if((%@#type%>0)&&(%@#type%<=2));dec(@#type);else;@#type=2;endif;@&label=//%&command[%@#type%]%}$$


⑤下のコードを「PAGEUP」ボタンの通常マクロに入力します。

$${@#key=4;$$<command.txt>}$$

⑥下のコードを「PAGEDOWN」ボタンの通常マクロに入力します。

$${@#key=5;$$<command.txt>}$$

⑦下のtxtファイルをダウンロードしliteconfig/common/macros配下に置きます。(txtファイルの中身をコピペし自分でファイルを作ってリンクさせるとかでもいいです。)

⑧ゲーム画面(キーボードのキーバインド画面→左上にあるマクロ設定一覧をキーからGUIに変更することでゲーム画面の編集に入れます)にラベルを2つ設置し、1つはラベル名を%%、バインディングを@&labelに、もう一つはラベル名を%%に、バインディングを@#numにします。

⑨イベントマクロの「on Join Game」に下のコードを追加します。

$${@#k=0;@#num=1;@#type=0;@&label=//move}$$


各ボタンを押して動作してれば完了です。
動作しない場合は、グローバル変数の重複などを確認してください。
ラベルがうまく表示されない場合は内部名やバインディングを確認してください。

 2.コードの仕組み(WorldEditの場合)

コードの構造は大まかにこんな感じです。

↑、↓などの方向キーを押すと@#keyというグローバル変数に値が代入される

@#typeが4未満ならば、&f1という配列変数の値と現在の方角とが一致した場合のインデックスを変数#f2に渡す

#f2の値と@#keyの和を4で割った時の余りを変数#f3に渡す

配列変数&f1の#f3番目の値を取得し小文字に変換した後、変数&dirに代入する

@#typeが5以上の場合は配列変数&f1の@#key番目の値を取得し変数&dirに代入する

配列変数&commandの@#type番目の値を取得し、最終行のコマンド実行部に代入する(実行したいコマンドのタイプ)

@#numの値を最終行のコマンド実行部に代入する(引数のマス数部分)

先ほどの変数&dirを最終行のコマンド実行部に代入する(引数の方角部分)

配列変数&optの@#type番目の値を取得し、最終行のコマンド実行部に代入する(-aや-sなどのオプション部分)

command.txtの中身                   
&command[0]=move
&command[1]=shift
&command[2]=stack
&opt[0]=-s
&opt[2]=-as
&f1[0]=N;
&f1[1]=E;
&f1[2]=S;
&f1[3]=W;
&f1[4]=u;
&f1[5]=d;
if(%@#key%<4)
INDEXOF(&f1[],#f2,%DIRECTION%)
  #f3=(%#f2%+%@#key%)-(((%#f2%+%@#key%)/4)*4)
  LCASE(%&f1[%#f3%]%,&dir)
else
  &dir=%&f1[%@#key%]%
endif
echo("//%&command[%@#type%]% %@#num% %&dir% %&opt[%@#type%]%")

出てくる変数名が多すぎてわけわかんないですね。。(これでも少なくした方です)
順を追って説明します。


まずmove、shift、stackのコマンドを構成している部品ごとに分解します。

shiftのみ-aなどのオプションがないだけで構成パーツはほぼ同じだと分かります。
ならば各パーツのパターンを各々配列変数に格納し、例えばインデックスが0の時はmoveに使うパーツ、1の時はshiftに使うパーツのようにインデックスを指定すればコマンド実行コードを共有化できるはずです。

それがcommand.txtの1~5行目にあたる部分です。
6行目からも同じような記述が続きますが、これらは方角指定用の値で後ほど使うので今はスルーします。

1~3行目は実行したいコマンドのタイプを配列変数「&command」に代入しています。もしコマンドの種類を増やしたい場合はここで調整します。
4、5行目は各コマンドで使うオプション(-aや‐sのこと)を配列変数「&opt」に代入しています。代入する順番は上のコマンドのタイプの順番と連動しているので注意です。(そのためインデックス1番はshift用なので空欄になっています)

コマンドのタイプの切り替えは、ctrl+「←」or「→」キーで行っています。
ctrl+「→」を押した場合(真のとき)のコードが下のものです。

$${&command[0]=move;
   &command[1]=shift;
   &command[2]=stack;
   if((%@#type%>=0)&&(%@#type%<2));
     inc(@#type);
   else;
     @#type=0;
   endif;
   @&label=//%&command[%@#type%]%
}$$

お気づきだと思いますがこのコードは三相切り替え構造を採用しています。
9行目の@&labelはゲーム画面に、「現在使用しているコマンドのタイプ」を表示させる用のグローバル変数ですがsetlabel関数でも大丈夫です。setlabel関数を使用する場合はイベントマクロの「on Join Game」の方も変更してください。

「←」キーのものも基本的には同じですが、if条件の不等号の向きが違うのとdec関数を使っている点が「→」キーのものとの違いです。

command.txtの6~11行目は12~18行目で使用するための配列変数を作成しています。
では12~18行目は具体的に何をやっているのかというと、「方角の相対化」をしています。

WorldEditはstackコマンドなどの引数に方角(n、e、s、w)または方向(front、right、back、left、up、down)を使うことが出来ます。
ですが両方の引数にもメリット・デメリットがあります。

引数に方角を使うメリット 
 上や下を向いても方角を指定すればその方角にコマンドが働く。
引数に方角を使うデメリット
 コマンドを働かせたい方角をいちいち確認する必要があるため方向(↑→↓←)に割り振ったときに直感的に使いづらい。

引数に方向を使うメリット
 自分から見て上下左右前後にコマンドが働くため方向キーとの相性がいい
引数に方向を使うデメリット
 上や下を向いた場合、コマンドがうまく機能しない。

このように引数に方向を使うと下を向いたときにうまく動かなくなり、方角を使ったとしても方向キーで操作するには確認工程が多すぎて使い物になりません。
逆をいえば方角の確認工程を自動化さえすれば下を向いてもコマンドが動作できるため、次はこの自動化について解説していきます。

まず私が考えたのは方角にそれぞれ数値を割り振ることです。数値化することで計算式が使えるので色々と工夫が利くからです。
具体的には配列変数を利用し、インデックスの0番を北、1番を東、2番を南、3番を西のように時計回りに代入しました。
これがcommand.txt6~11行目でやっていることです。(インデックス0~3番までが大文字の理由と上方向と下方向も代入している理由は後程)

次に「今見ている方角」を数値化して計算に使える形にする処理もします。
先ほどは方角自体を数値化したわけですが、今度は「今見ている方角」がどの数値になるのかを判断する処理をするわけです。下がそのコードです。

INDEXOF(&f1[],#f2,%DIRECTION%)

INDEXOF関数は配列の中に一致する文字があれば、それが格納されているインデックスを返すという機能を持ちます。
第一引数に探索先の配列変数(&f1には方角)、第二引数に一致した値が格納されているインデックスの番号(#f2)、第三引数に探索したい文字(定数DIRECTIONは今見ている方角をN、E、S、Wという英大文字で出力します)を指定することで関数が機能します。
つまり%DIRECTION%にN(北)がはいれば0が#f2に入るということです。


さらに、方向キーにも数値を割り振っておきます。これも計算で使うためです。
この割り振りは各ボタンの条件付きマクロの偽の場合の処理で行っています。

command.txtが実行される前の部分を見てもらと「@#key」に数値が代入されています。「↑」は0、「→」は1、「↓」は2、「←」は3、「pageup」は4、「pagedown」は5が代入されます。
ここでポイントなのは方角の数値と同期させる点です。

ここで具体的なシチュエーションを出して理解を深めましょう。

Aさんが北を向いているとします。
Aさんは向かって右にコマンドを動作させたいです。
なのでAさんは「→」ボタンを押しました。
この時「→」が表す方角は何でしょう。

これはもちろん東です。
ではAさんが90度時計回りをして東を向いたらどうでしょうか。
この時「→」が表す方角は南になります。

つぎはこの文章内の方角を先ほど数値化したものに置き換えてみましょう。

Aさんが0を向いているとします。
Aさんは向かって右にコマンドを動作させたいです。
なのでAさんは1を代入しました。
この時が「→」表す方角は1となります。

Aさんが90度時計回りをして1を向きました。
Aさんは向かって右にコマンドを動作させたいので1を代入しました。
この時「→」が表す方角は2となります。

これでお分かりいただけたことでしょう。押したときに代入される値と現在見ている方角の数値の和がコマンドを動作させる方角の数値となるのです。

ただし、これは和が3を超えない場合の話です。
下の表は見ている方角とボタンを押したときに代入される値の和の関係性を表しています。横には本来、コマンドが実行されるべき方角(数値化された値)が載っています。

こうしてみると、2値の合算で判定するにも3を超える値が出てくるためこれをうまく処理しなければなりません。

ここでこの表に合算値を4で割った際の余りの値を追加してみます。(剰余計算)

するとどうでしょうか、値が一致しました。
理系科目が好きな方は「2つの値の合算値である10進数を、4進数に変換した際の1の位に着目する」といえばわかってもらえると思います。

これをマクロmodで表現するとこうなります。(@#keyはボタンを押したときに代入される値、#f2は見ている方角の数値)

#f3=(%#f2%+%@#key%)-(((%#f2%+%@#key%)/4)*4)

マクロmodは数値の小数点以下は切り捨てるので(%#f2%+%@#key%)/4)で商が出ます。これに再度4をかけることで(割られる数-余り)が求められ、これを割られる数から引けば余りが出るというわけです。

こうして出た余りの値はインデックスの番号として使われ、配列変数&f1からそのインデックス内の値を取り出すわけですが、取り出した値はすべて英大文字です。
これはINDEXOF関数で判定する際、英小文字だと同じ文字だと判定されないので、配列変数に代入する際、英大文字にしたわけです。

しかしWorldEditの引数は英小文字しか認識しないので変換する必要があります。そこで、LCASE関数を使うことで小文字に変換しています。
これらの処理はcommand.txtの15行目でまとめて行っています。

command.txtの16行目には@#typeが4以上つまり、「pageup」、「pagedown」が押された場合の処理が書かれています。

stackコマンドの引数のうちupとdownは上や下を向いていても機能するので、変数節約も兼ねて&f1の4・5番インデックス内に値を収納しています。
それを呼び出す記述がcommand.txtの16行目にあたる下のコードです。

&dir=%&f1[%@#key%]%


コードの最終行、コマンド実行部について説明します。(かなり長くなってしまった。。)
ここでは今まで設定した変数を代入しコマンドを実行させています。

echo("//%&command[%@#type%]% %@#num% %&dir% %&opt[%@#type%]%")

ここで@#numに関しての説明がまだなので解説します。
このグローバル変数の役割は序盤の表でも書かれていた通りコマンドを実行するマス数の制御です。

マス数の調整はctrl+「↑」or「↓」キーで行っています。
ctrl+「↑」を押した場合(真のとき)のコードが下のものです。

$${inc(@#k);if(@#k=0);@#num=1;else;@#num=5*(%@#k%);endif}$$

@#kは制御用変数、@#numはcommand.txtに代入される値です。
このボタンを押した場合の動作を説明すると、まず@#kが1増加し、if分岐にかけられます。@#k=0の場合は@#num=1、それ以外は@#kを5倍した数値を@#numに代入しています。(実際は初期状態が@#k=0つまりボタンを押した瞬間@#k=1になるので真の場合の処理はいらないんですけど保険です)

つまりボタンを押すごとに1、5、10、15、20・・と続きます。
これは私がこれくらいの幅が使いやすいかなと思って設定したので幅はご自由に。
一方、「↓」キーのコードは押す度に@#kが減少する処理が書かれているので@#kがマイナスにならないように一番始めに条件分岐で制御しています。

以上がWorldEdit用のコマンド解説です。次はFastAsyncWorldEdit(以下FAWE)用のコード解説です。


 3.コードの仕組み(FAWEの場合)

FAWEを導入されている場合、stackやmoveコマンドの引数に8方位(ne 、
se、sw、nw)が使えます。
なので次は8方位に対応したコードの解説をします。

まず、下のcommand2.txtをダウンロードし、liteconfig/common/macros配下に置きます。

そして、「←」、「→」、「↑」、「↓」、「PAGEUP」、「PAGEDOWN」すべての条件付きマクロの偽の場合のコード欄にあるcommand.txtを
command2.txtに変更します。

これで導入は完了です。続いてはコードの内容の解説です。
下のコードはcommand2.txtのものです。

&command[0]=move
&command[1]=shift
&command[2]=stack
&opt[0]=-as
&opt[2]=-as
&f1[0]=N
&f1[1]=E
&f1[2]=S
&f1[3]=W
&f1[4]=u
&f1[5]=d
&f4[0]=ne
&f4[1]=se
&f4[2]=sw
&f4[3]=nw
if(%@#key%<4)
  if(%@#type%=1)
    INDEXOF(&f1[],#f2,%DIRECTION%)
    #p=1
  elseif((%CARDINALYAW%>=23)&&(%CARDINALYAW%<=67))
    #f2=0
    #p=0
  elseif((%CARDINALYAW%>=113)&&(%CARDINALYAW%<=157))
    #f2=1
    #p=0
  elseif((%CARDINALYAW%>=203)&&(%CARDINALYAW%<=247))
    #f2=2
    #p=0
  elseif((%CARDINALYAW%>=293)&&(%CARDINALYAW%<=337))
    #f2=3
    #p=0
  else
    INDEXOF(&f1[],#f2,%DIRECTION%)
    #p=1
  endif
    #f3=(%#f2%+%@#key%)-(((%#f2%+%@#key%)/4)*4)
  if(%#p%=0)
    &dir=%&f4[%#f3%]%    
  else  
    LCASE(%&f1[%#f3%]%,&dir)
  endif
else
  &dir=%&f1[%@#key%]%
endif
echo("//%&command[%@#type%]% %@#num% %&dir% %&opt[%@#type%]%")

かなり行数が増えていますかやっていることは単純です。
8方位に増えたからといって方向キーは4つで固定、つまりNE(北東)を向いているときはNE、SE、SW、NWの方向にしかコマンドが働かず、他の4方位は無視できるということです。
つまり、方角算出の計算式は同じものを使い、「N、E、S、W」用の配列変数のほかに「NE、SE、SW、NW」用の配列変数を作ればいいということです。

ただここで1つ問題があり、マクロmodには斜め方位を検出する定数がありません。
なのでif・elseif文とCARDINALYAWを使い、無理やり条件分岐をさせ、見ている方角に対応する数値(NEなら0など)を#f2に代入しています。

そして、斜め方位の場合は#p=0、東西南北の場合(22・23行目)は#p=1になる処理をしているのは、方角を格納されている配列が別グループのため処理内容が異なるからです。(if(%#p%=0)の部分)

最後にコード中腹でなぜif(%@#type%=1)と分岐しているのかを解説します。
これはshiftコマンドが斜めに対応してしないためです。だからその下の8方位判定分岐に行く前にif分岐で4方位の処理に流す必要があるのです。

 4.使っている変数・グローバル変数名一覧

下がこの機能全体で使われた変数名一覧です。
変数名重複のチェック等にお使いください。

WorldEdit編
@#num、@#k、@#type、@&label、@#key、&f1、#f2、#f3、&command、&dir、&opt
FAWE編
@#num、@#k、@#type、@&label、@#key、&f1、#f2、#f3、&command、&dir、&opt、&f4、#p


横向きhcyl生成

動作の様子はこんな感じです。

これは//hcylで生成される空洞の円を90度横向きに生成できるコードです。
現在立っている場所を円の中心とし、向いている方向に空洞の円を生成します。入力が求められる画面が出てきますがこれは円の半径を決定する用です。
汎用性の面を考えて幅は1マスにしていますが、生成後は円がスッポリ収まるような選択状態になっているので伸ばしたい場合はstackコマンドを使ってください。

1.導入方法

下のコードをお好きなボタンに割り当ててください。

$${#r=$$?;&a="%XPOS%,%YPOS%,%ZPOS%";echo(//pos1 %&a%);wait(2t);echo(//pos2 %&a%);wait(2t);echo(//outset %#r%);if((%DIRECTION%=="S")||(%DIRECTION%=="N"));  &d=x; echo(//contract %#r% n); echo(//contract %#r% s);wait(2t);else;  &d=z; echo(//contract %#r% e); echo(//contract %#r% w);  wait(2t);endif;echo(//g -ch 1 %&d%^2+y^2<=(%#r%+0.5)^2);echo(//re 1 %ITEMIDDMG%)}$$

2.コードの仕組み

コードの構造は大まかにこんな感じです。

ボタンを押すと半径を入力する画面が現れ、変数#rに格納する。

現在の座標に//pos1、//pos2をセット
(うまく選択できていないときがあったのでwaitで遅延させています)

//outsetで全方向に#rマス分拡大し自分の見ている方角には#rマス縮小する

自分の見ている方角が南北なら変数&dにxを、東西ならyを代入
(この後の円の方向の決定変数として使用)

//gコマンドの数式に半径と円の方向を代入し実行

このコードではWorldEditのgコマンドを使っています。

gコマンドとは特定の数式を使うことで、//pos1、//pos2の選択範囲内の数式に合致する部分にブロックを置くというものです。

皆さんも中学や高校で直線や曲線の方程式を習ったと思います。
あの公式がgコマンドで使うことのできる数式になります。

ただ、Minecraftの世界はボクセル状なので曲線の式を=を使ったままgコマンドに入れると生成できません。(曲線上の座標はほとんど分数・小数だから)
そのため曲線の式は領域を表す不等式を使って生成させます。

領域・不等式とはなんぞやって方はだんぼーるさんの記事をご覧ください。
また、数式内で使うことのできる演算子、定数等はWorldEditの式の構文をご覧ください。

ただ、gコマンドはバニラ以外のブロックを受け付けないようで(私の環境ではそうでした)一旦バニラ石で生成させてその後範囲内の石を手持ちブロックに置き換えています。
一旦挟むブロックはお好きなものに変えてください。

あと、数式の右辺が半径+0.5になっているのは//hcylの円と比較したとき若干小さかったので調整した関係です。

もし、空洞の円ではなく中まで詰まった円を生成したい場合は
//g -chを//g -cにしてください。


かぼちゃの着脱ボタン

下のコードを貼り付けることで、ボタンを押すごとにかぼちゃが着いたり外れたりします。
これはcocricotやMiniaturiaにはかぼちゃをつけると画面に三分割線という構図決定に便利な線が表示される機能があるのでこのボタンがあると着脱が便利になります。

$${if(@pumpkin=0);echo("/replaceitem entity @p slot.armor.head pumpkin");@pumpkin=1;else;echo("/replaceitem entity @p slot.armor.head air");@pumpkin=0;endif}$$

コマンドの中身は単純なものなので解説は省きます。


視点先replase

視点を合わせるだけで手持ちと入れ替えたり、同じブロックなら方角サイクルをしたりしてくれるマクロです。
MiniaTuria版とcocricot版の2種類があります。
動作の様子はこんな感じです。

1.1.導入方法(MiniaTuria版)

正しく検知できるのはフルブロック、階段系全種、アーチ全種、オーニング、ハーフ、レイヤー、縦階段、縦ハーフ、細い横棒、太い横棒だけです。それ以外はお手数ですが手動でお願いします。

①下の2ファイルをliteconfig/common/macros配下に置きます。
 中をコピペしゲーム内でファイルを作ってもOKです(その際はファイル名に気を付けて)

②機能を割り当てたいボタンに以下のコードを入れます。

$${$$<mtrepl.txt>}$$

1.2.導入方法(cocricot版)

正しく検知できるのはフルブロック、階段系全種、アーチ全種、オーニング、ハーフ、レイヤー、縦階段、縦ハーフ、プレート、屋根系、車輪、shiftでずらせる棚・ショーケースだけです。それ以外はお手数ですが手動でお願いします。

①下の2ファイルをliteconfig/common/macros配下に置きます。
 中をコピペしゲーム内でファイルを作ってもOKです(その際はファイル名に気を付けて)

②機能を割り当てたいボタンに以下のコードを入れます。

$${$$<crepl.txt>}$$

2.コードの中身(解説なし)

mtmeta.txtの中身

//default side
&dir1[0]=E
&dir1[1]=W
&dir1[2]=S
&dir1[3]=N

//for vwall
&dir2[0]=S
&dir2[1]=W
&dir2[2]=N
&dir2[3]=E

//for vstairs&awning
&dir3[0]=N
&dir3[1]=E
&dir3[2]=S
&dir3[3]=W

//exec command
&direction=$$[1]
#side=$$[2]
&itemcode=$$[3]
&item=$$[4]
#durability=$$[5]

#meta=%#durability%

IFMATCHES(%&item%,"(_stairs|_arch|_steps|_2hbar)")
	SPLIT(".",%&itemcode%,&variant[])
	POP(&variant[],&string)
	INDEXOF(&dir1[],#dir,%&direction%)
	IF(%&string%=="a")
		#meta=((-1)*%#dir%)+3+(%#side%*4)
	ELSEIF(%&string%=="b")
		#meta=((-1)*%#dir%)+3+(%#side%*4)+8
	ELSE
		#meta=%#dir%+(%#side%*4)
	ENDIF
ENDIF

IFMATCHES(%&item%,"_vstairs")
	SPLIT(".",%&itemcode%,&variant[])
	POP(&variant[],&string)
	INDEXOF(&dir3[],#dir,%&direction%)
	IF(%&string%=="b")
		#meta=%#dir%+4
	ELSEIF(%&string%=="c")
		#meta=%#dir%+8
	ELSEIF(%&string%=="d")
		#meta=%#dir%+12
	ELSE
		#meta=%#dir%
	ENDIF
ENDIF

IFMATCHES(%&item%,"_awning")
	SPLIT(".",%&itemcode%,&variant[])
	POP(&variant[],&string)
	INDEXOF(&dir3[],#dir,%&direction%)
	IF(%&string%=="a")
		#meta=%#dir%
	ELSEIF(%&string%=="c")
		#meta=%#dir%+8
	ELSE
		INDEXOF(&dir1[],#dir0,%&direction%)
		#meta=%#dir0%+(%#side%*4)
	ENDIF
	IFMATCHES(%&item%,"_arm")
		#meta=%#dir%+(%#side%*4)
	ENDIF
ENDIF

IFMATCHES(%&item%,"_half")
	IFMATCHES(%&itemcode%,"meta")
		#meta=%#durability%+(%#side%*8)
	ELSE
		#meta=((-1)*%#side%)+1
	ENDIF
ENDIF

IFMATCHES(%&item%,"_slab")
	#meta=%#durability%+(%#side%*8)
ENDIF

IFMATCHES(%&item%,"_hpole")
	INDEXOF(&dir1[],#dir,%&direction%)
	#dir0=((-1)*%#dir%)+3
	#meta=(((15*%#dir0%)+5)/10)+(%#side%*6)
ENDIF

IFMATCHES(%&item%,"_vwalls")
	SPLIT(".",%&itemcode%,&variant[])
	POP(&variant[],&string)
	INDEXOF(&dir2[],#dir,%&direction%)
	IF(%&string%=="a")
		#meta=%#dir%
	ELSEIF(%&string%=="b")
		#meta=%#dir%+4
	ENDIF
ENDIF

@#meta=%#meta%

mtrepl.txtの中身

&dir1[0]=N
&dir1[1]=E
&dir1[2]=S
&dir1[3]=W

&dir2[0]=north
&dir2[1]=east
&dir2[2]=south
&dir2[3]=west
&dir2[4]=ne
&dir2[5]=se
&dir2[6]=sw
&dir2[7]=nw
&dir2[8]=x_n
&dir2[9]=z_e
&dir2[10]=x_s
&dir2[11]=z_w

&dir3[0]=bottomFalse
&dir3[1]=topFalse
&dir3[2]=True
&dir3[3]=False

IF((%HITID%=%ITEM%)&&(%HITDATA%=-1*%DURABILITY%))
	&block1=%ITEM%
	&block2=%ITEM%
	&itemcode1=%ITEMCODE%
	&itemcode2=%ITEMCODE%
	&item1=%ITEM%
	&item2=%ITEM%
	#durability1=%HITDATA%
	#durability2=-1*%DURABILITY%
	&side=%HIT_HALF%%HIT_UP%
	INDEXOF(&dir3[],#side1,%&side%)
	#side1=%#side1%-((%#side1%/2)*2)
	&direction=%HIT_FACING%%HIT_POSITION%
	
	IFMATCHES(%&item1%,"(_stairs|_arch|_steps|_vstairs|awning|_vwalls|_2hbar|_hpole)")
		INDEXOF(&dir2[],#dir,%&direction%)
		#dir=%#dir%-((%#dir%/4)*4)
		IFMATCHES(%&item1%,"(_basket|_middle|_long|_frill_arm)")
			IF(%#dir%>=2)
				#dir=%#dir%+2-4
			ELSE
				#dir=%#dir%+2
			ENDIF
		ENDIF
		&direction1=%&dir1[%#dir%]%
		IF(CTRL)
			#side2=(-1*%#side1%)+1
			&direction2=%&direction1%
		ELSE
			#side2=%#side1%
			IF(%#dir%=3)
				#dir=%#dir%-3
			ELSE
				#dir=%#dir%+1
			ENDIF
			&direction2=%&dir1[%#dir%]%
		ENDIF
	ENDIF
	
	IFMATCHES(%&item1%,"(_half|_slab)")
		#side2=(-1*%#side1%)+1
	ENDIF

	IFMATCHES(%&item1%,"_layers")
		#durability1=%HIT_LAYERS%-1
		IF(%#durability1%=7)
			#durability2=%#durability1%-7
		ELSE
			#durability2=%#durability1%+1
		ENDIF
		IFMATCHES(%&item1%,"_quarter")
			#durability1=%#durability1%+(4*%#side1%)
			IF(CTRL)
				#durability2=%#durability1%+(-8*%#side1%)+4
			ELSE
				IF(%#durability1%=3)
					#durability2=%#durability1%-3
				ELSE
					#durability2=%#durability1%+1
				ENDIF
			ENDIF
		ENDIF
	ENDIF
 
ELSE
	IF(CTRL)
		#side2=1
	ELSE
		#side2=0
	ENDIF
	&block2=%ITEM%
	&direction2=%DIRECTION%
	&itemcode2=%ITEMCODE%
	&item2=%ITEM%
	#durability2=-1*%DURABILITY%
	IFMATCHES(%&item2%,"_quarter_layers")
		#durability2=%#durability2%+(%#side2%*4)
	ENDIF
	IF(%HITID%="air")
		&block1="0" 
		&itemcode1="0"
		&item1="0"
		#side1=0
		&direction1=%DIRECTION% 
		#durability1=0
	ELSE
		key(swaphands)
		wait(3t)
		key(pick)
		wait(3t)
		&block1=%ITEM%
		&item1=%ITEM%
		&itemcode1=%ITEMCODE%
		#durability1=%HITDATA%
		&side=%HIT_HALF%%HIT_UP%
		INDEXOF(&dir3[],#side1,%&side%)
		#side1=%#side1%-((%#side1%/2)*2)
		&direction=%HIT_FACING%%HIT_POSITION%
		
		IFMATCHES(%&item1%,"(_stairs|_arch|_steps|_vstairs|awning|_vwalls|_2hbar|_hpole)")
			INDEXOF(&dir2[],#dir,%&direction%)
			#dir=%#dir%-((%#dir%/4)*4)
			IFMATCHES(%&item1%,"(_basket|_middle|_long|_frill_arm)")
				IF(%#dir%>=2)
					#dir=%#dir%+2-4
				ELSE
					#dir=%#dir%+2
				ENDIF
			ENDIF 
				&direction1=%&dir1[%#dir%]%
		ENDIF
		IFMATCHES(%&item1%,"_layers")
			#durability1=%HIT_LAYERS%-1
			IFMATCHES(%&item2%,"_layers")
				#durability2=%#durability1%
			ENDIF
			IFMATCHES(%&item1%,"_quarter")
				#durability1=%#durability1%+(%#side1%*4)
				IFMATCHES(%&item2%,"_quarter")
					#durability2=%#durability1%
				ENDIF
			ENDIF
		ENDIF
		setslotitem(0,%INVSLOT%-1)
		key(swaphands)
	ENDIF
ENDIF

exec("mtmeta.txt","1",%&direction1%,%#side1%,%&itemcode1%,%&item1%,%#durability1%)
#meta1=%@#meta%
exec("mtmeta.txt","1",%&direction2%,%#side2%,%&itemcode2%,%&item2%,%#durability2%)
#meta2=%@#meta%

echo(//re %&block1%:%#meta1% %&block2%:%#meta2%)

cmeta.txtの中身

//stairs
&dir1[0]=E
&dir1[1]=W
&dir1[2]=S
&dir1[3]=N

//vslab,arch
&dir2[0]=N
&dir2[1]=E
&dir2[2]=S
&dir2[3]=W

//plate
&dir3[0]=S
&dir3[1]=E
&dir3[2]=W

//exec command
&direction=$$[1]
#side=$$[2]
&item=$$[3]
#look=$$[4]
#durability=$$[5]

#meta=%#durability%
#unlook=(-1*%#look%)+1

IFMATCHES(%&item%,"(awning|_vslab|roof|shutter_)")
	IFMATCHES(%&item%,"(awning|_vslab|roof|_fringe|shutter_)")
		INDEXOF(&dir2[],#dir,%&direction%)
		#meta=(%#dir%*%#unlook%)+(((%#dir%+2)-(((%#dir%+2)/4)*4))*%#look%)
	ENDIF
	IFMATCHES(%&item%,"(_block|_rod|_stick)")
		#meta=%#durability%
	ENDIF
ENDIF

IFMATCHES(%&item%,"(_stairs|molding|_incline|stringer)")
	INDEXOF(&dir1[],#dir,%&direction%)
	#meta=%#dir%
	IFMATCHES(%&item%,"(_stairs|molding|stringer)")
		#meta=%#meta%+%#side%*4
	ENDIF
ENDIF

IFMATCHES(%&item%,"_arch")
	INDEXOF(&dir2[],#dir,%&direction%)
	#meta=(%#dir%*%#unlook%)+(((%#dir%+2)-(((%#dir%+2)/4)*4))*%#look%)
	IFMATCHES(%&item%,"(halftimber|window)")
		#meta=%#durability%
	ENDIF
ENDIF

IFMATCHES(%&item%,"_slab")
	#meta=%#side%+1
	IFMATCHES(%&item%,"(woodenbox|basket)")
		#meta=1
	ENDIF
	IFMATCHES(%&item%,"minecraft")
		#meta=%#durability%+(%#side%*8)
	ENDIF
	IFMATCHES(%&item%,"palm")
		#meta=(-1*%#side%)+10
	ENDIF
ENDIF

IFMATCHES(%&item%,"(wheel|_plate)")
	IF(%#side%=2)
		INDEXOF(&dir1[],#dir,%&direction%)
		#meta=(((%#dir%+4)-(((%#dir%+2)/4)*4))*%#unlook%)+(((-1*%#dir%)+5)*%#look%)
	ELSE
		#meta=%#side%
	ENDIF
	IFMATCHES(%&item%,"(minecraft|range|sink|mirror|hamburger)")
		#meta=%#durability%
	ENDIF
	IFMATCHES(%&item%,"roof")
		IF((%&direction%="N")&&(%#side%!=2))
			#meta=%#side%
		ELSEIF((%&direction%!="N")&&(%#side%!=2))
			INDEXOF(&dir3[],#dir,%&direction%)
			#meta=6+%#dir%+(%#side%*3)
		ENDIF
	ENDIF
ENDIF

IFMATCHES(%&item%,"wood_3board")
	SPLIT("_",%&item%,&array[])
	ARRAYSIZE(&array[],#size)
	POP(&array[],&string)
	INDEXOF(&dir1[],#dir,%&direction%)
	#dir=%#dir%/2
	IF(%#size%=3)
		#meta=%#dir%
	ELSEIF((%#size%=4)&&(%&string%="slab"))
		#meta=(%#dir%*2)+(-1*%#side%)+1
	ENDIF
ENDIF

IFMATCHES(%&item%,"glass_frame")
	INDEXOF(&dir1[],#dir,%&direction%)
	#meta=((%#dir%+2)-(((%#dir%+2)/4)*4))+(%#side%*4)
	IFMATCHES(%&item%,"pane")
		#meta=0
	ENDIF
ENDIF

IFMATCHES(%&item%,"(rustication|wallshelf_floating|wallshelf_chain|wallshelf_bracket)")
	INDEXOF(&dir2[],#dir,%&direction%)
	#meta=%#dir%+(%#side%*4)
	IFMATCHES(%&item%,"(_vslab|_plate)")
		#meta=(%#dir%*%#unlook%)+(((%#dir%+2)-(((%#dir%+2)/4)*4))*%#look%)
	ENDIF
ENDIF

@#meta=%#meta%

crepl.txtの中身

&dir1[0]=N
&dir1[1]=E
&dir1[2]=S
&dir1[3]=W

&dir2[0]=north
&dir2[1]=east
&dir2[2]=south
&dir2[3]=west
&dir2[4]=z
&dir2[5]=x

&dir3[0]=bottom
&dir3[1]=top
&dir3[2]=lower
&dir3[3]=upper
&dir3[4]=down
&dir3[5]=up

IF((%HITID%=%ITEM%)&&(%HITDATA%=-1*%DURABILITY%))
	#switch=0
	&block1=%ITEM%
	&block2=%ITEM%
	&item1=%ITEM%
	&item2=%ITEM%
	&look1=1
	&look2=1
	#durability1=%HITDATA%
	#durability2=-1*%DURABILITY%
	
	&side=%HIT_HALF%%HIT_SLAB%
	&hit_dir=%HIT_FACING%%HIT_AXIS%
	INDEXOF(&dir3[],#side1,%&side%)
	#side1=%#side1%-((%#side1%/2)*2)
	INDEXOF(&dir2[],#dir,%&hit_dir%)
	IFMATCHES(%&item1%,"(wheel|_plate)")
		#side1=2
	ENDIF
	IFMATCHES(%HIT_FACING%,"(up|down)")
		SPLIT("_",%HIT_FACING%,&array[])
		INDEXOF(&dir3[],#side1,%&array[0]%)
		#side1=%#side1%-((%#side1%/2)*2)
		INDEXOF(&dir2[],#dir,%&array[1]%)
	ENDIF
	#dir=%#dir%-((%#dir%/4)*4)
	&direction1=%&dir1[%#dir%]%

	IF(CTRL)
		#side2=(-1*%#side1%)+1
		&direction2=%&direction1%	
		IFMATCHES(%&item1%,"(wheel|_plate)")
			IF(%#side1%=1)
				#side2=2
				&direction2=%DIRECTION%
				#look2=0
			ELSE
				#side2=((-1*%#side1%)/2)+1
			ENDIF
		ENDIF
	ELSE
		#side2=%#side1%
		IF(%#dir%>=3)
			#dir=%#dir%-3
		ELSE
			#dir=%#dir%+1
		ENDIF
		&direction2=%&dir1[%#dir%]%

		IFMATCHES(%&item1%,"(_slab|_plate|wheel)")
			#side2=(((3*%#side1%)-2)*(%#side1%-1))/2
			IFMATCHES(%&item1%,"(wood_3board|roof)")
				#side2=%#side1%
			ENDIF
		ENDIF
	ENDIF    
	
	IFMATCHES(%&item1%,"_pile")
		#durability1=%HIT_LAYERS%-1
		IF(%#durability1%=7)
			#durability2=%#durability1%-7
		ELSE
			#durability2=%#durability1%+1
		ENDIF
	ENDIF

ELSE
	#switch=0
	IF(CTRL)
		#side2=1
	ELSE
		#side2=0
	ENDIF
	&block2=%ITEM%
	&direction2=%DIRECTION%
	&item2=%ITEM%
	#look2=0
	#durability2=-1*%DURABILITY%
	IFMATCHES(%&item2%,"(wheel|_plate)")
		#side2=(-1*%#side2%)+1
	ENDIF
	
	IF(%HITID%="air")
		&block1="0" 
		&item1="0"
		#side1=0
		#look1=1
		&direction1=%DIRECTION% 
		#durability1=0
	ELSE
		&block1=%HITID%
		&item1=%HITID%
		#durability1=%HITDATA%
		#look1=1
		&side=%HIT_HALF%%HIT_SLAB%
		&hit_dir=%HIT_FACING%%HIT_AXIS%
		INDEXOF(&dir3[],#side1,%&side%)
		#side1=%#side1%-((%#side1%/2)*2)
		INDEXOF(&dir2[],#dir,%&hit_dir%)
		IFMATCHES(%&item1%,"(wheel|_plate)")
			#side1=2
		ENDIF
		IFMATCHES(%HIT_FACING%,"(up|down)")
			SPLIT("_",%HIT_FACING%,&array[])
			INDEXOF(&dir3[],#side1,%&array[0]%)
			#side1=%#side1%-((%#side1%/2)*2)
			INDEXOF(&dir2[],#dir,%&array[1]%)
		ENDIF
		#dir=%#dir%-((%#dir%/4)*4)
		&direction1=%&dir1[%#dir%]%
	
		IFMATCHES(%&item1%,"_pile")
			#durability1=%HIT_LAYERS%-1
			IFMATCHES(%&item2%,"_pile")
				#durability2=%#durability1%
			ENDIF
		ENDIF
	ENDIF
ENDIF

IFMATCHES(%ITEM%,"(wallshelf_floating|wallshelf_chain|wallshelf_bracket|glass_frame)")
	&block2=%ITEM%
	&item2=%ITEM%
	&look2=0
	#durability2=-1*%DURABILITY%
	&direction2=%DIRECTION%
	
	#switch=1
	IF(CTRL)
		#ctrl=1
	ELSE
		#ctrl=0
	ENDIF
	#unctrl=(-1*%#ctrl%)+1
	#side2=1*%#unctrl%+((%@#shift%-1)*(%@#shift%-1)*%#ctrl%)
	
	IFMATCHES(%ITEM%,"glass_frame")
		IF(%@#shift%>=2)
			#side2=0
		ELSE
			#side2=(0*%#unctrl%)+((%@#shift%+1)*%#ctrl%)
		ENDIF
	ENDIF
	@#shift=%#side2%
ENDIF

IF(%#switch%=0)
	exec("cmeta.txt","1",%&direction1%,%#side1%,%&item1%,%#look1%,%#durability1%)
	#meta1=%@#meta%
	exec("cmeta.txt","1",%&direction2%,%#side2%,%&item2%,%#look2%,%#durability2%)
	#meta2=%@#meta%
	echo(//re %&block1%:%#meta1% %&block2%:%#meta2%)
ELSE
	exec("cmeta.txt","1",%&direction2%,%#side2%,%&item2%,%#look2%,%#durability2%)
	#meta2=%@#meta%
	echo(//set %&block2%:%#meta2%)
ENDIF



自己流!プログラムコードの書き方

ここからはマクロmodに関わらず、javascriptやvba、Pythonなどで作る小さめなマクロプログラムを作る際の思考過程や大事だと思うことを書いていきます。

1.プログラムの結果を想定して後ろから作っていく

コードは上から順に処理されていくので上から考えがちですが、マクロとか小さなプログラムは結果・出力から逆算して考えていくとスムーズに組めると個人的には思います。
 
まず最終的に実行するコマンドなどを作り、それを決定するための材料となる変数や関数を用意していきます。そしてその関数に必要な変数などを作っていく。このように後ろから順に想定して作ると必要なものが列挙しやすくなり、コードに無駄が少なくなります。

2.フローチャートを作る

フローチャートとは各機能の解説編の序盤で示したようなプログラムの動作の流れを言語化したものです。
流れを把握しないことには、必要な関数も定数もわからないので、この作業は大事だと思います。

流れを意識してフローチャートを書くには初めに始点(何のボタンを押すのか、どういった定数を使って判断材料を得るか)と、終点(最終結果)を書いてから中間を結ぶといいと思います。

3.分割して、テストして、合体させる

コードが複雑化していくと思ったように出力されないことがままあります。
そういうときは区切りのいいところで分割して(関数2,3個くらいの)その部分がうまく動作するのかチェックしていきます。
うまくいった場合といかなかった場合で表示される文字を変えてチェック判断をしましょう。
テストに合格したものから順につなげていけば動作すると思います。

4.カッコの閉じ忘れ、変数の打ち間違えをチェック

これは思考過程じゃないですがかなり大事です。
コードが動かない時の原因は主にカッコの閉じ忘れ、変数型や変数名の違いとかです。
特に色々チェックして最後にこれに気づいたときの徒労感は結構きます。
なので動かなかったらまずこれを疑いましょう。


参考サイト・使用MOD一覧

最後に参考にさせていただいたブログ記事や関数・定数一覧、公式DISCODEサーバを紹介して締めとなります。

・はやなささんのブログ記事

導入から応用までとても分かりやすく解説されています。
私がマクロmodに触れる最初のきっかけとなった記事でもあります。

・あまぐらすさんのブログ記事

配列といった少し踏み込んだ内容を扱っていますが、図や例を用いてやさしく解説されています。

・ケイヅキさんのブログ記事(Internet archive)

ここに書いてあることがすべて理解できれば、どんなマクロmodコードでも作れると思います。4編に分かれて解説されており、上のリンクは応用編になります。

・だんぼーるさんのブログ記事

WorldEditのgコマンドの基礎が図解多めで解説されています。
数学が苦手・忘れた方でも分かりやすいと思います。

・マクロmodの関数・定数一覧サイト

ここに行けば一発で使いたい変数とかが探せます。
さらに変数名をクリックするとコード例みたいなのも載っていたりします。
ただ、英語のサイトなのでGoogle翻訳拡張機能が必要です。(変な翻訳とかは少ないです)

・マクロmod公式DISCODEサーバ

・WorldEditコマンド一覧

注意として1.12.2のWorldEditは6系統なので使えないコマンドやブロックパターンがあります。(6系統のドキュメントも探したんですけどなかったです)

・解説内で使用したMODのリンク

Macro / Keybind Mod
WorldEdit
FastAsyncWorldEdit
MiniaTuria
大明神クローゼット


編集後記

あれもこれも解説していたらいつの間にか2万行超えていました。。。
(相変わらずまとめるのが下手)

長々と書いてきましたがこの記事が少しでも役に立ち、コード記述力の足しになればとても嬉しいです。

もし、「こういうコードを書きたいけどうまく書けない」とか「こういう機能があったら便利!」とかあれば私のTwitterのDMに送ってくださいね!


この記事が気に入ったらサポートをしてみませんか?