Animationen
Grundlagen
Interner Ablauf und formale Berechnung
Die Animationen sind für unser Spiel unumgänglich, denn diese laufen permanent ab, egal ob wir gerade angreifen, laufen, stehen oder schon gestorben sind. Das Bild, welches wir von unserem Charakter erhalten, ist bloß die Darstellung verschiedenster aneinander gereihter Animationen. Falls das Spiel aufhörte unsere Animationen anzuzeigen, würde unser Charakter vom Bildschirm verschwinden.
Zu Beginn dieses doch recht umfangreichen Themas möchte ich erklären, wie eine Animation programmintern überhaupt abläuft:
Das Programm ermittelt die Art der nächsten Animation und holt (bzw berechnet) sich alle für die Berechnungen benötigten Werte aus diversen Spieldateien. Anschließend werden der Counter-Endwert und der Counter-Anstiegswert nach folgenden Formeln berechnet:
Counter-Endwert = Hitshift*FramesPerDirection
Counter-Anstiegswert = [AnimationSpeed*Acceleration/100]
Hitshift ... Das Ergebniss eines jeden Rechenschrittes wird als Integer-Wert (ganzzahliger Wert) abgespeichert, weshalb alle Nachkomastellen entfallen (entspricht für positive Werte einem Abrunden). Wir verwenden hier also Divisionen mit ganzen Zahlenwerten und damit werden die möglichen Abstufungen unseres Ergebnisses zu groß (zB 20, 10, 6, ... für die Divisionen 20/1, 20/2, 20/3, ...). Aus diesem Grund verwenden wir diesen Faktor in Dividend und Divisor, welcher für die Berechnung der Animationslängen einen Wert von 256 aufweist. Dadurch werden uns feinere Abstufungen ermöglicht (zB 20, 19, 18, ... für die Divisionen 5120/256, 5120/269, 5120/284, ...). Wie bereits erwähnt, muss das Hitshift auf beiden Seiten des Bruches auftreten, damit es sich anschließend wieder hinauskürzt. Im Divisor (Counter-Anstiegswert) ist das Hitshift auf den ersten Blick nicht erkennbar, weil es bereits in der AnimationSpeed inkludiert ist. Auch hier ist somit eine Feinabstimmung möglich, es gibt nun 255 mögliche Abstufungen mehr als zuvor.
FramesPerDirection ... Dies ist die Anzahl an Frames, aus der eine bestimmte Animation besteht. Der Wert wird somit klarerweise über die verwendete Animation bestimmt, sodass der Spieler keinen Einfluss darauf hat. Je mehr Frames eine bestimmte Animation besitzt, desto mehr Frames müssen abgespielt werden und desto länger dauert die Animation.
AnimationSpeed ... Dies ist der Grundwert der Geschwindigkeit, mit der eine Animation abgespielt wird. Der Standartwert liegt bei einem Frame pro Tick, wobei auch andere Werte möglich sind. Zwischen den Werten "1" und "2" der AnimationSpeed, gibt es eine Änderung von 100% und die Anschreibung als Integer-Wert lässt keine Zwischenstufen zu. Daher ist hier das Hitshift inkludiert, sodass wir zwischen den Werten 1 (=256) und 2 (=512) 256 Abstufungen erhalten und die Animationsdauern besser steuerbar sind.
Acceleration ... Dies ist ein Prozentwert und steuert die (positive wie negative) Beschleunigung der Animation. Dieser Wert ist für den Anwender einer bestimmten Animation die einzige Möglichkeit die Animationsdauer zu beeinflussen, alle anderen Werte sind vorgegeben. Je nach Art der verwendeten Animation fließen hier verschiedenste Werte in diese Berechnung ein. Die Grenzen dieses Wertes liegen bei 15 und 175. Um die Erklärung einfach und allgemein zu halten, werde ich vorerst bei diesem Begriff bleiben und die Zusammensetzung in den jeweiligen Teilbereichen erläutern.
Da das Spiel ausschließlich Integer-Werte speichern kann, wird der Counter-Anstiegswert abgerundet. Dabei ist jedoch zu erwähnen, dass die Multiplikation vor der Division stattfindet, sodass auch nur der endgültige Wert gerundet werden muss und der Rundungsfehler geringer wird.
Um genau zu sein, werden diese Berechnungen nicht nur einmal durchlaufen, sondern in jedem Tick wieder. Es gilt daher nicht ausschließlich der Counter-Anstiegswert zum Start der Animation, sondern immer der momentane Wert. Dies ist für uns jedoch vollkommen nebensächlich, sodass wir einfach von einem gleichbleibendem Wert ausgehen. Da die Berechnungen nun abgeschlossen sind, kommen wir zum nächsten Teil des Animationsablaufes.
Jede Animation besteht aus einer bestimmten Anzahl an Frames und die Summe all dieser bildet den FramesPerDirection-Wert. Welches Frame zu welchem Tick abgespielt wird, entscheidet nun die aktuelle Stellung des Counters. Die Anzahl der möglichen Counterstellungen entspricht natürlich dem Counter-Endwert und so teilen wir diese Zahl durch die Anzahl der Frames unserer Animation. Dies werde ich anhand eines Beispiels verdeutlichen und nehme dafür FramesPerDirection mit 5, AnimationSpeed mit 256 und Acceleration mit 100 an:
Counter-Endwert = Hitshift*FramesPerDirection = 256*5 = 1280
Counter-Anstiegswert = [AnimationSpeed*Acceleration/100] = 256*100/100 = 256
Wir haben somit 1280 verschiedene Counterstellungen (von 0 bis 1279) und teilen diese auf 5 Frames auf. Somit hat jedes Frame genau 1280/5 = 256 Counterstellungen. Diese Zahl entspricht natürlich immer dem Hitshift, denn dessen Aufgabe war es schließlich die Anzahl der möglichen Counter-Stellungen pro Frame zu erhöhen. Dies sortieren wir nun anhand der Stellung des Counters:
Counter = 0000-0255 => Bild 00 wird dargestellt
Counter = 0256-0511 => Bild 01 wird dargestellt
Counter = 0512-0767 => Bild 02 wird dargestellt
Counter = 0768-1023 => Bild 03 wird dargestellt
Counter = 1024-1279 => Bild 04 wird dargestellt
Diese Tabelle könnten wir uns eigentlich auch ersparen, denn das darzustellende Frame lässt sich auch einfacher ermitteln. Wir müssen bloß unseren aktuellen Counter-Wert durch unser Hitshift dividieren und schneiden vom Ergebniss die Nachkomastellen ab. Damit erhalten wir ebenfalls für jeden Counter-Wert das abzuspielende Frame (zb [500/256] = [1,95] = 1 => Frame 01 wird dargestellt).
Da wir bei 0 beginnen, ist die Counterstellung 1280 kein Teil dieser Animation mehr. Sobald der Counter den Wert 1279 überschreitet, endet die aktuelle Animation und die nächste startet noch im selben Tick. Daher dürfen wir das Tick, bei welchem der Counter-Endwert erreicht bzw überschritten wird, nicht mehr zu unserer ersten Animation zählen.
Jetzt starten wir unsere Animation, welche Tick für Tick abläuft. Am Anfang eines jeden Ticks wird der Counter um den errechneten Counter-Anstiegswert erhöht und anschließend abgefragt, ob der Counter-Endwert schon erreicht oder überschritten wurde (selbiges gilt auch für das FrameDataActionFlag, bei dem die Aktion wirklich durchgeführt wird). Ist dies nicht der Fall, wird ein Bild der Animation dargestellt. Sollte der Counter-Endwert jedoch schon erreicht/überschritten sein, endet die Animation. Die neue Animation (es läuft natürlich immer eine Animation nach der anderen ab) beginnt nun in eben jenem Tick.
Die Animationen lassen sich in zwei Gruppen unterteilen. Einerseits können sie eine Art "Strafe" sein, weil sie eigentlich nichts bewirken aber Zeit beanspruchen. Dies ist zum Beispiel beim Blocken der Fall. Indem wir blocken, lösen wir die Blockanimation aus, doch diese bringt uns keinen Nutzen mehr, sondern hindert uns bloß daran weitere Aktionen in dieser Zeit auszuführen. Diese Animationen werden also durch eine Aktion ausgelöst, bewirken selbst allerdings keine.
Andererseits gibt es auch Animationen, die eine Aktion bewirken. Ein Beispiel dafür wäre die Angriffsanimation, denn irgendwann zwischen Animationsstart und Animationsende (zum so genannten "Aktionstick") wird der tatsächliche Angriff ausgeführt, bzw ein Geschoss zum Angriff generiert. Genaueres darüber folgt später noch im entsprechenden Unterkapitel.
Soweit zur Theorie, nun ein kurzes Beispiel zum leichteren Verständniss des Ablaufes:
FramesPerDirection = 3
AnimationSpeed = 256
Acceleration = 60
Counter-Endwert = Hitshift*FramesPerDirection = 256*3 = 768
Counter-Anstiegswert = AnimationSpeed*Acceleration/100 = 256*60/100 = 153
Counter = 000-255 => Bild 00 wird dargestellt
Counter = 256-511 => Bild 01 wird dargestellt
Counter = 512-767 => Bild 02 wird dargestellt
Tick 1:
Counter ist 0
Counter wird erhöht auf 153
Ist der Counter >= 768? Nein => kein Abbruch
[153/256] = 0 => Bild 00 abspielen
Tick 2:
Counter ist 153
Counter wird erhöht auf 306
Ist der Counter >= 768? Nein => kein Abbruch
[306/256] = 1 => Bild 01 abspielen
Tick 3:
Counter ist 306
Counter wird erhöht auf 459
Ist der Counter >= 768? Nein => kein Abbruch
[459/256] = 1 => Bild 01 abspielen
Tick 4:
Counter ist 459
Counter wird erhöht auf 612
Ist der Counter >= 768? Nein => kein Abbruch
[612/256] = 2 => Bild 02 abspielen
Tick 5:
Counter ist 612
Counter wird erhöht auf 765
Ist der Counter >= 768? Nein => kein Abbruch
[765/256] = 2 => Bild 02 abspielen
Tick 6:
Counter ist 765
Counter wird erhöht auf 918
Ist der Counter >= 768? Ja => Animation abbrechen, augenblicklicher Start der nächsten Animation
Dieses Tick zählt somit nicht mehr zu unserer Animation
Da das letzte Tick nicht mehr zur aktuellen Animation zählt, umfasst unsere Animation eine Dauer von 5 Ticks.
Diese Werte gelten für die Blockanimation eines Paladins ohne Heiligem Schild, der über 11 FBR verfügt. Ein Blick in diverse Tabellen bestätigt unser Ergebniss: eine Dauer von 5 Ticks. Außerdem fällt uns noch etwas auf, wenn wir Tick 5 betrachten. Die Differenz zwischen dem aktuellen Counterwert und dem Counter-Endwert beträgt nur "3", sodass auch schon die kleinstmögliche Erhöhung des Counter-Anstiegswertes die Animation weiter verkürzen würde. Wir stehen damit direkt vor einem Breakpoint, welche ich weiter unten noch behandeln werde.
Natürlich ist dieser interne Ablauf für uns nicht alzu relevant, denn die Ermittlung der Animationsdauer beansprucht mit dieser Methode zu viel Zeit. Daher wandeln wir den obigen Ablauf nun in eine Formel um.
Herleitung der Formel
Wie weiter oben schön ersichtlich ist, gibt uns die Dauer in Ticks an, wie oft wir den Counter um den Counter-Anstiegswert erhöhen mussten um den Counter-Endwert zu erreichen bzw zu überschreiten. Damit enspricht das Grundgerüst unserer Formel natürlich einer Division, weil wir zur Ermittlung der Dauer den Counter-Endwert durch den Counter-Anstiegswert dividieren müssen.
FPA = Counter-Endwert/Counter-Anstiegswert
Nun müssen wir beim Ergebniss dieser Division zwischen zwei Möglichkeiten unterschieden. Ist das Ergebniss eine ganze Zahl, so waren Counter-Endwert und Counter-Anstiegswert im letzten Tick ident. Damit zählt das letzte Tick nicht mehr, weil die Animation in diesem Tick abgebrochen wird. Somit müssen wir am Ende der Formel noch ein Tick abziehen.
Ist die Division allerdings nicht ganzzahlig (zB 3,5), so müssen wir den Wert aufrunden, denn nach drei Ticks ist der Counter-Endwert noch nicht erreicht, 3,5 Ticks sind nicht möglich und erst nach dem viertem Tick haben wir den Counter-Endwert überschritten. Auch in diesem Fall zählt das letzte Tick nicht mehr zur aktuellen Animation und wird damit ebenfalls am Ende abgezogen.
Im ersten Fall haben wir bereits eine ganze Zahl, sodass das Aufrunden nichts verändert und wir beide Fälle in einer Formel zusammenfassen können. Dabei setze ich diesmal gleich die Zusammensetzungen für Counter-Endwert und Counter-Anstiegswert ein:
FPA = {(Hitshift*FramesPerDirection)/[AnimationSpeed*Acceleration/100]} - 1
Oftmals bekommt man zu hören, dass das erste Frame einer Animation ident mit dem Letztem sei und deshalb nicht angezeigt werde, woraus das "-1" in der Formel resultiere. Dies stimmt allerdings nicht. Die beiden Frames sind unterschiedlich; wie ich oben veranschaulicht habe werden sie beide angezeigt und den Grund für die "-1" am Formelende habe ich ebenfalls erklärt.
Zum Vergleich der Formel mit dem internem Ablauf rechnen wir das vorherige Beispiel nochmal:
FramesPerDirection = 3
AnimationSpeed = 256
Acceleration = 60
FPA = {(Hitshift*FramesPerDirection)/[AnimationSpeed*Acceleration/100]} - 1
FPA = {(256*3)/[256*60/100]} - 1
FPA = {(768)/[153,6]} - 1
FPA = {(768)/153} - 1
FPA = {5,02} - 1
FPA = 6 - 1
FPA = 5
Auch hier sehen wir wieder, dass wir kurz vor dem nächstem Breakpoint stehen und die Dauer von 5 Ticks deckt sich mit der vorherigen schrittweisen Berechnung, sodass unsere Formel richtig hergeleitet wurde.
Nun müssen wir nur noch die Zusammensetzung des Acceleration-Wertes wissen. Diese hängt allerdings von der verwendeten Animation ab, setzt sich jedoch immer zumindest aus einem Grundwert (ohne Grundwert hätte ein Malus keinen Effekt, sofern kein Bonus vorhanden ist oder dieser bereits negiert wurde) und einem Effektivwert zusammen. Zur Wahrung der Übersichtlichkeit werde ich diesen Teil der Formel in den jeweiligen Teilbereichen behandeln und nun mit der Ermittelung der Breakpoints fortfahren.
Breakpoints
Nun können wir uns für bestimmte Werte unsere Animationsgeschwindigkeit berechnen. Unser Interresse beruht allerdings hauptsächlich auf den Breakpoints, mit der reinen Berechnung mit vordefinierten Werten können wir nur wenig anfangen. Leider können wir hierfür allerdings keine Formel verwenden, da die explizite Anschreibung des Effektivwertes an dem Runden der Werte scheitert. Somit berechnen wir uns (zb in Excel) für jeden möglichen Acceleration-Wert die Animationsdauer und notieren uns, bei welchem Wert wir den Sprung auf die nächstkürzere Animation schaffen. Je nach verwendeter Animation kann man sich dann berechnen, welche Eigenschaften man noch erhöhen kann, um den nächsten Breakpoint zu erreichen. Ein Beispiel für eine solche Berechnung:
So oft anklicken, bis das Bild lesbar wird.
Rechts werden unsere Werte eingegeben und durch schrittweise Berechnung sehen wir, bei welchen Werten sich die Animation verkürzt. Man muss bloß darauf achten den Grundwert (Teil der Acceleration) richtig anzugeben und keine Werte zu verwenden, die das Maximum von 175 überschreiten. Den so ermittelten Effektivwert rechnen wir dann erneut auf den ursprünglichen Wert zurück:
Wert = {120*EWert/(120 - EWert)}
Auslösen einer Aktion
Bisher haben wir die Animationen am Beispiel eines Blockvorganges betrachtet. Dieser beinhaltet keine Aktion, sondern stellt gewissermaßen eine "Strafe" für das Abwenden des geblockten Schadens dar. Eine Angriffsanimation dagegen wird gezielt gestartet und dient dem Zweck, den Gegner zu schädigen. Daher muss es innerhalb dieser Animation einen Zeitpunkt geben, an welchem eine Aktion (in diesem Fall das Treffen des Gegners) ausgeführt wird. Dieser Zeitpunkt wird als Aktionstick (üblich ist auch "kritisches Tick" und andere Benennungen) bezeichnet und kann ebenso berechnet werden.
Unter Berücksichtigung des Aktionsticks, können wir eine Animation in drei Abschnitte unterteilen. Von Animationsbeginn bis zum Aktionstick wird zuerst der einleitende Animationsteil abgespielt. Nur in diesem Abschnitt kann die Animation noch durch eine andere Animation unterbrochen werden, sodass die Aktion verhindert wird. Ein Abbruch nach dem eigentlichen Angriff würde bloß den Wegfall eines unnötigen Animationsteiles bedeuten und ist nicht möglich, weil wir davon nur profitieren würden. Daher können die Animationen ab dem Aktionstick nicht mehr abgebrochen werden. Dies gilt auch für Rollback-Angriffe wie "Eifer", weshalb diese nach dem ersten Schlag nicht mehr unterbrochen werden können.
Nach dem einleitenden Animationsteil folgt das Aktionstick, welches unsere Aktion beinhaltet. Hier können wir zwischen zwei Aktionen unterscheiden, nämlich dem Treffen eines Gegners im Nahkampf und dem Erzeugen eines Geschosses für den Fernkampf. Das Aktionstick liegt zumeist recht zentral innerhalb der Animation, sodass der einleitende und der abschließende Animationsteil annähernd gleich lang sind.
Nach dem Aktionstick folgt nur noch der abschließende Animationsteil. Dieser bietet uns keinen Nutzen mehr, sondern verzögert nur den Start der nächsten Animation. Praktisch ist jedoch, dass eine eventuelle Zauberverzögerung schon während diesem abschließenden Abschnitt geringer wird, sodass die Zwangspause nach Abschluss der Animation geringer ist als der Brutto-Wert der Zauberverzögerung.
Zu Beginn haben wir bereits den Begriff des Counter-Endwertes als Multiplikation aller Frames mit den möglichen Counterstellungen derselben eingeführt. Für die Aktion ist nun ein bestimmtes Frame von Interresse, nämlich das FrameDataActionFlag, welches aus der animdata.txt ausgelesen werden kann. Dieses Frame ist in den "FrameDataXXX"-Spalten jener Datei durch eine Ziffer ("1" für das Treffen eines Gegners und "2" für das Erzeugen eines Geschosses) gekennzeichnet. Beim Wert des FrameDataActionFlags ist zu berücksichtigen, dass die Frames bei der Bezeichnung 00 beginnen, sodass beispielsweise das vierte Frame der Animation das Frame mit der Bezeichnung 03 ist.
Wie schon zuvor, multiplizieren wir nun das jeweilige Frame mit den 256 Counterstellungen, um den entsprechenden Counterwert zu erhalten - in diesem Falle den Counter-Aktionswert für die Berechnung einer Aktion:
Counter-Aktionswert = Hitshift*FrameDataActionFlag
Die jeweilige Aktion ist nun nicht an die Darstellung des entsprechenden Frames gebunden, denn bei hohem Counter-Anstiegswert wäre es möglich, das FrameDataActionFlag zu überspringen. Die Auslösung der Animation erfolgt stattdessen sobald der Counter-Aktionswert erstmalig erreicht oder überschritten wird, selbst wenn zu diesem Zeitpunkt nicht das FrameDataActionFlag abgespielt wird. Daher gibt es nach jeder Erhöhung des Counters eine Abfrage, ob der Counter-Aktionswert schon erreicht wurde. Falls dies der Fall ist, wird unsere Aktion in diesem Tick ausgeführt. Auch andere Ereignisse können mit diesem Zeitpunkt zusammenhängen, doch diese werden erst später behandelt.
Nun wird also in jedem Tick der Counter erhöht und unsere Aktion wird beim Erreichen oder Überschreiten des Counter-Aktionswertes durchgeführt. Daraus ergibt sich für unsere Formel erneut die bereits bekannte Division und da es bei der Anzahl der Erhöhungen des Counters wieder nur ganze Zahlen geben kann, müssen wir erneut aufrunden und landen somit bei jenem Frame, bei dem die Aktion durchgeführt wird (entweder das FrameDataActionFlag oder das nachfolgende Frame, falls das FrameDataActionFlag nicht abgespielt wird). Anders als bei der Berechnung der Gesammtdauer wird die Animation nun allerdings nicht abgebrochen, sondern bloß die Aktion durchgeführt. Damit müssen wir das letzte Tick nicht abziehen und erhalten folgende Formel:
AT = {(Hitshift*FrameDataActionFlag)/[AnimationSpeed*Acceleration/100]}
Ablauf der Animation und Anwendung der Formel
Nun wählen wir uns ein paar beliebige Werte aus und gehen den Animationsablauf Schritt für Schritt durch. Im Anschluss werden wir noch überprüfen, ob unsere neue Formel zum gleichen Ergebniss kommt.
FramesPerDirection = 15
FrameDataActionFlag = 07
AnimationSpeed = 256
Acceleration = 175
Counter-Endwert= Hitshift*FramesPerDirection = 256*15 = 3840
Counter-Aktionswert = Hitshift*FrameDataActionFlag = 256*7 = 1792
Counter-Anstiegswert= [AnimationSpeed*Acceleration/100] = [256*175/100] = 448
Tick 1:
Counter ist 0
Counter wird erhöht auf 448
Ist der Counter >= 3840? Nein => kein Abbruch
Ist der Counter >= 1792? Nein => keine Aktion
[448/256] = 1 => Bild 01 abspielen
Tick 2:
Counter ist 448
Counter wird erhöht auf 896
Ist der Counter >= 3840? Nein => kein Abbruch
Ist der Counter >= 1792? Nein => keine Aktion
[896/256] = 3 => Bild 03 abspielen
Tick 3:
Counter ist 896
Counter wird erhöht auf 1344
Ist der Counter >= 3840? Nein => kein Abbruch
Ist der Counter >= 1792? Nein => keine Aktion
[1344/256] = 5 => Bild 05 abspielen
Tick 4:
Counter ist 1344
Counter wird erhöht auf 1792
Ist der Counter >= 3840? Nein => kein Abbruch
Ist der Counter >= 1792? Ja => Aktion wird ausgeführt
[1792/256] = 7 => Bild 07 abspielen
[...]
Tick 9:
Counter ist 3584
Counter wird erhöht auf 4032
Ist der Counter >= 3840? Ja => Animation abbrechen, augenblicklicher Start der nächsten Animation
AT = {(Hitshift*FrameDataActionFlag)/[AnimationSpeed*Acceleration/100]}
AT = {(256*7)/[256*175/100]}
AT = {1792/448}
AT = {4}
AT = 4
Unser Angriff (der normaler Angriff des Paladins mit einhändiger Schwungwaffe) ist also bei maximaler Acceleration bei einer Länge von 8 Ticks. Wie beide Berechnungsmöglichkeiten zeigen, findet unser Angriff im vierten Tick statt.
Im ersten Moment wirkt dies nun nicht sonderlich interressant, ob der Gegner nun ein paar Sekundenbruchteile früher oder später getroffen wird. Allerdings wird dieses System auch für andere Berechnungen benötigt. Ein Beispiel dafür wäre der Angriff Eifer, welcher wie oben zu sehen ist beim Paladin auf bis zu 4 Ticks beschleunigt werden kann.
Animationsarten
Grundsätzlich kann man zwischen zwei Animationsarten unterscheiden. Die gewöhnliche Animation haben wir soeben mitsammt Ablauf und Formel kennengelernt. Diese Animationen können leicht eingesehen werden, weil die grundlegenden Werte softcoded sind. Das bedeutet, dass sie vom Spiel aus bestimmten Dateien ausgelesen werden, welche für den Spieler leicht zugänglich sind und ausreichend Information enthalten. Für jede dieser Animationen gibt es eine Reihe von Frames, wobei der FramesPerDirection-Wert jeweils angibt, wie viele dieser Frames für eine jeweilige Animation wirklich verwendet werden.
Neben den bisher genannte Animationen gibt es auch die
Sequenz-Animationen, welche von den bisher bekannten in mehreren Punkten abweichen. Den Sequenzen sind keine eigenen Frames zugewiesen, sondern sie bestehen aus vordefinierten Frames einer oder mehrerer Animationen. Von diesen Sequenzen sind leider nur jene softcoded, die von Monstern verwendet werden. Die Sequenzen der Spieler allerdings sind hardcoded, stecken also irgendwo an unbekannter Stelle in einer dll-Datei. Daher gibt es nur wenige, die diese Sequenzen ebenfalls auslesen können.
Die Formel für Sequenzen ist beinahe mit der bereits bekannten ident. Der einzige Unterschied besteht im Fehlen der "-1" am Formelende:
FPA = {(Hitshift*FramesPerDirection)/[AnimationSpeed*Acceleration/100]}
Sofern es sich bei einer Sequenz um einen Angriff (exklusive Zaubern) handelt, gibt es noch eine weitere Änderung, welche unsere SQ-Animation verlangsamt. Dies werde ich jedoch erst später im entsprechenden Teilbereich behandeln.
Verwandlung
Jede Spieleinheit durchläuft pausenlos eine Animation nach der anderen. Es gibt Animationen für den Angriff, für das Stehen und Laufen, ja sogar für das Sterben. Unsere Leiche ist ebenfalls nichts anderes als eine Animation mit nur einem Frame. Das Bild des Charakters wie wir es sehen, wird uns ausschließlich über unsere verschiedenen Animationen gezeigt. Wir sehen unseren Charakter nur dank unzähliger Frames, welche verschiedensten Animationen entstammen. Würde einen Moment lang keine Animation ablaufen, so wäre unser Charakter verschwunden. Daraus folgt logischerweise, dass die verwendete Animation von unserem aktuellem Erscheinungsbild abhängig ist. Nach einer Verwandlung brauchen wir für unseren Werwolf eigene Animationen, weil die Animation des Druiden eben auch nur einen Druiden abbildet. Daher haben alle Verwandlungsformen eigene Animationen, welche vom ehemaligen Charakter unabhängig sind. Wenn wir uns in einen Werbären verwandeln, spielt es somit keine Rolle ob wir einst ein Druide oder eine Zauberin waren.
Allerdings gibt es auch hier die berühmte Ausnahme der Regelung. Bei einer Verwandlung in einen Werwolf oder Werbären hat Blizzard eine Sonderregelung in Bezug auf die Angriffsgeschwindigkeit hinzugefügt. Dank dieser wird die AnimationSpeed nicht aus einer Spieldatei ausgelesen, sondern eigens berechnet. Diese Berechnung beinhaltet die FramesPerDirection der unverwandelten Spieleinheit, sodass bei diesen beiden Verwandlungen die Angriffsgeschwindigkeit vom unverwandelten Charakter abhängig ist.
Auslesen der gebrauchten Werte
Animdata.txt
Wie bereits angesprochen, werden verschiedenste Werte zu Beginn jeder Animation aus diversen Spieldateien ausgelesen. Dabei handelt es sich unter anderem um die Werte "FramesPerDirection" und "AnimationSpeed". Diese findet man in der animdata.d2, welche man aus der d2data.mpq extrahieren kann. Zum Extrahieren verwendet man das Programm "MPQView", welches im Mod-Starterkit auf der Mainpage enthalten ist. Um die Extrahierte animdata.d2 auslesen und verändern zu können, wandelt man sie mit dem Programm "Animdata_edit" in eine Textdatei um. Besagtes Programm findet man
hier im PK in der Kategorie "Animation Editing".
Um sich dies zu ersparen, kann man auch diese
Liste verwenden oder sich
hier eine fertige und geordnete animdata.txt herunterladen. Außerdem beinhalten die
FAQtoids von librarian und ebenso meine
FAQ die wesentlichsten Daten der Animationen.
Die Bezeichnungen dieser Animationen setzen sich aus 3 Teilen zusammen und verwenden das Schema
CCAAWWW.
CC steht für "class" und gibt an, welche Spieleinheit diese Animation benutzt. Die wesentlichsten Kürzel lauten:
AI ... Assassine
AM ... Amazone
BA ... Barbar
DZ ... Druide
NE ... Nekromant
PA ... Paladin
SO ... Zauberin
40 ... Werwolf
TG ... Werbär
VA ... Vampir
FK ... Untoter Rattenmann
0A ... Barbarensöldner
GU ... Stadtwache
IW ... Eisenwolf
RG ... Rogue
Sollte man noch das Kürzel einer bestimmten Monsterart brauchen, findet man dieses in der MonStats.txt in der Spalte "Code".
AA steht natürlich für "animation" und bezeichnet die Tätigkeit, bei welcher diese Animation verwendet wird. Die Kürzel lassen sich aus der PlrMode.txt auslesen und lauten:
A1 ... Angriff 1
A2 ... Angriff 2
KK ... Kick
TH ... Werfen
S1 ... Skill 1
S2 ... Skill 2
S3 ... Skill 3
S4 ... Skill 4
SC ... Zaubern
BL ... Blocken
GH ... Treffererholung
TN ... Neutral (in der Stadt stehen)
NU ... Neutral (außerhalb der Stadt stehen)
TW ... Gehen (in der Stadt)
WL ... Gehen (außerhalb der Stadt)
RN ... Rennen
DT ... Sterben
DD ... Leiche
SQ ... Sequenz
Die Sequenz ist dabei nur der Vollständigkeit halber angegeben, da sich diese bekanntlich aus Animationsteilen zusammensetzt und deren Daten somit nicht in der animdata.txt verzeichnet sind. Die Informationen über SQ-Animationen sind in der D2Common.dll verzeichnet, wobei der genaue Offset vom Patch abhängt.
WWW steht für "weapon class" und bezeichnet die verwendete Waffenart. Diese ist in der wWeapons.txt in der Spalte "wclass" bzw "2handedwclass" für jede Waffen-Grundklasse angegeben:
hth ... Faustkampf (keine Waffe)
ht1 ... eine Klaue
ht2 ... zwei Klauen
1hs ... einhändige Schwungwaffen (Schwerter, Äxte, Knüppel, Keulen, Szepter, Hämmer, Wurfäxte)
2hs ... zweihändige Schwerter
1ht ... einhändige Stichwaffen (Dolche, Wurfspieße, Wurfmesser, Wurfelexiere)
2ht ... zweihändige Stichwaffen (Speere)
stf ... Stäbe (Stangenwaffen sowie zweihändige Äxte, Hämmer und Stäbe)
bow ... Bögen
xbw ... Armbrüste
1ss ... zwei Schwungwaffen
1jt ... zwei Stichwaffen
1js ... links Stichwaffe, rechts Schwungwaffe
1st ... links Schwungwaffe, rechts Stichwaffe
Im ungemoddeten Spiel sind die Werte für ht1 und ht2 ident, sodass man hierbei nicht zu unterscheiden braucht.
Die letzten vier Animationen verwendet nur der Barbar, da keine andere Charakterklasse diese Waffen gleichzeitig tragen könnte. Glücklicherweise entfällt auch hier die Unterscheidung, denn die Werte der Animationen 1ss, 1jt, 1js und 1st sind (abgesehen von einer vernachlässigbaren Verschiebung des FrameDataActionFlags) ident.
Skills.txt
Aus den mpq-Archiven lassen sich unter Zuhilfenahme von "MPQView" auch weitere Dateien extrahieren, zu denen unter anderem die Skills.txt zählt, in welcher die Eigenschaften unserer Fertigkeiten festlegt werden. Da unsere Animationen über die Acceleration durch verschiedenste Fertigkeiten beeinflusst werden können, sollten wir uns auch damit kurz auseinandersetzen. Dabei sind nur drei Eigenschaften erwähnenswert, wobei nur eine davon wirklich eine große Rolle spielt.
Eine wichtige Eigenschaft unserer Fertigkeiten ist die
attackrate, welche zur Beschleunigung unserer Angriffe dient. Für gewöhnlich wird allerdings die Bezeichnung "SIAS" verwendet und die betroffenen Fertigkeiten mit einem Bonus oder Malus auf diesen Wert sind "Altern", "Heiliger Frost", "Fanatismus", "Raserei", "Werwolf", "Tempoblitz" und "Delirium".
Eher unbedeutend und seltener anzutreffen ist die
other_animrate, welche nur von den Fertigkeiten "Heiliger Frost" und "Delerium" beeinflusst wird. Diese wirkt zum Beispiel bei der Standanimation, welche für uns vollkommen uninterressant ist. Nur die Ausweichanimationen machen diese Eigenschaft für uns relevant, denn diese verwenden die other_animrate.
Für die Animationslänge bedeutungslos, aber dennoch interressant, ist die
velocitypercent zur Steigerung unserer Bewegungsgeschwindigkeit. Einen Bonus oder Malus liefern uns dabei die Fertigkeiten "Altern", "Heiliger Frost", "Gedeihen", "Raserei", "Mehr Tempo", "Barbarenwut", "Tempoblitz" und "Delirium".