Benutzer-Werkzeuge

Webseiten-Werkzeuge


scripting:tutorials:level2:tribute

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen der Seite angezeigt.

Link zu der Vergleichsansicht

Nächste Überarbeitung
Vorherige Überarbeitung
scripting:tutorials:level2:tribute [2023/11/08 15:30] – angelegt fritz_98scripting:tutorials:level2:tribute [2023/11/30 09:28] (aktuell) fritz_98
Zeile 6: Zeile 6:
 <code lua> <code lua>
 -- Komfortfunktion für das Bereitstellen von Tributen -- Komfortfunktion für das Bereitstellen von Tributen
-function AddTribute(_tribute+function AddTribute(_Tribute
-    assert(type(_tribute) == "table", "Tribut muß ein Table sein"); +    assert(type(_Tribute) == "table", "Tribut must be a table, got .. type(_Tribute)
-    assert(type(_tribute.text) == "string", "Tribut.text muß ein String sein"); +    assert(type(_Tribute.Text) == "string", "Text must be a string, got .. type(_Tribute.Text)
-    assert(type(_tribute.cost) == "table", "Tribut.cost muß ein Table sein"); +    assert(type(_Tribute.Cost) == "table", "Cost must be a table, got .. type(_Tribute.Cost)
-    assert(type(_tribute.playerId) == "number", "Tribut.playerId muß eine Nummer sein"); +    assert(type(_Tribute.PlayerId) == "number", "PlayerId must be a numbergot " .. type(_Tribute.PlayerId))
-    assert(not _tribute.Tribute, "Tribut.Tribute darf nicht vorbelegt sein");+
  
     -- Hier wird eine globale Variable gesetzt, die außerhalb dieser Funktion nicht manipuliert werden sollte     -- Hier wird eine globale Variable gesetzt, die außerhalb dieser Funktion nicht manipuliert werden sollte
-    uniqueTributeCounter uniqueTributeCounter or 1; +    TributeIdCounter TributeIdCounter or 1 
-    _tribute.Tribute = uniqueTributeCounter; +    _Tribute.Tribute = TributeIdCounter 
-    uniqueTributeCounter uniqueTributeCounter + 1;+    TributeIdCounter TributeIdCounter + 1
  
-    local tResCost = {}; +    local CostTable = {} 
-    for kin pairs(_tribute.cost) do +    for ResourceNameResourceAmount in pairs(_Tribute.Cost) do 
-        assert(ResourceType[k]); +        assert(ResourceType[ResourceName], ResourceName .. " is not a valid resource"
-        assert(type(v) == "number"); +        assert((type(ResourceAmount) == "number"and (ResourceAmount >= 0), "Resource costs must be a positive number, got " .. ResourceName .. " = " .. type(ResourceAmount)) 
-        table.insert(tResCost, ResourceType[k]); +        table.insert(CostTable, ResourceType[ResourceName]) 
-        table.insert(tResCostv);+        table.insert(CostTableResourceAmount)
     end     end
  
-    Logic.AddTribute(_tribute.playerId_tribute.Tribute, 0, 0, _tribute.text, unpack(tResCost)); +    Logic.AddTribute(_Tribute.PlayerId_Tribute.Tribute, 0, 0, _Tribute.Text, unpack(CostTable)) 
-    SetupTributePaid(_tribute);+    SetupTributePaid(_Tribute)
  
-    return _tribute.Tribute;+    return _Tribute.Tribute
 end end
 </code> </code>
 Die Tribut-Handhabung wird dadurch deutlich erleichtert. Die Tribut-Handhabung wird dadurch deutlich erleichtert.
 +
 +**Hinweis**: Die Groß- und Kleinschreibung der Keys wurden in dieser Version der Komfortfunktion der der anderen Keys angepasst.
  
 ---- ----
Zeile 38: Zeile 39:
 ====Einfache Tribute erstellen==== ====Einfache Tribute erstellen====
  
-Die oben gezeigte Funktion ''AddTribute(_tribute)'' hat nur einen Parameter ''_tribute''. ''_tribute'' muss ein Table sein, das den zu erstellenden Tribut beschreibt. Ein Standard-Tribut hat nur wenige notwendige Parameter:+Die oben gezeigte Funktion ''AddTribute(_Tribute)'' hat nur einen Parameter ''_Tribute''. ''_Tribute'' muss ein Table sein, das den zu erstellenden Tribut beschreibt. Ein Standard-Tribut hat nur wenige notwendige Parameter:
  
 ^Key^Value-Typ^Bedeutung^ ^Key^Value-Typ^Bedeutung^
-|**playerId**|Player Id (Ganze Zahl 1-8)|Spieler-Id des Spielers, der den Tribut bezahlen kann| +|**PlayerId**|Player Id (Ganze Zahl 1-8)|Spieler-Id des Spielers, der den Tribut bezahlen kann| 
-|**text**|String|Text, der im Tributmenü angezeigt wird. Er sollte die Kosten des Tributs und dessen Wirkung beschreiben| +|**Text**|String|Text, der im Tributmenü angezeigt wird. Er sollte die Kosten des Tributs und dessen Wirkung beschreiben| 
-|**cost**|Table|Die Kosten des Tributs in einem [[ scripting:tutorials:level1:tables#tables_als_woerterbuecher|assoziativen Table]], bei dem der Ressourcentyp Key und die geforderte Menge Value ist (siehe auch die folgenden Beispiele)|+|**Cost**|Table|Die Kosten des Tributs in einem [[ scripting:tutorials:level1:tables#tables_als_woerterbuecher|assoziativen Table]], bei dem der Ressourcentyp Key und die geforderte Menge Value ist (siehe auch die folgenden Beispiele)|
 |**Callback**|Funktion|Funktion, die aufgerufen wird, sobald der Tribut bezahlt wurde| |**Callback**|Funktion|Funktion, die aufgerufen wird, sobald der Tribut bezahlt wurde|
  
-**Wichtig**: Beachte bei den Keys des ''_tribute''-Tables sowie des ''cost''-Tables darin auf die korrekte Groß- und Kleinschreibung! Die ist leider nicht über alle Parameter hinweg konsistent. 
  
 In einem einfachen Tribut-Beispiel soll Spieler 1 die Eisengrube eines Verbündeten für 500 Gold und 300 Holz erstehen können. Die Eisengrube existiert auf der Karte und hat den Skriptnamen ''"IronMine"''. In einem einfachen Tribut-Beispiel soll Spieler 1 die Eisengrube eines Verbündeten für 500 Gold und 300 Holz erstehen können. Die Eisengrube existiert auf der Karte und hat den Skriptnamen ''"IronMine"''.
Zeile 52: Zeile 52:
 function CreateTributeBuyIronMine() function CreateTributeBuyIronMine()
     local Tribute = {     local Tribute = {
-        playerId = 1, +        PlayerId = 1, 
-        text = "Für 500 Taler und 300 Holz erhaltet Ihr die Eisenmine Eures Verbündeten", +        Text = "Für 500 Taler und 300 Holz erhaltet Ihr die Eisenmine Eures Verbündeten", 
-        cost = {+        Cost = {
             -- Die Namen der Ressourcen werden groß geschrieben             -- Die Namen der Ressourcen werden groß geschrieben
             Gold = 500,             Gold = 500,
Zeile 71: Zeile 71:
 end end
 </code> </code>
 +
 +**Wichtig:** Es können maximal **6** Tribute gleichzeitig im F3-Menü angezeigt werden! Wenn du weitere Tribute hinzufügst, hat der Spieler keinen Zugriff mehr darauf.
  
 ---- ----
Zeile 77: Zeile 79:
  
 Die Verwendung einer Callback-Funktion erlaubt es dir, maximal flexibel auf die Entrichtung eines Tributs zu reagieren. Für einige Standardfälle gibt es allerdings vorgefertigte, optionale Parameter, die ein Standard-Callback automatisch generieren und ausführen. Die Verwendung einer Callback-Funktion erlaubt es dir, maximal flexibel auf die Entrichtung eines Tributs zu reagieren. Für einige Standardfälle gibt es allerdings vorgefertigte, optionale Parameter, die ein Standard-Callback automatisch generieren und ausführen.
 +
 +**Achtung**: Diese optionalen Parameter sollten nur im Singleplayer benutzt werden!
 +
 +^Key^Value-Typ^Bedeutung^
 +| **Resources** | Table (assoziativ) | [[ scripting:tutorials:level1:tables#tables_als_woerterbuecher|Assoziatives Table]], bei dem der Ressourcentyp Key und die geforderte Menge Value ist (**Achtung**: Keys hier kleingeschrieben!). Der Spieler erhält die angegebene Menge an Ressourcen |
 +| **Technologies** | Table (Liste) | Liste der Technologien, die beim Bezahlen des Tributs für den Spieler automatisch erforscht werden |
 +| **Entity** | String oder Zahl | Skriptname oder Entity-Id der Entity, die nach Bezahlung zum Spieler wechselt |
 +| **Entities** | String | Präfix eines Skriptnames, der durchnummeriert Entities bezeichnet, die nach Bezahlung zum Spieler wechseln (also zum Beispiel ''"Serf"'' für ''"Serf1"'', ''"Serf2"'', usw.) |
 +| **Ralleypoint** | String oder Zahl | Skriptname oder Entity-Id der Entity, zu der die nach Bezahlung zum Spieler gewechselten Entities automatisch gehen (falls Siedler) |
 +| **Spawn** | Table (Liste) | Liste an Truppenbeschreibungen für Truppen, die nach der Bezahlung erscheinen sollen (siehe Einträge unterhalb) |
 +| **Spawn[i].Pos** | String oder Zahl | Für Truppenspawn: Skriptname oder Entity-Id der Entity, an deren Position Truppen spawnen sollen (pro Trupp) |
 +| **Spawn[i].LeaderType** | Zahl (Leader Entity Type) | Für Truppenspawn: Entity-Typ (''Entities.'') des zu spawnenden Hauptmanns (pro Trupp) |
 +| **Spawn[i].Soldiers** | Ganze Zahl | Für Truppenspawn: Anzahl der Soldaten für den Hauptmann |
 +| **Spawn[i].Ralleypoint** | String oder Zahl | Für Truppenspawn: Skriptname oder Entity-Id der Entity, zu der der gespawnte Trupp gehen soll (pro Trupp) |
 +| **Spawn[i].AttackRalleypoint** | String oder Zahl | Für Truppenspawn: Skriptname oder Entity-Id der Entity, deren Position der gespawnte Trupp angreifen soll (pro Trupp) |
 +
 +**Hinweis**: Für die **Spawn**-Option müssen pro Trupp alle Parameter angegeben werden, wobei die ''Ralleypoint'' und ''AttackRalleypoint''-Optionen sich gegenseitig ausschließen.
 +
 +Für jeden dieser optionalen Parameter wollen wir ein kleines Beispiel und dessen Voraussetzungen geben.
 +
 +===Ressourcentausch===
 +
 +Ein Händler bietet einen Tribut zum Tausch einer Ressource gegen eine andere an:
 +<code lua>
 +function CreateTributeBuyIron()
 +    local Tribute = {
 +        PlayerId = 1,
 +        Text = "Für 500 Holz erhaltet Ihr 300 Eisen",
 +        Cost = {
 +            Wood = 500
 +        },
 +        Resources = {
 +            -- Achtung! Die Keys der erhaltenen Ressourcen werden klein geschrieben!
 +            iron = 300
 +        }
 +    }
 + 
 +    AddTribute(Tribute)
 +end
 +</code>
 +
 +===Technologien per Tribut===
 +
 +Ein Dieb bietet an, die Technologie "Sabotieren" zu verkaufen:
 +<code lua>
 +function CreateTributeBuySabotage()
 +    local Tribute = {
 +        PlayerId = 1,
 +        Text = "Für 700 Taler und 400 Schwefel erhaltet Ihr die Technologie 'Sabotage'",
 +        Cost = {
 +            Gold = 700,
 +            Sulfur = 400
 +        },
 +        Technologies = { Technologies.T_ThiefSabotage }
 +    }
 +
 +    AddTribute(Tribute)
 +end
 +</code>
 +
 +===Einzelne Entities kaufen===
 +
 +Wie das Beispiel von ganz oben: Der Spieler kann eine Eisengrube erstehen. Sie hat den Skriptnamen ''"IronMine"'' auf der Karte:
 +
 +<code lua>
 +function CreateTributeBuyIronMine()
 +    local Tribute = {
 +        PlayerId = 1,
 +        Text = "Für 500 Taler und 300 Holz erhaltet Ihr die Eisenmine Eures Verbündeten",
 +        Cost = {
 +            Gold = 500,
 +            Wood = 300
 +        },
 +        Entity = "IronMine"
 +    }
 +    
 +    AddTribute(Tribute)
 +end
 +</code>
 +
 +===Mehrere Entities kaufen===
 +
 +Dario kauft sich zu Beginn einer Partie einige Leibeigene. Für das folgende Beispiel müssen auf der Karte eine Entity mit dem Skriptnamen ''"Dario"'' existieren, sowie einige Leibeigene, die nach dem Schema ''"Serf1"'', ''"Serf2"'', usw. benannt sind.
 +
 +<code lua>
 +function CreateTributeBuySerfs()
 +    local Tribute = {
 +        PlayerId = 1,
 +        Text = "Für 200 Taler erhaltet Ihr einige Leibeigene",
 +        Cost = {
 +            Gold = 200,
 +        },
 +        -- Alle Entities mit dem Namen "Serf"..i werden dem Spieler übergeben...
 +        Entities = "Serf",
 +        -- ...und laufen nach dem Kauf zu Dario
 +        Ralleypoint = "Dario"
 +    }
 +    
 +    AddTribute(Tribute)
 +end
 +</code>
 +
 +===Truppen kaufen===
 +
 +Dario holt sich bei einem befreundeten Dorf Verstärkung. Damit die Truppen eine Position zum Spawnen haben, sollte eine ''ScriptEntity'' angelegt werden, die wir hier ''"TroopSpawn"'' nennen. Auch muss wieder ein ''"Dario"'' auf der Karte sein, zu dem die Truppen laufen können.
 +
 +<code lua>
 +function CreateTributeBuyTroops()
 +    local Tribute = {
 +        PlayerId = 1,
 +        Text = "Für 800 Taler, 400 Holz und 200 Eisen erhaltet Ihr vom Nachbardorf einige Truppen zur Unterstützung",
 +        Cost = {
 +            Gold = 800,
 +            Wood = 400,
 +            Iron = 200
 +        },
 +        -- Das Table Spawn ist eine Liste, in der die zu spawnenden Truppen nacheinander beschrieben sind
 +        Spawn = {
 +            -- Gespawnt werden:
 +            -- 1 Trupp Kurzschwertkämpfer
 +            { Pos = "TroopSpawn", LeaderType = Entities.PU_LeaderSword1, Soldiers = 4, Ralleypoint = "Dario" },
 +            -- 2 Truppen Kurzbogenschützen
 +            { Pos = "TroopSpawn", LeaderType = Entities.PU_LeaderBow1, Soldiers = 4, Ralleypoint = "Dario" },
 +            { Pos = "TroopSpawn", LeaderType = Entities.PU_LeaderBow1, Soldiers = 4, Ralleypoint = "Dario" },
 +            -- und 1 Trupp schwerer Reiter
 +            { Pos = "TroopSpawn", LeaderType = Entities.PU_LeaderHeavyCavalry1, Soldiers = 3, Ralleypoint = "Dario" }
 +        }
 +    }
 +    
 +    AddTribute(Tribute)
 +end
 +</code>
 +
 +----
  
 ====Tribute entfernen==== ====Tribute entfernen====
 +
 +Um Tribute wieder zu entfernen, wird die Funktion ''Logic.RemoveTribute(_PlayerId, _TributeId)'' verwendet. Die ''_TributeId'' ist dabei der Rückgabewert der Funktion ''AddTribute''. Wenn du beispielsweise so einen Tribut definierst:
 +
 +<code lua>
 +function CreateTributeBuyIronMine()
 +    local Tribute = {
 +        PlayerId = 1,
 +        Text = "Für 500 Taler und 300 Holz erhaltet Ihr die Eisenmine Eures Verbündeten",
 +        Cost = {
 +            Gold = 500,
 +            Wood = 300
 +        },
 +        Callback = CallbackTributeBuyIronMine
 +    }
 +    
 +    return Tribute.PlayerId, AddTribute(Tribute)
 +end
 +</code>
 +
 +bekommst du die Spieler-Id und die Tribut-Id beim Aufruf zurück:
 +
 +<code lua>
 +TributeBuyIronMinePlayerId, TributeBuyIronMineTributeId = CreateTributeBuyIronMine()
 +</code>
 +
 +Danach kannst du mit diesen beiden Variablen den Tribut wieder entfernen:
 +
 +<code lua>
 +Logic.RemoveTribute(TributeBuyIronMinePlayerId, TributeBuyIronMineTributeId)
 +</code>
 +
 +----
  
 ====Zusätzliche Parameter==== ====Zusätzliche Parameter====
 +
 +Du kannst dem ''_Tribute''-Table auch noch weitere Key-Value-Paare hinzufügen. Diese haben zwar keine automatische Auswirkung, können aber im Callback benutzt werden, da das gesamte ''_Tribute''-Table der Callback-Funktion als Parameter übergeben wird. Das ist in der Regel vor allem nützlich, wenn mehrere Tribute die gleiche Callback-Funktion nutzen.
 +
 +Auf diese Weise lässt sich zum Beispiel eine Schwierigkeitswahl definieren:
 +<code lua>
 +function InitDifficultyChoice()
 +    -- Wir geben den Schwierigkeitsgraden Namen, um sie später im Skript leichter identifizieren zu können
 +    DifficultyModes = {
 +        Easy = 1,
 +        Normal = 2,
 +        Hard = 3,
 +        VeryHard = 4
 +    }
 +    
 +    -- Jeder Schwierigkeitsgrad soll im Tribut als Name angezeigt werden können. Da diese Namen nur für die
 +    -- Tribute gebraucht werden und nicht global identifizierbar sein müssen, genügt es, dieses Table lokal
 +    -- zu definieren
 +    local DifficultyModeNames = {
 +        [DifficultyModes.Easy] = "Leicht",
 +        [DifficultyModes.Normal] = "Normal",
 +        [DifficultyModes.Hard] = "Schwer",
 +        [DifficultyModes.VeryHard] = "Sehr Schwer"
 +    }
 +    
 +    -- Eine globale Variable soll später den gewählten Schwierigkeitsgrad ausdrücken, deshalb setzen wir sie
 +    -- hier zurück
 +    -- 0 ist laut unserem DifficultyModes-Table noch kein gültiger Schwierigkeitsgrad
 +    Difficulty = 0
 +    
 +    -- Es soll nur einer der angebotenen Schwierigkeitsgrade ausgewählt werden können
 +    -- Wir speichern deshalb alle Tribute-Ids in einem Table zwischen
 +    -- Später werden alle Tribute außer der gewählte wieder entfernt
 +    DifficultyTributeIds = {}
 +    -- Für jeden Schwierigkeitsgrad soll ein Tribut angelegt werden...
 +    for DifficultyMode, DifficultyModeName in ipairs(DifficultyModeNames) do
 +        -- ...also brauchen wir die Zahl, die ihn ausdrückt und seinen Namen, den wir im lokalen
 +        -- Table festgehalten haben
 +        local TributeId = CreateTributeDifficultyChoice(DifficultyMode, DifficultyModeName)
 +        -- Die Funktion CreateTributeDifficultyChoice gibt die Tribut-Id zurück, die wir im globalen
 +        -- Table DifficultyTributeIds speichern
 +        table.insert(DifficultyTributeIds, TributeId)
 +    end
 +end
 +
 +-- Es reicht eine Funktion für alle Schwierigkeitsgrade
 +-- Für jeden Schwierigkeitsgrad muss nur der Zahlenwert und der Name angegeben werden
 +function CreateTributeDifficultyChoice(_DifficultyMode, _DifficultyName)
 +    local Tribute = {
 +        PlayerId = 1,
 +        -- Der Text benutzt einen der Parameter
 +        Text = "Wählt den Schwierigkeitsgrad " .. _DifficultyName,
 +        -- Die Wahl eines Schwierigkeitsgrades darf nichts kosten, deshalb setzen wir die Kosten
 +        -- einer beliebigen Ressource auf 0
 +        Cost = {
 +            Gold = 0
 +        },
 +        -- Hier speichern wir den Schwierigkeitsgrad, der zu dem Tribut gehört, mit in dem Tribut ab
 +        -- Im Callback können wir auf ihn zugreifen
 +        DifficultyMode = _DifficultyMode,
 +        Callback = CallbackTributeDifficultyChoice
 +    }
 + 
 +    -- Die Tribut-Id muss zurückgegeben werden, damit später alle Schwierigkeitsgradtribute wieder
 +    -- entfernt werden können, sobald einer davon ausgewählt wurde
 +    return AddTribute(Tribute)
 +end
 +
 +function CallbackTributeDifficultyChoice(_Tribute)
 +    -- Innerhalb dieser Funktion haben wir Zugriff auf das Table _Tribute, in dem alle Informationen, 
 +    -- die wir zur Definition des Tributs definiert haben, gespeichert sind
 +    -- _Tribute.DifficultyMode enthält deshalb den gewählten Schwierigkeitsgrad:
 +    Difficulty = _Tribute.DifficultyMode
 +    
 +    -- _Tribute.Tribute enhält außerdem die Tribut-Id des Tributs. Damit und dem DifficultyTributeIds-Table
 +    -- können wir die anderen Schwierigkeitstribute wieder entfernen
 +    for _, TributeId in ipairs(DifficultyTributeIds) do
 +        -- Lösche alle Tribute in DifficultyTributeIds, deren Id von der eigenen unterschiedlich ist
 +        if TributeId ~= _Tribute.Tribute then
 +            -- Alle Tribute waren für Spieler 1
 +            Logic.RemoveTribute(_Tribute.PlayerId, TributeId)
 +        end
 +    end
 +    
 +    -- Hiernach sollte dann der Questablauf starten
 +end
 +</code>
 +
 +Später kann dann anhand der globalen Variable ''Difficulty'' beispielsweise die Menge an Truppen für einen Computergegner gewählt werden:
 +
 +<code lua>
 +function SpawnEnemyHorsemen()
 +    local NumberOfTroops = 0
 +    if Difficulty == DifficultyModes.Easy then
 +        NumberOfTroops = 3
 +    elseif Difficulty == DifficultyModes.Normal then
 +        NumberOfTroops = 5
 +    elseif Difficulty == DifficultyModes.Hard then
 +        NumberOfTroops = 6
 +    elseif Difficulty == DifficultyModes.VeryHard then
 +        NumberOfTroops = 8
 +    end
 +    
 +    for i = 1, NumberOfTroops do
 +        CreateMilitaryGroup(2, Entities.PU_LeaderHeavyCavalry1, 3, GetPosition("EnemyHorsemenSpawn"))
 +    end
 +end
 +</code>
 +
 +----
 +
 +Im nächsten Kapitel wird gezeigt, wie sich das Auftragsmenü füllen und verwalten lässt.
 +
 +[[ scripting:tutorials:level2:find_entities | Voriges Kapitel: Spieler-Einheiten und -Gebäude finden ]] \\
 +[[ scripting:tutorials:level2:quest_menu | Nächstes Kapitel: Das Auftragsmenü ]] \\
 +[[ scripting:tutorials:level2:tribute | Zurück nach oben ]]
scripting/tutorials/level2/tribute.1699457416.txt.gz · Zuletzt geändert: 2023/11/08 15:30 von fritz_98