Introduction to Macro Loops/ja

From RPTools Wiki
Jump to navigation Jump to search



We've looked at branching in macros, using [if():], [switch():], and the more advanced roll options [macro():] and [token():]. Branching is one of the most important tools for macro writing, since it lets you automate decisions based on certain factors or conditions that arise during play.


Another common task in MapTool macros is to repeat a process multiple times - for example, you may want to repeat an attack roll several times, against multiple targets (for instance, if you threw a grenade, you might need to roll to see which targets are hit by the blast), or you may want to go through a list of skills, and print out the skill rating for each one. In both cases, you're repeating the same operation several times in a row, generating different results each time. In macro writing, we call this process looping (you may see it described in places as "looping through a list" or "iterating over an array" - regardless, the processes to do it are loops).


There are four loop structures in MapTool macros: [count():], [while():], [for():], and [foreach():]. Each of the three is a roll option, which means - as we've seen before:

MapTool のマクロには、[count():][while():][for():][foreach():]、の4つの反復構造が存在している。これらはいずれもロール・オプションであり、すでに見てきたように:

  1. It is placed at the beginning of a macro command
  2. It is followed by a colon
    1. If more than one roll option is used, they are separated by commas, and the last one is followed by a colon
  3. After the colon, you place the operation you want to do every time the loop runs
  1. マクロコマンドの先頭に置かねばならない
  2. 次にコロンがついていなければならない
    1. 複数のロール・オプションを使うなら、それぞれコンマで区切らなければならず、最後のオプションはコロンで終わらなければならない
  3. コロンの後ろに、ループ1回ごとに処理して欲しい内容を書いておく
Before we continue, we'll need to introduce a couple concepts that will be used heavily in the examples below, especially for [for():] and [foreach():] loops.



RPGs have a lot of information that goes into playing them - there are stats, and skills, and dice rolls, and weapons, and equipment, and powers and magic and...well, you name it, and there's an RPG out there that covers it.


For basic things, it might make sense to create a token property for each piece of information. In fact, you can do this for every possible bit of information you want to record about a character - but already, I bet you're thinking "that's a lot of properties"). And it is!


Fortunately, there are other ways to store information in MapTool properties (and macro variables) that let you group information together. The two new information storing methods we'll use - which are properly referred to as data types - are string lists and string properties.



A string list is, first, a string - that is, a collection of alphanumeric text that is treated as just text (that is, it's not a number, so you can't add it to another number; it's not a dice roll, so MapTool won't automatically roll it; it's just a string of characters).

まず、文字列リストは文字列である。この文字列は、テキストとして扱う英数字から成り立つ(純粋な文字列。数値ではないので、後から数値を追加することは出来ない。ダイスロールではないので MapTool が自動的にロールすることはない。)。

Second, it is a list - a collection of single items, separated by some sort of separating character - the (you guessed it) separator (also called a delimiter). The separator marks the beginning and end of an individual item in a list. This is a long way of saying "a string list is a single value that is a list of things, like colors: blue, green, red, orange, mauve."

第2に、文字列リストはリストである。区切り文字(セパレーターとも呼ばれる)と見なされる文字に区切られた項目の集まり。区切り文字はリスト内の個々の項目の始まりと終わりを示す。簡潔に言えば「文字列リストは、一覧(例えば色の一覧:blue, green, red, orange, mauve)を表す単体の値である」。

Formally - in macro code - a string list looks like:


[h:listOfColors = "blue, green, red, orange, mauve"]
In the example, you'll see that we've made a variable assignment, and given the variable listOfColors the values blue, green, red, orange, mauve. The whole thing is enclosed in double quotes, which tells MapTool that it's a string, and from its very format, MapTool knows that it's a string list.

上記例では、変数定義が書かれていて、変数 listOfColorsblue, green, red, orange, mauve が与えられている。MapTool は文字列を表す二重引用符に挟まれ、規則正しく書式化された箇所を文字列リストであると解釈する。

A string list can contain anything - it could be a list of names, of numbers, of dice rolls - anything you might want to keep a list of.


However! It is important to remember that no matter what each item in a string list is, it is always treated as a string. So if MapTool reads a string list that looks like "1d6, 2d8, 1d20", it will not see the first item and see a command to roll 1 six-sided die; instead it will see the character "1", the character "d", and the character "6", all put together. They may look the same to us, but they don't look the same to MapTool - to turn that "1d6" into 1d6 so that MapTool will roll it, we need to use the eval() function to tell MapTool "evaluate that string as if it were a dice roll." You'll see some examples of eval() later on.

ただし! 重要なことだが、文字列リスト内の各項目は常に文字列として扱われることを忘れないように。そのため、"1d6, 2d8, 1d20" のような文字列リストを読んだ MapTool は、最初の項目を1つの6面ダイスをロールするコマンドとは見なさず、「1」の文字、「d」の文字、「6」の文字を組み合わせたものとして解釈される。見た目は同じように感じるが、MapTool にとっては異なるものである。「1d6」を MapTool がロールする 1d6 にするには、eval() 関数を使い、MapTool に「文字列をダイスロールとして評価しなさい」と伝える必要がある。後述の eval() の例を見ると良いだろう。


String properties are very similar to string lists - they are strings with special formatting, and they contain a collection of items. However, string properties have additional features that make them very useful for storing information in a different way.


The essence of a string property is the the key - value pairing. Basically, for each item in the string property, there is a key that is paired with a value. For instance, if you have a weapon with the following details:


  • Weapon Name: Broadsword
  • Weapon Damage Dice: 1d8
  • Weapon Damage Type: Slashing
  • Weapon Category: Versatile
You have a series of key - value pairs: the key "Weapon Name" is paired with the value "Broadsword," the key "Weapon Damage Dice" is paired with the value "1d8," and so on. A string property is simply a "formal" way to set up this kind of pairing in a single variable. The string property for the above weapon might read:

この例では:キー「Weapon Name」は値「Broadsword」とペア、キー「Weapon Damage Dice」は値「1d8」とペア、と行った具合にキー=値のペアが連なっている。文字列プロパティは一つの変数に上記のようなペアを示す「規則的な」書式でなりたつ。上記の武器の文字列プロパティは次のようになる:

[h:weapon1 = "name=Broadsword; damageDice=1d8; damageType=Slashing; category=Versatile;"]
In that string property, the word to the left of the equal sign is the key, and the word to the right is the value. The semicolon is the separator or delimiter. MapTool has special functions to retrieve and change the values within a string property, which you'll see in use later.


Now, let's get to the loops!



The first looping structure we'll cover is the [count():] option. This option is the simplest loop - it repeats the operation following the colon a number of times equal to its argument (remember, arguments are the values or variables you put inside the parentheses). The format of a [count():] (which can also be abbreviated [c():] statement is:


[count(repetitions): command]
[count(繰り返し回数): コマンド]


[c(repetitions): command]
[c(繰り返し回数): コマンド]
The example should be pretty self-explanatory. It contains:


  • The [count():] option itself - without the option, we wouldn't need this tutorial, right?
  • repetitions: this is the value that tells [count():] how many times to repeat command
  • command: this is the actual macro command you want count to do over and over again.
  • [count():]オプションそのもの。もう説明しなくても良いね?
  • 繰り返し回数コマンドを何回繰り返すか[count():]に指示する値。
  • コマンド:実際に繰り返し実行させたいマクロ・コマンド。
Let's look at an example. Suppose you have a character who can cast a spell, which creates a cloud of poisonous gas that can hit up to nine targets at the same time. Suppose we also have a cluster of 6 hapless orcs standing in the room, that you are about to poison with this toxic cloud. The rules of your game indicate that you must roll a separate attack for each possible target, meaning that you'd have to roll your attack 6 different times. You can either do that by hand, each time, or you could write a macro that uses [count():] to roll the attack over and over, and all you need to give it is the number of times!


Here's how you'd write that macro:


[h:attackBonus = 7]

Toxic Cloud: [count(numAttacks): 1d20+attackBonus]
[h:attackBonus = 7]

毒性雲:[count(numAttacks): 1d20+attackBonus]
What we did here was:


  • Line 1 sets a value for attackBonus, to be used later.
  • Line 3 sends the text "Toxic Cloud: " to chat, and then, begins the Count Loop. Since numAttacks is undeclared, MapTool will prompt you for a value before it can start the loop. Once you enter that value, the count loop will process the calculation of 1d20+attackBonus that many times, sending the result to chat each time, and separating each result with a comma.
  • 1行目で、後で使うために、attackBonusに値を代入。
  • 3行目で、チャット・ウィンドウに「毒性雲:」のテキストを送り、COUNTの繰り返しを開始する。numAttacks定義されていないので、繰り返しの処理を始める前にMapToolが値を催促してくる。値を入力すると、COUNTの繰り返し処理が1d20+atackBonusを何度も計算し、その計算結果をコンマで区切りながらチャット・ウィンドウに出力する。
The output of this macro will look something like (assuming you entered 4 when prompted for numAttacks):


Toxic Cloud: 17, 19, 12, 8

毒性雲:17, 19, 12, 8


Since it's often useful to know what "round" or "turn" we're on when a [count():] loop is running, MapTool creates a special variable every time you start a count loop. This variable is called roll.count, and it's value is equal to whatever loop you're currently on. So, if you're on the first loop, roll.count is equal to 1; on the second time through, it's equal to 2, and so on. That way, you can use that value in various ways inside your macro command.


A more advanced example of the Toxic Cloud macro might look like this:


[h:attackBonus = 7]

Toxic Cloud vs:<br>

[c(numAttacks, "<br>"),CODE:
   [attack = 1d20+attackBonus]
   [damage = 1d6 + 2]
   Target [r:roll.count]: Attack [r:attack]; [damage] damage.
[h:attackBonus = 7]


[c(numAttacks, "<br>"),CODE:
   [attack = 1d20+attackBonus]
   [damage = 1d6 + 2]
   対象 [r:roll.count]:攻撃 [r:attack]、ダメージ [damage]。
A bit more is going on here.


  • Line 1 still just sets attackBonus to 7, so we can use it later.
  • Line 3 outputs "Toxic Cloud vs:
    " to chat (the <br> part sends a line break to chat, meaning that the next output will start on the line below the words "Toxic Cloud vs:")
  • Line 4 starts a more complex Count Loop. First off, we used the abbreviation for "count", which is just c. We left numAttacks as is, but added a second argument - in this case, a different "separator." All loops in MapTool macros have a default separator, which is the comma. However, you don't always want to separate your results with a comma, right? In this situation, we want to separate them with a line break, so each result is on its own line in chat. So, we put the new separator in - an HTML line break character, or <br>.
  • Line 4 also uses the CODE roll option, which is discussed in Introduction to Macro Branching, and lets us do multiple operations as a single group.
  • Line 6 sets the variable attack to the sum of attackBonus and 1d20.
  • Line 7 sets the variable damage to the sum of 1d6+2.
  • Line 8 is a combination of text and variables, which are output to chat. Note that the variable roll.count is in there, which will be replaced with whatever iteration the loop happens to be on.
  • Line 9 closes the CODE block, and the whole command.
  • 1行目で、前回と同じように、後ほど使用するためにattackBonusに7を代入。
  • 3行目では、チャットに「有毒雲:
  • 4行目では、より複雑な COUNT の繰り返しを開始する。まず、「count」の短縮形であるcを使っている。numAttacksはそのままだが、2つめの引数(標準ではない「区切り文字」)を追加している。MapToolのすべての繰り返し処理はコンマを標準の区切り文字として備えているが、結果を表示するときにいつもコンマを区切り文字として使いたいわけではないだろう? ここでは、チャットにそれぞれの結果を一行づつ表示したいので改行で区切りたいところだ。そこで、HTMLの改行文字(<br>)を区切り文字に指定した。
  • 4行目では、さらにロールオプションの CODE (はじめてのマクロ分岐で 取り上げた)を使い、複数の処理をひとつのグループにまとめている。
  • 6行目では、変数attackattackBonusと1d20を合計したものを代入。
  • 7行目では、変数damage1d6+2の合計を代入。
  • 8行目では、テキストと変数をつなぎ合わせて、チャットに出力している。ここで変数roll.countは現在の繰り返し回数と置き換わっているわけだ。
  • 9行目で CODE ブロックを閉じ、すべてのコマンドを終了する。
The output from this would look something like:


Toxic Cloud vs:
Target 1: Attack 17; 6 damage.
Target 2: Attack 12; 5 damage.
Target 3: Attack 19; 7 damage.
Target 4: Attack 10; 3 damage.

対象 1:攻撃 17、ダメージ 6。
対象 2:攻撃 12、ダメージ 5。
対象 3:攻撃 19、ダメージ 7。
対象 4:攻撃 10、ダメージ 3。


Let's move on to a new looping structure: [while():]. This structure is the first one we'll discuss that uses a condition to determine how many times to loop (previously, count used a value - but not a comparison of any kind). The general format of a [while():] loop is:

では次の繰り返し構造:[while():]を学んでいこう。この構造は、繰り返し回数を決めるために条件文を用いる際、まず初めに考慮に入れるものである(前述の count も値を使用するが、いかなる比較もしていない)。一般的な[while():]の書式は:

[while(condition): command]
[while(条件式): コマンド]
You're probably getting used to reading these by now.


  • [while():]: of course, we need to add the roll option itself
  • condition: this is the comparison that we make, to see if the loop needs to stop - it can be any of the logical comparisons we've discussed already (such as, loops < 10 or numDice > 5 or anything you can think of).
  • command: the macro command (or commands, if you use the CODE option) to run each time the loop goes 'round.
  • [while():]:言うまでもなく、ロールオプションを加えなければならない。
  • 条件式:繰り返しを止めるべきかどうか判断するために必要な比較を行うものだ(例えば、loops < 10numDice > 5など思いつくものなら何でも)。
  • command:繰り返しの処理が巡ってくるたびに実行するマクロコマンド(CODEオプションを使う場合は一連のコマンド)。
So, if you read this, the while loop really just says, "while some condition is true, keep doing this." Let's look at some examples.

つまり、while を用いた繰り返しは「条件式が真であるあいだ、これをやり続けなさい」ということだ。いくつかの例を見てみよう。


This is a very basic example, just to illustrate the basic parts of the [while():] loop. Suppose you wanted to count down from 10 to 1. There are many ways to do this, of course, but we'll do it with a [while():] loop. The macro would look like this:


[h:num = 10]
[while(num > 0): num = num - 1]
The above macro simply says that "while the variable num is greater than 0, subtract 1 from num, and repeat." Each time through the loop, the value of num will be checked by MapTool. If it is greater than 0, it will let the command num = num-1 happen, and display the result in chat; if num is not greater than 0, then the loop will be halted. The output of this macro looks like:

上記のマクロは、「変数numが 0 より大きい値でいる間、numから1を引き、さらに繰り返しを続けなさい」ということが書いてある。繰り返し中、毎回MapToolがnumの値を確認している。値が0より大きければ、num = num-1のコマンドを実行し、結果をチャットに表示する。もし、numの値が0より大きくなければ、繰り返しを中止する。このマクロの出力は次のようになる:


You'll note that "10" was never shown. That's because while the loop may have started with num having the value of 10, the first time we see any output is when the operation num -1 takes place - so the first thing we see is num - 1, which is 9.

「10」が表示されないことに気づくだろう。繰り返し WHILE は numは10の値とともに開始して、なんらか出力を最初に見るときは、num -1の処理が行われる場合なので、最初に目にする値は num -1の結果、9になるわけだ。


Let's look at a more complicated (and perhaps more interesting) example. In this example, we won't be using the Sample Ruleset, mostly because I couldn't think of a useful example from that game. So, let's assume we have the following game situation:


  • A character has a machine gun with 30 rounds of ammunition
  • They may fire one to six rounds for every action
  • If their attack roll of 1d20 is greater than 15, they may make another attack; otherwise, they must end their turn. They always get at least 1 attack, though.
  • キャラクターは30発の弾薬を持つマシンガンを装備している
  • このキャラクターはアクションごとに1〜6発、発砲することができる
  • 1d20の攻撃ロールが16以上の場合、さらにもう一回攻撃することができる。15以下であれば、ターンを終える。とはいえ常に最低1回の攻撃機会を得る。

So what we need to do is repeat the operation until either the weapon runs out of ammo, or they roll less than a 15 on their attack. Here's how that macro would look:


[h:ammo = 30]
[h:hit = 1]

[while(ammo > 0 && hit == 1, "<br>"),CODE:
  [h:attackRoll = 1d20]
  [h:ammoSpent = 1d6]
  [h,if(attackRoll > 15): hit = 1; hit = 0]
  [h:ammo = ammo - ammoSpent]
  Your first attack expends [r:ammoSpent] rounds, and [if(hit==1, "hits.", "misses.")] You have [r:ammo] rounds remaining.

[if(hit==0): "Your turn ends because you missed a target."]
[if(ammo==0): "Your turn ends because you are out of ammo."]
[h:ammo = 30]
[h:hit = 1]

[while(ammo > 0 && hit == 1, "<br>"),CODE:
  [h:attackRoll = 1d20]
  [h:ammoSpent = 1d6]
  [h,if(attackRoll > 15): hit = 1; hit = 0]
  [h:ammo = ammo - ammoSpent]
  君の攻撃は、[r:ammoSpent]発消費し、[if(hit==1, "命中した。", "失敗した。")] 弾薬は [r:ammo]発残っている。

[if(hit==0): "攻撃に失敗したので、ターン終了。"]
[if(ammo==0): "弾薬が無くなったので、ターン終了。"]
Here's the breakdown of this macro:


  • Line 1 and 2 set two important variables: ammo and hit. We set ammo to 30, per the assumptions above, and we set hit to 1, so that the character always gets at least one attack roll (if we didn't set hit to 1, the loop might stop before it started!).
  • Line 4 is the start of the While Loop: we establish the loop, and give it a combined condition. We say that while ammo has a value greater than 30, and (remember, two ampersands is the logical operator "and") hit is equal to 1, the loop should go 'round. If either or both of those is not true, then the loop should stop. Also, we set the separator to <br>, so that a new line will be printed each time the loop runs.
  • Lines 5 - 9 handle the actual loop processing. Note that in that loop, we make sure to set a new value for hit and ammo - this is critical. If you never change the variables that your conditions are based on, then your loop will never stop.
  • 1行目と2行目では、二つの重要な変数:ammohitを設定している。上記の前提の通りammoには30を設定し、キャラクターは常に1回の攻撃ロールを行えるのでhitには1を設定している(もしhitに1を設定しなければ、この繰り返しは処理を開始する前に終わってしまう!)。
  • 4行目で、WHILEの繰り返しが始まる。繰り返しを作り、組み合わせた条件式を与えている。ammoの値が30より大きく、そして(二つのアンパサンド(&&)は論理演算子「AND(≒及び)」のことだ)hit1と等しい状態である間、この繰り返しは処理を繰り返しつづける。この条件の片方または両方が真ではなくなったとき、繰り返しは処理を止める。さらに、繰り返しが実行されるごとに、セパレーターの<br>が新しい行を出力する。
  • 5〜9行では、繰り返しの実際の処理がおこなわれる。この繰り返しでは、hitammoに新しい値を代入していることによくよく注意を払うように。これは危険を伴う。条件式はこの変数に基づいており、値が変更されなければ、この繰り返しは止まることなく永遠に処理をし続ける。
It's worth repeating: in a while loop, you must change the variable that your condition is checking, or you can end up with a loop that never stops.


So, the output of this macro will look something like:


Your attack expends 6 rounds, and hits. You have 24 rounds remaining.
Your attack expends 2 rounds, and hits. You have 22 rounds remaining.
Your attack expends 3 rounds, and misses. You have 19 rounds remaining. Your turn ends because you missed a target.



The next loop structure to address is the [for():] roll option. This option is somewhat similar to [while():], because it repeats a sequence of code a number of times based on a particular condition; it is also like [count():] because that particular condition is "has our counter reached a particular number yet?"

次に取り組む繰り返しの構造は、[for():] ロール・オプションだ。このオプションは、一連のコードを特定の条件に基づいた回数繰り返すので [while():] に少し似ている。また、特定の条件は「カウンターはまだ指定の値に届いているのか?」というものなので [count():] に似ているともいえる。

The general format for a [for():] loop is:

[for():] の繰り返しの一般的な書式は次の通り:

[for(counter, start, end, stepsize, separator): command]
[for(カウンター, 開始値, 終了値, 増分, 区切り文字): コマンド]
Here's how that breaks down:


  • [for():]: as always, we need to actually put the roll option in there
  • counter: this is the variable that will be used to count the iterations through the loop; typically people us a simple 1-letter variable in here, like x or i. For examples below, we'll use i.
  • start: this is what the counter variable starts at (it can be zero, another variable, or any other numeric value)
  • end: this is the value that ends the loop
    • In a loop where the counter variable is increasing - in other words, a loop with a positive stepsize - the loop runs as long as counter is less than end
    • In a loop where the counter variable is decreasing- in other words, a loop with a negative stepsize - the loop runs as long as counter is greater than end
  • stepsize: this is how big the increment is for each iteration of the loop (for example, if you set stepsize to 2, then counter will increase by 2 ever iteration). The default stepsize is +1 (that is, by default, the counter variable increments by 1 each time the loop is processed).
  • separator: like with [count():] and [while():], this is an optional separator to show between each output line from the loop; the default is a comma.
  • [for():]:いつも通り、ここにロール・オプションをおく必要がある
  • カウンター:繰り返しの処理中に反復回数を記録するための変数。一般的に、xi などの単純な1文字の変数を指定することが多い。後述の例では、i を使用している。
  • 開始値カウンター変数が最初に指定する値(この値は、ゼロや他の変数、あらゆる数値を指定可能)
  • 終了値:繰り返しが終了する値
    • カウンター変数が増加していく(増分が正の値の)場合は、カウンター変数が終了値より少ない限り処理を繰り返す。
    • カウンター変数が減少していく(増分が負の値の)場合は、カウンター変数が終了値より多い限り処理を繰り返す。
  • 増分:繰り返しの処理が巡るごとに増加する値(たとえば、増分を2としたら、繰り返しが続く限り、カウンターが2づつ増加する)。標準の増分は+1である(つまり、標準ではカウンター変数は繰り返しの処理ごとに1づつ増加していく)。
  • 区切り文字[count():][while():]と同じように、繰り返しごとに出力する任意の区切り文字。標準はコンマである。


This example illustrates how the [for():] option's various components work. As with the previous looping structure, this is a basic countdown:


[FOR(i,10,0,-2): "i is now " + i]
[FOR(i,10,0,-2): "i は " + i]
In this example, we have specified all components of the loop:


  • The counter variable is i.
  • The start value is 10
  • The end value is 0
  • The stepsize is -2 (the counter decrements by 2 every time the loop processes)
  • カウンター変数 i
  • 開始値 10
  • 終了値 0
  • 増分 -2 (繰り返しの処理を行うごとにカウンターを2づつ減少させる
The output of this will look like:


i は 10, i は 8, i は 6, i は 4, i は 2

You will note that there is no i is now 0 step - this is because when the counter counts down to 0, i is no longer greater than 0.

この例では i は 0 の段階には進まないことに注意するように。カウンターが 0 になれば、i はもはや 0 より大きい値ではなくなるからだ。


This example illustrates a practical use of the [for():] option to create a table that will be sent to chat, with a number of rows based on how many properties are in a String List.


[h:theList = "Strength, Endurance, Dexterity, Intelligence"]

[h:numberOfRows = listCount(theList)]

<table border="1">
[for(i, 0, numberOfRows, 1, ""):  "<tr><td>"+listGet(theList, i)+"</td></tr>"]
The example above uses a few cool commands to generate the output.


  • theList is simply a string list variable containing four elements
  • listCount() is a function that, when you put the name of a string list variable in it, will return the number of elements in the string list
  • We create the beginning of an HTML table by using the <table> tag
  • The [for():] loop here uses the counter variable i, which starts at 0, and counts up until it reaches numberOfRows. It increases by 1 each loop, and the default separator has been changed to "" so that no extraneous commas are printed to chat.
  • Inside the loop, we concatenate the HTML tags for table rows and table cells around the function listGet(). This function will retrieve, from a string list variable, the value of the element that is at the position specified in the second argument. In this case, we say, "get from the variable theList the value of the element that's in the same position as i" - so that it starts with element 0 (all lists start at item 0 in MapTool) and each time we loop through, it gets the next element.
  • At the end, we close the table with the appropriate HTML tag.
  • theListは4つの要素を持つ簡単な文字列リストの変数だ
  • listCount()は、文字列リストの変数名を与えると、リストの要素数を返す関数だ。
  • まず、<table>を使ってHTMLテーブルの開始を作成する。
  • [for():]の繰り返しのところでカウンター変数のiを使用している。この変数は 0 より始まり、numberOfRowsに達するまで繰り返しごとに1づつ増えていく。区切り文字は、不必要なコンマをチャットに出力しないように、標準のものから""に変更している。
  • 繰り返しの中では、テーブルの行とセルのHTMLタグをlistGet()の周りで結合している。この関数は、文字列リストの変数より、2つ目の引数として与えられた位置にある要素の値を取り出すものだ。今回の例では、「変数theListi番目にある要素の値を取り出すように」と伝えている。要素はゼロ番目から始まり(MapToolのリストはすべてゼロ番目から始まる)、繰り返しの終わりまで毎回取得するので、次の要素を取得する。
  • 最後に、適切なHTMLタグによってテーブルを閉じている。
The output looks like this. If you add in several more elements to the variable theList, the table will grow in size to accommodate the new elements.




The [for():] loop structure lets you repeat a set of commands a specified number of times, with flexible beginning, ending, and steps. That looping method is applicable to many things, and can be looked at as a very "general" way to loop - it gives you lots of flexibility with where you start and end, and can be used for many operations.


However, frequently loops are used to go through a list of items that is already established, and it is kind of a pain to have to make sure to count each list, then assign the variables in a [for():] loop, and make sure you can figure out how the counter variable corresponds to the position of an item in a list. So, a different kind of for loop - one that handles most of that without bothering you about it - also exists. This one is called [foreach():].


In a [foreach():] loop, the looping structure is given two arguments: the name of a string list, and a variable. The variable takes on the value of each element in the list, in turn, as the looping structure iterates. That's a bit confusing, so first, let's look at the general structure of a [foreach():] loop:


[foreach(item, list): command]
  • [foreach():]: once again, the roll option itself.
  • item: this is the variable that takes on the value of each successive element in the list or property
  • list: this is the string list you want the foreach() to work on
  • command: the operation you want performed on each successive item in list
  • [foreach():]:おなじみのロールオプション自身である。
  • item:リストやプロパティの一連の要素のそれぞれの値を保持する変数。
  • listforeach()で処理を行う文字列リスト
  • commandリスト内の一連の項目に対して実行する処理。

To explain that in plain English: assume for a moment that there you have the names of several targets (NPC tokens) that your character wants to attack at the same time. You need to make an attack roll against each target, which is equal to 1d20 + 7, but you have to roll separately for each target.


You could write out the attacks each time like this:


I attack target 1: [1d20+7]<br>
I attack target 2: [1d20+7]<br>
I attack target 3: [1d20+7]<br>
I attack target 4: [1d20+7]<br>
対象 1 への攻撃: [1d20+7]<br>
対象 2 への攻撃: [1d20+7]<br>
対象 3 への攻撃: [1d20+7]<br>
対象 4 への攻撃: [1d20+7]<br>

And so forth and so on. Not so bad if you have 3 targets. But what if you have 6? Or 12?


So instead, what if you created a single variable that was a list of the names of each target? Then, you can use the [foreach():] loop to go through the list one by one and let you make the roll with a very efficient little bit of code. [foreach():] may be a little hard to explain, but once you understand what it can do, you will see how useful it can be!

その代わりとして、各対象の名称のリストの変数を作るとしたらどうだろうか? リストの一つ一つを取り出しながら一か所のコードで効率的にロールを行うために、[foreach():]の繰り返しを使うことができる。[foreach():]は少しばかり説明が難しいが、何ができるかを理解すれば、いかに便利か解るだろう!

Now, let's jump to some examples.



This simple example will go through a string list, and list the value of each element in the list, from beginning to end.


[h:theList = "18, Bob, 29, Foo, 1009, Snorkel"]

[foreach(item, theList, "<br>"): item]

In the above macro, we've created a [foreach():] loop that takes the list variable theList, and goes to each element in that list, and assigns the value of that element to the variable item. We've set the separator to the HTML code for a line break, and then -- after the colon -- instructed the macro to print the value of the variable item to the chat window.

上記のマクロでは、リストの変数 theList を扱う [foreach():] を作り、リスト内の各要素を取り出し、その要素の値を変数 item に割り当てている。改行を表す HTML コードをセパレーターとして設定し、コロンに続けて変数 item の値をチャットウィンドウに出力するマクロ指定している。

The output of that macro looks like:



Do you see what happened there? The foreach() option went to each element in theList, said, "the variable item is now equal to the element," and then printed the value of item to chat.

なにが起きているの解っただろうか? この foreach() オプションは theList の各要素を取り出して「変数 item はこの要素と同じである」と宣言し、item の値をチャットウィンドウに出力している。


Now, let's look at the example described in the beginning. Remember how we had those target names to attack? Here's how we'd do that:


[h:targetList = "Orc 1, Goblin 2, Orc 4, Zombie 17, Big Boss"]

[foreach(target, targetList, "<br>"),CODE:
   [h:attackRoll = 1d20+7]
   I attack [r:target] with an attack roll of [r:attackRoll]
[h:targetList = "オーク 1, ゴブリン 2, オーク 4, ゾンビ 17, 親玉"]

[foreach(target, targetList, "<br>"),CODE:
   [h:attackRoll = 1d20+7]
   [r:target] に攻撃し、攻撃ロールは [r:attackRoll] 。
  • The first line simply sets up the variable targetList, as discussed.
  • The foreach loop goes through each element in targetList, assigning its value to the variable target. So, the first time through, target has the value of the first item in targetList - in other words, target is equal to Orc 1. The second time through, target equals the second item in the list, and so on.
  • The [code():] roll option is used so we can execute multiple commands (and because it sometimes makes formatting the output a little easier)
  • Inside the CODE brackets, we calculate a variable called attackRoll. This is recalculated on every loop, so it's different for each target.
  • Finally, we generate some chat output, inserting the variable target and the variable attackRoll in the appropriate places.
  • 前述の通り、最初の行は変数 targetList を定義している。
  • foreach の繰り返しを使い targetList の各要素を取り出し、その値を変数 target に割り当てている。最初の繰り返しでは targettargetList の最初の値(つまり targetオーク 1となる)を持っている。繰り返しの2回目では target はリストの2番目の値となり、以降順々に繰り返されていく。
  • 複数のコマンドを実行するためにロールオプション [code():] を使用している(出力の書式を簡単にするために使うこともある)。
  • CODE の確固の中で、attackRollと名付けた変数を計算している。この変数は繰り返しごとに再々計算されるので、それぞれの target ごとに異なる結果となる。
  • 最後に、変数 targetattacRoll の値を埋め込みチャットへの出力を生成している。

The output of that macro, when sent to chat, looks like:


I attack Orc 1 with an attack roll of 12
I attack Goblin 2 with an attack roll of 11
I attack Orc 4 with an attack roll of 21
I attack Zombie 17 with an attack roll of 17
I attack Big Boss with an attack roll of 9

オーク 1 に攻撃し、攻撃ロールは 12
ゴブリン 2 に攻撃し、攻撃ロールは 11
オーク 4 に攻撃し、攻撃ロールは 21
ゾンビ 17 に攻撃し、攻撃ロールは 17
親玉 に攻撃し、攻撃ロールは 9


That about covers the looping basics for MapTool macros. The examples shown above are very simple, of course - just enough to show you how these work. But looping is incredibly useful for many applications in a MapTool macro, from generating multiple die rolls to building tables to editing token properties, so make sure to experiment with it.

ここでは、MapTool マクロにおける基本的な繰り返し処理を学んできた。上記の例は非常に単純なものである(もちろん例示する作業には純分なものだ)。しかし、繰り返し処理は MapTool マクロを、複数のダイスロールの生成から、テーブルの構築、トークンのプロパティの編集などに応用するうえで非常にに有用なものでありので、ぜひこの機能を試してもらいたい。