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

0 Mitglieder und 1 Gast betrachten dieses Thema.

Offline FG28_Kodiak

  • Blasenteetrinker
  • *****
  • Beiträge: 1.893
Bei der letzten Lektion haben wir uns ja damit beschäftigt, eine einzelne SubMission laufend alle 30sek zu laden und die zerstörten Fahrzeuge mit Hilfe der Methode OnActorDestroyed() zu zählen.
Mit dieser Lektion werden wir jeweils eine von mehreren SubMissionen zufallsgesteuert auswählen und laden. Außerdem bauen wir eine kleine Falle für den Spieler ein, nämlich befreundete Fahrzeuge. Wird eines von diesen vom Spieler zerstört, gilt die Mission als Fehlschlag. Der Spieler wird lernen genauer hinzusehen und wir werden lernen wie man die Armeezugehörigkeit eines Actors feststellt, so haben beide was davon ;). Außerdem zählen wir nicht mehr die Gesamtanzahl der Fahrzeuge sondern nur die der Feindlichen.

Also erstmal die Submissionen vorbereiten, die Hauptmission bleibt unverändert. Man öffnet einfach die SubMission vom letzten Mal, ändert das Fahrzeug und speichert die Mission dann unter anderem Namen. Hier mal als Beispiel unser befreundetes Fahrzeug:


Insgesamt habe ich neben der vom letzten Mal, 3 weitere Submissionen erstellt, davon eine mit einem befreundeten Fahrzeug.

Der Code zu dieser Beispielmission
using System;
using maddox.game;
using maddox.game.world;
using System.Collections.Generic;


public class Mission : AMission
{
    enum MissionCondition {Neutral, Success, Failure}
   
    MissionCondition AktuelleMissionCondition = MissionCondition.Neutral;
    AiAircraft PlayerPlane;
    const int MaxAnzahlFeindlicheFahrzeuge = 10;
    int Zeitspanne = 4000;
    int AnzahlFeindlicheFahrzeuge = 0;
    int ZerstoerteZiele = 0;
   
    public override void OnBattleStarted()
    {
        base.OnBattleStarted();
        MissionNumberListener = -1;
        PlayerPlane = (AiAircraft)GamePlay.gpPlayer().Place();
    }   


    private void serverMessage(string msg)
    {
        GamePlay.gpLogServer (null, msg, new object [] {msg});
    }
   
   
    public override void OnTickGame()
    {
        if (AktuelleMissionCondition == MissionCondition.Neutral)
        {   
            if (Time.tickCounter() % 1000 == 0 && (AnzahlFeindlicheFahrzeuge < MaxAnzahlFeindlicheFahrzeuge))  //ca. alle 30sek die Karte laden
            {
                Zeitspanne += 1000;  // Bei jedem neuen Fahrzeug 30 sekunden zur GesamtZeit hinzu
                Random ZufaelligesFahrzeug = new Random();
           
                switch (ZufaelligesFahrzeug.Next(1,5))
                {
                    case 1:
                        GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub1.mis");
                    break;
                    case 2:
                        GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub2.mis");
                    break;
                    case 3:
                        GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub3.mis");
                    break;
                    case 4:
                        GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub4.mis");
                    break;
                    default:
                        serverMessage("Mission nicht gefunden");
                    break;
                }
            }
           
            if (Time.tickCounter() > Zeitspanne && AktuelleMissionCondition != MissionCondition.Success)     
            {
                GamePlay.gpHUDLogCenter("Sie haben "+ ZerstoerteZiele.ToString() + " von " + AnzahlFeindlicheFahrzeuge.ToString() + " Fahrzeugen zerstört" );
                AktuelleMissionCondition = MissionCondition.Success;
            }
        }
    }
   
    public override void OnActorCreated(int missionNumber, string shortName, AiActor actor)
    {
        base.OnActorCreated(missionNumber, shortName, actor);
       
        if (actor is AiGroundActor)
        {
            GamePlay.gpLogServer (null, "Fahrzeug: {0}\n", new object [] {(actor as AiGroundActor).InternalTypeName()});//Testmeldung
           
            if (actor != null && actor.Army() == 1)   // 1 steht für Rote Seite 2 würde für Blaue Seite stehen
            {
                AnzahlFeindlicheFahrzeuge++;   // Wird nur gezählt wenn gegnerische Seite
            }
         
            Timeout(75, () => {
                if (actor != null)
                {
                    (actor as AiGroundActor).Destroy();
                }
            });
        }
    }
   
    public override void OnActorDead(int missionNumber, string shortName, AiActor actor, System.Collections.Generic.List<DamagerScore> damages)
    {
        base.OnActorDead(missionNumber, shortName, actor, damages);
       
        string KilledName;
         
         KilledName = missionNumber.ToString()+ ":0_Chief";
         
         if ((damages.Count != 0) && (PlayerPlane.Name().Equals(damages[0].initiator.Actor.Name())) && KilledName.Equals(actor.Name()))
         {
             
             if (actor != null && actor.Army() == 2)   // 2 steht für Rote Seite 1 würde für Blaue Seite stehen
            {
                GamePlay.gpHUDLogCenter("Sie haben eines ihrer eigenen Fahrzeuge zerstört - <<<Mission fehlgeschlagen>>>" );
                AktuelleMissionCondition = MissionCondition.Failure;
            }

             ZerstoerteZiele++;
         }
    }
}

Zuerst wieder die in der Mission global gütligen Variablen:
enum MissionCondition {Neutral, Success, Failure}
MissionCondition AktuelleMissionCondition = MissionCondition.Neutral;

Da wir diesmal wieder eine Abbruchbedingung haben, benutzen wir die bereits aus einer früheren Lektion bekannte Enumeration

const int MaxAnzahlFeindlicheFahrzeuge = 10; Da wir diesmal nur die Gesamtzahl der feindlichen Fahrzeuge berücksichtigen möchten, wurde ein aussagekräftigerer Variablenname gewählt.
int Zeitspanne = 4000; Mit dieser Variable berücksichtigen wir das das letzte Fahrzeug auch noch eine gewisse Zeit benötigt um den Weg zurückzulegen. Diese Variable wird später im Code für jede neu hinzugekommene SubMission um 1000 erhöht, da wir nicht wissen können, wieviele befreundete Fahrzeuge sich am Ende auf den Weg gemacht haben.
int AnzahlFeindlicheFahrzeuge = 0; Hier wird die aktuelle Zahl der gegnerischen Fahrzeuge gespeichert.
Der Rest ändert sich zur vorigen Lektion nicht.

OnBattleStarted() und serverMessage(string msg) bleiben ebenfalls unverändert.


public override void OnTickGame()
{
    if (AktuelleMissionCondition == MissionCondition.Neutral)
    {   
        if (Time.tickCounter() % 1000 == 0 && (AnzahlFeindlicheFahrzeuge < MaxAnzahlFeindlicheFahrzeuge))  //ca. alle 30sek die Karte laden
        {
            Zeitspanne += 1000;  // Bei jedem neuen Fahrzeug 30 sekunden zur GesamtZeit hinzu
            Random ZufaelligesFahrzeug = new Random();
       
            switch (ZufaelligesFahrzeug.Next(1,5))
            {
                case 1:
                    GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub1.mis");
                break;
                case 2:
                    GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub2.mis");
                break;
                case 3:
                    GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub3.mis");
                break;
                case 4:
                    GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub4.mis");
                break;
                default:
                    serverMessage("Mission nicht gefunden");
                break;
            }
        }
       
        if (Time.tickCounter() > Zeitspanne)     
        {
            GamePlay.gpHUDLogCenter("Sie haben "+ ZerstoerteZiele.ToString() + " von " + AnzahlFeindlicheFahrzeuge.ToString() + " Fahrzeugen zerstört" );
            AktuelleMissionCondition = MissionCondition.Success;
        }
    }
}

Verändert wurde hingegen die Methode OnTickGame().     

if (AktuelleMissionCondition == MissionCondition.Neutral) zuerst wird erst einmal abgefragt ob der Missionsstatus noch Neutral ist. Sollte also die Mission bereits ein Fehlschlag durch das zerstören eines befreundeten Ziels sein, werden keine neuen SubMissionen mehr geladen.

if (Time.tickCounter() % 1000 == 0 && (AnzahlFeindlicheFahrzeuge < MaxAnzahlFeindlicheFahrzeuge)) Die Abfrage wird ausgeführt wenn ca 30sek vergangen sind und die Anzahl der bereits gespawnten gegnerischen Fahrzeuge noch nicht die Maximale Anzahl erreicht hat.
Ist die If Abfrage also Wahr
wird zuerst die Zeitspanne um 1000 erhöht das Zeitspanne += 1000 kann man auch als Zeitspanne = Zeitspanne + 1000 schreiben. Das += (genauso gibt es -=, *= und /=) ist eine Abkürzung für Schreibfaule (Ok nicht ganz ;)).
Random ZufaelligesFahrzeug = new Random() hiermit erzeugen wir ein neues Objekt mit dem Namen ZufaelligesFahrzeug aus der Klasse Random, diese Klasse wird uns durch .Net zu Verfügung gestellt und liefert uns Pseudo-Zufallszahlen. Pseudo heißt unter gleichen Bedingungen werden auch immer die gleichen Zahlen erzeugt. Aber für unseren Zweck reicht diese Art der Zufallszahlen aus (Hier findet sich  Näheres dazu.).

 switch (ZufaelligesFahrzeug.Next(1,5))
 {
     case 1:
         GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub1.mis");
     break;
     case 2:
         GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub2.mis");
     break;
     case 3:
         GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub3.mis");
     break;
     case 4:
         GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub4.mis");
     break;
     default:
         serverMessage("Mission nicht gefunden");
     break;
 }

Zum festlegen welche SubMission geladen wird verwenden wir eine switch .. case Anweisung. Dabei wird überprüft, ob der hinter switch aufgeführte Ausdruck, der entweder eine Ganzzahl oder eine Zeichenfolge sein muss, mit einer der hinter case angegebenen Konstanten übereinstimmt. Dabei wird der Reihe nach vorgegangen also zuerst wird überprüft ob der erste case zutrifft, dann ob der zweite etc. Man kann in C# keinen Bereich hinter dem case angeben, Dinge wie case 1..5: sind in C# nicht möglich. Trifft eine case zu, werden alle Anweisungen bis zum break; ausgeführt. Mit default kann man einen Zweig definieren der auf jeden Fall ausgeführt wird, wenn keine case zutrifft. So aber schauen wir uns die Switch - Anweisung unseres Scripts näher an. switch (ZufaelligesFahrzeug.Next(1,5)) Mit ZufaelligesFahrzeug.Next(..,..) erzeugen wir mit Hilfe des Random Objektes eine ganzzahlige Zufallszahl. Der erste Wert in Klammern gibt den Minimal Wert an, dieser ist einschließlich, der zweite Wert gibt den Maximal Wert an, dieser ist ausschließlich, das bedeutet der Maximalwert wird durch den Zufallszahlengenerator nicht erzeugt, der Minimalwert hingegen schon. Möchte man zum Beispiel Zufallszahlen von 1 bis 10 müsste man .Next(1,11) angeben. In unserem Beispiel haben wir momentan 4 Missionen deswegen benötigen wir Zahlen von 1 bis 4, daher müssen wir bei .Next(1,5) festlegen. Wurde die Zufallszahl erzeugt, vergleicht die Switch-Anweisung alle Case-Fälle bis der Richtige gefunden wurde und lädt dann die dort angegebene SubMission. Hätten wir aus Versehen .Next(1,6) als Zufallszahlbereich angegeben, würde auch ab und zu die Zahl 5 erzeugt, dafür existiert aber kein Case-Fall, somit würde dann hier der default-Bereich aktiv und dieser gibt uns eine kurze Meldung in der Chatleiste aus.

Mit if (Time.tickCounter() > Zeitspanne) überprüfen wir ob die Zeit bereits abgelaufen ist, wenn Ja geben wir eine "Gewinnmeldung" aus und werten mit AktuelleMissionCondition = MissionCondition.Success; die Mission als Erfolg.

public override void OnActorCreated(int missionNumber, string shortName, AiActor actor)
{
    base.OnActorCreated(missionNumber, shortName, actor);
   
    if (actor is AiGroundActor)
    {
        GamePlay.gpLogServer (null, "Fahrzeug: {0}\n", new object [] {(actor as AiGroundActor).InternalTypeName()});//Testmeldung
       
        if (actor != null && actor.Army() == 1)   // 1 steht für Rote Seite 2 würde für Blaue Seite stehen
        {
            AnzahlFeindlicheFahrzeuge++;   // Wird nur gezählt wenn gegnerische Seite
        }
     
        Timeout(75, () => {
            if (actor != null)
            {
                (actor as AiGroundActor).Destroy();
            }
        });
    }
}

Zur Methode OnActorCreated kamen folgende Anweisungen hinzu:
GamePlay.gpLogServer (null, "Fahrzeug: {0}\n", new object [] {(actor as AiGroundActor).InternalTypeName()});
Mit Hilfe von .InternalTypeName() kann man den internen Spielnamen eines AiGroundActors abfragen bzw. ausgeben. Die Ausgabe sieht dann folgendermaßen aus:


if (actor != null && actor.Army() == 1) Mit der If-Anweisung wird abgefragt ob der Actor vorhanden ist und ob er der gegnerischen Armee angehört. Army() gibt einen Integer-Wert für die Armee zurück, hierbei steht 1 in Cliffs of Dover für die Rote Seite und 2 für die Blaue Seite. Gehört der Actor also zur Roten Seite wird AnzahlFeindlicheFahrzeuge um eins erhöht.

public override void OnActorDead(int missionNumber, string shortName, AiActor actor, System.Collections.Generic.List<DamagerScore> damages)
{
    base.OnActorDead(missionNumber, shortName, actor, damages);
   
    string KilledName;
     
    KilledName = missionNumber.ToString()+ ":0_Chief";
     
    if ((damages.Count != 0) && (PlayerPlane.Name().Equals(damages[0].initiator.Actor.Name())) && KilledName.Equals(actor.Name()))
    {
         
        if (actor != null && actor.Army() == 2)   // 2 steht für Blaue Seite
        {
            GamePlay.gpHUDLogCenter("Sie haben eines ihrer eigenen Fahrzeuge zerstört - <<<Mission fehlgeschlagen>>>" );
            AktuelleMissionCondition = MissionCondition.Failure;
        }

        ZerstoerteZiele++;
    }
}


Im Vergleich zur Mission aus der letzten Lektion wurde diese If-Anweisung eingefügt:
if (actor != null && actor.Army() == 2)   // 2 steht für Blaue Seite
{
    GamePlay.gpHUDLogCenter("Sie haben eines ihrer eigenen Fahrzeuge zerstört - <<<Mission fehlgeschlagen>>>" );
    AktuelleMissionCondition = MissionCondition.Failure;
}

Sie fragt ab ob der Actor vorhanden ist und durch actor.Army() == 2 ob er zur blauen (also unserer) Seite gehört, ist dies der Fall wird eine Meldung ausgegeben und AktuelleMissionCondition auf Failure gesetzt, die Mission war also ein Fehlschlag.



Beim Testen der obigen Mission, wird einem relativ schnell auffallen das manche Fahrzeuge gar nicht zum zweiten Tarnnetz kommen, bevor sie verschwinden. Diese Fahrzeuge sind zu langsam und brauchen daher mehr Zeit bis sie ihr Ziel erreichen. Man könnte jetzt natürlich einfach die Zeit erhöhen, nach der die Fahrzeuge mit Destroy() entfernt werden. Aber dann gäbe es eventuell Staus am Ziel, die für den Spieler ein einfaches Ziel wären.

Um dies zu vermeiden, kann man auch eine Methode schreiben die Überprüft wo sich ein Actor gerade befindet und wenn dieser eine bestimmte Stelle erreicht wird er einfach aus dem Spiel entfernt. So eine Methode kann folgendermassen aussehen:

private void DestroyGroundActorAtPosition(double x, double y, double destroyRadius)
{
    Point3d DestroyPos;
    DestroyPos.x = x;
    DestroyPos.y = y;
    DestroyPos.z = 1;

    for (int i = 0; i < MissionsCount; i++)
    {
        AiGroundActor curActor;
        for (int j = 0; j < 10; j++)
        {
            string nameActor = i.ToString() + ":0_Chief" + j.ToString();
            curActor = GamePlay.gpActorByName(nameActor) as AiGroundActor;
            if (curActor != null)
            {
                if (curActor.Pos().distance(ref DestroyPos) < destroyRadius)
                {   
                        curActor.Destroy();
                }
            }                 
                                           
        }
    }       
}

Schauen wir sie uns genauer an.
private void DestroyGroundActorAtPosition(double x, double y, double destroyRadius)
Als Übergabewerte verlangt diese selbstgeschriebene Methode die X und Y Koordinate des Zielpunkts, sowie einen Radius um den Punkt, so das man die Entfernung zum Punkt angeben kann ab dem ein GroundActor entfernt werden kann.
Point3d DestroyPos; DestroyPos wird als Point3d deklariert, Point3d ist eine Interne Datenstruktur von Cliffs of Dover. Zugriff auf diese Struktur erhalten wir dadurch das wir das Namespace maddox.GP durch using maddox.GP; am Beginn unseres Scripts laden.
Point3d enthält 3 double Werte, jeweils einen für x, y und z, also alle Angaben die eine Lokalisierung in einem 3Dimensionalen Raum nötig sind. DestroyPos wird mit .x = x und .y = y durch die Übergabewerte der Methode und durch .z =1 von uns festgelegt, den Z-Wert legen wir mit 1 fest da wir nur Bodenziele verschwinden lassen wollen.
Um sicherzustellen das auch alle Aktoren aus allen Missionen berücksichtigt werden kann man diese mit der Methode OnMissionLoaded() zählen, diese wird vom Spiel immer aufgerufen, wenn eine Mission neu geladen wurde. Zuerst müssen wir allerdings noch eine zusätzliche globale Variable einführen int MissionsCount = 1; dieses MissionsCount wird dann in OnMissionLoaded jeweils um eins erhöht.
public override void OnMissionLoaded(int missionNumber)
{
    base.OnMissionLoaded(missionNumber);
    MissionsCount++;           
}   

So zurück zu DestroyGroundActorAtPosition dort wird mit Hilfe einer for - Schleife
for (int i = 0; i < MissionsCount; i++)
die Missionsnummer hochgezählt, innerhalb der Schleife dann ein neuer AiGroundActor mit Namen curActor deklariert.
Anschliessend benötigen wir noch eine zweite Schleife for (int j = 0; j < 10; j++), mit Hilfe der beiden Schleifen setzten wir dann alle momentan möglichen Actornamen durch string nameActor = i.ToString() + ":0_Chief" + j.ToString(); zusammen, dies ist nötig damit auch immer der richtige Actor "erwischt" wird. Mit curActor = GamePlay.gpActorByName(nameActor) as AiGroundActor; weisen wir dann der Variablen curActor mit Hilfe von gpActorByName zu. Mit if (curActor != null) überprüfem wir dann noch ob dieser Actor auch existiert und mit if (curActor.Pos().distance(ref DestroyPos) < destroyRadius) stellen wir fest ob der Actor schon in der Nähe unseres Punktes ist. Mit curActor.Pos() bekommen wir die aktuelle Position unseres Actors als Point3d geliefert, von dieser Position können wir mit distance(ref DestroyPos) den Abstand zu unserem vorher angegebenen Punkt bestimmen lassen. distance ist eine Methode von Point3d und erwartet als Übergabe Wert eine Referenz auf den Zielpunkt. Ist der Actor nah genug am Zielpunkt wird der Actor mit Destroy() entfernt.

DestroyGroundActorAtPosition bringen wir am Ende der OnTick-Methode unter
DestroyGroundActorAtPosition(16761.94, 14817.99, 10.0); Die XY-Werte habe ich aus dem SubMission-File entnommen, der Wert 10.0 für die Entfernung vom Zielpunkt ist ausreichend.
DestroyGroundActorAtPosition werden wir in einer späteren Lektion erweitern, so dass wir mit dieser Methode später einen ganzen Bereich von Actors "säubern" können.

Hier noch das gesamte Script:
using System;
using System.Collections;
using maddox.game;
using maddox.game.world;
using maddox.GP;
using System.Collections.Generic;


public class Mission : AMission
{
    enum MissionCondition {Neutral, Success, Failure}
   
    MissionCondition AktuelleMissionCondition = MissionCondition.Neutral;
    AiAircraft PlayerPlane;
    const int MaxAnzahlFeindlicheFahrzeuge = 10;
    int Zeitspanne = 4000; // Zeitspanne bis letztes Fahrzeug sicher verschwunden ist.
    int AnzahlFeindlicheFahrzeuge = 0;
    int ZerstoerteZiele = 0;
    int MissionsCount = 1;
   
    public override void OnMissionLoaded(int missionNumber)
    {
        base.OnMissionLoaded(missionNumber);
        MissionsCount++;           
    }

    public override void OnBattleStarted()
    {
        base.OnBattleStarted();
        MissionNumberListener = -1;
        PlayerPlane = (AiAircraft)GamePlay.gpPlayer().Place();
    }   

    private void serverMessage(string msg)
    {
        GamePlay.gpLogServer (null, msg, new object [] {msg});
    }
   
    private void DestroyGroundActorAtPosition(double x, double y, double destroyRadius)
    {
        Point3d DestroyPos;
        DestroyPos.x = x;
        DestroyPos.y = y;
        DestroyPos.z = 1;

        for (int i = 0; i < MissionsCount; i++)
        {
            AiGroundActor curActor;
            for (int j = 0; j < 10; j++)
            {
                string nameActor = i.ToString() + ":0_Chief" + j.ToString();
                curActor = GamePlay.gpActorByName(nameActor) as AiGroundActor;
                if (curActor != null)
                {
                    if (curActor.Pos().distance(ref DestroyPos) < destroyRadius)
                    {   
                            curActor.Destroy();
                    }
                }                 
                                               
            }
        }       
    }
   
    public override void OnTickGame()
    {
        if (AktuelleMissionCondition != MissionCondition.Failure  && AktuelleMissionCondition != MissionCondition.Success)
        {   
            if (Time.tickCounter() % 1000 == 0 && (AnzahlFeindlicheFahrzeuge < MaxAnzahlFeindlicheFahrzeuge))  //ca. alle 30sek die Karte laden
            {
                Zeitspanne += 1000;  // Bei jedem neuen Fahrzeug 30 sekunden zur GesamtZeit hinzu
                Random ZufaelligesFahrzeug = new Random();
           
                switch (ZufaelligesFahrzeug.Next(1,5))
                {
                    case 1:
                        GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub1.mis");
                    break;
                    case 2:
                        GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub2.mis");
                    break;
                    case 3:
                        GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub3.mis");
                    break;
                    case 4:
                        GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub4.mis");
                    break;
                    default:
                        serverMessage("Mission nicht gefunden");
                    break;
                }
            }
           
            if (Time.tickCounter() > Zeitspanne && AktuelleMissionCondition != MissionCondition.Success)     
            {
                GamePlay.gpHUDLogCenter("Sie haben "+ ZerstoerteZiele.ToString() + " von " + AnzahlFeindlicheFahrzeuge.ToString() + " Fahrzeugen zerstört" );
                AktuelleMissionCondition = MissionCondition.Success;
            }
        }
   
        DestroyGroundActorAtPosition(16761.94, 14817.99, 10.0);
       
    }
   
    public override void OnActorCreated(int missionNumber, string shortName, AiActor actor)
    {
        base.OnActorCreated(missionNumber, shortName, actor);
       
        if (actor is AiGroundActor)
        {
            GamePlay.gpLogServer (null, "Fahrzeug: {0}\n", new object [] {(actor as AiGroundActor).InternalTypeName()});//Testmeldung
           
            if (actor != null && actor.Army() == 1)   // 1 steht für Rote Seite 2 würde für Blaue Seite stehen
            {
                AnzahlFeindlicheFahrzeuge++;   // Wird nur gezählt wenn gegnerische Seite
            }
        }
    }
   
    public override void OnActorDead(int missionNumber, string shortName, AiActor actor, System.Collections.Generic.List<DamagerScore> damages)
    {
        base.OnActorDead(missionNumber, shortName, actor, damages);
       
        string KilledName;
         
        KilledName = missionNumber.ToString()+ ":0_Chief";
         
        if ((damages.Count != 0) && (PlayerPlane.Name().Equals(damages[0].initiator.Actor.Name())) && KilledName.Equals(actor.Name()))
        {
             
            if (actor != null && actor.Army() == 2)   // 2 steht für Blaue Seite
            {
                GamePlay.gpHUDLogCenter("Sie haben eines ihrer eigenen Fahrzeuge zerstört - <<<Mission fehlgeschlagen>>>" );
                AktuelleMissionCondition = MissionCondition.Failure;
            }

            ZerstoerteZiele++;
        }
    }
}

Für Fragen, Kritik und Anregungen bin ich wie immer offen.

Im Anhang finden sich die beiden Beispielmissionen.

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

Offline tbag

  • Hallenfeger
  • **
  • Beiträge: 12
Genial, so langsam kommen wir zum Eingemachten :) Gibt es irgendwo eine umfassende Liste mit Objekten, Methoden und Funktionen die CloD zur Verfuegung stellt?

Offline FG28_Kodiak

  • Blasenteetrinker
  • *****
  • Beiträge: 1.893
Nö, du kannst aber auf die Dlls mit Hilfe von z.B. Visual Studio (die Express Edition tuts auch) verweisen und dir so die Klassenschnittstellen anschauen. Aber um Try und Error kommst du momentan leider nicht vorbei.
« Letzte Änderung: 08.Juni.2011, 06:34 von FG28_Kodiak »

Offline Tigger

  • Hallenfeger
  • **
  • Beiträge: 16
Danke, Kodiak!

Zwei Anmerkungen hätte ich noch:

1. switch case Anweisung lässt sich mit einem Trick doch als "zwischen 1 und 3" z. B. definieren, in dem man einfach die breaks weglässt. Z. B. wird in diesem Fall immer die gleiche Mission geladen, wenn der Zufallswert zwischen 1 und 3 liegt:

                switch (ZufaelligesFahrzeug.Next(1,5))
                {
                    case 1:
                    case 2:
                    case 3:
                        GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub1.mis");
                    break;
                    case 4:
                        GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen6Sub4.mis");
                    break;
                    default:
                        serverMessage("Mission nicht gefunden");
                    break;
                }

2. Mir ist aufgefallen, dass diese Anwesiung  GamePlay.gpLogServer (null, msg, new object [] {msg}); nicht beliebig oft aus einem Trigger hintereinander aufgerufen werden kann. Warum auch immer. Also, wenn man in einer OnTrigger - Methode diese Anweisung aufruft mit einer Chatmeldung und gleich in der nächsten Zeile noch ein Mal mit einem anderen Text, dann kommt nur die erste Meldung durch, die zweite wird ignoriert. Somit bekommt nicht zwei Zeilen im Chat, wie man es zu erwarten wäre, sondern nur eine und die zweite ist nicht da. Ich bin noch nicht dahinter gekommen, woran das liegt und ob man dort einfach Parameter einstellen muss. Es wäre aber auch interessant, eine Methode zu schreiben, die aus der onTick-Methode regelmässig aufgerufen wird und die HUD-Meldungen in einem bestimmten Interval ausgibt, die wiederum vorher nicht direkt an HUD-Methode geschickt werden, sondern in einer Liste abgelegt werden. Der Vorteil ist dabei, dass man nicht eine Flut von Meldungen im HUD bekommt, wenn man in einer Mission auf ein Mal mehrere Trigger auslöst oder mehrere Ziele zerstört. Somit kommen eben die Meldungen mit einem Zeitabstand nach und nach. Dies kann man natürlich dann auch für die Trigger in Verbindung mit Chatnachrichten nutzen, die dann wohl auch funktionieren würden.

Offline FG28_Kodiak

  • Blasenteetrinker
  • *****
  • Beiträge: 1.893
Wer wird denn den nächsten Lektionen vorgreifen  ;D
Soll ja auch alles später einmal in eine externe Datei oder Datenbank geschrieben werden.

Das mit mehreren Case-Anweisungen untereinander wusste ich schon, bin mir nur nie ganz sicher ob ich immer alles ansprechen soll oder ob das ganze für die meisten nicht schon verwirrend genug ist. Auch das es ein goto bei switch Anweisungen gibt. Ich denke ich werde bei den nächsten Lektionen, bei nativen C# Befehlen am besten auf externe Quellen zur Vertiefung hinweisen.

Das mit gpLogServer ist mir allerdings bislang nicht aufgefallen  ::), eventuell ein Bug, auch das Scripting ist teils noch eine Baustelle und das gpLogServer gibt es erst seit dem vorletzten Patch oder war es der letzte Beta-Patch.  Oder du hast den schnelleren Rechner  ::). Landet die Meldung wenigstens in der Console?

Bei gpHUDLogCenter sollten zwischen zwei Ausgaben min 5 sek. liegen.
also
GamePlay.gpHUDLogCenter("Meldung 1" );
Timeout(5, () => {   
    GamePlay.gpHUDLogCenter("Meldung 2" );
});
Vielleicht muss man bei gpLogServer auch eine gewisse Verzögerung in Betracht ziehen, sonst "verschluckt" sich die Chatleiste.
« Letzte Änderung: 09.Juni.2011, 08:23 von FG28_Kodiak »