Dsion, в общем, результаты: наложенное заклинание или выданное абилити после выгрузки НПС завершают работу своих MGEF (т.е. срабатывает OnEffectFinish()). При загрузке НПС в мир MGEF не начинают свою работу снова, однако абилити проходит проверку HasSpell() (для заклинания не знаю, чем проверить его наличие, кроме как через маг. эффект, исчезающий после выгрузки).
В случае, описанном Dsion'ом, я бы сделал на игроке апдейт с кастом др. заклинания большой площадью (игнорируя преграды), которое проверяет наличие абилити и отсутствие эффекта абилити. Если проверка пройдена, абилити накладывается снова. Пока все, что придумал.
Есть еще вариант помещать НПС в алиасы, на которых события OnLoad() и OnUnload(), но он оч. громоздкий и для конечного числа целей.
UPD: Короче, я заколебался искать косяки там, где их не должно быть.
3) Перейди во вкладку квеста Scripts. Там появится скрипт с названием QF_(Editor ID квеста)_(Form ID квеста). 4) Нажми ОК в самом квесте (т.к. сперва скрипт не может показать исходник и кнопку Add Property). 5) Открой заново этот квест и ЛКМх2 по созданному скрипту. 6) Нажми кнопку Add Property. 7) В появившемся окне выбери тип переменной (ObjectReference) и введи ее имя (MyREFR). ОК. 8) Перейди во вкладку стадий квеста, выбери нужную стадию, щелкни Log Entry. 9) Сотри написанное и скопируй туда:
MyREFR.Enable()
10) Перейди во вкладку квеста Scripts (чтобы скрипт скомпилировался снова). Нажми кнопку ОК квеста. 11) Открой квест и проверь исходник. Он должен представлять из себя такую... не знаю, как еще это назвать, шнягу.
;BEGIN FRAGMENT CODE - Do not edit anything between this and the end comment ;NEXT FRAGMENT INDEX 1 Scriptname QF__MVAActivateQUST_0207D9CF Extends Quest Hidden
;BEGIN ALIAS PROPERTY _MVAActivateREFA ;ALIAS PROPERTY TYPE ReferenceAlias ReferenceAlias Property Alias__MVAActivateREFA Auto ;END ALIAS PROPERTY
;BEGIN FRAGMENT Fragment_0 Function Fragment_0() ;BEGIN CODE MyREFR.Enable() ;END CODE EndFunction ;END FRAGMENT
;END FRAGMENT CODE - Do not edit anything between this and the begin comment
ObjectReference Property MyREFR Auto
12) Открой Property скрипта и для MyREFR укажи конкретный объект в окне рендера.
Вроде все. Алгоритма с меньшим кол-вом действий я пока не нашел.
Изменение репутации для пользователя Multigone
MultigoneOffline
Сообщение №607
| Тема: Вопросы по скриптам Papyrus
написано: 24 июня 2015, 07:27
| Отредактировано: Multigone - 24 июня 2015, 07:29
1) Я же закомментировал akTarget. 2) Для ванильных актеров лучше использовать алиас, для модных - можно вешать на ActorBase или на его ObjectReference. В зависимости от задачи, в общем.
nepewka, грузанул ты меня тем скриптом. Написал свой вариант, позволяющий актеру иметь здоровье любой величины и учитывающий любые воздействия, в т.ч. растянутый по времени урон, лечение, регенерацию и прочее. Если нужно, выложу (позже, когда проведу окончательный тест).
Scriptname _MySCRIPT2 extends Actor ; Multigone ; Для публичного распространения - комментарий выше не удалять! ; Назначение: установка произовольной величины здоровья. После инициализации текущий уровень здоровья не может превышать половины текущего максимума здоровья (нужно, чтобы учитывать все изменения уровня здоровья - как лечение, так и урон). ; Макс. урон за раз, который может получить актер, не может превышать половины текущего максимума здоровья. Макс. восстановление бонусного здоровья за раз не может превышать такую же величину. ; Нужны доп. тесты.
Float Property fB = 500.0 Auto ; Бонусное здоровье.
Bool bL ; Замок, запрещающий одновременное выполнение нескольких копий функции.
EVENT OnInit() ; Если на игрока, то события OnLoad() и OnUnload() срабатывать не будут, их можно убрать. Если на НПС, то OnInit() можно убрать. IF Self == Game.GetPlayer() S() ENDIF ENDEVENT
EVENT OnLoad() ResetHealthAndLimbs() ; Не уверен, что эта функция нужна. Возможно, актеры загружаются с восстановленными по умолчанию параметрами. S() ENDEVENT
EVENT OnCombatStateChanged(Actor xT, Int iS) ; При выходе из боя НПС восстанавливает все текущее и бонусное здоровье. IF iS == 0 && Self != Game.GetPlayer() fS = fB F() ENDIF ENDEVENT
FUNCTION S() StartDeferredKill() ; Если актер всегда переживает хит величиной, равной половине текущ. максимума здоровья, StartDeferredKill() и EndDeferredKill() не нужны, и скрипт можно подкорректировать. fS = fB fP = 0.0 fZ = 0.0 F() ENDFUNCTION
FUNCTION F() IF !bL bL =true Utility.Wait(0.001) Float fC = GetAV("Health")
; IF fC <= 0.0 ; IF fS > 0.0 ; && !IsInKillMove() ; RestoreAV("Health", 1.0 - fC) ; fS -= 1.0 ; fC = 1.0 ; ELSE ; EndDeferredKill() ; bL = false ; Return ; ENDIF ; ENDIF
IF IsInKillMove() ; Киллмуви убивает. EndDeferredKill() ; Эта функция убивает актера независимо от его текущ. здоровья. Если ее не применять, актер будет бессмертен. Этого можно избежать? bL = false Return ELSEIF fC <= 0.0 ; На всякий случай и меньше тоже. IF fS > 0.0 RestoreAV("Health", 1.0 - fC) fS -= 1.0 fC = 1.0 ELSE EndDeferredKill() bL = false Return ENDIF ENDIF Float fM = fC / GetAVPercentage("Health") * 0.5 - fP ; Дальше я сам ничего не понимаю. fP += fM fM = fP - fC fS += fC - fZ fZ = fC fC = 0.0 IF fS > fM || fM < 0.0 fC = fM ELSEIF fS > 0.0 fC = fS ENDIF IF fC > 0.0 RestoreAV("Health", fC) ELSE DamageAV("Health", -fC) ENDIF fZ += fC IF fS > fB fS = fB ENDIF RegisterForSingleUpdate(5.0) ; Время между проверками, если хитов не поступает. Можно ставить меньшие значения (желательно - выше 1.0 с). bL = false ENDIF ENDFUNCTION
FUNCTION E() WHILE bL Utility.Wait(0.001) ENDWHILE UnregisterForUpdate() ENDFUNCTION
STATE Stopped ENDSTATE
UPD. UPD. 2 UPD. 3
Изменение репутации для пользователя Multigone
MultigoneOffline
Сообщение №614
| Тема: Вопросы по скриптам Papyrus
написано: 29 июня 2015, 11:38
| Отредактировано: Multigone - 27 апреля 2020, 17:08
1) Можно задавать любой бонус к текущему максимуму здоровья. 2) Учитывается регенерация и лечение - они будут восполнять бонус здоровья, если актер имеет здоровье не меньше половины. 3) Если актер имеет большой максимум здоровья (величина вражеского хита не должна превышать половину максимума здоровья), то DefferedKill() можно убирать из кода (в версии AleksTirex'а DefferedKill() играет ключевую роль).
Тестирование показало, что свою задачу скрипт полностью выполняет (хотя всегда есть, что изменить или улучшить, а также найти неожиданные баги). К недостаткам можно отнести:
1) Здоровье всегда поддерживается на 50%, пока бонус здоровья больше нуля. Ничего нельзя поделать, это является условием реализации идеи. 2) Игрок не видит своего (или чужого) суммарного здоровья. Для игрока его можно выводить уведомлением по запросу или при достижении опред. порогов.
Цитата nepewka
Это что же получается, если атакующий наносит слишком много урона, часть его просто будет пропадать в никуда?
Именно. Если текущий максимум здоровья актера = 1000, то хиты величиной больше 500 будут ограничиваться числом 500. Это происходит потому, что текущее значение AV Health (как и сила / магия) не может быть отрицательным числом (наблюдал в игре, однако, сейчас ни в чем нельзя быть уверенным).
nepewka, а, еще кое-что.
У AleksTirex'а есть такое:
Код
Event OnUpdate() ; Регистрация через 1.0 с. if GetAV("Health") <= 0
Проверка далеко не всегда будет срабатывать. За ту секунду, которая пройдет между проверками, здоровье может отрегенерироваться на некоторую величину. Особенно это актуально для актеров, имеющих стадию предсмертного состоянию (т.е. чуть более, чем у каждого). В этой стадии они довольно быстро восполняют HP. В результате получаем:
Проверка: здоровье 0.678 (жив). Переход на следующий уровень здоровья не требуется, RestoreAV("Health", GetBaseAV("Health")) не выполняется. Удар. Здоровье = 0 Проверка: здоровье 0.777 (жив). Удар. Здоровье = 0 И т.д.
В общем, идея понятна. Чем больше интервал между обновлениями (проверками), тем с меньшим шансом (GetAV("Health") <= 0.0) вернет true.
Изменение репутации для пользователя Multigone
MultigoneOffline
Сообщение №615
| Тема: Вопросы по скриптам Papyrus
написано: 29 июня 2015, 16:20
| Отредактировано: Multigone - 29 июня 2015, 16:27
nepewka, то, что изменяемые параметры актера (магия, сила, и т.д.) не могут быть ниже 0, применимо ко всей игре. Ограничение урона - если по актеру с здоровьем 100 попадет хит величиной 5000, то здоровье актера станет не -4900, а 0.0. Поэтому уловить величину хита в 5000 невозможно, т.к. определения хита скриптом не существует, кроме как измерить разницу здоровья до хита и после него. А раз при хите 5000 изменение здоровья будет 100 - 0 = 100, то хит будет считаться равным 100. Это и есть ограничение урона, когда вместо настоящего урона в 5000 актер получает только 100. То же самое и с лечением, если при текущем здоровье 300 из 1000 будет выпито зелье, восстанавливающее 9000 здоровья, то здоровье актера станет не 9000 + 300 = 9300, а только 1000, т.е. оставшиеся 8300 уйдут в никуда. Т.е. изменяемый параметр имеет нижний предел 0, ниже которого он не может опуститься, и верхний предел в (GetBaseAV("Параметр") + все модификаторы от заклинаний и прочего).
Цитата nepewka
интервал же вообще можно убрать, ведь так?
Можно, но пауза между последовательными проверками все равно будет иметь место. К тому же, чем чаще происходит обновление, тем больше неэффективных расчетов или проверок.
Для прикола - возьми тяжелый кусок кода с множеством действий (проверок, расчетов, выполнения функций) и вставь его в обновление с малым периодом. Например, так:
EVENT OnUpdate() RegisterForSingleUpdate(0.01) (Дофигища действий, которые точно не смогут выполниться за 0.01 с.) ENDEVENT
Получится вылет на раб. стол. Ибо копии обновления (каждая со своими расчетами) не будут успевать выполняться, и количество одновременно рассчитываемых событий обновления будет возрастать в геометрической прогрессии до бесконечности (на самом деле, до вылета).
Dsion, хочешь сказать, что DeferredKill() позволяет НПС выжить после киллмуви и бегать с отрубленной головой? Неожиданная инфа... Ну, поскольку использование того скрипта имеет смысл при здоровье > 10000, и вряд ли какой агрессор может нанести хит 5000, DeferredKill() можно убрать... Или отключить киллмуви...
Или проверять, было ли киллмуви, если да, EndDeferredKill(), актер мертв.
Цитата Dsion
может, объяснишь, для чего тебе это всё?
Он хочет не просто неуязвимость, а настолько много здоровья, сколько нельзя установить через СК или игру. Может, сделать супербосса какого-нибудь. Я не проверял ограничение на 9999 HP, но допускаю, что оно имеет место быть.
(Далее текст про то, какие скрипты гибкие, универсальные и т.д.)
Изменение репутации для пользователя Multigone
MultigoneOffline
Сообщение №617
| Тема: Вопросы по скриптам Papyrus
написано: 29 июня 2015, 20:58
| Отредактировано: Multigone - 29 июня 2015, 20:59
1) Это разные вещи. Настолько, что даже представить нельзя. 2) Потому что Actor AkTarget - это параметр события OnEffectStart(). Параметр (переменная) любого события может использоваться там и нигде больше. Чтобы компилировал, нужно создать переменную в теле скрипта, которой присвоить значение переменной AkTarget при старте эффекта:
Actor MyAct
EVENT OnEffectStart(Actor xT, Actor xC) MyAct = xT ENDEVENT
EVENT OnUpdate() MyAct.TrapSoul() ENDEVENT
Почему работает в Actor (и кое-где еще) - ибо там референс (игровая копия формы СК), к которому прикреплен данный скрипт, определяется через Self:
MyAct = Self
Во всех случаях, где Self выступает в качестве объекта действия к.-л. функции, его можно не писать:
Self.GetAV("Health") GetAV("Health")
Изменение репутации для пользователя Multigone
MultigoneOffline
Сообщение №618
| Тема: Вопросы по скриптам Papyrus
написано: 30 июня 2015, 06:09
| Отредактировано: Multigone - 30 июня 2015, 06:11
Нужен трудноубиваемый босс - дай ему 99% сопротивление урону.
Трудно возразить. Вот только допускаю, что в перке нужно еще и снижать ArmorRating на такой же коэффициент, для сохранения пропорциональности полученного после всех расчетов урона. Вообще не понимаю, как работает его вычет в зависимости от величины брони.
nepewka, это мне и самому нужно. Когда вижу чью-то проблему, стараюсь ее решить, если могу это сделать (или думаю, что могу; иногда действую методом ошибок и ошибок), я пишу комментарий. Если она решена - приобретаю новые знания или лучше понимаю некоторые вещи. Такой вот эгоистичный подход.
Dsion, игрока тоже можно сделать особенным с помощью SetEssential(true)? Если да, фактически, это полноценная замена DefferedKill(), но без ее проблем. Интересно, если особенному актеру в Class установить Bleedout Default = 0.0, или непосредственно в ActorBase Bleedout Override = 0, будет ли он бессмертен? И жаль, что это можно менять только из СК.
Цитата Dsion
Если этот флаг скомбинировать с высочезной регенерацией ХП, то актор не будет ползать на коленях...
Как вариант, сразу восстанавливать ему здоровье выше предела вхождения в Bleedout.
Забавно, что bleed out подразумевает истечение кровью, а НПС в это время усиленно лечатся.
Изменение репутации для пользователя Multigone
MultigoneOffline
Сообщение №620
| Тема: Вопросы по скриптам Papyrus
написано: 30 июня 2015, 10:06
| Отредактировано: Multigone - 30 июня 2015, 10:07
nepewka, OnLoad() срабатывает, когда игрок входит в локацию (или в Cell? я в этом не разбираюсь), где находится Self в текущий момент игры. Т.е. при загрузке этой локации. Загружена 3D модель объекта или нет - не имеет значения. OnHit() срабатывает после воздействия, и когда первая строчка кода в OnHit() начнет выполняться, актер будет уже мертв.
Изменение репутации для пользователя Multigone
MultigoneOffline
Сообщение №621
| Тема: Вопросы по скриптам Papyrus
написано: 30 июня 2015, 10:40
| Отредактировано: Multigone - 30 июня 2015, 10:44
nepewka, ничего не теряется, просто скрипт прекращает работу после завершения маг. эффекта. Если в момент завершения остались какие-то активные события, то они дорабатывают свои задачи, а новые события не происходят. Сумма времени в скрипте и продолжительность маг. эффекта формально совпадают, да. Но, фактически, за 20 обновлений потратится времени больше 2 с. Интервал между последовательными обновлениями складывается из паузы (0.1) и времени на обработку кода (небольшое, но есть). Отсюда разница. Чтобы избежать, ставь продолжительность больше 2 с или увеличивай частоту обновлений.
; Adds the given count of the given form to the under the given level in this leveled list Function AddForm(Form apForm, int aiLevel, int aiCount) native
Vladislav_V, в смысле? У меня в СК такое же окно, что и на картинке.
Мировая модель задается в Armor -> World Model; модель от 3 и 1 лиц - в Armor Addon -> Biped Model и First Person соотв. Чтобы выбирать через Select -> Edit, нужные модели должны находиться в папке Meshes (распаковать из .BSA). Если путь к модели известен, можно просто скопировать его окно Select File в строку "Имя файла".
Arin, если копию объекта как-то можно заставить быть Enable / Disable при выполнении стандартных условий, то, наверное, да.
А можно сделать невидимого НПС, дать ему факел или магию со светом, запретить двигаться. Настроить пакет для доставания факела или использования магии по времени суток. Гениально!
1) За размещение насекомых в мире отвечает активатор critterSpawnInsects_Many. 2) Симуляция бабочек осущ. скриптами CritterSpawn и CritterSpawn01. 3) Для каждого из них есть Property CritterTypes, заполненное листами critterInsectsDiurnal и critterInsectsNocturnal, в которых указаны типы насекомых. 4) Каждый скрипт спавнит свои типы насекомых в заданном интервале времени между Property fStartSpawnTime и fEndSpawnTime.
Таким образом, если просто поместить этот активатор в мир, не трогая Property скрипта в референсе, то днем будут появляться только синие и оранжевые бабочки, ночью - только светляки и лунные бабочки.
SereGray, в диалоге в окне End написать одно из двух:
1)
MyREFA.ForceRefTo(akSpeaker) akSpeaker.EvaluatePackage() ; где MyREFA - твой Property алиас с нужным пакетом и флагом Optional.
2)
MyGLOB.SetValue(1.0) akSpeaker.EvaluatePackage() ; где MyGLOB - твоя Property глобальная с начальным значением 0.0. Выдай пакет нужному НПС заранее, в его условиях напиши (S) (GetGlobalValue) (Имя глобальной, которой ты заполнил Property MyGLOB) (==) (1.0) (AND).
Не забудь добавить Property для скрипта и заполнить его. Вот способ, как это сделать (написан для вкладки квеста, для диалога похожий, но не точно такой же).