Ил-2 Штурмовик: Битва за Британию. Скрипты. Скрипты для чайников. Часть 3 - Загрузка подмиссий

Материал из АвиаВики
Перейти к: навигация, поиск
Автор: FG28_Kodiak
Ссылка: Перейти (перевод Google)
Samples.zip

В предыдущих двух миссиях мы загружали под-миссии после начала миссии-хоста. В этой части мы научимся контролировать загрузку миссиий при помощи метода public virtual void OnTickGame ()

public override void OnTickGame()

Метод OnTickGame() направляет вызовы с частотой 34 тика в секунду, в зависимости от компьютера может быть больше или меньше времени.

Например:

using System;
using maddox.game;
using maddox.game.world;
public class Mission : AMission
{
   public override void OnTickGame()
   {
       double WievielSekunden = 0;
       
       if (Time.tickCounter() == 334) 
       {
           WievielSekunden = Time.TicksToSecs(Time.tickCounter());
           GamePlay.gpLogServer (null, "Meldung nach: {0} Ticks das sind {1} sek.\n", new object [] {Time.tickCounter(), WievielSekunden});
       }
       if (Time.tickCounter() == 667) 
       {
           WievielSekunden = Time.TicksToSecs(Time.tickCounter());
           GamePlay.gpLogServer (null, "Meldung nach: {0} Ticks das sind {1} sek.\n", new object [] {Time.tickCounter(), WievielSekunden});
       }
       if (Time.tickCounter() == 1000) 
       {
           WievielSekunden = Time.TicksToSecs(Time.tickCounter());
           GamePlay.gpLogServer (null, "Meldung nach: {0} Ticks das sind {1} sek.\n", new object [] {Time.tickCounter(), WievielSekunden});
       }
  }
} 

Данный пример ничего не делает, кроме как отправляет сообщение в чат сервера каждые 10, 20, 30 секунд.

Time.tickCounter() - содержит количество тиков, прошедших с момента начала мисиии Time.TicksToSecs - переводит такты в секунды

Далее выводим сообщение на экран:

GamePlay.gpLogServer (null, "Meldung nach: {0} Ticks das sind {1} sek.\n", new object [] {Time.tickCounter(), WievielSekunden});

В игре это будет выглядеть так:


Sfdp3-1.jpg


Также вы можете использовать OnTickGame() для задания циклов, зависящих от времени, например:


using System;
using maddox.game;
using maddox.game.world;
public class Mission : AMission
{
   public override void OnTickGame()
   {
       double WievielSekunden = 0;
       
       if (Time.tickCounter() % 334 == 0) 
       {
           WievielSekunden = Time.TicksToSecs(Time.tickCounter());
           GamePlay.gpLogServer (null, "Erste Meldung: {0} Ticks = {1} sek.\n", new object [] {Time.tickCounter(), WievielSekunden});
       }
       if (Time.tickCounter() % 509 == 0) 
       {
           WievielSekunden = Time.TicksToSecs(Time.tickCounter());
           GamePlay.gpLogServer (null, "Zweite Meldung: {0} Ticks = {1} sek.\n", new object [] {Time.tickCounter(), WievielSekunden});
       }
   }
}

Для вычисления остатка при целочисленном делении в языке С# используется оператор деления по модулю %. Он применяется таким же образом, что и оператор деления, но в результате вычисляется не частное, а остаток.

3%3   результат: 0
3%2   результат: 1
5%3   результат: 2
Подробнее можно прочитать здесь: Деление с остатком )

Условие Time.tickCounter() % 334 == 0 всегда выполняется когда значениеtickCounter() равно 334 тика (то есть остаток от деления 334%334 равен нулю и отправляет сообщения в чат через каждые 10 секунд. Условие Time.tickCounter() % 509 == 0 всегда выполняется когда значениеtickCounter() равно 509 тиков (то есть остаток от деления 509%509 равен нулю и отправляет сообщения в чат через каждые 15 секунд.


Sfdp3-2.jpg


Если вы не хотите выводить сообщение в начале миссии, можете использовать задержку изменив ноль в предыдущем примере на значение меньше, чем делитель. Вот примеры неправильных условий:

if (Time.tickCounter() % 334 == 334)
if (Time.tickCounter() % 334 == 335)
Эти условия никогда не будут выполнены и сообщение не появится на экране.

Зададим конкретное время вывода сообщения:

using System;
using maddox.game;
using maddox.game.world;
public class Mission : AMission
{
   public override void OnTickGame()
   {
       double WievielSekunden = 0;
       
       if (Time.tickCounter() % 334 == 333) 
       {
           WievielSekunden = Time.TicksToSecs(Time.tickCounter());
           GamePlay.gpLogServer (null, "Erste Meldung: {0} Ticks = {1} sek.\n", new object [] {Time.tickCounter(), WievielSekunden});
       }
       if (Time.tickCounter() % 509 == 508) 
       {
           WievielSekunden = Time.TicksToSecs(Time.tickCounter());
           GamePlay.gpLogServer (null, "Zweite Meldung: {0} Ticks = {1} sek.\n", new object [] {Time.tickCounter(), WievielSekunden});
       }
   }
} 

Результат:

Sfdp3-3.jpg


В основную миссию два добавим камуфляжную палатку, она появится перед исчезновением и скроет технику.


Sfdp3-4.jpg


Далее нам необходима подмиссия. В ней будет один автомобиль, который проедет от одного конца ВПП до другого и остановится.


Sfdp3-5.jpg


Подмиссия будет загружаться скажем каждые 30 секунд, так мы создадим колонну грузовиков, которые будут курсировать по ВПП и станут для нас отличной мишенью. Транспортные средства будут двигаться на некотором расстоянии друг от друга. В нашей миссии автомобили буду исчезать через 75 секунд - это время, которое понадобится им, чтобы достичь конечной точки пути. Игрок должен уничтожить как можно больше автомобилей. Количество машин (точнее под-миссий) будет равно 10. В этот раз мы не будем использовать триггеры, зачет результатов атаки и сведения о поврежденной технике будут содержаться в коде скрипта.

Код с тестовыми сообщениями:


using System;
using maddox.game;
using maddox.game.world;
using System.Collections.Generic;
public class Mission : AMission
{    
   AiAircraft PlayerPlane;
   const int MaxAnzahlWellen = 10;
   int AnzahlWellen = 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 (Time.tickCounter() % 1000 == 0 && (AnzahlWellen < MaxAnzahlWellen))  //ca. alle 30sek die Karte laden
       {
           GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen5Sub1.mis");
           AnzahlWellen++;
           GamePlay.gpLogServer (null, "{0} nach {1} sek.\n", new object [] {AnzahlWellen, Time.TicksToSecs(Time.tickCounter())});//Testmeldung
       }        
       if (Time.tickCounter() % 11500 == 0)     
       {
                   GamePlay.gpLogServer (null, "{0} nach {1} sek.\n", new object [] {AnzahlWellen, Time.TicksToSecs(Time.tickCounter())});//Testmeldung
                   GamePlay.gpHUDLogCenter("Sie haben "+ ZerstoerteZiele.ToString() + " von " + AnzahlWellen.ToString() + " Fahrzeugen zerstört" );
       }
   }    
   public override void OnActorCreated(int missionNumber, string shortName, AiActor actor)
   {
       base.OnActorCreated(missionNumber, shortName, actor);
       if (actor is AiGroundActor)
       {
           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()))
    		{
    		    ZerstoerteZiele++;
    		    serverMessage(ZerstoerteZiele.ToString());
    	  }
   }
}


Разберем код нашего скрипта. Во-первых мы используем новый метод:

using System.Collections.Generic;

В скрипте нам понадобятся следующие переменные:

AiAircraft PlayerPlane; - класс, в котором содержатся все доступные самолеты в игре, все самолеты за пределами этой группы являются статическими.

const int MaxAnzahlWellen = 10; - количество запусков под-миссий, в нашем случае равно кол-ву грузовиков (целей) int AnzahlWellen = 0; - кол-во уже запущенных под-миссий int ZerstoerteZiele; - количество уничтоженных целей

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

В методе OnBattleStarted установим MissionNumberListener = -1, чтобы иметь доступ к под-миссиям.

PlayerPlane = (AiAircraft)GamePlay.gpPlayer().Place(); - определяем самолет игрока. For us, the assignment IGamePlay class method gpPlayer () to disposition by the (AiAircraft) we share the game with what we mean in this case, the class AiAircraft, there are several derived classes it is necessary to inform the system that we because my (eg, the player is a member of the class AiActor, AiCart, AiAircraft..). Place () belongs to the class and tells players where the player is currently located. Note also that you can only use gpPlayer in single missions, multiplayer games coming gpRemotePlayers used. Important is currently the only one with the help of (AiAircraft)GamePlay.gpPlayer().Place() in a single player game the player gets the plane as a return.

public override void OnTickGame()
{
   if (Time.tickCounter() % 1000 == 0 && (AnzahlWellen < MaxAnzahlWellen))  //ca. alle 30sek die Karte laden
   {
       GamePlay.gpPostMissionLoad("missions\\Single\\Samples\\TestSubmissions\\MissionNachladen5Sub1.mis");
       AnzahlWellen++;
       GamePlay.gpLogServer (null, "{0} nach {1} sek.\n", new object [] {AnzahlWellen, Time.TicksToSecs(Time.tickCounter())});//Testmeldung
   }
       
   if (Time.tickCounter() == 11500)     
   {
       GamePlay.gpLogServer (null, "{0} nach {1} sek.\n", new object [] {AnzahlWellen, Time.TicksToSecs(Time.tickCounter())});//Testmeldung
       GamePlay.gpHUDLogCenter("Sie haben "+ ZerstoerteZiele.ToString() + " von " + AnzahlWellen.ToString() + " Fahrzeugen zerstört" );
       MissionAbgeschlossen = true;
   }
}


OnTickGame ()

if (Time.tickCounter() % 1000 == 0 && (AnzahlWellen < MaxAnzahlWellen)) - условие, которое загружает под-миссию, если прошло 30 секунд И (оператор &&) и номер текущей подмиссии меньше переменной const int MaxAnzahlWellen.

The query load the map every 30 seconds. and that is the number of waves <MaxAnzahlWellen long as, in our case just 10th without this "& & (count waves <MaxAnzahlWellen)" The submission would be charged indefinitely, or just until the player has no more desire. Where, if I'm considering the name and number of waves in a vehicle MaxAnzahlWellen were somewhat exaggerated . If the condition is true our SubMission charged and thereby caused the vehicle on the road. Then increases the number of waves around an issue and issued a check in the chat bar so you can adjust the times may still, as I said the number of ticks per second can vary depending on usage. Should of course be removed from the final version.

if (Time.tickCounter() == 11500) - is our Abruchbedingung the 11 500 ticks should of course be chosen so that the last vehicle can also complete the route. This condition makes the use of a time trigger unnecessary. So the time is expired, a control message is output which outputs the number of waves and the time required to remove this message also in the final. There is still a message on the screen:

Sfdp3-6.jpg


public override void OnActorCreated(int missionNumber, string shortName, AiActor actor)
{
   base.OnActorCreated(missionNumber, shortName, actor);
   if (actor is AiGroundActor)
   {
       Timeout(75, () => {
           if (actor != null)
           { 
               (actor as AiGroundActor).Destroy(); 
           }
       });
   }
}


The method OnActorCreated(int missionNumber, string shortName, AiActor actor) is called each time a new Actor is created, an Actor in Cliffs of Dover is anything to either move or function (eg artillery), has something that only stupid in the area is not part of it (ie buildings, trucks static, static aircraft, etc.). Also here is the first base call to make sure everything is initialized properly.

if (actor is AiGroundActor) - here is checked whether the current actor is among the AiGroundActor class in this class are all items that are traveling on the ground, thus moving trucks, artillery, ships, etc. If the query is TRUE is next

Timeout(75, () => { Anweisungen }); executed. When the timeout is specified first time, after something is to be executed, in this case 75 seconds. in the curly brackets are the instructions to be executed then why is the spelling with () => a little strange, beyond the scope of this lesson, we say stop is just so . The timeout after 75 seconds then executes instructions If the query in this query is whether the actor is also currently available (it may already have been removed elsewhere), if nothing happens, but if (actor as AiGroundActor) we put hereby determine that the actor should be treated as AiGroundActor (if we do not do this, we get an error message because the system does not know what we mean Actor) with.

.Destroy() then this is completely removed from the mission, then simply disappears. This makes you an "overpopulation" of the card to avoid running so essentially multiplayer maps (as is the despawn time course much larger) the number of times. But here it fits quite well purely .

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()))
   {
       ZerstoerteZiele++;
       serverMessage(ZerstoerteZiele.ToString()); //Testmeldung
   }
}


To register if we have destroyed a target, we overloaded the method OnActorDead(..) public override void OnActorDead(int missionNumber, string shortName, AiActor actor, System.Collections.Generic.List<DamagerScore> damages)

This method is used by system called each time when an actor is destroyed (or better Incapacitate is) and that this happens twice, once for the vehicle and then for the imaginary occupant, here you realize it is in the game also provided the ground vehicles and by human players can be controlled. Since we do not want our "DeadCounter" is incremented twice, we introduce the variable name of type String killed. Killed name then sets up the name to be observed, because here you have to decide actor.Name () (ie the name of the destroyed target) once the value of "1:0_Chief0" and the second call "1:0_Chief" (one that varies depending on mission number), according to the number after the occupants of the Chief is all spent. The base name can be found in the Mis-file in our example SubMission here:

[Chiefs]
0_Chief Vehicle.Austin_10_Tilly gb

One can of course use the Mis-file and edit there own. I signed up for "Missionnummer:0_Chief" decided, that the vehicle itself

if ((damages.Count != 0) && (PlayerPlane.Name().Equals(damages[0].initiator.Actor.Name())) && KilledName.Equals(actor.Name())) 

Thus the explanation for the If statement is again somewhat larger

damages.Count != 0 - System.Collections.Generic.List<DamagerScore> damages of the type, well actually not a type but a list, this list is by us from C #; provided (using System.Collections.Generic;). This list contains elements of type DamagerScore be on this list from the game all the actuators were placed in the destruction of this object (Actor) involved. The more players than can be had even AIs. With the help of .Count one can count the number of items in the list, this value is zero, no one was involved, the query is needed because the method .Destroy() method throws the OnActorDead because we were only on the Players interested destroyed vehicles, we ignore the system simply eliminated.

&& (PlayerPlane.Name().Equals(damages[0].initiator.Actor.Name())) - this is to ensure that even the player is the one who destroyed the vehicle has PlayerPlane.Name() contains the name of the pilot ( has fired in my case, therefore, Kodiak) and 'damages[0].initiator.Actor.Name() returns the name of the person as the last of the car (or plane). damages[0] - sufficient because only one aircraft and a player were on the road. Both will be compared and if consensus is part of this perception. With & & KilledName.Equals (actor.Name () is then queried even if the target is consistent with our desired. Thus, if the entire if statement is true, the ZerstoerteZiele counter incremented by one and spent a brief test message on the screen.

But do not worry in the next lessons will deepen the whole still. As always, I am grateful for criticism and suggestions.

The attachment contains the current instance missions.