Autor Thema: [Scripting for Dummies] Missionen in bereits laufende Missionen laden - Teil 2  (Gelesen 4415 mal)

0 Mitglieder und 1 Gast betrachten dieses Thema.

Offline FG28_Kodiak

  • Blasenteetrinker
  • *****
  • Beiträge: 1.893
Wie wir bereits in der letzten Lektion erfahren haben, werden neu hinzugeladene Objekte von den Triggern ignoriert. Man kann hier Abhilfe schaffen indem man die Submissionen mit Triggern versieht.





Es ist ratsam in jeder Mission einen anderen Triggernamen zu verwenden, bei gleichen Triggernamen kann es zu "Merkwürdigkeiten" kommen. Denke hier liegt noch ein Bug in CloDo vor, bei gleichen Triggernamen hatte ich ab und zu das Problem, das der dritte Trigger mal ausgelöst wurde und ein anderes mal nicht :/. Auch kann man gpGetTrigger("Triggername") keine Missionsnummer mitübergeben.

So wir haben jetzt drei SubMissionen jede jeweils mit einem eigenen Triggerkreis für die zu zerstörenden Bodenziele. Jetzt wird es auch an der Zeit diese Trigger miteinander zu verbinden. Man könnte jetzt z.B. als Missionsziel vorgeben das der Spieler (oder die jeweilige Seite) mindestens zwei von drei Teilzielen erfüllen muss um die Mission erfolgreich abzuschließen. Man könnte jetzt natürlich programmieren das Trigger1 UND Trigger2 ODER das Trigger2 UND Trigger3 ODER das Trigger1 UND Trigger3 erfüllt sein müssen um die Mission zu gewinnen. Gibt natürlich dann einen ganz schönen Wust von if - Abfragen. Spätestens wenn wir noch mehr SubMissionen mit eigenen Triggern einbauen wird das ganze mehr als unübersichtlich. Als Alternative bietet sich hier an einfach die Anzahl der ausgelösten Win(oder Fail)-Trigger zu zählen und dann einfach mit einer Zahl zu vergleichen, welche die Anzahl der nötigen Teilzielen darstellt.
Außerdem wäre noch eine Variable nützlich die festlegen ob die Mission bereits erfolgreich bzw. verlorengegangen ist. Diese Variable könnte man dann auch später abfragen, etwa wenn man möchte das die Mission erst nach erfolgreicher Landung gewertet wird.

Kommen wir also zum Code:
using System;
using maddox.game;
using maddox.game.world;


public class Mission : maddox.game.AMission
{
    enum MissionCondition {Neutral, Success, Failure}   
   
    MissionCondition AktuelleMissionCondition = MissionCondition.Neutral;
    const int NeededForMissionSuccess = 2;
    int WinConditionCounter = 0;
   

    private void serverMessage (string msg)
    {
        GamePlay.gpLogServer (null, msg, new object [] {msg});
    }
   
    public override void OnBattleStarted()
    {
        base.OnBattleStarted();
        MissionNumberListener = -1;

        serverMessage ("\nZerstören sie jeweils mindestens 3 Fahrzeuge aus zwei Gruppen\n\n");

        GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen3Sub1.mis");
        GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen3Sub2.mis");
        GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen3Sub3.mis");
    }

    public override void OnTrigger(int missionNumber, string shortName, bool active)
{
        base.OnTrigger(missionNumber, shortName, active);
   
        GamePlay.gpLogServer (null, "OnTrigger({0} | {1} | {2})  wurde ausgelöst", new object [] {missionNumber, shortName, active}); //Nur für Testzwecke, löschen oder auskommentieren wenn Endfassung
   
        if (AktuelleMissionCondition == MissionCondition.Neutral)
        {
            if ("WinCondition1".Equals(shortName) && active)
            {
                WinConditionCounter++;
                GamePlay.gpGetTrigger(shortName).Enable = false;
            }
            if ("WinCondition2".Equals(shortName) && active)
            {
                WinConditionCounter++;
                GamePlay.gpGetTrigger(shortName).Enable = false;
            }
            if ("WinCondition3".Equals(shortName) && active)
            {
                WinConditionCounter++;
                GamePlay.gpGetTrigger(shortName).Enable = false;
            }

            serverMessage ("Anzahl erledigter Teilziele: " + WinConditionCounter.ToString()); // Für Testzwecke.
       
            if (WinConditionCounter >= NeededForMissionSuccess)
            {
                AktuelleMissionCondition = MissionCondition.Success;
                GamePlay.gpHUDLogCenter("Ziel erreicht");
                serverMessage ("Endanzahl: " + WinConditionCounter.ToString()); // Für Testzwecke.
                GamePlay.gpGetTrigger("FailCondition1").Enable = false;
            }

            if ("FailCondition1".Equals(shortName) && active)
            {
                AktuelleMissionCondition = MissionCondition.Failure;
            GamePlay.gpHUDLogCenter("Sie haben die Zeit überschritten");
                serverMessage ("Endanzahl: " + WinConditionCounter.ToString()); // Für Testzwecke.
                GamePlay.gpGetTrigger(shortName).Enable = false;
            }
        }
    }   
}   

enum MissionCondition {Neutral, Success, Failure} enum kennzeichnet einen Aufzählungstyp hier mit Namen MissionCondition, in den geschweiften Klammern befinden sich die Zustände die MissionCondition annehmen kann. Hier steht Neutral für Mission ist noch in der Schwebe, Success für die Mission ist Erfolgreich und Failure dann natürlich für Mission ist fehlgeschlagen. Hinter der geschweiften Klammer kommt kein Semikolon.
MissionCondition AktuelleMissionCondition = MissionCondition.Neutral; Initialisiert eine Variable AktuelleMissionCondition vom Typ MissionCondition mit dem Wert Neutral.
const int NeededForMissionSuccess = 2; Enthält die für das gelingen der Mission nötigen Triggeranzahl im Beispiel mal auf 2 festgelegt, ausserdem wurde die Zahl als Konstante (const) definiert, würde man die Variable später ändern, bekäme man eine Fehlermeldung.
int WinConditionCounter = 0; Das ist unser "Triggerzähler" er wird bei Aufruf eines "WinTriggers" um Eins erhöht. Zu Beginn wurde sein Wert auf 0 gesetzt.
Die gerade gemachten Deklarationen sind in der gesamten Klasse Mission gültig, d.h. sie können von jeder Methode angesprochen und bis auf die Konstante verändert werden.

private void serverMessage (string msg)
{
    GamePlay.gpLogServer (null, msg, new object [] {msg});
}

Hier habe ich etwas neues eingeführt. Ich habe eine Methode programmiert die Zeichenketten in die Chatleiste (bzw. in die Console oder wenn aktiviert im Serverlog) ausgibt.
Verwenden kann man die Methode einfach in dem man z.B.
serverMessage ("BeispielText");
im Programmcode einfügt.

Einfaches Beispiel:
using System;
using maddox.game;
using maddox.game.world;


public class Mission : maddox.game.AMission
{
    private void serverMessage (string msg)
    {
        GamePlay.gpLogServer (null, msg, new object [] {msg});
    }
   
    public override void OnBattleStarted()
    {
        base.OnBattleStarted();
       
        serverMessage("Dieser Text wird zum Beginn der Mission ausgegeben");
    }
}
würde dann folgendes ergeben:


serverMessage hat als Übergabewert eine Zeichenkette und übergibt diese an die gpLogserver Methode der IGamePlay-Klasse.
GamePlay.gpLogServer (null, msg, new object [] {msg});
Schauen wir uns das genauer an
gpLogServer hat drei Übergabeparameter:
void gpLogServer(maddox.game.Player[] to, string format, object[] args)
und zwar wird mit dem ersten Parameter (maddox.game.Player[] to) der Adressat der Meldung angegeben, das können alle Spieler sein (null) oder auch nur ein bestimmter Spieler.
Der zweite Parameter enthält die auszugebende Meldung als String ("Zeichenfolge").
Mit Hilfe des dritten Parameters kann man noch Variablen übergeben, die man im String mit {0}, {1} ... {n} einfügen kann. Wobei {0} die erste Variable, {1} die Zweite, {n} die n-te Variable (je nach Anzahl) bezeichnet. Im ersten Beispiel oben nutze ich folgenden Aufruf (als Debughilfe in OnTrigger):
GamePlay.gpLogServer (null, "OnTrigger({0} | {1} | {2})  wurde ausgelöst", new object [] {missionNumber, shortName, active});
Das gibt z.B. wenn der Trigger WinCondition2 aus der Submission 2 ausgelöst wurde, folgendes in die Chatleiste aus:



Nach diesem kurzen Einschub zurück zum eigentlichen Thema:

Gehen wir zu OnBattleStarted(), dieses bleibt zur Vorgängerversion aus der letzten Lektion weitestgehend gleich nur wird mit Hilfe von serverMessage ein kurzer Erläuterungstext ausgegeben.
serverMessage ("\nZerstören sie jeweils mindestens 3 Fahrzeuge aus zwei Gruppen\n\n");
\n ist eine Steuersequenz und sorgt für einen Zeilenumbruch.


Die größten Änderungen fanden bei OnTrigger statt.

public override void OnTrigger(int missionNumber, string shortName, bool active)
{
    base.OnTrigger(missionNumber, shortName, active);
          
    GamePlay.gpLogServer (null, "OnTrigger({0} | {1} | {2})  wurde ausgelöst", new object [] {missionNumber, shortName, active}); //Nur für Testzwecke, löschen oder auskommentieren wenn Endfassung
   
    if (AktuelleMissionCondition == MissionCondition.Neutral)
    {
        if ("WinCondition1".Equals(shortName) && active)
        {
            WinConditionCounter++;
            GamePlay.gpGetTrigger(shortName).Enable = false;
          }
          if ("WinCondition2".Equals(shortName) && active)
        {
            WinConditionCounter++;
            GamePlay.gpGetTrigger(shortName).Enable = false;
          }
          if ("WinCondition3".Equals(shortName) && active)
        {
            WinConditionCounter++;
            GamePlay.gpGetTrigger(shortName).Enable = false;
          }

          serverMessage ("Anzahl erledigter Teilziele: " + WinConditionCounter.ToString()); // Für Testzwecke.
             
        if (WinConditionCounter >= NeededForMissionSuccess)
        {
            AktuelleMissionCondition = MissionCondition.Success;
            GamePlay.gpHUDLogCenter("Ziel erreicht");
            serverMessage ("Endanzahl: " + WinConditionCounter.ToString()); // Für Testzwecke.
            GamePlay.gpGetTrigger("FailCondition1").Enable = false;
          }

          if ("FailCondition1".Equals(shortName) && active)
        {
            AktuelleMissionCondition = MissionCondition.Failure;
            GamePlay.gpHUDLogCenter("Sie haben die Zeit überschritten");      
            serverMessage ("Endanzahl: " + WinConditionCounter.ToString()); // Für Testzwecke.   
            GamePlay.gpGetTrigger(shortName).Enable = false;   
          }
    }
}
      

if (AktuelleMissionCondition == MissionCondition.Neutral) Ist sozusagen unsere Hauptabfrage, solange die Mission noch nicht entschieden ist, kann der Inhalt der Abfrage ausgeführt werden, ist die Mission erfolgreich oder erfolglos verlaufen werden alle weiteren Triggerereignisse ignoriert. Möchte man die Ereignisse dennoch weiterzählen, etwa wenn die Mission schon erfolgreich war, kann man die if-Abfrage auch erweitern zu if ((AktuelleMissionCondition == MissionCondition.Neutral) || (AktuelleMissionCondition == MissionCondition.Success)) das || steht für ODER.

if ("WinCondition1".Equals(shortName) && active)
{
    WinConditionCounter++;
    GamePlay.gpGetTrigger(shortName).Enable = false;
}

Die if-Abfragen für die WinCondition-Trigger wurden angepasst, und zwar wird in der Aufruf des Triggers registriert indem WinConditionCounter um eins erhöht wird (dieses ++ ist sozusagen die Abkürzung von WinConditionCounter = WinConditionCounter + 1). Dies wird für alle unsere WinTrigger wiederholt.

Um sich das Kopieren und Einfügen der if Anweisung zu sparen und es auch nicht bei jedem neuhinzugekommenen Trigger wiederholen zu müssen, können wir ausnützen das die WinCondition - Trigger einfach durchnummeriert wurden. Und zwar mit

if (shortName.Substring(0, 12).Equals("WinCondition") && active)
{
    WinConditionCounter++;
    GamePlay.gpGetTrigger(shortName).Enable = false;   
}

shortName.Substring(0, 12).Equals("WinCondition") .Substring(0, 12) sorgt dafür das nur die ersten 12 Buchstaben des Triggernamens beachtet werden (Eigentlich hätten auch die ersten drei gereicht also shortName.Substring(0, 3) ), dadurch wird das innere der Anweisung jedesmal ausgeführt wenn der Anfang des Triggers WinCondition lautet.

serverMessage ("Anzahl erledigter Teilziele: " + WinConditionCounter.ToString()); // Für Testzwecke.
dient als Kontrollausgabe für Debugzwecke, ich habe absichtlich diese Form gewählt ich hätte auch schreiben können
GamePlay.gpLogServer (null, "Anzahl erledigter Teilziele: {0}", new object [] {WinConditionCounter});
Wie bereits erwähnt kann man mit serverMessage ("...") eine Zeichenfolge ausgeben. WinConditionCounter ist aber vom Typ int (Integer) und muss deswegen erst in eine Zeichenfolge (String) umgewandelt werden, dazu gibt es die Methode ToString(), die das für uns erledigt.

Weiter mit

if (WinConditionCounter >= NeededForMissionSuccess)
{
    AktuelleMissionCondition = MissionCondition.Success;
    GamePlay.gpHUDLogCenter("Ziel erreicht");
    serverMessage ("Endanzahl: " + WinConditionCounter.ToString()); // Für Testzwecke.
    GamePlay.gpGetTrigger("FailCondition1").Enable = false;
}

Diese if-Anweisung fragt ab ob WinConditionCounter bereits größer gleich NeededForMissionSuccess (also der zum Sieg notwendigen Anzahl von TeilZielen) ist. Ist dies der Fall wird die Variable AktuelleMissionCondition auf Success gesetzt (Mission gilt als gewonnen) und die Meldung "Ziel erreicht" auf dem Bildschirm ausgegeben. Auch wird der Trigger "FailCondition1" deaktiviert.

if ("FailCondition1".Equals(shortName) && active)
{
    AktuelleMissionCondition = MissionCondition.Failure;
    GamePlay.gpHUDLogCenter("Sie haben die Zeit überschritten");      
    serverMessage ("Endanzahl: " + WinConditionCounter.ToString()); // Für Testzwecke.   
    GamePlay.gpGetTrigger(shortName).Enable = false;   
}

Diese if Abfrage fragt ab der Trigger FailCondition1 ausgelöst wurde && dieser Aktiv ist.
Sollte der Ausdruck wahr werden, wird in der if Anweisung die Meldung "Sie haben die Zeit überschritten", die Variable AktuelleMissionCondition auf Failure gesetzt (die Mission gilt als verloren. Und wie so oft wird auch der Trigger FailCondition1 deaktiviert.


Zum Abschluss noch mal der gesamte Code ohne die Testmeldungen und mit "shortName.Substring(0, 12).Equals("WinCondition")"

Als Missionsdesigner muss man eigentlich nur die Anzahl der zum Sieg nötigen Ereignisse mit NeededForMissionSuccess festlegen, die Pfade und Dateinamen der zu ladenden Missionen und gegebenenfalls die Bildschirmmeldungen (oder Kontrollmeldungen) durch eigene ersetzen.

using System;
using maddox.game;
using maddox.game.world;

public class Mission : maddox.game.AMission
{
    enum MissionCondition {Neutral, Success, Failure}   
   
    MissionCondition AktuelleMissionCondition = MissionCondition.Neutral;
    const int NeededForMissionSuccess = 2;
    int WinConditionCounter = 0;
   

    private void serverMessage (string msg)
    {
        GamePlay.gpLogServer (null, msg, new object [] {msg});
    }
   
    public override void OnBattleStarted()
    {
        base.OnBattleStarted();
        MissionNumberListener = -1;

        serverMessage ("\nZerstören sie jeweils mindestens 3 Fahrzeuge aus zwei Gruppen\n\n");

        GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen3Sub1.mis");
        GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen3Sub2.mis");
        GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen3Sub3.mis");
    }

    public override void OnTrigger(int missionNumber, string shortName, bool active)
{
    base.OnTrigger(missionNumber, shortName, active);
   
    if (AktuelleMissionCondition == MissionCondition.Neutral)
    {
        if (shortName.Substring(0, 12).Equals("WinCondition") && active)
            {
                WinConditionCounter++;
                GamePlay.gpGetTrigger(shortName).Enable = false;
        }
       
        if (WinConditionCounter >= NeededForMissionSuccess)
        {
            AktuelleMissionCondition = MissionCondition.Success;
            GamePlay.gpHUDLogCenter("Ziel erreicht");
            GamePlay.gpGetTrigger("FailCondition1").Enable = false;
        }

    if ("FailCondition1".Equals(shortName) && active)
            {
            AktuelleMissionCondition = MissionCondition.Failure;
            GamePlay.gpHUDLogCenter("Sie haben die Zeit überschritten");
            GamePlay.gpGetTrigger(shortName).Enable = false;
    }
    }
}   
}   


Auch hier bin ich wie immer für Kritik und Anregungen dankbar.

Als Attachment die Übungsmission, MissionNachladen3.cs enthält die erste Fassung ganz oben und MissionNachladen4.cs die bereinigte unten.

Das Sample Verzeichnis nach ...\Documents\1C SoftClub\il-2 sturmovik cliffs of dover\missions\Single kopieren oder die Pfade zu den Submissionen manuell anpassen.

Edit: Korrigierte Version hochgeladen.

[gelöscht durch Administrator]
« Letzte Änderung: 07.Juni.2011, 11:39 von Deichwart »

Offline JG53Frankyboy

  • Flieger
  • ****
  • Beiträge: 390

Offline III./JG27_Culli

  • Hallenfeger
  • **
  • Beiträge: 39
Alle Achtung. Hut ab.

Offline III./JG27_Culli

  • Hallenfeger
  • **
  • Beiträge: 39
Ich habe mir gerade Deine Beispielmission angeschaut. Aber da passiert nichts! Die Submissionen werden nicht geladen (Keine Ziele).
Und der Text wird auch nicht im Chatfenster angezeigt.

Gruss Culli

Offline FG28_Kodiak

  • Blasenteetrinker
  • *****
  • Beiträge: 1.893
das Sample Verzeichnis nach "...\Documents\1C SoftClub\il-2 sturmovik cliffs of dover\missions\Single" kopiert?


Offline III./JG27_Culli

  • Hallenfeger
  • **
  • Beiträge: 39
Jepp. Wie die anderen auch und die funktionieren alle.

Offline FG28_Kodiak

  • Blasenteetrinker
  • *****
  • Beiträge: 1.893
Kannst du mal die Console öffnen und ein Bildschirmfoto machen?
« Letzte Änderung: 01.Juni.2011, 19:42 von FG28_Kodiak »


Offline FG28_Kodiak

  • Blasenteetrinker
  • *****
  • Beiträge: 1.893
In Zeile 37 fehlt hinten ein Semikolon, laut Fehlermeldung. Bloss warum.
Also in Zeile 37 das Semikolon setzen dann gehts.

Offline III./JG27_Culli

  • Hallenfeger
  • **
  • Beiträge: 39
Es lag nicht nur am Semikolon, es muss auch die doppelte Klammer entfernt werden und schon geht es.

so geht es nicht
Zitat
if (shortName.Substring(0, 12).Equals("WinCondition") && active))

und so geht es:

Zitat
if (shortName.Substring(0, 12).Equals("WinCondition") && active);

Offline FG28_Kodiak

  • Blasenteetrinker
  • *****
  • Beiträge: 1.893
Mach das Semikolon weg, das ist eine leere Anweisung, d.h. die if Anweisung macht gar nichts.
Aber immerhin fast die richtige Lösung  ;)

Habe heute früh ne falsche Version hochgeladen (die falsche Datei auf die Schnelle umbenannt) und dann nicht mehr kontrolliert, meine Testversion war korrekt  :'(
Also Sorry für die Unannehmlichkeiten, habe die bereinigte Version hochgeladen.
« Letzte Änderung: 02.Juni.2011, 05:56 von FG28_Kodiak »

Offline III./JG27_Culli

  • Hallenfeger
  • **
  • Beiträge: 39
Kein Problem