Модератор форума: Kris†a™  
Форум » TES V: Skyrim » Мастерская » Вопросы по скриптам Papyrus (О скриптах Papyrus (Skyrim). Скриптеры не проходите мимо!)

Вопросы по скриптам Papyrus
sansuli  Offline  Сообщение №1 написано: 31 августа 2012, 13:29 | Отредактировано: Multigone - 23 апреля 2020, 14:24


The Red Sun


193
Уроки по скриптованию на языке Papyrus
Прежде чем задать вопрос просмотрите вышеуказанные уроки.
ok


Тема регламентирована.


• Прежде, чем задать вопрос, пожалуйста, убедитесь, что такой вопрос не задавался раньше. Старайтесь описать суть вопроса как можно подробней.
• Прежде, чем опубликовать ответ на вопрос, пожалуйста, убедитесь, что обладаете необходимыми для этого знаниями. Старайтесь cформулировать суть ответа как можно лаконичней.
• При желании ответить в приватном порядке, пожалуйста, воспользуйтесь ЛС.
• При желании поблагодарить ответившего, пожалуйста, воспользуйтесь кнопкой "
+" полезного сообщения.

Сообщения, не относящиеся к вопросам по скриптам Papyrus, ответам на них или уточнениям, являются оффтопом и могут быть удалены.

Красное солнце
Есть вопросы по скриптам Papyrus? Пиши не в ЛС, а в эту тему.
Multigone  Offline  Сообщение №2581 написано: 12 декабря 2017, 15:01



832
Князь_Далик, для каждого маркера потребуется пара "квест. задача + алиас". Скрипт для любого кол-ва маркеров (насколько хватит алиасов, нужен тест):Е


Код
Scriptname _TESTTEST43 Extends ObjectReference ; Скрипт на контейнер.

Message Property pxNotNoted Auto
    ; Сообщение 1
    ;0 - доступ к инвентарю сундука
    ;1 - забрать все в инвентарь игрока
    ;2 - добавить маркер

Message Property pxNoted Auto
    ; Сообщение 2
    ;0 - доступ к инвентарю сундука
    ;1 - забрать все в инвентарь игрока
    ;2 - удалить маркер
    
Quest Property MMarker Auto
MiscObject Property chestparts Auto
Int Property piNoted = -1 Auto Hidden

EVENT OnActivate(ObjectReference akActionRef)
    GoToState("Empty")
    Int button
    IF piNoted < 0
        button = pxNotNoted.Show()
    ELSE
        button = pxNoted.Show()
    ENDIF
    if !button
        Activate(Game.GetPlayer(), true)
    elseif button == 1
        removeAllItems(Game.GetPlayer(), true)
        Game.GetPlayer().AddItem(chestparts)
        Delete()
    ELSEIF piNoted < 0
        button = 0
        WHILE button < 128 && (MMarker.GetAlias(button) AS ReferenceAlias).GetReference()
                            ; Вместо 128 указать реальное кол-во алиасов. Напр., если последний номер равен 15, общее кол-во = 16.
                            ; Алиасы должны иметь ID-номера строго по порядку от 0 до Х (0, 1, 2, 3, ..., X).
                            ; Чтобы нумерация была строгой, алиасы нельзя удалять после создания.
                            ; Квестовые задачи должны нумероваться строго по порядку от 0 до Х.
            button += 1
        ENDWHILE
        IF button < 128 ; Вместо 128 указать свое число.
            piNoted = button
            (MMarker.GetAlias(button) AS ReferenceAlias).ForceRefTo(Self)
            MMarker.SetObjectiveDisplayed(button, true)
        ELSE
            Debug.MessageBox("Слишком много активных маркеров")
        ENDIF
    ELSE
        MMarker.SetObjectiveDisplayed(piNoted, false)
        (MMarker.GetAlias(piNoted) AS ReferenceAlias).Clear()
        piNoted = -1
    ENDIF
    GoToState("")
ENDEVENT

STATE Empty
    EVENT OnActivate(ObjectReference akActionRef)
    ENDEVENT
ENDSTATE

All55  Offline  Сообщение №2582 написано: 16 декабря 2017, 11:20



8
Уважаемые скриптеры, подскажите, пожалуйста.
Есть скрипт
Код
myFX.playAnimation("playAnim02")
который проигрывает анимацию открытия портала.
Нужна анимация закрытия этого портала. Есть такая? А если есть, то как называется.

modgms_user  Offline  Сообщение №2583 написано: 19 декабря 2017, 18:14 | Отредактировано: Multigone - 22 апреля 2020, 13:06



20
Приветствую. Хотелось задать вопрос об одной проблеме, непонятной проблеме.
Проблема такая. В лог папирус периодически кидает сообщение об ошибке:


Есть код в скрипте:
Код
function OnUpdate()

   if PlayerRef.IsWeaponDrawn() || PlayerRef.GetAnimationVariableBool("bIsRiding") || PlayerRef.GetAnimationVariableBool("IsFirstPerson")
      if PlayerRef.GetAnimationVariableBool("IsNPC")
         PlayerRef.SetLookAt(PlayerRef as objectreference, false)
         PlayerRef.ClearLookAt()
         PlayerRef.SetAnimationVariableInt("IsNPC", 0)
      endIf
      self.RegisterForSingleUpdate(1.00000)
      return
   elseIf !PlayerRef.GetAnimationVariableBool("IsNPC")
      PlayerRef.SetAnimationVariableInt("IsNPC", 1)
   endIf
   if game.GetCurrentCrosshairRef() != none && math.Abs(PlayerRef.GetHeadingAngle(game.GetCurrentCrosshairRef())) < 90 as Float
      PlayerRef.ClearLookAt()
      PlayerRef.SetLookAt(game.GetCurrentCrosshairRef(), false)
   else
      PlayerRef.ClearLookAt()
      PlayerRef.SetLookAt(PlayerRef as objectreference, false)
   endIf
   self.RegisterForSingleUpdate(0.250000)
endFunction


И вроде как есть условие проверки на None: if game.GetCurrentCrosshairRef() != none
..но все равно периодически кидает указанную выше ошибку.

В чем здесь может быть проблема, может проверка какая другая нужна, может дополнительная?

Dsion  Offline  Сообщение №2584 написано: 19 декабря 2017, 19:41



modgms_user, нативные функции в этих скриптах выполняются очень медленно. А у тебя их досточно много между проверкой на None и установкой цели. Может, игрок успевает убрать прицел с цели, пока всё это выполняется.
А даже если и не так, всё-равно лажа это всё. Автору надо разобраться с переменными. И сделать так, чтоб во всем скрипте был только один вызов GetCurrentCrosshairRef() с занесением результата в переменную:

ObjectReference CrosshairRef = Game.GetCurrentCrosshairRef();

modgms_user  Offline  Сообщение №2585 написано: 22 декабря 2017, 06:05 | Отредактировано: Multigone - 22 апреля 2020, 13:08



20
Цитата Dsion

нативные функции в этих скриптах выполняются очень медленно. А у тебя их досточно много между проверкой на None и установкой цели. Может, игрок успевает убрать прицел с цели, пока всё это выполняется.


Спасибо за наставление, посыл понятен. Действительно, автор другой, это redux-вариант мода Headtracking, что в сборке SLMP-GR, может ему тоже ссыль на ответ отправлю, или уже исправленный скрипт. Самостоятельно, в основном, только правки косяков в скриптах модов делались, которые авторы оставили по тем или иным причинам, чтобы игра стабильнее была. Подобные мысли про тормоза тоже были, но как-то не дошло, что значение Game.GetCurrentCrosshairRef() может измениться и занести заранее значение в переменную будет разумнее, ну и руки не дошли, поэтому, наверное, все же было полезно узнать здесь. Спасибо еще раз.

Добавлено (22 Декабря 2017, 09:05)
---------------------------------------------
Приветствую еще раз. Есть другой вопрос по ошибке в скрипте.
Есть скрипт из мода Worlds dawn, начинается так:
Код
Scriptname XTD_FortifyDecimal extends activemagiceffect

Event onEffectStart(Actor akTarget, Actor akCaster)
   ValueMod = self.GetMagnitude()/100.0


Бывает, порой часто, что кидает в лог следующую ошибку:


Часто группа таких ошибок находится недалеко от конца лога, в той сессии, когда произошел вылет игры. Пробовал добавлять проверку "if self as XTD_FortifyDecimal", но не прокатывает. Что-то можно ещё сделать, проверку какую, или нужно в других частях мода искать причину?

Multigone  Offline  Сообщение №2586 написано: 22 декабря 2017, 13:20



832
modgms_user, попробуй так:


Код
Event onEffectStart(Actor akTarget, Actor akCaster)
    IF !akTarget
        RETURN
    ENDIF
    ValueMod = GetMagnitude()/100.0
    if IsNegative
        ; ...

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

modgms_user  Offline  Сообщение №2587 написано: 22 декабря 2017, 19:52 | Отредактировано: Multigone - 22 апреля 2020, 13:09



20
Цитата Multigone

попробуй так:


IF !akTarget тоже не прокатило, такая же ошибка в лог, только номер строки соответствующий, проверялось, естественно, с загрузкой игры, где мод ещё был выключен и скрипт еще не был загружен.
Возникла дурацкая мысль и вопрос, связанный с этим. Если, допустим, сделать условие "if self.GetMagnitude()" и спрятать под ним остальную часть, выполнится ли та основная часть кода, до endif, если будет ошибка в этом условии или же тупо будет проигнорирована строка условия if и будут тупо выполняться все остальные строки под ним? Тоесть, будет первая строка условия "if self.GetMagnitude()" и под ней, допустим, три строки кода, а потом endif и в случае ошибки перепрыгнет сразу за endif или будет просто дальше выполнять те три строки кода. Интересно узнать, как будет работать в такой ситуации.
Может еще что-то можно попробовать?

Dsion  Offline  Сообщение №2588 написано: 23 декабря 2017, 00:59



modgms_user, дались тебе эти мутные SKSE-шные скрипты :(
"If" проверка, которую ты описал, от ошибки в логе не избавит. Потому что функция таки выполняется, а проверяется уже возвращенное из неё значение.
Если функция возвращает Bool, то
"if func()" - это то же самое, что "if func() == true".
А если возвращает Int или Float, то, скоре всего,
"if func()" - это то же самое, что "if func() != 0".
Во всяком случае, в Си так, а в папирусе не проверял.

Multigone  Offline  Сообщение №2589 написано: 23 декабря 2017, 16:11



832
Dsion, немного дополню. В случае, если Х - переменная Bool, писать (IF X == true) нет никакого смысла.


Код
IF X ; Если Х истинно, то...
IF X == true ; Если (X == true) истинно, то...

Переменная (X) и результат выражения (X == true) всегда имеют одинаковые значения, но во втором случае затрачивается лишнее действие на вычисление результата.

modgms_user  Offline  Сообщение №2590 написано: 23 декабря 2017, 18:08



20
Multigone, в описанном выше моде Worlds dawn, в одном из скриптов видел условие "if target && target != NONE", а ведь по той же логике с условиями "if target" это то же, что и "target != NONE" и второе там избыточно и можно обойтись вместо этого просто условием "if target"? Считаю так, но мало ли, может в папирусе свои особенности еще какие есть.

По ошибке с self.GetMagnitude(), видимо все же, как писал выше о глупой идее, то даже если возникнет ошибка в условии "self.GetMagnitude()" и тогда будет пропущено всё, что стоит вложенное в это условие, до endif. Это конечно все равно будет вызывать ошибку и её регистрацию в логе, но по крайней мере можно предотвратить деструктивные изменения в дальнейшем скрипте, да и ошибка бы была в любом случае, но в условии, по крайней мере, не будет запущен спрятанный под условием код. Хотя бы как временное решение, меньше зло. Может еще что можно придумать, чтобы предотвратить ошибку с self.GetMagnitude() на OnEffectStart, в озвученном выше скрипте?
Пробовал прятать код под условия "if self as XTD_FortifyDecimal", "if akTarget"(и предложенный обратный "if !akTarget" с return), но не сработало, так и выдает ошибку, вот, пока только временно, засунув в условие self.GetMagnitude(), хоть как-то предотвратить выполнение кода, точнее последствий его выполнения, в виде искасяченных статов неписей, но идеально было бы вообще найти решение без ошибки в лог.
Видел моды, которые даже авторы годаим не могли довести до нормального состояния, а другие пользователи делали патчи, где в том числе и в скриптах исправляли казавшиеся неисправляемыми косяки, точно вспоминая LL мод Beeing Female и патч к нему, где автор писал[а] о множестве безрезультатных попыток исправить баги, которые рушили стабильность игры, были и другие моды с похожей историей, да и неофициальный патч к игре тоже имеет множество фиксов скриптов, на которые даже у разрабов игры руки не дошли.

Dsion  Offline  Сообщение №2591 написано: 24 декабря 2017, 04:55



Multigone, ты оскорбляешь компиляторы!)

modgms_user, идем, лучше, играть в WOW и писать аддоны для WOW, а? Там хоть нормальное API и код самого близзарда очень качественный.
А по GetMagnitude() - ну возьми проверь, какое значение оно возвращает при ошибке. Если там ноль и ноль не является допустимым для работы остального скрипта, то можно сделать проверку на ноль.

modgms_user  Offline  Сообщение №2592 написано: 25 декабря 2017, 17:48 | Отредактировано: Multigone - 22 апреля 2020, 13:11



20
По GetMagnitude в условии, сначала изменил скрипты, тестил, вроде видел только ошибку в условии и не было других, но потом, после более долгих тестов увидел две ошибка подряд:

Код
Error: Unable to call GetMagnitude - no native object bound to the script object, or object is of incorrect type
stack:
[None].XTD_ProcSelf.GetMagnitude() - "<native>" Line ?
[None].XTD_ProcSelf.OnEffectStart() - "xtd_procself.psc" Line 7
Error: Unable to call GetMagnitude - no native object bound to the script object, or object is of incorrect type
stack:
[None].XTD_ProcSelf.GetMagnitude() - "<native>" Line ?
[None].XTD_ProcSelf.OnEffectStart() - "xtd_procself.psc" Line 9

, т.е. после ошибки в условии. строка с присвоением, где тоже произошла записсанная в лог ошибка, также выполнялась. Это другой скрит, там их несколько таких с похожим присвоением. Получается, что все же, после ошибка в условии строка с условием просто игнорится, пишется ошибка в лог и продолжает выполнять дальше, по другому, эти две подряд ошибки, с указанием на обе строки с условием и присвоением, где было GetMagnitude , объяснить сложно.
Сделал другим способом, опять же, меньшим злом, убрал первое условие с GetMagnitude и после строки присвоения сделал проверку, чтобы выполнять только если значение больше нуля, т.к. вспомнил, что совал debug.trace и помнится, числовые переменные просто были равны нулю, когда была ошибка в self.GetMagnitude(). Все же одна ошибка- меньшее зло и пропуск последующего кода, во первых, сэкономит время папируса и не будет тормозить выполнением кучи лишних команд, а во вторых, предотвратит возможные отрицательные последствия выполнения кода с неправильными значениями.

Опять же, если все же будут у кого умные мысли, как ещё можно проверить, чтобы и ошибки не было и выполнялось, когда можно, будет очень хорошо. Может где есть примеры скриптов модов, где есть похожие исправления, все же, многое как раз там и подсматривается, в разных неоф. патчах к разным модам, в скриптах самих модов.

Multigone  Offline  Сообщение №2593 написано: 25 декабря 2017, 21:05



832
modgms_user, обычно, если функции применять к none, они возвращают false, 0, "", none. Конкретно в том скрипте - не будет никакого влияния на глобальную и AV актера, если GetMagnitude() вернет 0.0.
Там другая проблема. Если события старта и финиша срабатывают почти одновременно (например, когда продолжительность эффекта 0), то это может привести к возврату AV не в исходное состояние. Параноик-код должен выглядеть примерно так:


Код
Scriptname XTD_FortifyDecimal extends activemagiceffect

String Property StatToMod  Auto  
Bool Property IsNegative  Auto
Bool Property AffectsGV  Auto
GlobalVariable Property affectedGV  Auto  

Float ValueMod
Bool Kicked
Int iLock

Event onEffectStart(Actor akTarget, Actor akCaster)
    WHILE iLock
    ENDWHILE
    iLock = 1
    ValueMod = GetMagnitude()
    IF !ValueMod
        iLock = 3
        RETURN
    ELSE
        ValueMod /= 100.0
    ENDIF
    IF IsNegative
        ValueMod = -ValueMod
    Endif
    If AffectsGV
        If akTarget == Game.GetPlayer() || akTarget.IsPlayerTeammate()
            Kicked = true
            affectedGV.Mod(ValueMod)
        Endif
    else
        if StatToMod == "weaponSpeedMult" && akTarget.GetActorValue(StatToMod) == 0.0
            ValueMod += 1.0 ; ???
        endif
        akTarget.ModActorValue(StatToMod, ValueMod)
    endif
    iLock = 2
Endevent

Event onEffectFinish(Actor akTarget, Actor akCaster)
    WHILE iLock < 2
    ENDWHILE
    IF iLock == 3
    ELSEIF Kicked
        affectedGV.Mod(-ValueMod)
    else
        akTarget.ModActorValue(StatToMod, -ValueMod)
    endif
    iLock = 0
Endevent

modgms_user  Offline  Сообщение №2594 написано: 26 декабря 2017, 10:03 | Отредактировано: Multigone - 22 апреля 2020, 13:11



20
Цитата Multigone

Конкретно в том скрипте - не будет никакого влияния на глобальную и AV актера, если GetMagnitude() вернет 0.0


Но код все равно будет выполняться и создаст лишнюю нагрузку впустую, выполнит рассчеты с неверным значением. В Worlds Dawn, из отрицательных эффектов, бывало, как раз ловил завышенные статы у компаньонов, когда они носили шмотки с чарами из этого мода.

А так, конечно, можно скрипт попробовать. Только там, в предложенном коде что-то не дошло. На onEffectFinish, понятно, цикл WHILE iLock < 2 крутится пока iLock меньше двух и не дойдет до конца onEffectStart где он станет равен 2, чтобы как раз описанную возможную ситуацию предотвратить, с нулевой длительностью эффекта, но на начале onEffectStart стоит WHILE iLock. WHILE iLock, это разве не крутить, пока переменная не примет какое-то значение, больше нуля? Или я просто запутался или там восклицательный знак перед iLock должен был быть, в начале onEffectStart, хотя цель наличия цикла в начале onEffectStart тоже не дошла.

Multigone  Offline  Сообщение №2595 написано: 26 декабря 2017, 12:53 | Отредактировано: Multigone - 26 декабря 2017, 12:59



832
modgms_user, финиш должен ждать окончания старта. Старт должен ждать финиш - в одном скрипте могут много раз срабатывать эти события, если в EffectItem заклинания есть какое-то периодически срабатывающее условие.

Код будет выполняться, но нагрузка там смешная. К тому же она уже заложена в функционал мода.

Чем защищенней код, тем в нем будет больше проверок. Я бы даже сказал, иногда, чтобы использовать одну строчку, которая что-то там по задумке делает, нужно обеспечить ее десятью защитами, чтобы она срабатывала только тогда и ровно столько раз...
С другой стороны, если знаешь, что код будет выполняться только так, и никак иначе, и создавать лишние проверки нет нужды.

modgms_user  Offline  Сообщение №2596 написано: 26 декабря 2017, 13:17 | Отредактировано: Multigone - 22 апреля 2020, 13:12



20
Цитата Multigone

modgms_user, финиш должен ждать окончания старта. Старт должен ждать финиш


Ясно, свои особенности. Финиш понятно, там стоит цикл с WHILE iLock < 2 и пока выполняется начало эффекта, будет меньше двух, но на самом начале стоит цикл WHILE iLock, которому вроде как надо число отличное от нуля, чтобы выйти из цикла и продолжить, а iLock равно нулю изначально и в конце финиша. Или рассуждение ошибочно и там на самом деле должен стоять WHILE iLock, не WHILE !iLock? (речь про код предложенного примера скрипта и цикл в самом начале)

Multigone  Offline  Сообщение №2597 написано: 26 декабря 2017, 14:28



832
modgms_user

Код
WHILE iLock ; Пока (iLock AS Bool) истинно. Аналог: (iLock != 0)

Операторы.
Приведение.

modgms_user  Offline  Сообщение №2598 написано: 27 декабря 2017, 05:43 | Отредактировано: Multigone - 22 апреля 2020, 13:12



20
Вот не понятно, почему условие "if self as XTD_FortifyDecimal" не работает перед GetMagnitude(). Перед опробыванием последнего предложенного варианта скрипта засунул строку debug.trace перед return, чтобы, когда ошибка, выводила информацию о значении переменных. После очередного теста в игре смотрел лог и рядом с ошибкой с "Unable to call GetMagnitude" для XTD_FortifyDecimal была выведена отладочная вписанная строчка отладки: "XTD_FortifyDecimal > self=[XTD_FortifyDecimal ],ValueMod=0.000000". ValueMod, понятно, при ошибке с GetMagnitude() остался в значении ноль, но ведь self равен [XTD_FortifyDecimal ], т.е. вроде бы как self as XTD_FortifyDecimal должен был бы сработать в условии перед GetMagnitude(), но даже с ним, прямо перед GetMagnitude(), все равно отображалась ошибка.

Может как-то по другому надо условие прописать условие?

Multigone  Offline  Сообщение №2599 написано: 27 декабря 2017, 10:13



832
modgms_user, сделай заклинание, добавь туда эффект магнитудой 10 и продолжительностью 0, повесь скрипт...


Код
Scriptname TEST777 extends activemagiceffect

Event onEffectStart(Actor akTarget, Actor akCaster)
    Utility.Wait(5.0)
    Debug.MessageBox(GetMagnitude())
Endevent

... запусти заклинание на игроке и посмотри, что будет в логе и в сообщении.

modgms_user  Offline  Сообщение №2600 написано: 27 декабря 2017, 12:20 | Отредактировано: modgms_user - 27 декабря 2017, 12:23



20
Multigone, сразу подвох какой-то почувствовал, еще перед тем, как что-то делать..., но все равно сделал.
Сообщение отобразило 0.0000000, а в лог кинуло ошибку "Error: Unable to call GetMagnitude - no native object bound to the script object, or object is of incorrect type". Это такой ответ через предложенное действие, что при нулевой длительности происходит такое или просто для проверки? Показалось, что это ответ такой.
Если все таки это был ответ, то было бы полезно услышать о механике в таких ситуациях, вроде раз ноль длительности, то эффект был удален сразу, то брать магнитуду уже было неоткуда или в таком роде, корректными словами. Также было бы полезно узнать, как же все таки предотвратить ошибку, раз проверка if self не работала.

Multigone  Offline  Сообщение №2601 написано: 27 декабря 2017, 16:49



832
modgms_user, как только эффект снимается с актера, он перестает существовать. А скрипт этого эффекта будет до тех пор, пока не выполнятся все уже запущенные события и функции. Поэтому if self будет (должен?) проходить проверку всегда. А чтобы работала GetMagnitude(), выходит, нужно наличие эффекта на актере. Ведь она возвращает не базовую магнитуду из EffectItem заклинания, а настоящую, с учетом всех его перков / сопротивлений.

Этим можно воспользоваться - если между стартом и финишем пройдет меньше 0.1 с, значит,..
1) ...эффект имеет продолжительность 0...
2) ...или заклинание было снято принудительно, например, если актер в связанных скриптах имеет подобное:

Код
Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
    DispelAllSpells()
EndEvent
Хотя в этом случае первые строки должны успеть выполниться, т.е. магнитуда должна была бы определиться корректно. Наверное.

Недостаток - необходимость ждать 0.1 с перед выполнением основного кода.

Код
Scriptname XTD_FortifyDecimal extends activemagiceffect

String Property StatToMod  Auto  
Bool Property IsNegative  Auto
Bool Property AffectsGV  Auto
GlobalVariable Property affectedGV  Auto  

Float ValueMod
Bool Kicked
Int iLock

Event onEffectStart(Actor akTarget, Actor akCaster)
    iLock += 2
    Utility.Wait(0.1)
    IF iLock < 2
        iLock -= 2
        RETURN
    ENDIF
    ValueMod = GetMagnitude() / 100.0
    IF !ValueMod
        iLock -= 2
        RETURN
    ENDIF
    if IsNegative
        ValueMod = -ValueMod
    Endif
    If AffectsGV
        If akTarget == Game.GetPlayer() || akTarget.IsPlayerTeammate()
            Kicked = true
            affectedGV.Mod(ValueMod)
        Endif
    else
        if StatToMod == "weaponSpeedMult" && akTarget.GetActorValue(StatToMod) == 0.0
            ValueMod += 1.0
        endif
        akTarget.ModActorValue(StatToMod, ValueMod)
    endif
    iLock -= 1
Endevent

Event onEffectFinish(Actor akTarget, Actor akCaster)
    iLock -= 1
    WHILE iLock > 0
    ENDWHILE
    IF iLock < 0
    ELSEIF Kicked
        affectedGV.Mod(-ValueMod)
    else
        akTarget.ModActorValue(StatToMod, -ValueMod)
    endif
Endevent

modgms_user  Offline  Сообщение №2602 написано: 28 декабря 2017, 15:34 | Отредактировано: modgms_user - 28 декабря 2017, 15:35



20
Multigone, еще с предыдущей версии этого скрипта в игре столкнулся с одной очень неприятной проблемой, фпс стал падать с установленного 50-60-ти до 37-ми. Начало происходит сразу после начала тестов с той версией, где был добавлен while. Последний предложенный вариант уже постабильней, долго не падало, после хождений в Ривервуд, прохожденийя одной пещеры и только в пещерах Хелгена, там, где при обычном старте проходишь обучение, фпс опять упал до 38ми, м самое интересное, там, где валялись 4ре трупа(игра начата через альтернативный старт), в пещере перед мостом, где еще потом пауки и спящий медведь в нормальном старте, а в альтернативном у моста завален вход.

Сделал сохранение с просевшим фпс и потом сравнил с нормальным сохранением в Save Cleaner'е. В активных появились два скрипта XTD_FortifyDecimal. Для проверки конкретной причины в нихудалил их из активных клинером и сохранил. После загрузки сохранения с удаленными из активных указанными скриптами фпс вернулся до 60ти. Потом еще раз проверил, в том же месте, загрузил ближайшее нормальное, дошел до места и словил падение фпс, сделал сохранение, посмотрел, опять наличие двух экземпляров этого скрипта в активных, удалил оттуда и загруженное то же сохранение, но без экземпляров скрипта в активных, грузится в 60фпс, когда с ними сразу 38.
Может на этом цикле while застревает?

Потом отредактировал этот скрипт, добавив туда несколько debug.trace'ов в разных частях, с переменными, но после нескольких повторных попыток загрузки последнего сохранения с нормальным фпс, без этого скрипта, пока поймать падение фпс не получилось, т.к., видимо, зачарования каждый раз генерируются разные и последний предложенный вариант постабильней предыдущего, падение может наблюдаться только с отдельными зачарованиями, не со всеми, с которыми грузится экземпляр этого скрипта.
Поставлю шансы выпадения маг. вещей очень высокими и попробую еще попробовать побегать половить падение. Вообще странно, что и на мертвых телах эффекты действуюи, загружаются скрипты, потом может полезно будет добавить к ним, в esp, что-то вроде условия isdead 0, чтобы работали только на живых.
Вот, какие данные можно получить в Save Cleaner'е об указанных активных скриптах, если это хоть что-то даст. Кинул ссылку на архив с текстовыми файлами, т.к. текста прилично, а у меня здесь что-то форматирование сообщения не работает, только на MG.( rgho.st/ private/8h2T7Vh9P/e8dbf8659152a8ea255a97962159b672 ), в ссылке убрать пробел перед private.

Multigone  Offline  Сообщение №2603 написано: 28 декабря 2017, 19:18



832
modgms_user, проверил на игроке, как при нулевой, так и при ненулевой продолжительности эффекта скрипт всегда завершает и старт, и финиш. Не знаю, в чем там у тебя дело.
Во втором скрипте допущен маленький косячок, который при нулевой продолжительности дает теоретический шанс, что финиш все-таки сработает раньше старта, но в этом случае GetMagnitude() вернет 0 и AV не будет затронуто. Ничего критичного.
В обоих случаях цикл крутится только тогда, пока выполняется код другого события. Чтобы цикл крутился вечно, нужно изначально допустить ошибку в логике - но при первом же тесте это будет выявлено.
Ладно, вот тебе версия, где цикл гарантированно завершается не более чем через 2 секунды после срабатывания события финиша.


Код
Bool Property IsNegative  Auto
Bool Property AffectsGV  Auto
GlobalVariable Property affectedGV  Auto  

Float ValueMod
Bool Kicked
Int iLock = 80

Event onEffectStart(Actor akTarget, Actor akCaster)
    Utility.Wait(0.2)
    IF iLock <= 40
        iLock -= 1000
        RETURN
    ENDIF
    ValueMod = GetMagnitude() / 100.0
    IF !ValueMod
        iLock -= 1000
        RETURN
    ENDIF
    if IsNegative
        ValueMod = -ValueMod
    Endif
    If AffectsGV
        If akTarget == Game.GetPlayer() || akTarget.IsPlayerTeammate()
            Kicked = true
            affectedGV.Mod(ValueMod)
        Endif
    else
        if StatToMod == "weaponSpeedMult" && akTarget.GetActorValue(StatToMod) == 0.0
            ValueMod += 1.0
        endif
        akTarget.ModActorValue(StatToMod, ValueMod)
    endif
    iLock -= 40
Endevent

Event onEffectFinish(Actor akTarget, Actor akCaster)
    iLock -= 40
    WHILE iLock > 0
        iLock -= 1
        Utility.Wait(0.05)
    ENDWHILE
    IF iLock < -500
    ELSEIF Kicked
        affectedGV.Mod(-ValueMod)
    else
        akTarget.ModActorValue(StatToMod, -ValueMod)
    endif
    iLock = 80
;    Debug.MessageBox("onEffectFinish")
Endevent

V Финиш ждет старт - безопасный возврат AV в исходное состояние.
V Выполнение GetMagnitude() предотвращается, если эффект имеет слишком малую продолжительность - не будет ошибки в лог.
V Цикл всегда завершается - гарантия возврата AV и отсутствия накапливающейся нагрузки.
V C осторожностью (и тестами) можно использовать в эффекте, имеющем условия в EffectItem.
X Возможная проблема с weaponSpeedMult остается - после снятия первого из 2х таких эффектов актер может быть замедлен.

modgms_user  Offline  Сообщение №2604 написано: 29 декабря 2017, 06:37 | Отредактировано: Multigone - 22 апреля 2020, 12:50



20
Цитата Multigone

проверил на игроке, как при нулевой, так и при ненулевой продолжительности эффекта скрипт всегда завершает и старт, и финиш. Не знаю, в чем там у тебя дело.


Так пока и не получилось повторить снова, нужные чары не сгенерировались, чтобы вызвать повторения бага с падением. Вот, только два сохранения, о которых писалось, в которых два экземпляра скриптов вызывали сильное падение фпс, с восстановлением после удаления их из активных.

Попробую третий вариант в следующем забеге по тому же маршруту в игре. В этом моде, на самом деле есть несколько скриптов, ошибки в которых происходят с тем же GetMagnitude(). Кроме xtd_fortifydecimal ошибки происходят так же в таких скриптах Worlds Dawn как: xtd_statpercentage, xtd_procself, xtd_alchattributepercent и XTD_ProcOnBlockBuff. В них, пока использованы простые проверки или пробую аналоги предлагаемых вариантов, возможно, после нахождения оптимального варианта, без ошибок в лог, непредвиденных изменений статов персонажей и падения фпс, можно будет уже использовать что-то конкретное. В может есть и другие проблемы и вопросы, многое по балансу и известно из отзывов пользователей оригинальной версии, но это пока второе.

Стоит ли в esp к некоторым эффектам добавить условие IsDead 0, чтобы избежать лишнего применения когда не надо или не требуется? Что-то уже пробую, вроде применения условия Health<100%, через esp, для эффекта ауры, восстановливающей здоровье союзных NPC. Насколько такие проверки избавят от проблем и повлияют на общую производительность? Если есть какая информация и особенности, будет полезно узнать.

А предложенный скрипт попробую обязательно, в следующей новой тестовой игре. Жаль, не вышло поймать падение с добавленной отладкой, из выводов только, что такое происходило только для определенных эффектах, на неписях и второй вариант предложенного скрипта стабильней первого, в этом плане.

Multigone  Offline  Сообщение №2605 написано: 29 декабря 2017, 12:30



832
modgms_user,

- эффект с архетипом ValueMod не накладывается на уже мертвых актеров.
- когда актер погибает, большинство эффектов снимаются, если в них не указано NoDeathDispel. С игрока - не всегда.
- если для заклинания, кастуемого игроком, по результатам проверок условий и сопротивлений на другую цель не будет наложено ни одного эффекта, в верхнем углу появится сообщение "такой-то избегает действия того-то".
- проверки в EffectItem происходят раз в секунду, в окне TargetConditions эффекта - один раз при наложении. Множество постоянных проверок нежелательно.
- если условие TargetConditions не дает эффекту наложиться, его скрипт не инициализируется, требование DispelEffectsWithTheseKeywords не выполняется.
- условие в EffectItem не препятствует наложению эффекта, просто "переключает" его состояние, при этом срабатывают события старта и финиша (соответствующее, скрипт продолжает работать); требование DispelEffectsWithTheseKeywords выполняется один раз в момент наложения; в отключенном состоянии он все еще определяется через HasMagicEffect().
- если заклинание имеет несколько эффектов, условия TargetConditions следующего эффекта начнут проверяться после того, как будут проверены у вышестоящего.
- для эффектов Concentration условия в TargetConditions и EffectItem играют обратную роль (с СК.сом / не проверял / возможно, это только кажется из-за продолжительности большинства их в 1 секунду).

Если что еще вспомню, напишу.

Ссылка 1.
Ссылка 2.

modgms_user  Offline  Сообщение №2606 написано: 6 января 2018, 12:28 | Отредактировано: modgms_user - 6 января 2018, 15:35



20
Multigone, спасибо, особенности полезно знать, чтобы не пихать куда попало чего не следует или наоборот.

Сделал один забег с измененным скриптом, пока чего-то такого не заметил, хоть и недолго бегал, только пара ошибок с этой же магнитудой, от пары других скриптов, но там тоже проверки стоят, когда в магнитуде ошибка. По измененному скрипту ничего в логе не показало. Падения фпс не было и вроде как, раз бесконечного цикла нет, не должно быть. А основном только другие недостатки высматривались. Подольше побегать, потом может быть можно будет применить и к другим нескольким перечисленным ранее скриптам примерно такие же проверки, чтобы предотвратить ошибку с магнитудой и не сломать ничего.

[b]Добавлено[/b] (06 Января 2018, 15:28)
---------------------------------------------
Приветствую в очередной раз. Делал тесты скрипта и мода в целом. В моде нашел один ломающий характеристики неписей баг, но о нем позже.

1)
По скрипту xtd_fortifydecimal. Смотрел в логе, сообщения об ошибках с GetMagnitude все равно встречаются. Появились кое какие размышления. В других скриптах этого мода, например в XTD_PlayerRecoveryScript, были также разные ошибки, например RegisterForSingleUpdate выдавал ошибку no native object bound... . В том случае я вставлял сразу перед RegisterForSingleUpdate проверку вроде "if target.HasMagicEffect(abRecovery.GetNthEffectMagicEffect(0))" и больше ошибок в логе не было. Так вот, относительно xtd_fortifydecimal, как магического эффекта и ранее обсуждавшейся ситуации возникновения ошибка с GetMagnitude из за снятия эффекта, несмотря на это, но выполнения строк скрипта до конца. Обдумывался вариант получить айди эффекта через что нибудь и засунуть его в строку "if target.HasMagicEffect(abRecovery.GetNthEffectMagicEffect(0))", вместо abRecovery. Но пока писал это сообщение подумал о более тупом способе и опробовал его на том тестовом заклинании и скрипте TEST777, что ранее предлагалось посмотреть.
Условие "if self as xtd_fortifydecimal" возвращает TRUE и равно "[xtd_fortifydecimal <Active Efffect ## on ########>]", когда есть еффект, или "[xtd_fortifydecimal <None>]", когда его нет.
Сначала подумал присвоить значение какой-нибудь переменной типа activemagiceffect, например ActEffect, но если ей присвоить значение через "ActEffect = self as xtd_fortifydecimal", в моем тестовом скрипте было "ActEffect = self as xtd_fortifydecimal", соответственно, в Debug.trace\messagebox такая переменная будет показывать значение None, но в моем тестовом скрипте условие "if ActEffect == None" все равно не выполнялось.
Тогда решил сделать проверку через строковую. Создал "string EffString", потом присвоил, в тестовом скрипте TEST777, значение через "EffString=(self as TEST777) as string" и после была проверка if EffString=="[TEST777 <None>]", которая уже прошла, ведь строковое значение self as TEST777 отличалось, в случаях когда эффект был или его не было. Можно и без переменной, полагаю, просто условием " if ((self as TEST777) as string) == "[TEST777 <None>]" ".
Может тупо так и предотвращать ошибку с следующим далее GetMagnitude? Может это можно прописать более быстрым вариантом? Кстати, какое условие легче и выполнится быстрее "if target.HasMagicEffect(SpellVisual.GetNthEffectMagicEffect(0))" или " if ((self as XTD_CombatBuffScript) as string) != "[XTD_CombatBuffScript <None>]" "?

2)
Касательно описанной большой проблемой мода. Ранее писал, что замечал, что с модом у неписей косячились показатели и в одной из тестовой игр, видимо, увидел причину, которая их портит. В моде Worlds Dawn, кроме зачарований, есть еще РПГ атрибуты(сила, выносливость, проворность, ловкость, интеллект, мудрость,харизма), которые имеют свои эффекты. На игроке они работают нормально, но они также могут оказывать эффект и на неписей, вроде спутников, т.к. присутствуют в зачарованиях на вещах, которые можно одеть на тех же компаньонов. Так вот, просматривая характеристики компаньона, случайно(или не очень), увидел, в каком случае характеристики запарывались. Первое, что увидел, чрезвычайно большой бонус от характеристик, аномально большой. Своему персу, для тестов, установил все указанные атрибуты в значение 100, чтобы видеть и насколько они несбалансированы, что надо уменьшать, и в случае чего, вот такие баги.
Посмотрел у спутника параметр AttackDamageMult, он был равен 1. Была шмотка с чарами +1 к силе, а сила здесь, одним из эффектов, имеет +к урону. Надел на спутника шмотку с +1 силы и увидел, что AttackDamageMult вырос с 1-го до 2.88, что, очевидно, мягко говоря перебор, соответственно, +10 к силе уже увеличивало до неприличных значений. Это при РПГ-атрибутах игрока в значении 100. Для сравнения, 100 силы у игрока увеличивает AttackDamageMult, если не ошибаюсь, до 1.77, но это 100 силы.
Проверил, там же в игре, понизил все атрибуты до 50-ти, сбросом через Debug мода и +1 силы стало давать компаньону, в общем-то, в два раза меньший бонус к AttackDamageMult.
Далее выяснил, тогда же, когда сбросил атрибуты, в какой ситуации AttackDamageMult остался измененным навечно. Когда менял атрибуты, то шмотка была надета на персонаже и когда шмотка была снята, эффект уменьшился только на величину, которая была при значении РПГ-атрибутов 50, и AttackDamageMult уже не вернулся в значение 1.
Примерно понял, что эти дела присваиваются в скрипте xtd_alchattributepercent, сначала попробовал немного поменять в указанном скрипте код, в надежде исправить баг, не прокатило, в результате, не нашел ничего лучше, чем просто отрубить процесс для неписей, оставив только для игрока, после чего неписи перестали получать бонусы от плюсов к рпг-атрибутам на надетых на них шмотках, но и дикие прибавки и навечно измененные значения характеристик также исчезли, вроде onlyplayer рпг-атрибуты.
Вообще, начал проверять эти дела после того, как увидел, что у спутника скорость в игре стала 1500 вместо ста и из шмоток была только та, что добавляла бонус от рпг-атрибута, притом значение росло по ходу игры, видимо, когда распределял атрибуты и в других ситуация, вроде переодевания шмоток и т.п. Думал, может чего накосячил в скриптах и вернул оригинальные, начал новую игру и проверил снова, с оригинальными были такие же дела. В XTD_AlchAttributePercent тоже есть вначале GetMagnitude в начале, тоже кидал те же ошибки иногда.

Может в XTD_AlchAttributePercent там какая ошибка есть в коде? Так-то код небольшой, и из XTD_AlchAttributePercent используется также и xtd_attributes, в котором задаются величины эффектов от рпг-атрибутов, баланс велечины которых - отдельный вопрос. Код скрипта могу кинуть, если надо, но вроде, как понял с xtd_fortifydecimal, исходники есть.
Не знаю, какой именно атрибут игрока влиял на бонус от силы для непися, не проверил еще, полагаю та же сила 100, бонус которой каким-то образом перемножается с бонусом силы для непися.
Может и onlyplayer атрибуты, с отключением кода для неписей, даже и лучше, но как бы это не повлияло на работоспособность многих эффектов от других зачарований. Видимо надо кучу debug-строк вставлять в скрипт и смотреть, что там чему равно.

Multigone  Offline  Сообщение №2607 написано: 6 января 2018, 19:21



832
modgms_user, проверил, возможно, (Self AS String != "[ScriptName ]") и даст необходимую проверку, наложен ли этот эффект на актера.

Код
if target.HasMagicEffect()
Не рекомендую, т.к. активных копий одного эффекта может быть больше 1 (некоторые архетипы позволяют это, напр., ValueMod). Один конкретный эффект уже исчез, но на актере есть и другие - проверка в скрипте первого будет положительна.


2)
Честно говоря, не хочется быть тестером.

modgms_user  Offline  Сообщение №2608 написано: 7 января 2018, 19:40 | Отредактировано: Multigone - 22 апреля 2020, 12:54



20
Есть вопрос о скорости и затрачиваемых действиях в примере далее. Есть, допустим скрипт, в нем я, допустим, добавил необходимую проверку "IF self as string != "[Scriptname ]" ". Эта проверка по скрипту повторяется раз 5-10. Будет ли иметь смысл, с точки зрения скорости и затрачиваемого папирусом времени, добавить в начале определение переменной, например "string MEffectNone = "[Scriptname ] " и далее по скрипту заменить все проверки на "IF self as string != MEffectNone"? Будет ли это быстрее хоть как-то? Скомпилированный скрипт становится чуть меньше после таких изменений и часто это имеет смысл, особенно, когда много действий при присвоении выполняется, но здесь как-то не настолько прямо всё ясно.

Dsion  Offline  Сообщение №2609 написано: 8 января 2018, 16:40 | Отредактировано: Dsion - 8 января 2018, 16:42



Multigone, чо-то у тебя реально странные представления о программах... Попрограммировал бы на Си...

modgms_user, возьми да замерь время выполнения первого и второго вариантов... В Utility Script, вроде, есть функция получения точного реального времени. Если будет слишком быстро выполняться, можно повторить тест 1000 раз или сколько нужно. Вряд ли у нас на форуме есть кто-то, кто понимает компилятор папируса достаточно хорошо, чтоб сразу сказать, какой вариант быстрее.

Multigone  Offline  Сообщение №2610 написано: 8 января 2018, 19:57



832
modgms_user, действительно, это быстрее. В среднем на 0.0000003 с (на моем компе). Но переменная, в отличие от литерала, занимает какую-то память, пока живет в отрезке кода, где была объявлена.
Тогда предположу, что создание переменной и литерала - это примерно одно и то же, на это затрачивается опред. время.

Как я и сказал, в твоем случае это не имеет значения.

Форум » TES V: Skyrim » Мастерская » Вопросы по скриптам Papyrus (О скриптах Papyrus (Skyrim). Скриптеры не проходите мимо!)
Поиск:





Ответ на жалобу смотрите в разделе жалоб