Dies ist eine alte Version des Dokuments!
NPCs
Grundsätzlich sind alle Nichtspielercharaktere (Non-Player-Characters) NPCs. Im Kontext von Siedler 5 sind allerdings speziell jene Charaktere gemeint, die dem Spieler über Helden Interaktionen anbieten, gekennzeichnet durch ein großes Ausrufezeichen über dem Kopf.
Theoretisch kann bei der Interaktion mit einem NPC jede beliebige Funktion ausgelöst werden. In der Regel ist das aber ein Briefing, weshalb wir diesen Fall zuerst anschauen.
NPCs werden mit der Funktion CreateNPC(_NpcDescription)
erstellt. Die _NpcDescription
ist ein Table, das einige notwendige Informationen enthalten muss und optionale Einstellungen darüber hinaus erlaubt. In der untenstehenden Tabelle sind alle Keys, die man angeben kann, aufgelistet. Im Anschluss werden wir sie nacheinander erklären.
Key | Bedeutung | Erforderlich? |
---|---|---|
name | Skriptname des NPCs (kann im Editor gesetzt werden) | Ja |
callback | Funktion, die bei der Interaktion mit dem NPC aufgerufen wird | Ja |
briefing | Briefing, das bei der Interaktion mit dem NPC gestartet wird | Nur, wenn kein callback angegeben wurde |
heroName | Wenn der NPC nur mit einem bestimmten Helden interagieren soll, wird hier dessen Skriptname eingetragen (Standardmäßig kann mit allen Helden interagiert werden) | Nein |
wrongHeroMessage | Nachricht, die angezeigt wird, wenn der NPC mit dem falschen Helden angesprochen wird (alle außer derjenige, der mit heroName vorgeschrieben wird) | Nein, sollte bei der Angabe von heroName aber ebenfalls angegeben werden |
follow | Kann entweder true oder ein Skriptname sein. Der NPC folgt bei true dem nächsten Helden, sonst der angegebenen Entity | Nein |
vanishPos | Nach der Interaktion geht der NPC zur angegebenen Position und verschwindet, sobald er aus der Sichtweite des Spielers gerät | Nein |
Tatsächlich kann man einer _NpcDescription
beliebige Key-Value-Paare geben. In der callback
-Funktion kann dann darauf zugegriffen werden (siehe auch das Beispiel unten).
Hinweis: Wir werden im Folgenden nur mit der callback
-Option arbeiten und die briefing
-Angabe außen vor lassen. Das hat den Grund, dass der Weg über das callback
deutlich übersichtlicher und flexibler ist.
Achtung: Die Entity, die bei name
angegeben wird, muss eine Siedler-Einheit sein (also Präfix PU
oder CU
)! Andernfalls wird das Spiel abstürzen.
Der einfachste Fall ist ein NPC, der einfach nur ein Gespräch über ein Briefing anbietet:
-- Wir schreiben jeweils eine Funktion für die Erstellung des NPCs und eine für das Callback, in dem -- Fall den Start des Briefings function CreateNpcScout() local Npc = { -- Auf der Karte muss eine Einheit (am besten ein Kundschafter) mit dem Namen "Scout" existieren name = "Scout", -- Der Callback ist einfach nur der Name der Funktion callback = CreateBriefingScout } CreateNPC(Npc) end function CreateBriefingScout() local Briefing = { { title = "Kundschafter", text = "Alles ruhig hier an den Grenzen, mein Herr!", position = GetPosition("Scout") } } StartBriefing(Briefing) end
Interaktion mit einem bestimmten Helden
Möglicherweise wollen wir den Kundschafter nur mit Dario sprechen lassen. Dafür muss die Dario-Entity den Skriptnamen „Dario“
besitzen:
-- Wir schreiben jeweils eine Funktion für die Erstellung des NPCs und eine für das Callback, in dem -- Fall den Start des Briefings function CreateNpcScout() -- Zur Anschauung die äquivalente, verkürzte Schreibweise CreateNPC{ name = "Scout", callback = CreateBriefingScout, heroName = "Dario", wrongHeroMessage = "Meine Informationen übergebe ich nur Dario persönlich!" } end function CreateBriefingScout() local Briefing = { { title = "Kundschafter", text = "König Dario, hier ist immer noch alles ruhig.", position = GetPosition("Scout") } } StartBriefing(Briefing) end
Zusätzliche Parameter der Callback-Funktion
Im vorigen Beispiel haben wir gesehen, dass es ganz schön sein kann, wenn der NPC den Helden, mit dem er angesprochen wird, direkt beim Namen nennen kann bzw. generell der Heldenname im Callback verfügbar ist.
Tatsächlich werden sowohl die _NpcDescription
als auch die Entity-Id des interagierenden Helden an das Callback übergeben. Über die Entity-Id wiederum lässt sich der Skriptname des Helden ermitteln. Nimmt man wie in den obigen Beispielen diese Parameter in der Funktion nicht an, werden sie einfach ignoriert.
In diesem Beispiel haben wir zuvor auf der Karte Dario mit dem Skriptnamen „Dario“
und Ari mit dem Skriptnamen „Ari“
auf der Karte platziert.
-- An der Erstellung des NPCs ändert sich nichts function CreateNpcScout() CreateNPC{ name = "Scout", callback = CreateBriefingScout } end function CreateBriefingScout(_NpcDescription, _HeroId) -- Mit der Funktion GetEntityName kann der Skriptname einer Entity anhand der Entity-Id -- ermittelt werden -- Weil der Skriptname genau dem Anzeigenamen der Helden entspricht, bekommen wir so den -- korrekten Namen für jeden Helden -- Hätten wir Dario den Skriptnamen "Karl" gegeben, würden wir hier stattdessen auch "Karl" erhalten local HeroName = GetEntityName(_HeroId) -- Mit dieser Information können wir nun festlegen, wie der Kundschafter den Helden anspricht local Address = "" if HeroName == "Ari" then Address = "Herrin" else Address = "Herr" end local Briefing = { { title = "Kundschafter", text = Address..", hier ist weiterhin alles ruhig.", position = GetPosition("Scout") }, { -- Der Name des Helden kann hier benutzt werden, um den Titel und die Position der -- Briefingseite korrekt zu setzen title = HeroName, text = "Sehr gut, weitermachen!", position = GetPosition(HeroName) } } StartBriefing(Briefing) end
Weitere Parameter
Im nächsten Beispiel wollen wir weitere Parameter für NPCs demonstrieren und zeigen, dass auf eine NPC-Interaktion nicht notwendigerweise ein Briefing folgen muss. Außerdem zeigen wir, dass einem NPC noch weitere, frei wählbare Werte gegeben werden können, auf die in der NPC-Interaktion zugegriffen werden kann.
Hierfür haben wir Drake (Skriptname „Drake“
) und vier Schafe (Skriptnamen „Sheep1“
bis „Sheep4“
) auf der Karte platziert.
function SetupSheep() -- Alle 4 Schafe sollen zum NPC werden for i = 1, 4 do CreateNpcSheep(i) end end function CreateNpcSheep(_Number) local Npc = { -- Weil wir die Namen der Schafe durchnummeriert haben, können wir hier an das Präfix "Sheep" einfach -- die Zahl anhängen name = "Sheep".._Number, -- Zusätzlicher Wert: Wir merken uns die Nummer des Schafs. Das wird im Callback wichtig number = _Number, -- Alle Schafe benutzen das gleiche Callback! callback = SheepInteraction, -- Alle Schafe sollen Drake folgen follow = "Drake" } CreateNPC(Npc) end function SheepInteraction(_NpcDescription, _HeroId) -- Sobald Drake mit einem der Schafe interagiert, soll es einfach nur "Määh" machen Sound.PlayGUISound(Sounds.AmbientSounds_Sheep_rnd_1) -- Damit das "angesprochene" Schaf Drake weiterhin folgt, muss der Schaf-NPC erneut erstellt werden -- Dazu benutzen wir die oben definierte Funktion CreateNpcSheep und geben als Parameter die Nummer -- an, die wir uns beim ersten Erstellen des Schaf-NPCs gemerkt haben CreateNpcSheep(_NpcDescription.number) end
Funktionen für NPCs
Es gibt einige Funktionen, die für NPCs benutzt werden können, die im Folgenden kurz erklärt sind.
Einen NPC deaktivieren
Mit der Funktion DestroyNPC(_NpcDescription)
kann ein zuvor erstellter NPC wieder „zerstört“ werden. Dabei wird nicht die Entity gelöscht, sondern nur die Interaktivität deaktiviert (also das Ausrufezeichen entfernt).
Technisch gesehen arbeitet DestroyNPC
mit einer _NpcDescription
. Für die Funktion relevant ist allerdings nur der Name, sodass man die NPC-Beschreibung verkürzt angeben kann. Will man beispielsweise Drake aus seiner persönlichen Schaf-Hölle befreien, reicht der Aufruf
for i = 1, 4 do DestroyNPC{name = "Sheep"..i} end
Wegpunkte für NPCs
Wir können NPCs wie den Kundschafter oben zwischen verschiedenen Wegpunkten umherwandern lassen. Dazu müssen auf der Karte Entities platziert werden, deren Skriptnamen einem durchnummerierten Schema folgen.
Zum Beispiel können wir dem Kundschafter oben Wegpunkte geben, indem wir einige XD_ScriptEntity
s mit den Skriptnamen „ScoutWaypoint1“
bis „ScoutWaypoint5“
auf der Karte platzieren. Es kann zwar prinzipiell jede Entity als Wegpunkt dienen, zerstörte Entities wie gefällte Bäume oder eingerissene Gebäude können aber für Fehler sorgen. Die Nummerierung muss bei 1 beginnen und darf keine Lücken haben.
Zuerst muss der NPC mit CreateNPC
erstellt werden. Dann kann mit
SetNPCWaypoints("Scout", "ScoutWaypoint", 30)
alle Wegpunkte, deren Namen mit „ScoutWaypoint“
beginnt als Wegpunkte für den Kundschafter festgelegt werden. Die 30 ist die Zeit in Sekunden die gewartet wird, bevor der NPC einen weiteren Wegpunkt ansteuert. Wird die Zeit zu gering gewählt, wird der NPC oft einen der Wegpunkte nicht erreichen, sondern auf halbem Weg abbrechen und einen neuen Wegpunkt ansteuern. Ist die Zeit sehr hoch gewählt, wird der NPC bei jedem Wegpunkt eine zeitlang verweilen.
Die Wegpunkte werden nicht der Reihe nach angesteuert. Es wird (in diesem Fall alle 30 Sekunden) ein Wegpunkt ausgewählt, zu dem der NPC hinläuft. Bei dieser Zufallsauswahl kann der nächste Wegpunkt auch der sein, an dem er sich bereits befindet. Der NPC wird also an diesem Punkt etwas länger stehenbleiben.
Wird ein Zielpunkt während des Spiels zerstört, so wird er nicht automatisch aus der Liste entfernt. Die so entstandene „Lücke“ in den durchnummerierten Wegpunkten sorgt dafür, dass alle Wegpunkte, deren Nummerierung nach der der zerstörten Entity kommt, nicht mehr besucht werden können.
Dem NPC kann zu jedem Zeitpunkt neue Wegpunkte zuweisen. Außerdem verweilt der NPC automatisch an einem Wegpunkt, wenn ein Held in der Nähe ist, was das Ansprechen erleichtert. Er bleibt allerdings nicht automatisch stehen, wenn er einen Held auf seinem Weg trifft.
NPC-Status abfragen
Um zu prüfen, ob mit einem NPC bereits gesprochen wurde, wird die Funktion TalkedToNPC(_NpcDescription)
benutzt. Wie auch DestroyNPC
arbeitet diese Funktion nur mit dem Namen der NPC-Beschreibung. Deshalb ist folgender Aufruf ausreichend:
TalkedToScout = TalkedToNPC{name = "Scout"}
NPCs können nun mit Helden sprechen und ihnen Aufträge erteilen. Wie die Erfüllung dieser Aufträge ermittelt wird, wird im nächsten Kapitel gezeigt.
Nächstes Kapitel: Orte erreichen, Gebäude bauen, Gegner besiegen
Zurück nach oben