Danke für die Erklärung
Extended am Sonntag #5
Ein Minispiel: Geschenke Sortieren
Heute soll es um ein Extended-Item gehen, mit dem man ein kleines Spiel spielen kann.
In diesem geht es darum, Geschenke möglichst schnell in die richtige Kategorie (bzw. Geschenksack) einzusortieren. Wenn man den falschen Sack auswählt oder zu langsam ist, ist das Spiel zuende.
Import-Code
Probieren geht hier vor analysieren, deshalb gleich zu Beginn der Import-Code, danach die Erklärung.
Geschenksortierspiel
!TN1xtTnss8pllvQ6c5YzSSnBaxfpa2qaIrMWyc3LTwzpwASLonEgxZmcc416ZY9q(mSpLNo)f76EKKT5poqi3v7UxTa1OrJ65x)h1DpDl8C86456a)vUAzp3kEeNkvQvE7nFtTT305QRpXZTkqb5ixCmsyusCsN)HhPzBpsRJG7mEK9AJGqoC3SBpCFp3nlxgwVJ3EEesdCpAdvzWjKo4iYyclJ7i0baf(QOXW2PQH6Shq4sFkpTNp1WgkvxN2BN0EJOMWskQiqo6LoVoTx11tpoNUidBKlDeZs3lNKE8K16WccUU)SpRw71PhV2PjJhZSZouYVHfPzI(0qHDLpgjeAjpa4v2cNr1Ag3o9aP00NYZUzpAsmF2VAUjdjIFiSaJpEatfyx5ez)iE2d)inPptD1SVOgaqnfwcKQ1ElF2xcVIfLXhIjYpg0ToYbdyGSFAsqqKyyb4YyjNgKb3ju1nrJPz7ZvMmKAgRO8irUi3UV2elv9ZXvY55pypf1qhpixDiJzXjCQjkrBVVfRFCIFiqBHe6MO1XcQFmlZW9oM6MCOUGcIPy8aoBitZmz2hwatL2lreaQb4MKjT7X0gMFCbrxqv4RMdyC(OzFPW85o7ZH3WsMRUcgt7hgX6NZ5wr(HgMkMzYrTbD0yW8qt0QzFrBsal100JNU(pv4L8Z)0DDrCkV(pNEC6XGK8bQ6LOdgS6AY1WXITH3umF9vqBHdgEtX81bxE8xc8hpsdJcpsfCHQ33hpq6NmIjmD1HYRULR(Dc8s7neW(HGUgUqL7d9Luvxz))jZ)2HqxzdR3bdKjMWi9zs4TKNBnpYlMOW5tFiMufxyteA4AFWu6hcxNZpFjGr(n5sqMaaor(XDbaO3I9lymYjRaTtEQLvTrPLm18nzfr45)yX8Az7gP9tWtM9z)qWDtN27TOdetexQujiPINlKikitkGjmo9Aq(TBtGQiS4BUVX0W(0TnJTyrwx8Bss7XPIHA6OsPh3eIHTrSIyZn0qE90EVyI2xQytro6w5U2wdQaR2RWNl1S7)24nlSaNU78D25V7rMe6u3FAHgdIIwQmrmftmzJqhiUyY4PnPGr5cmQubzQ8dhaHLP9e0qtEeBqeBHvdXicxvi9dHrm8BoSMFadJ2JXI4WI7dG96jBmoJnt8L86LHFgmykzCeJJr1t2awDk4mZcN9RkyprcGFmyyzY7ZPjOeNrlbs7uc5tojBbenfsrXe6rrqwa(Tbfb7UOA3Yqi)GyvWsPiVblh4CdQHEiKntOnZ(SiG9y4NVxL0Cp0lSgWlf4seeNupsCz3rrA)U9Pd76uUBFEcR(pwB6)(FH)(W0muXyIhJiflajzotdRKX1LmStGJgIFL9O3x1sQTQK5VO)HP5InS1Wkzjai714DEUBbxrpmI7UWr8YyRBj(0dR29apYBHCshifgxPAeL3cIpyEKdoZRVh5IJYp0)qhKYMGtwK4AK4dtqQoScU87tG1Xf7MT6PfO2rk5MOXhYGJ6uEKtAIcXjqngUTZepB6WNzThqADBcZvw6Hkr09kPkEahZk)0ZULLKQ4uJ5j3s)LFPi92LlLeyP0jzBe1P2xc67Qob5bsZ)FzrDNNKGkG0HNTuQ4LYNzVfG153odSZFumWRwqFcg4Q)2zGR(hfd8Qf0hZaV45FnR8JDq9Qk7Bf1MLjA)vBTz2QgS1L58qgZVXQ(wgz18k(EqKxuQ3tc5Nx9KyvBzvS9N989N989)N98TOxIV3Us2cr6R1NWZV7rSvcY2F7z2CkJRSfsU53zDeUDXCqgVNE9)Ug5S3o)8VV2PgF)5Ox15k3t7S96DfvHTRaddOC0RBE7jpd18Ho05XCwQU49Z3A)QPhxCmsPfXm1TTTz7V5ftkIbNA7DbFY74qYualAYG820uycKOH40cm0GfWctEVz2oJW6wF1xVnTIwWkOVYkOFElB3DdvxXgYBFlV)TmR1VV7bZuiyn3h82XMkzqh9SOLTYzVo1y))aYe4vVZwG)95eSBSw4(k(0aEKgTrHg1YL(6exzZ9ZZraIPasE)U2qdWxcvs74h21McTHvVBI8PcQ4h1a8qlm0dJgy6wgIkE)(ivN7IJK9WXgwCVyFeetrBIsX5A2ZpsENVNi5VHu7zFg(NuG6tqExu3z5hsExkuoGzOrCT1aTFJUNT)UK2UDHxbhDANCGZeTIgdY2MTrDs7ZlSVMcplstqMRTHtLnQ4aNx7u3PwDhyRnW(2zC2Lm9FRtiuQd4h8bWXgIlc(aypkvRun77X7rf6RUpKLXaKB)pAqA1gLBaZBXRYBxFZ3uVgEWb6eII3rwFJZWtXjKJGlFQM1TakAL08CRV2jxK7TOz(jQiZ1TaMZT6n4E3jJ8mDKSlmvXapzDSn5KakEizu2Po9zdHYozcek8RzCe(HlSomwnZ459Fc
Dieses Item verwendet einen Lua-Skripteffekt, um die zufälligen Geschenke zu erzeugen. Daher ist die Sicherheitsstufe des Items niedrig.
Wie es funktioniert
Grundstruktur
Das Spiel-Item enthält zwei innere Dokument-Objekte. Das Dokument intro
weist den Spieler in das Spiel ein, es enthält einen Link, mit dem sich das Spiel starten lässt.
Die Hauptarbeit leistet das Dokument game
. Damit wird eine komplette Spielrunde modelliert, also:
- Geschenk zufällig auswählen
- Geschenk anzeigen
- Aktion des Spielers auswerten
Geschenk auswählen
Jedes Geschenk wird zufällig aus 30 möglichen Geschenken ausgewählt. Diese sind vorab einer der drei Kategorien zugeordnet. Die Auswahl wird von einem Lua-Skripteffekt übernommen.
In diesem Effekt verwende ich math.random
, um zufällige Werte auszuwürfeln, sowie setVar
. setVar
ermöglich es mir, Objektvariablen aus dem Skripteffekt heraus zu setzen. Ich exportiere also die beiden Ergebnisse category
, damit das Spiel weiß, was die richtige Lösung ist, sowie itemName
, damit der Spieler sieht, um welches Geschenk es sich handelt.
Erste Abbruchbedingung: Falsche Kategorie
Falls der Spieler den falschen Geschenksack erwischt, soll das Spiel beendet werden. Dazu bereite ich zwei Arbeitsabläufe vor: nextRound
und gameOver
. Diese beiden Arbeitsabläufe werden innerhalb der drei Arbeitsabläufe cat1
, cat2
bzw. cat3
aufgerufen, diese wiederum durch Klicks auf die im game
-Dokument dargestellten Säcke.
gameOver
wird dabei ausgelöst, wenn der vorher gespeicherte Wert von category
nicht mit der Kategorie übereinstimmt. (Beachte die Effektbedingung)
Zweite Abbruchbedingung: Zu langsam
Für diese ist ein kleiner Trick nötig. Zunächst starte ich nach dem Anzeigen des game
-Dokuments einen Verzögerungs-Effekt. Nachdem die Verzögerung abgelaufen ist, ist das Spiel gelaufen, aber halt! Das soll natürlich nur dann passieren, wenn die Spieler zwischendurch nichts angeklickt hat.
Das realisiere ich mit einem Rundenzähler round
. Diesen gibt es einmal als Objektvariable und einmal als Arbeitsablauf-Variable thisRound
. Zu Beginn jeder Runde (d.h. nachdem der Spieler etwas angeklickt hat) erhöhe ich den Rundenzähler um eins und weise dann diesen Wert der Variable thisRound
zu. Auf diese Weise kann ich erkennen, ob ein „Timeout“ noch gültig ist (round = thisRound
), oder nicht (round > thisRound
).
An dieser Stelle sei nochmal daran erinnert, dass Arbeitsablauf-Variablen eine kürzere Gültigkeit haben als Objekt-Variablen. Mit jedem Klick im Dokument erzeuge ich neue Arbeitsablauf-Variablen, deshalb kann es auch mehrere Versionen der Variable thisRound
geben, die Objektvariable gibt es aber nur ein einziges Mal pro Item.
Ende des Spiels
Am Ende des Spiels zeige ich eine lapidare Meldung an, weshalb das Spiel beendet wurde (zu langsam oder falsch geklickt), dazu noch eine Punktzahl score
, die ich vorher in jeder Runde um eins erhöht habe.
Genug Platz für Ideen
Das Item ist ausdrücklich nicht nur dazu da, benutzt zu werden, sondern auch verbessert.
Zum Beispiel kannst du dich an folgendem versuchen:
- Verändere die Liste der Geschenke oder füge welche hinzu.
- Gestalte das
game
- undintro
-Dokument hübscher. - Verschönere die „Spiel zuende“-Meldung, und/oder baue einen „nochmal spielen“-Link ein.
- Verringere oder erhöhe die Zeit, die der Spieler zum Reagieren hat.
- Hübsch sortierte Säcke. Wäre doch schade drum, wenn jemand in jeder Runde die Reihenfolge vertauschen würde… Wer tut sowas nur?
- Mach ein Endlosspiel draus. Bei Fehlern gibt es Punktabzug statt Spielende.
- Speichere einen Highscore ab.
- Es geht auch ohne Lua-Skript. Überführe das Skript in normale Arbeitsablauf-Elemente.
- (für Fortgeschrittene) Ein Dokument reicht auch aus. Versuche, das ganze Spiel mit einem einzigen Dokument zu bauen.
- …
Viel Spaß beim Experimentieren und
bis zum nächsten Sonntag…
Extended am Sonntag #6
Zwischensequenzen
Zwischensequenzen (engl. Cutscenes) oder Dialoge sind, ähnlich wie Dokumente, besondere Objekte, die eine bestimmte Situation auf dem Bildschirm anzeigen. In Zwischensequenzen geht es darum, eine Szene mit bis zu zwei Akteuren darzustellen. Sie sind maßgeschneidert für die Anzeige von Dialogen, insbesondere innerhalb von Quests, z.B.:
- Erklärung einer Questaufgabe in Dialogform
- ein NSC erzählt etwas
- Darstellung von Interaktionen mit Objekten (Truhen, Geräte,…)
Wie Dokumente sind auch Zwischensequenzen keine eigenständigen Objekte, sondern müssen als innere Objekte eingebettet werden.
Sichtbare Struktur
Für den Spieler sichtbar ist ein Fenster mit zwei 3D-Modellen, die sich anschauen. Im unteren Bereich befindet sich ein Textfeld, welches den gesprochenen Text der Akteure darstellen kann.
Jede Zwischensequenz kann aus mehreren „Szenenbildern“ bestehen. Mit einem Klick auf das Textfeld gelangt man zum nächsten Schritt.
Außerdem ist es möglich, dem Spieler bis zu fünf Wahlmöglichkeiten anzubieten. In diesem Fall muss sich der Spieler für eine der angebotenen Optionen entscheiden, bevor es weitergeht.
Anzeige einer Zwischensequenz
Mit dem Effekt „Kampagne und Quests > Zwischensequenz starten“ wird eine bestimmte Zwischensequenz angezeigt. Diese beginnt jedes Mal von vorn. es ist also nicht möglich, eine Zwischensequenz ab einem bestimmten Schritt starten zu lassen.
Auch wenn der Effekt unter „Kampagnen“ gelistet ist, können Zwischensequenzen auch außerhalb einer Kampagne verwendet werden.
Verlauf einer Zwischensequenz
Der einfachste Dialog verläuft geradlinig. Jeder Dialogschritt wird der Reihe nach durchlaufen.
Aber es geht noch viel dynamischer.
Wahlmöglichkeiten
Mit der Schaltfläche „Wahlmöglichkeiten“ öffnet man das Menü zur Einstellung von Optionen. Jede Option ist mit einer Schrittnummer versehen. Eine Wahlmöglichkeit ist also eine Sprungmarke. Nachdem der Spieler auf eine Option klickt, springt der Dialog zum angegebenen Schritt.
Optionen können auch mit Bedingungen versehen werden. Man kann den Spieler also auch in seiner Auswahl einschränken. Bestimmte Optionen könnten nur Magiern zur Verfügung stehen, oder nur dann, wenn man vorher eine bestimmte Quest erledigt hat, oder oder oder…
Nächster Schritt
Im Feld „Nächster Schritt“ kann man eintragen, womit es im Dialog weitergehen soll. Das ist zusammen mit Wahlmöglichkeiten praktisch, denn auf diese Weise kann man Dialogteile überspringen, die zu einer anderen Auswahl gehören.
Wenn man das Feld leer lässt, geht es mit dem direkt folgenden Schritt weiter.
Es ist auch möglich, im Dialog rückwärts zu springen, bspw. wenn man möchte, dass der Spieler solange probiert, bis die „richtige“ Option gefunden wurde.
Endschritt
Setzt man die Markierung Endschritt, hört der Dialog nach Anzeige des betreffenden Schritts auf, auch wenn danach noch weitere Schritte folgen sollten. Auch das ist im Zusammenhang mit Wahlmöglichkeiten interessant, denn so lassen sich Dialogteile am Ende überspringen.
Ereignisse und Arbeitsabläufe
Vor, während, und nach einer Zwischensequenz treten Ereignisse ein, die man mit Arbeitsabläufen verknüpfen kann.
- Bevor der Dialog startet, tritt das Ereignis „Dialog startet“ ein.
- Nachdem der Dialog beendet wurde, tritt das Ereignis „Dialog beendet“ ein. Wichtig: Dieses Ereignis tritt nicht ein, wenn der Spieler das Dialogfenster zwischendurch schließt.
- Jeder einzelne Dialogschritt kann mit einem Arbeitsabluf verknüpft werden. Dieser wird vor der Anzeige des Schritts ausgeführt. Hier kann man z.B. Sounds einbauen.
Akteure
Bei „Linkes Modell“ und „Rechtes Modell“ können wir einstellen, wer oder was angezeigt werden soll. Dazu stehen verschiedene Optionen zur Verfügung.
Spielfigur
Geben wir player
ein, erscheint die Spielfigur des Spielers.
Bitte beachte, dass es nicht möglich ist, das Aussehen der eigenen Spielfigur in Extended abzuspeichern, bspw. kann ich nicht meinen Charakter als Akteur in einem Dialog anzeigen, den du auf deinem Account ansiehst.
Ziel
Mit target
legen wir fest, dass das momentan anvisierte Ziel angezeigt werden soll.
In der einfachsten Version eines Questgeberdialogs steht demnach auf einer Seite player
und auf der anderen target
.
NSC-Id
Wenn man einen numerischen Wert eingibt, z.B. 10184
, wird der NSC mit der angegebenen Id dargestellt, in meinem Beispiel Onyxia. Wer sich an den Beitrag zu Sounds erinnert, wir ahnen, woher ich diese Id kenne: https://www.wowhead.com/npc=10184/onyxia
. Man suche nach dem Namen des gewünschten NSC, und der Wowhead-Link wird dessen Id enthalten.
Bei der Benutzung von NSC-Ids ist allerdings Vorsicht geboten:
NSC-Anzeige-Id
NSC-Ids haben zwei Einschränkungen:
- Das NSC-Modell wird nur angezeigt, wenn es sich im Cache befindet. Das ist normalerweise der Fall, wenn man in letzter Zeit in der Nähe des NSCs war. Beim oberen Beispiel würde Onxyia wahrscheinlich nur dann zu sehen sein, wenn man in letzter Zeit die Raidinstanz besucht hat.
- Manche NSCs haben mehrere Modelle. Die meisten Stadtwachen haben z.B. ein weibliches und ein männliches Modell. Wenn man die NSC-Id benutzt, wird ein zufälliges Modell ausgewählt.
Beide Einschränkungen kann man mit Anzeige-Ids (engl. display id) umgehen. Diese Ids sind auch auf Wowhead zu finden. Dazu klicke ich auf „Links“ und dann „Anzeige ID“. Wowhead sagt 8570
. Damit Extendend normale NSC-Ids von Anzeige-Ids unterscheiden kann, muss ich DID
voranstellen. Ich trage also DID8570
ein und Onyxia erscheint (mal wieder).
Wer ein Bisschen auf Wowhead stöbert, wird merken, dass dort immer nur eine einzige Anzeige-Id steht, selbst wenn es mehrere Modelle gibt.
Leider ist es nicht so leicht, alle Anzeige-Ids eines NSCs zu finden, meistens liegen die numerischen Werte aber nahe beieinander. Die gefundene Id plus/minus 1,2,3,… liefert oft die anderen Modelle.
Noch etwas Deko
Dialogtext
Neben Variablen-Tags (${variable}
) kann man auch Sternchen oder Spitze Klammern verwenden, um Emotes zu kennzeichnen. Diese erscheinen dann in der gewohnten Emote-Farbe.
Beispiel: <Der Questgeber gähnt laut.> Was willst du?
Hintergrund
Standard-Hintergrund einer Zwischensequenz ist eine nachtelfisch angehauchte Szene. Dort kann man jeden Bildpfad aus dem Spiel eingeben, sofern man ihn kennt. Wie man diese findet, steht in einem anderen Tutorial.
Fürs erste kannst du versuchen, nightelf
im Standardpfad durch den englischen Namen eines anderen Volks zu ersetzen, um thematisch an deren Hintergrundbilder zu kommen.
Bild
Zusätzlich zum Hintergrund ist es möglich, ein Bild in der Mitte des Dialogs anzuzeigen.
Viel Spaß beim Experimentieren und
bis zum nächsten Sonntag…
Extended am Sonntag #7
Heute mal etwas für Fortgeschrittene und/oder Unerschrockene:
Der Lua-Skripteffekt
WoW-Addons (und auch Weakauren) sind in der Programmiersprache Lua geschrieben. Auch Extended erlaubt es, Lua-Codeschnipsel in eine Kreation einzubauen, falls die vorgefertigten Effekte nicht ausreichen sollten.
Den Effekt findest du unter „Experte > Eingeschränktes Lua-Skript ausführen“.
Eingeschränkt?
Extended kennt verschiedene Sicherheitsstufen. Der Lua-Effekt ist als niedrig eingestuft, trotzdem bietet das Addon einen gewissen Schutz, denn man kann mit dem Effekt nicht beliebigen Code ausführen. Wir werden gleich sehen, dass der Funktionsumfang trotzdem recht groß ist.
Damit erfüllt der Lua-Effekt seinen Zweck als „Sammelstelle“ für vieles, was mit den anderen Effekten nicht geht.
Die Blizzard-API bleibt dem Effekt allerdings verwehrt.
Das Basispaket
Zunächst kann man neben den Strukturelementen if
, for
, function
, … die globalen Elemente date
, ipairs
, math
, next
, pairs
, select
, string
, table
, tonumber
, tostring
, type
und unpack
benutzen.
string
ist hier besonders hervorzuheben, denn darin stecken die üblichen Funktionen zur Manipulation von Zeichenketten.
Auch math
kann interessant sein, wenn man bspw. eine Wurzel berechnen muss zur Entfernungsmessung.
Der Eingabeparameter args
Code, den man in das Eingabefeld eingibt, wird letztendlich als Funktion ausgeführt:
function(args)
-- dein Code wird hier eingefügt
end
args
ist dabei ein Parameter, den man weiterreichen sollte, wenn man die folgenden Funktionen verwendet.
getVar
und setVar
Mit getVar
kann man Variablen aus Arbeitsablauf, Objekt oder Kampagne in das Skript laden:
local wfVar = getVar(args, "w", "workflowVar")
local objVar = getVar(args, "o", "objectVar")
local campVar = getVar(args, "c", "campaignVar")
Umgekehrt kann man mit setVar
ein Ergebnis, das im Skript berechnet wird, in Arbeitsablauf, Objekt oder Kampagne speichern:
setVar(args, "w", "workflowVar", wfVar)
setVar(args, "o", "objectVar", objVar)
setVar(args, "c", "campaignVar", campVar)
Effekte auslösen mit effect
Aus einem Lua-Skript heraus kann man auch Effekte auslösen. Das ist praktisch, denn so kann man sich pro Arbeitsablauf auf ein Skript beschränken, sofern man überhaupt auf Skripte angewiesen ist.
Grundsätzlich löst man Effekte mit
effect("effect_id", args, ...)
aus. Man muss also die Ids der Effekte kennen. Bevor ich mit einer langweiligen Liste aufkreuze, lieber ein paar Beispiele.
effect("text", args, "Gruß aus Luanien", 1)
-- gibt einen Text im Chatfenster aus
effect("item_add", args, nil, "xyz123", "5", false)
-- legt 5 Mal das Item mit der Id xyz123 ins Inventar
effect("sound_id_self", args, "SFX", 1234, false)
-- spielt den Sound mit der Id 1234 im SFX-Kanal ab
Alle Effekt-Ids
cam_load
cam_save
cam_zoom_in
cam_zoom_out
companion_dismiss_critter
companion_dismiss_mount
companion_random_critter
companion_summon_mount
dialog_quick
dialog_start
do_emote
document_close
document_show
item_add
item_bag_durability
item_consume
item_cooldown
item_loot
item_remove
item_roll_dice
item_sheath
item_use
quest_goToStep
quest_markObjDone
quest_revealObjective
quest_start
run_item_workflow
run_workflow
script
secure_macro
signal_send
sound_id_local
sound_id_local_stop
sound_id_self
sound_id_stop
sound_music_local
sound_music_local_stop
sound_music_self
sound_music_stop
speech_env
speech_npc
speech_player
text
var_object
var_operand
var_prompt
Operanden auswerten mit op
result = op("operand_id", args, ...)
wertet den Operanden mit einer vorgegebenen Id aus und gibt das Ergebnis an die Variable result
zurück. Auch hier muss man die Ids kennen.
Zum Beispiel:
local playerName = op("unit_name", args, "player")
-- gibt den Namen des Spielers
local playerName = op("time_hour", args)
-- gibt die momentane Stunde
local itemCount = op("inv_item_count", args, "xyz123", nil)
-- gibt an, wieviele Items mit der Id xyz123 im Inventar sind
Alle Operanden-Ids
char_achievement
char_cam_distance
char_facing
char_falling
char_flying
char_indoors
char_minimap
char_mounted
char_resting
char_stealth
char_subzone
char_swimming
char_zone
check_event_var
check_event_var_n
date_day
date_day_of_week
date_month
date_year
inv_container_slot_id
inv_item_count
inv_item_icon
inv_item_id_weight
inv_item_name
inv_item_quality
inv_item_value
inv_item_weight
quest_is_npc
quest_is_step
quest_obj
quest_obj_all
quest_obj_current
random
time_hour
time_minute
unit_class
unit_classification
unit_creature_family
unit_creature_type
unit_distance_inspect
unit_distance_me
unit_distance_point
unit_distance_trade
unit_exists
unit_faction
unit_guild
unit_guild_rank
unit_health
unit_id
unit_is_dead
unit_is_player
unit_level
unit_name
unit_npc_id
unit_position_x
unit_position_y
unit_race
unit_sex
unit_speed
var_check
var_check_n
Benutze den Skript-Effekt sparsam
Wenn der Lua-Effekt alles kann, was die anderen Effekte auch können, könnte man in Versuchung kommen, einfach alles mit einem Skript umzusetzen.
Davon sollte man aus mindestens zwei Gründen absehen:
- Der Effekt ist als letztes Mittel gedacht, falls es unmöglich sein sollte, die Kreation auf andere Weise zu erstellen.
- Der Effekt umgeht den normalen Schritt-für-Schritt-Ansatz des Addons und macht Arbeitsabläufe schwerer nachvollziehbar.
Überlege also, ob es wirklich ein Skript sein muss, oder ob es vielleicht auch einfacher geht.
Den Überblick behalten
In seiner Rolle als Auffanglösung hat der Skript-Effekt nur ein einfaches Eingabefenster bekommen, in das der Lua-Code hineingeschrieben werden muss.
Ich empfehle, den Code in einem „guten“ Editor, z.B. einem mit Syntaxhervorhebung, zu schreiben, und dann alles zu kopieren und in das Fenster einzufügen.
Verwende Kommentare ausgiebig, damit du auch noch nach Wochen weißt, was dein Code macht oder machen soll.
Viel Spaß beim Experimentieren und
bis zum nächsten Sonntag…
Wunderschöne Guidesammlung!
Vielleicht baue ich damit auch mal weiter an eigenen Ideen.
Vielen lieben Dank dafür!
Extended am Sonntag #8
Spielereignisse
Heute soll es um einen Mechanismus gehen, mit dem das Spiel dem Interface (also auch Addons wie Extended) mitteilt, was gerade auf dem Bildschirm passiert: Spielereignisse oder Events. Wer beim Wort „Event“ an ein Rockfestival denkt, den muss ich enttäuschen. Auch simple Dinge wie ein Mausklick sind Ereignisse, und das Spiel wird uns über jeden Mausklick informieren, wenn wir das wollen.
Ereignisse "abhören"
Technisch gesehen ist ein Ereignis einfach nur eine Nachricht, die jedem Addon übermittelt wird, das danach fragt. Wenn ich z.B. informiert werden möchte, sobald meine Spielfigur das Gebiet wechselt, muss sich ein Addon für das Ereignis ZONE_CHANGED
registrieren. Extended übernimmt hierbei die Registrierungsarbeit für uns und wir müssen lediglich einen Arbeitsablauf festlegen, der bei einem bestimmten Ereignis starten soll.
Die Auswahl an Ereignissen ist reichlich. Probiere es aus:
/eventtrace
öffnet ein Fenster, das alle Ereignisse in Echtzeit auflistet. Selbst, wenn nichts besonderes auf dem Bildschirm passiert, können Ereignisse im Sekundentakt eintreten. Wenn du kämpfst oder in einer belebten Stadt bist, explodiert das Log förmlich.
Wo kann man Ereignisse einsetzen?
Grundsätzlich können nur Kampagnen auf Ereignisse „hören“, bei Items geht das nicht.
Innerhalb von Kampagnen kannst du Ereignisverknüpfungen auf Kampagnen-, Quest- sowie auf Questschrittebene einstellen.
- Kampagnenereignisse bleiben solange aktiv, wie die Kampagne aktiv ist. Wird die Kampagne pausiert, wird kein Ereignis verarbeitet.
- Questereignisse werden verarbeitet, solange die Quest sichtbar und nicht erfüllt ist.
- Questschrittereignisse werden verarbeitet, solange die zugehörige Quest sich im betreffenden Schritt befindet und nicht erfüllt wurde. Es ist also nicht sinnvoll, einem Endschritt Ereignisse zuzuweisen.
Bedingte Ereignisse
Unter „Ereignisverknüpfungen“ kannst du jedem Ereignis mit STRG
+Klick eine Bedingung zuweisen. Diese sorgt dafür, dass das Ereignis nur dann weiterverarbeitet wird, wenn die Bedingung erfüllt ist.
Das ist hilfreich, denn die meisten Ereignisse sind allgemein gehalten. Bspw. informiert das oben erwähnte Ereignis ZONE_CHANGED
nur, dass man das Gebiet gewechselt hat, aber nicht, in welches. Die Quest „Reise nach Eisenschmiede“ könnte man also erfolgreich abschließen, wenn das Ereignis ZONE_CHANGED
eintritt und der Gebietsname „Eisenschmiede“ lautet.
Ereignisparameter
Manche Ereignisse übermitteln zusätzliche Informationen. Z.B. liefert das Mausklickereignis GLOBAL_MOUSE_UP
mit, ob die linke (leftButton
) oder rechte (rightButton
) Maustaste betätigt wurde.
Der Operand „Experte > Ereignisparameter (Text/Zahl)“ ist sowohl in der Ereignisbedingung als auch im aufgerufenen Arbeitsablauf abrufbar.
Alternativ kannst du das Variablen-Tag ${event.X}
benutzen, wobei X
für den Argument-Index steht.
Beachte, dass die Argumente nur in unmittelbar aufgerufenen Arbeitsabläufen verfügbar sind.
Spezielle Extended-Ereignisse
Fünf Ereignisse werden nicht vom Spiel generiert, sondern von Extended selbst. Um es ganz genau zu nehmen, reagiert Extended auf ein anderes Spielereignis und erzeugt daraus ein angepasstes Ereignis.
Sie werden genauso behandelt wie die anderen Ereignisse, tauchen aber nicht im /eventtrace
-Log auf.
-
TRP3_EMOTE
, wenn jemand ein vorgefertigtes Emote wie/hallo
verwendet -
TRP3_ITEM_USED
, wenn der Spieler ein TRP-Item benutzt -
TRP3_KILL
, wenn der Spieler oder ein Gruppenmitglied eine Einheit tötet -
TRP3_ROLL
, wenn der „Würfeln“-Effekt verwendet wurde -
TRP3_SIGNAL
, wenn der „Signal senden“-Effekt verwendet wurde
Wie man Ereignisse findet
Wenn du ein neues Ereignis einstellst, hast du die Möglichkeit, den Ereigniskatalog zu durchsuchen. Dort sind viele (aber nicht alle) Ereignisse nach Kategorie aufgelistet, zusammen mit ihren Parametern, d.h. den Informationen, die mitgeliefert werden. Wenn du schon ungefähr weißt, in welche Kategorie dein Ereignis gehören könnte, kann dieser Katalog eine Hilfe sein.
Die „Probiermethode“ kann auch zum Ziel führen. Öffne dazu das /eventtrace
-Tool. Dann tust du, was in deiner Quest bzw. Kampagne getan werden soll. Beobachte dabei, welche Ereignisse eintreten. Wenn ein Ereignis immer eintritt, wenn du die Questaufgabe erledigt, hast du einen guten Kandidaten gefunden.
Sei präzise
Bedenke, dass Ereignisse nicht nur für Extended entwickelt wurden. Meistens werden Ereignisse öfter ausgelöst als benötigt. Du kannst Bedingungen einsetzen, um das „richtige“ Ereignis herauszufischen.
Es kann auch vorkommen, dass mehrere Ereignisse bei einer bestimmten Aktion auslösen. Das Aufsteigen auf ein Reittier löst z.B. eine Reihe von Ereignissen aus, darunter UNIT_SPELLCAST_STOP
(weil aufmountern als Zauber zählt) und COMPANION_UPDATE
. Wenn die Questaufgabe darin besteht, auf ein Mount zu steigen, solltest du COMPANION_UPDATE
verwenden, wenn es darum geht, einen Zauber zu wirken, kann UNIT_SPELLCAST_STOP
helfen. Versuche also, ein möglichst passendes Ereignis zu wählen.
Viel Spaß beim Experimentieren und
bis zum nächsten Sonntag…
Extended am Sonntag #9
Mehr Einsicht mit TRP3:Intended
Wenn die eigenen TRP3-Kreationen ambitionierter werden, wenn sich in deiner Kampagne eine stattliche Anzahl an Quests gesammelt hat oder wenn dein Item mit mehreren Variablen jongliert, kann es schonmal passieren, dass der Überblick verloren geht, insbesondere, wenn irgendetwas nicht so funktioniert, wie es soll.
Damit ich bei der Erstellung mehr Kontrolle über meine Kreation habe, habe ich mir ein Addon-Modul gebastelt, dass ihr euch bei CurseForge herunterladen könnt:
https://www.curseforge.com/wow/addons/total-rp-3-intended
Ein stiller Helfer
Zunächst wirst du nur eine neue -Schaltfläche im TRP3-Menü sehen und so soll es auch sein: Das Modul wird nur aktiv, wenn man es explizit anweist, ein Item oder eine Kampagne zu beobachten, ansonsten bleibt es dezent im Hintergrund.
Kampagnen kannst du mit der Schaltfläche „Aktive Kampagne verfolgen“, Items mit STRG
+Klick auf die Beobachtungsliste setzen. Bei einem Item wird nur das ausgewählte Exemplar beobachtet, denn jedes Exemplar kann einen anderen Status besitzen.
Objektdaten
Unter „Objektdaten“ findest du alle Objektvariablen mit ihren momentanen Werten. Außerdem gibt es eine Funktion, um den momentanen Zustand zu exportieren. So kann man bspw. andere Spieler auf den selben Stand in einer Kampagne bringen, um etwa einen Fehler zu untersuchen.
Questübersicht
Bei einer verfolgten Kampagne kannst du dir eine Übersicht mit allen enthaltenen Quests ansehen und diese nach Belieben starten oder zu beliebigen Schritten wechseln.
Das ist hilfreich, wenn du Quests testen möchtest, denn auf diese Weise musst du nicht jedes Mal die Kampagne neu starten.
Arbeitsabläufe
Zu jeder verfolgten Kreation notiert sich Intended alle Arbeitsabläufe, die ausgelöst wurden.
So kannst du nachvollziehen, ob und in welcher Reihenfolge deine Arbeitsabläufe ausgeführt wurden, oder ob sie z.B. öfter bzw. seltener als gedacht auslösen.
Kontrollpunkte
Innerhalb von Arbeitsabläufen ist es möglich, mit einem speziellen Effekt einen Kontrollpunkt anzulegen (Debug-Effekte > Kontrollpunkt). Damit hast du Einsicht in den Arbeitsablauf selbst.
Auch wenn dieser Effekt nur Intended-Benutzern zur Verfügung steht, funktionieren Kreationen weiter, wenn man das Modul nicht installiert hat. Trotzdem solltest du Kontrollpunkte löschen, sobald du sie nicht mehr benötigst, so wie ein Baugerüst abgebaut wird, wenn das Haus fertig ist.
Warnungen
Intended hat eine Bescheidene Form der sog. statischen Analyse, d.h. es kann bestimmte problemantische Konstrukte erkennen.
Wegen der Komplexität von TRP3 kann diese Liste nicht vollständig sein, und es kann mitunter falschen Alarm geben, aber es kann nicht schaden, mal einen Blick auf die Warnungen zu werfen, wenn etwas nicht richtig funktioniert.
Spoilerwarnung
Intended lässt zu, dass du mehr von einem Item oder einer Kampagne siehst, als das normalerweise der Fall wäre. Beim Erstellen, Testen und der Fehlersuche ist das praktisch, für alles andere ist das Modul nicht gedacht.
- Benutze das Modul nicht, während du eine Kampagne durchspielst!
- Fordere andere Spieler nicht auf, eine Variable manuell einzustellen, bevor sie ein von dir erstelltes Item benutzen können!
- Gehe immer davon aus, dass deine Mitspieler Intended nicht installiert haben!
Viel Spaß beim Experimentieren und
bis zum nächsten Sonntag…
Extended am Sonntag #10
Der dynamische Notizblock
Ein Notizblock? Könnte man da nicht einfach ein Dokument-Item erstellen und fertig?
Könnte man.
Aber dann lernen wir ja gar nichts über dynamische Dokumente. Und Variablen. Und man braucht jedes Mal ein neues Item für eine simple Notiz. Und außerdem. Und überhaupt.
Import-Code
Bevor es losgeht findet ihr hier das fertige Item zum Schnell-Importieren:
Notizblock
!1Ev3YPTru4xgFvMMyjHTtqtCNbd4c1oIeV4F60PRqiDSzRxKi6hNyiwpl9POx1RAEX65SlcqGi21tYypIDxT7589D(5BbUjVp3Xe)3OMb3XIZmmTQBw30W61hCYh)yc3PgUdwxh6zyukSCwqKp9r)FJZA1JZoTlolLZoQhzqwNg6PDAZDoWWaxVp)ioJ1KotuyJGG(WNtBkf(3sRW6tpj0WanKWrIao7oVy3jXrJNG2Yl(Me9lzhjstH8bKnYhaIWBGHqOIaEbbGY2e0zrCN9wXFDdNKHVyF6fPQ)y4)srsAX0126JbTOH)j4xgArkyCOY9(rHPqyQce7mB(ShMn5H)9VMT7eAWoZwa4hOLwhwSqoZIwWAtaGzGSXObDtgf9PsyyTSy(ak1LpGszBJ2mXnHpT0XtGZXrYefPNnQMT)dziVJNcc)rHaMVUlkmFWoZsJNypr6DpeBFDMuI0FuTTZ)A)pbZVF4FOGtcYlV0SyqHN2Iqmo4eLkMUvCSzi6hvWNixn(IKW7BSWd9VIIDMySdXNivcu0X8HL1q5)QEmfP1iMDuZt4oVHZExlYcVd7jD6PmR7XC2hYGK0JXd42j7gGZo(mQD8YUyuzFS7SJjTPw4EeH3tBtVRowvD2o1Ov)fVXq)OizQyshWlaIreqeG50aTu0TQOtb1ivIU9x2)FEc8nRZwl6ImnO49Z3Ko97pcWkwMxPuViuKk8KIPqGY7QAZdNlLDhg(JZGcG9SZBBRPmol09trX3ET87dWZ)YxECOV(X3SewnDLn8SBYl6RwfRuN1QiBJ40Ev7KVHWUifKRPSR6dkPQtvrBrm)zRxuH81mPi82xu(sRxOV6zKiCA21F9FUbcjn95BCHw6lOrcigF7wv32Blfspz0wCbtvgFf1f1f0SENxe4WLoNX19KZxI1ahonBC(GtHeafhJb)rPj3subtc4jA2tzKU8HOcZLChu6O15QpiRGGG1LZ2)Z0GZWb0rk(MaAXHwTPBcxikBBJkYQ881Eylr8CT5PawfGkXS3ZqsBw3sHvuW6u84QAdfugQfBw5iQQAC5p0qfSqfvCY5o0ZlAOWzZZOjN1LGHQIY2(TNaeiu29NrN2ICQLrD0mTPn3TjvUFN7yrIVljk4AGH(lv2MDe9SPYPx2M8q6QkWTVQi8RP)s(uYOt8MaXdZcdKGHLNMMxGRibbw9haLclBq9HYislzjZTkyUgJvWC)ryjfKyBBAyuIX6WSM3AUPJERXWNQo(tRiwdgvrmINTQIurlDseg0CfbUjG86sUGD8vKt0rB0ld)U179Ynb92A9Woa0IdxyBC5dESlfCdx)AHcxvCLWBxEHGJXIgCT7hg7f6pIMuHMlwGp2f)(Mp(1Ek)yQJBedXkvuMjk((QyQQn)GT4oKSj4v58kUpcNfaPEcjbLQvO0DsN2J2PQGKXajChK8Y(JI9KyVWf4VVathSwyIS(UgM7AzLpWYWU2BSTOI(sRBwhl4TTEnIe8CQF0trBkRzfgp4cmk8Q9ELQo5zO9v81EQuOTK46pLpG0DrHis3DLU98bEdNMj)6FNOvGta)Syr69NIavsjiT4Fbk5)h
Was das Item können soll
Ich möchte einen handelsüblichen Notizblock modellieren, von dem man Zettel abreißen und einzeln beschreiben kann.
- Jeder Zettel soll zunächst eine Überschrift bekommen.
- Man kann danach beliebig viel Text hinzufügen, das muss nicht sofort passieren, d.h. man soll alte Notizen später ergänzen können.
- Man kann eine Notiz unterschreiben. Wenn man eine Notiz unterschrieben hat, soll es nicht mehr möglich sein, Text anzufügen.
Bei einem echten Notizzettel würde das natürlich trotzdem gehen, aber… ich will das so
Objektstruktur
Der Notizblock ist ein Item (wow, Sherlock…), der ein inneres Item enthält, das einen einzelnen Notizzettel modelliert. In diesem befindet sich ein Dokument, dass die eigentliche Notiz anzeigt. Wir haben es also mit einem Dokument in einem Item in einem Item zu tun.
Da ich mehrere Arbeitsabläufe pro Item und Dokument benötigen werde, überführe ich als erste Amtshandlung meine Objekte in den Expertenmodus.
Variablen
Der Notizblock an sich benötigt nur eine Objektvariable: Mit charges
speichere ich, wieviele (leere) Zettel der Block noch enthält.
Die einzelnen Zettel brauchen da etwas mehr Information. Beachte, dass die Objektvariablen der Notizzettel nichts mit der des Blocks zu tun haben. Auch wenn der Block zerstört wird, bleiben die abgelösten Zettel erhalten.
-
initialized
erhält den Werttrue
, sobald der Zettel zum ersten Mal benutzt wird. -
title
speichert den Titel bzw. das Thema der Notiz. -
content
enthält den eigentlichen Inhalt, der nach und nach eingegeben wird. -
controls
beinhaltet die Schaltflächen „Text hinzufügen“ und „Signieren“. Nachdem signiert wurde, wird diese Variable mit einem Text „unterschrieben von…“ überschrieben. -
signature
enthält die „Unterschrift“, sobald unterschrieben wurde. -
addedText
ist eine temporäre Variable, um Text zwischenzuspeichern, der hinzugefügt werden soll.
Das äußere Item: Notizblock
Der Notizblock ist ein benutzbares Item. Im Tooltip informiere ich den Spieler mit ${charges::100}
darüber, wieviele leere Zettel noch am Block heften. Die ::100
stellt dabei sicher, dass auch dann etwas angezeigt wird, wenn der Block noch nie benutzt wurde und somit die Variable charges
noch nicht existiert.
Benutzt man den Notizblock, wird onUse
ausgeführt. Achtung: Es gibt einen weiteren Arbeitsablauf mit selbem Namen im Notizzettel-Item.
- Initialisiere die Variable
charges
mit 100. Indem ich „Initialisieren“ statt „Wertzuweisung“ verwende, wird der Effekt nur bei der ersten Benutzung aktiv. Danach wird er ignoriert, da es die Variablecharges
schon gibt. - Verringere
charges
um 1, denn wir reißen gleich ein Blatt ab. - Spiele einen passenden Sound ab. Geschenkt.
- Füge das innere Item (Notizzettel) dem Spielerinventar hinzu.
- Bedingung: wenn
charges
Null erreicht: - Verbrauche (d.h. Zerstöre) den Notizblock.
Das Dokument
Der Inhalt des Dokuments, also des Objekts, das später meine Notiz auf den Bildschirm bringt, sieht… unspektakulär aus:
{h1:c}${title}{/h1}${content}
${controls}
In diesem Fall liegt die Würze in der Kürze.
${title}
wird durch den vom Spieler vergebenen Titel ersetzt, ${content}
entsprechend durch den Inhalt.
${controls}
beinhaltet die beiden Schaltflächen/Links, mit denen ich Text hinzufügen kann oder signieren.
Die drei Dokument-Arbeitsabläufe onAddTextClick
, onAddTextInput
und onSignClick
heben wir uns für später auf, denn zuerst kümmern wir uns um…
Das innere Item: Notizzettel
Wie der Notizblock ist dieses Item als benutzbar angelegt. Im Tooltip zeige ich mit ${title::<Kein Titel>}
den Titel an. Man muss also nicht jede Notiz „durchklicken“, um den Titel zu erfahren.
Außerdem fülle ich das Feld „Beschreibungstext“ mit ${signature::Ein einfacher Notizzettel}
aus. Das sorgt dafür, dass eine nicht unterzeichnete Notiz mit der Bemerkung „Ein einfacher Notizzettel“ versehen wird, ein unterschriebener hingegen mit der entsprechenden „Unterschrift“.
Der Notizzettel hat zwei Arbeitsabläufe: onUse
und initialize
. Wenn man einen Notizzettel benutzt (also rechtsklickt), …
- … wird das innere Dokument angezeigt, aber nur, wenn die Variable
initialized
auftrue
gesetzt ist. - … wird
initialize
aufgerufen, aber nur, wenn die Variableinitialized
nicht auftrue
gesetzt ist.
Anders ausgedrückt: onUse
bereitet den Notizzettel vor, falls das noch nicht geschehen ist, ansonsten zeigt es seinen Inhalt an.
initialize
kümmert sich darum, dass beim ersten Benutzen alle Variablen korrekt eingerichtet werden:
- Setze
initialized
auftrue
. - Lege die Variable
content
an. In der Datenbank sieht das Konstrukt etwas merkwürdig aus. Dort stehtcontent [=]
, aber es hat seine Richtigkeit. Der Inhalt soll anfangs leer sein. - Weise der Variable
controls
den Wert{h3:c}{link*onAddTextClick*Text hinzufügen} {link*onSignClick*Signieren}{/h3}
zu. Hier sind also die beiden Schaltflächen, die später im Dokument zu sehen sein werden. Sie stehen deshalb nicht direkt im Dokument, damit ich sie nach dem Signieren entfernen kann. - Eingabeaufforderung für die Variable
title
. Der Spieler soll hier einen Titel wählen. Sobald das geschehen ist, wirdonUse
aufgerufen.
Wer aufmerksam gelesen hat, wird feststellen, dass die beiden Arbeitsabläufe sich gegenseitig aufrufen. Das könnte sich zu einer gefährlichen Endlosschleife entwickeln. In diesem Fall ist das kein Problem, denn wir steuern die Aufrufe über die Variable initialized
. Indem wir sie im ersten Schritt auf true
setzen, stellen wir sicher, dass es kein ewiges hin und her geben kann.
Interaktionen im Dokument
Wie oben erwähnt, bleiben noch die drei Dokument-Arbeitsabläufe zu klären.
onAddTextClick
wird ausgelöst, sobald der Spieler auf die entsprechende Schaltfläche unten im Dokument klickt. Als einzigen Effekt enthält der Arbeitsablauf eine Eingabeaufforderung für die Variable addedText
.
Ist der Spieler fertig mit Schreiben, wird onAddTextInput
aufgerufen.
Hier wird im ersten Schritt die Variable content
modifiziert: ${content}{p} {/p}{p}${addedText}{/p}
. Der neue Wert besteht aus dem alten Wert (dafür sorgt der erste Teil ${content}
), sowie dem neuen Text ${addedText}
, der in einen Absatz eingebettet wird (dafür sorgen {p}
und {/p}
).
Dazwischen habe ich ein komisches Konstrukt {p} {/p}
reingemogelt. Dieses erzeugt eine Leerzeile. Das Leerzeichen in der Mitte ist ein geschütztes Leerzeichen, sonst funktioniert der Trick nicht.
Das geschützte Leerzeichen ist über die Tastenkombination ALT+255
erreichbar. Im Gegensatz zum normalen Leerzeichen wird es wie Text behandelt. Ohne dieses spezielle Zeichen würde Extended „denken“, dass mein Absatz leer ist, und ihn nicht anzeigen. Ich habe also einen nicht leeren Absatz und Extended spendiert mir den Zeilenumbruch, den ich haben möchte. Der Absatz an sich ist nicht zu sehen, weil… naja, weil er nur aus Leerzeichen besteht.
Im zweiten Schritt wird das Dokument erneut angezeigt. Bedenke, dass an dieser Stelle das Dokument bereits offen ist, aber indem ich es erneut öffne, wird auch der Inhalt mit dem neuen Wert von content
aktualisiert.
Der letzte Arbeitsablauf ist onSignClick
, der ausgelöst wird, wenn der Spieler seine Notiz unterzeichnen möchte.
Ähnlich wie bei onAddTextInput
werden hier Variablen neu gesetzt.
Zunächst bekommt controls
den neuen Wert {h3:c}unterzeichnet von ${trp:player:full}{/h3}
. Damit werden die beiden Schaltflächen durch einen Text ersetzt und sind nicht mehr anklickbar. ${trp:player:full}
ist ein Variablen-Tag, das den Spielernamen beinhaltet.
Danach wird das Dokument erneut angezeigt, damit die Änderungen sichtbar werden.
Im letzten Schritt wird die Variable signature
eingestellt. Dadurch ist dann bereits im Item-Tooltip ersichtlich, dass die Notiz fertiggestellt wurde, und nicht mehr veränderbar ist.
Viel Spaß beim Experimentieren und
bis zum nächsten Sonntag…
Diese Richtlinie mit nur 3 Posts ist seltsame. Die Guides sind aber gut.
Extended am Sonntag #11
Karten und Würfel
Heute geht es um eine häufig vorkommende Frage: Wie kann man Würfel oder das Ziehen von Karten aus einem Kartenstapel modellieren?
Man ahnt es vielleicht schon, es wird um Zufallswerte gehen.
Würfel erzeugen unabhängige Zufallsereignisse. Wenn ich mehrmals würfle, hat das Ergebnis nichts mit den vorherigen Würfen zu tun. Selbst bei einem gezinkten Würfel ist das so, auch wenn dort nicht alle Ergebnisse gleich wahrscheinlich sind. Münzwürfe fallen auch in diese Kategorie.
Im Gegensatz dazu entstehen beim Kartenziehen abhängige Ergebnisse. Wenn ich in einem normalen 32-Karten-Blatt einmal das Pik As ziehe, kann die nächste Karte sicher kein Pik As mehr sein. Beim Lotto ist es ähnlich.
Überlege also zuerst, in welche Kategorie deine zufälligen Ereignisse passen könnten.
Einmal Würfeln, gleiche Chancen
Wenn man „nur“ würfeln möchte, hat Extended das passende Werkzeug: Inventar > Würfeln.
Hier kannst du einstellen, wie gewürfelt werden soll. 1d6
steht z.B. für den klassischen, sechsseitigen Würfel, 1d100
entspricht dem /würfeln
-Makro. Das Würfelergebnis wird auf dem Bildschirm ausgegeben. Optional kannst du das Ergebnis in eine Variable speichern.
Wenn das Ergebnis keine Zahl, sondern eine zufällige Begrüßung, ein zufälliger Soundeffekt, o.ä. sein soll, benutzt man einen Trick: Jedes mögliche Ergebnis bekommt eine Nummer. Dann würfelt man die Nummer aus und voilà.
Hierzu sollte man Experte > Dynamischer Wert > Zufallswert einsetzen, denn wir sind nicht am Ergebnis selbst interessiert (der Zahl), sondern dem zugewiesenen Ereignis.
Eine ausführliche Beschreibung zum Erstellen einer Wurfmünze findet ihr hier: [Guide][Wiki-Beitrag] How-To Total RP 3 Extended ❔ - #4 von Enestress-die-aldor .
Das folgende Beispielitem gibt die Worte „EINSicht“, „AbZWEIgung“ oder „RunDREIse“ aus, jenachdem wie man gewürfelt hat. Das Grundprinzip ist immer das selbe: Erzeuge im ersten Effekt einen zufälligen Wert und frage ihn danach in den Effektbedingungen der Reihe nach ab.
Wörterwürfel
!9ozuVTjmqC8prRfCGULi1hiatcPoNUCjG2EWjuIxckeycijD7H8XApT36xS()m0TQLSxRWY(8DNV7hN9PSvZusBmSgyPKcfzjSCX4g3HzwXwFujhapOijMBv0SVazSN8zTvLZB0SanJN5yr6UacP8vk6qA9IQVRRtlXM061nDwPv5z6PvffMuEKtcX(uTtjDEHi2gm02)ryuK3G5sfjyfoNNVw9JWqwfsxV6EBDOKTrNTDboFAphhna8hA48WkU92()7d)LGxJ)09LbtdJWppZO9)siwh8MWM4cS59WxtcJwVVC9LHJlFoScXBcI2xaXWijLNTP9)d4RU2xPBtZlA6E)9PaoS(b4DQ71wIRfItlfwJCVzK7qOFmEkQl0h0nVB2M6uGefBECrxWYDt4GJyIvjlhJNWx5CLdqjUVJGOZs1WrqpqXFcJYKi1diejkjCoyUzbnhOctuKICFKfMcb(in6S915T)4oWrHHRXECqUNq6Sh(b0BHJMxEyXU8MSfCDDHLTjvijspfL80VQB11hF631FdHGB8GLp7zk737XBMl55ypdk(t5nbC8f2VhEgACHuJn1eiNyoKVjojH8HAno01K33ItEq8N73DAzsxMlpTSwJBWMTf5zB1LDx9oVuaBvQN)d
Würfeln mit ungleichen Chancen
Wenn ein gezinkter Würfel oder allgemein unterschiedlich wahrscheinliche Ereignisse benötigt werden, kann man sehr ähnlich wie im ersten Teil vorgehen, nur, dass man diesmal den Ergebnisbereich abfragt.
Als Beispiel habe ich eine Schatulle gebaut, die mit 15%-iger Wahrscheinlichkeit ein seltenes Item enthält. Ich habe das Item so konstruiert, dass es nach der Benutzung nicht verschwindet oder „leer“ ist. So könnt ihr den Effekt leichter ausprobieren.
Schatulle
!nx12UTnpm4NO1AlNGGvSnG0KITa060f5KIDJSvSzs0IpuiRCO7c)ySNMDxFXgPSttNtXF)3agWsmKLejf)iP(OfUIaHVl(445i8zcUdZPlZ1L50zAqxZEHVhQbFKpnA0YYv0KPCAKpqWlYNwccJGFzFARB54r5(wxRkZf8bJPr)(c(h1p(J8Y7L5ODKlDWFc(N7BDk)ssT76tJZWTqjdMqlgsNhZ1d18kA9i0LQ8THFf2bPXA5cJkFzypNWLqMdlCPga0b3ApOP(weeyp6ROd1yXCWbuhyHsaUn9NWkUZ4rK(3CNfedNAFrbAh81ibV7EAYeCczInTGVVziz6nJXqDmTo4lOmReTudVEgJXEHmwaOsRI2wKxfnotwf9jvAcuwfnujtGStsJEVAA0Pt70ySUyxEOJ7)sPSJZFrpZPBSCO(AlotLi4BL6WI7bTmhxi1llRLYtuXWKI0u7DSD2uePtrMWVZH79Kmkn2esO3XNuvjoI3Ky0gEN6rLbYcLj4S4c0PnIAKxdO4vq86q8mKnOzNfgpHjYx0gFOHITfvQ7bm8SGOfHSkQMfsM6sMoNcgQAc5Mc9dTJcdfe)nX)7E)VBay5e)FXpvf8owCkH4nAL5HRHTqQTI2MzX7Jt)2MSQOh)(If5asF0q8kt56uv8AS9WF292293oqHMxSpKiwh5T84vsZM0uWEJD(tn5Eof7)Sjhgrx(RK5wmYAUZ1i3H62nGugsXes5BcwPLyTHpt43ZEeNiHpeRiDp3HDoJvfXCVWT3fyR)dmrEYmSUCwNZWqYmR57c8bNye7cmOrOKagPkTSPLavjRn2ie)m
Obwohl es in diesem Beispiel nur zwei mögliche Items gibt, würfle ich von 1 bis 100. Wenn ich eine Zahl zwischen 1 und 15 erwürfelt habe (beachte die Effektbedingung), bekommt der Spieler das besondere Item, ansonsten Schrott. 15 von 100 Würfelergebnissen sind also „gut“, das entspricht den 15%, die ich vorher festgelegt habe. Ich hätte auch 86 bis 100 als „gut“ definieren können, nur die Größe des Bereichs ist hier wichtig.
Karten ziehen
Sobald wir mehr als eine Karte aus einem Deck ziehen, müssen wir etwas tiefer in die Trickkiste greifen. Extended muss sich nämlich irgendwie merken, welche Karten bereits gezogen wurden, oder alternativ, welche noch gezogen werden können.
Bevor ich mich darum kümmere, erstelle ich ein Kartendeck-Item für ein Skatdeck, d.h. ich werde 32 innere Items benötigen. Jetzt könnte ich die 32 Items von Hand erstellen… oder ich baue mir fix ein paar Makros.
Kleiner Exkurs in die TRP3-API
Der folgende Teil ist optional. Ihr könnt die inneren Items auch wie gewohnt erstellen.
/run _A = function(id, name) TRP3_ToolFrame.specificDraft.IN[id] = {TY = TRP3_DB.types.ITEM, MD = {MO = TRP3_DB.modes.NORMAL}, BA = {NA = name}} end
Makro Nummer 1 erzeugt eine Funktion, die ein inneres Item mit vorgegebener Id und vorgegebenem Namen erstellt. Das Item muss dabei in der Datenbank geöffnet sein.
/run _V = {"Sieben", "Acht", "Neun", "Zehn", "Bube", "Dame", "König", "As"} _C = {"Herz", "Pik", "Kreuz", "Karo"}
Das zweite Makro enthält die zukünftigen Namen der Karten.
/run local i = 1 for _, v in pairs(_V) do for _, c in pairs(_C) do _A(("card%02d"):format(i), c .. " " .. v) i = i + 1 end end
Das dritte Makro führt letztendlich meine Einfügefunktion 32 Mal aus. Damit man die Items auch sieht, muss man gegebenenfalls einmal ins Übersichtstab und zurück wechseln.
Zurück zum Deck
Wir haben es also irgendwie geschafft, die inneren Items card01
bis card32
zu erstellen.
Bei einem echten Kartenspiel würde man das Deck zuerst mischen, und dann so viele Karten von oben abnehmen, wie benötigt.
Es gibt aber noch eine andere Methode, die zumindest mathematisch gleichwertig ist: Man beginnt mit einem sortierten Deck. Dann würfelt man von 1 bis 32 und entnimmt die Karte an der entsprechenden Position. Für die zweite Karte würfelt man von 1 bis 31 usw.
Diese Methode werde ich vorstellen. In meinem Beispiel soll folgendes passieren:
- „mische“ den Stapel, d.h. sortiere alle Karten
- entnehme bis zu 5 Karten
- nachdem 5 Karten entnommen wurden, entferne alle gezogenen Karten aus dem Spielerinventar und mische neu
Kartendeck
!DEv4YnQnq43f3PZKmtQpHSVCjE69dsSN5CsoSRfoPT)qgmOeRAmCJqKEx6uES6lqFX6kjigi1tL7yhzqR(0(PD3Vfc1H6t9CGVObiQhMsqy05ieC7qIJxro1BaScYup1yuOigRqq(8y94mkXBgvsj()cSgyMRCvZ75sjFIjEPmyC4ogyxwHfBh258TDHcSZEOU57b6yjFVvWkac7XksB41JdCt)IFVDqnXPB)7)kL)udwBj6kh)RSnnyn(C7aRJvVXZwMJUnuK1nCHV0oSMZCRS0XKEB7uNHhb02bQbh1zTfF)GDinN0Rkw3OugDHDyR8A0gzdSw636cYwGDSm9ubUnRDSSOYW6ofKdSeRXXTv(iTgCjrnsUMsYsxMRmxH7ExQhcS4txtj)KRQzgzUR6MLEQX5eykNloNsEqp71l02iQXXkBy0aa4eTTzQrYvQX9zpcNTMLcw1U4Hjk)j1CXVMn(Ac4tRc2W(aZmBQA9F(bn)gV0qt4ame(zkL8(VQUybCHcIUbR8GbP6yHLjbtxWwjF8Xv50TTa64sHvXS9vpwwZRjE7cpSLTgmfEDKAw2gTQHuB1ILNyd22f8il7jz4C3GfYY2s6GvxWylJ0gx3nnJpkI3rOAz7fta71M4VQKnk1lXA15v1QXkVjzPXSOTnf64APQryx1iWOVRu2xwRSNc6UWOnC2ZSDSu5QNk4jXFHj2Ukjm(BjfQDUzxdl06eDFj19E6R4XusEKG)fy9HINYngijzrHjLbx7Uy8Q5U((tw4vg8XYGEDErRYavm87r44EL3OW3pB9VXIK9FouKRr82jZeLb)XFwEd)XYG0m5BxsFvmZSUV7a2(ryNXFOmqUHLwEtzWb3KpA8LAfpQ2qUEkNZgGldIZmguFyp(iG8KECjB3kbBx2ZSENz21ZAhhgb7ZUq5j8tbdoie6097ImCDcRppnNjKN8VYiadVcauBuDrLVLSVkB40EJ5mOwxxfvg87fIy1VPSIYGNy745GUVVA5oW(XsYzMnRkZj0NtGNB6lctJZ2DIZzhkCEAlKQeQgS5WyIfh8WioT9zqh)cJJ)pcEkNOJFWFyf)tJRLvW3eEUKw)HeZKH8KCDj8KRxTyIlzM3kqYmDUFv5A3x)xJ7UPkiZw24HVgzozmG483HWVddvbooJqdhHvIqq(syjGAl)h83ictsa9jOyVam1cccnYbnAa8KW7GogQNYkVV6Fgr3dzYpRf5VzZIVh0F9h2FOIl)pEC7NuDCA0DQ7RxqCHlfmOWiFBcpAllfcVf7GSdpTUuQm4fOa6foBJU)ljNfvi4YVDhW0evWus9gUNM0)5
Das ganze funktioniert mit einem Lua-Skripteffekt. Ich habe das Skript kopiert und Zeilennummern hinzugefügt, damit ich jede Zeile einzeln erklären kann. Wenn ihr den Code weiterverwenden wollt, müsst ihr die Zeilennummern löschen.
01 local CARD_PATTERN = "0206001034S1Nus card%02d"
02 args.object.vars = args.object.vars or {}
03 if not args.object.vars.deck or #args.object.vars.deck <= 27 then
04 args.object.vars.deck = {}
05 for i = 1,32 do
06 effect("item_remove", args, CARD_PATTERN:format(i), 1000)
07 table.insert(args.object.vars.deck, i)
08 end
09 effect("text", args, "Die Karten wurden neu gemischt.", 1)
10 else
11 local r = math.random(1, #args.object.vars.deck)
12 local card = table.remove(args.object.vars.deck, r)
13 effect("item_add", args, CARD_PATTERN:format(card), 1, 2)
14 end
Zeile 1
Hier speichere ich die Id einer Spielkarte ab %02d
steht als Platzhalter für die Kartennummer. Der erste Teil ist die Id des Kartendecks. Wenn ihr ein eigenes Item baut, müsst ihr diese entsprechend ändern.
Zeile 2
Mit args.object.vars
habe ich direkten Zugriff auf die Objektvariablen. Ich muss getVar
nicht verwenden und in diesem Fall könnte ich es auch gar nicht, denn meine Variable deck
wird eine Tabelle enthalten. Dieser Fall wird von getVar
nicht richtig behandelt.
Diese Zeile übernimmt den Fall, dass es noch keine Variablen im Objekt gibt (also Initialisierung).
Zeile 3
Hier prüfe ich, ob mein Deck gespeichert wurde, und wenn ja, wieviele Karten noch darin sind. Es muss gemischt werden, wenn die Variable deck
nicht existiert oder 27 oder weniger Karten im Deck sind.
Zeile 4
Bevor ich mische, lege ich einen leeren Kartenstapel an.
Zeile 5
Ich betrete eine Schleife über alle meine 32 möglichen Karten.
Zeile 6
Innerhalb der Schleife entferne ich jede Karte aus dem Inventar. Sollte die Karte gar nicht im Inventar liegen, passiert nichts. Als Vorsichtsmaßnahme werden bis zu 1000 Exemplare einer Karte entfernt. Es könnte nämlich sein, dass der Spieler mehrere Decks im Inventar hat und simultan zieht.
Zeile 7
Hier wird die Tabelle mit verfügbaren Karten gefüllt. nachdem das passiert ist, stehen dort einfach nur die Zahlen 1 bis 32 der Reihe nach.
Zeile 9
Ich gebe dem Spieler den Hinweis, wenn gemischt wird, damit sich niemand wundert, warum die ausgegebenen Karten verschwinden.
Zeile 10
Ab hier wird der Fall behandelt, dass eine Karte gezogen werden soll anstatt gemischt.
Zeile 11
Zuerst würfle ich aus (math.random
), welche Karte vom Stapel genommen werden soll. Diese Zahl ist die Kartenposition, nicht die Kartennummer.
Zeile 12
Mit table.remove
entnehme ich die Karte an der zuvor ausgewürfelten Position. In der Variable card
steht jetzt eine Kartennummer.
Zeile 13
Schließlich muss ich noch die Karte ins Spielerinventar legen. Mit CARD_PATTERN:format(card)
erzeuge ich aus der Kartennummer eine Karten-Id.
Uff, so ein Kartendeck zu bauen war dann doch ein ganzes Stück schwieriger als ein paar Würfel. Das liegt daran, dass sich Extended die verfügbaren Karten merken muss. In diesem Fall ist der Lua-Effekt kaum zu vermeiden, denn das ist momentan die einzige Möglichkeit, in Extended Tabellen bzw. Listen zu verarbeiten.
Viel Spaß beim Experimentieren und
bis zum nächsten Sonntag…
Extended am Sonntag #12
Kampagnen in Aktion - Echte Beispiele
Dieser Beitrag wird weniger ein Tutorial und mehr eine Vorstellung meiner eigenen veröffentlichten Extended-Kampagnen.
Vielleicht dienen sie dem einen oder anderen als Inspiration für eigene Projekte oder als Musterlösung für eigene Quests.
Wer wissen möchte, wie die Quests umgesetzt wurden, darf selbstverständlich gern einen Blick in die Datenbank riskieren. (Spoilerwarnung und so)
Was man benötigt
Für jede der vier Kampagnen ist ein Hordecharakter erforderlich, dieser muss aber nicht die Maximalstufe erreicht haben.
Wegen der Größe der Kampagnen kann der Import-Code nicht direkt ins Forum eingebettet werden, stattdessen gebe ich einen Wago.io-Link an. Auf Wago könnt ihr den Import-Code mit Hilfe der Schaltfläche „Import-String kopieren“ rechts oben abrufen.
Jenachdem, mit welcher Hardware ihr ausgestattet seid, kann das Spiel beim Importieren für einige Sekunden einfrieren. Wenn ihr bereits sehr viele Extended-Kreationen gesammelt habt, ist es ratsam, vorher eine Sicherheitskopie eurer Extended-Datenbank zu machen. (Okay, das ist eigentlich immer ratsam.)
"Die Waldläuferin Seleves"
https://wago.io/ZJwzNecYd
Mit dieser Kampagne habe ich mir selbst Extended beigebracht. Sie ist gespickt mit Rätseln und vom Schwierigkeitsgrad höher angesiedelt als die anderen Kampagnen.
Als Mischung aus Prototyp und Probieren-was-geht werden beim Durchspielen sicherlich einige Ecken und Kanten auffallen.
"Jagd auf Velaves"
https://wago.io/EmDGBbUUV
Wenn der Main eine Kampagne bekommt, warum nicht auch die Twinks? Wie in allen vier Kampagnen geht es thematisch um einen Charakterkonzept und dessen Umgebung.
"Azeroth ist nicht genug"
https://wago.io/HzQHtU3Vu
Die Antwort auf die Frage, ob man mit Extended auch Parodien machen kann. Sie lautet: Ja, es geht. Ob man es auch machen sollte, bleibt offen
"Söhne der Steppe"
https://wago.io/NLBslCmbY
Wer nicht genug vom Brachland bekommt, ist hier genau richtig. Wer Quests mit inhaltlichen Entscheidungen mag, auch.
Ich hoffe, diese kleine Sammlung dient als Inspiration für ein ähnliches Projekt. Vielleicht eine Charakterkampagne? Vielleicht eine Gildenkampagne? Oder eine eventbegleitende Kampagne?
An dieser Stelle sei auch darauf hingewiesen, dass es auf Wago noch weit mehr interessante Kreationen von anderen Autoren gibt. Klickt euch einfach mal ein wenig durch https://wago.io/totalrp
.
Viel Spaß beim Experimentieren und
bis zum nächsten Sonntag…
Extended am Sonntag #13
Wie man seine Extended-Daten rettet
Zur Abwechslung gibt es jetzt mal was total langweiliges, aber gleichzeitig auch total wichtiges.
Wenn man sich einloggt und plötzlich keine Profile mehr hat, oder alle eigenen Extended-Kreationen verschwunden sind, ist es meistens schon zu spät.
Daten muss man sichern, bevor das Unheil eintritt. In diesem Beitrag erkläre ich, wie, warum und wogegen ihr eure Extended-Daten schützen solltet.
Warum muss ich mich darum kümmern?
Deine Profile, dein Inventar und auch deine Extended-Kreationen werden auf deinem eigenen Computer gespeichert und nirgendwo sonst (es sei denn, du sorgst manuell dafür).
Wenn diese Daten aus irgendeinem Grund verschwinden, kann dir niemand helfen, selbst wenn er/sie es wollte.
Sich eigene Kreationen auszudenken kann durchaus Spaß machen, aber das ganze nochmal abzutippen oder einzugeben macht dann schon weniger Spaß.
10 Minuten Backup-Verwaltung können hier stundenlangen Frust ersparen.
Wieso können die Daten überhaupt verloren gehen?
Man sollte meinen, dass Datenverlust ein Problem aus den 90ern ist.
Meistens ist WoW selbst schuld
Ein typisches Szenario: WoW stürzt ab und danach sind alle Addon-Einstellungen futsch. Der Spielabsturz ist in diesem Fall oft nicht das Problem, erst mit dem darauf folgenden Spielneustart gehen die Addondaten verloren. Aber wer denkt bei einem Absturz im Eifer des Gefechts schon daran, zuerst nach rettbaren Daten zu sehen? Auch hier wieder mein Mantra: Daten vorher sichern, nicht erst warten, bis das Spiel abstürzt!
Bedenke, dass nicht nur Spielabstürze ein Problem verursachen können. WoW verwaltet für uns den verfügbaren Speicher. Das ist ein Sicherheitsmechanismus, damit Addons die Festplatte nicht zumüllen können. Wenn du viele Addons verwendest, die viele Daten abspeichern, kann es passieren, dass WoW sich weigert, all diese Daten auf der Festplatte zu speichern. Extended muss dabei nichtmal der Verursacher sein, denn alle Addons teilen sich denselben Speicher.
Andere Ursachen
Natürlich kann es auch zu Hardware-Fehlern kommen (d.h. deine Festplatte geht kaputt).
Auch wenn es banal klingt, es kommt hin und wieder vor, dass man selbst Kreationen oder Profile löscht, die man später wiederhaben möchte. Mitunter hast du dich bei der Erstellung eines Items verhaspelt und möchtest dieses auf einen früheren (funktionstüchtigen) Stand zurückversetzen.
Wo speichert Extended seine Daten ab?
In deinem WoW-Installationsverzeichnis (unter Windows standardmäßig C:\Programme\World of Warcraft\
) gibt es das Unterverzeichnis World of Warcraft\_retail_\WTF\Account\[AccountName]\SavedVariables
. Den grün hinterlegten Teil musst du dabei durch deinen Account bzw. deine Accounts ersetzen.
Besonders drei Dateien sind für Extended wichtig.
-
totalRP3.lua
speichert deine eigenen Profile, deine TRP3-Einstellungen und das Extended-Inventar sowie das Extended-Questlog deiner Charaktere. - In
totalRP3_Data.lua
werden die Profile anderer Spieler abgespeichert.
AHA! Mein Profil wird also doch auch woanders abgespeichert! … Mag sein, aber es ist nicht ohne weiteres in ein eigenes Profil umwandelbar. -
totalRP3_Extended.lua
enthält deine eigenen Items und Kampagnen sowie solche von anderen Spielern, und außerdem alle Verstecke und sonstige Drops aus dem Dropsystem.
Diese drei Dateien sind das Minimum, dass du sichern solltest. Wenn du dann schonmal dabei bist, kannst du selbstverständlich auch großzügig den gesamten WTF
-Ordner sichern. Dann ist nicht nur Extended gesichert, sondern auch alle anderen Addons.
Backup-Lösungen
Cloudservices
Das jedes Backup darauf hinausläuft, die Daten irgendwo hinzukopieren, wo sie sicher(er) sind, kann man einen Cloudservice benutzen. Diese kann man üblicherweise so einstellen, dass sie die Daten automatisch synchronisieren, man muss sich also nur ein einziges Mal wirklich darum kümmern. Außerdem kann man relativ komfortabel beliebige ältere Versionen wiederherstellen.
Die TRP3-Entwickler haben unter https://github.com/Total-RP/Total-RP-3/wiki/How-to-backup-and-synchronize-your-add-ons-settings-using-a-cloud-service
einen englischsprachigen Guide bereitgestellt, wie man soetwas aufsetzen kann.
Manuelles Backup
Wer einen Cloudservice zu teuer/zu aufwändig/zu vertrauensunwürdig findet, kann die Dateien auch einfach selbst regelmäßig irgendwohin kopieren. Idealerweise ist das „irgendwohin“ ein anderes Gerät (andere Festplatte/USB-Stick/Handy).
Hierbei sollte man darauf achten, dass man das Backup auch wirklich regelmäßig macht, z.B. einmal im Monat, oder immer, nachdem man etwas größeres erstellt hat.
Ein Backup anwenden
Sollte der Fall der Fälle eingetreten sein, empfehle ich folgende Vorgehensweise:
- Atme beruhigt auf, denn du hast ja ein Backup gemacht.
- Sichere den aktuellen Stand deiner Addon-Daten, selbst wenn du ihn als „kaputt“ erachtest. Das ist für den seltenen Fall, dass das Backup nicht funktioniert.
- Wichtig: Beende das Spiel.
- Ersetze die Addon-Dateien im
SavedVariables
-Ordner durch dein Backup. - Starte das Spiel neu.
- Überprüfe, ob sich TRP3 (und ggf. andere Addons) wieder in einem akzeptablen Zustand befindet.
Viel Spaß beim… ähm Datensichern… und
bis zum nächsten Sonntag…
Juhuu! Ich sollte öfters mal Backups machen… allein dafür, sollte ich einen neuen PC kaufen.
Arigatou Sensei Seleves!
Extended am Sonntag #14
Neue Funktionen in Extended 1.5.0
Im Zuge von Patch 9.2 wurde Extended um einige Features angereichert. Auch wenn es sich hierbei um kein großes Release handelt, ist für den einen oder anderen bestimmt etwas nützliches dabei.
Id-Operanden
Zwei neue Operanden erlauben es, die Id der aktuellen Kampagne sowie die Id von Items in einem Behälter festzustellen.
Ersteres ist nützlich, wenn man sicherstellen will, dass Kampagnen-Items nur benutzt werden können, während eine bestimmte Kampagne läuft.
Den zweiten Operanden kann man benutzen, um zu erkennen, ob sich ein bestimmtes Item in einem Behälter befindet, oder ob es sich an einem bestimmten Platz befindet.
Item-Eigenschaften
Mit einer weiteren Reihe von neuen Operanden lässt sich der Name, das Icon, der Wert, die Qualität und das Gewicht eines Items mit vorgegebener Id ermitteln. In Kombination mit den oben genannten Id-Operanden kann man jetzt sehr viel mehr über den Inhalt einer Tasche erfahren, als es vorher möglich war.
Datums-Operanden
Während man bisher nur die Tageszeit (Stunde und Minute) ermitteln konnte, kommen jetzt auch Jahr, Monat, Monatstag und Wochentag hinzu.
Wer schon immer Dailys und Timegating in Extended umsetzen wollte, kann das jetzt tun .
Beachte, dass sich alle Zeitangaben auf die Ortszeit des Spielers beziehen.
Ausklingdauer bei Sounds
Mit der optionalen Ausklingdauer können Sounds nun auch sanft ausgeblendet werden. Sie werden dann über einen festgelegten Zeitraum kontinuierlich leiser, statt abrupt zu enden.
Dynamische Grenzen für Zufallswerte
Der Bereich, aus dem der Zufallszahlen-Operand auswählt (z.B. 1
- 100
), kann nun auch mit Variablen festgelegt werden, z.B. 1
- ${maxHP}
.
Verbesserungen bei Dokumenten
Es wurden zwei Schaltflächen hinzugefügt, mit denen man zur ersten bzw. letzten Seite gelangt.
Außerdem können Links in Dokumenten, die auf Arbeitsabläufe verweisen, optional Parameter enthalten. Jeder Parameter in einem Link wird zu einer Arbeitsablauf-Variablen, die im aufgerufenen Arbeitsablauf verfügbar ist.
Beispiel
{link*onLinkClicked(a=1,b=2)*Linktext}
ruft den Arbeitsablauf onLinkClicked
auf, und versieht ihn mit den Variablen a
und b
.
Gleichartige Arbeitsabläufe werden somit gruppierbar.
Lua-Effekt
Allen neuen Operanden können auch im Lua-Effekt verwendet werden. Zusätzlich ist es jetzt möglich, die Funktionen date
, tonumber
und tostring
zu benutzen.
Viel Spaß beim Experimentieren und
bis zum nächsten Sonntag…
Extended am Sonntag #15
Ein Minispiel: Pairs
In Zereth Mortis gibt es gar keine neuen Puzzlespiele (hust), also muss wohl mit Extended nachgeholfen werden.
Diesmal geht es um das Kartenspiel Pairs. (auch unter dem Markennamen Memory bekannt)
- Das Spiel besteht aus 32 Kartenpaaren mit unterschiedlichen Bildmotiven auf der einen Seite. Die Rückseite sieht bei allen Karten gleich aus.
- Vor dem Spiel werden alle Kartenpaare gemischt und mit der Rückseite nach oben in einem Rechteck angeordnet.
- Der Spieler darf pro Zug zwei Karten umdrehen, d.h. das Bild wird sichtbar.
- Wenn beide Karten das selbe Motiv zeigen, dürfen die Karten aufgedeckt bleiben. (Im Originalspiel werden diese Karten aus der Anordnung entfernt und der Spieler erhält sie als Punkte.)
- Wenn die Karten nicht das selbe Motiv zeigen, müssen sie wieder umgedreht werden und der nächste Spielzug beginnt.
- Sobald alle Karten aufgedeckt wurden, ist das Spiel zuende.
Import-Code
Vor der Erklärung findet ihr hier das fertige Item zum Schnell-Importieren:
Pairs
!LJvBZTnUr4)lA6hStVtMsQo1XZKpyl7ulxh5CbYj3nxdHbbxjItGaQaG2rrJ1V9UaK6DkL761V1rAiXlp4bl2DXIfmUv8G4(TW)rDWNTJjrDIoTDNorrD6pA6DM4(DqeKE99pt0mt6vAUV8GFjMC19XK76H1CXKlV3Zd5MlkREZ1X9)BNgHTpi(YycPRFmAvxKGUsbFSVkzG)PxciqPyGLePXel3iMGKWmJSLDqKAoto)roo(EQu4RZF8TZF0Pvf5jG5OrG7tmZrE8)W8hB8Cd)ZLyBC8XZVTIGNcd0dSPo53aUR5tmJD(TZVvme7T5qHX68sjIdbgHtsgOW(FS83MiwxGUfKwOKelW1Q0dYYgq2GMfykPY3H9xxBw)str16)TR3)k(wcyZPC1uxdL2m9ZQsvQPao0GwFE((JAtDvuDq2srudgy4q0oDudRUqLsfPulih6TWlm3K39Z(QV51D(74RHm0oC8kA8ML)uKEwDKQsln4)U8mo0QnW0s3ZWoSqxnASSr(cSd1g0ViuR1p4P7zrQlB(JVYxodeJYCZFmvxVpKyPbBblVDlHEJ2xxsR3tALSwvQztuSNjWXDEJYAp10xZ73SugcoNFPc85DA)sJVNPApZJuOg)Q1IOC0sf(BRMDrf0JF1wcvcJp2ksH1KIneJGjPQsPYhF(xrL(MAw(cDvLz4pIA6FPASlu(2BbkDZ8p2oIvZTOfJ56)rW)sHfFQIjT9n0E3WRPAEroOCuV)Wgrz3k4Ff3(i(Br)IFKpCXsQh8ZXKzzToN)YhyOt1StYA9YSjy153(xMficln7KjVuoYl7(pJ7Fwm59x5h87XJs6FFGr67Ij)dwomqRLoXKBawkyIjV7JXjXKp3R6CLBA5XDfyDc103PvUBkgbyZT9n)tfy7(gPvT2zbP(g7Rn5m5D46g76s)cG0)cKl94GAzXsZFcxVbRo76bl8h9uRDoNP2dFcB8M9IpmWoM5kxlCyNvtVl2Zx2DDEjl909HueQNOcvPCk0kAkZmoxRv(DoudmrBHg1styxCGJzRCoBWsesHBknfY1QScLdmurEUwY8S7JEwd4kCCJ2AXypWHH9SqM(VlepbM9GdN5Xu7eGzYyQ09cczJMZqzZLrtKOsH2Xcm3EPDctYsfkelgVsOgj9659G1W4cMKMykSzcNE4W9HtpQaOS8ee3EGyhp1amEgDOCkoRuEM3jDpGFMzmcTX)MBMUniEMaEccBZtqvnDmyWnUgM9BSdanTqncqpdUKzTcEv1CMTgvvnddLFoBIASHnA0bGpYl5weVtaMOwuMukyk(2luV76VbpdsUHn051hJ0YuqHkL6GMlSCAcYf6rtJAFG5xPtKakeixOkwZ9ESafoOmByBB)xUkskqVuV4LGT43WvJW96Of6ifWh3oPgimjpdYNsFteTWvAIDatwhstoA3nOOPsnGlJRT5GtWPP46MMaYTf1WG(kqBLrN80KudduAJTLhFRAGMWsM6kmoj0Mofqf6Z1bcNgQe3eLH7tnmrAP)TQEk5A9yVkcxCCHsXW9TjAfmuyZqFR64pfEsxyaJfJvlZlW5QM9GEGGAKqbO7Dj9JeFlxxdSHadZXB)RNHO83EFA8aPuwklNPCchm0GNLudUmqMJBEcHzqfXeHX7zvTBcJTgcYxLwNQ2tdwlNU9fU)21Umuvu5x2j5q1jT3mxqhdD6X0VSGXfYvYNM7mXYltjWQRYOpKW7lh)NE4BMERdYNG4gkftgO3vK)XwBkYv489NJ(znnyiED(r(PsTUOHSUmLB7VwoQVSQ)nBEfqXoyeLDhiCHOVJvyLAVSuDy25Ip7GOM798F1LauBQY(FDYYhiJIAZD(7NU8(C4))XSNtbhtiTH0kVUl9JxFb5((us3p27ddQN0W3OzdEl)cmK7FyroQytpqIltCTQjYfyrdWZC2XEZpOqpNICufHroN)izIaqxSVvGImGbtvGk8nCQ(woORAHbpo6o8ahzqOktA(deSsR3GcupK(FhjxsrODV3NgVpF7WTec5tJn8txG51hUobw5H((NFcBct0VBiV)R8Zv7O3GiVoaHe(SsxgUtqyqDd885R9dYT4AfKRqn4RpjQZjTXWHrrN35SZBFkc2pyqIli7poiZGNWGZxC)oTcCUtxD90C6cAA358orNh1P8wlxJx7X9PQVGgj9t4DdAEAtuWVd7SurIRzVfQNxaF)NdRQREi8YBIWLnPxm50V6l8rSqWS61gLe5IJ)pp
Das Item verwendet den Lua-Effekt. Um es zu benutzen, müsst ihr mit ALT
+Rechtsklick Lua-Skripte für dieses Item erlauben.
Struktur
Das Item ist als Dokument-Item aufgebaut. Der Arbeitsablauf onUse
im Item sorgt für das Mischen der Karten, während das innere Dokument sich um die Darstellung des Spielbretts sowie die einzelnen Spielzüge kümmert.
Das Spieldokument wird hierbei dynamisch erzeugt und nach jedem Zug aktualisiert. Dafür sorgt der Arbeitsablauf onCardClick
im Dokument.
Einstellen des Spielbretts (onUse
)
Grundeinstellungen
Im Lua-Effekt des Item-Arbeitsablaufs lege ich zunächst einige Variablen fest. Das erkennt man am vorangestellten args.object.vars
.
-
width
undheight
stellen die Breite und Höhe des Spielbretts ein. -
backside
ist das Icon, das die Rückseite der Karten darstellt. -
icons
ist die Liste der Motive.
Modellierung der Karten
Alle Karten werden in der Liste cards
gespeichert. Jede einzelne davon hat zwei Eigenschaften:
-
index
gibt an, um welche Karte es sich handelt. Ich verwende also keine Namen wieKaninchenkarte
, sondern simple Nummern von 1 bis 32, denn um 8x8 Karten zu belegen, brauche ich 32 Paare. Dieser Index wird sich nicht mehr ändern. -
shown
ist ein Wahrheitswert, der angibt, ob die Karte schon aufgedeckt wurde. Am Anfang ist dieser Wert bei allen Kartenfalse
, aber im Verlauf des Spiel werden diese nach und nach auftrue
gesetzt.
local n = args.object.vars.width * args.object.vars.height
local cards = {}
for i = 1, n/2 do
table.insert(cards, {index = i, shown = false})
table.insert(cards, {index = i, shown = false})
end
In diesem Code-Teil erkennt man, wie die Karten nach Paaren geordnet in die Liste eingefügt werden. table.insert
steht zwei mal dort, denn für jedes Motiv gibt es zwei Karten.
Mischen der Karten
Wären die Karten bereits geordnet, wäre das Spiel ziemlich leicht, also sorgt der nächste Teil fürs Mischen:
local temp, flipTo
for i = 1, n-1 do
flipTo = math.random(i, n)
temp = cards[flipTo]
cards[flipTo] = cards[i]
cards[i] = temp
end
Alle Karten müssen vollständig gemischt werden, also benutze ich eine Abwandlung der Vorgehensweise aus dem Beitrag zum Kartenmischen.
- Um festzustellen welche Karte an die erste Stelle soll, würfle von 1 bis 64. Vertausche dann die Karte an Position 1 mit der an der erwürfelten Position.
- Für die zweite Karte, würfle von 2 bis 64, denn die Karte an Position 1 wurde bereits ausgewürfelt. Danach wird wie im ersten Schritt getauscht.
- Fahre fort, bis alle Karten gemischt sind.
Vorbereiten des ersten Spielzugs
Schließlich speichere ich meine nun gemischte Kartenanordnung als Objektvariable (args.object.vars.cards = cards
). Außerdem lege ich zwei weitere Variablen firstCard
und secondCard
fest. Darin halte ich fest, welche Karten der Spieler umdreht. Beide Variablen sind zum Anfang 0
, d.h. der Spieler hat noch keine Karte angerührt.
Ganz am Ende des Lua-Codeblocks zeichne ich das Spielbrett board
. Die Erläuterung dazu kommt weiter unten.
Auswerten der Spielzüge im Dokument (onCardClick
)
Jedes Mal, wenn der Spieler auf eine Karte klickt, muss das Spiel angemessen reagieren. Das hängt einerseits davon ab, welche Karte angeklickt wurde, und andererseits davon, wieviele Karten bereits aufgedeckt sind.
Ganz am Anfang des Lua-Skripts importiere ich die Arbeitsablauf-Variable cardIndex
:
local cardIndex = tonumber(getVar(args, "w", "cardIndex"))
local v = args.object.vars
cardIndex
steht für die Position der angeklickten Karte, nicht für das Motiv.
In der zweiten Zeile lege ich eine Abkürzung fest, das erspart Tipparbeit.
Spielzuglogik
Danach folgt ein kleines Monster. Dieses Monster sind die in Code gegossenen Spielregeln:
if v.firstCard == 0 then -- 1
v.firstCard = cardIndex
elseif v.secondCard == 0 then -- 2
v.secondCard = cardIndex
if v.cards[v.firstCard].index == v.cards[v.secondCard].index then -- 2A
v.cards[v.firstCard].shown = true
v.cards[v.secondCard].shown = true
v.firstCard = 0
v.secondCard = 0
effect("sound_id_self", args, "SFX", 9637, false)
else -- 2B
effect("sound_id_self", args, "SFX", 9638, false)
end
else -- 3
v.firstCard = cardIndex
v.secondCard = 0
end
Okay, einmal kurz durchatmen, wir haben es mit einer Wenn-Dann-Sequenz zu tun.
-
1 beschreibt die Situation, wenn noch keine Karte umgedreht wurde. In diesem Fall setzen wir die Variable
firstCard
. D.h. die erste Karte ist die soeben umgedrehte Karte - Bei 2 wurde die zweite Karte umgedreht. Jetzt muss das Spiel überprüfen, ob ein Paar gefunden wurde.
-
2A behandelt den Fall, dass ein Paar gefunden wurde. Wir setzen die Marke
shown
für beide Karten auftrue
. Dann setzen wir unsere beiden VariablenfirstCard
undsecondCard
wieder auf0
zurück. Somit beginnt ein neuer Zug.
Schließlich lasse ich noch einen Sound abspielen, in dem eine freundliche Elfe darauf hinweist, dass ein Paar gefunden wurde. -
2B behandelt den Fall, dass kein Paar gefunden wurde. Auch dieser Fall wird von der überaus freundlichen Bluthelfe kommentiert.
Beachte, dass hier die VariablenfirstCard
undsecondCard
nicht zurückgesetzt werden, sonst würde der Spieler das falsche Paar nicht sehen. - Der letzte Fall 3 behandelt die Auswirkungen eines falschen Zuges. Sobald eine dritte Karte gezogen wird, werden die anderen beiden verdeckt.
Darstellung des Spielbretts
Die zweite Hälfte des Lua-Effekts befasst sich mit dem sichtbaren Teil des Spiels.
local board = ""
local c = 0
for i = 1, v.width * v.height do
if v.cards[i].shown or i == v.firstCard or i == v.secondCard then -- 1
board = board .. "{icon:" .. v.icons[v.cards[i].index] .. ":32}"
else -- 2
board = board .. "{link*onCardClick(cardIndex=" .. i .. ")*{icon:" .. v.backside .. ":32}}"
end
c = c + 1 -- 3
if c == v.width then
board = board .. "\n"
c = 0
end
end
args.object.vars.board = board
Für jede Karte wird ein passendes Icon angezeigt.
- An der Stelle 1 überprüfe ich, ob die Karte gerade sichtbar ist. Eine Karte ist sichtbar, wenn sie bereits als Paar gefunden wurde (
shown
) ODER wenn sie die erste (firstCard
) oder zweite (secondCard
) eines Spielzugs ist.
Eine sichtbare Karte kann man nicht anklicken (d.h. umdrehen), also wird die Karte als normales Icon dargestellt. - Wenn die Karte nicht sichtbar ist (Stelle 2), muss die Rückseite angezeigt werden (
v.backside
). Außerdem muss die Karte anklickbar sein. Das übernimmt der Teil{link*onCardClick(cardIndex=" .. i .. ")*
. Das ist ein parametrisierter Dokument-Link, ein neues Feature mit Extended 1.5.0. - Damit das Spielbrett schön rechteckig aussieht, füge ich in regelmäßigen Abständen (3) einen Zeilenumbruch ein:
board = board .. "\n"
. Die Sequenz\n
erzeugt einen solchen Zeilenumbruch.
Platz für Verbesserung
Nachdem du das Spiel ausprobiert hast, kannst du dir eine Kopie des Items erstellen und dich an folgendem probieren:
- Verändere die Motive, verwende z.B. nur Bilder, die zu einer bestimmten Thematik passen.
- Vergrößere oder verkleinere das Spielbrett. Wenn du das Spielbrett vergrößerst, brauchst du auch mehr Motive.
- Verwende andere Soundeffekte, z.B. ein Kommentar eines zufälligen Volks.
- Füge eine besonderen Effekt ein, wenn der Spieler alle Karten aufgedeckt hat. Dazu wirst du ein paar Codezeilen brauchen, die das Spielende feststellen.
- …
Viel Spaß beim Experimentieren und
bis zum nächsten Sonntag…
Extended am Sonntag #16
Geheime Dokumente
Heute geht es darum, geheime Informationen oder wertvolle Gegenstände vor den Blicken oder Händen Unbefugter zu schützen. Das kann ein neugieriger Bote sein, der eine Nachricht überbringen, aber nicht lesen soll. Das kann ein Teilnehmer einer Schnitzeljagd sein, der die Trophäe erst bekommen soll, wenn bestimmte Vorbedingungen erfüllt sind. Oder es kann irgendetwas anderes sein, was der Extended-Benutzer (noch) nicht sehen können soll.
In den folgenden Beispielen wird der Schutzmechanismus immer ein Passwort sein, man könnte die Information aber auch hinter einem Rätsel oder allgemein einer Aufgabe verbergen.
Beachte, dass die Sicherheit hier lediglich im Spielkontext gewährleistet ist. Du solltest mit den folgenden Items auf keinen Fall echte Daten schützen!
Level 1: Statische Daten, statisches Passwort
Importcode: Geheimes Dokument
!Trv4sjoqm4NL7b402c4CYm(dSG2ohwqtb88hHwBxHouAz2Db17g5z)s2f6nC6aZg2Szt(Y3(fqxmgJCPVoTOvpeiRRxRoT98E8(Tp5GrTOiGWiEnVoJnX)cH(JqyyiTtJWD9zV3rEIgX7d6z9hmaJUWXbHRJXRra8TUh3ZwtotpIWF20n7JBtR(9(KfILII1c5(KyXB6Z(Z5B(GUaDLR9)jg9dYoIVie0A(niCB6AXn1v6OA560YHPYfceU5b8zeMfIrT7qvoWLJSVqPlQENdoylhvGh7((TKF25CR3XhZACDDPUytGinxiPQYagI6rjQE121IknA)yyIW4dTguxnrj4FaX8QPdfn9Aroc7sLZ3iRxVHUlHxL9qyCQs9ATuBEa2y3KZmput9bczlfzRg34Vd7)acOVLfQgaDAKNcf64NLPvzljBdQYQRYpU5aoTW0KjcLhGy9PyJlg74QRoiq2rTAvrQC12Jq5L0IY7ekvktTpTDHS4Lx2NStiFvuSqi1FdHke8ogU3NzlsUzy75QL1VEcH9FI09jSY8lzeANsKTvwOFFOyNO0WQgHlmAYXNSgnm0N4k3ZDADUN3(eVwD7CzxNlqWNLVIskbQVhVuMwwIWuEOHC)fhLpLsZzDoZXovmGu56PhMXGHKNCIs8)uTA31JPdWFKbEHSsgEab69gcjZBTnknGm9NGrC2NXgnNqnr3k7a2eGVFp))Px3NukuIk71B6zRUEmqiZ9s6XmKoSOA38sHwlKZDVWaLNTs)BTZMQ9jndbS4Io9(EgPanwtBMeXRt7zGNVzySpxapNlPihycb4)nyMjCI9OvFtEMnW2mAJM3YFAe)7d
Der einfachste Fall ist ein Dokument, dass sich nur dann öffnet, wenn man ein Passwort eingibt.
- Erstelle einen Dokument-Gegenstand. Wenn du die Vorlage „Dokument-Gegenstand“ benutzt, wird automatisch ein vorbereitetes, inneres Dokument angelegt, dass sich bei Benutzung (
onUse
) öffnet. - Überführe den Gegenstand in den Experten-Modus, denn es wird ein zweiter Arbeitsablauf benötigt.
- Füge dem Gegenstand neben
onUse
einen zweiten Arbeitsablauf hinzu:checkPassword
. - Entferne den Effekt „Dokument öffnen“ aus dem
onUse
-Arbeitsablauf, denn das Dokument soll erst nach der Passwortkontrolle angezeigt werden. - Füge den Effekt „Experte > Eingabeaufforderung“ in den Arbeitsablauf
onUse
ein. Stelle darin die Objektvariablepassword
ein sowie den Rückruf-ArbeitsablaufcheckPassword
. - Der erste Effekt in
checkPassword
wird eine Bedingung sein: "Objektvariablepassword
ist gleich dem direkten Text-Werteniarku
"
In der Bedingung steht also das gesuchte Passwort. Als Fehlermeldung bei Nichterfüllung wähle eine Phrase wie „Zugriff verweigert“. Dieser Text wird angezeigt, wenn das falsche Passwort eingegeben wurde. - Der zweite Effekt ist der „Dokument öffnen“-Effekt, der vorher aus dem Arbeitsablauf
onUse
entfernt wurde. Das Öffnen des Dokuments wird also hinter die Passwortkontrolle verlegt.
Das ist schonmal ein guter Anfang, aber das Passwort ist fest an das Item gebunden. Wenn sich das Passwort einmal herumgesprochen hat, benötigt man eine neue Version mit einem neuen Passwort.
Level 2: Statische Daten, dynamisches Passwort
Importcode: Ganz Geheimes Dokument
!nsv3YPTsm4NLEBNPn2MqtdZKladjqkyiznH(ZCwSXwaUXExgVlesZe)003K(IvP1GZa9KZPJ9S)ODL0N(KwXT5(CpB83Qgo6Wz4SJLLvTtR7fwBBg3RgEdwppAmwgrt(FHZApKZ63d3P5SbTjPdqjEdP9DBwkVBhU3hSS4Sw(8wC2KECVtRJBh1S0PKP(mN90Qgrp3zRohYkcwalHKmiViWh2QFZtNS65IRlU2CNNste3)2OLHIfWOqL6bzE8B3Tqxe8RFkIHCXZKoO7ra0Y9tCVpIZKdzEnrylVFDgimNoA6LC2vHzGVuMQtw1fcr95SlVLpJZyUKoh6mscZNgnGhQcJKyoBty(0v5YSvOPdZxOkpK5bRbvrWEtueajOfNbcdBR0YCiUY(izZKilXzCV60sDzGG)PjknF)hRRnb(2GsNiE8sPq3D9caf7qIVb9OMeoDN0A7JusONmplmTpcr8OwdjqwswgsAe6zB7ZrOnMrjn3Hezqu3vHIFueCvz(bJOkQKOe8o300aFwlAZypA8oueMVDneABYYowNJ3SdTVNlYBIntZsurtfsnmxMgdX2XMce8ctmJUgJpPdzj9(0Iumwb)Vzd5SVdrhMnKgw)Bx8pgSEm7t8(rmotWzoKaN)6m((ksJRseRwR)xYVrlHO7Fr(RNSp6MhgZZQWtKueVFZoewcqJ6i(oGaoevKBiPxCXUx7)xQEeNvbtdpXMhMKoauQqQ46RRxKNmFErWgi)bizbKRFtviolpueT8vywSrJP2AQAP8Hdi3JApveq9KETxjkiADEI(X(Wgi1q(MwwSHJ3xcv19Qp29kgXSlwZZGuub178xMhMMILXCVZmf2)XjS2yIW5eRAN44ueyz1W2UHZzL9c7GT203TRZkl(o8MVV(7PNdhRKvTgNwNGc(yJqxpQIFWeZBN2JntyzpdRAy94S6BPf3IlivOEX98nVwXXMUV0IRiifuyBgJUvrBmOXuKY0GwBQelrLMZ)n
Um das obige Dokument zu verbessern, soll ein Mechanismus eingebaut werden, mit dem man das Passwort ändern kann. Das wird über einen Dokument-Link geschehen, d.h. wenn das Dokument geöffnet ist, hat man die Möglichkeit, das Passwort zu verändern.
Das Passwort wird dann nicht mehr in der Datenbank gespeichert, sondern individuell in jedem Item. Man kann also auch verschiedene Passwörter vergeben.
Ausgehend vom oben dargestellten Dokument-Item, gehe folgendermaßen vor:
- Im Arbeitsablauf
onUse
, füge den Effekt „Experte > Variablenoperation (Initialisierung)“ an erster Stelle ein. In diesem Effekt setzen wir die Objekt-VariablestoredPassword
. Das Standardpasswort soll zunächst leer bleiben. - Im Effekt „Eingabeaufforderung“ ändere den Variablennamen zu
inputPassword
. Das gesuchte Passwort wird also in der VariablestoredPassword
gespeichert, während das eingegebene Passwort ininputPassword
steht. - Die Bedingung im Arbeitsablauf
checkPassword
muss dann entsprechend angepasst werden. Statt das eingegebene Passwort mit einem festen Text zu vergleichen, steht nun auf der einen SeiteinputPassword
und auf der anderenstoredPassword
. - Jetzt fehlt nur noch die „Passwort ändern“-Funktion. Dazu legen wir einen dritten Arbeitsablauf
changePassword
an. Dieser liegt jedoch im inneren Dokument und nicht im Item selbst.
Einziger Effekt in diesem Arbeitsabaluf ist „Experte > Eingabeaufforderung“ mit der ObjektvariablestoredPassword
. - Damit
changePassword
auch aufgerufen werden kann, fügen wir dem Dokument einen Dokument-Link hinzu.
Wir haben jetzt ein Dokument-Item, bei dem wir jedem Exemplar ein individuelles Passwort zuweisen können, bevor es verteilt wird. Aber das reicht mir noch nicht, denn der Inhalt ist festgelegt und kann nur in der Datenbank geändert werden.
Level 3: Die allesverschlingende* Schließkassette
* allesverschlingend-aber-danach-auch-wieder-ausspuckend-sofern-man-sich-das-passwort-gemerkt-hat
Importcode: Schließkassette
!TF1xtnUnq8plDM(wBZz78hMlZKhcjP9YvWHdLaxBNQyh71jUySzKKdCCZ5Vk9f(MWxSURSXGdoPqkp2jzSLwjT)2D1U)Km3KpLBBI)nAIpT4m8TLHHLPHXYpOoiGB3eNbBSn(uXzt)nSn2NnGKMeptcud2u6jPlgKRqSvOpNT2vmpzXFbE4ADflL5dYs0q9h9(ts9mPYvLkNc3O42T4Sjbbqmcg9JH)JcL4ZyoZIe0CpbjkX7cWxdqGBeA21aqlsG1EcGoyu6ecWBLsEruiIA8pM5KEzMdeRw5gPGyiZ5xGLqSuD)DX(ypim(2uP3QOq4()UENVjjOnzrlkTTa3WOZtexeeLCnoBXxMftUjN5Le7)WSkCJCVWBfqJ7wXdkcneKu3E9k20xVrOcTIoLg2cHBS3kQtR9mGjvjcW)exP86er(otDoEBsWb1JXvIKlVQkg2qkiZCkuBUhTjsn1McHNDBQ5gOQi)C6t9YTgZ3r4omE9CCaLBymiMlJsuZjZUi0J)RlEhhgvk)hYf)svO1UvylTcB)kuyZDRWoAfEWRqHT2PcZ3TpOOTUVo6U30l92SUxjsRTSFV4v6vtr)TuvE)0GhRKZCQqeuh67fPtV6iopdeeYjs5)gfsvKKjPX(4o1Cjefubm2p)zco73BA0Pdb5In1B91)spr4g1LuZg5(td01KnOefKrmZPxMZth8b5FSoH0K)63QZ5AxkSKgSAMZJLW)F56lsHVKYvRCcXJbP0DjWzhcRU)o8iorMZLPsKhEDc20hEEDreqtsIN81OCZ8XZu2s9qn89vi6dJVkvvdppwMMNsSf(ED3Yj9QYB26PQvTLNfk31s348QslSsO(3txkcdcWqmiUgcxcc13vsNTz88TGD7n)knVDmFB9kJBLYB3euBJBQcX1h3jL2tQtEQbrxG6vFJPNDs2wVbR(YsDQXFFru8T3cfFHaj4LkcvF5iynePx8rJjaMm7HpfaN0mMU(zWdIy9XMF)xltL629)uEeX9Z(qFU(Rrgmrd)y0MzStP6vgBm(6Mw5wa(A4mUTbND850lCjhslLnCervfNQUfrWpeYPJWx)kwZbkfuyw(Up5QKzo3MM5OnlbsAXobV(OP57rqhpiNm9YqP3CABArYnlxLivHIeCt5Orue4Xu2UD15RFt7aOPB3hn3cp8IcdqtbHJ9P(6n8t6tDMztppRV2LgCk1zizewMy6XNgPNcJFiNDUE6SdPNdMQLmQiaC8qkaC0eoZh0tHXGiCdv(ttxjCJIq9JzbiKdgIrsR3z08DwwzoggDTm7AG46FgkVr7gytfov9hoYog13OpJ6Q6Im7AA0Tno(GNJJISa8SNiPoHz0G5NoQpBI9C0ZgFY0I0ZN95O18jQ5PNuyk3UuC()a
Das letzte Item ist ein Behälter, in den man beliebige Gegenstände einschließen kann.
Um das Passwort einzustellen, benutzt man den leeren, geöffneten Behälter. Wenn man Items in den Behälter legt und ihn dann benutzt, werden die Items eingeschlossen. Sie sind dann nicht mehr im Behälter zu sehen. Um die Items wieder auszuschließen, muss man den Behälter nochmals benutzen und das vorher festgelegte Passwort eingeben.
Damit das ganze funktioniert, braucht es etwas Luarkanmagie.
- Im
onUse
-Arbeitsablauf initialisiere ich zunächst einige Variablen, die wichtigste davon istlocked
, die festhält, ob der Behälter gerade auf- oder zugeschlossen ist. - Im fünften Effekt von
onUse
prüfe ich, ob der Beählter geöffnet ist. Wenn ja, prüfe im nächsten Effekt, ob der Behälter leer ist. Wenn der Behälter leer ist (und geöffnet), erfrage ein neues Passwort. - Der Arbeitsablauf
lock
kümmert sich um das Einschließen. Zunächst werden die Zustandsvariablen aktualisiert, dann kommt das Herzstück, ein Luaskript:
Der gesamte Inhalt (args.object.vars.content = args.object.content args.object.content = {}
args.object.content
) wandert in die Objektvariablecontent
. Das was der Code macht, ist quasi ein Etikettentausch, so wie ein cleverer Arbeiter, der Zementsäcke vom Keller ins Lager schleppen soll, und daraufhin einfach "Lager" auf die Kellertür schreibt. - Das Aufschließen funktioniert sehr ähnlich, nur, dass die Passwortabfrage vorgeschaltet wird. Außerdem prüfe ich, ob der Behälter leer ist, damit die eingeschlossenen Items keine anderen Items ersetzen.
tryUnlock
prüft, ob aufgeschlossen werden kann,onUnlock
überprüft das Passwort, ändert die Zustandsvariablen und führt dann ein sehr ähnliches Luaskript wie oben aus.
Die Schließkassette ist sehr vielseitig, da man beliebige Items, sogar ganze Taschen, einschließen kann.
Hierbei gibt es einen kleinen Schönheitsfehler: Der Behälter wird nicht aktualisiert, während er benutzt wird, d.h. man kann Items sehen, die nur scheinbar darin liegen, bzw. leere Slots, die in Wirklichkeit belegt sind. Man kann sich behelfen, indem man die Tasche mit Doppelklick erneut öffnet.
Wer diesen Anzeigefehler beheben kann, darf sich einen Keks aussuchen.
Viel Spaß beim Experimentieren und
bis zum nächsten Sonntag…
Juhuuu!
Vielen Dank für die Mühe und die vielen Guides und Tipps!
Extended am Sonntag #17
Die Konzeptomate
Kennst du das auch? Du möchtest einen neuen Charakter erstellen aber dir fällt einfach kein Konzept ein?
Hadere nicht länger, iss eine Konzeptomate!
Kostprobe:
Der Charakter verbrachte die Jugendjahre in einem Wanderzirkus in Unterstadt.
Eine im Alkohol ertrunkene Handelsreise machte schließlich überdeutlich, worauf es ankommt:
Geld. Und im Zweifelsfall richtet man sich nach Velen.
Importcode: Konzeptomate
!LZv3UTnUs4xMfO3C22yN2KUfyVWn)3M)ALtsp7fYM2I2I10KzjPIBsr9RYEZ(mSxL78l2zgkhBlhYeVahymafiqrswI8JdhoZ8nIKP1sBMEAn4Vn2eowpnb()21RxV2gBZhT136ME6MWtKC0P4XmDx8Fn)VPj7EwAYXhbx5stE)zyrKCyJYlpCp4LEZgW9BM((0KKDkV95nkRkSa(sAY3V(hJ)WUCZ427KZmSbo80B4Mogw3ChFC7mbC4df95QSVYYnWf)Y31grFH6hJBlu4LsDxMtOv)4LJ)W3FfwG(sDpHY)0)zbtk6j4MFGxXVHRCWzdNw(2U5sbFYFjfDZh3EY9D4MmEHdV8)mU9iTHv0BCBUDCBMAGE4q37Quj)Y3DgMWbvD7luzaIgoU9FmIl6XL2EmPCCBJaRihwJaAT(QrXWJ)Y3fzA5dOgeoOqCNpME6BH)JIPKtBaIy9GIHaM9Y0nBTFAYbSH891k3PAZqM8yMPppnz)pN2jn5QJsp91Oq)WA4tUl36eQBXh(Wc8PoSoE7pva3hVzRY7EEOBEYUicob6Gp9mp2CLD5h1CANzIL72dLM45jnXJVbpWN1blYstUHzAP78vEx417Qvzp8JtFIYhOBoV7GwQ0ew5VKmYReA81kE2V)7t1aVbAFpGgOHBN(86YNcFOeFpm(yGGY44QRHJ3(W7a)jfw4iuzBH341RdiV5kd5Df8oq)GUWecXVbVr9ARdixFJvgZxPHsUu1)aEVj)TQVsyxg(iW3yDa8A)2kd8l5gaPwgOUhqydYz4KARffKARUgYN5zbHBnVYrmDJR5gga0flEJV4h5lE8h1dXYV83kDcmthyzjJVMwld2RT6J2t6Ml61ZA4cBybKF8(MRduxFLb9o6UdCmHmQrkS9dc81aORTYG(9CWjPOFHQFieVPx743wlq(TRmMtUwBC3GJcSoM0fb7En(eOyxhGF7vh8DZlKwa0rg6JM8s2EDa6TwzmFa3h5JfK55G1fzejo2dcEIxdq)n)lGENcJd0t6B7XHahdHBSZlzTOKV664hcoIVJlCb1r8A2Rff7vxV(qqXacuoQjBVEDT1I2rTvx9yQJghWmi8WrVUrT1Y4XAR(asa2sK0tqi7hgwBT4CS2Q7D8CZK77ZLCrqm7DnwB94BC1DoEoqJnIz6spJ1wlaE1dF)eU1YFwpJ1kd6BTec)Qhb)XGOwXnt(NE9uXa(SUoGV6XttvawreIY6dzWOKcOwbnjmbcHAo0H0AvqZmkM1weeZuI2AfuFLq54M)SGlLlJBhz4RwbXxGaggNM5cjOjfL1k4(mtFJy4qwWyP(jL6Afb0NaLWx4YzswWHqeI86sJ7LG)oviithMRvqCJcmfVcvqx0uI7AvlvCRdZNCiqtksRvqDIRWmCKaQ4qQ00H1Avv6cJ2f2qfDiRUeIvd4YCMi44q6WuTcOpGL1N7UlSTdcXwTcO3diQQSDZhkIKfwkXyTcY)mZb4M7ckTPeH1QgqesWA9qDeliuI1AfCFsHSV2eMfiLOTwb0FmND342NOZImMKs0xRcCEmZieI0AvJFAzwm71Z700QlSiSNsvnGK3uOAnsBg0tQhfOMN9fAdunErtGHopFroJeDGsnwqFpFPEuMoy0nETUabS)8L4NE4d)hQyJXJ(5l2Z8Z(GqLPNgZ2Lkq4R8nOWTGdd41GR3ERT2(TPjz(YbEWmUKDlO95V(jcU45rutCkiecqXC9NP76NtbTS5lvMlnVpg3gNShHusJ5G2QluzTezTSCzVkfDY(Fbb8PBbb(bdi6eQylDFgy8QWXh2cgOAbCx91MwkZednjvMD8ZoKYqhK6)SGRU7FJY4))X7ZrAFb8MBWjbZGiFpbkLtN5G(kwF(WIhbyhzsMZCOEP2GtgOGcxsLhN5q(ymB8H9z9ZzgCMlA(mh6kh3UaNdyNzYIMmA6KgN5yFxH9oX1YW(0OtwCMd4gkMPBUG8zWzoIbxtzIyXmrQ83mhZjoEUmYWD6K6M54DF2qHmSsbDYBZc(x9rggaS0jFnZb7b8Sczyo40jtnlQkOCmByDbkLHMfhSHtfcBh(OcR1z5rOgsPe1Si4N83MbHZ2bLsrZCeFKYbS4IyCJsjOPcK5sjc6Guiiv2zw0WHf8u3DqqitOSZSiIJyOBX5rGpzfnjfttCTe4BaVFeqtMnml4KMHomnNJxwKpghLOyodTxQLV4Rp26SJmmmNJuEKO3if7YzWn5w5nmvKV(9pN0lNjBAMZK3ATgwqHdD4uodWnuzfuNp5CWkLCJiOSLsSjNH3lC5HZPnPisox59wCei1N46ZG7bS7K6reNg5cO1y028qOLo8iNH2pMZY6hzoGrhEKZG7hGitcAwGsCixWjHjYeeIu0gNJ3caV3goLyKI24meFsbMgp6tACoGzYEfMiPDKumgNb5J1Mxa(3IntrjeLX5qwif4A7(sTnC0RlYCC(hLUjPOpoBvY7BsSclWB3Vo2J46MoejFeYL4IcimQPeLYQ4UqnAY94QQ8r42rgQLvrSRW0PaOy6O)QNUkWLmvFq1wgqw)tmzZLKr8CJHlGQiOiIouoxE0FpCd9aSEfh80Hb6JWUH3pMLlkXeTkUVwlfoHD22NI38ByBcuIFA1gHs4)(9O0xHB7lszSoccXzTAtiJpCY9oruni6WETkU7ykCmzqithkSvHm34mS(pH9r6qMDjGJQhmJlIRpkXRTkWhYuIRlKWLHd3GuuCxc6t(htg3GwiP)NiTk0hi9OMNZ6frStjAVvXoUDe1qoqNRLGDDyalUizc7xIumHR2k6vihkuSibztj6WlzOP46C9tOZViL4PtO6MKIpC5EmxzGzs742FuGtTpUqHBfCjAzgZf(RRrhIXpztyXTEdmONqnfkXwEHgtH6HDjqMzOdJxBFVIMHWFv2hd)ZfMyQqKI78cih3Ufr1ha9mPdoRp36xGdyFqJm)wEy05G3pP0Qdl(6x46m5EtFUFo27vHFcjhDyBVSjLVo5EvFRdnOCJgAf12c3daXuhcGpWasAX)E5wZ7zfaMhez9ktjg4p2CYHfDaXUHHRUDUmMSNumWFCJWt(UbUzVoBKI1z4k)noqxaM8JhleD4LVSEfo7YM8x8OAw0HzEmKNj4bxVw0HG(YiVqXZ5EJRtJ8jOodD4PVm(BYToBbaHbUfc78obtY)g4SuGlFVhS6MbrXnU9iwUYkukr5OfLEy4OlOej)WEhXythXnd4MNkEckX4pC7qIBMV34ZL4t7GNszaiqtbysFfsxWCNWmi8w8ePseWYJLUIHKCIz9LuS)xg6GhVrt(NCLpeR9eQ74YbEAC(b(LzGeozVOE8Pukcc04Son6F5jns)q3TdZwq3cJWD7X8B4sFz6395to7I0PRwE3d7C5hFgMs80KeCZNNlHxW(RtNImjxMIeot2jWpLSlAu5vBS5RQxFC76B(UnQ)oWmMFJqFVVaf5LWV)Y38s41DxMwUH5NSt1xQ(7Qx7DB4TpTZzE4DeU5SFYvPNcV2Ux4)xYoyOWjjhb8S)gEYNNs4UCRFF39G6t5YN83sWbW75o2V(rn09FTdKGjNNGddE7wPjxKG7U(hdp9PSCdUSdX6St5Eh)03qpeyA6ZUIVz)Pg((KZB43X49hVSHht743e53fl86Bac3pThE9r7GMcUP1qGLDREADwRxVPxUc)2fNIh3PPVO2dleNhtqlOb8A3va2oUe6MV2NNbuJf3tyhaoMhGRlEuimRFlJJ7eYOIJx561piODPP)V
Wie es funktioniert
Man kann es sich sicherlich schon denken: Das Item benutzt vorgefertigte Textbausteine und fügt sie zufällig zusammen.
Ein Blick ins innere Dokument offenbart, welche Teile des Textes sich ändern können und welche nicht:
{p}
Der Charakter verbrachte die Jugendjahre ${origin} in ${location}.
{/p}
{p}
Eine ${qualifier} ${event} machte schließlich überdeutlich, worauf es ankommt:
{/p}
{p}
${trait}. Und im Zweifelsfall richtet man sich nach ${idol}.
{/p}
Die mit ${}
markierten Stellen sind Variablen, die vor der Anzeige im Item-Arbeitsablauf onUse
ihre Werte zugewiesen bekommen.
Das Auswürfeln der sechs Variablen habe ich auf einen Arbeitsablauf pro Variable aufgeteilt. Das ist nicht notwendig, aber übersichtlicher. Jeder der sechs Arbeitsabläufe (z.B. setEvent
) funktioniert ähnlich: Würfle eine Zufallszahl zwischen 1 und 20 aus und weise dann, abhängig vom Ergebnis, einen der vordefinierten Werte zu.
All diese sechs Arbeitsabläufe werden im onUse
-Arbeitsablauf der Reihe nach ausgeführt, zum Schluss noch etwas Deko und ganz am Ende das Anzeigen des Ergebnis-Dokuments.
Viel Spaß beim Experimentieren und
bis zum nächsten Sonntag…
Extended am Sonntag #18
Hinter den Kulissen der Datenbank
In diesem Beitrag von Enestress werden bereits alle wichtigen Funktionen der Objektdatenbank vorgestellt.
Im Folgenden wird es um das Konzept dahinter gehen, denn wenn man das Prinzip versteht, fällt es leichter, Kreationen zu verstehen.
Item ist nicht gleich Item
Wenn von „Item/Kampagne erstellen“ die Rede ist, ist üblicherweise das Festlegen von Eigenschaften, Arbeitsabläufen, etc. gemeint.
Mit einem Item in der Datenbank hat man aber noch kein Item in Inventar, denn die Datenbank ist keine Sammlung von Items, sondern von Itemvorlagen, man spricht hier üblicherweise von Klassen.
Dass die Datenbank kein „Lager“ ist, wird auch klar, wenn man die Datenbank-Funktion „Zu Inventar hinzufügen“ verwendet. Das betreffende Item verschwindet nicht aus der Datenbank, sondern ein Exemplar (Instanz) entsteht. Wenn dieses erstellte Exemplar zerstört wird, bleibt es in der Datenbank erhalten. Umgedreht bleibt ein Item im Inventar erhalten, wenn der zugehörige Datenbankeintrag verschwindet. Das Item ist dann aber nicht mehr benutzbar. Extended meldet uns „Missing Class“.
Datenbank = Bauplanbibliothek
Während das Inventar speichert, welche Items in meinem Besitz sind, speichert die Datenbank, was diese Items können, also ihre Eigenschaften.
Ein typischer Itemdatensatz ist sehr klein:
- Item-Klasse
- Taschenplatz
- Anzahl (falls stapelbar)
- ggf. Item-Variablen
Wenn das Item benutzt wird, konsultiert Extended die Datenbank:
- Gibt es die Item-Klasse in der Datenbank?
- Wenn ja, ist ein Verhalten „Bei Benutzug“ angegeben?
- Wenn ja, gibt es den angegebenen Arbeitsablauf?
- Wenn ja, führe den Arbeitsablauf aus.
Ohne Datenbankeintrag gibt es also kein Verhalten und auch keine Eigenschaften.
Austausch von Items
Wenn man einem anderen Spieler ein Item gibt, müssen folglich beide Teile (Klasse und Instanz) übertragen werden.
Wenn der Empfänger den Datenbankeintag bereits besitzt, wird die Klasseninformation ausgespart, aber halt:
Hier kommt die Version ins Spiel. Wer sich jemals gefragt hat, warum Extended zu jeder Item-Klasse eine Versionsnummer abspeichert, bekommt hier die Antwort. Die Versionsnummer wird verwendet, um nachzuprüfen, ob Absender und Empfänger eines Items oder einer Kampagne auf dem selben Stand sind. Sollte das nicht der Fall sein, fragt Extended nach, ob man die Version des Senders übernehmen möchte.
Die Datenbank kann immer nur eine einzige Version eines Objekts abspeichern, jedes Item der selben Klasse verhält sich also gleich. Wenn man eine neue Version eines Items erhält (entweder, weil man abspeichert, oder weil man einen neuen Import-Code einfügt), werden automatisch alle Inventar-Instanzen des Items aktualisiert.
Durchbrochene Hierarchien
In der Datenbank wird über innere Objekte eine Hierarchie aufgebaut. Diese Objekthierarchie hat genau zwei Aufgaben:
- Übersicht beim Erstellen verschaffen
- dafür sorgen, dass beim Handeln alle notwendigen Klassendaten mitgeliefert werden
Und das war’s. Im Inventar hat sie keinerlei (*) Bedeutung. Das einzige Ordnungsprinzip, das das Inventar kennt, ist die Anordnung der Taschen.
Jedes innere Item wird im Inventar zu einem eigenständigen Gegenstand, selbst, wenn das äußere Item suggeriert, dass das innere Item von diesem produziert/erschaffen/etc. wird oder auf sonstige Weise mit ihm zu tun hat.
(*) Bis auf eine Ausnahme: Wenn man bei einer Tasche festlegt, dass sie nur innere Items enthalten darf, wird es nicht möglich sein, ein anderes Item darin abzulegen.
Jedes Item steht für sich
Dass jedes Item (d.h. jede Instanz) losgelöst von der Objekthierarchie in der Datenbank existiert, hat zwei Konsequenzen.
Einerseits ist es möglich, jedem einzelnen Item einen individuellen Satz von Variablen zu verpassen, also einen individuellen Zustand.
Andererseits ist es nicht mehr möglich, mit einem Item im Inventar Einfluss auf ein anderes zu nehmen.
Typische Fragestellungen:
Wie kann ich die Variablen eines inneren Items verändern?
- Es ist nicht klar, welches innere Item gemeint ist, denn es könnten mehrere Exemplare im Inventar liegen.
- Hier liegt ein kategorisches Missverständnis vor: Innere Items sind Eigenschaften der Klasse, aber Variablen gehören zur Instanz.
Wie kann ich das äußere Item löschen?
- Wie bei der ersten Frage ist nicht klar, welches Exemplar gelöscht werden soll.
- In diesem konkreten Fall kann man sich behelfen, indem man das Item direkt über die Id anspricht, statt sich auf die Eigenschaft „äußeres Item“ zu verlassen.
Viel Spaß beim Experimentieren und
bis zum nächsten Sonntag…
Extended am Sonntag #19
Texturen und Bilder
Dokumente können Bilder enthalten, ebenso wie Zwischensequenzen. Bei Zwischensequenzen kann man außerdem noch ein Hintergrundbild einstellen.
Während Extended bei Dokumenten eine Bildersuche anbietet, muss man bei Zwischensequenzen den Bildpfad angeben.
Heute wird es darum gehen, wie man an diese Bildpfade kommt.
Grundsätzliches
In Extended können nur Bilder, Artworks oder Texturen aus dem Spielclient verwendet werden, eigene Kreationen (wie z.B. eine Charakterzeichnung) können nicht angezeigt werden.
Die Experten unter uns werden einwenden, dass man Bilder, die im Addons
-Verzeichnis liegen, sehr wohl im Spiel anzeigen kann. Das würde dann aber nur auf dem eigenen Rechner funktionieren, was dem Zweck von Extended, nämlich dem Austausch von Items, zuwiderläuft.
Die Bildersuche im Dokument-Editor enthält bereits eine ganze Reihe an Bildpfaden, die man verwenden kann, sie ist aber nicht vollständig.
Die einfache Methode
Im einfachsten Fall ist das gewünschte Bild bereits in der Bildersuche hinterlegt. Wenn man solch einen Bildpfad benötigt, kann man wie folgt vorgehen:
- Erstelle ein Dummy-Dokument.
- Suche das Bild mit der eingebauten Bildersuche und füge es ins Dokument ein.
- Der Bildpfad steht nun als Teil des
{img}
-Tags im Dokument, z.B.{img:Interface\QuestionFrame\answer-warboard-classic-StonetalonMountains:512:256}
. Hier ist der gesuchte PfadInterface\QuestionFrame\answer-warboard-classic-StonetalonMountains
.
Möchte man dieses Bild für eine Zwischensequenz verwenden, benutzt man genau diesen Pfad.
Die fortgeschrittene Methode
Sollte man kein passendes Bild in der Bildersuche finden, kann man zunächst „informiert raten“. Wenn der Standard-Hintergrund für Zwischensequenzen bspw. Interface\DRESSUPFRAME\DressUpBackground-NightElf1
ist, dann gibt es bestimmt auch Interface\DRESSUPFRAME\DressUpBackground-Orc1
…
Wer einen besseren Überblick haben möchte, kann sich auch auf vorgefertigten Ressourcensammlungen im Internet umsehen. Auf diversen Projektseiten haben sich Addon-Autoren die Mühe gemacht, alle im Spiel enthaltenen Bilder aufzulisten.
Beachte, dass alle Bilder aus dem Spielclient geistiges Eigentum von Blizzard sind!
Die Expertenmethode
Texturensammlungen im Internet können veraltet oder unvollständig sein. Für alle, die es wirklich wissen wollen, hat WOW eine eingebaute Exportfunktion. Damit kann man alle Bilder aus dem Spiel in ein separates Verzeichnis extrahieren, wie aus einer ZIP-Datei, um sie anschließend zu betrachten.
- Besorge dir ein Bildbetrachtungsprogramm, das in der Lage ist,
BLP
-Dateien anzuzeigen. - Stelle sicher, dass genügend Platz auf deiner Festplatte ist. Du wirst ca. 6 GB benötigen.
- Starte das Spiel mit der Konsolenoption:
Öffne die Spieloptionen im Battle.net (Zahnrad neben dem blauen Startknopf).
Setze einen Haken in „Zusätzliche Befehlszeilenargumente“. Ein Eingabefeld erscheint.
In dieses schreibst du-console
. - Logge dich auf einem beliebigen Charakter ein.
- Gib den Makrobefehl
/run SetConsoleKey("z")
in den Chat ein. Dieser Schritt ist notwendig, weil die Standardtaste, um die Konsole aufzurufen, sich mit dem deutschen Tastaturlayout beißt. - Logge dich aus. „Ausloggen“, nicht „Spiel verlassen“.
Du landest wieder im Charakterbildschirm. - Drücke die Taste
z
, also die eben festgelegte Konsolentaste.
Ein Konsolenfenster öffnet sich. - Gib
ExportInterfaceFiles art
ein und drücke dann die Eingabetaste.
Dieser Befehl braucht seine Zeit. Das Bild kann für mehrere Minuten einfrieren, denn hier werden mehrere GB an Bilddaten entpackt.
Übrigens: MitExportInterfaceFiles code
bekommt man den Lua-Code des Standardinterfaces. - Entferne das zusätzliche Argument aus Schritt 3 wieder. Der Makrobefehl aus 5. setzt sich nach einem Spielneustart von selbst zurück.
Nun sollten alle Texturen in _retail_\BlizzardInterfaceArt\
im WOW-Installationsverzeichnis liegen. Wenn du ein passendes Bild gefunden hast, entspricht der Pfad, den du in Extended angeben musst, dem Teil, der mit Interface
anfängt, aber ohne die Dateiendung .BLP
.
Viel Spaß beim Experimentieren und
bis zum nächsten Sonntag…
(OT) Edit/Nachtrag: Defekte Dokument-Links in Version 1.5.0
In Version 1.5.0 hat sich ein Fehler eingeschlichen, der bewirkt, dass externe Links in Dokumenten nicht mehr angezeigt werden.
Wer diese Feature ausgiebig benutzt, kann auf einen Bugfix warten, oder, im Geiste dieses Threads, die defekte Datei vorab selbst austauschen.
Die Korrektur ist bereits auf dem GitHub von Extended verfügbar. GitHub ist eine öffentlich zugängliche Code-Bibliothek für viele Software-Projekte, darunter auch WoW-Addons wie Extended.
- Lade die Datei
https://raw.githubusercontent.com/Total-RP/Total-RP-3-Extended/8656bced78199dde22792df5823e5569b7ae99e1/totalRP3_Extended/document/document.lua
herunter.
Auch zu erreichen unterhttps://github.com/Total-RP/Total-RP-3-Extended/blob/master/totalRP3_Extended/document/document.lua
. Von dort klickt man auf „History“ und dann auf das Commit vom 18. März („Document URL fix“). Dort klickt man rechts auf der Titelleiste der geänderten Datei auf weiteres „(…)“ und dann „View file“. Zum Schluss führt ein Klick auf „Raw“ zur gesuchten Datei. - Speichere die Datei in deinem WoW-Installationsverzeichnis unter
_retail_\Interface\AddOns\totalRP3_Extended\document\document.lua
.
Wer so vorgeht, bekommt das Beste aus beiden Welten, die aktuellste Version, aber ohne den nervigen Bug.
Sobald die nächste Extended-Version erscheint, werden alle Dateien regulär mit der neuesten veröffentlichten Version überschrieben, es ist nicht notwendig, diesen kleinen Vorab-Fix „aufzuräumen“.