Benutzer-Werkzeuge

Webseiten-Werkzeuge


scripting:tutorials:level2:find_entities

Dies ist eine alte Version des Dokuments!


Spieler-Einheiten und -Gebäude finden

Es gibt verschiedene Anwendungsfälle, in denen man bestimmte Gebäude oder Einheiten eines Spielers auf der Karte finden möchte. Beispielsweise könnte man ermitteln, wie viele Kanonentürme ein Spieler besitzt oder wie stark seine Militärpräsenz in einem gegebenen Gebiet ist. Auch im Zusammenhang mit SetupEstablish wird so eine Funktion oft gebraucht, um das vom Spieler gebaute Gebäude dem Spieler des Auftraggebers zu überreichen.

In diesem Artikel wollen wir deshalb einige Funktionen beleuchten, die Spieler-Einheiten und -Gebäude nach einstellbaren Kriterien finden. Konkret werden bei jeder dieser Funktionen die Entity-Ids jener Entities ermittelt, auf die die Kriterien (wie zum Beispiel Ort und Entity-Typ) zutreffen.

Auch handelt es sich bei den folgenden Funktionen um Logic.-Funktionen, sind also nicht mehr innerhalb des Comfort-Layers. Das bedeutet, dass wir Skriptnamen und Positions-Tables händisch verarbeiten müssen, da das nicht mehr automatisch im Hintergrund geschieht. Auch werden diese Funktionen mit mehreren Rückgabewerten und optionalen Parametern arbeiten. Die entsprechenden Stellen im verlinkten Artikel sollten also geläufig sein.


Logic.GetEntities

Logic.GetEntities(_EntityType, _Amount) gibt höchstens _Amount Entity-Ids zurück, deren Entities vom Typ _EntityType sind. Die Entity-Suche ist nicht weiter beschränkt, bezieht sich also auf die komplette Karte.

Die Funktion gibt mehrere Werte zurück, wovon der erste die Anzahl der gefundenen Entities ist und deshalb keine gültige Entity-Id ist!

Alle weiteren Rückgabewerte sind Entity-Ids der gefundenen Entities vom Typ _EntityType. Dabei entspricht die Anzahl der Rückgabewerte der Anzahl an gefundenen Entities und deshalb nicht notwendigerweise _Amount (wenn weniger als _Amount Entities vom Typ _EntityType auf der Karte existieren).
Das bedeutet, dass die Funktion genau 1 + #Entities Rückgabewerte hat.

Achtung: _Amount ist auf den Maximalwert 16 beschränkt! Mehr Entity-Ids kann die Funktion nicht zurückgeben. Wenn du beispielsweise bei _Amount 20 angibst, werden trotzdem maximal 16 Entity-Ids zurückgegeben.

Wie im Artikel zu mehreren Rückgabewerten beschrieben, können diese Rückgabewerte auf unterschiedliche Arten in Variablen geschrieben werden.

Variante 1: Direkt in Variablen

Diese Variante ist vor allem sinnvoll, wenn nicht mehr als 1 oder 2 Entities gesucht werden, da für jede potenziell gefundene Entity eine eigene Variable angelegt werden muss:

-- In diesem Beispiel suchen wir nach höchstens zwei Tempelaltären
local NumberOfEntities, EntityId1, EntityId2 = Logic.GetEntities(Entities.XD_TemplarAltar, 2)

Die Funktion kann maximal 2 Tempelaltäre finden, aber auch weniger. Davon abhängig resultieren unterschiedliche Rückgabewerte:

  • Wenn NumberOfEntities== 0, dann sind sowohl EntityId1 als auch EntityId2 beide nil
  • Wenn NumberFoundEntities == 1, dann ist EntityId1 die Entity-Id eines Tempelaltars und EntityId2 ist nil
  • Wenn NumberFoundEntities == 2, dann sind sowohl EntityId1 als auch EntityId2 Entity-Ids eines Tempelaltars

Diese Variante eignet sich vor allem, um zu prüfen, ob ein Entity-Typ überhaupt auf der Karte existiert:

function IsEntityTypeOnMap(_EntityType)
    -- Es ist guter Stil, mit dem Unterstrich anzuzeigen, dass dieser Wert ignoriert wird. 
    -- Auf der Logikebene macht es keinen Unterschied, für den Leser wird aber schneller ersichtlich, 
    -- welche Variablen von Bedeutung sind
    local NumberOfEntities, _ = Logic.GetEntities(_EntityType, 1)
    return NumberOfEntities > 0
end

Variante 2: Rückgabe in ein Table

Wenn man deutlich mehr als zwei Entities sucht, bekommt man über Variante 1 eine unübersichtliche Schreibarbeit. Wir suchen den selben Entity-Typ diesmal deutlich öfter:

-- In diesem Beispiel suchen wir nach höchstens acht Tempelaltären
local FoundEntityIds = { Logic.GetEntities(Entities.XD_TemplarAltar, 8) }

Die größe des Tables ist dabei immer 1 + #Entities, wobei in FoundEntityIds[1] die Anzahl der gefundenen Entities steht. Wenn FoundEntityIds[1] == 0 ist, ist das Table an der Stelle zuende. Andernfalls ist genau die Anzahl an EntityIds ebenfalls mit im Table (bei FoundEntities[1] == 4 sind beispielsweise FoundEntities[2] bis FoundEntities[5] befüllt).

Beispiel Anwendungsfall

Um die Siedlungsplätze auf unserer Karte etwas heimeliger zu machen, wollen wir vor jedem eine kleine Sitzbank aufstellen. Mit Logic.GetEntities holen wir uns die Entity-Ids aller Siedlungsplätze, emitteln anhand der Ids deren Position und stellen versetzt zu diesen Positionen Bänke auf.

function PlaceBenches()
    -- es sollten nicht mehr als 16 Siedlungsplätze auf der Karte sein
    local VillageCenterIds = { Logic.GetEntities(Entities.XD_VillageCenter, 16) }
 
    -- Im ersten Eintrag vom Table VillageCenterIds steht die Anzahl der gefundenen Entities
    -- Die benutzen wir direkt für die Schleife
    -- Die Zählvariable beginnt bei 2, da der erste Eintrag keine Entity-Id ist
    for i = 2, VillageCenterIds[1] + 1 do
        local VCPosition = GetPosition(VillageCenterIds[i])
        -- Liebes BlueByte, die Sitzbank heißt auf Englisch "Bench", nicht "Bank"! :<
        -- Wir verwenden hier Logic.CreateEntity, um die Rotation der Bank einstellen zu können
        Logic.CreateEntity(Entities.XD_MiscBank1, VCPosition.X + 300, VCPosition.Y - 600, 0, 0)
    end
end

Logic.GetEntitiesInArea

Logic.GetEntitiesInArea(_EntityType, _PositionX, _PositionY, _Radius, _Amount) gibt höchstens _Amount Entity-Ids zurück, deren Entities vom Typ _EntityType sind und höchstens den Abstand _Radius von der Position (_PositionX, _PositionY) haben.

Die Funktion gibt mehrere Werte zurück, wovon der erste die Anzahl der gefundenen Entities ist und deshalb keine gültige Entity-Id ist!

Alle weiteren Rückgabewerte sind Entity-Ids der gefundenen Entities vom Typ _EntityType. Dabei entspricht die Anzahl der Rückgabewerte der Anzahl an gefundenen Entities und deshalb nicht notwendigerweise _Amount (wenn weniger als _Amount Entities vom Typ _EntityType auf der Karte existieren).
Das bedeutet, dass die Funktion genau 1 + #Entities Rückgabewerte hat.

Achtung: _Amount ist auf den Maximalwert 16 beschränkt! Mehr Entity-Ids kann die Funktion nicht zurückgeben. Wenn du beispielsweise bei _Amount 20 angibst, werden trotzdem maximal 16 Entity-Ids zurückgegeben.

Wie im Artikel zu mehreren Rückgabewerten beschrieben, können diese Rückgabewerte auf unterschiedliche Arten in Variablen geschrieben werden.

Variante 1: Direkt in Variablen

Diese Variante ist vor allem sinnvoll, wenn nicht mehr als 1 oder 2 Entities gesucht werden, da für jede potenziell gefundene Entity eine eigene Variable angelegt werden muss:

-- In diesem Beispiel suchen wir nach höchstens zwei Tempelaltären
local NumberOfEntities, EntityId1, EntityId2 = Logic.GetEntities(Entities.XD_TemplarAltar, 2)

Die Funktion kann maximal 2 Tempelaltäre finden, aber auch weniger. Davon abhängig resultieren unterschiedliche Rückgabewerte:

  • Wenn NumberOfEntities== 0, dann sind sowohl EntityId1 als auch EntityId2 beide nil
  • Wenn NumberFoundEntities == 1, dann ist EntityId1 die Entity-Id eines Tempelaltars und EntityId2 ist nil
  • Wenn NumberFoundEntities == 2, dann sind sowohl EntityId1 als auch EntityId2 Entity-Ids eines Tempelaltars

Diese Variante eignet sich vor allem, um zu prüfen, ob ein Entity-Typ überhaupt im gegebenen Gebiet vorhanden ist:

function IsEntityTypeInArea(_EntityType, _Position, _Radius)
    -- Es ist guter Stil, mit dem Unterstrich anzuzeigen, dass dieser Wert ignoriert wird. 
    -- Auf der Logikebene macht es keinen Unterschied, für den Leser wird aber schneller ersichtlich, 
    -- welche Variablen von Bedeutung sind
    local NumberOfEntities, _ = Logic.GetEntitiesInArea(_EntityType, _Position.X, _Position.Y, _Radius, 1)
    return NumberOfEntities > 0
end

Variante 2: Rückgabe in ein Table

Wenn man deutlich mehr als zwei Entities sucht, bekommt man über Variante 1 eine unübersichtliche Schreibarbeit. Wir suchen den selben Entity-Typ diesmal deutlich öfter:

-- In diesem Beispiel suchen wir nach höchstens acht Tempelaltären
local FoundEntityIds = { Logic.GetEntities(Entities.XD_TemplarAltar, 8) }

Die größe des Tables ist dabei immer 1 + #Entities, wobei in FoundEntityIds[1] die Anzahl der gefundenen Entities steht. Wenn FoundEntityIds[1] == 0 ist, ist das Table an der Stelle zuende. Andernfalls ist genau die Anzahl an EntityIds ebenfalls mit im Table (bei FoundEntities[1] == 4 sind beispielsweise FoundEntities[2] bis FoundEntities[5] befüllt).

Beispiel Anwendungsfall

Bestimmte Bäume durch tote Bäume ersetzen (Winter)

Logic.GetPlayerEntities

Beispiel Anwendungsfall

Alle kleinen Wohnhäuser automatisch ausbauen

Logic.GetPlayerEntitiesInArea

Dafür brauchst du die Funktion Logic.GetPlayerEntitiesInArea(_playerId, _entityType, _posX, _posY, _range, _amount, accessCategory). Damit kann man Entities eines Spielers in einem bestimmten Gebiet finden.

Die Funktion gibt alle gefundenen Entities als EntityId einzeln aus, was möglicherweise ein bisschen ungewohnt ist. Außerdem ist der erste Rückgabewert keine EntityId, sondern die Anzahl der gefundenen EntityIds.

Ein Beispielaufruf zur Anschauung, bei dem an Position {X=4200, Y=4200} 2 kleine Wohnhäuser von Spieler 5 im Umkreis von 1500 Siedler-cm gesucht werden:

NumberFoundEntities, EntityId1, EntityId2 = Logic.GetPlayerEntitiesInArea(5, Entities.PB_Residence1, 4200, 4200, 1500, 2)

(die accessCategory kann man weglassen)

Die Funktion kann maximal 2 Wohnhäuser finden, aber auch weniger. Davon abhängig bekommst du unterschiedliche Rückgabewerte: - Wenn NumberFoundEntities == 0, dann sind sowohl EntityId1 als auch EntityId2 beide nil - Wenn NumberFoundEntities == 1, dann ist EntityId1 die EntityId eines kleinen Wohnhauses von Spieler 5 im Gebiet und EntityId2 ist nil - Wenn NumberFoundEntities == 2, dann sind sowohl EntityId1 als auch EntityId2 EntityIds eines kleinen Wohnhauses von Spieler 5 im Gebiet

Wenn man mehr als 2 Gebäude sucht (in deinem Fall nicht, aber zur Vollständigkeit), bekommt man da eine riesige Schreibarbeit. Deshalb gibt man meistens die Ergebnisse der Funktion in ein Table, indem man um den Funktionsaufruf herum geschweifte Klammern setzt. Angenommen wir suchen stattdessen 10 Wohnhäuser:

FoundEntities = { Logic.GetPlayerEntitiesInArea(5, Entities.PB_Residence1, 4200, 4200, 1500, 10) }

In dem Fall ist FoundEntities[1] immer die Anzahl der gefundenen Entities und kann auch 0 sein. Dann ist das Table dort zuende. Wenn sie größer 0 ist, ist genau die Anzahl an EntityIds ebenfalls mit im Table (bei FoundEntities[1] == 4 sind beispielsweise FoundEntities[2] bis FoundEntities[5] befüllt)

Für deinen Fall würde ich deshalb Folgendes schreiben:

-- Eigentlich sollte man nicht auf deutsch programmieren. Versuche, auf englische Namen umzusteigen
-- Kommentare kannst du dann dafür nutzen, zu erklären, was die Funktion macht, also z.B:
-- Diese Funktion wird nach dem Bau der Garnision bei Position "kaserne" im Zusammenhang mit der Quest ??? aufgerufen 
-- Sie übergibt die errichtete Garnision an Spieler 2 
-- (usw. was auch immer noch folgt)
-- Falls mal etwas nicht funktioniert, kannst du anhand solcher Notizen schneller prüfen, ob die Funktion
-- das tut, was sie soll
function Kasernefertig()
    -- Du lässt die Kaserne an Position "kaserne" mit Umkreis 3000 Siedler-cm bauen. Die Position muss man erst
    -- holen:
    local QuestPosition = GetPosition("kaserne")
    -- Wir brauchen die Variablen hier nur lokal. Kein Grund, sie global zu definieren
    -- Die Position ist jetzt in QuestPosition, der Umkreis ist 3000, Spieler hat Id 1 und wir suchen genau die eine Kaserne,
    -- die gebaut werden sollte
    local NumberFoundEntities, EntityId1 = Logic.GetPlayerEntitiesInArea(1, Entities.PB_Barracks2, QuestPosition.X, QuestPosition.Y, 3000, 1)
    -- Wenn die Quest korrekt funktioniert, sollte diese Bedingung immer zutreffen
    if NumberFoundEntities > 0 then
        -- Wechsle die Spieler-Id der gefundenen Kaserne zu Spieler 2
        ChangePlayer(EntityId1, 2)
    end
end

Beispiel Anwendungsfall

Gebäude nach Quest übergeben

Beliebige Gebäude oder Einheiten eines Spielers im Gebiet finden

scripting/tutorials/level2/find_entities.1699437691.txt.gz · Zuletzt geändert: 2023/11/08 10:01 von fritz_98