Ил-2 Штурмовик: Битва за Британию. Скрипты. FAQ
Содержание
- 1 Инструменты отладки при работе со скриптами и .dll
- 2 Обозначение групп с номерами I и V
- 3 Как отслеживать статики?
- 4 MissionNumberListener и присвоение номера миссии
- 5 Как убирать с земли самолеты, которые сели на вынужденную или разбились?
- 6 Что такое airport.cpp
- 7 Отправка сообщений для заданной армии и типа самолета (истребитель/бомбардировщик)
- 8 Имена групп
- 9 Удаление места появления группы (BirthPlaces)
- 10 Как во время миссии пересадить игрока в другой самолет?
- 11 Как задать периодические загрузки миссии без использования TickCounter()?
- 12 Какие параметры у GamePlay.gpHUDLogCenter?
- 13 Относятся ли корабли к AiGroundActor?
- 14 Как передать в лог сервера сообщения об уничтоженных объектах?
- 15 Как открыть подмиссию, чтобы в ней отображались все объекты?
- 16 Как можно суб-скрипт загружать из другого скрипта, на загружая .mis файл суб-миссии?
- 17 DamagerScore - параметры
- 18 Z_VelocityIAS и подобные - единицы измерения
- 19 Использование класса List(Of T) вместо массивов
- 20 Как удалить все юниты подмиссии
- 21 Формат GamePlay.gpSetOrderMissionMenu
- 22 За что отвечают методы OnBattleStarted() и OnMissionLoaded() и что означает ключевое слово base?
- 23 Поясните использование метода String.Substring в скриптах
- 24 Как переключить состояние триггера в скриптах?
- 25 Как удалить все юниты заданной подмиссиии не перебирая в циклах всю наземку?
- 26 Почему при старте миссии техника(корабли, танки и т.п.) не двигаются, хотя им всем задан маршрут?
Инструменты отладки при работе со скриптами и .dll
- К сожалению, похоже, что кроме собственной головы - никаких. Если кто найдет обратное, буду очень признателен.
- Почти наверняка влияет вот эта строчка - //-$debug, но я с ней пока не экспериментировал.
- Я делаю так - запускаю студию, новый проект любой C#. Прилинковал к проекту:
...\Steam\SteamApps\common\il-2 sturmovik cliffs of dover\parts\core\part.dll ...\Steam\SteamApps\common\il-2 sturmovik cliffs of dover\parts\core\maddox.dll ...\Steam\SteamApps\common\il-2 sturmovik cliffs of dover\parts\core\gameWorld.dll ...\Steam\SteamApps\common\il-2 sturmovik cliffs of dover\parts\core\gamePlay.dll ...\Steam\SteamApps\common\il-2 sturmovik cliffs of dover\parts\bob\Campaign.dll
- Подозреваю, что не все эти сборки необходимы, но особо туда не лез, пусть будут. Последняя же нужна только для миссий из кампании.
- Создал класс, в using прописал необходимый минимум
using System; using maddox.game; using maddox.game.world;
- После код просто копирую в игру. Единственное убираю неймспейс моего проекта. Не самая удобная штука, но писать вполне можно.
Обозначение групп с номерами I и V
- Пример из игры -"BoB_LW_JG53_I"
- 1 - латинская буква "I", 5 -латинская "V", 10 - латинская "X" и т.д... Собственно про эти римские цифры можно посмотреть на сайте wikipedia.org
Как отслеживать статики?
- Да, статики, это не акторы, у них нет "мозга", и они для красоты стоят, событий не вызывают.
- Да, статики, это не акторы, у них нет "мозга", и они для красоты стоят, событий не вызывают.
MissionNumberListener и присвоение номера миссии
- Это поле, которое показывает события какой миссии скрипт миссии слушает - при загрузке миссии в это поле ставится её номер - т.е. она "слышит" только свои события. Если выставить номер другой миссии - будет "слушать" только её, чтобы все миссии слышать - поле в меньше нуля выставить надо (то есть "-1").
Как убирать с земли самолеты, которые сели на вынужденную или разбились?
public override void OnAircraftCrashLanded(int missionNumber, string shortName, AiAircraft aircraft) { base.OnAircraftCrashLanded(missionNumber, shortName, aircraft); Timeout(5, () => { aircraft.Destroy(); }); }
Подробнее скрипт удаления самолетов здесь.
Что такое airport.cpp
- naryv: airport.cpp не убирает самолёты и обломки, он должен машинки к севшим самолётам подвозить, но не уверен что работает, это очень старый скрипт, как реликт скорее всего остался.
Отправка сообщений для заданной армии и типа самолета (истребитель/бомбардировщик)
- Определить армию игрока и выдавать ему соответствующее сообщение:
if (aircraft != null) switch (aircraft.Army()) { case 1: if (aircraft.Type() == AircraftType.Bomber) { GamePlay.gpHUDLogCenter(new Player[] {player},"Red Bomber, Bomb it all, hitler caput"); } else { GamePlay.gpHUDLogCenter(new Player[] { player }, "Red Fighter, fight them all"); } break; case 2: if (aircraft.Type() == AircraftType.Bomber) { GamePlay.gpHUDLogCenter(new Player[] { player }, "Das bomber!"); } else { GamePlay.gpHUDLogCenter(new Player[] { player }, "Das jager!"); } break; }
Подробнее здесь.
Имена групп
- Группа, которая стартует скриптом (с помощью действия, скажем), по сути относится уже к другой миссии. Попробуйте во-первых, назначить переменной MissionNumberListener значение -1, примерно вот так:
public override void OnBattleStarted() { base.OnBattleStarted(); MissionNumberListener = -1; }
- Во-вторых, имя группы состоит из собственно имени группы и префикса "принадлежности к миссии" (номер миссии с двоеточием). Получить правильно полное имя группы (например для использования в GamePlay.gpActorByName()) можно примерно вот так:
public override void OnActorCreated(int missionNumber, string shortName, AiActor actor) { base.OnActorCreated(missionNumber, shortName, actor); string fullName = ActorName.Full(missionNumber, shortName); }
- В любом событии, относящемся к акторам, в первых двух параметрах передаются собственно номер миссии, где произошло событие, и короткое имя актора (как указано в файле миссии). Из двух этих параметров мы и получаем полное имя актора.
Удаление места появления группы (BirthPlaces)
- Средствами редактора это невозможно, скриптом так:
foreach (AiBirthPlace bp in GamePlay.gpBirthPlaces()) { if (bp != null) bp.destroy(); }
Как во время миссии пересадить игрока в другой самолет?
- Находим группу, в цикле перебираем самолеты группы и сажаем игрока в первый же самолет, где есть кабина пилота.
AiActor actor = GamePlay.gpActorByName(ActorName.Full(MissionNumber, "BoB_LW_LG2_I.01")); if (actor is AiAirGroup && GamePlay.gpPlayer() != null) { Player player = GamePlay.gpPlayer(); foreach (AiAircraft airc in (actor as AiAirGroup).GetItems()) { bool isFound = false; for (int i = 0; i < airc.Places(); i++) { if (airc.ExistCabin(i)) { if (airc.CrewFunctionPlace(i).Equals(CrewFunction.Pilot)) { player.PlaceEnter(airc, i); isFound = true; break; } } } if (isFound) break; } }
Как задать периодические загрузки миссии без использования TickCounter()?
- Периодические загрузки миссии без использования TickCounter() осуществляются через MissionLoader:
//Запускаем единожды, когда миссия заружена public override void Init(maddox.game.ABattle battle, int missionNumber) { base.Init(battle,missionNumber); //Planned missions MissionLoader(30,10,"missions/Multi/Dogfight/bombers1.mis"); // 10 секунд от начала основной миссии и повторяется каждые 30 секунд MissionLoader(100,60,"missions/Multi/Dogfight/bombers2.mis"); // 60 секунд от начала основной миссии и повторяется каждые 100 секунд } public void MissionLoader(int period, int offset, string mission) { if (offset > 0) Timeout(offset, () => {MissionLoader(period,0,mission);}); else { GamePlay.gpPostMissionLoad(mission); Timeout(period, () => {MissionLoader(period,0,mission);}); } }
Какие параметры у GamePlay.gpHUDLogCenter?
- GamePlay.gpHUDLogCenter("",123) ,где 123 - длительность отображения надписи. Также можно показывать сообщение определённым игрокам, по остальным вопросам - пока никак, возможно позже добавится более широкое управление.
Относятся ли корабли к AiGroundActor?
- Да, корабль - это AiGroundActor и AiGroundGroup,с типом Ship, всех кораблей страны найти можно так:
foreach (AiGroundGroup gg in GamePlay.gpGroundGroups(army)) { if (gg.Type == AiGroundGroupType.Ship) {// тут делаем с ними что нам нужно, например if (gg != null) (gg 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); GamePlay.gpLogServer(null, "{0} actor dead, 1st killer is {1} ", new object[] { actor.Name(),damages[0].initiator.Actor.Name()}); }
Как открыть подмиссию, чтобы в ней отображались все объекты?
- Секция с описанием карты, времени и пр. начальных условий миссии не нужна в подмиссии - ведь она загружается к уже запущенной миссии - поэтому такую секцию я удалил - так подмиссия чуть-чуть, но быстрее грузится. Если надо в редакторе открыть - можно просто из основной эту секцию скопировать в файл подмиссии :
[PARTS] core.100 bob.100 [MAIN] MAP Land$Online_Cross_v_Roundel BattleArea 6000 6000 26000 26000 1000 TIME 16 WeatherIndex 0 CloudsHeight 1000 BreezeActivity 10 ThermalActivity 10
Как можно суб-скрипт загружать из другого скрипта, на загружая .mis файл суб-миссии?
- Создавать пустые подмиссии, в которых будут только нужные куски скрипта.
DamagerScore - параметры
- 1. В DamagerScore из maddox.game.world есть две числовые переменные score и time. Score - степень повреждения и в чем она измеряется?
- 2. Если смотреть список инициаторов сбитого самолета (как акторов) по функции OnActorDead (из AMission), то в нем всегда есть сам убитый актор, у которого score больше нуля. Как это понять? Типа повреждения, которые наносятся не напрямую противником, записываются на самого актора. Например, противник повредил систему охлаждения (записано противнику), из-за поврежденной системы охлаждения накрывается двигатель (а это уже пишут самому актору). Или на убитого актора просто записывают очки повреждения от самого падения?
- 3. Что показывает параметр time? Как я понимаю это время нанесения последнего повреждения для инициатора?
- 1. Score - это степень участия инициатора в демадже, т.е. если игроки вдвоём уничтожали/расстреливали один самолёт, убили его и нанесли одинаковые повреждения, у score будет по 0.5, если втроём с тем же результатом - по 0.3(3) ну и т.д., если повреждения не одинаковые - то у кого повреждения более значительные, у того и score больше.
- 2. На убитого актора записывают оставшийся демадж от падения, если взорвать его в воздухе - ему ничего не запишется. Т.е. мы сломали ему мотор нам записали, допустим 0.6 демаджа, дальше он упал и разбился - ему 0.4 запишут.
- 3. Да, именно так.
Z_VelocityIAS и подобные - единицы измерения
- 1. В каких единицах возвращается значение по запросу Z_VelocityIAS и подобные? Получал с subtype=-1. Значение в среднем ниже на 2,36, если ориентироваться по прибору в км/ч на 500-х метрах.
- 2. Так как не совсем ясно, в каких единицах возвращается значение по вопросу выше, пробовал получать по Z_VelocityMach. Стало похоже на правду, но возник еще вопрос: конкретное значение ск. звука зависит от температуры воздуха?
- 1. В метрах/секунду все подобные параметры.
- 2. Да, зависит.
Использование класса List(Of T) вместо массивов
- Было:
//Параметры меню string[] descMainMenu; //Описание пунктов главного меню descMainMenu = new string[] { "Тест системы сообщений", "Конвои" }; private void setMainMenu( Player player ) { GamePlay.gpSetOrderMissionMenu( player, false, 0, descMainMenu, new bool[] { false, true } ); }
- Стало:
List<string> descMainMenu = new List<string>(); descMainMenu.Add("Тест системы сообщений"); descMainMenu.Add("Конвои"); private void setMainMenu( Player player ) { GamePlay.gpSetOrderMissionMenu( player, false, 0, descMainMenu.ToArray(), new bool[] { false, true } ); }
Как удалить все юниты подмиссии
- Никак.
Что бы миссия не была неизвестной, надо перед ее загрузкой запомнить след. номер (NextMissionNumber или как то так).
Частичное решение состоит в переборе наземных и воздушных групп для всех представленных армий и сверке запомненного номера миссии с номером, заключенном в полном имени юнита (формат примерно такое - номер миссии, двоеточие, shortName). Полное имя получается через AiActor.Name().
Почему частичное? Перебором групп ты не найдешь юнитов, которые групп не имеют (артиллерия и стат. корабли как минимум), т.е. по OnActorCreated надо еще и их запоминать. Похожее решение используется в морском льве.
Домики, ящички и все что в разделе статический и т.д. не удалишь вообще никак.
Формат GamePlay.gpSetOrderMissionMenu
gpSetOrderMissionMenu( Player player, bool thisSubMenu, int ID, string[] keys, bool[] bSubMenu );
Player player - игрок которому выдаётся кастомное меню
bool thisSubMenu - является ли данный набор пунктов подменю
int ID - ID пункта по которому его можно идентифицировать
string[] keys массив строк-пунктов меню
bool[] bSubMenu массив bool показывающий есть ли у каждого пункта подменю
За что отвечают методы OnBattleStarted() и OnMissionLoaded() и что означает ключевое слово base?
Ответ:
OnBattleStarted()- Метод вызывается один раз при старте битвы. Точнее - как только битва уже стартовала.
OnMissionLoaded() - Вызывается как только загрузилась новая миссия. Причем вызывается во всех ранее загруженных миссиях.
Роль ключевого слова base. Вызывается переопределяемый метод класса-предка. На пример - у нас есть класс MyMission, предком которого является AMission. В классе MyMission переопределен OnBattleStarted(), в котором мы пишем на экран - "Битва началась!". Определим еще один класс RealMission, предком которого будет класс MyMission, и так же, переопределим в нем метод OnBattleStarted(), где на экран выведем - "Таки да, началась!" Что произойдет, если мы создадим экземпляр RealMission и вызовем в нем OnBattleStarted()? Как и предполагается, будет выведена надпись "Таки да, началась!". Получается, весь код, который мы писали в MyMission в том же методе, никак не используется. Для этого и существует base. Т.е. Этим мы вызовем метод предка. Т.е., если в реализации RealMission.OnBattleStarted() вначале дописать base.OnBattleStarted(), мы получим две последовательно выведенные надписи - "Битва началась!" и "Таки да, началась!".
Поясните использование метода String.Substring в скриптах
Ответ:
Статья на MSDN
В многих случаях нумерация символов с строке начинается с нуля (в других языках может и с единицы) Соответственно первые двенадцать символов будут иметь индексы с 0 до 11. В данном случае первая цифра (0) означает индекс символа, с которого надо начинать отсчет. "12" означает кол-во символов, которые надо забрать.
Этот метод можно применить для оптимизации кода следующим образом:
Чтобы не копировать постоянно названия триггеров, мы можем использовать такой код: if (shortName.Substring(0, 12).Equals("WinCondition") && active) { WinConditionCounter++; GamePlay.gpGetTrigger(shortName).Enable = false; }
Имеются подмиссии, в каждой триггер с именем "WinCondition1", "WinCondition2", "WinCondition3" ... "WinConditionn". При срабатывании любого триггера, он проверяется в условии.
shortName.Substring(0, 12) позволяет учитывать только первые 12 символов названия триггеров. Т.е. мы можем добавлять сколь угодно подмиссий с именами типа "WinConditionn" и не портить при этом код главного скрипта.
Как переключить состояние триггера в скриптах?
Ответ:
Используйте следующий код:
GamePlay.gpGetTrigger(shortName).Enable = false; - переключает триггер в состояние OFF. GamePlay.gpGetTrigger(shortName).Enable = enable; - переключает триггер в состояние ON. , где shortName - имя триггера
Как удалить все юниты заданной подмиссиии не перебирая в циклах всю наземку?
Ответ:
Пока никак.Чтобы миссия не была неизвестной, надо перед ее загрузкой запомнить след. номер.Частичное решение состоит в переборе наземных и воздушных групп для всех представленных армий и сверке запомненного номера миссии с номером, заключенном в полном имени юнита (формат примерно такое - номер миссии, двоеточие, shortName). Полное имя получается через AiActor.Name().Почему частичное? Перебором групп ты не найдешь юнитов, которые групп не имеют (артиллерия и стат. корабли как минимум), т.е. по OnActorCreated надо еще и их запоминать. Похожее решение используется в морском льве. Домики, ящички и все что в разделе статический и т.д. не удалишь вообще никак.
Почему при старте миссии техника(корабли, танки и т.п.) не двигаются, хотя им всем задан маршрут?
Ответ:
Они для игрока находящегося дальше радиуса видимости( ~20км) "спят", игрок о них не получает информацию, для сервера они перемещаются, когда игрок приблизится на расстояние ближе радиуса видимости, они у него на машине "проснутся" и переместятся туда, где они находятся у сервера и дальше будут двигаться синхронно. Сделано это, чтобы не грузить сеть ненужной информацией о дальних объектах и машину-клиента ненужными расчётами. Если в воздухе Вы появляетесь ближе радиуса видимости - техника "просыпается" сразу.