Zeige Ergebnis 1 bis 10 von 10
  1. #1

    Code-Einführungen

    Aktualisierungen
    • 2009-06-30: Kleinen Hinweis bezüglich "wichtiger" Informationen hinzugefügt.
    • 2009-06-10: Einführung in Gespräche hinzugefügt.


    Hallo,
    da mir gerade langweilig war und die Uhrzeit es nicht gerade zulässt, sich mit Bekannten zumindest auf elektronischem Wege zu unterhalten, habe ich mich mal hingesetzt und eine kleine Einführung in die ASL (Advanced Script Library, das sind die Systeme, die wir bei Die Macht des Feuers verwenden) bzw. in das dazugehörige Quest-System zu schreiben.
    Ich gedenke, eventuell noch mehr dieser Einführungen (zum Beispiel für das Gesprächesystem) zu schreiben, falls es entweder Sinn macht oder mir mal wieder langweilig ist.
    Ich könnte hierfür auch meinen ASL-Thread "missbrauchen", aber ich denke, dass ich eventuell auch noch einiges speziell über Die Macht des Feuers ergänzen werde.
    Ansonsten könnte ihr mich ja dafür lünchen.
    Es gilt vielleicht noch anzumerken, dass ich den Code in der Form, in welcher er hier von mir geschrieben wurde nicht getestet habe. Allerdings habe ich das Beispiel-Quest mit Quests aus Die Macht des Feuers verglichen und soweit müsste eigentlich alles stimmen. Wenn jemand einen Rechtschreib- oder Grammatikfehler entdeckt, bitte sofort melden und natürlich darf auch jeder schreiben, der was zum System zu sagen hat. Wer die Funktionen der ASL nachschlagen möchte, muss sich noch ein Weilchen gedulden, bis ich die aktuellste API-Dokumentation und den neusten Code auf der Projektseite hochgeladen habe.

    Gespräche
    Um ein Rollenspiel lebendiger zu gestalten, setzen Entwickler oft auf sogenannte Dialoge. Ein sehr gutes
    Beispiel dafür ist die Gothic-Reihe, in welcher man als Spieler stets einen gewissen Grad an Freiheit
    genießt, wählen zu können, was man zu anderen Nichtspielercharakteren sagt. Natürlich sind wir damit noch
    Lichtjahre von einer künstlichen Intelligenz entfernt, aber es gibt den computergesteurten Charakteren einen
    ganz eigenen Charakter und eine eigene Geschichte.
    Diese Möglichkeit wollte ich ebenfalls in Die Macht des Feuers und so implementierte ich in der ASL eine Art
    Gesprächssystem, welches sich sehr an dem von Gothic 2 orientiert.
    Insgesamt braucht man lediglich zwei oder bessergesagt sogar nur eine Struktur zu kennen, um es benutzen zu
    können. ATalk und AInfo bilden zusammen das gesamte System und interagieren natürlich auch mit der Struktur
    ACharacter, da sie selbst ebenfalls Bestandteil des Charaktersystems sind.
    ATalk stellt ein Gespräch mit genau einer Einheit bzw. einem Nichtspielercharakter dar. Ich habe mich bis
    jetzt darauf beschränkt, dass sich immer nur einen Charakter mit einem Nichtspielercharakter unterhalten
    kann. Natürlich wäre es auch möglich das System so zu erweitern, sodass er mit mehreren Charakteren
    gleichzeitig sprechen kann, allerdings finde ich, dass dies während dem Spiel ein wenig unrealistisch wirkt.
    Andererseits muss ich mir auch eingestehen, dass es etwas nervig seien könnte, wenn die Anzahl der Spieler
    eher groß und die Anzahl der Nichtspielercharaktere eher klein gehalten werden würden.
    Wie dem auch sei, beginnen wir einfach direkt mit der Initialisierung der beiden Strukturen:

    Code:
    library MyTalkLibrary initializer initFunction requires Asl
    
    	private function initFunction takes nothing returns nothing
    		call ATalk.init("smart", 300.0, "Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl", "Ziel unterhält sich bereits.", "Ende", "Zurück")
    		call AInfo.init(gg_cam_talk, KEY_ESCAPE, 1.0, "speech", "listen")
    	endfunction
    
    endlibrary
    Das erste Argument des Aufrufs von ATalk.init legt den Befehl fest, welcher mit einem Nichtspielercharakter
    als Ziel ausgeführt werden muss, damit das Gespräch beginnt. In diesem Fall handelt es sich um einen
    einfachen Rechtsklick, mit welchem man den Charakter auf den Nichtspielercharakter zu schickt. Das zweite
    Argument gibt die Mindestreichweite für den festgelegten Befehl an, damit der Charakter sich nicht mit dem
    Ziel unterhält, wenn er an einem ganzen anderen Ort steht. Das dritte Argument legt den Spezialeffekt fest,
    welcher auf Nichtspielercharakteren mit einem Gespräch erzeugt werden soll. Ist dieser Wert null, so wird
    kein Effekt verwendet.
    Das vierte Argument legt die Fehlermeldung fest, welche erscheint, wenn der Nichtspielercharakter bereits in
    ein Gespräch verwickelt ist und die letzten beiden Argumente legen die Texte für die beiden immer wieder
    verwendeten Buttons "Ende" und "Zurück" fest.
    Das erste Argument des Aufrufs von AInfo.init legt das verwendete Kameraobjekt für Gespräche fest. Das
    zweite Argument legt die Überspringtaste für Texte fest. Ist dieser Wert -1, so ist es den Spielern nicht
    möglich, Texte zu überspringen. Das dritte Argument legt den Zeitintervall in Sekunden fest, in welchem ein
    mögliches Überspringen durch den Spieler überprüft werden soll. Das bedeutet, dass der erschiene Text und
    der abgespielte Ton nicht unbedingt unmittelbar nach der Betätigung der Escape-Taste ausgeblendet werden.
    Die beiden letzten Argumente legen die Animationen für die Einheiten fest, die während eines Gesprächs
    abgespielt werden sollen.
    Leider gibt es kaum Modelle, die solche Animationen besitzen. Normalerweise besitzt nur das Portrait-Modell
    einer Einheit eine Sprechanimation. Sind diese beiden Werte null, so werden keine Animationen abgespielt.

    Nun möchte ich aber noch darauf eingehen, was denn nun eigentlich AInfo macht. AInfo stellt praktisch eine
    Information dar. Man kann sie sich auch als einfachen Button vorstellen, der eine Aktion auslöst.
    Allerdings bietet sie noch weitere Möglichkeiten. So kann eine Information zum Beispiel dauerhaft sein, was
    bedeutet, dass sie immer wieder zur Auswahl steht (insofern die Bedingung true liefert). Dazu aber später
    mehr. Schreiben wir zunächst einmal noch etwas mehr Code, in welchem wir ein kleines Beispielgespräch mit
    dem Nichtspielercharakter Peter definieren. Dazu verwende ich mal wieder meine geliebte Vererbung, auch wenn
    man es diesmal tatsächlich mit statischen Elementen umsetzen könnte, da ein Gespräch normalerweise pro Karte
    einzigartig sein sollte:

    Code:
    library MyTalkLibrary initializer initFunction requires Asl
    
    	private function initFunction takes nothing returns nothing
    		call ATalk.init("smart", 300.0, "Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl", "Ziel unterhält sich bereits.", "Ende", "Zurück")
    		call AInfo.init(gg_cam_talk, KEY_ESCAPE, 1.0, "speech", "listen")
    	endfunction
    	
    	private function startPageAction takes ATalk talk returns nothing
    		call talk.showInfo(0)
    		call talk.showInfo(1)
    		call talk.show()
    	endfunction
    	
    	private function infoAction0 takes AInfo info returns nothing
    		call speech(info, false, "Hallo.", null)
    		call speech(info, true, "Hallo. Wer bist du?", null)
    		call speech(info, false, "Ich bin der singende, tanzende Abschaum der Welt.", null)
    		call info.talk().showStartPage()
    	endfunction
    
    	struct TalkPeter extends ATalk
    	
    		public static method create takes nothing returns TalkPeter
    			local TalkPeter this = TalkPeter.create(gg_unit_peter, startPageAction)
    			call this.addInfo(true, false, 0, infoAction0, "Hallo.")
    			call this.addExitButton()
    			return this
    		endmethod
    	endstruct
    
    endlibrary
    Wie man sieht, erzeugen wir zunächst im Konstruktor unsere Instanz eines von ATalk geerbten Objekts. Dabei
    werden im Konstruktor die verwendete Einheit und die verwende Startseitenfunktion aufgerufen. Die
    Startseitenfunktion wird immer dann aufgerufen, wenn ein Charakter ein Gespräch mit dem
    Nichtspielercharakter beginnt und erhält das Gespräch als Parameter.
    Normalerweise ruft man in ihr mittels showInfo und dem zugehörigen Index alle AInfo-Instanzen des Gesprächs
    auf, welche als Button eingezeigt werden sollen, natürlich nur wenn ihre Bedingung 0 ist oder true liefert.
    Außerdem zeigen wir nach diesen Aufrufen noch den gesamten Gesprächs-Warcraft-Dialog mittels der Methode
    show an.
    Als nächstes Fügen wir ein AInfo-Objekt hinzu. Dazu bietet ATalk eine Art Kopie des Konstruktors eines
    AInfo-Objektes an, welches das erzeugte AInfo automatisch dem Gespräch zuordnet und dessen internen Index
    zurück gibt.
    Das erste Argument gibt an, dass die Information dauerhaft angezeigt werden soll, also nicht verschwindet
    nachdem der Spieler sie einmal ausgeführt hat. Der zweite Parameter gibt an, dass sie nicht "wichtig" ist.
    Das bedeutet, dass sie nicht beim Öffnen des Dialogs automatisch ausgeführt, ohne dass der Spieler
    irgendeinen Button drücken muss. Als nächstes folgt das Argument für die Bedingung, welches bei uns 0 ist,
    da wir keine benötigen, dann das Argument für die Aktion.
    Sowohl die Bedingungs- als auch die Aktions-Funktion erhalten die AInfo-Instanz als Parameter, über welche
    man natürlich auch direkt auf die ATalk-Instanz zugreifen kann. An unserer Beispielfunktion infoAction0 kann
    man gut erkennen, wie die meisten AInfo-Funktionen aussehen, wenn es sich um ein einfaches Gespräch handelt.
    Mittels der globalen Funktio speech (Wäre es eine statische Methode, so würde die TriggerSleepAction in
    unserer Funktion ignoriert werden, da sie vermutlich in einem neuen Thread gestartet werden würde.
    Normalerweise geschieht das nur, wenn man eine Methode noch vor ihrer Definition aufruft, was eigentlich
    nicht der Fall sein sollte. Trotzdem hat es bei mir in der Praxis nicht geklappt. Ich habe schon Vexorian
    davon berichtet, mal schauen was passieren wird) kann man einen Text, alternativ auch mit Ton, à la Gothic
    anzeigen lassen. Das erste Argument muss die Info-Instanz seien (diese würde bei einer Methode wegfallen),
    das zweite Argument legt fest, ob der Text vom Nichtspielercharakter zum Spielercharakter gesagt wird oder
    eben andersherum. Das dritte Argument legt den angezeigten Untertitel fest und das vierte Argument das
    abgespielte Tonobjekt. Ist das letzte Argument null, so wird normalerweise 5 Sekunden gewartet, ansonsten
    eben die Länge des Tonobjekts. Erinnern wir uns hierbei kurz an unserer Initialisierung der Struktur AInfo.
    Dabei haben eine Übersprungstaste und eine Übersprungsüberprüfungsrate festgelegt. Drückt der Spieler also
    bei einem dieser speech-Aufrufe die Taste Escape, so sollte diese spätestens nach ca. einer Sekunde
    übersprungen werden. Zu guter Letzt rufen wir dann noch die Methode showStartPage der ATalk-Instanz auf, mit
    welcher der Dialog zunächst geleert und danach die startPageAction-Funktion aufgerufen wird.
    Da das gesamte System eigentlich besonders kompliziert sein sollte, werde ich auf eine große Menge an
    weiteren Code-Beispielen verzichten. Ein weiteres sollte eigentlich reichen:

    Code:
    library MyTalkLibrary initializer initFunction requires Asl
    
    	private function initFunction takes nothing returns nothing
    		call ATalk.init("smart", 300.0, "Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl", "Ziel unterhält sich bereits.", "Ende", "Zurück")
    		call AInfo.init(gg_cam_talk, KEY_ESCAPE, 1.0, "speech", "listen")
    	endfunction
    	
    	private function startPageAction takes ATalk talk returns nothing
    		call talk.showInfo(0)
    		call talk.showInfo(1)
    		call talk.showInfo(2)
    		call talk.show()
    	endfunction
    	
    	private function infoCondition0 takes AInfo info returns boolean
    		local unit characterUnit = info.talk().character().unit()
    		local boolean result = GetUnitTypeId(characterUnit) == 'Hpal'
    		set characterUnit = null
    		return result
    	endfunction
    	
    	private function infoAction0 takes AInfo info returns nothing
    		call speech(info, true, "Du verdammter Paladin, verpiss dich bloß!", null)
    		call info.talk.disable()
    	endfunction
    	
    	private function infoCondition1 takes AInfo info returns boolean
    		local unit characterUnit = info.talk().character().unit()
    		local boolean result = GetUnitTypeId(characterUnit) != 'Hpal'
    		set characterUnit = null
    		return result
    	endfunction
    	
    	private function infoAction1 takes AInfo info returns nothing
    		call speech(info, false, "Hallo.", null)
    		call speech(info, true, "Hallo. Wer bist du?", null)
    		call speech(info, false, "Ich bin der singende, tanzende Abschaum der Welt.", null)
    		call info.talk().showInfo(3)
    		call info.talk().showInfo(4)
    		call info.talk().showInfo(5)
    		call info.talk().show()
    	endfunction
    	
    	private function infoAction0_0 takes AInfo info returns nothing
    		call speech(info, false, "Und, wie läuft's so?", null)
    		call speech(info, true, "Nerv nich!", null)
    		call info.talk().showStartPage()
    	endfunction
    	
    	private function infoAction0_1 takes AInfo info returns nothing
    		call speech(info, false, "Nerv nich!", null)
    		call speech(info, true, "Und, wie läuft's so?", null)
    		call info.talk().showStartPage()
    	endfunction
    
    	struct TalkPeter extends ATalk
    	
    		public static method create takes nothing returns TalkPeter
    			local TalkPeter this = TalkPeter.create(gg_unit_peter, startPageAction)
    			call this.addInfo(false, true, infoCondition0, infoAction0, null) //0
    			call this.addInfo(true, false, infoCondition1, infoAction1, "Hallo.") //1
    			call this.addExitButton() //2
    			
    			call this.addInfo(true, false, 0, infoAction0_0, "Und, wie läuft's so?") //3
    			call this.addInfo(true, false, 0, infoAction0_1, "Nerv nich!") //4
    			call this.addBackToStartPageButton() //5
    			return this
    		endmethod
    	endstruct
    
    endlibrary
    Bei größeren Gesprächen kann es schon einmal zu Verwirrung bezüglich der Indexe kommen, allerdings ist dies
    die schnellste Methode auf AInfo-Instanzen zuzugreifen. Natürlich kann man die Infos auch mit ihrem
    Konstruktor erzeugen und in Elemente der Struktur speichern, jedoch würde das kaum einen Unterschied machen.
    Zur Hilfe habe ich hinter jeden addInfo-Aufruf den dazugehörigen Index geschrieben.
    Die Methode addBackToStartPageButton fügt einen Zurück-Button hinzu, welcher immer die Funktion
    startPageAction aufruft.
    Die Instanzen mit den Indexen 3 und 4 werden nicht auf der Startseite, sondern erst nachdem man "Hallo."
    gesagt hat angezeigt.
    Die "wichtige" Information erscheint, wenn der Charakter ein Paladin ist. Daraufhin liefert auch die
    Bedingung von "Hallo." immer false, wodurch ein Paladin sich einmal eine Beleidigung anhören muss und sich
    dann nicht mehr mit dem Nichtspielercharakter unterhalten kann.

    Es gilt vielleicht noch anzumerken, dass es mit "wichtigen" Informationen nicht ganz so einfach ist, wie es in dieser Einführung beschrieben wird. Das musste ich zumindest bei einigen Tests von DMdF feststellen. Man muss manuell überprüfen, ob die wichtige Information angezeigt wird und nur andernfalls den Standardgesprächsdialog anzeigen lassen, da dieser sonst unmittelbar nach dem Aufruf der Informationsfunktion angezeigt wird.
    Da die Methode showInfo inzwischen mittels ihres Rückgabewerts anggibt, ob die Information wirklich angezeigt wird (Bedingungen werden überprüft), lässt sich das ganze mit einem einfachen If-Konstrukt ganz gut umsetzen:
    Code:
    private function startPageAction takes ATalk talk returns nothing
    	if (not talk.showInfo(0)) then
    		call talk.showInfo(1)
    		call talk.show()
    	endif
    endfunction
    Leider ist es nicht möglich dieses Art der Anwendung zu umgehen, da die Startseitenfunktion per .execute und die Informationsfunktion nochmals per .execute aufgerufen wird (Allgemeines Problem von Funktionsschnittstellen).
    Übrigens gibt es inzwischen auch einige neue Methoden wie z. B. infoHasBeenShown, welche dem Benutzer angibt, ob eine Information dem Spieler des Charakters, der sich momentan im Gespräch befindet bereits angezeigt wurde.
    Da ich nicht gerade die Lust verspüre, diese Einführung ständig auf den neusten Stand zu bringen, empfehle ich jedem sich die Quell-Code-Dateien bzw. die API-Dokumentation der ASL einmal genauer anzuschauen und sobald es eine erste Veröffentlichung von DMdF gibt, sich deren Quell-Code ebenfalls anzusehen.


    Quests
    In vielen Rollenspielen erhält der Spieler diverse Aufgaben für deren Erfüllung meist Belohnungen ausstehen. Diese Aufgaben werden auch Quests genannt. Sie sorgen dafür, dass man vom einfachen Gegnertöten weg und hin zu einem mit Handlung vollgepackten Spiel kommt. Natürlich kann ein Quest auch sehr einfach aufgebaut sein, dennoch bietet es meist eine willkommene Abwechslung zum „ziellosen“ Töten.
    Für die Umsetzung solcher Quests bietet die ASL in ihrem Charaktersystem die Strukturen AAbstractQuest, AQuest und AQuestItem an.
    Normalerweise werden davon nur die beiden Strukturen AQuest und AQuestItem benötigt. AabstractQuest ist, wie der Name schon vermuten lässt, eine abstrakte Struktur (in vJass gibt es keine wirklich abstrakten Strukturen, lediglich Schnittstellen (interfaces)) und somit auch die Elternstruktur der beiden anderen.
    Quests sind in der ASL folgendermaßen aufgebaut. Ein Quest hat einen Namen, einen zugehörigen Charakter, welcher das Quest erledigen soll, ein oder mehrere Ziele (hier kommt AquestItem ins Spiel) und alternativ auch eine Beschreibung und einen Symbolpfad für das Warcraft-3-Quest-Log. Selbstverständlich ist es auch möglich Quests für alle Charaktere und nicht nur für einen, also sozusagen Haupt-Quests, zu erzeugen. Dabei wird als zugehöriger Charakter einfach der Wert 0 eingetragen.
    Doch kommen wir nun zunächst zur Initialisierung der benötigten Strukturen. Wie alle Strukturen der ASL, die explizit vom Benutzer initialisiert werden müssen, so besitzen auch die Strukturen AAbstractQuest und AQuest eigene Initialisierungsmethoden. Diese sollten auf jeden Fall vor der Benutzung jeglicher anderer Methoden (das gilt natürlich auch für die Struktur AQuestItem) aufgerufen werden:

    Code:
    library MyQuestLibrary initializer initFunction requires Asl
    
    	private function initFunction takes nothing returns nothing
    		call AAbstractQuest.init(30.0, "Sound\\Interface\\QuestNew.wav", "Sound\\Interface\\QuestCompleted.wav", "Sound\\Interface\\QuestFailed.wav", "%s (|c00ffffffNeu|r)", "%s (|cffc3dbffAbgeschlossen|r)", "%s (|c00ff0000Fehlgeschlagen|r)", "+%i Stufe(n)", "+%i Fähigkeitenpunkt(e)", "+%i Erfahrung", "+%i Stärke", "+%i Geschick", "+%i Wissen", "+%i Goldmünze(n)", "+%i Holz")
    		call AQuest.init0(true, "Sound\\Interface\\QuestLog.wav")
    	endfunction
    	
    endlibrary
    Wie man sieht nimmt vor allem die Initialisierungsmethode der Struktur AabstractQuest eine Menge Argumente entgegen. Das erste Argument gibt die Ping-Rate aller aktiven Quests an. Jedes Quest und auch jedes Quest-Ziel kann einen Ping auf der Minikarte anzeigen lassen solange es aktiv ist. Beträgt der Wert dieser Rate 0, so wird die Pingfunktion für alle Quests und Quest-Ziele deaktivert. Die drei darauffolgenden Argumente geben die Dateipfade der Tondateien an, welche bei den drei möglichen Statusänderungen abgespielt werden. Sowohl ein Quest als auch ein Quest-Ziel kann entweder den Status „Neu“, „Abgeschlossen“ oder „Fehlgeschlagen“ haben. Alle weiteren Argumente dienen dem Quest-Belohnungssystem bzw. den möglichen Belohnungstypen dessen.
    Die Initialisierungsmethode der Struktur Aquest - init0 – empfängt dagegen weit weniger Argumente. Das erste Argument gibt an, ob das warcraftinterne Quest-Log (welches normalerweise über F9 aufrufbar ist) verwendet werden soll. Ist dieses Argument wahr, so werden alle Änderungen am Quest auch automatisch im Quest-Log durchgeführt. Das zweite Argument gibt den Dateipfad der Tondatei an, welche bei Aktualisierungsnachrichten abgespielt werden soll.
    Nachdem wir nun also die beiden Systeme initialisiert haben, können wir uns ans Werk machen, ein einfaches Quest zu basteln.
    Am Anfang aller Dinge kommt natürlich erst einmal die Überlegung, worum es in unserem Quest überhaupt gehen soll. Ein einfaches Beispiel wäre das Unterstützen eines Verbündeten Kriegers, welcher von bösen Kreaturen angegriffen wird.
    Wie das Quest letztendlich implementiert wird ist natürlich die Sache eines jeden selbst. Ich bevorzuge dabei die Methode der Vererbung:

    Code:
    library MyQuestLibrary initializer initFunction requires Asl
    
    	private function initFunction takes nothing returns nothing
    		call AAbstractQuest.init(30.0, "Sound\\Interface\\QuestNew.wav", "Sound\\Interface\\QuestCompleted.wav", "Sound\\Interface\\QuestFailed.wav", "%s (|c00ffffffNeu|r)", "%s (|cffc3dbffAbgeschlossen|r)", "%s (|c00ff0000Fehlgeschlagen|r)", "+%i Stufe(n)", "+%i Fähigkeitenpunkt(e)", "+%i Erfahrung", "+%i Stärke", "+%i Geschick", "+%i Wissen", "+%i Goldmünze(n)", "+%i Holz")
    		call AQuest.init0(true, „Sound\\Interface\\QuestLog.wav“)
    	endfunction
    
    	struct QuestHelpPeter extends AQuest
    
    		public static method create takes ACharacter character returns QuestHelpPeter
    			local QuestHelpPeter this = QuestHelpPeter.allocate(character, „Hilf Peter!“)
    			return this
    		endmethod			
    	endstruct
    endlibrary
    Damit habe ich nun nichts anderes getan als eine neue Struktur zu erschaffen, welche von AQuest erbt und deren Konstruktor überlädt.
    Allerdings fehlen uns jetzt noch die Quest-Ziele und irgendein Auslöser, der das Quest aktiviert und sich um Dinge wie den Tod Peters kümmern.
    Da bei den meisten Quests ein solches Schema angewandt wird, kann man mittels der Methoden setStateEvent, setStateCondition und setStateAction für jedes Quest und auch jedes Quest-Ziel ein Ereignis, eine Bedingung und eine Aktion pro Status festlegen.
    Nehmen wir also einmal an, dass Peter auf einem Hügel steht und um ihn herum wütende Orks stehen, die jeden Moment losschlagen könnten. Betritt der Charakter nun ein bestimmtes Gebiet, in welchem sich Peter aufhält, so soll das Quest aktiviert werden. Außerdem wollen wir noch ein Quest-Ziel namens „Hilf Peter die Orks zu vertreiben.“ hinzufügen:

    Code:
    library MyQuestLibrary initializer initFunction requires Asl
    
    	private function initFunction takes nothing returns nothing
    		call AAbstractQuest.init(30.0, "Sound\\Interface\\QuestNew.wav“, „Sound\\Interface\\QuestCompleted.wav", "Sound\\Interface\\QuestFailed.wav", "%s (|c00ffffffNeu|r)", "%s (|cffc3dbffAbgeschlossen|r)", "%s (|c00ff0000Fehlgeschlagen|r)", "+%i Stufe(n)", "+%i Fähigkeitenpunkt(e)", "+%i Erfahrung", "+%i Stärke", "+%i Geschick", "+%i Wissen", "+%i Goldmünze(n)", "+%i Holz")
    		call AQuest.init0(true, "Sound\\Interface\\QuestLog.wav")
    	endfunction
    
    	private function stateEventNew takes AQuest usedQuest, trigger usedTrigger returns nothing
    		local event triggerEvent = TriggerRegisterEnterRectSimple(usedTrigger, gg_rct_peters_zone)
    		set triggerEvent = null
    	endfunction
    
    	private function stateConditionNew takes AQuest usedQuest returns boolean
    		local unit triggerUnit = GetTriggerUnit()
    		local ACharacter character = ACharacter.getCharacterByUnit(triggerUnit)
    		set triggerUnit = null
    		return character == usedQuest.character()
    	endfunction
    
    	private function stateActionNew takes AQuest  usedQuest returns nothing
    		local player user = usedQuest.character().user()
    		call  usedQuest.questItem(0).setState(AAbstractQuest.stateNew)
    		call TransmissionFromUnitForPlayer(user, gg_unit_peter, "Hilf mir, die verdammten Orks wollen mir an den Kragen!", null)
    		set user = null
    	endfunction
    
    	struct QuestHelpPeter extends AQuest
    		private AQuestItem m_questItem0
    
    		public static method create takes ACharacter character returns QuestHelpPeter
    			local QuestHelpPeter this = QuestHelpPeter.allocate(character, „Hilf Peter!“)
    			call this.setStateEvent(AAbstractQuest.stateNew, stateEventNew)
    			call this.setStateCondtiion(AAbstractQuest.stateNew, stateConditionNew)
    			call this.setStateAction(AAbstractQuest.stateNew, stateActionNew)
    			set this.m_questItem0 = AquestItem.create(this, "Hilf Peter die Orks zu vertreiben.")
    			return this
    		endmethod			
    	endstruct
    endlibrary
    So, da hat sich jetzt auf den ersten Blick eine Menge geändert. Gehen wir die Änderungen einmal Schritt für Schritt durch. Im Konstruktor werden dem Quest wie geplant ein Ereignis, eine Bedingung und eine Aktion für den Status „Neu“ zugewiesen. Dafür wird von der vJass-Implementation von Funktionsschnittstellen (function interfaces) Gebrauch gemacht. Die zugehörigen Funktionen sind oberhalb der Struktur definiert.
    Zunächst einmal ist dort die Funktion stateEventNew. Sie hat die Aufgabe, wie auch alle anderen Ereignisfunktionen von Quests, dem internen Statusauslöser der Quest-Instanz ein Ereignis zuzuweisen. Dazu wird die Funktion TriggerRegisterEnterRectSimple aufgerufen, da das Quest ja aktiviert werden soll, sobald der Charakter das entsprechende Gebiet betritt.
    Natürlich müssen wir jetzt auch noch prüfen, ob die Einheit, welche das Gebiet betritt ein Charakter ist. Bei anderen Ereignissen hätten wir die Charaktereinheit schon bei der Registrierung direkt als Parameter übergeben können, in diesem Fall geht das leider nicht. Also prüft die Funktion stateConditionNew das. Sie hat keinen Auslöser als Parameter, da sie intern von einer standardmäßigen Bedingung mittels .evaluate aufgerufen wird. Es gilt hier noch zu beachten, dass sie wie eine normale Bedingung immer einen boolean-Wert zurückgeben muss.
    Und zu guter Letzt wird noch die Aktionsfunktion namens stateActionNew definiert. Darin wird zunächst einmal das erste Quest-Ziel ebenfalls auf den Status „Neu“ gesetzt, was nicht automatisch passiert. Das liegt daran, dass man auch Quest-Ziele definieren können muss, die erst nachträglich erscheinen. Würde das Quest aber intern all seine Ziele auf den Status „Neu“ setzen, so wäre dies unmöglich.
    Außerdem wird noch eine kleine Nachricht von Peter an den Besitzer des Charakters gesendet. Dazu wird eine ebenfalls aus der ASL stammende Funktion verwendet.
    Nun ist also das Quest aktiv, doch was kommt als nächstes? Was passiert zum Beispiel, wenn Peter stirbt oder wann ist Peter gerettet?
    Implementieren wir also als nächstes die Möglichkeit eines Fehlschlags, falls Peter stirbt. Es gilt hierbei zu beachten, dass dieses Quest nicht unbedingt mehrspielertauglich ist, außer man nimmt es in Kauf, dass es nur solange abgeschlossen werden kann, solange es bei keinem Spieler fehlschlägt und die eher merkwürdige Tatsache, dass Peter die ganze Zeit von Orks bedroht wird.

    Code:
    library MyQuestLibrary initializer initFunction requires Asl
    
    	private function initFunction takes nothing returns nothing
    		call AAbstractQuest.init(30.0, "Sound\\Interface\\QuestNew.wav", "Sound\\Interface\\QuestCompleted.wav", "Sound\\Interface\\QuestFailed.wav", "%s (|c00ffffffNeu|r)", "%s (|cffc3dbffAbgeschlossen|r)", "%s (|c00ff0000Fehlgeschlagen|r)", "+%i Stufe(n)", "+%i Fähigkeitenpunkt(e)", "+%i Erfahrung", "+%i Stärke", "+%i Geschick", "+%i Wissen", "+%i Goldmünze(n)", "+%i Holz")
    		call AQuest.init0(true, "Sound\\Interface\\QuestLog.wav")
    	endfunction
    
    	private function stateEventNew takes AQuest usedQuest, trigger usedTrigger returns nothing
    		local event triggerEvent = TriggerRegisterEnterRectSimple(usedTrigger, gg_rct_peters_zone)
    		set triggerEvent = null
    	endfunction
    
    	private function stateConditionNew takes AQuest usedQuest returns boolean
    		local unit triggerUnit = GetTriggerUnit()
    		local ACharacter character = ACharacter.getCharacterByUnit(triggerUnit)
    		set triggerUnit = null
    		return character == usedQuest.character()
    	endfunction
    
    	private function stateActionNew takes AQuest  usedQuest returns nothing
    		local player user = usedQuest.character().user()
    		call  usedQuest.questItem(0).setState(AAbstractQuest.stateNew)
    		call TransmissionFromUnitForPlayer(user, gg_unit_peter, "Hilf mir, die verdammten Orks wollen mir an den Kragen!", null)
    		set user = null
    	endfunction
    
    	private function stateEventFailed takes AQuest usedQuest, trigger usedTrigger returns nothing
    		local event triggerEvent = TriggerRegisterUnitEvent(usedTrigger, gg_unit_peter, EVENT_UNIT_DEATH)
    		set triggerEvent = null
    	endfunction
    
    	private function stateActionFailed takes AQuest  usedQuest returns nothing
    		local player user = usedQuest.character().user()
    		if (usedQuest.questItem(0).state() == AabstractQuest.stateNew) then
    			call  usedQuest.questItem(0).setState(AAbstractQuest.stateFailed)
    		endif
    		call TransmissionFromUnitForPlayer(user, gg_unit_peter, "Ahhh, ich sterbe, wie furchtbar!!!!einseinself", null)
    		set user = null
    	endfunction
    
    	struct QuestHelpPeter extends AQuest
    		private AQuestItem m_questItem0
    
    		public static method create takes ACharacter character returns QuestHelpPeter
    			local QuestHelpPeter this = QuestHelpPeter.allocate(character, "Hilf Peter!")
    			call this.setStateEvent(AAbstractQuest.stateNew, stateEventNew)
    			call this.setStateCondtiion(AAbstractQuest.stateNew, stateConditionNew)
    			call this.setStateAction(AAbstractQuest.stateNew, stateActionNew)
    			call this.setStateEvent(AAbstractQuest.stateFailed, stateEventFailed)
    			call this.setStateAction(AAbstractQuest.stateFailed, stateEventAction)
    			set this.m_questItem0 = AQuestItem.create(this, "Hilf Peter die Orks zu vertreiben.")
    			return this
    		endmethod			
    	endstruct
    	
    endlibrary
    Dazu gibt es im Grunde genommen nicht viel zu schreiben, da es genau das gleiche Schema, nur dieses Mal ohne Bedingung, wie beim Status „Neu“ ist. Falls man es tatsächlich mehrspielerfähig machen wollte, so müsste man jetzt noch die Quest-Instanzen der anderen Charaktere zerstören, worauf ich aber verzichten werde.
    Nun sollte man das Quest vielleicht auch noch abschließen können und dafür eine nette Belohnung erhalten.
    Dafür nehme ich das einfache Beispiel zur Hand, dass man eine bestimmte Anzahl von Orks töten muss, bis Peter „geholfen“ ist. Das erfordert ein zusätzliches Zähler-Element, welches die Anzahl der getöteten Orks speichert. Wir nehmen einfach einmal, dass immer neue Orks erzeugt werden, die Peter auf seinem kleinen Hügel attackieren.
    Nachdem der Charakter 10 von ihnen getötet hat, freut sich Peter über die Hilfe und gibt einem ein wenig Gold und natürlich erhält man auch noch etwas Erfahrung. Danach kann man Peter abkratzen lassen.

    Code:
    library MyQuestLibrary initializer initFunction requires Asl
    	private function initFunction takes nothing returns nothing
    		call AAbstractQuest.init(30.0, "Sound\\Interface\\QuestNew.wav", "Sound\\Interface\\QuestCompleted.wav", "Sound\\Interface\\QuestFailed.wav", "%s (|c00ffffffNeu|r)", "%s (|cffc3dbffAbgeschlossen|r)", "%s (|c00ff0000Fehlgeschlagen|r)", "+%i Stufe(n)", "+%i Fähigkeitenpunkt(e)", "+%i Erfahrung", "+%i Stärke", "+%i Geschick", "+%i Wissen", "+%i Goldmünze(n)", "+%i Holz")
    		call AQuest.init0(true, "Sound\\Interface\\QuestLog.wav")
    	endfunction
    
    	private function stateEventNew takes AQuest usedQuest, trigger usedTrigger returns nothing
    		local event triggerEvent = TriggerRegisterEnterRectSimple(usedTrigger, gg_rct_peters_zone)
    		set triggerEvent = null
    	endfunction
    
    	private function stateConditionNew takes AQuest usedQuest returns boolean
    		local unit triggerUnit = GetTriggerUnit()
    		local ACharacter character = ACharacter.getCharacterByUnit(triggerUnit)
    		set triggerUnit = null
    		return character == usedQuest.character()
    	endfunction
    
    	private function stateActionNew takes AQuest  usedQuest returns nothing
    		local player user = usedQuest.character().user()
    		call  usedQuest.questItem(0).setState(AAbstractQuest.stateNew)
    		call TransmissionFromUnitForPlayer(user, gg_unit_peter, "Hilf mir, die verdammten Orks wollen mir an den Kragen!", null)
    		set user = null
    	endfunction
    
    	private function stateEventFailed takes AQuest usedQuest, trigger usedTrigger returns nothing
    		local event triggerEvent = TriggerRegisterUnitEvent(usedTrigger, gg_unit_peter, EVENT_UNIT_DEATH)
    		set triggerEvent = null
    	endfunction
    
    	private function stateActionFailed takes AQuest  usedQuest returns nothing
    		local player user = usedQuest.character().user()
    		if (usedQuest.questItem(0).state() == AabstractQuest.stateNew) then
    			call  usedQuest.questItem(0).setState(AAbstractQuest.stateFailed)
    		endif
    		call TransmissionFromUnitForPlayer(user, gg_unit_peter, "Ahhh, ich sterbe, wie furchtbar!!!!einseinself", null)
    		set user = null
    	endfunction
    
    	private function stateEventCompleted0 takes AQuestItem questItem, trigger usedTrigger returns nothing
    		local player orcOwner = Player(10)
    		local event triggerEvent = TriggerRegisterUnitEventSimple(usedTrigger, orcOwner, EVENT_PLAYER_UNIT_DEATH)
    		set orcOwner = null
    		set triggerEvent = null
    	endfunction
    
    	private function stateConditionCompleted0 takes AQuestItem questItem returns boolean
    		local unit triggerUnit = GetTriggerUnit()
    		local unit killingUnit = GetKillingUnit()
    		local boolean result = false
    		if (Acharacter.getCharacterByUnit(killingUnit) == questItem.character()) then
    			if (GetUnitTypeId(triggerUnit) == 'ogru') then
    				call QuestHelpPeter(questItem.quest()).addKilledOrcs(1)
    				set result = QuestHelpPeter(questItem.quest()).killedOrcs() == 10
    				if (not result) then
    					call questItem.quest().displayUpdateMessage("Noch zu tötende Orks: " + I2S(10 – QuestHelpPeter(questItem.quest()).killedOrcs()) + ".")
    				endif
    			endif
    		endif
    		set triggerUnit = null
    		set killingUnit = null
    		return result
    	endfunction
    
    	private function stateActionCompleted0 takes AQuestItem questItem returns nothing
    		local player user = questItem.character().user()
    		call TransmissionFromUnitForPlayer(user, gg_unit_peter, "Danke, du hast mir wirklich sehr geholfen! Hier hast du etwas Gold.", null)
    		set user = null
    	endfunction
    
    	struct QuestHelpPeter extends AQuest
    		private AQuestItem m_questItem0
    		private integer m_killedOrcs
    
    		public method addKilledOrcs takes integer number returns nothing
    			set this.m_killedOrcs = this.m_killedOrcs + number
    		endmethod
    
    		public method killedOrcs takes nothing returns integer
    			return this.m_killedOrcs
    		endmethod
    		
    		public static method create takes ACharacter character returns QuestHelpPeter
    			local QuestHelpPeter this = QuestHelpPeter.allocate(character, "Hilf Peter!")
    			call this.setStateEvent(AAbstractQuest.stateNew, stateEventNew)
    			call this.setStateCondtiion(AAbstractQuest.stateNew, stateConditionNew)
    			call this.setStateAction(AAbstractQuest.stateNew, stateActionNew)
    			call this.setStateEvent(AAbstractQuest.stateFailed, stateEventFailed)
    			call this.setStateAction(AAbstractQuest.stateFailed, stateEventAction)
    			set this.m_questItem0 = AQuestItem.create(this, "Hilf Peter die Orks zu vertreiben.")
    			call this.m_questItem0.setStateEvent(AAbstractQuest.stateCompleted, stateEventCompleted0)
    			call this.m_questItem0.setStateCondition(AAbstractQuest.stateCompleted, stateConditionCompleted0)
    			call this.m_questItem0.setStateAction(AAbstractQuest.stateCompleted, stateActionCompleted0)
    			call this m_questItem0.setReward(AAbstractQuest.rewardGold, 300)
    			call this m_questItem0.setReward(AAbstractQuest.rewardExperience, 1000)
    			set this.m_killedOrcs = 0
    			return this
    		endmethod			
    	endstruct
    endlibrary
    Bei diesem Beispiel brauchen wir die Aktion nur noch aufgrund der abgesendeten Nachricht von Peter. Der Zähler wird bereits in der Bedingung bearbeitet. Dazu muss der Rückgabewert der Methode quest der Struktur AQuestItem zum Typ QuestHelpPeter konvertiert werden.
    Dieses Mal müssen wir den Status des Quest-Ziels nicht manuell setzen. Wird einem Quest der Status „Abgeschlossen“ oder „Fehlgeschlagen“ zugewiesen, so werden alle Quest-Ziele mit dem Status „Neu“ auf denselben Status gesetzt.
    Die Quest-Belohnung hätte man in diesem Fall genauso gut der Quest- und nicht der Quest-Ziel-Instanz zuordnen können.
    Belohnungen werden automatisch an den oder die Charaktere verteilt, sobald der entsprechende Status gesetzt wird.
    Möchte man das Quest nun tatsächlich in seiner Karte benutzen, so muss man es nur noch mittels QuestHelpPeter.create(character) für den entsprechenden Charakter erzeugen. Vorher sollte man eventuell noch die globalen Variablen für den Peter und das Gebiet austauschen.
    Es gilt noch anzumerken, dass man den Aufruf der Methode setEvent auch weglassen kann. In einem solchen Fall wird dann kein interner Auslöser erzeugt und die Bedingung wird überprüft sobald man setState aufruft (insofern man eine festgelegt hat), daraufhin wird dann auch die festgelegte Aktion aufgerufen.
    So kann man das ganze auch mittels setState aus einem schon vorhandenen Auslöser steuern. Will man das Quest jetzt als Haupt-Quest, sprich für alle Spielercharakter, implementieren, so muss man den Parameter character entfernen und stattdessen 0 übergeben. Außerdem muss man die Bedingungen und Aktionen an alle Spieler anpassen:

    Code:
    library MyQuestLibrary initializer initFunction requires Asl
    
    	private function initFunction takes nothing returns nothing
    		call AAbstractQuest.init(30.0, "Sound\\Interface\\QuestNew.wav", "Sound\\Interface\\QuestCompleted.wav", "Sound\\Interface\\QuestFailed.wav", "%s (|c00ffffffNeu|r)", "%s (|cffc3dbffAbgeschlossen|r)", "%s (|c00ff0000Fehlgeschlagen|r)", "+%i Stufe(n)", "+%i Fähigkeitenpunkt(e)", "+%i Erfahrung", "+%i Stärke", "+%i Geschick", "+%i Wissen", "+%i Goldmünze(n)", "+%i Holz")
    		call AQuest.init0(true, "Sound\\Interface\\QuestLog.wav")
    	endfunction
    
    	private function stateEventNew takes AQuest usedQuest, trigger usedTrigger returns nothing
    		local event triggerEvent = TriggerRegisterEnterRectSimple(usedTrigger, gg_rct_peters_zone)
    		set triggerEvent = null
    	endfunction
    
    	private function stateConditionNew takes AQuest usedQuest returns boolean
    		local unit triggerUnit = GetTriggerUnit()
    		local boolean result  = ACharacter.isUnitCharacter(triggerUnit)
    		set triggerUnit = null
    		return result
    	endfunction
    
    	private function stateActionNew takes AQuest  usedQuest returns nothing
    		call  usedQuest.questItem(0).setState(AAbstractQuest.stateNew)
    		call TransmissionFromUnit(user, gg_unit_peter, "Helft mir, die verdammten Orks wollen mir an den Kragen!", null)
    	endfunction
    
    	private function stateEventFailed takes AQuest usedQuest, trigger usedTrigger returns nothing
    		local event triggerEvent = TriggerRegisterUnitEvent(usedTrigger, gg_unit_peter, EVENT_UNIT_DEATH)
    		set triggerEvent = null
    	endfunction
    
    	private function stateActionFailed takes AQuest  usedQuest returns nothing
    		if (usedQuest.questItem(0).state() == AabstractQuest.stateNew) then
    			call  usedQuest.questItem(0).setState(AAbstractQuest.stateFailed)
    		endif
    		call TransmissionFromUnit(null, gg_unit_peter, "Ahhh, ich sterbe, wie furchtbar!!!!einseinself", null)
    	endfunction
    
    	private function stateEventCompleted0 takes AQuestItem questItem, trigger usedTrigger returns nothing
    		local player orcOwner = Player(10)
    		local event triggerEvent = TriggerRegisterUnitEventSimple(usedTrigger, orcOwner, EVENT_PLAYER_UNIT_DEATH)
    		set orcOwner = null
    		set triggerEvent = null
    	endfunction
    
    	private function stateConditionCompleted0 takes AQuestItem questItem returns boolean
    		local unit triggerUnit = GetTriggerUnit()
    		local unit killingUnit = GetKillingUnit()
    		local boolean result = false
    		if (ACharacter.isUnitCharacter(killingUnit)) then
    			if (GetUnitTypeId(triggerUnit) == 'ogru') then
    				call QuestHelpPeter(questItem.quest()).addKilledOrcs(1)
    				set result = QuestHelpPeter(questItem.quest()).killedOrcs() == 10
    				if (not result) then
    					call questItem.quest().displayUpdateMessage("Noch zu tötende Orks: " + I2S(10 – QuestHelpPeter(questItem.quest()).killedOrcs()) + ".")
    				endif
    			endif
    		endif
    		set triggerUnit = null
    		set killingUnit = null
    		return result
    	endfunction
    
    	private function stateActionCompleted0 takes AQuestItem questItem returns nothing
    		call TransmissionFromUnit(gg_unit_peter, "Danke, ihr habt mir wirklich sehr geholfen! Hier habt ihr etwas Gold.", null)
    	endfunction
    
    	struct QuestHelpPeter extends AQuest
    		private AQuestItem m_questItem0
    		private integer m_killedOrcs
    
    		public method addKilledOrcs takes integer number returns nothing
    			set this.m_killedOrcs = this.m_killedOrcs + number
    		endmethod
    
    		public method killedOrcs takes nothing returns integer
    			return this.m_killedOrcs
    		endmethod
    		
    		public static method create takes nothing returns QuestHelpPeter
    			local QuestHelpPeter this = QuestHelpPeter.allocate(0, "Hilf Peter!")
    			call this.setStateEvent(AAbstractQuest.stateNew, stateEventNew)
    			call this.setStateCondtiion(AAbstractQuest.stateNew, stateConditionNew)
    			call this.setStateAction(AAbstractQuest.stateNew, stateActionNew)
    			call this.setStateEvent(AAbstractQuest.stateFailed, stateEventFailed)
    			call this.setStateAction(AAbstractQuest.stateFailed, stateEventAction)
    			set this.m_questItem0 = AQuestItem.create(this, "Hilf Peter die Orks zu vertreiben.")
    			call this.m_questItem0.setStateEvent(AAbstractQuest.stateCompleted, stateEventCompleted0)
    			call this.m_questItem0.setStateCondition(AAbstractQuest.stateCompleted, stateConditionCompleted0)
    			call this.m_questItem0.setStateAction(AAbstractQuest.stateCompleted, stateActionCompleted0)
    			call this m_questItem0.setReward(AAbstractQuest.rewardGold, 300)
    			call this m_questItem0.setReward(AAbstractQuest.rewardExperience, 1000)
    			set this.m_killedOrcs = 0
    			return this
    		endmethod			
    	endstruct
    	
    endlibrary
    Hierbei könnte man jetzt natürlich auch statische Elemente verwenden, da das Quest ja nur ein einziges Mal erzeugt wird.
    Ein kleines Problem bereitet mir zudem noch das Warcraft-Quest-Log, da dort ja die Quests aller Spieler angezeigt werden und man somit auch die Quests der anderen sieht. Eine Möglichkeit bestünde darin, einfach den Namen des entsprechenden Spielers vor den Quest-Namen zu setzen, was aber nicht gerade schön wäre.
    Anderenfalls kann man auch einfach sein eigenes Quest-Log implementieren.
    Nun könnten wir dem Ganzen noch seinen letzten Schliff geben, indem wir dem Quest ein Symbol und eine Beschreibung zuweisen und einen regelmäßigen Ping-Effekt erzeugen lassen:

    Code:
    library MyQuestLibrary initializer initFunction requires Asl
    
    	private function initFunction takes nothing returns nothing
    		call AAbstractQuest.init(30.0, "Sound\\Interface\\QuestNew.wav", "Sound\\Interface\\QuestCompleted.wav", "Sound\\Interface\\QuestFailed.wav", "%s (|c00ffffffNeu|r)", "%s (|cffc3dbffAbgeschlossen|r)", "%s (|c00ff0000Fehlgeschlagen|r)", "+%i Stufe(n)", "+%i Fähigkeitenpunkt(e)", "+%i Erfahrung", "+%i Stärke", "+%i Geschick", "+%i Wissen", "+%i Goldmünze(n)", "+%i Holz")
    		call AQuest.init0(true, "Sound\\Interface\\QuestLog.wav")
    	endfunction
    
    	private function stateEventNew takes AQuest usedQuest, trigger usedTrigger returns nothing
    		local event triggerEvent = TriggerRegisterEnterRectSimple(usedTrigger, gg_rct_peters_zone)
    		set triggerEvent = null
    	endfunction
    
    	private function stateConditionNew takes AQuest usedQuest returns boolean
    		local unit triggerUnit = GetTriggerUnit()
    		local boolean result  = ACharacter.isUnitCharacter(triggerUnit)
    		set triggerUnit = null
    		return result
    	endfunction
    
    	private function stateActionNew takes AQuest  usedQuest returns nothing
    		call  usedQuest.questItem(0).setState(AAbstractQuest.stateNew)
    		call TransmissionFromUnit(user, gg_unit_peter, "Helft mir, die verdammten Orks wollen mir an den Kragen!", null)
    	endfunction
    
    	private function stateEventFailed takes AQuest usedQuest, trigger usedTrigger returns nothing
    		local event triggerEvent = TriggerRegisterUnitEvent(usedTrigger, gg_unit_peter, EVENT_UNIT_DEATH)
    		set triggerEvent = null
    	endfunction
    
    	private function stateActionFailed takes AQuest  usedQuest returns nothing
    		if (usedQuest.questItem(0).state() == AabstractQuest.stateNew) then
    			call  usedQuest.questItem(0).setState(AAbstractQuest.stateFailed)
    		endif
    		call TransmissionFromUnit(null, gg_unit_peter, "Ahhh, ich sterbe, wie furchtbar!!!!einseinself", null)
    	endfunction
    
    	private function stateEventCompleted0 takes AQuestItem questItem, trigger usedTrigger returns nothing
    		local player orcOwner = Player(10)
    		local event triggerEvent = TriggerRegisterUnitEventSimple(usedTrigger, orcOwner, EVENT_PLAYER_UNIT_DEATH)
    		set orcOwner = null
    		set triggerEvent = null
    	endfunction
    
    	private function stateConditionCompleted0 takes AQuestItem questItem returns boolean
    		local unit triggerUnit = GetTriggerUnit()
    		local unit killingUnit = GetKillingUnit()
    		local boolean result = false
    		if (ACharacter.isUnitCharacter(killingUnit)) then
    			if (GetUnitTypeId(triggerUnit) == 'ogru') then
    				call QuestHelpPeter(questItem.quest()).addKilledOrcs(1)
    				set result = QuestHelpPeter(questItem.quest()).killedOrcs() == 10
    				if (not result) then
    					call questItem.quest().displayUpdateMessage("Noch zu tötende Orks: " + I2S(10 – QuestHelpPeter(questItem.quest()).killedOrcs()) + ".")
    				endif
    			endif
    		endif
    		set triggerUnit = null
    		set killingUnit = null
    		return result
    	endfunction
    
    	private function stateActionCompleted0 takes AQuestItem questItem returns nothing
    		call TransmissionFromUnit(gg_unit_peter, "Danke, ihr habt mir wirklich sehr geholfen! Hier habt ihr etwas Gold.", null)
    	endfunction
    
    	struct QuestHelpPeter extends AQuest
    		private AQuestItem m_questItem0
    		private integer m_killedOrcs
    
    		public method addKilledOrcs takes integer number returns nothing
    			set this.m_killedOrcs = this.m_killedOrcs + number
    		endmethod
    
    		public method killedOrcs takes nothing returns integer
    			return this.m_killedOrcs
    		endmethod
    		
    		public static method create takes nothing returns QuestHelpPeter
    			local QuestHelpPeter this = QuestHelpPeter.allocate(0, "Hilf Peter!")
    			call this.setIconPath("ReplaceableTextures\\CommandButtons\\BTNVillagerMan.blp")
    			call this.setDescription("Peter wurde auf seinem kleinen Hügel von Orks eingekreist. Helft ihm wenigstens eine Weile zu überleben.")
    			call this.setStateEvent(AAbstractQuest.stateNew, stateEventNew)
    			call this.setStateCondtiion(AAbstractQuest.stateNew, stateConditionNew)
    			call this.setStateAction(AAbstractQuest.stateNew, stateActionNew)
    			call this.setStateEvent(AAbstractQuest.stateFailed, stateEventFailed)
    			call this.setStateAction(AAbstractQuest.stateFailed, stateEventAction)
    			set this.m_questItem0 = AQuestItem.create(this, "Hilf Peter die Orks zu vertreiben.")
    			call this.m_questItem0.setStateEvent(AAbstractQuest.stateCompleted, stateEventCompleted0)
    			call this.m_questItem0.setStateCondition(AAbstractQuest.stateCompleted, stateConditionCompleted0)
    			call this.m_questItem0.setStateAction(AAbstractQuest.stateCompleted, stateActionCompleted0)
    			call this m_questItem0.setReward(AAbstractQuest.rewardGold, 300)
    			call this m_questItem0.setReward(AAbstractQuest.rewardExperience, 1000)
    			call this.m_questItem0.setPingRect(gg_rct_peters_zone)
    			set this.m_killedOrcs = 0
    			return this
    		endmethod			
    	endstruct
    	
    endlibrary
    Dynamische Ping-Koordinaten wie zum Beispiel die einer sich bewegenden Einheit, werden leider noch nicht vom System unterstützt.

  2. #2
    Benutzerbild von Kricz
    Registriert seit
    Apr 2008
    BNet Account
    Kricz
    Beiträge
    1.942
    Moin,

    also ich hab mir jetzt nicht ALLES durchgelsen aber das was ich gelesen habe hört sich echt genial an.
    Ich würde mich freuen, wenn du mal eine Demo Map mit ein paar Quests machen könntest so das man sich als normaler Mapper ein richtiges Bild von dem System/Code machen kann :P
    Dota 2
    >> Besucht uns im neuen ingame Portal inDota2! <<

  3. #3
    Ja, könnte ich machen. Ich muss das Tutorial des Dialogsystems bezüglich "wichtiger" Informationen aktualisieren, weil ich während den Arbeiten an DMdF noch ein paar Mängel entdeckt habe.
    Und erst gestern habe ich endlich einen sehr alten Bug im Questsystem behoben, der einige Zeit lang verhinderte, dass die Auslöseraktionen des Quests korrekt ausgeführt wurden :-/.
    Also wenn ich mal dazu komme eine Testkarte zu machen ... wäre sicherlich praktisch, aber momentan arbeite ich ziemlich viel an DMdF. Ansonsten wird die DMdF-Karte ja sowieso ungeschützt veröffentlicht und man kann sich darin dann auch alles anschauen.
    wc3lib (C++-Entwickler gesucht)
    Advanced Script Library (vJass-Entwickler gesucht)
    Die Macht des Feuers (Modelierer und 2d-Grafiker gesucht)
    Die-Macht-des-Feuers-Blog
    Mappedia

  4. #4
    Benutzerbild von Kricz
    Registriert seit
    Apr 2008
    BNet Account
    Kricz
    Beiträge
    1.942
    Original geschrieben von Barade
    Ansonsten wird die DMdF-Karte ja sowieso ungeschützt veröffentlicht und man kann sich darin dann auch alles anschauen.
    Wer weiß, wie lange es dauert bis die Map endlcihe released wird?^^
    Btw, kannst du mir mal nen Link geben, wo ich die ASL komplet dl'n kann, hab bisher iwie ncihts gefunden oO
    Dota 2
    >> Besucht uns im neuen ingame Portal inDota2! <<

  5. #5
    Also am Anfang hatte ich den Code immer auf dem Server meines Bruders gelagert und dann bin ich auf das Versionsverwaltungsprogramm Subversion (kurz SVN) umgestiegen, weil das einfach ein paar Vorteile gegenüber dem ständigen Vonhandersetzen bietet. Hier findest du die SVN-Ablage der ASL und auch der anderen Programme/Projekte, die ich bezüglich Warcraft 3 betreibe.
    Unter Linux kann ich die Ablage mit einem einfachen Befehl abrufen. Keine Ahnung, wie das unter Windoof ist, vielleicht gibt's da auch ein GUI-Programm. Ich bin zurzeit auch drauf und drann, die Ablage mit den Modellen/Texturen usw. der MPQ-Archive, die auch von DMdF verwendet werden zu füllen. Ist vielleicht ganz nützlich für Leute, die auch daran denken, eine Modifkation zu basteln.
    wc3lib (C++-Entwickler gesucht)
    Advanced Script Library (vJass-Entwickler gesucht)
    Die Macht des Feuers (Modelierer und 2d-Grafiker gesucht)
    Die-Macht-des-Feuers-Blog
    Mappedia

  6. #6
    Benutzerbild von Kricz
    Registriert seit
    Apr 2008
    BNet Account
    Kricz
    Beiträge
    1.942
    Moin,

    also ich kenn mich mit sowas schelcht aus^^
    kA ob Windoof Nichtsda (Vista) nen GUI-Programm hat^^

    Ich meine:
    die ASL is doch ein großer vJass-Code oder ist der tief mit deinem vjassdoc verbunden, so das ich den nciht ich ne Map direkt einbauen kann?
    Dota 2
    >> Besucht uns im neuen ingame Portal inDota2! <<

  7. #7
    Nein, mit vjassdoc hat der gar nix zu tun. Ich veröffentliche mein Zeugs halt auf einer Seite unter einem Projektnamen.
    Der Code ist auf mehrere Dateien verteilt und du musst nur die Dateie "Import Asl.j" mittels eines Makros in deiner Karte im Header importieren.
    Vielleicht werde ich auch mal ein Nicht-SVN-Paket hochladen.
    Achso, du solltest dann natürlich in der jasshelper.conf-Datei das Importverzeichnis hinzufügen, damit er auch alle Dateien findet.
    Normalerweise müssen deine Bibliotheken dann nur die Bibliothek "Asl" anfordern, die wiederum selbst alle restlichen anfordert. Natürlich kannst du auch nur mit Teilen arbeiten, da das schon ne Menge Code ist. Mir wäre das aber zu umständlich.
    wc3lib (C++-Entwickler gesucht)
    Advanced Script Library (vJass-Entwickler gesucht)
    Die Macht des Feuers (Modelierer und 2d-Grafiker gesucht)
    Die-Macht-des-Feuers-Blog
    Mappedia

  8. #8
    Benutzerbild von Kricz
    Registriert seit
    Apr 2008
    BNet Account
    Kricz
    Beiträge
    1.942
    Moin,

    okay danke^^

    Nächstes Problem:
    Textmakros kenn mich damit nicht soo gut aus XD

    Da käme das Nicht-SVN-Paket vielleicht nicht schlecht für einen wie mich (bin noch net sooo erfahren in vJass, für nice Spells reichts zwar aber am besten lerne ich durch Beispiele :P)

    btw, wieviel Zeilen Code hat die gesamte ASL ungefähr?
    Dota 2
    >> Besucht uns im neuen ingame Portal inDota2! <<

  9. #9
    Original geschrieben von Kricz
    Moin,

    okay danke^^

    Nächstes Problem:
    Textmakros kenn mich damit nicht soo gut aus XD

    Da käme das Nicht-SVN-Paket vielleicht nicht schlecht für einen wie mich (bin noch net sooo erfahren in vJass, für nice Spells reichts zwar aber am besten lerne ich durch Beispiele :P)

    btw, wieviel Zeilen Code hat die gesamte ASL ungefähr?
    Nach der letzten Ausgabe mittels --warcity (Da werden einfach alle Dateien zusammengefügt, ohne Kompilierung) ca. 16200, allerdings mit Leerzeilen und Kommentaren.
    wc3lib (C++-Entwickler gesucht)
    Advanced Script Library (vJass-Entwickler gesucht)
    Die Macht des Feuers (Modelierer und 2d-Grafiker gesucht)
    Die-Macht-des-Feuers-Blog
    Mappedia

  10. #10
    Kleine aber wichtige Information am Rande.
    Die oben geschriebenen Einführungen wurden teils verbessert und werden in der SVN-Ablage der ASL und NICHT HIER weitergeführt.
    Vermutlich werde ich die ASL jetzt auch mal in Form eines Archives veröffentlichen, es lohnt sich aber auf jeden Fall die Dokumentationen der SVN-Ablage anzusehen.
    wc3lib (C++-Entwickler gesucht)
    Advanced Script Library (vJass-Entwickler gesucht)
    Die Macht des Feuers (Modelierer und 2d-Grafiker gesucht)
    Die-Macht-des-Feuers-Blog
    Mappedia

Forumregeln

  • Es ist dir nicht erlaubt, neue Themen zu verfassen.
  • Es ist dir nicht erlaubt, auf Beiträge zu antworten.
  • Es ist dir nicht erlaubt, Anhänge hochzuladen.
  • Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
  •