Модератор форума: 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  Сообщение №3121 написано: 22 сентября 2022, 16:55



832
Lissenok, если от первого скрипта сообщение выводилось, значит, обновляется в обход флага. Референсы ванильных манекенов ни с чем не связаны, кроме XMarkerHeading (которые в свою очередь связаны только с конкретными манекенами и не имеют скриптов). Поэтому, маловероятно, что какой-то внешний скрипт может управлять обновлением манекена напрямую (для этого FormID манекена должно вызываться из скрипта, минуя Property). Сложно сказать.

Цитата Lissenok

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

Значит, в папке Scripts / Source отсутствуют исходники импортируемых скриптов (Debug, Utility).

Ладно, проблема решилась, и хорошо.


valambar  Offline  Сообщение №3122 написано: 25 сентября 2022, 19:55



513
Думаю о такой вот идее - у игрока есть изученные заклинания, у компаньона тоже есть изученные заклинания. 

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

Например, строки диалога выглядели бы так:

"У меня есть заклинания, которых ты еще не знаешь. Изучи их".

"Есть ли у тебя заклинания, которым я могу научиться?".

И в каждой строке срабатывал бы соответствующий скрипт.


Multigone  Offline  Сообщение №3123 написано: 26 сентября 2022, 15:24 | Отредактировано: Multigone - 26 сентября 2022, 17:58



832
Цитата valambar

"У меня есть заклинания, которых ты еще не знаешь. Изучи их".

Actor xP = Game.GetPlayer() ; объявляется переменная с типом актер, ее значение становится равным "игрок"
Spell xS ; объявляется пустая переменная с типом заклинание
Int iC = xP.GetSpellCount() ; объявляется переменная Int (целые числа) = кол-во заклинаний у актера xP (т.е. игрока)
WHILE iC > 0 ; пока iC > 0, выполняется цикл
    iC -= 1 ; уменьшаем значение iC на 1
    xS = xP.GetNthSpell(iC) ; переменная xS = заклинание в списке актера xP за номером iC, т.е. с конца в начало, первый элемент списка имеет номер 0.
    IF !akSpeaker.HasSpell(xS) ; если akSpeaker (актер, участвующий в диалоге с игроком, особая перем. топиков) не имеет заклинания xS
        akSpeaker.AddSpell(xS) ; то добавляем ему заклинание xS
    ENDIF
ENDWHILE ; конец цикла, возврат в начало цикла для проверки условия
; конец кода, удаление объявленных перем. из памяти игры.
Цитата valambar

"Есть ли у тебя заклинания, которым я могу научиться?".

Actor xP = Game.GetPlayer() ; то же самое, только игрок и akSpeaker меняются местами
Spell xS
Int iC = akSpeaker.GetSpellCount()
WHILE iC > 0
    iC -= 1
    xS = akSpeaker.GetNthSpell(iC)
    IF !xP.HasSpell(xS)
        xP.AddSpell(xS)
    ENDIF
ENDWHILE
Требуется SKSE. Строки диалога будут показываться всегда, даже в случае, если игрок и актер синхронизировали заклинания. Потому, что простого условия, которое можно было бы использовать в Conditions, и которое проверяло бы соответствие заклинаний, конечно же, не существует. Для этого надо заранее проверять синхронность заклинаний и управлять определенной глобальной, а в Conditions диалога уже проверять ее значение. Как компилировать фрагменты.

nepewka  Offline  Сообщение №3124 написано: 27 октября 2022, 09:06



255
Всем привет! Давненько сюда не заходил из-за нехватки времени, сейчас появилось несколько выходных - появилось желание поиграть в фолач. Напомню, что в прошлый раз общались на тему скрипта, который поможет осуществить правильную работу резистов. (опять же напомню, что в 4 фоле криво работает одна из формул подсчёта энерго урона, хочу это исправить костылями )
 Был бы очень любезен, если бы Вы направили на нужный путь и помогли в написании)

Цель: 
Нужно чтобы висела ability на игроке и на окружающих игрока актёрах (с которыми игрок может вступить в бой) (эта ability будет отвечать за работу резиста)

По идее нужно 2 или 3 скрипта как я понимаю.

1 скрипт будет на игроке, выдавать ability окружающим игрока актёрам. При переходе в локу или как это правильно осуществить, кто-нибудь знает ?

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

3 скрипт ещё раз на игроке, тоже будет отвечать за просчёт нового резиста. (вот этот наоборот должен повторяться и обновляться, так как значение резиста для игрока динамически должно меняться по ходу игры)

Заранее спасибо за помощь!

Multigone  Offline  Сообщение №3125 написано: 28 октября 2022, 11:59 | Отредактировано: Multigone - 28 октября 2022, 12:01



832
nepewka, ты ж это еще в апреле спрашивал. Аbility раздаются актерам через плащ с большим радиусом, или через заклинание с большим радиусом (площадью), периодически (3 сек) кастуемое от игрока. На конечном эффекте плаща / эффекте заклинания скрипт "добавить указанное ability цели этого эффекта". Аbility отвечает за пересчет сопротивления актеров (каким именно образом, тебе виднее, поскольку мы сейчас говорим о Ф4, а не о Скайриме).
Сопротивление игрока пересчитывается в апдейте скрипта отдельного ability, выданного игроку в референс-алиасе (каким именно образом, не в курсе, т.к. это Ф4). Об особенностях апдейтов Ф4 мы уже говорили выше.
Сам плащ выдается игроку так же в референс-алиасе. Заклинание с площадью кастуется из второго апдейта в том же скрипте, в котором производится пересчет сопротивления игрока.

nepewka  Offline  Сообщение №3126 написано: 5 ноября 2022, 08:05 | Отредактировано: Multigone - 6 ноября 2022, 14:01



255
Multigone, привет! подскажи, пожалуйста, как правильно дописать данный скрипт для Fallout 4, накидал очень криво, но мысль такова: задача ванильного кода 42 раза пополнить здоровье нпц, когда оно опускается в 0 или меньше, а для меня важно, чтобы это разбивалось на две проверки по 21 разу, для первой используется int Pika, для второй int Pika2).

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

Scriptname AAAdefa extends Actor

int Pika
int Pika2
float DAM
ActorValue property Health auto

event onload()
    DAM = 10000.0
    StartDeferredKill()
    StartTimer(0.001)
Endevent

Event OnTimer(int aiTimerID)
    Debug.Notification( "Working" )
    if GetValue(Health) <= 0
        if Pika >= 21
            Pika2 += 1
        else
            Pika += 1
            RestoreValue(Health, DAM)
        endif
        if Pika2 >= 21
            EndDeferredKill()
            CancelTimer()
            return
        else
            Pika2 += 1
            RestoreValue(Health, DAM)
        endif
    endif
    StartTimer(0.001)
endEvent

Multigone  Offline  Сообщение №3127 написано: 5 ноября 2022, 12:13 | Отредактировано: Multigone - 6 ноября 2022, 11:46



832
nepewka,

Scriptname AAAdefa extends Actor

int Pika = 21
int Pika2 = 21
ActorValue property Health auto

event onload()
    StartDeferredKill()
    StartTimer(0.1)
Endevent

Event OnTimer(int aiTimerID)
    if GetValue(Health) > 0.0
        StartTimer(0.1)
    else
        Debug.Notification( "Restore health..." )
        if Pika > 0
            RestoreValue(Health, 10000.0)
            Pika -= 1
            StartTimer(0.1)
        elseif Pika2 > 0
            RestoreValue(Health, 10000.0)
            Pika2 -= 1
            StartTimer(0.1)
        else
            Debug.Notification( "...then ending!" )
            EndDeferredKill()
        endif
    endif
endEvent


Не надо делать апдейты с периодом 0.001 с, это мало того, что бессмысленно, так еще и очень вредно.

1)Все твои скриптовые ресурсы будут заняты ненужной проверкой здоровья актера.
2) Даже простая проверка здоровья вряд ли сможет выполняться с такой высокой частотой (1000 Гц).

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


Multigone  Offline  Сообщение №3128 написано: 5 ноября 2022, 22:35 | Отредактировано: Multigone - 6 ноября 2022, 16:15



832
nepewka, алгоритм написания кода:

1) ставишь базовую простую задачу (основу);
2) реализуешь ее в коде, проверяешь его работу;
3) если все хорошо, расширяешь в коде функционал и возможности основы, учитываешь разнообразные условия ее работы, если это необходимо.

Так вот, если основа работает хорошо и нет необходимости что-то менять, то менять / добавлять ничего и не нужно.

По поводу реализации большого числа проверок. Там вообще ничего сложного, нужно просто знать, что...
* порядок выполнения кода всегда сверху вниз (исключая цикл WHILE) до конца события или функции;
* порядок операторов IF ELSEIF ELSE ENDIF должен соблюдаться (назовем это элементарным блоком проверок);
* в промежутки ПОД каждым оператором можно поставить любое количество действий или других блоков проверок, или не ставить ничего;
* оператор ELSEIF может повторяться несколько раз подряд (IF ELSEIF ELSEIF ELSEIF ELSE ENDIF). Он служит для проверки условия, если проверка вышестоящего оператора этого блока не выполнена (под проверкой подразумевается одно или несколько условий, связанных логическими операторами && или ||);
* в блоке проверок, первая выполненная проверка допускает к действиям, написанным под этим оператором. Остальные проверки этого блока не осуществляются вообще;
* оператор ELSE автоматически выполняется, если ни один вышестоящий оператор не прошел свою проверку.
* После того, как все действия внутри "успешного" оператора завершены, машина переходит к действиям, следующим за ENDIF этого блока проверок.
* операторы ELSEIF ELSE необязательны, и их можно не включать в блок;
* если несколько условий связаны логическим И (&&), то проверка проводится до первого НЕВЫПОЛНЕННОГО условия, а остальные не проверяются вовсе. Например, А > 0 && В() < 3, где В() - функция, результатом своей работы возвращающая число; и если А не выполняется, то функция В() запускаться НЕ БУДЕТ, чтобы сравнить свой результат < 3;
* если несколько условий связаны логическим ИЛИ (||), то проверка проводится до первого ВЫПОЛНЕННОГО условия, а остальные не проверяются вовсе (в вышеописанной манере);
* из этого следует, что в цепочке условий && на первое место выгодно ставить условия, вероятнее всего НЕ выполняющиеся, и для || - условия, выполняющиеся вероятнее всего. Кроме случаев, когда требуется гарантированно выполнить некоторую возвращаемую функцию прямо в условии, тогда ее ставят на первое место.


В целом, в скриптописании нужно обращать внимание на следующие вещи:
- функции и переменные могут изменять работу нижестоящих условий, событий и функций;
- функции могут заставлять срабатывать события (например, экипирование предмета в тот же момент вызовет событие "OnObjectEquipped");
- независимые события могут срабатывать одновременно, и, если в них используется одна и та же переменная, менять ее значение непредсказуемо (требуется делать защиту). То же самое может происходить, если одно и то же событие (например, событие при нажатии игроком опред. клавиши) выполняет свой код медленнее, чем оно вызывается (игрок может очень быстро нажимать клавишу);
- перед проверкой в игре нужно умозрительно проверять срабатывание блоков условий, подставляя стартовые значения переменных, 0, 1 (в зависимости от конкретного назначения переменной);
- нужно пользоваться messagebox для отладки блоков условий (и вообще, чтобы понимать, как работает код);
- и, самое главное, нужно курить базисы википедии, понимать взаимодействие скриптов, магических эффектов и прочих форм, проводить внутриигровые опыты, тестировать код при различных игровых условиях, и вообще, проводить за этим делом много времени (я не думаю, что это лучшее времяпрепровождение).

nepewka  Offline  Сообщение №3129 написано: 6 ноября 2022, 10:32



255
Цитата Multigone

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


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

Hommyak  Offline  Сообщение №3130 написано: 13 ноября 2022, 14:47 | Отредактировано: Hommyak - 14 ноября 2022, 14:37



13
Приветствую.

В скриптах ничего особо не смыслю. Прошу о помощи, потому что искал сам, но так и не смог найти.

Необходим скрипт, при котором, пример:

есть призрак (не важна раса) и он в начале - Отключенный (Enable)

в 21.00 он становится Enable и, соответственно - видимым (шляется по пакетным задачам и т.д.)

в 6.00 утра - он опять Disable

и так - бесконечно.

То есть призрак именно отключается и включается по расписанию, а не просто зависает и становится невидимым, в него все втыкаются и т.д.

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

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

Заранее благодарю за помощь. Пардон, если такой вопрос уже рассматривали, 105 страниц не простой объем smile


Multigone  Offline  Сообщение №3131 написано: 13 ноября 2022, 17:21 | Отредактировано: Multigone - 14 ноября 2022, 20:26



832
Hommyak,

ScriptName _TESTNEW2 Extends Actor ; на актера, нужен тест

ObjectReference Property pxMarkerRef Auto ; референс любого объекта (напр., хМаркера), в какой точке актер должен быть включен. Не заполнять, если это не требуется.

Float fEnableTime = 21.0 ; время включения, часов, (fEnableTime + fRandomDispersion) < 24.0, (fEnableTime - fRandomDispersion) > (fDisableTime + fRandomDispersion)
Float fDisableTime = 6.0 ; время отключения, часов, (fDisableTime - fRandomDispersion) >= 0.0
Float fRandomDispersion = 1.0 ; случайное отклонение (в плюс или минус) от времени включения или отключения, часов, fRandomDispersion >= 0.0

Float fR ; вспомогат.

EVENT OnInit()
    RegisterForSingleUpdate(3.0)
ENDEVENT

EVENT OnUpdate()
    IF GetParentCell().IsAttached()
        CheckActorState()
    ENDIF
ENDEVENT

EVENT OnUpdateGameTime()
    CheckActorState()
ENDEVENT

EVENT OnCellAttach()
    CheckActorState()
ENDEVENT

EVENT OnCellDetach()
    UnregisterForUpdateGameTime()
ENDEVENT

FUNCTION CheckActorState()
    IF GetState() == "Work"
        RETURN
    ENDIF
    GoToState("Work")
    Float fT = Utility.GetCurrentGameTime()
    fT = (fT - fT AS Int) * 24.0
    IF fT >= fEnableTime + fR || fT < fDisableTime + fR
        IF IsDisabled()
            IF pxMarkerRef
                MoveTo(pxMarkerRef)
            ENDIF
            EnableNoWait(true)
            EvaluatePackage()
            fR = fRandomDispersion * (Utility.RandomFloat(0.0, 2.0) - 1.0)
        ENDIF
        RegisterForSingleUpdateGameTime(fMod(fDisableTime + fR - fT, 24.0) + 0.01)
    ELSE
        IF !IsDisabled()
            DisableNoWait(true)
            fR = fRandomDispersion * (Utility.RandomFloat(0.0, 2.0) - 1.0)
        ENDIF
        RegisterForSingleUpdateGameTime(fEnableTime + fR + 0.01 - fT)
    ENDIF
    GoToState("")
ENDFUNCTION

Float FUNCTION fMod(Float A, Float B = 1.0) Global ; Модуло.
    IF B
        RETURN A - Math.Floor(A / B) * B
    ENDIF
ENDFUNCTION

STATE Work
ENDSTATE

Hommyak  Offline  Сообщение №3132 написано: 14 ноября 2022, 15:10 | Отредактировано: Hommyak - 14 ноября 2022, 16:47



13
Приветствую.

Большущее спасибо!

Для меня это конечно монстр, но вроде все скомпилировалось. 

Единственное, в property скрипта (где Add Property) не получается никак сделать Auto, он пишет либо Default, либо (если все же попытаться добавить) выдает окно назначения Reference. Так и должно быть?

Еще раз пардон, для меня это очень темная тема....


Hommyak  Offline  Сообщение №3133 написано: 14 ноября 2022, 16:47 | Отредактировано: Hommyak - 14 ноября 2022, 16:50



13

Приветствую.

Я испытал скрипт. С первой загрузки - призрака нет, место пустое, никаких коллизий и т.д.

С 21-го до 22, он появляется. Правда он не исчезает обычным способом.

Примерно с 6-7-ми часов, он прыгает наполовину в землю и в таком виде пребывает все время. Если подождать время через "Т", то на улице ( в открытом мире) это не сработает и призрак так и остается на своем месте, я даже уходил из поля зрения моего и его, но он так и не исчезал.

НО, если зайти в дом и тут же выйти, то приходишь уже на пустое место. При этом, если подождать внутри (даже час) - на подходе к месту дислокации призрака - он будет, но почти сразу же исчезнет (когда просто "зашел - вышел" - его уже нет). Даже если простоять перед ним весь день после 6-ти утра и уйти в дом ждать часов в 20.00, то на выходе - он исчезнет перед тобой и может через 15 минут появиться опять.

Переход в другие ячейки не сподвигает призрака к исчезновению (к примеру призрак в Riverwood 2, я уходил и ждал в Riverwood).

Воот....


Multigone  Offline  Сообщение №3134 написано: 14 ноября 2022, 17:05



832
Hommyak, если переменная скрипта имеет такое же название и тип, как и EditorID формы, существующей в этом плагине или связанных с ним мастер-файлах, то она может автозаполняться кнопкой Auto-fill. В противном случае, нужно указывать ее значение вручную.

Попробуй сейчас, я подредактировал.

Multigone  Offline  Сообщение №3135 написано: 14 ноября 2022, 17:20 | Отредактировано: Multigone - 14 ноября 2022, 17:20



832
Hommyak, протестируй на упрощенном скрипте:

ScriptName _TESTNEW3 Extends Actor

ObjectReference Property pxMarkerRef Auto

EVENT OnInit()
    RegisterForSingleUpdate(5.0)
ENDEVENT

EVENT OnUpdate()
    Debug.MessageBox("+")
    IF IsDisabled()
        IF pxMarkerRef
            MoveTo(pxMarkerRef)
        ENDIF
        EnableNoWait(true)
        EvaluatePackage()
    ELSEIF !IsDisabled()
        DisableNoWait(true)
    ENDIF
    RegisterForSingleUpdate(10.0)
ENDEVENT

Hommyak  Offline  Сообщение №3136 написано: 14 ноября 2022, 17:38 | Отредактировано: Multigone - 14 ноября 2022, 19:32



13
Каждые десять секунд, не больше, стало вылазить окошко в котором один "+" написан, надо нажать "ОК" и через пару сек призрак появляется. Потом 10 сек и снова окно, снова "ОК" и он исчезает и так бесконечноsmile


Большой скрипт исправлял, призрак действует точно так же как писал выше ( с исчезновением через заход в дом). 

С большим есть еще одна забавность - defaultGhostScript не работает и эффект призрака только через Spellist ложится. Мне не принципиально, но может это тоже что-то говорит...


Multigone  Offline  Сообщение №3137 написано: 14 ноября 2022, 20:52 | Отредактировано: Multigone - 14 ноября 2022, 20:53



832
Hommyak, там была, кхм, проблемка с перепутанными переменными, из-за чего апдейт работал непрерывно (а должен 2 раза в сутки). Сейчас вроде все на месте, по крайней мере, актер исчезает и появляется, когда нужно. Можно поставить ему флаг "изначально отключен", чтобы при первом посещении локации, если он сразу виден после ее загрузки, он не мозолил глаза 3 секунды перед тем, как исчезнуть (днем).
В скрипт можно добавить эффектов / шейдеров исчезновения (это уже по вкусу).
Если актер сам уходит в др. локацию, то могут наблюдаться проблемы. Чтобы исключить это, эвенты должны выглядеть так (вместо 5 будет 2):
EVENT OnInit()
    RegisterForSingleUpdateGameTime(0.1)
ENDEVENT

EVENT OnUpdateGameTime()
    CheckActorState()
ENDEVENT
Скрипт будет потенциально стабильней, но апдейт 2 раза в сутки будет выполняться независимо от того, где находится игрок (а в первом случае апдейт отключается, пока ячейка, в которой находится актер, отсоединена).

Hommyak  Offline  Сообщение №3138 написано: 15 ноября 2022, 20:35 | Отредактировано: Hommyak - 15 ноября 2022, 20:40



13
Цитата Multigone


Скрипт будет потенциально стабильней, но апдейт 2 раза в сутки будет выполняться независимо от того, где находится игрок (а в первом случае апдейт отключается, пока ячейка, в которой находится актер, отсоединена).


Сработало! Большущее спасибо за помощь!

Этот скрипт будет работать только на НПС (Actor), так ведь? Тот же код нельзя применять к объекту (Object) или к FX анимации (заменив в коде Actor на Object? 

А это будет считаться "тяжелым скриптом" или чем-то нагружающим систему? 

Будут ли, предположительно, возникать конфликты с модами или квестами использующими События (Event)? Или это сугубо на одного (одних) НПС распространяемое действо и на весь остальной мир влиять не будет?

Пардон, если вопросы немного не корректные, я правда не особо пока что в скриптах понимаю....smile


Multigone  Offline  Сообщение №3139 написано: 16 ноября 2022, 11:16



832
Hommyak, если закомментировать одну строку вот так (без кавычек) " ; EvaluatePackage()", поскольку эта команда применяется только к актерам, то скрипт можно вешать на большинство форм, которые могут иметь ObjectReference (т.е. формы, которые могут быть представлены в игре в виде предметов или др. объектов). Для этого шапка скрипта должна расширять референсы "Extends ObjectReference".

Сам скрипт не представляет из себя какой-либо ценности, кроме формулы расчета времени, через которое требуется производить новый апдейт. Поскольку апдейт производится 2 раза в сутки (72 минуты реального времени при ванильном TimeScale = 20), и объем расчетов там ничтожный, считаться тяжелым скриптом, нагружающим систему, он никак не может. Для примера, относительно тяжелым скриптом может считаться объем мат. расчетов размером со страницу текста, выполняемый с апдейтом 0.15 с.

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

События являются неотъемлемой частью любого скрипта (по крайней мере, большинства из них). Конфликты могут возникать тогда, когда работа одного скрипта не предполагает вмешательства в процесс со стороны других скриптов, оперирующих тем же объектом.


Hommyak  Offline  Сообщение №3140 написано: 17 ноября 2022, 17:25



13
Multigone, Благодарю за такой подробное объяснение и помощь. 

Творческих успехов Вам!


Hommyak  Offline  Сообщение №3141 написано: 21 ноября 2022, 16:47 | Отредактировано: Hommyak - 21 ноября 2022, 18:16



13
Приветствую.

Помогите пожалуйста разобраться.

В Синем дворце на лестнице перед троном есть триггер. На нем находится вот такой скрипт:

---------------------------------

auto STATE waitingForPlayer

 EVENT onTriggerEnter(objectReference triggerRef)

  if triggerRef == getPlayer()

   if MS05.GetCurrentStageID() < MS05DoNotStartRangeLowestStage || MS05.GetCurrentStageID() > MS05DoNotStartRangeHighestStage ;if outside of range

    if !MQ302.isrunning()

     GetOwningQuest().setStage(MS06StageToSet)

     gotoState("hasBeenTriggered") ;trigger will only trigger once

    endif

   endif

  endif

 endEVENT

endSTATE

STATE hasBeenTriggered

 ; this is an empty state.

endSTATE

-----------------------

Я немного запутался как он работает. Из написанного я понял, что триггер - в режиме ожидания, пока игрок в него не зайдет. Зайдя в него, игрок может активировать квест MS06, если квест MS05 находится на стадии не ниже и не выше указанных параметров (MS05.GetCurrentStageID()). И если при всем этом уже запущен квест MQ302 уже активирован, то установить квест MS06 в такую-то стадию. 

MS06 - "Пробуждение Королевы-Волчицы"

MS05 - "Поджигай!" (песнь о короле Олафе)

MQ302 - "Бесконечная пора", где нужно поговорить с Тулием, Ульфриком и прочими и заключить перемирие.

Я понимаю, что если эта "ген-линия" уже запущена, то нужно как-то по особому квест 06 запустить, но я не понял в чем выражается эта "особенность".....


Не могли бы Вы объяснить, все ли я так прочитал в скрипте?

Стоит ли использовать такие скрипты с подобными "кросс-ссылками", вместо установления условий Condition внутри других квестов?

Правильно ли я понял (по тексту скрипта), что данный триггер не вписывается в квест Алиасом, а стоит индивидуально, аля "глобальный игрок", а не "внутриквестовый"?

Как правильно понять на какую стадию квеста тут идет ссылка? Или тут говорится что стадии квеста вооообще включаться могут - GetOwningQuest().setStage(MS06StageToSet)

Является ли эта строка "универсалом" для триггеров (активирующих квесты или их стадии), чтобы после активации "выключать или удалять" эти триггеры -  gotoState("hasBeenTriggered")? Если в скрипте триггера (OnTriggerEnter) нет подобных функций, он будет работать один раз или постоянно один и тот же квест запускать?

Еще, на одном иностранном форуме, видел утверждение, что триггеры, которые при заходе в них активируют квесты или их стадии, нужно привязывать к предметам. Это нужно всегда или это только в определенных случаях?


Заранее благодарю за помощь!

Пардон за, скорее всего, не во всем корректные вопросы. smile


Multigone  Offline  Сообщение №3142 написано: 21 ноября 2022, 21:30 | Отредактировано: Multigone - 21 ноября 2022, 23:10



832
Hommyak, не совсем так. Есть квест MS06Start, в нем алиас MS06StartIntroTriggerAlias, заполненный референсом (RefEditorID) MS06StartIntroTrigger. Это референс триггера, находящегося в ячейке SolitudeBluePalace. Вышеуказанный скрипт MS06AliasSetStageTriggerPlus прикреплен к этому реф. алиасу, а не к триггеру (фактически, он не имеет прямого отношения к конкретно этому референсу, вместо этого он имеет прямое отношение к любому референсу, которым будет заполнен данный алиас).

Исходя из описания скрипта, "MS06 specific Alias based Setstage Trigger that that verifies MS05 is not in a range of stages before setting the stage", т.е. "триггер проверяет, что текущая стадия др. квеста (MS05) не находится в заданном диапазоне, перед тем, как перевести стадию владеющего этим алиасом квеста (GetOwningQuest()) на заданную.

; это копия скрипта, связанная с работой алиаса MS06StartIntroTriggerAlias (расширяет эту конкретную форму, т.е. этот скрипт является не только алиасом MS06StartIntroTriggerAlias, но и чем-то большим, чем именно, определяется кодом скрипта). Если этот же скрипт прикрепить к др. алиасам, у них будут свои копии этого скрипта, никак друг с другом не связанные, и работающие параллельно и независимо. Все переменные, объявляемые внутри скрипта, принадлежат только конкретной копии скрипта, и в разных копиях одного и того же скрипта они могут иметь разные значения, обусловленные работой их кода.
; сам алиас (любой) является ссылкой на референс (или форму), с возможностью производить над ней определенные действия

; вверху объявляются переменные, им присваивается какое-то начальное значение прямо в тексте скрипта, либо в его Property.
; если значение не присвоено, то начальным значением автоматически является 0, 0.0, "", none (для Int, float, string и любых других форм соответственно).
; Для алиаса MS06StartIntroTriggerAlias, переменные заполнены в Property скрипта (ПКМ на прикрепленном скрипте), и их можно там же посмотреть и изменить.

auto STATE waitingForPlayer
; состояние скрипта с произвольным именем состояния "waitingForPlayer", в котором может быть выполнено событие onTriggerEnter
; auto означает, что в это состояние скрипт переводится сразу при инициализации
    EVENT onTriggerEnter(objectReference triggerRef)
; событие, срабатывающее в момент попадания любого objectReference внутрь триггера (если какой-либо скрипт с этим событием прикреплен к референсу триггера или к алиасу, заполненному референсом триггера), с присвоением референсу попавшего в него объекта (для целей данного события) произвольного имени triggerRef
; иными словами, при срабатывании onTriggerEnter создается переменная с типом objectReference и именем triggerRef, которой присваивается значение попавшего в триггер референса. Эта переменная существует до конца кода этого события. В процессе выполнения кода этого события ей можно, как и любой другой переменной, присвоить новое значение (если требуется). Если одновременно срабатывают несколько копий события (см. ниже), то для каждой копии создается своя копия переменной triggerRef.
; пока скрипт остается в состоянии waitingForPlayer, для КАЖДОГО попавшего в триггер референса срабатывает это событие, независимо от того, сколько одновременно референсов попало в триггер, попали ли они одновременно или каждый в свое время, выполнился ли код предыдущей копии события, или нет.
; если скрипт прикреплен не к триггеру, то это событие не будет срабатывать никогда.
        if triggerRef == getPlayer() ; если попавший объект == референс, возвращаемый функцией game.getPlayer() (т.е. игрок)
; поскольку скриптовый объект game импортирован в данный скрипт (вверху есть import game), то вместо game.getPlayer() можно писать getPlayer()
; импортирование означает, что все функции импортируемого скриптового объекта находятся как будто бы в этом скрипте, и к ним не нужно обращаться, каждый раз указывая название этого скриптового объекта перед названием функции.
; если условие не выполняется (попавший референс не является игроком), то ничего не произойдет (за endif код завершается), скрипт останется в этом состоянии
            if MS05.GetCurrentStageID() < MS05DoNotStartRangeLowestStage || MS05.GetCurrentStageID() > MS05DoNotStartRangeHighestStage
; MS05 - переменная этого скрипта с типом квест и произвольным именем MS05 (имя совпадает с EditorID квеста MS05, чтобы было удобно заполнять Property кнопкой Авто).
; если (значение MS05, т.е. указанный в Property квест).(имеет № текущей стадии) < значения переменной с произвольным именем MS05DoNotStartRangeLowestStage (типа int, т.е. целочисленная, ее начальное значение указано в Property этого скрипта), или квест MS05 имеет № текущей стадии > значения MS05DoNotStartRangeHighestStage
                if !MQ302.isrunning()
; если квест MQ302 не запущен (! означает логическое "не")
                    GetOwningQuest().setStage(MS06StageToSet)
; функция GetOwningQuest(), примененная к конкретному алиасу, возвращает ID квеста, владеющего тем алиасом
; функция применена к Self, что означает, что функция применяется к ЭТОЙ копии скрипта, т.е. к алиасу MS06StartIntroTriggerAlias
; результат Self.GetOwningQuest() возвращает ID квеста, владеющего данным алиасом
; Self в большинстве случаев можно не писать
; вместо Self можно, к примеру, указать другой алиас (т.е. заполненную переменную с типом Alias или ReferenceAlias), чтобы узнать ID владеющего им квеста
; перевести квест, ID которого возвращает функция GetOwningQuest, в стадию с №, равным значению переменной MS06StageToSet (заполняется в Property этого скрипта)
                    gotoState("hasBeenTriggered")
; перевести скрипт в состояние с произвольным именем hasBeenTriggered
                endif
            endif
        endif
    endEVENT
endSTATE

STATE hasBeenTriggered
; состояние, в котором нет событий и функций
; состояния являются взаимоисключающими
; состояния определяют, код каких именно "версий" одних и тех же событий и фунций (или наборов событий и функций) будет выполняться при их вызове (к примеру, onTriggerEnter)
; в данном случае при вызове onTriggerEnter не произойдет ничего (событие отсутствует в наборе этого состояния), несмотря на то, что триггер это событие будет посылать (при попадании в него любого референса, как и прежде, поскольку триггер данным скриптом не отключается  / перемещается, хотя этот триггер может выполнять и др. задачи в др. квестах и т.д., кто знает, я его дальше не ковырял).
; скрипт будет находиться в этом состоянии, пока не будет переведен в другое командой gotoState. Поскольку в этом состоянии таковой команды нет, это можно сделать только извне. Подробнее о состояниях можно почитать на СК-вики по поиску STATE или на последних 30 страницах этой темы (+-).
endSTATE


Нарада  Offline  Сообщение №3143 написано: 20 декабря 2022, 14:56



129
Приветствую знатаков!

У меня такой вопрос. Евент на открытие книги мне известен. Но вот как сделать чтобы отследить момент закрытия книги?


Multigone  Offline  Сообщение №3144 написано: 21 декабря 2022, 20:51 | Отредактировано: Multigone - 21 декабря 2022, 21:00



832
Нарада, в SKSE есть UI.psc, там много полезного. Попробуй так:

EVENT OnRead()
   WHILE UI.IsMenuOpen("Book Menu")
      Utility.WaitMenuMode(0.5) ; если эта функция ставится на паузу вне меню (аналогично Wait() в меню), то убрать ее. ТЕСТ.
   ENDWHILE
   ; твой код
ENDEVENT

Нарада  Offline  Сообщение №3145 написано: 23 декабря 2022, 11:13



129
Благодарю )

Я никогда не пользовался SKSE. Его нужно установить в ту же папку где и СК? При упакове в bsa нужно что-то дополнительно делать? 


Multigone  Offline  Сообщение №3146 написано: 23 декабря 2022, 11:56



832
Нарада, вот осн. страница SKSE. Там же можно просмотреть инструкцию. При использовании от модмейкера требуется: для компиляции своих скриптов, SKSE функции которых он использует - иметь в папке Scripts/Source .psc скриптов SKSE. Для использования мода от пользователя требуется: иметь SKSE установленным.

Нарада  Offline  Сообщение №3147 написано: 23 декабря 2022, 13:18



129
Понял, благодарю )

Hommyak  Offline  Сообщение №3148 написано: 15 февраля 2023, 16:27



13
Multigone, благодарю за такой объемный и подробный ответ. Но кажется это для меня еще слишком сложно даже для чисто теоретического понимания, пазл никак не складывается....smilesmilesmile

Multigone  Offline  Сообщение №3149 написано: 17 февраля 2023, 15:43



832
Hommyak, а чего-там сложного-то? Ну, хорошо, давай прямо пройдемся по заданным тобой вопросам.
"В Синем дворце на лестнице перед троном есть триггер. На нем находится вот такой скрипт:"
Скрипт находится на алиасе, а не триггере. Это другое. Пока алиас заполнен триггером, скрипт алиаса будет принимать события, посылаемые триггером.

"Из написанного я понял, что триггер - в режиме ожидания, пока игрок в него не зайдет. Зайдя в него, игрок может активировать квест MS06, если квест MS05 находится на стадии не ниже и не выше указанных параметров (MS05.GetCurrentStageID()). И если при всем этом уже запущен квест MQ302 уже активирован, то установить квест MS06 в такую-то стадию. "
Триггер активен - ловит объекты, попадающие в него, и посылает скриптам (прикрепленным к нему непосредственно, либо к таким алиасам) событие об этом. Если объектом является игрок, и квест MS05 находится на стадии меньше 150 или выше 200, и квест MQ302 запущен, то перевести квест MS06 на указанную стадию, а скрипт - в неактивное состояние. Если условия не выполняются, то ничего не делать, и быть активным дальше.

"Стоит ли использовать такие скрипты с подобными "кросс-ссылками", вместо установления условий Condition внутри других квестов?"
Фактически, многие квесты управляются извне при наступлении подходящих условий. Тут посчитали, что такими условиями являются приход игрока в точку местности при определенных состояниях других квестов.
Возможно, необходимость в запуске квеста - просто способ поддержать сюжетную связь между несколькими взаимосвязанными квестами без нарушения логичности.

"Правильно ли я понял (по тексту скрипта), что данный триггер не вписывается в квест Алиасом, а стоит индивидуально, аля "глобальный игрок", а не "внутриквестовый"?"
Объекты в мире могут быть размещены заранее (в СК), или созданы (заспавнены) скриптом или алиасом в процессе игры (возможно, есть и др. пути). Насчет триггеров как объектов - вроде бы, их можно только размещать в СК. Далее, при запуске квеста, этим триггером заполняется соотв. алиас.

"Как правильно понять на какую стадию квеста тут идет ссылка? Или тут говорится что стадии квеста вооообще включаться могут - GetOwningQuest().setStage(MS06StageToSet) "
В СК, в окне добавления скрипта к объекту, щелкнуть по нему мышью, нажать ПКМ - появится меню управления Property скрипта, где можно посмотреть или изменить стартовые значения переменных, использующихся в скрипте.

"Является ли эта строка "универсалом" для триггеров (активирующих квесты или их стадии), чтобы после активации "выключать или удалять" эти триггеры -  gotoState("hasBeenTriggered")? Если в скрипте триггера (OnTriggerEnter) нет подобных функций, он будет работать один раз или постоянно один и тот же квест запускать?"
С триггером ничего не происходит - он будет так же посылать событие при попадании в него объектов. Перевод состояния происходит только для этой копии скрипта алиаса. В состоянии, где нет событий, скрипт не будет получать событие onTriggerEnter, и, следовательно, делать что-то. Вместо перевода скрипта в неактивное состояние можно, к примеру:
* очистить алиас;
* отключить триггер (Disable) (не уверен);
* переместить триггер в неигровую локацию (операции с триггером повлияют на все объекты, где он используется);
* в скрипте предусмотреть переменную bool, которая используется таким образом:
Bool bActivated = false

EVENT onTriggerEnter()
    IF (основные условия) == true
        IF bActivated == false
            bActivated = true
            (команды)
        ENDIF
    ENDIF
ENDEVENT

Stea1ch  Offline  Сообщение №3150 написано: 28 апреля 2023, 08:16



192
Напрочь все забыл! Просто от слова совсем. Задача: прикрутить к квесту скрипт с таймером на 3 игровых суток и дальнейшим enable предмета. То есть по достижении определенной стадии квеста должен стартануть таймер, по завершении которого предмет появится на локации/в ящике торговца (надо оба варианта). Ткните носом плз, в папирусе только правил существующие скрипты, логику в принципе понимаю, но забыл как и что делать...


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





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