Общие функции.
string GetGlobalGameVar( strVal, strDefault )
Достает значение глобальной переменной с именем strVal. Если таковой нет - возвращает значение strDefault. Значения глобальных переменных сохраняются в сейве игры, собственно, потому они и называются глобальными. Пример иллюстрирует небольшой нюанс, связанный скорее, не с данной функцией, а с механизмом конвертации типов, использованном в данном диалекте Lua:local prob = GetGlobalGameVar( "Probability", "0" ); prob = prob + 0; if(random(100)<prob) then -- что-то делаем endЕсли убрать строчку с прибавлением к prob нуля, то фрагмент работать не будет - выдаст ошибку на сравнении, типа, "пытаешься ты, дорогой друг, сравнить строку с числом. Нифига делать не буду".
SetMaxCriticalSeverity( nSeverity )
"Устанавливает cheat Severity для всех критических поражений (и наших, и вражеских)". Говоря по простому, чем меньше число nSeverity, тем меньше вероятность того, что очередной выстрел приведет к критикалу. Не следует думать, что после вызова SetMaxCriticalSeverity(0) критикалов не будет вообще. Вероятность словить критикал все равно есть. Хотя небольшая. Рекомендуется использование данной функции в роликах, например, когда бьют морду главному герою. Так:SetMaxCriticalSeverity(0) UnitShoot( sniper, GetHero(), HL_ANY ) WaitForUnit(sniper) SetMaxCriticalSeverity(1000)
WaitForInterface()
Эта функция есть только в руководстве по редактору. В системе ее нет. И не мудрено - для "приостанавления выполнения скрипта до завершения движения камеры" надо пользовать WaitForUI, так:WaitForUI( CameraMove( T2V, 3000 ) )а "приостанавления выполнения скрипта до завершения диалога" ее же, так:
WaitForUI( DialogPlay( Dialog ) )
GetCurrentZoneAILevel()
Функция присутствует только в том случае, если загружена "сценарная" зона. Реальный уровень врагов на карте будет равен возвращаемой величине плюс значение поля AILevel из соответствующей строки таблицы DifficultyConstants.Работа с юнитами.
DividedDeploy( prefix = "" )
Размещает партию по вейпоинтам вида "UnitHero[prefiх]", "Unit1[prefiх]" и т.д. Т.е. вызов DividedDeploy( "_cd" ) приведет к расстановке партии по вейпоинтам с именами "UnitHero_cd", "Unit1_cd", ... "Unit6_cd". Иногда бывает полезно подождать расстановки партии, так:DividedDeploy("_cd") WaitForGroupRoute(GetParty())
unit CreateUnit( PersID, PlayerID, name, waypoint, level )
Создание юнита с идентификатором PersID (брать из таблицы RPGPers) изначально принадлежащего игроку PlayerID с именем name по месту waypoint. Юнит будет иметь уровень level. Несколько замечаний по ходу дела:function isExistWaypoint( wname ) local strman = CreateUnit( 692, 0, "StremMan", wname, 2 ) if(strman~=nil) then UnitRemove( strman ) return(true) end return(false) endСледует заметить, что по каким-то одним разработчикам ведомым причинам юниты, созданные данной функцией появляются на карте частенько в положении "лежа". Поэтому имеет смысл следующий фрагмент:
enemy = CreateUnit( 692, 1, "enemy" , "w1", 2 ) if(enemy) then UnitSetPose( enemy, POSE_RUN ) WaitForUnit(enemy) UnitSetNormalLogic( enemy ) endПоследний оператор выставляет юниту нормальную логику поведения, дабы он не стоял, как болван.
UnitShootPrepare( unit, target, location )
Согласно руководству, вызов данной функции приводит к прицеливанию юнита unit в юнит target. Location определяет, куда производится прицеливание, и может быть одной из HL_ констант (т.е. HL_ANY, HL_BODY,HL_HEAD,HL_RHAND,HL_LHAND,HL_RLEG или HL_LLEG). На самом деле производится не прицеливание, а эмуляция выстрела, т.е. расходуется патрон из обоймы и вызывается обработчик OnShotAtUnit. В случае, если у юнита активно огнестрельное оружие, вреда жертве не причиняется. Если же юнит вооружен холодным оружием (или вообще не вооружен) последует полноценный удар, т.е. в данном случае эта функция эквивалентна UnitShoot( unit, target, location ). Такие же последствия будет иметь применение данной функции к юниту, вооруженному тяжелым оружием, т.е. гранатометом.UnitActivateWeapon( unit, bActivate )
Обнажить или спрятать ствол. Только анимация, на состояние слотов и инвентаря данная функция не воздействует. Холодное оружие доставаться/прятаться не будет.UnitDrawWeapon( unit )
Обнажить ствол. Если в инвентаре есть огнестрельное оружие, то оно будет помещено в активный слот, а содержимое активного слота, соответственно, переместится в инвентарь. "Предметы в слотах не учитываются", как заботливо сообщает нам руководство. В переводе на русский язык это означает, что если оружие лежит не в инвентаре, а в слоте (активном или неактивном), и другого огнестрела у юнита нет, то ничего делаться не будет. Немного пофиксить это можно с помощью функции CreateAndActivateItem( unit, nRPGItemID ), которая создает предмет и помещает его в активный слот. В качестве побочного эффекта она так же выбивает предметы из обоих слотов в инвентарь. Выглядит это примерно так:function trueDrawWeapon( unit ) CreateAndActivateItem( unit, 123 ) -- это легкая граната WaitForUnit(unit) DestroyItemInHand(unit) -- уничтожаем ее, она свою функцию выполнила UnitDrawWeapon( unit ) WaitForUnit(unit) endЕсли места в инвентаре нет, то будем иметь неприятности. Холодное оружие обнажить нельзя.
DestroyItemInHand( unit )
Уничтожить предмет в активном слоте персонажа.Практически единственная возможность отобрать у юнита что-либо. Можно еще пользовать функцию ItemRemove( item ), но чтобы ее использовать надо этот самый итем сначала получить. Что бывает проблематично, если предмет не имеет собственного имени. (Функция item FindItem( itemID ) ищет предмет на карте, она не учитывает инвентарь и слоты юнитов.)UnitMoveToWaypoint( unit, waypoint )
"Персонаж идёт к точке c именем waypoint". Ну, реально, ползет или бежит, а иногда и вообще никуда не двигается :) Последнее скорее всего говорит о том, чтоИллюстрация работы:
enemy = CreateUnit( 692, 1, "enemy" , "w1", 2 ) if(enemy) then UnitSetPose( enemy, POSE_RUN ) UnitSetWishPose( enemy, POSE_RUN ) WaitForUnit(enemy) UnitSetNormalLogic( enemy ) UnitMoveToWaypoint( enemy, "center" ) WaitForUnitRoute( enemy ) endUnitSetWishPose используется, чтобы указать юниту позу, в которой он будет передвигаться. WaitForUnitRoute - ждем, пока юнит не достигнет точки назначения. Если в принципе не может достичь, или отключен AI, то скрипт на этом месте повиснет. Вообще говоря, WaitForUnitRoute разумно использовать только в роликах.
Работа с группами юнитов
int GroupGetSize(group)
Казалось бы - элементарная функция, возвращает размер группы. Однако, реально данная функция возвращает вовсе не текущий размер группы. При подсчете учитываются убитые, и находящиеся без сознания юниты - поэтому, если надо получить количество способных вести бой, то надо пользовать GroupCanFightSize( group ), или, если хочется получить количество только живых юнитов в группе - писать функцию самому. Так:function GroupLiveSize( group ) local summ = 0 for I = 0, GroupGetSize( group ) - 1 do local unit = GroupGetUnit( group, I ) if UnitIsDead( unit)~=true then summ = summ + 1 end end return summ end
Обработчики событий.
Т.е., как написано в руководстве, "функции, активизирующиеся по тому или иному событию и передающие в себя указанные переменные". Или иногда не активизирующиеся, хотя по идее должны бы :) Вообще стиль руководства местами напоминает известное чеховское "Проезжая мимо станции с меня слетела шляпа".OnEnterZone()
Вызывается после загрузки зоны по истечении одного временного сегмента, т.е. 1/20 секунды. Лично мне непонятно, где это можно использовать. И чем использование данного обработчика отличается от простой последовательности операторов в теле скрипта.OnExit()
Вызывается, когда пользователь нажмет кнопку "Выход". Реакцию надо прописать самому, т.к. иначе с зоны будет не выйти. Стандартная реализация с запросом диалога "Хотите ли Вы покинуть зону" выглядит так:function OnExit() ShowLeaveZoneDialog( 16837 ) endМожно и без всяких вопросов:
function OnExit() ExitToChapter() end
OnRealExit()
Вызывается, если в упомянутом выше диалоге пользователь таки нажал кнопку "Да". Реакцию надо прописать самому, т.к. иначе с зоны будет не выйти. Стандартная реализация:function OnRealExit() ExitToChapter() endОбработчик можно использовать для простановки целей и задач, у которых указан таг "Completed by script" в состояние "выполнено", и выдачи улик.
function OnRealExit() ScenarioSetGoalComplete( 279, true ) ScenarioGiveClue( "Defend_Informer", true, false) Sleep( 5 ) ExitToChapter() end
OnShotAtUnit( unit )
Вызывается "если кто-то выстрелил в персонажа", как написано в руководстве. Вообщем, не так это. И не только "выстрелил", но и "прицелился" (в смысле, функция UnitShootPrepare также вызывает данный обработчик) и не обязательно в персонажа. По началу, я предполагал, что данный обработчик можно использовать для отслеживания состояния diplomacy - типа, напал на нейтрального, он это дело распознал и стал враждебным. Однако, жестоко обломился. Данный обработчик НЕ вызывается при работе холодным оружием и кулаками. Надо сказать, что холодное оружие вообще стоит особняком в игре - его невозможно обнажить, например, на него не реагирует данный обработчик, функция UnitShootPrepare если у юнита в руках нож приводит не к подготовке, а к реальному удару и т.п. Но что-то я увлекся. Проблему пришлось решать, запуская поток, который отслеживает состояние здоровья юнитов нейтрала, и выставляет diplomacy в случае необходимости. Так:neitralDiplomacyChanged = false function changeNeitralDiplomacy() neitralDiplomacyChanged = true SetDiplomacy( 0, 2, DS_ENEMY ) SetDiplomacy( 2, 0, DS_ENEMY ) local group = PlayerGetUnits(2) local sz = GroupGetSize(group) for i = 0, sz-1 do local unit = GroupGetUnit(group,i) UnitSetNormalLogic(unit) end end function checkNeitrals() while(neitralDiplomacyChanged~=true) do local group = PlayerGetUnits(2) local sz = GroupGetSize(group) for i = 0, sz-1 do local unit = GroupGetUnit(group,i) if(UnitGetSkill( unit, ST_VP ) ~= UnitGetSkillMaxValue( unit, ST_VP )) then changeNeitralDiplomacy() break end end Sleep(5) if(sz==0) then break end end end StartThread(checkNeitrals)Нарекания на предыдущий кусок - гражданские станут враждебными даже если кто-то из них пострадал в результате действий противника. Ну и разумеется, предполагается, что гражданские - это игрок с номером 2, и этот игрок на карте присутствует.
OnPlayerLose( bScenarioLose )
Вызывается, в случае, если главный герой помер или без сознания (bScenarioLose=false) либо сценарий провален по другой причине (bScenarioLose=true). Стандартная реализация обработчика выглядит так:function OnPlayerLose( bScenarioLose ) if bScenarioLose == 1 then ShowLoseDialog( 20985 ) -- убит юнит, являющийся ключевой уликой else ShowLoseDialog( 20984 ) -- главный герой убит или без сознания end endСоответственно, если убрать вызов ShowLoseDialog(20984), то игру можно будет продолжить и без главного героя.
OnTalk( HeroUnit, TargetUnit )
Очередной перл от руководства : "Когда главный герой хочет поговорить с персонажем TargetUnit". Нет, ребята. Никакой не главный герой. А произвольный член партии с юнитом, у которого предварительно было выставлено UnitSetCanTalk( unit, true ) или UnitSetDialog( unit, dialogCode ). Пример:function OnTalk( who, target ) if(IsEqual( who, GetHero() )) then WaitForUI( DialogPlay("testDlg") ) UnitSetCanTalk( target, false ) else UnitSayAck( who, 113 ) -- почекаэм, пока главный ни прийдэ end end
Про прочие обработчики как то
сказать мне особо нечего, они достаточно ясно (хоть и излишне кратко) описаны в руководстве. В принципе, есть еще один неописанный обработчик - OnScriptNotify( sID, nParam ). Он используется в движке игры для узкоспециализованной цели - перелистывать страницы папок на экране выбора сценария. Можно ли его приспособить еще к чему-то мне не известно.
К оглавлению