Цитата Сообщение от Geniok Посмотреть сообщение
С открытым кодом ЛЮБОЙ может туда дописать все что угодно, а потом выдать за оригинальную кампанию(то есть заняться распространением уже своего скрипта под видом оригинального). То есть теоретически любой может получить доступ к чужому ПК.
Такой открытый код можно проверить, а с закрытым - нельзя, т.е. опять же любой возьмёт Вашу кампанию, напишет вместо Вашего скрипта свой - и увидеть это нельзя будет никак.

Цитата Сообщение от Geniok Посмотреть сообщение
И еще такой вопрос, МГ имеет возможность помогать тем кто делает Офф-лайн кампании? Помощь нужна такого плана, чтобы сама кампания хранилась на сервере Стима. А то сейчас во-первых при обновлении кэша все нестандартные кампании удаляются. Во-вторых пользователям приходится руками править ini-файлы. Что как бы не есть Юзер-френдли.
На стиме, к сожалению, хранить кампании не получится, почему - попробуйте у Лютьера уточнить. А можно детали про удаление нестандартных кампаний? По идее кампания хранящаяся в своей папке не должна удаляться, если удаляется может быть баг какой-то, постараемся исправить.

Цитата Сообщение от Geniok Посмотреть сообщение
И спасибо за консультации.
всегда пожалуйста

2 -atas- вот скрипт для движения ЛФ со списком маркеров для любой карты - при инициализации из основной миссии берутся начальные маркеры, к ним делаются триггеры смены фронта и дальше маркеры переключаются. Я комментарии написал, думаю понятно должно быть как и что там делается.

Код:
using System;
using System.Collections;
using maddox.game;
using maddox.game.world;
using maddox.GP;
using System.Collections.Generic;


public class Mission : AMission
{
    internal class MissionMarker
    {
        internal double x;
        internal double y;
        internal int army;
        internal MissionMarker(double x, double y, int army) { this.x = x; this.y = y; this.army = army; }
    }
        
    private List <MissionMarker> MissionMarkers = new List<MissionMarker> ();

    string BPMaxPlanesAllowed = "3";                       // количество макс. возможно создаваемых самолётов на спаунточке                     
    string BPSpawnParked = "1";                            // рожать ли самолёты на спауне запаркованными 1 - да, 0 - нет 
    int MissionsCount = 1;                                 // счётчик миссий - для вычисления полных имён стат. объектов, например артиллерии 
    int NextMissionNum = 2;                                // флажок номера следующей миссии - для рефреша спаун точек, после смены линии фронта
    int ScoreRed = 0;                                      // "счёт"
    int ScoreBlue = 0;
    
    
    private string[] RedPlanesSet = new string[]           // описание наборов самолётов на спаунточках разных стран
    {   "Aircraft.SpitfireMkIIa",
        "Aircraft.SpitfireMkI",
        "Aircraft.BlenheimMkIV",
        "Aircraft.SpitfireMkIa",
        "Aircraft.DH82A",
        "Aircraft.HurricaneMkI_dH5-20",
        "Aircraft.HurricaneMkI"
    };

    private string[] BluePlanesSet = new string[] 
    {     "Aircraft.Bf-110C-7",
          "Aircraft.Bf-110C-4",
          "Aircraft.Bf-109E-3B",
          "Aircraft.Ju-88A-1",
          "Aircraft.G50",
          "Aircraft.He-111P-2",
          "Aircraft.He-111H-2",
          "Aircraft.Bf-109E-3",
          "Aircraft.BR-20M",
          "Aircraft.Ju-87B-2"
    };

    public ISectionFile triggersFile ;                     // объявление файла для генерации миссии с триггерами смены линии фронта


    public override void Init(ABattle battle, int missionNumber)
    {
        base.Init(battle, missionNumber);
        ISectionFile missFile = GamePlay.gpLoadSectionFile("missions\\Multi\\Dogfight\\xxxxx.mis");   // загружаем в missFile основную миссию, для того, чтобы найти в ней маркеры фронта
        triggersFile = GamePlay.gpCreateSectionFile();                                                 //заодно создаём  файл для генерации миссии с триггерами
        string section, key, value;
        string sectionTr, keyTr, valueTr;
        section = "FrontMarker";                                                                       
        sectionTr = "Trigger";
        int n = missFile.lines(section);                            // определяем, сколько маркеров фронта в миссии 
        for (int i = 0 ; i < n; i++)
        {                                                           // в этом цикле пробегаем по всем маркерам
            key = "FrontMarker"+i.ToString();            
            if (missFile.exist(section,key))                        // проверяем, что маркер есть 
            {
                value = missFile.get(section, key);    
                string[] strs = value.Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries); // и парсим строку с маркером, берём его координаты и страну
                if (strs.Length == 3) 
                {
                    double x; double.TryParse( strs[0], out x);
                    double y; double.TryParse( strs[1], out y);
                    int army; int.TryParse( strs[2], out army);
                    MissionMarker mMarker = new MissionMarker(x,y,army);
                    MissionMarkers.Add(mMarker);                                             // добавляем в список маркеров MissionMarkers проверенный маркер

                    keyTr = "changeArmy" + i.ToString()+"_1";                                // попутно на маркере делаем два триггера - для каждой стороны  
                    valueTr = " TPassThrough 3 1 " + strs[0] + " " + strs[1] + " 500";       // "TPassThrough 3" - триггер сработает при заезде в него наземки, "1" красной, strs[0] + " " + strs[1] координаты, "500"- радиус триггера
                    triggersFile.add(sectionTr, keyTr, valueTr);                             // сохраняем триггер в файле триггерной миссии
                    keyTr = "changeArmy" + i.ToString() + "_2";                              // то же самое для синей стороны
                    valueTr = " TPassThrough 3 2 " + strs[0] + " " + strs[1] + " 500";       
                    triggersFile.add(sectionTr, keyTr, valueTr);                             // сохраняем триггер в файле триггерной миссии
                }
            }   
        }       

    }

    public override void OnBattleStarted()
    {
        base.OnBattleStarted();
        MissionNumberListener = -1;                                 // ставим MissionNumberListener = -1 , чтобы основная миссия "слышала" все триггеры и события в битве
        GamePlay.gpPostMissionLoad(triggersFile);                   // подгружаем миссию с триггерами созданую раньше, в Init
        RefreshBirthPlaces();                                       // и запускаем рефреш спаунов - чтобы они рассчитались   
    }


    private void RefreshBirthPlaces()
    {
        foreach (AiBirthPlace bp in GamePlay.gpBirthPlaces())      // сначала - очистим существующие спауны
        {
            if (bp != null)
                bp.destroy();
        }


        ISectionFile f = GamePlay.gpCreateSectionFile();          // создаём файл миссии в котором переопределим спауны в соответствии с линиями фронта
        string sect;
        string key;
        string value;

        
        for (int i = 0; i < GamePlay.gpAirports().Length; i++)   // сделаем спауны на всех аэродромах карты
        {
            Point3d airpPos = GamePlay.gpAirports()[i].Pos();    // координаты аэродрома
            int armyAtPos = GamePlay.gpFrontArmy(airpPos.x, airpPos.y);   // текущая армия в точке с аэродромом
            sect = "BirthPlace";
            key = "BirthPlace_" + i.ToString();                           // дальше заполняем инфо для спаунов
            string Country = ".";
            if (armyAtPos == 1) Country = "gb";
            if (armyAtPos == 2) Country = "de";
            value = " " + armyAtPos.ToString() + " " + ((int)airpPos.x).ToString() + " " + ((int)airpPos.y).ToString() + " 0 " + BPMaxPlanesAllowed + " " + BPSpawnParked + " 0 " + Country + " . .";
            f.add(sect, key, value);

            sect = "BirthPlace" + i.ToString();                          // и для каждого заполняем списком разрешённых самолётов
            string[] PlaneSet = new string[0];
            if (armyAtPos == 1)
                PlaneSet = RedPlanesSet;
            if (armyAtPos == 2)
                PlaneSet = BluePlanesSet;
            if (PlaneSet.Length > 0)
                for (int j = 0; j < PlaneSet.Length; j++)
                {
                    key = PlaneSet[j];
                    value = "";
                    f.add(sect, key, value);
                }
        }
        GamePlay.gpPostMissionLoad(f);                      // загружаем новые спауны
    }

    private void DestroyEnemyAtCaptured(int markerNum, int army)
    {                                                        // если надо уничтожить всех врагов на захваченном маркере - уничтожаем в этой процедуре
        Point3d AirportPos;
        AirportPos.x = MissionMarkers[markerNum].x;
        AirportPos.y = MissionMarkers[markerNum].y;
        AirportPos.z = 1;

       
        
        foreach (AiGroundGroup gg in GamePlay.gpGroundGroups(army))
        {            
            if (gg != null) 
                if (gg.Pos().distance(ref AirportPos) < 2000)   // 2000 это расстояние от маркера на котором уничтожим врагов
                {
                    string triggerName = gg.Name().Substring(0,gg.Name().IndexOf(":"))+":"+markerNum.ToString()+"_" + army .ToString()+ "_attack";
                    AiTrigger trigger = GamePlay.gpGetTrigger(triggerName);
                    if (trigger != null) trigger.Enable = false;
                    foreach (AiActor ggActor in gg.GetItems())
                    { if ((ggActor as AiGroundActor) != null) 
                          (ggActor as AiGroundActor).Destroy(); 
                    }
                }
        }       

        for (int i = 0; i < MissionsCount; i++)
        {
            AiGroundActor curActor;
            for (int j = 0; j < 10; j++)
            {
                string nameActor = i.ToString() + ":Static" + j.ToString();
                curActor = GamePlay.gpActorByName(nameActor) as AiGroundActor;
                if (curActor != null)
                {
                    if ( (curActor.Army() == army) && 
                         (curActor.Pos().distance(ref AirportPos) < 2000) &&
                         ((curActor.Type() == AiGroundActorType.AAGun)||(curActor.Type() == AiGroundActorType.Artillery) )
                            )  
                        {   
                            curActor.Destroy();
                        }
                }                 
                                               
            }
        }        
    }

    internal ISectionFile CreateNewFrontLineMission(int markerNum, int newArmy)  // создаём новый файл миссии с фронтмаркерами
    {                                                               
        DestroyEnemyAtCaptured(markerNum, MissionMarkers[markerNum].army);   // если надо - уничтожаем врагов
        MissionMarkers[markerNum].army = newArmy;                            // в списке маркеров задаём новую армию на захваченном маркере
           
        ISectionFile f = GamePlay.gpCreateSectionFile();
        string sect;
        string key;
        string value;       
        
        for (int i = 0; i < MissionMarkers.Capacity; i++)          // и для всего списка маркеров пробегаем и записываем в файл их координаты и армии
        {            
            sect = "FrontMarker";
            key = "FrontMarker" + i.ToString();
            value = MissionMarkers[i].x.ToString(System.Globalization.CultureInfo.InvariantCulture.NumberFormat) + " " + MissionMarkers[i].y.ToString(System.Globalization.CultureInfo.InvariantCulture.NumberFormat) + " " + MissionMarkers[i].army.ToString();
            f.add(sect, key, value);                     
        }        
        return f;
    }


    public override void OnMissionLoaded(int missionNumber)
    {
        base.OnMissionLoaded(missionNumber);
        MissionsCount++;                             // счётчик миссий обновляем
        if (missionNumber == NextMissionNum)         // проверяем флажок номера следующей миссии - для рефреша спаун точек, после смены линии фронта
         RefreshBirthPlaces();                       // и обновляем спауны если надо        
    }


    public override void OnTrigger(int missionNumber, string shortName, bool active)              // обрабатываем триггеры сработавшие
    {
        base.OnTrigger(missionNumber, shortName, active);

        int n = MissionMarkers.Capacity;                                                        //пробегаем по списку маркеров, чтобы выяснить - не сработал ли наш триггер на смену линии фронта
        for (int i = 0; i < n; i++)
        {
            for (int j = 1; j < 3; j++)
            {
                string str = "changeArmy" + i.ToString() + "_" + (j).ToString();
                if (str.Equals(shortName))                                                  // если сработал  - тогда обрабатываем его - счёт обновляем, пишем что фрмия сменилась 
                {
                    if (MissionMarkers[i].army != j)
                    {
                        string armyOwner;
                        if (j == 1)
                        {
                            armyOwner = " Red army";
                            ScoreRed += 10;
                        }
                        else
                        {
                            armyOwner = " Blue army";
                            ScoreBlue += 10;
                        }

                        GamePlay.gpHUDLogCenter("Front turn to " + armyOwner + " at sector " + GamePlay.gpSectorName(MissionMarkers[i].x, MissionMarkers[i].y).ToString() + "; Red " + ScoreRed.ToString() + ":" + ScoreBlue.ToString() + " Blue");
                        NextMissionNum = GamePlay.gpNextMissionNumber();                     // ставим флажок номера следующей миссии - чтобы после загрузки новых линий фронта пересчитались спауны
                        GamePlay.gpPostMissionLoad(CreateNewFrontLineMission(i, j));         // грузим миссию с обновлёнными маркерами ЛФ
                        // ну и можно как в предыдущей версии загрузить подмиссию на изменённый маркер, у меня это были подмиссии с защищающимися артами вида "x_y_defend.mis", где x - номер маркера, y - защищающаяся страна :
                        GamePlay.gpPostMissionLoad("missions\\Multi\\Dogfight\\CTF_submissions\\" + i.ToString() + "_" + (j).ToString() + "_defend.mis");       
                    }
                    break;
                }

                // ну и заодно проверяем не уничтожили  ли атакующих из подмиссий в каждой такой подмисси был триггер вида "x_y_attack" срабатывавший на уничтожение атакующей группы
                str = i.ToString() + "_" + (j).ToString() + "_attack";
                string addStr = " ground group eliminated at sector " + GamePlay.gpSectorName(MissionMarkers[i].x, MissionMarkers[i].y).ToString();
                if (str.Equals(shortName))                  // соответственно если сработал такой триггер, обрабатываем его , счёт меняем, пишем что группу уничтожили и запускаем ещё одну атакующую миссию
                {
                    if (j == 2)
                    {
                        ScoreRed++;
                        addStr = "Blue" + addStr;
                    }
                    else
                    {
                        ScoreBlue++;
                        addStr = "Red" + addStr;
                    }
                    GamePlay.gpHUDLogCenter(addStr + ",and the score is Red " + ScoreRed.ToString() + ":" + ScoreBlue.ToString() + " Blue");
                    GamePlay.gpPostMissionLoad("missions\\Multi\\Dogfight\\CTF_submissions\\" + str + ".mis");
                    break;
                }
                // если логика битвы другая , тогда этот блок  с проверкой убираем
            }
        }        

    }

}