• Добро пожаловать, Гость. Войдите или зарегистрируйтесь.

Автор Тема: Tes3mod: Скриптовые Ловушки  (Прочитано 3899 раз)

Описание:

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн GL.Sam

  • *
  • Сообщений: 60
  • Репутация: 0
  • Пол: Мужской
  • I love you Kate
    • Просмотр профиля
    • ESF
    • Email
Tes3mod: Скриптовые Ловушки
« : 08 Январь, 2007, 03:48:47 »
0
TES3Mod: Скриптовые ловушки

Перевод: GL.Sam
Оригинал: http://www.uesp.net/wiki/Tes3Mod:Scripting_Pitfalls

Введение
В этом документе показаны общие и/или неизвестные скриптовые ловушки. Эти проблемы могут возникать в связи с ограничениями скриптового компилятора, багов в исполняемом файле Морровинда или непредвиденных побочных эффектов игрового процесса.

Синтаксис

"Проваливание" сквозь ForceGreeting
Если вы используете функцию ForceGreeting в блоке if, ставьте после нее return. В противном случае скрипт будет продолжать исполнять оставшиеся elseif/else проверки, вместо того, чтобы пропустить их. Это баг в процессе исполнения скриптов.
if ( test1 )
  wr_testNPC->forceGreeting;--Неправильно! Исполнение "провалится" сквозь строку и messagebox будет выведен.
else                                           
  messagebox "Didn't forcegreet"
endif

if ( test1 )
  wr_testNPC->forceGreeting;--Правильно. Последующее return предотвратит "проваливание".
  return                             
else
  messagebox "Didn't forcegreet"
endif

Дробные числа
Постоянная дробь, определенная без цифры перед точкой, вызовет исполняемую ошибку. Дроби в диапазоне -1 < 0 < 1 должны быть определены с нулем перед точкой. В противном случае произойдет исполняемая ошибка.
if ( number == .2 );--Неправильно!
...

if ( number == 0.2 );--Правильно

Неполное использование запятых
Скриптовой язык не слишком разборчив в том, используете вы запятые или нет, но из-за неполного использования запятых могут возникнуть трудности:
Это будет работать:
Player->PositionCell, -1396, 124, 3312, 90, "Cell ID"

и это тоже:
Player->PositionCell -1396 124 3312 90 "Cell ID"

но с этим иногда возникают проблемы:
Player->PositionCell -1396, 124, 3312, 90 "Cell ID"

К тому же, запятые в функциях использованных внутри if(), while(), или set утверждений могут иметь результатом скомпилированный итог, включающий в себя запятые даже если они не нужны. Хотя утверждение может исполняться правильно, рекомендуется не использовать запятые в функциях по вышеизложенной причине.
Итог будет нормальный:
     set TestVar to ( player->GetPos X )
     if ( ( GetFactionReaction faction_01 "faction_02" ) >= 50 )

но здесь итогом будут служить дополнительные запятые в скомпилированных данных:
     set TestVar to ( player->GetPos, X )
     if ( ( GetFactionReaction, faction_01, "faction_02" ) >= 50 )
Неизвестно, какова цель запятых в скомпилированных данных, если она вообще есть.

Имена объектов
Не начинайте имена объектов знаком подчеркивания. В противном случае откажут некоторые скриптовые операции. Вы можете предотвратить эту ошибку, заключив имя объекта в кавычки.
_wr_testNPC->forceGreeting  ;Неправильно: вызовет исполняемую ошибку

"_wr_testNPC"->forceGreeting;Правильно?
wr_testNPC->forceGreeting    ;Правильно
У вас могут появиться такие же проблемы при использовании имен, начинающихся с чисел.
123wr_testNPC->forceGreeting    ;Неправильно: может вызвать ошибку

"123wr_testNPC"->forceGreeting  ;Правильно
wr_testNPC->forceGreeting      ;Правильно
Это также относится к ID скриптов. Однако, поскольку ID скрипта на может быть заключено в кавычки в своем собственном скрипте, рекомендуется не начинать скриптовые имена с чисел или знаков подчеркивания.

Пробелы
В отличие от других языков, правильное использование пробелов важно в некоторых ситуациях. Опускание пробелов может вызвать в скриптах кажущуюся правильность компилирования, но выходные данные будут
испорчены и в некоторых случаях скрипт не будет работать как ожидалось.

Оператор сравнения
Один пробел должен быть включен на каждой стороне оператора сравнения в утверждениях if() и while().
if ( SomeVar>=10 )      ;Неправильно
if ( SomeVar >= 10 )    ;Правильно

while (Count!=20)      ;Неправильно
while ( Count !=20 )    ;Правильно

Скобки и операторы
Скобки и операторы должны иметь по пробелу на каждой стороне, когда использованы в утверждениях if(), while() или set.
if(SomeVar >= 10)      ;Неправильно
if ( SomeVar >= 10 )    ;Правильно

set SomeVar to (10*Global01/(Global02+92))          ;Неправильно
set SomeVar to ( 10 * Global01 / ( Global02 + 92 ) );Правильно

Оператор стрелка
При использовании оператора стрелка нужно или заключить ссылающийся объект в кавычки, или же не допускать пробелов между ним и стрелкой. В противном случае вы получите исполняемую ошибку в случайном и обычно не связанным с текущим скрипте.
set strength to wr_testNPC -> getStrength;--Неправильно!

set strength to wr_testNPC->getStrength;--Правильно.
set strength to "wr_testNPC" -> getStrength;--Правильно.

Переменные

34-ая переменная
Если у вас в скрипте 34 или более переменных одинакового типа (short, long или float), использование 34-ой переменной того же типа будет вызывать ошибки. По этой причине большинство скриптеров называют ее
"DoNotUse" или чем-то вроде этого, чтобы помнить что ее нельзя использовать. Причина этой проблемы, наверное, в том, что ASCII символ 34 является кавычками.
short Var1
short Var2
short Var3
short Var4
...
short Var33
short Var34
short Var35

if ( Var34 == 0 )
    Set Var34 to 1;Эта линия и линия выше могут вызывать странные ошибки.
endif
Вместо этого вы можете использовать для предотвращения ошибок следующее:
short Var1
short Var2
short Var3
short Var4
...
short Var33
short DoNotUse
short Var34
short Var35

if ( Var34 == 0 )
    Set Var34 to 1;Теперь это будет работать правильно, т. к. Var34 на самом деле 35-ая.
endif

Если типы переменных смешанные, делайте что-то вроде этого:
short Var1S
float Var1F
short Var2S
float Var2F
...
short Var33S
float Var33F
short DoNotUseS
float DoNotUseF
short Var34S
float Var34F

Отметьте, что теперь "плохая" переменная не 34-ая, а 34-ая short и 34-ая float.

Удаленный доступ к переменным
Цитировать (выделенное)
- Следующее не полностью правильно. Вы можете получать доступ к локальным переменным другого объекта (обычно), но вы не можете иметь доступ к локальным переменным глобального скрипта. Однако, я думаю что существуют некоторые ограничения по копии переменной объекта (например, их нельзя использовать в выражениях?) Для локальных переменных глобального скрипта - если вы попытаетесь получить их, они просто возвратят 0.
- Также, подход с использованием GetAttribute и GetSkill возможно неработающий, так как эти функции возвращают 0, если игрок не встречал NPC за последние 72 игровых часа.


Одно серьезное ограничение скриптового языка состоит в том, что нет очевидного пути для проверки переменной на другом скрипте. Хотя можно задавать значения переменным другого локального скрипта используя следующий формат:

set object_ID.variable to 1;--Правильно.
Или задавать значение переменным другого глобального скрипта (внутри другого локального/глобального скрипта и даже из поля result диалогового окна) используя следующий формат:

set global_script_ID.variable to 1;--Правильно.
Обратное невозможно:
set local_var to object_ID.variable;--Неправильно!
set local_var to global_script_ID.variable;--Неправильно!
set object_ID.variable to object_ID.variable + 1;--Неправильно!
object_ID->set variable to variable + 1;--Неправильно!

Но:

if ( object_ID.variable == 1 );--Правильно.
Теперь, принимая во внимание все эти ограничения, как можно получить значение из другого скрипта?

Существует несколько вариантов:

1. Использовать глобальную переменную. Это быстрейший и легчайший метод, но и у него есть свои ловушки (см. ниже).

2. Ввести некоторый тип скрипта обратного вызова. Вызвать определенную сигнальную переменную в скрипте, которая передаст этому скрипту выслать значение назад. Это потребует, по крайней мере, задержки в одном кадре, так что в этом случае вам нужно структуировать свой скрипт, чтобы он подождал пока другой скрипт исполнит обратный вызов.

3. Использовать некоторое другое, универсально доступное свойство объекта. Определенные свойства объектов всегда доступны удаленно, хотя в большинстве случаев эти свойства используются для других вещей,  в определенных ситуациях вы можете их использовать.
Примеры:
-GetPos (X,Y,Z), GetAngle (X,Y,Z), GetScale. Если объект - NPC или существо, вы должны знать, что GetAngle будет работать только с осью Z, и только если объект парализован. (В противном случае они могут поворачиваться сами.) Также, GetPos будет работать только с осями X и Y, если только существо не летает, или имеет на себе заклинание левитации, так как значение Z будет продолжать возвращать их к земле. Не говоря уже о том, что они должны быть парализованы или перегружены, чтобы предотвратить их движение. Если вы
не хотите справляться с движением или перемасштабированием объекта, вы можете получить эти свойства на другом объекте, таком как кольцо, спрятанное в стене, например. ("References Persist" на вызывающем объекте
конечно же должно быть отмечено, если это не NPC или существо, и также объект должен быть уникальным, чтобы предотвратить путаницу в скрипте.)

-GetAttribute, GetSkill. GetSkill конечно же будет работать только на NPC, а GetAttribute будет работать для NPC и существ. Вам нужно будет выбрать характеристику или навык, изменение которого не будет заметно, так что
Привлекательность, Сила Воли или Выносливость будет отличным выбором для атрибутов. Среди навыков у вас больший выбор. Попробуйте использовать, скажем, Колдовство, на NPC, у которого нет заклинаний этой школы. Очевидно, этот метод имеет один недостаток - вы можете получить неправильные результаты, если игрок наложит на цель заклинание уменьшения/увеличения статистик.

-GetItemCount (используйте "AddItem" и "RemoveItem" для изменения переменной) Работает только на NPC, существах и контейнерах. Очевидно, при использовании этого метода нельзя будет позволять игроку получать
доступ к инвентарю объекта. Если это NPC или существо, вещи должны быть заскриптованы чтобы удаляться по смерти объекта. К тому же NPC может быть обворован, так что любая вещь, которую вы можете использовать, должна быть одеждой (не броней), которую они будут носить. Или рассматриваемый NPC должен быть спрятан в стене, где вы не можете получить к нему доступ напрямую (спрятанный контейнер также отлично работает).

-GetCurrentAIPackage. Работает только на NPC и существах. Если объект парализован или перегружен, вы можете менять его пакет AI (AIFollow, AIWander, AIEscort, и т. д.), не волнуясь о побочных эффектах. (Просто
убедитесь, что он достаточно далеко от любых дверей, чтобы не последовать за вами.

-GetEnabled. Работает, сохраняя двоичную (0 или 1) переменную на объекте показывая/скрывая его. Будет работать с чем угодно, но конечно же объект исчезает когда скрывается, так что это должно быть чем-то спрятанным в стене, чтобы игрок не видел этого.

Функции

Скобки
Функции, использованные в срединении с if(), while() и set утверждениями, должны быть заключены в скобки. Если скобки опущены, это может вызвать странную исполняемую ошибку или же неправильную итог компиляции, что может привести к неправильной работе скрипта в некоторых ситуациях. Не забывайте включать пробелы по обеим сторонам скобок и функции.
if ( player->GetStrength > 10 )    ;Неправильно
if ( ( player->GetStrength ) > 10 );Правильно

set LocalVar to ( GetFactionReaction faction_01 "faction_02" - 100 ) * 1.2    ;Неправильно
set LocalVar to ( ( GetFactionReaction faction_01 "faction_02" ) - 100 ) * 1.2;Правильно

Фаза луны в ячейках интерьеров
Функции проверки лунной фазы GetMasserPhase and GetSecundusPhase не обновляются в ячейках интерьеров.
При использовании в них они всегда будут передавать значение, бывшее истинным за последнее пребывание вне интерьера.

AIWander
Функция AIWander не результирует как остальные. Офицально утвержденный синтаксис для функции следующий:
AiWander [Range] [Duration] [Time] [Idle2] [Idle3] ... [Idle9] [Reset]Но скомпилированный результат для функции всегда опускает параметр [Idle2] и добавляет дополнительный необязательный параметр [Idle10] перед последним [Reset]. Например:
Вызов функции:
   AiWander 100 1000 0 10 20 30

Скомпилированные данные:
   [Opcode] 100 1000 0 20 30
По неподтвержденным данным, параметры idle просто сдвигаются один за другим на показанном синтаксисе.
При использовании функции вы можете добавить 'дополнительный' параметр на месте [Idle2].

Еще одна проблема с функцией AIWander это параметр Reset. В отличие от других функций AI, параметр Reset в этой функции работает по другому и несколько случайно. Если не использован в одиночном скрипте, выходное
значение reset всегда байт 01 в скомпилированных данных. Если использован единожды или более раз в одном скрипте, его компилированное значение может изменяться между 00, 01 и 05. Рекомендуется не использовать
параметр reset с функцией AIWander, пока его назначение не подтверждено.

PlayGroup и LoopGroup
Эти две функции принимают анимационную группу как первый параметр. При компиляции текстовое значение этой группы конвертируется в код операции анимационной группы, 16-битное целое число. В какой-то момент коды операций для анимационных групп, закодированные в приложении, были изменены, скорее всего одним из дополнений или патчей (пока не подтверждено каким именно). Большинство анимационных групп изменило коды операций, а также было введено несколько десятков новых.

Проблема в том, что скрипты, использующие PlayGroup/LoopGroup, скомпилированные в более старой версии Construction Set, содержат старые коды операций. Если такой скрипт запущен в более новой версии игры, коды
операций анимационных групп в этом скрипте могут не соответствовать тем, которые имел в виду автор скрипта. Скрипт должен быть перекомпилирован с новой версией Construction Set, чтобы коды операций были обновлены (хотя потом этот скрипт не сможет быть запущен в более старых версиях игры).

Этот баг еще не подтвержден, и вполне может быть скрытый способ конвертирования между старыми и новыми кодами операций.

Глобальные Скрипты

StartScript c удаленного объекта
Если вы запустите глобальный скрипт с объекта, который может быть удален, привяжите его к чему-нибудь постоянному, такому как игрок. В противном случае вы получите CTD (вылет в Windows) при удалении объекта, если скрипт все еще исполняется. Например в NOM 2.12 имеется скрипт на горшках с отваром,
стартующий глобальный скрипт, который выполняется некоторое время. Но горшок можно подобрать и положить в инвентарь, что означает удаление выложенного горшка.

elseif ( state == 30 )
  startScript NOM_tisane;--Неправильно! Вызовет CTD когда горшок будет удален
...

elseif ( state == 30 )
  player -> startScript NOM_tisane;--Правильно. Нет вылетов по удалении горшка
Примечание: все глобальные скрипты, кроме некоторых начальных, связаны с каким-то объектом. Это объект, предполагаемый скриптом всякий раз, когда скрипт использует функцию со стрелкой без определения ссылающегося объекта. Если вы не определили объект, использующий стрелку, глобальный скрипт унаследует объект скрипта, который его запустил.

Задержка StopScript
Не думайте, что StopScript сразу же останавливает исполнение скрипта. StopScript дает понять Морровинду, что не надо запускать глобальный скрипт опять. Но он не останавливает текущее исполнение скрипта.

При вызове StopScript из останавлиемого скрипта или убедитесь что оставшаяся часть может быть исполнена не вызывая проблем, или же используйте после StartScript утверждение return, чтобы заставить скрипт
немедленно прекратить свое исполнение.

Постоянство Переменных

Не полагайтесь на постоянство переменных глобального скрипта, если только этот скрипт не гарантированно исполняется хотя бы раз во время каждой загрузки. Если глобальный скрипт не запускается во время загрузки,
переменные в нем не будут сохранены во время сохранения игры.

Если исполнение скрипта не гарантированно:
-Сохраните где-нибудь эти переменные, например в глобальную переменную
-Заставьте скрипт исполняться единожды при каждой загрузке. Это может быть сделано стартующим скриптом.

Например:
begin wr_testHeartbeatSS
set wr_test1.heartbeat to 1
stopscript wr_testHeartbeatSS
end

begin wr_test1
short heartbeat
short persist1
short persist2

if ( heartbeat )
  set heartbeat to 0
  stopScript wr_test1
endif
...
end


Унарные операторы

Унарные операторы - это знаки + или - перед символом в выражении, например:
set local to -6                    ;Унарный -
set local to 1 - 6                ;Обычное вычитание -
set local to -global              ;Унарный -
set local to -(global1 + global2)  ;Унарный -

Простые утверждения, как показанные выше, CS компилирует правильно, хотя обычно конвертирует их в произведение с -1.

set local to -1      => set local to -1 * 1
set local to +15     => set local to 15 +
set local to -global => set local to -1 * global
if  ( local == -1 )     => if ( local == -1 )          ;Не меняется в утверждениях if

Более комплексные утверждения могут результировать некорректно, например:

set local1 to -(-1 -9) * -local2 / -local3
Результирует как:    set local1 to 9 * -local2 / -local3
   
if     ( -testmisc2.local1 == 10 ) 
Результирует как:    if -1 *(  testmisc2.local1 == 10 )

Рекомендуется не использовать унарный минус нигде, кроме простых выражений и использовать явное умножение (* -1) для более комплексных конструкций.
~Desertwalker

Теги:
 

Wiki: Morrowind:Скриптовые ловушки

Автор WikiBotРаздел Мастерская TES III

Ответов: 0
Просмотров: 1070
Последний ответ 11 Ноябрь, 2011, 14:00:37
от WikiBot
Скриптовые эксперименты

Автор HelmutРаздел Скриптинг под TES III

Ответов: 1
Просмотров: 5492
Последний ответ 20 Январь, 2005, 00:00:23
от Helmut
Плагин на ловушки

Автор SorcererРаздел Плагины для TES 3: Morrowind

Ответов: 0
Просмотров: 1565
Последний ответ 19 Июнь, 2010, 17:25:00
от Sorcerer
Скрипты и скриптовые функции в игре Скайрим.

Автор GarinРаздел Скриптинг под TES V

Ответов: 4
Просмотров: 21064
Последний ответ 08 Декабрь, 2011, 12:36:25
от Garin

Поиск

 
Top
SimplePortal 2.3.5 © 2008-2012, SimplePortal