XGM Forum
Сайт - Статьи - Проекты - Ресурсы - Блоги

Форуме в режиме ТОЛЬКО ЧТЕНИЕ. Вы можете задать вопросы в Q/A на сайте, либо создать свой проект или ресурс.
Вернуться   XGM Forum > Warcraft> Академия: форум для вопросов> Желтая пресса: обучающие статьи
Ник
Пароль
Войти через VK в один клик
Сайт использует только имя.

 
FellGuard
Losyash
offline
Опыт: 39,547
Активность:
Создание ИИ в блокноте

Правила написания ИИ скриптов для кампаний


Автор статьи Moyack
Вольный перевод FellGuard

______________________

Предупреждение:
более всего подходит для melee карт или миссий кампании в стиле blizzard

______________________

СОДЕРЖАНИЕ

  • Типы ИИ
  • Необходимые утилиты
  • Что такое Jass AI
  • Итак, начнем!
  • Стратегия отстройки
  • Стратегия нападения
  • Стратегия улучшений
  • Способности героев
  • Контроль ИИ скрипта триггерами
  • Проверка ИИ

Ссылки на статьи на данную тему


Структура AI-файлов by Sargeras

ВСТУПЛЕНИЕ


Цель данной статьи – познакомить читателя c основами создания несложных ИИ, которые будут имитировать одного игрока и выполнять последовательность команд к атаке, строительству и улучшениям.


» ТИПЫ ИИ

Прежде чем начать, надо сказать, что в игре присутствуют два типа ИИ, это
  1. Стандартный ИИ (Standart AI)
  2. ИИ для кампании (Campaign AI)
    ИИ для кампании, по мнению автора, самый легкий для создания, так как следует вполне четким, раз установленным и не меняющимся правилам.
    Стандартный же скрипт в большинстве случаев расчитан на симуляцию человеческого поведения, а потому более разнообразен, непредсказуем, ит.д., ит.п., и, что не менее важно, порой совершенно не поддается проверкам и исправлениям.
    Этот тип можно освоить прямо в WorldEditor`е, однако если вы пишите ИИ именно для кампании, то вам больше подойдет первый тип. Впрочем, существуют довольно вменяемые ИИ созданные по типу 2).
    Наверное, тут следует добавить еще о существовании отдельного скрипта, названного
  3. AMAI
    но в статье речи о нем не будет, могу только от себя добавить, что это целая система, которая призвана максимально приблизить стиль и поведение компьютерного игрока к человеческому.

» НЕОБХОДИМЫЕ УТИЛИТЫ

Чтобы прямо сейчас создать ИИ можете воспользоватся встроенным в WorldEditor редактором интеллекта, и на выходе вы получите стандартный ИИ, который тяжел как мамонт в своих размерах, так как он учитывает кучу факторов, вне зависимости от желания создателя.
Для создания же ИИ по типу вшитых в базу игры скриптов для кампаний, нужен только блокнот.

Автор рекомендует воспользоваться также утилитами JASSCRAFT или JASS SHOP PRO, так как они содержат очень удобную подсветку синтаксиса, и, что важно, легкий доступ к коммандам из cоmmon.ai

» ЧТО ТАКОЕ JASS AI

По мнению автора, Jass AI это язык, который Blizzard ent. используют для программирования искусственного интеллекта. Jass AI полностью идентичный синтаксис и типы переменных, что и Jass, так что вы легко разберетесь со всем, что касается Jass AI.

Все команды для ИИ скриптов расположены в файле common.ai, его можно легко найти в архивах war3.mpq, war3x.mpq, во внутренней директории \Scripts. Еще нужно сказать, что использовать в создании ИИ скриптов можно не только команды, содержащиеся в common.ai - как Jass, так и Jass AI прекрасно переваривает все функции из common.j.

» ИТАК, НАЧНЕМ!

Заходим в JassShopPro или JASSCRAFT и создаем новый документ. Для всех, кто отважится использовать блокнот, или может быть Ворд, или какое-нибудь еще програмное обеспечение, скажу, что создавать и писать нужно обычный текстовый файл, а потом в ручную переименовать разширение *.txt (*.doc) на *.ai.
Вот как выглядит структура *.ai-скрипта, почти неотличимо от скрипта карты.
Код:
globals
    // Сюда ложим глобальные переменные
endglobals

function ____ takes _____ returns ______
    //Сюда ложится набор ф-ий. Есть определенный набор для наиболее часто    
    //встречающегося поведения юнитов в кампании, иными словами суицида, вы можете     
    //посмотреть этот набор в любом *.ai скрипте все в тех же war3.mpq, war3x.mpq.
endfunction

...

function main takes nothing returns nothing
    //Главный код лежит здесь.
endfunction
Для людей, незнакомых с Jass`ом в секции globals мы объявляем глобальные переменные, ниже мы пишем сколько-то своих функций, которые понадобятся в коде (что необязательно), и последней идет функция main, которая будет использовать глобальные переменные и функции, которые мы прописали до нее. Это функция которая будет выполняться игрой.

Чтож, с таким вот базисом мы можем начинать наш скриптинг. Сперва в секции globals добавим переменную игрока, которого будет атаковать наш ИИ, обзовем его MyVictim
Код:
globals
    player MyVictim = Player(0) // Это Игрок_1 (красный)
endglobals
Теперь создаем главную функцию (main)
Код:
function main takes nothing returns nothing
    call CampaignAI( <Farm ID>, null ) 
endfunction
Функция CampaignAI говорит сама за себя – она инициализирует и конфигурирует ИИ компьютерного игрока в соответствии со стандартами для карт кампаний. У функции два параметра:
<Farm ID> - это код юнита-фермы – помогает определить ИИ тип юнита-производителя пищи, и второй –
код героя; возвращает функцию, оперирующую со способностями героя, если тому вздумается повысить уровень. Пока этот пораметр оставляем в виде null, вернемся к нему позже.

Теперь давайте настроим некоторые параметры внутри ИИ скрипта. По умолчанию, наш скрипт имеет следующие характеристики:
  • Герои не являются более приоритетными целями
  • Юниты вне зависимости от НР включены в атакующую группу. (к примеру, в стандартном ИИ сильно поврежденные юниты остаются на базе)
  • Работникик ремонтируют только здания и то, когда свободны (от добычи золота, например)
  • Работники добывают ресурсы крайне медленно (за раз по 1ед. дерева или золота каждый)
  • Ну и еще герои не имеют привычки отступать или подбирать/покупать предметы
Для карт кампании, замечу, характеристики в самый раз – потому как в кампании компьютер должен оставаться жив до тех пор, пока его не уничтожишь, и добыча золота и дерева контролируется триггерами. Это помогает избежать и некоторых неприятных моментов с излишней вырубкой леса компьютерм в том числе, но это уже мелочи.
Относительно же равнодушного отношения к юнитам, которые ИИ включает в атакующую группу – это как раз тот самый суицид, в кампаниях атаки организованы по принципу волн камикадзе, как ни глупо это звучит. После того, как последний камикаджзе погибает, начинается отчсчет до следующей волны.

Если вас не устраивает такое положение вещей, надо конфигурировать наш ИИ кое-какими AI функциями. Вот самые употребимые, на взгляд автора:
Цитата:
  • SetTargetHeroes(Boolean): дать героям приоритет как целям
  • SetUnitsFlee(Boolean): Позволить покалеченым юнитам отсидеться на базе.
  • SetHeroesFlee(Boolean): Тоже самое, но с героями.
  • SetGroupsFlee(Boolean): Тоже самое, но со всей атакующей группой (тут автор рекомендует не включать ничего, улыбается, и говорит, что ИИ начинает вести себя как цыпленок)
  • SetSlowChopping(Boolean): Включить нормальную добычу ресурсов
  • SetPeonsRepair(Boolean): Позволить работникам восстанавливать технику
  • SetHeroesBuyItems(Boolean): Герой вдруг решает попутно заглянуть в лавочку гоблина
  • SetHeroesTakeItems(Boolean): Герой подбирает упавшие предметы
Так, для теста нужно внести некоторые добавления в код функции main
А кто ее еще не создал – бегом создавать
Код:
function main takes nothing returns nothing
    call CampaignAI( MOON_WELL, null ) // автор отдал предпочтение эльфам
    call SetSlowChopping( false ) 
    call SetPeonsRepair( true) 
endfunction
Теперь ИИ будет нормально добывать ресурс и ремонтировать технику.
Цитата:
Все стандартные юниты имеют свои постоянные, «собственные» имена, они присваиваются в common.ai для удобства скриптинга.
В скриптах *.j (тоесть, в триггерах) карты имя MOON_WELL не будет работать!!!
В *.j скриптах для этих целей используются рав-коды юнитов. К примеру, лунный колодец имеет следующий рав-код: 'emow'
А вот в *.ai скриптах прекрасно работают как «собственные» имена, так и рав-коды.

И если вы внесете своего нестандартного юнита, то либо пишите его рав-код (ибо он не имеет имени), либо добавляйте его имя в common.ai.
Последнее, впрочем, хорошо только для полноценных модов, так как common.ai довольно громоздкий и импортировать его нет смысла.

Чтобы узнать рав-код объекта, нужно в Object Editor-е нажать Ctrl+D
Таким образом, можно писать как
Цитата:
call CampaignAI( MOON_WELL, null )
так и
Цитата:
call CampaignAI( ‘emov’, null )
Если вы хотите организовать все «по полочкам» в новосотворенном скрипте, можно оформить функцию main так:
Код:
function ConfigureAI takes nothing returns nothing
    call SetSlowChopping( false )
    call SetPeonsRepair( true )
endfunction

function main takes nothing returns nothing
    call CampaignAI( MOON_WELL, null )
    call ConfigureAI( )
endfunction

» СТРАТЕГИЯ ОТСТРОЙКИ

Самая моя любимая часть в ИИ – это написание отстройки зданий. Здесь важно помнить 2 вещи: очередность постройки, когда и что вы хотите стрoить, и зависимости/требования (так например, без Охотничьего зала мы не можем вырастить Сторожевое древо)
Помня это, отдаем соответствующие команды в скрипте:
Код:
globals
    player MyVictim = Player( 0 )
endglobals

function ConfigureAI takes nothing returns nothing
    call SetSlowChopping( false )
    call SetPeonsRepair( true )
endfunction

function main takes nothing returns nothing
    call CampaignAI( MOON_WELL, null )
    call ConfigureAI( )
    // **********************************
    // *            Building Strategy         *
    // **********************************
    call SetBuildUnitEx( 1, 1, 1, TREE_LIFE )
    call SetBuildUnit( 15, WISP )
    call SetBuildUnitEx( 1, 2, 3, MOON_WELL )
    call SetBuildUnitEx( 1, 1, 2, ANCIENT_WAR )
    call SetBuildUnit( 1, ELF_ALTAR )
    call SetBuildUnit( 1, HUNTERS_HALL )
    // **********************************
    // *                 EndStrategy       *
    // **********************************
endfunction
Итак, мы возвели базовые постройки для первоуровнего лагеря! Для этого мы использовали новые функции:
  • SetBuildUnit( n, UnitID ) ф-ия говорит за себя, она отдает ИИ команду тренировать либо строить n юнитов/зданий такого-то типа.
  • SetBuildUnitEx( e, n, i, UnitID) это в принципе то же самое, разве что только для каждого уровня сложности ИИ (easy, nomal, insnare) задается свое количество юнитов.
Теперь у нас есть город, но его некому защищать, так что отдадим соответствующую команду ИИ:
Код:
globals
    player MyVictim = Player( 0 )
endglobals

function ConfigureAI takes nothing returns nothing
    call SetSlowChopping( false )
    call SetPeonsRepair( true )
endfunction

function main takes nothing returns nothing
    call CampaignAI( MOON_WELL, null )
    call ConfigureAI( )
    // **********************************
    // *           Building Strategy         *
    // **********************************
    call SetReplacements( 1, 2, 3 ) // <==(1)
    call SetBuildUnitEx( 1, 1, 1, TREE_LIFE )
    call SetBuildUnit( 15, WISP )
    call SetBuildUnitEx( 1, 2, 3, MOON_WELL )
    call SetBuildUnitEx( 1, 1, 2, ANCIENT_WAR )
    call SetBuildUnit( 1, ELF_ALTAR )
    call SetBuildUnit( 1, HUNTERS_HALL )
    call SetBuildUnitEx( 0, 1, 2, ANCIENT_PROTECT )
    call CampaignDefenderEx( 2, 2, 3, ARCHER ) // <==(2)
    call CampaignDefenderEx( 1, 1, 1, HUNTRESS ) 
    // **********************************
    // *               EndStrategy *
    // **********************************
endfunction
Новые функции:
  • SetReplacements(e, n, i): Задает, сколько раз будет ИИ менять убитых защитников, в нашем случае, для каждого уровня сложности соответственно это будет 1, 2 и 3 раза
  • CampaignDefenderEx(e, n, i, UnitID): почти тоже, что и SetUnitBuild, с той лишь разницей, что заменять предыдущих павших защитников они будут ровно столько, сколько указано в функции SetReplacements.

» СТРАТЕГИЯ НАПАДЕНИЯ

Итак, теперь наш «малыш» способен за себя постоять, так что теперь пришло время тренировать нападающих юнитов и слать их в атаку.
Для этого воспользуемс следующими функциями:
Код:
function main takes nothing returns nothing

   ...// Здесь у нас код отстройки

//*** WAVE 1 *** 
    call InitAssaultGroup() // <==(1)
    call CampaignAttackerEx( 2, 3, 3, ARCHER ) // <==(2)
    call CampaignAttackerEx( 0, 1, 2, HUNTRESS ) 
    call CampaignAttackerEx( 0, 1, 1, BALLISTA ) 
    call SuicideOnPlayerEx( M2, M3, M3, MyVictim ) // <==(3)
endfunction
Следующие действия выполняются:
  • InitAssaultGroup(): Расформировывает предыдущую атакующую группу (условно - волну).
  • CampaignAttackerEx( e, n, i, UnitID ): Тут все ясно, формирует список юнитов в группу по уровню сложности. Группа будет ждать, пока все включенные в нее юниты не будут обучены, так что и здесь не забывайте про зависимости.
  • SuicideOnPlayerEx( te, tn, ti, TargetPlayer ): Функция ждет по уровню сложности определенное время (te, tn, ti), и потом отдает приказ атаковать определенного игрока. Впрочем, слово атаковать не слишком подходит – все видно из названия.
Интересная особенность функции – если игрок-цель, которому эта функция «посвящает» группу значится проигравшим, то ИИ изберет на его место другого противника. Если же все противники ИИ повержены, то группа никуда не идет.

В качестве величин te, tn, ti, в имеющихся в архиве игры скриптах использованы M1, M2, M3… M15. Что это? Это сколько минут ждет ИИ до следующей волны, после того, как группа окончательно сформирована. Например, М2 это 2мин. ожидания (2*60сек). Можно использовать этм обозначения от М1 до М15, можете посмотреть в common.ai.
В *.j скриптах эта единица не работает.
Цитата:
Существует несколько спсосбов приказать группе напасть. SuicideOnPlayerEx берет, по мнению автора, за цель, главное вражеское поселение (его стартовую позицию), но это лишь до тех пор, добавлю от себя, пока у вас не появится другого поселения (на свежем руднике, например). А возможно вам вообще понадобится сконцентрировать «огонь» на отдельной боевой единице (как в картах, где нужно охранять какой-нибудь караван), в таких случаях вам надо будет использовать функции типа AttackMoveKillA( TargetUnit ). Рассмотрим эту ф-ию на примере отдельного нейтрально-враждебного лагеря в качестве цели.
Код:
function main takes nothing returns nothing
    local unit crp

    ... // Здесь у нас код отстройки

//*** WAVE 1 *** 
    call InitAssaultGroup()
    set crp = GetCreepCamp(0,9,false) // Обозначаем цель
    call CampaignAttackerEx( 2, 3, 3, ARCHER ) 
    call CampaignAttackerEx( 0, 1, 2, HUNTRESS ) 
    call Sleep( M3 ) // Ждем 3 минуты
    call AttackMoveKillA(crp) // Отдаем приказ атаковать обозначенную цель
endfunction
Здесь новое:
  • GetCreepCamp( min, max, flyers ): Функция возвращает любой нейтр.-враждебный лагерь определенной, хм, «опасности», между значениями min и max, включая летающих (true) или не включая (false).
  • Sleep( n ): «подвешивает» скрипт на n времени.
  • AttackMoveKillA( Unit ): Приказ сформированной группе отаковать юнита.
Таким образом у нас есть одна работающая волна. Для полноценного ИИ это конечно недостаточно, а потому мы добавим еще несколько волн, соостветствующих общей стратегии развития в игре: одна волна для первой партии юнитов первого уровня, другая волна, с не слишком большим промежутком – партия покрупнее с другими юнитами первого уровня, затем – еще одна, как с первоуровневыми, так и со второуровневыми юнитами-кастерами, итд:
» Код
Код:
globals
    player MyVictim = Player( 0 )
endglobals

function ConfigureAI takes nothing returns nothing
    call SetSlowChopping( false )
    call SetPeonsRepair( true )
endfunction

function main takes nothing returns nothing
    call CampaignAI( MOON_WELL, null )
    call ConfigureAI( )
    // **********************************
    // *           Building Strategy         *
    // **********************************
    //
    // Уровень 1, постройки
    call SetReplacements( 1, 2, 3 )
    call SetBuildUnitEx( 1, 1, 1, TREE_LIFE )
    call SetBuildUnit( 15, WISP )
    call SetBuildUnitEx( 1, 2, 3, MOON_WELL )
    call SetBuildUnitEx( 1, 1, 2, ANCIENT_WAR )
    call SetBuildUnit( 1, ELF_ALTAR )
    call SetBuildUnit( 1, HUNTERS_HALL )
    call CampaignDefenderEx( 2, 2, 3, ARCHER ) 
    call CampaignDefenderEx( 1, 1, 1, HUNTRESS )
    call CampaignDefenderEx( 0, 1, 2, ANCIENT_PROTECT )
    // Уровень 2, постройки
    call SetBuildUnit( 1, TREE_AGES )
    call SetBuildUnitEx( 1, 1, 2, ANCIENT_LORE )
    call SetBuildUnit( 1, ANCIENT_WIND )
    // Уровень 2, постройки
    call SetBuildUnit( 1, TREE_ETERNITY )
    call SetBuildUnitEx( 0, 1, 1, CHIMAERA_ROOST )
    // **********************************
    // *                    End Strategy       *
    // **********************************
    // **********************************
    // *                   Attack Strategy          *
    // **********************************
    //*** WAVE 1 ***
    call InitAssaultGroup()
    call CampaignAttackerEx( 2, 3, 3, ARCHER )
    call CampaignAttackerEx( 0, 1, 2, HUNTRESS )  
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** WAVE 2 *** Спустя 2-3 минуты после первой волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, ARCHER )
    call CampaignAttackerEx( 1, 2, 3, HUNTRESS )  
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** WAVE 3 *** Спустя 2-3 минуты после второй волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, ARCHER )
    call CampaignAttackerEx( 1, 2, 3, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** WAVE 4 *** Спустя 2-3 минуты после третьей волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, ARCHER )
    call CampaignAttackerEx( 1, 2, 3, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** WAVE 5 *** Спустя 3-4 минуты после четвертой волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 3, 3, ARCHER )
    call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call CampaignAttackerEx( 0, 1, 2, DRYAD )
    call SuicideOnPlayerEx( M4, M3, M3, MyVictim )
    //*** WAVE 6 *** Спустя 3-4 минуты после пятой волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
    call CampaignAttackerEx( 1, 2, 3, DRYAD )
    call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
    call SuicideOnPlayerEx( M5, M3, M3, MyVictim )
    //*** WAVE 7 *** Спустя 3-5 минут после шестой волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
    call CampaignAttackerEx( 1, 2, 3, DRYAD )
    call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
    call CampaignAttackerEx( 0, 1, 2, MOUNTAIN_GIANT )
    call SuicideOnPlayerEx( M5, M4, M3, MyVictim )
    //*** WAVE 8 *** Спустя 3-4-5 минут после седьмой волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
    call CampaignAttackerEx( 1, 2, 3, DRYAD )
    call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
    call SuicideOnPlayerEx( M6, M5, M4, MyVictim )
    //*** WAVE 9 *** Спустя 4-5-6 минут после восьмой волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
    call CampaignAttackerEx( 1, 2, 3, DRYAD )
    call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
    call CampaignAttackerEx( 0, 1, 2, MOUNTAIN_GIANT )
    call CampaignAttackerEx( 1, 1, 2, FAERIE_DRAGON )
    call CampaignAttackerEx( 0, 1, 2, CHIMAERA ) 
    call SuicideOnPlayerEx( M6, M5, M4, MyVictim )
    // **********************************
    // *              End Strategy       *
    // **********************************
endfunction
После последней волны ИИ скрипт закончится и больше не будет посылать команд к наступлению на противника. Если вы хотите, чтобы ИИ не останавливался впоть до поражения, и продолжал атаковать, можно добавить повтор (loop), для, скажем, трех последних волн. Тогда, скрипт будет выглядеть следующим образом:
» Код
Код:
globals
    player MyVictim = Player( 0 )
endglobals

function ConfigureAI takes nothing returns nothing
    call SetSlowChopping( false )
    call SetPeonsRepair( true )
endfunction

function main takes nothing returns nothing
    call CampaignAI( MOON_WELL, null )
    call ConfigureAI( )
    // **********************************
    // *      Building Strategy         *
    // **********************************
    //
    // Уровень 1, постройки
    call SetReplacements( 1, 2, 3 )
    call SetBuildUnitEx( 1, 1, 1, TREE_LIFE )
    call SetBuildUnit( 15, WISP )
    call SetBuildUnitEx( 1, 2, 3, MOON_WELL )
    call SetBuildUnitEx( 1, 1, 2, ANCIENT_WAR )
    call SetBuildUnit( 1, ELF_ALTAR )
    call SetBuildUnit( 1, HUNTERS_HALL )
    call CampaignDefenderEx( 2, 2, 3, ARCHER ) 
    call CampaignDefenderEx( 1, 1, 1, HUNTRESS )
    call CampaignDefenderEx( 0, 1, 2, ANCIENT_PROTECT )
    // Уровень 2, постройки
    call SetBuildUnit( 1, TREE_AGES )
    call SetBuildUnitEx( 1, 1, 2, ANCIENT_LORE )
    call SetBuildUnit( 1, ANCIENT_WIND )
    // Уровень 3, постройки
    call SetBuildUnit( 1, TREE_ETERNITY )
    call SetBuildUnitEx( 0, 1, 1, CHIMAERA_ROOST )
    // **********************************
    // *    End Strategy       *
    // **********************************
        // **********************************
    // *                   Attack Strategy          *
    // **********************************
    //*** WAVE 1 *** 
    call InitAssaultGroup()
    call CampaignAttackerEx( 2, 3, 3, ARCHER )
    call CampaignAttackerEx( 0, 1, 2, HUNTRESS )  
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** WAVE 2 *** Спустя 2-3 минуты после первой волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, ARCHER )
    call CampaignAttackerEx( 1, 2, 3, HUNTRESS )  
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** WAVE 3 *** Спустя 2-3 минуты после второй волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, ARCHER )
    call CampaignAttackerEx( 1, 2, 3, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** WAVE 4 *** Спустя 2-3 минуты после третьей волны 
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, ARCHER )
    call CampaignAttackerEx( 1, 2, 3, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** WAVE 5 *** Спустя 3-4 минуты после четвертой волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 3, 3, ARCHER )
    call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call CampaignAttackerEx( 0, 1, 2, DRYAD )
    call SuicideOnPlayerEx( M4, M3, M3, MyVictim )
    //*** WAVE 6 *** Спустя 3-4 минуты после пятой волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
    call CampaignAttackerEx( 1, 2, 3, DRYAD )
    call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
    call SuicideOnPlayerEx( M5, M3, M3, MyVictim )
    loop // Старт неостанавливаемого повтора волн
            //*** WAVE 7 *** Спустя 3-5 минут после шестой волны
        call InitAssaultGroup()
        call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
        call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
        call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
        call CampaignAttackerEx( 1, 2, 3, DRYAD )
        call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
        call CampaignAttackerEx( 0, 1, 2, MOUNTAIN_GIANT )
        call SuicideOnPlayerEx( M5, M4, M3, MyVictim )
            //*** WAVE 8 *** Спустя 3-4-5 минут после седьмой волны
        call InitAssaultGroup()
        call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
        call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
        call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
        call CampaignAttackerEx( 1, 2, 3, DRYAD )
        call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
        call SuicideOnPlayerEx( M6, M5, M4, MyVictim )
            //*** WAVE 9 *** Спустя 4-5-6 минут после восьмой волны
        call InitAssaultGroup()
        call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
        call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
        call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
        call CampaignAttackerEx( 1, 2, 3, DRYAD )
        call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
        call CampaignAttackerEx( 0, 1, 2, MOUNTAIN_GIANT )
        call CampaignAttackerEx( 1, 1, 2, FAERIE_DRAGON )
        call CampaignAttackerEx( 0, 1, 2, CHIMAERA ) 
        call SuicideOnPlayerEx( M6, M5, M4, MyVictim )
    endloop
    // **********************************
    // *   Attack Strategy never ends   *
    // **********************************
endfunction

» СТРАТЕГИЯ УЛУЧШЕНИЙ

Чтож, большая часть ИИ уже написана, и мы добрались до улучшений. Отдавать приказ к началу исследования можно, в принципе, где угодно в скрипте, разве что у вас должны быть уже прописаны здания, на которых эти усовершенствования будут производиться. По сути же, если в игре этого здания на момент отдачи приказа по какой либо причине нет (например, было разрушено игроком), то ничего срашного - исследование запустится сразу, как только ИИ отстроит здание, важно не прописывать улучшения (да и наем юнитов) до зданий, так как ИИ не будет работать.
Вообще, в компаниях «принято» ставить улучшения между атакующими волнами – это делает ИИ жизненнее, создает некий прогресс в развитии ИИ и делает его жизненнее.

Рассмотрим улучшающую функцию.
  • SetBuildUpgrEx( e, n, i, UpgradeID ): Наверное, объяснять уже лишне, но я скажу – это уровень заданного улучшения в зависимости от уровня сложности.
Вот как смотрится скрипт теперь, когда мы добавили в него улучшения.
» Код
Код:
globals
    player MyVictim = Player( 0 )
endglobals

function ConfigureAI takes nothing returns nothing
    call SetSlowChopping( false )
    call SetPeonsRepair( true )
endfunction

function main takes nothing returns nothing
    call CampaignAI( MOON_WELL, null )
    call ConfigureAI( )
    // **********************************
    // *      Building Strategy         *
    // **********************************
    //
    // Уровень 1, постройки
    call SetReplacements( 1, 2, 3 )
    call SetBuildUnitEx( 1, 1, 1, TREE_LIFE )
    call SetBuildUnit( 15, WISP )
    call SetBuildUnitEx( 1, 2, 3, MOON_WELL )
    call SetBuildUnitEx( 1, 1, 2, ANCIENT_WAR )
    call SetBuildUnit( 1, ELF_ALTAR )
    call SetBuildUnit( 1, HUNTERS_HALL )
    call CampaignDefenderEx( 2, 2, 3, ARCHER ) 
    call CampaignDefenderEx( 1, 1, 1, HUNTRESS )
    call CampaignDefenderEx( 0, 1, 2, ANCIENT_PROTECT )
    // Уровень 2, постройки
    call SetBuildUnit( 1, TREE_AGES )
    call SetBuildUnitEx( 1, 1, 2, ANCIENT_LORE )
    call SetBuildUnit( 1, ANCIENT_WIND )
    // Уровень 3, постройки
    call SetBuildUnit( 1, TREE_ETERNITY )
    call SetBuildUnitEx( 0, 1, 1, CHIMAERA_ROOST )
    // **********************************
    // *    End Strategy       *
    // **********************************
    // **********************************
    // *                   Attack Strategy          *
    // **********************************
    //*** WAVE 1 *** 
    call InitAssaultGroup()
    call CampaignAttackerEx( 2, 3, 3, ARCHER )
    call CampaignAttackerEx( 0, 1, 2, HUNTRESS )  
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** Базовые улучшения для Охотничьего зала ***
    call SetBuildUpgrEx( 1, 2, 3, UPG_STR_MOON )
    call SetBuildUpgrEx( 1, 2, 3, UPG_MOON_ARMOR )
    call SetBuildUpgrEx( 1, 2, 3, UPG_STR_WILD )
    call SetBuildUpgrEx( 1, 2, 3, UPG_HIDES )
    //*** WAVE 2 *** Спустя 2-3 минуты после первой волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, ARCHER )
    call CampaignAttackerEx( 1, 2, 3, HUNTRESS )  
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** WAVE 3 *** Спустя 2-3 минуты после второй волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, ARCHER )
    call CampaignAttackerEx( 1, 2, 3, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** WAVE 4 *** Спустя 2-3 минуты после третьей волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, ARCHER )
    call CampaignAttackerEx( 1, 2, 3, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** Dryad Upgrade
    call SetBuildUpgrEx( 1, 1, 1, UPG_ABOLISH )
    //*** WAVE 5 *** Спустя 3-4 минуты после четвертой волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 3, 3, ARCHER )
    call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call CampaignAttackerEx( 0, 1, 2, DRYAD )
    call SuicideOnPlayerEx( M4, M3, M3, MyVictim )
    //*** Druid of the Claw Upgrade
    call SetBuildUpgrEx( 0, 1, 2, UPG_DRUID_CLAW )
    //*** WAVE 6 *** Спустя 3-4 минуты после пятой волны/style
    call InitAssaultGroup()
    call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
    call CampaignAttackerEx( 1, 2, 3, DRYAD )
    call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
    call SuicideOnPlayerEx( M5, M3, M3, MyVictim )
    //*** chimaera Upgrade
    call SetBuildUpgrEx( 0, 0, 1, UPG_CHIM_ACID )
    loop // Старт неостанавливаемого повтора волн
            //*** WAVE 7 *** Спустя 3-5 минут после шестой волны
        call InitAssaultGroup()
        call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
        call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
        call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
        call CampaignAttackerEx( 1, 2, 3, DRYAD )
        call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
        call CampaignAttackerEx( 0, 1, 2, MOUNTAIN_GIANT )
        call SuicideOnPlayerEx( M5, M4, M3, MyVictim )
            //*** WAVE 8 *** Спустя 3-4-5 минут после седьмой волны
        call InitAssaultGroup()
        call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
        call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
        call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
        call CampaignAttackerEx( 1, 2, 3, DRYAD )
        call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
        call SuicideOnPlayerEx( M6, M5, M4, MyVictim )
            //*** WAVE 9 *** Спустя 4-5-6 минут после восьмой волны
        call InitAssaultGroup()
        call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
        call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
        call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
        call CampaignAttackerEx( 1, 2, 3, DRYAD )
        call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
        call CampaignAttackerEx( 0, 1, 2, MOUNTAIN_GIANT )
        call CampaignAttackerEx( 1, 1, 2, FAERIE_DRAGON )
        call CampaignAttackerEx( 0, 1, 2, CHIMAERA ) 
        call SuicideOnPlayerEx( M6, M5, M4, MyVictim )
    endloop
    // **********************************
    // *   Attack Strategy never ends   *
    // **********************************
endfunction

» СПОСОБНОСТИ ГЕРОЕВ

Если мы сейчас проверим наш ИИ, то станет ясно, что герои не разучивают никаких способностей. Почему? И как нам этого избежать?
Ответим разом на первый и на второй вопрос – для этого всего навсего добавим «обучающий» скрипт для наших героев в функцию CampaignAI. Помните, я обещал к этому вернуться позже? Эта функция будет работать, как только у любого из героев ИИ появится доступные к разучиванию способности. Рассмотрим наш пример, там добавиласб новая функция hero_levels, в которой решается вопрос со способностями у Demon Hunter`а (Охотника на Демонов) и Priestress of the Moon (Жрицы Луны) и к которой обращается ф-ия CampaignAI.
Код:
function hero_levels takes nothing returns integer
    local integer hero  = GetHeroId()
    local integer level = GetHeroLevelAI()
    local integer a = 0
    if hero == DEMON_HUNTER then
        if level == 1 or level == 3 or level == 5 then
            set a = IMMOLATION
        endif
        if level == 2 or level == 4 or level == 7 then
            set a = MANA_BURN
        endif
        if level >= 8 then
            set a = EVASION
        endif
        if level == 6 then
            set a = METAMORPHOSIS
        endif 
    endif
    if hero == MOON_CHICK then
        if level == 1 or level == 3 or level == 5 then
            set a = TRUESHOT
        endif
        if level == 2 or level == 4 or level == 7 then
            set a = SEARING_ARROWS
        endif
        if level >= 8 then
            set a = SCOUT
        endif
        if level == 6 then
            set a = STARFALL
        endif 
    endif
    return a
endfunction
Как видно из примера, наша функция ничего не «берет» и возвращает «собственное» имя способности, либо рав-код спсобности.
ВАЖНО! Эта функция ДОЛЖНА ВСЕГДА возвращать integer (целоисчисленную).

Теперь, когда мы внесли конфигурацию способностей, разместим функцию hero_levels над функцией main (только надд, иначе нельзя будет обратиться к hero_levels). Разместили? Тогда переписываем вызов CampaignAI следующим образом:
Цитата:
call CampaignAI( MOON_WELL, function hero_levels )
Код в целом теперь будет выглядеть так:
» Код
Код:
globals
    player MyVictim = Player( 0 )
endglobals

function ConfigureAI takes nothing returns nothing
    call SetSlowChopping( false )
    call SetPeonsRepair( true )
endfunction

function hero_levels takes nothing returns integer
    local integer hero  = GetHeroId()
    local integer level = GetHeroLevelAI()
    local integer a = 0
    if hero == DEMON_HUNTER then
        if level == 1 or level == 3 or level == 5 then
            set a = IMMOLATION
        endif
        if level == 2 or level == 4 or level == 7 then
            set a = MANA_BURN
        endif
        if level >= 8 then
            set a = EVASION
        endif
        if level == 6 then
            set a = METAMORPHOSIS
        endif 
    endif
    if hero == MOON_CHICK then
        if level == 1 or level == 3 or level == 5 then
            set a = TRUESHOT
        endif
        if level == 2 or level == 4 or level == 7 then
            set a = SEARING_ARROWS
        endif
        if level >= 8 then
            set a = SCOUT
        endif
        if level == 6 then
            set a = STARFALL
        endif 
    endif
    return a
endfunction

function main takes nothing returns nothing
    call CampaignAI( MOON_WELL, function hero_levels ) //<== Теперь эта функция
    call ConfigureAI( )                              //      улучшает героев
    // **********************************
    // *      Building Strategy         *
    // **********************************
    //
    // Уровень 1, постройки
    call SetReplacements( 1, 2, 3 )
    call SetBuildUnitEx( 1, 1, 1, TREE_LIFE )
    call SetBuildUnit( 15, WISP )
    call SetBuildUnitEx( 1, 2, 3, MOON_WELL )
    call SetBuildUnitEx( 1, 1, 2, ANCIENT_WAR )
    call SetBuildUnit( 1, ELF_ALTAR )
    call SetBuildUnit( 1, HUNTERS_HALL )
    call CampaignDefenderEx( 2, 2, 3, ARCHER ) 
    call CampaignDefenderEx( 1, 1, 1, HUNTRESS )
    call CampaignDefenderEx( 0, 1, 2, ANCIENT_PROTECT )
    // Уровень 2, постройки
    call SetBuildUnit( 1, TREE_AGES )
    call SetBuildUnitEx( 1, 1, 2, ANCIENT_LORE )
    call SetBuildUnit( 1, ANCIENT_WIND )
    // Уровень 3, постройки
    call SetBuildUnit( 1, TREE_ETERNITY )
    call SetBuildUnitEx( 0, 1, 1, CHIMAERA_ROOST )
    // **********************************
    // *    End Strategy       *
    // **********************************
    // **********************************
    // *                   Attack Strategy          *
    // **********************************
    //*** WAVE 1 *** 
    call InitAssaultGroup()
    call CampaignAttackerEx( 2, 3, 3, ARCHER )
    call CampaignAttackerEx( 0, 1, 2, HUNTRESS )  
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** Базовые улучшения для Охотничьего зала ***
    call SetBuildUpgrEx( 1, 2, 3, UPG_STR_MOON )
    call SetBuildUpgrEx( 1, 2, 3, UPG_MOON_ARMOR )
    call SetBuildUpgrEx( 1, 2, 3, UPG_STR_WILD )
    call SetBuildUpgrEx( 1, 2, 3, UPG_HIDES )
    //*** WAVE 2 *** Спустя 2-3 минуты после первой волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, ARCHER )
    call CampaignAttackerEx( 1, 2, 3, HUNTRESS )  
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** WAVE 3 *** Спустя 2-3 минуты после второй волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, ARCHER )
    call CampaignAttackerEx( 1, 2, 3, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** WAVE 4 *** Спустя 2-3 минуты после третьей волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, ARCHER )
    call CampaignAttackerEx( 1, 2, 3, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** Dryad Upgrade
    call SetBuildUpgrEx( 1, 1, 1, UPG_ABOLISH )
    //*** WAVE 5 *** Спустя 3-4 минуты после четвертой волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 3, 3, ARCHER )
    call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call CampaignAttackerEx( 0, 1, 2, DRYAD )
    call SuicideOnPlayerEx( M4, M3, M3, MyVictim )
    //*** Druid of the Claw Upgrade
    call SetBuildUpgrEx( 0, 1, 2, UPG_DRUID_CLAW )
    //*** WAVE 6 *** Спустя 3-4 минуты после пятой волны
    call InitAssaultGroup()
    call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
    call CampaignAttackerEx( 1, 2, 3, DRYAD )
    call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
    call SuicideOnPlayerEx( M5, M3, M3, MyVictim )
    //*** chimaera Upgrade
    call SetBuildUpgrEx( 0, 0, 1, UPG_CHIM_ACID )
    loop // Старт неостанавливаемого повтора волн
            //*** WAVE 7 *** Спустя 3-5 минут после шестой волны
        call InitAssaultGroup()
        call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
        call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
        call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
        call CampaignAttackerEx( 1, 2, 3, DRYAD )
        call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
        call CampaignAttackerEx( 0, 1, 2, MOUNTAIN_GIANT )
        call SuicideOnPlayerEx( M5, M4, M3, MyVictim )
            //*** WAVE 8 *** Спустя 3-4-5 минут после седьмой волны
        call InitAssaultGroup()
        call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
        call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
        call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
        call CampaignAttackerEx( 1, 2, 3, DRYAD )
        call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
        call SuicideOnPlayerEx( M6, M5, M4, MyVictim )
            //*** WAVE 9 *** Спустя 4-5-6 минут после восьмой волны
        call InitAssaultGroup()
        call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
        call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
        call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
        call CampaignAttackerEx( 1, 2, 3, DRYAD )
        call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
        call CampaignAttackerEx( 0, 1, 2, MOUNTAIN_GIANT )
        call CampaignAttackerEx( 1, 1, 2, FAERIE_DRAGON )
        call CampaignAttackerEx( 0, 1, 2, CHIMAERA ) 
        call SuicideOnPlayerEx( M6, M5, M4, MyVictim )
    endloop
    // **********************************
    // *   Attack Strategy never ends   *
    // **********************************
endfunction

» КОНТРОЛЬ ИИ СКРИПТА ТРИГГЕРАМИ

Когда мы создаем кампанию, мы в любом случае должны как-то контролировать наш ИИ – например, в зависимости от задания, в определенный момент начинать «бомбардировку» игрока самоубийцами компьютера, извивните за каламбур. Или отдать приказ атаковать определенного юнита, когда он, например, войдет в область. Как это сделать? Ведь ИИ скрипты игнорируют события. Решение есть – в World Editor`е создается триггер, который отслеживает событие. И управляет ИИ за счет команд. В качестве примера, создадим триггер, который отдаз приказ стартовать атакующие волны, как только юнит игрока войдет в указанную область:
DEAD URL
Теперь нам нужно, чтобы ИИ начал атаковать, как только получит AI Command (команду ИИ, первое действие в триггере). Для этого добавим вызов функции WaitForSignal() перед кодом в разделе ** Attack Strategy **. Эта функция ничего не «берет» и возвращает integer; так что мы можем ее использовать двумя разными способами:
Код:
function main takes nothing returns nothing
    local integer cmd // <== это впишите только если будете использовать 2-й способ
    . . .  // Ваш код отстройки здесь
    //***************
    // * *  Способ 1  **
    //***************
    call WaitForSignal() // <== Просто ждем сигнала с карты

    //***************
    // * *  Способ 2  **
    //***************
    set cmd = WaitForSignal() // <== Ждем до сигнала, и когда тот поступит, заносим начение команды в переменную
    if cmd == 0 then
        // выполнить алгоритм 0
    elseif cmd == 1 then
        // выполнить алгоритм 1. итд
    ...
    endif 
endfunction
Второй способ более интересен, так как мы можем заставить ИИ реагировать по разному в зависимости от команды (например, атаковать игрока 1 в одном случае, и в другом – атаковать игрока 4 итд)
Важно:
Дело в том, что когда мы отдаем ИИ команду через триггеры, вместе с этой командой идет два целоисчисленных значения – Command и Data.
Функция WaitForSignal() хороша тем, что останавливает выполнение ИИ скрипта до поступления сигнала, иными словами, заменяет собой регистрацию события; к тому же, возвращает одно из посланных функцией CommandAI(Player, command, data) целоисчисленых значенений (это значение - Command)
Вот почему автор решил обозначить только эту функцию, как управляющую ИИ, однако воздействовать на поведение ИИ можно не только самим событием Команды, но и двумя со-идущими значениями. Значения Command и Data никуда не деваются, и их всегда можно использовать с помощью 2-х функций из common.ai
:
Цитата:
GetLastCommand()
GetLastData()

Код:
function main takes nothing returns nothing
    local integer cmd
    . . .  // Здесь код постройки, и допустим, несколько волн атакующих
    set cmd = GetLastData() // <== Здесь одна из двух ф-ий, либо WaitForSignal()
    if cmd == 0 then 
        // выполнить алгоритм 0
    elseif cmd == 1 then
        // выполнить алгоритм 1. итд
    ...
    endif 
endfunction
Вот наглядный пример, как использовать эти две функции.
В случае если вам надо удалить последнюю ИИ команду, используйте функцию
PopLastCommand()
Код:
function any_word takes nothing returns nothing
  . . .  //Весь предыдущий код здесь
    call WaitForSignal()
    call PopLastCommand() // удаляем последнюю команду, сразу после ее поступления
  . . . 
endfunction

» ПРОВЕРКА ИИ

Эта часть, по мнению автора статьи, самая ответственная, что не удивительно, ведь наше чадо еще нужно скормить игре так, чтобы был результат.
Чтобы проверить наш ИИ самым быстрым способом, нужно провести следующие манипуляции.
Цитата:
  1. Сохраняем наш ИИ скрипт как *.ai файл
  2. Открываем WorldEditor
  3. Нажимаем F8 или кликаем по кнопке AI Editor.
  4. В AI Editor`е выбираем последнюю вкладку Проверка конфигурации(Test Configuration)
  5. Здесь задаем скорость игры 2X (Теперь становится ясно, почему это быстрейший путь, но не ставьте скорость > 2)
  6. Выбераем карту, на которой вы бы хотели проверить ваш ИИ. По умолчанию, WE предлагает PlunderIsle. Если хотите – меняйте. Тут же неплохо бы отключить туман войны.
  7. Задаем Игроков. Помните, что если вы настроили ИИ на атаку Игрока 1 (красного) (Player(0) в JASS) вы не должны давать этому игроку ваш ИИ, дабы он не атаковал себя. Звучит глупо, однако чаще всего именно такие ошибки допускаются при тестах .
  8. Выбираем расу для вашего ИИ (не надо здесь выбирать рандомную расу, или, к примеру, орков, если у нас ИИ для Ночных эльфов).
  9. В слоте, где наш ИИ будет работать, меняем тип ИИ на custom. Когда вы это сделаете, в конце строки разблокируется кнопка, нажав на которую вы сможете выбрать свежесозданный ИИ.
  10. Игрока-цель нашего ИИ задаем как компьютера любой на ваш выбор сложности со стандартным ИИ.
  11. Убедитесь, что «ваш» и «чужой» компьютерные игроки находятся в разных командах
  12. Почти все. Теперь только задайте себя как зрителя, и…
  13. Нажимайте CTRL + F9 или кнопку Проверить ИИ(Test AI).
И начнется проверка карты . Если все нормально, вы увидите, как тестируемый ИИ-игрок начнет с помощью рабочих возводить базовые постройки. Если же светлячки (Whisp) (или их аналоги) будут просто добывать ресурсы, значит в код ИИ закралась ошибка, выходите и проверяйте синтаксис.

Если же вы хотите сразу включить ИИ в вашу карту, то через Менеджер импорта импортируйте нужный *.ai в карту. А потом в триггере Melee Initialization добавьте действие:
ИИ – StartMeleeAIScript for Player(здесь наш компьютер которому предназначен ИИ): map.ai (это поле меняем. Щелчек мышкой, и в активном окошке Импорт выбираем наш импортированый ИИ).
Если вы добавили в скрипт функцию WaitForSignal(), не забудте этот сигнал отдать , как это делать было сказано выше.
» Вот, что у нас должно было получиться

Код:
//*************************************************************************
//*                           Night Elf Campaign AI                            *
//*                                   By Moyack                                       *
//*************************************************************************
//*
globals
    player MyVictim = Player( 0 )
endglobals

function ConfigureAI takes nothing returns nothing
    call SetSlowChopping( false )
    call SetPeonsRepair( true )
endfunction

function hero_levels takes nothing returns integer
    local integer hero  = GetHeroId()
    local integer level = GetHeroLevelAI()
    local integer a = 0
    if hero == DEMON_HUNTER then
        if level == 1 or level == 3 or level == 5 then
            set a = IMMOLATION
        endif
        if level == 2 or level == 4 or level == 7 then
            set a = MANA_BURN
        endif
        if level >= 8 then
            set a = EVASION
        endif
        if level == 6 then
            set a = METAMORPHOSIS
        endif 
    endif
    if hero == MOON_CHICK then
        if level == 1 or level == 3 or level == 5 then
            set a = TRUESHOT
        endif
        if level == 2 or level == 4 or level == 7 then
            set a = SEARING_ARROWS
        endif
        if level >= 8 then
            set a = SCOUT
        endif
        if level == 6 then
            set a = STARFALL
        endif 
    endif
    return a
endfunction

function main takes nothing returns nothing
    call CampaignAI( MOON_WELL, function hero_levels )
    call ConfigureAI( )
// **********************************
    // *      Building Strategy         *
    // **********************************
    //
    // Tier 1 Buildings
    call SetReplacements( 1, 2, 3 )
    call SetBuildUnitEx( 1, 1, 1, TREE_LIFE )
    call SetBuildUnit( 15, WISP )
    call SetBuildUnitEx( 1, 2, 3, MOON_WELL )
    call SetBuildUnitEx( 1, 2, 2, ANCIENT_WAR )
    call SetBuildUnit( 1, ELF_ALTAR )
    call SetBuildUnit( 1, HUNTERS_HALL )
    call CampaignDefenderEx( 2, 2, 3, ARCHER ) 
    call CampaignDefenderEx( 1, 1, 1, HUNTRESS )
    call SetBuildUnitEx( 1, 2, 3, ANCIENT_PROTECT )
// Tier 2 buildings
    call SetBuildUnit( 1, TREE_AGES )
    call SetBuildUnitEx( 1, 2, 2, ANCIENT_LORE )
    call SetBuildUnit( 1, ANCIENT_WIND )
// Tier 3 buildings
    call SetBuildUnit( 1, TREE_ETERNITY )
    call SetBuildUnitEx( 0, 1, 1, CHIMAERA_ROOST )
// **********************************
    // *    End Strategy       *
    // **********************************
    // **********************************
    // *       Attack Strategy          *
    // **********************************
    //*** WAVE 1 *** AI will begin to attack in 5 minutes
    call InitAssaultGroup()
    call CampaignAttackerEx( 2, 3, 3, ARCHER )
    call CampaignAttackerEx( 0, 1, 2, HUNTRESS )  
    call SuicideOnPlayerEx( M5, M5, M5, MyVictim )
    //*** Basic upgrades from HUNTERS HALL ***
    call SetBuildUpgrEx( 1, 2, 3, UPG_STR_MOON )
    call SetBuildUpgrEx( 1, 2, 3, UPG_MOON_ARMOR )
    call SetBuildUpgrEx( 1, 2, 3, UPG_STR_WILD )
    call SetBuildUpgrEx( 1, 2, 3, UPG_HIDES )
     //*** WAVE 2 *** Between 2 or 3 minutes after Wave 1
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, ARCHER )
    call CampaignAttackerEx( 1, 2, 3, HUNTRESS )  
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** WAVE 3 *** Between 2 or 3 minutes after Wave 2
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, ARCHER )
    call CampaignAttackerEx( 1, 2, 3, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** WAVE 4 *** Between 2 or 3 minutes after Wave 3
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, ARCHER )
    call CampaignAttackerEx( 1, 2, 3, HUNTRESS )
    call CampaignAttackerEx( 0, 1, 1, BALLISTA )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call SuicideOnPlayerEx( M3, M2, M2, MyVictim )
    //*** Dryad Upgrade
    call SetBuildUpgrEx( 1, 1, 1, UPG_ABOLISH )
    //*** WAVE 5 *** Between 3 or 4 minutes after Wave 4
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 3, 3, ARCHER )
    call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call CampaignAttackerEx( 0, 1, 2, DRYAD )
    call SuicideOnPlayerEx( M4, M3, M3, MyVictim )
    //*** Druid of the Claw Upgrade
    call SetBuildUpgrEx( 0, 1, 2, UPG_DRUID_CLAW )
    //*** WAVE 6 *** Between 3 or 4 minutes after Wave 5
    call InitAssaultGroup()
    call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
    call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
    call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
    call CampaignAttackerEx( 1, 2, 3, DRYAD )
    call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
    call SuicideOnPlayerEx( M4, M3, M3, MyVictim )
    //*** chimaera Upgrade
    call SetBuildUpgrEx( 0, 0, 1, UPG_CHIM_ACID )
    loop //Init the infinite attack loop
        //*** WAVE 7 *** Between 3 or 5 minutes after Wave 6
        call InitAssaultGroup()
        call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
        call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
        call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
        call CampaignAttackerEx( 1, 2, 3, DRYAD )
        call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
        call CampaignAttackerEx( 0, 1, 2, MOUNTAIN_GIANT )
        call SuicideOnPlayerEx( M5, M4, M3, MyVictim )
        //*** WAVE 8 *** Between 3 or 5 minutes after Wave 7
        call InitAssaultGroup()
        call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
        call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
        call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
        call CampaignAttackerEx( 1, 2, 3, DRYAD )
        call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
        call SuicideOnPlayerEx( M5, M4, M3, MyVictim )
        //*** WAVE 9 *** Between 4 or 6 minutes after Wave 8
        call InitAssaultGroup()
        call CampaignAttackerEx( 2, 2, 2, HUNTRESS )
        call CampaignAttackerEx( 1, 1, 1, DEMON_HUNTER )
        call CampaignAttackerEx( 1, 1, 1, MOON_CHICK )
        call CampaignAttackerEx( 1, 2, 3, DRYAD )
        call CampaignAttackerEx( 0, 1, 2, DRUID_CLAW )
        call CampaignAttackerEx( 0, 1, 2, MOUNTAIN_GIANT )
        call CampaignAttackerEx( 1, 1, 2, FAERIE_DRAGON )
        call CampaignAttackerEx( 0, 1, 2, CHIMAERA ) 
        call SuicideOnPlayerEx( M6, M5, M4, MyVictim )
    endloop
    // **********************************
    // *   Attack Strategy never ends   *
    // **********************************
endfunction

Вот собственно и все, надеюсь мы с автором статьи многим помогли! Конечно, статья ужасно не полная, в common.ai еще масса вещей, которая требует изучения, например, управление «Капитанами» группы (как в миссии с Кенариусом в РОК). Я постараюсь уделить этому максимум внимания и что-нибудь написать.

Отредактировано FellGuard, 03.06.2007 в 14:01.
Старый 03.01.2007, 22:02
NETRAT

offline
Опыт: 83,762
Активность:
Так, сразу оговорка что в статье упоминается исключительно милишный АИ, то есть в ней вообще нет упоминаний про АИ для карт типа, отличного от Melee
Старый 03.01.2007, 22:25
DioD

offline
Опыт: 45,184
Активность:
шозабред столько букоф ни очём.
Старый 03.01.2007, 22:54
Aspid

offline
Опыт: 8,361
Активность:
Цитата:
Jass AI сходный почти во всем синтаксис и типы переменных

судя по всему пропущено слово "имеет"

Jass AI имеет сходный почти во всем синтаксис и типы переменных

Собственно сабж: статья может быть полезной тогда и только тогда если вы действительно решились всерьез заниматься созданием AI - с нее следует начинать. А для среднестатистического модмейкера это действительно
Цитата:
столько букоф ни очём

Тем не менее фелл поздравляю большую проделал работу.
Старый 03.01.2007, 23:44
DioD

offline
Опыт: 45,184
Активность:
пс аи имеет синтаксис один в один с обычным джазом, разница только в том что функции другие
Старый 04.01.2007, 01:14
FellGuard
Losyash
offline
Опыт: 39,547
Активность:
DioD, статью выложил не свою, а перевод. Практически слово в слово, разве что некоторые слова пропущены.
Многабукоф ни о чем выложил по острой нужде TheBestOrc`а.

Цитата:
исключительно милишный АИ

Хм, ну не только для melee, больше для кампании, этот ИИ рационально себя вести не будет (тоесть, отступать при неравных силах итп)
Цитата:
Jass AI имеет сходный почти во всем синтаксис и типы переменных

Цитата:
аи имеет синтаксис один в один с обычным джазом

Знаю, повторяю, перевод слово в слово, все вопросы к автору статьи ;)

Всем спасибо, все поправлю.

FellGuard добавил:
Про не-милишное использование сказал в конце:
Цитата:
онечно, статья ужасно не полная, в common.ai еще масса вещей, которая требует изучения, например, управление «Капитанами» группы (как в миссии с Кенариусом в РОК). Я постараюсь уделить этому максимум внимания и что-нибудь написать.


FellGuard добавил:
Хм, может стоит удалить весь код, тогда статья займет полторы страницы.. Хз
Старый 04.01.2007, 10:13
dk

offline
Опыт: 61,843
Активность:
А вот если бы ты сам провел эти исследования насчет управления капитанами, то было бы вобще гуд, а то сейчас политика насчет перевода буржуев ужесточилась походу...
Старый 04.01.2007, 10:35
FellGuard
Losyash
offline
Опыт: 39,547
Активность:
Извини, не хвататет у мну энтузиазму на такие исследования. Если пораскинуть мозгом, можно все самому уяснить, мне это ясно дали понять. Следственно, в такого рода статьях нужды нет.
Старый 04.01.2007, 13:07
TheBestOrc

offline
Опыт: 350
Активность:
FellGuard исключительно спосибо для статьи

Отредактировано TheBestOrc, 04.01.2007 в 16:19.
Старый 04.01.2007, 15:56
adic3x

offline
Опыт: 107,539
Активность:
Интересно, но как я понял если это для мили - то малоактуально.
Старый 04.01.2007, 16:34
FellGuard
Losyash
offline
Опыт: 39,547
Активность:
ADOLF, не для мили, в который раз повторю, больше для кампании. А у кого голова думает и кто не поленится прочитать, так тот может и выудит для себя принципы работы с ИИ скриптами, и базируясь на них наворотит свой аналог АМАИ, ну или хотя-бы приемлемых ботов для карт типа АОС без всякого триггерного управления и глюков.
В принципе, в сфере ИИ возможны не менее впечатляющие наработки и системы, чем в Jass-е (скрипты ИИ - Jass AI), только, разве что, это будет не так заметно. Достаточно почитать common.ai чтобы составить себе представление о возможностях ИИ скриптов.

В качестве хорошего примера можно привести ИИ Кенариуса или ИИ нежити и Архимонда из РОК-а и поведение Иллидана с нагами в последней миссии ТФТ

Просто ИИ здесь рассмотрен на примере рядового скрипта из кампании - раздолби лагерь нежити прежде, чем лагерь раздолбит тебя...
Старый 04.01.2007, 19:43
exploder
iOS zealot
offline
Опыт: 19,394
Активность:
Каты бесят, как хорошо без них было жыть... Теперь норовят целую галерею фанарта в кат упрятать...
Старый 04.01.2007, 20:10
FellGuard
Losyash
offline
Опыт: 39,547
Активность:
А по существу :) Тут поступают жалобы, что многабукоф, так что каты вещь нелишняя в организации статей, мое имхо.
Старый 04.01.2007, 22:15
Aspid

offline
Опыт: 8,361
Активность:
FellGuard лично мне с катами гораздо удобнее читать.

Отредактировано Q, 05.01.2007 в 19:34.
Старый 05.01.2007, 18:16
TheBestOrc

offline
Опыт: 350
Активность:
1 ) у некто если другие статье для ИИ скриптов :-)
FellGuard ты сделал исключительна и по существу первая статей для ИИ скриптов на этот форум
буду рад если некто / из которы поступают жалобы / рассмотрет Стандартный ИИ на примере :-)
Старый 05.01.2007, 20:16
ArchWorm
Матрица незалежности
offline
Опыт: 4,675
Активность:
Чуствую, я на вц3ц наслушаюсь от мояка за этот перевод.
Старый 06.01.2007, 10:06
dk

offline
Опыт: 61,843
Активность:
Ну если они и узнали о главной странице, то вряд ли им хватит ума зайти в этот раздел и найти тут эту статью... Так что успокойся, все норм
Старый 06.01.2007, 10:24
FellGuard
Losyash
offline
Опыт: 39,547
Активность:
Dead_knight, ума хватит, если ктонибудь подскажет :)
ArchWorm12612, а что может Moyack иметь против этого перевода? Имхо, все момдмейкеры с головами на плечах и месячным стажем в варе все что здесь написано знают... А в том, что некоторым людям проще читать русские буквы, нежели английские, ничего страшного нет.

FellGuard добавил:
К тому же, если бы я писал все с нуля, то написал бы тоже самое, так что так и так перевод :)

FellGuard добавил:
4 All, Статья доведена до ума.
Старый 06.01.2007, 12:16
VBSniper
Ёк макарёк
offline
Опыт: 3,486
Активность:
оО прикольная статья!! Мне понравилось. Респект
__________
MPI3 нервно курит в сторонке!
Старый 22.01.2007, 18:56
remal
нечто
offline
Опыт: 2,087
Активность:
во-многом бред...
и про отстройку и про атаку.... и даже про проверку...
  • ничего не сказано про то, как сделать, чтобы юниты строились НЕ один раз
  • ничего не сказано про глюки с зависимостями юнитов (а их дохрена)
  • ниченго не сказано про приоритет целей атаки
  • автор не знает, что карту надо проверять МАКСИМУМ при скорости х2. иначе глючит постройка юнитов.
  • нету альтернатив передачи инфы из триггеров
  • ничего не сказано про многопоточность
продолжать?
Старый 28.01.2007, 22:33

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы можете скачивать файлы

BB-коды Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход



Часовой пояс GMT +3, время: 02:18.