Languages: English • 日本語
Contents |
You missed! if you miss. マクロを書いていると、何度も同じ処理を繰り返したり、マクロ・コマンドの結果に応じて複数の選択肢から一つを選んで実行したりしたくなることが良くある。ゲームを例に取るなら、(例えば手榴弾の爆発に複数の敵を巻き込んだときのように、)何度も続けてダメージ・ダイスを振りたいこともあるだろうし、また、もし攻撃が命中したらダメージ・ダイスを振り、外したらハズレ! と表示してくれるようなマクロを作りたいと考えるかも知れない。
プログラム用語では、こういう概念を繰り返し(一つの処理を繰り返す)と分岐(プログラム―ここではマクロだが―が異なる複数の処理に枝分かれする)と呼ぶ。MapToolのマクロ言語にはダイスを振るための複数のオプション(や関数)があり、これであなたの命令を分岐・繰り返しさせることができる。
そして最後に、分岐や繰り返しを使って一度に複数の事をさばきたいと感じるのは良くあることなので、繰り返しや分岐の際に複数のマクロ・コマンドをまとめて一つの「単位」として扱えるcodeというオプションも用意されている。ちょっと分かりにくいかも知れないが、すぐに分かるようになる。
話さなければならないことが沢山あるので、このチュートリアルでは分岐(ある条件によって異なる命令を実行する)について説明することにする。繰り返し(ある処理を止めるまで繰り返す)についてはIntroduction to Macro Loopsで解説する。
これから急ぎ足でいろいろなオプションを使っていくことになる。そんなわけで、Introduction to Macro Writingを読み終え、新しいマクロの作り方を知っていて、その中に含まれていた(変数を作ったり、ダイス振りをしたりするなどの)基本的なコマンドが使えると想定することにする。
まず最初にいくつかの概念を理解してもらうが、これは分岐という概念を(そしてこの先Introduction to Macro Loopsで説明する繰り返しの概念を)理解するという大変な作業を行うために必要だからだ。その新しい概念について、これから説明していこう。
MapToolでは、分岐や繰り返しの技法を使うと、一つのことしかできないのが普通だ。この一つのこととは、つまりコマンド1個ということだ。だから「もしある条件が満たされたなら、なにかイケてることをしろ」では、この「なにかイケてること」の部分には一つのことしか入れられない。ダイスを振るとか、変数を宣言するとか、チャット・ウィンドウになにかのテキストを表示するとか、だ。しかし、ダイスを振って、変数を宣言して、さらにもう一つ変数を宣言して、計算を行い、それからそのなにかを出力する、といった処理はできない。処理の数が多すぎるからだ。
分岐や繰り返しを使うと一つのことしかできないというのでは、マクロの可能性は限られたものになってしまう。そこで、マクロ言語は特殊なロールのオプションを用意している。これが[code():]だ。これは、複数の処理を行いたいが、それが「分岐」や「繰り返し」の一つとして行われるべきだということをMapToolに伝えるためのものだ。複数のコマンドを一組の中カッコ({})の中に書く。
以下の例では[code():]のオプションを使っている。その仕組みが分かってもらえるだろう。
マクロを作成する場合、二つの数値を比べたいと考えることがよくある。ダイスの出目は20より大きいか? ヒットポイントは 0 より小さいか? その武器の名前は "Warhammer" と等しいか?
比較演算子とはプログラミングで用いる用語で、MapToolで二つの値を特定の基準で比べあうときに使う記号のことだ(演算子とは、ある演算を行うための記号だ)。例えば+記号は加算を表す。
論理演算子は、MapToolに対してどういう順番で比較を解釈したらよいか、それをどうまとめるかを教えるための記号だ。比較演算子と論理演算子については以下で説明する:
以下の例では、if() を使って説明している。これについては後でもう少し詳しく解説するが、基本的なif()の「文法」は以下の通りだ:
if(comparison, value_if_true, value_if_false)if(比較, 真のときの値, 偽のときの値)
value1 > value2, you read it based on the left side: "is value1 greater than value2. This is the rule for comparisons in MapTool - the left side of the operator is the "point of view." 比較演算子を以下に挙げる。こうした比較は常に左辺値を起点として考えねばならない点に注意すること。だから、value1 > value2という比較では、左辺値を基本として「value1はvalue2よりも大きい」と読むことになる。これはMapToolで比較を行う場合の規則だ。演算子の左辺値がかならず比較の「視点」となる。
[if(hit == "yes", "you hit!", "you missed!")]
[if(roll > 17, "Hit!", "Miss")]. You can put a number on the left side, like [if(17 > roll, "Miss", "Hit!")] (note that it basically reverses the first example, so you need to switch the true and false outputs).
[if(roll >= 17, "Hit!", "Miss")]
[if(roll < 19, "Miss", "Hit!")]}
[if(roll <= 18, "normal hit", "critical hit")]
[if(roll != 1, "Not a fumble", "You fumbled!")]
[if(hit == "yes", "命中!", "外した!")]
[if(roll > 17, "Hit!", "Miss")]という具合だ。左辺値は数値にすることもでき、その場合はこのようになる。[if(17 > roll, "外れ!", "命中!")] (これは単に最初の例を逆にしただけだ。なので、結果が真のときと偽の時の処理を逆にする必要がある)。
[if(roll >= 17, "命中!", "外れ")]という具合に。
[if(roll < 19, "外れ", "命中!")]} のように。
[if(roll <= 18, "通常命中", "クリティカルヒット")] のように。
[if(roll != 1, "ファンブルでない", "ファンブルした!")] のように。
以下に挙げる記号が論理演算子だ。これらの演算子は組み合わせて使うことができる(が、それが必要なのは一度に複数の比較を行いたい場合だけだろう)。こうした演算子は比較演算子の間で使われる(前述の比較演算子を置き換えるものではない)。
[if(roll > 1 && roll < 20, "Hit", "Miss")] requires both comparisons to be true, for the whole comparison group to be true. In other words, the roll must be greater than 1 and less than 20 in order for it to be a hit. If both of those aren't true, the output is Miss.
. In the example, if either condition is true (that is, if enemyHealth is "dead" or "dying") the entire comparison group is true. Only if neither comparison is true does the whole thing become false.
[if(roll > 1 && roll < 20, "命中", "外れ")]とした場合、二つの比較の両方とも真となり、比較群全体としても真となることが求められている。 言い換えると、命中するためには、ダイスの出目は1より大きくて、かつ、20未満でなければならないことになる。この両方が真にならない場合、出力結果は外れになる。
というように。この例では、どちらかの条件が真なら(つまり、enemyHealthが"死亡"または"瀕死"なら)、比較群全体が真になる。両方とも偽である場合にのみ、全体が偽となる。
あらゆるコードで使われる中でも最も基本的な分岐の方法はif - thenだ。つまり、if(もし)何らかの比較結果が真になる、then(なら)何か特別なことをする。このifは、「もし、私の攻撃が命中したら、ダメージ値を表示する!」というように使うことになるだろう。
MapToolのマクロ言語には二種類の if がある。―関数とロール・オプションだ。関数とは、あらかじめ定義されている一連の命令のことで、名前をつかってそれを「呼び出す」ことができる。ロール・オプションとは、MapToolに対してある命令の扱い方を教えるための「スイッチ」とか「トグル」のことだ。
if() and putting the thing you want compared, what to do if the comparison is true, and what to do if the comparison is false, all inside the parentheses. The general format is:if() 関数は単にif()と書いて、比較すべきものと、その結果が真になったときにすることと、偽になったときにすることを、全てカッコの中に入れるだけで使える。基本的な文法はこうなる:
if(comparison, value_if_true, value_if_false)
if(比較, 真のときの値, 偽の時の値)
そして実際の例はこんな風になる:
[if(attackHits == "yes", "You hit!", "You missed")]
[if(attackHits == "yes", "命中!", "外れ")]
この一行でやらせていることは以下のとおり:
attackHits to see if it has the value "yes"
You hit! to chat, or
You missed to chat
attackHits の値が"yes"かどうかを確認
命中! 、
外れ と、チャットに表示する。
if()関数の中の、真の時の値と偽の時の値の二つの部分はテキストでも、ダイスロール・コマンド(1d6とか1d20とか)でも、変数でも構わない。そこに入れてはいけないのは、変数の代入だ。つまり、if()をこういう風には書けないことになる:
[if(attackHits=="yes", output = "You Hit!", output = "You missed")]
[if(attackHits=="yes", output = "命中!", output = "外れ")]
一見よさそうに見えるが、これはうまくいかない。これをやると、MapToolは俗に「ヌルポインター例外」と呼ばれるものを出し、マクロはエラーを起こしてしまう。しかし、これをうまく回避するワザもある。if()は関数であり、すべての関数は実行すると値を返すのだから、その戻り値を変数に代入することができるんだ。こんな風に:
[output = if(attackHits=="yes", "You Hit!", "You missed")]
[output = if(attackHits=="yes", "命中!", "はずれ")]
このように書いておくと、MapToolはこんな風に動く:
output, which you can then use like any variable
outputに代入し、それを他の変数と同じようにいろいろ使う
if()の他にも、「もし~ならば~」の概念をマクロ・コードで使う方法がもう一つある。それが[if():]のロール・オプションだ。ロール・オプションとは、前述したとおり、あらかじめ与えておいたマクロ・コマンドの扱い方をMapToolに「切り替え」あるいは「トグル」させるためのものだ。Introduction to Macro Writingにはそのいくつかが載っている。[h:]や[e:]などがそれだ。
ロール・オプションは以下の規則に従わねばならない:
[h:]
[h,if(HP > 0): command]
[h:]
[h,if(HP > 0): command]
[if():]オプションを比較に使う場合、以下の文法を使わなければならない:
[if(comparison): command_if_true; command_if_false]
[if(比較): 真のときのコマンド; 偽の時のコマンド]
command_if_false part entirely.
偽のときのコマンドも書かなくていい。
[if():]ロール・オプションの例は以下の通りだ:
[h,if(attackHits == "yes"): output="You hit!"; output="You missed"] Result of your attack: [r:output]
[h,if(attackHits == "yes"): output="命中!"; output="外れ"] 攻撃結果: [r:output]
上の例では、以下のように処理されている:
attackHits to the value "yes"
attackHits is indeed equal to "yes" - it assigns the value "You hit!" to the variable output.
attackHits is not equal to "yes" - it assigns the value "You missed" to the variable output.
output to chat.
attackHitsの値と"yes"の値とを比較する
attackHitsが本当に"yes"と等しいなら、変数outputに"命中!"の値を代入する。
"yes"と等しくないなら、変数outputに"外れ"の値を代入する。
outputの値をチャットに表示する。
command_if_true and command_if_false sections.1行目で気づいた人もいるだろう。ここでは一つの行で、[h:] と [if():]の二つのロール・オプションを使っている。この二つのオプションはコンマで区切られていて、コロンは最後のロール・オプションの後ろ、真のときのコマンドと偽の時のコマンドのセクションの前にある。
比較の結果としてやりたいことが複数あったらどうしたらいいだろうか? 例えば、複数の変数にそれぞれ値を代入するとか? それには[code():]ロール・オプションが使える。
他のロール・オプションと同じように、[code():]は行の先頭に置き、他のロール・オプションとはコンマで区切る。マクロプログラミング規約(要は、ほとんどのマクロがそうなってるって意味だ)では、[code():] をロール・オプションの一番最後につけることになっている。だから、マクロの中では一般にはこんな風になる:
[roll_option1, roll_option2, code: macro_commands]
[roll_option1, roll_option2, code: macro_commands]
[code():]オプションの二番目の要素は中カッコ({})だ。これを使って複数のコマンドを一つの群にまとめる。[if():]ロール・オプションの文法を覚えているかな?
[if(comparison): command_if_true; command_if_false]
[if(comparison): 真のときのコマンド; 偽のときのコマンド]
command_if_true and command_if_false with multiple macro commands. Let's look at an example:
さて、[code():]オプションでは 真のときのコマンド と 偽のときのコマンド に複数のマクロ・コマンドを置くことができる。例を見てみよう:
attackRoll. We want to compare it to a number (the target number), which is held by the variable targetNumber. Here's what we want the macro to do:
これからattackRollという変数の値を調べるマクロを書くとしよう。そしてこれをある値(目標値)と比較しようと考えている。この値はtargetNumberという変数に格納されている。マクロにやらせたいのはこういうことだ:
attackRoll is greater than or equal to targetNumber, the macro should:
attackUsed to "yes"
attackResult to "hits"
attackRecharge to 3
damageRoll to the result of the dice roll 1d8+4.
もし attackRoll が targetNumber 以上なら:
attackUsed に "yes" をセット
attackResult に "命中" をセット
attackRecharge に 3 をセット
damageRoll には 1d8+4 の結果をセット
attackRoll is not greater than or equal to targetNumber, the macro should:
attackUsed to "Yes"
attackResult to "misses"
attackRecharge to 3
damageRoll to "no"
もし attackRoll が targetNumber未満なら:
attackUsed に "yes" をセット
attackResult に "外れ" をセット
attackRecharge に 3 をセット
damageRoll には "no" をセット
では、以下にそのマクロを載せる:
[h:attackRoll = 1d20] [h:targetNumber = 15] [h,if(attackRoll >= targetNumber), code: { [attackUsed = "yes"] [attackResult = "hits"] [attackRecharge = 3] [damageRoll = 1d8+4] }; { [attackUsed = "yes"] [attackResult = "misses"] [attackRecharge = 3] [damageRoll = "no"] }] Your attack [attackResult], and you do [damageRoll] damage. Your attack will recharge in [attackRecharge] rounds.
[h:attackRoll = 1d20] [h:targetNumber = 15] [h,if(attackRoll >= targetNumber), code: { [attackUsed = "yes"] [attackResult = "命中"] [attackRecharge = 3] [damageRoll = 1d8+4] }; { [attackUsed = "yes"] [attackResult = "外れ"] [attackRecharge = 3] [damageRoll = 0] }] あなたの攻撃は [attackResult] で、与えたダメージは [damageRoll] 点。 次の攻撃ができるまで [attackRecharge] ラウンドかかる。
ここではいろいろなことをしているが、注目して欲しいのは一番最初の行にある CODE オプションと、中カッコだ。中カッコの中には複数のコマンドが入っているが、MapToolに対してこれを「一つのものとして扱う」よう指示していることになる。従って、上の例はこのように動作する:
attackRoll and targetNumber, and give them initial values (in this case, attackRoll will be the result of a 1d20 roll, and targetNumber is set to 15).
command_if_true and command_if_false - will actually consist of multiple separate commands.
code, to mark off the end of all the roll options. There is only ONE colon in the line!
command_if_true portion of the IF statement. We then put in our commands, each one separately and enclosed in square brackets. Once finished, we close that section of the IF statement with a }, and put a semicolon on the end (remember, the IF roll option needs a semicolon to separate command_if_true from command_if_false.
command_if_false section - a { followed by a series of commands, and then closed with a }.
attackRoll と targetNumber を宣言し、初期値を与える(この場合、attackRoll は 1d20 の結果、targetNumber は15になるはずだ)。
真のときのコマンド と 偽のときのコマンドのそれぞれの部分に[code():]を置いて、この部分の中には複数のコマンドが入るのだということをMapToolにあらかじめ報せておく。
code文の後ろにコロンを置き、ロール・オプションの終端であることを知らせる。この行にあるコロンはこれ一つだけ!
真のときのコマンド の最初に { を置く。それからそれぞれ大カッコでくくったコマンドを並べていく。全部終わったら、このセクションを } で閉じ、最後にセミコロンをつける( IF ロール・オプションでは、真のときのコマンド と 偽のときのコマンド を区切るのにセミコロンが必要だということを思い出して欲しい)
偽のときのコマンド のセクションについても行う。 { の後に一連のコマンドを並べ、最後に } で閉じる。
注意: CODE ロール・オプションは他のロール・オプションと組み合わせたときにだけ動作する。関数であるif() とは組み合わせられない。ちょっとややこしいかも知れないが、「 CODE は ロール・オプションと組み合わせてしか使えない」とだけ、覚えておいて欲しい。
if()関数と[if():]ロール・オプションは両方とも二つある処理のどちらか一つを選ぶものだ。比較結果が真ならこちら、偽ならこちらという具合に。しかし人生というものは、そしてRPGもだが、常に白黒はっきりしたものではない。複数の選択肢の中から異なった動作を一つ選ぶ場合には、[switch():]ロール・オプションを使いたまえ。
一般的な文法は以下の通り:
[switch(val): case case_value1: command_1; case case_value2: command_2; case case_value3: command_3; default: command_Default]
[switch(val): case 第1の値: 第1のコマンド; case 第2の値: 第2のコマンド; case 第3の値: 第3のコマンド; default: デフォルトのコマンド]
MapToolはこういう風に動作する:
val
case statements in the switch, and compares val to case_value1, case_value2, and case_value3
val is equal to one of those cases, the appropriate command (either command_1, command_2, or command_3) is executed, and then MapTool exits the switch statement (which just means, once it's found a match, it does what that case says, and then stops checking for matches).
val の値を調べる
case 文を一つ一つ見ていって、val を 第1の値、第2の値、第3の値 と比較する
val が case 文のどれかと等しくなったら、それに該当するコマンドを(第1のコマンド、第2のコマンド、第3のコマンドのいずれかを)実行する。それが終わったら、switch 文を抜ける(要は、一致するものを探して、そこに書いてある通りのことをして、そこで探すのをやめる、ということだ)。
Armor value to a token, based on the token's Class. If you've been following along, you might recognize the Armor value as one of the attributes in the Sample Ruleset. If you visit the Sample Ruleset page, you'll see that a character can have one of several armor values, based on the character's class:例えば、あるトークンに対して、そのClassに応じた正しいArmor値を自動的に代入するマクロが欲しいと仮定してみる。ここまで読んできてくれたのなら、ここでいうArmor値はSample Rulesetにある属性値の一つだということが分かってもらえるだろう。Sample Rulesetのページを読めば、キャラクターが自分のクラスに応じていくつかの装甲値(armor value)のなかから一つを選べることが分かるはずだ:
class, and then use that variable to assign the right Armor value. Here's how we'd do it:そこで、マクロがそのトークンのclassを質問してくるようにして、その後で正しいArmor値を代入するようしよう。こういう風になる:
[h:class = "Rogue"] [h,switch(class): case "Warrior": Armor = 6; case "Rogue": Armor = 2; case "Wizard": Armor = 1; case "Priest": Armor = 4; default: Armor = 0] Your Armor Value is [Armor].
[h:class = "Rogue"] [h,switch(class): case "Warrior": Armor = 6; case "Rogue": Armor = 2; case "Wizard": Armor = 1; case "Priest": Armor = 4; default: Armor = 0] あなたの装甲値は [Armor] です。
この例で何をやっているかというと、こういうことだ:
class - if you try this out, it will always show the value for "Rogue." If you alter the [h:class="Rogue"] line, you can see how changing that value affects the switch statement).
class is equal to "Warrior", "Rogue", "Wizard", or "Priest".
class equals any of those (and we mean EXACTLY equals - case sensitive, no spaces, an exact match), run the command to set the variable Armor to the appropriate value.
default option (in other words, set Armor to 0.
class 変数の値を調べる。このマクロを試しに動かしてみると、常に "Rogue" の値を表示してくるだろう。[h:class="Rogue"] の行を変更すれば、それが switch 文にどういう影響を与えるか、分かるはずだ。
classの値が"Warrior"、"Rogue"、"Wizard"、"Priest"、のどれと等しいか調べる。
class が上のどれかと同じ値であれば(ここでいう同じとは、「全く同じ」ということだ。大文字小文字やスペースの有無にいたるまで、全く同じでなければならない)、対応するコマンドを実行して、Armor に正しい値を与える。
default の後ろにあるとおりに実行する(ここでは、Armor に 0 をセットする)。
以前に [if():] でもできたように、[code():] は [switch():] とも組み合わせることができる。多少ややこしいが、例に挙がっているパターンにさえ従っていれば、うまく動いてくれる。
[code():] と [switch ():] とを組み合わせる場合、基本的な文法は以下の通りだ:
[switch(val),code: case case_1: { commands_for_case_1}; case case_2: { commands_for_case_2}; case case_3: { commands_for_case_3}; default: { commands_for_default}]
[switch(val),code: case 第1のケース: { 第1のケースのコマンド }; case 第2のケース: { 第3のケースのコマンド }; case 第3のケース: { 第3のケースのコマンド }; default: { デフォルトのコマンド}]
beginningPowers. To do that, you'd write a SWITCH that looks like:これは Sample Ruleset からそのまま引いてきたもう一つの実例だ。キャラクターのクラスが決めるのは装甲値だけでなく、そのキャラクターが選択できる「作成時パワー」のリストもそれで決まる。装甲値以外にも beginningPowers 変数も代入したいと仮定する。これを行うには、SWITCH 文を以下のように書けばいい:
[h,switch(class),code: case "Warrior": { [Armor = 6] [beginningPowers = "Sword, Shield Bash, Bow, Shield, Torch"] }; case "Rogue": { [Armor = 2] [beginningPowers = "Dagger, Hide, Backstab, Pick Lock, Torch"] }; case "Wizard": { [Armor = 1] [beginningPowers = "Dagger, Staff, Light, Lightning Bolt, Fire Ball"] }; case "Priest": { [Armor = 4] [beginningPowers = "Mace, Heal, Protect, Banish Undead, Torch"] }; default: { [Armor = 0] [beginningPowers = "Fists, Feet"] }] Your Armor Value is [Armor] and your beginning powers are [beginningPowers].
[h,switch(class),code: case "Warrior": { [Armor = 6] [beginningPowers = "Sword, Shield Bash, Bow, Shield, Torch"] }; case "Rogue": { [Armor = 2] [beginningPowers = "Dagger, Hide, Backstab, Pick Lock, Torch"] }; case "Wizard": { [Armor = 1] [beginningPowers = "Dagger, Staff, Light, Lightning Bolt, Fire Ball"] }; case "Priest": { [Armor = 4] [beginningPowers = "Mace, Heal, Protect, Banish Undead, Torch"] }; default: { [Armor = 0] [beginningPowers = "Fists, Feet"] }] あなたの装甲値は [Armor] で、開始時パワーのリストは [beginningPowers] です。
command_for_case_1, with a group of commands.お分かりのように、それぞれのケースが一つの処理として扱われている。だからそれぞれのケースは中カッコで囲み、セミコロンで区切っておかなければならない。そして一番最後を大カッコ(])で閉じ、コマンド全体を区切る。ここでも、CODE オプションを使うことで、第1のケースのコマンド のコマンドを、コマンドの「グループ」で置き換えることができる。
なお、読みやすくするために、それぞれの処理グループごとに改行を入れているのが分かると思う。ここはMapToolのいいところで、一つのコマンドの中での改行を無視してくれる(一つのコマンドは、[ と ] で囲まれているという点に注意)。これはマクロを大幅に読みやすくできて、都合がいい。
ここで説明した二つのオプションは、マクロを書くときに最もよく使われる分岐方法だ。しかし、マクロで分岐を行う手段はこれだけではない。他にも二つのオプションがあるが、その中には、あるマクロの中から完全に抜け出して別のマクロを呼び出したり、マクロのフォーカスを切り替える(つまり、マクロのCurrent Tokenが表すトークンを一時的に変える)ものが含まれている。こうした処理はそれ単体でもやや複雑なものなので、この先のMore Branching Optionsガイドで読むことにする。
Languages: English • 日本語