Gui-Spell-Tutorial

Gui-Spell-Tutorial


zum Anfang


Welchen Zauber wollen wir uns denn heute basteln?

Zum Thema Feuer hatte ich mir überlegt, dass es sicherlich cool wäre irgendwie Feuerbälle über den Bildschirm fliegen zu lassen. Da der normale Feuerball Zauber a la Diablo2 imo aber schon eher an Reiz verliert, basteln wir uns etwas tolleres!
Nehmen wir als Model einmal den Bloodmage zur Verfügung. Er verkörpert ja praktisch schon das Feuer. Wenn wir uns den Bloodmage jetzt anschauen, sehen wir, dass um seinen Kopf so Kugeln herumschwirren. Das sieht doch schonmal recht interessant aus – das nehmen wir als Grundidee für unseren Zauber.
Wir lassen also Feuerbälle um unseren Helden kreisen. Damit es nicht ganz so langweilig ist, lassen wir die Feuerbälle nahe am Magier entstehen und mit der Zeit entfernen sich dann diese Feuerbälle vom Magier und umkreisen ihn in einer fixen Maximaldistanz.
Jetzt können wir noch entscheiden, was diese Feuerbälle bewirken. Ich habe mir eine einfache Kollision ausgedacht. Wenn nun also ein Feuerball mit einer gegnerischen Einheit kollidiert, explodiert er und schädigt dabei die Einheit und stunned sie für kurze Zeit. Ein Ziel, Schaden + Stun und Feuer – was würde sich da wohl besser als Dummyzauber eignen als Firebolt? Stimmt! Gar nichts, und deswegen ist Firebolt unser Dummyzauber, der dann von unseren Dummyeinheiten gezaubert wird.


zum Anfang


Die Spellidee steht – was brauchen wir noch?

Richtig! Wir brauchen noch unsere ganzen Objekte, wie Zauber, Dummyzauber, Dummyeinheit, und eine Einheit, die um den Helden kreist (unsere Feuerbälle).
Damit unser Bloodmage nicht lange herumstehen muss um den Effekt des Zaubers zu wirken, nehmen wir als Grundzauber Berserk der Troll Headhunter her. Somit kann man den Zauber auch während dem Laufen aktivieren. Ebenso haben wir einen schönen Buff in der Statusanzeige im Spiel um unsere Gegner zu informieren: „Achtung! Komm mir bloß nicht mehr zu nahe!“
Als Dummyzauber haben wir uns ja schon auf Firebolt geeignet. Kommen wir also zur Dummyeinheit. Am besten dafür geeignet ist meiner Meinung nach der Footman. Er hat keine störenden Nebeneffekt, wie ein Workersymbol oder dass man seine Acquisition Range per Trigger nicht setzen kann, etc.. Als Dummy soll er natürlich nicht anklickbar sein und erhält somit als einzige Fähigkeit Locust (deutsch: Heuschrecke). Weiters besitzt er kein Model, keinen Schatten und seine Animationsgeschwindigkeiten (für bewegen, zauber, angreifen) setzen wir alle auf 0.00, damit er sofort agieren kann.
Ebenso stellen wir seinen Angriff aus, geben ihm als Movementtype Fly, damit er nicht durch ungewünschte Kollisionsberechnungen verschoben wird.

Zu guter letzt entfernen wir noch sein Soundset, stellen seine Foodkosten und Pointvalue auf 0 und reduzieren seine Sichtweite. Eine Namensänderung wäre wohl auch nicht schlecht.
Bleibt uns also noch die Feuerballeinheit. Dazu kopieren wir am besten unseren Dummy und verpassen der so neu erstellten Einheit ein Firebolt Model -> fertig!


zum Anfang


Die GrundIdee

Einen solchen Zauber zu Erstellen ist nicht leicht. Es können 20 Feuerbälle gleichzeitig um 4 verschiedene Einheiten kreisen und es soll immer noch funktionieren, das heisst eine Instanz des Zaubers darf die andere nicht beeinflussen. Mit „Instanz“ ist hier gemeint, dass es zwar der gleiche Zaubertyp ist, aber nicht der selbe Zauber.
Um das zu realisieren benutzen wir folgende Grundidee: Jede Zauber-Instanz bekommt eine eigene Nummer. Alle Daten die der Zauber braucht sind in Arrays unter dieser Nummer gespeichert. Wir nennen diese Technik auch oft „parallele Arrays“.


zum Anfang


Welche Daten müssen wir für eine Instanz speichern?

Dies ist die nächste Frage, die wir uns stellen müssen und ist nicht immer leicht zu entscheiden. Was wir auf jeden Fall brauchen ist die Einheit die den Zauber gezaubert hat. Da sich die Feuerbälle um die Einheit drehen sollen brauchen wir noch eine Variable, die diese Drehung speichert und bei jedem Schritt erhöht und natürlich auch ein Array für die Dummy-Einheiten, die die Feuerbälle darstellen. Wir wollen außerdem, dass der Radius am Anfang sehr klein ist und sich langsam vergrößert, also benötigen wir für diesen auch noch eine Variable. Hinzu kommt noch die Geschwindigkeit da die Drehung langsam beginnen soll und mit jedem Schritt einen Tick schneller wird.

Wir werden allen Variablen, die wir für diesen Zauber verwenden die Vorsilbe FP_ geben, damit die ßbersicht unter den Variablen nicht verloren geht, wenn noch mehr dazu kommen. Den Variablen, die die Daten einer Zauber-Instanz enthalten bekommen zusätzlich noch ein data_ vorne angehängt, was Ebenfalls der ßbersicht dienen soll. Wir haben also für die Daten folgende Variablen:

Name

Typ

Kommentar

FP_data_angle

Real Array

Drehung
FP_data_caster Unit Array Einheit die den Zauber gezaubert hat

FP_data_distance

Real Array Radius
FP_data_missile Unit Array Dummy-Einheiten, die die Feuerbälle darstellen (da es 5 Einheiten pro Instanz sind verwenden wir dieses Array als 2D-Array, dazu später mehr)

FP_data_speed Real Array Geschwindigkeit


zum Anfang


Der komplizierte und wichtigste Teil

Wie oben schon erwähnt wird jede Instanz ihre eigene Nummer bekommen. Das werden wir folgendermaßen realisieren: In einem Array liegen alle nicht benutzten Nummern. Eine Integer-Variable speichert wie viele freie Nummern im Array sind. Eine weitere Variable speichert, wie viele Nummern bisher vergeben wurden.

Wenn wir nun eine Nummer für unsere Zauber-Instanz wollen gibt es zwei Möglichkeiten. Entweder ist eine freie Nummer im Array, dann nehmen wir die oberste Nummer. Wenn nicht gucken wir wie viele Nummern schon vergeben wurden und nehmen diese Zahl. Wenn also die 0, 1, 2 und 3 schon vergeben wären würden wir nun die 4 bekommen, da schon 3 Nummern vergeben wurden. In Triggern ausgedrückt sieht das so aus:

Variablen:

Name

Typ
Kommentar

FP_freeIndexes Integer Array In diesem Array werden die unbenutzten, freien Nummern gespeichert.
FP_freeIndexes_count

Integer

Speichert, wie viele Nummern im Array FP_freeIndexes gespeichert sind.

FP_Index_count

Integer Speichert, wie viele Nummern bereits vergeben wurden.

_TempIndex

Integer Dies ist eine temporäre Variable. Wir verwenden sie einfach dazu, um den Index, mit dem wir Momentan arbeiten, zwischen zu speichern.

 Actions

 ——– Einen Array-Index für die Spell-Instanz finden ——–

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 FP_freeIndexes_count Equal to 0

 Then – Actions

 ——– Kein freier Index im Array -> neuen Index nehmen ——–

 Set _TempIndex = FP_Index_count

 Set FP_Index_count = (FP_Index_count + 1)

 Else – Actions

 ——– Ein unbenutzer Index liegt im Array -> obersten Index benutzen ——–

 Set FP_freeIndexes_count = (FP_freeIndexes_count – 1)

 Set _TempIndex = FP_freeIndexes[FP_freeIndexes_count]

Nun ist die Nummer in der Variable _TempIndex gespeichert.

Als nächstes kommt der zweite Teil der Verwaltung. Um den zweiten Auslöser, der 32 mal pro Sekunde laufen wird möglichst Effizient gestalten zu können benutzen wir ein Array, in dem die Nummern aller Instanzen stehen, die zur Zeit aktiv sind. Wir hängen unsere neue Nummer nun einfach hinten an das Array an.
Ist unsere Instanz die einzige aktive Instanz müssen wir den zweiten Auslöser noch einschalten, da dieser dann noch ausgeschaltet ist. Das ganze sollte dann so aussehen:

Variablen:

Name
Typ

Kommentar
FP_active Integer Array Speichert alle aktiven Instanz-Nummern.
FP_active_count Integer Speichert, wie viele aktive Instanzen es gibt.

 ——– In das Array der aktiven Instanzen eintragen: ——–

 Set FP_active[FP_active_count] = _TempIndex

 Set FP_active_count = (FP_active_count + 1)

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 FP_active_count Equal to 1

 Then – Actions

 Trigger – Turn on Fire Protes Periodic <gen>

 Else – Actions

Fire Protes Periodic ist hier einfach ein neuer Auslöser, den wir schon einmal erstellen aber erst später mit Aktionen füllen werden.

Der folgende Teil ist nun recht einfach, es werden einfach alle Startwerte eingestellt. Ihr könnt mit diesen Startwerten auch rumspielen und gucken, was sich verändert. Das einzig komplizierte ist das erstellen der Dummys. Hier muss man ein bisschen mit Winkeln umgehen können (siehe Mathe-Buch) und die Funktion Point with Polar Offset verstehen. Ein Kreis hat 360 Grad, also erstellen wir jeden Dummy jeweils 360/5 = 72 Grad weiter versetzt.

Außerdem muss man hier beachten, dass es pro Instanz fünf Feuerbälle gibt, also müssen wir das Array für die Dummys etwas strecken, damit alle 5 rein passen. Als Index nehmen wir also nicht wie sonst _TempIndex sondern _TempIndex*5 + IntegerA (mehr dazu hier: Matrizen (2D-Arrays) ).

 ——– Startwerte setzen: ——–

 Set FP_data_angle[_TempIndex] = (Facing of (Triggering unit))

 Set FP_data_caster[_TempIndex] = (Triggering unit)

 Set FP_data_distance[_TempIndex] = 25.00

 Set FP_data_speed[_TempIndex] = 90.00

 For each (Integer A) from 0 to 4, do (Actions)

 Loop – Actions

 Unit – Create 1 Missile | Fire Protes for (Owner of (Triggering unit)) at ((Position of (Triggering unit)) offset by FP_data_distance[((_TempIndex x 5) + (Integer A))] towards (FP_data_angle[_TempIndex] + ((Real((Integer A))) x 72.00)) degrees) facing ((FP_data_angle[_TempIndex] + 90.00) + ((Real((Integer A))) x 72.00)) degrees

 Set FP_data_missile[((_TempIndex x 5) + (Integer A))] = (Last created unit)

Damit ist der erste Trigger abgeschlossen, fehlt nur noch das Ereignis und die Bedingungen. Der komplette Trigger sieht dann so aus:

 Fire Protes Start

 Events

 Unit – A unit Starts the effect of an ability

 Conditions

 (Ability being cast) Equal to Fire Protes

 ((Triggering unit) has buff Fire Protes ) Equal to False

 Actions

 ——– Einen Array-Index für die Spell-Instanz finden ——–

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 FP_freeIndexes_count Equal to 0

 Then – Actions

 ——– Kein freier Index im Array -> neuen Index nehmen ——–

 Set _TempIndex = FP_Index_count

 Set FP_Index_count = (FP_Index_count + 1)

 Else – Actions

 ——– Ein unbenutzer Index liegt im Array -> obersten Index benutzen ——–

 Set FP_freeIndexes_count = (FP_freeIndexes_count – 1)

 Set _TempIndex = FP_freeIndexes[FP_freeIndexes_count]

 ——– In das Array der aktiven Instanzen eintragen: ——–

 Set FP_active[FP_active_count] = _TempIndex

 Set FP_active_count = (FP_active_count + 1)

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 FP_active_count Equal to 1

 Then – Actions

 Trigger – Turn on Fire Protes Periodic <gen>

 Else – Actions

 ——– Startwerte setzen: ——–

 Set FP_data_angle[_TempIndex] = (Facing of (Triggering unit))

 Set FP_data_caster[_TempIndex] = (Triggering unit)

 Set FP_data_distance[_TempIndex] = 25.00

 Set FP_data_speed[_TempIndex] = 90.00

 For each (Integer A) from 0 to 4, do (Actions)

 Loop – Actions

 Unit – Create 1 Missile | Fire Protes for (Owner of (Triggering unit)) at ((Position of (Triggering unit)) offset by FP_data_distance[((_TempIndex x 5) + (Integer A))] towards (FP_data_angle[_TempIndex] + ((Real((Integer A))) x 72.00)) degrees) facing ((FP_data_angle[_TempIndex] + 90.00) + ((Real((Integer A))) x 72.00)) degrees

 Set FP_data_missile[((_TempIndex x 5) + (Integer A))] = (Last created unit)

zum Anfang


Der periodische Trigger

Der Haupt Trigger gibt dem Zauber erst seine Funktionalität. Er läuft 32 mal pro Sekunde und lässt die Feuerbälle um den Helden kreisen und einige andere Dinge, die dazu gehören. Wir stellen den Trigger zu Beginn aus, er wird erst eingeschaltet, wenn der erste Zauber gewirkt wird (siehe oben).

Als Ereignis stellen wir also ein, dass der Auslöser 32 mal pro Sekunde läuft, das ist etwa die Zahl bei der die Animation sowohl flüssig ist als auch gut für die Performance ist.

 Fire Protes Periodic

 Events

 Time – Every (1.00 / 32.00) seconds of game time

 Conditions

Im Trigger gehen wir nun mit einer Schleife die einzelnen Instanzen durch, die ja im FP_active Array gespeichert sind. Damit das löschen einfacher
ist, gehen wir die aktiven Instanzen von hinten nach vorne durch. Dazu verwenden wir die Variable TempCount. Wenn IntegerA von 1 bis TempCount läuft,
dann läuft der Ausdruck (TempCount – IntegerA) von Tempcount-1 bis 0, also rückwärts genau wie wir es wollen:

 Actions

 Set TempCount = FP_active_count

 For each (Integer A) from 1 to TempCount, do (Actions)

 Loop – Actions

 Set _TempIndex = FP_active[(TempCount – (Integer A))]

Nun aktualisieren wir die Daten, also erhöhen den Radius und die Geschwindigkeit und drehen die Feuerbälle eins weiter. Da der Trigger 32 mal pro Sekunde läuft, unsere Daten aber für eine Sekunde zählen, teilen wir sie jeweils durch 32.

 Set FP_data_distance[_TempIndex] = (Min((FP_data_distance[_TempIndex] + (25.00 / 32.00)), 250.00))

 Set FP_data_speed[_TempIndex] = (Min((FP_data_angle[_TempIndex] + (90.00 / 36.00)), 180.00))

 Set FP_data_angle[_TempIndex] = (FP_data_angle[_TempIndex] + (FP_data_speed[_TempIndex] / 32.00))

Jetzt kommt eine weitere innere Schleife, in der wir die fünf Feuerbälle bewegen. Am Ende der Schleife wollen wir außerdem wissen, ob alle Feuerbälle tot sind, denn dann ist der Zauber zu Ende. Dafür setzen wir vor der Schleife die Variable „AlleTot“ auf wahr und in der Schleife auf false, sobald wir einen lebenden Feuerball finden.

 Set AlleTot = True

 For each (Integer B) from 0 to 4, do (Actions)

 Loop – Actions

Wir prüfen außerdem, ob der Zauberer den Buff noch hat. Ist dies nicht der Fall können wir die Feuerbälle zerstören. Wenn der Buff noch vorhanden ist und der Feuerball noch lebt wird der Feuerball bewegt. Wie beim ersten Trigger werden wieder die gleichen Winkel-Operationen benötigt.

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 (FP_data_caster[_TempIndex] has buff Fire Protes ) Equal to False

 Then – Actions

 Unit – Kill FP_data_missile[((_TempIndex x 5) + (Integer B))]

 Else – Actions

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 (FP_data_missile[((_TempIndex x 5) + (Integer B))] is alive) Equal to True

 Then – Actions

 Set AlleTot = False

 Unit – Move FP_data_missile[((_TempIndex x 5) + (Integer B))] instantly to ((Position of FP_data_caster[_TempIndex]) offset by FP_data_distance[_TempIndex] towards (FP_data_angle[_TempIndex] + ((Real((Integer B))) x 72.00)) degrees)

 Unit – Make FP_data_missile[((_TempIndex x 5) + (Integer B))] face ((FP_data_angle[_TempIndex] + 90.00) + ((Real((Integer B))) x 72.00)) over 0.00 seconds

Nun überprüfen wir noch, ob der Feuerball mit anderen Einheiten kollidiert. Ist dies der Fall, erstellen wir einen Dummy, der den Feuerblitz auf die getroffene Einheit zaubert. Dann töten wir den Feuerball-Dummy und erstellen noch einen schönen Spezialeffekt.

 Unit Group – Pick every unit in (Units within 100.00 of (Position of FP_data_missile[((_TempIndex x 5) + (Integer B))]) matching ((((Matching unit) belongs to an enemy of (Owner of FP_data_caster[_TempIndex])) Equal to True) and (((Matching unit) is alive) Equal to True))) and do (Actions)

 Loop – Actions

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 ((Picked unit) is Magic Immune) Equal to False

 (FP_data_missile[((_TempIndex x 5) + (Integer B))] is alive) Equal to True

 Then – Actions

 Unit – Create 1 Dummy for (Owner of FP_data_caster[_TempIndex]) at (Position of FP_data_missile[((_TempIndex x 5) + (Integer B))]) facing 0.00 degrees

 Unit – Add Dummy | Fire Protes to (Last created unit)

 Unit – Add a 1.00 second Generic expiration timer to (Last created unit)

 Unit – Order (Last created unit) to Neutral – Firebolt (Picked unit)

 Unit – Kill FP_data_missile[((_TempIndex x 5) + (Integer B))]

 Special Effect – Create a special effect attached to the chest of (Picked unit) using Abilities\Weapons\SteamTank\SteamTankImpact.mdl

 Special Effect – Destroy (Last created special effect)

 Else – Actions

Else – Actions

Nachdem wir nun alle Feuerbälle durchgegangen sind überprüfen wir noch, ob alle Feuerbälle tot sind. Wenn ja beenden wir den Zauber. Dies ist nicht so einfach, da wir sowohl die Nummer des Zaubers wieder freigeben müssen, als auch den Eintrag aus dem Array der aktiven Instanzen entfernen müssen. Das erste Problem ist noch recht einfach, wir hängen den Index einfach hinten an das Array mit den freien Indizes und er ist freigegeben.

Beim zweiten Problem verwenden wir einen kleinen Trick. Wir verschieben einfach den letzten Eintrag im active-Array an die Stelle des zu löschenden Eintrags und überschreiben ihn damit.
Man darf auch nicht durcheinander kommen, mit den verschiedenen Array-Positionen und muss sich genau überlegen, welcher Index zu welchem Array gehört.

 Else – Actions

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 AlleTot Equal to True

 Then – Actions

 Unit – Remove Fire Protes buff from FP_data_caster[_TempIndex]

 ——– Index freigeben ——–

 Set FP_freeIndexes[FP_freeIndexes_count] = _TempIndex

 Set FP_freeIndexes_count = (FP_freeIndexes_count + 1)

 ——– Aus der Liste der aktiven Instanzen entfernen ——–

 Set FP_active_count = (FP_active_count – 1)

 Set FP_active[(TempCount – (Integer A))] = FP_active[FP_active_count]

Zum Abschluss stellen wir noch den Trigger aus wenn es keinen aktiven Zauber mehr gibt, um Rechenpower zu sparen.

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 FP_active_count Equal to 0

 Then – Actions

 Trigger – Turn off (This trigger)

 Else – Actions

 Else – Actions

Damit sieht der gesamte Trigger so aus:

 Fire Protes Periodic

 Events

 Time – Every (1.00 / 32.00) seconds of game time

 Conditions

 Actions

 Set TempCount = FP_active_count

 For each (Integer A) from 1 to TempCount, do (Actions)

 Loop – Actions

 Set _TempIndex = FP_active[(TempCount – (Integer A))]

 Set FP_data_distance[_TempIndex] = (Min((FP_data_distance[_TempIndex] + (25.00 / 32.00)), 250.00))

 Set FP_data_speed[_TempIndex] = (Min((FP_data_angle[_TempIndex] + (90.00 / 36.00)), 180.00))

 Set FP_data_angle[_TempIndex] = (FP_data_angle[_TempIndex] + (FP_data_speed[_TempIndex] / 32.00))

 Set AlleTot = True

 For each (Integer B) from 0 to 4, do (Actions)

 Loop – Actions

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 (FP_data_caster[_TempIndex] has buff Fire Protes ) Equal to False

 Then – Actions

 Unit – Kill FP_data_missile[((_TempIndex x 5) + (Integer B))]

 Else – Actions

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 (FP_data_missile[((_TempIndex x 5) + (Integer B))] is alive) Equal to True

 Then – Actions

 Set AlleTot = False

 Unit – Move FP_data_missile[((_TempIndex x 5) + (Integer B))] instantly to ((Position of FP_data_caster[_TempIndex]) offset by FP_data_distance[_TempIndex] towards (FP_data_angle[_TempIndex] + ((Real((Integer B))) x 72.00)) degrees)

 Unit – Make FP_data_missile[((_TempIndex x 5) + (Integer B))] face ((FP_data_angle[_TempIndex] + 90.00) + ((Real((Integer B))) x 72.00)) over 0.00 seconds

 Unit Group – Pick every unit in (Units within 100.00 of (Position of FP_data_missile[((_TempIndex x 5) + (Integer B))]) matching ((((Matching unit) belongs to an enemy of (Owner of FP_data_caster[_TempIndex])) Equal to True) and (((Matching unit) is alive) Equal to True))) and do (Actions)

 Loop – Actions

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 ((Picked unit) is Magic Immune) Equal to False

 (FP_data_missile[((_TempIndex x 5) + (Integer B))] is alive) Equal to True

 Then – Actions

 Unit – Create 1 Dummy for (Owner of FP_data_caster[_TempIndex]) at (Position of FP_data_missile[((_TempIndex x 5) + (Integer B))]) facing 0.00 degrees

 Unit – Add Dummy | Fire Protes to (Last created unit)

 Unit – Add a 1.00 second Generic expiration timer to (Last created unit)

 Unit – Order (Last created unit) to Neutral – Firebolt (Picked unit)

 Unit – Kill FP_data_missile[((_TempIndex x 5) + (Integer B))]

 Special Effect – Create a special effect attached to the chest of (Picked unit) using Abilities\Weapons\SteamTank\SteamTankImpact.mdl

 Special Effect – Destroy (Last created special effect)

 Else – Actions

 Else – Actions

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 AlleTot Equal to True

 Then – Actions

 Unit – Remove Fire Protes buff from FP_data_caster[_TempIndex]

 ——– Index freigeben ——–

 Set FP_freeIndexes[FP_freeIndexes_count] = _TempIndex

 Set FP_freeIndexes_count = (FP_freeIndexes_count + 1)

 ——– Aus der Liste der aktiven Instanzen entfernen ——–

 Set FP_active_count = (FP_active_count – 1)

 Set FP_active[(TempCount – (Integer A))] = FP_active[FP_active_count]

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 FP_active_count Equal to 0

 Then – Actions

 Trigger – Turn off (This trigger)

 Else – Actions

 Else – Actions

zum Anfang


Schlusswort

Damit ist der Zauber fertiggestellt. Das Prinzip ist recht kompliziert, aber dafür kann man damit sehr viele Zauber umsetzen. In manchen Fällen kann man das ganze auch etwas vereinfachen, indem man auf das active-Array verzichtet. Testet eure Zauber auch gut durch, indem ihr sie mehrmals gleichzeitig laufen lasst – man hat leicht mal eine Variable vertauscht und irgendwas kommt durcheinander.

Noch ein weiterer wichtiger Hinweis: Ich habe bei diesem Tutorial darauf verzichtet, Speicherlecks zu entfernen, da die Auslöser ohne übersichtlicher sind und es leichter fällt sich auf das wesentliche zu konzentrieren. In einer richtigen Map solltet ihr zumindest in den periodisch laufenden Auslösern alle Lecks beseitigen. Mehr dazu gibt es hier:

http://www.mappedia.de/wiki/Speicherleck

Map zum Tutorial herunterladen

zum Anfang

  • 06.04.2009 um 21:46
vJass Spell Tutorial Check gewinnt AvalonOnline Masters

Gui-Spell-Tutorial

Gui-Spell-Tutorial


zum Anfang


Welchen Zauber wollen wir uns denn heute basteln?

Zum Thema Feuer hatte ich mir überlegt, dass es sicherlich cool wäre irgendwie Feuerbälle über den Bildschirm fliegen zu lassen. Da der normale Feuerball Zauber a la Diablo2 imo aber schon eher an Reiz verliert, basteln wir uns etwas tolleres!
Nehmen wir als Model einmal den Bloodmage zur Verfügung. Er verkörpert ja praktisch schon das Feuer. Wenn wir uns den Bloodmage jetzt anschauen, sehen wir, dass um seinen Kopf so Kugeln herumschwirren. Das sieht doch schonmal recht interessant aus – das nehmen wir als Grundidee für unseren Zauber.
Wir lassen also Feuerbälle um unseren Helden kreisen. Damit es nicht ganz so langweilig ist, lassen wir die Feuerbälle nahe am Magier entstehen und mit der Zeit entfernen sich dann diese Feuerbälle vom Magier und umkreisen ihn in einer fixen Maximaldistanz.
Jetzt können wir noch entscheiden, was diese Feuerbälle bewirken. Ich habe mir eine einfache Kollision ausgedacht. Wenn nun also ein Feuerball mit einer gegnerischen Einheit kollidiert, explodiert er und schädigt dabei die Einheit und stunned sie für kurze Zeit. Ein Ziel, Schaden + Stun und Feuer – was würde sich da wohl besser als Dummyzauber eignen als Firebolt? Stimmt! Gar nichts, und deswegen ist Firebolt unser Dummyzauber, der dann von unseren Dummyeinheiten gezaubert wird.


zum Anfang


Die Spellidee steht – was brauchen wir noch?

Richtig! Wir brauchen noch unsere ganzen Objekte, wie Zauber, Dummyzauber, Dummyeinheit, und eine Einheit, die um den Helden kreist (unsere Feuerbälle).
Damit unser Bloodmage nicht lange herumstehen muss um den Effekt des Zaubers zu wirken, nehmen wir als Grundzauber Berserk der Troll Headhunter her. Somit kann man den Zauber auch während dem Laufen aktivieren. Ebenso haben wir einen schönen Buff in der Statusanzeige im Spiel um unsere Gegner zu informieren: „Achtung! Komm mir bloß nicht mehr zu nahe!“
Als Dummyzauber haben wir uns ja schon auf Firebolt geeignet. Kommen wir also zur Dummyeinheit. Am besten dafür geeignet ist meiner Meinung nach der Footman. Er hat keine störenden Nebeneffekt, wie ein Workersymbol oder dass man seine Acquisition Range per Trigger nicht setzen kann, etc.. Als Dummy soll er natürlich nicht anklickbar sein und erhält somit als einzige Fähigkeit Locust (deutsch: Heuschrecke). Weiters besitzt er kein Model, keinen Schatten und seine Animationsgeschwindigkeiten (für bewegen, zauber, angreifen) setzen wir alle auf 0.00, damit er sofort agieren kann.
Ebenso stellen wir seinen Angriff aus, geben ihm als Movementtype Fly, damit er nicht durch ungewünschte Kollisionsberechnungen verschoben wird.

Zu guter letzt entfernen wir noch sein Soundset, stellen seine Foodkosten und Pointvalue auf 0 und reduzieren seine Sichtweite. Eine Namensänderung wäre wohl auch nicht schlecht.
Bleibt uns also noch die Feuerballeinheit. Dazu kopieren wir am besten unseren Dummy und verpassen der so neu erstellten Einheit ein Firebolt Model -> fertig!


zum Anfang


Die GrundIdee

Einen solchen Zauber zu Erstellen ist nicht leicht. Es können 20 Feuerbälle gleichzeitig um 4 verschiedene Einheiten kreisen und es soll immer noch funktionieren, das heisst eine Instanz des Zaubers darf die andere nicht beeinflussen. Mit „Instanz“ ist hier gemeint, dass es zwar der gleiche Zaubertyp ist, aber nicht der selbe Zauber.
Um das zu realisieren benutzen wir folgende Grundidee: Jede Zauber-Instanz bekommt eine eigene Nummer. Alle Daten die der Zauber braucht sind in Arrays unter dieser Nummer gespeichert. Wir nennen diese Technik auch oft „parallele Arrays“.


zum Anfang


Welche Daten müssen wir für eine Instanz speichern?

Dies ist die nächste Frage, die wir uns stellen müssen und ist nicht immer leicht zu entscheiden. Was wir auf jeden Fall brauchen ist die Einheit die den Zauber gezaubert hat. Da sich die Feuerbälle um die Einheit drehen sollen brauchen wir noch eine Variable, die diese Drehung speichert und bei jedem Schritt erhöht und natürlich auch ein Array für die Dummy-Einheiten, die die Feuerbälle darstellen. Wir wollen außerdem, dass der Radius am Anfang sehr klein ist und sich langsam vergrößert, also benötigen wir für diesen auch noch eine Variable. Hinzu kommt noch die Geschwindigkeit da die Drehung langsam beginnen soll und mit jedem Schritt einen Tick schneller wird.

Wir werden allen Variablen, die wir für diesen Zauber verwenden die Vorsilbe FP_ geben, damit die Übersicht unter den Variablen nicht verloren geht, wenn noch mehr dazu kommen. Den Variablen, die die Daten einer Zauber-Instanz enthalten bekommen zusätzlich noch ein data_ vorne angehängt, was Ebenfalls der Übersicht dienen soll. Wir haben also für die Daten folgende Variablen:

Name

Typ

Kommentar

FP_data_angle

Real Array

Drehung
FP_data_caster Unit Array Einheit die den Zauber gezaubert hat

FP_data_distance

Real Array Radius
FP_data_missile Unit Array Dummy-Einheiten, die die Feuerbälle darstellen (da es 5 Einheiten pro Instanz sind verwenden wir dieses Array als 2D-Array, dazu später mehr)

FP_data_speed Real Array Geschwindigkeit


zum Anfang


Der komplizierte und wichtigste Teil

Wie oben schon erwähnt wird jede Instanz ihre eigene Nummer bekommen. Das werden wir folgendermaßen realisieren: In einem Array liegen alle nicht benutzten Nummern. Eine Integer-Variable speichert wie viele freie Nummern im Array sind. Eine weitere Variable speichert, wie viele Nummern bisher vergeben wurden.

Wenn wir nun eine Nummer für unsere Zauber-Instanz wollen gibt es zwei Möglichkeiten. Entweder ist eine freie Nummer im Array, dann nehmen wir die oberste Nummer. Wenn nicht gucken wir wie viele Nummern schon vergeben wurden und nehmen diese Zahl. Wenn also die 0, 1, 2 und 3 schon vergeben wären würden wir nun die 4 bekommen, da schon 3 Nummern vergeben wurden. In Triggern ausgedrückt sieht das so aus:

Variablen:

Name

Typ
Kommentar

FP_freeIndexes Integer Array In diesem Array werden die unbenutzten, freien Nummern gespeichert.
FP_freeIndexes_count

Integer

Speichert, wie viele Nummern im Array FP_freeIndexes gespeichert sind.

FP_Index_count

Integer Speichert, wie viele Nummern bereits vergeben wurden.

_TempIndex

Integer Dies ist eine temporäre Variable. Wir verwenden sie einfach dazu, um den Index, mit dem wir Momentan arbeiten, zwischen zu speichern.

 Actions

 ——– Einen Array-Index für die Spell-Instanz finden ——–

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 FP_freeIndexes_count Equal to 0

 Then – Actions

 ——– Kein freier Index im Array -> neuen Index nehmen ——–

 Set _TempIndex = FP_Index_count

 Set FP_Index_count = (FP_Index_count + 1)

 Else – Actions

 ——– Ein unbenutzer Index liegt im Array -> obersten Index benutzen ——–

 Set FP_freeIndexes_count = (FP_freeIndexes_count – 1)

 Set _TempIndex = FP_freeIndexes[FP_freeIndexes_count]

Nun ist die Nummer in der Variable _TempIndex gespeichert.

Als nächstes kommt der zweite Teil der Verwaltung. Um den zweiten Auslöser, der 32 mal pro Sekunde laufen wird möglichst Effizient gestalten zu können benutzen wir ein Array, in dem die Nummern aller Instanzen stehen, die zur Zeit aktiv sind. Wir hängen unsere neue Nummer nun einfach hinten an das Array an.
Ist unsere Instanz die einzige aktive Instanz müssen wir den zweiten Auslöser noch einschalten, da dieser dann noch ausgeschaltet ist. Das ganze sollte dann so aussehen:

Variablen:

Name
Typ

Kommentar
FP_active Integer Array Speichert alle aktiven Instanz-Nummern.
FP_active_count Integer Speichert, wie viele aktive Instanzen es gibt.

 ——– In das Array der aktiven Instanzen eintragen: ——–

 Set FP_active[FP_active_count] = _TempIndex

 Set FP_active_count = (FP_active_count + 1)

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 FP_active_count Equal to 1

 Then – Actions

 Trigger – Turn on Fire Protes Periodic <gen>

 Else – Actions

Fire Protes Periodic ist hier einfach ein neuer Auslöser, den wir schon einmal erstellen aber erst später mit Aktionen füllen werden.

Der folgende Teil ist nun recht einfach, es werden einfach alle Startwerte eingestellt. Ihr könnt mit diesen Startwerten auch rumspielen und gucken, was sich verändert. Das einzig komplizierte ist das erstellen der Dummys. Hier muss man ein bisschen mit Winkeln umgehen können (siehe Mathe-Buch) und die Funktion Point with Polar Offset verstehen. Ein Kreis hat 360 Grad, also erstellen wir jeden Dummy jeweils 360/5 = 72 Grad weiter versetzt.

Außerdem muss man hier beachten, dass es pro Instanz fünf Feuerbälle gibt, also müssen wir das Array für die Dummys etwas strecken, damit alle 5 rein passen. Als Index nehmen wir also nicht wie sonst _TempIndex sondern _TempIndex*5 + IntegerA (mehr dazu hier: Matrizen (2D-Arrays) ).

 ——– Startwerte setzen: ——–

 Set FP_data_angle[_TempIndex] = (Facing of (Triggering unit))

 Set FP_data_caster[_TempIndex] = (Triggering unit)

 Set FP_data_distance[_TempIndex] = 25.00

 Set FP_data_speed[_TempIndex] = 90.00

 For each (Integer A) from 0 to 4, do (Actions)

 Loop – Actions

 Unit – Create 1 Missile | Fire Protes for (Owner of (Triggering unit)) at ((Position of (Triggering unit)) offset by FP_data_distance[((_TempIndex x 5) + (Integer A))] towards (FP_data_angle[_TempIndex] + ((Real((Integer A))) x 72.00)) degrees) facing ((FP_data_angle[_TempIndex] + 90.00) + ((Real((Integer A))) x 72.00)) degrees

 Set FP_data_missile[((_TempIndex x 5) + (Integer A))] = (Last created unit)

Damit ist der erste Trigger abgeschlossen, fehlt nur noch das Ereignis und die Bedingungen. Der komplette Trigger sieht dann so aus:

 Fire Protes Start

 Events

 Unit – A unit Starts the effect of an ability

 Conditions

 (Ability being cast) Equal to Fire Protes

 ((Triggering unit) has buff Fire Protes ) Equal to False

 Actions

 ——– Einen Array-Index für die Spell-Instanz finden ——–

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 FP_freeIndexes_count Equal to 0

 Then – Actions

 ——– Kein freier Index im Array -> neuen Index nehmen ——–

 Set _TempIndex = FP_Index_count

 Set FP_Index_count = (FP_Index_count + 1)

 Else – Actions

 ——– Ein unbenutzer Index liegt im Array -> obersten Index benutzen ——–

 Set FP_freeIndexes_count = (FP_freeIndexes_count – 1)

 Set _TempIndex = FP_freeIndexes[FP_freeIndexes_count]

 ——– In das Array der aktiven Instanzen eintragen: ——–

 Set FP_active[FP_active_count] = _TempIndex

 Set FP_active_count = (FP_active_count + 1)

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 FP_active_count Equal to 1

 Then – Actions

 Trigger – Turn on Fire Protes Periodic <gen>

 Else – Actions

 ——– Startwerte setzen: ——–

 Set FP_data_angle[_TempIndex] = (Facing of (Triggering unit))

 Set FP_data_caster[_TempIndex] = (Triggering unit)

 Set FP_data_distance[_TempIndex] = 25.00

 Set FP_data_speed[_TempIndex] = 90.00

 For each (Integer A) from 0 to 4, do (Actions)

 Loop – Actions

 Unit – Create 1 Missile | Fire Protes for (Owner of (Triggering unit)) at ((Position of (Triggering unit)) offset by FP_data_distance[((_TempIndex x 5) + (Integer A))] towards (FP_data_angle[_TempIndex] + ((Real((Integer A))) x 72.00)) degrees) facing ((FP_data_angle[_TempIndex] + 90.00) + ((Real((Integer A))) x 72.00)) degrees

 Set FP_data_missile[((_TempIndex x 5) + (Integer A))] = (Last created unit)

zum Anfang


Der periodische Trigger

Der Haupt Trigger gibt dem Zauber erst seine Funktionalität. Er läuft 32 mal pro Sekunde und lässt die Feuerbälle um den Helden kreisen und einige andere Dinge, die dazu gehören. Wir stellen den Trigger zu Beginn aus, er wird erst eingeschaltet, wenn der erste Zauber gewirkt wird (siehe oben).

Als Ereignis stellen wir also ein, dass der Auslöser 32 mal pro Sekunde läuft, das ist etwa die Zahl bei der die Animation sowohl flüssig ist als auch gut für die Performance ist.

 Fire Protes Periodic

 Events

 Time – Every (1.00 / 32.00) seconds of game time

 Conditions

Im Trigger gehen wir nun mit einer Schleife die einzelnen Instanzen durch, die ja im FP_active Array gespeichert sind. Damit das löschen einfacher
ist, gehen wir die aktiven Instanzen von hinten nach vorne durch. Dazu verwenden wir die Variable TempCount. Wenn IntegerA von 1 bis TempCount läuft,
dann läuft der Ausdruck (TempCount – IntegerA) von Tempcount-1 bis 0, also rückwärts genau wie wir es wollen:

 Actions

 Set TempCount = FP_active_count

 For each (Integer A) from 1 to TempCount, do (Actions)

 Loop – Actions

 Set _TempIndex = FP_active[(TempCount – (Integer A))]

Nun aktualisieren wir die Daten, also erhöhen den Radius und die Geschwindigkeit und drehen die Feuerbälle eins weiter. Da der Trigger 32 mal pro Sekunde läuft, unsere Daten aber für eine Sekunde zählen, teilen wir sie jeweils durch 32.

 Set FP_data_distance[_TempIndex] = (Min((FP_data_distance[_TempIndex] + (25.00 / 32.00)), 250.00))

 Set FP_data_speed[_TempIndex] = (Min((FP_data_angle[_TempIndex] + (90.00 / 36.00)), 180.00))

 Set FP_data_angle[_TempIndex] = (FP_data_angle[_TempIndex] + (FP_data_speed[_TempIndex] / 32.00))

Jetzt kommt eine weitere innere Schleife, in der wir die fünf Feuerbälle bewegen. Am Ende der Schleife wollen wir außerdem wissen, ob alle Feuerbälle tot sind, denn dann ist der Zauber zu Ende. Dafür setzen wir vor der Schleife die Variable „AlleTot“ auf wahr und in der Schleife auf false, sobald wir einen lebenden Feuerball finden.

 Set AlleTot = True

 For each (Integer B) from 0 to 4, do (Actions)

 Loop – Actions

Wir prüfen außerdem, ob der Zauberer den Buff noch hat. Ist dies nicht der Fall können wir die Feuerbälle zerstören. Wenn der Buff noch vorhanden ist und der Feuerball noch lebt wird der Feuerball bewegt. Wie beim ersten Trigger werden wieder die gleichen Winkel-Operationen benötigt.

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 (FP_data_caster[_TempIndex] has buff Fire Protes ) Equal to False

 Then – Actions

 Unit – Kill FP_data_missile[((_TempIndex x 5) + (Integer B))]

 Else – Actions

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 (FP_data_missile[((_TempIndex x 5) + (Integer B))] is alive) Equal to True

 Then – Actions

 Set AlleTot = False

 Unit – Move FP_data_missile[((_TempIndex x 5) + (Integer B))] instantly to ((Position of FP_data_caster[_TempIndex]) offset by FP_data_distance[_TempIndex] towards (FP_data_angle[_TempIndex] + ((Real((Integer B))) x 72.00)) degrees)

 Unit – Make FP_data_missile[((_TempIndex x 5) + (Integer B))] face ((FP_data_angle[_TempIndex] + 90.00) + ((Real((Integer B))) x 72.00)) over 0.00 seconds

Nun überprüfen wir noch, ob der Feuerball mit anderen Einheiten kollidiert. Ist dies der Fall, erstellen wir einen Dummy, der den Feuerblitz auf die getroffene Einheit zaubert. Dann töten wir den Feuerball-Dummy und erstellen noch einen schönen Spezialeffekt.

 Unit Group – Pick every unit in (Units within 100.00 of (Position of FP_data_missile[((_TempIndex x 5) + (Integer B))]) matching ((((Matching unit) belongs to an enemy of (Owner of FP_data_caster[_TempIndex])) Equal to True) and (((Matching unit) is alive) Equal to True))) and do (Actions)

 Loop – Actions

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 ((Picked unit) is Magic Immune) Equal to False

 (FP_data_missile[((_TempIndex x 5) + (Integer B))] is alive) Equal to True

 Then – Actions

 Unit – Create 1 Dummy for (Owner of FP_data_caster[_TempIndex]) at (Position of FP_data_missile[((_TempIndex x 5) + (Integer B))]) facing 0.00 degrees

 Unit – Add Dummy | Fire Protes to (Last created unit)

 Unit – Add a 1.00 second Generic expiration timer to (Last created unit)

 Unit – Order (Last created unit) to Neutral – Firebolt (Picked unit)

 Unit – Kill FP_data_missile[((_TempIndex x 5) + (Integer B))]

 Special Effect – Create a special effect attached to the chest of (Picked unit) using Abilities\Weapons\SteamTank\SteamTankImpact.mdl

 Special Effect – Destroy (Last created special effect)

 Else – Actions

Else – Actions

Nachdem wir nun alle Feuerbälle durchgegangen sind überprüfen wir noch, ob alle Feuerbälle tot sind. Wenn ja beenden wir den Zauber. Dies ist nicht so einfach, da wir sowohl die Nummer des Zaubers wieder freigeben müssen, als auch den Eintrag aus dem Array der aktiven Instanzen entfernen müssen. Das erste Problem ist noch recht einfach, wir hängen den Index einfach hinten an das Array mit den freien Indizes und er ist freigegeben.

Beim zweiten Problem verwenden wir einen kleinen Trick. Wir verschieben einfach den letzten Eintrag im active-Array an die Stelle des zu löschenden Eintrags und überschreiben ihn damit.
Man darf auch nicht durcheinander kommen, mit den verschiedenen Array-Positionen und muss sich genau überlegen, welcher Index zu welchem Array gehört.

 Else – Actions

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 AlleTot Equal to True

 Then – Actions

 Unit – Remove Fire Protes buff from FP_data_caster[_TempIndex]

 ——– Index freigeben ——–

 Set FP_freeIndexes[FP_freeIndexes_count] = _TempIndex

 Set FP_freeIndexes_count = (FP_freeIndexes_count + 1)

 ——– Aus der Liste der aktiven Instanzen entfernen ——–

 Set FP_active_count = (FP_active_count – 1)

 Set FP_active[(TempCount – (Integer A))] = FP_active[FP_active_count]

Zum Abschluss stellen wir noch den Trigger aus wenn es keinen aktiven Zauber mehr gibt, um Rechenpower zu sparen.

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 FP_active_count Equal to 0

 Then – Actions

 Trigger – Turn off (This trigger)

 Else – Actions

 Else – Actions

Damit sieht der gesamte Trigger so aus:

 Fire Protes Periodic

 Events

 Time – Every (1.00 / 32.00) seconds of game time

 Conditions

 Actions

 Set TempCount = FP_active_count

 For each (Integer A) from 1 to TempCount, do (Actions)

 Loop – Actions

 Set _TempIndex = FP_active[(TempCount – (Integer A))]

 Set FP_data_distance[_TempIndex] = (Min((FP_data_distance[_TempIndex] + (25.00 / 32.00)), 250.00))

 Set FP_data_speed[_TempIndex] = (Min((FP_data_angle[_TempIndex] + (90.00 / 36.00)), 180.00))

 Set FP_data_angle[_TempIndex] = (FP_data_angle[_TempIndex] + (FP_data_speed[_TempIndex] / 32.00))

 Set AlleTot = True

 For each (Integer B) from 0 to 4, do (Actions)

 Loop – Actions

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 (FP_data_caster[_TempIndex] has buff Fire Protes ) Equal to False

 Then – Actions

 Unit – Kill FP_data_missile[((_TempIndex x 5) + (Integer B))]

 Else – Actions

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 (FP_data_missile[((_TempIndex x 5) + (Integer B))] is alive) Equal to True

 Then – Actions

 Set AlleTot = False

 Unit – Move FP_data_missile[((_TempIndex x 5) + (Integer B))] instantly to ((Position of FP_data_caster[_TempIndex]) offset by FP_data_distance[_TempIndex] towards (FP_data_angle[_TempIndex] + ((Real((Integer B))) x 72.00)) degrees)

 Unit – Make FP_data_missile[((_TempIndex x 5) + (Integer B))] face ((FP_data_angle[_TempIndex] + 90.00) + ((Real((Integer B))) x 72.00)) over 0.00 seconds

 Unit Group – Pick every unit in (Units within 100.00 of (Position of FP_data_missile[((_TempIndex x 5) + (Integer B))]) matching ((((Matching unit) belongs to an enemy of (Owner of FP_data_caster[_TempIndex])) Equal to True) and (((Matching unit) is alive) Equal to True))) and do (Actions)

 Loop – Actions

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 ((Picked unit) is Magic Immune) Equal to False

 (FP_data_missile[((_TempIndex x 5) + (Integer B))] is alive) Equal to True

 Then – Actions

 Unit – Create 1 Dummy for (Owner of FP_data_caster[_TempIndex]) at (Position of FP_data_missile[((_TempIndex x 5) + (Integer B))]) facing 0.00 degrees

 Unit – Add Dummy | Fire Protes to (Last created unit)

 Unit – Add a 1.00 second Generic expiration timer to (Last created unit)

 Unit – Order (Last created unit) to Neutral – Firebolt (Picked unit)

 Unit – Kill FP_data_missile[((_TempIndex x 5) + (Integer B))]

 Special Effect – Create a special effect attached to the chest of (Picked unit) using Abilities\Weapons\SteamTank\SteamTankImpact.mdl

 Special Effect – Destroy (Last created special effect)

 Else – Actions

 Else – Actions

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 AlleTot Equal to True

 Then – Actions

 Unit – Remove Fire Protes buff from FP_data_caster[_TempIndex]

 ——– Index freigeben ——–

 Set FP_freeIndexes[FP_freeIndexes_count] = _TempIndex

 Set FP_freeIndexes_count = (FP_freeIndexes_count + 1)

 ——– Aus der Liste der aktiven Instanzen entfernen ——–

 Set FP_active_count = (FP_active_count – 1)

 Set FP_active[(TempCount – (Integer A))] = FP_active[FP_active_count]

 If (All Conditions are True) then do (Then Actions) else do (Else Actions)

 If – Conditions

 FP_active_count Equal to 0

 Then – Actions

 Trigger – Turn off (This trigger)

 Else – Actions

 Else – Actions

zum Anfang


Schlusswort

Damit ist der Zauber fertiggestellt. Das Prinzip ist recht kompliziert, aber dafür kann man damit sehr viele Zauber umsetzen. In manchen Fällen kann man das ganze auch etwas vereinfachen, indem man auf das active-Array verzichtet. Testet eure Zauber auch gut durch, indem ihr sie mehrmals gleichzeitig laufen lasst – man hat leicht mal eine Variable vertauscht und irgendwas kommt durcheinander.

Noch ein weiterer wichtiger Hinweis: Ich habe bei diesem Tutorial darauf verzichtet, Speicherlecks zu entfernen, da die Auslöser ohne übersichtlicher sind und es leichter fällt sich auf das wesentliche zu konzentrieren. In einer richtigen Map solltet ihr zumindest in den periodisch laufenden Auslösern alle Lecks beseitigen. Mehr dazu gibt es hier:

http://www.mappedia.de/wiki/Speicherleck

Map zum Tutorial herunterladen

zum Anfang

  • 06.04.2009 um 19:46
Check gewinnt AvalonOnline Masters Germany's next Funmap Abend
ingame Netzwerk
Call of Duty | Counter-Strike: Global Offensive | Diablo 3 | Dota 2 | League of Legends | Quake 3 | Heroes of the Storm | Unreal Tournament | Overwatch | Starcraft 2 | Torchlight 2 | Warcraft 3 | World of Warcraft | Hearthstone | Kino, TV, Film und Promis | Spiele Datenbank

Support | | AGB |
Online Werbung | Mediadaten | Unternehmen | Karriere | Impressum

© inwave media GmbH, ingame™ ist ein eingetragenes Markenzeichen der inwave media GmbH. Verwendung von Inhalten nur mit schriftlicher Genehmigung.

Design und Umsetzung: Jan Wagner Design