Macros for Dummies - A MapTool Macro-Tutorial (german)

Doc requests, organization, and submissions

Moderators: dorpond, trevor, Azhrei

Thargun
Giant
Posts: 188
Joined: Sun Sep 14, 2014 4:27 am

Re: Macros for Dummies - A guide to MapTool-Macros (german)

Post by Thargun »

17.6 - Checkboxen

Bei Checkboxen hat der User die Wahl sie zu aktivieren oder zu deaktivieren. Sie werden mit <input> erstellt. Zwar können Checkboxen auch wie Radio Buttons zu einer Gruppe zusammengefasst werden, da eine Mehrfachauswahl aber nicht möglich ist, bzw. Fehler verursacht, macht das keinen Sinn. Verwende in einem entsprechenden Fall also lieber Radio Buttons und lasse Checkboxen immer für sich alleine stehen, fasse sie also nicht zu einer Gruppe zusammen. Das Format einer Checkbox:

Code: Select all

<input type='checkbox' name='' value='' checked='checked'>
Die Attribute (alle Attribute müssen zwingend angegeben werden):

type
Für Checkboxen muss hier zwingend "checkbox" angegeben werden.

name
Der Name der Checkbox.

value
"value" stellt den eigentlichen Inhalt der Checkbox dar. Der hier angegebene Wert wird an das verarbeitende Makro übertragen wenn die Checkbox aktiviert wurde. Da der Wert bei der Ausgabe in einem Fenster nicht sichtbar ist, solltest du einen entsprechenden Hinweis auf die Funktion der Checkbox davor oder dahinter platzieren. Nur so kann der User sehen welche Funktion die Checkbox überhaupt hat.

checked
Dieses Attribut dient der Vorselektierung einer Checkbox. Wenn du die Vorselektierung verwendest ist die Checkbox also beim Aufruf des Formulars bereits angewählt. Der Wert des Attributs ist immer "checked".

Beispiel für das Formular:

Code: Select all

[h: processLink = macroLinkText("formProcess@Lib:makros") "all"]
<form action='[r: processLink]'>
  Waffenbonus aktivieren: <input type='checkbox' name='waffenBonus' value='1'>
  <br><br>
  Rüstungsbonus aktivieren: <input type='checkbox' name='ruestungBonus' value='1' checked='checked'>
  <br><br>
  <input type='submit' name='processButton' value='Klicken zum Senden'>
</form>
Image

Beim Empfangen der Daten im verarbeitenden Makro musst du bei Checkboxen aufpassen: ist die Checkbox aktiviert wird der der Wert (value) ganz normal übertragen. Ist die Checkbox aber nicht aktiviert wird sie überhaupt nicht übertragen, also weder Schlüssel noch Wert. Ein Beispiel für das verarbeitende Makro "formProcess":

Code: Select all

[h: daten = macro.args]
[r: daten]
Wie du siehst taucht die Checkbox in keiner Weise in der String Property List auf wenn sie nicht aktiviert wurde. Um also überhaupt ein verwertbares Ergebnis zu erhalten müsstest du vorher prüfen ob der Schlüssel (also der Name der Checkbox) in der String Property List vorhanden ist. Das geht aber leider nur auf Umwegen, daher greifen wir zu einem kleinen Trick. Wir prüfen nicht ob der gesuchte Schlüssel vorhanden ist, sondern ob genau dieser Schlüssel einen Wert hat oder nicht. Der Parser kann das natürlich nicht prüfen wenn es den Schlüssel nicht gibt, wird uns aber dann trotzdem einen Rückgabewert liefern, nämlich False. Ist dagegen der Schlüssel vorhanden existiert auch der passende Wert dazu, und der Parser wird uns True zurückliefern. Hier das Beispiel für das verarbeitende Makro "formProcess":

Code: Select all

[h: daten = macro.args]
[h,if(getStrProp(daten, "waffenBonus") != ""): checkbox1 = getStrProp(daten, "waffenBonus"); checkbox1 = "Nein"]
[h,if(getStrProp(daten, "ruestungBonus") != ""): checkbox2 = getStrProp(daten, "ruestungBonus"); checkbox2 = "Nein"]
Checkbox 1 aktiviert: [r: checkbox1]<br>
Checkbox 2 aktiviert: [r: checkbox2]
Wenn du als Übertragungsmethode ein JSON-Object statt einer String Property List nutzt, kannst du mit der Funktion json.contains direkt prüfen ob der Schlüssel vorhanden ist oder nicht. In beiden Fällen lohnt es sich in der Praxis natürlich diese Prüfung, bzw. die IF-Anweisung, gleich mit den jeweiligen Makro-Befehlen zu kombinieren, die ausgeführt werden sollen wenn die Checkbox aktiviert oder eben nicht aktiviert ist.

Thargun
Giant
Posts: 188
Joined: Sun Sep 14, 2014 4:27 am

Re: Macros for Dummies - A guide to MapTool-Macros (german)

Post by Thargun »

17.7 - Versteckte Daten

Manchmal, oder auch oft, wird es vorkommen, dass du mit einem Formular auch noch andere Daten, die nicht von einem Eingabefeld stammen, an das verarbeitende Makro weitergeben möchtest. Für diesen Fall gibt es unsichtbare Felder die du nutzen kannst, der User wird also nichts davon zu sehen bekommen. Diese unsichtbaren Felder werden mit <input> erstellt. Das Format:

Code: Select all

<input type='hidden' name='' value=''>
Die Attribute (alle Attribute müssen zwingend angegeben werden):

type
Für unsichtbare Felder muss hier zwingend "hidden" angegeben werden.

name
Der Name des unsichtbaren Feldes.

value
Der Inhalt des unsichtbaren Feldes, der dann an das verarbeitende Makro weitergegeben wird.

Beispiel für das Formular:

Code: Select all

[h: processLink = macroLinkText("formProcess@Lib:makros") "all"]
<form action='[r: processLink]'>
  In diesem Formular verstecken sich ein paar Daten.
  <br><br>
  <input type='hidden' name='testData' value='Im Formular sieht man von mir nichts.'>
  <input type='submit' name='processButton' value='Klicken zum Senden'>
</form>
Beispiel für das verarbeitende Makro "formProcess":

Code: Select all

[h: daten = macro.args]
[h: test.data = getStrProp(daten, "testData")]
[r: test.Data]

Thargun
Giant
Posts: 188
Joined: Sun Sep 14, 2014 4:27 am

Re: Macros for Dummies - A guide to MapTool-Macros (german)

Post by Thargun »

17.8 - Layout

Wie du bei den ganzen Beispielen in den letzten Kapiteln bestimmt gesehen hast sieht es nicht sehr schön aus, wenn wir ohne weitere Gestaltung Eingabefelder benutzen. Die Ausrichtung passt meistens einfach nicht. Deswegen empfehle ich dir auch hier wieder zur Gestaltung HTML-Tabellen zu verwenden. Schauen wir uns den Unterschied doch an einem einfachen Beispiel an. Auf das verarbeitende Makro verzichte ich dabei, denn es geht ja nur um das Layout des Formulars.

Code: Select all

[dialog("design", "width=260; height=460"): 
  {
    <html>
      <head>
        <title>
          Designbeispiel
        </title>
      </head>
      <body>
        [h: processLink = macroLinkText("formProcess@Lib:makros") "all"]
        <span style='font-size: 14pt; font-weight: bold'>Nicht so hübsch</span>
        <br><br>
        <form action='[r: processLink]'>
        Textfeld: <input type='text' name='textfeld1' size='15'>
        <br>
        Liste: <select name='list2' size='5'>
          <option>Eintrag 1</option>
          <option>Eintrag 2</option>
          <option selected='selected'>Eintrag 3</option>
          <option>Eintrag 4</option>
          <option>Eintrag 5</option>
        </select>
        <br>
        Checkbox 1: <input type='checkbox' name='checkbox1' value='1'>
        <br>
        Checkbox 2: <input type='checkbox' name='checkbox2' value='1'>
        <br><br><br><br>
        <span style='font-size: 14pt; font-weight: bold'>Schon besser</span>
        <br><br>
        <form action='[r: processLink]'>
          <table>
            <tr>
              <td>
                Textfeld:
              </td>
              <td>
                <input type='text' name='textfeld2' size='15'>
              </td>
            </tr>
            <tr>
              <td valign='top'>
                Liste:
              </td>
              <td>
                <select name='list2' size='5'>
                  <option>Eintrag 1</option>
                  <option>Eintrag 2</option>
                  <option selected='selected'>Eintrag 3</option>
                  <option>Eintrag 4</option>
                  <option>Eintrag 5</option>
                  </select>
              </td>
            </tr>
            <tr>
              <td>
                Checkbox 3:
              </td>
              <td>
                <input type='checkbox' name='checkbox3' value='1'>
              </td>
            </tr>
            <tr>
              <td>
                Checkbox 4:
              </td>
              <td>
                <input type='checkbox' name='checkbox4' value='1'>
              </td>
            </tr>
          </table>
        </form>
      </body>
    </html>
  }
]
Image

Bei der Gestaltung musst du auf eine Sache noch unbedingt achten: Checkboxen und Radio Buttons haben einen Rand um sich herum. Die Farbe dieses Rands entspricht der normalen Hintergrundfarbe von Fenstern (Dialoge & Frames) in MapTool. Abgesehen davon, dass dadurch Checkboxen und Radio Buttons etwas größer sind als auf den ersten Blick erkennbar, wirst du davon nicht viel merken wenn du die Hintergrundfarbe nicht änderst. Aber genau das machen wir jetzt. Ändere die Hintergrundfarbe für das body-Element im obigen Beispiel:

Code: Select all

<body style='background-color: #808000'>
Jetzt ist der Rand bei den Checkboxen deutlich erkennbar. Weder Farbe noch Größe des Rands lassen sich ändern. Das musst du also bei der Gestaltung deiner Formulare berücksichtigen.

Thargun
Giant
Posts: 188
Joined: Sun Sep 14, 2014 4:27 am

Re: Macros for Dummies - A guide to MapTool-Macros (german)

Post by Thargun »

18 - Bastelstunde III: Das Mini-Framework

18.1 - Vorbereitungen

Wir sind fast mit dem Tutorial durch. Als große Abschlussübung wollen wir zum Schluss noch ein kleines, aber komplettes Framework für ein imaginäres Rollenspielsystem entwickeln. Dazu gehören z. B. Charakterbogen, Würfelmakros, Hitpointverwaltung, Charaktereditor und diverse Inventarsysteme. Dabei werden wir Gelerntes wiederholen, ein paar neue Dinge lernen, die Arbeit mit Datentypen vertiefen und eine Menge Praxiserfahrung sammeln.

Ich werde nicht mehr jede Kleinigkeit erklären, denn das Meiste kennst du ja schon. Wenn du trotzdem irgendwo Probleme bekommst lese nochmal in dem entsprechenden Kapitel zum Thema nach. Aber keine Angst, neue Funktionen und verwendete Verfahrensweisen werden natürlich ausführlich genug erläutert. Außerdem werden wir den Code ordentlich dokumentieren. Mache dir keine Sorgen wenn du nicht gleich alles verstehst. Schaue dir den Code einfach Zeile für Zeile an und versuche herauszufinden was genau da passiert.

Ich habe das fertige Framework für dich zum Download bereitgestellt. Wenn du möchtest lade es herunter und probiere es aus. Dann weißt du wenigstens was du in den nächsten Kapiteln bastelst, und kannst vielleicht den Erklärungen besser folgen.

So, bevor es richtig losgeht müssen noch ein paar Vorbereitungen getroffen werden:

1. Wir wollen keine Altlasten, erstelle also eine komplett neue Kampagne und speichere sie. Mit dieser neuen Kampagne werden wir arbeiten.

2. Erstelle ein Library-Token mit dem Namen "Lib:makros".

3. Wir werden die Bilder der Mapool Standardtabellen "D6" und "D20" verwenden. Öffne also das Tabellenfenster und prüfe ob diese Tabellen vorhanden sind. Wenn nicht, klicke oben im MapTool-Menü auf "Hilfe --> Standardtabelle hinzufügen". Warte kurz und öffne erneut das Tabellenfenster. Jetzt sollten die Standardtabellen vorhanden sein.

4. Füge über die Kampagneneigenschaften folgende Properties mit Standardwerten hinzu:

Code: Select all

Kraft:13
Geschick:12
Klugheit:10
Angriff:12
Verteidigung:11
Ruestung:2
HP:40
HPmax:40
Klasse:Krieger
Waffen:WaffenAnzahl=0; Waffe1Name=Faust; Waffe1Bonus=0; Waffe1Schaden=1
Zauber
Inventar:[]
  • Kraft, Geschick und Klugheit sind die Attribute eines Charakters. Um eine Probe auf ein Attribut zu bestehen muss gleich oder niedriger gewürfelt werden.
  • Angiff und Verteidigung werden im Kampf für Attacke und Parade genutzt. Für einen Erfolg muss mit 1W20 gleich oder niedriger gewürfelt werden.
  • Die Ruestung wird im Kampf vom Schaden abgezogen.
  • HP ist die aktuelle Anzahl an Hitpoints (Lebensenergie) eines Charakters, während HPmax für die maximale Anzahl an Hitpoints steht, die der Charakter haben kann.
  • Klasse ist die Charakterklasse. Es wird Krieger, Schurken und Magier geben.
  • Im Property Waffen werden wir mit einer String Property List die Waffenliste mit verschiedenen Angaben zu jeder Waffe speichern. Warum wir den Eintrag "WaffenAnzahl" benötigen wirst du später noch sehen. Am Anfang hat jeder Charakter nur seine eigene Faust als Waffe.
  • Im Property Zauber werden mit einer String-Liste die verschiedenen Zauber gespeichert. Am Anfang haben die Charaktere noch keine Zauber.
  • Im Property Inventar werden alle Gegenstände mit verschiedenen Angaben gespeichert die der Charakter dabei hat. Dafür werden wir JSOn-Objects innerhalb eines JSON-Arrays verwenden. Als Standardwert ist ein leeres JSON-Array notiert, denn am Anfang haben die Charaktere noch nichts.

Thargun
Giant
Posts: 188
Joined: Sun Sep 14, 2014 4:27 am

Re: Macros for Dummies - A guide to MapTool-Macros (german)

Post by Thargun »

18.2 - Charakterbogen

18.2.1 - Charakterbogen: Das Makro

Code: Select all

[h: "<!-- Pruefe auf verkoerpertes Token -->"]

[h: assert(hasImpersonated(), "<span style='font-weight: bold'>Fehler!</span> Du musst erst einen Token verkörpern.", 0)]

[h: "<!-- Beginn des Charakterbogens -->"]

[frame("charbogen", "width=260; height=400; temporary=1"): 
  {
    <html>
      <head>
        <title>
          Charakterbogen
        </title>
      </head>
      <body style='text-align: center; background-color: a4c8a4'>

        [h: "<!-- Variablen fuer den Kopfbereich definieren -->"]

        [h: char.bild = getTokenImage(50)]
        [h: char.name = getImpersonatedName()]
        [h: char.klasse = Klasse]

        [h: "<!-- Ausgabe des Kopfbereichs -->"]

        [r: strformat("
        <table style='border-spacing: 0px; background-color: #008000; border: 2px solid #008000; width: 220'>
          <tr>
            <td style='text-align: center; padding: 6px'>
              <img src='%s'>
            </td>
            <td style='color: #ffffff; width: 150'>
              <span style='font-size: 14pt; font-weight: bold'>%s</span>
              <br>
              Klasse: %s
            </td>
          </tr>
        </table>
        ", char.bild, char.name, char.klasse)]

        [h: "<!-- Variablen fuer Charakterattribute und Kampfwerte definieren -->"]

        [h: char.kraft.link = macroLink("Kraft", "probeAttribut@Lib:makros", "all", "Kraft")]
        [h: char.geschick.link = macroLink("Geschick", "probeAttribut@Lib:makros", "all", "Geschick")]
        [h: char.klugheit.link = macroLink("Klugheit", "probeAttribut@Lib:makros", "all", "Klugheit")]

        [h: char.kraft = Kraft]
        [h: char.geschick = Geschick]
        [h: char.klugheit = Klugheit]

        [h: char.angriff = Angriff]
        [h: char.verteidigung = Verteidigung]
        [h: char.ruestung = Ruestung]

        [h: "<!-- Ausgabe von Charakterattributen und Kampfwerten -->"]

        [r: strformat("
        <table style='border-spacing: 0px; border-left: 2px solid #008000; border-right: 2px solid #008000; background-color: #ffffff; width: 220'>
          <tr>
            <td valign='middle'>
              <table style='border-spacing: 0px'>
                <tr>
                  <td>
                    <span style='color: #000000; text-decoration: none'>%s:</span>
                  </td>
                  <td style='font-weight: bold; text-align: right'>
                    %s
                  </td>
                </tr>
                <tr>
                  <td>
                    <span style='color: #000000; text-decoration: none'>%s:</span>
                  </td>
                  <td style='font-weight: bold; text-align: right'>
                    %s
                  </td>
                </tr>
                <tr>
                  <td>
                    <span style='color: #000000; text-decoration: none'>%s:</span>
                  </td>
                  <td style='font-weight: bold; text-align: right'>
                    %s
                  </td>
                </tr>
              </table>
            </td>
            <td style='width: 20px'>
               
            </td>
            <td>
              <table style='border-spacing: 0px'>
                <tr>
                  <td>
                    Angriff:
                  </td>
                  <td style='font-weight: bold; text-align: right'>
                    %s
                  </td>
                </tr>
                <tr>
                  <td>
                    Verteidigung:
                  </td>
                  <td style='font-weight: bold; text-align: right'>
                    %s
                  </td>
                </tr>
                <tr>
                  <td>
                    Rüstung:
                  </td>
                  <td style='font-weight: bold; text-align: right'>
                    %s
                  </td>
                </tr>
              </table>
            </td>
          </tr>
        </table>
        ", char.kraft.link, char.kraft, char.geschick.link, char.geschick, char.klugheit.link, char.klugheit, char.angriff, char.verteidigung, char.ruestung)]

        [h: "<!-- Variablen fuer HP Anzeige definieren -->"]

        [h: hp.prozent = (HP / HPmax) * 100]
        [h: hp.prozent = round(hp.prozent)]
        [h,if(hp.prozent < 51): hp.farbe = "#FF8000"; hp.farbe = "#28a328"]
        [h,if(hp.prozent < 26): hp.farbe = "#FF0000"; hp.farbe = hp.farbe]

        [h: hp.plus.link = macroLink("⇑", "hpPlus@Lib:makros", "all")]
        [h: hp.minus.link = macroLink("⇓", "hpMinus@Lib:makros", "all")]

        [h: "<!-- HP Anzeige -->"]

        [r: strformat("
        <table style='border-spacing: 0px; border-left: 2px solid #008000; border-right: 2px solid #008000; background-color: #ffffff; padding: 6px 0px 10px 0px; width: 220' cellpadding='0'>
          <tr>
            <td>
               
            </td>
            <td>
              <span style='font-weight: bold'>HP:</span> %s/%s
            </td>
            <td style='border: 1px solid %s; width: 100'>
              <table style='border-spacing: 0px'>
                <tr>
                  <td style='background-color: %s; width: %s'>
                  </td>
                </tr>
              </table>
            </td>
            <td style='text-align: right'>
              ( <span style='text-decoration: none; color: #000000'>%s</span> / <span style='text-decoration: none; color: #000000'>%s</span> )
            </td>
            <td>
               
            </td>
          </tr>
        </table>
        ", HP, HPmax, hp.farbe, hp.farbe, hp.prozent, hp.plus.link, hp.minus.link)]         

        [h: "<!-- Variablen fuer die Ueberschrift des Waffenbereichs definieren -->"]

        [h: neu.waffe.link = macroLink("Neu", "waffeNeu@Lib:makros")]
        [h: loesche.waffe.link = macroLink("Löschen", "waffeLoeschen@Lib:makros")]

        [h: "<!-- Ueberschrift des Waffenbereichs ausgeben -->"]

        [r: strformat("
        <table style='border-spacing: 0px; color: #ffffff; border-left: 2px solid #008000; border-right: 2px solid #008000; background-color: #008000; width: 220'>
          <tr>
            <td style='font-weight: bold; width: 110'>
              Waffen
            </td>
            <td style='text-align: right; width: 110' valign='bottom'>
              <span style='font-size: 10pt; color: #ffffff; text-decoration: none'>(%s | %s)</span>
            </td>
          </tr>
        </table>
        ", neu.waffe.link, loesche.waffe.link)]

        [h: "<!-- Ueberschrift der Waffenliste ausgeben-->"]

        [r: "<table style='border-spacing: 0px; border-left: 2px solid #008000; border-right: 2px solid #008000; background-color: #ffffff; width: 220'>
          <tr style='font-style: italic; border-bottom: 1px solid'>
            <td style='width: 115'>
              Waffe
            </td>
            <td style='text-align: center; width: 20'>
              B
            </td>
            <td style='text-align: center; width: 25'>
              A
            </td>
            <td style='text-align: center; width: 25'>
              V
            </td>
            <td style='text-align: center; width: 35'>
              S
            </td>
          </tr>
        </table>"]

        [h: "<!-- Beginn der Waffenliste -->"]

        [r: "<table style='border-spacing: 0px; border-left: 2px solid #008000; border-right: 2px solid #008000; background-color: #ffffff; width: 220'>"]

        [h: "<!-- Ausgabe wenn keine Waffen vorhanden sind -->"]

        [r,if(getStrProp(Waffen, "WaffenAnzahl") == 0), code:
          {
            [r: "<tr>
              <td style='text-align: center; width: 220'>
                Keine Waffen vorhanden
              </td>
            </tr>"]
          };{}
        ]

        [h: "<!-- Waffennummer festlegen und Schleife starten wenn Waffen vorhanden sind -->"]

        [h: num = 1]
        [r,count(getStrProp(Waffen, "WaffenAnzahl"), ""), code:
          {
            [h: "<!-- Variablen fuer Waffe definieren -->"]

            [h: waffe.name = getStrProp(Waffen, strformat("Waffe%{num}Name"))]
            [h: waffe.bonus = getStrProp(Waffen, strformat("Waffe%{num}Bonus"))]
            [h: waffe.schaden = getStrProp(Waffen, strformat("Waffe%{num}Schaden")) + "W6"]
            [h: waffe.angriff = Angriff + waffe.Bonus]
            [h: waffe.verteidigung = Verteidigung + waffe.Bonus]
            [h: angriff.link = macroLink(waffe.angriff, "probeKampf@Lib:makros", "all", strformat("Angriff, %s", num))]
            [h: verteidigung.link = macroLink(waffe.verteidigung, "probeKampf@Lib:makros", "all", strformat("Verteidigung, %s", num))]

            [h: "<!-- Tabellenzeile fuer Waffe ausgeben -->"]

            [r: strformat("
              <tr>
                <td style='width: 115'>
                  %s
                </td>
                <td style='text-align: center; width: 20'>
                  %s
                </td>
                <td style='text-align: center; width: 25'>
                  <span style='font-weight: bold; color: #000000; text-decoration: none'>%s</span>
                </td>
                <td style='text-align: center; width: 25'>
                  <span style='font-weight: bold; color: #000000; text-decoration: none'>%s</span>
                </td>
                <td style='text-align: center; width: 35'>
                  %s
                </td>
              </tr>
            ", waffe.name, waffe.Bonus, angriff.link, verteidigung.link, waffe.schaden)]

            [h: "<!-- Waffennummer erhoehen -->"] 

            [h: num = num + 1]
          }
        ]

        [h: "<!-- Tabelle der Waffenliste schliessen -->"]

        [r: "</table>"]   

        [h: "<!-- Variablen fuer die Ueberschrift des Zauberbereichs definieren -->"]

        [h: neu.zauber.link = macroLink("Neu", "zauberNeu@Lib:makros")]
        [h: loesche.zauber.link = macroLink("Löschen", "zauberLoeschen@Lib:makros")]

        [h: "<!-- Ueberschrift des Zauberbereichs ausgeben -->"]

        [r: strformat("
        <table style='border-spacing: 0px; color: #ffffff; border-left: 2px solid #008000; border-right: 2px solid #008000; background-color: #008000; width: 220'>
          <tr>
            <td style='font-weight: bold; width: 110'>
              Zauber
            </td>
            <td style='text-align: right; width: 110' valign='bottom'>
              <span style='font-size: 10pt; color: #ffffff; text-decoration: none'>(%s | %s)</span>
            </td>
          </tr>
        </table>
        ", neu.zauber.link, loesche.zauber.link)]

        [h: "<!-- Tabelle der Zauberliste erstellen -->"]

        [r: "<table style='border-spacing: 0px; border-left: 2px solid #008000; border-right: 2px solid #008000; background-color: #ffffff; width: 220'>"]

        [h: "<!-- Ausgabe wenn keine Zauber vorhanden sind -->"]

        [r,if(listCount(Zauber) == 0), code:
          {
            [r: "<tr>
              <td style='text-align: center; width: 220'>
                Keine Zauber vorhanden
              </td>
           </tr>"]
          };{}
        ]

        [h: "<!-- Schleife starten und Tabellenzeilen ausgeben wenn Zauber vorhanden sind -->"]

        [r,foreach(item, Zauber, ""), code:
          {
            [r: strformat("
            <tr>
              <td style='border-bottom: 1px solid; width: 220'>
                %s
              </td>
            </tr>
            ", item)]
          }
        ]

        [h: "<!-- Tabelle der Zauberliste schliessen -->"]

        [r: "</table>"] 

        [h: "<!-- Variablen fuer die Fusszeile definieren -->"]

        [h: charedit.link = macroLink("Charaktereditor", "charedit@Lib:makros")]
        [h: ini.link = macroLink("Initiative", "iniWurf@Lib:makros", "all")]
        [h: inventar.link = macroLink("Inventar", "inventar@Lib:makros")]

        [h: "<!-- Ausgabe der Fusszeile -->"]

        [r: strformat("
        <table style='border-spacing: 0px; border-left: 2px solid #008000; border-right: 2px solid #008000; background-color: #008000; width: 220'>
          <tr>
            <td style='text-align: left; width: 110'>
              <span style='color: #ffffff; text-decoration: none'>%s</span>
            </td>
            <td style='text-align: right; width: 110'>
              <span style='color: #ffffff; text-decoration: none'>%s | %s</span>
            </td>
          </tr>
        </table>
        ", charedit.link, ini.link, inventar.link)]

[h: "<!-- Ende des Charakterbogens -->"]

      </body>
    </html>
  }
]

Thargun
Giant
Posts: 188
Joined: Sun Sep 14, 2014 4:27 am

Re: Macros for Dummies - A guide to MapTool-Macros (german)

Post by Thargun »

18.2.2 - Charakterbogen: Die Erklärung

Wir beginnen mit dem Charakterbogen von dem aus auch alles andere gesteuert wird. Es kann hilfreich sein wenn du dir in einem weiteren Fenster das komplett fertige Makro anzeigen lässt, damit du auch immer den kompletten Code vor Augen hast und die Zusammenhänge besser erkennen kannst (das gilt auch bei allen anderen Makros die wir noch schreiben). Damit du die Erklärungen besser nachvollziehen kannst schauen wir uns schon mal das fertige Ergebnis an.

Image

Gut, erstelle zuerst ein neues Makro mit dem Namen "charbogen" im Library-Token. Damit die Spieler auch ihre Charakterbögen aufrufen können, erstelle ein weiteres Makro im Kampagnenfenster (Campaign), nenne es "Charakterbogen", und rufe damit das eigentliche Charakterbogen-Makro auf:

Code: Select all

[macro("charbogen@Lib:makros"): ""]
Jetzt aber zum Charakterbogen, den wir in verschiedene Bereiche gliedern:
  • Zuerst der Kopfbereich mit dem Tokenbild, dem Charakternamen und der Charakterklasse.
  • Der nächste Bereich darunter beinhaltet die Charakterattribute und die Grundwerte für den Kampf.
  • Anschließend kommt die Anzeige der Hitpoints inkl. eines Balkens, damit der aktuelle Stand auch graphisch angezeigt wird.
  • Die nächsten zwei Bereiche zeigen die Waffen- und Zauberliste.
  • Ganz unten kommt dann noch eine Fußzeile in der wir das Inventar, den Charaktereditor und den Initiativewurf verlinken.
Im Charakterbogen prüfen wir ganz oben mit assert() ob der User überhaupt einen Token verkörpert hat (impersonated). Wenn nicht wird hier direkt abgebrochen und eine Fehlermeldung erscheint:

Code: Select all

[h: assert(hasImpersonated(), "<span style='font-weight: bold'>Fehler!</span> Du musst erst einen Token verkörpern.", 0)]
Jetzt notieren wir den Code für das Fenster (wir benutzen ein Frame mit dem Namen "charbogen") inkl. des grundsätzlichen HTML-Aufbaus. Alles was wir danach an Code schreiben kommt in das body-Element, also zwischen <body> und </body>.

Code: Select all

[frame("charbogen", "width=260; height=400; temporary=1"):
  {
    <html>
      <head>
        <title>
          Charakterbogen
        </title>
      </head>
      <body style='text-align: center; background-color: a4c8a4'>

      </body>
    </html>
  }
]
Nun sind die einzelnen Bereiche an der Reihe. Nach jedem fertiggestellten Bereich kannst du zum Testen den Charakterbogen aufrufen und dir anschauen wie dieser im Moment aussieht.


Der Kopfbereich

Eine kleine Zwischenbemerkung: Das Festlegen von Größenangaben kann schwierig, sogar eine echte Herausforderung sein, denn oft weißt du am Anfang noch nicht wie viel Platz die untergeordneten Inhalte benötigen. Daher wirst du die Größenangaben wahrscheinlich zwischendurch immer mal wieder anpassen müssen, oder legst sie vielleicht erst später fest. Bei unserem kleinen Mini-Framework stimmen aber alle Angaben, denn ich habe sie vorher schon abgemessen.

Jetzt der Kopfbereich. Da wir mit strformat() arbeiten wollen um den Stack-Speicher von MapTool etwas zu entlasten, müssen wir vorher die Variablen für Charakterbild, Charaktername und Charakterklasse definieren:

Code: Select all

[h: char.bild = getTokenImage(50)]
[h: char.name = getImpersonatedName()]
[h: char.klasse = Klasse]
Kommen wir zur Tabelle für den Kopfbereich mit strformat(). Beachte, dass ich den Anfang der Funktion [r: strformat(" und das Ende der Funktion ", variable1, variable2, variable3)] jeweils in einer separaten Zeile notiert habe. Das macht es einfach übersichtlicher, und MapTool macht das nichts aus.

Code: Select all

[r: strformat("
<table style='border-spacing: 0px; background-color: #008000; border: 2px solid #008000; width: 220'>
  <tr>
    <td style='text-align: center; padding: 6px'>
      <img src='%s'>
    </td>
    <td style='color: #ffffff; width: 150'>
      <span style='font-size: 14pt; font-weight: bold'>%s</span>
      <br>
      Klasse: %s
    </td>
  </tr>
</table>
", char.bild, char.name, char.klasse)]
Charakterwerte

Als nächstes der Bereich mit den Charakterattributen und Kampfwerten, auch hier legen wir erst die Variablen für die Werte fest. Allerdings wollen wir, dass der User bei einem Mausklick auf den Namen eines Charakterattributs gleich eine Probe auf das Attribut würfeln kann. Daher erstellen wir mit macroLink() auch Links zu unserem Probenmakro "probeAttribut", das wir später noch einfügen. Außerdem geben wir bei den Links Daten an das Probenmakro weiter, nämlich den Namen des jeweiligen Attributs als String. Wofür das gut ist siehst du ebenfalls später.

Code: Select all

[h: char.kraft.link = macroLink("Kraft", "probeAttribut@Lib:makros", "all", "Kraft")]
[h: char.geschick.link = macroLink("Geschick", "probeAttribut@Lib:makros", "all", "Geschick")]
[h: char.klugheit.link = macroLink("Klugheit", "probeAttribut@Lib:makros", "all", "Klugheit")]

[h: char.kraft = Kraft]
[h: char.geschick = Geschick]
[h: char.klugheit = Klugheit]

[h: char.angriff = Angriff]
[h: char.verteidigung = Verteidigung]
[h: char.ruestung = Ruestung]
Und jetzt die Tabelle zur Ausgabe, wieder mit strformat():

Code: Select all

[r: strformat("
<table style='border-spacing: 0px; border-left: 2px solid #008000; border-right: 2px solid #008000; background-color: #ffffff; width: 220'>
  <tr>
    <td valign='middle'>
      <table style='border-spacing: 0px'>
        <tr>
          <td>
            <span style='color: #000000; text-decoration: none'>%s:</span>
          </td>
          <td style='font-weight: bold; text-align: right'>
            %s
          </td>
        </tr>
        <tr>
          <td>
            <span style='color: #000000; text-decoration: none'>%s:</span>
          </td>
          <td style='font-weight: bold; text-align: right'>
            %s
          </td>
        </tr>
        <tr>
          <td>
            <span style='color: #000000; text-decoration: none'>%s:</span>
          </td>
          <td style='font-weight: bold; text-align: right'>
            %s
          </td>
        </tr>
      </table>
    </td>
    <td style='width: 20px'>
       
    </td>
    <td>
      <table style='border-spacing: 0px'>
        <tr>
          <td>
            Angriff:
          </td>
          <td style='font-weight: bold; text-align: right'>
            %s
          </td>
        </tr>
        <tr>
          <td>
            Verteidigung:
          </td>
          <td style='font-weight: bold; text-align: right'>
            %s
          </td>
        </tr>
        <tr>
          <td>
            Rüstung:
          </td>
          <td style='font-weight: bold; text-align: right'>
            %s
          </td>
        </tr>
      </table>
    </td>
  </tr>
</table>
", char.kraft.link, char.kraft, char.geschick.link, char.geschick, char.klugheit.link, char.klugheit, char.angriff, char.verteidigung, char.ruestung)]
Die HP-Anzeige

Kommen wir zur Hitpoint-Anzeige, die wir unter den Charakterwerten mittig platzieren wollen. Die Anzeige soll die aktuellen und maximalen HP in Zahlen und zusätzlich in Form eines Balkens darstellen. Der Balken soll außerdem die Farbe wechseln sobald die aktuellen HP auf 50% und 25% sinken.

Fangen wir wieder mit ein paar Variablen an. Zuerst müssen wir herausfinden wie viel Prozent unserer HP wir noch haben, das geht mit einfacher Prozentrechnung. Wir dividieren also die aktuellen HP durch die maximalen HP und multiplizieren das Ergebnis mit 100. Danach runden wir das Ganze noch.

Code: Select all

[h: hp.prozent = (HP / HPmax) * 100]
[h: hp.prozent = round(hp.prozent)]
Mit zwei IF-Anweisungen bestimmen wir die Balkenfarbe. Besitzt der Charakter weniger als 51% der HP ist die Balkenfarbe orange (#FF8000), ansonsten grün (#28a328). Die zweite IF-Anweisung prüft nochmal ob der Charakter weniger als 26% seiner HP besitzt, und setzt die Farbe in diesem Fall auf rot (#FF0000).

Code: Select all

[h,if(hp.prozent < 51): hp.farbe = "#FF8000"; hp.farbe = "#28a328"]
[h,if(hp.prozent < 26): hp.farbe = "#FF0000"; hp.farbe = hp.farbe]
Jetzt noch schnell die Variablen für die Links zu den Makros um HP hinzuzufügen oder abzuziehen. Als Linktext nutzen wir mit HTML maskierte Zeichen, nämlich den Doppelpfeil nach oben (⇑) und den Doppelpfeil nach unten (⇓).

Code: Select all

[h: hp.plus.link = macroLink("⇑", "hpPlus@Lib:makros", "all")]
[h: hp.minus.link = macroLink("⇓", "hpMinus@Lib:makros", "all")]
Der erste Teil der Tabelle für die HP-Anzeige:

Code: Select all

[r: strformat("
<table style='border-spacing: 0px; border-left: 2px solid #008000; border-right: 2px solid #008000; background-color: #ffffff; padding: 6px 0px 10px 0px; width: 220' cellpadding='0'>
  <tr>
    <td>
       
    </td>
    <td>
      <span style='font-weight: bold'>HP:</span> %s/%s
    </td>
"cellpadding" kennst du noch nicht. Das ist ein HTML-Attribut mit dem sich der Abstand vom Inhalt einer Zelle zum Rand festlegen lässt. In Fenstern fügt MapTool automatisch so einen Abstand ein, was in den meisten Fällen auch in Ordnung ist, aber beim HP-Balken stören würde. Da wir diesen Abstand auch nicht mit der CSS-Eigenschaft "padding" entfernen können, müssen wir also "cellpadding" mit dem Wert "0" notieren.

Schauen wir uns nun die nächste Zelle der Tabelle an, die zur Darstellung des HP-Balkens genutzt wird:

Code: Select all

    <td style='border: 1px solid %s; width: 100'>
      <table style='border-spacing: 0px'>
        <tr>
          <td style='background-color: %s; width: %s'>
          </td>
        </tr>
      </table>
    </td>
Diese Zelle ist unser kompletter HP-Balken, stellt also die maximalen HP dar, weswegen sie unbedingt eine Breite von 100 Pixeln (100%) haben muss. In dieser Zelle erstellen wir eine weitere Tabelle mit nur einer Zelle und ohne Inhalt. Diese Tabelle stellt die aktuellen HP dar, um ihre Breite festzulegen benutzen wir also die Variable "hp.prozent" und für ihre Hintergrundfarbe die Variable "hp.farbe". Das wird deutlicher wenn wir uns den Rest der kompletten Tabelle anschauen:

Code: Select all

    <td style='text-align: right'>
      ( <span style='text-decoration: none; color: #000000'>%s</span> / <span style='text-decoration: none; color: #000000'>%s</span> )
    </td>
    <td>
       
    </td>
  </tr>
</table>
", HP, HPmax, hp.farbe, hp.farbe, hp.prozent)]
Beachte, dass wir die Variable "hp.farbe" zweimal nutzen: für den Rahmen des kompletten HP-Balkens und als Hintergrundfarbe für die Tabelle, die den Balken für die aktuellen HP darstellt.


Der Waffenbereich

Nun ist die Waffenliste an der Reihe. Beginnen wir mit einer Art Überschrift für den gesamten Waffenbereich um die Waffen optisch etwas vom oberen Teil abzutrennen. Auf der rechten Seite fügen wir Links ein um neue Waffen hinzuzufügen oder zu löschen. Definieren wir erst die Variablen für die Links:

Code: Select all

[h: neu.waffe.link = macroLink("Neu", "waffeNeu@Lib:makros")]
[h: loesche.waffe.link = macroLink("Löschen", "waffeLoeschen@Lib:makros")]
Und jetzt die Tabelle für die Überschrift:

Code: Select all

[r: strformat("
  <table style='border-spacing: 0px; color: #ffffff; border-left: 2px solid #008000; border-right: 2px solid #008000; background-color: #008000; width: 220'>
    <tr>
      <td style='font-weight: bold; width: 110'>
        Waffen
      </td>
      <td style='text-align: right; width: 110' valign='bottom'>
        <span style='font-size: 10pt; color: #ffffff; text-decoration: none'>(%s | %s)</span>
      </td>
    </tr>
  </table>
", neu.waffe.link, loesche.waffe.link)]
Anschließend eine weitere Tabelle als Überschrift der eigentlichen Waffenliste. Da wir in dem kleinen Charakterbogen allerdings nicht so viel Platz haben benutzen wir Abkürzungen. "B" steht für "Bonus", "A" für "Angriff", "V" für "Verteidigung" und "S" für "Schaden". Und weil wir hier keine Variablen benötigen geben wir das einfach als String aus:

Code: Select all

[r: "<table style='border-left: 2px solid #008000; border-right: 2px solid #008000; background-color: #ffffff; width: 220'>
  <tr style='font-style: italic; border-bottom: 1px solid'>
    <td style='width: 115'>
      Waffe
    </td>
    <td style='text-align: center; width: 20'>
      B
    </td>
    <td style='text-align: center; width: 25'>
      A
    </td>
    <td style='text-align: center; width: 25'>
      V
    </td>
    <td style='text-align: center; width: 35'>
      S
    </td>
  </tr>
</table>"]
Und noch eine Tabelle, jetzt mit der Auflistung der Waffen. Allerdings nur der erste Teil ohne Zeilen und Zellen:

Code: Select all

[r: "<table style='border-spacing: 0px; border-left: 2px solid #008000; border-right: 2px solid #008000; background-color: #ffffff; width: 220'>"]
Wie es mit der Tabelle weitergeht hängt davon ab ob der Charakter überhaupt Waffen dabei hat oder nicht. Wir prüfen mit einer IF-Anweisung also die Waffenanzahl. Dafür rufen wir in der String Property List des Propertys "Waffen" den Wert für den Schlüssel "WaffenAnzahl" ab. Ist die Anzahl "0" wird eine Tabellenzeile mit einer Zelle und einer entsprechenden Meldung ausgegeben. Ist die Anzahl aber nicht "0", hat der Charakter also mindestens eine Waffe dabei, passiert gar nichts.

Code: Select all

[r,if(getStrProp(Waffen, "WaffenAnzahl") == 0), code:
  {
    [r: "<tr>
      <td style='text-align: center; width: 220'>
        Keine Waffen vorhanden
      </td>
    </tr>"]
  };{}
]
Wenn eine oder mehrere Waffen vorhanden sind sollen diese nun mit ihren verschiedenen Werten (Name, Bonus, Angriff, Verteidigung, Schaden) jeweils in einer eigenen Tabellenzeile aufgelistet werden. Das machen wir mit einer Count-Schleife. Die Anzahl der Durchläufe bestimmen wir logischerweise mit der Anzahl der Waffen. Sind also keine Waffen vorhanden, gibt es keinen einzigen Durchlauf und es wird nichts ausgegeben. Vor der Schleife definieren wir noch die neue Variable "num" als Waffennummer.

Code: Select all

[h: num = 1]
[r,count(getStrProp(Waffen, "WaffenAnzahl"), ""), code:
  {

  }
]
Kommen wir zum Inhalt der Count-Schleife. Bei jedem Durchlauf müssen wir zuerst alle Variablen festlegen die wir für die aktuelle Waffe benötigen:
  • Der Name der Waffe
  • Der Waffenbonus, der bei jeder Waffe zum Grundangriffs- und Grundverteidigungswert dazugerechnet wird.
  • Der Schaden den die Waffe verursacht. In unserem System macht jede Waffe entweder 1W6, 2W6 oder 3W6 Schaden.
  • Der endgültige Angriffswert der Waffe, der sich aus dem Grundangriffswert und dem Waffenbonus zusammensetzt.
  • Der endgültige Verteidigungswert der Waffe, der sich aus dem Grundverteidigungswert und dem Waffenbonus zusammensetzt.
  • Ein Link um beim Mausklick auf den Angriffswert gleich das Angriffsmakro aufzurufen. Bei diesem Link geben wir die den Text "Angriff" und die Waffennummer als String-List an das Angriffsmakro weiter.
  • Ein Link um beim Mausklick auf den Verteidigungswert gleich das Verteidigungsmakro aufzurufen. Bei diesem Link geben wir den Text "Verteidigung" und die Waffennummer als String-List an das Verteidigungsmakro weiter.
Die Waffen in unserem Property "Waffen" folgen einem bestimmten Schema bei dem die Waffennummer eine wichtige Rolle spielt (Waffe1Name, Waffe1Bonus, etc.). Wenn wir jetzt die verschiedenen Waffenwerte abrufen wollen, müssen wir auch bei jedem Durchlauf diese Waffennummer um eins erhöhen, sonst werden ja immer nur die Daten der gleichen Waffe abgerufen. Aus diesem Grund haben wir vor der Schleife die Variable "num" mit der Waffennummer erstellt. Mit dieser Nummer und strformat() (dieses Mal die Variante mit geschweiften Klammern) können wir bei jedem Durchlauf die Daten der nächsten Waffe abrufen. Dafür müssen wir nur die Nummer am Ende eines jeden Durchlaufs um eins erhöhen. Definieren wir erst mal die benötigten Variablen:

Code: Select all

[h: waffe.name = getStrProp(Waffen, strformat("Waffe%{num}Name"))]
[h: waffe.bonus = getStrProp(Waffen, strformat("Waffe%{num}Bonus"))]
[h: waffe.schaden = getStrProp(Waffen, strformat("Waffe%{num}Schaden")) + "W6"]
[h: waffe.angriff = Angriff + waffe.Bonus]
[h: waffe.verteidigung = Verteidigung + waffe.Bonus]
[h: angriff.link = macroLink(waffe.angriff, "probeKampf@Lib:makros", "all", strformat("Angriff, %s", num))]
[h: verteidigung.link = macroLink(waffe.verteidigung, "probeKampf@Lib:makros", "all", strformat("Verteidigung, %s", num))]
Natürlich müssen wir auch noch dafür sorgen, dass in jedem Durchlauf eine Tabellenzeile mit den Werten der aktuellen Waffe ausgegeben wird:

Code: Select all

[r: strformat("
  <tr>
    <td style='width: 115'>
      %s
    </td>
    <td style='text-align: center; width: 20'>
      %s
    </td>
    <td style='text-align: center; width: 25'>
      <span style='font-weight: bold; color: #000000; text-decoration: none'>%s</span>
    </td>
    <td style='text-align: center; width: 25'>
      <span style='font-weight: bold; color: #000000; text-decoration: none'>%s</span>
    </td>
    <td style='text-align: center; width: 35'>
      %s
    </td>
  </tr>
", waffe.name, waffe.Bonus, angriff.link, verteidigung.link, waffe.schaden)]
Am Anfang war die Waffennumer "num" 1. Jetzt erhöhen wir die Nummer um eins, damit im nächsten Durchlauf nicht "Waffe1Name, Waffe1Bonus, etc." abgerufen wird, sondern "Waffe2Name, Waffe2Bonus, etc.":

Code: Select all

[h: num = num + 1]
Damit ist die Count-Schleife fertig. Unter der Schleife müssen wir aber jetzt noch die Tabelle schließen. Also:

Code: Select all

[r: "</table>"]
Der Zauberbereich

Nach der Waffenliste kommt nun der Bereich für die Zauber. Auch hier gibt es wieder eine Überschrift, und auch hier fügen wir Links hinzu um neue Zauber einzutragen oder bestehende Zauber zu löschen. Beginnen wir also erneut mit dem Definieren der Variablen für die Links:

Code: Select all

[h: neu.zauber.link = macroLink("Neu", "zauberNeu@Lib:makros")]
[h: loesche.zauber.link = macroLink("Löschen", "zauberLoeschen@Lib:makros")]
Und die Tabelle:

Code: Select all

[r: strformat("
  <table style='border-spacing: 0px; color: #ffffff; border-left: 2px solid #008000; border-right: 2px solid #008000; background-color: #008000; width: 220'>
    <tr>
      <td style='font-weight: bold; width: 110'>
        Zauber
      </td>
      <td style='text-align: right; width: 110' valign='bottom'>
        <span style='font-size: 10pt; color: #ffffff; text-decoration: none'>(%s | %s)</span>
      </td>
    </tr>
  </table>
", neu.zauber.link, loesche.zauber.link)]
Bei der Zauberliste gibt es keine weitere Überschrift, denn als einzigen Zauberwert gibt es nur den Namen, sonst nichts. Erstellen wir also den Anfang der Tabelle für die Zauberliste:

Code: Select all

[r: "<table style='border-spacing: 0px; border-left: 2px solid #008000; border-right: 2px solid #008000; background-color: #ffffff; width: 220'>"]
Auch hier müssen wir wieder mit einer IF-Anweisung prüfen ob überhaupt Zauber vorhanden sind, und wenn nicht eine Tabellenzeile mit der entsprechenden Meldung ausgeben. Da es sich bei der Zauberliste, also dem Property "Zauber", um eine einfache String-List handelt, nutzen wir die Funktion listCount(), die uns als Rückgabewert die Anzahl der Einträge in der String-List liefert. Als Parameter wird die String-List angegeben.

Code: Select all

[r,if(listCount(Zauber) == 0), code:
  {
    [r: "<tr>
      <td style='text-align: center; width: 220'>
        Keine Zauber vorhanden
      </td>
    </tr>"]
  };{}
]
Falls Zauber vorhanden sind wollen wir diese nun Zeile für Zeile auflisten. Dafür benutzen wir eine FOREACH-Schleife, die den entsprechenden Code für jeden Eintrag in der Liste erstellt:

Code: Select all

[r,foreach(item, Zauber, ""), code:
  {
    [r: strformat("
    <tr>
      <td style='border-bottom: 1px solid; width: 220'>
        %s
      </td>
    </tr>
    ", item)]
  }
]
Unter der Schleife schließen wir wieder die Tabelle:

Code: Select all

[r: "</table>"]
Die Fußzeile

Damit ist der Charakterbogen fast fertig. Zum Schluss kommt noch die Fußzeile, die Links zu dem Charaktereditor, dem Inventar und dem Initiativewurf enthält. Also erst wieder die Variablen definieren:

Code: Select all

[h: charedit.link = macroLink("Charaktereditor", "charedit@Lib:makros")]
[h: ini.link = macroLink("Initiative", "iniWurf@Lib:makros", "all")]
[h: inventar.link = macroLink("Inventar", "inventar@Lib:makros")]
Und jetzt die Ausgabe der Fußzeile:

Code: Select all

[r: strformat("
<table style='border-spacing: 0px; border-left: 2px solid #008000; border-right: 2px solid #008000; background-color: #008000; width: 220'>
  <tr>
    <td style='text-align: left; width: 110'>
      <span style='color: #ffffff; text-decoration: none'>%s</span>
    </td>
    <td style='text-align: right; width: 110'>
      <span style='color: #ffffff; text-decoration: none'>%s | %s</span>
    </td>
  </tr>
</table>
", charedit.link, ini.link, inventar.link)]
Jetzt haben wir es hinter uns, der komplette Charakterbogen ist fertig! Du kannst den Bogen auch schon aufrufen, nur die Links zu den anderen Makros funktionieren eben noch nicht, die müssen wir ja erst noch basteln.

Thargun
Giant
Posts: 188
Joined: Sun Sep 14, 2014 4:27 am

Re: Macros for Dummies - A guide to MapTool-Macros (german)

Post by Thargun »

18.3 - Attributsproben

18.3.1 - Attributsproben: Das Makro

Code: Select all

[h: "<!-- Attributsname aus dem Charakterbogen empfangen und in Variable speichern -->"]

[h: attribut.name = macro.args]

[h: "<!-- Attributswert festlegen und in Variable speichern -->"]

[h,switch(attribut.name):
  case "Kraft": attribut.wert = Kraft;
  case "Geschick": attribut.wert = Geschick;
  case "Klugheit": attribut.wert = Klugheit;
  default: attribut.wert = 0
]

[h: "<!-- Fenster zur Usereingabe von Erschwernis und Erleichterung -->"]

[h: status = input(
  "tab0 | "+attribut.name+"-Probe || TAB",
    "probe.erschwernis|0|Erschwernis|TEXT|WIDTH=2 ##
    probe.erleichterung|0|Erleichterung|TEXT|WIDTH=2"
)]
[h: abort(status)]

[h: "<!-- Usereingaben pruefen -->"]

[h,if(probe.erschwernis == ""): probe.erschwernis = 0; probe.erschwernis = probe.erschwernis]
[h,if(probe.erleichterung == ""): probe.erleichterung = 0; probe.erleichterung = probe.erleichterung]

[h: assert(isNumber(probe.erschwernis), "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine Zahl. Bitte wiederhole den Vorgang.", 0)]
[h: assert(isNumber(probe.erleichterung), "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine Zahl. Bitte wiederhole den Vorgang.", 0)]

[h,if(probe.erschwernis != round(probe.erschwernis) || probe.erleichterung != round(probe.erleichterung)): pruefwert = 0; pruefwert = 1]
[h: assert(pruefwert, "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine ganze Zahl. Bitte wiederhole den Vorgang.", 0)]

[h,if(probe.erschwernis < 0 || probe.erleichterung < 0): pruefwert = 0; pruefwert = 1]
[h: assert(pruefwert, "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist eine negative Zahl. Bitte wiederhole den Vorgang.", 0)]

[h: "<!-- Wuerfeln und Variablen fuer die Ausgabe definieren -->"]

[h: wurf = 1d20]
[h: wurf.bild = tableImage("D20", wurf, 50)]

[h: wurf.endergebnis = wurf + probe.erschwernis - probe.erleichterung]
[h,if(wurf.endergebnis <= attribut.wert): ergebnis.text = "Erfolg"; ergebnis.text = "Misserfolg"]

[h: wurf.mod = probe.erschwernis - probe.erleichterung]
[h,if(wurf.mod > 0): wurf.mod = "+"+wurf.mod; wurf.mod = wurf.mod]

[h: "<!-- Probenergebnis im Chat ausgeben -->"]

[r: strformat("
<table style='border-spacing: 0px; background-color: #cfe9cf; border: 2px solid #008000'>
  <tr>
    <td style='font-weight: bold; padding: 5px; border-right: 1px solid' valign='middle'>
      %s-Probe
    </td>
    <td style='padding: 5px 2px 5px 5px' valign='middle'>
      %s-Wert:<br>
      Modifikator:
    </td>
    <td style='text-align: right; padding: 5px 5px 5px 2px; border-right: 1px solid' valign='middle'>
      %s<br>
      %s
    </td>
    <td style='font-size: 14pt; font-weight: bold; padding: 5px 4px 5px 5px' valign='middle'>
      %s
    </td>
    <td style='border-right: 1px solid' valign='middle'>
      <img src='%s'>
    </td>
    <td style='font-size: 14pt; font-weight: bold; padding: 5px' valign='middle'>
      %s
    </td>
  </tr>
</table>
", attribut.name, attribut.name, attribut.wert, wurf.mod, wurf, wurf.bild, ergebnis.text)]

Thargun
Giant
Posts: 188
Joined: Sun Sep 14, 2014 4:27 am

Re: Macros for Dummies - A guide to MapTool-Macros (german)

Post by Thargun »

18.3.2 - Attributsprobe: Die Erklärung

Die Attributsproben erledigen wir mit einem einzigen Makro, egal um welches Attribut es sich handelt. Um eine erfolgreiche Attributsprobe (Kraft, Geschick, Klugheit) abzulegen müssen die Charaktere gleich oder kleiner als den Attributswert würfeln. Zur Usereingabe einer eventuellen Erschwernis oder Erleichterung der Probe werden wir die input()-Funktion nutzen. Erstelle nun das Makro für die Attributsprobe im Library-Token und nenne es "probeAttribut".

Der Link zu einer Attributsprobe im Charakterbogen sendet ja auch den Namen des entsprechenden Attributs als Daten weiter. Zuerst müssen wir also diese Daten empfangen:

Code: Select all

[h: attribut.name = macro.args]
Jetzt brauchen wir den Wert den ein Charakter in diesem Attribut hat. Dafür benutzen wir die switch-Codeverzweigung und machen den Wert abhängig vom Attributsname:

Code: Select all

[h,switch(attribut.name):
  case "Kraft": attribut.wert = Kraft;
  case "Geschick": attribut.wert = Geschick;
  case "Klugheit": attribut.wert = Klugheit;
  default: attribut.wert = 0
]
Anschließend erstellen wir mit input() das Fenster zur Usereingabe von einer eventuellen Erschwernis oder Erleichterung. Dabei benutzen wir einen Tab mit dem Attributsnamen als Titel. Das sieht einfach besser aus, und der User weiß auch welche Probe er da gerade würfelt:

Code: Select all

[h: status = input(
  "tab0 | "+attribut.name+"-Probe || TAB",
    "probe.erschwernis|0|Erschwernis|TEXT|WIDTH=2 ##
    probe.erleichterung|0|Erleichterung|TEXT|WIDTH=2"
)]
[h: abort(status)]
Nachdem der User auf "OK" geklickt hat prüfen wir seine Eingaben. Bei leeren Eingabefeldern setzen wir eine "0" ein. Wenn die Eingabe keine Zahl oder keine ganze Zahl ist brechen wir das Makro ab und geben eine Fehlermeldung aus.

Code: Select all

[h,if(probe.erschwernis == ""): probe.erschwernis = 0; probe.erschwernis = probe.erschwernis]
[h,if(probe.erleichterung == ""): probe.erleichterung = 0; probe.erleichterung = probe.erleichterung]

[h: assert(isNumber(probe.erschwernis), "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine Zahl. Bitte wiederhole den Vorgang.", 0)]
[h: assert(isNumber(probe.erleichterung), "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine Zahl. Bitte wiederhole den Vorgang.", 0)]

[h,if(probe.erschwernis != round(probe.erschwernis) || probe.erleichterung != round(probe.erleichterung)): pruefwert = 0; pruefwert = 1]
[h: assert(pruefwert, "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine ganze Zahl. Bitte wiederhole den Vorgang.", 0)]

[h,if(probe.erschwernis < 0 || probe.erleichterung < 0): pruefwert = 0; pruefwert = 1]
[h: assert(pruefwert, "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist eine negative Zahl. Bitte wiederhole den Vorgang.", 0)]
Jetzt führen wir den Wurf mit einem W20 durch und besorgen uns auch gleich das passende Würfelbild für das Ergebnis aus der MapTool-Tabelle "D20". Die Größe des Bildes setzen wir auf "50".

Code: Select all

[h: wurf = 1d20]
[h: wurf.bild = tableImage("D20", wurf, 50)]
Anschließend bestimmen wir das Endergebnis des Wurfs, indem wir die Erschwernis auf den Wurf draufrechnen und die Erleichterung abziehen. Dann prüfen wir mit einer IF-Anweisung ob die Probe erfolgreich war und speichern einen entsprechenden Text für die spätere Ausgabe ("Erfolg" oder "Misserfolg") in einer Variable.

Code: Select all

[h: wurf.endergebnis = wurf + probe.erschwernis - probe.erleichterung]
[h,if(wurf.endergebnis <= attribut.wert): ergebnis.text = "Erfolg"; ergebnis.text = "Misserfolg"]
Auch den Modifikator, also die Erschwernis abzüglich der Erleichterung, wollen wir später noch im Chat ausgeben. Wir definieren dafür eine Variable:

Code: Select all

[h: wurf.mod = probe.erschwernis - probe.erleichterung]
Handelt es sich bei dem Modifikator um eine negative Zahl zeigt MapTool diese auch korrekt an, also z. B. "-2". Positive Zahlen zeigt MapTool natürlich auch korrekt an, z. B. "2". Bei positiven Zahlen wollen wir jedoch ein Plus-Zeichen davorstehen haben, da es sich dann ja um eine Erschwernis, bzw. um einen Aufschlag handelt. Also prüfen wir ob die Zahl größer als 0 ist und setzen gegebenenfalls ein Plus-Zeichen davor. Das Plus-Zeichen können wir dabei aber nicht direkt eingeben, denn dann sieht es MapTool einfach als normales Vorzeichen einer Zahl und zeigt es am Ende doch wieder nicht an. Maskieren wir das Plus-Zeichen aber mit HTML (+) funktioniert es:

Code: Select all

[h,if(wurf.mod > 0): wurf.mod = "+"+wurf.mod; wurf.mod = wurf.mod]
Und zum Schluss geben wir alle Informationen über die Probe in der Chatbox aus. Dafür erstellen wir eine Tabelle mit strformat():

Code: Select all

[r: strformat("
<table style='border-spacing: 0px; background-color: #cfe9cf; border: 2px solid #008000'>
  <tr>
    <td style='font-weight: bold; padding: 5px; border-right: 1px solid' valign='middle'>
      %s-Probe
    </td>
    <td style='padding: 5px 2px 5px 5px' valign='middle'>
      %s-Wert:<br>
      Modifikator:
    </td>
    <td style='text-align: right; padding: 5px 5px 5px 2px; border-right: 1px solid' valign='middle'>
      %s<br>
      %s
    </td>
    <td style='font-size: 14pt; font-weight: bold; padding: 5px 4px 5px 5px' valign='middle'>
      %s
    </td>
    <td style='border-right: 1px solid' valign='middle'>
      <img src='%s'>
    </td>
    <td style='font-size: 14pt; font-weight: bold; padding: 5px' valign='middle'>
      %s
    </td>
  </tr>
</table>
", attribut.name, attribut.name, attribut.wert, wurf.mod, wurf, wurf.bild, ergebnis.text)]

Thargun
Giant
Posts: 188
Joined: Sun Sep 14, 2014 4:27 am

Re: Macros for Dummies - A guide to MapTool-Macros (german)

Post by Thargun »

18.4 - HP Verwaltung

18.4.1 - HP abziehen: Das Makro

Code: Select all

[h: "<!-- Fenster zur Usereingabe -->"]

[h: status = input(
  "schaden|0|HP abziehen|TEXT|WIDTH=2",
  "rs|1|Ruestung einrechnen|CHECK"
)]
[h: abort(status)]

[h: "<!-- Usereingabe pruefen -->"]

[h,if(schaden == ""): schaden = 0; schaden = schaden]

[h: assert(isNumber(schaden), "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine Zahl. Bitte wiederhole den Vorgang.", 0)]

[h,if(schaden != round(schaden)): pruefwert = 0; pruefwert = 1]
[h: assert(pruefwert, "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine ganze Zahl. Bitte wiederhole den Vorgang.", 0)]

[h,if(schaden < 0): pruefwert = 0; pruefwert = 1]
[h: assert(pruefwert, "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist eine negative Zahl. Bitte wiederhole den Vorgang.", 0)]

[h: "<!-- Ruestung einberechnen falls aktiviert -->"]

[h,if(rs == 1), code:
  {
    [schaden = schaden - Ruestung]
    [if(schaden < 0): schaden = 0; schaden = schaden]
  };{}
]

[h: "<!-- Endgueltigen Schaden feststellen und von HP abziehen -->"]

[h,if(schaden > HP): schaden = HP; schaden = schaden]
[h: HP = HP - schaden]

[h: "<!-- Charakterbogen aktualisieren falls geoeffnet -->"]

[h,if(isFrameVisible("charbogen") == 1), code:
  {
    [macro("charbogen@Lib:makros"): ""]
  };{}
]

[h: "<!-- Schadensinfo in der Chatbox ausgeben -->"]

[r: strformat("
<table style='border-spacing: 0px; background-color: #cfe9cf; border: 2px solid #008000'>
  <tr>
    <td style='padding: 5px; border-right: 1px solid' valign='middle'>
      Verlorene HP: <span style='font-weight: bold'>%s</span>
    </td>
    <td style='padding: 5px' valign='middle'>
      Aktuelle HP: <span style='font-weight: bold'>%s</span>
    </td>
  </tr>
</table>
", schaden, HP)]

Thargun
Giant
Posts: 188
Joined: Sun Sep 14, 2014 4:27 am

Re: Macros for Dummies - A guide to MapTool-Macros (german)

Post by Thargun »

18.4.2 - HP abziehen: Die Erklärung

Nun geht es um den Verlust von HP. Erstelle das neue Makro "hpMinus" in deinem Library-Token, das du ja auch schon im Charakterbogen verlinkt hast.

Wir erstellen mit input() ein Eingabefenster, damit der User den Schaden angeben kann den sein Charakter erhält. Zusätzlich gibt es noch eine Checkbox um die Rüstung automatisch vom Schaden abzuziehen. Da ein Charakter in den meisten Fällen im Kampf Schaden erhalten wird, werden wir diese Checkbox vorselektieren.

Code: Select all

[h: status = input(
  "schaden|0|HP abziehen|TEXT|WIDTH=2",
  "rs|1|Ruestung einrechnen|CHECK"
)]
[h: abort(status)]
Jetzt prüfen wir wieder ob der User bei der Eingabe auch alles richtig gemacht hat:

Code: Select all

[h,if(schaden == ""): schaden = 0; schaden = schaden]

[h: assert(isNumber(schaden), "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine Zahl. Bitte wiederhole den Vorgang.", 0)]

[h,if(schaden != round(schaden)): pruefwert = 0; pruefwert = 1]
[h: assert(pruefwert, "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine ganze Zahl. Bitte wiederhole den Vorgang.", 0)]

[h,if(schaden < 0): pruefwert = 0; pruefwert = 1]
[h: assert(pruefwert, "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist eine negative. Bitte wiederhole den Vorgang.", 0)]
Nun prüfen wir mit einer IF-Anweisung ob die Checkbox zum Abzug der Rüstung aktiviert ist. Falls ja, ziehen wir die Rüstung vom Schaden ab. Dadurch kann der Schaden allerdings in den negativen Bereich kommen (1 Schaden - 2 Rüstung = -1 Schaden). Das darf natürlich nicht sein. Also prüfen wir mit einer weiteren IF-Anweisung auch das, und setzen den Schaden auf "0" wenn er tatsächlich im negativen Bereich liegt.

Code: Select all

[h,if(rs == 1), code:
  {
    [schaden = schaden - Ruestung]
    [if(schaden < 0): schaden = 0; schaden = schaden]
  };{}
]
In unserem imaginären Rollenspielsystem können die HP nicht unter "0" fallen. Wir prüfen mit einer IF-Anweisung also ob der Schaden größer ist als die Anzahl der aktuellen HP. Falls dies der Fall ist setzen wir den Schaden mit den aktuellen HP gleich, ansonsten bleibt der Schaden wie er ist.

Code: Select all

[h,if(schaden > HP): schaden = HP; schaden = schaden]
Damit haben wir den endgültigen Schaden festgestellt. Diesen Schaden ziehen wir jetzt von den aktuellen HP ab:

Code: Select all

[h: HP = HP - schaden]
Falls der User seinen Charakterbogen noch geöffnet hat muss der Bogen natürlich aktualisiert werden, denn die HP haben sich ja geändert. Mit einer IF-Anweisung und der Funktion isFrameVisible(), bei der als Parameter der Name des zu überprüfenden Frames angegeben wird, schauen wir also ob der Bogen geöffnet ist. Trifft das zu rufen wir den Bogen mit der Macro Roll-Option neu auf damit er aktualisiert wird.

Code: Select all

[h,if(isFrameVisible("charbogen") == 1), code:
  {
    [macro("charbogen@Lib:makros"): ""]
  };{}
]
Und zum Schluss geben wir noch die Schadensinfo in der Chatbox aus:

Code: Select all

[r: strformat("
<table style='border-spacing: 0px; background-color: #cfe9cf; border: 2px solid #008000'>
  <tr>
    <td style='padding: 5px; border-right: 1px solid' valign='middle'>
      Verlorene HP: <span style='font-weight: bold'>%s</span>
    </td>
    <td style='padding: 5px' valign='middle'>
      Aktuelle HP: <span style='font-weight: bold'>%s</span>
    </td>
  </tr>
</table>
", schaden, HP)]

Thargun
Giant
Posts: 188
Joined: Sun Sep 14, 2014 4:27 am

Re: Macros for Dummies - A guide to MapTool-Macros (german)

Post by Thargun »

18.4.3 - HP hinzufügen: Das Makro

Code: Select all

[h: "<!-- Fenster zur Usereingabe -->"]

[h: status = input(
  "zusatz.hp|0|HP hinzufuegen|TEXT|WIDTH=2"
)]
[h: abort(status)]

[h: "<!-- Usereingabe pruefen -->"]

[h,if(zusatz.hp == ""): zusatz.hp = 0; zusatz.hp = zusatz.hp]

[h: assert(isNumber(zusatz.hp), "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine Zahl. Bitte wiederhole den Vorgang.", 0)]

[h,if(zusatz.hp != round(zusatz.hp)): pruefwert = 0; pruefwert = 1]
[h: assert(pruefwert, "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine ganze Zahl. Bitte wiederhole den Vorgang.", 0)]

[h,if(zusatz.hp < 0): pruefwert = 0; pruefwert = 1]
[h: assert(pruefwert, "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist eine negative Zahl. Bitte wiederhole den Vorgang.", 0)]

[h: "<!-- Endgueltige Zusatz-HP feststellen und zu HP hinzufuegen -->"]

[h,if(zusatz.hp + HP > HPmax): zusatz.hp = HPmax - HP; zusatz.hp = zusatz.hp]
[h: HP = HP + zusatz.hp]

[h: "<!-- Charakterbogen aktualisieren falls geoeffnet -->"]

[h,if(isFrameVisible("charbogen") == 1), code:
  {
    [macro("charbogen@Lib:makros"): ""]
  };{}
]

[h: "<!-- HP-Info in der Chatbox ausgeben -->"]

[r: strformat("
<table style='border-spacing: 0px; background-color: #cfe9cf; border: 2px solid #008000'>
  <tr>
    <td style='padding: 5px; border-right: 1px solid' valign='middle'>
      Gewonnene HP: <span style='font-weight: bold'>%s</span>
    </td>
    <td style='padding: 5px' valign='middle'>
      Aktuelle HP: <span style='font-weight: bold'>%s</span>
    </td>
  </tr>
</table>
", zusatz.hp, HP)]

Thargun
Giant
Posts: 188
Joined: Sun Sep 14, 2014 4:27 am

Re: Macros for Dummies - A guide to MapTool-Macros (german)

Post by Thargun »

18.4.4 - HP hinzufügen: Die Erklärung

Natürlich muss der User verlorene HP auch wieder herstellen können. Also erstelle das neue Makro "hpPlus" in deinem Library-Token, das du ja auch schon im Charakterbogen verlinkt hast.

Erneut beginnen wir mit einem Eingabefenster das wir mit input() erstellen. Hier kann der User angeben wie viele HP wiederhergestellt werden sollen.

Code: Select all

[h: status = input(
  "zusatz.hp|0|HP hinzufuegen|TEXT|WIDTH=2"
)]
[h: abort(status)]
Die Eingabe des Users muss natürlich wieder überprüft werden:

Code: Select all

[h,if(zusatz.hp == ""): zusatz.hp = 0; zusatz.hp = zusatz.hp]

[h: assert(isNumber(zusatz.hp), "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine Zahl. Bitte wiederhole den Vorgang.", 0)]

[h,if(zusatz.hp != round(zusatz.hp)): pruefwert = 0; pruefwert = 1]
[h: assert(pruefwert, "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine ganze Zahl. Bitte wiederhole den Vorgang.", 0)]

[h,if(zusatz.hp < 0): pruefwert = 0; pruefwert = 1]
[h: assert(pruefwert, "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist eine negative Zahl. Bitte wiederhole den Vorgang.", 0)]
Die wiederherzustellenden HP plus die aktuellen HP dürfen natürlich die maximale HP-Anzahl des Charakters nicht überschreiten. Das prüfen wir jetzt mit einer IF-Anweisung. Und falls genau dieser Fall eintritt setzen wir die wiederherzustellenden HP mit den noch fehlenden HP bis zur Maximalgrenze gleich.

Code: Select all

[h,if(zusatz.hp + HP > HPmax): zusatz.hp = HPmax - HP; zusatz.hp = zusatz.hp]
Damit haben wir die genaue Anzahl der wiederherzustellenden HP. Diese fügen wir jetzt zu den aktuellen HP hinzu:

Code: Select all

[h: HP = HP + zusatz.hp]
Falls der User seinen Charakterbogen noch geöffnet hat muss der Bogen natürlich aktualisiert werden, denn die HP haben sich ja geändert. Mit einer IF-Anweisung und der Funktion isFrameVisible(), bei der als Parameter der Name des zu überprüfenden Frames angegeben wird, schauen wir also ob der Bogen geöffnet ist. Trifft das zu rufen wir den Bogen mit der Macro Roll-Option neu auf damit er aktualisiert wird.

Code: Select all

[h,if(isFrameVisible("charbogen") == 1), code:
  {
    [macro("charbogen@Lib:makros"): ""]
  };{}
]
Und zum Schluss geben wir noch die HP-Info in der Chatbox aus:

Code: Select all

[r: strformat("
<table style='border-spacing: 0px; background-color: #cfe9cf; border: 2px solid #008000'>
  <tr>
    <td style='padding: 5px; border-right: 1px solid' valign='middle'>
      Gewonnene HP: <span style='font-weight: bold'>%s</span>
    </td>
    <td style='padding: 5px' valign='middle'>
      Aktuelle HP: <span style='font-weight: bold'>%s</span>
    </td>
  </tr>
</table>
", zusatz.hp, HP)]

Thargun
Giant
Posts: 188
Joined: Sun Sep 14, 2014 4:27 am

Re: Macros for Dummies - A guide to MapTool-Macros (german)

Post by Thargun »

18.5 - Angriff & Verteidigung

18.5.1 - Angriff & Verteidigung: Das Makro

Code: Select all

[h: "<!-- Daten aus dem Charakterbogen empfangen -->"]

[h: daten = macro.args]
[h: kampf.aktion = listGet(daten, 0)]
[h: waffe.nummer = listGet(daten, 1)]

[h: "<!-- Waffendaten abrufen -->"]

[h: waffe.name = getStrProp(Waffen, strformat("Waffe%{waffe.nummer}Name"))]
[h: waffe.bonus = getStrProp(Waffen, strformat("Waffe%{waffe.nummer}Bonus"))]
[h: schaden.wuerfel = getStrProp(Waffen, strformat("Waffe%{waffe.nummer}Schaden"))]

[h: "<!-- Wert fuer Angriff oder Verteidigung bestimmen -->"]

[h,if(kampf.aktion == "Angriff"): kampf.wert = Angriff + waffe.bonus; kampf.wert = Verteidigung + waffe.bonus]

[h: "<!-- Fenster zur Usereingabe von Erschwernis und Erleichterung -->"]

[h: status = input(
  "tab0 | "+kampf.aktion+" || TAB",
    "probe.erschwernis|0|Erschwernis|TEXT|WIDTH=2 ##
    probe.erleichterung|0|Erleichterung|TEXT|WIDTH=2"
)]
[h: abort(status)]

[h: "<!-- Usereingaben pruefen -->"]

[h,if(probe.erschwernis == ""): probe.erschwernis = 0; probe.erschwernis = probe.erschwernis]
[h,if(probe.erleichterung == ""): probe.erleichterung = 0; probe.erleichterung = probe.erleichterung]

[h: assert(isNumber(probe.erschwernis), "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine Zahl. Bitte wiederhole den Vorgang.", 0)]
[h: assert(isNumber(probe.erleichterung), "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine Zahl. Bitte wiederhole den Vorgang.", 0)]

[h,if(probe.erschwernis != round(probe.erschwernis) || probe.erleichterung != round(probe.erleichterung)): pruefwert = 0; pruefwert = 1]
[h: assert(pruefwert, "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine ganze Zahl. Bitte wiederhole den Vorgang.", 0)]

[h,if(probe.erschwernis < 0 || probe.erleichterung < 0): pruefwert = 0; pruefwert = 1]
[h: assert(pruefwert, "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist eine negative Zahl. Bitte wiederhole den Vorgang.", 0)]

[h: "<!-- Wuerfeln und weitere Variablen fuer die Ausgabe definieren -->"]

[h: wurf = 1d20]
[h: wurf.bild = tableImage("D20", wurf, 50)]

[h: wurf.endergebnis = wurf + probe.erschwernis - probe.erleichterung]

[h: wurf.mod = probe.erschwernis - probe.erleichterung]
[h,if(wurf.mod > 0): wurf.mod = "+"+wurf.mod; wurf.mod = wurf.mod]

[h: "<!-- Schaden der Waffe bestimmen -->"]

[h: schaden.ergebnis = 0]
[h: schaden.bild.list = ""]

[h,count(schaden.wuerfel), code:
  {
    [schaden.wurf = 1d6]
    [schaden.ergebnis = schaden.ergebnis + schaden.wurf]
    [schaden.bild = tableImage("D6", schaden.wurf, 40)]
    [schaden.bild.list = schaden.bild.list + strformat("<img src='%s'>", schaden.bild)]
  }
]

[h: "<!-- Probenergebnis im Chat ausgeben -->"]

[h: "<!-- Ergebnistabelle Teil 1: Allgemeine Angaben -->"]

[r: strformat("
<table style='border-spacing: 0px; background-color: #cfe9cf; border: 2px solid #008000'>
  <tr>
    <td style='font-weight: bold; text-align: center; padding: 5px; border-right: 1px solid' valign='middle'>
      %s
    </td>
    <td style='padding: 5px 2px 5px 5px' valign='middle'>
      %s:<br>
      Modifikator:
    </td>
    <td style='text-align: right; padding: 5px 5px 5px 2px; border-right: 1px solid' valign='middle'>
      %s<br>
      %s
    </td>
    <td style='font-size: 14pt; font-weight: bold; padding: 5px 4px 5px 5px' valign='middle'>
      %s
    </td>
    <td style='border-right: 1px solid' valign='middle'>
      <img src='%s'>
    </td>
", waffe.name, kampf.aktion, kampf.wert, wurf.mod, wurf, wurf.bild)]

[h: "<!-- Ergebnistabelle Teil 2: Erfolgsermittlung -->"]

[r,if(wurf.endergebnis <= kampf.wert && kampf.aktion == "Angriff"), code:
  {
    [h: "<!-- Ausgabe bei erfolgreichem Angriff: Schadensangabe -->"]

    [r: strformat("
    <td style='font-size: 14pt; font-weight: bold; padding: 5px 4px 5px 5px' valign='middle'>
      %s Schaden:
    </td>
    <td valign='middle'>
      %s
    </td>
    ", schaden.ergebnis, schaden.bild.list)]
  };
  {
    [h: "<!-- Ausgabe bei Verteidigung oder misslungenem Angriff -->"]

    [r: "<td style='font-size: 14pt; font-weight: bold; padding: 5px' valign='middle'>"]
    [r,if(wurf.endergebnis <= kampf.wert): output = "Erfolg"; output = "Misserfolg"]
    [r: "</td>"]
  }
]

[h: "<!-- Ergebnistabelle Teil 3: Zeile und Tabelle schliessen -->"]

[r: "</tr>
</table>"]

Thargun
Giant
Posts: 188
Joined: Sun Sep 14, 2014 4:27 am

Re: Macros for Dummies - A guide to MapTool-Macros (german)

Post by Thargun »

18.5.2 - Angriff & Verteidigung: Die Erklärung

Ähnlich wie bei den Attributsproben benutzen wir für Angriff und Verteidigung nur ein einziges Makro. Erstelle also zuerst das Makro "probeKampf" in deinem Library-Token, das du ja auch schon im Charakterbogen verlinkt hast.

Im Charakterbogen haben wir an dieses Makro Daten weitergesendet, nämlich eine String-List mit dem Text "Angriff" oder "Verteidigung" und die Waffennummer. Also empfangen wir die Daten:

Code: Select all

[h: daten = macro.args]
Jetzt extrahieren wir die einzelnen Daten mit listGet() aus der String-List:

Code: Select all

[h: kampf.aktion = listGet(daten, 0)]
[h: waffe.nummer = listGet(daten, 1)]
Da wir jetzt die Waffennummer haben besorgen wir uns ein paar Daten der Waffe mit der angegriffen oder verteidigt wird. Das sind der Name der Waffe, der Bonus der Waffe und die Anzahl der Schadenswürfel (W6) der Waffe. Wir greifen also mit getStrProp() auf die String Property List des Propertys Waffen zu, und sorgen mit Hilfe von strformat() und der Waffennummer dafür, dass wir auch die Daten der gewünschten Waffe erhalten.

Code: Select all

[h: waffe.name = getStrProp(Waffen, strformat("Waffe%{waffe.nummer}Name"))]
[h: waffe.bonus = getStrProp(Waffen, strformat("Waffe%{waffe.nummer}Bonus"))]
[h: schaden.wuerfel = getStrProp(Waffen, strformat("Waffe%{waffe.nummer}Schaden"))]
Mit einer IF-Anweisung schauen wir nun ob es sich um einen Angriff oder eine Verteidigung handelt, und bestimmen so den Wert auf den für die Probe gewürfelt werden muss. Dieser "Kampfwert" besteht aus dem Waffenbonus plus dem Grundwert für Angriff oder eben Verteidigung.

Code: Select all

[h,if(kampf.aktion == "Angriff"): kampf.wert = Angriff + waffe.bonus; kampf.wert = Verteidigung + waffe.bonus]
Als nächstes kommt ein Eingabefenster für den User, damit er eine eventuelle Erschwernis oder Erleichterung eingeben kann. Dafür benutzen wir erneut input() und bauen auch wieder ein Tab als eine Art Fenstertitel ein, damit der User auch nochmal sieht ob es sich um einen Angriff oder eine Verteidigung handelt.

Code: Select all

[h: status = input(
  "tab0 | "+kampf.aktion+" || TAB",
    "probe.erschwernis|0|Erschwernis|TEXT|WIDTH=2 ##
    probe.erleichterung|0|Erleichterung|TEXT|WIDTH=2"
)]
[h: abort(status)]
Die Eingabe des Users muss natürlich wieder auf Richtigkeit überprüft werden:

Code: Select all

[h,if(probe.erschwernis == ""): probe.erschwernis = 0; probe.erschwernis = probe.erschwernis]
[h,if(probe.erleichterung == ""): probe.erleichterung = 0; probe.erleichterung = probe.erleichterung]

[h: assert(isNumber(probe.erschwernis), "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine Zahl. Bitte wiederhole den Vorgang.", 0)]
[h: assert(isNumber(probe.erleichterung), "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine Zahl. Bitte wiederhole den Vorgang.", 0)]

[h,if(probe.erschwernis != round(probe.erschwernis) || probe.erleichterung != round(probe.erleichterung)): pruefwert = 0; pruefwert = 1]
[h: assert(pruefwert, "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist keine ganze Zahl. Bitte wiederhole den Vorgang.", 0)]

[h,if(probe.erschwernis < 0 || probe.erleichterung < 0): pruefwert = 0; pruefwert = 1]
[h: assert(pruefwert, "<span style='font-weight: bold'>Fehler!</span> Die Eingabe ist eine negative Zahl. Bitte wiederhole den Vorgang.", 0)]
Wie bei der Attributsprobe wird nun mit einem W20 gewürfelt, und das Ergebnis, sowie das passende Würfelbild des Ergebnisses, jeweils in einer Variable gespeichert:

Code: Select all

[h: wurf = 1d20]
[h: wurf.bild = tableImage("D20", wurf, 50)]
Jetzt bestimmen wir das endgültige Würfelergebnis indem wir die Erschwernis zum Wurf dazurechnen und die Erleichterung abziehen:

Code: Select all

[h: wurf.endergebnis = wurf + probe.erschwernis - probe.erleichterung]
Auch der Modifikator (Erschwernis - Erleichterung) wird wieder in einer Variable gespeichert. Und falls es sich um eine positive Zahl handelt setzen wir erneut ein Plus-Zeichen davor, das wir mit HTML maskieren (+).

Code: Select all

[h: wurf.mod = probe.erschwernis - probe.erleichterung]
[h,if(wurf.mod > 0): wurf.mod = "+"+wurf.mod; wurf.mod = wurf.mod]
Falls es sich um einen Angriff handelt möchten wir auch gleich den Schaden bestimmen den die Waffe verursacht. Dafür benutzen wir eine COUNT-Schleife die so oft ausgeführt wird wie die Anzahl der Schadenswürfel beträgt. Außerdem wollen wir später bei der Chatausgabe auch die Schadenswürfel als Bild anzeigen. Um das alles hinzubekommen müssen wir vor der Schleife zwei Variablen erstellen:

Code: Select all

[h: schaden.ergebnis = 0]
[h: schaden.bild.list = ""]
  • In der Variable schaden.ergebnis speichern wir den kompletten Schaden. Noch gibt es keinen Schaden, also ist der Anfangswert "0".
  • In der Variable schaden.bild.list speichern wir die Bilder der einzelnen Schadenswürfel, und zwar inklusive dem HTML-Code zum Anzeigen eines Bildes. Diese Variable ist ein einfacher String. Am Anfang enthält dieser String natürlich noch keine Bilder, daher notieren wir als Anfangswert einen leeren String ("").
Jetzt die COUNT-Schleife. Denke daran: die Schleife wird so oft ausgeführt wie die Anzahl der Schadenswürfel der Waffe beträgt.

Code: Select all

[h,count(schaden.wuerfel), code:
  {
    [schaden.wurf = 1d6]
    [schaden.ergebnis = schaden.ergebnis + schaden.wurf]
    [schaden.bild = tableImage("D6", schaden.wurf, 40)]
    [schaden.bild.list = schaden.bild.list + strformat("<img src='%s'>", schaden.bild)]
  }
]
Nehmen wir die Schleife jetzt Stück für Stück auseinander:

1. - Wir würfeln mit einem W6 (also einem Schadenswürfel) und speichern das Ergebnis in der Variable schaden.wurf.

2. - Das Ergebnis des einzelnen Schadenswürfel rechnen wir jetzt zum Gesamtschaden hinzu, der ja in der Variable schaden.ergebnis gespeichert wird.

3. - Jetzt besorgen wir uns das passende Bild für das Würfelergebnis aus Schritt 1, und speichern es in der Variable schaden.bild.

4. - Das eben ermittelte Bild für den einzelnen Schadenswürfel fügen wir jetzt der Variable schaden.bild.list hinzu, bzw. dem String der in der Variable gespeichert ist. Dafür müssen wir strformat() benutzen, damit auch gleich der HTML-Code zum Anzeigen von Bildern miteingefügt wird. Wird die Variable also später in der Chatbox ausgegeben erscheinen alle Bilder der einzelnen Schadenswürfel.

Nach der COUNT-Schleife fehlt nur noch die Ausgabe der Probe in der Chatbox. Dafür benutzen wir eine Tabelle die wir in drei Teile gliedern. Der erste Teil enthält allgemeine Informationen, z. B. der Waffenname oder das Ergebnis des W20-Wurfs für die Probe:

Code: Select all

[r: strformat("
<table style='border-spacing: 0px; background-color: #cfe9cf; border: 2px solid #008000'>
  <tr>
    <td style='font-weight: bold; text-align: center; padding: 5px; border-right: 1px solid' valign='middle'>
      %s
    </td>
    <td style='padding: 5px 2px 5px 5px' valign='middle'>
      %s:<br>
      Modifikator:
    </td>
    <td style='text-align: right; padding: 5px 5px 5px 2px; border-right: 1px solid' valign='middle'>
      %s<br>
      %s
    </td>
    <td style='font-size: 14pt; font-weight: bold; padding: 5px 4px 5px 5px' valign='middle'>
      %s
    </td>
    <td style='border-right: 1px solid' valign='middle'>
      <img src='%s'>
    </td>
", waffe.name, kampf.aktion, kampf.wert, wurf.mod, wurf, wurf.bild)]
Wie du siehst haben wir die Tabellenzeile und die Tabelle selbst noch nicht geschlossen. Wie es weiter geht machen wir davon abhängig ob die Probe gelungen ist, also mit dem W20 kleiner oder gleich dem Angriffs- bzw. dem Verteidigungswert gewürfelt wurde, und ob es sich dabei um einen Angriff handelt oder nicht. Dafür bietet sich natürlich eine IF-Anweisung mit zwei Codeblöcken an:

Code: Select all

[r,if(wurf.endergebnis <= kampf.wert && kampf.aktion == "Angriff"), code:
  {
    [r: strformat("
    <td style='font-size: 14pt; font-weight: bold; padding: 5px 4px 5px 5px' valign='middle'>
      %s Schaden:
    </td>
    <td valign='middle'>
      %s
    </td>
    ", schaden.ergebnis, schaden.bild.list)]
  };
  {
    [r: "<td style='font-size: 14pt; font-weight: bold; padding: 5px' valign='middle'>"]
    [r,if(wurf.endergebnis <= kampf.wert): output = "Erfolg"; output = "Misserfolg"]
    [r: "</td>"]
  }
]
Falls die Probe gelungen ist UND es sich um einen Angriff handelt wird der erste Codeblock ausgeführt. Dort werden dann einfach zwei weitere Tabellenzellen erzeugt und der Gesamtschaden sowie die Schadenswürfel als Bild ausgegeben.

Falls die Probe nicht gelungen ist ODER es sich nicht um einen Angriff handelt wird der zweite Codeblock ausgeführt. Dort wird eine Tabellenzelle erzeugt und der Inhalt der Zelle mit einer weiteren IF-Anweisung bestimmt. Handelt es sich bei der Probe um eine Verteidigung, und war die Probe erfolgreich, wird der Text "Erfolg" ausgegeben. Handelt es sich jedoch um eine nicht erfolgreiche Verteidigungs- oder Angriffsprobe wird der Text "Misserfolg" ausgegeben.

Zum Schluss muss die Tabellenzeile und die Tabelle nur noch geschlossen werden:

Code: Select all

[r: "</tr>
</table>"]

Thargun
Giant
Posts: 188
Joined: Sun Sep 14, 2014 4:27 am

Re: Macros for Dummies - A guide to MapTool-Macros (german)

Post by Thargun »

18.6 - Waffenverwaltung

18.6.1 - Waffe hinzufügen (Formular): Das Makro

Code: Select all

[h: "<!-- Eingabefenster oeffnen und HTML-Aufbau notieren -->"]

[dialog("waffeAdd", "width=308; height=264; temporary=1; input=0; noframe=0"):
  {
    [r: "
    <html>
      <head>
        <title>
          Waffe hinzufügen
        </title>
      </head>
      <body>
    "]

        [h: "<!-- Beginn des Eingabeformulars -->"]

        [h: processLink = macroLinkText("waffeNeuProcess@Lib:makros")]
        [r: strformat("<form action='%s'>", processLink)]

          [h: "<!-- Beginn der aeusseren Tabelle -->"]

          [r: "
          <table style='border-spacing: 0px; border: 2px solid #008000; background-color: #ffffff; width: 280'>
            <tr>
              <td style='text-align: center'>

                <!-- Tabelle fuer Ueberschrift -->

                <table style='border-spacing: 0px; margin-top: 10px; margin-bottom: 10px; width: 240'>
                  <tr>
                    <td style='font-size: 14pt; font-weight: bold; color: #ffffff; text-align: center; background-color: #008000'>
                      Neue Waffe
                    </td>
                  </tr>
                </table>

          <!-- Weiterfuehrung der aeusseren Tabelle -->

              </td>
            </tr>
            <tr>
              <td style='text-align: center'>

                <!-- Tabelle fuer Eingabefelder -->

                <table style='border-spacing: 0px'>
                  <tr>
                    <td>
                      Waffenname:
                    </td>
                    <td>
                      <input type='text' name='wName' size='15' maxlength='12'>
                    </td> 
                  </tr>
                  <tr>
                    <td>
                      Waffenbonus:
                    </td>
                    <td>
                      <input type='text' name='wBonus' size='2' maxlength='1'>
                    </td> 
                  </tr>  
                  <tr>
                    <td>
                      Schadenswürfel:
                    </td>
                    <td>
                      <input type='text' name='wSchaden' size='2' maxlength='1'>
                    </td> 
                  </tr>
                </table>

          <!-- Abschluss der aeusseren Tabelle mit Senden-Button -->

              </td>
            </tr>
            <tr>
              <td style='text-align: center; padding: 10px'>
                <input type='submit' name='processButton' value='Waffe eintragen'>
              </td>
            </tr>
          </table>

        <!-- Formular, HTML-Bereich und Dialog-Fenster schliessen -->

        </form>
      </body>
    </html>
    "]
  }
]

Post Reply

Return to “Documentation Requests/Discussion”